MQTT "Test Client"


#1

I’ve been experimenting with Home Assistant for the past few weeks, and I wanted to use MQTT to publish data from some of my Particle devices. Having never used MQTT before, and being new to Home Assistant, I decided that I needed to configure Home Assistant with test data. That led to the development of the code listed below.

I compiled and flashed the code to my Photon, and I now have an easy way to publish and subscribe to any MQTT broker via the Particle Console as seen in the following screen-shot:

  • The SetBrokerIP function allows me to enter the MQTT broker’s IP address or DNS name.
  • The SetPubTopic function allows me to enter an MQTT topic that will be used when I publish a message.
  • The Publish-Msg function allows me to publish an MQTT message.
  • The Subscribe function allows me to subscribe to a MQTT topic.
  • The getMessage variable allows me to display the last MQTT subscription message received.

I’ve found this to be very useful tool and wanted to share. Perhaps someone with more MQTT experience can take this and make it into something even more useful. In the meantime, you are more than welcome to use my code. To date, I’ve only tested it on a Photon.

#include "Particle.h"
#include "MQTT.h"

	MQTT* mqtt;
	char clientName[64] = "";       // obtained from particle/device/name
	byte brokerIP[] = {0,0,0,0};    // obtained via Particle.function when an IP address is entered
	char brokerDNS[256];            // obtained via the same Particle.function as brokerIP, when a DNS name is entered
	char pubTopic[256] = "";        // obtained via Particle.function
	char buffer[512] = "";          // obtained via Particle.function
    char subTopic[256] = "";        // obtained via Particle.function
    char subMessage[512] = "";      // obtained via MQTT subscribe, exposed via Particle.variable
    Timer connectTimer(15000, displayConnectionState); // a simple + or - is displayed in the Serial console
    bool mqttConnectViaDNS = false; // true: connect via brokerDNS, false: connect via brokerIP
    bool mqttIsInitialized = false; // true: mqtt points to an initialized MQTT instance
    bool mqttIsActive = false;      // true: mqttIsInitialized and mqtt->isConnected are true (maintained in loop())
 
void setup() {
    Serial.begin();
    Particle.function("SetBrokerIP", SetBrokerIP);
	Particle.function("SetPubTopic", SetPubTopic);
	Particle.function("Publish-Msg", Publish);
	Particle.function("Subscribe", Subscribe);
	Particle.variable("GetMessage", subMessage);
	Particle.subscribe("particle/device/name", handler, MY_DEVICES);
    Particle.publish("particle/device/name", NULL, 60, PRIVATE);
}

void loop() {
    if (mqttIsInitialized && mqtt->isConnected()) {
        mqttIsActive = true;
        mqtt->loop();
    } else {
        mqttIsActive = false;
    }
}

// --------------------------------------------- Connect to the MQTT Broker via DNS name or IP address
// triggered by Particle.function
int SetBrokerIP(String s) {
    // determine if IP address or DNS name was entered ...
    mqttConnectViaDNS = false;
    s.toCharArray(buffer, sizeof(s));
    for (int i = 0; i < strlen(buffer); i++) {
        if (( !isDigit(buffer[i])) && (buffer[i] != '.') && (buffer[i] != '\0')) {
            mqttConnectViaDNS = true;
            break;
        }
    }
    if (mqttConnectViaDNS) {
		// connect via DNS Name -----------------------------
        strncpy(brokerDNS, buffer, sizeof(brokerDNS));
        Serial.printf("brokerDNS: %s\n\r", brokerDNS);
	    mqtt = new MQTT(brokerDNS, 1883, 15, callback);
		mqttIsInitialized = true;
    } else {
	    // connect via IP Address ---------------------------
        for (int i = 0; i < 4; i++) brokerIP[i] = 0;
        sscanf( buffer, "%u.%u.%u.%u", &brokerIP[0], &brokerIP[1], &brokerIP[2], &brokerIP[3] );
        Serial.printf("brokerIP: %u.%u.%u.%u\r\n", brokerIP[0], brokerIP[1], brokerIP[2], brokerIP[3] );
	    mqtt = new MQTT(brokerIP, 1883, 15, callback);
		mqttIsInitialized = true;
    }
    mqtt->connect(clientName);
    connectTimer.start();
    return 0;
}

// --------------------------------------------- Set the Publish Topic
// triggered by Particle.function
int SetPubTopic(String s) {
	s.toCharArray(pubTopic, sizeof(buffer));
	return 0;
}

// --------------------------------------------- Publish an MQTT Message
// triggered by Particle.function
int Publish(String s) {
    if (mqtt->isConnected()) {
		s.toCharArray(buffer,sizeof(buffer));
		mqtt->publish(pubTopic, buffer);
		return 0;
	} else return 1;
}

// --------------------------------------------- Subscribe to a Topic
// triggered by Particle.function
int Subscribe(String s) {
    if (mqtt->isConnected()) {
        s.toCharArray(subTopic,sizeof(s));
	    mqtt->subscribe(subTopic);
    	return 0;
	} else return 1;
}

// --------------------------------------------- Receive an MQTT Message
// triggered when a message is received from the MQTT Broker
void callback(char* topic, byte* payload, unsigned int length) {
    strncpy(subTopic, topic, sizeof(subTopic));
	memcpy(subMessage, payload, length);
	subMessage[length] = '\0';
}

// --------------------------------------------- Display MQTT connection State
// triggered by connectTimer
void displayConnectionState(void) {  
    if (mqttIsActive) Serial.print("+"); else {
        Serial.print("-");
    }
    return;
}

// -------------------------------------------- Get ClientName
// triggered by the Particle.subscribe and Particle.publish run in setup()
void handler(const char *topic, const char *data) {
    strncpy(clientName, data, sizeof(clientName));
}
    


Has anybody been able to use MQTT library with Argon?
#2

Hi Bear,
How is the integration with Home Assistant going? I read your recent post about HA and have been reading about it. The code you share here was one of my obstacles. Thanks!
Did you use the MQTT library from the WebIDE? I am wondering how much processing power and memory it takes up. I want to control a touch display and read a a few DS18 sensors.


#3

Yes, I used the MQTT library from the web IDE. Here is its GitHub link: https://github.com/hirotakaster/MQTT

I’ve been testing it with my largest code set and I have not noticed any memory issues or degradation. It’s coexisting nicely with SparkJSON and my TzCfg libraries. Updates post to Home Assistant very quickly. I haven’t triggered any Particle Functions from HA yet, but those triggered from the code I shared occur almost instantaneously.

I have a DS18B20 sitting in front of me … My next circuit will use it to monitor outdoor temperature, three YHDC SCT013 current transformers to monitor the amperage used by our boiler, and will monitor the presence of power to our boiler via a small relay which opens when the power drops. It’s possible that I might also add another relay to prevent the boiler’s controller from powering on until I know the power is stable.


#4

As long as you aren’t using TLS, MQTT doesn’t take up too much flash or RAM. It’s mostly just a TCP client + a buffer of configurable length in the .h file that defaults pretty small.


#5

Thank you @Bear. Sounds like you are running even more code than I would.
I forget if you mentioned it, is HA running locally on… RPi, PC…?

Thanks @justicefreed_amper for the insight. I’m planning to keep HA running on local network, so I can avoid encryption.


#6

First, I’m glad I decided to take a look at HA. It is exceeding my expectations. It is not a panacea, but it has a lot of power and I’ve found it relatively easy to work with once I got a grasp of the basics. It can run locally, which is important to me.

If I had a Windows, Linux, or Apple system that didn’t travel outside the house, I may have chosen to load Oracle’s (free) "VirtualBox’ software https://www.virtualbox.org/ to host a virtual machine running HA on Ubuntu, but I chose to host on a Raspberry Pi 3B+ which has also exceeded my expectations.

I tried Hassbian and HASS.IO, but they left me feeling like I had taken over someone else’s computer after they’d installed, deleted, modified, and configured the OS and applications … not a good place to start. I finally decided to load Raspbian, and HA myself, and that has worked well for me.

I took a risk when I chose to load Raspbian Stretch, but I really wanted a GUI interface that would:

  • Allow me to use a text editor similar to Windows Notepad.
  • Allow me to run HA’s front-end in a browser on the same desktop.
  • Allow me to copy/paste information into from the browser into the text editor.
    Stretch does all that and it performs very well.

I found an inexpensive smokin’ SD card (170MB/s reads, 90MB/s writes) at Best Buy … https://www.bestbuy.com/site/sandisk-extreme-plus-64gb-microsdxc-uhs-i-memory-card/6282920.p?skuId=6282920

Oops, I forgot to answer the encryption question. I’m starting with my MQTT broker running on the RPi, and all traffic will be local … so I don’t plan to encript MQTT traffic at this time. Later on, I may bring up another broker for encrypted traffic … we’ll see. I did activate SSL on my RPi, and I set up an internet pathway to it via DuckDNS. That lets me securely access my HA front-end via the internet.

If we have any more discussion on HA, let’s take it offline.


#7

Thanks for the detailed reply. I’m thinking along the same lines.