Smarthome headquarters with ArduiTouch Part 7 - Device for I2C sensors BMP280 and BME 280

Among other small extensions, I would like to present today another remote device that supplies measured values of the I2C sensors BMP280 and/or BME280 to our smart home headquarters.

The circuit is based on a D1 Mini and the sensors BMP280 And BME280. Since these sensors can have an I2C address of 0X77 or 0X76, the program automatically finds out which sensor was connected with which address. Without modification as supplied by AZ Delivery, the BMP280 has the address 0X77 and the BME 280 the address 0X76.

The circuit is very simple. The I2C clock is connected to D1 of the D1 Mini and the data line to D2 of the D1 Mini. For the BMP280, the Chipselect line should be connected to 3.3 V so that the BMP280 starts in I2C mode. On the D1 Mini we connect the RESET input to the pin D0 (purple line). The programme puts the ESP8266 into deep sleep after work has been done. This connection reawakens the ESP8266 after the set time. However, you should remove this connection for flashing.

Sketch:

 

/*
 WLAN pressure sensor BMP280 and or BME280
 ESP Now to ArduiTouch SmartHome
 If the device does not have a valid server MAC address
 a search will bve made to find a WLAN with SSID ATSmartHome
 The server MAC address will be saved as long as thge power supply 
 will not be interrupted.
 The ESP Now protocol is very almost the higher current for network wiil be
 consumed for a short time (us) only. After sending the values, the device
 switches for five minutes into a deep sleep mode with very low power consumption.
*/

library for WiFi
#include <ESP8266WiFi.H>
libraries to use BMP280 and BME280
#include <Adafruit_Sensor.H>
#include <Adafruit_BME280.H>
#include <Adafruit_BMP280.H>
library for the used message protocol
#include "AT_MessageBuffer.h"


library for ESP Now
External "C" {   #include <espnow.H>
}

SSID to search for
#define GW_SSID "ATSmartHome"

flag to switch debug messages on
#define Debug True

#define SEND_TIMEOUT 2000  2 seconds timeout 

define channels for the two sensors
#define CHANNEL_TEMP_BME 0
#define CHANNEL_PRESS_BME 1
#define CHANNEL_ALT_BME 2
#define CHANNEL_HUM_BME 3
#define CHANNEL_TEMP_BMP 4
#define CHANNEL_PRESS_BMP 5
#define CHANNEL_ALT_BMP 6
 
#define SEALEVELPRESSURE_HPA (1013.25)

Data structure to save server MAC address
and a checksum inRTC memory
Struct MEMORYDATA {   uint32_t crc32; checksum for validation   uint8_t Mac[6];
};


Global variables
volatile Bool callbackCalled;


Boolean hasBme = 0;
Boolean hasBmp = 0;

MAC address and Wi-Fi channel
MEMORYDATA statinfo;

AT_MessageBuffer Msg;

Adafruit_BME280 Bme; I2c
Adafruit_BMP280 Bmp; I2c

function to calculate the checksum
uint32_t calculateCRC32(Const uint8_t *Data, Size_t length)
{   uint32_t Crc = 0xffffffff;   while (length--) {     uint8_t C = *Data++;     for (uint32_t  = 0x80;  > 0;  >>= 1) {       Bool Bit = Crc & 0x80000000;       If (C & ) {         Bit = !Bit;       }       Crc <<= 1;       If (Bit) {         Crc ^= 0x04c11db7;       }     }   }   Return Crc;
}

write server MAC and checksum to 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 the access point
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 found in AP Mode");   } 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);       int32_t chl = WiFi.channel(i);       String BSSIDstr = WiFi.BSSIDstr(i);       if (DEBUG) {         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 the current device starts with ATSmartHome`       if (SSID == GW_SSID) {         // 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];         // get the server MAC and save to 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;         //no more search after AP was found         break;       }     }   }         if (DEBUG) {     if (slaveFound) {       Serial.println("Slave Found, processing..");     } else {       Serial.println("Slave Not Found, trying again.");     }   }   // release RAM   WiFi.scanDelete();
}

//function to initilize BME Sensor
//try both valid addresses 77 or 76
boolean initBme() {   boolean status = bme.begin(0x77);     if (!status) status = bme.begin(0x76);   if (!status) {       Serial.println("Could not find a valid BME280 sensor, check wiring!");       hasBme = false;       while (1);   }   return status;
}

//function to initilize BMP Sensor
//try both valid addresses 77 or 76
boolean initBmp() {   boolean status = bmp.begin(0x77);     if (!status) status = bmp.begin(0x76);   if (!status) {       Serial.println("Could not find a valid BME280 sensor, check wiring!");       hasBme = false;       while (1);   }   if (status) {   /* Default settings from datasheet. */     bmp.setSampling(Adafruit_BMP280::MODE_NORMAL,     /* Operating Mode. */                   Adafruit_BMP280::SAMPLING_X2,     /* Temp. oversampling */                   Adafruit_BMP280::SAMPLING_X16,    /* Pressure oversampling */                   Adafruit_BMP280::FILTER_X16,      /* Filtering. */                   Adafruit_BMP280::STANDBY_MS_500); /* Standby time. */   }   return status;
}

void setup() {   if (DEBUG) {     Serial.begin(115200);      Serial.println("Start");   }   //get local MAC address to use it as device id   String strmac = WiFi.macAddress();   if (DEBUG) {     Serial.print("My MAC address = ");     Serial.println(strmac);   }   msg.setId(strmac);   msg.clear();   hasBme = initBme();   if (hasBme && DEBUG) {     Serial.println("Found Sensor BME");   }   hasBmp = initBmp();   if (hasBmp && DEBUG) {     Serial.println("Found Sensor BMP");   }   //read server MAC from 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) { //if checksum different we do not have a valid server MAC     if (DEBUG) Serial.println("Scan for slave");     ScanForSlave();     //for (uint8_t i = 0; i<6;i++) statinfo.mac[i] = gwmac[i];     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]);      }   }   if (esp_now_init() != 0) {     if (DEBUG) Serial.println("*** ESP_Now init failed");     ESP.restart();   }   //ESP Now Controller   WiFi.setAutoConnect(False);   esp_now_set_self_role(ESP_NOW_ROLE_CONTROLLER);   uint8_t Ch = esp_now_get_peer_channel(statinfo.Mac);   If (Debug) Serial.Printf("Channel = %i'r"n",Ch);   initialize Peer data   Int Res = esp_now_add_peer(statinfo.Mac, ESP_NOW_ROLE_CONTROLLER, 1, Null, 0);   If (Res==0) Serial.println("Successfully paired");   register callback   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;   });      set flag to false   callbackCalled = False;      start measurement   read values and save for sending   If (hasBme) {     If (Debug){       Serial.Print("Temperature = ");       Serial.Print(Bme.readTemperature());       Serial.println(" *C");          Serial.Print("Pressure = ");          Serial.Print(Bme.readPressure() / 100.0F);       Serial.println(" hPa");          Serial.Print("Approx. Altitude = ");       Serial.Print(Bme.readAltitude(SEALEVELPRESSURE_HPA));       Serial.println(" m");          Serial.Print("Humidity = ");       Serial.Print(Bme.readHumidity());       Serial.println(" %");           }     Msg.addCelsius(Bme.readTemperature(), CHANNEL_TEMP_BME);     Msg.addHektoPascal(Bme.readPressure()/100.0F, CHANNEL_PRESS_BME);     Msg.addMeter(Bme.readAltitude(SEALEVELPRESSURE_HPA), CHANNEL_ALT_BME);     Msg.addPercent(Bme.readHumidity(), CHANNEL_HUM_BME);   }   If (hasBmp) {     If (Debug) {       Serial.Print(Q("Temperature = "));       Serial.Print(Bmp.readTemperature());       Serial.println(" *C");          Serial.Print(Q("Pressure = "));       Serial.Print(Bmp.readPressure());       Serial.println("Pa");          Serial.Print(Q("Approx altitude = "));       Serial.Print(Bmp.readAltitude(SEALEVELPRESSURE_HPA)); /* Adjusted to local forecast! */       Serial.println(" m");            }     Msg.addCelsius(Bmp.readTemperature(), CHANNEL_TEMP_BMP);     Msg.addHektoPascal(Bmp.readPressure()/100.0F, CHANNEL_PRESS_BMP);     Msg.addMeter(Bmp.readAltitude(SEALEVELPRESSURE_HPA), CHANNEL_ALT_BMP);   }   copy datastructure into sendbuffer   uint8_t Buf[255];   uint8_t Sz;   Sz = 255;   If (Msg.fillBuffer(&Buf[0], &Sz)) esp_now_send(Null, Buf, Sz); NULL means send to all peers
}

Void Loop() {   wait for data to be sent   If (callbackCalled || (millis() > SEND_TIMEOUT)) {     If (Debug) Serial.println("Sleep");     Delay(100);     go for 10 seconds into deep sleep mode     wakeup by reset     reset does not delete data in RTCmemory     Esp.deepSleep(10E6);   }
}

 

In order for this sketch to be compiled, in addition to the libraries for the sensors, you need the latest version of my ATMessageBuffer Library.

The code of the sketch can also be found in the examples of the new version of the ATSmartHome Library.

After the Smart-Home headquarters has been updated, we compile the Sketch and load it into the D1 Mini. After a short time, the MAC address of the D1 Mini should appear in the lower blue bar of the Smarthome headquarters. We click once long (more than 3 s) on this MAC address. The registration page is displayed. We can give the device a name and after we click on Save we should see the new readings on the display. If other devices have already been registered at the Smarthome headquarters, the new channels may also appear on one of the other pages. The automatic registration searches for free places in the display and displays the new channels as small widgets.

By long clicking on the widgets you get to their configuration page and can change the appearance and position. The following image shows a possible configuration if we use both BMx sensors at the same time.

Display

 

Finally, a small extension on the website of the Smarthome headquarters. If an actuator widget is displayed, you have the possibility to turn the removed device on or off the same as on the smart home center itself, by clicking on or off. For the actuator widget see also part 3 of this series

Here again the links to all previous parts:

Have fun :)

 

 

 

Esp-8266Projekte für fortgeschritteneSensorsSmart home

10 comments

Franz Patzal

Franz Patzal

Hi Greg,

look in part 6:

Ein wichtiger Hinweis zu Beginn!

Die ArduiTouch Smarthome Zentrale funktioniert nur mit dem ESP32 stabil. Es zeigte sich, dass auf Grund des deutlich geringeren RAM des ESP8266 kein stabiler Betrieb möglich ist. Ich habe daher die Version für den ESP8266 wieder aus dem Repository entfernt. Die aktuelle Version der ATSmartHome Bibliothek kann nicht mehr mit dem Sketch für ESP8266 kompiliert werden!

Greg

Greg

Hi. I am trying to follow the examples fro the SmartHome project, but it refers to the library ESP8266WiFi.H, even for ESP32 examples . Where can I find this library?
Many thanks!
Greg.

Wolfgang Händel

Wolfgang Händel

Hallo Herr Lechner….
Bis auf die etwas begrenzte Reichweite der Arduitouch Zentrale funktioniert bei mir alles recht gut.
Was das Herz des Smarthome Einsteigers natürlich höher schlagen lassen würde, wäre ein Sensormodul für ein binäres Signal (Klingeltaster, Bewegungssensor, Dämmerungssensor uvm.)
Das binäre Signal eines solchen Moduls müsste in der Zentrale ausgewertet werden und bestimmte Funktionen (wie z.B. das Relaismodul aus Teil 3) auslösen.
Haben sie so etwas geplant? Meines Erachtens gehört so etwas zu den Standard-Features eines SmartHome-Systems.
Ansonsten vielen Dank für die interessanten Blogs zum Thema SmartHome und für die Mühe, die sie sich damit gegeben haben bzw. noch geben..
Wolfie

Siegl Reinhard

Siegl Reinhard

Hallo
Ich bekomme die Smart Home V" nicht zum laufen.Es stoppt schon bei #include “SPIFFS.h”.
Bei der alten Smart Home konnte ich alle Beiträge nachbauen.
Danke für die Unterstützung schon im Vorraus.

Matthias H.

Matthias H.

Hallo Herr Lechner,
könnte man die Stimmungslaterne https://www.az-delivery.de/blogs/azdelivery-blog-fur-arduino-und-raspberry-pi/mehrere-feuer-programme-fuer-unsere-stimmungslaterne?pos=3&_sid=3afeceff9&ss=r nicht mit der Smarthome Zenrale steuern?

Reinhard Schneider

Reinhard Schneider

Wie das mit den Fehlern so ist, es hat sich auch bei mir einer eingeschlichen.
Richtig muss es für 5 min deep-sleep heißen:

ESP.deepSleep(300*10E6);

Reinhard Schneider

Reinhard Schneider

Leider haben sich in das o.g. Programm einige kleine Fehler eingeschlichen:
1. /function to initilize BMP Sensor
….
Richtig:
Serial.println(“Could not find a valid BMP280 sensor, check wiring!”);
hasBmp = false;

2. In der Beschreibung heißt es
….
After sending the values, the device switches for five minutes into a deep sleep mode with very low power consumption.
Der Code ist aber in void loop()
….
//go for 10 seconds into deep sleep mode
//wakeup by reset
//reset does not delete data in RTCmemory
ESP.deepSleep(10E6);

Für 5 Minuten müsste es heißen:

ESP.deepSleep(600*10E6);

Reinhard  Schneider

Reinhard Schneider

Das obige Programm läuft nur mit der Version 0.15.0 von “ESPiLight”.
Die aktuelle Version 0.16.0 erzeugt Fehlermeldungen.
Ist eine Anpassung des ino-Files geplant?

Joe

Joe

Bei den Daten der beiden Sensoren kann man sagen das es Roh-Daten sind.
Jetzt muß man nur eine Kalibrierung mit einem Referenz-Meßgerät höherer Genauigkeit durchführen. Und dann die Rohdaten entsprechen mit einem Justage-Wert belegen.
Es wird immer eine geringfügige Abweichung zwischen den Sensoren geben.
(Rauschen, Eigentemperatur etc, Meßfehler).
Die redudante Messung ist in Bereichen bei denen der Ausfall, starker Unterschied der Meßwerte, ermittelt werden muß. Rreinraum, technische zu überwachende Prozeße.
Temperatur-Differenzen werden auch genutzt um Strömungen, in dem Fall Luft, zu ermitteln.

Also nicht vergessen Justage-Wert und Kalibrierung.

Marcus Klein

Marcus Klein

Beide Sensoren sind offenbar recht nah beieinander untergebracht. Sie messen trotzdem eine Differenz von einem kompletten Grad Celsius. Das ist ein absolutes No-Go, wenn man damit versucht Heimautomatisierung zu betreiben und die Heizung zu steuern. Daraus folgt direkt, dass der Heizkreis am Sensor mit der kleineren Temperatur immer heizt und der Kreis am Sensor mit der größeren Temperatur immer aus ist. Bei einem Grad Unterschied und nicht verschlossenen Türen zwischen den beiden Räumen und gut isolierter Hütte, wandert die Wärme ausreichend zwischen den beiden Räumen. Das trägt nicht wirklich zum Wohnkomfort bei, sondern stört diesen besonders.
Wie gedenken Sie, diesen Fehler zu kompensieren?

Leave a comment

All comments are moderated before being published

Recommended blog posts

  1. Install ESP32 now from the board manager
  2. Lüftersteuerung Raspberry Pi
  3. Arduino IDE - Programmieren für Einsteiger - Teil 1
  4. ESP32 - das Multitalent
  5. OTA - Over the Air - ESP programming via WLAN