Raspberry Pi:Примеры/Чип ESP8266, публикующий данные от DHT22 в базу данных SQLite

Материал из Онлайн справочника
Перейти к навигацииПерейти к поиску

Перевод: Максим Кузьмин
Проверка/Оформление/Редактирование: Мякишев Е.А.


Черновик


Чип ESP8266, публикующий данные от DHT22 в базу данных SQLite[1]

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

Для создания веб-сервера мы воспользуемся микрофреймворком Flask, использующим языком программирования Python. Ниже – схематичное изображение системы, которая должна получиться в итоге:

Настройка Raspberry Pi

О том, как начать работать с Raspberry Pi, читайте тут. О дополнительных настройках Raspberry Pi читайте тут.

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

Плата Raspberry Pi будет коммуницировать с ESP8266 при помощи протокола MQTT. Поэтому сначала устанавливаем брокер Mosquitto, а затем запускаем его в фоновом режиме:

pi@raspberry:~ $ mosquitto -d

Установка Flask

Чтобы превратить Raspberry Pi в веб-сервер, мы воспользуемся микрофреймворком Flask.

Чтобы установить Flask, у вас должен быть установлен pip. Чтобы обновить Raspberry 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 скрипт, представленный ниже:

#
# Создан Руи Сантосом (Rui Santos)
# Подробнее о проекте: http://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

# Функция обратного вызова, которая будет вызвана, 
# когда клиент получит от сервера ответ CONNACK:
def on_connect(client, userdata, flags, rc):
    print("Connected with result code " + str(rc))
    # Если подписаться на on_connect(), то при потере соединения 
    # можно просто заново подключиться и восстановить подписки.
    client.subscribe("/esp8266/dhtreadings")

# Функция обратного вызова на тот случай, 
# если от ESP8266 придет сообщение PUBLISH:
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)

        # Подключаемся к базе данных SQLite. 
        # Название файла – «sensordata.db», но без кавычек.
        # ВНИМАНИЕ: База данных должна быть в той же папке,
        # что и файл «app.py» или иметь правильный путь.
        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():
   # Подключаемся к базе данных SQLite. 
   # Название файла – «sensordata.db», но без кавычек.
   # ВНИМАНИЕ: База данных должна быть в той же папке,
   # что и файл «app.py» или иметь правильный путь.
   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

О том, как установить базу данных SQLite на Raspberry Pi и подготовить базу данных, читайте в этом руководстве. По итогу в вашем распоряжении должны быть таблицы, имеющие следующую схему:

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». Или в файле «app.py» должен быть указан правильный путь к файлу базы данных (в строчке

conn=sqlite3.connect('sensordata.db')

).

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

Чтобы проект был хорошо организован, теги HTML нужно держать отдельно от скрипта Python. Flask использует Jinja2 – это движок для HTML-шаблонов, который позволяет отправлять динамические данные от скрипта 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

Копируем и вставляем в Raspberry Pi шаблон, показанный ниже:

<!DOCTYPE html>
<head>
   <title>RPi Web Server</title>
   <!-- Последний сжатый и скомпилированный CSS -->
   <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7" crossorigin="anonymous">
   <!-- Опциональная тема -->
   <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap-theme.min.css" integrity="sha384-fLW2N01lMqjakBkx3l/M9EahuwpSfeNvV63J5ezn3uZzapT0u7EYsXMjQV+0En5r" crossorigin="anonymous">
   <!-- Последний сжатый и скомпилированный 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».
  4. Переместите папку «pubsubclient» в папку «libraries» IDE Arduino

В комплекте с библиотекой идет несколько скетчей-примеров. Чтобы увидеть их, кликните в IDE Arduino на Файл > Примеры > PubSubClient (File > Examples > PubSubClient).

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

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

  1. Кликните здесь, чтобы скачать библиотеку. В результате на ваш компьютер, в папку «Загрузки», должен скачаться архив в формате ZIP.
  2. Распакуйте скачанный ZIP-архив. В результате у вас должна получиться папка под названием «DHT-sensor-library-master».
  3. Переименуйте ее на «DHT».
  4. Переместите папку «DHT» в папку «libraries» IDE Arduino
  5. Снова откройте IDE Arduino

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

Наконец, загружаем на ESP8266 скетч, показанный ниже (замените значения для SSID, пароля и IP-адреса Pi).

/*****
 
Все ресурсы для этого проекта здесь: https://rntlab.com/
 
*****/

// загружаем библиотеки ESP8266WiFi и PubSubClient:
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include "DHT.h"

// убираем знаки комментария у одной из строчек ниже, 
// соответствующей типу датчика DHT:
//#define DHTTYPE DHT11   // DHT 11
//#define DHTTYPE DHT21   // DHT 21 (AM2301)
#define DHTTYPE DHT22   // DHT 22  (AM2302), AM2321

// меняем эти константы, чтобы ESP8266 мог подключиться к роутеру:
const char* ssid = "YOUR_SSID";
const char* password = "YOUR_PASSWORD";

// меняем переменную ниже на IP-адрес Raspberry Pi,
// чтобы она могла подключиться к брокеру MQTT:
const char* mqtt_server = "YOUR_RPi_IP_Address";

// инициализируем espClient:
WiFiClient espClient;
PubSubClient client(espClient);

// датчик DHT:
const int DHTPin = 14;

// инициализируем датчик DHT:
DHT dht(DHTPin, DHTTYPE);

// вспомогательные переменные для таймера:
long now = millis();
long lastMeasure = 0;

char data[80];

// не меняйте функцию ниже; она подключает ESP8266 к роутеру:
void setup_wifi() {
  delay(10);
  // начинаем с подключения к WiFi-сети:
  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: ");
  //  "Подключение к WiFi выполнено – IP-адрес ESP8266: "
  Serial.println(WiFi.localIP());
}

// эта функция выполняется, когда какой-то девайс публикует сообщение
// в топик, на который подписан ESP8266; поменяйте функцию ниже 
// в соответствии с логикой вашей программы – чтобы, когда девайс 
// будет публиковать сообщение в топик, на который подписан ESP8266,
// ваша программа работала как нужно:
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();
}

// эта функция переподключает ESP8266 к MQTT-брокеру; 
// измените эту функцию, если хотите, чтобы ESP8266 подписывался 
// на большее количество топиков:
with your ESP8266 
void reconnect() {
  // заново запускаем цикл, пока не подключимся:
  while (!client.connected()) {
    Serial.print("Attempting MQTT connection...");
    // "Попытка подключиться к MQTT-брокеру... "

    // Пытаемся подключиться:

     /*
     ЕСЛИ У ВАС ПРОБЛЕМЫ С ПОДКЛЮЧЕНИЕМ НЕСКОЛЬКИХ УСТРОЙСТВ
     К MQTT-БРОКЕРУ, ПОМЕНЯЙТЕ СТРОЧКУ НИЖЕ.

       *  Чтобы поменять ID чипа ESP8266, ему нужно дать 
          уникальное название. Так оно выглядит сейчас: 
       if (client.connect("ESP8266Client")) {

       *  Если вы хотите подключить к MQTT-брокеру 
          дополнительные устройства, его можно назвать так:
       if (client.connect("ESPOffice")) {

       *  Для других ESP:
       if (client.connect("ESPGarage")) {

       Это должно решить проблему с подключением 
       нескольких устройств к MQTT-брокеру:

       Также обратите внимание, что это название должно 
       соответствовать тому, что будет указано ниже, 
       в блоке loop().
    */

    if (client.connect("ESP8266Client")) {
      Serial.println("connected");  //  "подключен"
      // подписываемся или переподписываемся на топик;
      // можно подписаться не только на один, а на несколько топиков
      // (что касается конкретно этого примера, то это позволит
      // управлять большим количеством светодиодов):
    } else {
      Serial.print("failed, rc=");  //  "подключение не удалось"
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");  
      // "5 секунд до следующей попытки"
      // ждем 5 секунд перед тем, как попробовать снова:
      delay(5000);
    }
  }
}

// эта функция настраивает датчик DHT, запускает 
// последовательную коммуникацию на скорости 115200 бод, 
// настраивает MQTT-брокер и задает функцию обратного вызова;
// функция обратного вызова служит для получения сообщений:
void setup() {
  dht.begin();
  Serial.begin(115200);
  setup_wifi();
  client.setServer(mqtt_server, 1883);
  client.setCallback(callback);
}

// для этого проекта в функции loop() ничего менять не нужно;
// в сущности, эта функция устанавливает соединение 
// между ESP8266 и MQTT-брокером:
void loop() {
  if (!client.connected()) {
    reconnect();
  }
  if(!client.loop())

     /*
     ЕСЛИ У ВАС ПРОБЛЕМЫ С ПОДКЛЮЧЕНИЕМ НЕСКОЛЬКИХ УСТРОЙСТВ
     К MQTT-БРОКЕРУ, ПОМЕНЯЙТЕ СТРОЧКУ НИЖЕ.

       *  Чтобы поменять ID чипа ESP8266, ему нужно дать 
          уникальное название. Так оно выглядит сейчас: 
       if (client.connect("ESP8266Client")) {

       *  Если вы хотите подключить к MQTT-брокеру 
          несколько устройств, его можно назвать так:
       if (client.connect("ESPOffice")) {

       *  Для других ESP:
       if (client.connect("ESPGarage")) {

       Это должно решить проблему с подключением 
       нескольких устройств к MQTT-брокеру:

       Также обратите внимание, что это название должно 
       соответствовать тому, что будет указано ниже, 
       в блоке loop().
    */

    client.connect("ESP8266Client");
    
  now = millis();
  // публикуем новые данные о температуре и влажности 
  // каждые 30 секунд:
    if (now - lastMeasure > 10000) {
    lastMeasure = now;
    // данные от датчика могут отставать на 2 секунды
    // (это очень медленный датчик)
    float h = dht.readHumidity();
    // считываем температуру в Цельсиях (по умолчанию):
    float t = dht.readTemperature();
    // считываем температуру в Фаренгейтах (isFahrenheit = true):
    float f = dht.readTemperature(true);

    // проверяем, получилось ли прочитать данные с датчика,
    // и если нет, то завершаем операцию и пробуем снова:
    if (isnan(h) || isnan(t) || isnan(f)) {
      Serial.println("Failed to read from DHT sensor!");
      //  "Не удалось прочесть данные с датчика!"
      return;
    }

    // рассчитываем температуру в Цельсиях:
    float hic = dht.computeHeatIndex(t, h, false);
    static char temperatureTemp[7];
    dtostrf(hic, 6, 2, temperatureTemp);
    
    // если нужно рассчитать температуру в Фаренгейтах,
    // убираем знаки комментирования у строчек ниже:
    // 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));
    
    // публикуем данные о температуре и влажности:
    client.publish("/esp8266/dhtreadings", data);
    Serial.println(data);
    Serial.print("Humidity: ");  //  "Влажность: "
    Serial.print(h);
    Serial.print(" %\t Temperature: ");  //  " %\t Температура: "
    Serial.print(t);
    Serial.print(" *C ");
    Serial.print(f);
    Serial.print(" *F\t Heat index: ");  //  " *F\t Тепловой индекс: "
    Serial.print(hic);
    Serial.println(" *C ");
    // Serial.print(hif);
    // Serial.println(" *F");
  }
}

Схема

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

Примечание: Для этого проекта подойдут и другие модели датчика DHT, но для их использования понадобится немного поменять код.

Ниже – схема подключения:

Важно! Датчику DHT для корректной работы требуется 5 вольт, поэтому убедитесь, что подключили его к контакту Vin на ESP8266, который выдает необходимые 5 вольт.

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

Чтобы запустить веб-сервер Raspberry Pi, сначала переходим к папке, содержащей файл «app.py»:

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

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

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

Сразу после этого должен запуститься веб-сервер, и это должно произойти на порте «:8181».

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

Впишите в браузере IP-адрес Raspberry Pi. В моем случае это «http://192.168.1.98:8181».

Примечание: После IP-адреса обязательно должна быть приписка «:8181».

См.также

Внешние ссылки