Array Relay Button Actuator
-
Ok, there is an improvement. Relays now turn to same state as they were before reboot most of the time but not always. Sometimes they inverse - the one which was OFF turns ON and vice-versa. Sometimes (1 in 3) they switch ON and OFF (only for split second) in the loop function. See below screenshots:
This sketch is used with toggle button, same as on this photo.
-
Ok, there is an improvement. Relays now turn to same state as they were before reboot most of the time but not always. Sometimes they inverse - the one which was OFF turns ON and vice-versa. Sometimes (1 in 3) they switch ON and OFF (only for split second) in the loop function. See below screenshots:
This sketch is used with toggle button, same as on this photo.
Could be the toggle is still bouncing at read time. Try increasing the debouncer interval. For example, double it until no more issues. Then decrease it in halves of what the last increase was until you see the issue again. This way you can try to find the smallest debounce time needed.
-
Ok, there is an improvement. Relays now turn to same state as they were before reboot most of the time but not always. Sometimes they inverse - the one which was OFF turns ON and vice-versa. Sometimes (1 in 3) they switch ON and OFF (only for split second) in the loop function. See below screenshots:
This sketch is used with toggle button, same as on this photo.
@niccodemi You can forget using the oldValue, because the debounce takes track of it's last status. When update() is called and a debounced pin-change is detect this function will return true otherwise it will send false.
Having a short delay each loop will stabilize the debouncers so your loop() can look like this:void loop() { for (byte i = 0; i < noRelays; i++) { if (debouncer[i].update()) { Relays[i].relayState = !Relays[i].relayState; digitalWrite(Relays[i].relayPin, Relays[i].relayState?RELAY_ON:RELAY_OFF); send(msg[i].set(Relays[i].relayState? true : false)); // save sensor state in EEPROM (location == sensor number) saveState( i, Relays[i].relayState ); } } wait(10); } -
@niccodemi You can forget using the oldValue, because the debounce takes track of it's last status. When update() is called and a debounced pin-change is detect this function will return true otherwise it will send false.
Having a short delay each loop will stabilize the debouncers so your loop() can look like this:void loop() { for (byte i = 0; i < noRelays; i++) { if (debouncer[i].update()) { Relays[i].relayState = !Relays[i].relayState; digitalWrite(Relays[i].relayPin, Relays[i].relayState?RELAY_ON:RELAY_OFF); send(msg[i].set(Relays[i].relayState? true : false)); // save sensor state in EEPROM (location == sensor number) saveState( i, Relays[i].relayState ); } } wait(10); }Why do you think it's better to add the wait than to increase debounce interval? Didn't know update returns a boolean, cool.
-
Why do you think it's better to add the wait than to increase debounce interval? Didn't know update returns a boolean, cool.
@martinhjelmare there is no reason to have that many button checks per millisecond, if you "wait" each while loop the MySensors core has more time to process messages i.s.o. of reading I/O pins especially when your node acts as a repeater. (During a wait the process function is called)
A person will not notices a button to take 50 i.s.o 10 milliseconds to respond. -
@martinhjelmare there is no reason to have that many button checks per millisecond, if you "wait" each while loop the MySensors core has more time to process messages i.s.o. of reading I/O pins especially when your node acts as a repeater. (During a wait the process function is called)
A person will not notices a button to take 50 i.s.o 10 milliseconds to respond.Yes, very good point.
-
@martinhjelmare @BartE thank you both for trying to solve this issue but the problem is still there. On reboot relay (usually just 1 never both) gets set randomly in loop function (setup function always restores correct state). I also tried increasing debouncer up to 500 without success. And I am also sure this is not hardware related because if I load sketch which is meant for momentary switch relays work as expected (tested with 10 consecutive reboots). Just to be on the same page this is latest sketch:
//MultiRelayButton Sketch, MySensors 1.6 #define MY_RADIO_NRF24 #define MY_REPEATER_FEATURE #include <MySensor.h> #include <SPI.h> #include <Bounce2.h> #define RELAY_ON 0 // switch around for realy HIGH/LOW state #define RELAY_OFF 1 // #define noRelays 2 //2-4 const int relayPin[] = {14,15}; // switch around pins to your desire const int buttonPin[] = {3,4}; // switch around pins to your desire class Relay // relay class, store all relevant data (equivalent to struct) { public: int buttonPin; // physical pin number of button int relayPin; // physical pin number of relay //byte oldValue; // last Values for key (debounce) boolean relayState; // relay status (also stored in EEPROM) }; Relay Relays[noRelays]; Bounce debouncer[noRelays]; MyMessage msg[noRelays]; void setup(){ sendHeartbeat(); wait(250); // Initialize Relays with corresponding buttons for (int i = 0; i < noRelays; i++){ Relays[i].buttonPin = buttonPin[i]; // assign physical pins Relays[i].relayPin = relayPin[i]; msg[i].sensor = i; // initialize messages msg[i].type = V_LIGHT; debouncer[i] = Bounce(); // initialize debouncer debouncer[i].attach(buttonPin[i]); debouncer[i].interval(100); pinMode(Relays[i].buttonPin, INPUT_PULLUP); wait(250); pinMode(Relays[i].relayPin, OUTPUT); //Relays[i].oldValue = digitalRead(Relays[i].buttonPin); Relays[i].relayState = loadState(i); // retrieve last values from EEPROM digitalWrite(Relays[i].relayPin, Relays[i].relayState? RELAY_ON:RELAY_OFF); // and set relays accordingly send(msg[i].set(Relays[i].relayState? true : false)); // make controller aware of last status wait(250); } } void presentation() { sendSketchInfo("MultiRelayButton", "0.9k"); wait(250); for (int i = 0; i < noRelays; i++) present(i, S_LIGHT); // present sensor to gateway wait(250); } void loop() { for (byte i = 0; i < noRelays; i++) { if (debouncer[i].update()) { Relays[i].relayState = !Relays[i].relayState; digitalWrite(Relays[i].relayPin, Relays[i].relayState?RELAY_ON:RELAY_OFF); send(msg[i].set(Relays[i].relayState? true : false)); // save sensor state in EEPROM (location == sensor number) saveState( i, Relays[i].relayState ); } } wait(50); } // process incoming message void receive(const MyMessage &message){ if (message.type == V_LIGHT){ if (message.sensor <noRelays){ // check if message is valid for relays..... previous line [[[ if (message.sensor <=noRelays){ ]]] Relays[message.sensor].relayState = message.getBool(); digitalWrite(Relays[message.sensor].relayPin, Relays[message.sensor].relayState? RELAY_ON:RELAY_OFF); // and set relays accordingly saveState( message.sensor, Relays[message.sensor].relayState ); // save sensor state in EEPROM (location == sensor number) } } wait(50); } -
@martinhjelmare @BartE thank you both for trying to solve this issue but the problem is still there. On reboot relay (usually just 1 never both) gets set randomly in loop function (setup function always restores correct state). I also tried increasing debouncer up to 500 without success. And I am also sure this is not hardware related because if I load sketch which is meant for momentary switch relays work as expected (tested with 10 consecutive reboots). Just to be on the same page this is latest sketch:
//MultiRelayButton Sketch, MySensors 1.6 #define MY_RADIO_NRF24 #define MY_REPEATER_FEATURE #include <MySensor.h> #include <SPI.h> #include <Bounce2.h> #define RELAY_ON 0 // switch around for realy HIGH/LOW state #define RELAY_OFF 1 // #define noRelays 2 //2-4 const int relayPin[] = {14,15}; // switch around pins to your desire const int buttonPin[] = {3,4}; // switch around pins to your desire class Relay // relay class, store all relevant data (equivalent to struct) { public: int buttonPin; // physical pin number of button int relayPin; // physical pin number of relay //byte oldValue; // last Values for key (debounce) boolean relayState; // relay status (also stored in EEPROM) }; Relay Relays[noRelays]; Bounce debouncer[noRelays]; MyMessage msg[noRelays]; void setup(){ sendHeartbeat(); wait(250); // Initialize Relays with corresponding buttons for (int i = 0; i < noRelays; i++){ Relays[i].buttonPin = buttonPin[i]; // assign physical pins Relays[i].relayPin = relayPin[i]; msg[i].sensor = i; // initialize messages msg[i].type = V_LIGHT; debouncer[i] = Bounce(); // initialize debouncer debouncer[i].attach(buttonPin[i]); debouncer[i].interval(100); pinMode(Relays[i].buttonPin, INPUT_PULLUP); wait(250); pinMode(Relays[i].relayPin, OUTPUT); //Relays[i].oldValue = digitalRead(Relays[i].buttonPin); Relays[i].relayState = loadState(i); // retrieve last values from EEPROM digitalWrite(Relays[i].relayPin, Relays[i].relayState? RELAY_ON:RELAY_OFF); // and set relays accordingly send(msg[i].set(Relays[i].relayState? true : false)); // make controller aware of last status wait(250); } } void presentation() { sendSketchInfo("MultiRelayButton", "0.9k"); wait(250); for (int i = 0; i < noRelays; i++) present(i, S_LIGHT); // present sensor to gateway wait(250); } void loop() { for (byte i = 0; i < noRelays; i++) { if (debouncer[i].update()) { Relays[i].relayState = !Relays[i].relayState; digitalWrite(Relays[i].relayPin, Relays[i].relayState?RELAY_ON:RELAY_OFF); send(msg[i].set(Relays[i].relayState? true : false)); // save sensor state in EEPROM (location == sensor number) saveState( i, Relays[i].relayState ); } } wait(50); } // process incoming message void receive(const MyMessage &message){ if (message.type == V_LIGHT){ if (message.sensor <noRelays){ // check if message is valid for relays..... previous line [[[ if (message.sensor <=noRelays){ ]]] Relays[message.sensor].relayState = message.getBool(); digitalWrite(Relays[message.sensor].relayPin, Relays[message.sensor].relayState? RELAY_ON:RELAY_OFF); // and set relays accordingly saveState( message.sensor, Relays[message.sensor].relayState ); // save sensor state in EEPROM (location == sensor number) } } wait(50); }I think you should setup the button first, pinMode etc, and then setup the debouncer and attach it to the button.
https://github.com/thomasfredericks/Bounce2/wiki
If you attach the debouncer while the pin is floating, the debouncer will probably have a hard time figuring out correct new state.
-
That was it. After I amended sketch according to your advise all the random state changes disappeared. I rebooted node over 20 times in various button / relay states and it always started with relays in correct position. For anyone else interested below is amended sketch.
//MultiRelayButton Sketch, MySensors 1.6, toggle switch #define MY_RADIO_NRF24 #define MY_REPEATER_FEATURE #include <MySensor.h> #include <SPI.h> #include <Bounce2.h> #define RELAY_ON 0 // switch around for ACTIVE LOW / ACTIVE HIGH relay #define RELAY_OFF 1 // #define noRelays 2 //2-4 const int relayPin[] = {14,15}; // switch around pins to your desire const int buttonPin[] = {3,4}; // switch around pins to your desire class Relay // relay class, store all relevant data (equivalent to struct) { public: int buttonPin; // physical pin number of button int relayPin; // physical pin number of relay boolean relayState; // relay status (also stored in EEPROM) }; Relay Relays[noRelays]; Bounce debouncer[noRelays]; MyMessage msg[noRelays]; void setup(){ sendHeartbeat(); wait(100); // Initialize Relays with corresponding buttons for (int i = 0; i < noRelays; i++){ Relays[i].buttonPin = buttonPin[i]; // assign physical pins Relays[i].relayPin = relayPin[i]; msg[i].sensor = i; // initialize messages msg[i].type = V_LIGHT; pinMode(Relays[i].buttonPin, INPUT_PULLUP); wait(100); pinMode(Relays[i].relayPin, OUTPUT); Relays[i].relayState = loadState(i); // retrieve last values from EEPROM digitalWrite(Relays[i].relayPin, Relays[i].relayState? RELAY_ON:RELAY_OFF); // and set relays accordingly send(msg[i].set(Relays[i].relayState? true : false)); // make controller aware of last status wait(50); debouncer[i] = Bounce(); // initialize debouncer debouncer[i].attach(buttonPin[i]); debouncer[i].interval(20); wait(50); } } void presentation() { sendSketchInfo("MultiRelayButton", "0.9t"); wait(100); for (int i = 0; i < noRelays; i++) present(i, S_LIGHT); // present sensor to gateway wait(100); } void loop() { for (byte i = 0; i < noRelays; i++) { if (debouncer[i].update()) { Relays[i].relayState = !Relays[i].relayState; digitalWrite(Relays[i].relayPin, Relays[i].relayState?RELAY_ON:RELAY_OFF); send(msg[i].set(Relays[i].relayState? true : false)); // save sensor state in EEPROM (location == sensor number) saveState( i, Relays[i].relayState ); } } wait(20); } // process incoming message void receive(const MyMessage &message){ if (message.type == V_LIGHT){ if (message.sensor <noRelays){ // check if message is valid for relays..... previous line [[[ if (message.sensor <=noRelays){ ]]] Relays[message.sensor].relayState = message.getBool(); digitalWrite(Relays[message.sensor].relayPin, Relays[message.sensor].relayState? RELAY_ON:RELAY_OFF); // and set relays accordingly saveState( message.sensor, Relays[message.sensor].relayState ); // save sensor state in EEPROM (location == sensor number) } } wait(20); } -
What is the simplest method to add two extra butons to this sketch not tied to the relay?
I have a four button device. Two will work with relays and two extra utilised in openhab rules -
Just in case someone else finds this thread and decides to use the sketch and runs in to the same issue I did.
I was using push buttons and the sketch above, when I pushed the button the relay turned on but as soon as I let go of the button the relay turned back off. I changed the sketch like this:
void loop() { for (byte i = 0; i < noRelays; i++) { if (debouncer[i].update()) { int value = debouncer[i].read(); if ( value == LOW) { Relays[i].relayState = !Relays[i].relayState; digitalWrite(Relays[i].relayPin, Relays[i].relayState ? RELAY_ON : RELAY_OFF); send(msg[i].set(Relays[i].relayState ? true : false)); // save sensor state in EEPROM (location == sensor number) saveState( i, Relays[i].relayState ); } } } //wait(20); }Now the relay changes state as soon as I push the button and keeps the state until I push the button again.
Because my inputs for the push buttons are ground when pushed I used
if ( value == LOW)This way the relay would turn on immediately when I push the button and not wait until I let go of the button.
-
Just in case someone else finds this thread and decides to use the sketch and runs in to the same issue I did.
I was using push buttons and the sketch above, when I pushed the button the relay turned on but as soon as I let go of the button the relay turned back off. I changed the sketch like this:
void loop() { for (byte i = 0; i < noRelays; i++) { if (debouncer[i].update()) { int value = debouncer[i].read(); if ( value == LOW) { Relays[i].relayState = !Relays[i].relayState; digitalWrite(Relays[i].relayPin, Relays[i].relayState ? RELAY_ON : RELAY_OFF); send(msg[i].set(Relays[i].relayState ? true : false)); // save sensor state in EEPROM (location == sensor number) saveState( i, Relays[i].relayState ); } } } //wait(20); }Now the relay changes state as soon as I push the button and keeps the state until I push the button again.
Because my inputs for the push buttons are ground when pushed I used
if ( value == LOW)This way the relay would turn on immediately when I push the button and not wait until I let go of the button.
@korttoma I used this sketch but my vera founds only 1 device and i have no idea why.
-
@korttoma I used this sketch but my vera founds only 1 device and i have no idea why.
@Стоян-Георгиев please post the complete sketch along with info about MySensors library version you are using.