Working with the Cayenne Dashboard - gateway as access point and network client (part 5)

In Part 3 we have built a gateway that works as a client on a Wi-Fi network. It was therefore necessary to store the corresponding access data in constants in the program memory. Since we also want to make the gateway usable for ESP-Now, it would be convenient if the gateway could also work as an access point.

Now the ESP32 can do that. When we sit the WiFi mode on WIFI_AP_STA the ESP works as both an access point and a station. However, only one channel can be used. The connection to the router network is a priority for channel selection. This means that the access point always uses the same channel as the connection to the router network.

We now want to use this double mode to configure our gateway via a browser. The necessary access data such as SSID and password as well as the access data to Cayenne simply on empty strings. If the gateway is started, it cannot connect to the router network because the credentials are missing. After the connection attempt is timed out, the access point is started with the SSID MQTTGateway. The IP address of the gateway is always in this network

To configure the gateway, we log a computer or smartphone on this network (no password) and start a browser with the address We should then see the following configuration page.

When the credentials are saved, the gateway attempts to connect to the router network. If the connection attempt is successful, the display displays the IP address with which the gateway can be reached in the router network. 

After a connection to the router network, you always get the list of registered devices in the browser. You can access the configuration page and change the access data via the /conf path. 



/* 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 <CayenneMQTTESP32.H>
#include <CayenneLPP.H>
#include <Wifi.H>
#include <Web.H>
#include <Time.H>
#include "FS.h"
#include "SPIFFS.h"

NTP Server for time synchronization
#define NTP_SERVER ""
#define GMT_OFFSET_SEC 3600

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

#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

Format Flash Filesystem if not already done

#define Debug 1

Building blocks for the web server
"<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>"
"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; "

"<body><div style='margin-left:30px;' >";

"<script language="javascript">"
"function reload()

"</div><script language="javascript">setTimeout(reload, 10000);</script></body>"
"<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>";
"<div style=""margin-top:20px;">%s Name: <input type="text" style="width:200px" name="devname"" maxlength="10" value=""> <button name="register" value="%s">Register</button></div>";
"<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>";
"<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>"

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

Device definition
Struct Device {   uint8_t Active;   uint8_t Service; 0=LoRa, 1=ESP-Now   uint8_t Id[6];   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

List of defined devices
Device Devices[MAXDEVICE];

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;

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(",");     Q.Print(Devices[].Service);Q.Print(",");     Q.Print(Getid(Devices[].Id));Q.Print(",");     Q.Print(Devices[].Name);Q.Print(",");     Q.println(Devices[].Last);   }

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 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     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.       Serial.println("Connecting Cayenne");       Cayenne.Begin(txtUser, txtPwd, txtId);     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();   }   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");   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();

Process a message from a LoRa client
Void readLoRa() {   Int devnr;   uint8_t Daniel[6];   uint8_t Channel;   uint8_t Type;   uint8_t Len;   uint8_t Dat;   Boolean Output;   Get data if available   Int packetSize = Lora.parsePacket();   have we received data ?   If (packetSize > 5) {
#ifdef Debug         Serial.println(getLocalTime());     Serial.Print(" RX ");     Serial.Print(packetSize);     Serial.println(" Bytes");     Serial.Print("Device ID");
#endif      first read the device id        for (uint8_t =0; <6;++){       Daniel[]=Lora.Read();
#ifdef Debug       Serial.Printf("-%02x",Daniel[]);
#endif     }
#ifdef Debug     Serial.println();
#endif     Calculate residual package     packetSize -= 6;     check if the device is registered     devnr = findDevice(Daniel);     If (devnr >= 0)  {       if yes, we set the time stamp for the last message and       read the data       Devices[devnr].Last = getLocalTime();       writeConfiguration("/configuration.csv");       while (packetSize > 0) {         Channel number = device number * 16 + device channel         Channel = Lora.Read() + devnr*16;
#ifdef Debug         Serial.Printf("Channel: %02x",Channel);
#endif         type of channel         Type = Lora.Read();
#ifdef Debug         Serial.Printf("Type: %02x",Type);
#endif         determine the length of the data packet and whether the channel is an actuator         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;         Remaining package = 2 less because channel and type were read         packetSize -= 2;
#ifdef Debug         Serial.Print("Data:");
#endif         now we read the received data with the determined length         for (uint8_t =0; <Len; ++) {           Dat = Lora.Read();           for actuators, we don't remember any data           If (! Output) messages[Channel].Data[] = Dat;
#ifdef Debug           Serial.Printf("-%02x",Dat);
#endif           Reduce the remaining package by one           packetSize --;         }
#ifdef Debug         Serial.println();
#endif       }       Update status       loraCnt++;       loraLast = getLocalTime();       Display();     } else {       The device is not registered        we remember the device id to display it for registration       for (uint8_t  = 0; <6; ++) Unknown[] = Daniel[];       newGeraet = True;       newGeraetType = 0; LoRa Device     }     Part two Send response to LoRa device     Delay(100);     Lora.beginPacket();     at the beginning, the device id     Lora.Write(Daniel,6);     we check if we have output data for the current LoRa device     Int devbase = devnr*16;     for (Int  = devbase; <devbase+8; ++) {       depending on the type of digital or analog data       Switch (messages[].Type) {           Case LPP_DIGITAL_OUTPUT : Lora.Write(-devbase);             Lora.Write(messages[].Type);             Lora.Write(messages[].Data,1);
#ifdef Debug             Serial.println("Digital Output");
#endif             Break;           Case LPP_ANALOG_OUTPUT :  Lora.Write(-devbase);             Lora.Write(messages[].Type);             Lora.Write(messages[].Data,2);
#ifdef Debug             Serial.println("Analog Output");
#endif             Break;       }     }          Int lstatus = Lora.endPacket();
#ifdef Debug     Serial.Print("Send Status = ");     Serial.println(lstatus);
#endif   }

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;   }   while (Q.available() && (<MAXDEVICE)) {     Tmp = Q.readStringUntil(',');     Devices[].Active = (Tmp == "1");     Tmp = Q.readStringUntil(',');     Devices[].Service = Tmp.toInt();     Tmp = Q.readStringUntil(',');     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(',');     Devices[].Name = Tmp;     Tmp = Q.readStringUntil(',');     Devices[].Last = Tmp;     ++;   }    }
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;    }    }

Void Setup() {   Initialize device storage   for (uint8_t  =0; <MAXDEVICE; ++) Devices[].Active = 0;   OLED Display Initialize   pinMode(16,Output);   digitalWrite(16, Low);   Delay(50);    digitalWrite(16, 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());   Wifi.Begin(txtSSID, txtPassword);   Connecting to the router network   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   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");   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());     Cayenne.Begin(txtUser, txtPwd, txtId);     Serial.println("Cayenne Connection Made");     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();   Serial.println("*********************************************");


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


Send data from the message buffer to the MQTT server
{   Boolean Output = False;   Boolean sentData = False;
#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 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 : Cayenne.virtualWrite(i,(messages[i].daten[0]*256 + messages[i].data[1])/100,"analog_sensor",UNIT_UNDEFINED); break; break;           Case LPP_ANALOG_OUTPUT : Output = True; Break;           Case LPP_LUMINOSITY : Cayenne.luxWrite(i,messages[i].daten[0]*256 + messages[i].daten[1]); break;           case LPP_PRESENCE : Cayenne.digitalSensorWrite(i,messages[i].daten[0]); break;           case LPP_TEMPERATURE : Cayenne.celsiusWrite(i,(messages[i].daten[0]*256 + messages[i].daten[1])/10); break;           case LPP_RELATIVE_HUMIDITY : Cayenne.virtualWrite(i,messages[i].daten[0]/2,TYPE_RELATIVE_HUMIDITY,UNIT_PERCENT); break;           case LPP_ACCELEROMETER : Cayenne.virtualWrite(i,(messages[i].daten[0]*256 + messages[i].daten[1])/1000,"gx","g"); break;           case LPP_BAROMETRIC_PRESSURE : Cayenne.hectoPascalWrite(i,(messages[i].daten[0]*256 + messages[i].daten[1])/10); break;           //case LPP_GYROMETER : len = LPP_GYROMETER_SIZE - 2; break;           //case LPP_GPS : len = LPP_GPS_SIZE - 2; break;       }       if (!output) {         messages[i].neu = 0;         sentData = true;       }            }   }   if (sentData) {     //Status aktualisieren     cayCnt++;     cayLast = getLocalTime();     anzeige();   }


{   uint8_t * pData;   int val;   int ch =;
#ifdef DEBUG   Serial.println("Cayenne recive");   Serial.printf("MQTT Daten für Kanal %i = %s\n",ch,getValue.asString());
#endif   switch (messages[ch].typ) {       case LPP_DIGITAL_OUTPUT : messages[ch].daten[0] = getValue.asInt();         messages[ch].neu = 1;         break;       case LPP_ANALOG_OUTPUT :  val = round(getValue.asDouble()*100);         messages[ch].daten[0] = val / 256;         messages[ch].daten[1] = val % 256;         messages[ch].neu = 1;         break;   }   CAYENNE_LOG("Channel %u, value %s",, getValue.asString());   //Process message here. If there is an error set an error message using getValue.setError(), e.g getValue.setError("Error message");    }


Viel Spaß beim Testen.

Esp-32Projects for advanced

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