ESP8266: публикация показаний DHT22 в базу данных SQLite

В этом проекте вы создадите автономный веб-сервер на Raspberry Pi, который отображает показания температуры и влажности с датчика DHT22, хранящиеся в базе данных SQLite.

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

Схема системы DHT22 Raspberry Pi ESP8266

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

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

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

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

Вы можете прочитать руководство «Начало работы с Raspberry Pi» ( :doc:`Getting Started with the Raspberry Pi Guide </raspberry/rnt/getting-started-with-raspberry-pi/index>`_ ), чтобы установить Raspbian и выполнить базовую настройку.

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

Raspberry Pi будет взаимодействовать с ESP8266 по протоколу MQTT. Если :doc:`брокер Mosquitto установлен </raspberry/rnt/how-to-install-mosquitto-broker-on-raspberry-pi/index>`_, вам необходимо запустить брокер Mosquitto в фоновом режиме:

pi@raspberry:~ $ mosquitto -d

Установка 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 git-core

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

pi@raspberrypi ~ $ sudo pip install flask

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

Это основной скрипт нашего приложения. Он настраивает веб-сервер, принимает показания температуры/влажности и сохраняет эти показания датчика в базе данных SQLite.

Для удобства организации начните с создания новой папки:

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
import json
import sqlite3

app = Flask(__name__)

def dict_factory(cursor, row):
    d = {}
    for idx, col in enumerate(cursor.description):
        d[col[0]] = row[idx]
    return d

# The callback for when the client receives a CONNACK response from the server.
def on_connect(client, userdata, flags, rc):
    print("Connected with result code " + str(rc))
    # Subscribing in on_connect() means that if we lose the connection and
    # reconnect then subscriptions will be renewed.
    client.subscribe("/esp8266/dhtreadings")

# The callback for when a PUBLISH message is received from the ESP8266.
def on_message(client, userdata, message):
    if message.topic == "/esp8266/dhtreadings":
        print("DHT readings update")
        #print(message.payload.json())
        #print(dhtreadings_json['temperature'])
        #print(dhtreadings_json['humidity'])

        dhtreadings_json = json.loads(message.payload)

        # connects to SQLite database. File is named "sensordata.db" without the quotes
        # WARNING: your database file should be in the same directory of the app.py file or have the correct path
        conn=sqlite3.connect('sensordata.db')
        c=conn.cursor()

        c.execute("""INSERT INTO dhtreadings (temperature,
            humidity, currentdate, currentime, device) VALUES((?), (?), date('now'),
            time('now'), (?))""", (dhtreadings_json['temperature'],
            dhtreadings_json['humidity'], 'esp8266') )

        conn.commit()
        conn.close()

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

@app.route("/")
def main():
   # connects to SQLite database. File is named "sensordata.db" without the quotes
   # WARNING: your database file should be in the same directory of the app.py file or have the correct path
   conn=sqlite3.connect('sensordata.db')
   conn.row_factory = dict_factory
   c=conn.cursor()
   c.execute("SELECT * FROM dhtreadings ORDER BY id DESC LIMIT 20")
   readings = c.fetchall()
   #print(readings)
   return render_template('main.html', readings=readings)

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

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

Подготовка файла SQLite

Следуйте следующему руководству, чтобы узнать, :doc:`как установить базу данных SQLite на Raspberry Pi и подготовить базу данных </raspberry/rnt/sqlite-database-on-a-raspberry-pi/index>`_. Создайте файл базы данных SQLite со следующей схемой:

sqlite> .fullschema
CREATE TABLE dhtreadings(id INTEGER PRIMARY KEY AUTOINCREMENT, temperature NUMERIC, humidity NUMERIC, currentdate DATE, currentime TIME, device TEXT);

Ваш файл базы данных SQLite должен называться «sensordata.db» (без кавычек).

ВНИМАНИЕ: ваш файл базы данных должен находиться в той же директории, что и файл app.py, или содержать правильный путь в вашем Python-файле app.py, созданном в предыдущем разделе (раздел conn=sqlite3.connect(„sensordata.db“)).

Создание 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>
   <script src="https://code.jquery.com/jquery-3.1.1.min.js" integrity="sha256-hVVnYaiADRTO2PzUGmuLJr8BLUSjGIZsDYGmIJLv2b8=" crossorigin="anonymous"></script>
   <meta name="viewport" content="width=device-width, initial-scale=1">
</head>

<body>
   <h1>RPi Web Server - ESP8266 SQLite Data</h1>
   <table class="table table-hover">
     <tr><th>ID</th>
     <th>Temperature</th>
     <th>Humidity</th>
     <th>Date</th>
     <th>Time</th>
     <th>Device</th></tr>
     {% for entry in readings %}
        <tr><td>{{ entry.id }}</td>
        <td>{{ entry.temperature }}</td>
        <td>{{ entry.humidity }}</td>
        <td>{{ entry.currentdate }}</td>
        <td>{{ entry.currentime }}</td>
        <td>{{ entry.device }}</td></tr>
     {% endfor %}
   </table>
</body>
</html>

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

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

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

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

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

2) Распакуйте файл .zip, и вы получите папку pubsubclient-master.

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

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

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

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

Библиотека датчика DHT предоставляет простой способ использования любого датчика DHT для считывания температуры и влажности с помощью плат ESP8266 или Arduino.

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

2) Распакуйте файл .zip, и вы получите папку DHT-sensor-library-master.

3) Переименуйте папку из DHT-sensor-library-master в DHT.

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

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

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

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

/*****

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

*****/

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

// Uncomment one of the lines bellow for whatever DHT sensor type you're using!
//#define DHTTYPE DHT11   // DHT 11
//#define DHTTYPE DHT21   // DHT 21 (AM2301)
#define DHTTYPE DHT22   // DHT 22  (AM2302), AM2321

// 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);

// DHT Sensor
const int DHTPin = 14;

// Initialize DHT sensor.
DHT dht(DHTPin, DHTTYPE);

// Timers auxiliar variables
long now = millis();
long lastMeasure = 0;

char data[80];

// 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();
}

// 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)
    } 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 DHT sensor, 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() {
  dht.begin();
  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");

  now = millis();
  // Publishes new temperature and humidity every 30 seconds
  if (now - lastMeasure > 10000) {
    lastMeasure = now;
    // Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
    float h = dht.readHumidity();
    // Read temperature as Celsius (the default)
    float t = dht.readTemperature();
    // Read temperature as Fahrenheit (isFahrenheit = true)
    float f = dht.readTemperature(true);

    // Check if any reads failed and exit early (to try again).
    if (isnan(h) || isnan(t) || isnan(f)) {
      Serial.println("Failed to read from DHT sensor!");
      return;
    }

    // Computes temperature values in Celsius
    float hic = dht.computeHeatIndex(t, h, false);
    static char temperatureTemp[7];
    dtostrf(hic, 6, 2, temperatureTemp);

    // Uncomment to compute temperature values in Fahrenheit
    // float hif = dht.computeHeatIndex(f, h);
    // static char temperatureTemp[7];
    // dtostrf(hic, 6, 2, temperatureTemp);

    static char humidityTemp[7];
    dtostrf(h, 6, 2, humidityTemp);

    String dhtReadings = "{ \"temperature\": \"" + String(temperatureTemp) + "\", \"humidity\" : \"" + String(humidityTemp) + "\"}";
    dhtReadings.toCharArray(data, (dhtReadings.length() + 1));

    // Publishes Temperature and Humidity values
    client.publish("/esp8266/dhtreadings", data);
    Serial.println(data);
    Serial.print("Humidity: ");
    Serial.print(h);
    Serial.print(" %\t Temperature: ");
    Serial.print(t);
    Serial.print(" *C ");
    Serial.print(f);
    Serial.print(" *F\t Heat index: ");
    Serial.print(hic);
    Serial.println(" *C ");
    // Serial.print(hif);
    // Serial.println(" *F");
  }
}

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

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

Для выполнения этого проекта вам понадобятся следующие компоненты:

Примечание: другие типы датчиков DHT также будут работать с небольшим изменением в коде.

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

Вот схема подключения:

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

Важно: датчик DHT требует 5В для корректной работы, поэтому убедитесь, что вы используете пин Vin вашего ESP8266, который выдаёт 5В.

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

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

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

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

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

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

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

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

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

Веб-сервер Raspberry Pi с данными SQLite ESP8266

Подведение итогов

На этом пока всё, надеюсь, вы сможете взять эти примеры и создать больше функций, соответствующих вашим потребностям.

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