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


#1
  • 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);
  }
}

#2

@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.


#3

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,


#4

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 ?


#5

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.


#6

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.


#7

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.


#8

@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.


#9

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


#10

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.


#11

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);
  }
}