Odesílání dat z ESP32 do Raspberry prostřednictvím MQTT

08.10.2022 Raspberry Pi #raspberrypi #esp23 #mqtt

Návod jak odesílat data z čidla MBP280 do RPi prostřednictvím MQTT. Jedná se o úvod do této technologie.


V tomto projektu vytvoříte samostatný webový server s Raspberry Pi, který zobrazuje hodnoty teploty, vlhkosti a tlaku pomocí senzoru MBP280.

 

K vytvoření webového serveru budete používat mikrorámec Pythonu s názvem Flask.

Základní nastavení Raspberry Pi

Než budete pokračovat ve čtení tohoto projektu, ujistěte se, že máte v Raspberry Pi nainstalovaný operační systém Raspbian.

 Chcete-li nainstalovat Raspbian a dokončit základní nastavení, můžete si přečíst Instalace Raspberry Pi OS, nastavení Wi-Fi a SSH.

Spusťte a nainstalujte Mosquitto broker

Raspberry Pi bude komunikovat s ESP32 pomocí protokolu MQTT. Po instalaci Mosquitto brokera musíte mít Mosquitto broker spuštěný na pozadí:

pi@raspberry:~ $ mosquitto -d

Instalace Flask

K přeměně Raspberry Pi na webový server použijeme mikrorámec Pythonu s názvem Flask.

Chcete-li nainstalovat Flask, musíte mít nainstalovaný pip. Spusťte následující příkazy pro aktualizaci vašeho Pi a instalaci pip:

pi@raspberrypi ~ $ sudo apt-get update
pi@raspberrypi ~ $ sudo apt-get upgrade
pi@raspberrypi ~ $ sudo apt-get install python-pip python-flask git-core

Poté pomocí pip nainstalujete Flask a Paho MQTT (pro python2):

pi@raspberrypi ~ $ sudo pip install flask
pi@raspberrypi ~ $ sudo pip install paho-mqtt

Pro python 3 proveďte následující příkazy:

pi@raspberrypi ~ $ sudo apt install python3-flask
pi@raspberrypi ~ $ sudo apt install python3-paho-mqtt

Vytvoření skriptu Python

Jedná se o základní skript naší aplikace. Nastaví webový server, který je přihlášen k odběru témat MQTT teploty, vlhkosti a tlaku, aby mohl přijímat naměřené hodnoty.

Chcete-li mít vše uspořádané, začněte vytvořením nové složky:

pi@raspberrypi ~ $ mkdir web-server
pi@raspberrypi ~ $ cd web-server
pi@raspberrypi:~/web-server $

Vytvořte nový soubor s názvem app.py.

pi@raspberrypi:~/web-server $ nano app.py

Zkopírujte a vložte následující skript do svého Raspberry Pi

import paho.mqtt.client as mqtt

MQTT_ADDRESS = '192.168.0.8'
MQTT_USER = 'pi'
MQTT_PASSWORD = 'pi'
MQTT_TOPIC = 'data/+/+'


def on_connect(client, userdata, flags, rc):
    """ The callback for when the client receives a CONNACK response from the server."""
    print('Connected with result code ' + str(rc))
    client.subscribe(MQTT_TOPIC)


def on_message(client, userdata, msg):
    """The callback for when a PUBLISH message is received from the server."""
    print(msg.topic + ' ' + str(msg.payload))


def main():
    mqtt_client = mqtt.Client()
    mqtt_client.username_pw_set(MQTT_USER, MQTT_PASSWORD)
    mqtt_client.on_connect = on_connect
    mqtt_client.on_message = on_message

    mqtt_client.connect(MQTT_ADDRESS, 1883)
    mqtt_client.loop_forever()


if __name__ == '__main__':
    print('MQTT to InfluxDB bridge')
    main()

Přidání WiFi a povolení MQTT pro vydavatele MQTT (ESP32)

Vydavatelská část vychází ze základního kódu pro práci s čidlem MBP280. V této části se navážeme WiFi připojení k domácí síti a odešlou se data přes protokol MQTT brokerovi.

Do ESP32 se nahraje následující kód:

#include <WiFi.h>
#include <PubSubClient.h>
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>

/*#include <SPI.h>
#define BME_SCK 18
#define BME_MISO 19
#define BME_MOSI 23
#define BME_CS 5*/

#define SEALEVELPRESSURE_HPA (1013.25)
#define CORRECT_TEMP (0.40)

// WiFi
const char* ssid = "IoT";                 // Your personal network SSID
const char* wifi_password = "IoTdlink";     // Your personal network password

// MQTT
const char* mqtt_server = "192.168.0.100";  // IP of the MQTT broker (RPi)
const char* humidity_topic = "data/bme/humidity";
const char* temperature_topic = "data/bme/temperature";
const char* mqtt_username = "pi"; // MQTT username
const char* mqtt_password = "pi"; // MQTT password
const char* clientID = "data_bme"; // MQTT client ID

// Initialise the WiFi and MQTT Client objects
WiFiClient wifiClient;
// 1883 is the listener port for the Broker
PubSubClient client(mqtt_server, 1883, wifiClient); 

Adafruit_BME280 bme;  // I2C
//Adafruit_BME280 bme(BME_CS);  // hardware SPI
//Adafruit_BME280 bme(BME_CS, BME_MOSI, BME_MISO, BME_SCK);  // software SPI

// Custom function to connet to the MQTT broker via WiFi
void connect_MQTT(){
  Serial.print("Connecting to ");
  Serial.println(ssid);

  // Connect to the WiFi
  WiFi.begin(ssid, wifi_password);

  // Wait until the connection has been confirmed before continuing
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  // Debugging - Output the IP Address of the ESP8266
  Serial.println("WiFi connected");
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());

  // Connect to MQTT Broker
  // client.connect returns a boolean value to let us know if the connection was successful.
  // If the connection is failing, make sure you are using the correct MQTT Username and Password (Setup Earlier in the Instructable)
  if (client.connect(clientID, mqtt_username, mqtt_password)) {
    Serial.println("Connected to MQTT Broker!");
  }
  else {
    Serial.println("Connection to MQTT Broker failed...");
  }
}

void setup() {
  Serial.begin(9600);
  bool status = bme.begin(0x76);
  if (!status) {
    Serial.println("Could not find a valid BME280 sensor, check wiring or change I2C address!");
    while (1);
  }
}

void loop() {

  connect_MQTT();
  Serial.setTimeout(2000);

  String temp=String(bme.readTemperature()-CORRECT_TEMP);
  String hum=String(bme.readHumidity());
  String press=String(bme.readPressure()/100.0F);
  String alt=String(bme.readAltitude(SEALEVELPRESSURE_HPA));

  Serial.print("Temperature: ");
  Serial.println(temp);
  Serial.print("Humidity: ");
  Serial.println(hum);
  Serial.print("Pressure: ");
  Serial.println(press);
  Serial.print("Altitude: ");
  Serial.println(alt);

  // PUBLISH to the MQTT Broker (topic = Temperature, defined at the beginning)
  if (client.publish(temperature_topic, String(temp).c_str())) {
    Serial.println("Temperature sent!");
  }
  // Again, client.publish will return a boolean value depending on whether it succeded or not.
  // If the message failed to send, we will try again, as the connection may have broken.
  else {
    Serial.println("Temperature failed to send. Reconnecting to MQTT Broker and trying again");
    client.connect(clientID, mqtt_username, mqtt_password);
    delay(10); // This delay ensures that client.publish doesn't clash with the client.connect call
    client.publish(temperature_topic, String(temp).c_str());
  }

 // PUBLISH to the MQTT Broker (topic = Humidity, defined at the beginning)
  if (client.publish(humidity_topic, String(hum).c_str())) {
    Serial.println("Humidity sent!");
  }
  // Again, client.publish will return a boolean value depending on whether it succeded or not.
  // If the message failed to send, we will try again, as the connection may have broken.
  else {
    Serial.println("Humidity failed to send. Reconnecting to MQTT Broker and trying again");
    client.connect(clientID, mqtt_username, mqtt_password);
    delay(10); // This delay ensures that client.publish doesn't clash with the client.connect call
    client.publish(humidity_topic, String(hum).c_str());
  }

  client.disconnect();  // disconnect from the MQTT broker
  delay(1000*60);       // print new values every 1 hour  
}

Vysvětlení kódu

Popsání pasáží týkajících se převážně MQTT.

#include <PubSubClient.h>

PubSubClient umožňuje mikrokontroléru ESP32 být vydavatelem MQTT.

// WiFi
const char* ssid = "IoT";                 // Your personal network SSID
const char* wifi_password = "IoTdlink";     // Your personal network password

// MQTT
const char* mqtt_server = "192.168.0.100";  // IP of the MQTT broker (RPi)
const char* humidity_topic = "data/bme/humidity";
const char* temperature_topic = "data/bme/temperature";
const char* mqtt_username = "pi"; // MQTT username
const char* mqtt_password = "pi"; // MQTT password
const char* clientID = "data_bme"; // MQTT client ID

Definice proměnných, které zajišťují data pro připojení k místní WiFi a následně k MQTT.

Další částí je vlastní funkce nazvaná connect_MQTT() pro připojení k brokerovi MQTT prostřednictvím bezdrátového připojení. Nejprve se vytiskne SSID a heslo. 

Serial.print("Connecting to ");
Serial.println(ssid);

Poté skript počká na navázání bezdrátového spojení a vypíše IP mikrokontroléru.

  // Wait until the connection has been confirmed before continuing
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  // Debugging - Output the IP Address of the ESP8266
  Serial.println("WiFi connected");
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());

Po nastavení WiFi se připojíme k brokerovi MQTT pomocí ID klienta, uživatelského jména MQTT a hesla MQTT. Pokud je spojení úspěšné nebo neúspěšné, tiskneme v seriálu.

if (client.connect(clientID, mqtt_username, mqtt_password)) {
    Serial.println("Connected to MQTT Broker!");
}else {
    Serial.println("Connection to MQTT Broker failed...");
}

Funkce loop() začíná provedením funkce připojení MQTT.

connect_MQTT();

Můžete také nastavit WiFi připojení ve funkci nastavení a nepřipojovat znovu každou iteraci ve funkci smyčky. Protože chceme posílat teplotu a vlhkost pouze každou hodinu, odpojí se WiFi připojení po odeslání dat MQTT a znovu se připojíme po jedné hodině.

Po navázání MQTT spojení odečteme hodnoty z BMP senzoru a vytiskneme je na sériový výstup. Hodnoty jsou také uloženy jako řetězce, protože MQTT může přenášet pouze řetězce a žádné jiné datové typy.

String temp=String(bme.readTemperature()-CORRECT_TEMP);
String hum=String(bme.readHumidity());
String press=String(bme.readPressure()/100.0F);
String alt=String(bme.readAltitude(SEALEVELPRESSURE_HPA));

Serial.print("Temperature: ");
Serial.println(temp);
Serial.print("Humidity: ");
Serial.println(hum);
Serial.print("Pressure: ");
Serial.println(press);
Serial.print("Altitude: ");
Serial.println(alt);

Posledním krokem je odeslání hodnot brokerovi MQTT přes navázané spojení. Proto používáme funkci client.publish knihovny PubSubClient. Pokud se zpráva nepodaří odeslat, pokusíme se znovu navázat spojení s brokerem a pokusíme se odeslat užitečné zatížení hodnoty podruhé.

 if (client.publish(temperature_topic, String(temp).c_str())) {
    Serial.println("Temperature sent!");
  }

Po odeslání hodnot brokerovi MQTT odpojíme vydavatele od brokera a přidáme prodlevu na konci skriptu o 1 hodinu.

client.disconnect(); 
delay(1000*60); 

Spuštění webového serveru

Chcete-li spustit webový server Raspberry Pi, přesuňte se do složky, která obsahuje soubor app.py:

pi@raspberrypi:~/web-server/templates $ cd ..

Poté spusťte následující příkaz:

pi@raspberrypi:~/web-server $ sudo python app.py

Nyní by již mělo být v konzoli vidět příjem dat z ESP32.