本文所使用到的所有技术与对应的版本号如下:\ 树莓派:Raspberry Pi 4B;温度传感器:DS18B20;Python:3.7.3;数据库:MariaDB-10.0.28;Canal(Server/Client):1.1.3;Java:1.8;Javax mail:1.5.4
项目所在地址: Gitee:Temperature Alarm System: 基于 树莓派+DS18B20+Canal 实现 温度报警系统 (gitee.com)
1、系统架构
最近突然想到一种温度报警系统的简单实现思路:大概就是通过DS18B20采集室内温度,树莓派循环读取室内温度值,达到温度报警阈值后,向数据库中写入报警信息。同时搭建一台简单服务器并且在其上部署一台Canal实时监听树莓派中数据库的数据变更事件,获取后发送邮件提醒温度预警。
整个系统可以分为采集层、通信层、告警层。系统结构如下图所示:
由于整个过程都是线性处理的,因此整个系统理解起来也非常轻松。
2、系统拆解
有了系统架构和简单实现思路后,让我们来思考下如何将系统拆解成一个个小任务并逐个解决。
2.1、数据采集层
对于数据采集层来说,难点在于树莓派如何读取传感器采集的温度信息。
玩过单片机的都知道,对于模拟信号(温度),我们需要一个AD芯片将模拟信号转换成8位、12位或16位的数字信号以高低电平的形式发送给单片机进行相应处理。温度采样精度取决于AD芯片的位数。这种方式处理非常复杂。
而DS18B20这种常用的温度传感器直接输出数字信号,体积小,抗干扰能力强。并且封装好后只有三根引脚,能够极大节省处理器的IO资源。与树莓派集成使用时,可以通过驱动将温度直接写入树莓派的设备文件中,节省大家的硬件驱动开发成本,也不用困扰在各种时序处理逻辑上。
因此,树莓派与DS18B20集成使用,我们可以使用Python读取设备文件的方式获取温度信息。
2.2、通信层
系统的通信层我选用了Canal监听MySQL数据库变更的方案(不要问我为什么搞这么麻烦 (~ ̄▽ ̄)~ ,问就是最近工作中用到了?)。
通信层还有很多其它方案的选择:
简单点可以去掉通信层,直接使用Python发送邮件
服务端部署Netty,自己实现一个简单的RPC协议
通过消息队列的方式发送告警信息
...
这里使用写数据库的方式还有一种好处,可以在以后有需要的时候将数据提取出来进行数据分析。(好吧,这就是在强行好处?)
方案确定了以后,实现就比较简单了。整个通信层我们需要:1、实现Python写入MySQL数据库;2、搭建Canal服务器监听数据库;3、将监听消息投递出去。
2.3、告警层
告警层的邮件发送没有什么难点,当初我纠结的地方就在于如何获取Canal监听的消息,是使用RockeyMQ还是Kafka,无论使用两个中的其中一个都还需要搭建一台服务器,感觉还是比较麻烦。当初想到这里的时候我其实是有点想放弃Canal了。
后来看到Canal自己也实现一套简单客户端Canal-Client通信,我就直接将其代码搬了过来,然后简单修改了下。
因此告警层技术点在于:1、通过Canal客户端获取服务器投递的消息;2、发送告警邮件。
2.4、小结
总结一下,要实现一个简单的温度报警系统,要完成以下几个步骤:
使用Python读取树莓派设备文件,获取温度值信息
判断温度值是否达到阈值,达到后写入MySQL数据库
搭建Canal服务器,监听树莓派上的MySQL数据库
使用Canal-Client读取Canal-Server投递的消息,然后发送邮件告警
3、具体实现
3.1、树莓派连接DS18B20
系统使用的DS18B20如下图所示:
该测温模块总共3Pin,分别位VCC、GND和DQ(Data Pin)。由于DS18B20只通过一根引脚传递数据,因此其使用的是单总线(One Wire) 通讯协议传输数据。模块与树莓派的引脚连接如下表所示:
将DB18B20的引脚与树莓派使用杜邦线连接起来即可(记得最好在断电情况下连接)。
3.2、开启树莓派单总线协议
我一般都是通过SSH连接树莓派的,因此无法通过GUI直接开启树莓派单线总功能。需要通过修改启动配置文件的方式开启。(无屏幕、键盘配置,树莓派启动连接wifi,以及获取树莓派IP地址的方式可以参考这里,获取到IP地址后,便可以通过SSH远程连接了)
开启步骤如下:
1、 修改启动配置文件
vim/boot/config.txt
2、 在文件中的最后一行加如下配置,树莓派默认使用Pin 7(物理引脚,BOARD编码;该引脚的BCM编码为4)作为单总线引脚,但我们也可以手动指定其它引脚。
dtoverlay=w1-gpio#如果想要指定其它引脚,以下表示使用BCM编码为18的引脚dtoverlay=w1-gpio,gpiopin=18
树莓派的引脚有:物理引脚(即BOARD编码);功能名;BCM编码;wiringPi编码。这里贴一张引脚对照表,防止大家出错。
ps:一定要连接对引脚,否则无法在设备文件中找到对应的测温模块设备文件,像我一开始连接错后在设备文件这种只能找到00-**
开头的设备文件,无法读取温度。
3、 重启树莓派
sudoreboot
4、 使用如下命令校验单总线协议是否开启。
lsmod
如果能在终端中看到如下输出则代表单总线协议已经开启。
5、 在终端中输入如下两行命令,如果没有任何输出,则单总线已经初始化完成,可以进行温度采集了。
modprobew1-gpiomodprobew1-therm
3.3、获取温度值
1、 进入系统设备目录:
cd/sys/bus/w1/devices
2、 如果以上都成功执行了,可以看到当前目录下存在一个28-
开头的文件夹,这个文件夹的名称代表连接的温度传感器设备的编号,不同设备编号可能不一致。
3、 进入设备文件夹,文件夹下有一个名字为w1_slave
的文件,这个文件即存储了传感器的采样温度。我们执行以下命令看看文件中写了什么。
catw1_slave
第一行比较熟悉就是CRC、YSE两个单词了。大概可以猜出使用循环冗余校验后得到的数据是有效的。
第二行t=27437
表示当前的温度值,不过要除以1000来换算成摄氏度。图中表明当前温度值为27.437℃。
3.4、Python循环读取温度值
这一部分比较简单,只需要使用Python的内置os、time模块就行了,这里只贴核心代码,全部代码可以到Gitee/GitHub地址去看:
#传感器编号deviceNum="28-01205b67097c"#设备记录数据的文件地址deviceFile='/sys/bus/w1/devices/'+deviceNum+'/w1_slave'#打开并读取文件数据defreadDeviceFile():f=open(deviceFile,'r')lines=f.readlines()f.close()returnlines#解析温度数据#deviceFile文件中的数据一般如下所示:其中YES表明是有效数据,t后面是温度,(t%1000.0)就是摄氏度#b7014b467fff0c104b:crc=4bYES#b7014b467fff0c104bt=27437defreadTemp():lines=readDeviceFile()#如果第一行末尾不是YES,则等待0.2s后重复读取,直至读取有效数据为止whilelines[0].strip()[-3:]!='YES':time.sleep(0.2)#循环继续读lines=readDeviceFile()#读取温度tempIndex=lines[1].find('t=')iftempIndex!=-1:temp=lines[1][tempIndex+2:]tempC=float(temp)/1000.0returntempC
3.5、树莓派安装MySQL
由于树莓派系统 Raspbian OS 是基于 Debian 的,因此我们可以直接使用apt命令直接下载MySQL。
sudoaptinstallmysql-server
执行上述命令后,终端会打印如下信息。说明树莓派的操作系统不支持MySQL,这也是MySQL被Oracle收购后的痛苦。但提示信息告诉我们可以使用mariadb-server,开源的好处就在这里体现了出来。
Readingpackagelists...DoneBuildingdependencytreeReadingstateinformation...DonePackagemysql-serverisnotavailable,butisreferredtobyanotherpackage.Thismaymeanthatthepackageismissing,hasbeenobsoleted,orisonlyavailablefromanothersourceHoweverthefollowingpackagesreplaceit:mariadb-server-10.0E:Package'mysql-server'hasnoinstallationcandidate
接下来我们安装mariadb-server
dtoverlay=w1-gpio#如果想要指定其它引脚,以下表示使用BCM编码为18的引脚dtoverlay=w1-gpio,gpiopin=180
初始安装后我们可以使用如下命令修改root账号的密码(没错,"123456"永远的密码之神)
dtoverlay=w1-gpio#如果想要指定其它引脚,以下表示使用BCM编码为18的引脚dtoverlay=w1-gpio,gpiopin=181
然后使用登录命令就可以登录到mariabd中了
dtoverlay=w1-gpio#如果想要指定其它引脚,以下表示使用BCM编码为18的引脚dtoverlay=w1-gpio,gpiopin=182
ps:登录过后,我们可能想尝试使用Navicat或其它客户端远程登录测试,但出现错误码为10061的错误信息
dtoverlay=w1-gpio#如果想要指定其它引脚,以下表示使用BCM编码为18的引脚dtoverlay=w1-gpio,gpiopin=183
此时我们可以按照如下方式解决:
将 /etc/mysql/mariadb.conf.d/50-server.cnf
的 bind-address = 127.0.0.1
修改为 0.0.0.0
。或直接注释掉bind-address = 127.0.0.1
。
重启MySQL,service mysqld restart
3.6、Python写入MySQL
Python关于MySQL的CRUD比较简单,大家可以参考Python3 MySQL 数据库连接 - PyMySQL 驱动快速上手即可。
这里有点需要注意:PyMySQL是Python3的库,使用pip3安装。而我一开始烧录的树莓派系统只默认携带Python2。因此,大家可以将原系统中的Python2卸载,然后安装Python3,具体可以参考将树莓派内置的 Python2.7 升级成 Python3
掌握了如何用Python对MySQL进行CRUD操作后就可以直接看项目相关的核心代码了:
dtoverlay=w1-gpio#如果想要指定其它引脚,以下表示使用BCM编码为18的引脚dtoverlay=w1-gpio,gpiopin=184
3.7、搭建Canal-Server
这里我并未选择在树莓派中搭建Canal-Server,而是另起一台虚拟机进行搭建。
Canal版本我选择的是1.1.3。大家在版本选择的时候也需要注意下,客户端和服务端版本最好一致;高版本MySQL最好选择高版本Canal。否则出现各种问题,比如我就出现了低版本Canal连接高版本MySQL出现密码加密方式错误的问题。
搭建Canal-Server的方式如下:
下载Canal-Server
dtoverlay=w1-gpio#如果想要指定其它引脚,以下表示使用BCM编码为18的引脚dtoverlay=w1-gpio,gpiopin=185
解压缩
dtoverlay=w1-gpio#如果想要指定其它引脚,以下表示使用BCM编码为18的引脚dtoverlay=w1-gpio,gpiopin=186
解压完成后进入目录,目录下有bin、conf、lib、logs
四个文件夹
dtoverlay=w1-gpio#如果想要指定其它引脚,以下表示使用BCM编码为18的引脚dtoverlay=w1-gpio,gpiopin=187
四个目录的作用如下:
bin:存放了启动和停止Canal-Server的可执行文件
conf:存放了Canal-Server的可配置文件
lib:存放了运行时依赖jar包
logs:存放了日志文件
修改配置文件
dtoverlay=w1-gpio#如果想要指定其它引脚,以下表示使用BCM编码为18的引脚dtoverlay=w1-gpio,gpiopin=188
下面我只将我修改了的配置给列出来,其它的大家可以参考官网查看配置详情
dtoverlay=w1-gpio#如果想要指定其它引脚,以下表示使用BCM编码为18的引脚dtoverlay=w1-gpio,gpiopin=189
启动
sudoreboot0
查看 server 日志
sudoreboot1
查看 instance 日志
sudoreboot2
关闭
sudoreboot3
3.8、使用Canal-Client并发送邮件
这一部分我使用的是Canal官网自带的example测试用例,并在其上进行一些修改而成。
首先在工程当中引入了如下依赖:
sudoreboot4
然后将canal-example中的SimpleCanalClientTest、AbstractCanalClientTest、BaseCanalClientTest
拷贝进工程,然后编写发送邮件的工具类即可完成监听并发送邮件了。详细代码可以参考Gitee或GitHub。
4、成果展示
由下面的结果可以看出:开始时一直在轮询传感器温度值,超过32.5℃后,立马输出已经发送邮件。此时IDEA(即最下面黑色部分为IDEA的控制台)也输出了相应的日志。等待1~2s后,邮箱中的邮件也到达了。