Digital actuators (relays) do not return to state prior to arduino reset

  • Device & model you are using
    Arduino Uno with W5100 ethernet shield

  • What dashboard are you using?
    Web and Android

  • Please describe the bug / issue as detailed as possible. Attaching the code and any relevant screenshots would be very helpful!

Upon migrating to MQTT, I discovered that if I have an Arduino reste, for example in case of a momentary power cut, when the Arduino goes back online, it does not read the state of the digital actuators and, therefore, it does not go back to the same state as prior to the reset due to blackout.
This is very bad if the system is set in a remote location and it has to be relyed upon for working in a determined manner. For example, in my case, I use to control a heather, a pump and a mixer, in a solar water heather system. In case of a momentary power cut, the system will go back to default state (be it on or off) regardless of the actual state of the actuators on the dashboard. This means that it is possible thet the actuator indicates a “ON” for (say for example) of the heather, but the heather is actually OFF (or viceversa); thus giving a totally incorrect information.
Moreover, if a system is turned ON and needs to stay ON, the power cut might put it in the OFF state and create a damage.

It would be better as it was in the old system, where upon Arduino reset after a power cut, the system would read the actuator state and go back to the same state.

Here is the code:

/*
  Solarduino main
  From "Cayenne DS18B20 Example"and other examples

                                                          "cayenne5MQTT_SolArduino1Main_camping"


  This sketch shows how to send temperature data to a DS18B20 Sensor in the Cayenne Dashboard.

  The CayenneMQTT Library is required to run this sketch. If you have not already done so you can install it from the Arduino IDE Library Manager.

  Steps:
  1. Install the OneWire library (http://www.pjrc.com/teensy/td_libs_OneWire.html) from the Arduino Library Manager.
  2. Install the DallasTemperature library (http://milesburton.com/Main_Page?title=Dallas_Temperature_Control_Library#Code.2FLibrary) from the Arduino Library Manager.
  3. In the Cayenne Dashboard add a new DS18B20 widget.
  4. Set the widget to Value Display.
  5. Select Virtual Pins and a virtual pin number.
  6. Set VIRTUAL_CHANNEL to the pin number you selected.
  7. Attach a DS18B20 to an digital pin on your Arduino.
  Schematic:
  [Ground] -- [DS18B20] -- [4.7k resistor] -- [5V]
                 |______________|
                 |
            Digital Pin
  8. Set the tmpPin variable to match the pin used to connect the DS18B20.
  9. Set the token variable to match the Arduino token from the Dashboard.
  10. Compile and upload this sketch.
  11. Once the Arduino connects to the Dashboard it should automatically update the DS18B20 widget with data.
*/

// SETTING FOT THE SOLARDUINO MAIN FOR CAMPING VERNA SOLAR PANELS SYSTEM. V 1.0
// SAME AS SOLARDUINO AMBIENT, BUT WITH ALL 4 SENSORS AND 3 ACTUATORS USED IN THIS CASE, BUT THE SKETCH HAS BEEN KEPT
// THE SAME TO ENSURE IT IS CONSISTENT. THE ONLY CHANGES CONCERN THE "Resolution test", NOT PRSENT HERE.

#define CAYENNE_PRINT Serial  // Comment this out to disable prints and save space
#include <OneWire.h>
#include <DallasTemperature.h>
#include <CayenneMQTTEthernet.h>//NEW MQTT library



// Virtual Pin of the DS18B20 widget.
#define VIRTUAL_CHANNEL1 1// sensor
#define VIRTUAL_CHANNEL2 2// sensor
#define VIRTUAL_CHANNEL3 3// sensor
#define VIRTUAL_CHANNEL4 4// sensor
#define VIRTUAL_CHANNEL5 5// sensor

#define VIRTUAL_CHANNEL7 7// relè
#define VIRTUAL_CHANNEL8 8// relè
#define VIRTUAL_CHANNEL9 9// relè


#define RELAY_DIGITAL_PIN1 7// relè pin
#define RELAY_DIGITAL_PIN2 8// relè pin
#define RELAY_DIGITAL_PIN3 9// relè pin

// Digital pin the DS18B20 is connected to. Do not use digital pins 0 or 1 since those conflict with the use of Serial.
const int tmpPin1 = 2;
const int tmpPin2 = 3;
const int tmpPin3 = 4;
const int tmpPin4 = 5;
const int tmpPin5 = 6;

unsigned long lastMillis = 0;
int x = 0;// set default for actuators; on (=1) or OFF (=0)

OneWire oneWire1(tmpPin1);
OneWire oneWire2(tmpPin2);
OneWire oneWire3(tmpPin3);
OneWire oneWire4(tmpPin4);
OneWire oneWire5(tmpPin5);

DallasTemperature sensors1(&oneWire1);
DallasTemperature sensors2(&oneWire2);
DallasTemperature sensors3(&oneWire3);
DallasTemperature sensors4(&oneWire4);
DallasTemperature sensors5(&oneWire5);


// NEW Cayenne authentication info. This should be obtained from the Cayenne Dashboard.
// Solarduino Main  - campeggio@gmail.com
char username[] = "5xxxxxxxxxxx";
char password[] = "5xxxxxxxxxxxxxxx";
char clientID[] = "9xxxxxxxxxxxxxxxxx";


// ---- static IP setting START ----

void setup()
{
  // set digital pin to output
  pinMode(RELAY_DIGITAL_PIN1, OUTPUT);// relè pin
  pinMode(RELAY_DIGITAL_PIN2, OUTPUT);// relè pin
  pinMode(RELAY_DIGITAL_PIN3, OUTPUT);// relè pin
 
 /* digitalWrite(RELAY_DIGITAL_PIN1, HIGH);// set relay to off at startup
    digitalWrite(RELAY_DIGITAL_PIN2, HIGH);// set relay to off at startup
    digitalWrite(RELAY_DIGITAL_PIN3, HIGH);// set relay to off at startup
  */

  Serial.begin(9600);
  Cayenne.begin(username, password, clientID);

  sensors1.begin();
  sensors2.begin();
  sensors3.begin();
  sensors4.begin();
  sensors5.begin();
}


void loop()
{
  Cayenne.loop();
  //Publish data every 10 seconds (10000 milliseconds). Change this value to publish at a different interval.
  if (millis() - lastMillis > 10000) {
    lastMillis = millis();
    //Write data to Cayenne here. This example just sends the current uptime in milliseconds.
    Cayenne.virtualWrite(0, (lastMillis / 1000));// write seconds since on on channel 0
    //NOTE: the following added lines is jut to try a workaorund (only on pin 7), just to test, but it did not work !
    CAYENNE_IN(7);// this should read the actuator state and reset the relay to the same value
    delay (10);
    Cayenne.virtualWrite(7, x, "digital_actuator", "d");// and this, if the abbove did work, would set the actuator pin to the corret state
  }
}

// This function is called when the Cayenne widget requests data for the Virtual Pin.
CAYENNE_OUT(VIRTUAL_CHANNEL1)
{
  // Send the command to get temperatures.
  sensors1.requestTemperatures();
  // This command writes the temperature in Celsius to the Virtual Pin.
  Cayenne.celsiusWrite(VIRTUAL_CHANNEL1, sensors1.getTempCByIndex(0));
  // To send the temperature in Fahrenheit use the corresponding code below.
  //Cayenne.fahrenheitWrite(VIRTUAL_PIN, sensors.getTempFByIndex(0));
}
CAYENNE_OUT(VIRTUAL_CHANNEL2)
{
  // Send the command to get temperatures.
  sensors2.requestTemperatures();
  // This command writes the temperature in Celsius to the Virtual Pin.
  Cayenne.celsiusWrite(VIRTUAL_CHANNEL2, sensors2.getTempCByIndex(0));
  // To send the temperature in Fahrenheit use the corresponding code below.
  //Cayenne.fahrenheitWrite(VIRTUAL_PIN, sensors.getTempFByIndex(0));
}
CAYENNE_OUT(VIRTUAL_CHANNEL3)
{
  // Send the command to get temperatures.
  sensors3.requestTemperatures();
  // This command writes the temperature in Celsius to the Virtual Pin.
  Cayenne.celsiusWrite(VIRTUAL_CHANNEL3, sensors3.getTempCByIndex(0));
  // To send the temperature in Fahrenheit use the corresponding code below.
  //Cayenne.fahrenheitWrite(VIRTUAL_PIN, sensors.getTempFByIndex(0));
}

CAYENNE_OUT(VIRTUAL_CHANNEL4)
{
  // Send the command to get temperatures.
  sensors4.requestTemperatures();
  // This command writes the temperature in Celsius to the Virtual Pin.
  Cayenne.celsiusWrite(VIRTUAL_CHANNEL4, sensors4.getTempCByIndex(0));
  // To send the temperature in Fahrenheit use the corresponding code below.
  //Cayenne.fahrenheitWrite(VIRTUAL_PIN, sensors.getTempFByIndex(0));
}

CAYENNE_OUT(VIRTUAL_CHANNEL5)
{
  // Send the command to get temperatures.
  sensors5.requestTemperatures();
  // This command writes the temperature in Celsius to the Virtual Pin.
  Cayenne.celsiusWrite(VIRTUAL_CHANNEL5, sensors5.getTempCByIndex(0));
  // To send the temperature in Fahrenheit use the corresponding code below.
  //Cayenne.fahrenheitWrite(VIRTUAL_PIN, sensors.getTempFByIndex(0));
}



CAYENNE_IN(7)
{
  CAYENNE_LOG("Channel %u, value %s", request.channel, getValue.asString());
  //Process message here. If there is an error set an error message using getValue.setError(), e.g getValue.setError("Error message");

  {
    x = getValue.asInt();// tried this as suggested, but no joy
    digitalWrite(RELAY_DIGITAL_PIN1, !x);
  }

  /*// get value sent from dashboard
    int currentValue7 = getValue.asInt(); // 7 to 9

    // assuming you wire your relay as normally open
    if (currentValue7 == 0) {
    digitalWrite(RELAY_DIGITAL_PIN1, HIGH);
    } else {
    digitalWrite(RELAY_DIGITAL_PIN1, LOW);
    }
  */
}

CAYENNE_IN(8)
{
  CAYENNE_LOG("Channel %u, value %s", request.channel, getValue.asString());
  //Process message here. If there is an error set an error message using getValue.setError(), e.g getValue.setError("Error message");

  // get value sent from dashboard
  int currentValue8 = getValue.asInt(); // 7 to 9


  // assuming you wire your relay as normally open
  if (currentValue8 == 0) {
    digitalWrite(RELAY_DIGITAL_PIN2, HIGH);
  } else {
    digitalWrite(RELAY_DIGITAL_PIN2, LOW);
  }
}

CAYENNE_IN(VIRTUAL_CHANNEL9)
{
  CAYENNE_LOG("Channel %u, value %s", request.channel, getValue.asString());
  //Process message here. If there is an error set an error message using getValue.setError(), e.g getValue.setError("Error message");

  // get value sent from dashboard
  int currentValue9 = getValue.asInt(); // 7 to 9

  // assuming you wire your relay as normally open
  if (currentValue9 == 0) {
    digitalWrite(RELAY_DIGITAL_PIN3, HIGH);
  } else {
    digitalWrite(RELAY_DIGITAL_PIN3, LOW);
  }
}

@eptak do you happen to know if the retain flag was ever enabled on the MQTT server? It’s been mentioned a couple times but I don’t think I’ve seen a definite answer.

HI @adam and @campeggio

Unfortunately the retain flag is currently not implemented in our broker since we are using delorean for data storage and streaming-api for delivery to apps.

To fulfill actuator reset feature, I’m considering extending our MQTT API with new topics the device could use to retrieve last state from delorean through MQTT. But I concur using retain flag for command would be better.

Best,

1 Like

Hello, sorry I am not in to this technical matters, I cannot quite understand all this jargon, but does it mean that we will have a solution soon ?

Sounds like it’s still in the planning stage, so might be a little while yet. @eptak would have an ETA if there is one yet.

I observed similar results : following a restart of my Arduino, the actuator status on my dashboard sometimes differs from my Arduino output pin state. Furthermore, if a CAYENNE_IN(x) message has been initiated by a Trigger I don’t think (cannot actually test it) that the actuator status on the dashboard is updated accordingly. So for every critical actuator widget I read the status of my Arduino output pin and send it back to an independant digital state widget. I send this status during the Setup function and thereafter at every status change.

I tested if I could change the actuator status on the dashboard with a Cayenne.virtualWrite(…) and yes I could, for digital and analog actuators. So I would do this during the setup function, to reflect the default startup values. But I will anyway continue to send back the status of my Arduino output pin, as a true confirmation of the command. In fact the closest to the field I can take my confirmation input is the better.

Concerning the actuator status on the dashboard, I could not read it. The CAYENNE_IN (x) function probably returns something only if a message coming from Cayenne is already present in the input buffer.

Finally, I don’t think this would be a good idea to always automatically restore the values to what they were before a shutdown or power failure. The user should have the choice to restore the values or not. Some process may require specific initial conditions which may not be present after the power failure or require a specific sequence of events (delays between different actuators , previous changes of states required, etc. ) . Meanwhile, if required, one could for exemple store the last values before the power failure in Arduino EEPROM or maybe on an SD card of the Ethernet shield and use these values during the Restart.

All this requires a lot of “extra” programming and, consequently, programming skills, thus making Cayenne advertised simple programming not so simple any longer.
Moreover, we all know that Arduino eeprom has a limited life, so to keep on writing to it with a high frequency might cause instability in the long run.
I agree that there might be some cases when an initial sequence of events needs to be done, but this is the “out of standard” case and such users, that have a special need, should put a starting procedure in their setup and not affecting all other users.

Finally, the change from previous system to new one was not given a a choice, but forced down. And the fact that there are inconsistencies in the two way of working was not sufficiently highlighted, to my view, this is causing mach discomfort and lots of issues to low level users, such as I am.
High level users do not require the easy and comfortable environment of Cayenne, as they could program with other, more sophisticated, platforms.

I sincerely hope that Cayenne staff will put a quick and urgent fix to this issue, or I fear lots of users might be pushed away from this platform that might loose out popularity very quickly.

@campeggio yes, I agree with all the points in your post. Cayenne is still growing, and these MQTT pains will eventually disappear and provide everyone with a much more stable platform. I can assure you that everything you touched on is being worked on, unfortunately for us we just have to wait it out until it’s released.

1 Like

Well noted, but no ETA yet on this. Will be clearer after the next big Cayenne release.

The problems are not improving, instead they are getting much worse. Specially the slowness is greatly unacceptable; it takes minutes for the display to refresh and nobody can stay on a display for minutes, after clicking on a button, to verify if it has acted or not. And even if one did, there is no reasonable certainty that it has actually changed the state of the actuator, as the extremely long time makes it impossible to have a reliable situation under control.
As much as I like Cayenne platform, I fear I will have to look for some alternative soon, because the slow response is so bad that it makes the system totally useless.
Hope to have fast reaction on speed issues, but we are talking of days, not weeks or months, to be of any use.
It is a real shame, as the system, prior to MQTT was quite good.

made some changes to your code. can you give it a try and get back if there are any bugs.

/*
  Solarduino main
  From "Cayenne DS18B20 Example"and other examples

                                                          "cayenne5MQTT_SolArduino1Main_camping"


  This sketch shows how to send temperature data to a DS18B20 Sensor in the Cayenne Dashboard.

  The CayenneMQTT Library is required to run this sketch. If you have not already done so you can install it from the Arduino IDE Library Manager.

  Steps:
  1. Install the OneWire library (http://www.pjrc.com/teensy/td_libs_OneWire.html) from the Arduino Library Manager.
  2. Install the DallasTemperature library (http://milesburton.com/Main_Page?title=Dallas_Temperature_Control_Library#Code.2FLibrary) from the Arduino Library Manager.
  3. In the Cayenne Dashboard add a new DS18B20 widget.
  4. Set the widget to Value Display.
  5. Select Virtual Pins and a virtual pin number.
  6. Set VIRTUAL_CHANNEL to the pin number you selected.
  7. Attach a DS18B20 to an digital pin on your Arduino.
  Schematic:
  [Ground] -- [DS18B20] -- [4.7k resistor] -- [5V]
                 |______________|
                 |
            Digital Pin
  8. Set the tmpPin variable to match the pin used to connect the DS18B20.
  9. Set the token variable to match the Arduino token from the Dashboard.
  10. Compile and upload this sketch.
  11. Once the Arduino connects to the Dashboard it should automatically update the DS18B20 widget with data.
*/

// SETTING FOT THE SOLARDUINO MAIN FOR CAMPING VERNA SOLAR PANELS SYSTEM. V 1.0
// SAME AS SOLARDUINO AMBIENT, BUT WITH ALL 4 SENSORS AND 3 ACTUATORS USED IN THIS CASE, BUT THE SKETCH HAS BEEN KEPT
// THE SAME TO ENSURE IT IS CONSISTENT. THE ONLY CHANGES CONCERN THE "Resolution test", NOT PRSENT HERE.

#define CAYENNE_PRINT Serial  // Comment this out to disable prints and save space
#include <OneWire.h>
#include <DallasTemperature.h>
#include <CayenneMQTTEthernet.h>//NEW MQTT library



// Virtual Pin of the DS18B20 widget.
#define VIRTUAL_CHANNEL1 1// sensor
#define VIRTUAL_CHANNEL2 2// sensor
#define VIRTUAL_CHANNEL3 3// sensor
#define VIRTUAL_CHANNEL4 4// sensor
#define VIRTUAL_CHANNEL5 5// sensor

#define VIRTUAL_CHANNEL7 7// relè
#define VIRTUAL_CHANNEL8 8// relè
#define VIRTUAL_CHANNEL9 9// relè


#define RELAY_DIGITAL_PIN1 7// relè pin
#define RELAY_DIGITAL_PIN2 8// relè pin
#define RELAY_DIGITAL_PIN3 9// relè pin

// Digital pin the DS18B20 is connected to. Do not use digital pins 0 or 1 since those conflict with the use of Serial.
const int tmpPin1 = 2;
const int tmpPin2 = 3;
const int tmpPin3 = 4;
const int tmpPin4 = 5;
const int tmpPin5 = 6;

unsigned long lastMillis = 0;
int x = 0;// set default for actuators; on (=1) or OFF (=0)

OneWire oneWire1(tmpPin1);
OneWire oneWire2(tmpPin2);
OneWire oneWire3(tmpPin3);
OneWire oneWire4(tmpPin4);
OneWire oneWire5(tmpPin5);

DallasTemperature sensors1(&oneWire1);
DallasTemperature sensors2(&oneWire2);
DallasTemperature sensors3(&oneWire3);
DallasTemperature sensors4(&oneWire4);
DallasTemperature sensors5(&oneWire5);


// NEW Cayenne authentication info. This should be obtained from the Cayenne Dashboard.
// Solarduino Main  - campeggio@gmail.com
char username[] = "5xxxxxxxxxxx";
char password[] = "5xxxxxxxxxxxxxxx";
char clientID[] = "9xxxxxxxxxxxxxxxxx";


// ---- static IP setting START ----

void setup()
{
  // set digital pin to output
  pinMode(RELAY_DIGITAL_PIN1, OUTPUT);// relè pin
  pinMode(RELAY_DIGITAL_PIN2, OUTPUT);// relè pin
  pinMode(RELAY_DIGITAL_PIN3, OUTPUT);// relè pin

  /* digitalWrite(RELAY_DIGITAL_PIN1, HIGH);// set relay to off at startup
     digitalWrite(RELAY_DIGITAL_PIN2, HIGH);// set relay to off at startup
     digitalWrite(RELAY_DIGITAL_PIN3, HIGH);// set relay to off at startup
  */

  Serial.begin(9600);
  Cayenne.begin(username, password, clientID);

  sensors1.begin();
  sensors2.begin();
  sensors3.begin();
  sensors4.begin();
  sensors5.begin();
}


void loop()
{
  Cayenne.loop();
  //Publish data every 10 seconds (10000 milliseconds). Change this value to publish at a different interval.
  if (millis() - lastMillis > 10000) {
    lastMillis = millis();
    sensors1.requestTemperatures();
    Cayenne.celsiusWrite(VIRTUAL_CHANNEL1, sensors1.getTempCByIndex(0));
    sensors2.requestTemperatures();
    Cayenne.celsiusWrite(VIRTUAL_CHANNEL2, sensors2.getTempCByIndex(0));
    sensors3.requestTemperatures();
    Cayenne.celsiusWrite(VIRTUAL_CHANNEL3, sensors3.getTempCByIndex(0));
    sensors4.requestTemperatures();
    Cayenne.celsiusWrite(VIRTUAL_CHANNEL4, sensors4.getTempCByIndex(0));
    sensors5.requestTemperatures();
    Cayenne.celsiusWrite(VIRTUAL_CHANNEL5, sensors5.getTempCByIndex(0));

    Cayenne.virtualWrite(0, (lastMillis / 1000));// write seconds since on on channel 0
    Cayenne.virtualWrite(7, x, "digital_actuator", "d");// and this, if the abbove did work, would set the actuator pin to the corret state.

  }

}

CAYENNE_IN(7)
{
  CAYENNE_LOG("Channel %u, value %s", request.channel, getValue.asString());
  //Process message here. If there is an error set an error message using getValue.setError(), e.g getValue.setError("Error message");

  {
    x = getValue.asInt();// tried this as suggested, but no joy
    digitalWrite(RELAY_DIGITAL_PIN1, !x);
  }

  /*// get value sent from dashboard
    int currentValue7 = getValue.asInt(); // 7 to 9

    // assuming you wire your relay as normally open
    if (currentValue7 == 0) {
    digitalWrite(RELAY_DIGITAL_PIN1, HIGH);
    } else {
    digitalWrite(RELAY_DIGITAL_PIN1, LOW);
    }
  */
}

CAYENNE_IN(8)
{
  CAYENNE_LOG("Channel %u, value %s", request.channel, getValue.asString());
  //Process message here. If there is an error set an error message using getValue.setError(), e.g getValue.setError("Error message");

  // get value sent from dashboard
  int currentValue8 = getValue.asInt(); // 7 to 9


  // assuming you wire your relay as normally open
  if (currentValue8 == 0) {
    digitalWrite(RELAY_DIGITAL_PIN2, HIGH);
  } else {
    digitalWrite(RELAY_DIGITAL_PIN2, LOW);
  }
}

CAYENNE_IN(VIRTUAL_CHANNEL9)
{
  CAYENNE_LOG("Channel %u, value %s", request.channel, getValue.asString());
  //Process message here. If there is an error set an error message using getValue.setError(), e.g getValue.setError("Error message");

  // get value sent from dashboard
  int currentValue9 = getValue.asInt(); // 7 to 9

  // assuming you wire your relay as normally open
  if (currentValue9 == 0) {
    digitalWrite(RELAY_DIGITAL_PIN3, HIGH);
  } else {
    digitalWrite(RELAY_DIGITAL_PIN3, LOW);
  }
}

Hi Shramik, I had to abandon the issue, as more urgent activities neede my attention. I have had no chance yet to try your modifications, as I would have to dismantle the existing system and try your modifications and, if not good, return to original setup. The user is now very disappointed and is asking me to abandon Cayenne to find a more reliable system, so I cannot spend time in tests that do not have a very good possibility to fix all the issues.
I need to understand the cause of the problems, to properly evaluate any possible solution so, if you can, I would ask you to state what changes did you make and why these should/could fix the problems.
After this, I will decide if to give it a try, if I am confident it might work, or if to take other solutions.
Thank yoo for understanding and for your help, Giorgio

it was small change i had made, which on reading your entire post seems to not solve the problem.
so lets start with new. the problem you are facing is when a arduino reset, the dashboard state and relay pin state are not same. So the best solution for this is to use EEPROM and save data on state change. Next read and write this state to cayenne on startup.

Thank you Shramik, but I am not in favor of using the EEprom, as already stated, because it entails too much complex code and, most of all, it has a limited life time.

But this was not the most important issue. The speed of reaction (now seems a little better) combined with an unreliable refresh of the icons, give a great issue, making the entire system completely useless, in a sense that you cannot be sure the action you have made has or not had effect.
This means that if you do press a button to turn (say for example) a pump on, you never can be sure it has activated after you command and you loose completely the notion if the pump is now ON or OFF.
But as mentioned, it seems that the situations is now a little better, dough I never seen any info o message telling us of any changes made, it does however appear a little improved. So we hope it will return to a properly working situation soon and we will not have to abandon Cayenne altogether.
Thank you, Giorgio

you can write 100000 times on arduino uno EEPROM, which according to me is more than enough for 10 years if you change state of 3 outputs for 10 times a days.

this all issues can be solved by reading the state of the relay pin and writing to the dashboard widget every 10 seconds to make sure that the relay pin and dashboard are in sync.

my panel continues with this problem

Any updates on this? Better solution still to write on EEPROM?

no update on retain flag till now. EEPROM is the best option now.

Hi Shramik, also in the other topics answered by you looks like there´s some kind of syncronization issue with relays (real state of it) and Mobile App + PC Dashboard. I.e if I open my Android app now my garage light are turned on but in reality it´s turned off. Sometimes state in App is different from Dashboard…

This synch issue between mobile app / Dashboard could be also due to higher latency of MQTT protocol?

Do you think I can improve this writing the state changes in EEPROM? Any link to a code example on how to do it!?

Thanks!

i was not able to reproduce this issue at my end. Both my device and web/app were sync.
you dont need EEFROM if the device wont reboot.
one way to sync the output and dashboard device is to read the status of the pin and send this value back to cayenne dashboard.
if the device reboots you will need to store the data in EEPROM and on reboot read the data from the EEPROM and send it back to cayenne and write it to output so all are in sync.