Lavorare con il cruscotto di Cayenne - Gateway da LoRa a MQTT (parte 3)

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;   }    }

 

 

DisplaysEsp-32Projekte für fortgeschrittene

3 Kommentare

moi

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

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

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

Einen Kommentar hinterlassen

Alle Kommentare werden vor der Veröffentlichung moderiert

Post di blog consigliati

  1. Installa ESP32 ora dal gestore del consiglio di amministrazione
  2. Lüftersteuerung Raspberry Pi
  3. Arduino IDE - Programmieren für Einsteiger - Teil 1
  4. ESP32 - das Multitalent
  5. OTA - Over the Air - Programmazione ESP tramite WLAN

Prodotti consigliati