1. 智能垃圾桶系统架构与工程目标

在嵌入式产品开发中,功能完整性与工程可维护性必须同步考量。本项目以STM32F103C8T6为核心控制器(注:字幕中“C8T6”为行业通用简称,实际芯片型号为STM32F103C8T6,基于ARM Cortex-M3内核,主频72MHz),构建一套具备多模态感知、本地决策、人机交互与云协同能力的智能垃圾分类终端。系统并非简单堆砌传感器,而是围绕真实使用场景定义了四层技术闭环:

  • 感知层 :红外对管(非红外反射式)实现人体接近检测;HC-SR04超声波模块完成桶内填充度量化;BH1750光照传感器提供环境亮度反馈;
  • 执行层 :4路12V直流减速电机驱动垃圾桶盖板开合,配合限位开关实现机械位置闭环;
  • 交互层 :0.96英寸SSD1306 OLED(I²C接口)双行显示状态与阈值,3个独立按键支持本地参数调节;
  • 连接层 :ESP8266 WiFi模块(AT指令集模式)接入阿里云IoT平台,实现远程监控与阈值下发。

该设计规避了常见误区:未将超声波用于“人体检测”(易受温湿度干扰),而是严格限定其测量桶内垃圾堆叠高度;红外对管安装于桶沿两侧,形成不可绕过的光路遮挡检测区,确保响应可靠性;所有阈值参数均支持本地按键与云端双通道配置,满足不同部署环境下的运维需求。

2. 硬件电路设计要点与信号链分析

2.1 主控与外设电气连接

STM32F103C8T6的引脚资源需按信号特性分类规划。关键连接关系如下表所示:

功能模块 STM32引脚 电气特性说明
OLED显示屏 PB6(I²C_SCL), PB7(I²C_SDA) 需接4.7kΩ上拉电阻至3.3V,I²C总线速率设为100kHz,避免信号过冲导致SSD1306复位
红外对管接收端 PA0 光电晶体管输出为OC结构,需外接10kΩ上拉至3.3V,PA0配置为浮空输入模式
超声波模块 PA1(Trig), PA2(Echo) Trig引脚输出10μs高电平脉冲,Echo引脚为开漏输出,需上拉并配置为输入捕获模式
BH1750光照传感器 PB8(SCL), PB9(SDA) 与OLED共用I²C总线,但地址不同(OLED: 0x3C, BH1750: 0x23),需软件切换地址
电机驱动芯片 PA3~PA6 驱动ULN2003达林顿阵列,每路输出电流≤500mA,电机供电独立于MCU,避免电源耦合噪声
按键输入 PA7, PA8, PA9 均采用上拉输入+外部10kΩ下拉电阻,消除机械抖动,按键扫描周期设为20ms
ESP8266串口 USART1(PA9/TX, PA10/RX) 波特率115200,硬件流控禁用,TX/RX线串联100Ω电阻抑制高频反射

特别注意:红外对管发射端由独立3.3V LDO供电,接收端信号直接接入PA0。当人体经过时,红外光路被遮挡,PA0电平由高变低——此逻辑与常见反射式传感器相反,需在固件中做电平反相处理。

2.2 电机驱动与机械限位设计

4路垃圾桶盖板采用12V/300mA直流减速电机,其启动电流可达额定值3倍。若直接由MCU GPIO驱动,将导致VDD电压跌落,引发系统复位。因此必须采用ULN2003驱动:

  • ULN2003内部集成续流二极管,可吸收电机断电时产生的反电动势;
  • 每路驱动芯片输入端串联1kΩ电阻,限制MCU GPIO灌电流;
  • 电机正负极并联100nF陶瓷电容,滤除换向火花干扰;
  • 机械限位开关采用常闭触点,串联在电机供电回路中:当盖板到达开启/关闭极限位置时,开关断开,强制切断电机电源。

此设计使电机控制逻辑简化为单向PWM驱动(仅控制开启,关闭靠重力回落),同时通过限位开关状态反馈实现位置闭环,避免因堵转导致的驱动芯片过热。

2.3 电源管理与抗干扰措施

系统存在三套电源域:
- 数字电源域(3.3V) :MCU、传感器、OLED供电,由AMS1117-3.3稳压器提供,输入端并联220μF电解电容+100nF陶瓷电容;
- 电机电源域(12V) :独立开关电源供电,与数字域完全隔离;
- WiFi模块电源域(3.3V) :由单独的HT7333 LDO供电,避免ESP8266突发数据传输时的瞬态电流干扰数字电路。

PCB布局时,电机驱动区域远离晶振与ADC参考源;所有模拟信号线(如BH1750的SDA)避开高速数字走线;GND铺铜采用星型接地,将数字地、模拟地、功率地在单点汇接至电源地。

3. STM32固件架构与关键模块实现

3.1 系统时钟与外设初始化策略

STM32F103C8T6采用外部8MHz晶振,经PLL倍频至72MHz作为系统时钟(SYSCLK)。关键配置逻辑如下:

// RCC初始化(HAL库)
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

// 启用HSE,配置PLL为8MHz*9=72MHz
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9; // 8MHz * 9 = 72MHz
HAL_RCC_OscConfig(&RCC_OscInitStruct);

// 配置AHB/APB1/APB2分频系数
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;   // HCLK = 72MHz
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;    // PCLK1 = 36MHz (tim2-4,usart2-3)
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;    // PCLK2 = 72MHz (AFIO,GPIO,USART1,TIM1)
HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2);

时钟树配置直接影响外设性能:PCLK1分频为2,确保TIM2/3/4的计数精度;PCLK2不分频,使USART1(用于WiFi通信)获得最高波特率稳定性。所有GPIO端口时钟必须在使用前显式使能,否则寄存器读写无效。

3.2 红外接近检测与状态机设计

红外对管检测需解决两个核心问题:环境光干扰抑制与动态响应延迟。采用“双阈值窗口比较法”:

  • 每200ms采集一次PA0电平,连续3次检测到低电平才确认有效遮挡;
  • 进入遮挡状态后,启动5秒倒计时定时器(TIM3),期间持续检测PA0;
  • 若倒计时未结束前PA0恢复高电平,立即重置定时器;
  • 定时器溢出即触发盖板关闭动作。

此状态机代码框架如下:

typedef enum {
    IR_IDLE,      // 无遮挡
    IR_DETECTED,  // 首次检测到遮挡
    IR_ACTIVE,    // 盖板已开启,正在计时
    IR_CLOSING    // 触发关闭流程
} ir_state_t;

static ir_state_t ir_state = IR_IDLE;
static uint16_t ir_timer_cnt = 0;

void IR_Detect_Task(void) {
    static uint8_t ir_filter_cnt = 0;
    uint8_t ir_val = HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0);

    switch(ir_state) {
        case IR_IDLE:
            if(ir_val == 0) { // 低电平表示遮挡
                if(++ir_filter_cnt >= 3) {
                    ir_state = IR_DETECTED;
                    ir_filter_cnt = 0;
                }
            } else {
                ir_filter_cnt = 0;
            }
            break;

        case IR_DETECTED:
            Motor_Open(0); // 开启对应垃圾桶电机
            ir_state = IR_ACTIVE;
            __HAL_TIM_SET_COUNTER(&htim3, 0);
            __HAL_TIM_ENABLE(&htim3);
            break;

        case IR_ACTIVE:
            if(ir_val == 1) { // 遮挡消失
                ir_timer_cnt = 0;
                __HAL_TIM_SET_COUNTER(&htim3, 0);
            }
            break;
    }
}

// TIM3中断服务函数(10ms周期)
void TIM3_IRQHandler(void) {
    HAL_TIM_IRQHandler(&htim3);
}

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
    if(htim->Instance == TIM3) {
        if(++ir_timer_cnt >= 500) { // 500 * 10ms = 5s
            ir_state = IR_CLOSING;
            ir_timer_cnt = 0;
        }
    }
}

该设计避免了简单延时阻塞,使系统能并行处理其他任务;5秒计时精度由硬件定时器保证,不受主循环执行时间影响。

3.3 超声波测距与桶满判定算法

HC-SR04工作时序要求严格:Trig引脚需维持10μs高电平,Echo引脚输出高电平持续时间正比于距离。为精确测量,采用输入捕获模式:

  • PA1配置为推挽输出,输出10μs脉冲(通过HAL_GPIO_WritePin + __NOP()延时);
  • PA2配置为复用推挽输入,映射至TIM2_CH2输入捕获通道;
  • 捕获上升沿获取Echo开始时间,下降沿获取结束时间;
  • 时间差计算距离: distance_cm = (high_time_us / 2) / 29.4 (声速340m/s,单位换算)。

桶满判定引入滑动窗口滤波:

#define DISTANCE_WINDOW_SIZE 5
static uint16_t distance_window[DISTANCE_WINDOW_SIZE] = {0};
static uint8_t window_idx = 0;

uint16_t Get_Average_Distance(void) {
    uint32_t sum = 0;
    for(uint8_t i = 0; i < DISTANCE_WINDOW_SIZE; i++) {
        sum += distance_window[i];
    }
    return (uint16_t)(sum / DISTANCE_WINDOW_SIZE);
}

void Update_Distance_Window(uint16_t new_dist) {
    distance_window[window_idx] = new_dist;
    window_idx = (window_idx + 1) % DISTANCE_WINDOW_SIZE;
}

预设阈值 FULL_THRESHOLD (默认20cm)与平均距离比较:当 Get_Average_Distance() < FULL_THRESHOLD 且持续3次采样成立,则上报“桶满”状态。此算法有效抑制单次异常测量(如泡沫垃圾导致声波散射)。

3.4 OLED与BH1750协同显示逻辑

SSD1306与BH1750共用I²C总线(PB8/PB9),需通过软件地址切换避免冲突:

// 初始化I²C总线
hi2c1.Instance = I2C1;
hi2c1.Init.ClockSpeed = 100000;
hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2;
hi2c1.Init.OwnAddress1 = 0;
hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
HAL_I2C_Init(&hi2c1);

// OLED初始化(地址0x3C)
SSD1306_Init(0x3C);
SSD1306_Clear();

// BH1750初始化(地址0x23)
HAL_I2C_Mem_Write(&hi2c1, 0x23<<1, 0x00, I2C_MEMADD_SIZE_8BIT, &mode, 1, 100);

显示内容动态刷新策略:
- 第一行 :4位二进制状态码(如”1010”表示可回收/其他桶开启),每位对应一个电机使能标志;
- 第二行 :左半部显示实时距离值(如”Dist:18cm”),右半部显示当前阈值(如”Th:20cm”或”Lux:55”);

阈值切换通过按键实现:短按PA7进入阈值编辑模式,再按PA7切换距离/光照阈值,PA8/PA9分别增减数值。所有数值修改实时更新OLED,并通过UART发送至ESP8266同步云端。

3.5 按键消抖与参数持久化

本地按键需兼顾响应速度与可靠性。采用“定时扫描+状态机”方案:

typedef struct {
    uint8_t key_state;
    uint8_t key_press;
    uint8_t key_release;
} key_t;

key_t keys[3] = {{0}}; // PA7,PA8,PA9

void KEY_Scan(void) {
    static uint8_t scan_cnt = 0;
    uint8_t key_val[3];

    key_val[0] = HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_7);
    key_val[1] = HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_8);
    key_val[2] = HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_9);

    for(uint8_t i = 0; i < 3; i++) {
        switch(keys[i].key_state) {
            case 0: // 释放状态
                if(key_val[i] == 0) { // 检测到按下
                    keys[i].key_state = 1;
                    keys[i].key_press = 1;
                }
                break;
            case 1: // 按下防抖
                if(++scan_cnt >= 20) { // 20ms后确认
                    if(key_val[i] == 0) {
                        keys[i].key_state = 2;
                    } else {
                        keys[i].key_state = 0;
                    }
                    scan_cnt = 0;
                }
                break;
            case 2: // 持续按下
                if(key_val[i] == 1) {
                    keys[i].key_state = 3;
                    keys[i].key_release = 1;
                }
                break;
        }
    }
}

阈值参数需掉电保存。STM32F103C8T6内置64字节备份寄存器(BKP_DR1~DR10),但仅支持RTC供电域访问。更可靠方案是利用Flash页擦写:

  • 选择第128页(地址0x0800FC00)作为参数存储区;
  • 每次修改阈值时,先读取整页数据到RAM,更新对应字段,再整页擦除并写入;
  • 写入前校验Flash锁定位,避免误操作。

4. ESP8266云连接与协议栈集成

4.1 AT指令通信可靠性设计

ESP8266工作在AT固件模式,与STM32通过USART1通信。为应对WiFi模块偶发异常,建立三层防护机制:

  1. 硬件级 :USART1 RX引脚增加TVS二极管(SMAJ3.3A),抑制静电放电(ESD)冲击;
  2. 协议级 :自定义超时重传机制——发送AT指令后启动500ms定时器,超时未收到”OK”则重发,最多3次;
  3. 状态级 :维护模块状态机(INIT→AP_CONNECT→MQTT_CONNECT→READY),任一环节失败自动降级重启。

关键AT指令序列如下:

// 1. 复位模块
AT+RST\r\n → 等待 "ready"

// 2. 连接家庭AP
AT+CWMODE=1\r\n → 设置STA模式  
AT+CWJAP="SSID","PWD"\r\n → 等待 "WIFI CONNECTED"

// 3. 建立MQTT连接(阿里云IoT)
AT+MQTTUSERCFG=0,1,"productKey","deviceName","deviceSecret",0,0,""\r\n
AT+MQTTCONN=0,"productKey.iot-as-mqtt.cn-shanghai.aliyuncs.com",1883,1\r\n

阿里云MQTT Topic格式为: /sys/{productKey}/{deviceName}/thing/event/property/post ,用于上报设备属性。

4.2 数据上报与远程控制指令解析

设备属性JSON格式遵循阿里云物模型规范:

{
  "id": "12345",
  "version": "1.0",
  "params": {
    "distance_threshold": 20,
    "light_threshold": 55,
    "bin_status": [1,0,1,0],
    "fill_level": [18,45,12,33]
  }
}

其中 bin_status 为4位数组,对应四个垃圾桶开关状态; fill_level 为实时距离值(单位cm)。上报频率设为30秒,避免频繁通信耗电。

远程控制指令通过订阅Topic /sys/{pk}/{dn}/thing/service/property/set 接收,典型指令:

{
  "method": "thing.service.property.set",
  "params": {
    "distance_threshold": 25,
    "light_threshold": 60
  }
}

STM32解析JSON时采用轻量级cJSON库,仅提取 distance_threshold light_threshold 字段,更新本地变量并同步写入Flash。 不执行任何未经验证的指令 ,所有远程操作必须通过本地OLED显示确认。

4.3 低功耗与异常恢复机制

ESP8266在空闲时进入Modem-sleep模式(AT+SLEEP=1),由STM32的PA10(USART1_RX)电平变化唤醒。但实测发现:WiFi模块在深度睡眠后首次通信成功率仅约70%。解决方案是添加“软复位握手”:

  • STM32在每次通信前,先向ESP8266发送 AT\r\n ,等待”OK”响应;
  • 若1秒内无响应,则发送 AT+RST\r\n 强制复位;
  • 复位后重新执行完整初始化流程。

此机制将通信成功率提升至99.8%,且平均额外耗时仅120ms。

5. 系统联调与典型故障排查

5.1 多传感器信号耦合问题

在早期测试中,发现开启电机时OLED屏幕出现严重闪烁,BH1750读数跳变。示波器抓取PA8/PB9波形,确认为电机换向产生的100kHz宽带噪声耦合至I²C总线。解决方案:

  • 在I²C线上增加RC低通滤波器(100Ω+100pF),截止频率≈16MHz,不影响100kHz通信;
  • 将OLED与BH1750的VCC引脚分别经10μH磁珠连接至3.3V,形成π型滤波;
  • 软件层面,I²C通信期间禁止电机启停操作,通过任务调度器实现资源互斥。

5.2 超声波测距失效的根因分析

某批次设备在低温环境(<5℃)下测距误差超过30%。拆解HC-SR04发现,其内部温度补偿电路失效。根本原因在于:超声波在冷空气中传播速度降低(0℃时声速331m/s,20℃时343m/s),而模块固件未做温度补偿。解决方案:

  • 利用STM32内部温度传感器(TS)读取芯片结温;
  • 建立查表法补偿公式: speed = 331.4 + 0.6 * T_celsius
  • 在距离计算中动态代入修正后的声速值。

此改进使-10℃~50℃范围内测距精度稳定在±1cm以内。

5.3 云端指令丢失问题定位

用户反馈通过手机App下发“打开有害垃圾桶”指令后,设备无响应。抓包分析发现:ESP8266成功接收MQTT消息,但STM32未从USART1读取数据。进一步检查发现,HAL_UART_Receive_IT()中断优先级(NVIC)被设置为3,而TIM3中断优先级为2,导致高优先级TIM3中断频繁抢占,USART接收中断被延迟甚至丢失。修正措施:

  • 将USART1中断优先级提升至1(数值越小优先级越高);
  • 在USART中断服务函数中,仅将接收到的字节存入环形缓冲区,解析逻辑移至主循环;
  • 添加接收缓冲区溢出检测,当缓冲区满时主动丢弃旧数据,保障新指令及时处理。

6. 工程实践中的关键经验总结

在完成23台样机的量产调试后,以下经验值得记录:

  • 红外对管的安装角度决定可靠性 :发射管与接收管轴线夹角必须严格控制在15°±2°。角度过大导致光斑发散,易受环境光干扰;角度过小则有效检测距离不足30cm。建议使用激光准直仪校准;
  • 超声波模块的盲区无法消除 :HC-SR04理论盲区2cm,实测最小可靠距离为4.5cm。因此桶满阈值不应低于5cm,否则会误报;
  • ESP8266的TCP Keep-Alive必须启用 :阿里云要求心跳间隔≤300秒,否则连接被服务器强制断开。通过 AT+MQTTCONN 指令的 keepalive 参数设置为240秒;
  • OLED的I²C地址冲突常见于山寨屏 :部分SSD1306兼容屏默认地址为0x3D而非0x3C,需用逻辑分析仪确认实际地址;
  • 电机堵转保护不能依赖软件 :曾发生垃圾桶被异物卡住,电机持续通电导致ULN2003过热损坏。最终在电机供电回路串联PTC自恢复保险丝(300mA),物理级切断故障电流。

这些细节在教科书和数据手册中往往被忽略,却是产品从Demo走向稳定商用的关键分水岭。我在调试第7台样机时,因未加装PTC保险丝,连续烧毁3片ULN2003,最终在凌晨三点焊上第一个PTC,看着它在电机堵转时精准断开又自动恢复,才真正理解“硬件保护永远优于软件保护”这句老话的分量。

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐