Sensoren durch Verwendung von ESP-NOW über sehr lange Zeit mit Batterie versorgen

Ein Temperatur-, Luftfeuchtigkeit- und Luftdruck-Sensor sollte mit Batterie betrieben werden und alle fünf Minuten seine Messwerte über WLAN senden. Die Daten sollen dann über eine Webseite abgerufen werden können.

Konzept

Um diese Aufgabenstellung zu lösen ist es notwendig, den Energiebedarf des Sensors möglichst klein zu halten und es wird ein Webserver benötigt, der die Daten sammelt und bei Bedarf als HTML-Dokument liefert. Als Controller für den Sensor sollte ein D1-Mini mit ESP 8266 und als Sensor ein BME280 verwendet werden. Durch entsprechende Änderungen im Sketch kann auch  ein beliebiger Anderer Sensor verwendet werden. In der Pause zwischen den Messungen soll der Controller in den Tiefschlaf-Modus versetzt werden, um den Stromverbrauch zu mindern. D1 Mini plus BME280 brauchen in diesem Zustand gemeinsam 35 µA. Zum Senden der Daten über WLAN benötigt der D1 Mini etwa 80 mA. Es ist daher notwendig die Zeit zum Übertragen der Daten möglichst kurz zu halten. Die Netzwerk-Protokolle TCP oder UDP sind dafür nicht so gut geeignet, da nach dem Tiefschlaf-Modus erst eine Verbindung mit dem Netzwerk hergestellt werden muss, ehe die Daten gesendet werden können. So ein Verbindungsaufbau kann je nach Netzwerk bis zu zehn Sekunden dauern. In einem Netzwerk können aber auch Datenpakete, die mit der MAC-Adresse des Empfängers versehen sind, ohne bestehende Verbindung gesendet werden. Diese Pakete können allerdings nur im selben Netzwerk empfangen werden. Sie können nicht über einen Router in andere Netzwerke übermittelt werden. Der Hersteller des ESP8266, die Firma Espressif, hat dafür ein eigenes Protokoll „ESP-NOW“ entwickelt. Wird dieses Protokoll verwendet, reduziert sich die Zeit zum Messen und Übertragen auf 0,55 Sekunden.
Das bedeutet eine Messung benötigt 0,55s x 77mA = 42,35mAs. In der Pause zwischen den Messungen im Tiefschlaf-Modus werden 299,45s x 0.035mA = 10,48mAs benötigt. Ein ganzer Messzyklus benötigt demnach 42,35mAs + 10,48mAs = 52,83mAs. An einem Tag werden 288 Messungen durchgeführt mit einem gesamten Energiebedarf von 288 x 52,83mAs = 15.215,04mAs oder 4,23 mAh. Es wird ein Batteriehalter für 4 AA-Mignon Batterien oder Akkus verwendet. Beim geringen Stromverbrauch kann man den Sensor etwa ein Jahr betreiben.

Als Server wird ein ESP32-D1-Mini verwendet, der über ein USB-Netzteil betrieben wird. Dieser Server muss dauernd im Netzwerk zugreifbar sein. Der Sensor sendet die Messwerte alle fünf Minuten über ESP-NOW an den Server, der die Messwerte speichert und auf Anfrage als HTTP Seite im Netzwerk zur Verfügung stellt. Der Server muss daher auch eine TCP-Verbindung zum Netzwerk haben. Damit ESP-NOW zusammen mit WiFi genutzt werden kann, muss das WiFi Netzwerk den Kanal 1 benutzen!

Benötigte Hardware

Anzahl Bauteil Anmerkung

1

D1-Mini

 

1

Sensor BME-280

 

1

Batteriehalter

 

4

Batterien AA 1.5V oder Akku 1.2V

 

1

Federleiste 4-polig

 

1

Stiftleiste 2-polig

 

1

Jumper

1

Widerstand 470 kOhm

1

Lochrasterplatte 40 x 60 mm

 

1

ESP32 Modul

 

 

Schaltung

Der Aufbau kann zum Beispiel auf einer Lochrasterplatte 4 x 6 cm erfolgen. Die folgende Abbildung zeigt die Verdrahtung. Als Sockel für den D1-Mini können die dem Modul beiliegenden Federleisten verwendet werden. Für den Reset-Jumper braucht man eine 2-polige Stiftleiste plus Jumper. Für den BME280 eine 4-polige Federleiste. Die Leitungen zum Batteriehalter kann man direkt anlöten oder eine zweipolige Schraubklemme verwenden.


Die Abbildung zeigt die Bestückung und die Verdrahtung auf der Unterseite

Für den Server wird nur der ESP32-D1-Mini ohne irgendwelche äußere Beschaltung verwendet. Es müssen auch die beiliegenden Kontaktleisten nicht bestückt werden. Nur ein USB-Netzteil ist erforderlich.

Software

Damit der Sketch kompiliert werden kann, muss die Arduino IDE entsprechend vorbereitet werden. Die Arduino IDE unterstützt standardmäßig eine große Anzahl von Boards mit unterschiedlichen Mikrocontrollern, nicht aber den ESP8266 und auch nicht den ESP32. Damit man Programme für diese Controller erstellen und hochladen kann, muss daher je ein Softwarepaket für die Unterstützung installiert werden.

Zuerst müssen Sie der Arduino-IDE mitteilen, wo sie die zusätzlich benötigten Daten findet. Dazu öffnen Sie im Menü Datei den Punkt Voreinstellungen. Im Voreinstellungs-Fenster gibt es das Eingabefeld mit der Bezeichnung „Zusätzliche Boardverwalter URLs“. Wenn Sie auf das Ikon rechts neben dem Eingabefeld klicken, öffnet sich ein Fenster in dem Sie die URL https://arduino.esp8266.com/stable/package_esp8266com_index.json für den ESP8266 und
https://dl.espressif.com/dl/package_esp32_index.json eingeben können.

Nun wählen Sie in der Arduino IDE unter Werkzeug → Board die Boardverwaltung.

Es öffnet sich ein Fenster, in dem alle zur Verfügung stehenden Pakete aufgelistet werden. Um die Liste einzugrenzen, gibt man im Suchfeld „esp“ ein. Dann erhält man nur noch einen Eintrag in der Liste. Installieren Sie die Pakete „esp32“ und „esp8266“.


Für den Sensor BME280 benötigen Sie eine Bibliothek, die über die Arduino Bibliotheksverwaltung installiert werden kann. Das ist die Bibliothek „BlueDot BME280“.

 

Wenn alle Bibliotheken installiert sind, kann der Sketch kompiliert und auf die Hardware hochgeladen werden. Achtung! Zum Hochladen muss der Jumper zwischen D0 und RST entfernt werden.

Der Sketch für den Sensor

 

#include <Wire.h>
#include <BlueDot_BME280.h>
#include <ESP8266WiFi.h>
#include <ArduinoJson.h>
//library for ESP Now
#include <espnow.h>

//SSID of the gateway
#define GW_SSID "WebhookGateway"

//series resistor for supply voltage measurement
#define RESISTOR 470 //for battery = 470 for akku loader = 130
//Switch debug messages on
#define DEBUG 1

#define SEND_TIMEOUT 2000  // 2 seconds timeout
#define RECON D5 // Pin for reconnect Button
//Instance for sensor
BlueDot_BME280 bme;

//data strukture to save gateways MAC address
struct MEMORYDATA {
  uint32_t crc32; //checksum
  uint8_t mac[6];
};

//global variables
uint32_t ms; //timestamp to calculate process time
bool callbackCalled =false; //flag for data sent
MEMORYDATA statinfo; //memory for MAC address

//Callback function will be called if data has been sent to the gateway
void sendData(uint8_t* smac, uint8_t sendStatus) {
  if (DEBUG) {
    Serial.print("send_cb, Status = "); Serial.print(sendStatus); 
    Serial.print(", an MAC: "); 
    char macString[50] = {0};
    sprintf(macString,"%02X:%02X:%02X:%02X:%02X:%02X", statinfo.mac[0], statinfo.mac[1], statinfo.mac[2], statinfo.mac[3], statinfo.mac[4], statinfo.mac[5]);
    Serial.println(macString);
  }
  callbackCalled = true;
}

//function to calculate checksum
uint32_t calculateCRC32(const uint8_t *data, size_t length)
{
  uint32_t crc = 0xffffffff;
  while (length--) {
    uint8_t c = *data++;
    for (uint32_t i = 0x80; i > 0; i >>= 1) {
      bool bit = crc & 0x80000000;
      if (c & i) {
        bit = !bit;
      }
      crc <<= 1;
      if (bit) {
        crc ^= 0x04c11db7;
      }
    }
  }
  return crc;
}

//Save gateways MAC address and checksum in RTC memory
void UpdateRtcMemory() {
    uint32_t crcOfData = calculateCRC32(((uint8_t*) &statinfo) + 4, sizeof(statinfo) - 4);
    statinfo.crc32 = crcOfData;
    ESP.rtcUserMemoryWrite(0,(uint32_t*) &statinfo, sizeof(statinfo));
}

//Search for gateways access point
boolean ScanForSlave() {
  bool slaveFound = false;
  int8_t scanResults = WiFi.scanNetworks();
  if (DEBUG) Serial.println("Scan done");
  if (scanResults == 0) {
    if (DEBUG) Serial.println("No accesspoint found");
  } else {
    if (DEBUG) {
      Serial.print("Gefunden "); 
      Serial.print(scanResults); 
      Serial.println(" Netze ");
    }
    for (int i = 0; i < scanResults; ++i) {
      String SSID = WiFi.SSID(i);
      int32_t RSSI = WiFi.RSSI(i);
      int32_t chl = WiFi.channel(i);
      String BSSIDstr = WiFi.BSSIDstr(i);
      if (DEBUG) {
        // display what we found
        Serial.print(i + 1);
        Serial.print(": ");
        Serial.print(SSID);
        Serial.print(" /");
        Serial.print(chl);
        Serial.print(" (");
        Serial.print(RSSI);
        Serial.print(")");
        Serial.println("");
      }
      delay(10);
      // check if we have the gateway
      if (SSID == GW_SSID) {
        if (DEBUG) {
          Serial.println("Network found");
          Serial.print(i + 1); Serial.print(": "); 
          Serial.print(SSID); Serial.print(" ["); 
          Serial.print(BSSIDstr); Serial.print("]"); 
          Serial.print(" ("); Serial.print(RSSI); 
          Serial.print(")"); Serial.println("");
        }
        int mac[6];
        // Save gateways MAC address
        if ( 6 == sscanf(BSSIDstr.c_str(), "%x:%x:%x:%x:%x:%x%c",  &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5] ) ) {
          for (int ii = 0; ii < 6; ++ii ) {
            statinfo.mac[ii] = (uint8_t) mac[ii];
          }
          UpdateRtcMemory();
        }

        slaveFound = true;
        //break scan if accesspoint was found
        break;
      }
    }
  }
  
  
  if (DEBUG) {
    if (slaveFound) {
      Serial.println("Found accesspoint!");
    } else {
      Serial.println("Did not found the accesspoint! New try.");
    }
  }
  // release memory
  WiFi.scanDelete();
  return slaveFound;

}


void setup() {
  boolean netExists = true;
  ms = millis(); //remember start time
  Serial.begin(74880);
  pinMode(RECON,INPUT_PULLUP);
  //Start BME280
  bme.parameter.communication = 0;                    //I2C communication for Sensor
  bme.parameter.I2CAddress = 0x76;                    //I2C Address for Sensor
  bme.parameter.sensorMode = 0b01;                    //forced mode a single measured is performed
  bme.parameter.IIRfilter = 0b000;                    //factor 0 (filter off)
  bme.parameter.humidOversampling = 0b001;            //Humidity Oversampling factor 1
  bme.parameter.tempOversampling = 0b001;             //Temperature Oversampling factor 1
  bme.parameter.pressOversampling = 0b001;            //Pressure Oversampling factor 1
  if (bme.init() != 0x60)
  {    
    Serial.println("BME280 Sensor not found!");
  }
  delay(50); //wait 50 ms to complete the measurement
                  
  //Read gateways MAC address from RTC memory
  ESP.rtcUserMemoryRead(0, (uint32_t*) &statinfo, sizeof(statinfo));
  if (DEBUG) Serial.println("RTC fertig");
  uint32_t crcOfData = calculateCRC32(((uint8_t*) &statinfo) + 4, sizeof(statinfo) - 4);
  if ((statinfo.crc32 != crcOfData) || (digitalRead(RECON) == 0)){ 
    //if the checksum is wrong, the MAC address is invalid
    //we have to scan for gateway
    if (DEBUG) Serial.println("Search for gateway");
    netExists = ScanForSlave();
  }
  if (DEBUG) {
    Serial.print("Gateway MAC address: ");
    Serial.printf("%x-%x-%x-%x-%x-%x\n",statinfo.mac[0],statinfo.mac[1],statinfo.mac[2],statinfo.mac[3],statinfo.mac[4],statinfo.mac[5]);
    Serial.print("My MAC address: ");
    Serial.println(WiFi.macAddress());
  }
  if (netExists) {
    //initialize ESP-NOW
    if (esp_now_init() != 0) {
      if (DEBUG) Serial.println("*** ESP_Now INIT has failed");
      ESP.restart();
    }
    esp_now_set_self_role(ESP_NOW_ROLE_CONTROLLER);
    //Initialize peer
    int res = esp_now_add_peer(statinfo.mac, ESP_NOW_ROLE_CONTROLLER, 1, NULL, 0);
    if ((res==0) && DEBUG) Serial.println("Erfolgreich gepaart");
    //register callback function
    esp_now_register_send_cb(sendData);
    callbackCalled = false; //cleare flag
    //buffer for message (max 250 characters)
    char buf[500];
    //Unique name from sensor name and MAC address
    strcpy(buf,"BME280-");
    String mc = WiFi.macAddress();
    mc.replace(":","");
    strcat(buf,mc.c_str());
    uint16_t len = strlen(buf);
    //read values
    float humidity = bme.readHumidity();
    float temperature = bme.readTempC();
    float pressure = bme.readPressure();
    pinMode(A0,INPUT);
    uint16_t raw = analogRead(A0);
    float volt = raw/1023.0;
    float battery = volt/100 * (320 + RESISTOR);
    char mbuf[200];
    String unit = "°C";
    //first we create a full text message. 
    //This will be used for web display and for Alexa
    sprintf(mbuf,"&Die Temperatur ist %5.1fGrad Celsius\ndie Feuchtigkeit ist %5.0f%%\nder Luftdruck ist %7.1fhPa\ndie Batteriespannung ist %3.1fVolt&",temperature,humidity,pressure,battery);
    uint8_t n1 = strlen(mbuf);
    //add data to the message buffer
    strlcat(buf,mbuf,n1+len+1);
    //now we create another message with values JSON formatted for MQTT
    StaticJsonDocument<400> doc;
    //the JSON object will be filled
    doc["t"]=temperature;    //Value
    doc["tu"]=unit;          //Unit
    doc["h"]=humidity;       //Value
    doc["hu"]="%";           //Unit
    doc["p"]=round(pressure);       //Value
    doc["pu"]="hPa";         //Unit
    doc["b"]=battery;
    doc["bu"]="V";
    //create a JSON string from JSON object
    uint16_t n = serializeJson(doc, mbuf);
    //add it to the message buffer
    //as a delimiter ampersand character will be used
    strlcat(buf,mbuf,n+n1+len+1);
    if (DEBUG) Serial.println(buf);
    //Send message buffer to gateway
    esp_now_send(NULL,(uint8_t *) &buf,len+n1+n);
  } else {
    //No gatway was found, we go to sleep
    //and restart 5 minutes later to try it again
    ESP.deepSleep(300E6);
  }
}

void loop() {
  //wait untril data were sent
  if (callbackCalled || (millis() > 5000)) {
    if (DEBUG) Serial.println("Sleep");
    delay(100);
    //go to deep sleep and restart the controller after 5 minutes
    //sleep time is in micro seconds
    Serial.printf("Processing time %i ms\n",millis()-ms); //display processing time
    ESP.deepSleep(300E6);
  }
}

 

Sketch zum Herunterladen

Hinweis: Der ESP32 verfügt über 8K statisches RAM, bekannt als Real Time Clock Random Access Memory (Static RTC RAM), der zum Speichern und Halten von Variablen während des Tiefschlafs verwendet werden kann.

Das Programm wird praktisch nur in der Setup-Funktion ausgeführt. Nachdem der Sensor initialisiert wurde, wird im Speicher der Echtzeituhr nachgeschaut, ob dort bereits die MAC Adresse des Gateways gespeichert ist. Ist das nicht der Fall wird ein Netzwerkscan durchgeführt, um das Gateway zu finden. Wurde das Gateway gefunden, wird die MAC Adresse im RTC Speicher für später gespeichert. Der RTC Speicher behält seine Informationen, solange der Chip mit Strom versorgt wird. Mit der MAC Adresse des Gateways wird dann eine Paarung durchgeführt, die Messwerte vom Sensor gelesen und an das Gateway gesendet. In der Funktion loop() wird darauf gewartet, dass die Datenübertragung fertig ist, dann wird der ESP8266 für fünf Minuten in den Tiefschlaf versetzt. Das Aufwecken besorgt ein interner Timer, der nach der programmierten Zeit einen Impuls auf dem Pin D0 ausgibt, der über den Jumper mit Reset verbunden ist und so einen Neustart auslöst.

Der Sketch für den Server

Für den Server benötigen Sie zwei Bibliotheken, die nicht über die Bibliotheksverwaltung installiert werden können. Diese müssen zuerst als ZIP Datei heruntergeladen werden. Der asynchrone Webserver von https://github.com/me-no-dev/ESPAsyncWebServer und Asynchron TCP von https://github.com/me-no-dev/AsyncTCP. Zum Herunterladen einfach auf den grünen Knopf Code klicken und „Download ZIP“ auswählen.

Um die heruntergeladenen ZIP-Dateien in der Arduino IDE zu installieren, rufen Sie im Menü Sketch -> Bibliothek einbinden -> .ZIP Bibliothek hinzufügen auf. Es erscheint ein Datei-Auswahl-Dialog, indem Sie die heruntergeladenen Dateien auswählen. Mehr ist nicht zu tun.

Schließlich wird noch eine weitere Bibliothek benötigt, die über die Arduino Bibliotheksverwaltung installiert werden kann. Das ist die Bibliothek „AsyncWebConfig“, die die Konfiguration über den Browser implementiert

 

Der Server Sketch startet einen Web-Server, der die Messwerte vom Sensor anzeigt und über den die Konfiguration durchgeführt werden kann. Er kümmert sich auch um das ESP-Now Protokoll um die Daten von den Sensoren anzuzeigen. Eine Echtzeit-Uhr wird angezeigt.


 

#include "WiFi.h"                //WiFi support
#include <ESPmDNS.h>             //Dynamik name server
#include <esp_now.h>             //ESP NOW
#include <SPIFFS.h>              //Flash Filesystem
#include <FS.h>                  //File streams
#include "ESPAsyncWebServer.h"   //Asynchron web server
#include <AsyncWebConfig.h>      //Configuration by asynchron web server

#define ACCESSPOINT "WebhookGateway"  //SSID of the accesspoint
#define MAXDEVICES 100                //maximum number of devices
#define EMPTYMSG "Es sind noch keine Messwerte vorhanden"  //message if no values exist
#define DEVICEFILE "/devices.csv"     //filename to save devices in SPIFFS
#define TIMEZONE TZ_Europe_Berlin     //Timezone constant

//Define parameter for WebConfig
String params = "["
  "{"
  "'name':'ssid',"
  "'label':'Name des WLAN',"
  "'type':"+String(INPUTTEXT)+","
  "'default':''"
  "},"
  "{"
  "'name':'pwd',"
  "'label':'WLAN Passwort',"
  "'type':"+String(INPUTPASSWORD)+","
  "'default':''"
  "},"
  "{"
  "'name':'ntp_server',"
  "'label':'NTP Server',"
  "'type':"+String(INPUTTEXT)+","
  "'default':'fritz.box'"
  "}"
  "]";

//Templates for the web page
//Start part
const char HTML_START[] PROGMEM =
"<!DOCTYPE HTML>\n"
"<html lang='de'>\n"
"<head>\n"
"<meta http-equiv='Content-Type' content='text/html; charset=utf-8'>\n"
"<meta name='viewport' content='width=320' />\n"
"<meta http-equiv='refresh' content='30'>\n"
"<link rel='stylesheet' href='https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css'>\n"
"<title>Dashboard</title>\n"
"<style>\n"
"body {\n"
"  background-color: #d2f3eb;\n"
"  font-family: Arial, Helvetica, Sans-Serif;\n"
"  Color: #000000;\n"
"  font-size:12pt;\n"
"  width:320px;\n"
"}\n"
".titel {\n"
"font-weight:bold;\n"
"text-align:center;\n"
"width:100%%;\n"
"padding:5px;\n"
"}\n"
".frame {\n"
"margin:5px;\n"
"padding:5px;\n"
"border:1px solid black;\n"
"text-align: center;\n"
"background-color: lightyellow;\n"
"}\n"
"button {\n"
"font-size:10pt;\n"
"width:30px;\n"
"border-radius:6px;\n"
"margin:5px;\n"
"background-color: gold;\n"
"}\n"
"</style>\n"
"</head>\n"
"<body>\n"
"<div id='main_div'>\n"
"<div class='titel'>Meine Geräte</div>\n"
"<form method='post'>\n"
"<div style='text-align:center;'>%s&nbsp;&nbsp;<a href='/config' target='blank'><i class='fa fa-gear'></i></a></div>\n";

//Template to show a device
const char HTML_DEVICE[] PROGMEM =
"<div><input type='text' value='%s' name='%s'\>\n"
"<button type='submit' name='edt-%s'><i class='fa fa-edit'></i></button>\n"
"<button type='submit' name='del-%s'><i class='fa fa-trash'></i></button></div>\n";

//Template for a single line
const char HTML_LINE[] PROGMEM =
"<div>%s</div>\n";

//Template for final close
const char HTML_END[] PROGMEM =
"</form>\n"
"</div>\n"
"</body>\n"
"</html>\n";

//Instance for web server
AsyncWebServer server(80);
//Instance for web config
AsyncWebConfig conf;

//structure to store a sensor device
typedef struct{
  char id[32];       //unique id
  time_t dateTime;   //timestamp
  char keyword[32];  //name of the device
  char msg[256];     //msg with values
  char json[150];    //json formatted values
} Device;

//global variables
Device devices[MAXDEVICES];  //devise list
uint16_t count;              //number of registered devices
boolean connected = false;   //true if we have a WiFi connection
uint16_t nextTry = 0;        //counter for reconnect

//add a device to the device list
void addDevice(const char* id, const char * keyword = "", const char * msg = EMPTYMSG) {
  if (count < MAXDEVICES) {
    strcpy(devices[count].id,id);
    strcpy(devices[count].keyword,keyword);
    strcpy(devices[count].msg,msg);
    devices[count].dateTime = 0;
    count++;
  }
}

//read device list from flash file system
void readDevices() {
  uint16_t len;
  count = 0;
  if (SPIFFS.exists(DEVICEFILE)) {
    File f = SPIFFS.open(DEVICEFILE,"r");
    Serial.println("Lese Geräte");
    if (f) {
      String data;
      String id;
      String keyword;
      Serial.println("Lese Geräte");
      uint16_t size = f.size();

      while(f.position() < size) { 
         data = f.readStringUntil(10);
         Serial.println(data);
         uint8_t p = data.indexOf(",");
         id = data.substring(0,p).c_str();
         keyword = data.substring(p+1);
         if (id != "") {
           addDevice(id.c_str(),keyword.c_str());
         }
      }
    }
  }
}

//save device list into flash filesystem
void saveDevices() {
  File f = SPIFFS.open(DEVICEFILE,"w");
  Serial.printf("Speichere Geräte auf %s \n",DEVICEFILE);
  if (f) {
    for (uint8_t i = 0; i<count; i++){
      f.printf("%s,%s\n",devices[i].id,devices[i].keyword);
      Serial.printf("%s,%s\n",devices[i].id,devices[i].keyword);
    }
    f.close();
  } else {
    Serial.printf("Kann file %s nicht öffnen",DEVICEFILE);
  }
}

//find a device with certain id return index or -1 if not found
int16_t findId(const char * id) {
  int16_t ix = count - 1;
  while ((ix >= 0) && (strcmp(devices[ix].id,id) != 0)) ix--;
  return ix;
}

//find a device with certain name return index or -1 if not found
int16_t findKeyword(const char * keyword) {
  int16_t ix = count - 1;
  while ((ix >= 0) && (strcmp(devices[ix].keyword,keyword) != 0)) ix--;
  return ix;
}

//delete the device with name keyword from device list
void deleteDevice(const char * keyword) {
  int16_t ix = findKeyword(keyword);
  if (ix < 0) return;
  count--;
  if (ix != count) { 
    memcpy(&devices[ix],&devices[count],sizeof(Device));
  }
}

//delete the device with unique id from device list
void deleteDeviceId(const char * id) {
  int16_t ix = findId(id);
  if (ix < 0) return;
  count--;
  if (ix != count) { 
    memcpy(&devices[ix],&devices[count],sizeof(Device));
  }
}

//set the name for device with unique id
void setKeyword(const char * id, const char * keyword){
  int16_t ix = findId(id);
  if (ix >= 0) strncpy(devices[ix].keyword,keyword,32);
}

//initialioze WiFi connection
boolean initWiFi() {
    boolean connected = false;
    WiFi.mode(WIFI_AP_STA);
    WiFi.softAP(ACCESSPOINT,"",0,0);
    Serial.print("Verbindung zu ");
    Serial.print(conf.values[0]);
    Serial.println(" herstellen");
    //if we have a config we try to connect
    if (conf.values[0] != "") {
      WiFi.begin(conf.values[0].c_str(),conf.values[1].c_str());
      uint8_t cnt = 0;
      while ((WiFi.status() != WL_CONNECTED) && (cnt<20)){
        delay(500);
        Serial.print(".");
        cnt++;
      }
      Serial.println();
      if (WiFi.status() == WL_CONNECTED) {
        Serial.print("IP-Adresse = ");
        Serial.println(WiFi.localIP());
        connected = true;
        bool flag = (esp_now_init() == ESP_OK);
        if (flag) {
          Serial.println("ESP-NOW gestartet");
        }
      }
    }
    //if we have no connection /no config or wrong config)
    //we start an access point to allow configuration
    if (!connected) {
       Serial.println("Keine Verbindung! \nStarte Access-Point.");
          WiFi.mode(WIFI_AP);
          WiFi.softAP(conf.getApName(),"",1);
          nextTry = 0;  
    }
    return connected;
}

//show configuration form
void handleConfig(AsyncWebServerRequest *request) {
  conf.handleFormRequest(request);
}

//response to a root request
void handleRoot(AsyncWebServerRequest *request) {
  char * ptr;
  char buf[256];
  struct tm * timeinfo;
  String nam;
  String id;
  boolean conf = false;
  if (!connected) { //if not connected show config page
    handleConfig(request);
  } else {
    uint8_t args = request->args();
    //we checkk the requests arguments for commands
    for (uint8_t i = 0; i<args; i++) {
      nam = request->argName(i);
      if (nam == "config") conf =true;
      if (nam.startsWith("edt-")) { //an edit button was clicked
        id = nam.substring(4);
        Serial.printf("Change name for %s\n",id.c_str());
        if (request->hasArg(id.c_str())) setKeyword(id.c_str(),request->arg(id).c_str());
        saveDevices(); 
      }
      if (nam.startsWith("del-")) { //a delete button was clicked
        id = nam.substring(4);
        Serial.printf("Delete device %s\n",id.c_str());
        deleteDeviceId(id.c_str()); 
        saveDevices(); 
      }
    }
    if (!conf) {
      //build the eb page, show all devices and their values
      AsyncResponseStream *response = request->beginResponseStream("text/html");
      time_t now = time(nullptr);
      timeinfo = localtime(&now);
      strftime(buf, 256, "%d.%m.%Y %H:%M:%S", timeinfo);
      Serial.printf("we have %i devices\n",count);
      response->printf(HTML_START,buf);
      for (uint16_t i = 0; i<count; i++) {
        Serial.printf("Device [%s] name [%s]\n",devices[i].id,devices[i].keyword);
        response->print("<div class='frame'>");
        if (devices[i].keyword[0] != 0) {
          response->printf(HTML_DEVICE,devices[i].keyword,devices[i].id,devices[i].id,devices[i].id);
        } else {
          response->printf(HTML_DEVICE,devices[i].id,devices[i].id,devices[i].id,devices[i].id);
        }
        if(devices[i].dateTime != 0){
          timeinfo = localtime(&devices[i].dateTime);
          strftime(buf, 256, "%d.%m.%Y %H:%M:%S", timeinfo);
          response->printf(HTML_LINE,buf);
        }
        strncpy(buf,devices[i].msg,256);
        ptr = strtok(buf,"\n");
        while (ptr != NULL) {
          response->printf(HTML_LINE,ptr);
          ptr = strtok(NULL,"\n");
        }
        response->print("</div>");
      }
      response->print(HTML_END);
      request->send(response);
    } else {
      handleConfig(request);
    }
  }
}

// callback for ESP Now
void readESPNow(const uint8_t *mac_addr, const uint8_t *r_data, int data_len) {
  char buf[256];
  char * msg;
  char * json;
  char * id;
  if (data_len < 256) {
    memcpy(&buf,r_data,data_len);
    buf[data_len]=0;
    Serial.printf("Received from ESPNOW %x : %s\n",mac_addr, buf);
    //split the message in parts. Separator is "&"
    id = strtok(buf,"&");
    msg = strtok(NULL,"&");
    json = strtok(NULL,"&");
    int16_t index = findId(id);
    if (index < 0) { //if device not exists, add it
      addDevice(id,"",msg);
      saveDevices();
    } else { //if device exists, update values
      strlcpy(devices[index].msg,msg,255); 
      if (json) strlcpy(devices[index].json,json,149); 
      devices[index].dateTime = time(nullptr);
      
    }
  }
}

//setup the gateway
void setup() {
  Serial.begin(115200);
  Serial.println(params);
  //read configuration
  conf.setDescription(params);
  conf.readConfig();
  //init WiFi connection
  connected = initWiFi();
  //start ESP NOW
  esp_now_register_recv_cb(readESPNow);
  //prepare nameserver 
  char dns[30];
  sprintf(dns,"%s.local",conf.getApName());
  if (MDNS.begin(dns)) {
    Serial.println("MDNS responder gestartet");
  }
  //register request callbacks
  server.on("/",handleRoot);
  server.on("/config",handleConfig);
  //start webserver
  server.begin();
  //initialize real time clock
  if (connected) { //if we have an internet connection
    //init the internal clock
    configTzTime("CET-1CEST,M3.5.0/03,M10.5.0/03", conf.getValue("ntp_server"));
    Serial.print(conf.getValue("ntp_server"));
    Serial.println(" Uhrzeit gesetzt!");
  }
  //read devivce list
  readDevices();
}

void loop() {
  //all happens asynchron, no actions required
}

 Sketch zum Herunterladen

In Betrieb nehmen

Wenn der Sketch ohne Fehler kompiliert und hochgeladen wurde, startet das Programm. Da noch keine Konfigurationsdaten vorhanden sind, wird ein Accesspoint gestartet. Die SSID wird aus der MAC-Adresse des D1-Minis gebildet. Mit einem Smartphone oder einem anderen WLAN-fähigen Computer kann jetzt eine Verbindung zu diesem Accesspoint hergestellt werden. Der Zugriff ist offen, es ist also kein Passwort erforderlich. Nachdem die WLAN-Verbindung hergestellt ist, kann man im Browser die Adresse 192.168.4.1 aufrufen. Die Konfigurationsseite wird dargestellt.

Der Name des Accesspoints wird später als DNS-Name verwendet. Es folgen die Zugangsdaten zum WLAN.
Der NTP-Server wird zur Synchronisation der internen Uhr verwendet. Hier könnte z.B. auch fritz.box stehen, wenn die Fritz-Box als Zeitserver verwendet werden soll.
Mit dem Button „Save“ wird die Konfiguration im Flash-Filesystem des D1-Minis gespeichert.
Mit dem Button „Restart“ wird die Konfiguration ebenfalls gespeichert und dann der D1-Mini neu gestartet.
Die Erstkonfiguration sollte mit „Restart“ beendet werden, da sich der D1-Mini nach dem Neustart mit dem WLAN verbinden sollte. Ist die Verbindung erfolgreich, wird kein Accesspoint gestartet.
Es sollte jetzt möglich sein, die Homepage mit der URL <nameDesAccesspoint>.local also im dargestellten Beispiel, mit Gateway1.local  aufzurufen. Wenn Ihr Router mDNS nicht unterstützt, müssen Sie die IP-Adresse, die über den seriellen Monitor ausgegeben wurde, verwenden.


War noch keine Verbindung mit einem Sensor aufgetreten, so wird folgende Homepage dargestellt.

 

Mit dem Knopf mit dem Zahnrad kann man auf die Konfigurationsseite wechseln. Nachdem ein Sensor eine Verbindung mit dem Gateway aufgenommen hat, erhält man die folgende Homepage.

 Im Eingabefeld sieht man die eindeutige Kennung des Sensors bestehend aus Sensortyp und MAC-Adresse. Hier kann man einen aussagekräftigeren Namen eingeben und diesen mit dem Edit-Knopf dauerhaft speichern. Mit dem Löschknopf kann das Gerät aus der Liste gelöscht werden. Bis zu 100 Sensoren können mit dem Gateway verbunden werden.

Es wird ein zweiter Teil zu diesem Projekt folgen. In diesem zweiten Teil sendet das Gateway die Messwerte der Sensoren an einen MQTT-Broker und/oder an Alexa, sodass eine Abfrage über den Sprachassistenten möglich sein wird.

Viel Spaß beim Nachbau.

Nachtrag:

Da ESP-NOW nicht funktioniert, wenn eine falsche Server MAC Adresse verwendet wird, habe ich eine neue Verion, des Sensor Sketchs erstellt, bei der über einen Taster ein Netzwerk-Scan ausgelöst werden kann. Der Taster muss zwischen Anschluss D5 und GND geschaltet werden. Wenn er während des Resets gedrückt wird, wird ein Netzwerk-Scan gestaret.



 

#include <Wire.h>
#include <BlueDot_BME280.h>
#include <ESP8266WiFi.h>
#include <ArduinoJson.h>
//library for ESP Now
#include <espnow.h>

//SSID of the gateway
#define GW_SSID "WebhookGateway"

//series resistor for supply voltage measurement
#define RESISTOR 470 //for battery = 470 for akku loader = 130
//Switch debug messages on
#define DEBUG 1

#define SEND_TIMEOUT 2000  // 2 seconds timeout
#define RECON D5 // Pin for reconnect Button
//Instance for sensor
BlueDot_BME280 bme;

//data strukture to save gateways MAC address
struct MEMORYDATA {
  uint32_t crc32; //checksum
  uint8_t mac[6];
};

//global variables
uint32_t ms; //timestamp to calculate process time
bool callbackCalled =false; //flag for data sent
MEMORYDATA statinfo; //memory for MAC address

//Callback function will be called if data has been sent to the gateway
void sendData(uint8_t* smac, uint8_t sendStatus) {
  if (DEBUG) {
    Serial.print("send_cb, Status = "); Serial.print(sendStatus); 
    Serial.print(", an MAC: "); 
    char macString[50] = {0};
    sprintf(macString,"%02X:%02X:%02X:%02X:%02X:%02X", statinfo.mac[0], statinfo.mac[1], statinfo.mac[2], statinfo.mac[3], statinfo.mac[4], statinfo.mac[5]);
    Serial.println(macString);
  }
  callbackCalled = true;
}

//function to calculate checksum
uint32_t calculateCRC32(const uint8_t *data, size_t length)
{
  uint32_t crc = 0xffffffff;
  while (length--) {
    uint8_t c = *data++;
    for (uint32_t i = 0x80; i > 0; i >>= 1) {
      bool bit = crc & 0x80000000;
      if (c & i) {
        bit = !bit;
      }
      crc <<= 1;
      if (bit) {
        crc ^= 0x04c11db7;
      }
    }
  }
  return crc;
}

//Save gateways MAC address and checksum in RTC memory
void UpdateRtcMemory() {
    uint32_t crcOfData = calculateCRC32(((uint8_t*) &statinfo) + 4, sizeof(statinfo) - 4);
    statinfo.crc32 = crcOfData;
    ESP.rtcUserMemoryWrite(0,(uint32_t*) &statinfo, sizeof(statinfo));
}

//Search for gateways access point
boolean ScanForSlave() {
  bool slaveFound = false;
  int8_t scanResults = WiFi.scanNetworks();
  if (DEBUG) Serial.println("Scan done");
  if (scanResults == 0) {
    if (DEBUG) Serial.println("No accesspoint found");
  } else {
    if (DEBUG) {
      Serial.print("Gefunden "); 
      Serial.print(scanResults); 
      Serial.println(" Netze ");
    }
    for (int i = 0; i < scanResults; ++i) {
      String SSID = WiFi.SSID(i);
      int32_t RSSI = WiFi.RSSI(i);
      int32_t chl = WiFi.channel(i);
      String BSSIDstr = WiFi.BSSIDstr(i);
      if (DEBUG) {
        // display what we found
        Serial.print(i + 1);
        Serial.print(": ");
        Serial.print(SSID);
        Serial.print(" /");
        Serial.print(chl);
        Serial.print(" (");
        Serial.print(RSSI);
        Serial.print(")");
        Serial.println("");
      }
      delay(10);
      // check if we have the gateway
      if (SSID == GW_SSID) {
        if (DEBUG) {
          Serial.println("Network found");
          Serial.print(i + 1); Serial.print(": "); 
          Serial.print(SSID); Serial.print(" ["); 
          Serial.print(BSSIDstr); Serial.print("]"); 
          Serial.print(" ("); Serial.print(RSSI); 
          Serial.print(")"); Serial.println("");
        }
        int mac[6];
        // Save gateways MAC address
        if ( 6 == sscanf(BSSIDstr.c_str(), "%x:%x:%x:%x:%x:%x%c",  &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5] ) ) {
          for (int ii = 0; ii < 6; ++ii ) {
            statinfo.mac[ii] = (uint8_t) mac[ii];
          }
          UpdateRtcMemory();
        }

        slaveFound = true;
        //break scan if accesspoint was found
        break;
      }
    }
  }
  
  
  if (DEBUG) {
    if (slaveFound) {
      Serial.println("Found accesspoint!");
    } else {
      Serial.println("Did not found the accesspoint! New try.");
    }
  }
  // release memory
  WiFi.scanDelete();
  return slaveFound;

}


void setup() {
  boolean netExists = true;
  ms = millis(); //remember start time
  Serial.begin(74880);
  pinMode(RECON,INPUT_PULLUP);
  //Start BME280
  bme.parameter.communication = 0;                    //I2C communication for Sensor
  bme.parameter.I2CAddress = 0x76;                    //I2C Address for Sensor
  bme.parameter.sensorMode = 0b01;                    //forced mode a single measured is performed
  bme.parameter.IIRfilter = 0b000;                    //factor 0 (filter off)
  bme.parameter.humidOversampling = 0b001;            //Humidity Oversampling factor 1
  bme.parameter.tempOversampling = 0b001;             //Temperature Oversampling factor 1
  bme.parameter.pressOversampling = 0b001;            //Pressure Oversampling factor 1
  if (bme.init() != 0x60)
  {    
    Serial.println("BME280 Sensor not found!");
  }
  delay(50); //wait 50 ms to complete the measurement
                  
  //Read gateways MAC address from RTC memory
  ESP.rtcUserMemoryRead(0, (uint32_t*) &statinfo, sizeof(statinfo));
  if (DEBUG) Serial.println("RTC fertig");
  uint32_t crcOfData = calculateCRC32(((uint8_t*) &statinfo) + 4, sizeof(statinfo) - 4);
  if ((statinfo.crc32 != crcOfData) || (digitalRead(RECON) == 0)){ 
    //if the checksum is wrong, the MAC address is invalid
    //we have to scan for gateway
    if (DEBUG) Serial.println("Search for gateway");
    netExists = ScanForSlave();
  }
  if (DEBUG) {
    Serial.print("Gateway MAC address: ");
    Serial.printf("%x-%x-%x-%x-%x-%x\n",statinfo.mac[0],statinfo.mac[1],statinfo.mac[2],statinfo.mac[3],statinfo.mac[4],statinfo.mac[5]);
    Serial.print("My MAC address: ");
    Serial.println(WiFi.macAddress());
  }
  if (netExists) {
    //initialize ESP-NOW
    if (esp_now_init() != 0) {
      if (DEBUG) Serial.println("*** ESP_Now INIT has failed");
      ESP.restart();
    }
    esp_now_set_self_role(ESP_NOW_ROLE_CONTROLLER);
    //Initialize peer
    int res = esp_now_add_peer(statinfo.mac, ESP_NOW_ROLE_CONTROLLER, 1, NULL, 0);
    if ((res==0) && DEBUG) Serial.println("Erfolgreich gepaart");
    //register callback function
    esp_now_register_send_cb(sendData);
    callbackCalled = false; //cleare flag
    //buffer for message (max 250 characters)
    char buf[500];
    //Unique name from sensor name and MAC address
    strcpy(buf,"BME280-");
    String mc = WiFi.macAddress();
    mc.replace(":","");
    strcat(buf,mc.c_str());
    uint16_t len = strlen(buf);
    //read values
    float humidity = bme.readHumidity();
    float temperature = bme.readTempC();
    float pressure = bme.readPressure();
    pinMode(A0,INPUT);
    uint16_t raw = analogRead(A0);
    float volt = raw/1023.0;
    float battery = volt/100 * (320 + RESISTOR);
    char mbuf[200];
    String unit = "°C";
    //first we create a full text message. 
    //This will be used for web display and for Alexa
    sprintf(mbuf,"&Die Temperatur ist %5.1fGrad Celsius\n die Luftfeuchtigkeit ist %5.0f%%\n der Luftdruck ist %7.1fhPa\n die Batteriespannung ist %3.1fVolt&",temperature,humidity,pressure,battery);
    uint8_t n1 = strlen(mbuf);
    //add data to the message buffer
    strlcat(buf,mbuf,n1+len+1);
    //now we create another message with values JSON formatted for MQTT
    StaticJsonDocument<400> doc;
    //the JSON object will be filled
    doc["t"]=temperature;    //Value
    doc["tu"]=unit;          //Unit
    doc["h"]=humidity;       //Value
    doc["hu"]="%";           //Unit
    doc["p"]=pressure;       //Value
    doc["pu"]="hPa";         //Unit
    doc["b"]=battery;
    doc["bu"]="V";
    //create a JSON string from JSON object
    uint16_t n = serializeJson(doc, mbuf);
    //add it to the message buffer
    //as a delimiter ampersand character will be used
    strlcat(buf,mbuf,n+n1+len+1);
    if (DEBUG) Serial.println(buf);
    //Send message buffer to gateway
    esp_now_send(NULL,(uint8_t *) &buf,len+n1+n);
  } else {
    //No gatway was found, we go to sleep
    //and restart 5 minutes later to try it again
    ESP.deepSleep(300E6);
  }
}

void loop() {
  //wait untril data were sent
  if (callbackCalled || (millis() > 5000)) {
    if (DEBUG) Serial.println("Sleep");
    delay(100);
    //go to deep sleep and restart the controller after 5 minutes
    //sleep time is in micro seconds
    Serial.printf("Processing time %i ms\n",millis()-ms); //display processing time
    ESP.deepSleep(300E6);
  }
}

Die Code-Erweiterungen sind gelb markiert.
Beim Gatway-Sketch sollte man auch die Anzeige der beiden MAC-Adressen für Station und Access-Point einbauen.

  Serial.print("Station MAC address: ");
  Serial.println(WiFi.macAddress());
  Serial.print("Accesspoint MAC address: ");
  Serial.println(WiFi.softAPmacAddress());

Dann kann man überprüfen ob der Sensor die richtige MAC-Adresse findet. Er muss dioe MAC-Adresse des Accespoints benutzen.

Die Sketches zum Herunterladen haben die Erweiterungen jetzt eingebaut.

Blogbeitrag als PDF

 

Esp-32Esp-8266Projets pour débutantsCapteursMaison intelligenteSource de courant

15 commentaires

Klaus

Klaus

Hallo Herr Lechner, versuche wieder mal, eines Ihrer Projekte nachzubauen. Jetzt kommt die Fehlermeldung, dass das Programm “AsyncWebConfig.h” nicht gefunden wird. Es befindet sich nicht in den von Ihnen angegebenen Bibliotheken. Fehlt mir da noch irgendwas?
BTW, die angeblich im Beitrag korrigierte URL für die AsyncTCP Bibliothek führt immer noch auf die falsche Webseite.
LG Klaus

Gerald Lechner

Gerald Lechner

Der offene HotSpot stellt keine Gefahr dar. Der Treiber im ESP 32 implementiert einen Protokollstack mit dem er WLAN Datenpakete die an seine MAC Adresse gesendet werden empfangen kann OSI-Modell Schicht 2). Diese Daten können dann weiterverarbeitet werden, dafür sind Dienste notwendig, die in ein Programm eingebunden und gestartet werden müssen. Im Fall dieses Beitrags ist das der Dienst ESP-Now der die empfangenen Daten aus dem Netzwer-Frame extrahiert und über eine Callback-Funktion dem Sketch zur Verfügung stellt. Mehr macht auch dieser Dienst nicht. Es ist also ohne zusätzliche Software am ESP32 nicht möglich über den Access-Point eine Verbindung zum zweiten Protokoll-Stack, der im Station Mode mit dem lokalen WLAN verbunden ist, herzustellen.

AnsgarFleichhut

AnsgarFleichhut

Moin,
danke für die Anleitung. Ich habe eine Frage hierzu:
WiFi.mode(WIFI_AP_STA);
WiFi.softAP(ACCESSPOINT,"",0,0);
Kann es eine Sicherheitslücke darstellen, wenn der ESP immer einen offenen Hotspot aktiv hat? Könnte jemand sich mit dem offenen Wlan verbinden und dann eine Verbindung zu meinem Heimnetz herstellen?

Schöne Grüße

Gerald Lechner

Gerald Lechner

Einige hatten beim Nachbau dieses Projekts das Problem, dass keine Daten übermittelt wurden. Der Grund dafür war, dass die neueste Version von Arduino JSON bei Fließkommazahlen die Anzahl der Nachkommastellen erhöht hat und dadurch die Nachricht zu lang wurde. ESP NOW kann nur 250 Bytes in einem Paket Übertragen. Um das Problem zu lösen, müssen Sie zwei kleine Änderungen vornehmen:
Die Zeile für die Erzeugung des Strings muss geändert werden
sprintf(mbuf,“&Die Temperatur ist 5.1fGrad Celsius\ndie Feuchtigkeit ist %5.0f%\nder Luftdruck ist %7.1fhPa\ndie Batteriespannung ist %3.1fVolt&”,temperature,humidity,pressure,battery);
Also die unnötigen Zwischenräume nach dem Zeilenvorschub entfernen und statt “Luftfeuchtigkeit” nur “Feuchtigkeit”
Die zweite Änderung betrifft den Luftdruck. Hier macht es keinen Sinn Nachkommastellen zu nutzen, daher wird der Luftdruck bei der Zuweisung zum JSON Dokument gerundet.
doc[“p”]=round(pressure); //Value
Mit diesen Änderungen funktioniert die Übertragung.
Der Sketch zum Herunterladen enthält bereits die Änderungen.

Gerald Lechner

Gerald Lechner

Wenn ESP-Now nicht funktioniert, also das Gateway keine Daten empfängt, muss überprüft werden ob der Sensor die richtige MAC-Adresse benutzt. Um diese Prüfung zu erleichtern, habe ich den Sketch für den Sensor erweitert. Siehe Abschnitt Nachtrag am Ende des Beitrags. Ebenso hab ich die Ausgabe der Station-MAC-Adresse und der Accesspoint MAC Adresse im Sketch für das Gateway eingebaut. Die geänderten Sketchs können von Google-Drive heruntergeladen werden.

Gerald Lechner

Gerald Lechner

ACHTUNG WICHTIGER HINWEIS!
Im Beitrag war eine falsche URL für die AsyncTCP Bibliothek. Die URL ist nicht https://github.com/me-no-dev/ESPAsyncTCP sondern https://github.com/me-no-dev/AsyncTCP.
Die URL ist jetzt auch im Beitrag korrigiert.

Knut Hansen

Knut Hansen

Hallo Herr Lechner,
ich habe das gleiche Problem wie Herr Wiegand.
Meine Ausgaben am seriellen Port sind fast identisch.
Als Server verwende ich auch einen ESP32 und keinen D1 mini.
Beim Neustart wird das Gateway gefunden und auch die Mac-Adresse.
Der Webserver zeigt keine Geräte an.
auf dem Serial Monitor wird die Nachricht:
we have 0 devices ausgegeben.
Können Sie mir helfen?
Mit freundlichen Grüßen

Knut Hansen

Burkhard

Burkhard

Hallo, beim Compilieren des Webservers bekomme ich immer die Fehlermeldung:
Alternatives for interrupts.h: []
/home/burkhard/Arduino/libraries/ESPAsyncTCP-master/src/SyncClient.cpp:25:24: fatal error: interrupts.h: No such file or directory
ResolveLibrary(interrupts.h)
→ candidates: []
compilation terminated.

exit status 1
Fehler beim Kompilieren für das Board ESP32 Dev Module.
Wo bekomme ich interrupts.h her?
VG
Burkhard

Gerald Lechner

Gerald Lechner

ESP-NOW ist ein sehr einfaches Protokoll, wenn die MAC-Adresse des Empfängers bekannt ist, werden die Daten an diese MAC-Adresse gesendet ohne irgendwelche Überprüfungen. Kommt das Datenpaket bei keinem Empfänger an, kann der Sender das nicht feststellen. Der Sensor im Beitrag ist ESP-NOW Sender. Wenn er noch keine MAC-Adresse des Servers hat, wird ein Netzwerk-Scan durchgeführt um diese Adresse zu erhalten und für die Zukunft zu speichern. Wird die Stromversorgung nicht unterbrochen, so sendet der Sensor die Daten an die gespeicherte MAC-Adresse, egal ob das die Adresse des Servers ist oder nicht. Um sicherzustellen, dass die richtige MAC-Adresse gespeichert wird, sollte der Sensor von der Stromversorgung getrennt werden, damit er irgendwelche falsche MAC-Adressen vergisst. Dann sollte der Server gestartet werden und erst danach der Sensor wieder mit der Stromversorgung verbunden werden. Im seriellen Monitor beim Sender wird ausgegeben, dass ein Netzwerkscan ausgeführt wird.

Fritz Wiegand

Fritz Wiegand

Hallo Herr Lechner,
sehr schönes Projekt. Erst mal vielen Dank für die Mühe…
Leider funktioniert es nicht so wie es soll…
Als Gateway habe ich einen ESP 32 und der Client ist ein D1 mini ESP8266.
Nach aufspielen der Sketche konnte ich den Server wie beschrieben konfigurieren.
Fritz Box Kanal 1 (in der Beschreibung Kanl 0 den gibt es nicht).
Server connectet sich problemslos mit FritzBox WiFi….
Client bekommt auch Verbimdung zum Gateway….
Aber leider werden keine Daten übertragen..
Eine Idee WARUM ???
Hier ein Auszug des logs…..Gateway
:11:27.122 → rst:0×1 (POWERON_RESET),boot:0×13 (SPI_FAST_FLASH_BOOT)
18:11:27.122 → configsip: 0, SPIWP:0xee
18:11:27.122 → clk_drv:0×00,q_drv:0×00,d_drv:0×00,cs0_drv:0×00,hd_drv:0×00,wp_drv:0×00
18:11:27.122 → mode:DIO, clock div:1
18:11:27.122 → load:0×3fff0018,len:4
18:11:27.122 → load:0×3fff001c,len:1100
18:11:27.122 → load:0×40078000,len:9232
18:11:27.122 → load:0×40080400,len:6400
18:11:27.122 → entry 0×400806a8
18:11:27.427 → [{’name’:‘ssid’,‘label’:‘Name des WLAN’,‘type’:0,‘default’:‘’},{’name’:‘pwd’,‘label’:‘WLAN Passwort’,‘type’:1,‘default’:‘’},{’name’:‘ntp_server’,‘label’:‘NTP Server’,‘type’:0,‘default’:‘fritz.box’}]
18:11:27.529 → Read configuration
18:11:27.529 → apName=WebhookGateway
18:11:27.529 → ssid=FRITZ!Box Fon WLAN 7270
18:11:27.563 → pwd=*************
18:11:27.563 → ntp_server=fritz.box
18:11:27.631 → Verbindung zu FRITZ!Box Fon WLAN 7270 herstellen
18:11:28.209 → ..
18:11:28.685 → IP-Adresse = 192.168.1.123
18:11:28.685 → ESP-NOW gestartet
18:11:28.719 → MDNS responder gestartet
18:11:28.719 → fritz.box Uhrzeit gesetzt!
18:11:52.062 → dhcps: send_offer>>udp_sendto result 0
18:11:52.062 → dhcps: send_nak>>udp_sendto result 0
18:11:55.561 → dhcps: send_nak>>udp_sendto result 0
18:11:55.561 → dhcps: send_offer>>udp_sendto result 0
18:11:55.561 → dhcps: send_nak>>udp_sendto result 0
18:16:15.674 → dhcps: send_offer>>udp_sendto result 0
18:16:17.506 → dhcps: send_offer>>udp_sendto result 0
und hier Client

18:02:23.912 → ets Jan 8 2013,rst cause:2, boot mode:(3,6)
18:02:23.946 →
18:02:23.946 → load 0×4010f000, len 3460, room 16
18:02:23.946 → tail 4
18:02:23.946 → chksum 0xcc
18:02:23.946 → load 0×3fff20b8, len 40, room 4
18:02:23.946 → tail 4
18:02:23.946 → chksum 0xc9
18:02:23.946 → csum 0xc9
18:02:23.946 → v00048000
18:02:23.946 → ~ld
18:02:24.082 → RTC fertig
18:02:24.082 → Erfolgreich gepaart
18:02:24.116 → BME280-B4E62D697FD0&Die Temperatur ist 25.1Grad Celsius
18:02:24.116 → die Luftfeuchtigkeit ist 34%
18:02:24.116 → der Luftdruck ist 989.7hPa
18:02:24.116 → die Batteriespannung ist 4.8Volt&{"t":25.09000015,“tu”:“°C”,h,“hu”:“%”,p,“pu”:“hPa”,b,“bu”:"V"}
18:02:28.968 → Sleep
18:02:29.070 → Processing time 5030 ms

Lutz Ti

Lutz Ti

Hilferuf …

Versuche schon seit ein paar Tagen den Nachbau, scheitere aber in beiden Scripten bei Compilieren, offensichtlich fehlen ( mir ) da noch Bibliotheken:
WebhookGateway:
ESPAsyncWebServer.h: No such file or directory

und beim Client:
ESP8266WiFi.h: No such file or directory
In beiden Fällen bricht der Compiler ab …

Welchen ( Anfänger ? ) Fehler mache ich? Wo finde ich die zusätzlichen Biblioheken ?

Danke für eine rettende Idee …

ti

Andreas Wolter

Andreas Wolter

@Äd Franzis: mit RTC ist in diesem Fall nicht gemeint, dass eine Real Time Clock verwendet wird. Wir haben einen zusätzlichen Hinweis eingefügt. Der ESP32 hat 8Kbyte Speicher, der RTC RAM genannt wird. Damit können Variablen während des Deep Sleep gehalten werden.

Grüße,
Andreas Wolter
AZ-Delivery Blog

Äd Franzis

Äd Franzis

Sehr schöner Artikel, vielen Dank.
Allerdings habe ich nicht verstanden, dass in der SW und im Text auf den Speicher der RTC Bezug genommen wird, wobei ich aber im Anschlußplan und der Stückliste kein Uhrenmodul sehe.
LG
Äd

Gerald Lechner

Gerald Lechner

Vielen Dank für den Hinweis. Habe den Code geändert. Der Fehler war aber nicht tragisch, da die Funktion scanForSlave nur einmal ausgeführt wird. Der Sensor erhält nach dem Tiefschlaf einen Reset und da werden ohnehin alle Speicherstrukturen neu initialisiert.

Ulrich Klaas

Ulrich Klaas

In “scanForSlave” steht ein WiFi.scanDelete(); nach einem return. Wird also nie ausgeführt.

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