MQTT + Blynk

I absolutely hate working with Char variable types!
My solution - and this might just be lazy, but it works perfectly for me - is to convert everything to strings. I love strings because you can easily concatenate, dissect and compare them without any problems.

I structure my MQTT topics so that for each device that I’m working with I have a “base topic” that remains the same, and just the last few parts of the topic string change depending on what I’m doing. for example, if I have a device that monitors light levels and controls a relay my MQTT topic might look like this…

Home/Garden/Lights/Zone1/Light_Sensor/Level
Home/Garden/Lights/Zone1/Relay/CMD
Home/Garden/Lights/Zone1/Relay/Status

etc. etc.

Home/Garden/Lights/Zone1 is my 'base topic" for that device, which I declare just once…

String base_mqtt_topic = "Home/Garden/Lights/Zone1"; 

MY MQTT callback then looks like this…

void MQTTcallback(char* topic, byte* payload, unsigned int length)
{
  Serial.print(F("Message arrived ["));
  Serial.print(topic);
  Serial.print(F("] ["));
  for (int i=0;i<length;i++)
  {
    Serial.print((char)payload[i]);
  }
  Serial.println(F("]"));    
  
  String msg_topic = String((char *)topic);
  String msg_payload = String((char *)payload);
  msg_payload.remove(length); // Trim any unwanted characters off the end of the string
  // We now have two string variables, 'msg_topic' and 'msg_payload' that we can use in 'if' statements below... 


  if (msg_topic==base_mqtt_topic + "/Relay/CMD")
  {  
    if (msg_payload=="1")
    {
      setOutputLoadPower(1);
      return;
    }
  
    if (msg_payload=="0")
    {
      setOutputLoadPower(0);
      return;
    }
  }


  // Handle messages from other topics in here,
  // just like we did with the /Relay/CMD topic above.
  // DON'T FORGET to subscribe to the topic in void MQTT_Connect()

} // End of void MQTTcallback

You’ll see that I split the incoming message into two string elements, the topic and the payload, which makes it simpler to do if statements and direct the program flow accordingly. Of course you could easily do this without using the “base_topic” approach, but it makes the code simpler and it’s much easier top avoid typos in the topic. It also makes it much simpler if you have multiple devices doing similar tasks. In this situation you just change the base topic slightly and keep virtually everything else the same. For example, to add another zone to my garden lights I would just change my base topic to Zone 2…

String base_mqtt_topic = "Home/Garden/Lights/Zone2"; 

When I write messages back to the MQTT server then I have to concatenate the topic I want to write to on to the "base topic, and convert this into a character. If I’m sending other string values then these also need to be converted into char variables, but this is fairly easy using the c_str() command.

My MQTT_Connect() function looks like this…

void MQTT_Connect()
{
  Serial.print(F("Connecting to MQTT...  "));
  // We'll connect with a Retained Last Will that updates the '.../Status' topic with "Dead" when the device goes offline...
  // Attempt to connect...
  /* 
  MQTT Connection syntax:
  boolean connect (client_id, username, password, willTopic, willQoS, willRetain, willMessage)
  Connects the client with a Will message, username and password specified.
  Parameters
    client_id : the client ID to use when connecting to the server.
    username : the username to use. If NULL, no username or password is used (const char[])
    password : the password to use. If NULL, no password is used (const char[])
    willTopic : the topic to be used by the will message (const char[])
    willQoS : the quality of service to be used by the will message (int : 0,1 or 2)
    willRetain : whether the will should be published with the retain flag (int : 0 or 1)
    willMessage : the payload of the will message (const char[])
  Returns
    false - connection failed.
    true - connection succeeded
  */

  if(MQTTclient.connect(mqtt_client_id.c_str(), mqtt_username, mqtt_password, (base_mqtt_topic + "/Status").c_str(),0, 1, "Dead"))
  {
    // We get here if the connection was successful... 
    Serial.println(F("MQTT Connected"));
    mqtt_connect_count++;

    // Once connected, publish some announcements...
    // These all have the Retained flag set to true, so that the value is stored on the server and can be retrieved at any point
    // Check the .../Status topic to see that the device is still online before relying on the data from these retained topics
    MQTTclient.publish((base_mqtt_topic + "/MQTT_Client_ID").c_str(),mqtt_client_id.c_str(),true);   
    MQTTclient.publish((base_mqtt_topic + "/Status").c_str(),"Alive",true);
    MQTTclient.publish((base_mqtt_topic + "/Short_Filename").c_str(),short_filename.c_str(),true);                   // Sketch filename (without full path)
    MQTTclient.publish((base_mqtt_topic + "/Compiled_Date").c_str(),compiled_date.c_str(),true);                     // Sketch compilation date     
    MQTTclient.publish((base_mqtt_topic + "/Compiled_Time").c_str(),compiled_time.c_str(),true);                     // Sketch compilation time
    
    MQTTclient.loop();
    
    MQTTclient.publish((base_mqtt_topic + "/MAC_Address").c_str(),WiFi.macAddress().c_str(),true);                   // Device MAC Address
    MQTTclient.publish((base_mqtt_topic + "/IP_Address").c_str(),actual_ip_address.c_str(),true);                    // Device IP Address
    MQTTclient.publish((base_mqtt_topic + "/WiFi_Connect_Count").c_str(),String(wifi_connect_count).c_str(),true);   // Wi-Fi Connect Count
    MQTTclient.publish((base_mqtt_topic + "/MQTT_Connect_Count").c_str(),String(mqtt_connect_count).c_str(),true);   // MQTT Connect Count
    MQTTclient.publish((base_mqtt_topic + "/Uptime").c_str(),String(uptime).c_str(),true);           // Number 0f 49 day rollovers since booting
    MQTTclient.loop();
           

    // ... and then re/subscribe to the watched topics
    MQTTclient.subscribe((base_mqtt_topic + "/Relay/CMD").c_str());   // Watch the .../Relay/CMD topic for incoming MQTT messages

    // Add other watched topics in here...
  }
  else
  {
    // We get here if the connection failed...   
    Serial.print(F("MQTT Connection FAILED, Return Code = "));
    Serial.println(MQTTclient.state());
    Serial.println(); 
    /*
    MQTTclient.state return code meanings...
    -4 : MQTT_CONNECTION_TIMEOUT - the server didn't respond within the keepalive time
    -3 : MQTT_CONNECTION_LOST - the network connection was broken
    -2 : MQTT_CONNECT_FAILED - the network connection failed
    -1 : MQTT_DISCONNECTED - the client is disconnected cleanly
     0 : MQTT_CONNECTED - the client is connected
     1 : MQTT_CONNECT_BAD_PROTOCOL - the server doesn't support the requested version of MQTT
     2 : MQTT_CONNECT_BAD_CLIENT_ID - the server rejected the client identifier
     3 : MQTT_CONNECT_UNAVAILABLE - the server was unable to accept the connection
     4 : MQTT_CONNECT_BAD_CREDENTIALS - the username/password were rejected
     5 : MQTT_CONNECT_UNAUTHORIZED - the client was not authorized to connect * 
     */
  }
} // End of void MQTT_Connect

This gives you an example of how you can write string values to topics in other places within your code.

The only other thing I’d say is that I don’t like to mix Blynk and MQTT code on my devices. I have a couple of devices where I do this, so that I have a ‘kill switch’ that will work via Blynk even if my MQTT server is down, but it gets far too messy doing this for most projects.
I prefer to use Node-Red as the Blynk/MQTT bridge and use MQTT only code on my devices.

More info here, if you’ve not seen this already…

Pete.

1 Like