CC2530/ESP32+传感器+ZigBee+MQTT+MYSQL+Springboot+Vue+Echarts的老人卫生间防摔倒自动报警系统
写在前言
这是第一次发布也是我第一次制作一个软硬件结合的小系统。分为两个版本,一种是使用esp32作为开发板,使用红外、触控、温湿度、蜂鸣器等作为传感器获取数据。通过MQTT将数据发送,使用python接收并保存到mysql数据库中,后端使用Springboot,前端使用vue结合echarts制作了一个可视化大屏。另一个版本使用CC2530作为开发板,使用激光测距传感器、红外传感器、温湿度传感器、光敏传感器等获取数据,并使用zigbee协议进行组网,将整个感知层分为4个节点和一个协调器,个节点负责采集数据,使用zigbee将数据传送到协调器,协调器通过esp8266使用mqtt将数据发送出去。后面数据获取、保存、前后端展示和esp32模块相同。下文分为一代和二代进行讲解。内容很长,希望你能看完。
运行效果展示
废话不多说,先看运行效果。演示视频录得有点草率,请多包涵,将就着看。 演示视频
一代版本
一代版本使用了3个ESP32开发板,分别获取卫生间、马桶和淋浴的信息。整体架构图如下:
ESP32+MQTT读取红外传感器
首先使用红外传感器判断老人是否使用卫生间。引脚连接图如下:
代码如下:
#include <WiFi.h>
#include <PubSubClient.h>
const char* ssid = "";
const char* password = "";
const char* mqtt_server = "broker.emqx.io";
const int buzzpin = 16;
const int ledpin = 2;
int infrared_value = 0;
int infrared_hight = 0;
int infrared_low = 0;
int flag = 0;
boolean infrared_flag = false;
char attributes[1000];
#define LED_BUILTIN 13
WiFiClient espClient;
PubSubClient client(espClient);
void setup_wifi() {
delay(10);
Serial.println();
Serial.print("连接: ");
Serial.println(ssid);
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
randomSeed(micros());
Serial.println("");
Serial.println("WiFi连接成功!");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
}
void callback(char* topic, byte* payload, unsigned int length) {
Serial.print("消息来自: [");
Serial.print(topic);
Serial.print("] 主题,内容:");
char str[100];
for (int i = 0; i < length; i++) {
Serial.print((char)payload[i]);
str[i] = (char)payload[i];
}
Serial.println();
if (strcmp(topic, "light_linfeng") == 0) {
if (strcmp(str, "开灯") == 0) {
digitalWrite(LED_BUILTIN, HIGH);
} else if (strcmp(str, "关灯") == 0) {
digitalWrite(LED_BUILTIN, LOW);
}
} else if (strcmp(topic, "alarm_linfeng") == 0) {
if (strcmp(str, "开始") == 0) {
digitalWrite(buzzpin, HIGH);
} else if (strcmp(str, "关闭") == 0) {
digitalWrite(buzzpin, LOW);
}
}
}
void reconnect() {
while (!client.connected()) {
Serial.print("尝试MQTT连接…");
String clientId = "ESP8266Client-";
clientId += String(random(0xffff), HEX);
if (client.connect(clientId.c_str())) {
Serial.println("连接成功!");
client.subscribe("linfeng_data");
client.subscribe("light_linfeng");
client.subscribe("alarm_linfeng");
} else {
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" try again in 5 seconds");
delay(5000);
}
}
}
void setup() {
pinMode(LED_BUILTIN, OUTPUT);
pinMode(buzzpin, OUTPUT);
Serial.begin(115200);
setup_wifi();
client.setServer(mqtt_server, 1883);
client.setCallback(callback);
}
void loop() {
if (!client.connected()) {
reconnect();
}
client.loop();
infrared_value = digitalRead(ledpin);
if (infrared_value == HIGH && flag == 0)
{
flag = 1;
infrared_hight = 1;
}
else if(infrared_value == LOW)
{
flag = 0;
}
if (infrared_hight == 1) {
infrared_hight = 0;
if (infrared_flag) {
Serial.println("离开卫生间!");
String payload = "{";
payload += "\"place\":"; payload += "\"wc\",";
payload += "\"IsWc\":"; payload += false;
payload += "}";
payload.toCharArray( attributes, 1000 );
client.publish("linfeng_data", attributes);
client.publish("wc_linfeng", "不行");
} else if (!infrared_flag) {
Serial.println("进入卫生间!");
String payload = "{";
payload += "\"place\":"; payload += "\"wc\",";
payload += "\"IsWc\":"; payload += true;
payload += "}";
payload.toCharArray( attributes, 1000 );
client.publish("linfeng_data", attributes);
client.publish("wc_linfeng", "可以");
}
infrared_flag = !infrared_flag;
infrared_hight = 0;
}
delay(200);
}
使用串口调试助手可查看信息的收发,结果如下:
ESP32+MQTT读取触控传感器
使用触控传感器采集老人使用马桶的数据,引脚连接图如下: 关键代码如下:
void loop() {
if (!client.connected()) {
reconnect();
}
client.loop();
if (wc_flag == 1) {
touch_value = digitalRead(TOUCH_PIN);
}
if (touch_value == HIGH)
{
touch_hight = 1;
delay(100);
}
else
{
if (touch_hight == 1) {
touch_low = 1;
}
delay(100);
}
if (touch_hight == 1 && touch_low == 1) {
if (touch_flag) {
String payload = "{";
payload += "\"place\":"; payload += "\"toilet\",";
payload += "\"IsToilet\":"; payload += false;
payload += "}";
payload.toCharArray( attributes, 1000 );
client.publish("linfeng_data", attributes);
Serial.println("离开马桶!");
} else if (!touch_flag) {
String payload = "{";
payload += "\"place\":"; payload += "\"toilet\",";
payload += "\"IsToilet\":"; payload += true;
payload += "}";
payload.toCharArray( attributes, 1000 );
client.publish("linfeng_data", attributes);
Serial.println("使用马桶!");
}
touch_flag = !touch_flag;
touch_hight = 0;
touch_low = 0;
}
delay(200);
}
使用串口调试助手可查看信息的收发,结果如下:
ESP32+MQTT读取温湿度传感器
前两个传感器采集了卫生间和马桶的信息。此处就采集淋浴的使用情况,淋浴的使用和马桶一样,通过触控传感器来采集,此处多了一个温湿度的采集。温湿度引脚连接图如下: 关键代码如下:
void getAndSendTemperatureAndHumidityData()
{
delay(200);
byte hum;
byte tem;
int err = SimpleDHTErrSuccess;
if ((err = dht11.read(&tem, &hum, NULL)) != SimpleDHTErrSuccess) {
Serial.print("读取数据失败, err="); Serial.println(err);delay(1000);
return;
}
if (isnan(hum) || isnan(tem)) {
Serial.println("从DHT传感器读取失败!");
return;
}
String temperature = String(tem);
String humidity = String(hum);
String payload = "{";
payload += "\"temperature\":"; payload += temperature; payload += ",";
payload += "\"humidity\":"; payload += humidity;
payload += "}";
payload.toCharArray( attributes, 100 );
client.publish( "Tem_hum", attributes );
}
使用淋浴结果如下:
python订阅MQTT保存数据到MySQL
感知层的数据获取完成后,接下来就是数据的存储。使用python订阅感知层发送的MQTT主题,接收这些主题的信息,并将其保存到MySQL数据库中。关键代码如下:
"""
@File : 老人卫生间防摔倒自动报警系统.py
@Time : 2022/5/22 16:32
@Author : 林枫
@Software: PyCharm
"""
import time
import paho.mqtt.client as mqtt
import pymysql
broker = 'broker.emqx.io'
port = 1883
def saveMysql(tableName, data):
conn = pymysql.connect(host='localhost',
user='root',
password='',
db='oldman',
charset='utf8')
cursor = conn.cursor()
if tableName == 'toilet':
sql = """
insert into toilet(
in_time, out_time
)
values(%s, %s)
"""
cursor.execute(sql, (data[0], data[1]))
conn.commit()
conn.rollback()
time.sleep(1)
..............
print("保存成功!")
conn.close()
def on_connect(client, userdata, flags, rc):
if rc == 0:
print("连接成功!")
client.subscribe(topic='linfeng_data')
else:
print("Failed to connect, return code %dn", rc)
def on_message(client, userdata, msg):
global wcs_time, toilet_times, showers_time
message = []
data = eval(msg.payload.decode())
if data["place"] == "wc":
if data["IsWc"] == 1:
wcs_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
print(wcs_time)
message.clear()
message = [1, 0, 0, wcs_time]
print("开始保存数据到state中......................")
saveMysql("state", message)
if data["IsWc"] == 0:
wc_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
print(wc_time)
message.clear()
message.append(wcs_time)
message.append(wc_time)
print("开始保存数据到wc中......................")
saveMysql("wc", message)
message.clear()
message = [0, 0, 0, wc_time]
print("开始保存数据到state中......................")
saveMysql("state", message)
.....................................
def run():
client = mqtt.Client()
client.on_connect = on_connect
client.on_message = on_message
client.connect("broker.emqx.io", 1883, 60)
client.loop_forever()
if __name__ == '__main__':
run()
运行结果如下: 数据库部分信息如下:
Springboot后端读取MySQL
本系统较为简单,对数据库的操作只有添加和读取,添加使用python,读取部分使用Springboot框架和mybatis-plus。
State类:
package com.example.floodprevention.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
@TableName("state")
@Data
public class State {
@TableId(type = IdType.AUTO)
private Integer id;
private String wc;
private String toilet;
private String shower;
private String nowTime;
}
StateMapper接口:
package com.example.floodprevention.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.floodprevention.entity.State;
public interface StateMapper extends BaseMapper<State> {
}
StateController方法:
package com.example.floodprevention.controller;
import com.example.floodprevention.common.Result;
import com.example.floodprevention.mapper.StateMapper;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
@RestController
@RequestMapping("/state")
public class StateController {
@Resource
StateMapper stateMapper;
@GetMapping
public Result<?> GetAll() {
return Result.success(stateMapper.selectList(null));
}
}
访问路由结果如下:
Vue+Axios+Echarts+js可视化大屏展示
由于前端代码过于复杂,就不进行代码展示,效果图如下: 注:由于截图时硬件没有运行,所以没有温湿度的数据!系统的整体运行效果可访问下面的连接。视频草率,请勿吐槽😂😂😂 演示视频
二代版本
二代版本作为一代版本的升级版,主要对硬件部分做了改变,使用更高级的CC2530作为开发板,在传感器的选型方面也做了改变,一代基于老人的摔倒主要是通过时间判断,通过比较老人在一个场所滞留的时间与这一周的平均使用时间作比较,若超过平均时间则黄色预警,若黄色预警一段时间后仍未离开,则判定为摔倒并发出红色警报。二代对于传感器的改变,使用激光测距传感器来判断老人是否使用卫生间,若老人摔倒,激光测距的值会发生剧变,以此来判断老人是否摔倒。 二代版本共使用4个CC2530开发板,将感知层布局为星状网络结构,分为4个终端节点和一个协调器,终端节点和协调器之间通过ZigBee进行通信,协调器将接收到的信息统一打包出来后使用串口发送到ESP8266,然后通过MQTT协议将数据发送出去。
CC2530读取红外传感器
对于使用卫生间的判断仍采用红外传感器。引脚连接图如下: 关键代码如下:
if(P0_4 == 1)
{
flage_count++;
if(flage_count == 1)
{
TX_buff[1] = 1;
basicRfSendPacket(RECEIVE_ADDR1, (uint8*)TX_buff, 7);
Uart0_Send_String((uint8*)"come\r\n",7);
}
else if(flage_count == 2)
{
TX_buff[1] = 0;
basicRfSendPacket(RECEIVE_ADDR1, (uint8*)TX_buff, 7);
flage_count = 0;
Uart0_Send_String((uint8*)"go\r\n",7);
}
LED3 = 0;
delay_ms(20);
LED3 = 1;
delay_ms(200);
delay_ms(200);
delay_ms(200);
while(P0_4);
}
CC2530读取小车循迹传感器
此处采用小车循迹传感器采集马桶使用数据。当老人使用马桶时会接近传感器,此时传感器高电平,当老人离开时,传感器低电平,以此来采集数据。引脚连接图如下: 关键代码如下:
if(P0_4 == 0)
{
TX_buff[1] = 1;
basicRfSendPacket(RECEIVE_ADDR1, (uint8*)TX_buff, 7);
Uart0_Send_String((uint8*)"come\r\n",6);
LED1 = 0;
delay_ms(20);
LED1 = 1;
while(P0_4 == 0);
delay_ms(20);
while(P0_4 == 0);
delay_ms(20);
while(P0_4 == 0);
delay_ms(20);
while(P0_4 == 0);
}
else if(P0_4 == 1)
{
TX_buff[1] = 0;
basicRfSendPacket(RECEIVE_ADDR1, (uint8*)TX_buff, 7);
Uart0_Send_String((uint8*)"go\r\n",6);
LED1 = 0;
delay_ms(20);
LED1 = 1;
while(P0_4 == 1);
delay_ms(20);
while(P0_4 == 1);
delay_ms(20);
while(P0_4 == 1);
delay_ms(20);
while(P0_4 == 1);
}
CC2530读取激光测距传感器
使用激光测距传感器采集老人进出卫生间的数据,以及判断老人是否摔倒。引脚连接图如下: 关键代码如下:
if(flage == 1)
{
flage = 0;
old_distance6 = old_distance5;
old_distance5 = old_distance4;
old_distance4 = old_distance3;
old_distance3 = old_distance2;
old_distance2 = old_distance;
old_distance = distance;
distance = VL53LX_read();
if(distance >= 100 && distance <= 600 && people_flage == 0)
{
people_flage = 1;
error_count = 0;
TX_buff[1] = 1;
basicRfSendPacket(RECEIVE_ADDR1, (uint8*)TX_buff, 7);
sprintf(show_Buff1,"data:%d come\r\n",distance);
Uart0_Send_String((uint8*)show_Buff1,20);
LED3 = 1;
delay_ms(20);
LED3 = 0;
}
else if( (distance == 20 || distance == 8190)&& people_flage == 1)
{
error_count++;
if(error_count >= 3)
{
error_count = 0;
people_flage = 0;
if(old_distance5 < 500 && old_distance6 < 500 )
{
TX_buff[1] = 2;
basicRfSendPacket(RECEIVE_ADDR1, (uint8*)TX_buff, 7);
sprintf(show_Buff1,"data:%d shuai\r\n",distance);
LED1 = 1;
tim_flag = 1;
}
else
{
TX_buff[1] = 0;
basicRfSendPacket(RECEIVE_ADDR1, (uint8*)TX_buff, 7);
sprintf(show_Buff1,"data:%d go\r\n",distance);
}
CC2530读取温湿度传感器
使用DHT11温湿度传感器读取卫生间温湿度信息。引脚连接图如下: 关键代码如下:
if(flage == 1)
{
flage = 0;
dht11ReadDat(datBuf);
sprintf(show_Buff1,"tem:%d.%d hum:%d.%d",datBuf[2],datBuf[3],datBuf[0],datBuf[1]);
tem = datBuf[2]+datBuf[3]*0.1;
hum = datBuf[0]+datBuf[1]*0.1;
TX_buff[3] = datBuf[0];
TX_buff[4] = datBuf[1];
TX_buff[5] = datBuf[2];
TX_buff[6] = datBuf[3];
light = gy30_read();
check();
TX_buff[1] = light & 0XFF;
TX_buff[2] = light >> 8;
sprintf(show_Buff2,"light:%d lx ",TX_buff[2]*256 + TX_buff[1]);
TX_buff[0] = 1;
basicRfSendPacket(RECEIVE_ADDR1, (uint8*)TX_buff, 7);
LED3 = 1;
delay_ms(20);
LED3 = 0;
}
python订阅MQTT保存数据到MySQL
二代版本数据存储和一代相似,不过对于python的代码进行了分类增加了邮件报警功能。整体结构如下: 代码就不赘述了。
Springboot后端读取MySQL
和一代类似,后端并没有做更改,在此就不赘述。
Vue+Axios+Echarts+js可视化大屏展示
二代版本因为传感器的选择变换,判断老人摔倒由原来的时间判断改为传感器判断,所以逻辑更改了一部分,但是界面整体并没有改变。 演示视频
总结
因为学年设计和课程设计,第一次做一个软硬件结合的系统,整体用了一个星期。各部分的功能也并不怎么完美,但是涉及到了传感器的读取、无线数据传输、MySQL存储数据、前后端交互、可视化展示等等。也就想把它给发出来,第一次写这么长的文章,非常感谢你能看到这里。有需要源码学习交流的可以加我qq3090677003 。
|