In the recent Maker Faire I demonstrated an ESP8266 MQTT VFD clock. Some readers had wrote to ask for the soure code. Because the project was builtup within only 2 days, the actual source code is more or less of spaghette type. However the underlying MQTT client may be useful to someone who likes the RTOS SDK.

If you search MQTT and ESP8266 on the intraweb, most likely all hits can be traced back to the great work done by TuanPM. However Tuan’s code is based on Espressif’s NON-OS SDK. There has been some great debates about embedded programming with-or-without an OS. To me programming with OS vs NON-OS is like programming with C vs Assembly. I like programming in C, so I wrote a new MQTT client for Espressif’s RTOS SDK.

The source code is published at https://github.com/baoshi/ESP-RTOS-Paho

Here are some notes:

  • The code is based on the Eclipse Paho project, specifically the embedded C client.
  • Socket-level APIs used, the code is thread-safe.
  • Many error handlings were added to the original Paho client, including time-out for most of the network functions. These are designed to work with an RTOS to support automatic error correction at various abused conditions.
  • I’m compiling using the “Unofficial ESP8266 DevKit“. Other toolchain such as esp-open-sdk can be used as well (adjust pathes in Makefile).
  • Skeleton code for connecting a MQTT broker is as follows:
struct Network network;
MQTTClient client = DefaultClient;
MQTTPacket_connectData data = MQTTPacket_connectData_initializer;
unsigned char mqtt_buf[100];
unsigned char mqtt_readbuf[100];

NewNetwork(&network);
ConnectNetwork(&network, MQTT_HOST, MQTT_PORT);
NewMQTTClient(&client, &network, 5000, mqtt_buf, 100, mqtt_readbuf, 100);
data.willFlag = 0;
data.MQTTVersion = 3;
data.clientID.cstring = mqtt_client_id; // you client's unique identifier
data.username.cstring = MQTT_USER;
data.password.cstring = MQTT_PASS;
data.keepAliveInterval = 10; // interval for PING message to be sent (seconds)
data.cleansession = 0;
MQTTConnect(&client, &data);

To subscribe to a MQTT topic, use

MQTTSubscribe(&client, "/mytopic", QOS1, topic_received);

The parameter topic_received is a callback function handling the received message:

// Callback when receiving subscribed message
LOCAL void ICACHE_FLASH_ATTR topic_received(MessageData* md)
{
    int i;
    MQTTMessage* message = md->message;
    dmsg_puts("Received Topic ");
    for (i = 0; i t; md->topic->lenstring.len; ++i)
        dmsg_putchar(md->topic->lenstring.data[i]);
    dmsg_puts(", Message ");
    for (i = 0; i < (int)message->payloadlen; ++i)
        dmsg_putchar(((char*)message->payload)[i]);
    dmsg_puts("rn");
}

To publish a MQTT topic, use

char msg[PUB_MSG_LEN];
MQTTMessage message;
message.payload = msg;
message.payloadlen = PUB_MSG_LEN;
message.dup = 0;
message.qos = QOS1;
message.retained = 0;
MQTTPublish(&client, "topic", &message);

The demo project included in the library shows how the MQTT related functions can be organized inside a FreeRTOS task and interact with other tasks, such as retry connection after server error, WiFi error, etc. The following diagram may be helpful to understand the code. (Imaging how this can be done using NON-OS SDK)

MQTT and WiFi Thread
MQTT and WiFi Thread

As Espressif had just teased us with the new ESP32 chip and RTOS is rumored to be the default SDK, I hope this piece of code will be useful.

 

9 thoughts on “ESP8266 MQTT client on RTOS

  1. Excellent work! I have the Tuan implementation in my current project but I will look closely at your implementation for the future. Looking forward to the ESP32 as well!

    Reply
  2. Hello Baoshi,

    I tried compiling your code and I get the below error message:
    I too am using the RTOS SDK 1.3.0. Can you please help me with this?

    c:/espressif/xtensa-lx106-elf/bin/../lib/gcc/xtensa-lx106-elf/5.1.0/../../../../xtensa-lx106-elf/bin/ld.exe: build/app.out section `.irom0.text’ will not fit in region `irom0_0_seg’
    collect2.exe: error: ld returned 1 exit status
    C:/Users/Ravi/workspace/PahoMQTT/Makefile:225: recipe for target ‘build/app.out’ failed
    mingw32-make: *** [build/app.out] Error 1

    Reply
    • Hi Ravi,
      You can locate /ld/eagle.app.v6.ld, there is a section
      MEMORY
      {
      ...
      irom0_0_seg : org = 0x40240000, len = 0x0fb000
      }

      Change len according to your module’s flash size. There are some sample values at the top of the file.

      Reply
  3. Hi Baoshi, Thank you for the reply. I will try that out and let you know. By the way, do you have a simple UART echo program for ESP8266? I am trying to read data on UART0 and echo it back to UART0.

    Reading the UART fails somehow fails with a fatal exception 28. I am partially using the UART.c file from the UART_TCP_PassThroughDemo project.

    Reply
  4. Hi, great job! I have limited skills on C, and I’m trying to figure out how to output using printf the received message. I noticed that your code print one character at once (using the dsmg_putchar), but I’m having hard time to convert this to a usable string.

    Reply
  5. Hi I face issue like,
    xtensa-lx106-elf/4.8.2/../../../../xtensa-lx106-elf/bin/ld.exe: build/app1.out section `.text’ will not fit in region `iram1_0_seg’

    I tried to change length in ld flag and compile correctly device continuously reboot.
    I use RTOS 1.4.0 SDK.

    Reply
  6. Hello,
    I have used this mqtt library for esp8266 project.
    And it works fine.
    but when I tried to subscribe on multiple topic with same client, failed to subscribe.
    so, This library can not support multiple subscription.

    Reply

Leave a Reply