Remote Control of Timed Relays with Arduino UNO - New 

 

This project permits to remotely control, with an Arduino UNO board and miuPanel, up to five timed relays with individually programmable delay. The delay can be programmed through the graphical interface. A dedicated button allows the user to force an immediate switch at any time.

Hardware


Please note that for programming Arduino Uno, the RxD line (blue wire) has to be disconnected from the WiFi Module
(If you configure the project for controlling more than 1 relay, connect the 2nd one to PIN 9, the 3rd to PIN 10, and so on...)  

µPanel definition 

The panel definition includes a header with an edit box for setting the delay and a series of identical channels. Each channel contains the elements for controlling a Relay, from left to right:

  • A white/green round LED that represents the actual relay state
  • A grey/green Arrow LED that can be clicked (when green) to force an immediate relay switch
  • A time indicator that shows the set delay or the count-down
  • A switch that can be used to start the delayed switch
  • A button that can be used to change the delay of a channel according to the value written into the edit box

The HCTML definition of the panel is very short and consists of just three elements:

1) The desktop background and panel header:

D!g11;{%100,10!222^{%95|<Tfi#666:Powered by miuPanel;|>{>T:Set delay [s] ;E0%10:60;}}}

2) The macro definition for creating a relay channel (with one parameter for the channel name). Please note that the container cell of the Arrow LED has the click event attached (. event) used to capture clicks on the arrow. Also note that the auto-indexing feature is used to automatically assign ID to: the round LED, the click event of the cell containing the Arrow LED, the Arrow LED, the message for the count-down, the Switch, and the SET Button.

K1:/5{%90<T:?;}{!88F,33D-r20p10%90|L??1N:0;|.??L??M:0:1.817,1.818;|>%18{^b>M??:;}|^W??B:0;|B??:SET;}$

3) The macro usage that creates the relay channels:

J1(Relay A)J1(Relay B)



Arduino Code

/************************************************************************
* Remote Control of Timed Relay with Arduino UNO and miuPanel
*
* www.miuPanel.com
*
* support@miuPanel.com
*
*************************************************************************/

// Change the following defines to change the number of Relays (Max 5) and the used Pins

#define NumberOfRelays 2    // Max allowed channels 5 (It will be more with the new App release)
#define FirstRelayPIN  8    // The Arduino PIN number of the first Relay

// Connect the 1st relay to Arduino digital output 8
// Connect the 2nd relay to Arduino digital output 9
// ...
// Connect the 5th relay to Arduino digital output 12

char RelayState[NumberOfRelays];          // Current state of the Relays (0 Open, 1 Closed)
int RelayDelay_s[NumberOfRelays];         // Current delay in seconds set for each relay 
char RelayToChange[NumberOfRelays];       // Relay is counting down (0 = no, 1 = yes)
long RelayCommandTime[NumberOfRelays];    // Time of commanded switch in ms for each relay

const String RelayNames[NumberOfRelays] = {"Relay A","Relay B"};  // Set here names you like for each relay

int EditedDelay = 60;       // Current value to appear in the edit box

String Msg;                 // Variable to contain the message sent by the miuPanel Module

void InitVars(void)         // Initialize all the program variables
{
  // Setup all relay channels variables
  for(int n=0; n<NumberOfRelays; n++)
  {
     RelayState[n] = 0;          // Set all relays open 
     RelayDelay_s[n] = 10;       // Set all delays to 10 s
     RelayToChange[n] = 0;       // Set all relays steady
     RelayCommandTime[n] = 0;    // Clear all time array 
  } 
}

void setup() {

  Serial.begin(57600);           // Initialise serial
  delay(3000);                   // let miuPanel start

  InitVars();                    // Initialise program variables
  
  Serial.println("");            // Dismiss partial sent messages
  Serial.println("$PING 200");   // Enable real-time communication

  // Begin Panel Definition with background image
  Serial.print("$P:D!g11;");     
  // Define panel header 
  Serial.print("{%100,10!222^{%95|<Tfi#666:Powered by miuPanel;|>{>T:Set delay [s] ;E0%10:"+String(EditedDelay)+";}}}");
  // Define Macro for creating the relay channel 
  Serial.print("K1:/5{%90<T:?;}{!88F,33D-r20p10%90|L??1N:0;|.??L??M:0:1.817,1.818;|>%18{^b>M??:;}|^W??B:0;|B??:SET;}$");
  // Create each Relay channel using the macro
  for(int n=0; n< NumberOfRelays; n++) Serial.print("J1("+RelayNames[n]+")");
  // End the Panel definition command
  Serial.println("");

  // Configure Arduino PINS and open all relays
  for(int n=0; n<NumberOfRelays; n++) {SetRelayState(n,0); pinMode(n+FirstRelayPIN, OUTPUT);}

  // Update panel showing the delay of each relay
  for(int n=0; n< NumberOfRelays; n++) ShowSetDelay(n);

}

/*****************************************************
* This function waits a data message from the uPanel

* Input: timeout_ms time to wait for message
*        -1 for forever
* Return: 0 = Timeout, 1 = Message received
******************************************************/
int WaitMessage(int timeout_ms)
{
  int c;                                // the received byte
  unsigned long entrytime = millis();   // Read the time at the function entry
  static char KeepBuffer = 0;           // This tells if we have a partial message in the buffer

  if (!KeepBuffer) Msg = "";            // If Keepbuffer is false than clear the old message
  KeepBuffer = 0;                       // in any case set now the keep buffer to false
  
  do
  {
    while ((c = Serial.read()) > '\n') Msg += (char) c;  // Read incoming chars, if any, until new line
    if (c == '\n')                                       // is message complete?
    {
      if (Msg.substring(0,1).equals("#")) return 1;      // if it is a data message return 1
      Msg = "";                                          // otherwise, wait for another one
    }
  } while ((timeout_ms < 0) || (millis()-entrytime < timeout_ms));  // has max time passed?
  
  KeepBuffer = 1;                                                   // Keep the partial buffer content
  return 0;                                                         // if time passed, return 0
}

/*****************************************************
* This function sets relay pin state and panel LED.
* N is the number of relay and s is the state to set
******************************************************/
void SetRelayState(int n, int s)
{
  if (s)  // If s is not 0, we close the relay
  {
    RelayState[n] = 1;                      // Remeber that the ralay is now closed
    Serial.println("#L"+String(n*2)+"1");   // Turn ON the LED on the panel (we have 2 LEDs for each relay)
    digitalWrite(n+FirstRelayPIN,0);        // Close the relay
  }
   else   // otherwise, if s is 0, we open the relay
  {
    RelayState[n] = 0;                      // Remember that the relay is now open
    Serial.println("#L"+String(n*2)+"0");   // Turn ON the LED on the panel (we have 2 LEDs for each relay)
    digitalWrite(n+FirstRelayPIN,1);        // Open the relay
  }
}

/*****************************************************
* This function toggles the Relay state. 
* N is the relay number
******************************************************/
void ChangeRelayState(int n)
{
  SetRelayState(n, RelayState[n]^1);   
}

/*****************************************************
* This function starts the countdouwn for a relays 
* m is the message of the clicked Switch on the panel 
******************************************************/
void ProgramRelayState(String m)
{
  
   int RelayNumber = m.substring(2,3).toInt();   // Read the clicked Switch number 
   int RelayState = m.substring(3,4).toInt();    // Read the new switch state
   if (RelayNumber >= NumberOfRelays) return;    // Exit if the relay does not exist
  
   RelayToChange[RelayNumber] = 1;                    // Start the count-down
   Serial.println("#L"+String(RelayNumber*2+1)+"1");  // Make the arrow GREEN on the panel
   RelayCommandTime[RelayNumber] = millis();          // Save the current time of command
   SetRelayState(RelayNumber, !RelayState);           // Make sure the relay is in the other state
}

/*****************************************************
* This function processes the event generated when
* the user click on the Arraw of a relay channel 
******************************************************/
void ProcessEvent(String s)
{
   int RelayNumber = s.substring(6,7).toInt();   // Read the clicked Arraw number
   if (RelayNumber >= NumberOfRelays) return;    // Exit if the relay does not exist
   if (!RelayToChange[RelayNumber]) return;      // Exit if the relay is not in count-down
   
   // Change the time of command in order to make it expired. This will trigger the switch
   RelayCommandTime[RelayNumber] = millis() - (long)RelayDelay_s[RelayNumber]*1000; 
}

/*****************************************************
* This function processes the message generated when
* the user change the content of the edit box 
******************************************************/
void ProcessEdit(String s)
{
   int NewDelay = s.substring(4).toInt();  // Read the value into the edit box
   EditedDelay = NewDelay;                 // Save the new value 
}

/*****************************************************
* This function processes the message generated when
* the user click on a SET button 
******************************************************/
void ProcessSet(String s)
{

   int RelayNumber = s.substring(2,3).toInt();   // Read the button number
   if (RelayNumber >= NumberOfRelays) return;    // Exit if the relay does not exist
   RelayDelay_s[RelayNumber] = EditedDelay;      // Change the delay value of this relay
   ShowSetDelay(RelayNumber);                    // Update its value on the panel
}

void ShowSetDelay(int n)  // Update the delay value of Relay number N
{
   Serial.println("#M"+String(n)+String(RelayDelay_s[n])+" s");
}

void loop() {

  long Now;                  // Used to read the current time
  unsigned long DeltaTime;   // Used to compute time difference

  if (WaitMessage(500))      // Wait for a miuPanel message, but no longer than 0.5 s
  {
      if (Msg.substring(0,2).equals("#W")) ProgramRelayState(Msg);  // a Switch clicked?
      if (Msg.substring(0,6).equals("#.EVT:")) ProcessEvent(Msg);   // an Arrow clicked?
      if (Msg.substring(0,2).equals("#E")) ProcessEdit(Msg);        // the Edit box changed?
      if (Msg.substring(0,2).equals("#B")) ProcessSet(Msg);         // a SET button pushed?
  }
  
  Now = millis();  // Read the current time
  
  for(int n=0; n<NumberOfRelays; n++)                   // Process each relay channel
  {
    if (RelayToChange[n])                               // is this relay in count-down state? if yes:
    {                                                   // 
        DeltaTime = Now - RelayCommandTime[n];          // compute the passed time since command 
        if (DeltaTime >= (long)RelayDelay_s[n]*1000)    // is this passed time greater than the set delay?
        {                                               // if yes:
          ShowSetDelay(n);                              // show the set initial delay time on the panel
          RelayToChange[n] = 0;                         // remember that this relay no more counts-down
          Serial.println("#L"+String(n*2+1)+"0");       // Make its Arrow grey
          ChangeRelayState(n);                          // Switch the Relay state
        }
        else                                            // if it's not time yet to switch
        {                                               // then show del remaining time on panel
           
           Serial.println("#M"+String(n)+String(RelayDelay_s[n] - DeltaTime/1000)+" s");
        }
    }
  }

}