目录
[PCB图 编辑编辑](#PCB图 编辑编辑)
一、前言
项目成品图片:

哔哩哔哩视频链接:
(资料分享见文末)
二、项目简介
1.功能详解
基于STM32的智能智能独居老人看护系统
功能如下:
- 环境采集:DS18B20温度传感器检测环境温度、MAX30102心率血氧传感器采集老人心率血氧、超声波传感器检测与前方障碍物距离、六轴陀螺仪检测老人是否发生跌倒、空气质量传感器检测环境空气质量情况
- 显示功能:环境数据显示在OLED屏幕上
- 模式切换:通过按键可以切换手动模式和自动模式
- 自动模式:如果温度超过阈值、心率大于阈值、血氧小于阈值、超声波距离小于阈值、发生跌倒、空气质量超过阈值时蜂鸣器报警
- 手动模式:可以通过按键控制报警开关与LED灯开关
- 阈值调节:可通过按键进入阈值设置界面,可调节温度、心率、血氧、空气质量、距离阈值
- 机智云APP:WIFI接入机智云平台,通过手机APP可查看信息数据和控制指令下发
2.主要器件
- STM32F103C8T6最小系统板
- OLED显示屏(4针IIC协议)
- 18B20温度传感器
- MQ-135空气质量传感器
- HC-SR04超声波传感器
- ESP8266-01S(WIFI模块)
- MAX30102心率血氧传感器
- MPU6050陀螺仪传感器
- 蜂鸣器
- LED灯
三、原理图设计

四、PCB硬件设计
PCB图 

五、程序设计
cpp
#include "stm32f10x.h"
#include "led.h"
#include "beep.h"
#include "usart.h"
#include "delay.h"
#include "oled.h"
#include "key.h"
#include "Modules.h"
#include "TIM2.h"
#include "adcx.h"
#include "flash.h"
#include "usart2.h"
#include "usart3.h"
#include "max30102_read.h"
#include "myiic.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "ds18b20.h"
#include "string.h"
#include "ultrasonic.h"
#include "timer.h"
#include "TIM3.h"
#include "MPU6050.h"
#include "gizwits_product.h"
#include "gizwits_protocol.h"
/****************异方辰电子工作室******************
STM32
* 项目 : STM32独居老人看护系统
* 版本 : V2.0
* 日期 : 2025.11.07
* MCU : STM32F103C8T6
* 接口 : 见代码
* IP账号 : 异方辰电子/辰哥单片机设计(同BILIBILI|抖音|快手|小红书|CSDN|公众号|视频号等)
* 作者 : 辰哥
* 工作室 : 异方辰电子工作室
* 授权IP : 辰哥单片机设计、异方辰电子、YFC电子、异方辰系列
* 官方网站 : www.yfcdz.cn
**********************BEGIN***********************/
// 按键功能宏定义
#define KEY_Long1 11 // 按键1长按
#define KEY_1 1 // 按键1短按
#define KEY_2 2 // 按键2
#define KEY_3 3 // 按键3
#define KEY_4 4 // 按键4
#define FLASH_START_ADDR 0x0801f000
#define AUTO_MODE 1 // 自动模式
#define MANUAL_MODE 2 // 手动模式
#define SETTINGS_MODE 3 // 设置模式
uint8_t ID; //定义用于存放ID号的变量
int16_t AX, AY, AZ, GX, GY, GZ; //定义用于存放各个数据的变量
int16_t SUM,SUM_OLD = 0,sum,test=0,count = 0;
extern SensorModules sensorData;
extern SensorThresholdValue Sensorthreshold;
extern DriveModules driveData;
// 系统核心全局变量
uint8_t mode = 1; // 系统模式控制:1=自动模式,2=手动模式,3=设置模式
u8 dakai;// 串口3数据传递临时变量
u8 Flag_dakai;// 串口3接收完成标志位
uint8_t is_secondary_menu = 0; // 二级菜单状态标识:0=一级菜单,1=二级菜单(预留RTC时间设置等扩展功能)
uint8_t secondary_pos = 1; // 二级菜单光标位置:1-3对应时/分/秒(预留扩展)
uint8_t secondary_type = 0; // 二级菜单类型:0=RTC时间,1=定时开启,2=定时关闭
// 外部变量声明:从其他.c文件导入的全局变量
extern unsigned char p[16];// 温度数据字符串缓存(DS18B20采集后格式化存储到该数组)
short temperature = 0; // 温度原始数据存储变量
// 系统静态变量用于记录模式内状态
//static uint8_t count_a = 1; //自动模式按键数
uint8_t count_m = 1; // 手动模式按键计数:记录当前选中的控制项(1=灯光,2=蜂鸣器)
uint8_t count_s = 1; // 设置模式按键计数:记录当前选中的阈值项(1=温度,2=心率,3=光照,4=距离)
uint8_t last_mode = 0; // 记录上一次的模式(注释,主函数内重新定义静态变量)
uint8_t last_count_s = 0; // 记录设置模式内上一次的页面(注释,未使用)
uint8_t auto_page = 1;
int main(void)
{
SystemInit();
delay_init(72);
ADCX_Init();
LED_Init();
BEEP_Init();
BEEP_OFF;
Ultrasonic_Init();
USART1_Config();
USART2_Config();
USART3_Config();
Key_Init();
Init_MAX30102();
OLED_Init();
DS18B20_Init();
OLED_Clear();
// MyI2C_Init();
MPU6050_Init();
srand((unsigned int)delay_get_tick());
TIM2_Init(72-1,1000-1);
TIM3_Int_Init(1000-1,72-1);
delay_ms(100);
FLASH_ReadThreshold();
static uint8_t last_mode = 0; // 记录上一次的系统模式(用于模式切换时清屏)
static uint32_t last_sensor_time = 0; // 传感器扫描时间戳(控制采集频率)
static uint32_t last_display_time = 0; // 显示刷新时间戳(控制屏幕刷新频率)
// 阈值合法性检查:若Flash中阈值超出合理范围,恢复默认值
if (Sensorthreshold.tempValue > 40 ||
Sensorthreshold.hrAvgValue >120 ||
Sensorthreshold.sopAvgValue > 100 ||
Sensorthreshold.distanceValue > 50||
Sensorthreshold.mq135Value>200)
{
// 写入默认阈值:温度30℃,心率80次/分,光照100Lux,距离10CM
FLASH_W(FLASH_START_ADDR, 30, 95, 96, 10,20);
FLASH_ReadThreshold(); // 重新读取默认阈值
}
userInit(); // 机智云用户初始化(数据点初始化等)
gizwitsInit(); // 机智云协议初始化(WiFi配网、数据通信初始化)
gizwitsSetMode(WIFI_AIRLINK_MODE); //默认一键配网
delay_ms(200);
printf("Start \n");
gizwitsHandle((dataPoint_t *)¤tDataPoint);
ScanGizwitsMode();
delay_ms(200);
while (1)
{
gizwitsHandle((dataPoint_t *)¤tDataPoint);
// ==================== 获取当前系统时间 ====================
uint32_t current_time = delay_get_tick(); // 使用系统滴答计数获取当前时间(毫秒级)
// ==================== 优化传感器扫描频率 ====================
if(current_time - last_sensor_time > 100) // 每100ms扫描一次传感器
{
SensorScan(); //获取传感器数据(温度、心率、光照、距离、碰撞)
last_sensor_time = current_time; // 更新传感器扫描时间戳
}
// ==================== 立即处理按键 ====================
uint8_t current_key_num = KeyNum; // 保存当前按键值(避免被其他逻辑修改)
// 模式切换按键立即处理(短按KEY1/长按KEY1)
if(current_key_num != 0)
{
switch(mode)
{
case AUTO_MODE:
if(current_key_num == KEY_1) // 短按KEY1:自动模式→手动模式
{
mode = MANUAL_MODE;
count_m = 1; // 手动模式默认选中灯光控制项
// 切换到手动模式时关闭灯和蜂鸣器
driveData.LED_Flag = 0;
driveData.BEEP_Flag = 0;
KeyNum = 0; // 立即清除按键标志,避免重复触发
}
else if(current_key_num == KEY_Long1) // 长按KEY1:自动模式→设置模式
{
mode = SETTINGS_MODE;
count_s = 1; // 设置模式默认选中温度阈值项
KeyNum = 0; // 立即清除按键标志
}
break;
case MANUAL_MODE:
if(current_key_num == KEY_1) // 短按KEY1:手动模式→自动模式
{
mode = AUTO_MODE;
KeyNum = 0; // 立即清除按键标志
}
break;
case SETTINGS_MODE:
// 设置模式内部按键在各自模式中处理
break;
}
}
// ==================== 模式切换优化 ====================
if(last_mode != mode) // 检测到系统模式变化
{
OLED_Clear(); // 清屏,避免不同模式页面重叠
last_mode = mode; // 更新上一次模式
// 立即绘制新模式的固定标题内容
switch(mode)
{
case AUTO_MODE:
auto_page = 1;
OLED_autoPage1(); // 自动模式:绘制传感器标题页面
SensorDataDisplay1();
break;
case MANUAL_MODE:
OLED_manualPage1(); // 手动模式:绘制控制项标题页面
break;
case SETTINGS_MODE:
OLED_settingsPage1(); // 设置模式:绘制阈值标题页面
SettingsThresholdDisplay1();
break;
}
OLED_Refresh(); // 立即刷新显示,使新模式页面生效
}
// ==================== 模式处理 ====================
switch(mode)
{
// -------------------- 自动模式 --------------------
case AUTO_MODE:
{
uint8_t curr_auto_page = SetAuto();
if(curr_auto_page == 1)
{
SensorDataDisplay1();
// 仅时间变化时刷新
OLED_Refresh();
}
else
{
SensorDataDisplay2();
}
AutoControl();
Control_Manager();
break;
}
// -------------------- 手动模式 --------------------
case MANUAL_MODE:
{
// 手动模式静态状态变量(保持模式内状态)
static uint8_t manual_page_initialized = 0; // 页面初始化标志
static uint8_t last_manual_count = 0; // 上一次选中的控制项序号
static uint8_t last_LED_Flag = 0; // 上一次LED状态
static uint8_t last_BEEP_Flag = 0; // 上一次蜂鸣器状态
static uint8_t force_refresh = 0; // 强制刷新标志(模式切换时使用)
// 模式切换时重新初始化状态
if(last_mode != mode)
{
manual_page_initialized = 0;
last_manual_count = 0;
last_LED_Flag = driveData.LED_Flag;
last_BEEP_Flag = driveData.BEEP_Flag;
force_refresh = 1; // 设置强制刷新标志
// 确保光标指向灯光控制项
count_m = 1;
// 确保设备状态为关闭
driveData.LED_Flag = 0;
driveData.BEEP_Flag = 0;
}
uint8_t current_manual_count = SetManual(); // 获取当前选中的控制项序号
// 检查设备状态是否改变(LED/蜂鸣器),若改变则需要刷新显示
uint8_t need_refresh = 0;
if(driveData.LED_Flag != last_LED_Flag || driveData.BEEP_Flag != last_BEEP_Flag)
{
need_refresh = 1;
last_LED_Flag = driveData.LED_Flag;
last_BEEP_Flag = driveData.BEEP_Flag;
}
// 页面未初始化/光标位置改变/设备状态改变/强制刷新→重新绘制页面
if(!manual_page_initialized || current_manual_count != last_manual_count || need_refresh || force_refresh)
{
OLED_manualPage1(); // 绘制控制项固定标题
OLED_manualOption(current_manual_count); // 显示当前光标位置
ManualSettingsDisplay1(); // 显示设备当前状态(开/关)
manual_page_initialized = 1;
last_manual_count = current_manual_count;
force_refresh = 0; // 清除强制刷新标志
OLED_Refresh(); // 强制刷新显示
}
// 立即处理手动模式按键(控制设备开关)
if(current_key_num != 0)
{
ManualControl(current_manual_count); // 执行当前控制项的按键操作
OLED_manualPage1(); // 重新绘制固定标题
OLED_manualOption(current_manual_count); // 显示光标
ManualSettingsDisplay1(); // 刷新设备状态
OLED_Refresh(); // 按键操作后立即刷新显示
KeyNum = 0; // 立即清除按键标志
}
Control_Manager(); // 执行硬件开关控制(LED、蜂鸣器)
break;
}
// -------------------- 设置模式 --------------------
case SETTINGS_MODE:
{
// 优化设置模式响应速度:静态初始化标志
static uint8_t is_threshold_page_inited = 0;
uint8_t curr_count_s = SetSelection(); // 获取当前选中的阈值项序号
// 立即处理设置模式内的按键
if(current_key_num != 0)
{
if (is_secondary_menu == 1) // 二级菜单(预留,当前未实现)
{
// 二级菜单按键立即处理
if (current_key_num == KEY_2 || current_key_num == KEY_3 || current_key_num == KEY_4)
{
// 这里根据你的二级菜单逻辑处理(如时间设置)
// 处理完后立即刷新
OLED_Refresh();
KeyNum = 0;
}
else if (current_key_num == KEY_1) // 按键1→返回一级菜单
{
is_secondary_menu = 0;
secondary_pos = 1;
OLED_Clear();
OLED_settingsPage1();
SettingsThresholdDisplay1();
OLED_settingsOption(curr_count_s);
OLED_Refresh();
KeyNum = 0;
}
}
else
{
if (current_key_num == KEY_3 || current_key_num == KEY_4)
{
ThresholdSettings(curr_count_s);
// 核心修复:根据当前页选择对应的显示刷新函数
if(curr_count_s >=1 && curr_count_s <=4)
{
SettingsThresholdDisplay1(); // 第一页刷新
}
else if(curr_count_s ==5)
{
SettingsThresholdDisplay2(); // 第二页刷新(不再调用第一页)
}
OLED_Refresh();
KeyNum = 0;
}
else if (current_key_num == KEY_1) // 按键1→返回自动模式
{
mode = AUTO_MODE;
is_threshold_page_inited = 0;
// 保存调整后的阈值到Flash
FLASH_W(FLASH_START_ADDR, Sensorthreshold.tempValue,
Sensorthreshold.hrAvgValue,
Sensorthreshold.sopAvgValue,
Sensorthreshold.distanceValue,
Sensorthreshold.mq135Value);
KeyNum = 0;
}
}
}
// 正常显示逻辑
if (is_secondary_menu == 1)
{
// 二级菜单显示(预留,当前未实现)
}
else
{
// 一级菜单显示(阈值调整页面)
if (curr_count_s >= 1 && curr_count_s <= 4) // 光标在有效阈值项范围内
{
if (is_threshold_page_inited == 0) // 页面未初始化→绘制标题
{
OLED_settingsPage1();
is_threshold_page_inited = 1;
SettingsThresholdDisplay1();
}
OLED_settingsOption(curr_count_s); // 显示第一页光标
}
else if (curr_count_s ==5 )
{
if (is_threshold_page_inited == 0)
{
OLED_settingsPage2();
SettingsThresholdDisplay2(); // 显示第二页阈值
is_threshold_page_inited = 1;
}
OLED_settingsOption(curr_count_s);
}
}
break;
}
}
// ==================== 限制显示刷新频率 ====================
if(current_time - last_display_time > 25) // 每25ms刷新一次显示
{
// 所有模式都需要刷新显示
OLED_Refresh();
last_display_time = current_time; // 更新显示刷新时间戳
}
userHandle();
}
}
六、实验效果

七、包含内容
