Raspberry Pi:Примеры/Чип ESP8266, публикующий данные от DHT22 в базу данных SQLite
Содержание | Введение | Продукты | Операционная система | Настройка | Основы Linux | Аппаратные средства | Неисправности | Типовые проблемы | Часто возникающие вопросы | Библиотеки | Примеры |
Черновик |
Чип 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
- Чтобы скачать библиотеку PubSubClient, кликните здесь. В результате на ваш компьютер, в папку «Загрузки», должен скачаться архив в формате ZIP.
- Распакуйте скачанный ZIP-архив. В результате у вас должна получиться папка под названием «pubsubclient-master».
- Переименуйте эту папку на «pubsubclient».
- Переместите папку «pubsubclient» в папку «libraries» IDE Arduino
В комплекте с библиотекой идет несколько скетчей-примеров. Чтобы увидеть их, кликните в IDE Arduino на Файл > Примеры > PubSubClient (File > Examples > PubSubClient).
Установка библиотеки для датчика DHT
Эта библиотека упрощает использование любого датчика DHT для считывания данных о температуре и влажности при помощи ESP8266 или платы Arduino.
- Кликните здесь, чтобы скачать библиотеку. В результате на ваш компьютер, в папку «Загрузки», должен скачаться архив в формате ZIP.
- Распакуйте скачанный ZIP-архив. В результате у вас должна получиться папка под названием «DHT-sensor-library-master».
- Переименуйте ее на «DHT».
- Переместите папку «DHT» в папку «libraries» IDE Arduino
- Снова откройте 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");
}
}
Схема
Для завершения проекта вам понадобятся следующие компоненты:
- Один модуль ESP8266 12E – на eBay
- Один датчик DHT22 – на eBay
- Один резистор на 4700 Ом
Примечание: Для этого проекта подойдут и другие модели датчика 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».