Inch Deel 3 we hebben een gateway gebouwd die werkt als een client op een Wi-Fi-netwerk. Het was daarom noodzakelijk om de bijbehorende toegangsgegevens in constanten op te slaan in het geheugen van het programma. Aangezien we de gateway ook bruikbaar willen maken voor ESP-Now, zou het handig zijn als de gateway ook als toegangspunt zou kunnen werken.
Nu kan de ESP32 dat doen. Wanneer we zitten de WiFi-modus op WIFI_AP_STA de ESP werkt als zowel een access point en een station. Er kan echter slechts één kanaal worden gebruikt. De verbinding met het routernetwerk is een prioriteit voor kanaalselectie. Dit betekent dat het toegangspunt altijd hetzelfde kanaal gebruikt als de verbinding met het routernetwerk.
We willen nu deze dubbele modus gebruiken om onze gateway via een browser te configureren. De nodige toegangsgegevens zoals SSID en wachtwoord, evenals de toegangsgegevens tot Cayenne gewoon op lege snaren. Als de gateway is gestart, kan deze geen verbinding maken met het routernetwerk omdat de referenties ontbreken. Nadat de verbindingspoging is getimed, wordt het toegangspunt gestart met de SSID MQTTGateway. Het IP-adres van de gateway is altijd 192.168.4.1 in dit netwerk
Om de gateway te configureren, loggen we een computer of smartphone in op dit netwerk (geen wachtwoord) en starten we een browser met het adres http://192.168.4.1 We moeten dan de volgende configuratiepagina zien.
Wanneer de referenties worden opgeslagen, probeert de gateway verbinding te maken met het routernetwerk. Als de verbindingspoging is geslaagd, geeft het display het IP-adres weer waarmee de gateway in het routernetwerk kan worden bereikt.
Na een verbinding met het routernetwerk krijgt u altijd de lijst met geregistreerde apparaten in de browser. U hebt toegang tot de configuratiepagina en wijzigt de toegangsgegevens via het /conf-pad.
Schets:
/* De MQTT Gateway vormt een interface tussen LoRa-apparaten of ESP Nowe-apparaten * en Cayenne MQTT dashboards. Het draait op ESP32 met LoRa en OLED-scherm * De configuratie wordt gedaan door de browser */ #include <Spi.H> #include <Lora.H> #include "SSD1306.h" #include<Arduino.H> #include <CayenneMQTTESP32.H> #include <CayenneLPP CayenneLPP.H> #include <Wifi.H> #include <Web.H> #include <Tijd.H> #include "FS.h" #include "SPIFFS.h" NTP-server voor tijdsynchronisatie #define NTP_SERVER "de.pool.ntp.org" #define GMT_OFFSET_SEC 3600 #define DAYLIGHT_OFFSET_SEC 0 Pinnen voor de LoRa-chip #define Ss 18 #define Rst 14 #define DI0 26 Frequentie voor de LoRa-chip #define Band 433175000 // #define MAXKANALEN 256 maximaal aantal beheerde kanalen #define MAXDEVICE 32 maximaal aantal beheerde apparaten MAXCHANNELS/MAXDEVICE = 8 resulteert in het maximum aantal kanalen per apparaat Flash Filesystem opmaken als dit nog niet is gedaan #define FORMAT_SPIFFS_IF_FAILED Waar #define Debug 1 Bouwstenen voor de webserver Const PROGMEM Char HTML_HEADER[] = "<! DOCTYPE HTML>" "<html>" "<head>" "<metaname = "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>" "<stijl>" "lichaam - achtergrond-kleur: #d2f3eb; lettertype-familie: Arial, Helvetica, Sans-Serif; Kleur: #000000;tekengrootte:12pt; }" "de achtergrondkleur: #b6c0db; kleur: #050ed2;lettertypegewicht:lichter;tekengrootte:10pt; "tabel, th, td "rand: 1px effen zwart;" ".titel .font-size:18pt;font-weight:bold;text-align:center; " "</stijl>"; Const PROGMEM Char HTML_HEADER_END[] = "</hoofd>" "<body><div style='margin-left:30px;' >"; Const PROGMEM Char HTML_SCRIPT[] = "<scripttaal="javascript">" "functie herladen() "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[] = "</tabel>"; Const PROGMEM Char HTML_NEWDEVICE[] = "<div style=""margin-top:20px;">%s Name: <input type="text" style="width:200px" name="devname"" maxlength="10" value="> <name="register" value="%s">Register</button></div>"; Const PROGMEM Char HTML_TAB_ZEILE[] = "<tr><td>%s</td><td>%i</td><td>%i tot %i</td><td>%s>%s>%s>Lt;//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 Gebruikersnaam</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> </td><td><button name="save" value=>Save</button></td></tr>" "</table></form></body></html>"; Structuren Nieuws Buffer Struct MSG_BUF { uint8_t Type; uint8_t Nieuw; uint8_t Gegevens[10]; }; Apparaatdefinitie Struct Apparaat { uint8_t Actieve; uint8_t Service; 0=LoRa, 1=ESP-Nu uint8_t Id[6]; Tekenreeks Naam; Tekenreeks Laatste; }; Globale variabele Toegang tot gegevens deze kunnen worden ingevoerd via de webserver Tekenreeks wlanssid wlanssid = "Lechner LAN"; Tekenreeks wlanpwd wlanpwd wlanpwd = "Guadalquivir2711"; Tekenreeks mqttuser = ""; Tekenreeks mqttpwd = ""; Tekenreeks mqttid = ""; Webserverinstantie Web Server(80); OLED-scherm SSD1306 Weergeven(0x3c, 4, 15); Buffer voor cachingberichten per kanaal MSG_BUF Berichten[MAXKANALEN]; Lijst van gedefinieerde hulpmiddelen Apparaat Apparaten[MAXDEVICE]; Id van een niet-geregistreerd apparaat uint8_t Onbekende[6]; Vlag altijd waar wanneer een nieuw apparaat wordt gedetecteerd Booleaanse newGeraet = Valse; Type nieuw apparaat 0=LöRa 1 =ESPNow uint8_t newGeraetType = 0; Counters en activiteiten Status voor het display uint32_t loraCnt loraCnt = 0; Aantal ontvangen LoRa-berichten Tekenreeks loraLast = ""; Datum en tijd van laatst ontvangen LoRa-bericht uint32_t nowCnt = 0; Aantal ontvangen ESP Now-berichten Tekenreeks nowLast = ""; Datum en tijd van laatst ontvangen LoRa-bericht uint32_t cayCnt = 0; Aantal verzonden MQTT-berichten Tekenreeks cayLast = ""; // Datum en tijd van het laatst verzonden MQTT-bericht // Functie retourneert datum en tijd in de notatie jjjj-mm-dd hh: mm: ss als een tekenreeks String getLocalTime() { char sttime[20] = ""; struct tm tijdinfo; als (WiFi.status() == WL_CONNECTED) { als(!getLocalTime(&tijdinfo)){ Serie.println("Kan tijd niet verkrijgen"); terug sttime; } strftime(sttime, grootte van(sttime), "%Y-%m-%d %H:%M:%S", &tijdinfo); } terug sttime; } // Functie retourneert een 6-byte apparaat-ID in de notatie xx: xx: xx: xx: xx: xx als een tekenreeks String krijgen(uint8_t id[6]) { String stid; char tmp[4]; sprintf(tmp,"% 02x",id[0]); stid=tmp; voor (uint8_t j = 1; j<6; j++) { sprintf(tmp,":% 02x",id[j]); stid = stid += tmp ; } terug stid; } // bereidt de berichtenbuffer voor // stelt alle berichten in op Klaar nietig initMessageBuffer() { voor (int ik = 0;ik<MAXKANALEN;ik++) berichten[ik].nieuw = 0; } // Functie om de configuratie op te slaan nietig schrijf configuratie(const char *fn) { Bestand f = SPIFFS.open(fn, FILE_WRITE); als (!f) { Serie.println(F("FOUT: SPIFFS kan configuratie niet opslaan")); terug; } voor (uint8_t ik = 0; ik<MAXDEVICE; ik++) { f.afdrukken(apparaten[ik].actief);f.afdrukken(","); f.afdrukken(apparaten[ik].dienst);f.afdrukken(","); f.afdrukken(krijgen(apparaten[ik].id));f.afdrukken(","); f.afdrukken(apparaten[ik].naam);f.afdrukken(","); f.println(apparaten[ik].laatste); } } // Functie voor het opslaan van de toegangsgegevens nietig schrijf toegang(const char *fn) { Bestand f = SPIFFS.open(fn, FILE_WRITE); als (!f) { Serie.println(F("FOUT: SPIFFS kan aanmeldingsgegevens niet opslaan")); terug; } f.afdrukken("WLANSSID =");f.afdrukken(wlanssid);f.afdrukken('\ n'); f.afdrukken("WLANPWD =");f.afdrukken(wlanpwd);f.afdrukken('\ n'); f.afdrukken("MQTTUSER =");f.afdrukken(mqttuser);f.afdrukken('\ n'); f.afdrukken("MQTTPWD =");f.afdrukken(mqttpwd);f.afdrukken('\ n'); f.afdrukken("MQTTID =");f.afdrukken(mqttid);f.afdrukken('\ n'); } // Functie voor het registreren van een nieuw apparaat nietig apparaat Registreren() { uint8_t ik = 0; // zoek gratis toegang terwijl ((ik<MAXDEVICE) && apparaten[ik].actief) ik++; // als er geen nieuwe invoer is, doen we niets als (ik < MAXDEVICE) { // registreer anders apparaatnaam = ingevoerde naam // of onbekend als er geen is ingevoerd als (server.hasArg("devname")) { apparaten[ik].naam = server.slecht("devname"); } anders { apparaten[ik].naam = "onbekend"; } voor (uint8_t j = 0; j<6; j++) apparaten[ik].id[j]=onbekend[j]; apparaten[ik].actief = 1; apparaten[ik].dienst= type nieuw apparaat; apparaten[ik].laatste = ""; schrijf configuratie("/konfiguration.csv"); nieuw apparaat = fout; } } // De configuratiepagina wordt weergegeven door de webserver nietig handleConfig(){ char htmlbuf[1024]; Boolean herstart = fout; int index; // werd de geheugenknop ingedrukt? als (server.hasArg("opslaan")) { // Gegevens van het POST-verzoek wlanssid = server.slecht("ssid"); // als de SSID een spatie bevat, krijgen we een "+" // dit moet weer worden omgezet in een spatie voor de registratie wlanssid.vervangen("+"," "); wlanpwd = server.slecht("pwd"); mqttuser = server.slecht("mquser"); mqttpwd = server.slecht("mqpwd"); mqttid = server.slecht("mqid"); Serie.println("Nieuwe configuratie:"); Serie.afdrukken("SSID:");Serie.println(wlanssid); Serie.afdrukken("Wachtwoord:");Serie.println(wlanpwd); Serie.afdrukken("Gebruiker:");Serie.println(mqttuser); Serie.afdrukken("Wachtwoord:");Serie.println(mqttpwd); Serie.afdrukken("ID:");Serie.println(mqttid); // Sla de nieuwe configuratie op in SPIFFS schrijf toegang("/zugang.txt"); // we onthouden dat de wifi-verbinding opnieuw moet worden gestart maar eerst moet de webserver de HTML-pagina leveren Opnieuw starten = Waar; } Uitvoer van de configuratiepagina we vormen verwijzingen naar het interne geheugen van de toegangstekenreeksen om ze te gebruiken voor sprintf en om de Wi-Fi en Cayenne verbinding te starten Char* txtSSID txtSSID = const_cast<Char*>(wlanssid wlanssid.c_str()); Char* txtPassword txtPassword = const_cast<Char*>(wlanpwd wlanpwd wlanpwd.c_str()); Char* txtUser txtUser = const_cast<Char*>(mqttuser.c_str()); Char* txtPwd txtPwd = const_cast<Char*>(mqttpwd.c_str()); Char* txtId txtId = const_cast<Char*>(mqttid.c_str()); Huidige HTML-pagina naar browser verzenden Server.setContentLength(CONTENT_LENGTH_UNKNOWN); Header Server.Verzenden(200, "tekst/html",HTML_HEADER); Server.sendContent(HTML_HEADER_END); Het formulier met de invoervelden is gevuld met de huidige waarden Sprintf (Sprintf)(htmlbuf,HTML_CONFIG,txtSSID txtSSID,txtPassword txtPassword,txtUser txtUser,txtPwd txtPwd,txtId txtId); en verzonden naar de Browsewr Server.sendContent(htmlbuf); Server.sendContent(HTML_END); Als (Opnieuw starten) { Is de startvlag set moet de WiFi-verbinding loskoppelen en opnieuw verbinding maken worden opgebouwd Seriële.println("Opnieuw opstarten"); uint8_t Timeout = 0; Seriële.println("Loskoppelen"); Wifi.Verbreken(); Terwijl ((Wifi.Status() == WL_CONNECTED) && (Timeout < 10)) { Vertraging(1000); Timeout++; } Seriële.println("Opnieuw verbinding maken"); Wifi.Beginnen(txtSSID txtSSID,txtPassword txtPassword); Terwijl ((Wifi.Status() != WL_CONNECTED) && (Timeout < 10)) { Vertraging(1000); Timeout++; } Seriële.Afdrukken("IP-adres: "); Seriële.println(Wifi.localIP()); Als (Wifi.Status() == WL_CONNECTED) { neustrart was succesvol, moet de verbinding aan Cayenne ook worden herbouwd. Seriële.println("Aansluiten cayennepeper"); Cayenne.Beginnen(txtUser txtUser, txtPwd txtPwd, txtId txtId); Klok synchroniseren met tijdserver configTime configTime(GMT_OFFSET_SEC, DAYLIGHT_OFFSET_SEC, NTP_SERVER); Huidige tijd uitvoer Seriële.println(getLocalTime()); } } } De resetpagina is opgevraagd door de webserver Void handleReset() { we resetten de toegangsgegevens wlanssid wlanssid= ""; wlanpwd wlanpwd wlanpwd = ""; mqttuser = ""; mqttpwd=""; mqttid=""; en de configuratiegegevens weer te geven alleen wanneer de knop op de configuratiepagina opslaan wordt geklikt, de toegangsgegevens worden ook verwijderd in SPIFFS. handleConfig(); } De pagina met de apparaatlijst is opgevraagd door de webserver Void handleWLANRequest(){ Char htmlbuf[512]; Char tmp1 tmp1[20]; Char tmp2[20]; Char tmp3 tmp3[20]; Int Index; was de knop verwijderen geklikt? Als (Server.hasArg hasArg("verwijderen")) { Index = Server.Slechte("verwijderen").toInt(); #ifdef DEGUG DEGUG Seriële.Printf("Verwijder apparaat %i = ",Index); Seriële.println(Apparaten[Index].Naam); #endif Apparaten[Index].Actieve=0; writeConfiguratie("/configuration.csv"); } is er op de knop Registreren geklikt? Als (Server.hasArg hasArg("registreren")) { geraetRegister(); } Huidige HTML-pagina naar browser verzenden Server.setContentLength(CONTENT_LENGTH_UNKNOWN); Header Server.Verzenden(200, "tekst/html",HTML_HEADER); IP-adres voor herladen script Wifi.localIP().Tostring().Tochararray(tmp1 tmp1,20); Sprintf (Sprintf)(htmlbuf,HTML_SCRIPT,tmp1 tmp1); Server.sendContent(htmlbuf); Server.sendContent(HTML_HEADER_END); Begin van het formulier Server.sendContent("<div class="titel">MQTT - Gateway</div><form method="post">"); Tabel met actieve apparaten Server.sendContent(HTML_TAB_GERAETE); Voor (uint8_t I. = 0; I.<MAXDEVICE; I.++) { Als (Apparaten[I.].Actieve == 1) { Getid (Getid)(Apparaten[I.].Id).Tochararray(tmp1 tmp1,20); Apparaten[I.].Naam.Tochararray(tmp2,20); Apparaten[I.].Laatste.Tochararray(tmp3 tmp3,20); Sprintf (Sprintf)(htmlbuf,HTML_TAB_ZEILE,tmp1 tmp1,I.,I.*8,I.*8+7,tmp2,tmp3 tmp3,I.); Server.sendContent(htmlbuf); } } Server.sendContent(HTML_TAB_END); // Als een nieuw apparaat is gevonden, de ID en een invoerveld voor de naam // en een knop voor het registreren van het nieuwe apparaat wordt weergegeven als (nieuw apparaat) { krijgen(onbekend).toCharArray(tmp1,20); sprintf(htmlbuf,HTML_NEWDEVICE,tmp1,tmp1); server.sendContent(htmlbuf); } server.sendContent(HTML_END_RELOAD); } // Servicefuncties van de webserver voor de hoofdmap nietig handleRoot() { als (WiFi.status() != WL_CONNECTED) { // als we geen verbinding hebben met het routernetwerk // De configuratiepagina wordt weergegeven zodat de toegangsgegevens kunnen worden ingevoerd handleConfig(); } anders { handleWLANRequest(); } } // Functie om naar een apparaat in de apparatenlijst te zoeken // Retourindex van het apparaat of -1 als het niet werd gevonden int findDevice(uint8_t dev[6]) { uint8_t j; uint8_t ik = 0; Boolean gevonden = fout; doen { j = 0; als (apparaten[ik].actief == 0) { ik++; } anders { terwijl ((j < 6) && (dev[j] == apparaten[ik].id[j])) {j++;} gevonden = (j == 6); als (!gevonden) ik++; } } terwijl ((ik<MAXDEVICE) && (!gevonden)); als (gevonden) {terug ik;} anders {terug -1;} } // Functie voor het weergeven van de status op het OLED-display nietig weer te geven() { weer te geven.duidelijk(); weer te geven.drawString(0,0,"MQTT Gateway"); weer te geven.drawString(0,10,getLocalTime()); weer te geven.drawString(0,20,WiFi.localIP().toString()); weer te geven.drawString(0,34,"MQTT:"); weer te geven.drawString(60,34,String(cayCnt)); weer te geven.drawString(0,44,"LoRa:"); weer te geven.drawString(60,44,String(loraCnt)); weer te geven.drawString(0,54,"NU:"); weer te geven.drawString(60,54,String(nowCnt)); weer te geven.weer te geven(); } // Verwerk een bericht van een LoRa-client nietig readLoRa() { int devnr; uint8_t devid[6]; uint8_t kanaal; uint8_t typ; uint8_t len; uint8_t dat; Boolean uitgang; // haal gegevens op indien beschikbaar int pakketgrootte = LoRa.parsePacket(); // hebben we gegevens ontvangen? als (pakketgrootte > 5) { #ifdef DEBUG Serie.println(getLocalTime()); Serie.afdrukken("RX"); Serie.afdrukken(pakketgrootte); Serie.println("Bytes"); Serie.afdrukken("Device ID"); #endif // lees eerst het apparaat-ID voor (uint8_t ik=0; ik<6;ik++){ devid[ik]=LoRa.lezen(); #ifdef DEBUG Serie.printf("-% 02x",devid[ik]); #endif } #ifdef DEBUG Serie.println(); #endif // bereken het resterende pakket pakketgrootte -= 6; // controleer of het apparaat is geregistreerd devnr = findDevice(devid); als (devnr >= 0) { // zo ja, dan stellen we de tijdstempel in voor het laatste bericht en // lees de gegevens apparaten[devnr].laatste = getLocalTime(); schrijf configuratie("/konfiguration.csv"); terwijl (pakketgrootte > 0) { // Kanaalnummer = apparaatnummer * 16 + apparaatkanaal kanaal = LoRa.lezen() + devnr*16; #ifdef DEBUG Serie.printf("Kanaal:% 02x",kanaal); #endif // type kanaal typ = LoRa.lezen(); #ifdef DEBUG Serie.printf("Type:% 02x",typ); #endif // bepaal de lengte van het datapakket en of het kanaal een actuator is uitgang = fout; schakelaar(typ) { geval LPP_DIGITAL_INPUT : len = LPP_DIGITAL_INPUT_SIZE - 2; pauze; geval LPP_DIGITAL_OUTPUT : len = LPP_DIGITAL_OUTPUT_SIZE - 2; uitgang = waar; pauze; geval LPP_ANALOG_INPUT : len = LPP_ANALOG_INPUT_SIZE - 2; pauze; geval LPP_ANALOG_OUTPUT : len = LPP_ANALOG_OUTPUT_SIZE - 2; uitgang = waar; pauze; geval LPP_LUMINOSITY : len = LPP_LUMINOSITY_SIZE - 2; pauze; geval LPP_PRESENCE : len = LPP_PRESENCE_SIZE - 2; pauze; geval LPP_TEMPERATURE : len = LPP_TEMPERATURE_SIZE - 2; pauze; geval LPP_RELATIVE_HUMIDITY : len = LPP_RELATIVE_HUMIDITY_SIZE - 2; pauze; geval LPP_ACCELEROMETER : len = LPP_ACCELEROMETER_SIZE - 2; pauze; geval LPP_BAROMETRIC_PRESSURE : len = LPP_BAROMETRIC_PRESSURE_SIZE - 2; pauze; geval LPP_GYROMETER : len = LPP_GYROMETER_SIZE - 2; pauze; geval LPP_GPS : len = LPP_GPS_SIZE - 2; pauze; standaard: len = 0; } als het kanaal geen actuator is, stellen we de berichtbuffer opnieuw in 1 zodat de gegevens bij de volgende gelegenheid naar de MQTT-server worden verzonden Als (!Output) Berichten[Kanaal].Nieuw =1; Berichten[Kanaal].Type = Type; Resterende pakket = 2 minder omdat kanaal en type werden gelezen packetSize -= 2; #ifdef Debug Seriële.Afdrukken("Gegevens:"); #endif nu lezen we de ontvangen gegevens met de bepaalde lengte Voor (uint8_t I.=0; I.<Len; I.++) { Dat = Lora.Lezen(); voor actuatoren herinneren we ons geen gegevens Als (! Output) Berichten[Kanaal].Gegevens[I.] = Dat; #ifdef Debug Seriële.Printf("-%02x",Dat); #endif Het resterende pakket met één verminderen packetSize --; } #ifdef Debug Seriële.println(); #endif } Status bijwerken loraCnt loraCnt++; loraLast = getLocalTime(); Weergeven(); } Anders { Het apparaat is niet geregistreerd we herinneren ons de apparaat-id om het weer te geven voor registratie Voor (uint8_t I. = 0; I.<6; I.++) Onbekende[I.] = Daniel[I.]; newGeraet = Waar; newGeraetType = 0; LoRa-apparaat } Deel twee Reactie verzenden naar LoRa-apparaat Vertraging(100); Lora.beginPacket(); aan het begin is de apparaat-id Lora.Schrijven(Daniel,6); we controleren of we outputgegevens hebben voor het huidige LoRa-apparaat Int devbase = devnr*16; Voor (Int I. = devbase; I.<devbase+8; I.++) { afhankelijk van het type digitale of analoge gegevens Schakelen (Berichten[I.].Type) { Geval LPP_DIGITAL_OUTPUT : Lora.Schrijven(I.-devbase); Lora.Schrijven(Berichten[I.].Type); Lora.Schrijven(Berichten[I.].Gegevens,1); #ifdef Debug Seriële.println("Digitale output"); #endif Breken; Geval LPP_ANALOG_OUTPUT : Lora.Schrijven(I.-devbase); Lora.Schrijven(Berichten[I.].Type); Lora.Schrijven(Berichten[I.].Gegevens,2); #ifdef Debug Seriële.println("Analoge uitgang"); #endif Breken; } } Int lstatus = Lora.endPacket(); #ifdef Debug Seriële.Afdrukken("Status verzenden = "); Seriële.println(lstatus); #endif } } Functie om de configuratie te lezen Void leesConfiguratie(Const Char *Fn) { uint8_t I. = 0; Tekenreeks Tmp; Char Hex[3]; Als (!SPIFFS (SPIFFS).Bestaat(Fn)) { bestaat nog niet en genereert dan writeConfiguratie(Fn); Terug; } Bestand V = SPIFFS (SPIFFS).Open(Fn, "r"); Als (!V) { Seriële.println(V("FOUT:: SPIFFS kan configuratie niet openen")); Terug; } Terwijl (V.Beschikbaar() && (I.<MAXDEVICE)) { Tmp = V.leesStringUntil(','); Apparaten[I.].Actieve = (Tmp == "1"); Tmp = V.leesStringUntil(','); Apparaten[I.].Service = Tmp.toInt(); Tmp = V.leesStringUntil(','); Voor (uint8_t J=0; J<6; J++){ Hex[0]=Tmp[J*3]; Hex[1]=Tmp[J*3+1]; Hex[2]=0; Apparaten[I.].Id[J]= (Byte) strtol strtol(Hex,Null Null Null,16); } Tmp = V.leesStringUntil(','); Apparaten[I.].Naam = Tmp; Tmp = V.leesStringUntil(','); Apparaten[I.].Laatste = Tmp; I.++; } } Functie voor het lezen van de toegangsgegevens Void leesAccess(Const Char *Fn) { uint8_t I. = 0; Tekenreeks Sleutel; Tekenreeks Val; Char Hex[3]; Als (!SPIFFS (SPIFFS).Bestaat(Fn)) { bestaat nog niet en genereert dan writingAccess(Fn); Terug; } Bestand V = SPIFFS (SPIFFS).Open(Fn, "r"); Als (!V) { Seriële.println(V("FOUT:: SPIFFS kan geen referenties openen")); Terug; } Terwijl (V.Beschikbaar() && (I.<MAXDEVICE)) { Sleutel = V.leesStringUntil('='); Val = V.leesStringUntil('n'); Als (Sleutel == "WLANSSID") wlanssid wlanssid = Val; Als (Sleutel == "WLANPWD") wlanpwd wlanpwd wlanpwd = Val; Als (Sleutel == "MQTTUSER") mqttuser = Val; Als (Sleutel == "MQTTPWD") mqttpwd = Val; Als (Sleutel == "MQTTID") mqttid = Val; } } Void Setup() { Apparaatopslag initialiseren Voor (uint8_t I. =0; I.<MAXDEVICE; I.++) Apparaten[I.].Actieve = 0; OLED-scherm initialiseren pinMode(16,Output); digitalWrite(16, Lage); Vertraging(50); digitalWrite(16, Hoge); Weergeven.Init(); Weergeven.flipScreenVerticaal(); Weergeven.setFont(ArialMT_Plain_10); Weergeven.setTextAlignment(TEXT_ALIGN_LEFT); Seriële interface starten Seriële.Beginnen(115200); Terwijl (!Seriële); Seriële.println("Begin"); Flash-bestandssysteem Als (SPIFFS (SPIFFS).Beginnen(FORMAT_SPIFFS_IF_FAILED)) Seriële.println(V("SPIFFS geladen")); Lees in configuratie- en toegangsgegevens leesConfiguratie("/configuration.csv"); leesAccess("/access.txt"); initMessageBuffer(); Spi en LoRa initialiseren Spi.Beginnen(5,19,27,18); Lora.setPins(Ss,Rst,DI0); Seriële.println("LoRa TRX"); Als (!Lora.Beginnen(Band)) { Seriële.println("BeginnenLoRa mislukt!"); Terwijl (1); } Lora.enableCrc(); Seriële.println("LoRa Aanvankelijk OK!"); Vertraging(2000); Uitvoer van leestoegangsgegevens voor besturingselement Seriële.Afdrukken("SSID: ");Seriële.println(wlanssid wlanssid); Seriële.Afdrukken("Wachtwoord: ");Seriële.println(wlanpwd wlanpwd wlanpwd); Seriële.Afdrukken("Gebruiker: ");Seriële.println(mqttuser); Seriële.Afdrukken("Wachtwoord: ");Seriële.println(mqttpwd); Seriële.Afdrukken("ID: ");Seriële.println(mqttid); Verbinding maken met de Wi-Fi- en MQTT-server Seriële.println("Sluit Wi-Fi aan"); we gebruiken de ESP32 als access poin, maar ook als client in het routernetwerk Wifi.Mode(WIFI_AP_STA); we hebben verwijzingen naar het tekengeheugen binnen de tekenreeksen nodig Char* txtSSID txtSSID = const_cast<Char*>(wlanssid wlanssid.c_str()); Char* txtPassword txtPassword = const_cast<Char*>(wlanpwd wlanpwd wlanpwd.c_str()); Char* txtUser txtUser = const_cast<Char*>(mqttuser.c_str()); Char* txtPwd txtPwd = const_cast<Char*>(mqttpwd.c_str()); Char* txtId txtId = const_cast<Char*>(mqttid.c_str()); Wifi.Beginnen(txtSSID txtSSID, txtPassword txtPassword); Verbinding maken met het routernetwerk uint8_t Timeout = 0; Terwijl ((Wifi.Status() != WL_CONNECTED) && (Timeout<10)) { Timeout++; Vertraging(1000); } we wachten maximaal 10 seconden tot de verbinding op zijn plaats is Ongeacht de verbinding met het routernetwerk starten we de AccessPoint dit maakt configuratie via een browser mogelijk, als we deze Aanmelden bij AccessPoint Wifi.softAP("MQTTGateway"); Als (Wifi.Status() == WL_CONNECTED) { Als de verbinding met het routernetwerk succesvol is, starten we MQTT naar Cayenne en de interne klok synchroniseren met de tijdserver Seriële.Afdrukken("IP-adres: "); Seriële.println(Wifi.localIP()); Cayenne.Beginnen(txtUser txtUser, txtPwd txtPwd, txtId txtId); Seriële.println("Cayenne Verbinding gemaakt"); Klok synchroniseren met tijdserver configTime configTime(GMT_OFFSET_SEC, DAYLIGHT_OFFSET_SEC, NTP_SERVER); Huidige tijd uitvoer Seriële.println(getLocalTime()); } Webserver initialiseren Server.Op("/", handleRoot); Server.Op("/conf",handleConfig); Server.Op("/reset",handleReset); Server.Beginnen(); Seriële.println("*********************************************"); } Void Lus() { Weergeven(); Als (Wifi.Status() == WL_CONNECTED) { LoRa-interface controleren op gegevens readLoRa(); communiceren met Cayenne MQTT Server Cayenne.Lus(1); } Webserver serveren Server.handleClient(); } Gegevens van de berichtbuffer verzenden naar de MQTT-server CAYENNE_OUT_DEFAULT() { Booleaanse Output = Valse; Booleaanse verzondenGegevens = Valse; #ifdef Debug Seriële.println(getLocalTime()); Seriële.println("Cayenne sturen"); #endif Voor (Int I. = 0; I.<MAXKANALEN; I.++) { alleen nieuwe berichten verzenden Als (Berichten[I.].Nieuw == 1) { #ifdef Debug Seriële.Printf("Stuur MQTT Type %i'n",Berichten[I.].Type); #endif gegevens verzenden, afhankelijk van het type Schakelen (Berichten[I.].Type) { Geval LPP_DIGITAL_INPUT : Cayenne.digitalSensorWrite(I.,Berichten[I.].Gegevens[0]); Breken; Geval LPP_DIGITAL_OUTPUT : Output = Waar; Breken; case LPP_ANALOG_INPUT : Cayenne.virtualWrite(i,(berichten[i].daten[0]*256 + berichten[i].data[1])/100,"analog_sensor",UNIT_UNDEFINED); breken; breken; Geval LPP_ANALOG_OUTPUT : Output = Waar; Breken; Geval LPP_LUMINOSITY : Cayenne.luxSchrijven(I,Berichten[I].daten[0]*256 + Berichten[I].daten[1]); Breken; Geval LPP_PRESENCE : Cayenne.digitalSensorWrite(I,Berichten[I].daten[0]); Breken; Geval LPP_TEMPERATURE : Cayenne.celsiusSchrijf(I,(Berichten[I].daten[0]*256 + Berichten[I].daten[1])/10); Breken; Geval LPP_RELATIVE_HUMIDITY : Cayenne.virtualWrite(I,Berichten[I].daten[0]/2,TYPE_RELATIVE_HUMIDITY,UNIT_PERCENT); Breken; Geval LPP_ACCELEROMETER : Cayenne.virtualWrite(I,(Berichten[I].daten[0]*256 + Berichten[I].daten[1])/1000,"gx","g"); Breken; Geval LPP_BAROMETRIC_PRESSURE : Cayenne.hectoPascalWrite hectoPascalWrite(I,(Berichten[I].daten[0]*256 + Berichten[I].daten[1])/10); Breken; geval LPP_GYROMETER : len = LPP_GYROMETER_SIZE - 2; breken; geval LPP_GPS : len = LPP_GPS_SIZE - 2; breken; } Als (!Output) { Berichten[I].Neu = 0; verzondenGegevens = Waar; } } } Als (verzondenGegevens) { Status aktualisieren cayCnt++; cayLast = getLocalTime(); Anzeige(); } } CAYENNE_IN_DEFAULT() { uint8_t * pData; Int Val; Int Ch = Verzoek.Kanaal; #ifdef Debug Seriële.println("Cayenne recive"); Seriële.Printf("MQTT Daten für Kanal %i = %s\n",Ch,getValue.asString()); #endif Schakelen (Berichten[Ch].typ) { Geval LPP_DIGITAL_OUTPUT : Berichten[Ch].daten[0] = getValue.asInt(); Berichten[Ch].Neu = 1; Breken; Geval LPP_ANALOG_OUTPUT : Val = Ronde(getValue.asDubbel()*100); Berichten[Ch].daten[0] = Val / 256; Berichten[Ch].daten[1] = Val % 256; Berichten[Ch].Neu = 1; Breken; } CAYENNE_LOG("Kanaal %u, waarde %s", Verzoek.Kanaal, getValue.asString()); Proces bericht hier. Als er een fout is ingesteld, stelt u een foutbericht in met getValue.setError(), bijvoorbeeld getValue.setError('Foutbericht'); }
Viel Spaß beim Testen.