Go Down

Topic: MQTT serial bridge on ESP8266 (Read 1 time) previous topic - next topic

on4tux

Sep 03, 2017, 03:50 pm Last Edit: Sep 03, 2017, 04:26 pm by on4tux
I want to create a TX/RX bridge between an arduino and MQTT gateway. The goal is that serial messages from the arduino are published on a MQTT topic and visa versa.

The code below is put on the ESP8266 via the arduino IDE:

Code: [Select]

#include <ESP8266WiFi.h>
#include <PubSubClient.h>

const char* ssid = "";
const char* password = "";
const char* mqtt_server = "";

WiFiClient espClient;
PubSubClient client(espClient);
char string[50];
char byteRead;

void setup() {
  pinMode(BUILTIN_LED, OUTPUT);     // Initialize the BUILTIN_LED pin as an output
  Serial.begin(115200);
  setup_wifi();
  client.setServer(mqtt_server, 1883);
  client.setCallback(callback);
}

void setup_wifi() {
  delay(10);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
  }
}

void callback(char* topic, byte* payload, unsigned int length) {
  for (int i = 0; i < length; i++) {
    Serial.print((char)payload[i]);
  }
  Serial.println();
}

String macToStr(const uint8_t* mac)
{
  String result;
  for (int i = 0; i < 6; ++i) {
    result += String(mac[i], 16);
    if (i < 5)
      result += ':';
  }
  return result;
}

void reconnect() {
  String clientName;
  clientName += "esp8266-";
  uint8_t mac[6];
  WiFi.macAddress(mac);
  clientName += macToStr(mac);
  clientName += "-";
  clientName += String(micros() & 0xff, 16);
  while (!client.connected()) {
    if (client.connect((char*) clientName.c_str())) { // random client id
      digitalWrite(BUILTIN_LED, LOW); // low = high, so this turns on the led
      client.subscribe("test"); // callback: mqtt bus -> arduino
    } else {
      digitalWrite(BUILTIN_LED, HIGH); // high = low, so this turns off the led
      delay(5000);
    }
  }
}

void loop() {
  if (!client.connected()) {
    reconnect();
  }
  client.loop();

  while (Serial.available()) {
  delay(2);
  int availableBytes = Serial.available();
  for(int i=0; i<availableBytes; i++)
    {
    string[i] = Serial.read();
    string[i+1] = '\0'; // Append a null
    }
  client.publish("test", string); // publish: arduino -> mqtt bus
}

}


2 issues:

1. When I start or reset the ESP with the code above, I get a reset error on 74880 baud. This has something todo with the watchdog timers, but I can't get rid of it. The test board is a Huzzah feather, so I don't think it's related to the power. I once saw an error during operation, but didn't capture it.

Code: [Select]

 ets Jan  8 2013,rst cause:2, boot mode:(3,6)

load 0x4010f000, len 1384, room 16
tail 8
chksum 0x2d
csum 0x2d
v09f0c112
~ld
βΈ®


2. If I send text via the serial console of the arduino IDE, the first line is always wrapped after the 24th character and thus sent as another message. Example: "12345678901234567890123456789012345678901234567890" gives "123456789012345678901234" and "56789012345678901234567890". The second line usually wraps between character 41-44. If I lower the baudrate to 57600, the first line wraps after 12 characters.

Bonus question:
I want to send a message in the form 'a/b/c/d' where 'a/b/c' is the (variable) topic and 'd' is the actual message. How can I change the line 'client.publish("test", string);' to accomplish this?

on4tux

I simplified the sketch and uploaded it to an arduino UNO, with the same results. I then increased the delay value to 5, where the lines are no longer wrapped, but it doesn't always print the "Serial.println(string);" output.


Is there a more reliable way to relay the input from Serial, keeping in mind the output should be in char[] format for client.publish()?


Code: [Select]

char string[50];
char byteRead;

void setup() {
 Serial.begin(115200);
}

void loop() {
 while (Serial.available()) {
 delay(5);
 int availableBytes = Serial.available();
 Serial.println(availableBytes);

 for(int i=0; i<availableBytes; i++)
   {
   string[i] = Serial.read();
   string[i+1] = '\0'; // Append a null
   }
 Serial.println(string);
}

}

on4tux

As I experienced the same issue on both the ESP8266 as the Arduino UNO, I started looking for other code and found the solution on the Serial Input Basics (http://forum.arduino.cc/index.php?topic=288234.0) I was able to successfully merge the code with mine.

If you are looking for ESP8266 code that bridges serial text <> mqtt, look no further:


Code: [Select]

#include <ESP8266WiFi.h>
#include <PubSubClient.h>

const char* ssid = "";
const char* password = "";
const char* mqtt_server = "";

const byte numChars = 100;
char receivedChars[numChars]; // an array to store the received data
boolean newData = false;

WiFiClient espClient;
PubSubClient client(espClient);

void setup() {
  pinMode(BUILTIN_LED, OUTPUT);     // Initialize the BUILTIN_LED pin as an output
  Serial.begin(115200);
  setup_wifi();
  client.setServer(mqtt_server, 1883);
  client.setCallback(callback);
}

void setup_wifi() {
  delay(10);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
  }
}

void callback(char* topic, byte* payload, unsigned int length) {
  for (int i = 0; i < length; i++) {
    Serial.print((char)payload[i]);
  }
  Serial.println();
}

String macToStr(const uint8_t* mac)
{
  String result;
  for (int i = 0; i < 6; ++i) {
    result += String(mac[i], 16);
    if (i < 5)
      result += ':';
  }
  return result;
}

void reconnect() {
  String clientName;
  clientName += "esp8266-";
  uint8_t mac[6];
  WiFi.macAddress(mac);
  clientName += macToStr(mac);
  clientName += "-";
  clientName += String(micros() & 0xff, 16);
  while (!client.connected()) {
    if (client.connect((char*) clientName.c_str())) { // random client id
      digitalWrite(BUILTIN_LED, LOW); // low = high, so this turns on the led
      client.subscribe("test"); // callback: mqtt bus -> arduino
    } else {
      digitalWrite(BUILTIN_LED, HIGH); // high = low, so this turns off the led
      delay(5000);
    }
  }
}

void loop() {
  if (!client.connected()) {
    reconnect();
  }
  client.loop();

  recvWithEndMarker();
  showNewData();
}

void recvWithEndMarker() {
  static byte ndx = 0;
  char endMarker = '\n';
  char rc;
 
  while (Serial.available() > 0 && newData == false) {
    rc = Serial.read();

    if (rc != endMarker) {
      receivedChars[ndx] = rc;
      ndx++;
      if (ndx >= numChars) {
        ndx = numChars - 1;
      }
    }
    else {
      receivedChars[ndx] = '\0'; // terminate the string
      ndx = 0;
      newData = true;
    }
  }
}

void showNewData() {
  if (newData == true) {
    client.publish("test", receivedChars); // publish: arduino -> mqtt bus
    newData = false;
  }
}



patikpatrik

I have been using your code, and it works really well. However, I would like to split the incoming serial message into two parts, like "Temperature,23.3"- "Temperature" would then be used as the MQTT topic (instead of "test") and "23.3" as the MQTT payload. However, despite many attempts I have not been sucessful. Can anyone please help me with some guidance how to modify the code to make this possible?

cattledog

Have you studied Serial Input Basics? http://forum.arduino.cc/index.php?topic=288234.0

Can you use those techniques to receive a character string like "Temperature,23.3" ? It should be straight forward to use strtok  to the ","  as shown in the example, to separate the word "Temperature" from the value and get both character arrays into client.publish();

Quote
However, despite many attempts I have not been successful. Can anyone please help me with some guidance how to modify the code to make this possible?
What have you tried, and what was the result? Can you please provide more explanation as to the problems you are having?

Please post your code for more assistance. 

Go Up