Hello
First of all first of all thank you for all the help I have been able to find here so far in looking for answers. This is the first time I have to ask my own question, because my limited knowledge at the lower level is blocking me...
I try to exchange data between my Adafruit HUZZAH which contains an ESP8266 and my Arduino Uno (because I need an Arduino to control things like LED stripes).
I succeed with basic tests in both ways but it became more complicated when I wanted to do it simultaneously. Indeed, I can receive real time data from both boards and I read somewhere (?) the HUZZAH can only received or send data at the same time (and I saw corrupted data in my serial display when doing that).
I spare you my many attempts but finally I tried to used EspSoftwareSerial to replace the hardware Serial because its seems to be able to manage that :
Except at high bitrates, depending on other ongoing activity, interrupts in particular, this software serial adapter supports full duplex receive and send.
And it seems to work but, as suggested on the library page, it could generate errors. I use a very low baud rate but I have about 5% of characters that appear corrupted. My knowledge in such domain are weak, I thought about sending several times the data, use checksum etc... Finally I tried to use Reed-Solomon Forward Error Correction Library. It's better but not perfect (still 2-3% corruption).
As I've tried a lot of things, I end up wondering if I'm doing things right!
What this testing code do is quiet simple:
- When Huzzah is connected to the wifi, it sends "<TOPIC?>"
- When Uno receives "<TOPIC?>", it sends several packages with "<TOPIC!mqtt_topic>"
- At each received "<TOPIC!mqtt_topic>", Huzzah sends (right away) the reformatted command
- The Uno displays the received command
The 3rd line is done for testing purpose. I suppose it can be done after all the commands is received from Uno but I suppose that sometimes, data will be transferred both ways at the same time so that why I keep it that way to try to resolve the issue. Maybe I'm not clear and maybe it makes no sense ^^'
Uno Code
#include <RS-FEC.h>
#include <SoftwareSerial.h>
#define MAX_STRING_LEN 64
SoftwareSerial mySerial(10, 11); // RX, TX
String fromMQTT;
byte index=0;
const char SOPmarker = '<'; //This is the start of packet marker
const char EOPmarker = '>'; //This is the end of packet marker
char serialbuf[64]; //This gives the incoming serial some room. Change it if you want
char *MQTT_topics[] = {"led/state",
"switch/state",
"switch/connected"};
const int msglen = 60; const uint8_t ECC_LENGTH = 10; //Max message lenght, and "guardian bytes", Max corrected bytes ECC_LENGTH/2
char message_frame[msglen]; // The message size would be different, so need a container
char repaired[msglen]; char encoded[msglen + ECC_LENGTH];
RS::ReedSolomon<msglen, ECC_LENGTH> rs;
void setup() {
Serial.begin(115200);
while (!Serial) {
; // wait for serial port to connect. Needed for native USB port only
}
Serial.println("Running on the Arduino");
pinMode(13,OUTPUT);
// set the data rate for the SoftwareSerial port
mySerial.begin(4800);
}
void loop() {
if (mySerial.available()) {
static int bufpos = 0;
char inchar = mySerial.read();
if (inchar != EOPmarker && inchar != SOPmarker) {
serialbuf[bufpos] = inchar;
bufpos++;
}else if (inchar == SOPmarker) {
bufpos = 0;
}else{
serialbuf[bufpos] = 0;
bufpos = 0;
Serial.print("received: ");
Serial.println(serialbuf);
if(strcmp(serialbuf, "TOPICS?") == 0) {
for (int i = 0; i < sizeof(MQTT_topics[i]); i++) {
char message[] = "<TOPIC!";
strcat(message,MQTT_topics[i]);
strcat(message,">");
memset(message_frame, 0, sizeof(message_frame)); // Clear the array
for(int i = 0; i <= msglen; i++) { message_frame[i] = message[i]; } // Fill with the message
rs.Encode(message_frame, encoded);
mySerial.print(encoded);
}
}
}
}
}
char* subStr (char* input_string, char *separator, int segment_number) {
char *act, *sub, *ptr;
static char copy[MAX_STRING_LEN];
int i;
strcpy(copy, input_string);
for (i = 1, act = copy; i <= segment_number; i++, act = NULL) {
sub = strtok_r(act, separator, &ptr);
if (sub == NULL) break;
}
return sub;
}
Huzzah Code
#include <RS-FEC.h>
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <SoftwareSerial.h>
#define BAUD_RATE 4800
#define MAX_STRING_LEN 64
#define LED_PIN 5
#define BUTTON_PIN 4
bool debug = false;
String fromUno;
const char SOPmarker = '<'; //This is the start of packet marker
const char EOPmarker = '>'; //This is the end of packet marker
char serialbuf[64];
// From Arduino-FEC example
const int msglen = 60; const uint8_t ECC_LENGTH = 10; //Max message lenght, and "guardian bytes", Max corrected bytes ECC_LENGTH/2
char message_frame[msglen]; // The message size would be different, so need a container
char repaired[msglen]; char encoded[msglen + ECC_LENGTH];
RS::ReedSolomon<msglen, ECC_LENGTH> rs;
WiFiClient espClient;
PubSubClient client(espClient);
SoftwareSerial swSer(14, 12, false, 256);
void setup() {
if (debug) {
Serial.begin(115200);
}
Serial.begin(4800); //Software Serial to Arduino at 4800
swSer.begin(BAUD_RATE);
/* Wifi / MQTT code removed to simplify the code */
}
void reconnect() {
while (!client.connected()) {
if (client.connect("ESP8266Client")) {
//Request the topics to subscribe to arduino
char message[] = "<TOPICS?>";
memset(message_frame, 0, sizeof(message_frame)); // Clear the array
for(int i = 0; i <= msglen; i++) { message_frame[i] = message[i]; } // Fill with the message
rs.Encode(message_frame, encoded);
swSer.print(encoded);
}
}
}
void callback(char* topic, byte* payload, unsigned int length) { // Not currently used
}
void loop() {
if (!client.connected()) {
reconnect();
}
client.loop();
if (swSer.available()) {
static int bufpos = 0;
char inchar = swSer.read();
if (inchar != EOPmarker && inchar != SOPmarker) {
serialbuf[bufpos] = inchar;
bufpos++;
} else if (inchar == SOPmarker) {
bufpos = 0;
} else {
serialbuf[bufpos] = 0;
bufpos = 0;
char* header = subStr(serialbuf, "!", 1);
char* content = subStr(serialbuf, "!", 2);
// Code to test the header then subscribe - Instead, command sent back for test purpose
char message[msglen] = "<H: ";
strcat(message,header);
strcat(message," / C: ");
strcat(message,content);
strcat(message,">");
memset(message_frame, 0, sizeof(message_frame)); // Clear the array
for(int i = 0; i <= msglen; i++) { message_frame[i] = message[i]; } // Fill with the message
rs.Encode(message_frame, encoded);
swSer.print(encoded);
}
}
}
char* subStr (char* input_string, char *separator, int segment_number) {
char *act, *sub, *ptr;
static char copy[MAX_STRING_LEN];
int i;
strcpy(copy, input_string);
for (i = 1, act = copy; i <= segment_number; i++, act = NULL) {
sub = strtok_r(act, separator, &ptr);
if (sub == NULL) break;
}
return sub;
}
(Currently, I also have a bug when I have several topics to send : first one return to me "correctly" (with the 2-3% corruption), second one come to me entirely corrupted and third one never arrives... Then the Huzzah resets itself in loop...)
The code is I suppose far from perfect and I'm also interested in your feedback on best practices: by testing different things and copying examples, it ends up being a bit of a mess...
Thank you for taking the time to read me!