How to detect mqtt publisher presence

1,002 views
Skip to first unread message

Michel Jutras

unread,
Mar 9, 2017, 6:32:10 PM3/9/17
to Node-RED
I've been trying to figure this out for a while now...

I have an ESP8266 sending the readings from a couple of sensors (temperature) every 5 seconds.
I pickup the readings with node mqtt input, process with a function and then publish on the dashboard.
If the publisher's connection drops, the last readings remains on the dashboard gauges giving false indications.
if ESP8266 stops sending data, I would like to report 0 on the gauges or display some king of indication.

Nick O'Leary

unread,
Mar 9, 2017, 6:44:42 PM3/9/17
to Node-RED Mailing List
Hi Michel,  have a look at the 'Last Will & Testament' feature of MQTT. The client library you're using on the ESP ought to support it. This is where the client can register a message when it connects that the broker should publish if the client loses its connection unexpectedly. You can then use the Will message to trigger a tidy up of the gauges etc.

Nick

--
http://nodered.org
 
Join us on Slack to continue the conversation: http://nodered.org/slack
---
You received this message because you are subscribed to the Google Groups "Node-RED" group.
To unsubscribe from this group and stop receiving emails from it, send an email to node-red+unsubscribe@googlegroups.com.
To post to this group, send email to node...@googlegroups.com.
Visit this group at https://groups.google.com/group/node-red.
To view this discussion on the web, visit https://groups.google.com/d/msgid/node-red/23c4a631-a1bf-4f79-8f2a-f3971ac90c91%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Michel Jutras

unread,
Mar 10, 2017, 7:48:51 AM3/10/17
to Node-RED
Thank you Nick,

I think I will take a step back and go through the mqtt documentation.

Michel Jutras

unread,
Mar 10, 2017, 3:18:27 PM3/10/17
to Node-RED
OK that didn't take long to get myself stuck but I am stuck.

I have this code:

/*
 MQTT "will" message example
  - connects to an MQTT server with a will message
  - publishes a message
  - waits a little bit
  - disconnects the socket *without* sending a disconnect packet
  You should see the will message published when we disconnect
*/


#include <ESP8266WiFi.h>
#include <PubSubClient.h>

const char *ssid =  "myssid";   // cannot be longer than 32 characters!
const char *pass =  "mypass";   //
String clientId = "FLOjab1";

// Update these with values suitable for your network.
IPAddress server(172, 16, 0, 109);

WiFiClient wclient;
PubSubClient client(wclient, server);

void setup() {
 
// Setup console
 
Serial.begin(115200);
  delay
(10);
 
Serial.println();
 
Serial.println();
}

void loop() {
  delay
(1000);

 
if (WiFi.status() != WL_CONNECTED) {
   
Serial.print("Connecting to ");
   
Serial.print(ssid);
   
Serial.println("...");
   
WiFi.begin(ssid, pass);

   
if (WiFi.waitForConnectResult() != WL_CONNECTED)
     
return;
   
Serial.println("WiFi connected");
   
 
}

 
if (WiFi.status() == WL_CONNECTED) {
    MQTT
::Connect con("arduinoClient");
    con
.set_will(clientId, "Lost Connection");
   
   
if (client.connect(con)) {
      client
.publish(clientId, "I am up!");
     
//delay(1000);
     
// wclient.stop();
   
}
   
//  Serial.println("MQTT connection failed.");

    delay
(500);
 
}
}

I have commented everything I could to get just the basics.
Even though I've commented wclient.stop();
My code is cycling between "I am up!" and "Lost Connection"

If I power off disgracefully I do get the "Lost Connection"

I am not sure which is the best source to use, I am using https://github.com/Imroy/pubsubclient
mainly because it was the only one that provided an example.

Zenofmud

unread,
Mar 10, 2017, 6:08:02 PM3/10/17
to node...@googlegroups.com
Here is a link to teh PubSubClient.h: https://github.com/knolleary/pubsubclient/blob/master/src/PubSubClient.h
In it you will see:
#define MQTT_KEEPALIVE 15
as I understand it (and I admit to NOT being an MQTT expert) This is the amount of time the client has promised to ping the broker in. In other words, if the broker doesn’t hear from the client in that amount of time, it will send the last will message to anyone who has subscribed to the topic.

so in the simple case you have

client => broker => subscriber(s)

after first connecting, as long as the client sends something to the broker WITHIN THE TIME LIMIT, the subscribers will get the message. If the broker doesn’t get anything within the time limit, it will send out the ‘last will’ message.
> --
> http://nodered.org
>
> Join us on Slack to continue the conversation: http://nodered.org/slack
> ---
> You received this message because you are subscribed to the Google Groups "Node-RED" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to node-red+u...@googlegroups.com.
> To post to this group, send email to node...@googlegroups.com.
> Visit this group at https://groups.google.com/group/node-red.
> To view this discussion on the web, visit https://groups.google.com/d/msgid/node-red/93fe0d49-2b30-4b8e-90e7-6b2f3f1f8bf7%40googlegroups.com.

Michel Jutras

unread,
Mar 10, 2017, 8:27:27 PM3/10/17
to Node-RED
This is a different source...
I replaced what I had with this new source.
Looking at PubSubClient.h
I see:
boolean connect(const char* id, const char* user, const char* pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage);
I am not sure of what this all means but I think I need 5 values so I tried this right after the client.connect
client.connect(clientId.c_str(),"outTopic",0,1,"Disconnected");
I am not getting any errors but not getting a will message when powering off the ESP.

Here is the complete code:
#include <ESP8266WiFi.h>
#include <PubSubClient.h>


// Update these with values suitable for your network.

const char* ssid = "myssid";
const char* password = "mypassword";
const char* mqtt_server = "172.16.0.109";

WiFiClient espClient;
PubSubClient client(espClient);
long lastMsg = 0;
char msg[50];
int value = 0;

void setup_wifi() {

  delay
(10);
 
// We start by connecting to a WiFi network
 
Serial.println();
 
Serial.print("Connecting to ");
 
Serial.println(ssid);

 
WiFi.begin(ssid, password);

 
while (WiFi.status() != WL_CONNECTED) {
    delay
(500);
   
Serial.print(".");
 
}

  randomSeed
(micros());

 
Serial.println("");
 
Serial.println("WiFi connected");
 
Serial.println("IP address: ");
 
Serial.println(WiFi.localIP());
}

void callback(char* topic, byte* payload, unsigned int length) {
 
Serial.print("Message arrived [");
 
Serial.print(topic);
 
Serial.print("] ");
 
for (int i = 0; i < length; i++) {
   
Serial.print((char)payload[i]);
 
}
 
Serial.println();

 
// Switch on the LED if an 1 was received as first character
 
if ((char)payload[0] == '1') {
    digitalWrite
(BUILTIN_LED, LOW);   // Turn the LED on (Note that LOW is the voltage level
   
// but actually the LED is on; this is because
   
// it is acive low on the ESP-01)
 
} else {
    digitalWrite
(BUILTIN_LED, HIGH);  // Turn the LED off by making the voltage HIGH
 
}

}

void reconnect() {
 
// Loop until we're reconnected
 
while (!client.connected()) {
   
Serial.print("Attempting MQTT connection...");
   
// Create a random client ID
   
String clientId = "ESP8266Client-";
    clientId
+= String(random(0xffff), HEX);
   
// Attempt to connect
   
if (client.connect(clientId.c_str())) {
      client
.connect(clientId.c_str(),"outTopic",0,1,"Disconnected");
     
     
Serial.println("connected");
     
// Once connected, publish an announcement...
      client
.publish("outTopic", "hello world");
     
// ... and resubscribe
      client
.subscribe("inTopic");
     
   
} else {
     
Serial.print("failed, rc=");
     
Serial.print(client.state());
     
Serial.println(" try again in 5 seconds");
     
// Wait 5 seconds before retrying
      delay
(5000);
   
}
 
}
}

void setup() {
  pinMode
(BUILTIN_LED, OUTPUT);     // Initialize the BUILTIN_LED pin as an output
 
Serial.begin(115200);
  setup_wifi
();
  client
.setServer(mqtt_server, 1883);
  client
.setCallback(callback);
}

void loop() {

 
if (!client.connected()) {
    reconnect
();
 
}
  client
.loop();

 
long now = millis();
 
if (now - lastMsg > 2000) {
    lastMsg
= now;
   
++value;
    snprintf
(msg, 75, "hello world #%ld", value);
   
Serial.print("Publish message: ");
   
Serial.println(msg);
    client
.publish("outTopic", msg);
 
}
}


Michel Jutras

unread,
Mar 11, 2017, 9:55:02 AM3/11/17
to Node-RED

I got it figured out.

All I had to do was to replace the first line
client.connect(clientId.c_str())

with this:

hello world

unread,
Mar 11, 2017, 7:37:58 PM3/11/17
to Node-RED

I came up with this for checking for stale or old data.  Here's a simplified version:

[{"id":"261cb3b6.b15f44","type":"function","z":"a1c5a94c.d64cb8","name":"timeNow","func":"var now = new Date();\nflow.set(\"sensorTimeNow\", now);\nreturn msg;","outputs":1,"noerr":0,"x":420,"y":1680,"wires":[["67df8282.14ea3c"]]},{"id":"73657caa.d7d024","type":"function","z":"a1c5a94c.d64cb8","name":"inSensor","func":"var lastUpdate = flow.get(\"sensorTimeNow\")||0;\nvar now = new Date();\nvar threshold = 20; // Inactive sensor limit in sec\nvar msg = {payload: 0}; // Value when data is too old\nif (flow.get(\"sensorTimeNow\") !== 0) {\n    lastUpdate = ((now - lastUpdate) / 1000).toFixed(0);\n    flow.set(\"sensorLastUpdate\", now);\n    if (lastUpdate <= threshold) {\n        node.status({fill:\"green\",shape:\"ring\",text:\"Last Update: \"+lastUpdate+\"s\"});\n        return[null];\n    }\n    else {\n        node.status({fill:\"red\",shape:\"ring\",text:\"Last Update: \"+lastUpdate+\"s\"});\n        return[msg];\n    }\n}\nreturn null;","outputs":"1","noerr":0,"x":420,"y":1600,"wires":[["978af557.45bca8"]]},{"id":"4cf6c1f7.5ac818","type":"inject","z":"a1c5a94c.d64cb8","name":"Check every 5sec for old data","topic":"","payload":"","payloadType":"num","repeat":"5","crontab":"","once":false,"x":190,"y":1600,"wires":[["73657caa.d7d024"]]},{"id":"39a96bb4.8572ec","type":"inject","z":"a1c5a94c.d64cb8","name":"Simulate data input for alive sensor","topic":"","payload":"77.9","payloadType":"num","repeat":"","crontab":"","once":false,"x":200,"y":1680,"wires":[["261cb3b6.b15f44"]]},{"id":"67df8282.14ea3c","type":"debug","z":"a1c5a94c.d64cb8","name":"to dashboard guage","active":true,"console":"false","complete":"payload","x":600,"y":1680,"wires":[]},{"id":"978af557.45bca8","type":"debug","z":"a1c5a94c.d64cb8","name":"0 to dashboard if too old","active":true,"console":"false","complete":"payload","x":610,"y":1600,"wires":[]}]





Julian Knight

unread,
Mar 12, 2017, 8:05:44 AM3/12/17
to Node-RED
As Nick points out, it is better to use the MQTT LWT if you can as it will let you know if data is stale and you only need subscribe to the LWT message. Of course, you still need to check if the MQTT service is still live but the connection in Node-RED will error if the broker is no longer accessible & so you can trap that.

Polling every few seconds can put serious stress on your system, especially as the number of checks grow.

Michel Jutras

unread,
Mar 12, 2017, 9:12:01 AM3/12/17
to Node-RED
Some of you may laugh as I am not a pro at this (yet) but this was my solution using LWT.


[{"id":"7241794.1b27788","type":"ui_gauge","z":"384607de.b7b0e","name":"","group":"3be931fb.4c8876","order":0,"width":0,"height":0,"gtype":"gage","title":"Water Temp","label":"Celcius","format":"{{value | number:1}}º","min":"0","max":"50","colors":["#0080ff","#0080ff","#ca3838"],"x":709,"y":170,"wires":[]},{"id":"a12d5381.5028b8","type":"ui_gauge","z":"384607de.b7b0e","name":"","group":"3be931fb.4c8876","order":0,"width":0,"height":0,"gtype":"gage","title":"Engine Room Temp","label":"Celcius","format":"{{value | number:1}}º","min":"0","max":"50","colors":["#0080ff","#0080ff","#ca3838"],"x":763,"y":120,"wires":[]},{"id":"7c5ff603.8a9688","type":"mqtt in","z":"384607de.b7b0e","name":"Water temp","topic":"FLOjab/temperature/28ec8bbf3001e","qos":"2","broker":"bc047110.9c1b28","x":320,"y":170,"wires":[["7241794.1b27788"]]},{"id":"861f2bd.7395358","type":"mqtt in","z":"384607de.b7b0e","name":"Engine room temp","topic":"FLOjab/temperature/283b7fbf300e6","qos":"2","broker":"bc047110.9c1b28","x":341,"y":121,"wires":[["a12d5381.5028b8"]]},{"id":"1d175372.dd6595","type":"mqtt in","z":"384607de.b7b0e","name":"Temp Will","topic":"FLOjab/temperature/#","qos":"2","broker":"bc047110.9c1b28","x":308,"y":60,"wires":[["bb8691b3.e37b5"]]},{"id":"bb8691b3.e37b5","type":"function","z":"384607de.b7b0e","name":"set to 0","func":"if(msg.payload == \"Disconnected\" || msg.payload == \"Connected\"){\n    msg.payload=0;\nreturn [msg];}","outputs":"1","noerr":0,"x":534,"y":58,"wires":[["a12d5381.5028b8","7241794.1b27788"]]},{"id":"3be931fb.4c8876","type":"ui_group","z":"","name":"Default","tab":"d96a2d5e.127ec8","disp":false,"width":"6"},{"id":"bc047110.9c1b28","type":"mqtt-broker","z":"","broker":"172.16.0.109","port":"1883","clientid":"","usetls":false,"compatmode":true,"keepalive":"60","cleansession":true,"willTopic":"","willQos":"0","willPayload":"","birthTopic":"","birthQos":"0","birthPayload":""},{"id":"d96a2d5e.127ec8","type":"ui_tab","z":"","name":"Instruments","icon":"dashboard"}]


Zenofmud

unread,
Mar 12, 2017, 2:28:19 PM3/12/17
to node...@googlegroups.com
Michel,

If you set the last will topic in the sketch you can just use that topic in the MQTT in and get rid of the function node altogether. 

Say your connect is:

   client.connect(clientId.c_str(),"FLOjab/temperature/will",0,1,”Disconnected”);

then your MQTT ‘in’ node would just need to use a topic of "FLOjab/temperature/will”. If the device is powered off or no message is sent before the keepAlive time is exceeded, then the broker will send out the message “Disconnected” using the exceeds the "FLOjab/temperature/will” as the topic.

Zenofmud

unread,
Mar 12, 2017, 2:32:33 PM3/12/17
to node...@googlegroups.com
oops, change the last line from
out the message “Disconnected” using the exceeds the "FLOjab/temperature/will” as the topic.
to
out the message “Disconnected” using  "FLOjab/temperature/will” as the topic.

Michel Jutras

unread,
Mar 12, 2017, 2:56:39 PM3/12/17
to Node-RED


Hi Paul,

I may not have been clear in what I did. Based on the picture above;
I have one ESP8266 reading many temperature sensors of type 18B20. The code in the ESP scans each sensor for their serial number and then every few seconds publish their value one at a time to the topic ''FLOjab/temperature/[serialnumber]''
Each of these are attached to their respective gauge.

I then added the mqtt node using ''FLOjab/temperature/#'' and there I detect if the payload is ''Disconnected'' I then set all gauges to 0

But what you wrote made me think, if instead of:
client.connect(clientId.c_str(),"FLOjab/temperature/will",0,1,”Disconnected”);

I do like this:
client.connect(clientId.c_str(),"FLOjab/temperature/will",0,1,”0”);

I guess I can remove the function as well and the ''0'' as a response will be applied to all gauges, does this make more sense?



Auto Generated Inline Image 1

Paul Woodard

unread,
Mar 13, 2017, 8:23:59 PM3/13/17
to Node-RED
Great thinking outside the box!!!
Reply all
Reply to author
Forward
0 new messages