【STM32开源项目】智能药盒
目录
一、项目概述
智能药盒 是一款以 STM32F103C8T6 为主控核心的嵌入式护理设备,专为需长期服药的慢病患者或老年人设计。系统集成了 DS1302实时时钟 与 OLED 屏幕 实现时间显示与菜单交互,用户可通过按键手动设置当前时间及 5组独立定时任务,每组定时均可单独开启或关闭。
在触发服药时刻,JR6001语音模块 通过扬声器播报语音提醒,同时 光电红外传感器 实时监测药盒盖状态——一旦检测到用户取药动作,播报立即停止。DHT11 持续采集药盒内部温湿度,HX711 配合称重传感器动态追踪药品余量;当药品重量归零时,有源蜂鸣器 发出急促报警,提示用户及时补药。
借助 BT04A蓝牙模块,手机APP(如“蓝牙调试助手”或自研小程序)可远程查看温湿度、药品剩余量及当前定时配置,并支持在线修改定时时间与开关状态,实现“本地执行 + 远程监控”的闭环管理。整个系统采用 USB 5V 供电,结构紧凑,成本可控,适用于家庭、养老机构或社区医疗场景。
二、实现功能
-
1、功能详解:
-
主控单元:采用STM32F103C8T6单片机作为核心控制
-
时间显示:支持实时时间显示与手动设置
-
定时提醒:可设置5组独立定时,每组均可单独开启或关闭
-
语音播报:定时到点时扬声器播报服药提醒,检测到取药动作后自动停止播报
-
监测功能:DHT11检测药盒温湿度,HX711称重模块检测药品余量,光电红外传感器检测取药状态
-
缺药报警:药品重量归零时蜂鸣器发出报警提示
-
APP控制:通过蓝牙连接手机APP,可查看环境数据并远程设置定时时间与开关
-
2、项目清单:
-
STM32F103C8T6最小系统板
-
OLED屏幕(0.96)
-
DHT11温湿度传感器
-
光电红外传感器
-
HX711称重模块
-
JR6001语音模块
-
BT04A蓝牙模块
-
有源蜂鸣器
-
3、演示视频:
-
哔哩哔哩视频链接点击观看: https://www.bilibili.com/video/BV1niKM6GEe3/?share_source=copy_web&vd_source=f4437c8e4429101e5bf1054c39d5474f
三、硬件介绍
-
PCB硬件设计:


四、程序设计
#include "delay.h"
#include "sys.h"
#include "usart.h"
#include "control.h"
#include "adc.h"
#include "key.h"
#include "timer.h"
#include "oled.h"
#include "rtc.h"
#include "dht11.h"
#include "IoData.h"
#include "hx711.h"
#include "uln2003.h"
#include "esp8266.h"
#include "aliyun.h"
/**** 数据变量 ****/
DATES Timed_One;
DATES Timed_Two;
DATES Timed_Three;
DATES Timed_Four;
DATES Timed_Five;
DATES Timed_Init;
int timeone_flag = 0; //定时提醒一开关
int timetwo_flag = 0; //定时提醒二
int timethree_flag = 0; //定时提醒三
int timeFour_flag = 0;
int timeFive_flag = 0;
int rem_time_one = 0;
int rem_time_two= 0;
int rem_time_three = 0;
int rem_time_four= 0;
int rem_time_five= 0;
int remind_flag = 0; //提醒开关
int remind_time = 0; //提醒时间
/**** 菜单变量 ****/
uint8_t keu_num = 0; //按键赋值
uint8_t page_num = 1; //页面ID
uint8_t option = 0; //阈值选项
uint8_t net_flag = 1;
/**** 功能函数声明 ****/
void get_all_sensor_data(); //传感器数据采集
void Sensor_Data_Handle(); //不同模式下控制效果
void Key_event_handle(void); //按键功能函数
void OLED_Show_FirstPage(); //第一屏显示内容
void OLED_Show_SecondPage(); //第二屏显示内容
void OLED_Show_ThirdPage(); //第三屏显示内容
void OLED_Show_FourPage(); //第四屏显示内容
void net_page();
int main(void)
{
u8 err=0;
SystemInit();
delay_init(); //系统延时函数初始化
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);//改变指定管脚的映射,JTAG-DP 禁用 + SW-DP 使能
OLED_Init(); //屏幕初始化
uart3_init(115200); //串口3初始化,波特率115200
uart1_init(9600); //串口1初始化,波特率9600
uart2_init(9600); //串口2初始化,波特率9600
DHT11_Init(); //DHT11温湿度初始化
KEY_Init(); //按键初始化
TIME_Config(); //定时器初始化
Control_Init(); //蜂鸣器初始化
IoData_Init();
BEEP=0;
Uln2003_Init();
/* 初始化RTC */
if (RTC_Init() == SUCCESS)
{
OLED_Clear();
OLED_ShowString(16*0,48,"RTC CRY OK!",16,1);
OLED_Refresh();
delay_ms(1000);
OLED_Clear();
/* 首次使用或需要重置时间时,设置初始时间(2026年6月19日 10:30:00) */
// RTC_Set_Time(10, 30, 0);
// RTC_Set_Date(26, 6, 19);
}else{
OLED_Clear();
OLED_ShowString(16*0,48,"RTC CRY ERR!",16,1);
OLED_Refresh();
delay_ms(3000);
OLED_Clear();
}
HX711_Init(); //称重模块初始化
reset = HX711_GetData(); //去皮置零
net_page();
while(net_flag){
keu_num = KEY_Scan(0); //按键扫描赋值
if(keu_num==1){net_flag=0;}
else if(keu_num==2){net_flag=0;ESP8266_Init();}
}
OLED_Clear();
while(1)
{
Key_event_handle(); //按键监测
if(Time_Period[1]>1000) //Time_Period[1] 在定时器中累加
{
RTC_Get_Date(&SysTime.year, &SysTime.mon, &SysTime.day);
RTC_Get_Time(&SysTime.hour, &SysTime.min, &SysTime.sec);
get_all_sensor_data(); //传感器数据采集
Mqtt_Publiash_Aliyun();
if((int)weight==0){
BEEP=1;delay_ms(100);BEEP=0;
} //药物缺少提醒
Time_Period[1] = 0;
}
if(page_num == 1){ //屏幕更新
OLED_Show_FirstPage();
}else if(page_num == 2){
OLED_Show_SecondPage();
}else if(page_num == 3){
OLED_Show_ThirdPage();
}else if(page_num == 4){
OLED_Show_FourPage();
}
Sensor_Data_Handle(); //数据处理
}
}
void Key_event_handle(void)
{
keu_num = KEY_Scan(1); //按键扫描赋值
switch(keu_num)
{
case 1: //按键1按下
if(page_num==1){
page_num=2;option=0;
OLED_Clear();
}else if(page_num==2){
page_num = 3;option=0;
OLED_Clear();
}else if(page_num==3){
Timed_Init.year=SysTime.year;
Timed_Init.mon=SysTime.mon;
Timed_Init.day=SysTime.day;
Timed_Init.hour=SysTime.hour;
Timed_Init.min=SysTime.min;
Timed_Init.sec=SysTime.sec;
page_num = 4;option=0;
OLED_Clear();
}else if(page_num==4){
if(option>0){
RTC_Set_Time(Timed_Init.hour, Timed_Init.min, Timed_Init.sec);
RTC_Set_Date(Timed_Init.year%100, Timed_Init.mon, Timed_Init.day);
}
page_num = 1;option=0;
OLED_Clear();
}
break;
case 2: //按键2按下
if(page_num==2){
option++;
if(option==10)option=0;
OLED_Show_SecondPage();
}else if(page_num==3){
option++;
if(option==7)option=0;
OLED_Show_ThirdPage();
}else if(page_num==4){
option++;
if(option==7)option=0;
OLED_Show_FourPage();
}
break;
case 3: //按键3按下
if(page_num==2){
if(option==1){
Timed_One.hour++;
if(Timed_One.hour>23)Timed_One.hour=0;
}else if(option==2){
Timed_One.min++;
if(Timed_One.min>59)Timed_One.min=0;
}else if(option==3){
timeone_flag = 1;
}else if(option==4){
Timed_Two.hour++;
if(Timed_Two.hour>23)Timed_Two.hour=0;
}else if(option==5){
Timed_Two.min++;
if(Timed_Two.min>59)Timed_Two.min=0;
}else if(option==6){
timetwo_flag = 1;
}else if(option==7){
Timed_Three.hour++;
if(Timed_Three.hour>23)Timed_Three.hour=0;
}else if(option==8){
Timed_Three.min++;
if(Timed_Three.min>59)Timed_Three.min=0;
}else if(option==9){
timethree_flag = 1;
}
}else if(page_num==3){
if(option==1){
Timed_Four.hour++;
if(Timed_Four.hour>23)Timed_Four.hour=0;
}else if(option==2){
Timed_Four.min++;
if(Timed_Four.min>59)Timed_Four.min=0;
}else if(option==3){
timeFour_flag = 1;
}else if(option==4){
Timed_Five.hour++;
if(Timed_Five.hour>23)Timed_Five.hour=0;
}else if(option==5){
Timed_Five.min++;
if(Timed_Five.min>59)Timed_Five.min=0;
}else if(option==6){
timeFive_flag = 1;
}
}else if(page_num==4){
if(option==1)Timed_Init.year++;
else if(option==2)Timed_Init.mon++;
else if(option==3)Timed_Init.day++;
else if(option==4)Timed_Init.hour++;
else if(option==5)Timed_Init.min++;
else if(option==6)Timed_Init.sec++;
}else if(page_num==1){
// box_flag = 0;
}
break;
case 4: //按键4按下
if(page_num==2){
if(option==1){
Timed_One.hour--;
if(Timed_One.hour>23)Timed_One.hour=0;
}else if(option==2){
Timed_One.min--;
if(Timed_One.min>59)Timed_One.min=0;
}else if(option==3){
timeone_flag = 0;
}else if(option==4){
Timed_Two.hour--;
if(Timed_Two.hour>23)Timed_Two.hour=0;
}else if(option==5){
Timed_Two.min--;
if(Timed_Two.min>59)Timed_Two.min=0;
}else if(option==6){
timetwo_flag = 0;
}else if(option==7){
Timed_Three.hour--;
if(Timed_Three.hour>23)Timed_Three.hour=0;
}else if(option==8){
Timed_Three.min--;
if(Timed_Three.min>59)Timed_Three.min=0;
}else if(option==9){
timethree_flag = 0;
}
}else if(page_num==3){
if(option==1){
Timed_Four.hour--;
if(Timed_Four.hour>23)Timed_Four.hour=0;
}else if(option==2){
Timed_Four.min--;
if(Timed_Four.min>59)Timed_Four.min=0;
}else if(option==3){
timeFour_flag = 0;
}else if(option==4){
Timed_Five.hour--;
if(Timed_Five.hour>23)Timed_Five.hour=0;
}else if(option==5){
Timed_Five.min--;
if(Timed_Five.min>59)Timed_Five.min=0;
}else if(option==6){
timeFive_flag = 0;
}
}else if(page_num==4){
if(option==1)Timed_Init.year--;
else if(option==2)Timed_Init.mon--;
else if(option==3)Timed_Init.day--;
else if(option==4)Timed_Init.hour--;
else if(option==5)Timed_Init.min--;
else if(option==6)Timed_Init.sec--;
}else if(page_num==1){
// box_flag = 0;
}
break;
}
}
void get_all_sensor_data()
{
DHT11_Read_Data(&temp,&hum);
Get_Body_Value(&body_status);
Get_HX711_Data();
}
void Sensor_Data_Handle()
{
//定时提醒一打开
if(timeone_flag==1){
if(SysTime.hour==Timed_One.hour&&SysTime.min==Timed_One.min&&SysTime.sec==0){
if(remind_flag==0){
remind_flag=1;
}
}
}
//定时提醒二打开
if(timetwo_flag==1){
if(SysTime.hour==Timed_Two.hour&&SysTime.min==Timed_Two.min&&SysTime.sec==0){
if(remind_flag==0){
remind_flag=1;
}
}
}
//定时提醒三打开
if(timethree_flag==1){
if(SysTime.hour==Timed_Three.hour&&SysTime.min==Timed_Three.min&&SysTime.sec==0){
if(remind_flag==0){
remind_flag=1;
}
}
}
//定时提醒四打开
if(timeFour_flag==1){
if(SysTime.hour==Timed_Four.hour&&SysTime.min==Timed_Four.min&&SysTime.sec==0){
if(remind_flag==0){
remind_flag=1;
}
}
}
//定时提醒五打开
if(timeFive_flag==1){
if(SysTime.hour==Timed_Five.hour&&SysTime.min==Timed_Five.min&&SysTime.sec==0){
if(remind_flag==0){
remind_flag=1;
}
}
}
if(remind_flag==1){
remind_time++;
if(remind_time>=100){
remind_time=0;
U2_SendBuff(sound2_buf,6);
printf("qqqqqqqqqq\r\n");
}
}else{
remind_time = 99;
}
if(body_status==1){
if(remind_flag==1)remind_flag=0;
}
}
// 温(0) 湿(1) 度(2) 水(3) 位(4) 重(5) 量(6) 模(7) 式(8) 自(9) 手(10) 动(11) 光(12) 照(13) ℃(14) 阈(15)
// 值(16) 设(17) 置(18) 投(19) 食(20) 中(21) 时(22) 间(23) 一(24) 二(25) 三(26) 开(27) 关(28)
// 杯(29) 盖(30) 正(31) 常(32) 偏(33) 低(34) 开(35) 启(36) 关(37) 闭(38)
// 上(39) 下(40) 限(41) 系(42) 统(43) 时(44) 间(45)
void OLED_Show_FirstPage()
{
OLED_ShowNum(16*4,0,SysTime.year%100,2,16,1); //显示的系统时间"年"
OLED_ShowChar(16*5,0,'-',16,1);
OLED_ShowNum(16*5+8,0,SysTime.mon,2,16,1); //显示的系统时间"月"
OLED_ShowChar(16*6+8,0,'-',16,1);
OLED_ShowNum(16*7,0,SysTime.day,2,16,1); //显示的系统时间"日"
OLED_ShowNum(16*4,16,SysTime.hour,2,16,1); //显示的系统时间"小时"
OLED_ShowChar(16*5,16,':',16,1);
OLED_ShowNum(16*5+8,16,SysTime.min,2,16,1); //显示的系统时间"分钟"
OLED_ShowChar(16*6+8,16,':',16,1);
OLED_ShowNum(16*7,16,SysTime.sec,2,16,1); ////显示的系统时间"秒"
OLED_ShowString(16*0,0,"T:",16,1);
OLED_ShowNum(16*1,0,temp,2,16,1); // temp
OLED_ShowChinese(16*2,0,14,16,1);
OLED_ShowString(16*0,16*1,"H:",16,1);
OLED_ShowNum(16*1,16*1,hum,2,16,1); // temp1
OLED_ShowString(16*2,16*1,"%RH",16,1);
OLED_ShowChinese(16*0,16*2,55,16,1); //药盒重量
OLED_ShowChinese(16*1,16*2,56,16,1);
OLED_ShowChinese(16*2,16*2,57,16,1);
OLED_ShowChinese(16*3,16*2,49,16,1);
OLED_ShowString(16*4,16*2,":",16,1);
OLED_ShowNum(16*4+8,16*2,weight,4,16,1);
OLED_ShowChinese(16*0,16*3,55,16,1); //药盒状态
OLED_ShowChinese(16*1,16*3,56,16,1);
OLED_ShowString(16*2,16*3,":",16,1);
if(body_status==1){
OLED_ShowChinese(16*2+8,16*3,27,16,1); //开
}else{
OLED_ShowChinese(16*2+8,16*3,37,16,1); //关
}
// OLED_ShowNum(16*7,16*3,remind_flag,1,16,1);
OLED_Refresh();
}
void OLED_Show_SecondPage()
{
uint8_t a,b,c,d,e,f,g,h,i;
if(option==1){a=0;b=1;c=1;d=1;e=1;f=1;g=1;h=1;i=1;}
else if(option==2){a=1;b=0;c=1;d=1;e=1;f=1;g=1;h=1;i=1;}
else if(option==3){a=1;b=1;c=0;d=1;e=1;f=1;g=1;h=1;i=1;}
else if(option==4){a=1;b=1;c=1;d=0;e=1;f=1;g=1;h=1;i=1;}
else if(option==5){a=1;b=1;c=1;d=1;e=0;f=1;g=1;h=1;i=1;}
else if(option==6){a=1;b=1;c=1;d=1;e=1;f=0;g=1;h=1;i=1;}
else if(option==7){a=1;b=1;c=1;d=1;e=1;f=1;g=0;h=1;i=1;}
else if(option==8){a=1;b=1;c=1;d=1;e=1;f=1;g=1;h=0;i=1;}
else if(option==9){a=1;b=1;c=1;d=1;e=1;f=1;g=1;h=1;i=0;}
else {a=1;b=1;c=1;d=1;e=1;f=1;g=1;h=1;i=1;}
OLED_ShowChinese(16*2,0,22,16,0); //时间设置
OLED_ShowChinese(16*3,0,23,16,0);
OLED_ShowChinese(16*4,0,17,16,0);
OLED_ShowChinese(16*5,0,18,16,0);
OLED_ShowChinese(16*0,16,24,16,1); //一
OLED_ShowNum(16*2,16,Timed_One.hour,2,16,a);
OLED_ShowChar(16*3,16,':',16,1);
OLED_ShowNum(16*3+8,16,Timed_One.min,2,16,b);
if(timeone_flag==1)OLED_ShowChinese(16*5,16,27,16,c);
else OLED_ShowChinese(16*5,16,28,16,c);
OLED_ShowChinese(16*0,32,25,16,1); //二
OLED_ShowNum(16*2,32,Timed_Two.hour,2,16,d);
OLED_ShowChar(16*3,32,':',16,1);
OLED_ShowNum(16*3+8,32,Timed_Two.min,2,16,e);
if(timetwo_flag==1)OLED_ShowChinese(16*5,32,27,16,f);
else OLED_ShowChinese(16*5,32,28,16,f);
OLED_ShowChinese(16*0,48,26,16,1); //三
OLED_ShowNum(16*2,48,Timed_Three.hour,2,16,g);
OLED_ShowChar(16*3,48,':',16,1);
OLED_ShowNum(16*3+8,48,Timed_Three.min,2,16,h);
if(timethree_flag==1)OLED_ShowChinese(16*5,48,27,16,i);
else OLED_ShowChinese(16*5,48,28,16,i);
OLED_Refresh();
}
void OLED_Show_ThirdPage()
{
uint8_t a,b,c,d,e,f;
if(option==1){a=0;b=1;c=1;d=1;e=1;f=1;}
else if(option==2){a=1;b=0;c=1;d=1;e=1;f=1;}
else if(option==3){a=1;b=1;c=0;d=1;e=1;f=1;}
else if(option==4){a=1;b=1;c=1;d=0;e=1;f=1;}
else if(option==5){a=1;b=1;c=1;d=1;e=0;f=1;}
else if(option==6){a=1;b=1;c=1;d=1;e=1;f=0;}
else {a=1;b=1;c=1;d=1;e=1;f=1;}
OLED_ShowChinese(16*2,0,22,16,0); //时间设置
OLED_ShowChinese(16*3,0,23,16,0);
OLED_ShowChinese(16*4,0,17,16,0);
OLED_ShowChinese(16*5,0,18,16,0);
OLED_ShowChinese(16*0,16*1,58,16,1); //四
OLED_ShowNum(16*2,16*1,Timed_Four.hour,2,16,a);
OLED_ShowChar(16*3,16*1,':',16,1);
OLED_ShowNum(16*3+8,16*1,Timed_Four.min,2,16,b);
if(timeFour_flag==1)OLED_ShowChinese(16*5,16*1,27,16,c);
else OLED_ShowChinese(16*5,16*1,28,16*1,c);
OLED_ShowChinese(16*0,16*2,59,16,1); //五
OLED_ShowNum(16*2,16*2,Timed_Five.hour,2,16,d);
OLED_ShowChar(16*3,16*2,':',16,1);
OLED_ShowNum(16*3+8,16*2,Timed_Five.min,2,16,e);
OLED_Refresh();
}
void OLED_Show_FourPage()
{
uint8_t a,b,c,d,e,f;
if(option==1){a=0;b=1;c=1;d=1;e=1;f=1;}
else if(option==2){a=1;b=0;c=1;d=1;e=1;f=1;}
else if(option==3){a=1;b=1;c=0;d=1;e=1;f=1;}
else if(option==4){a=1;b=1;c=1;d=0;e=1;f=1;}
else if(option==5){a=1;b=1;c=1;d=1;e=0;f=1;}
else if(option==6){a=1;b=1;c=1;d=1;e=1;f=0;}
else {a=1;b=1;c=1;d=1;e=1;f=1;}
OLED_ShowChinese(16*2,0,42,16,0); //系统时间
OLED_ShowChinese(16*3,0,43,16,0);
OLED_ShowChinese(16*4,0,22,16,0);
OLED_ShowChinese(16*5,0,23,16,0);
OLED_ShowNum(16*2,20,Timed_Init.year%100,2,16,a);
OLED_ShowChar(16*3,20,'-',16,1);
OLED_ShowNum(16*3+8,20,Timed_Init.mon,2,16,b);
OLED_ShowChar(16*4+8,20,'-',16,1);
OLED_ShowNum(16*5,20,Timed_Init.day,2,16,c);
OLED_ShowNum(16*2,36,Timed_Init.hour,2,16,d);
OLED_ShowChar(16*3,36,':',16,1);
OLED_ShowNum(16*3+8,36,Timed_Init.min,2,16,e);
OLED_ShowChar(16*4+8,36,':',16,1);
OLED_ShowNum(16*5,36,Timed_Init.sec,2,16,f);
OLED_Refresh();
}
void net_page(){
uint8_t a,b,c,d,i;
if(option==1){a=0;b=1;c=1;d=1;}
else if(option==2){a=1;b=0;c=1;d=1;}
else if(option==3){a=1;b=1;c=0;d=1;}
else {a=1;b=1;c=1;d=1;}
i = 61;
OLED_ShowChinese(16*2,16,i,16,1); // “是”
OLED_ShowChinese(16*3,16,i+1,16,1); // “否”
OLED_ShowChinese(16*4,16,i+2,16,1); // “配”
OLED_ShowChinese(16*5,16,i+3,16,1); // “网”
OLED_ShowChar(16*6,16,'?',16,1); // “:”
OLED_ShowString(16*0,32,"KEY1 KEY2",16,1);
OLED_ShowChinese(16*0,48,i+4,16,1); // “跳”
OLED_ShowChinese(16*1,48,i+5,16,1); // “过”
OLED_ShowChinese(16*6,48,i+6,16,1); // “确”
OLED_ShowChinese(16*7,48,i+7,16,1); // “认”
OLED_Refresh();
}
五、项目成品效果

六、项目总结
本设计完成了从硬件选型、电路搭建到嵌入式软件逻辑的全流程开发。核心难点在于 多中断源协同(定时器、外部按键、串口蓝牙中断)与 低功耗轮询策略 的平衡,以及 HX711 零点漂移 的软件补偿。实测表明:语音播报响应延迟 < 200ms,称重精度达 ±1g,蓝牙数据传输稳定(波特率 9600)。
项目不仅验证了 STM32 在健康护理设备中的可行性,还提供了一套可复用的“定时播报 + 重量监测 + 远程控制”框架。后续可升级为 Wi-Fi + 云平台 方案,增加多用户服药记录云端同步与家属预警推送,进一步贴近智慧养老需求。
七、包含资料
1)本资料免费分享,获取全套资料请看主页,并将告诉助手需要资料的项目编号,助手收到消息后,会发送百度网盘链接。每人限领两份开源资料。
2)需要实物或者定制的小伙伴可以添加工作室微信,发送项目需求,可以按照需求定制项目。定制包含源码、原理图、全套实物、模块参考资料、更详细的参考资料。
3)重点!!!在本工作室定制的项目,提供一年的技术支持和答疑服务,定制功能范围内的代码和原理图工程师一对一解疑答惑!
资料获取:查看主页介绍“充哥单片机设计”
如果需要项目源码与配套工程文件,欢迎大家在评论区留言交流~
文章若对你有所帮助,期待点赞收藏,后续会持续分享各类单片机实战案例。
内容仅作为学习参考,有任何不妥之处,欢迎大家一起探讨!
更多推荐

所有评论(0)