Remote Control of Timed Relays with Arduino UNO
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
- Arduino UNO Board
- ESP-01 WiFi module (with µPanel Firmware)
- ADP-01 Breadboard adapter
- Breadboard
- Relay Board
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"); } } } }