Nach weiteren Tests ist es mir gelungen das MQTT Gateway so zu erweitern, dass es Geräte mit ESP-Now unterstützen kann. Das ermöglicht den Einsatz der sehr preiswerten Boards auf Basis vom ESP8266. Die Reichweite ist dann allerdings auf den Bereich des lokalen WLAN Netzwerks begrenzt. Es gibt noch eine weitere Einschränkung. ESPNow arbeitet nur mit dem WLAN Kanal 1. Es ist daher notwendig den Router für das lokale Netzwerk (z.B. Fritzbox) fix auf Kanal 1 einzustellen.
Da ich bisher die ESP-Now Verbindung nur vom Gerät zum Gateway zum arbeiten brachte, kann das ESP-Now Gerät nur Daten von Sensoren an das Gateway liefern aber keine Befehle vom Gateway erhalten. Ich werde jedoch versuchen dieses Problem zu lösen und in diesem Blog zu posten.
Der Code enthält auch noch einige Verbesserungen und den Fix für einen Fehler beim Speichern der Geräteliste, der dann auftrat wenn mehr als ein Gerät registriert war.
/* Das MQTT Gateway bildet ein Interface zwischen LoRa Geräten bzw. ESP Nowe Geräten * und Cayenne MQTT Dashboards. Es läuft auf ESP32 mit LoRa und OLED Display * Die Konfiguration erfolgt vom Browser */ #include <SPI.h> #include <LoRa.h> #include "SSD1306.h" #include<Arduino.h> #include <CayenneMQTTESP32.h> #include <CayenneLPP.h> #include <WiFi.h> #include <WebServer.h> #include <time.h> #include "FS.h" #include "SPIFFS.h" #include <esp_now.h> //NTP Server zur Zeitsynchronisation #define NTP_SERVER "de.pool.ntp.org" #define GMT_OFFSET_SEC 3600 #define DAYLIGHT_OFFSET_SEC 0 //Pins für den LoRa Chip #define SS 18 #define RST 14 #define DI0 26 //Frequenz für den LoRa Chip #define BAND 433175000 //Pin für das Display-Reset #define DISPLRESET 16 // #define MAXCHANNELS 256 //maximale Zahl der verwalteten Kanäle #define MAXDEVICE 32 //maximale Anzahl der verwalteten Geräte MAXCHANNELS/MAXDEVICE = 8 ergibt die maximale Anzahl von Kanälen pro Gerät #define MAXKANAL 8 //maximale Anzahl an Kanälen pro Gerät //Format Flash Filesystem wenn noch nicht geschehen #define FORMAT_SPIFFS_IF_FAILED true #define APPWD "123456789" #define APCHANNEL 0 #define DEBUG 0 const String gwversion = "1.0"; //Bausteine für den 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;}" ".titel {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%\">Nr.</th>" "<th style=\"width:20%\">Kanäle</th><th style=\"width:20%\">Name</th>" "<th style=\"width:20%\">Letzte Daten</th><th style=\"width:10%\">Aktion</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=\"registrieren\" value=\"%s\">Registrieren</button></div>"; const PROGMEM char HTML_TAB_ZEILE[] = "<tr><td>%s</td><td>%i</td><td>%i bis %i</td><td>%s</td><td>%s</td><td><button name=\"delete\" value=\"%i\">Löschen</button></td></tr>"; const PROGMEM char HTML_CONFIG[] = "<form method=\"post\"><h1>Zugangsdaten</h1><table>" "<tr><td>WLAN SSID</td><td><input type=\"text\" name=\"ssid\" value=\"%s\" size=50 maxlen=30/></td></tr>" "<tr><td>WLAN Passwort</td><td><input type=\"text\" name=\"pwd\" value=\"%s\" size=50 maxlen=30/></td></tr>" "<tr><td>Cayenne Benutzername</td><td><input type=\"text\" name=\"mquser\" value=\"%s\" size=50 maxlen=40/></td></tr>" "<tr><td>Cayenne Passwort</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> </td><td><button name=\"save\" value=>Speichern</button></td></tr>" "</table></form></body></html>"; //Datenstrukturen //Nachrichten Buffer struct MSG_BUF { uint8_t typ; uint8_t neu; uint8_t daten[10]; }; //Gerätedefinition struct DEVICE { uint8_t aktiv = 0; uint8_t dienst = 0; //0=LoRa, 1=ESP-Now uint8_t id[6] = {0,0,0,0}; String name = ""; String last = ""; }; //Globale Variable //Zugangsdaten diese können über den Web-Server eingegeben werden String wlanssid = "Lechner LAN"; String wlanpwd = "Guadalquivir2711"; String mqttuser = ""; String mqttpwd = ""; String mqttid = ""; //Webserver Instanz WebServer server(80); //OLED Display SSD1306 display(0x3c, 4, 15); //Buffer zum Zwischenspeichern der Nachrichten je Kanal MSG_BUF messages[MAXCHANNELS]; //Liste der definierten Geräte DEVICE devices[MAXDEVICE]; //MQTT Status int mqtt_con = 0; //Id eines nicht registrierten Gerätes uint8_t unbekannt[6]; //Flag immer dann wahr wenn ein neues Gerät entdeckt wurde boolean neuesGeraet = false; //Typ des neuen Gerätes 0=LöRa 1 =ESPNow uint8_t neuesGeraetTyp = 0; //Zähler und Aktivitaets Status für das Display uint32_t loraCnt = 0; //Anzahl der empfangenen LoRa Nachrichten String loraLast = ""; //Datum und Zeit der letzten empfangenen LoRa Nachricht uint32_t nowCnt = 0; //Anzahl der empfangenen ESP Now Nachrichten String nowLast = ""; //Datum und Zeit der letzten empfangenen LoRa Nachricht uint32_t cayCnt = 0; //Anzahl der gesendeten MQTT Nachrichten String cayLast = ""; //Datum und Zeit der letzten gesendeten MQTT Nachricht //Funktion liefert Datum und Uhrzeit im Format yyyy-mm-dd hh:mm:ss als 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; } //Funktion liefert Teil eines Datenbuffers im format xx, xx, xx .... als String String getData(uint8_t buf[], uint8_t start, uint8_t ende) { String stdata; char tmp[4]; sprintf(tmp,"%02x",buf[start]); stdata=tmp; for (uint8_t j = start+1; j<ende; j++) { sprintf(tmp,",%02x",buf[j]); stdata = stdata += tmp ; } return stdata; } //bereitet den Nachrichtenbuffer vor //setzt alle Nachrichten auf erledigt void initMessageBuffer() { for (int i = 0;i<MAXCHANNELS;i++) { messages[i].neu = 0; } } //Funktion zum Speichern der Konfiguration void schreibeKonfiguration(const char *fn) { File f = SPIFFS.open(fn, FILE_WRITE); if (!f) { Serial.println(F("ERROR: SPIFFS Kann Konfiguration nicht speichern")); return; } for (uint8_t i = 0; i<MAXDEVICE; i++) { f.print(devices[i].aktiv);f.print('\n'); if (devices[i].aktiv) { f.print(devices[i].dienst);f.print('\n'); f.print(getId(devices[i].id));f.print('\n'); f.print(devices[i].name);f.print('\n'); f.print(devices[i].last);f.print('\n'); } else { f.printf("0\n00:00:00:00:00:00\n-\n-\n"); } } } //Funktion zum Speichern der Zugangsdaten void schreibeZugang(const char *fn) { File f = SPIFFS.open(fn, FILE_WRITE); if (!f) { Serial.println(F("ERROR: SPIFFS Kann Zugangsdaten nicht speichern")); return; } f.print("WLANSSID=");f.print(wlanssid);f.print('\n'); f.print("WLANPWD=");f.print(wlanpwd);f.print('\n'); f.print("MQTTUSER=");f.print(mqttuser);f.print('\n'); f.print("MQTTPWD=");f.print(mqttpwd);f.print('\n'); f.print("MQTTID=");f.print(mqttid);f.print('\n'); } //Funktion zum Lesen der Konfiguration void leseKonfiguration(const char *fn) { uint8_t i = 0; String tmp; char hex[3]; if (!SPIFFS.exists(fn)) { //existiert noch nicht dann erzeugen schreibeKonfiguration(fn); return; } File f = SPIFFS.open(fn, "r"); if (!f) { Serial.println(F("ERROR:: SPIFFS Kann Konfiguration nicht öffnen")); return; } #ifdef DEBUG Serial.println("Lese Geräteliste"); #endif while (f.available() && (i<MAXDEVICE)) { Serial.printf("Lese Gerät %i",i); tmp = f.readStringUntil('\n'); devices[i].aktiv = (tmp == "1"); tmp = f.readStringUntil('\n'); devices[i].dienst = tmp.toInt(); tmp = f.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[i].id[j]= (byte) strtol(hex,NULL,16); } tmp = f.readStringUntil('\n'); devices[i].name = tmp; tmp = f.readStringUntil('\n'); devices[i].last = tmp; #ifdef DEBUG Serial.print("Gerät "+getId(devices[i].id)+ " Name " + devices[i].name); Serial.printf(" Dienst %i Aktiv %i\r\n",devices[i].dienst,devices[i].aktiv); #endif i++; } } //Funktion zum Lesen der Zugangsdaten void leseZugang(const char *fn) { uint8_t i = 0; String key; String val; char hex[3]; if (!SPIFFS.exists(fn)) { //existiert noch nicht dann erzeugen schreibeZugang(fn); return; } File f = SPIFFS.open(fn, "r"); if (!f) { Serial.println(F("ERROR:: SPIFFS Kann Zugangsdaten nicht öffnen")); return; } while (f.available() && (i<MAXDEVICE)) { key = f.readStringUntil('='); val = f.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; } } //Funktion zum Registrieren eines neuen Gerätes void geraetRegistrieren() { uint8_t i = 0; //suche freien Eintrag while ((i<MAXDEVICE) && devices[i].aktiv) i++; //gibt es keinen neuen Eintrag tun wir nichts if (i < MAXDEVICE) { //sonst Geraet registrieren Name = eingegebener Name //oder unbekannt wenn keiner eingegeben wurde if (server.hasArg("devname")) { devices[i].name = server.arg("devname"); } else { devices[i].name = "unbekannt"; } for (uint8_t j = 0; j<6; j++) devices[i].id[j]=unbekannt[j]; devices[i].aktiv = 1; devices[i].dienst= neuesGeraetTyp; devices[i].last = ""; schreibeKonfiguration("/konfiguration.csv"); neuesGeraet = false; } } //Die Konfigurationsseite wird vom Web-Server angezeigt void handleConfig(){ char htmlbuf[1024]; boolean restart = false; int index; //wurde der Speicherknopf gedrückt ? if (server.hasArg("save")) { //Daten aus dem POST request wlanssid = server.arg("ssid"); //wenn die SSID ein Leerzeichen enthält erhalten wir ein "+" //das muss für die Anmeldung wieder in ein Leerzeichen gewandelt werden wlanssid.replace("+"," "); wlanpwd = server.arg("pwd"); mqttuser = server.arg("mquser"); mqttpwd = server.arg("mqpwd"); mqttid = server.arg("mqid"); Serial.println("Neue Konfiguration:"); Serial.print("SSID: ");Serial.println(wlanssid); Serial.print("Passwort: ");Serial.println(wlanpwd); Serial.print("User: ");Serial.println(mqttuser); Serial.print("Passwort: ");Serial.println(mqttpwd); Serial.print("ID: ");Serial.println(mqttid); //Die neue Konfiguration in SPIFFS speichern schreibeZugang("/zugang.txt"); //wir merken uns dass die WiFi Verbindung neu gestartet werden muss //zuerst muss aber der web server die HTML Seite liefern restart = true; } //Ausgabe der Konfigurationsseite //wir bilden Zeiger auf den internen speicher der Zugangsstrings //um diese für sprintf und zum Starten der WLAN und Cayenne Verbindung zu nutzen 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()); //Aktuelle HTML Seite an Browser senden server.setContentLength(CONTENT_LENGTH_UNKNOWN); //Header server.send(200, "text/html",HTML_HEADER); server.sendContent(HTML_HEADER_END); //Das Formular mit den Eingabefeldern wird mit den aktuellen Werten befüllt sprintf(htmlbuf,HTML_CONFIG,txtSSID,txtPassword,txtUser,txtPwd,txtId); //und an den Browsewr gesendet server.sendContent(htmlbuf); server.sendContent(HTML_END); if (restart) { //War das restart flag gesetzt muss die WiFi Verbindung getrennt und neu //aufgebaut werden mqtt_con = 0; Serial.println("Neustart"); uint8_t timeout = 0; Serial.println("Verbindung trennen"); WiFi.disconnect(); while ((WiFi.status() == WL_CONNECTED) && (timeout < 10)) { delay(1000); timeout++; } Serial.println("Neu verbinden"); 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) { //war der Neustrart erfolgreich muss auch die Verbindung zu Cayenne neu aufgebaut werden. if ((mqttuser != "")&&(mqttpwd != "") && (mqttid != "")) { Serial.println("Cayenne verbinden"); Cayenne.begin(txtUser, txtPwd, txtId); mqtt_con = 1; } //Uhr mit Zeitserver synchronisieren configTime(GMT_OFFSET_SEC, DAYLIGHT_OFFSET_SEC, NTP_SERVER); //Aktuelle Uhrzeit ausgeben Serial.println(getLocalTime()); } } } //Die reset Seite wurde vom WebServer abgefragt void handleReset() { //wir setzen die Zugangsdaten zurück wlanssid= ""; wlanpwd = ""; mqttuser = ""; mqttpwd=""; mqttid=""; //und zeigen die Konfigurationsdaten an //erst wenn auf der Konfigurationsseite der Button //Speichern geklickt wird, werden die Zugangsdaten //auch im SPIFFS gelöscht. handleConfig(); } //Die Seite mit der Geräteliste wurde vom Webserver abgefragt void handleWLANRequest(){ char htmlbuf[512]; char tmp1[20]; char tmp2[20]; char tmp3[20]; int index; //wurde der Lösch Knopf geklickt ? if (server.hasArg("delete")) { index = server.arg("delete").toInt(); #ifdef DEGUG Serial.printf("Lösche device %i = ",index); Serial.println(devices[index].name); #endif devices[index].aktiv=0; schreibeKonfiguration("/konfiguration.csv"); } //wurde der Registrieren Knopf geklickt ? if (server.hasArg("registrieren")) { geraetRegistrieren(); Serial.println("Konfiguration wird gespeichert"); schreibeKonfiguration("/konfiguration.csv"); } //Aktuelle HTML Seite an Browser senden server.setContentLength(CONTENT_LENGTH_UNKNOWN); //Header server.send(200, "text/html",HTML_HEADER); //IP Adresse für reload script WiFi.localIP().toString().toCharArray(tmp1,20); sprintf(htmlbuf,HTML_SCRIPT,tmp1); server.sendContent(htmlbuf); server.sendContent(HTML_HEADER_END); //Formular Anfang server.sendContent("<div class=\"titel\">MQTT - Gateway</div><form method=\"post\">"); //Tabelle der aktiven Geräte server.sendContent(HTML_TAB_GERAETE); for (uint8_t i = 0; i<MAXDEVICE; i++) { if (devices[i].aktiv == 1) { getId(devices[i].id).toCharArray(tmp1,20); devices[i].name.toCharArray(tmp2,20); devices[i].last.toCharArray(tmp3,20); sprintf(htmlbuf,HTML_TAB_ZEILE,tmp1,i,i*8,i*8+7,tmp2,tmp3,i); server.sendContent(htmlbuf); } } server.sendContent(HTML_TAB_END); //Falls ein neues Gerät gefunden wurde wird seine ID sowie ein Eingabefeld für den Namen // und ein Knopf zum Registrieren des neuen Gerätes angezeigt if (neuesGeraet) { getId(unbekannt).toCharArray(tmp1,20); sprintf(htmlbuf,HTML_NEWDEVICE,tmp1,tmp1); server.sendContent(htmlbuf); } server.sendContent(HTML_END_RELOAD); } //Service Funktions des Web Servers für das Root Verzeichnis void handleRoot() { if (WiFi.status() != WL_CONNECTED) { //wenn wir keine Verbindung ins Routernetz haben //wird die Konfigurationsseite angezeigt, sodass die Zugangsdaten eingegeben werden können handleConfig(); } else { handleWLANRequest(); } } //Funktion zum Suchen eines Gerätes in der Geräteliste //Rückgabe Index des Gerätes oder -1 wenn es nicht gefunden wurde int findDevice(uint8_t dev[6]) { uint8_t j; uint8_t i = 0; boolean found = false; do { j = 0; if (devices[i].aktiv == 0) { i++; } else { while ((j < 6) && (dev[j] == devices[i].id[j])) {j++;} found = (j == 6); if (!found) i++; } } while ((i<MAXDEVICE) && (!found)); if (found) {return i;} else {return -1;} } //Funktion zum Anzeigen des Status am OLED Display void anzeige() { 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(); } //Empfangene Daten in den Nachrichtenbuffer speichern //Rückgabewert die Gerätenummer oder -1 falls nicht registriert uint8_t processData(uint8_t buf[], int buflen) { int devnr; int index; uint8_t devid[6]; uint8_t channel; uint8_t typ; uint8_t len; uint8_t i; boolean output; index = 0; while ((index < 6) && (index < buflen)) { devid[index] = buf[index]; index++; } #ifdef DEBUG Serial.print("Geräte Id = ");Serial.println(getId(devid)); #endif //nachschauen ob das Gerät registriert ist devnr = findDevice(devid); if (devnr >= 0) { #ifdef DEBUG Serial.println(getLocalTime()); Serial.print("Geräte Nummer = ");Serial.println(devnr); #endif //wenn ja setzen wir den Zeitstempel für die letzte Meldung und //lesen die Daten devices[devnr].last = getLocalTime(); //schreibeKonfiguration("/konfiguration.csv"); //nun die Daten lesen while (index < buflen) { channel = buf[index++]+devnr*MAXKANAL; if (index == buflen) break; typ = buf[index++]; output = false; switch(typ) { 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; } //ist der Kanal kein Aktuator, setzen wir im Nachrichtenbuffer neu auf 1 //damit die Daten bei nächster Gelegenheit an den MQTT Server gesendet werden if (!output) messages[channel].neu =1; messages[channel].typ = typ; i = 0; while ((i<len) && (index < buflen)) { if (!output) messages[channel].daten[i] = buf[index]; i++; index++; } #ifdef DEBUG Serial.printf("Kanal %i Typ %i Daten: ",channel,typ);Serial.println(getData(messages[channel].daten,0,len)); #endif } return devnr; } else { for (uint8_t i = 0; i<6; i++) unbekannt[i] = devid[i]; neuesGeraet = true; return -1; } } uint8_t antwortBilden(uint8_t buf[], uint8_t devnr) { // wir prüfen ob wir Output Daten für das aktuelle LoRa-Gerät haben int index = 6; //erste sechs bytes sin die Geräte Id int devbase = devnr*MAXKANAL; #ifdef DEBUG Serial.printf("Aktivatoren für Gerät %i Kanal %i bis %i\r\n",devnr,devbase,devbase+8); #endif for (int i = devbase; i<devbase+8; i++) { //je nach typ Digital oder Analogdaten switch (messages[i].typ) { case LPP_DIGITAL_OUTPUT : buf[index++]= i-devbase; buf[index++]=messages[i].typ; buf[index++]=messages[i].daten[0]; #ifdef DEBUG Serial.println("Digital Ausgang"); #endif break; case LPP_ANALOG_OUTPUT : buf[index++]= i-devbase; buf[index++]=messages[i].typ; buf[index++]=messages[i].daten[0]; buf[index++]=messages[i].daten[1]; #ifdef DEBUG Serial.println("Analog Ausgang"); #endif break; } } return index; } //Eine Nachricht von einem LoRa Client verarbeiten void readLoRa() { uint8_t buf[256]; int ix; int devnr; uint8_t len; uint8_t byt; //Daten holen falls vorhanden int packetSize = LoRa.parsePacket(); //haben wir Daten erhalten ? if (packetSize > 0) { #ifdef DEBUG Serial.printf("%i Bytes von LoRa empfangen\r\n",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) { //Status aktualisieren loraCnt++; loraLast = getLocalTime(); } else { neuesGeraetTyp = 0; //LoRa Gerät } //Teil zwei Antwort an das LoRa Gerät senden //delay(500); //in den ertsen sechs bytes des Buffers steht bereits die GeräteId len = 6; //falls wir ein registriertes Gerät haben senden wir auch Daten mit if (devnr >= 0) len = antwortBilden(buf, devnr); #ifdef DEBUG Serial.printf("Sende an Gerät %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("Sendestatus = "); Serial.println(lstatus); #endif } } // callback für 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 Bytes von ESP-Now empfangen\r\n",data_len); #endif memcpy(&data, r_data, sizeof(data)); devnr = processData(data,data_len); if (devnr >=0) { //Status aktualisieren nowCnt++; nowLast = getLocalTime(); } else { neuesGeraetTyp = 1; //ESP Now Gerät } delay(100); //Teil zwei Antwort an das ESP-Now Gerät senden //in den ertsen sechs bytes des Buffers steht bereits die GeräteId len = 6; //falls wir ein registriertes Gerät haben senden wir auch Daten mit if (devnr >= 0) len = antwortBilden(data, devnr); #ifdef DEBUG Serial.printf("Sende an Gerät %i %i bytes\r\n",devnr,len); Serial.println(getData(data,0,len)); #endif esp_now_send(data, data, len); #ifdef DEBUG Serial.println("Ende"); #endif } void setup() { //gerätespeicher initialisieren for (uint8_t i =0; i<MAXDEVICE; i++) devices[i].aktiv = 0; // OLED Display initialisieren 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); //Serielle Schnittstelle starten Serial.begin(115200); while (!Serial); Serial.println("Start"); //Flash File system if (SPIFFS.begin(FORMAT_SPIFFS_IF_FAILED)) Serial.println(F("SPIFFS geladen")); //Konfiguration und Zugangsdaten einlesen leseKonfiguration("/konfiguration.csv"); leseZugang("/zugang.txt"); initMessageBuffer(); //SPI und LoRa initialisieren 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); //Ausgabe der gelesenen Zugangsdaten zur Kontrolle Serial.print("SSID: ");Serial.println(wlanssid); Serial.print("Passwort: ");Serial.println(wlanpwd); Serial.print("User: ");Serial.println(mqttuser); Serial.print("Passwort: ");Serial.println(mqttpwd); Serial.print("ID: ");Serial.println(mqttid); //Mit dem WLAN und MQTT Server verbinden Serial.println("WLAN verbinden"); //wir benutzen den ESP32 als Access Poin aber auch als Client im Routernetz WiFi.mode(WIFI_AP_STA); //wir benötigen Zeiger auf den Zeichenspeicher innerhalb der 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()); //Unabhängig von der Verbindung ins Routernetz starten wir den AccessPoint //das ermöglicht die Konfiguration über einen Browser, wenn wir diesen //am AccessPoint anmelden WiFi.softAP("MQTTGateway",APPWD,APCHANNEL,0); //Verbindung ins Routernetz wird hergestellt WiFi.begin(txtSSID, txtPassword); uint8_t timeout = 0; while ((WiFi.status() != WL_CONNECTED) && (timeout<10)) { timeout++; delay(1000); } //wir warten maximal 10 Sekunden bis die Verbindung steht if (WiFi.status() == WL_CONNECTED) { //War die Verbindung ins Routernetz erfolgreich, starten wir MQTT zu Cayenne //und synchronisieren die interne Uhr mit dem 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 Verbindung hergestellt"); mqtt_con = 1; } //Uhr mit Zeitserver synchronisieren configTime(GMT_OFFSET_SEC, DAYLIGHT_OFFSET_SEC, NTP_SERVER); //Aktuelle Uhrzeit ausgeben Serial.println(getLocalTime()); } //Web Server initialisieren server.on("/", handleRoot); server.on("/conf",handleConfig); server.on("/reset",handleReset); server.begin(); if (esp_now_init() == ESP_OK) Serial.println("ESP-Now initialisiert!"); esp_now_register_recv_cb(readESPNow); Serial.println("*********************************************"); } void loop() { anzeige(); if (WiFi.status() == WL_CONNECTED) { //LoRa Interface auf Daten prüfen readLoRa(); //mit Cayenne MQTT Server kommunizieren if (mqtt_con == 1) Cayenne.loop(1); } //Web Server bedienen server.handleClient(); delay(100); } //Daten aus dem Nachrichtenbuffer an den MQTT Server senden CAYENNE_OUT_DEFAULT() { boolean output = false; boolean sentData = false; float val; #ifdef DEBUG Serial.println(getLocalTime()); Serial.println("Cayenne send"); #endif for (int i = 0; i<MAXCHANNELS; i++) { //nur neue Nachrichten senden if (messages[i].neu == 1) { #ifdef DEBUG Serial.printf("Sende MQTT Kanal %i Typ %i\n",i,messages[i].typ); #endif //je nach Typ Daten senden switch (messages[i].typ) { case LPP_DIGITAL_INPUT : Cayenne.digitalSensorWrite(i,messages[i].daten[0]); break; case LPP_DIGITAL_OUTPUT : output = true; break; case LPP_ANALOG_INPUT : val = (messages[i].daten[0]*256 + messages[i].daten[1]);Cayenne.virtualWrite(i,val/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 : val = (messages[i].daten[0]*256 + messages[i].daten[1]); Cayenne.celsiusWrite(i,val/10); break; case LPP_RELATIVE_HUMIDITY : val=messages[i].daten[0];Cayenne.virtualWrite(i,val/2,TYPE_RELATIVE_HUMIDITY,UNIT_PERCENT); break; case LPP_ACCELEROMETER : val = (messages[i].daten[0]*256 + messages[i].daten[1]);Cayenne.virtualWrite(i,val/1000,"gx","g"); break; case LPP_BAROMETRIC_PRESSURE : val = (messages[i].daten[0]*256 + messages[i].daten[1]);Cayenne.hectoPascalWrite(i,val/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(); } } CAYENNE_IN_DEFAULT() { uint8_t * pData; int val; int ch = request.channel; #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", 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"); }
Das Display zeigt jetzt auch eine Versionsnummer an. Weiter Informationen zu diesem Gateway findet Ihr in den anderen Teilen dieser Serie.
2 comments
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
Jetzt spiele ich mich mit den Projekten von 1 bis 7
Wie in der Beschreibung erwähnt startet aber der Access-Point „MQTTGateway“ nicht.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!
Was mache ich da falsch?