WASHING MACHINE+ HA + PHOTON + MQTT = unknown sensor


#1

as been checked (in my capacity) for I don’t know how many times.
CASE: I have a particle photon with a photoresistor connected between two pins, which publishes the value of the resistance placed in front of the “end” light of a washing machine under my own WIFI. The part of Particle works, so much so that with IFTT I can start Pushbullet when the light comes on with the message “the washing machine has run out”.
I therefore created a mqtt sensor on Hassio, with also the states of unavailability (AFAIK)

[code]
 sensor:
  - platform: mqtt
    name: "Lavatrice"
    state_topic: "tele/LAVATRICE/status"
    icon: mdi:washing-machine   
    payload_available: "online"
    payload_not_available: "offline"
[/code]

and…

nothing, mosquitto refuses -o, better, truncates-the connection with

[Code]
1556805730: New client connected from 192.168.0.15 as WASHING MACHINE (c1, k15, u'MQTT ').
1556805744: Socket error on client WASHING MACHINE, disconnecting.
[/ Code]

and the sensor remains in “unknown”

This is the Photon code. I’m sure I made a trivial mistake (or maybe not, I’m not very well on MQTT) but I don’t see it.
Thank you in advance.

[code]
#include "MQTT.h"
 // Lavatrice


int photoresistor = A0; // This is where your photoresistor is plugged in. The other side goes to the "power" pin (below).

int power = A5; // This is the other end of your photoresistor. The other side is plugged into the "photoresistor" pin (above).

int analogvalue;

void callback(char* topic, byte* payload, unsigned int length);


  
   byte server[] = { 192,168,0,xx };   //IP del HA+raspi
   MQTT client(server, 1883, callback);

// receive message              UNUSED BUT I HAVE NOT REMOVED IT FOR POSSIBLE UPGRADES
void callback(char* topic, byte* payload, unsigned int length) {
    char p[length + 1];
    memcpy(p, payload, length);
    p[length] = NULL;

    if (!strcmp(p, "RED"))
        RGB.color(255, 0, 0);
    else if (!strcmp(p, "GREEN"))
        RGB.color(0, 255, 0);
    else if (!strcmp(p, "BLUE"))
        RGB.color(0, 0, 255);
    else
        RGB.color(255, 255, 255);
    delay(1000);
}


void setup() {
    
 
    pinMode(photoresistor,INPUT);  //  photoresistor pin  input (reading the photoresistor)
    pinMode(power,OUTPUT); //  pin powering the photoresistor is output (sending out consistent power)
    digitalWrite(power,HIGH);

 
    Spark.variable("analogvalue", &analogvalue, INT); //variable to be published on cloud particle


  
    
    
    RGB.control(true);  //led control
    // connect to the server
    client.connect("LAVATRICE", "MQTTUSER", "MQTTpwd");  //login to broker

    // publish/subscribe
    if (client.isConnected()) {
        client.publish("tele/LAVATRICE/status","CONNESSA");  //I don't already see this: it should put the sensor as CONNECTed as soon as the connection is made.
    }
}

void loop() {
    
    analogvalue = analogRead(photoresistor);  //reads the resistor
    if (client.isConnected())          //if it is still connected to the MQTT ...
  {
    delay(10000);                      //...after ten sec...
    RGB.color(0, 255, 0);          // ... the LED turns green

    if (analogvalue>3750)       //.....e if the light is on ...
    {
    Spark.publish("analogvalue");         //....publicishes the value of the resistor in the cloud ...
    client.publish("tele/LAVATRICE/status","FINITO");    // ...and"FINITO" on MQTTT...
    delay(300000);                     // ...and wait 5 minutes before the next reading
    }
    
    else 
    {
    Spark.publish("analogvalue");     //... otherwise it only publishes the value of the resistance in the cloud (AS IF!)
    delay(20000);                          // ...and wait only 20 seconds
    }



   
    client.loop();      // I found this, I suppose it's the keepalive
 }
        
    else            // Instead, if it's not connected to the broker ....
    {
        RGB.color(255, 0, 0);   //....the LED turns red ...
        client.publish("tele/LAVATRICE/status","DISCONNESSA");  //.....for security post the disconnection even if it will never arrive ...
    if (analogvalue>3750)           //...e if the LED is on ...
    {
    Spark.publish("analogvalue");   //.... posts the value on the cloud ...
    delay(300000);       // ...and waits 5 minutes before the next reading
    }
    else 
    {
    Spark.publish("analogvalue"); //... otherwise only publish the value of the resistance in the cloud
    delay(20000);             // ...and wait only 20 seconds
    }    
        
        //(sorry for the rubberducking).
    }
    
             }


[/code]

The problem is that the sensor on HASSIO does not even present the “online” and “offline” payloads.
I’m afraid I got something wrong between PHOTON and MQTT, but …
Again, thanks.


#2

Some points

  • you should avoid using delay() with multi second periods since that renders the device rather sluggish in regards to responsiveness.
  • A photoresistor should typically be part of a voltage divider and not merely a series resistor.
  • you are still using the outdate Spark.xxxx() functions - Nowadays it would be Particle.xxxx()
  • for Spark.variable() it should now be Particle.variable("analogvalue", analogvalue).
  • Particle.publish() (e.g. Particle.publish("analogvalue", NULL, PRIVATE); should provide a scope (since you don’t have one I’d deduce you are still targeting a rather old device OS version)
  • your code indentation doesn’t make reading your code easy, so there may be some logical not-yet-found issues too

#3

Thanks for the tips.
The Cloud spark-particle part is so far working, so I think the fault is in the MQTT part.
Anyway, I’ll try changing the cloud part too :slight_smile:
I have read and reread nearly everything I could find about it.
Am I losing the “socket” part? :confused:


#4

After reformattig your code (and slightly cleaning it up - but not yet removed the delay() calls) I found at least one more puzzling part

cleaned up code
#include "MQTT.h"

const char   *id        = "LAVATRICE";
const char   *user      = "MQTTUSER";
const char   *pwd       = "MQTTpwd";
const char   *topic     = "tele/LAVATRICE/status";
const uint8_t server[]  = { 192, 168,   0,   0};        // IP del HA+raspi

const int photoresistor = A0;                           // photoresistor in here plugged in ... 
const int power         = A5;                           // ... the other side goes to the "power" pin
int analogvalue;

void callback(char*, uint8_t*, unsigned int);
MQTT client((char*)server, 1883, callback);             // poor implementation requires a non-const char* as domain parameter - should be const!!!

void setup() {
  pinMode(photoresistor,INPUT);                         // photoresistor pin  input (reading the photoresistor)
  pinMode(power,OUTPUT);                                // pin powering the photoresistor is output (sending out consistent power)
  digitalWrite(power,HIGH);

  Particle.variable("analogvalue", analogvalue);        // variable to be published on cloud particle
  
  RGB.control(true);  //led control

  client.connect(id, user, pwd);                        // login to broker

  // publish/subscribe
  if (client.isConnected()) {
    client.publish(topic, "CONNESSA");                  // I don't already see this: it should put the sensor as CONNECTed as soon as the connection is made.
  }
}

void loop() {
  analogvalue = analogRead(photoresistor);              // reads the resistor
  if (client.isConnected()) {                           // if it is still connected to the MQTT ...
    delay(10000);                                       // ... after ten sec ...
    RGB.color(0, 255, 0);                               // ... the LED turns green

    if (analogvalue>3750) {                             // ... e if the light is on ...
      Particle.publish("analogvalue", NULL, PRIVATE);   // ... publicishes the value of the resistor in the cloud ...
      client.publish(topic, "FINITO");                  // ... and"FINITO" on MQTTT ...
      delay(300000);                                    // ... and wait 5 minutes before the next reading
    }
    else {
      Particle.publish("analogvalue", NULL, PRIVATE);   // ... otherwise it only publishes the value of the resistance in the cloud (AS IF!)
      delay(20000);                                     // ... and wait only 20 seconds
    }
    client.loop();                                      // I found this, I suppose it's the keepalive
  }
  else {                                                // Instead, if it's not connected to the broker ...
    RGB.color(255, 0, 0);                               // ... the LED turns red ...
    client.publish(topic, "DISCONNESSA");               // ... for security post the disconnection even if it will never arrive ...
    if (analogvalue > 3750) {                           // ... e if the LED is on ...
      Particle.publish("analogvalue", NULL, PRIVATE);   // ... posts the value on the cloud ...
      delay(300000);                                    // ... and waits 5 minutes before the next reading
    }
    else {
      Particle.publish("analogvalue", NULL, PRIVATE);   // ... otherwise only publish the value of the resistance in the cloud
      delay(20000);                                     // ... and wait only 20 seconds
    }    
    // (sorry for the rubberducking).
  }
}

// receive message              UNUSED BUT I HAVE NOT REMOVED IT FOR POSSIBLE UPGRADES
void callback(char* topic, byte* payload, unsigned int length) {
  char p[length + 1];
  memcpy(p, payload, length);
  p[length] = NULL;

  if (!strcmp(p, "RED"))
    RGB.color(255, 0, 0);
  else if (!strcmp(p, "GREEN"))
    RGB.color(0, 255, 0);
  else if (!strcmp(p, "BLUE"))
    RGB.color(0, 0, 255);
  else
    RGB.color(255, 255, 255);
}

The else block (for if (client.isConnected())) does call client.publish(topic, "DISCONNESSA"); which doesn’t make sense.
If the client is not connected, how would it then use that client to publish?
Also wouldn’t it be logical to attempt to reconnect once the lack of connectivity has become known?


#5

Yes, I know it is puzzling. The “hail mary” publish is just a “I know it won’t work, but maybe I can see if sometimes there’s a false disconnection”; really, I can’t understand why it does disconnect on MQTT.
Yes again, the attempt of reconnection has sense IN the loop. I moved the code a bit, I think (I SEE now) I put the connect in the wrong place.

I have to check if when the first disconnection is recoverable, is then more stable. As you pointed, as it is, it’s a one-shot :confused:

Again, I can’t understand why I can’t get into the MQTT. Am I losing part of the mandatory configuration?

(again, thank you for the cleaning :slight_smile: )


#6

You can try this
https://go.particle.io/shared_apps/5cd2d52d2327c10017875b60


#7

ASAP.
I’ll let you know the results
Thank you in advance.


#8

Ok, yesterday I was eventually at home, and I tried the code you kindly provided.
I changed only id, user, pwd and server IP.

Nothing.
Red Led after some blinking cyan and blue.
Checking and rechecking, Mosquitto’s log does not even show the connection attempt.
Thank you for now, I’ll try tweaking around mosquitto itself (that works with tasmotadmin, though) and maybe the router’s log.
Or maybe with another photon/core, to exclude an hardware fault.

Again, thank you for the help.


#9

I think there was just a minor bug in the typecast here

MQTT client((char*)server, 1883, callback);  

it should rather be

MQTT client((uint8_t*)server, 1883, callback);  

Have a try with this
https://go.particle.io/shared_apps/5ce2d70886cf77000a9edc8f
For testing I have reduced the 5 minute delay time to 30sec in line 11

const uint32_t wait[]     = { 20000, 30000 }; // delay for LED should be 20000, 300000 

#10

Something is actually happening:
from mosquitto log there’s nothing, but from particle’s consolle I had some “last evet: status ON”.
now I have to understand why MQTT service can’t see it.
uff.
I’ll be back soon.


#11

There are several problems here that may affect this behavior.

First of all, your Mosquitto logs appear to indicate a disconnection due to a timeout. IIRC the default timout in the MQTT library is 15 seconds. Thus, you see the device disconnect after 15 seconds. Socket error is a generic error.

Thus, when you call delay(10000); followed later by delay(20000); you are guaranteeing your client will disconnect

Trying to publish via mqtt if client.isConnected() == false is futile and will not help you do anything. It will just return false immediately because it has an internal check to that same connected status.

Further, if your client disconnects (for any reason), how do you reconnect? client.loop() does not do this.

solution:

  • you must get rid of your delays, as per the above recommendations
  • you should reconnect if not connected

Try this code which should do the above. I also set a last will and testament to have Mosquitto auto generate your “disconnected” message when the client disconnects.

#include "MQTT.h"

const char   *id        = "LAVATRICE";
const char   *user      = "MQTTUSER";
const char   *pwd       = "MQTTpwd";
const char   *topic     = "tele/LAVATRICE/status";
const uint8_t server[]  = { 192, 168,   0,   0};        // IP del HA+raspi

const int photoresistor = A0;                           // photoresistor in here plugged in ... 
const int power         = A5;                           // ... the other side goes to the "power" pin
int analogvalue;

const uint32_t sensor_read_delay_machine_done = 300000UL;   // 5 min...
const uint32_t sensor_read_delay_machine_not_done = 20000UL;   // 20 sec...
uint32_t  last_reading_time = 0;   // keeps track of last time we read the value.
uint32_t  sensor_read_delay = sensor_read_delay_machine_not_done;

void callback(char*, uint8_t*, unsigned int);
MQTT client((uint8_t*)server, 1883, callback);             // poor implementation requires a non-const char* as domain parameter - should be const!!!

void setup() {
  pinMode(photoresistor,INPUT);                         // photoresistor pin  input (reading the photoresistor)
  pinMode(power,OUTPUT);                                // pin powering the photoresistor is output (sending out consistent power)
  digitalWrite(power,HIGH);

  Particle.variable("analogvalue", analogvalue);        // variable to be published on cloud particle
  
  RGB.control(true);  //led control
}
  

void loop() {
  if (!client.isConnected()) {
    // connect and set a last will and testament message to be sent upon client disconnection
    client.connect(id, user, pwd, topic, 0, false, "FINITO", true);
    // publish/subscribe
    if (client.isConnected()) {
      client.publish(topic, "CONNESSA");                  // I don't already see this: it should put the sensor as CONNECTed as soon as the connection is made.
    }
  }
  else {
    client.loop(); // maintain connection 
    RGB.color(0, 255, 0);
  }

  if ((millis() - last_reading_time) > sensor_read_delay) {
    analogvalue = analogRead(photoresistor); // reads the resistor and reset the timer
    last_reading_time = millis();

    if (analogvalue>3750) {   
      sensor_read_delay = sensor_read_delay_machine_done;  // set the wait for the longer amount of time
      // ... e if the light is on ...
      if (client.isConnected()) {
        client.publish(topic, "FINITO");                  // ... and"FINITO" on MQTTT ...
      }
    }
    else {
      sensor_read_delay = sensor_read_delay_machine_not_done;   // set the wait a shorter amount of time
    }    
    if (Particle.connected()) {
        Particle.publish("analogvalue", NULL, PRIVATE);   // ... otherwise it only publishes the value of the resistance in the cloud (AS IF!)
      }
  } 
}

// receive message              UNUSED BUT I HAVE NOT REMOVED IT FOR POSSIBLE UPGRADES
void callback(char* topic, byte* payload, unsigned int length) {
  char p[length + 1];
  memcpy(p, payload, length);
  p[length] = NULL;

  if (!strcmp(p, "RED"))
    RGB.color(255, 0, 0);
  else if (!strcmp(p, "GREEN"))
    RGB.color(0, 255, 0);
  else if (!strcmp(p, "BLUE"))
    RGB.color(0, 0, 255);
  else
    RGB.color(255, 255, 255);
}

#12

I have tried again. and again.

No MQTT, all the rest -particle publish, ecc- works.
I’ll try changing more parameters client side.
thank you again.


#13

Have you tried my code?
You should only need to change the IP address and it should run.
Also try subscribing to # on any of your other clients (or your computer e.g. via MQTTBox) - this is how I checked the functionality of my code above against a test MQTT broker with your credentials.


#14

Nearly there.
I can NOW see the Photon trying to connect to the MQTT, but…

1559137707: New client connected from 192.168.0.10 as TASMOTADHT2 (c1, k10, u'MQTT').
1559138007: Client LAVATRICE has exceeded timeout, disconnecting.
1559138007: Socket error on client LAVATRICE, disconnecting.
1559138035: New connection from 192.168.0.35 on port 1883.
[INFO] found MQTT on local database
1559138036: New client connected from 192.168.0.35 as LAVATRICE (c1, k15, u'MQTT').
1559138118: Socket error on client TASMOTAVETRINA, disconnecting.
1559138119: New connection from 192.168.0.5 on port 1883.

As you can see, the only difference between “our” LAVATRICE and a successful Tasmota is k10 instead of k15 (I put the same user and password just to be sure).

Nealy there, Now I have just to understand what k parameter is :smiley: (keepalive?)

Thank you again :smiley:


#15

The difference in the k value should not play a role.
I’d have to double check but I’ve got the hunch that this only is the keep alive setting (default is 15).


#16

it was my hypotesis too, I edited while you were writing.
Ok, back to work mosquitto-side :smiley:
Thank you again.


#17

IT WORKS.
Now I have a “CONNECTED” state on my sensor in HA.
The turning point has been restarting anew with a new photon, new sketch, new libraries. Of course, copy and paste of the ones you kindly submitted, so I think OR the “old” photon was somehow defective, OR I was dragging some bug since the first attempt.
Now I have only to play around with the pubished values, last wills and so on.

Again, thank you all.