How to make a MQTT Subscribe connection?

photon
Tags: #<Tag:0x00007f1ca8c102c0>

#1

Hi Guys, it sounds lazy, but it is not. I have tried many different possible codes, but I cannot make a led turn on or turn off, after my broke send a message. I have an esp8266 where it works fine.
My esp code is:

// Base ESP8266
#include <ESP8266WiFi.h>
WiFiClient WIFI_CLIENT;

// MQTT
#include <PubSubClient.h>
PubSubClient MQTT_CLIENT;


#define HEATER 14 //d5


// This function runs once on startup
void setup() {
  // Initialize the serial port
  Serial.begin(115200);


  // Configure HEATER pin as an output
  pinMode(HEATER, OUTPUT);


  // Attempt to connect to a specific access point
  WiFi.begin("xxxx", "yyyy");

  // Keep checking the connection status until it is connected
  while (WiFi.status() != WL_CONNECTED) {
      delay(500);
  }

  // Print the IP address of your module
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
}

// This function handles received messages
void myMessageArrived(char* topic, byte* payload, unsigned int length) {
  // Convert the message payload from bytes to a string
  String message = "";
  for (unsigned int i=0; i< length; i++) {
    message = message + (char)payload[i];
  }
   
  // Print the message to the serial port
  Serial.println(message);

  // Check if message is "turn HEATER on"
  if(message == "turn heater on") {
    // Turn the HEATER on
    digitalWrite(HEATER, HIGH);
  }

  // Check if message is "turn HEATER off"
  if(message == "turn heater off") {
    // Turn the HEATER off
    digitalWrite(HEATER, LOW);
  }
}

// This function connects to the MQTT broker
void reconnect() {
  // Set our MQTT broker address and port
  MQTT_CLIENT.setServer("192.168.0.200", 1883);
  //MQTT_CLIENT.setServer("iot.eclipse.org", 1883);
  
  MQTT_CLIENT.setClient(WIFI_CLIENT);

  // Loop until we're reconnected
  while (!MQTT_CLIENT.connected()) {
    // Attempt to connect
    Serial.println("Attempt to connect to MQTT broker");
    MQTT_CLIENT.connect("esp_heater");

    // Wait some time to space out connection requests
    delay(3000);
  }

  Serial.println("MQTT connected");

  // Subscribe to the topic where our web page is publishing messages
  MQTT_CLIENT.subscribe("heater");                                  ///*****topic******//
 

  // Set the message received callback
  MQTT_CLIENT.setCallback(myMessageArrived);
}

// This function runs over and over again in a continuous loop
void loop() {

  // Check if we're connected to the MQTT broker
  if (!MQTT_CLIENT.connected()) {
    // If we're not, attempt to reconnect
    reconnect();
  }


  // Check for incoming MQTT messages
  MQTT_CLIENT.loop();
}

What I am trying is

#include "application.h"
#include "MQTT.h"

void callback(char* topic, byte* payload, unsigned int length);
MQTT client("192.198.0.200", 1883, callback);


// This function handles received messages
void callback(char* topic, byte* payload, unsigned int length) {
  // Convert the message payload from bytes to a string
  String message = "";
  for (unsigned int i=0; i< length; i++) {
    message = message + (char)payload[i];
  }
   
  // Print the message to the serial port
  Serial.println(message);

  // Check if message is "turn HEATER on"
  if(message == "turn heater on") {
    // Turn the HEATER on
    digitalWrite(HEATER, HIGH);
  }

  // Check if message is "turn HEATER off"
  if(message == "turn heater off") {
    // Turn the HEATER off
    digitalWrite(HEATER, LOW);
  }
}


void setup() {
  
    // connect to the server(unique id by Time.now())
    client.connect("sparkclient_" + String(Time.now()));

    // publish/subscribe
    if (client.isConnected()) {
        client.subscribe("heater");
    }
}

void loop() {
    if (client.isConnected())
        client.loop();
}

#2

Your broker IP looks wrong.
Double checking your code would be the first step :wink:

Not sure why you are doing this tho’

You should be able to just write String message((const char*)payload, length);.
However, we strongly advise against the use of String for long running projects and variable string lengths due to the risk of heap fragmentation.

Without String you could write your handler like this

void callback(char* topic, byte* payload, unsigned int length) {
  Serial.write(payload, length);
  Serial.println();

  // check message for known commands
  if     (strcmp(payload, "turn heater on") == 0) { 
    digitalWrite(HEATER, HIGH);    // Turn the HEATER on
  }
  else if(strcmp(payload, "turn heater off") == 0) { 
    digitalWrite(HEATER, LOW);    // Turn the HEATER off
  }
}

BTW, commenting the obvious and dragging out code length with superfluous blank lines doesn’t really help making your code more readable.


#3

Hi @ScruffR , you are right, there is a type in that line, it should be:


MQTT client(“192.168.0.200”, 1883, callback);
  //

And here I am converting the message payload from bytes to a string, so I can compare the message.

  String message = "";
  for (unsigned int i=0; i < length; i++) {
    message = message + (char)payload[i];
  }

You are right, my comments are very poor and obvious. I am working on it.

Thank you for replying.


#4

I understand what you are doing there but not why since it’s not needed.


#5

Unfortunetely I cannot use that code, there is an error on that line if

(strcmp(payload, "turn heater on") == 0) {

invalid conversion from ‘byte* {aka unsigned char*}’ to ‘const char*’ [-fpermissive]


#6

That’s easily fixed this way (strcmp((const char*)payload, "turn heater on") == 0)

The compiler what it wants and you just give it exactly that :wink:


#7

My other client is definitely publishing a message to the broker
client.publish('heater', 'turn heater off');

But my Photon is not receiving it, even though it seems to be subscribed properly to the correct topic.
client.subscribe("heater");

How can I troubleshoot it?

My full code:

#include "application.h"
#include "MQTT.h"

int HEATER = D5;

void callback(char* topic, byte* payload, unsigned int length);
MQTT client("192.168.0.200", 1883, callback);


void callback(char* topic, byte* payload, unsigned int length) {
  Serial.write(payload, length);
  Serial.println();

  // check message for known commands
  if (strcmp((const char*)payload, "turn heater on") == 0) { 
    digitalWrite(HEATER, HIGH);    // Turn the HEATER on
  }
  else if (strcmp((const char*)payload, "turn heater off") == 0) { 
    digitalWrite(HEATER, LOW);    // Turn the HEATER off
  }
}

void setup() {
  
    // connect to the server(unique id by Time.now())
    client.connect("sparkclient_" + String(Time.now()));

    // publish/subscribe
    if (client.isConnected()) {
        client.subscribe("heater");
    }
}

void loop() {
    if (client.isConnected())
        client.loop();
}

#8

In loop() you are checking for client.isConnected() but you are not doing anything in case it isn’t. Should you not try to reconnect?
Also you don’t seem to check whether your connection ever gets established in setup() either.

That would be the first step in order to

I’m also no fan of using an IP address as string host name string (although it should work - it’s just not very clean).
Try this instead

MQTT client((byte[]){192,168,0,200}, 1883, callback);

And you are not setting the pinMode() for your HEATER pin.

And one mess-up from my side - it should actually be strncmp() like this

  if (strncmp((const char*)payload, "turn heater on", length) == 0) { 
    digitalWrite(HEATER, HIGH);    // Turn the HEATER on
  }
  else if (strncmp((const char*)payload, "turn heater off", length) == 0) { 
    digitalWrite(HEATER, LOW);    // Turn the HEATER off
  }

BTW, wouldn’t it be better to keep the client ID the same instead of making dynamic by adding the time to it?
If you keep the ID constant the broker can deliver queued messages when the same client comes back online.

You can try this
https://go.particle.io/shared_apps/5c8a2656f64a050005daf3a3
With that you can cycle through publishing an OFF, ON or wrong event to your broker by pressing the SETUP button and the D7 LED will reflect whether your ON or OFF was received.
Instead of hardcoding the comparisons against each possible command, I’ve created an array of commands which is traversed in order to find a match and then the respective index is used to trigger the respective action.


#9

Hi @ScruffR ,
That was very nice from you reviewing so many times my bad code, but I am very happy it is working now, and your tips are gold. Thanks a million.