Entfernter Temperatursensor für Thermometer mit ESP8266

This article complements the previous article. It describes a temperature sensor with ESP8266 which sends the current temperature every five minutes to the thermometer presented in the last article. In the break between two measurements the ESP8266 is put into deep sleep mode in which it consumes hardly any energy. For sending the data the ESP Now protocol is used, which sends the data directly to the MAC address of the thermometer without any complex connection setup.

We use the ESP8266-E12 module with adapter, because this has in contrast to the ESP8266-01 module the wake pin GPIO-16 out and no permanently lit LED on the supply voltage. We need the wake pin to wake up from deep sleep and the power LED only consumes energy unnecessarily.

Circuit:

The orange wire connects the Wake PIN with the reset input and ensures that the module is restarted again at the end of the sleep break. The sensor is connected to the GPIO2 (white wire).

To upload the program, we need an FDTI adapter to program the ESP8266 via the serial interface.

We connect the FDTI adapter to the GND connector and TX to RX and RX to TX. In addition, the flash pin GPIO 0 must be connected to ground (green wire) so that the ESP8266 goes into programming mode.

After programming the green wire must be removed again. If DEBUG is set to true in the sketch, you can follow the messages of the module via the serial interface.

Attention: Powering the ESP8266 through the FDTI board will cause crashes , when the scanNetwork function is called, because the current consumption is then too high.

Sketch:

To compile the board on

set to !!


/*
 WLAN temperature sensor
 ESP Now with the thermometer
 If no Mac address has yet been determined
 If the sensor is looking for a WLAN with SSID thermometer
 When he has found the AP, he will remember the Mac address 
 as long as the power supply was not interrupted
 The ESP Now protocol is very quick so that only a very short time (US)
 Higher current flows. The sensor sends temperature data and then goes into the 
 Deep sleep so that very little energy is used up and the
 Sensor can therefore be operated with batteries. 
*/
//Bibliothek für WiFi
#include <ESP8266WiFi.h>
//Bibliotheken für Temperatursensor DS18B20
#include <OneWire.h>

//Bibliothek für ESP Now
#include <DallasTemperature.h>
extern "C" {
  #include <espnow.h>
}

//Debug Flag wenn false werden alle Debug Meldungen unterdrückt
//um zusätzlich Strom zu sparen
#define DEBUG true

//Konstanten für WiFi
#define WIFI_CHANNEL 1
#define SEND_TIMEOUT 2450  // 245 Millisekunden timeout 

//Pins für Temperatursensor
const byte bus = 2;//pin GPIO2

//Datenstruktur für den Datenaustausch
struct DATEN_STRUKTUR {
    float temp = 0;
};

//Datenstruktur für das Rtc Memory mit Prüfsumme um die Gültigkeit
//zu überprüfen damit wird die MAC Adresse gespeichert
struct MEMORYDATA {
  uint32_t crc32;
  uint8_t mac[6];
};

//Globale daten
volatile bool callbackCalled;

MEMORYDATA statinfo;

OneWire oneWire(bus);

DallasTemperature sensoren(&oneWire);

//Array um Sensoradressen zu speichern
DeviceAddress adressen;

//Unterprogramm zum Berechnen der Prüfsumme
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;
}

//Schreibt die Datenstruktur statinfo mit korrekter Prüfsumme in das 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));
}

//sucht nach einem geeigneten AccessPoint
void ScanForSlave() {
  bool slaveFound = 0;
  
  int8_t scanResults = WiFi.scanNetworks();
  // reset on each scan

  if (DEBUG) Serial.println("Scan done");
  if (scanResults == 0) {
    if (DEBUG) Serial.println("No WiFi devices in AP Mode found");
  } else {
    if (DEBUG) Serial.print("Found "); 
    if (DEBUG) Serial.print(scanResults); 
    if (DEBUG) Serial.println(" devices ");
    for (int i = 0; i < scanResults; ++i) {
      // Print SSID and RSSI for each device found
      String SSID = WiFi.SSID(i);
      int32_t RSSI = WiFi.RSSI(i);
      String BSSIDstr = WiFi.BSSIDstr(i);

      if (DEBUG) {
        Serial.print(i + 1);
        Serial.print(": ");
        Serial.print(SSID);
        Serial.print(" (");
        Serial.print(RSSI);
        Serial.print(")");
        Serial.println("");
      }
      delay(10);
      // Check if the current device starts with `Thermometer`
      if (SSID.indexOf("Thermometer") == 0) {
        // SSID of interest
        if (DEBUG) {
          Serial.println("Found a Slave.");
          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];
        // wir ermitteln die MAC Adresse und speichern sie im RTC Memory
        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 = 1;
        //Nachdem der AP gefunden wurde können wir abbrechen
        break;
      }
    }
  }
  
  
  if (DEBUG) {
    if (slaveFound) {
      Serial.println("Slave Found, processing..");
    } else {
      Serial.println("Slave Not Found, trying again.");
    }
  }

  // RAM freigeben
  WiFi.scanDelete();
}
// function um eine Sensoradresse zu drucken
void printAddress(DeviceAddress adressen)
{
  for (uint8_t i = 0; i < 8; i++)
  {
    if (adressen[i] < 16) Serial.print("0");
    Serial.print(adressen[i], HEX);
  }
}

void setup() {
  if (DEBUG) {
    Serial.begin(115200); 
    Serial.println("Start");
  }
  pinMode(bus,INPUT_PULLUP);
  //Wir ermitteln die Anzah der Sensoren am Eindraht-Bus
  sensoren.begin();
  if (DEBUG) {
    Serial.print(sensoren.getDeviceCount(), DEC);
    Serial.println(" Sensoren gefunden.");
  }
  //Nun prüfen wir ob einer der Sensoren am Bus ein Temperatur Sensor ist
  if (!sensoren.getAddress(adressen,0)) {
    if (DEBUG) Serial.println("Kein Temperatursensor vorhanden!");
  }
  //adressen anzeigen
  if (DEBUG) {
    Serial.print("Adresse: ");
    printAddress(adressen);
    Serial.println();
  }
  //Nun setzen wir noch die gewünschte Auflösung (9, 10, 11 oder 12 bit)
  sensoren.setResolution(adressen,10);
  //Zur Kontrolle lesen wir den Wert wieder aus
  if (DEBUG) {
    Serial.print("Auflösung = ");
    Serial.print(sensoren.getResolution(adressen), DEC);
    Serial.println();
  }
  sensoren.requestTemperatures(); // Commando um die Temperaturen auszulesen
  //Wir lesen aus dem RTC Memory
  ESP.rtcUserMemoryRead(0, (uint32_t*) &statinfo, sizeof(statinfo));
  if (DEBUG) Serial.println("RTC Done");
  uint32_t crcOfData = calculateCRC32(((uint8_t*) &statinfo) + 4, sizeof(statinfo) - 4);
  WiFi.mode(WIFI_STA); // Station mode for esp-now sensor node
  if (DEBUG) Serial.println("WifiMode");
  if (statinfo.crc32 != crcOfData) { //wir haben keine gültige MAC Adresse
    if (DEBUG) Serial.println("Scan vor Slave");
    ScanForSlave();
    if (DEBUG) {
      Serial.printf("This mac: %s, ", WiFi.macAddress().c_str()); 
      Serial.printf("target mac: %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.printf(", channel: %i\n", WIFI_CHANNEL); 
    }
  }
  if (esp_now_init() != 0) {
    if (DEBUG) Serial.println("*** ESP_Now init failed");
    ESP.restart();
  }
  //ESP Now Controller
  esp_now_set_self_role(ESP_NOW_ROLE_CONTROLLER);
  //Peer Daten initialisieren
  esp_now_add_peer(statinfo.mac, ESP_NOW_ROLE_SLAVE, WIFI_CHANNEL, NULL, 0);
  //wir registrieren die Funktion, die nach dem Senden aufgerufen werden soll
  esp_now_register_send_cb([](uint8_t* mac, uint8_t sendStatus) {
    if (DEBUG) {
      Serial.print("send_cb, status = "); Serial.print(sendStatus); 
      Serial.print(", to 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;
  });
  
  //Flag auf false setzen
  callbackCalled = false;
  //Temperaturmessung starten
  sensoren.requestTemperatures();
  
  delay(750); //750 ms warten bis die Messung fertig ist
  //Temperaturwert holen und in Satenstruktur zum Senden speichern
  DATEN_STRUKTUR data;
  data.temp = sensoren.getTempC(adressen);
  uint8_t bs[sizeof(data)];
  //Datenstruktur in den Sendebuffer kopieren
  memcpy(bs, &data, sizeof(data));
  //Daten an Thermometer senden
  esp_now_send(NULL, bs, sizeof(data)); // NULL means send to all peers
  if (DEBUG) {
    Serial.print("Temperatur: "); Serial.print(data.temp); Serial.println("°C");
  }
}

void loop() {
  //warten bis Daten gesendet wurden
  if (callbackCalled || (millis() > SEND_TIMEOUT)) {
    if (DEBUG) Serial.println("Sleep");
    delay(100);
    //Für 300 Sekunden in den Tiefschlafmodus
    //dann erfolgt ein Reset und der ESP8266 wird neu gestartet
    //Daten im RTC Memory werden beim Reset nicht gelöscht.
    ESP.deepSleep(300E6);
  }
}

Testing:

After the module has been supplied with power, it does not yet know a valid MAC address of the thermometer. It tries to find an open network with the SSID "Thermometer". Since the thermometer hides its SSID about five minutes after startup, we need to trigger a restart of the thermometer. Now the sensor module should find the thermometer and transmit the current temperature value every five minutes. The thermometer should alternately display the local temperature (cottage icon) and the temperature from the sensor module (tree icon).

Esp-8266Projekte für fortgeschritteneSensoren

4 comments

Peter

Peter

Wie weit können die Module denn voneinander entfernt sein?

Peter

Peter

Tolles Projekt! Ist es möglich mehrere entfernte Module zu betreiben?
Viele Grüße Peter

Carsten Jürges

Carsten Jürges

Wenn kein “Thermometer” gefunden wird, landet irgendeine MAC-Adresse in statinfo und das war’s dann erstmal. Daher habe ich Funktion DeleteRtcMemory spendiert, den CRC-Wert “zerstört”. Diese Funktionen wird aufgerufen, wenn das Senden nicht erfolgreich (sendStatus) war …
Dann gibt es beim nächsten Start einen neuen Verbindungsversuch …

Jörg

Jörg

Super, genau so etwas habe ich gesucht.
Vielen Dank für die interessanten Projektvorstellungen.

Leave a comment

All comments are moderated before being published