【项目思路】基于STM32+ZigBee的智能家居--浴室场景设计

文章导读:本文全面解析了一套基于STM32F103RCT6微控制器的智能家居浴室系统解决方案。方案整合了Zigbee传感网络、WiFi云端互联和本地人机交互(HMI)三大核心模块,既适合物联网开发者与嵌入式工程师进行技术参考,也为智能家居爱好者提供了实用学习案例。

目录导航


项目背景

项目概述

项目设计了一套完整的智能浴室解决方案 ,基于STM32F103RCT6微控制器核心,实现了"环境感知 + 本地联动 + 远程可视化 + 云端连接"的一体化智能控制系统。

核心特色

特色功能 技术实现 应用价值
离线可用 本地传感采集、场景联动、手动控制 云端断连时系统正常工作
在线增值 云端/上位机远程控制、历史数据分析 提供远程监控和数据分析能力
安全可靠 TLS加密、身份鉴权、CRC校验、看门狗 保障系统稳定性和数据安全

技术栈

txt 复制代码
用户界面层:4.3寸USART HMI触摸屏 + Python上位机
通信层:ESP8266(WiFi) + CC2530(Zigbee协调器)
控制层:STM32F103RCT6 + FreeRTOS多任务系统
数据层:SQLite本地存储 + MQTT云端同步
设备层:智能传感器 + 执行器设备

系统架构设计

整体架构概览

本系统采用分层架构设计 ,实现设备层、控制层、通信层、应用层的结合:
)

核心接口

接口类型 连接方式 协议规范 主要功能 性能指标
WiFi通信 STM32 ↔ ESP8266 AT指令(115200bps) 云端MQTT连接 延迟<100ms
Zigbee网络 STM32 ↔ CC2530 自定义帧协议 传感器数据采集 响应<500ms
人机界面 STM32 ↔ HMI屏 USART协议 本地交互控制 刷新率20fps
上位机 Python ↔ ESP8266 TCP/UDP 远程监控管理 并发连接>10

硬件选型配置

主控制器选型分析

STM32F103RCT6 核心参数
技术指标 参数值 应用优势 资源分配
CPU频率 72MHz 满足多任务实时处理需求 FreeRTOS调度开销<5%
Flash容量 256KB 存储应用代码+配置参数 代码占用约180KB
SRAM容量 48KB 支持多任务栈空间 任务栈总计约16KB
工作电压 3.3V 兼容大部分传感器模块 低功耗设计
外设接口分配表

)

无线通信模块

WiFi模块对比选择
模块型号 优势特点 技术规格 选型理由
ESP8266-01S 成本低,AT指令简单 802.11 b/g/n, 80MHz 推荐:性价比最优
ESP32-C3 性能强,蓝牙支持 双核160MHz, BLE5.0 过度设计,成本高
RTL8710BN 功耗极低 100MHz ARM Cortex-M4 生态支持不足
Zigbee协调器方案
txt 复制代码
CC2530 核心特性:
├──  技术参数
│   ├── CPU: 8051内核 @ 32MHz  
│   ├── Flash: 256KB
│   ├── RAM: 8KB
│   └── RF: 2.4GHz IEEE 802.15.4
├── 通信性能
│   ├── 传输距离: 室内30m, 室外100m
│   ├── 网络容量: 支持65000个节点
│   └── 功耗控制: 睡眠<1μA, 接收23mA
└──  接口设计
    ├── UART: 115200bps 与STM32通信
    ├── SPI: 可选高速数据传输
    └── GPIO: 状态指示与控制

人机交互设备

HMI触摸屏规格
技术参数 规格值 应用说明
屏幕尺寸 4.3英寸 适合浴室环境,视觉效果佳
分辨率 480×272像素 支持丰富的界面元素显示
触摸技术 电阻式触摸 防水性好,适合湿润环境
通信接口 UART串口 指令简单,实时性好

传感器设备

环境监测传感器
传感器类型 型号推荐 测量范围 精度指标 安装位置
温湿度 SHT30/DHT22 -40~80℃, 0~100%RH ±0.3℃, ±2%RH 浴室中央,避开热源
人体存在 PIR+毫米波 3-5米检测范围 99%检测准确率 门口或马桶上方
水浸检测 电极式 0~3mm水深 1mm检测精度 地漏周围,洗手台下
气体检测 MQ-135 CO2: 400-2000ppm ±50ppm 通风口附近
光照检测 BH1750 0-65535 lux ±20% 窗户或镜前
智能执行器设备

软件架构实现

FreeRTOS 多任务系统设计

任务优先级与资源分配
任务名称 优先级 堆栈大小 周期 核心职责 CPU占用率
WiFiTask 4 (最高) 512 Words 事件驱动 云端通信管理 ~15%
ZigbeeTask 3 (高) 256 Words 50ms Zigbee网络管理 ~20%
ControlTask 3 (高) 256 Words 100ms 设备控制逻辑 ~10%
SensorTask 2 (中) 256 Words 200ms 传感器数据采集 ~25%
DisplayTask 2 (中) 384 Words 50ms HMI界面更新 ~20%
IdleTask 0 (最低) 128 Words - 系统空闲处理 ~10%
系统启动与初始化流程
c 复制代码
/**
 * @brief 系统主任务启动函数
 * @param argument 任务参数(未使用)
 * @note 完成系统初始化并创建所有业务任务
 */
void StartDefaultTask(void *argument) {
    //硬件外设初始化
    HAL_Init();                    // HAL库初始化
    SystemClock_Config();          // 系统时钟配置 72MHz
    
    //串口通信初始化
    MX_GPIO_Init();                // GPIO端口初始化
    MX_USART1_UART_Init();         // ESP8266 WiFi模块 (115200bps)
    MX_USART2_UART_Init();         // CC2530 Zigbee协调器 (115200bps) 
    MX_USART3_UART_Init();         // HMI触摸屏 (115200bps)
    MX_USB_DEVICE_Init();          // USB调试接口
    
    // 定时器与ADC初始化
    MX_TIM2_Init();                // 通用定时器
    MX_ADC1_Init();                // 模拟量采集
    MX_IWDG_Init();                // 独立看门狗
    
    // 创建任务间通信对象
    CreateQueuesAndMutexes();
    
    // 创建业务处理任务
    xTaskCreate(WiFiTask,     "WiFi_Task",     512, NULL, 4, &wifiTaskHandle);
    xTaskCreate(ZigbeeTask,   "Zigbee_Task",   256, NULL, 3, &zigbeeTaskHandle);
    xTaskCreate(ControlTask,  "Control_Task",  256, NULL, 3, &controlTaskHandle);
    xTaskCreate(SensorTask,   "Sensor_Task",   256, NULL, 2, &sensorTaskHandle);
    xTaskCreate(DisplayTask,  "Display_Task",  384, NULL, 2, &displayTaskHandle);
    
    // 启动任务调度器
  vTaskStartScheduler();
    
    // 正常情况下不会执行到这里
    while(1) {
        HAL_Delay(1000);
    }
}

/**
 * @brief 创建队列、信号量等同步对象
 */
static void CreateQueuesAndMutexes(void) {
    //消息队列创建
    sensorDataQueue = xQueueCreate(10, sizeof(SensorData_t));
    controlCmdQueue = xQueueCreate(5, sizeof(ControlCmd_t));
    displayEventQueue = xQueueCreate(8, sizeof(DisplayEvent_t));
    
    //互斥锁创建
    uartMutex = xSemaphoreCreateMutex();
    configMutex = xSemaphoreCreateMutex();
    
    //事件组创建
    systemEventGroup = xEventGroupCreate();
    
    //错误检查
    configASSERT(sensorDataQueue && controlCmdQueue && displayEventQueue);
    configASSERT(uartMutex && configMutex && systemEventGroup);
}

核心任务详细设计

1 WiFi通信任务 (WiFiTask)
c 复制代码
/**
 * @brief WiFi通信管理任务
 * @param argument 任务参数
 * @features
 *   - ESP8266 AT指令控制
 *   - MQTT连接管理与重连
 *   - 云端数据收发
 *   - 网络状态监控
 */
void WiFiTask(void *argument) {
    TickType_t lastWakeTime = xTaskGetTickCount();
    ESP8266_Status_t wifiStatus = ESP_IDLE;
    uint32_t reconnectDelay = 1000; // 初始重连延时1s
    
    for(;;) {
        switch(wifiStatus) {
            case ESP_IDLE:
                if(ESP8266_Init() == ESP_OK) {
                    wifiStatus = ESP_READY;
                    LogInfo("WiFi模块初始化成功");
                }
                break;
                
            case ESP_READY:
                if(ESP8266_ConnectAP(WIFI_SSID, WIFI_PASSWORD) == ESP_OK) {
                    wifiStatus = ESP_CONNECTED;
                    reconnectDelay = 1000; // 重置重连延时
                    xEventGroupSetBits(systemEventGroup, WIFI_CONNECTED_BIT);
                }
                break;
                
            case ESP_CONNECTED:
                //  MQTT连接与数据收发处理
                if(MQTT_IsConnected()) {
                    ProcessMQTTMessages();
                    PublishSensorData();
                    xEventGroupSetBits(systemEventGroup, MQTT_CONNECTED_BIT);
                } else {
                    MQTT_Connect();
                }
                break;
                
            case ESP_ERROR:
                //  指数退避重连策略
                vTaskDelay(pdMS_TO_TICKS(reconnectDelay));
                reconnectDelay = MIN(reconnectDelay * 2, 60000); // 最大60s
                wifiStatus = ESP_IDLE;
                break;
        }
        
        //  100ms周期检查
        vTaskDelayUntil(&lastWakeTime, pdMS_TO_TICKS(100));
    }
}
2 Zigbee网络任务 (ZigbeeTask)
c 复制代码
/**
 * @brief Zigbee网络管理任务
 * @param argument 任务参数
 * @features
 *   - CC2530协调器控制
 *   - 传感器节点管理
 *   - 数据帧解析与路由
 *   - 网络拓扑维护
 */
void ZigbeeTask(void *argument) {
    uint8_t rxBuffer[ZIGBEE_RX_BUFFER_SIZE];
    ZigbeeFrame_t frame;
    TickType_t lastHeartbeat = xTaskGetTickCount();
    
    //  Zigbee网络启动
    if(Zigbee_StartCoordinator() != ZB_OK) {
        LogError("Zigbee协调器启动失败");
        vTaskSuspend(NULL);
    }
    
    for(;;) {
        // 📡 接收Zigbee数据帧
        if(Zigbee_ReceiveFrame(&frame, pdMS_TO_TICKS(50)) == ZB_OK) {
            switch(frame.cmd) {
                case ZB_SENSOR_REPORT:
                    ProcessSensorReport(&frame);
                    break;
                    
                case ZB_DEVICE_JOIN:
                    ProcessDeviceJoin(&frame);
                    break;
                    
                case ZB_DEVICE_LEAVE:
                    ProcessDeviceLeave(&frame);
                    break;
                    
                case ZB_NETWORK_STATUS:
                    UpdateNetworkStatus(&frame);
                    break;
                    
                default:
                    LogWarning("未知Zigbee命令: 0x%02X", frame.cmd);
                    break;
            }
        }
        
        //  网络心跳维护 (每30秒)
        if(xTaskGetTickCount() - lastHeartbeat > pdMS_TO_TICKS(30000)) {
            Zigbee_SendHeartbeat();
            lastHeartbeat = xTaskGetTickCount();
        }
        
        //  检查离线设备
        CheckOfflineDevices();
    }
}
3 传感器数据任务 (SensorTask)
c 复制代码
/**
 * @brief 传感器数据处理任务
 * @param argument 任务参数
 * @features
 *   - 多传感器数据融合
 *   - 数字滤波与去噪
 *   - 阈值检测与告警
 *   - 数据有效性校验
 */
void SensorTask(void *argument) {
    SensorData_t currentData = {0};
    SensorData_t filteredData = {0};
    AlarmStatus_t alarmStatus = {0};
    
    for(;;) {
        // 从Zigbee队列获取传感器数据
        if(xQueueReceive(sensorDataQueue, &currentData, pdMS_TO_TICKS(200)) == pdTRUE) {
            
            // 🔧 数据预处理与滤波
            ApplyDigitalFilter(&currentData, &filteredData);
            
            //  阈值检测与告警判断
            CheckAlarmConditions(&filteredData, &alarmStatus);
            
            //  更新历史数据缓存
            UpdateSensorHistory(&filteredData);
            
            //  发送处理后的数据到其他任务
            SendDataToDisplay(&filteredData);
            SendDataToControl(&filteredData);
            
            //  上报到云端 (如果WiFi已连接)
            if(xEventGroupGetBits(systemEventGroup) & MQTT_CONNECTED_BIT) {
                PublishToMQTT(&filteredData);
            }
        }
        
        //  超时告警处理
        HandleTimeoutAlarms();
    }
}

/**
 * @brief 数字滤波算法实现
 * @param raw 原始数据
 * @param filtered 滤波后数据
 * @note 采用中值滤波+指数平滑算法
 */
static void ApplyDigitalFilter(SensorData_t *raw, SensorData_t *filtered) {
    static float temp_history[FILTER_WINDOW_SIZE] = {0};
    static float hum_history[FILTER_WINDOW_SIZE] = {0};
    static uint8_t index = 0;
    
    //  更新历史数据窗口
    temp_history[index] = raw->temperature;
    hum_history[index] = raw->humidity;
    index = (index + 1) % FILTER_WINDOW_SIZE;
    
    //  中值滤波
    filtered->temperature = MedianFilter(temp_history, FILTER_WINDOW_SIZE);
    filtered->humidity = MedianFilter(hum_history, FILTER_WINDOW_SIZE);
    
    //  指数平滑 (α=0.3)
    static float temp_smooth = 25.0f, hum_smooth = 50.0f;
    temp_smooth = 0.3f * filtered->temperature + 0.7f * temp_smooth;
    hum_smooth = 0.3f * filtered->humidity + 0.7f * hum_smooth;
    
    filtered->temperature = temp_smooth;
    filtered->humidity = hum_smooth;
    
    //  其他传感器数据直接传递
    filtered->occupancy = raw->occupancy;
    filtered->water_leak = raw->water_leak;
    filtered->co2_level = raw->co2_level;
    filtered->lux_level = raw->lux_level;
    filtered->timestamp = HAL_GetTick();
}

任务间通信机制

消息队列设计
c 复制代码
//  传感器数据结构
typedef struct {
    float temperature;      // 温度 (℃)
    float humidity;         // 湿度 (%)
    uint8_t occupancy;      // 人体存在 (0/1)
    uint8_t water_leak;     // 漏水状态 (0/1) 
    uint16_t co2_level;     // CO2浓度 (ppm)
    uint16_t lux_level;     // 光照强度 (lux)
    uint32_t timestamp;     // 时间戳
    uint8_t device_id;      // 设备ID
} SensorData_t;

//  控制命令结构
typedef struct {
    uint8_t device_type;    // 设备类型
    uint8_t device_id;      // 设备ID  
    uint8_t command;        // 控制命令
    uint16_t param1;        // 参数1
    uint16_t param2;        // 参数2
    uint8_t source;         // 命令源 (HMI/MQTT/自动)
} ControlCmd_t;

//  显示事件结构
typedef struct {
    uint8_t event_type;     // 事件类型
    uint8_t page_id;        // 页面ID
    uint8_t widget_id;      // 控件ID
    uint32_t param;         // 事件参数
} DisplayEvent_t;

通信协议设计

MQTT云平台协议详解

云平台架构选择
云平台 技术特点 适用场景 成本评估
阿里云IoT 完整生态、设备管理强 商业化产品推荐 中等
腾讯云IoT 微信生态集成 需要微信集成时 中等
私有EMQX 完全可控、无云依赖 企业内部部署 低(部署成本高)
MQTT主题设计与数据流
txt 复制代码
Topic 层次结构设计:
├── 上行主题 (设备→云端)
│   ├── home/bathroom01/telemetry    # 遥测数据 (QoS1)
│   ├── home/bathroom01/event        # 事件告警 (QoS1) 
│   ├── home/bathroom01/status       # 设备状态 (QoS0)
│   └── home/bathroom01/response     # 命令响应 (QoS1)
│
└── 下行主题 (云端→设备)
    ├── home/bathroom01/command      # 控制命令 (QoS1)
    ├── home/bathroom01/config       # 配置下发 (QoS1)
    └── home/bathroom01/ota          # 固件升级 (QoS1)
MQTT消息格式标准
json 复制代码
// 遥测数据格式
{
  "msgId": "msg_20241215_001",
  "deviceId": "bathroom01", 
  "timestamp": 1702636800,
  "data": {
    "temperature": 26.5,      // 温度 (℃)
    "humidity": 65.2,         // 湿度 (%)
    "occupancy": 1,           // 人体存在 (0/1)
    "water_leak": 0,          // 漏水状态 (0/1)
    "co2": 450,               // CO2浓度 (ppm)
    "lux": 320,               // 光照强度 (lux)
    "devices": {              // 设备状态
      "fan": 0,               // 风扇 (0=关闭, 1=开启)
      "heater": 1,            // 浴霸 (0=关闭, 1=开启) 
      "light": 1,             // 照明 (0=关闭, 1=开启)
      "mirror": 0             // 除雾镜 (0=关闭, 1=开启)
    }
  }
}

// 控制命令格式
{
  "msgId": "cmd_20241215_001",
  "command": "device_control",
  "params": {
    "device": "fan",          // 设备名称
    "action": "turn_on",      // 操作类型
    "duration": 1800,         // 持续时间(秒),0=永久
    "priority": "user"        // 优先级: user/auto/emergency
  },
  "timestamp": 1702636800,
  "expire": 1702636860       // 命令过期时间
}

// 事件告警格式
{
  "msgId": "alert_20241215_001",
  "deviceId": "bathroom01",
  "event": {
    "type": "water_leak",     // 事件类型
    "level": "critical",      // 告警级别: info/warning/critical
    "message": "检测到漏水,位置:洗手台下方",
    "location": "sink_area",  // 告警位置
    "actions": ["turn_off_water", "send_notification"]
  },
  "timestamp": 1702636800
}

HMI串口屏通信协议

指令格式与控件映射
txt 复制代码
HMI通信帧格式:指令 + 参数 + 结束符
├── STM32 → HMI (控制指令)
│   ├── 文本更新: t[id].txt="内容"
│   ├── 数值更新: n[id].val=数值
│   ├── 图片切换: p[id].pic=图片ID
│   ├── 进度条: j[id].val=百分比
│   ├── 曲线添加: add [id],通道,数值
│   └── 页面跳转: page 页面ID
│
└── HMI → STM32 (事件反馈)  
    ├── 按钮点击: 65 00 07 [页面ID] [控件ID] 01 FF FF FF
    ├── 滑块变化: 65 00 07 [页面ID] [控件ID] [值H] [值L] FF FF FF  
    └── 页面切换: 65 00 04 01 [页面ID] FF FF FF
页面布局与控件设计
页面 控件ID 控件类型 显示内容 交互功能
P0 主页 t0 文本 温度显示 只读
t1 文本 湿度显示 只读
t2 文本 CO2浓度 只读
ico0 图标 人体存在 状态指示
ico1 图标 漏水告警 状态指示
P1 控制 b0 按钮 排风扇 开关控制
b1 按钮 浴霸 开关控制
b2 按钮 照明 开关控制
h0 滑块 亮度调节 PWM输出
P2 历史 waveform0 波形 温湿度曲线 数据展示
t10 文本 最近告警 日志显示

)
)

HMI数据刷新策略
c 复制代码
/**
 * @brief HMI界面刷新管理器
 */
typedef struct {
    uint8_t page_id;          // 当前页面ID
    uint32_t last_update;     // 上次更新时间
    uint8_t need_refresh;     // 刷新标志
    float temp_cache;         // 温度缓存值
    float hum_cache;          // 湿度缓存值
} HMI_Manager_t;

/**
 * @brief 智能刷新策略实现
 * @param data 传感器数据
 * @param hmi HMI管理器
 * @note 减少不必要的串口通信,提高响应速度
 */
void HMI_SmartUpdate(SensorData_t *data, HMI_Manager_t *hmi) {
    uint32_t current_time = HAL_GetTick();
    
    //温度刷新:变化>0.1°C 或 超过2s
    if(fabs(data->temperature - hmi->temp_cache) > 0.1f || 
       (current_time - hmi->last_update) > 2000) {
        HMI_SendCommand("t0.txt=\"%.1f°C\"", data->temperature);
        hmi->temp_cache = data->temperature;
    }
    
    //湿度刷新:变化>1% 或 超过2s  
    if(fabs(data->humidity - hmi->hum_cache) > 1.0f ||
       (current_time - hmi->last_update) > 2000) {
        HMI_SendCommand("t1.txt=\"%.0f%%\"", data->humidity);
        hmi->hum_cache = data->humidity;
    }
    
    // 告警状态:立即刷新
    if(data->water_leak) {
        HMI_SendCommand("ico1.pic=2");  // 显示告警图标
        HMI_SendCommand("page 3");      // 跳转告警页面
    }
    
    hmi->last_update = current_time;
}

Python上位机通信协议

网络层设计
python 复制代码
"""
TCP/UDP通信协议实现
支持命令-响应模式和实时数据推送
"""
import socket, json, struct, threading
from typing import Dict, Callable, Optional

class BathroomClient:
    def __init__(self, host: str = "192.168.1.100", port: int = 8888):
        self.host = host
        self.port = port
        self.socket = None
        self.connected = False
        self.callbacks: Dict[str, Callable] = {}
        
    def connect(self) -> bool:
        """建立TCP连接"""
        try:
            self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            self.socket.settimeout(5.0)
            self.socket.connect((self.host, self.port))
            self.connected = True
            
            # 启动接收线程
            threading.Thread(target=self._receive_loop, daemon=True).start()
            return True
            
        except Exception as e:
            print(f"连接失败: {e}")
            return False
    
    def send_command(self, command: str, params: dict = None) -> Optional[dict]:
        """发送命令并等待响应"""
        if not self.connected:
            return None
            
        # 构建请求包
        request = {
            "id": int(time.time() * 1000) % 10000,
            "command": command,
            "params": params or {},
            "timestamp": int(time.time())
        }
        
        # 发送长度前缀+JSON数据
        data = json.dumps(request, ensure_ascii=False).encode('utf-8')
        length_header = struct.pack('!H', len(data))
        
        try:
            self.socket.sendall(length_header + data)
            return self._wait_response(request["id"], timeout=3.0)
        except Exception as e:
            print(f"发送失败: {e}")
            return None
    
    def _receive_loop(self):
        """接收数据循环"""
        buffer = bytearray()
        
        while self.connected:
            try:
                chunk = self.socket.recv(4096)
                if not chunk:
                    break
                    
                buffer.extend(chunk)
                
                # 解析完整帧
                while len(buffer) >= 2:
                    length = struct.unpack('!H', buffer[:2])[0]
                    if len(buffer) < 2 + length:
                        break
                        
                    frame_data = bytes(buffer[2:2+length])
                    del buffer[:2+length]
                    
                    # 处理接收到的消息
                    try:
                        message = json.loads(frame_data.decode('utf-8'))
                        self._handle_message(message)
                    except Exception as e:
                        print(f"解析消息失败: {e}")
                        
            except Exception as e:
                print(f"接收错误: {e}")
                break
                
        self.connected = False

# 📡 使用示例
if __name__ == "__main__":
    client = BathroomClient("192.168.1.100", 8888)
    
    if client.connect():
        # 查询传感器数据
        sensors = client.send_command("get_sensors")
        print(f"传感器数据: {sensors}")
        
        # ⚡ 控制设备
        result = client.send_command("control_device", {
            "device": "fan", 
            "action": "on", 
            "duration": 1800
        })
        print(f"控制结果: {result}")
命令集定义
命令类型 命令名称 参数格式 响应格式 用途说明
查询类 get_sensors {} {temp, hum, co2, ...} 获取实时传感器数据
get_devices {} {fan, light, heater, ...} 获取设备状态
get_history {start, end, type} [{time, value}, ...] 获取历史数据
控制类 control_device {device, action, params} {success, message} 设备控制
set_scene {scene_name, params} {success, message} 场景控制
set_config {key, value} {success, message} 参数配置
订阅类 subscribe {types: []} {success} 订阅实时数据推送
unsubscribe {types: []} {success} 取消订阅

上位机开发

系统架构设计

)

技术栈选择对比
框架/库 优势特点 适用场景 推荐指数
PyQt5 界面美观、功能丰富、跨平台 桌面应用程序 ⭐⭐⭐⭐⭐
Tkinter Python内置、简单易用 简单工具 ⭐⭐⭐
Kivy 触摸友好、移动端 移动应用 ⭐⭐⭐
SQLite 轻量级、无服务器、高性能 本地数据存储 ⭐⭐⭐⭐⭐
PyQtGraph 实时绘图、性能优秀 数据可视化 ⭐⭐⭐⭐⭐

核心模块实现

1主界面设计 (MainWindow)
python 复制代码
"""
智能浴室监控系统主界面
功能:实时数据展示、设备控制、历史数据查看
"""
import sys
import time
import json
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
import pyqtgraph as pg
import sqlite3
from datetime import datetime, timedelta

class BathroomMonitor(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("智能浴室监控系统 v1.0")
        self.setGeometry(100, 100, 1200, 800)
        self.setStyleSheet("""
            QMainWindow {
                background-color: #f0f0f0;
            }
            QGroupBox {
                font: bold 14px;
                border: 2px solid #cccccc;
                border-radius: 5px;
                margin-top: 10px;
                padding-top: 10px;
            }
            QGroupBox::title {
                subcontrol-origin: margin;
                left: 10px;
                padding: 0 10px 0 10px;
            }
        """)
        
        #网络客户端初始化
        self.client = BathroomClient()
        self.connected = False
        
        #数据库初始化
        self.db_manager = DatabaseManager()
        
        #界面初始化
        self.init_ui()
        
        #定时器设置
        self.update_timer = QTimer()
        self.update_timer.timeout.connect(self.update_data)
        self.update_timer.start(1000)  # 1秒更新一次
        
    def init_ui(self):
        """初始化用户界面"""
        central_widget = QWidget()
        self.setCentralWidget(central_widget)
        
        #主布局
        main_layout = QHBoxLayout(central_widget)
        
        #左侧面板:实时数据与控制
        left_panel = self.create_left_panel()
        main_layout.addWidget(left_panel, 1)
        
        #右侧面板:历史数据与图表
        right_panel = self.create_right_panel() 
        main_layout.addWidget(right_panel, 2)
        
        #状态栏
        self.statusBar().showMessage("就绪")
        self.create_menu_bar()
        
    def create_left_panel(self):
        """创建左侧控制面板"""
        panel = QWidget()
        layout = QVBoxLayout(panel)
        
        #连接状态组
        conn_group = QGroupBox("连接状态")
        conn_layout = QHBoxLayout(conn_group)
        
        self.status_indicator = QLabel("●")
        self.status_indicator.setStyleSheet("color: red; font-size: 20px;")
        self.status_label = QLabel("离线")
        
        self.connect_btn = QPushButton("连接")
        self.connect_btn.clicked.connect(self.toggle_connection)
        
        conn_layout.addWidget(self.status_indicator)
        conn_layout.addWidget(self.status_label)
        conn_layout.addStretch()
        conn_layout.addWidget(self.connect_btn)
        
        #传感器数据组
        sensor_group = QGroupBox("环境数据")
        sensor_layout = QFormLayout(sensor_group)
        
        self.temp_label = self.create_value_label("--", "°C", "red")
        self.hum_label = self.create_value_label("--", "%", "blue") 
        self.co2_label = self.create_value_label("--", "ppm", "orange")
        self.lux_label = self.create_value_label("--", "lux", "green")
        
        sensor_layout.addRow("🌡️ 温度:", self.temp_label)
        sensor_layout.addRow("💧 湿度:", self.hum_label)
        sensor_layout.addRow("🌪️ CO2:", self.co2_label)
        sensor_layout.addRow("💡 光照:", self.lux_label)
        
        #状态指示组
        status_group = QGroupBox("状态指示")
        status_layout = QFormLayout(status_group)
        
        self.occupancy_led = self.create_led_indicator()
        self.leak_led = self.create_led_indicator()
        
        status_layout.addRow("👤 人体存在:", self.occupancy_led)
        status_layout.addRow("💧 漏水告警:", self.leak_led)
        
        #设备控制组
        control_group = QGroupBox("设备控制")
        control_layout = QGridLayout(control_group)
        
        self.fan_btn = self.create_device_button("🌪️ 排风扇", "fan")
        self.heater_btn = self.create_device_button("🔥 浴霸", "heater") 
        self.light_btn = self.create_device_button("💡 照明", "light")
        self.mirror_btn = self.create_device_button("🪞 除雾镜", "mirror")
        
        control_layout.addWidget(self.fan_btn, 0, 0)
        control_layout.addWidget(self.heater_btn, 0, 1)
        control_layout.addWidget(self.light_btn, 1, 0)
        control_layout.addWidget(self.mirror_btn, 1, 1)
        
        #场景控制组
        scene_group = QGroupBox("场景模式")
        scene_layout = QVBoxLayout(scene_group)
        
        shower_btn = QPushButton("🚿 淋浴模式")
        shower_btn.clicked.connect(lambda: self.set_scene("shower"))
        
        toilet_btn = QPushButton("🚽 如厕模式") 
        toilet_btn.clicked.connect(lambda: self.set_scene("toilet"))
        
        clean_btn = QPushButton("🧹 清洁模式")
        clean_btn.clicked.connect(lambda: self.set_scene("clean"))
        
        scene_layout.addWidget(shower_btn)
        scene_layout.addWidget(toilet_btn) 
        scene_layout.addWidget(clean_btn)
        
        #添加到主布局
        layout.addWidget(conn_group)
        layout.addWidget(sensor_group)
        layout.addWidget(status_group)
        layout.addWidget(control_group)
        layout.addWidget(scene_group)
        layout.addStretch()
        
        return panel
        
    def create_right_panel(self):
        """创建右侧数据面板"""
        panel = QWidget()
        layout = QVBoxLayout(panel)
        
        # 历史数据图表
        chart_group = QGroupBox("历史数据")
        chart_layout = QVBoxLayout(chart_group)
        
        # 图表控制栏
        chart_controls = QHBoxLayout()
        
        self.chart_type = QComboBox()
        self.chart_type.addItems(["温湿度", "CO2浓度", "设备状态"])
        self.chart_type.currentTextChanged.connect(self.update_chart)
        
        self.time_range = QComboBox()
        self.time_range.addItems(["最近1小时", "最近6小时", "最近24小时", "最近7天"])
        self.time_range.currentTextChanged.connect(self.update_chart)
        
        export_btn = QPushButton("导出数据")
        export_btn.clicked.connect(self.export_data)
        
        chart_controls.addWidget(QLabel("显示类型:"))
        chart_controls.addWidget(self.chart_type)
        chart_controls.addWidget(QLabel("时间范围:"))
        chart_controls.addWidget(self.time_range)
        chart_controls.addStretch()
        chart_controls.addWidget(export_btn)
        
        # 图表绘制区域
        self.plot_widget = pg.PlotWidget()
        self.plot_widget.setBackground('w')
        self.plot_widget.setLabel('left', '数值')
        self.plot_widget.setLabel('bottom', '时间')
        self.plot_widget.showGrid(x=True, y=True)
        
        chart_layout.addLayout(chart_controls)
        chart_layout.addWidget(self.plot_widget)
        
        # 告警日志
        log_group = QGroupBox("告警日志")
        log_layout = QVBoxLayout(log_group)
        
        self.log_text = QTextEdit()
        self.log_text.setMaximumHeight(200)
        self.log_text.setReadOnly(True)
        
        clear_log_btn = QPushButton("清空日志")
        clear_log_btn.clicked.connect(self.log_text.clear)
        
        log_layout.addWidget(self.log_text)
        log_layout.addWidget(clear_log_btn)
        
        layout.addWidget(chart_group)
        layout.addWidget(log_group)
        
        return panel
        
    def create_value_label(self, value, unit, color):
        """创建数值显示标签"""
        label = QLabel(f"<span style='color: {color}; font-size: 18px; font-weight: bold;'>{value}</span> {unit}")
        return label
        
    def create_led_indicator(self):
        """创建LED状态指示器"""
        indicator = QLabel("●")
        indicator.setStyleSheet("color: gray; font-size: 16px;")
        return indicator
        
    def create_device_button(self, text, device_name):
        """创建设备控制按钮"""
        btn = QPushButton(text)
        btn.setCheckable(True)
        btn.clicked.connect(lambda checked: self.control_device(device_name, checked))
        btn.setStyleSheet("""
            QPushButton {
                min-height: 50px;
                font-size: 14px;
                border: 2px solid #ddd;
                border-radius: 5px;
            }
            QPushButton:checked {
                background-color: #4CAF50;
                color: white;
                border-color: #45a049;
            }
        """)
        return btn
2 数据库管理模块
python 复制代码
class DatabaseManager:
    """数据库管理类 - 负责SQLite数据的存储与查询"""
    
    def __init__(self, db_path="bathroom_data.db"):
        self.db_path = db_path
        self.init_database()
        
    def init_database(self):
        """初始化数据库表结构"""
        conn = sqlite3.connect(self.db_path)
        cursor = conn.cursor()
        
        # 传感器数据表
        cursor.execute("""
            CREATE TABLE IF NOT EXISTS sensor_data (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                timestamp INTEGER NOT NULL,
                temperature REAL,
                humidity REAL,
                co2 INTEGER,
                lux INTEGER,
                occupancy INTEGER,
                water_leak INTEGER
            )
        """)
        
        # 设备状态表
        cursor.execute("""
            CREATE TABLE IF NOT EXISTS device_status (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                timestamp INTEGER NOT NULL,
                device_name TEXT NOT NULL,
                status INTEGER NOT NULL,
                action TEXT
            )
        """)
        
        # 告警事件表  
        cursor.execute("""
            CREATE TABLE IF NOT EXISTS alert_events (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                timestamp INTEGER NOT NULL,
                event_type TEXT NOT NULL,
                level TEXT NOT NULL,
                message TEXT,
                resolved INTEGER DEFAULT 0
            )
        """)
        
        conn.commit()
        conn.close()
        
    def insert_sensor_data(self, data):
        """插入传感器数据"""
        conn = sqlite3.connect(self.db_path)
        cursor = conn.cursor()
        
        cursor.execute("""
            INSERT INTO sensor_data 
            (timestamp, temperature, humidity, co2, lux, occupancy, water_leak)
            VALUES (?, ?, ?, ?, ?, ?, ?)
        """, (
            int(time.time()),
            data.get('temperature'),
            data.get('humidity'), 
            data.get('co2'),
            data.get('lux'),
            data.get('occupancy', 0),
            data.get('water_leak', 0)
        ))
        
        conn.commit()
        conn.close()
        
    def get_history_data(self, hours=24, data_type='all'):
        """获取历史数据"""
        conn = sqlite3.connect(self.db_path)
        cursor = conn.cursor()
        
        start_time = int(time.time()) - (hours * 3600)
        
        cursor.execute("""
            SELECT timestamp, temperature, humidity, co2, lux
            FROM sensor_data 
            WHERE timestamp > ?
            ORDER BY timestamp
        """, (start_time,))
        
        results = cursor.fetchall()
        conn.close()
        
        return results

界面效果展示

技术学习价值

本项目作为完整的IoT系统实现,具有极高的学习和参考价值:

知识体系覆盖
  • 嵌入式开发:STM32 + FreeRTOS多任务编程
  • 通信协议:UART/SPI/I2C/Zigbee/WiFi/MQTT全栈
  • 上位机开发:Python GUI编程与数据库操作
  • 系统集成:硬件选型、软件架构、安全设计
  • 产品思维:用户体验、成本控制、可维护性
🛠️ 实践技能提升
  1. 📖 学习路线建议

    • 基础:单片机 → FreeRTOS → 通信协议
    • 进阶:系统架构 → 安全设计 → 性能优化
    • 应用:产品化 → 市场分析 → 商业模式
  2. 🔧 开发能力培养

    • 代码规范与文档编写
    • 版本控制与团队协作
    • 测试验证与质量保证
    • 问题诊断与故障排除

结语

智能家居是物联网技术的重要应用场景,正处于快速发展阶段。本项目提供完整的技术方案详细的实现指南,为开发者打造了一个优质的学习范本和参考模板。

相关推荐
仰泳之鹅5 小时前
【C语言】深入理解指针(5)
c语言·开发语言
无为之士6 小时前
君正交叉编译链工具mips-gcc540-glibc222-64bit-r3.3.0.smaller.bz2编译st-device-sdk-c
c语言·开发语言
THMAIL8 小时前
机器学习从入门到精通 - 机器学习调参终极手册:网格搜索、贝叶斯优化实战
人工智能·python·算法·机器学习·支持向量机·数据挖掘·逻辑回归
ERP老兵-冷溪虎山8 小时前
Python/JS/Go/Java同步学习(第三篇)四语言“切片“对照表: 财务“小南“纸切片术切凭证到崩溃(附源码/截图/参数表/避坑指南/老板沉默术)
java·javascript·python·golang·中医编程·四语言同步学习·职场生存指南
科技树支点8 小时前
无GC的Java创新设计思路:作用域引用式自动内存管理
java·python·go·web·编程语言·编译器
THMAIL9 小时前
机器学习从入门到精通 - 集成学习核武器:随机森林与XGBoost工业级应用
人工智能·python·算法·随机森林·机器学习·集成学习·sklearn
程序员miki10 小时前
Pytorch的CUDA版本安装使用教程
人工智能·pytorch·python
古译汉书10 小时前
嵌入式铁头山羊stm32-SAR型ADC模块介绍、采样时间、转换时间-Day24
c语言·stm32·单片机·嵌入式硬件·iot