从数据到图形:使用 Flask 和 SQLite 的网络之旅
捕获真实数据 (RPi/DHT22),将它们保存在数据库 (SQLite) 中,创建图形 (Matplotlib) 并将它们呈现在网页上 (Flask)。
图>简介:从数据到图形。 Flask 和 SQLite 的网络之旅
在我之前的教程 Python WebServer With Flask 和 Raspberry Pi 中,我们学习了如何通过使用 Flask 构建的 Web 前端页面与物理世界进行交互。因此,下一个自然步骤是从现实世界中收集数据,并在网页上提供给我们。很简单的!但是,例如,如果我们想知道前一天的情况会发生什么?或者对这些数据进行某种分析?在这些情况下,我们必须将数据也存储在数据库中。
图>简而言之,在这个新教程中,我们将:
捕获真实数据 (空气温度和相对湿度)使用 DHT22 传感器;加载这些数据 在本地数据库上 , 使用 SQLite; 构建 创建图形 使用历史数据 使用 Matplotlib; 显示数据 带有动画“量具”,使用 JustGage; 创建 通过使用Python创建的本地网络服务器使所有内容在线可用 和烧瓶;
框图让我们了解了整个项目:
第 1 步:物料清单 - 物料清单
- Raspberry Pi V3 – 32.00 美元
- DHT22 温度和相对湿度传感器 – 9.95 美元
- 电阻 4K7 欧姆
第 2 步:安装 SQLite
图>好的,总体思路是从传感器收集数据并将其存储在数据库中。
但是应该使用什么数据库“引擎”?
市场上有很多选择,可能最常用于 Raspberry Pi 和传感器的 2 个是 MySQL 和 SQLite。 MySQL 是众所周知的,但在简单的基于 Raspberry 的项目中使用有点“重”(除了它是 Oracle 自己的!)。 SQLite 可能是最合适的选择。因为它是无服务器的、轻量级的、开源的并且支持大多数 SQL 代码(它的许可证是“公共域”)。另一个方便的事情是 SQLite 将数据存储在可以存储在任何地方的单个文件中。
但是,什么是 SQLite?
SQLite 是一个包含在 C 编程库中的关系数据库管理系统。与许多其他数据库管理系统相比,SQLite 不是客户端-服务器数据库引擎。而是嵌入到最终程序中。
SQLite 是一个流行的公共领域 选择作为嵌入式数据库软件,用于应用程序软件(如 Web 浏览器)中的本地/客户端存储。它可以说是部署最广泛的数据库引擎,因为今天它被几个广泛使用的浏览器、操作系统和嵌入式系统(例如移动电话)等使用。 SQLite 绑定了许多编程语言,例如 Python,我们的项目中使用了这种语言。
(更多关于维基百科)
我们不会在这里介绍太多细节,但可以在此链接中找到完整的 SQLite 文档:https://www.sqlite.org/docs.html
就这样吧!让我们在 Pi 上安装 SQLite
安装:
请按照以下步骤创建数据库。
1. 使用以下命令将 SQLite 安装到树莓派:
sudo apt-get install sqlite3
2.创建一个目录来开发项目:
mkdir Sensors_Database
3.移动到这个目录:
cd mkdir Sensors_Database/
3. 命名并创建一个像 databaseName.db 这样的数据库 (在我的例子中是“sensorsData.db”):
sqlite3 sensorData.db
将出现一个“shell”,您可以在其中输入 SQLite 命令。我们稍后会返回。
sqlite>
命令以“.”开头,例如“.help”、“.quit”等
4.退出shell返回终端:
sqlite> .quit
上面的终端打印屏幕显示了所解释的内容。
上面的“sqlite>”只是为了说明 SQLite shell 将如何出现。您不需要键入它。它会自动出现。
第 3 步:创建和填充表
图>为了在数据库中记录 DHT 传感器测量数据,我们必须创建表 (一个数据库可以包含多个表)。我们的表将被命名为“DHT_data”并且有 3 列,我们将在其中记录我们收集的数据:日期和时间(列名:时间戳 )、温度(列名:temp ) 和湿度(列名:hum ).
创建表格:
要创建表,您可以这样做:
- 直接在 SQLite shell 上,或
- 使用 Python 程序。
1.使用外壳:
打开上一步创建的数据库:
sqlite3 sensorData.db
并用SQL语句输入:
sqlite> BEGIN;sqlite> CREATE TABLE DHT_data (timestamp DATETIME, temp NUMERIC, hum NUMERIC);sqlite> COMMIT;
所有 SQL 语句都必须以“;”结尾。通常,这些语句使用大写字母书写。这不是强制性的,而是一种很好的做法。
2. 使用Python
import sqlite3 as liteimport syscon =lite.connect('sensorsData.db')with con:cur =con.cursor() cur.execute("DROP TABLE IF EXISTS DHT_data") cur.execute("CREATE TABLE DHT_data(timestamp DATETIME, temp NUMERIC, hum NUMERIC)")
从我的 GitHub 打开上面的代码:createTableDHT.py
在您的终端上运行它:
python3 createTableDHT.py
无论使用何种方法,都应创建该表。您可以使用“.table”命令在 SQLite Shell 上验证它。打开数据库外壳:
sqlite3>sensorsData.db
在 shell 中,一旦您使用 .table 命令,创建的表名称将出现(在我们的例子中只有一个:“DHT_table”。然后退出 shell,使用 .quit 命令。
sqlite> .tableDHT_datasqlite> .quit
在表格中插入数据:
让我们在我们的数据库中输入 3 组数据,其中每组将有 3 个组件:(时间戳、温度和嗡嗡声)。组件时间戳 将是真实的并从系统中获取,使用内置函数“now”和temp 和嗡嗡声 分别为 oC 和 % 中的虚拟数据。
注意 时间在“UTC”中,这有什么好处,因为您不必担心与夏令时和其他事项相关的问题。如果您想以本地化时间输出日期,只需将其转换为适当的时区即可。
创建表的方法相同,您可以通过 SQLite shell 或 Python 手动插入数据。在 shell 中,您将使用这样的 SQL 语句逐个数据地执行此操作(对于我们的示例,您将执行 3 次):
sqlite> INSERT INTO DHT_data VALUES(datetime('now'), 20.5, 30);
在 Python 中,您可以立即执行相同的操作:
import sqlite3 as liteimport syscon =lite.connect('sensorsData.db')with con:cur =con.cursor() cur.execute("INSERT INTO DHT_data VALUES (datetime('now'), 20.5, 30)") cur.execute("INSERT INTO DHT_data VALUES(datetime('now'), 25.8, 40)") cur.execute("INSERT INTO DHT_data VALUES(datetime(')现在'), 30.3, 50)")
从我的 GitHub 打开上面的代码:insertTableDHT.py
在 Pi 终端上运行它:
python3 insertTableDHT.py
为了确认以上代码是否有效,您可以通过shell检查表中的数据,使用SQL语句:
sqlite> SELECT * FROM DHT_DATA;
上面的终端打印屏幕显示了表格行的显示方式。
第 4 步:使用 Python 插入和验证数据
图>首先,让我们做与之前相同的操作(输入和检索数据),但同时使用 python 并在终端上打印数据:
import sqlite3import sysconn=sqlite3.connect('sensorsData.db')curs=conn.cursor()# 在tabledef add_data (temp, hum) 上插入数据的函数:curs.execute("INSERT INTO DHT_data values(datetime('now'), (?), (?))", (temp, hum)) conn.commit()#调用函数插入dataadd_data (20.5, 30 )add_data (25.8, 40)add_data (30.3, 50)# print database contentprint ("\nEntire database contents:\n")for row in curs.execute("SELECT * FROM DHT_data"):print (row)# close the useconn.close() 之后的数据库
从我的 GitHub 中打开上面的代码:insertDataTableDHT.py 并在您的终端上运行它:
python3 insertDataTableDHT.py
上面的终端打印屏幕显示了结果。
第 5 步:DHT22 温湿度传感器
图>到目前为止,我们已经在数据库中创建了一个表,我们将在其中保存传感器将读取的所有数据。我们还在那里输入了一些虚拟数据。现在是时候使用要保存在表格中的真实数据、气温和相对湿度了。为此,我们将使用旧的和好的 DHTxx(DHT11 或 DHT22)。 ADAFRUIT 网站提供了有关这些传感器的重要信息。波纹管,从那里检索到的一些信息:
概览
低成本的 DHT 温度和湿度传感器非常基本且速度较慢,但非常适合想要进行一些基本数据记录的爱好者。 DHT 传感器由两部分组成,电容式湿度传感器和热敏电阻。里面还有一个非常基础的芯片,可以做一些模数转换,并输出带有温度和湿度的数字信号。使用任何微控制器都可以很容易地读取数字信号。
DHT11 与 DHT22
我们有两个版本的 DHT 传感器,它们看起来有点相似并且具有相同的引脚排列,但具有不同的特性。以下是规格:
DHT11(通常为蓝色)
适用于 20-80% 湿度读数,精度为 5% 适用于 0-50°C 温度读数 ±2°C 精度不超过 1 Hz 采样率(每秒一次)
- 超低成本
- 3 至 5V 电源和 I/O
- 转换期间(请求数据时)使用的最大电流为 2.5mA
- 机身尺寸 15.5mm x 12mm x 5.5mm
- 4 个引脚,间距为 0.1 英寸
DHT22(通常为白色)
适用于 0-100% 湿度读数,精度为 2-5% 适用于 -40 至 125°C 温度读数 ±0.5°C 精度不超过 0.5 Hz 采样率(每 2 秒一次)
- 低成本
- 3 至 5V 电源和 I/O
- 转换期间(请求数据时)使用的最大电流为 2.5mA
- 机身尺寸 15.1mm x 25mm x 7.7mm
- 4 个引脚,间距为 0.1 英寸
如您所见,DHT22 在稍大的范围内更加准确和良好。两者都使用单个数字引脚并且“缓慢”,因为您不能每秒查询超过一次 (DHT11) 或两次 (DHT22)。
两个传感器都能正常工作,将室内信息存储在我们的数据库中。
DHTxx 有 4 个引脚(面向传感器,引脚 1 是最左边的):
- VCC(我们可以从 RPi 连接到外部 5V 或 3.3V);
- 数据输出;
- 未连接
- 地面。
我们将在我们的项目中使用 DHT22。
通常您会在小于 20m 的距离上使用传感器,数据和 VCC 引脚之间应连接一个 4K7 欧姆电阻器。 DHT22 输出数据引脚将连接到 Raspberry GPIO 16。
图>检查以上将传感器连接到 RPi 引脚的电气图,如下所示:
- 引脚 1 – Vcc ==> 3.3V
- 引脚 2 – 数据 ==> GPIO 16
- 引脚 3 – 未连接
- 引脚 4 – 地 ==> 地
不要忘记在 Vcc 和数据引脚之间安装 4K7 欧姆电阻。连接传感器后,我们还必须在我们的 RPi 上安装它的库。我们将在下一步中执行此操作。
第 6 步:安装 DHT 库
图>在您的 Raspberry 上,从 /home 开始,转到 /Documents:
cd 文档
创建一个目录来安装库并移动到那里:
mkdir DHT22_Sensorcd DHT22_Sensor
在浏览器上,转到 Adafruit GITHub:https://github.com/adafruit/Adafruit_Python_DHT
单击右侧的下载 zip 链接下载库,然后将存档解压缩到您最近创建的 Raspberry Pi 文件夹中。然后进入库所在目录(解压时自动创建的子文件夹),执行命令:
sudo python3 setup.py install
从我的 GITHUB 打开一个测试程序 (DHT22_test.py):
import Adafruit_DHTDHT22Sensor =Adafruit_DHT.DHT22DHTpin =16 湿度,温度 =Adafruit_DHT.read_retry(DHT22Sensor, DHTpin)如果湿度不是 None 并且温度不是 None:print('Temp={0:0.1f}*C Humidity={1:0.1f}%'.format(温度, 湿度))else:print('读取失败。再试一次!')
使用以下命令执行程序:
python3 DHT22_test.py
上面的终端打印屏幕显示了结果。
第 7 步:捕获真实数据
图>现在我们已经安装并配置了传感器和数据库,是时候读取和保存真实数据了。
为此,我们将使用以下代码:
import timeimport sqlite3import Adafruit_DHTdbname='sensorsData.db'sampleFreq =2 # time in seconds# get data from DHT sensordef getDHTdata():DHT22Sensor =Adafruit_DHT.DHT22 DHTpin =16 hum, temp =Adafruit_DHT.read_retry(DHT22Sensor, DHTpin) 如果 hum 不是 None 并且 temp 不是 None:hum =round(hum) temp =round(temp, 1) logData (temp, hum)# 在 databasedef logData 上记录传感器数据(temp, hum):conn=sqlite3.connect(dbname) curs=conn.cursor() curs.execute("INSERT INTO DHT_data values(datetime('now'), (?), (?))", (temp , hum)) conn.commit() conn.close()# display database datadef displayData():conn=sqlite3.connect(dbname) curs=conn.cursor() print ("\nEntire database contents:\n") for row in curs.execute("SELECT * FROM DHT_data"):print (row) conn.close()# main functiondef main():for i in range (0,3):getDHTdata() time.sleep(sampleFreq) displayData ()#执行程序main()
从我的 GitHub 中打开上述文件:appDHT.py 并在您的终端上运行它:
python3 appDHT.py
函数 getDHTdata() 捕获 3 个 DHT 传感器样本,测试它们是否有错误,如果正常,则使用函数logData (temp, hum) 将数据保存在数据库中 .代码的最后一部分调用函数displayData() 在终端上打印我们表格的全部内容。
上面的打印屏幕显示了结果。观察后3行(行)是本程序捕获的真实数据,前3行是之前手动输入的数据。
事实上 appDHT.py 并不是一个好名字。通常,“appSomething.py”与 Web 服务器上的 Python 脚本一起使用,我们将在本教程中进一步介绍。但当然你可以在这里使用它。
第 8 步:自动捕获数据
图>此时,我们必须实现的是一种自动在我们的数据库中读取和插入数据的机制,即我们的“记录器”。
打开一个新的终端窗口 并输入以下 Python 代码:
import timeimport sqlite3import Adafruit_DHTdbname='sensorsData.db'sampleFreq =1*60 # time in seconds ==> 每 1 分钟采样一次# 从 DHT sensordef getDHTdata() 获取数据:DHT22Sensor =Adafruit_DHT.DHT22 DHTpin =16 hum, temp =Adafruit_DHT.read_retry(DHT22Sensor, DHTpin) 如果 hum 不是 None 并且 temp 不是 None:hum =round(hum) temp =round(temp, 1) return temp, hum # 在数据库上记录传感器数据def logData (temp, hum):conn=sqlite3.connect(dbname) curs=conn.cursor() curs.execute("INSERT INTO DHT_data values(datetime('now'), (?), ( ?))", (temp, hum)) conn.commit() conn.close()# main functiondef main():while True:temp, hum =getDHTdata() logData (temp, hum) time.sleep(sampleFreq) # ------------ 执行程序 main()
或者从我的 GitHub 获取它:logDHT.py。在终端上运行它:
python3 logDHT.py
main() 函数的作用是:
调用函数 getDHTdata() ,这将返回 DHT22 传感器捕获的数据。获取这些数据(温度和湿度)并将它们传递给另一个函数:logData(temp, hum) 将它们连同实际日期和时间一起插入到我们的表中。然后进入睡眠状态,等待下一个预定时间来捕获数据(由 sampleFreq 定义) ,在本例中为 1 分钟)。
让终端窗口保持打开状态。
例如,在您使用 [Ctr+z] 终止程序之前,该程序将不断捕获数据,并将它们输入到我们的数据库中。我让它以 1 分钟的频率运行一段时间以更快地填充数据库,几个小时后将频率更改为 10 分钟。
还有其他机制可以比使用“time.sleep”更有效地执行这种“自动记录器”,但上面的代码可以很好地满足我们的目的。无论如何,如果你想实现一个更好的“调度器”,你可以使用 Crontab ,这是一个方便的 UNIX 工具来安排作业。在本教程中可以找到对 Crontab 的一个很好的解释:“使用 Crontab 在 Linux 上安排任务”,作者 Kevin van Zonneveld。
第 9 步:查询
图>现在我们的数据库是自动输入的,我们应该找到处理所有这些数据的方法。我们通过查询来实现!
什么是查询?
在数据库上使用 SQL 语言的最重要特性之一是能够创建“数据库查询”。换句话说,查询从数据库中提取数据,以可读的形式格式化它们。查询必须用SQL 语言编写 , 使用 SELECT 选择特定数据的语句。
事实上,我们在最后一步“广泛地”使用了它:“SELECT * FROM DHT_data”。
示例:
让我们对我们已经创建的表上的数据创建一些查询。为此,输入以下代码:
import sqlite3conn=sqlite3.connect('sensorsData.db')curs=conn.cursor()maxTemp =27.6print ("\n整个数据库内容:\n") for row in curs.execute("SELECT * FROM DHT_data"):print (row)print ("\nDatabase entries for a特定湿度值:\n")for row in curs.execute("SELECT * FROM DHT_data WHERE hum='29'"):print (row) print ("\nDatabase entry where the temperature is above 30oC:\n")for row in curs.execute("SELECT * FROM DHT_data WHERE temp>
30.0"):print (row) print ("\n温度高于 x 的数据库条目:\n")for row in curs.execute("SELECT * FROM DHT_data WHERE temp>
(?)", (maxTemp,)):print (row)
或者从我的 GitHub 获取它:queryTableDHT.py,然后在终端上运行它:
python3 queryTableDHT.py
您可以在上面终端的打印屏幕上看到结果。这些是让您了解查询的简单示例。花点时间了解一下上面代码中的SQL语句。
如果您想了解更多关于 SQL 语言的知识,一个很好的来源是 W3School SQL 教程。
第 10 步:在表中输入的最后数据:
图>一个非常重要的查询是检索最后输入的数据 (或登录)在一张桌子上。我们可以直接在 SQLite shell 上执行,使用命令:
sqlite> SELECT * FROM DHT_data ORDER BY timestamp DESC LIMIT 1;
或者运行一个简单的python代码如下:
import sqlite3conn =sqlite3.connect('sensorsData.db')curs=conn.cursor()print ("\n上次登录数据库的数据:\n")for curs.execute("SELECT * FROM DHT_data ORDER BY timestamp DESC LIMIT 1")中的行:打印(行)
您可以在上面的第一个终端打印屏幕上看到结果。
请注意,结果将显示为“值的元组”:('timestamp', temp, hum).
元组返回我们表格的最后一行内容,它由 3 个元素组成:
- row[0] =时间戳 [字符串]
- row[1] =temp [float]
- row[2] =hum [float]
所以,我们可以更好地工作我们的代码,从表中检索“干净”的数据,例如:
import sqlite3conn=sqlite3.connect('sensorsData.db')curs=conn.cursor()print ("\n上次登录数据库的原始数据:\n") for row in curs.execute("SELECT * FROM DHT_data ORDER BY timestamp DESC LIMIT 1"):print (str(row[0])+" ==> Temp ="+str(row[1])+" Hum ="+str(row[2]))
从我的 GitHub 中打开文件:lastLogDataTableDHT.py 并在终端上运行它:
python3 lastLogDataTableDHT.py
您可以在上面的第二个终端打印屏幕上看到结果。
第 11 步:用于数据可视化的 Web 前端
图>在我的上一个教程:Python WebServer 与 Flask 和 Raspberry Pi 中,我们学习了如何实现网络服务器(使用 Flask)来捕获来自传感器的数据并在网页上显示它们的状态。
这也是我们在这里想要完成的。不同之处在于要发送到前端的数据将从数据库中提取 而不是像我们在该教程中所做的那样直接来自传感器。
创建网络服务器环境:
首先要做的是在你的树莓派上安装 Flask。如果没有,请转到终端并输入:
sudo apt-get install python3-flask
当您开始一个新项目时,最好的办法是创建一个文件夹来组织您的文件。例如:
从家里,转到我们的工作目录:
cd Documents/Sensors_Database
新建一个文件夹,例如:
mkdir dhtWebServer
上面的命令将创建一个名为“dhtWebServer”的文件夹,我们将在其中保存我们的python脚本:
/home/pi/Documents/Sensor_Database/rpiWebServer
现在,在这个文件夹上,让我们创建 2 个子文件夹:static 用于 CSS 并最终用于 JavaScript 文件和模板 用于 HTML 文件。 转到新创建的文件夹:
cd dhtWebServer
并创建 2 个新的子文件夹:
mkdir static
和
mkdir 模板
最终目录“tree”将如下所示:
├── Sensors_Database ├──sensorsData.db ├── logDHT.py ├── dhtWebSensor ├── templates └── static
我们将创建的数据库保留在 /Sensor_Database 目录中,因此您需要使用“../sensorsData.db”连接 SQLite。
行!环境准备好后,让我们组装部件并创建我们的 Python WebServer 应用程序 .上图让我们知道应该做什么!
第 12 步:Python 网络服务器应用程序
图>从上一张图开始,让我们使用 Flask 创建一个 python WebServer。我建议 Geany 作为要使用的 IDE, 一旦您可以同时处理不同类型的文件(.py、.html 和 .css)。
下面的代码是我们第一个 web 服务器上使用的 python 脚本:
from flask import Flask, render_template, requestapp =Flask(__name__)import sqlite3# 从 databasedef getData() 检索数据: conn=sqlite3.connect('../sensorsData .db') curs=conn.cursor() for row in curs.execute("SELECT * FROM DHT_data ORDER BY timestamp DESC LIMIT 1"):time =str(row[0]) temp =row[1] hum =row [2] conn.close() return time, temp, hum# main route @app.route("/")def index():time, temp, hum =getData() templateData ={ 'time':time, ' temp':temp, 'hum':hum } return render_template('index.html', **templateData)if __name__ =="__main__":app.run(host='0.0.0.0', port=80, debug=错误)
你可以从我的 GitHub 获取 python 脚本 appDhtWebServer.py。上面代码的作用是:
有了这个请求,代码中做的第一件事就是使用函数time, temp, hum =getData()从数据库中获取数据。 此函数与之前用于检索存储在表中的数据的查询基本相同。有了手头的数据,我们的脚本返回到网页 (index.html ):时间 , 温度 和嗡嗡声 作为对先前请求的响应。
- 每次有人“点击”“/”,即我们网页的主页 (index.html) 时,就会生成一个 GET 请求;
所以,让我们看看 index.html 和 style.css 将用于构建我们的前端的文件:
index.html
DHT 传感器数据 DHT 传感器数据
TEMPERATURE ==> {{ tempLab }} oC
HUMIDITY (Rel.) ==> { { humLab }} %
最后传感器读数:{{ time }} ==> REFRESH
@2018 由 MJRoBot.org 开发