物联网项目 | LeanCloud+树莓派+安卓实现住宅环境异常告警系统
前言
本系统监测的条件限于温湿度、火焰、烟雾或有毒气体,以及登记授权人员人脸信息,(人脸检测和识别老早写了,懒得再接入了有时间再说吧) :neutral_face:出现异常时可及时记录和向用户APP推送消息告警。
目录
物料清单
配件名称 | 数量 | 功能 |
---|---|---|
Raspberry Pi 4B | 1 | 主机 |
闪迪Micro SD 16GB | 1 | 为树莓派烧录系统 |
树莓派专用电源适配器及电源线 | 1 | 为树莓派供电 |
安卓手机 | 1 | 控制和查看系统运行情况 |
MQ-2烟雾传感器 | 1 | 检测燃烧产生烟雾及有害气体 |
火焰传感器 | 1 | 检测一定线性范围的火焰 |
DHT11温湿度传感器 | 1 | 检测室内温度和相对湿度 |
杜邦线 | 9 | 连接传感器和树莓派电脑 |
软件架构
室内环境条件监控模块
系统同时使用多个传感器感知和上传所处环境的温湿度、烟雾和火焰触发数据,同时使用烟雾传感器、温度传感器和火焰传感器进行判断火情。
- 若只有烟雾传感器或火焰传感器被触发或温度高于45.0摄氏度,向用户发送普通告警;
- 其中两个条件满足,向用户发送中级告警;
- 若三者均满足,向用户发送紧急告警信息。
推送消息被用户点击后,会跳转到APP主界面让用户查看详细传感器数据。
手机监视APP模块
接受消息推送和查看传感器上传的数据。
硬件模块使用
DHT11温湿度传感器
1.连接VCC,GND,其中DATA接到GPIO 4,树莓派4B引脚如图:
2.安装python依赖Adafruit_DHT。
在控制台中输入命令
pip install Adafruit_Python_DHT
进行安装。安装成功后,编写测试程序:import time
import sys
import Adafruit_DHT
DHT_chanel = 18 #BCM编码引脚口
while true
time.sleep(1)
hum,temp = Adafruit_DHT.read_retry(11,DHT_chanel)
3.运行正常,结果如图:(以humidity表示相对湿度)
MQ-2烟雾传感器
1.连接VCC,GND,其中DO为TTL高低电平输出口,接GPIO 0
2.安装python依赖PRi.GPIO。在控制台中输入命令pip install PRi.GPIO
进行安装。
3.编写测试程序如下,高电平时正常:
import RPi.GPIO as GPIO
import time
CHANNEL=7 # GPIO引脚口
GPIO.setmode(GPIO.BOARD)
GPIO.setup(CHANNEL,GPIO.IN,pull_up_down=GPIO.PUD_DOWN)
try:
while True: # 执行一个while死循环
status=GPIO.input(CHANNEL) # 检测7号引脚口的输入高低电平状态
print(status) # 实时打印此时的电平状态
if status == True: # 如果为高电平,说明MQ-2正常,并打印“OK”
print ( ' OK ' )
else: # 如果为低电平,说明MQ-2检测到有害气体,并打印“dangerous”
print ( ' high toxic gas level ! ! ! ' )
time.sleep(5)
except KeyboardInterrupt: # 异常处理,当检测按下键盘的Ctrl+C,就会退出这个>脚本
GPIO.cleanup()
4.运行正常,结果如图:
云后端接入与使用
云后端的作用就是充当手机APP和物联网硬件之间的桥梁,也就是我们的树莓派和安卓APP之间用于推送消息以及保存传感器数据的中间人。经过上一轮的测试,Bmob云后端
虽然提供了比较完善的免费云后端服务,但在Android SDK接入时有难以解决的错误,故采用同样具有数据服务和消息推送的免费云后端服务LeanCloud
。
为安卓用户端接入LeanCloud数据及消息推送SDK
按照官方文档中对于Java SDK的说明,在Android Studio中使用Gradle引入LeanCloud的数据存储和消息推送的Java SDK,即在module级Gradle配置文件的相应位置中加入如下引入:
implementation 'cn.leancloud:storage-android:8.0.1'
implementation 'cn.leancloud:realtime-android:8.0.1'
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
然后再进行Gradle项目同步即可。
LeanCloud安卓SDK初始化
在Android项目的Application类的onCreate()方法内进行如下操作:
public class MyLeanCloudApp extends Application {
@Override
public void onCreate() {
super.onCreate();
// 提供 this、App ID、App Key、Server Host 作为参数
// 注意这里千万不要调用 cn.leancloud.core.LeanCloud 的 initialize 方法,否则会出现 NetworkOnMainThread 等错误。
LeanCloud.initialize(this,
"t8dmUAOAX4NptIX4oziNpYPc-gzGzoHsz",
"pDuJ1si51y5RGGdWyqReM8Ff",
"https://please-replace-with-your-customized.domain.com");//这里填入LeanCloud上提供或是自己绑定的域名,本项目使用官方提供的免费域名
PushService.setDefaultChannelId(this, "android");#配置默认消息通道
#订阅消息通道
PushService.subscribe(this, "public", MainActivity.class);
PushService.subscribe(this, "android", MainActivity.class);
#使用SharePreferences存储安装信息,是否为第一次安装以及安装得到的installationId
SharedPreferences sp = getSharedPreferences("installation", MODE_PRIVATE);
SharedPreferences.Editor edit = sp.edit();
boolean isInstalled = sp.getBoolean("isInstalled", false);
if(!isInstalled){
//LCInstallation.getCurrentInstallation().saveInBackground();
LCInstallation.getCurrentInstallation().saveInBackground().subscribe(new Observer<LCObject>() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(LCObject avObject) {
// 关联 installationId 到用户表等操作。
String installationId = LCInstallation.getCurrentInstallation().getInstallationId();
edit.putString("installationId",installationId).commit();
System.out.println("保存成功:" + installationId );
}
@Override
public void onError(Throwable e) {
System.out.println("保存失败,错误信息:" + e.getMessage());
}
@Override
public void onComplete() {
}
});
edit.putBoolean("isInstalled", true);
edit.commit();
}
}
完成后在AndroidManifest.xml声明此Application类,即在其中的<application下添加
android:name=".MyLeanCloudApp" >
这里的MyLeanCloudApp替换成自己的Application类名称。
LeanCloud安卓消息推送服务初始化
按照官方文档,在Android项目的AndroidManifest.xml的<application下添加如下服务和消息接收器:
<service android:name="cn.leancloud.push.PushService" />
<!--
<receiver android:name="cn.leancloud.push.LCBroadcastReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.USER_PRESENT" />
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
</intent-filter>
</receiver>
-->
<receiver
android:name=".MyCustomReceiver"
android:exported="false">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.USER_PRESENT" />
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
<action android:name="com.avos.UPDATE_STATUS" />
</intent-filter>
</receiver>
其中MyCustomReceiver
是一个自定义类,继承BroadcastReceiver
,代码如下:
public class MyCustomReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// 获取推送消息数据
String message = intent.getStringExtra("com.avoscloud.Data");
String channel = intent.getStringExtra("com.avoscloud.Channel");
System.out.println("push message");
System.out.println("message=" + message + ", channel=" + channel);
}
}
LeanCloud安卓消息推送服务接入完成测试
在LeanCloud的后台-推送-在线发送中,填写JSON推送内容如下:
{
"alert": "test content",
"title": "WARNING! Your house's condition is in danger!",
"silent": false
}
点击发送,Android端成功接到推送消息:
但我们要求的是由树莓派来主动触发消息推送,所以接下来为树莓派配置SDK。
为用户端监测硬件 —— 树莓派接入LeanCloud Python SDK
按照LeanCloud的官方文档,使用包依赖管理工具进行便捷的安装,即,在控制台中输入sudo pip install leancloud进行依赖包安装。
依赖包安装完成,在需要使用到LeanCloud服务的程序里先使用import leancloud导入,再使用命令leancloud.init("App ID ", " App Key ")初始化LeanCloud服务。
按照LeanCloud文档,使用Python在新增结构化数据的代码如下:
# 构建数据操作对象
StateObject = leancloud.Object.extend('State')# 'State'为表名
state_object = StateObject()
state_object.set('temperature', temp)# 'temperature' 为列名
state_object.set('relativeHumidity', hum)# 'relativeHumidity'为列名
state_object.set('isFireOK', isFireOK)# 'isFireOK'为列名
state_object.set('isSmokeOK', isMQ2OK)# 'isSmokeOK'为列名
state_object.set('recordDate', recordDate)# 'recordDate'为列名
state_object.save()#传入的各列数据类型需要一一对应,参照官方文档的数据类型
# 测试向Android端推送消息
content="temperature lower than 45.0, it’s just a test"
title="nothing"
data = json.loads(json.dumps({'alert': content, 'title': title}))
if(len(title)!=0 and len(content)!=0):
leancloud.push.sned(data,
channels=['android','public'],
push_time=None,
expiration_time=None,
expiration_interval=None,
where=None,cql=None,
flow_control=None,
prod=None)
在安卓端SDK和消息推送服务配置正常以后,可以正常收到如下推送:
传感器数据实体
传感器记录数据在云后端数据库中的实体如表所示:
State表
列名 | 数据类型 | 说明 |
---|---|---|
recordDate | DATE | 数据采集时间 |
temperature | NUMBER | 温度传感器数据 |
relativeHumidity | NUMBER | 相对湿度传感器数据 |
isFireOK | BOOLEN | 火焰传感器数据是否超阈值 |
isSmokeOK | BOOLEN | 烟雾传感器数据是否超阈值 |
IsException | BOOLEN | 是否有任一数据超过阈值 |
Android APP编写和打包测试
Android项目和集成开发环境
本项目使用基于intellJ IDEA社区版的Android Studio进行开发测试,新建Android项目并进行相应配置,使用Redmi Note 8 Pro 实体机进行测试。
App详细设计
为了减少项目复杂度,本项目采用最简单的安卓开发模式:MVC
。一个MainActivity
和对应界面即可。
App打开后自动进入到主界面MainActivity,并异步加载服务器记录的传感器数据。数据的展示采用RecyclerView + BaseRecyclerViewadapter
的模式。在MainActivity的layout
中,使用SwipeRefreshLayout
包裹RecyclerView以实现下拉加载的功能。
在MainActivity中为SwipeRefreshLayout添加下拉监听,下拉动作发生时执行服务器数据拉取动作。
点击右上角感叹号图标可以筛选有数据超过阈值的记录,再次下拉刷新可以取消筛选。完成后测试获得服务器数据如图:
参考资料
[1] PyPI. Adafruit_Python_DHT [EB/OL]. (2021-07-06). https://pypi.org/project/Adafruit_Python_DHT/
[2] 埃勒里灬波洛. 树莓派DHT22读Adafruit_DHT报错 can not import Beaglebone_Black_Driver的解决办法 [EB/OL]. (2020-09-09). https://blog.csdn.net/elleryer/article/details/108482087
[3] weixin_34112030. 基于树莓派(Raspberry Pi)平台的MQ-2烟雾报警系统实现(一) [EB/OL]. (2018-03-16). https://blog.csdn.net/weixin_34112030/article/details/92237494
[4] LeanCloud. LeanCloud文档 [EB/OL]. (2021-07-06). https://leancloud.cn/docs