Raspberry Pi публикует MQTT-сообщения на ESP8266

В этом проекте вы создадите автономный веб-сервер на Raspberry Pi, который может переключать два светодиода на ESP8266 с помощью протокола MQTT. Вы можете заменить эти светодиоды любым выходом (например, реле, управляющим лампой).

Для создания веб-сервера вы будете использовать Python-микрофреймворк Flask. Вот общая схема системы:

Общая схема системы: Raspberry Pi публикует MQTT-сообщения на ESP8266

Сначала посмотрите видеодемонстрацию

Рекомендуемые ресурсы:

Если вам нравится домашняя автоматизация и вы хотите построить полноценную систему домашней автоматизации, я рекомендую скачать мой курс по домашней автоматизации.

Базовая настройка Raspberry Pi

Прежде чем продолжить чтение этого проекта, убедитесь, что на вашем Raspberry Pi установлена операционная система Raspbian.

Вы можете прочитать мое руководство Начало работы с Raspberry Pi для установки Raspbian и выполнения базовой настройки.

Запуск и установка брокера Mosquitto

Raspberry Pi будет взаимодействовать с ESP8266 по протоколу MQTT. Если брокер Mosquitto установлен, вам нужно запустить брокер Mosquitto в фоновом режиме:

pi@raspberry:~ $ mosquitto -d

Веб-сервер на Python с Flask

Мы будем использовать Python-микрофреймворк Flask для превращения Raspberry Pi в веб-сервер.

Для установки Flask вам нужно установить pip. Выполните следующие команды для обновления вашего Pi и установки pip:

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

Затем используйте pip для установки Flask и его зависимостей:

pi@raspberrypi ~ $ sudo pip install flask

Установка Python Paho-MQTT

Пакет Paho-MQTT предоставляет клиентский класс, который позволяет приложениям подключаться к MQTT-брокеру для публикации сообщений, а также подписываться на топики и получать опубликованные сообщения. В этом примере веб-сервер на Python будет публиковать сообщения на ESP8266 для включения и выключения GPIO.

Для установки paho-mqtt выполните следующую команду:

pi@raspberrypi ~ $ sudo pip install paho-mqtt

Создание Python-скрипта

Это основной скрипт нашего приложения. Он настраивает веб-сервер, и при нажатии кнопок публикует MQTT-сообщение на ESP8266.

Чтобы все было организовано, начните с создания новой папки:

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

Создайте новый файл с именем app.py.

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

Скопируйте и вставьте следующий скрипт на ваш Raspberry Pi:

#
# Created by Rui Santos
# Complete project details: https://randomnerdtutorials.com
#

import paho.mqtt.client as mqtt
from flask import Flask, render_template, request
app = Flask(__name__)

mqttc=mqtt.Client()
mqttc.connect("localhost",1883,60)
mqttc.loop_start()

# Create a dictionary called pins to store the pin number, name, and pin state:
pins = {
   4 : {'name' : 'GPIO 4', 'board' : 'esp8266', 'topic' : 'esp8266/4', 'state' : 'False'},
   5 : {'name' : 'GPIO 5', 'board' : 'esp8266', 'topic' : 'esp8266/5', 'state' : 'False'}
   }

# Put the pin dictionary into the template data dictionary:
templateData = {
   'pins' : pins
   }

@app.route("/")
def main():
   # Pass the template data into the template main.html and return it to the user
   return render_template('main.html', **templateData)

# The function below is executed when someone requests a URL with the pin number and action in it:
@app.route("/<board>/<changePin>/<action>")

def action(board, changePin, action):
   # Convert the pin from the URL into an integer:
   changePin = int(changePin)
   # Get the device name for the pin being changed:
   devicePin = pins[changePin]['name']
   # If the action part of the URL is "on," execute the code indented below:
   if action == "1" and board == 'esp8266':
      mqttc.publish(pins[changePin]['topic'],"1")
      pins[changePin]['state'] = 'True'

   if action == "0" and board == 'esp8266':
      mqttc.publish(pins[changePin]['topic'],"0")
      pins[changePin]['state'] = 'False'

   # Along with the pin dictionary, put the message into the template data dictionary:
   templateData = {
      'pins' : pins
   }

   return render_template('main.html', **templateData)

if __name__ == "__main__":
   app.run(host='0.0.0.0', port=8181, debug=True)

Просмотреть исходный код

Создание HTML-файла

Разделение HTML-тегов и вашего Python-скрипта – это способ поддерживать организованность вашего проекта.

Flask использует шаблонизатор Jinja2, который вы можете использовать для отправки динамических данных из вашего Python-скрипта в HTML-файл.

Создайте новую папку templates:

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

Создайте новый файл с именем main.html.

pi@raspberrypi:~/web-server/templates $ nano main.html

Скопируйте и вставьте следующий шаблон на ваш Pi:

<!DOCTYPE html>
<head>
   <title>RPi Web Server</title>
   <!-- Latest compiled and minified CSS -->
   <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7" crossorigin="anonymous">
   <!-- Optional theme -->
   <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap-theme.min.css" integrity="sha384-fLW2N01lMqjakBkx3l/M9EahuwpSfeNvV63J5ezn3uZzapT0u7EYsXMjQV+0En5r" crossorigin="anonymous">
   <!-- Latest compiled and minified JavaScript -->
   <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js" integrity="sha384-0mSbJDEHialfmuBBQP6A4Qrprq5OVfW37PRR3j5ELqxss1yVqOtnepnHVP9aJ7xS" crossorigin="anonymous"></script>
   <meta name="viewport" content="width=device-width, initial-scale=1">
</head>

<body>
   <h1>RPi Web Server - ESP8266 MQTT</h1>
   {% for pin in pins %}
   <h2>{{ pins[pin].name }}
   {% if pins[pin].state == 'True' %}
      is currently <strong>on</strong></h2><div class="row"><div class="col-md-2">
      <a href="/esp8266/{{pin}}/0" class="btn btn-block btn-lg btn-default" role="button">Turn off</a></div></div>
   {% else %}
      is currently <strong>off</strong></h2><div class="row"><div class="col-md-2">
      <a href="/esp8266/{{pin}}/1" class="btn btn-block btn-lg btn-primary" role="button">Turn on</a></div></div>
   {% endif %}
   {% endfor %}
</body>
</html>

Просмотреть исходный код

Программирование ESP8266

Для взаимодействия ESP8266 с веб-сервером Raspberry Pi вам нужно установить библиотеку PubSubClient. Эта библиотека предоставляет клиент для простого обмена сообщениями publish/subscribe с сервером, поддерживающим MQTT (по сути, позволяет вашему ESP8266 общаться с веб-сервером на Python).

Установка библиотеки

1) Нажмите сюда, чтобы скачать библиотеку PubSubClient. У вас должна появиться папка .zip в папке Загрузки

2) Распакуйте папку .zip, и у вас должна получиться папка pubsubclient-master

3) Переименуйте папку из pubsubclient-master в pubsubclient

4) Переместите папку pubsubclient в папку libraries вашей установки Arduino IDE

5) Затем перезапустите Arduino IDE

Библиотека поставляется с рядом примеров скетчей. Смотрите File > Examples > PubSubClient в Arduino IDE.

Загрузка скетча

Наконец, вы можете загрузить полный скетч на ваш ESP8266 (замените на ваш SSID, пароль и IP-адрес RPi):

/*****

 All the resources for this project:
 https://randomnerdtutorials.com/

*****/

// Loading the ESP8266WiFi library and the PubSubClient library
#include <ESP8266WiFi.h>
#include <PubSubClient.h>

// Change the credentials below, so your ESP8266 connects to your router
const char* ssid = "YOUR_SSID";
const char* password = "YOUR_PASSWORD";

// Change the variable to your Raspberry Pi IP address, so it connects to your MQTT broker
const char* mqtt_server = "YOUR_RPi_IP_Address";

// Initializes the espClient
WiFiClient espClient;
PubSubClient client(espClient);

// Connect an LED to each GPIO of your ESP8266
const int ledGPIO5 = 5;
const int ledGPIO4 = 4;

// Don't change the function below. This functions connects your ESP8266 to your router
void setup_wifi() {
  delay(10);
  // We start by connecting to a WiFi network
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.print("WiFi connected - ESP IP address: ");
  Serial.println(WiFi.localIP());
}

// This functions is executed when some device publishes a message to a topic that your ESP8266 is subscribed to
// Change the function below to add logic to your program, so when a device publishes a message to a topic that
// your ESP8266 is subscribed you can actually do something
void callback(String topic, byte* message, unsigned int length) {
  Serial.print("Message arrived on topic: ");
  Serial.print(topic);
  Serial.print(". Message: ");
  String messageTemp;

  for (int i = 0; i < length; i++) {
    Serial.print((char)message[i]);
    messageTemp += (char)message[i];
  }
  Serial.println();

  // Feel free to add more if statements to control more GPIOs with MQTT

  // If a message is received on the topic home/office/esp1/gpio2, you check if the message is either 1 or 0. Turns the ESP GPIO according to the message
  if(topic=="esp8266/4"){
      Serial.print("Changing GPIO 4 to ");
      if(messageTemp == "1"){
        digitalWrite(ledGPIO4, HIGH);
        Serial.print("On");
      }
      else if(messageTemp == "0"){
        digitalWrite(ledGPIO4, LOW);
        Serial.print("Off");
      }
  }
  if(topic=="esp8266/5"){
      Serial.print("Changing GPIO 5 to ");
      if(messageTemp == "1"){
        digitalWrite(ledGPIO5, HIGH);
        Serial.print("On");
      }
      else if(messageTemp == "0"){
        digitalWrite(ledGPIO5, LOW);
        Serial.print("Off");
      }
  }
  Serial.println();
}

// This functions reconnects your ESP8266 to your MQTT broker
// Change the function below if you want to subscribe to more topics with your ESP8266
void reconnect() {
  // Loop until we're reconnected
  while (!client.connected()) {
    Serial.print("Attempting MQTT connection...");
    // Attempt to connect
     /*
     YOU  NEED TO CHANGE THIS NEXT LINE, IF YOU'RE HAVING PROBLEMS WITH MQTT MULTIPLE CONNECTIONS
     To change the ESP device ID, you will have to give a unique name to the ESP8266.
     Here's how it looks like now:
       if (client.connect("ESP8266Client")) {
     If you want more devices connected to the MQTT broker, you can do it like this:
       if (client.connect("ESPOffice")) {
     Then, for the other ESP:
       if (client.connect("ESPGarage")) {
      That should solve your MQTT multiple connections problem

     THE SECTION IN loop() function should match your device name
    */
    if (client.connect("ESP8266Client")) {
      Serial.println("connected");
      // Subscribe or resubscribe to a topic
      // You can subscribe to more topics (to control more LEDs in this example)
      client.subscribe("esp8266/4");
      client.subscribe("esp8266/5");
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
}

// The setup function sets your ESP GPIOs to Outputs, starts the serial communication at a baud rate of 115200
// Sets your mqtt broker and sets the callback function
// The callback function is what receives messages and actually controls the LEDs
void setup() {
  pinMode(ledGPIO4, OUTPUT);
  pinMode(ledGPIO5, OUTPUT);

  Serial.begin(115200);
  setup_wifi();
  client.setServer(mqtt_server, 1883);
  client.setCallback(callback);
}

// For this project, you don't need to change anything in the loop function.
// Basically it ensures that you ESP is connected to your broker
void loop() {
  if (!client.connected()) {
    reconnect();
  }
  if(!client.loop())
     /*
     YOU  NEED TO CHANGE THIS NEXT LINE, IF YOU'RE HAVING PROBLEMS WITH MQTT MULTIPLE CONNECTIONS
     To change the ESP device ID, you will have to give a unique name to the ESP8266.
     Here's how it looks like now:
       client.connect("ESP8266Client");
     If you want more devices connected to the MQTT broker, you can do it like this:
       client.connect("ESPOffice");
     Then, for the other ESP:
       client.connect("ESPGarage");
      That should solve your MQTT multiple connections problem

     THE SECTION IN recionnect() function should match your device name
    */
    client.connect("ESP8266Client");
}

Просмотреть исходный код

Схема подключения

Схема подключения для этого проекта очень простая. Просто подключите два светодиода с двумя резисторами к вашему ESP8266, как показано на рисунке ниже.

Схема подключения ESP8266 со светодиодами

Вы можете использовать ссылки выше или перейти непосредственно на MakerAdvisor.com/tools, чтобы найти все компоненты для ваших проектов по лучшей цене!

Запуск веб-сервера

Для запуска веб-сервера Raspberry Pi перейдите в папку, содержащую файл app.py:

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

Затем выполните следующую команду:

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

Ваш веб-сервер должен запуститься немедленно на порту :8181!

Запуск веб-сервера Python

Демонстрация

Откройте адрес вашего Raspberry Pi в браузере, введя его IP-адрес, в моем случае: http://192.168.1.98:8181

Примечание: вы должны ввести ваш IP-адрес, за которым следует :8181

Веб-сервер Python ESP8266 MQTT

Вот видеодемонстрация работы веб-сервера:

Заключение

В следующей статье мы будем публиковать показания датчиков с ESP8266 на веб-сервер Python.

Нравится домашняя автоматизация? Узнайте больше о Node-RED, Raspberry Pi, ESP8266 и Arduino с моим курсом: Постройте систему домашней автоматизации за $100.

У вас есть вопросы? Оставьте комментарий ниже!

Спасибо за чтение. Если вам понравилась эта статья, возможно, вам понравятся и следующие, поэтому, пожалуйста, поддержите меня, подписавшись на мой блог.