Travailler avec le Cayenne Dashboard - passerelle comme point d'accès et client réseau (partie 5)

Dans Partie 3 nous avons construit une passerelle qui fonctionne en tant que client sur un réseau Wi-Fi. Il était donc nécessaire de stocker les données d’accès correspondantes en constantes dans la mémoire du programme. Étant donné que nous voulons également rendre la passerelle utilisable pour ESP-Now, il serait commode si la passerelle pouvait également fonctionner comme un point d’accès.

Maintenant, l’ESP32 peut le faire. Lorsque nous nous asseyons le mode WiFi sur WIFI_AP_STA l’ESP fonctionne à la fois comme un point d’accès et une station. Cependant, un seul canal peut être utilisé. La connexion au réseau de routeurs est une priorité pour la sélection des canaux. Cela signifie que le point d’accès utilise toujours le même canal que la connexion au réseau de routeurs.

Nous voulons maintenant utiliser ce mode double pour configurer notre passerelle via un navigateur. Les données d’accès nécessaires telles que SSID et mot de passe ainsi que les données d’accès à Cayenne simplement sur les chaînes vides. Si la passerelle est démarré, elle ne peut pas se connecter au réseau de routeurs parce que les informations d’identification sont manquantes. Une fois la tentative de connexion effectuée, le point d’accès est commencé avec le SSID MQTTGateway. L’adresse IP de la passerelle est toujours 192.168.4.1 dans ce réseau

Pour configurer la passerelle, nous connectons un ordinateur ou un smartphone sur ce réseau (pas de mot de passe) et commençons un navigateur avec l’adresse http://192.168.4.1 Nous devrions ensuite voir la page de configuration suivante.

Lorsque les informations d’identification sont enregistrées, la passerelle tente de se connecter au réseau de routeurs. Si la tentative de connexion est réussie, l’affichage affiche l’adresse IP avec laquelle la passerelle peut être atteinte dans le réseau de routeurs. 

Après une connexion au réseau de routeurs, vous obtenez toujours la liste des appareils enregistrés dans le navigateur. Vous pouvez accéder à la page de configuration et modifier les données d’accès via le chemin /conf. 

dessin:

 

/- La passerelle MQTT forme une interface entre les appareils LoRa ou les appareils ESP Nowe 
 et les tableaux de bord De Cayenne MQTT. Il fonctionne sur ESP32 avec LoRa et OLED display
 La configuration est effectuée par le navigateur
 */
#include <Spi.H (en)>
#include <Lora.H (en)>
#include "SSD1306.h"
#include<Arduino.H (en)>
#include <CayenneMQTTESP32.H (en)>
#include <CayenneLPP CayenneLPP.H (en)>
#include <Wifi.H (en)>
#include <Web.H (en)>
#include <Temps.H (en)>
#include "FS.h"
#include "SPIFFS.h"


Serveur NTP pour la synchronisation du temps
#define NTP_SERVER "de.pool.ntp.org"
#define GMT_OFFSET_SEC 3600
#define DAYLIGHT_OFFSET_SEC 0

Épingles pour la puce LoRa
#define Ss      18
#define Tvd     14
#define DI0     26
Fréquence de la puce LoRa
#define Bande    433175000

//
#define MAXCHANNELS (EN) 256 nombre maximum de canaux gérés
#define MAXDEVICE (EN) 32 nombre maximum d’appareils gérés MAXCHANNELS/MAXDEVICE - 8 résultats dans le nombre maximum de canaux par appareil

Format Flash Filesystem si ce n’est déjà fait
#define FORMAT_SPIFFS_IF_FAILED Vrai

#define Debug 1

Blocs de construction pour le serveur web
Const PROGMEM Char Char HTML_HEADER[] =
"Lt;! DOCTYPE HTML -GT;"
"Lt;html-gt;"
"Lt;head’gt;"
"Lt;meta name - "viewport" contenu 'largeur 'largeur de l’appareil, échelle initiale '1.0, échelle maximale '1.0, utilisateur-évolutif '0'gt;"gt;"
contenu /html de type «lt;meta http-equivMD»"type contenu";; charset-UTF-8"gt;"
"Lt;title’gt;MQTT Gateway’lt;/title’gt;"
"Lt;style’gt;"
"corps - couleur de fond: #d2f3eb; police-famille: Arial, Helvetica, Sans-Serif; Couleur: #000000;font-taille:12pt; }"
"th fond-couleur: #b6c0db; couleur: #050ed2;font-weight:lighter;font-size:10pt;
"table, e, td "border: 1px noir solide;"
".title .font-size:18pt;font-weight:bold;text-align:center; "
"Lt;/style’gt;";

Const PROGMEM Char Char HTML_HEADER_END[] = 
"Lt;/tête-gt;"
"lt;body’lt;lt;div style’margin-left:30px;' -gt;";

Const PROGMEM Char Char HTML_SCRIPT[] =
"Lt;script language"javascript"gt;"
"rechargement de la fonction()
"document.location"http://%s";"
"Lt;/script’gt;";

Const PROGMEM Char Char HTML_END_RELOAD[] =
"'lt;/div’gt;lt;lt;'lt;'lt;"javascript" 'gt;setTimeout(reload, 10000);'lt;/script 'gt;'lt;/body 'gt;"
"Lt;/html’gt;";
Const PROGMEM Char Char HTML_END[] =
"Lt;/corps 'lt;/html’gt;";
Const PROGMEM Char Char HTML_TAB_GERAETE[] =
"Lt;table style""width:100%""lt;tr’gt;lt;lt;lt;th style"width:20%"""""'gt;ID’lt;/th 'lt;'lt;'lth style'"width:10%""gt;No.lt;/th’gt;"
"Lt;th style""largeur:20%"gt;Channels’lt;/th’lt;'lt;lt;th style'"largeur:20%"gt;Name’lt;/th’gt;"
"Lt;th style""largeur:20%"gt;Recent Data’lt;/th’lt;'lt;'lt;lt;th style'"width:10%""gt;Action’lt;/th 'gt;/tr’gt;";
Const PROGMEM Char Char HTML_TAB_END[] =
"Lt;/table’gt;";
Const PROGMEM Char Char HTML_NEWDEVICE[] =
"Lt;div style""margin-top:20px;"'gt;%s Nom: 'lt;input type"texte" style '"largeur:200px" nom "devname"" maxlength""10" valeur ""gt; Nom de bouton de lt;"register" valeur "%s"-gt;Register’lt;/button’lt;/div’gt;";
Const PROGMEM Char Char HTML_TAB_ZEILE[] =
"lt;tr’gt;lt;td;td’td’t’lt;/td 'lt;td;td;td;%i’lt;/td 'lt;'lt;td;td’td’gt;%i 'lt;/td’lt;td;td;td;%s’lt ;//td’lt;lt;td;td’td’td’t’lt;/td 'lt;td;td;lt;lt;button name '"delete" value'"%i"gt;Delete’lt;/button’lt;/td’lt;/td’lt;/tr’gt;";
Const PROGMEM Char Char HTML_CONFIG[] = 
"Lt;form method"post"gt;lt;h1 'gt;Access data’lt;/h1'lt;'lt;'lt;com’gt;table’gt;"
"Lt;tr’gt;lt;td;WLAN SSID-lt;/td’lt;td;td;td;td;lt;'lt;'lt;'lt;'lt;'lt"text""ssid" value'"%s" size'50 maxlen'30/'gt;'lt;/td’lt;/td’lt;/td’lt;/td’lt;/td’lt;/td’lt;/td’lt;/td’lt;/td’lt;/td’lt;/td’lt;/td’lt;/td’lt;/td’lt;/td’lt;/td’lt;/td’lt;/td’lt;'lt;/td’lt;'lt;td;td;td;td;tt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;
"Lt;tr’gt;lt;td;Td’gt;WLAN Password’lt;/td’lt;td;td;td;lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;
"lt;tr’gt;lt;td;td’gt;Cayenne Username’lt;/td’lt;td;td;td;'lt;'lt;'lt;'lt;'lt;'lt;'lt"text"""name"mquser" value'"%s" size'50 maxlen'40/'lt;'lt;/td’lt;/td’lt;/td’lt;/td’lt;/td’lt;/td’lt;/td’lt;/td’lt;/td’lt;/td’lt;/td’lt;/td’lt;/td’lt;/td’lt;/td’lt;/td’lt;'lt;'lt;'tt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'
"lt;tr’gt;lt;td;td’gt;Cayenne Password’lt;/td’lt;td;td;td;lt;lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;/td’lt;"tt;'lt;'lt;'tt;td’lt;/td’lt;/td’lt;/td’lt;/td’lt;/td’lt;/td’lt;/td’lt;/td’lt;/td’lt;/td’lt;/td’lt;/td’lt;/td’lt;/td’lt;/td’lt;/td’lt;/td’lt;/td’lt;/td’lt;/td’lt;/td’lt;tt;tt;tt;tt;tt;tt;tt;tt;tt;tt;tt
"'lt;tr’gt;lt;td;td’gt;Cayenne Client Id’lt;/td’lt;td;td;td;lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'tt;'lt;'lt;'lt;tt;'lt;'lt;'tt;'tt;'lt;tt;tt;'tt;'lt;'lt;'lt;tt;tt;tt;tt;tt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'lt;'
"lt;tr’t;lt;td'''''''''''''''''est-à-lt'' et n’en a pas pour rien, la valeur de l’épargne et de l’achat de l’et;/let;/td’lt;/td’lt;/tr’gt;"
"Lt;/table 'gt;'lt;/form’lt;/body 'lt;/html’gt;";

Structures
Mémoire tampon d’actualités
Struct MSG_BUF {   uint8_t Type;   uint8_t Nouveau;   uint8_t Données[10];
};

Définition de l’appareil
Struct Appareil {   uint8_t Active;   uint8_t Service; 0-LoRa, 1-ESP-Maintenant   uint8_t Id[6];   String Nom;   String Dernière;
};

Variable globale
Accéder aux données, celles-ci peuvent être saisies via le serveur web
String wlanssid = "Lechner LAN";
String wlanpwd = "Guadalquivir2711";
String mqttuser (en) = "";
String mqttpwd = "";
String mqttid (en) = "";

Instance de serveur Web
Web Serveur(80);

Affichage OLED
SSD1306  Affichage(0x3c, 4, 15);

Tampon pour la mise en cache des messages par canal
MSG_BUF Messages[MAXCHANNELS (EN)];

Liste des appareils définis
Appareil Dispositifs[MAXDEVICE (EN)];

Id d’un appareil non enregistré
uint8_t Inconnu[6];
Drapeau toujours vrai quand un nouvel appareil est détecté
Boolean nouveauGeraet = Faux;
Type de nouvel appareil 0-L’Ra 1 'ESPNow
uint8_t nouveauGeraetType = 0;

Compteurs et activités Statut pour l’affichage
uint32_t loraCnt = 0; Nombre de messages LoRa reçus
String loraLast = ""; Date et heure du dernier message LoRa reçu
uint32_t maintenantCnt = 0; Nombre de messages ESP Now reçus
String maintenantLast = ""; Date et heure du dernier message LoRa reçu
uint32_t cayCnt = 0; Nombre de messages MQTT envoyés
String cayLast = ""; Date et heure du dernier message MQTT envoyé


Fonction retourne date et heure dans le format yyyy-mm-dd hh:mm:ss comme une chaîne
String getLocalTime (en)()
{   Char Char sttime sttime[20] = "";   Struct Tm timeinfo (en anglais);   Si (Wifi.Statut() == WL_CONNECTED) {     Si(!getLocalTime (en)(&timeinfo (en anglais))){       Série.println("Échec à obtenir du temps");       Retour sttime sttime;     }     Strftime Strftime Strftime Strf(sttime sttime, Sizeof(sttime sttime), "%Y-%m-%d %H:%M:%S", &timeinfo (en anglais));   }   Retour sttime sttime;
}

Funktion liefert eine 6-Byte Geräte-Id im format xx:xx:xx:xx:xx:xx als String
String Getid Getid(uint8_t Id[6])
{   String Stid;   Char Char Tmp[4];   Sprintf(Tmp,"%02x",Id[0]);   Stid=Tmp;   Pour (uint8_t J = 1; J<6; J++) {     Sprintf(Tmp,":%02x",Id[J]);     Stid = Stid += Tmp ;   }   Retour Stid;
}

prépare le tampon de message
définit tous les messages sur fait
Vide initMessageBuffer() {   Pour (Int Ⅰ. = 0;Ⅰ.<MAXCHANNELS (EN);Ⅰ.++) Messages[Ⅰ.].Nouveau = 0;
}

Fonction pour enregistrer la configuration
Vide writeConfiguration(Const Char Char *Fn) {   Fichier Q = SPIFFS SPIFFS.Ouvert(Fn, FILE_WRITE);   Si (!Q) {     Série.println(Q("ERREUR: SPIFFS ne peut pas enregistrer la configuration"));     Retour;   }   Pour (uint8_t Ⅰ. = 0; Ⅰ.<MAXDEVICE (EN); Ⅰ.++) {     Q.Imprimer(Dispositifs[Ⅰ.].Active);Q.Imprimer(",");     Q.Imprimer(Dispositifs[Ⅰ.].Service);Q.Imprimer(",");     Q.Imprimer(Getid Getid(Dispositifs[Ⅰ.].Id));Q.Imprimer(",");     Q.Imprimer(Dispositifs[Ⅰ.].Nom);Q.Imprimer(",");     Q.println(Dispositifs[Ⅰ.].Dernière);   }
}

Fonction pour stocker les données d’accès
Vide writingAccess(Const Char Char *Fn) {   Fichier Q = SPIFFS SPIFFS.Ouvert(Fn, FILE_WRITE);   Si (!Q) {     Série.println(Q("ERROR: SPIFFS ne peut pas stocker les informations d’identification"));     Retour;   }   Q.Imprimer("WLANSSID");Q.Imprimer(wlanssid);Q.Imprimer('n');   Q.Imprimer("WLANPWD");Q.Imprimer(wlanpwd);Q.Imprimer('n');   Q.Imprimer("MQTTUSER");Q.Imprimer(mqttuser (en));Q.Imprimer('n');   Q.Imprimer("MQTTPWD");Q.Imprimer(mqttpwd);Q.Imprimer('n');   Q.Imprimer("MQTTID");Q.Imprimer(mqttid (en));Q.Imprimer('n');    }

Fonction d’enregistrement d’un nouvel appareil
Vide geraetRegister (geraetRegister)() {   uint8_t Ⅰ. = 0;   entrée sans recherche   Tandis que ((Ⅰ.<MAXDEVICE (EN)) && Dispositifs[Ⅰ.].Active) Ⅰ.++;   il n’y a pas de nouvelle entrée, nous ne faisons rien   Si (Ⅰ. < MAXDEVICE (EN)) {     autrement enregistrer le nom geraet - nom inscrit      ou inconnu si aucun n’a été entré     Si (Serveur.hasArg("devname")) {       Dispositifs[Ⅰ.].Nom = Serveur.Mauvais("devname");     } Autre {       Dispositifs[Ⅰ.].Nom = "inconnu";     }     Pour (uint8_t J = 0; J<6; J++) Dispositifs[Ⅰ.].Id[J]=Inconnu[J];     Dispositifs[Ⅰ.].Active = 1;     Dispositifs[Ⅰ.].Service= nouveauGeraetType;     Dispositifs[Ⅰ.].Dernière = "";     writeConfiguration("/configuration.csv");     nouveauGeraet = Faux;   }
}

La page de configuration est affichée par le serveur web
Vide poignéeConfig(){   Char Char htmlbuf[1024];   Boolean Redémarrer = Faux;   Int Index;   le bouton mémoire a-t-il été appuyé ?   Si (Serveur.hasArg("sauver")) {     Données de la demande POST     wlanssid = Serveur.Mauvais("ssid");     si le SSID contient un espace, nous recevrons un     cela doit être changé en un espace pour l’enregistrement     wlanssid.Remplacer("+"," ");     wlanpwd = Serveur.Mauvais("pwd");     mqttuser (en) = Serveur.Mauvais("mquser");     mqttpwd = Serveur.Mauvais("mqpwd");     mqttid (en) = Serveur.Mauvais("mqid");     Série.println("Nouvelle configuration:");     Série.Imprimer("SSID: ");Série.println(wlanssid);     Série.Imprimer("Mot de passe: ");Série.println(wlanpwd);     Série.Imprimer("Utilisateur: ");Série.println(mqttuser (en));     Série.Imprimer("Mot de passe: ");Série.println(mqttpwd);     Série.Imprimer("ID: ");Série.println(mqttid (en));     Enregistrer la nouvelle configuration dans SPIFFS     writingAccess("/access.txt");     nous nous souvenons que la connexion WiFi doit être redémarrée     mais d’abord le serveur web doit livrer la page HTML     Redémarrer = Vrai;   }   Sortie de la page de configuration   nous formons des pointeurs à la mémoire interne des chaînes d’accès   de les utiliser pour sprintf et de commencer la connexion Wi-Fi et Cayenne   Char Char* txtSSID = const_cast<Char Char*>(wlanssid.c_str());   Char Char* txtPassword (txtPassword) = const_cast<Char Char*>(wlanpwd.c_str());      Char Char* txtUser (en) = const_cast<Char Char*>(mqttuser (en).c_str());   Char Char* txtPwd = const_cast<Char Char*>(mqttpwd.c_str());   Char Char* txtId = const_cast<Char Char*>(mqttid (en).c_str());   Envoyer la page HTML actuelle au navigateur   Serveur.setContentLength(CONTENT_LENGTH_UNKNOWN);   En-tête   Serveur.Envoyer(200, "texte/html",HTML_HEADER);   Serveur.envoyerContent(HTML_HEADER_END);   Le formulaire avec les champs d’entrée est rempli des valeurs actuelles   Sprintf(htmlbuf,HTML_CONFIG,txtSSID,txtPassword (txtPassword),txtUser (en),txtPwd,txtId);   et envoyé à la Browsewr   Serveur.envoyerContent(htmlbuf);   Serveur.envoyerContent(HTML_END);   Si (Redémarrer) {     Est-ce que l’ensemble du drapeau de redémarrage doit déconnecter la connexion WiFi et se reconnecter     à construire     Série.println("Redémarrer");     uint8_t Timeout = 0;     Série.println("Déconnecter");     Wifi.Débrancher();     Tandis que ((Wifi.Statut() == WL_CONNECTED) && (Timeout < 10))     {       Retard(1000);       Timeout++;     }     Série.println("Reconnect");     Wifi.Commencer(txtSSID,txtPassword (txtPassword));     Tandis que ((Wifi.Statut() != WL_CONNECTED) && (Timeout < 10))     {       Retard(1000);       Timeout++;     }     Série.Imprimer("Adresse IP: ");     Série.println(Wifi.localIP());     Si (Wifi.Statut() == WL_CONNECTED) {       le Neustrart a été un succès, la connexion à Cayenne doit également être reconstruite.       Série.println("Connexion Cayenne");       Cayenne.Commencer(txtUser (en), txtPwd, txtId);     Synchroniser l’horloge avec le serveur de temps     configTime(GMT_OFFSET_SEC, DAYLIGHT_OFFSET_SEC, NTP_SERVER);     Temps courant de sortie     Série.println(getLocalTime (en)());     }   }
}

La page de réinitialisation a été interrogée par le serveur web
Vide poignéeReset() {   nous réinitialiser les données d’accès    wlanssid= "";    wlanpwd = "";    mqttuser (en) = "";    mqttpwd="";    mqttid (en)="";    et afficher les données de configuration    que lorsque le bouton de la page de configuration est    enregistrer est cliqué, les données d’accès est     également supprimé dans SPIFFS.    poignéeConfig();
}

La page avec la liste de l’appareil a été interrogée par le serveur web
Vide poignéeWLANRequest(){   Char Char htmlbuf[512];   Char Char tmp1[20];   Char Char tmp2[20];   Char Char tmp3[20];   Int Index;   le bouton de suppression a-t-il cliqué ?   Si (Serveur.hasArg("supprimer")) {     Index = Serveur.Mauvais("supprimer").Toint();
#ifdef DEGUG DEGUG     Série.Printf("Supprimer l’appareil %i ",Index);     Série.println(Dispositifs[Index].Nom);
#endif     Dispositifs[Index].Active=0;     writeConfiguration("/configuration.csv");   }   le bouton Registre a-t-il été cliqué ?   Si (Serveur.hasArg("registre")) {     geraetRegister (geraetRegister)();   }   Envoyer la page HTML actuelle au navigateur   Serveur.setContentLength(CONTENT_LENGTH_UNKNOWN);   En-tête   Serveur.Envoyer(200, "texte/html",HTML_HEADER);   Adresse IP pour recharger le script   Wifi.localIP().Tostring().Tochararray Tochararray(tmp1,20);   Sprintf(htmlbuf,HTML_SCRIPT,tmp1);   Serveur.envoyerContent(htmlbuf);   Serveur.envoyerContent(HTML_HEADER_END);   Début de la forme   Serveur.envoyerContent("Lt;div class"title"gt;MQTT - Gateway-lt;/div’lt;lt;form method'"post"gt;");   Tableau d’appareils actifs   Serveur.envoyerContent(HTML_TAB_GERAETE);   Pour (uint8_t Ⅰ. = 0; Ⅰ.<MAXDEVICE (EN); Ⅰ.++) {      Si (Dispositifs[Ⅰ.].Active == 1) {        Getid Getid(Dispositifs[Ⅰ.].Id).Tochararray Tochararray(tmp1,20);       Dispositifs[Ⅰ.].Nom.Tochararray Tochararray(tmp2,20);       Dispositifs[Ⅰ.].Dernière.Tochararray Tochararray(tmp3,20);       Sprintf(htmlbuf,HTML_TAB_ZEILE,tmp1,Ⅰ.,Ⅰ.*8,Ⅰ.*8+7,tmp2,tmp3,Ⅰ.);       Serveur.envoyerContent(htmlbuf);     }   }   Serveur.envoyerContent(HTML_TAB_END);   Si un nouvel appareil est trouvé, son ID et un champ d’entrée pour le nom de la   et un bouton pour enregistrer le nouvel appareil s’affiche   Si (nouveauGeraet) {     Getid Getid(Inconnu).Tochararray Tochararray(tmp1,20);     Sprintf(htmlbuf,HTML_NEWDEVICE,tmp1,tmp1);     Serveur.envoyerContent(htmlbuf);   }   Serveur.envoyerContent(HTML_END_RELOAD);
}

Fonction de service serveur Web pour l’annuaire racine
Vide handleRoot() {   Si (Wifi.Statut() != WL_CONNECTED) {     si nous n’avons pas de connexion au réseau de routeurs     la page de configuration est affichée afin que les données d’accès puissent être saisies     poignéeConfig();   } Autre {     poignéeWLANRequest();   }
}


Fonction pour trouver un appareil dans la liste des appareils
Indice de rendement de l’appareil ou -1 s’il n’a pas été trouvé
Int findDevice(uint8_t Dev[6]) {   uint8_t J;   uint8_t Ⅰ. = 0;   Boolean Trouvé = Faux;   Jeu {     J = 0;     Si (Dispositifs[Ⅰ.].Active == 0) {       Ⅰ.++;     } Autre {       Tandis que ((J < 6) && (Dev[J] == Dispositifs[Ⅰ.].Id[J])) {J++;}       Trouvé = (J == 6);       Si (!Trouvé) Ⅰ.++;      }    } Tandis que ((Ⅰ.<MAXDEVICE (EN)) && (!Trouvé));   Si (Trouvé) {Retour Ⅰ.;} Autre {Retour -1;}
}

Fonction pour afficher l’état sur l’écran OLED
Vide Affichage() {   Affichage.Clair();   Affichage.Cordon(0,0,"Porte d’entrée MQTT");   Affichage.Cordon(0,10,getLocalTime (en)());   Affichage.Cordon(0,20,Wifi.localIP().Tostring());   Affichage.Cordon(0,34,"MQTT: ");   Affichage.Cordon(60,34,String(cayCnt));   Affichage.Cordon(0,44,"LoRa: ");   Affichage.Cordon(60,44,String(loraCnt));   Affichage.Cordon(0,54,"MAINTENANT: ");   Affichage.Cordon(60,54,String(maintenantCnt));   Affichage.Affichage();
}


Traiter un message d’un client LoRa
Vide lireLoRa() {   Int devnr (en);   uint8_t Daniel[6];   uint8_t Canal;   uint8_t Type;   uint8_t Len;   uint8_t Dat;   Boolean Sortie;   Obtenez des données s’il est disponible   Int packetSize = Lora.parsePacket (en)();   avons-nous reçu des données ?   Si (packetSize > 5) {
#ifdef Debug         Série.println(getLocalTime (en)());     Série.Imprimer(" RX ");     Série.Imprimer(packetSize);     Série.println("Octets");     Série.Imprimer("Id de l’appareil");
#endif      première lecture de l’id appareil        Pour (uint8_t Ⅰ.=0; Ⅰ.<6;Ⅰ.++){       Daniel[Ⅰ.]=Lora.Lire();
#ifdef Debug       Série.Printf("-%02x",Daniel[Ⅰ.]);
#endif     }
#ifdef Debug     Série.println();
#endif     Calculer l’emballage résiduel     packetSize -= 6;     vérifier si l’appareil est enregistré     devnr (en) = findDevice(Daniel);     Si (devnr (en) >= 0)  {       si oui, nous définissons le délai pour le dernier message et       lire les données       Dispositifs[devnr (en)].Dernière = getLocalTime (en)();       writeConfiguration("/configuration.csv");       Tandis que (packetSize > 0) {         Numéro de canal ' numéro d’appareil ' 16 ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' '         Canal = Lora.Lire() + devnr (en)*16;
#ifdef Debug         Série.Printf("Canal: %02x",Canal);
#endif         type de canal         Type = Lora.Lire();
#ifdef Debug         Série.Printf("Type: %02x",Type);
#endif         déterminer la longueur du paquet de données et si le canal est un actionneur         Sortie = Faux;         Interrupteur(Type) {           Cas LPP_DIGITAL_INPUT : Len = LPP_DIGITAL_INPUT_SIZE - 2; Pause;           Cas LPP_DIGITAL_OUTPUT : Len = LPP_DIGITAL_OUTPUT_SIZE - 2; Sortie = Vrai; Pause;           Cas LPP_ANALOG_INPUT : Len = LPP_ANALOG_INPUT_SIZE - 2; Pause;           Cas LPP_ANALOG_OUTPUT : Len = LPP_ANALOG_OUTPUT_SIZE - 2; Sortie = Vrai; Pause;           Cas LPP_LUMINOSITY : Len = LPP_LUMINOSITY_SIZE - 2; Pause;           Cas LPP_PRESENCE : Len = LPP_PRESENCE_SIZE - 2; Pause;           Cas LPP_TEMPERATURE : Len = LPP_TEMPERATURE_SIZE - 2; Pause;           Cas LPP_RELATIVE_HUMIDITY : Len = LPP_RELATIVE_HUMIDITY_SIZE - 2; Pause;           Cas LPP_ACCELEROMETER : Len = LPP_ACCELEROMETER_SIZE - 2; Pause;           Cas LPP_BAROMETRIC_PRESSURE : Len = LPP_BAROMETRIC_PRESSURE_SIZE - 2; Pause;           Cas LPP_GYROMETER : Len = LPP_GYROMETER_SIZE - 2; Pause;           Cas LPP_GPS : Len = LPP_GPS_SIZE - 2; Pause;           Par défaut: Len =  0;         }         si le canal n’est pas un actionneur, nous réinitialisons le mémoire tampon de message à 1         de sorte que les données sont envoyées au serveur MQTT à la prochaine occasion         Si (!Sortie) Messages[Canal].Nouveau =1;         Messages[Canal].Type = Type;         Paquet restant 2 de moins parce que le canal et le type ont été lus         packetSize -= 2;
#ifdef Debug         Série.Imprimer("Données:");
#endif         maintenant nous lisons les données reçues avec la longueur déterminée         Pour (uint8_t Ⅰ.=0; Ⅰ.<Len; Ⅰ.++) {           Dat = Lora.Lire();           pour les actionneurs, nous ne nous souvenons d’aucune donnée           Si (! Sortie) Messages[Canal].Données[Ⅰ.] = Dat;
#ifdef Debug           Série.Printf("-%02x",Dat);
#endif           Réduire le reste du paquet d’un           packetSize --;         }
#ifdef Debug         Série.println();
#endif       }       Mise à jour de l’état       loraCnt++;       loraLast = getLocalTime (en)();       Affichage();     } Autre {       L’appareil n’est pas enregistré        nous nous souvenons de l’id de l’appareil pour l’afficher pour l’enregistrement       Pour (uint8_t Ⅰ. = 0; Ⅰ.<6; Ⅰ.++) Inconnu[Ⅰ.] = Daniel[Ⅰ.];       nouveauGeraet = Vrai;       nouveauGeraetType = 0; Dispositif LoRa     }     Partie deux Envoyer la réponse à l’appareil LoRa     Retard(100);     Lora.beginPacket();     au début, l’id de l’appareil     Lora.Écrire(Daniel,6);     nous vérifions si nous avons des données de sortie pour l’appareil LoRa actuel     Int devbase = devnr (en)*16;     Pour (Int Ⅰ. = devbase; Ⅰ.<devbase+8; Ⅰ.++) {       selon le type de données numériques ou analogiques       Interrupteur (Messages[Ⅰ.].Type) {           Cas LPP_DIGITAL_OUTPUT : Lora.Écrire(Ⅰ.-devbase);             Lora.Écrire(Messages[Ⅰ.].Type);             Lora.Écrire(Messages[Ⅰ.].Données,1);
#ifdef Debug             Série.println("Sortie numérique");
#endif             Pause;           Cas LPP_ANALOG_OUTPUT :  Lora.Écrire(Ⅰ.-devbase);             Lora.Écrire(Messages[Ⅰ.].Type);             Lora.Écrire(Messages[Ⅰ.].Données,2);
#ifdef Debug             Série.println("Sortie analogique");
#endif             Pause;       }     }          Int lstatus lstatus = Lora.endPacket();
#ifdef Debug     Série.Imprimer("Envoyer le statut ");     Série.println(lstatus lstatus);
#endif   }
}

Fonction pour lire la configuration
Vide lireConfiguration(Const Char Char *Fn) {   uint8_t Ⅰ. = 0;   String Tmp;   Char Char Hexagonale[3];   Si (!SPIFFS SPIFFS.Existe(Fn)) {     n’existe pas encore, puis générer     writeConfiguration(Fn);     Retour;   }   Fichier Q = SPIFFS SPIFFS.Ouvert(Fn, "r");   Si (!Q) {     Série.println(Q("ERREUR:: SPIFFS ne peut pas ouvrir la configuration"));     Retour;   }   Tandis que (Q.Disponible() && (Ⅰ.<MAXDEVICE (EN))) {     Tmp = Q.lireStringUntil(',');     Dispositifs[Ⅰ.].Active = (Tmp == "1");     Tmp = Q.lireStringUntil(',');     Dispositifs[Ⅰ.].Service = Tmp.Toint();     Tmp = Q.lireStringUntil(',');     Pour (uint8_t J=0; J<6; J++){       Hexagonale[0]=Tmp[J*3];       Hexagonale[1]=Tmp[J*3+1];       Hexagonale[2]=0;       Dispositifs[Ⅰ.].Id[J]= (Octet) strtol(Hexagonale,Null,16);     }     Tmp = Q.lireStringUntil(',');     Dispositifs[Ⅰ.].Nom = Tmp;     Tmp = Q.lireStringUntil(',');     Dispositifs[Ⅰ.].Dernière = Tmp;     Ⅰ.++;   }    }
Fonction pour la lecture des données d’accès
Vide lireAccess(Const Char Char *Fn) {   uint8_t Ⅰ. = 0;   String Clé;   String Val;   Char Char Hexagonale[3];   Si (!SPIFFS SPIFFS.Existe(Fn)) {     n’existe pas encore, puis générer     writingAccess(Fn);     Retour;   }   Fichier Q = SPIFFS SPIFFS.Ouvert(Fn, "r");   Si (!Q) {     Série.println(Q("ERROR:: SPIFFS ne peut pas ouvrir les informations d’identification"));     Retour;   }   Tandis que (Q.Disponible() && (Ⅰ.<MAXDEVICE (EN))) {     Clé = Q.lireStringUntil('=');     Val = Q.lireStringUntil('n');     Si (Clé == "WLANSSID") wlanssid = Val;     Si (Clé == "WLANPWD") wlanpwd = Val;      Si (Clé == "MQTTUSER") mqttuser (en) = Val;      Si (Clé == "MQTTPWD") mqttpwd = Val;      Si (Clé == "MQTTID") mqttid (en) = Val;    }    }

Vide Configuration() {   Initialiser le stockage de l’appareil   Pour (uint8_t Ⅰ. =0; Ⅰ.<MAXDEVICE (EN); Ⅰ.++) Dispositifs[Ⅰ.].Active = 0;   OLED Display Initialize   pinMode(16,Sortie);   digitalWrite (en)(16, Faible);   Retard(50);    digitalWrite (en)(16, Haute);   Affichage.Init();   Affichage.flipScreenVertically();   Affichage.setFont(ArialMT_Plain_10);   Affichage.setTextAlignment(TEXT_ALIGN_LEFT);   Démarrer l’interface sérielle   Série.Commencer(115200);   Tandis que (!Série);    Série.println("Démarrer");   Système de fichiers Flash   Si (SPIFFS SPIFFS.Commencer(FORMAT_SPIFFS_IF_FAILED)) Série.println(Q("SPIFFS chargé"));   Lire dans les données de configuration et d’accès   lireConfiguration("/configuration.csv");   lireAccess("/access.txt");   initMessageBuffer();   Initialiser SPI et LoRa   Spi.Commencer(5,19,27,18);   Lora.setPins setPins setPins setPin(Ss,Tvd,DI0);   Série.println("LoRa TRX");   Si (!Lora.Commencer(Bande)) {     Série.println("Démarrer LoRa a échoué!");     Tandis que (1);   }   Lora.enableCrc();   Série.println("LoRa initial OK!");   Retard(2000);   Sortie des données d’accès lus pour le contrôle   Série.Imprimer("SSID: ");Série.println(wlanssid);   Série.Imprimer("Mot de passe: ");Série.println(wlanpwd);   Série.Imprimer("Utilisateur: ");Série.println(mqttuser (en));   Série.Imprimer("Mot de passe: ");Série.println(mqttpwd);   Série.Imprimer("ID: ");Série.println(mqttid (en));   Connectez-vous au serveur Wi-Fi et MQTT   Série.println("Connect Wi-Fi");   nous utilisons l’ESP32 comme poin d’accès, mais aussi comme un client dans le réseau de routeurs   Wifi.Mode(WIFI_AP_STA);   nous avons besoin de pointeurs à la mémoire de caractère dans les cordes   Char Char* txtSSID = const_cast<Char Char*>(wlanssid.c_str());   Char Char* txtPassword (txtPassword) = const_cast<Char Char*>(wlanpwd.c_str());      Char Char* txtUser (en) = const_cast<Char Char*>(mqttuser (en).c_str());   Char Char* txtPwd = const_cast<Char Char*>(mqttpwd.c_str());   Char Char* txtId = const_cast<Char Char*>(mqttid (en).c_str());   Wifi.Commencer(txtSSID, txtPassword (txtPassword));   Connexion au réseau de routeurs   uint8_t Timeout = 0;   Tandis que ((Wifi.Statut() != WL_CONNECTED) && (Timeout<10)) {     Timeout++;     Retard(1000);   }   nous attendons un maximum de 10 secondes jusqu’à ce que la connexion soit en place   Quelle que soit la connexion au réseau de routeurs, nous commençons l’AccessPoint   cela permet la configuration via un navigateur, si nous utilisons ce    Connectez-vous à AccessPoint   Wifi.softAP (softAP)("MQTTGateway");   Si (Wifi.Statut() == WL_CONNECTED) {     Si la connexion au réseau de routeurs a été un succès, nous commençons MQTT à Cayenne     et synchroniser l’horloge interne avec le serveur de temps     Série.Imprimer("Adresse IP: ");     Série.println(Wifi.localIP());     Cayenne.Commencer(txtUser (en), txtPwd, txtId);     Série.println("Cayenne Connection Made");     Synchroniser l’horloge avec le serveur de temps     configTime(GMT_OFFSET_SEC, DAYLIGHT_OFFSET_SEC, NTP_SERVER);     Temps courant de sortie     Série.println(getLocalTime (en)());   }   Initialiser le serveur Web   Serveur.Sur("/", handleRoot);   Serveur.Sur("/conf",poignéeConfig);   Serveur.Sur("/réinitialisation",poignéeReset);   Serveur.Commencer();   Série.println("*********************************************");


}


Vide Boucle() {   Affichage();   Si (Wifi.Statut() == WL_CONNECTED) {     Vérifiez LoRa Interface pour les données     lireLoRa();     communiquer avec Cayenne MQTT Server     Cayenne.Boucle(1);   }   Serveur Web de service   Serveur.handleClient();

}

Envoyer des données à partir du tampon de message au serveur MQTT
CAYENNE_OUT_DEFAULT()
{   Boolean Sortie = Faux;   Boolean sentData = Faux;
#ifdef Debug   Série.println(getLocalTime (en)());   Série.println("Cayenne envoyer");
#endif   Pour (Int Ⅰ. = 0; Ⅰ.<MAXCHANNELS (EN); Ⅰ.++) {     envoyer uniquement de nouveaux messages     Si (Messages[Ⅰ.].Nouveau == 1) {
#ifdef Debug       Série.Printf("Envoyer MQTT Type %i’n",Messages[Ⅰ.].Type);
#endif       envoyer des données selon le type       Interrupteur (Messages[Ⅰ.].Type) {           Cas LPP_DIGITAL_INPUT : Cayenne.digitalSensorWrite(Ⅰ.,Messages[Ⅰ.].Données[0]); Pause;           Cas LPP_DIGITAL_OUTPUT : Sortie = Vrai; Pause;           cas LPP_ANALOG_INPUT : Cayenne.virtualWrite (i,(messages[i].daten[0] -256 - messages[i].data[1])/100", analog_sensor", UNIT_UNDEFINED); pause; pause;           Cas LPP_ANALOG_OUTPUT : Sortie = Vrai; Pause;           Cas LPP_LUMINOSITY : Cayenne.luxWrite (luxWrite)(,Messages[].Daten[0]*256 + Messages[].Daten[1]); Pause;           Cas LPP_PRESENCE : Cayenne.digitalSensorWrite(,Messages[].Daten[0]); Pause;           Cas LPP_TEMPERATURE : Cayenne.CelsiusWrite (en)(,(Messages[].Daten[0]*256 + Messages[].Daten[1])/10); Pause;           Cas LPP_RELATIVE_HUMIDITY : Cayenne.virtualWrite (en)(,Messages[].Daten[0]/2,TYPE_RELATIVE_HUMIDITY,UNIT_PERCENT); Pause;           Cas LPP_ACCELEROMETER : Cayenne.virtualWrite (en)(,(Messages[].Daten[0]*256 + Messages[].Daten[1])/1000,"gx","g"); Pause;           Cas LPP_BAROMETRIC_PRESSURE : Cayenne.hectoPascalWrite(,(Messages[].Daten[0]*256 + Messages[].Daten[1])/10); Pause;           cas LPP_GYROMETER : len LPP_GYROMETER_SIZE - 2; pause;           cas LPP_GPS : len LPP_GPS_SIZE - 2; pause;       }       Si (!Sortie) {         Messages[].Neu = 0;         sentData = Vrai;       }            }   }   Si (sentData) {     Statut aktualisieren     cayCnt++;     cayLast = getLocalTime (en)();     Anzeige();   }

}

CAYENNE_IN_DEFAULT()
{   uint8_t * Pdata;   Int Val;   Int Ch = Demande.Canal;
#ifdef Debug   Série.println("Cayenne recive");   Série.Printf("MQTT Daten f’r Kanal %i -%s’n",Ch,Getvalue.asString());
#endif   Interrupteur (Messages[Ch].Typ) {       Cas LPP_DIGITAL_OUTPUT : Messages[Ch].Daten[0] = Getvalue.asInt();         Messages[Ch].Neu = 1;         Pause;       Cas LPP_ANALOG_OUTPUT :  Val = Rond(Getvalue.asDouble()*100);         Messages[Ch].Daten[0] = Val / 256;         Messages[Ch].Daten[1] = Val % 256;         Messages[Ch].Neu = 1;         Pause;   }   CAYENNE_LOG("Channel %u, valeur %s", Demande.Canal, Getvalue.asString());   Message de processus ici. S’il y a une erreur définissez un message d’erreur à l’aide de getValue.setError(), par exemple getValue.setError (« message d’erreur »);    }

 

Viel Spa beim Testen.

Esp-32Projekte für fortgeschrittene

Laisser un commentaire

Tous les commentaires sont modérés avant d'être publiés

Messages de blogs recommandés

  1. Installez maintenant ESP32 via l'administrateur de la carte
  2. Lüftersteuerung Raspberry Pi
  3. Arduino IDE - Programmieren für Einsteiger - Teil 1
  4. ESP32 - das Multitalent
  5. OTA-Over the Air-ESP Programmation par WiFi

Produits recommandés