目录
项目概述
随着老龄化社会的加剧,患者按时服药的重要性日益凸显。智能药盒通过使用STM32微控制器和多种传感器,能够实时监测药盒状态,提醒患者按时服药。此外,通过MQTT与ROS系统的通信,智能药盒可以记录患者的用药情况,并将数据发送给医生,实现远程监控。本文将详细介绍该项目的系统设计、代码实现和总结。
系统设计
硬件设计
STM32微控制器
STM32系列微控制器是本项目的核心,负责处理传感器数据和控制逻辑。传感器
- 光电传感器:用于检测药盒的开关状态。
- 重量传感器:监测药物的取出情况。
- 开关传感器:用于确认药盒是否打开。
通信接口
- I2C:用于连接传感器,进行数据读取。
- UART:用于与Wi-Fi模块进行通信,连接互联网。
Wi-Fi模块
采用ESP8266或ESP32模块,负责将数据上传至云端。
软件设计
MQTT协议
- 使用MQTT协议实现数据的轻量级传输,适合不稳定的网络环境。
- 通过MQTT Broker(如Eclipse Mosquitto)管理消息的发布与订阅。
ROS系统
- 创建ROS节点,用于处理来自智能药盒的数据。
- 使用ROS的通信机制(如话题、服务和动作)来管理药盒数据。
后端服务器
- 使用Django或Flask框架构建后端,处理和存储患者的服药数据。
- 数据存储使用MySQL或MongoDB。
前端与移动应用
- 使用React或Vue.js构建医生和患者的Web应用程序。
- 使用React Native或Flutter开发移动端应用,方便患者和医生随时查看数据。
系统架构图
代码实现
1. 开发环境
在开始编码之前,确保您已经设置好以下开发环境:
硬件环境
- STM32 微控制器:建议使用 STM32F4 系列开发板(如 Nucleo 或 Discovery 开发板)。
- 传感器:光电传感器、重量传感器和开关传感器。
- Wi-Fi 模块:ESP8266 或 ESP32。
- 调试工具:ST-Link 或 J-Link 调试器。
软件环境
- 开发工具:使用 STM32CubeIDE 或 Keil,推荐 STM32CubeIDE。
- 库:使用 STM32 HAL 库进行硬件抽象。
- MQTT 客户端库:使用 Paho MQTT C Client, 需要下载并集成到 STM32 项目中。
- ROS 环境:使用 Ubuntu 系统,安装 ROS Noetic 或其他适合的版本。
- Python 环境:使用 Flask 和 Flask-SQLAlchemy 来创建后端服务。
安装依赖
在 Python 环境中,安装所需的库:
pip install flask flask-sqlalchemy paho-mqtt
2. STM32 微控制器代码示例
下面是 STM32 微控制器代码的详细实现,包括传感器的初始化、数据读取和 MQTT 消息发布功能。
代码示例
#include "stm32f4xx_hal.h" #include "mqtt.h" #include "sensor.h" // 定义 MQTT 相关参数 #define MQTT_BROKER "tcp://broker.hivemq.com:1883" #define CLIENT_ID "DrugBoxClient" #define TOPIC "drugbox/status" // 函数声明 void SystemClock_Config(void); static void MX_GPIO_Init(void); static void MX_USART2_UART_Init(void); void MQTT_Publish(const char *topic, const char *message); void Sensor_Init(void); int Sensor_Read(void); int main(void) { HAL_Init(); // 初始化 HAL 库 SystemClock_Config(); // 配置系统时钟 MX_GPIO_Init(); // 初始化 GPIO MX_USART2_UART_Init(); // 初始化 UART MQTT_Init(); // 初始化 MQTT 客户端 Sensor_Init(); // 初始化传感器 while (1) { if (Sensor_Read() == 1) { // 检测到药盒打开 MQTT_Publish(TOPIC, "药盒已打开"); // 发送消息 HAL_Delay(1000); // 避免重复发送 } HAL_Delay(500); // 主循环延时 } } // MQTT 发布函数 void MQTT_Publish(const char *topic, const char *message) { MQTTClient client; MQTTClient_create(&client, MQTT_BROKER, CLIENT_ID); MQTTClient_connect(client); MQTTClient_publish(client, topic, message, strlen(message), QOS, 0); MQTTClient_disconnect(client); } // 传感器初始化 void Sensor_Init(void) { // 配置 GPIO 引脚,用于传感器输入 __HAL_RCC_GPIOC_CLK_ENABLE(); GPIO_InitTypeDef GPIO_InitStruct = {0}; GPIO_InitStruct.Pin = GPIO_PIN_0; // 假设传感器连接在 PC0 GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); } // 读取传感器状态 int Sensor_Read(void) { return HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_0); // 返回传感器状态 }
代码说明
- 头文件包含:包括 STM32 HAL 库、MQTT 相关和传感器相关的头文件。
- 主函数:
- 初始化 HAL 库、系统时钟、GPIO 和 UART。
- 初始化 MQTT 客户端和传感器。
- 进入主循环,定期检查传感器状态。
- MQTT_Publish:用于发布消息到 MQTT Broker。
- Sensor_Init:初始化传感器的 GPIO 引脚。
- Sensor_Read:读取传感器的状态,返回 1 表示药盒打开,返回 0 表示关闭。
3. ROS 节点代码示例
以下是完整的 ROS 节点的实现代码,包括 MQTT 客户端的配置和消息处理。
代码示例
import rospy from std_msgs.msg import String import paho.mqtt.client as mqtt # 定义 MQTT Broker 地址和主题 MQTT_BROKER = "broker.hivemq.com" MQTT_TOPIC = "drugbox/status" # 处理收到的 MQTT 消息 def on_message(client, userdata, message): msg = message.payload.decode("utf-8") rospy.loginfo(f"Received message: {msg}") # 将消息发布到 ROS 主题 pub.publish(msg) # 初始化 ROS 节点和发布者 rospy.init_node('drugbox_listener', anonymous=True) pub = rospy.Publisher('drugbox/status', String, queue_size=10) # 设置 MQTT 客户端 client = mqtt.Client() client.on_message = on_message client.connect(MQTT_BROKER) # 连接到 MQTT Broker client.subscribe(MQTT_TOPIC) # 订阅主题 # 启动 MQTT 循环 client.loop_start() # ROS 主循环 try: rospy.spin() # 保持节点运行 except KeyboardInterrupt: pass finally: client.loop_stop() # 停止 MQTT 循环 client.disconnect() # 断开 MQTT 连接
代码说明
- MQTT Broker 和主题:设置 MQTT Broker 地址和主题,
drugbox/status
用于接收药盒状态消息。 - on_message:这是 MQTT 消息处理函数,当 MQTT Broker 发布消息时,会调用此函数。它将接收到的消息打印到 ROS 日志,并将其发布到 ROS 主题。
- ROS 节点初始化:使用
rospy.init_node
初始化 ROS 节点,并创建一个发布者pub
,用于将消息发布到drugbox/status
主题。 - MQTT 客户端设置:
- 创建 MQTT 客户端实例并设置消息处理函数。
- 连接到 MQTT Broker,并订阅相关主题。
- 启动 MQTT 循环:通过
client.loop_start()
启动 MQTT 循环,保持客户端运行并接收消息。 - ROS 主循环:使用
rospy.spin()
保持节点运行,直到用户中断。最后,确保在退出时停止 MQTT 循环并断开连接。
4. 后端服务器代码示例(Flask)
接下来是后端服务器代码示例,使用 Flask 框架实现一个简单的 API 来存储和检索患者的服药数据。
代码示例
from flask import Flask, request, jsonify from flask_sqlalchemy import SQLAlchemy app = Flask(__name__) app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///medications.db' db = SQLAlchemy(app) # 定义数据库模型 class Medication(db.Model): id = db.Column(db.Integer, primary_key=True) patient_id = db.Column(db.String(80), nullable=False) medication_name = db.Column(db.String(120), nullable=False) timestamp = db.Column(db.DateTime, nullable=False) # 初始化数据库 @app.before_first_request def create_tables(): db.create_all() # 添加药物记录的 API @app.route('/medication', methods=['POST']) def add_medication(): data = request.get_json() new_medication = Medication( patient_id=data['patient_id'], medication_name=data['medication_name'], timestamp=datetime.utcnow() ) db.session.add(new_medication) db.session.commit() return jsonify({"message": "Medication added successfully."}), 201 # 获取药物记录的 API @app.route('/medications/<patient_id>', methods=['GET']) def get_medications(patient_id): medications = Medication.query.filter_by(patient_id=patient_id).all() return jsonify([{ "id": med.id, "patient_id": med.patient_id, "medication_name": med.medication_name, "timestamp": med.timestamp.strftime("%Y-%m-%d %H:%M:%S") } for med in medications]) if __name__ == '__main__': app.run(debug=True)
代码说明
- Flask 应用初始化:创建 Flask 应用并配置 SQLite 数据库 URI。
数据库模型
- Medication 类:
id
:药物记录的唯一标识符,主键,自动递增。patient_id
:与患者相关的唯一标识符,表示该记录属于哪个患者。medication_name
:记录的药物名称,表示患者服用的药物。timestamp
:记录被添加的时间戳,使用 UTC 时间,以便追踪服药时间。
API 功能
初始化数据库:
- 使用
@app.before_first_request
装饰器在应用的第一次请求之前创建数据库表(如果尚不存在)。
- 使用
添加药物记录的 API:
- 路由:
/medication
,方法:POST
。 - 请求体:接收 JSON 数据,格式:
{ "patient_id": "12345", "medication_name": "Aspirin" }
- 功能:创建新的
Medication
实例,将其添加到数据库中,并返回成功消息。 - 响应:返回 HTTP 状态码 201(已创建)和消息。
@app.route('/medication', methods=['POST']) def add_medication(): data = request.get_json() new_medication = Medication( patient_id=data['patient_id'], medication_name=data['medication_name'], timestamp=datetime.utcnow() ) db.session.add(new_medication) db.session.commit() return jsonify({"message": "Medication added successfully."}), 201
- 路由:
获取药物记录的 API:
- 路由:
/medications/<patient_id>
,方法:GET
。 - 功能:根据患者 ID 查询所有药物记录。
- 响应:返回该患者的所有药物记录,格式为 JSON 数组,每个记录包含 ID、患者 ID、药物名称和时间戳。
@app.route('/medications/<patient_id>', methods=['GET']) def get_medications(patient_id): medications = Medication.query.filter_by(patient_id=patient_id).all() return jsonify([{ "id": med.id, "patient_id": med.patient_id, "medication_name": med.medication_name, "timestamp": med.timestamp.strftime("%Y-%m-%d %H:%M:%S") } for med in medications])
- 路由:
启动 Flask 应用:
- 使用
app.run(debug=True)
启动 Flask 应用,开启调试模式,方便开发时查看错误信息。
- 使用
数据库部分
在本项目中,我们使用 SQLite 数据库来存储患者的服药记录。SQLite 是一个轻量级的数据库,适合于小型应用和开发阶段的原型设计。通过 Flask-SQLAlchemy 来处理数据库的操作。
1. 数据库设计
数据库主要包含一个表,用于存储药物记录。我们将定义一个名为 medications
的表,表结构如下:
列名 | 数据类型 | 描述 |
---|---|---|
id | INTEGER | 主键,自增 |
patient_id | VARCHAR(80) | 患者的唯一标识符 |
medication_name | VARCHAR(120) | 药物名称 |
timestamp | DATETIME | 记录的时间戳 |
通过这个表,我们可以记录每个患者的服药情况,包括他们服用的药物和时间。
2. 数据库操作
以下是后端 Flask 应用中数据库操作的具体实现,包括模型定义和 CRUD 操作。
2.1 模型定义
在 Flask 应用中,我们使用 SQLAlchemy 定义数据库模型。以下是 Medication
模型的定义,它对应于 medications
表。
from flask_sqlalchemy import SQLAlchemy from datetime import datetime db = SQLAlchemy() class Medication(db.Model): __tablename__ = 'medications' # 指定表名 id = db.Column(db.Integer, primary_key=True) # 主键 patient_id = db.Column(db.String(80), nullable=False) # 患者 ID medication_name = db.Column(db.String(120), nullable=False) # 药物名称 timestamp = db.Column(db.DateTime, nullable=False, default=datetime.utcnow) # 时间戳 def __repr__(self): return f"<Medication {self.medication_name} for Patient {self.patient_id}>"
3. 数据库操作示例
3.1 添加药物记录
在后端的 API 中,使用 POST 请求来添加新的药物记录。以下是完整的 API 实现:
from flask import Flask, request, jsonify from flask_sqlalchemy import SQLAlchemy from datetime import datetime app = Flask(__name__) app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///medications.db' db = SQLAlchemy(app) class Medication(db.Model): __tablename__ = 'medications' id = db.Column(db.Integer, primary_key=True) patient_id = db.Column(db.String(80), nullable=False) medication_name = db.Column(db.String(120), nullable=False) timestamp = db.Column(db.DateTime, nullable=False, default=datetime.utcnow) # 初始化数据库 @app.before_first_request def create_tables(): db.create_all() # 添加药物记录的 API @app.route('/medication', methods=['POST']) def add_medication(): data = request.get_json() new_medication = Medication( patient_id=data['patient_id'], medication_name=data['medication_name'] ) db.session.add(new_medication) db.session.commit() return jsonify({"message": "Medication added successfully."}), 201
3.2 获取药物记录
使用 GET 请求可以根据患者 ID 查询所有药物记录。以下是实现代码:
# 获取药物记录的 API @app.route('/medications/<patient_id>', methods=['GET']) def get_medications(patient_id): medications = Medication.query.filter_by(patient_id=patient_id).all() return jsonify([{ "id": med.id, "patient_id": med.patient_id, "medication_name": med.medication_name, "timestamp": med.timestamp.strftime("%Y-%m-%d %H:%M:%S") } for med in medications])
3.3 更新药物记录
如果需要更新某条药物记录,可以实现一个 PUT 请求的 API。例如,更新药物名称:
# 更新药物记录的 API @app.route('/medication/<int:id>', methods=['PUT']) def update_medication(id): data = request.get_json() medication = Medication.query.get(id) if medication: medication.medication_name = data['medication_name'] db.session.commit() return jsonify({"message": "Medication updated successfully."}), 200 else: return jsonify({"message": "Medication not found."}), 404
3.4 删除药物记录
我们可以通过 DELETE 请求来实现删除药物记录的功能。用户可以根据药物记录的 ID 来删除特定的记录。
代码示例
以下是删除药物记录 API 的完整实现:
# 删除药物记录的 API @app.route('/medication/<int:id>', methods=['DELETE']) def delete_medication(id): medication = Medication.query.get(id) # 根据 ID 查询药物记录 if medication: db.session.delete(medication) # 删除记录 db.session.commit() # 提交更改 return jsonify({"message": "Medication deleted successfully."}), 200 else: return jsonify({"message": "Medication not found."}), 404
代码说明
- 路由定义:使用
@app.route('/medication/<int:id>', methods=['DELETE'])
来定义路由,<int:id>
是 URL 中的参数,表示药物记录的 ID。 - 查询记录:使用
Medication.query.get(id)
根据传入的 ID 查询药物记录。 - 删除记录:如果记录存在,调用
db.session.delete(medication)
删除该记录,并通过db.session.commit()
提交更改。 - 响应:
- 如果成功删除记录,返回 HTTP 状态码 200 和成功消息。
- 如果记录不存在,返回 HTTP 状态码 404 和错误消息。
4. 完整后端代码示例
以下是包含所有 CRUD 操作的完整 Flask 后端代码示例,包括添加、获取、更新和删除药物记录的 API。
from flask import Flask, request, jsonify from flask_sqlalchemy import SQLAlchemy from datetime import datetime app = Flask(__name__) app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///medications.db' db = SQLAlchemy(app) # 定义数据库模型 class Medication(db.Model): __tablename__ = 'medications' id = db.Column(db.Integer, primary_key=True) patient_id = db.Column(db.String(80), nullable=False) medication_name = db.Column(db.String(120), nullable=False) timestamp = db.Column(db.DateTime, nullable=False, default=datetime.utcnow) # 初始化数据库 @app.before_first_request def create_tables(): db.create_all() # 添加药物记录的 API @app.route('/medication', methods=['POST']) def add_medication(): data = request.get_json() new_medication = Medication( patient_id=data['patient_id'], medication_name=data['medication_name'] ) db.session.add(new_medication) db.session.commit() return jsonify({"message": "Medication added successfully."}), 201 # 获取药物记录的 API @app.route('/medications/<patient_id>', methods=['GET']) def get_medications(patient_id): medications = Medication.query.filter_by(patient_id=patient_id).all() return jsonify([{ "id": med.id, "patient_id": med.patient_id, "medication_name": med.medication_name, "timestamp": med.timestamp.strftime("%Y-%m-%d %H:%M:%S") } for med in medications]) # 更新药物记录的 API @app.route('/medication/<int:id>', methods=['PUT']) def update_medication(id): data = request.get_json() medication = Medication.query.get(id) if medication: medication.medication_name = data['medication_name'] db.session.commit() return jsonify({"message": "Medication updated successfully."}), 200 else: return jsonify({"message": "Medication not found."}), 404 # 删除药物记录的 API @app.route('/medication/<int:id>', methods=['DELETE']) def delete_medication(id): medication = Medication.query.get(id) if medication: db.session.delete(medication) db.session.commit() return jsonify({"message": "Medication deleted successfully."}), 200 else: return jsonify({"message": "Medication not found."}), 404 if __name__ == '__main__': app.run(debug=True)
项目总结
本项目致力于开发一个智能药盒系统,旨在通过先进的嵌入式技术和物联网(IoT)解决方案,提高患者的用药依从性,确保患者能够按时服药。该系统结合了 STM32 微控制器、传感器、MQTT 协议和 ROS 系统,形成了一个高效的药物监测与提醒机制。
项目成果
实时药物监测:
- 利用光电传感器、重量传感器和开关传感器,智能药盒能够实时监测其状态,确保患者在取药时能够得到及时的提醒。
数据传输与处理:
- 通过 MQTT 协议,系统实现了药盒状态数据的实时传输,确保数据能够快速、安全地传递到后端服务器。这种高效的数据传输方式适应了物联网环境中的低带宽和不稳定网络特点。
远程监控:
- 结合 ROS 系统,医生可以通过后端 API 获取患者的服药记录,实现远程监控和管理。医生能够及时了解患者的用药情况,从而为患者提供个性化的医疗建议。
数据库管理:
- 使用 Flask 和 SQLAlchemy 构建的后端服务,能够高效地管理患者的服药记录,包括添加、查询、更新和删除功能,保证了数据的一致性和完整性。