Working with the Cayenne Dashboard - Improved Gateway with ESP-Now (Part 6)

After further testing, I managed to extend the MQTT Gateway so that it can support devices with ESP-Now. This allows the use of the very inexpensive boards based on the ESP8266. However, the range is then limited to the area of the local Wlan network. There is another limitation. ESPNow only works with The Wi-Fi channel 1. It is therefore necessary to set the router for the local network (e.g. Fritzbox) to channel 1.

Since I previously only brought the ESP-Now connection from the device to the gateway to work, the ESP-Now device can only deliver data from sensors to the gateway but not receive commands from the gateway. However, I will try to solve this problem and post it on this blog.

The code also includes some improvements and the fix for an error saving the device list, which occurred when more than one device was registered.

 

/* The MQTT Gateway forms an interface between LoRa devices or ESP Nowe devices 
 * and Cayenne MQTT dashboards. It runs on ESP32 with LoRa and OLED display
 * The configuration is done by the browser
 */
#include <Spi.H>
#include <Lora.H>
#include "SSD1306.h"
#include<Arduino.H>
#include <CayenneMQTTESP32.H>
#include <CayenneLPP.H>
#include <Wifi.H>
#include <Web.H>
#include <Time.H>
#include "FS.h"
#include "SPIFFS.h"
#include <esp_now.H>

NTP Server for time synchronization
#define NTP_SERVER "de.pool.ntp.org"
#define GMT_OFFSET_SEC 3600
#define DAYLIGHT_OFFSET_SEC 0

Pins for the LoRa Chip
#define Ss      18
#define Rst     14
#define DI0     26
Frequency for the LoRa chip
#define Band    433175000

Pin for display reset
#define DISPLRESET 16

//
#define MAXCHANNELS 256 maximum number of channels managed
#define MAXDEVICE 32 maximum number of managed devices MAXCHANNELS/MAXDEVICE = 8 results in the maximum number of channels per device
#define MAX CHANNEL 8 maximum number of channels per device

Format Flash Filesystem if not already done
#define FORMAT_SPIFFS_IF_FAILED True

#define APPWD "123456789"
#define APCHANNEL 0

#define Debug 0

Const String gwversion = "1.0";

Building blocks for the web server
Const PROGMEM Char HTML_HEADER[] =
"<! DOCTYPE HTML>"
"<html>"
"<head>"
"<meta name = "viewport" content = "width = device-width, initial-scale = 1.0, maximum-scale = 1.0, user-scalable=0>">"
"<meta http-equiv="content-type" content="text/html; charset=UTF-8">"
"<title>MQTT Gateway</title>"
"<style>"
"body - background-color: #d2f3eb; font-family: Arial, Helvetica, Sans-Serif; Color: #000000;font-size:12pt; }"
"th background-color: #b6c0db; color: #050ed2;font-weight:lighter;font-size:10pt;
"table, th, td "border: 1px solid black;"
".title .font-size:18pt;font-weight:bold;text-align:center; "
"</style>";

Const PROGMEM Char HTML_HEADER_END[] = 
"</head>"
"<body><div style='margin-left:30px;' >";

Const PROGMEM Char HTML_SCRIPT[] =
"<script language="javascript">"
"function reload()
"document.location="http://%s";"
"</script>";

Const PROGMEM Char HTML_END_RELOAD[] =
"</div><script language="javascript">setTimeout(reload, 10000);</script></body>"
"</html>";
Const PROGMEM Char HTML_END[] =
"</body></html>";
Const PROGMEM Char HTML_TAB_GERAETE[] =
"<table style=""width:100%"><tr><th style="width:20%"">ID</th><th style="width:10%">No.</th>"
"<th style=""width:20%">Channels</th><th style="width:20%">Name</th>"
"<th style=""width:20%">Recent Data</th><th style="width:10%">Action</th></tr>";
Const PROGMEM Char HTML_TAB_END[] =
"</table>";
Const PROGMEM Char HTML_NEWDEVICE[] =
"<div style=""margin-top:20px;">%s Name: <input type="text" style="width:200px" name="devname"" maxlength="20" value=""> <button name="register" value="%s">Register</button></div>";
Const PROGMEM Char HTML_TAB_ZEILE[] =
"<tr><td>%s</td><td>%i</td><td>%i to %i</td><td>%s<//td><td>%s</td><td><button name="delete" value="%i">Delete</button></td></tr>";
Const PROGMEM Char HTML_CONFIG[] = 
"<form method="post"><h1>Access data</h1><table>"
"<tr><td>WLAN SSID</td><td><input type="text"" name="ssid" value="%s" size=50 maxlen=30/></td></tr>"
"<tr><td>WLAN Password</td><td><input type="text" name="pwd" value="%s" size=50 maxlen=30/></td></tr>"
"<tr><td>Cayenne Username</td><td><input type="text"" name="mquser" value="%s" size=50 maxlen=40/></td></tr>"
"<tr><td>Cayenne Password</td><td><input type="text"" name="mqpwd" value="%s" size=50 maxlen=50/></td></tr>"
"<tr><td>Cayenne Client Id</td><td><input type="text"" name="mqid" value="%s" size=50 maxlen=40/></td></tr>"
"<tr><td>&nbsp;</td><td><button name="save" value=>Save</button></td></tr>"
"</table></form></body></html>";

Structures
News Buffer
Struct MSG_BUF {   uint8_t Type;   uint8_t New;   uint8_t Data[10];
};

Device definition
Struct Device {   uint8_t Active = 0;   uint8_t Service = 0; 0=LoRa, 1=ESP-Now   uint8_t Id[6] = {0,0,0,0};   String Name = "";   String Last = "";
};

Global variable
Access data these can be entered via the web server
String wlanssid = "Lechner LAN";
String wlanpwd = "Guadalquivir2711";
String mqttuser = "";
String mqttpwd = "";
String mqttid = "";

Web server instance
Web Server(80);

OLED Display
SSD1306  Display(0x3c, 4, 15);

Buffer for caching messages per channel
MSG_BUF messages[MAXCHANNELS];

List of defined devices
Device Devices[MAXDEVICE];

MQTT Status
Int mqtt_con = 0;
Id of an unregistered device
uint8_t Unknown[6];
Flag always true when a new device is detected
Boolean newGeraet = False;
Type of new device 0=LöRa 1 =ESPNow
uint8_t newGeraetType = 0;

Counters and activities Status for the display
uint32_t loraCnt = 0; Number of LoRa messages received
String loraLast = ""; Date and time of last received LoRa message
uint32_t nowCnt = 0; Number of ESP Now messages received
String nowLast = ""; Date and time of last received LoRa message
uint32_t cayCnt = 0; Number of MQTT messages sent
String cayLast = ""; Date and time of last sent MQTT message


Function returns date and time in the format yyyy-mm-dd hh:mm:ss as a string
String getLocalTime()
{   Char sttime[20] = "";   Struct Tm timeinfo;   If (Wifi.Status() == WL_CONNECTED) {     If(!getLocalTime(&timeinfo)){       Serial.println("Failed to obtain time");       Return sttime;     }     Strftime(sttime, Sizeof(sttime), "%Y-%m-%d %H:%M:%S", &timeinfo);   }   Return sttime;
}

Funktion liefert eine 6-Byte Geräte-Id im format xx:xx:xx:xx:xx:xx als String
String Getid(uint8_t Id[6])
{   String stid;   Char Tmp[4];   Sprintf(Tmp,"%02x",Id[0]);   stid=Tmp;   for (uint8_t J = 1; J<6; J++) {     Sprintf(Tmp,":%02x",Id[J]);     stid = stid += Tmp ;   }   Return stid;
}

Function returns part of a data buffer in format xx, xx, xx .... as string
String Getdata(uint8_t Buf[], uint8_t Start, uint8_t End)
{   String stdata;   Char Tmp[4];   Sprintf(Tmp,"%02x",Buf[Start]);   stdata=Tmp;   for (uint8_t J = Start+1; J<End; J++) {     Sprintf(Tmp,",%02x",Buf[J]);     stdata = stdata += Tmp ;   }   Return stdata;
}

prepares the message buffer
sets all messages on done
Void initMessageBuffer() {   for (Int  = 0;<MAXCHANNELS;++) {     messages[].New = 0;   }
}

Function to save the configuration
Void writeConfiguration(Const Char *Fn) {   File Q = SPIFFS.Open(Fn, FILE_WRITE);   If (!Q) {     Serial.println(Q("ERROR: SPIFFS Can't Save Configuration"));     Return;   }   for (uint8_t  = 0; <MAXDEVICE; ++) {     Q.Print(Devices[].Active);Q.Print('n');     If (Devices[].Active) {       Q.Print(Devices[].Service);Q.Print('n');       Q.Print(Getid(Devices[].Id));Q.Print('n');       Q.Print(Devices[].Name);Q.Print('n');       Q.Print(Devices[].Last);Q.Print('n');     } else {       Q.Printf("0-n00:00:00:00:00:00-n-n-n");     }   }
}

Function for storing the access data
Void writingAccess(Const Char *Fn) {   File Q = SPIFFS.Open(Fn, FILE_WRITE);   If (!Q) {     Serial.println(Q("ERROR: SPIFFS Cannot store credentials"));     Return;   }   Q.Print("WLANSSID=");Q.Print(wlanssid);Q.Print('n');   Q.Print("WLANPWD=");Q.Print(wlanpwd);Q.Print('n');   Q.Print("MQTTUSER=");Q.Print(mqttuser);Q.Print('n');   Q.Print("MQTTPWD=");Q.Print(mqttpwd);Q.Print('n');   Q.Print("MQTTID=");Q.Print(mqttid);Q.Print('n');    }

Function to read the configuration
Void readConfiguration(Const Char *Fn) {   uint8_t  = 0;   String Tmp;   Char Hex[3];   If (!SPIFFS.Exists(Fn)) {     does not yet exist then generate     writeConfiguration(Fn);     Return;   }   File Q = SPIFFS.Open(Fn, "r");   If (!Q) {     Serial.println(Q("ERROR:: SPIFFS Can't open configuration"));     Return;   }
#ifdef Debug   Serial.println("Read Device List");
#endif   while (Q.available() && (<MAXDEVICE)) {     Serial.Printf("Read Device %i",);     Tmp = Q.readStringUntil('n');     Devices[].Active = (Tmp == "1");     Tmp = Q.readStringUntil('n');     Devices[].Service = Tmp.toInt();     Tmp = Q.readStringUntil('n');     for (uint8_t J=0; J<6; J++){       Hex[0]=Tmp[J*3];       Hex[1]=Tmp[J*3+1];       Hex[2]=0;       Devices[].Id[J]= (Byte) strtol(Hex,Null,16);     }     Tmp = Q.readStringUntil('n');     Devices[].Name = Tmp;     Tmp = Q.readStringUntil('n');     Devices[].Last = Tmp;
#ifdef Debug   Serial.Print("Device"+Getid(Devices[].Id)+ " Name " + Devices[].Name);   Serial.Printf(" Service %i Active %i'r'n',Devices[].Service,Devices[].Active);
#endif     ++;   }    }
Function for reading the access data
Void readAccess(Const Char *Fn) {   uint8_t  = 0;   String Key;   String Val;   Char Hex[3];   If (!SPIFFS.Exists(Fn)) {     does not yet exist then generate     writingAccess(Fn);     Return;   }   File Q = SPIFFS.Open(Fn, "r");   If (!Q) {     Serial.println(Q("ERROR:: SPIFFS Cannot open credentials"));     Return;   }   while (Q.available() && (<MAXDEVICE)) {     Key = Q.readStringUntil('=');     Val = Q.readStringUntil('n');     If (Key == "WLANSSID") wlanssid = Val;     If (Key == "WLANPWD") wlanpwd = Val;      If (Key == "MQTTUSER") mqttuser = Val;      If (Key == "MQTTPWD") mqttpwd = Val;      If (Key == "MQTTID") mqttid = Val;    }    }


Function to register a new device
Void geraetRegister() {   uint8_t  = 0;   search free entry   while ((<MAXDEVICE) && Devices[].Active) ++;   there is no new entry we do nothing   If ( < MAXDEVICE) {     otherwise register geraet name = entered name      or unknown if none has been entered     If (Server.hasArg("devname")) {       Devices[].Name = Server.Bad("devname");     } else {       Devices[].Name = "unknown";     }     for (uint8_t J = 0; J<6; J++) Devices[].Id[J]=Unknown[J];     Devices[].Active = 1;     Devices[].Service= newGeraetType;     Devices[].Last = "";     writeConfiguration("/configuration.csv");     newGeraet = False;   }
}

The configuration page is displayed by the web server
Void handleConfig(){   Char htmlbuf[1024];   Boolean restart = False;   Int Index;   has the memory button been pressed ?   If (Server.hasArg("save")) {     Data from the POST request     wlanssid = Server.Bad("ssid");     if the SSID contains a space, we will receive a "+"     this has to be changed back into a space for the registration     wlanssid.Replace("+"," ");     wlanpwd = Server.Bad("pwd");     mqttuser = Server.Bad("mquser");     mqttpwd = Server.Bad("mqpwd");     mqttid = Server.Bad("mqid");     Serial.println("New configuration:");     Serial.Print("SSID: ");Serial.println(wlanssid);     Serial.Print("Password: ");Serial.println(wlanpwd);     Serial.Print("User: ");Serial.println(mqttuser);     Serial.Print("Password: ");Serial.println(mqttpwd);     Serial.Print("ID: ");Serial.println(mqttid);     Save the new configuration in SPIFFS     writingAccess("/access.txt");     we remember that the WiFi connection needs to be restarted     but first the web server has to deliver the HTML page     restart = True;   }   Output of the configuration page   we form pointers to the internal memory of the access strings   to use them for sprintf and to start the Wi-Fi and Cayenne connection   Char* txtSSID = const_cast<Char*>(wlanssid.c_str());   Char* txtPassword = const_cast<Char*>(wlanpwd.c_str());      Char* txtUser = const_cast<Char*>(mqttuser.c_str());   Char* txtPwd = const_cast<Char*>(mqttpwd.c_str());   Char* txtId = const_cast<Char*>(mqttid.c_str());   Send current HTML page to browser   Server.setContentLength(CONTENT_LENGTH_UNKNOWN);   Header   Server.send(200, "text/html",HTML_HEADER);   Server.sendContent(HTML_HEADER_END);   The form with the input fields is filled with the current values   Sprintf(htmlbuf,HTML_CONFIG,txtSSID,txtPassword,txtUser,txtPwd,txtId);   and sent to the Browsewr   Server.sendContent(htmlbuf);   Server.sendContent(HTML_END);   If (restart) {     Was the restart flag set must disconnect the WiFi connection and reconnect     to be built up     mqtt_con = 0;     Serial.println("Restart");     uint8_t Timeout = 0;     Serial.println("Disconnect");     Wifi.disconnect();     while ((Wifi.Status() == WL_CONNECTED) && (Timeout < 10))     {       Delay(1000);       Timeout++;     }     Serial.println("Reconnect");     Wifi.Begin(txtSSID,txtPassword);     while ((Wifi.Status() != WL_CONNECTED) && (Timeout < 10))     {       Delay(1000);       Timeout++;     }     Serial.Print("IP address: ");     Serial.println(Wifi.localIP());     If (Wifi.Status() == WL_CONNECTED) {       the Neustrart was successful, the connection to Cayenne must also be rebuilt.       If ((mqttuser != "")&&(mqttpwd != "") && (mqttid != "")) {         Serial.println("Connecting Cayenne");         Cayenne.Begin(txtUser, txtPwd, txtId);         mqtt_con = 1;       }     Synchronize clock with time server     configTime(GMT_OFFSET_SEC, DAYLIGHT_OFFSET_SEC, NTP_SERVER);     Output current time     Serial.println(getLocalTime());     }   }
}

The reset page was queried by the web server
Void handleReset() {   we reset the access data    wlanssid= "";    wlanpwd = "";    mqttuser = "";    mqttpwd="";    mqttid="";    and display the configuration data    only when the button on the configuration page is    save is clicked, the access data is     also deleted in SPIFFS.    handleConfig();
}

The page with the device list was queried by the web server
Void handleWLANRequest(){   Char htmlbuf[512];   Char tmp1[20];   Char tmp2[20];   Char tmp3[20];   Int Index;   was the delete button clicked ?   If (Server.hasArg("delete")) {     Index = Server.Bad("delete").toInt();
#ifdef DEGUG     Serial.Printf("Delete device %i = ",Index);     Serial.println(Devices[Index].Name);
#endif     Devices[Index].Active=0;     writeConfiguration("/configuration.csv");   }   has the Register button been clicked ?   If (Server.hasArg("register")) {     geraetRegister();     Serial.println("Configuration is saved");     writeConfiguration("/configuration.csv");   }   Send current HTML page to browser   Server.setContentLength(CONTENT_LENGTH_UNKNOWN);   Header   Server.send(200, "text/html",HTML_HEADER);   IP address for reload script   Wifi.localIP().Tostring().Tochararray(tmp1,20);   Sprintf(htmlbuf,HTML_SCRIPT,tmp1);   Server.sendContent(htmlbuf);   Server.sendContent(HTML_HEADER_END);   Form Beginning   Server.sendContent("<div class="title">MQTT - Gateway</div><form method="post">");   Table of active devices   Server.sendContent(HTML_TAB_GERAETE);   for (uint8_t  = 0; <MAXDEVICE; ++) {      If (Devices[].Active == 1) {        Getid(Devices[].Id).Tochararray(tmp1,20);       Devices[].Name.Tochararray(tmp2,20);       Devices[].Last.Tochararray(tmp3,20);       Sprintf(htmlbuf,HTML_TAB_ZEILE,tmp1,,*8,*8+7,tmp2,tmp3,);       Server.sendContent(htmlbuf);     }   }   Server.sendContent(HTML_TAB_END);   If a new device is found, its ID and an input field for the name of the   and a button to register the new device is displayed   If (newGeraet) {     Getid(Unknown).Tochararray(tmp1,20);     Sprintf(htmlbuf,HTML_NEWDEVICE,tmp1,tmp1);     Server.sendContent(htmlbuf);   }   Server.sendContent(HTML_END_RELOAD);
}

Web server service function for the root directory
Void handleRoot() {   If (Wifi.Status() != WL_CONNECTED) {     if we don't have a connection to the router network     the configuration page is displayed so that the access data can be entered     handleConfig();   } else {     handleWLANRequest();   }
}


Function to find a device in the device list
Return index of device or -1 if it was not found
Int findDevice(uint8_t Dev[6]) {   uint8_t J;   uint8_t  = 0;   Boolean Found = False;   Thu {     J = 0;     If (Devices[].Active == 0) {       ++;     } else {       while ((J < 6) && (Dev[J] == Devices[].Id[J])) {J++;}       Found = (J == 6);       If (!Found) ++;      }    } while ((<MAXDEVICE) && (!Found));   If (Found) {Return ;} else {Return -1;}
}

Function to display the status on the OLED display
Void Display() {   Display.Clear();   Display.Drawstring(0,0,"MQTT Gateway"+gwversion);   Display.Drawstring(0,10,getLocalTime());   Display.Drawstring(0,20,Wifi.localIP().Tostring());   Display.Drawstring(0,34,"MQTT: ");   Display.Drawstring(60,34,String(cayCnt));   Display.Drawstring(0,44,"LoRa: ");   Display.Drawstring(60,44,String(loraCnt));   Display.Drawstring(0,54,"NOW: ");   Display.Drawstring(60,54,String(nowCnt));   Display.Display();
}

Store received data in the message buffer
Return value the device number or -1 if not registered
uint8_t processData(uint8_t Buf[], Int buflen) {   Int devnr;   Int Index;   uint8_t Daniel[6];   uint8_t Channel;   uint8_t Type;   uint8_t Len;   uint8_t ;   Boolean Output;   Index = 0;   while ((Index < 6) && (Index < buflen)) {     Daniel[Index] = Buf[Index];     Index++;   }
#ifdef Debug   Serial.Print("Devices Id = ");Serial.println(Getid(Daniel));
#endif     check if the device is registered   devnr = findDevice(Daniel);   If (devnr >= 0)  {
#ifdef Debug     Serial.println(getLocalTime());     Serial.Print("Devices number = ");Serial.println(devnr);
#endif       if yes, we set the time stamp for the last message and     read the data     Devices[devnr].Last = getLocalTime();     writeconfiguration("/configuration.csv");     now read the data     while (Index < buflen) {       Channel = Buf[Index++]+devnr*MAX CHANNEL;       If (Index == buflen) Break;       Type = Buf[Index++];       Output = False;       Switch(Type) {         Case LPP_DIGITAL_INPUT : Len = LPP_DIGITAL_INPUT_SIZE - 2; Break;         Case LPP_DIGITAL_OUTPUT : Len = LPP_DIGITAL_OUTPUT_SIZE - 2; Output = True; Break;         Case LPP_ANALOG_INPUT : Len = LPP_ANALOG_INPUT_SIZE - 2; Break;         Case LPP_ANALOG_OUTPUT : Len = LPP_ANALOG_OUTPUT_SIZE - 2; Output = True; Break;         Case LPP_LUMINOSITY : Len = LPP_LUMINOSITY_SIZE - 2; Break;         Case LPP_PRESENCE : Len = LPP_PRESENCE_SIZE - 2; Break;         Case LPP_TEMPERATURE : Len = LPP_TEMPERATURE_SIZE - 2; Break;         Case LPP_RELATIVE_HUMIDITY : Len = LPP_RELATIVE_HUMIDITY_SIZE - 2; Break;         Case LPP_ACCELEROMETER : Len = LPP_ACCELEROMETER_SIZE - 2; Break;         Case LPP_BAROMETRIC_PRESSURE : Len = LPP_BAROMETRIC_PRESSURE_SIZE - 2; Break;         Case LPP_GYROMETER : Len = LPP_GYROMETER_SIZE - 2; Break;         Case LPP_GPS : Len = LPP_GPS_SIZE - 2; Break;         Default: Len =  0;       }       if the channel is not an actuator, we reset the message buffer to 1       so that the data is sent to the MQTT server at the next opportunity       If (!Output) messages[Channel].New =1;       messages[Channel].Type = Type;        = 0;       while ((<Len) && (Index < buflen)) {         If (!Output) messages[Channel].Data[] = Buf[Index];         ++; Index++;       }
#ifdef Debug       Serial.Printf("Channel %i Type %i Data: ",Channel,Type);Serial.println(Getdata(messages[Channel].Data,0,Len));
#endif       }     Return devnr;   } else {     for (uint8_t  = 0; <6; ++) Unknown[] = Daniel[];     newGeraet = True;     Return -1;   }    }

uint8_t answerBilden(uint8_t Buf[], uint8_t devnr) {   we check if we have output data for the current LoRa device   Int Index = 6; first six bytes sin the devices Id   Int devbase = devnr*MAX CHANNEL;
#ifdef Debug   Serial.Printf("Activators for Device %i Channel %i to %i-r-n",devnr,devbase,devbase+8);
#endif   for (Int  = devbase; <devbase+8; ++) {     depending on the type of digital or analog data     Switch (messages[].Type) {         Case LPP_DIGITAL_OUTPUT : Buf[Index++]= -devbase;           Buf[Index++]=messages[].Type;           Buf[Index++]=messages[].Data[0];
#ifdef Debug           Serial.println("Digital Output");
#endif           Break;         Case LPP_ANALOG_OUTPUT :   Buf[Index++]= -devbase;           Buf[Index++]=messages[].Type;           Buf[Index++]=messages[].Data[0];           Buf[Index++]=messages[].Data[1];
#ifdef Debug           Serial.println("Analog Output");
#endif           Break;     }   }   Return Index;
}

Process a message from a LoRa client
Void readLoRa() {   uint8_t Buf[256];   Int Ix;   Int devnr;   uint8_t Len;   uint8_t Byt;   Get data if available   Int packetSize = Lora.parsePacket();   have we received data ?   If (packetSize > 0) {
#ifdef Debug     Serial.Printf("%i Bytes Received from LoRa",packetSize);
#endif     while ((Ix < packetSize) && (Ix < 256)) {       Byt= Lora.Read();
Serial.printf("%2x ",byt);       Buf[Ix++] = Byt;     }
Serial.println();
#ifdef Debug     Serial.println(Getdata(Buf,0,packetSize));
#endif     devnr = processData(Buf, packetSize);     If (devnr >=0) {       Update status       loraCnt++;       loraLast = getLocalTime();     } else {       newGeraetType = 0; LoRa Device     }     Part two Send response to LoRa device     delay(500);     in the first six bytes of the buffer, the deviceId     Len = 6;     if we have a registered device, we also send data with     If (devnr >= 0) Len = answerBilden(Buf, devnr);
#ifdef Debug     Serial.Printf("Send to device %i %i bytes"r-n",devnr,Len);     Serial.println(Getdata(Buf,0,Len));
#endif     Lora.beginPacket();     Lora.Write(Buf,Len);     Int lstatus = Lora.endPacket();
#ifdef Debug     Serial.Print("Send Status = ");     Serial.println(lstatus);
#endif   }
}

callback for ESP Now
Void readESPNow(Const uint8_t *mac_addr, Const uint8_t *r_data, Int data_len) {   uint8_t Data[70];   uint8_t devnr;   uint8_t Len;    #ifdef Debug   Serial.Printf("%i Receive Bytes from ESP-Now",data_len);
#endif   Memcpy(&Data, r_data, Sizeof(Data));   devnr = processData(Data,data_len);   If (devnr >=0) {     Update status     nowCnt++;     nowLast = getLocalTime();   } else {     newGeraetType = 1; ESP Now Device   }   Delay(100);   Part two Send response to ESP-Now device   in the first six bytes of the buffer, the deviceId   Len = 6;   if we have a registered device, we also send data with   If (devnr >= 0) Len = answerBilden(Data, devnr);
#ifdef Debug   Serial.Printf("Send to device %i %i bytes"r-n",devnr,Len);   Serial.println(Getdata(Data,0,Len));
#endif   esp_now_send(Data, Data, Len); 
#ifdef Debug   Serial.println("End");
#endif   }


Void Setup() {   Initialize device storage   for (uint8_t  =0; <MAXDEVICE; ++) Devices[].Active = 0;   OLED Display Initialize   pinMode(DISPLRESET,Output);   digitalWrite(DISPLRESET, Low);   Delay(50);    digitalWrite(DISPLRESET, High);   Display.Init();   Display.flipScreenVertically();   Display.setFont(ArialMT_Plain_10);   Display.setTextAlignment(TEXT_ALIGN_LEFT);   Start serial interface   Serial.Begin(115200);   while (!Serial);    Serial.println("Start");   Flash File system   If (SPIFFS.Begin(FORMAT_SPIFFS_IF_FAILED)) Serial.println(Q("SPIFFS Loaded"));   Read in configuration and access data   readConfiguration("/configuration.csv");   readAccess("/access.txt");   initMessageBuffer();   Initialize SPI and LoRa   Spi.Begin(5,19,27,18);   Lora.setPins(Ss,Rst,DI0);   Serial.println("LoRa TRX");   If (!Lora.Begin(Band)) {     Serial.println("Starting LoRa failed!");     while (1);   }   Lora.enableCrc();   Serial.println("LoRa Initial OK!");   Delay(2000);   Output of read access data for control   Serial.Print("SSID: ");Serial.println(wlanssid);   Serial.Print("Password: ");Serial.println(wlanpwd);   Serial.Print("User: ");Serial.println(mqttuser);   Serial.Print("Password: ");Serial.println(mqttpwd);   Serial.Print("ID: ");Serial.println(mqttid);   Connect to the Wi-Fi and MQTT Server   Serial.println("Connect Wi-Fi");   we use the ESP32 as access poin but also as a client in the router network   Wifi.Fashion(WIFI_AP_STA);   we need pointers to the character memory within the strings   Char* txtSSID = const_cast<Char*>(wlanssid.c_str());   Char* txtPassword = const_cast<Char*>(wlanpwd.c_str());      Char* txtUser = const_cast<Char*>(mqttuser.c_str());   Char* txtPwd = const_cast<Char*>(mqttpwd.c_str());   Char* txtId = const_cast<Char*>(mqttid.c_str());   Regardless of the connection to the router network, we start the AccessPoint   this allows configuration via a browser, if we use this    Sign in to AccessPoint   Wifi.softAP("MQTTGateway",APPWD,APCHANNEL,0);   Connecting to the router network   Wifi.Begin(txtSSID, txtPassword);   uint8_t Timeout = 0;   while ((Wifi.Status() != WL_CONNECTED) && (Timeout<10)) {     Timeout++;     Delay(1000);   }   we wait a maximum of 10 seconds until the connection is in place   If (Wifi.Status() == WL_CONNECTED) {     If the connection to the router network was successful, we start MQTT to Cayenne     and synchronize the internal clock with the time server     Serial.Print("IP address: ");     Serial.println(Wifi.localIP());     If ((mqttid != "") && (mqttuser != "") && (mqttpwd != "")) {       Serial.println("State MQTT");       Cayenne.Begin(txtUser, txtPwd, txtId);       Serial.println("Cayenne Connection Made");       mqtt_con = 1;     }     Synchronize clock with time server     configTime(GMT_OFFSET_SEC, DAYLIGHT_OFFSET_SEC, NTP_SERVER);     Output current time     Serial.println(getLocalTime());   }   Initialize Web Server   Server.On("/", handleRoot);   Server.On("/conf",handleConfig);   Server.On("/reset",handleReset);   Server.Begin();   If (esp_now_init() == ESP_OK) Serial.println("ESP-Now initialized!");   esp_now_register_recv_cb(readESPNow);   Serial.println("*********************************************");


}


Void Loop() {   Display();   If (Wifi.Status() == WL_CONNECTED) {     Check LoRa Interface for data     readLoRa();     communicate with Cayenne MQTT Server     If (mqtt_con == 1) Cayenne.Loop(1);   }   Serving Web Server   Server.handleClient();   Delay(100);

}

Send data from the message buffer to the MQTT server
CAYENNE_OUT_DEFAULT()
{   Boolean Output = False;   Boolean sentData = False;   Float Val;
#ifdef Debug   Serial.println(getLocalTime());   Serial.println("Cayenne send");
#endif   for (Int  = 0; <MAXCHANNELS; ++) {     send only new messages     If (messages[].New == 1) {
#ifdef Debug       Serial.Printf("Send MQTT Channel %i Type %i'n",,messages[].Type);
#endif       send data depending on type       Switch (messages[].Type) {           Case LPP_DIGITAL_INPUT : Cayenne.digitalSensorWrite(,messages[].Data[0]); Break;           Case LPP_DIGITAL_OUTPUT : Output = True; Break;           Case LPP_ANALOG_INPUT : Val = (messages[].Data[0]*256 + messages[].Data[1]);Cayenne.virtualWrite(,Val/100,"analog_sensor",UNIT_UNDEFINED); Break; Break;           Case LPP_ANALOG_OUTPUT : Output = True; Break;           Case LPP_LUMINOSITY : Cayenne.luxWrite(,messages[].Data[0]*256 + messages[].Data[1]); Break;           Case LPP_PRESENCE : Cayenne.digitalSensorWrite(,messages[].Data[0]); Break;           Case LPP_TEMPERATURE : Val = (messages[].Data[0]*256 + messages[].Data[1]); Cayenne.celsiusWrite(,Val/10); Break;           Case LPP_RELATIVE_HUMIDITY : Val=messages[].Data[0];Cayenne.virtualWrite(,Val/2,TYPE_RELATIVE_HUMIDITY,UNIT_PERCENT); Break;           Case LPP_ACCELEROMETER : Val = (messages[].Data[0]*256 + messages[].Data[1]);Cayenne.virtualWrite(,Val/1000,"gx","g"); Break;           Case LPP_BAROMETRIC_PRESSURE : Val = (messages[].Data[0]*256 + messages[].Data[1]);Cayenne.hectoPascalWrite(,Val/10); Break;           case LPP_GYROMETER : len = LPP_GYROMETER_SIZE - 2; break;           case LPP_GPS : len = LPP_GPS_SIZE - 2; break;       }       If (!Output) {         messages[].New = 0;         sentData = True;       }            }   }   If (sentData) {     Update status     cayCnt++;     cayLast = getLocalTime();   }

}

CAYENNE_IN_DEFAULT()
{   uint8_t * Pdata;   Int Val;   Int Ch = request.Channel;
#ifdef Debug   Serial.println("Cayenne recive");   Serial.Printf("MQTT Data for Channel %i = %s"n",Ch,Getvalue.asString());
#endif   Switch (messages[Ch].Type) {       Case LPP_DIGITAL_OUTPUT : messages[Ch].Data[0] = Getvalue.asInt();         messages[Ch].New = 1;         Break;       Case LPP_ANALOG_OUTPUT :  Val = round(Getvalue.asDouble()*100);         messages[Ch].Data[0] = Val / 256;         messages[Ch].Data[1] = Val % 256;         messages[Ch].New = 1;         Break;   }   CAYENNE_LOG("Channel %u, value %s", request.Channel, Getvalue.asString());   Process message here. If there is an error set an error message using getValue.setError(), e.g getValue.setError("Error message");    }

The display now also displays a version number. More information about this gateway can be found in the other parts of this series.

 

Basics software

2 comments

Roland

Roland

Ich habe den Fehler scheinbar gefunden. Ich habe das exakt gleiche Board von Heltec 2×.
Beim Ersten Board funktioniert das WLAN einfach nicht. Beim zweiten geht alles Problemlos.
Ich habe auf der Platine nichts gelötet (Pins) und habe so auch keinen Kurzschluss auf der Platine mit der Antenne verursacht. Schaut so aus als hätte die Platine einen Defekt. Ich habe jetzt den Heltec ESP32LORA als Client für LORA verwendet das funktioniert.
Optisch habe ich jetzt nichts gefunden was die WLAN Antenne stören könnte.

Roland

Roland

Jetzt spiele ich mich mit den Projekten von 1 bis 7
Das Hochladen und Kompilieren funktioniert bei allen Projekten.
Ich schaffe es aber nicht die WLAN Einstellungen zu ändern. Im Seriellen Monitor sieht man
LoRa TRX
LoRa Initial OK!
SSID: Lechner LAN
Passwort: Guadalquivir2711
User:
Passwort:
ID:
WLAN verbinden
ESP-Now initialisiert!

Wie in der Beschreibung erwähnt startet aber der Access-Point „MQTTGateway“ nicht.
Was mache ich da falsch?

Leave a comment

All comments are moderated before being published

Recommended blog posts

  1. Install ESP32 now from the board manager
  2. Lüftersteuerung Raspberry Pi
  3. Arduino IDE - Programmieren für Einsteiger - Teil 1
  4. ESP32 - das Multitalent
  5. OTA - Over the Air - ESP programming via WLAN