Výroba Wi-Fi teploměru s ESP8266
Wi-Fi teploměr jsem skládal z těchto součástek:
Pro programování jsem na destičku esp8266 nahrál Micropython. Cílem je, aby se destička připojovala k Mosquitto a posílala tak zprávy skrze MQTT protokol.
Warning
Edit 2.1.2024: Nakonec jsem Micropython opustil, protože se mi nepodařilo dosáhnout stabilního obnovování připojení při výpadku Wi-Fi či MQTT. Původní příspěvek ponechávám, nicméně na závěr připojím kód v C++ pro vyčítání dat ze senzoru a automatickou obnovu spojení v případě výpadku.
Jako výborný zdroj posloužil článek ESP32 MQTT – Publish and Subscribe with Arduino IDE senzor BME280. Nicméně v článku nepoužívají Python - kód jsem čerpat tedy odjinud.
Později jsem objevil tento článek MicroPython: BME280 with ESP32 and ESP8266 (Pressure, Temperature, Humidity) ze kterého jsem čerpal jak zapojení senzoru, tak kód Pythonu pro senzor.
Připojení destičky k Wi-Fi
Micropython počítá se 2 soubory:
- boot.py
- main.py
Snažil jsem se pochopit na co který použít. Pochopil jsem, že nejprve se po startu načte boot.py a potom teprve main.py. V rámci best practises je doporučeno používat oba, ale vzápětí jsem se dočetl, že když se rozhodnu použít jenom main.py a do něj nahrát veškerý svůj kód, tak je to také ok.
Zatím jsem to tedy udělal tak, že nastavení a připojení Wi-Fi jsem nahrál do boot.py a čtení dat z čidla a obsluha MQTT je zase main.py.
Nyní tedy kód boot.py pro připojení k Wi-Fi:
# This file is executed on every boot (including wake-boot from deepsleep) #import esp #esp.osdebug(None) import uos, machine #uos.dupterm(None, 1) # disable REPL on UART(0) import gc #import webrepl #webrepl.start() gc.collect() SSID = 'Wi-fi' PASS = 'super_secret_password' def connect(): import network sta_if = network.WLAN(network.STA_IF) if not sta_if.isconnected(): sta_if.active(True) sta_if.connect(SSID, PASS) while not sta_if.isconnected(): pass # wait till connection print('network config:', sta_if.ifconfig()) connect()
První co mě napadlo je, jak ošetřit to, že se Wi-Fi odpojí - třeba spadne síť. Jak vyřešit opětovný reconnect. Dobrá zpráva je, že není potřeba dělat nic a současný kód stačí. Testoval jsem kód tak, že jsem vypínal a zapínal Wi-Fi na mém routeru a vždycky, když jsem Wi-Fi zapnul, tak se připojilo i zařízení ESP8266 aniž bych musel cokoli dělat. Což mě docela mile překvapilo.
Změna Wi-Fi sítě
Narazil jsem na zajímavý problém v kódu. Zdá se, že ESP8266 si po úspěšném připojení k Wi-Fi ukládá SSID sítě a heslo do paměti. Protože v rámci testování jsem vytvořil jinou síť, změnil jsem v kódu údaje a ESP8266 se ne a ne připojit k nové síti. Byl jsem v situaci, kdy obě sítě běžely a ESP8266 se připojovalo pořád k původní, i když v kódu po ní nic nezůstalo. A nepomohl mi ani reset zařízení či odpojení od napájení.
Nakonec jsem to vyřešil v kódu tak, že jsem na první spuštění upravil funkci connect() takto:
def connect(): import network sta_if = network.WLAN(network.STA_IF) sta_if.connect(SSID, PASS) if not sta_if.isconnected(): sta_if.active(True) sta_if.connect(SSID, PASS) while not sta_if.isconnected(): pass # wait till connection print('network config:', sta_if.ifconfig())
Jakmile jsme se připojil k nové Wi-Fi, tak jsem kód vrátil do původního stavu. Nechtělo se mi hlouběji zkoumat, jak to vlastně funguje a hledat elegantnější řešení, protože pro můj účel mi to stačilo.
BME280 MicroPython knihovna
Pro senzor BME280 je potřeba knihovna, která bude umět se senzorem pracovat. Knihovna musí být pro Python a našel jsem ji v tomto zdroji MicroPython: BME280 with ESP32 and ESP8266 (Pressure, Temperature, Humidity). Stačí ji nakopírovat do ESP8266 a uložit jako soubor BME280.py.
from machine import I2C import time # BME280 default address. BME280_I2CADDR = 0x76 # Operating Modes BME280_OSAMPLE_1 = 1 BME280_OSAMPLE_2 = 2 BME280_OSAMPLE_4 = 3 BME280_OSAMPLE_8 = 4 BME280_OSAMPLE_16 = 5 # BME280 Registers BME280_REGISTER_DIG_T1 = 0x88 # Trimming parameter registers BME280_REGISTER_DIG_T2 = 0x8A BME280_REGISTER_DIG_T3 = 0x8C BME280_REGISTER_DIG_P1 = 0x8E BME280_REGISTER_DIG_P2 = 0x90 BME280_REGISTER_DIG_P3 = 0x92 BME280_REGISTER_DIG_P4 = 0x94 BME280_REGISTER_DIG_P5 = 0x96 BME280_REGISTER_DIG_P6 = 0x98 BME280_REGISTER_DIG_P7 = 0x9A BME280_REGISTER_DIG_P8 = 0x9C BME280_REGISTER_DIG_P9 = 0x9E BME280_REGISTER_DIG_H1 = 0xA1 BME280_REGISTER_DIG_H2 = 0xE1 BME280_REGISTER_DIG_H3 = 0xE3 BME280_REGISTER_DIG_H4 = 0xE4 BME280_REGISTER_DIG_H5 = 0xE5 BME280_REGISTER_DIG_H6 = 0xE6 BME280_REGISTER_DIG_H7 = 0xE7 BME280_REGISTER_CHIPID = 0xD0 BME280_REGISTER_VERSION = 0xD1 BME280_REGISTER_SOFTRESET = 0xE0 BME280_REGISTER_CONTROL_HUM = 0xF2 BME280_REGISTER_CONTROL = 0xF4 BME280_REGISTER_CONFIG = 0xF5 BME280_REGISTER_PRESSURE_DATA = 0xF7 BME280_REGISTER_TEMP_DATA = 0xFA BME280_REGISTER_HUMIDITY_DATA = 0xFD class Device: """Class for communicating with an I2C device. Allows reading and writing 8-bit, 16-bit, and byte array values to registers on the device.""" def __init__(self, address, i2c): """Create an instance of the I2C device at the specified address using the specified I2C interface object.""" self._address = address self._i2c = i2c def writeRaw8(self, value): """Write an 8-bit value on the bus (without register).""" value = value & 0xFF self._i2c.writeto(self._address, value) def write8(self, register, value): """Write an 8-bit value to the specified register.""" b=bytearray(1) b[0]=value & 0xFF self._i2c.writeto_mem(self._address, register, b) def write16(self, register, value): """Write a 16-bit value to the specified register.""" value = value & 0xFFFF b=bytearray(2) b[0]= value & 0xFF b[1]= (value>>8) & 0xFF self.i2c.writeto_mem(self._address, register, value) def readRaw8(self): """Read an 8-bit value on the bus (without register).""" return int.from_bytes(self._i2c.readfrom(self._address, 1),'little') & 0xFF def readU8(self, register): """Read an unsigned byte from the specified register.""" return int.from_bytes( self._i2c.readfrom_mem(self._address, register, 1),'little') & 0xFF def readS8(self, register): """Read a signed byte from the specified register.""" result = self.readU8(register) if result > 127: result -= 256 return result def readU16(self, register, little_endian=True): """Read an unsigned 16-bit value from the specified register, with the specified endianness (default little endian, or least significant byte first).""" result = int.from_bytes( self._i2c.readfrom_mem(self._address, register, 2),'little') & 0xFFFF if not little_endian: result = ((result << 8) & 0xFF00) + (result >> 8) return result def readS16(self, register, little_endian=True): """Read a signed 16-bit value from the specified register, with the specified endianness (default little endian, or least significant byte first).""" result = self.readU16(register, little_endian) if result > 32767: result -= 65536 return result def readU16LE(self, register): """Read an unsigned 16-bit value from the specified register, in little endian byte order.""" return self.readU16(register, little_endian=True) def readU16BE(self, register): """Read an unsigned 16-bit value from the specified register, in big endian byte order.""" return self.readU16(register, little_endian=False) def readS16LE(self, register): """Read a signed 16-bit value from the specified register, in little endian byte order.""" return self.readS16(register, little_endian=True) def readS16BE(self, register): """Read a signed 16-bit value from the specified register, in big endian byte order.""" return self.readS16(register, little_endian=False) class BME280: def __init__(self, mode=BME280_OSAMPLE_1, address=BME280_I2CADDR, i2c=None, **kwargs): # Check that mode is valid. if mode not in [BME280_OSAMPLE_1, BME280_OSAMPLE_2, BME280_OSAMPLE_4, BME280_OSAMPLE_8, BME280_OSAMPLE_16]: raise ValueError( 'Unexpected mode value {0}. Set mode to one of ' 'BME280_ULTRALOWPOWER, BME280_STANDARD, BME280_HIGHRES, or ' 'BME280_ULTRAHIGHRES'.format(mode)) self._mode = mode # Create I2C device. if i2c is None: raise ValueError('An I2C object is required.') self._device = Device(address, i2c) # Load calibration values. self._load_calibration() self._device.write8(BME280_REGISTER_CONTROL, 0x3F) self.t_fine = 0 def _load_calibration(self): self.dig_T1 = self._device.readU16LE(BME280_REGISTER_DIG_T1) self.dig_T2 = self._device.readS16LE(BME280_REGISTER_DIG_T2) self.dig_T3 = self._device.readS16LE(BME280_REGISTER_DIG_T3) self.dig_P1 = self._device.readU16LE(BME280_REGISTER_DIG_P1) self.dig_P2 = self._device.readS16LE(BME280_REGISTER_DIG_P2) self.dig_P3 = self._device.readS16LE(BME280_REGISTER_DIG_P3) self.dig_P4 = self._device.readS16LE(BME280_REGISTER_DIG_P4) self.dig_P5 = self._device.readS16LE(BME280_REGISTER_DIG_P5) self.dig_P6 = self._device.readS16LE(BME280_REGISTER_DIG_P6) self.dig_P7 = self._device.readS16LE(BME280_REGISTER_DIG_P7) self.dig_P8 = self._device.readS16LE(BME280_REGISTER_DIG_P8) self.dig_P9 = self._device.readS16LE(BME280_REGISTER_DIG_P9) self.dig_H1 = self._device.readU8(BME280_REGISTER_DIG_H1) self.dig_H2 = self._device.readS16LE(BME280_REGISTER_DIG_H2) self.dig_H3 = self._device.readU8(BME280_REGISTER_DIG_H3) self.dig_H6 = self._device.readS8(BME280_REGISTER_DIG_H7) h4 = self._device.readS8(BME280_REGISTER_DIG_H4) h4 = (h4 << 24) >> 20 self.dig_H4 = h4 | (self._device.readU8(BME280_REGISTER_DIG_H5) & 0x0F) h5 = self._device.readS8(BME280_REGISTER_DIG_H6) h5 = (h5 << 24) >> 20 self.dig_H5 = h5 | ( self._device.readU8(BME280_REGISTER_DIG_H5) >> 4 & 0x0F) def read_raw_temp(self): """Reads the raw (uncompensated) temperature from the sensor.""" meas = self._mode self._device.write8(BME280_REGISTER_CONTROL_HUM, meas) meas = self._mode << 5 | self._mode << 2 | 1 self._device.write8(BME280_REGISTER_CONTROL, meas) sleep_time = 1250 + 2300 * (1 << self._mode) sleep_time = sleep_time + 2300 * (1 << self._mode) + 575 sleep_time = sleep_time + 2300 * (1 << self._mode) + 575 time.sleep_us(sleep_time) # Wait the required time msb = self._device.readU8(BME280_REGISTER_TEMP_DATA) lsb = self._device.readU8(BME280_REGISTER_TEMP_DATA + 1) xlsb = self._device.readU8(BME280_REGISTER_TEMP_DATA + 2) raw = ((msb << 16) | (lsb << 8) | xlsb) >> 4 return raw def read_raw_pressure(self): """Reads the raw (uncompensated) pressure level from the sensor.""" """Assumes that the temperature has already been read """ """i.e. that enough delay has been provided""" msb = self._device.readU8(BME280_REGISTER_PRESSURE_DATA) lsb = self._device.readU8(BME280_REGISTER_PRESSURE_DATA + 1) xlsb = self._device.readU8(BME280_REGISTER_PRESSURE_DATA + 2) raw = ((msb << 16) | (lsb << 8) | xlsb) >> 4 return raw def read_raw_humidity(self): """Assumes that the temperature has already been read """ """i.e. that enough delay has been provided""" msb = self._device.readU8(BME280_REGISTER_HUMIDITY_DATA) lsb = self._device.readU8(BME280_REGISTER_HUMIDITY_DATA + 1) raw = (msb << 8) | lsb return raw def read_temperature(self): """Get the compensated temperature in 0.01 of a degree celsius.""" adc = self.read_raw_temp() var1 = ((adc >> 3) - (self.dig_T1 << 1)) * (self.dig_T2 >> 11) var2 = (( (((adc >> 4) - self.dig_T1) * ((adc >> 4) - self.dig_T1)) >> 12) * self.dig_T3) >> 14 self.t_fine = var1 + var2 return (self.t_fine * 5 + 128) >> 8 def read_pressure(self): """Gets the compensated pressure in Pascals.""" adc = self.read_raw_pressure() var1 = self.t_fine - 128000 var2 = var1 * var1 * self.dig_P6 var2 = var2 + ((var1 * self.dig_P5) << 17) var2 = var2 + (self.dig_P4 << 35) var1 = (((var1 * var1 * self.dig_P3) >> 8) + ((var1 * self.dig_P2) >> 12)) var1 = (((1 << 47) + var1) * self.dig_P1) >> 33 if var1 == 0: return 0 p = 1048576 - adc p = (((p << 31) - var2) * 3125) // var1 var1 = (self.dig_P9 * (p >> 13) * (p >> 13)) >> 25 var2 = (self.dig_P8 * p) >> 19 return ((p + var1 + var2) >> 8) + (self.dig_P7 << 4) def read_humidity(self): adc = self.read_raw_humidity() # print 'Raw humidity = {0:d}'.format (adc) h = self.t_fine - 76800 h = (((((adc << 14) - (self.dig_H4 << 20) - (self.dig_H5 * h)) + 16384) >> 15) * (((((((h * self.dig_H6) >> 10) * (((h * self.dig_H3) >> 11) + 32768)) >> 10) + 2097152) * self.dig_H2 + 8192) >> 14)) h = h - (((((h >> 15) * (h >> 15)) >> 7) * self.dig_H1) >> 4) h = 0 if h < 0 else h h = 419430400 if h > 419430400 else h return h >> 12 @property def temperature(self): "Return the temperature in degrees." t = self.read_temperature() ti = t // 100 td = t - ti * 100 return "{}.{:02d}C".format(ti, td) @property def pressure(self): "Return the temperature in hPa." p = self.read_pressure() // 256 pi = p // 100 pd = p - pi * 100 return "{}.{:02d}hPa".format(pi, pd) @property def humidity(self): "Return the humidity in percent." h = self.read_humidity() hi = h // 1024 hd = h * 100 // 1024 - hi * 100 return "{}.{:02d}%".format(hi, hd)
Čtení dat ze senzoru BME280
S knihovnou je pak jednoduché vyčítat hodnoty ze senzoru:
# Complete project details at https://RandomNerdTutorials.com from machine import Pin, I2C from time import sleep import BME280 # ESP32 - Pin assignment i2c = I2C(scl=Pin(22), sda=Pin(21), freq=10000) # ESP8266 - Pin assignment #i2c = I2C(scl=Pin(5), sda=Pin(4), freq=10000) while True: bme = BME280.BME280(i2c=i2c) temp = bme.temperature hum = bme.humidity pres = bme.pressure # uncomment for temperature in Fahrenheit #temp = (bme.read_temperature()/100) * (9/5) + 32 #temp = str(round(temp, 2)) + 'F' print('Temperature: ', temp) print('Humidity: ', hum) print('Pressure: ', pres) sleep(5)
Posílání dat skrze MQTT v MicroPythonu
Nejprve jsem si nainstaloval MQTT server Mosquitto a napsal jednoduchý kód v MicroPythonu pro komunikaci, dle článku Secrets of MicroPython: MQTT on ESP32.
Ochrana kódu proti odpojení od MQTT serveru
Wi-fi a její automatické připojení je sice vyřešeno, ale jiná věc je, že může sehlat spojení s MQTT serverem. Prostě a jednoduše MQTT server třeba spadne a zařízení nemůže naslouchat nebo posílat data.
V článku MicroPython – Getting Started with MQTT on ESP32/ESP8266 jsem se inspiroval, jak toto ošetřit. Řešení není až tak uspokojivé, jak jsem si představoval, protože ve chvíli, kdy se připojení rozpadne a dojde na MQTT k chybě, restartuje se celé zařízení ESP8266. Moje představa byla, že se zařízení bude zkoušet obnovit jenom MQTT připojení.
Protože se mi to nedařilo, tak jsem se prozatím spokojil s restartem celého zařízení. Výsledný kód tak vypadá takto:
from umqtt.simple import MQTTClient from time import sleep from machine import Pin, I2C from time import sleep import ubinascii import BME280 CLIENT_NAME = ubinascii.hexlify(machine.unique_id()) BROKER_ADDR = '192.168.1.120' print(CLIENT_NAME) # ESP8266 - Pin assignment i2c = I2C(scl=Pin(5), sda=Pin(4), freq=10000) def connect_and_subscribe(): mqttc = MQTTClient(CLIENT_NAME, BROKER_ADDR, keepalive=60) mqttc.connect() return mqttc def restart_and_reconnect(): print('Failed to connect to MQTT broker. Reconnecting...') sleep(10) machine.reset() try: mqttc = connect_and_subscribe() except OSError as e: restart_and_reconnect() BTN_TOPIC = b'dum/pracovna/svetelnost' while True: bme = BME280.BME280(i2c=i2c) temp = bme.temperature hum = bme.humidity pres = bme.pressure print('Temperature: ', temp , ' hum: ', hum ) try: mqttc.publish( BTN_TOPIC, str('temp:'+ temp + ', hum:' + hum + ', pres: ' + pres).encode() ) except OSError as e: restart_and_reconnect() sleep(5)
Úprava kódu po zapnutí autentizace na MQTT
Nejprve jsem musel přidat proměné s uživatelským jménem a heslem:
USER = b'majordomus' PASSWORD = b'supersecretpassword'
A poté upravit připojení tak, aby bralo proměnné v potaz:
mqttc = MQTTClient(CLIENT_NAME, BROKER_ADDR, 1883, USER, PASSWORD, keepalive=60)
Problém s přerušením spojení s MQTT serverem
V provozu se mi ukázalo, že když dojde k přerušení spojení s MQTT serverem, tak mi nezbyde nic jiného, než zařízení fyzicky resetovat. Což není zrovna příjemný počin - zejména, pokud budu mít více takových zařízení. Hledal jsem způsob, jak kontrolovat, zda nedošlo k přerušení spojení. Protože výjimky pro spuštění restart_and_reconnect nebyla v mém případě spuštěna.
Proto jsem vytvořil funkci, která kontroluje, zda je připojení aktivní. V rámci funkce posílám testovací zprávu na mqtt servrer. Podmínkou je, že řeším Quality Of Service = 1, tedy čekám na odpověď ze serveru, zda byla zpráva opravdu doručena. Pokud nebyla, vyvolá se výjimka.
def is_connected(mqttc): try: mqttc.publish(b"connection_test", b"test", qos=1) return True except OSError: return False
Warning
Edit 2.1.2024: Nakonec se ukázalo, že ani toto ošetření nefunguje. Zařízení se prostě po restartu MQTT serveru nebo Wi-Fi sítě nepřipojí. A co ještě hůř - do 14 dnů od spuštění se náhodně odpojí - aniž by došlo k výpadku na síti. Samozřejmě může dojít k mikrovýpadku, který ani nezaznamenám a to může být příčina.
Proto jsem použití MicroPython přehodnotil. Vede mě k tomu víc důvodů:
- softwarová podpora - je mnoho kódu v C pro Arduino, ale málo pro MicroPython. Pro další vzdělávání je to problém. Navíc u MicroPythonu se musí využívat knihovny, které jsou nějak minimalizované. Proto kód pro Micropython není jednoduše přenesitelný na Python a naopak, jak jsem si mylně sliboval.
- výkon/rychlost - program zkompilovaný pro C++ mi přijde, že se připojuje rychleji k síti a MQTT
- bezpečnost použití - kód v C je kompilovaný. Takže když někdo přijde k zařízení s esp8266, nebude mít jednoduché vyčíst hesla k Wi-Fi, certifikát atp. Kdežto při použití Micro Pythonu to bude velmi jednoduché. Stačí zařízení na chvíli připojit k USB portu a program a certifikát v otevřené podobě.
- od C++ očekávám větší stabilitu - co se týče čipu esp8266
Proto jsem myšlenku použití Micro Pythonu pro moje účely opustil a raději si napíšu program v C++. V rámci stránky připojuji i kód v C++ pro esp8266.
Finální verze kódu po dalších úpravách
from umqtt.simple import MQTTClient from time import sleep from machine import Pin, I2C from time import sleep import ubinascii import BME280 import ujson CLIENT_NAME = ubinascii.hexlify(machine.unique_id()) BROKER_ADDR = '192.168.1.20' USER = b'majordomus' PASSWORD = b'MQTTuserPassword' MQTT_TOPIC = b'dum/sensor/bme280' with open("key.der", 'rb') as f: key = f.read() with open("cert.der", 'rb') as f: cert = f.read() ssl_params = dict() ssl_params["cert"] = cert ssl_params["key"] = key print(CLIENT_NAME) # ESP8266 - Pin assignment i2c = I2C(scl=Pin(5), sda=Pin(4), freq=10000) def connect_and_subscribe(): mqttc = MQTTClient(CLIENT_NAME, BROKER_ADDR, 8883, USER, PASSWORD, ssl=True, ssl_params=ssl_params, keepalive=60) mqttc.connect() return mqttc def restart_and_reconnect(): print('Failed to connect to MQTT broker. Reconnecting...') sleep(10) # Místo resetování ESP8266 zkuste znovu navázat spojení s MQTT serverem try: mqttc = connect_and_subscribe() except OSError as e: restart_and_reconnect() # alternativou je reset celého zařízení #machine.reset() def is_connected(mqttc): try: mqttc.publish(b"connection_test", b"test", qos=1) return True except OSError: return False try: mqttc = connect_and_subscribe() except OSError as e: restart_and_reconnect() while True: bme = BME280.BME280(i2c=i2c) data = {} data['temperature'] = bme.temperature data['humidity'] = bme.humidity data['pressure'] = bme.pressure if not is_connected(mqttc): restart_and_reconnect() print(ujson.dumps(data)) try: mqttc.publish( MQTT_TOPIC, str(ujson.dumps(data)).encode() ) except OSError as e: restart_and_reconnect() sleep(5)
Zdroje s inspirací
Kód pro teploměr s ESP8266 napsaný v C++
I když jsem začal stránku s tím, že budu používat MicroPython, přešel jsem kvůli stabilitě a dalším důvodům k C++. Co se týče certifikátů, tak řešení s certifikáty je popsáno na stránce Self signed certificate. A tady už je kód v C++ pro ESP8266 kmpilovaný v Arduino IDE:
#include <ESP8266WiFi.h> #include <WiFiClientSecure.h> #include <PubSubClient.h> #include <Wire.h> #include <Adafruit_Sensor.h> #include <Adafruit_BME280.h> #include <ArduinoJson.h> #include "client_crt.h" // client certificate #include "client_key.h" // client key const char* WiFiName = "IOT-WiFi"; const char* WiFiPassword = "supersecretpassword"; // MQTT connection const char* clientName = "thermometer"; const char* mqttBroker = "192.168.0.120"; const int mqttPort = 8883; const char* mqttUser = "user"; const char* mqttPassword = "supersecretpassword"; const char* mqttTopic = "/sensor/bme280"; Adafruit_BME280 bme; WiFiClientSecure espClient; PubSubClient mqttClient(espClient); void setupWiFi() { WiFi.mode(WIFI_STA); WiFi.begin(WiFiName, WiFiPassword); // connect to WiFi Serial.print("Connecting to WiFi..."); // wait for connecting to the server // during this waiting it will write dot to serial link while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } // write new line, Wi-Fi network and IP address of connection Serial.println(""); Serial.print("Connected to WiFi "); Serial.println(WiFiName); Serial.print("IP address: "); Serial.println(WiFi.localIP()); WiFi.setAutoReconnect(true); WiFi.persistent(true); } void reconnectMQTT() { int retryCount = 0; while (!mqttClient.connected()) { Serial.println("Attempting to connect to MQTT server..."); // SSL/TLS certificate and key settings // The BearSSL::X509List and BearSSL::PrivateKey objects must be in scope (alive) // for the duration of their usage by espClient. If these objects are defined // within a separate function and used here, they will be destroyed when that // function exits, leading to undefined behavior and potential device resets. // A solution to this is to declare them as global variables if they need to // be used across multiple functions. BearSSL::X509List cert(cert_der, cert_der_len); BearSSL::PrivateKey key(key_der, key_der_len); espClient.setInsecure(); espClient.setClientRSACert(&cert, &key); // Setting the client's cert and key mqttClient.setServer(mqttBroker, mqttPort); if (mqttClient.connect(clientName, mqttUser, mqttPassword)) { Serial.println("Connected to MQTT"); } else { Serial.print("Failed, rc="); Serial.print(mqttClient.state()); Serial.println(" trying again in 5 seconds"); delay(5000); // 5-second delay between attempts retryCount++; if (retryCount > 5) { // Restart the ESP if it fails to connect 5 times //ESP.restart(); } } } } void setup() { // put your setup code here, to run once: // start communication on serial line Serial.begin(9600); // Inicializace BME280 if (!bme.begin(0x76)) { // BME280 I2C sensor address Serial.println("Nepodařilo se najít senzor BME280!"); while (1); } setupWiFi(); reconnectMQTT(); } void loop() { // put your main code here, to run repeatedly: if (!mqttClient.connected()) { reconnectMQTT(); } mqttClient.loop(); // send message each 5 sec static unsigned long lastMsg = 0; unsigned long now = millis(); if (now - lastMsg > 5000) { lastMsg = now; // read data from BME280 float humidity = bme.readHumidity(); float temperature = bme.readTemperature(); float pressure = bme.readPressure() / 100.0F; // Vytvoření JSON objektu StaticJsonDocument<200> doc; doc["humidity"] = humidity; doc["temperature"] = temperature; doc["pressure"] = pressure; // Serializace JSON objektu do řetězce char jsonBuffer[200]; serializeJson(doc, jsonBuffer); // Odeslání JSON řetězce na MQTT server mqttClient.publish(mqttTopic, jsonBuffer); } }
Warning
Ještě jsem se poučil s MQTT s pojmenováním zařízení. Protože jsem měl už zařízení se stejným názvem thermometer na síti, tak se mi zařízení neustále odpojovalo od MQTT. Přejmenování zařízení problém vyřešilo.