serial2mqtt

For a complete view : with UML sequence diagrams
MQTT for all micro-controllers ! The purpose is to offer MQTT publisher/subscriber functionality to all small micro controllers. Those with just a UART or USB interface.
Example : a cheap STM32 board on ebay.
Afbeeldingsresultaat voor stm32 maple miniAfbeeldingsresultaat voor lm4f120h5qrAfbeeldingsresultaat voor ebay stm32
This program will act as a full MQTT Client gateway and make integration as simple as possible.
This was created because Ethernet or WiFi is still absent in most ( cheap ) controllers .
Also the concept behind is that a central PC or Raspberry PI can act as the intelligent mind behind commodity components.

enter image description here

Arduino Sample program to communicate with the serial2mqtt gateway

#include <ArduinoJson.h>

class Mqtt {
  public:
    static String device;
    static void publish( String topic, String message, int qos = 0, bool retained = false ) {
      StaticJsonBuffer<200> jsonBuffer;
      JsonObject& data = jsonBuffer.createObject();
      data["cmd"] = "MQTT-PUB";
      data["topic"] = "src/" + device + "/" + topic;
      data["message"] = message;
      if ( qos != 0 ) data["qos"] = qos;
      if ( retained) data["retained"] = retained;
      data.printTo(Serial);
      Serial.println();
    }
    static void handleLine(String& line) {
      StaticJsonBuffer<200> jsonBuffer;
      JsonObject& root = jsonBuffer.parseObject(line);
      onMqttMessage(root["topic"], root["message"], root["qos"], root["retained"]);
    }

    static void onMqttMessage(String topic, String message, int qos, bool retained) {
    // add your own subscriber here 
      Serial.printf(" Mqtt Message arrived");
    }
};
// create a name for this device
String Mqtt::device = "ESP32-" + String((uint32_t)ESP.getEfuseMac(), HEX);

void setup() {
  Serial.begin(115200);
  pinMode(LED_BUILTIN, OUTPUT);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }
}

String line;
void loop() {
  while (Serial.available()) {
    char ch = Serial.read();
    if ( ch == '\r' || ch == '\n' ) {
      Mqtt::handleLine(line);
      line = "";
    } else
      line += ch;
  }
  digitalWrite(LED_BUILTIN, HIGH);   // turn the LED on (HIGH is the voltage level)
  delay(100);                       // wait for a 0.1 second
  digitalWrite(LED_BUILTIN, LOW);    // turn the LED off by making the voltage LOW
  delay(100);                       // wait for a 0.1 second
  Mqtt::publish( "system/upTime", String(millis(), 10), 0, false);
  Mqtt::publish("system/host", Mqtt::device, 0, false);
  Mqtt::publish("system/alive", "true", 0, false);
  }

Working assumptions and features

Optional

The serial2mqtt should be able to reset the device ( hard reset )

Protocol

TEXT JSON

{ "cmd":"MQTT-PUB","topic":"src/device/service/property","message":"1234.66","qos":0,"retained":false }\n

BINARY CBOR SLIP

<END><SLIP ENCODED MESSAGE><END>
<SLIP ENCODED MESSAGE> == <'M'><"PUB">,<qos Integer><retain boolean><topic string><message binary><CRC integer>

CONNECTION SETUP

µCserial2mqttMQTT BrokerCONNECTCONNECT(broker,port)CONNACKconnect serial{"topic":"src/DEVICE/SERVICE/PROP",...}SUBSCRIBE("dst/DEVICE/PUBLISH("src/DEVICE/SERVICE/PROP",...){"topic":"src/DEVICE/SERVICE/PROP1",...}PUBLISH("src/DEVICE/SERVICE/PROP1",...)no more messages after 5 sec, serial2mqttdisconnects serial port and tries toreconnect. MQTT connection always open.DISCONNECTCONNECTµCserial2mqttMQTT Broker

Programming through serial2mqtt

A command line utility will send a single mqtt request to the serial2mqtt gateway to program the microcontroller.

µCserial2mqttMQTT Brokerprogrammer CLIPUBLISH("dst/drive/serial2mqtt/flash",flash image binary)PUBLISHprogram flash imagePUBLISH(logs)logsstartup logslogslogsMQTT PubµCserial2mqttMQTT Brokerprogrammer CLI

Logging through serial2mqtt

The micrcontroller will also log to the central logging system

Build instructions

Or just deploy the pre-build versions from the Debug directory , 2 versions available : Linux 64bits Intel and Raspberry Pi ARM.

Tested

Still to do

Code design

Per serial port there is a main thread and mqtt threads for callback
The main thread waits for events and handle these primarily. 2 timers in this thread are checked for expiry ( not time critical ) : serial-watchdog and mqtt-connect.

To avoid concurrency issues , the callbacks of the mqtt threads are communicated back by writing an event code on a pipe.
The main threads waits on events : timeout of 1 sec, data on serial file-descriptor or pipe file-descriptor.
The mqtt event of received message is handled directly by writing the message on the serial port.