Dopo diversi tentativi falliti di costruire un gateway LoRa universale con ESP32, è stato creato il seguente gateway proprietario, che consente ai dispositivi IoT basati su LoRa di connettersi al dashboard di Cayenne tramite MQTT. In una fase di espansione avanzata, il gateway gestirà anche i dispositivi IoT in base al protocollo ESP-Now.
Per il gateway, abbiamo solo bisogno di un ESP32 con Display LoRa e OLEDnon sono necessari ulteriori componenti. L'alimentazione può essere fornita con qualsiasi alimentatore USB.
Descrizione:
Il gateway può gestire 32 dispositivi con un massimo di 8 canali ciascuno. I dati vengono trasmessi in modo asincrono. Quando il gateway riceve un pacchetto di dati LoRa, i primi sei byte vengono interpretati come ID dispositivo (indirizzo mac tipico). Il gateway controlla quindi un elenco di dispositivi per verificare se il dispositivo è già registrato. In caso contrario, l'ID del dispositivo viene salvato e visualizzato tramite l'interfaccia Web per la registrazione.
Se il dispositivo è già registrato, i dati vengono letti e archiviati per canale in un buffer dei messaggi. Il numero di canale è determinato dal numero di dispositivo (indice nell'elenco dei dispositivi) - 8 - numero di canale dei dati ricevuti. Dispositivo 0 ha quindi canali da 0 a 7, dispositivo 1 canali 8 a 15 ecc.
Un record inizia con un byte per il numero di canale seguito da un byte del tipo, seguito da 1 a 6 byte di dati, a seconda del tipo. Dopo che tutti i dati sono stati salvati nel buffer dei messaggi e contrassegnati come nuovi, il pacchetto di risposta viene compilato nel dispositivo Lora. Si riavvia con l'ID del dispositivo a sei byte. Il buffer dei messaggi controlla quindi i canali per questo dispositivo per verificare se è presente un pacchetto di dati di output da Cayenne al dispositivo contrassegnato come nuovo. Se tale pacchetto di dati viene trovato, viene collegato al pacchetto di risposta, utilizzando nuovamente il numero di canale relativo da 0 a 7. Come passaggio finale, il pacchetto di risposta viene trasferito al dispositivo LoRa.
La funzione Cayenne.loop(1) comunica con il server IoT. Nella funzione di callback CAYENNE_OUT_DEFAULT() tutti i canali contrassegnati con new e contenenti un pacchetto di dati di input per Cayenne vengono cercati nel buffer dei messaggi. Questi pacchetti vengono ora convertiti e inviati al server IoT, a seconda del tipo. Al termine del trasferimento, il flag Nuovo viene reimpostato.
Una seconda funzione di callback CAYENNE_IN_DEFAULT() viene chiamata ogni volta che Cayenne dispone di dati per un canale di azione. I dati del server IoT vengono convertiti in base al tipo e archiviati nel buffer dei messaggi. Essi sono contrassegnati con Nuovo in modo che vengano inviati al dispositivo la volta successiva LoRa comunica con il pacchetto di risposta.
Visualizzazione:
Oltre al nome, il display mostra la data e l'ora correnti. Questo include l'indirizzo IP.
La linea MQTT: mostra il numero di trasferimenti riusciti al server IoT, LoRa i trasferimenti riusciti ai dispositivi LoRa e ORA il numero di trasferimenti riusciti ai dispositivi ESP-Now. Quest'ultimo è attualmente sempre 0 perché questa funzione non è ancora implementata.
Elenco dei dispositivi e registrazione:
L'elenco dei dispositivi viene memorizzato come file CSV nel file System Flash (SPIFFS) in modo che venga mantenuto anche se manca l'alimentazione. Questo elenco può essere gestito tramite il server Web integrato nel gateway. I dispositivi possono essere eliminati e i nuovi dispositivi possono essere registrati.
Pacchetti:
Per il tipo, tuttavia, i codici vengono sottratti in base alle linee guida IPSO Alliance Smart Objects, ma 3200 viene sottratto.
digitare | Ipso | Tipo No. | Byte | Risoluzione |
Ingresso digitale | 3200 | 0 | 1 | 1 |
Uscita digitale | 3201 | 1 | 1 | 1 |
Ingresso analogico | 3202 | 2 | 2 | 0.01 con segno |
Uscita analogica | 3203 | 3 | 2 | 0.01 con segno |
Sensore di illuminazione | 3301 | 101 | 2 | 1 Lux |
Sensore di presenza | 3302 | 102 | 1 | 1 |
Sensore di temperatura | 3303 | 103 | 2 | 0,1 gradi centigradi con segno |
Sensore di umidità | 3304 | 104 | 1 | 0.5% |
Sensore di accelerazione | 3313 | 113 | 6 | 0.001G con segno asse X, Y e |
Sensore di pressione | 3315 | 115 | 2 | 0,1 hPa |
Girometro | 3334 | 134 | 6 | 0,01%/s segno asse X, Y e |
Posizione GPS | 3336 | 136 | 9 | Latitudine 0.0001 con segno Longitudine 0.0001 con segno Altezza 0,01 m con segno |
Schizzo:
La registrazione del gateway con Cayenne viene eseguita nello stesso modo descritto nella parte 1 di questa serie di blog. I dati di accesso devono quindi essere inseriti nello schizzo (marcatura gialla) e nei dati di accesso per l'accesso alla rete WLAN locale. Il Dashboard di Cayenne non visualizzerà alcun widget perché nessun dispositivo è ancora stato connesso al gateway.
Tavola per Arduino IDE - TTGO LoRa32-OLED V1
/: il gateway MQTT forma un'interfaccia tra dispositivi LoRa o dispositivi ESP Nowe e Cayenne MQTT. Funziona su ESP32 con Display LoRa e OLED La configurazione viene eseguita dal browser */ #include <Spi.H> #include <Lora.H> #include "SSD1306.h" #include<Arduino.H> #include <CayenneMQTTESP32.H> #include <CaienneLPP.H> #include <Wifi.H> #include <Web.H> #include <Tempo.H> #include "FS.h" #include "SPIFFS.h" Otteniamo i dati per questa impostazione da Cayenne Dashboard # define MQTT_USER "" # define MQTT_PASSWORD "" # define MQTT_CLIENTID "" Accedere ai dati per il Wi-Fi locale # define WIFI_SSID "" # define WIFI_PASSWORD "" Server NTP per la sincronizzazione dell'ora # define NTP_SERVER "de.pool.ntp.org" # define GMT_OFFSET_SEC 3600 # define DAYLIGHT_OFFSET_SEC 0 Pin per il chip LoRa # define Ss 18 # define Rst 14 # define DI0 26 Frequenza del chip LoRa # define Band 433175000 // # define CANALI MAXCANALI 256 numero massimo di canali gestiti # define MAXDISPOSITIVO 32 numero massimo di dispositivi gestiti MAXCHANNELS/MAXDEVICE: 8 risultati del numero massimo di canali per dispositivo Formatta file system Flash se non è già stato fatto # define FORMAT_SPIFFS_IF_FAILED Vero # define Eseguire il debug 1 Blocchi predefiniti per il server Web Const Char HTML_HEADER[] = "<! HTML DOCTYPE>" "<html>" "<testa>" "<nome meta : contenuto "viewport" - "larghezza : larghezza del dispositivo, scala iniziale : 1,0, scala massima - 1,0, scala dell'utente-0>">" contenuto "<meta http-equiv"content-type""text/html; charset-UTF-8">" "<titolo>Gateway MQTT</titolo>" "<linguaggio script"javascript">" "funzione reload() "document.location"http://%s";" "</script>" "<stile>" "corpo - sfondo-colore: #d2f3eb; font-famiglia: Arial, Helvetica, Sans-Serif; Colore: #000000;dimensione carattere:12 pt; }" "esimo colore di sfondo: #b6c0db; colore: #050ed2;font-weight:lighter;font-size:10pt; "tavolo, th, td "bordo: 1px nero solido;" ".title .font-size:18pt;font-weight:bold;text-align:center; "</style>" "</head>" "<corpo><stile div-'margin-left:30px;' >"; Const Char HTML_END[] = "</div><linguaggio script"javascript">setTimeout(reload, 10000);</script></body>" "</html>"; Const Char HTML_TAB_GERAETE[] = "<stile tabella""larghezza:100%"><tr><th style-"width:20%"">ID</th><esimo stile>"larghezza:10%">No.</th>" "<th style>"width:20%">Canali</th><th style>"width:20%">Nome</th>" "<th style>"width:20%">Dati recenti</th><th style>"width:10%">Azione</th></tr>"; Const Char HTML_TAB_END[] = "</tabella>"; Const Char HTML_NEWDEVICE[] = "<div style""margin-top:20px;">%s Nome: <input type-"text" style""width:200px" name "devname"" maxlength""10" value> <nome pulsante> "register" value; Const Char HTML_TAB_ZEILE[] = "<tr><td>%s</td><td>%i</td><td>%i a %i</td><td>%s<</td><td>%s</td><td><nome pulsante >"delete" value -"%i">Elimina</pulsante></td></tr>"; Strutture News Buffer Struct MSG_BUF { uint8_t digitare; uint8_t Nuovo; uint8_t Dati[10]; }; Definizione del dispositivo Struct Dispositivo { uint8_t Attivo; uint8_t Servizio; 0 - LoRa, 1 - ESP-Ora uint8_t Id[6]; Stringa Nome; Stringa Ultima; }; Variabile globale Istanza del server Web Web Server(80); OLED Display SSD1306 Visualizzazione(0x3c (in modo 0x3c), 4, 15); Buffer per la memorizzazione nella cache dei messaggi per canaleBuffer for caching messages per channel MSG_BUF Messaggi[CANALI MAXCANALI]; Elenco dei dispositivi definiti Dispositivo Dispositivi[MAXDISPOSITIVO]; ID di un dispositivo non registrato uint8_t Sconosciuto[6]; Flag sempre true quando viene rilevato un nuovo dispositivo Boolean newGeraet = False; Tipo di nuovo dispositivo 0- L-Ra 1 -ESPNow uint8_t newGeraetType = 0; Contatori e attività Stato per il display uint32_t loraCnt = 0; Numero di messaggi LoRa ricevuti Stringa loraUltimo = ""; Data e ora dell'ultimo messaggio LoRa ricevuto uint32_t nowCnt = 0; Numero di messaggi ESP Now ricevuti Stringa oraUltimo = ""; Data e ora dell'ultimo messaggio LoRa ricevuto uint32_t cayCnt = 0; Numero di messaggi MQTT inviati Stringa cayUltimo = ""; Data e ora dell'ultimo messaggio MQTT inviato Funzione restituisce data e ora nel formato aaaa-mm-gg hh:mm:ss come stringa Stringa getLocalTime (ora locale)() { Char sttime[20] = ""; Struct Tm timeinfo; Se(!getLocalTime (ora locale)(&timeinfo)){ Seriale.println("Impossibile ottenere il tempo"); Ritorno sttime; } Strftime(sttime, Sizeof(sttime), "%Y-%m-%d %H:%M:%S", &timeinfo); Ritorno sttime; } Funktion liefert eine 6-Byte Geräte-Id im format xx:xx:xx:xx:xx:xx als String Stringa Getid(uint8_t Id[6]) { Stringa stid; Char Tmp[4]; Sprintf(Tmp,"%02x",Id[0]); stid=Tmp; Per (uint8_t J = 1; J<6; J++) { Sprintf(Tmp,":%02x",Id[J]); stid = stid += Tmp ; } Ritorno stid; } prepara il buffer dei messaggi imposta tutti i messaggi su fatto Vuoto initMessageBuffer() { Per (Int Ho. = 0;Ho.<CANALI MAXCANALI;Ho.++) Messaggi[Ho.].Nuovo = 0; } Funzione per salvare la configurazione Vuoto writeConfiguration(Const Char *Fn) { file D = SPIFFS.Aperto(Fn, FILE_WRITE); Se (!D) { Seriale.println(D("ERRORE: SPIFFS Impossibile salvare la configurazione")); Ritorno; } Per (uint8_t Ho. = 0; Ho.<MAXDISPOSITIVO; Ho.++) { D.Stampare(Dispositivi[Ho.].Attivo);D.Stampare(","); D.Stampare(Dispositivi[Ho.].Servizio);D.Stampare(","); D.Stampare(Getid(Dispositivi[Ho.].Id));D.Stampare(","); D.Stampare(Dispositivi[Ho.].Nome);D.Stampare(","); D.println(Dispositivi[Ho.].Ultima); } } Funzione per registrare un nuovo dispositivo Vuoto geraetRegister() { uint8_t Ho. = 0; ricerca ingresso libero Mentre ((Ho.<MAXDISPOSITIVO) && Dispositivi[Ho.].Attivo) Ho.++; non c'è nessuna nuova voce non facciamo nulla Se (Ho. < MAXDISPOSITIVO) { in caso contrario, registrare il nome geraet - nome inserito o sconosciuto se non ne è stato inserito Se (Server.hasArg("nomedev")) { Dispositivi[Ho.].Nome = Server.Male("nomedev"); } Altro { Dispositivi[Ho.].Nome = "sconosciuto"; } Per (uint8_t J = 0; J<6; J++) Dispositivi[Ho.].Id[J]=Sconosciuto[J]; Dispositivi[Ho.].Attivo = 1; Dispositivi[Ho.].Servizio= newGeraetType; Dispositivi[Ho.].Ultima = ""; writeConfiguration("/configurazione.csv"); newGeraet = False; } } Funzione di servizio del server web Vuoto handleRoot() { Char htmlbuf (informazioni in lingua inlingua commi[1024]; Char tmp1[20]; Char tmp2[20]; Char tmp3[20]; Int Indice; è stato fatto clic sul pulsante Elimina ? Se (Server.hasArg("elimina")) { Indice = Server.Male("elimina").ToInt(); #ifdef DEGUG Seriale.Printf("Elimina il dispositivo %i - ",Indice); Seriale.println(Dispositivi[Indice].Nome); #endif Dispositivi[Indice].Attivo=0; writeConfiguration("/configurazione.csv"); } il pulsante Registra è stato fatto clic su ? Se (Server.hasArg("registra")) { geraetRegister(); } Invia la pagina HTML corrente al browser Server.setContentLength (lunghezza in cui è impostato)(CONTENT_LENGTH_UNKNOWN); Intestazione Wifi.localIP (informazioni in locale)().Stringa a().Tochararray(tmp1,20); Sprintf(htmlbuf (informazioni in lingua inlingua commi,HTML_HEADER,tmp1); Server.Invia(200, "testo/html",htmlbuf (informazioni in lingua inlingua commi); Inizio modulo Server.sendContent("<div class>"title">MQTT - Gateway</div><form>"); Tabella dei dispositivi attivi Server.sendContent(HTML_TAB_GERAETE); Per (uint8_t Ho. = 0; Ho.<MAXDISPOSITIVO; Ho.++) { Se (Dispositivi[Ho.].Attivo == 1) { Getid(Dispositivi[Ho.].Id).Tochararray(tmp1,20); Dispositivi[Ho.].Nome.Tochararray(tmp2,20); Dispositivi[Ho.].Ultima.Tochararray(tmp3,20); Sprintf(htmlbuf (informazioni in lingua inlingua commi,HTML_TAB_ZEILE,tmp1,Ho.,Ho.*8,Ho.*8+7,tmp2,tmp3,Ho.); Server.sendContent(htmlbuf (informazioni in lingua inlingua commi); } } Server.sendContent(HTML_TAB_END); Se viene trovato un nuovo dispositivo, il relativo ID e un campo di immissione per il nome del e viene visualizzato un pulsante per registrare il nuovo dispositivo Se (newGeraet) { Getid(Sconosciuto).Tochararray(tmp1,20); Sprintf(htmlbuf (informazioni in lingua inlingua commi,HTML_NEWDEVICE,tmp1,tmp1); Server.sendContent(htmlbuf (informazioni in lingua inlingua commi); } Server.sendContent(HTML_END); } Funzione per trovare un dispositivo nell'elenco dei dispositivi Indice restituito del dispositivo o -1 se non è stato trovato Int findDevice(uint8_t Dev[6]) { uint8_t J; uint8_t Ho. = 0; Boolean Trovato = False; Thu { J = 0; Se (Dispositivi[Ho.].Attivo == 0) { Ho.++; } Altro { Mentre ((J < 6) && (Dev[J] == Dispositivi[Ho.].Id[J])) {J++;} Trovato = (J == 6); Se (!Trovato) Ho.++; } } Mentre ((Ho.<MAXDISPOSITIVO) && (!Trovato)); Se (Trovato) {Ritorno Ho.;} Altro {Ritorno -1;} } Funzione per visualizzare lo stato sul display OLED Vuoto Visualizzazione() { Visualizzazione.Chiaro(); Visualizzazione.Coulisse(0,0,"Gateway MQTT"); Visualizzazione.Coulisse(0,10,getLocalTime (ora locale)()); Visualizzazione.Coulisse(0,20,Wifi.localIP (informazioni in locale)().Stringa a()); Visualizzazione.Coulisse(0,34,"MQTT: "); Visualizzazione.Coulisse(60,34,Stringa(cayCnt)); Visualizzazione.Coulisse(0,44,"LoRa: "); Visualizzazione.Coulisse(60,44,Stringa(loraCnt)); Visualizzazione.Coulisse(0,54,"ORA: "); Visualizzazione.Coulisse(60,54,Stringa(nowCnt)); Visualizzazione.Visualizzazione(); } Elaborare un messaggio da un client LoRaProcess a message from a LoRa client Vuoto leggereLoRa() { Int devnr; uint8_t Daniel[6]; uint8_t Canale; uint8_t digitare; uint8_t Len; uint8_t Dat; Boolean Output; Ottenere dati se disponibiliGet data if available Int dimensione pacchetto = Lora.parsePacket(); abbiamo ricevuto dati ? Se (dimensione pacchetto > 5) { #ifdef Eseguire il debug Seriale.println(getLocalTime (ora locale)()); Seriale.Stampare(" RX "); Seriale.Stampare(dimensione pacchetto); Seriale.println(" Byte"); Seriale.Stampare("ID dispositivo"); #endif prima leggere l'ID del dispositivo Per (uint8_t Ho.=0; Ho.<6;Ho.++){ Daniel[Ho.]=Lora.Leggere(); #ifdef Eseguire il debug Seriale.Printf("-%02x",Daniel[Ho.]); #endif } #ifdef Eseguire il debug Seriale.println(); #endif Calcolare il pacchetto residuo dimensione pacchetto -= 6; verificare se il dispositivo è registrato devnr = findDevice(Daniel); Se (devnr >= 0) { se sì, abbiamo impostato l'indicatore di data e ora per l'ultimo messaggio e leggere i dati Dispositivi[devnr].Ultima = getLocalTime (ora locale)(); writeConfiguration("/configurazione.csv"); Mentre (dimensione pacchetto > 0) { Numero di canale, numero di dispositivo, 16 e canale del dispositivo Canale = Lora.Leggere() + devnr*16; #ifdef Eseguire il debug Seriale.Printf("Canale: %02x",Canale); #endif tipo di canale digitare = Lora.Leggere(); #ifdef Eseguire il debug Seriale.Printf("Tipo: %02x",digitare); #endif determinare la lunghezza del pacchetto di dati e se il canale è un attuatore Output = False; Interruttore(digitare) { Caso LPP_DIGITAL_INPUT : Len = LPP_DIGITAL_INPUT_SIZE - 2; Pausa; Caso LPP_DIGITAL_OUTPUT : Len = LPP_DIGITAL_OUTPUT_SIZE - 2; Output = Vero; Pausa; Caso LPP_ANALOG_INPUT : Len = LPP_ANALOG_INPUT_SIZE - 2; Pausa; Caso LPP_ANALOG_OUTPUT : Len = LPP_ANALOG_OUTPUT_SIZE - 2; Output = Vero; Pausa; Caso LPP_LUMINOSITY : Len = LPP_LUMINOSITY_SIZE - 2; Pausa; Caso LPP_PRESENCE : Len = LPP_PRESENCE_SIZE - 2; Pausa; Caso LPP_TEMPERATURE : Len = LPP_TEMPERATURE_SIZE - 2; Pausa; Caso LPP_RELATIVE_HUMIDITY : Len = LPP_RELATIVE_HUMIDITY_SIZE - 2; Pausa; Caso LPP_ACCELEROMETER : Len = LPP_ACCELEROMETER_SIZE - 2; Pausa; Caso LPP_BAROMETRIC_PRESSURE : Len = LPP_BAROMETRIC_PRESSURE_SIZE - 2; Pausa; Caso LPP_GYROMETER : Len = LPP_GYROMETER_SIZE - 2; Pausa; Caso LPP_GPS : Len = LPP_GPS_SIZE - 2; Pausa; Predefinito: Len = 0; } se il canale non è un attuatore, reinsiamo il buffer dei messaggi su 1 in modo che i dati vengano inviati al server MQTT alla successiva opportunità Se (!Output) Messaggi[Canale].Nuovo =1; Messaggi[Canale].digitare = digitare; Pacchetto rimanente 2 in meno perché il canale e il tipo sono stati letti dimensione pacchetto -= 2; #ifdef Eseguire il debug Seriale.Stampare("Dati:"); #endif ora leggiamo i dati ricevuti con la lunghezza determinata Per (uint8_t Ho.=0; Ho.<Len; Ho.++) { Dat = Lora.Leggere(); per gli attuatori, non ricordiamo alcun dato Se (! Output) Messaggi[Canale].Dati[Ho.] = Dat; #ifdef Eseguire il debug Seriale.Printf("-%02x",Dat); #endif Ridurre il pacchetto rimanente di uno dimensione pacchetto --; } #ifdef Eseguire il debug Seriale.println(); #endif } Stato aggiornamento loraCnt++; loraUltimo = getLocalTime (ora locale)(); Visualizzazione(); } Altro { Il dispositivo non è registrato ricordiamo l'ID del dispositivo per visualizzarlo per la registrazione Per (uint8_t Ho. = 0; Ho.<6; Ho.++) Sconosciuto[Ho.] = Daniel[Ho.]; newGeraet = Vero; newGeraetType = 0; Dispositivo LoRa } Parte due Invia risposta al dispositivo LoRa Ritardo(100); Lora.beginPacket(); all'inizio, l'ID del dispositivo Lora.Scrivere(Daniel,6); controlliamo se abbiamo i dati di uscita per il dispositivo LoRa corrente Int devbase = devnr*16; Per (Int Ho. = devbase; Ho.<devbase+8; Ho.++) { a seconda del tipo di dati digitali o analogici Interruttore (Messaggi[Ho.].digitare) { Caso LPP_DIGITAL_OUTPUT : Lora.Scrivere(Ho.-devbase); Lora.Scrivere(Messaggi[Ho.].digitare); Lora.Scrivere(Messaggi[Ho.].Dati,1); #ifdef Eseguire il debug Seriale.println("Uscita digitale"); #endif Pausa; Caso LPP_ANALOG_OUTPUT : Lora.Scrivere(Ho.-devbase); Lora.Scrivere(Messaggi[Ho.].digitare); Lora.Scrivere(Messaggi[Ho.].Dati,2); #ifdef Eseguire il debug Seriale.println("Uscita analogica"); #endif Pausa; } } Int lstatus = Lora.endPacket(); #ifdef Eseguire il debug Seriale.Stampare("Stato di invio"); Seriale.println(lstatus); #endif } } Funzione di lettura della configurazione Vuoto readConfiguration(Const Char *Fn) { uint8_t Ho. = 0; Stringa Tmp; Char Hex[3]; Se (!SPIFFS.Esiste(Fn)) { non esiste ancora, quindi genera writeConfiguration(Fn); Ritorno; } file D = SPIFFS.Aperto(Fn, "r"); Se (!D) { Seriale.println(D("ERRORE:: SPIFFS Impossibile aprire la configurazione")); Ritorno; } Mentre (D.Disponibile() && (Ho.<MAXDISPOSITIVO)) { Tmp = D.ReadStringUntil (finestra in lettura)(','); Dispositivi[Ho.].Attivo = (Tmp == "1"); Tmp = D.ReadStringUntil (finestra in lettura)(','); Dispositivi[Ho.].Servizio = Tmp.ToInt(); Tmp = D.ReadStringUntil (finestra in lettura)(','); Per (uint8_t J=0; J<6; J++){ Hex[0]=Tmp[J*3]; Hex[1]=Tmp[J*3+1]; Hex[2]=0; Dispositivi[Ho.].Id[J]= (Byte) Strtol(Hex,Null,16); } Tmp = D.ReadStringUntil (finestra in lettura)(','); Dispositivi[Ho.].Nome = Tmp; Tmp = D.ReadStringUntil (finestra in lettura)(','); Dispositivi[Ho.].Ultima = Tmp; Ho.++; } } Vuoto Installazione() { Inizializzare l'archiviazione del dispositivo Per (uint8_t Ho. =0; Ho.<MAXDISPOSITIVO; Ho.++) Dispositivi[Ho.].Attivo = 0; Inizializzatore di visualizzazione OLED PinMode (Modalità pin)(16,Output); digitalWrite (Scrittura digitale)(16, Basso); Ritardo(50); digitalWrite (Scrittura digitale)(16, alto); Visualizzazione.Init(); Visualizzazione.flipScreenVerticale(); Visualizzazione.setFont (carattere di comando)(ArialMT_Plain_10); Visualizzazione.setTextAlignment (Allineamento di testo)(TEXT_ALIGN_LEFT); Avviare l'interfaccia seriale Seriale.Iniziare(115200); Mentre (!Seriale); Seriale.println("Avvia"); Syastem file Flash Se (SPIFFS.Iniziare(FORMAT_SPIFFS_IF_FAILED)) Seriale.println(D("SPIFFS caricato")); Leggere la configurazione readConfiguration("/configurazione.csv"); initMessageBuffer(); Inizializza SPI e LoRa Spi.Iniziare(5,19,27,18); Lora.setPins(Ss,Rst,DI0); Seriale.println("LoRa TRX"); Se (!Lora.Iniziare(Band)) { Seriale.println("Avvio LoRa non riuscita!"); Mentre (1); } Lora.enableCrc (attivato crisc)(); Seriale.println("LoRA Initial OK!"); Ritardo(2000); Connettersi al server Wi-Fi e MQTT Seriale.println("Connetti Wi-Fi"); Cayenne.Iniziare(MQTT_USER, MQTT_PASSWORD, MQTT_CLIENTID, WIFI_SSID, WIFI_PASSWORD); Seriale.Stampare("Indirizzo IP: "); Seriale.println(Wifi.localIP (informazioni in locale)()); Inizializza server Web Server.Attivato("/", handleRoot); Server.Iniziare(); Sincronizza orologio con il server di tempo configTime (momento di configurazione)(GMT_OFFSET_SEC, DAYLIGHT_OFFSET_SEC, NTP_SERVER); Tempo di corrente di uscita Seriale.println(getLocalTime (ora locale)()); } Vuoto Ciclo() { Visualizzazione(); Controllare L'interfaccia LoRa per i dati leggereLoRa(); comunicare con Cayenne MQTT Server Cayenne.Ciclo(1); Servire server Web Server.handleClient(); } Inviare i dati dal buffer dei messaggi al server MQTT CAYENNE_OUT_DEFAULT() { Boolean Output = False; Boolean dati inviati = False; #ifdef Eseguire il debug Seriale.println(getLocalTime (ora locale)()); Seriale.println("Cayenne inviare"); #endif Per (Int Ho. = 0; Ho.<CANALI MAXCANALI; Ho.++) { inviare solo nuovi messaggi Se (Messaggi[Ho.].Nuovo == 1) { #ifdef Eseguire il debug Seriale.Printf("Invia tipo MQTT %i'n",Messaggi[Ho.].digitare); #endif inviare dati a seconda del tipo Interruttore (Messaggi[Ho.].digitare) { Caso LPP_DIGITAL_INPUT : Cayenne.digitalSensorScrittura(Ho.,Messaggi[Ho.].Dati[0]); Pausa; Caso LPP_DIGITAL_OUTPUT : Output = Vero; Pausa; case LPP_ANALOG_INPUT : Cayenne.virtualWrite(i,(messages[i].daten[0]-256 - messages[i].data[1])/100,"analog_sensor",UNIT_UNDEFINED); pausa; pausa; Caso LPP_ANALOG_OUTPUT : Output = Vero; Pausa; Caso LPP_LUMINOSITY : Cayenne.luxWrite(Ho.,Messaggi[Ho.].Dati[0]*256 + Messaggi[Ho.].Dati[1]); Pausa; Caso LPP_PRESENCE : Cayenne.digitalSensorScrittura(Ho.,Messaggi[Ho.].Dati[0]); Pausa; Caso LPP_TEMPERATURE : Cayenne.celsiusScrittura(Ho.,(Messaggi[Ho.].Dati[0]*256 + Messaggi[Ho.].Dati[1])/10); Pausa; Caso LPP_RELATIVE_HUMIDITY : Cayenne.virtualWrite (Scrittura virtuale)(Ho.,Messaggi[Ho.].Dati[0]/2,TYPE_RELATIVE_HUMIDITY,UNIT_PERCENT); Pausa; Caso LPP_ACCELEROMETER : Cayenne.virtualWrite (Scrittura virtuale)(Ho.,(Messaggi[Ho.].Dati[0]*256 + Messaggi[Ho.].Dati[1])/1000,"gx","g"); Pausa; Caso LPP_BAROMETRIC_PRESSURE : Cayenne.hectoPascalWrite(Ho.,(Messaggi[Ho.].Dati[0]*256 + Messaggi[Ho.].Dati[1])/10); Pausa; caso LPP_GYROMETER : len - LPP_GYROMETER_SIZE - 2; pausa; caso LPP_GPS : len - LPP_GPS_SIZE - 2; pausa; } Se (!Output) { Messaggi[Ho.].Nuovo = 0; dati inviati = Vero; } } } Se (dati inviati) { Stato aggiornamento cayCnt++; cayUltimo = getLocalTime (ora locale)(); Visualizzazione(); } } CAYENNE_IN_DEFAULT() { uint8_t * Dati di proprietà; Int Val; Int Ch = Richiesta.Canale; #ifdef Eseguire il debug Seriale.println("Cayenne recive"); Seriale.Printf("MQTT Dati per il Canale %i : %s"n",Ch,Getvalue.asString (stringa asString)()); #endif Interruttore (Messaggi[Ch].digitare) { Caso LPP_DIGITAL_OUTPUT : Messaggi[Ch].Dati[0] = Getvalue.asInt (int)(); Messaggi[Ch].Nuovo = 1; Pausa; Caso LPP_ANALOG_OUTPUT : Val = Rotondo(Getvalue.asDouble (Doppio)()*100); Messaggi[Ch].Dati[0] = Val / 256; Messaggi[Ch].Dati[1] = Val % 256; Messaggi[Ch].Nuovo = 1; Pausa; } }
3 commenti
moi
für was genau ist der cayenne server zuständig?
kann ich auch einfach eine lokale ip eines mqtt servers eingeben und die pakete werden an diesen weiter geleitet?
Gerald Lechner
Hallo Marco
Ja du brauchst dafür einen zweiter ESP32 mit LoRa und im nächsten Teil folgt der notwendige Code.
Gruß Gerald
Marco
Hallo,
toller Artikel der Lust aufs Ausprobieren macht. Noch eine Frage. Ihr beschreibt hier das Gateway. Das “spricht” auf der einen Seite LoRa und auf der anderen Seite (via WLan) mit einem Netzwerk.
Ich bräuchte dann für die Kommunikation mittels LoRa dann noch einen zweiten ESP als Client, richtig? Könnt Ihr darüber vielleicht einen 4. Teil machen und erklären wie man dann damit ein Ende-Ende Szenario aufbaut?
Viele Grüße
Marco