1. 项目背景与核心功能

最近在帮学生做毕业设计时,发现智能家居天气时钟是个非常实用的课题。这个项目把STM32单片机、云端数据获取、语音交互和手机控制完美结合,特别适合作为物联网入门练手项目。我自己在调试过程中踩过不少坑,今天就把完整实现方案分享给大家。

这个天气时钟的核心功能很接地气:早上起床能语音报时,出门前看一眼屏幕就知道今天要不要带伞,上班时还能用手机远程查看家里的温湿度。硬件成本控制在200元以内,用到的都是常见模块:

  • STM32F103C8T6:性价比超高的主控芯片,72MHz主频完全够用
  • ESP8266-01s:十块钱就能搞定的WiFi模块
  • 0.96寸OLED:显示天气信息足够清晰
  • DHT11:经典温湿度传感器,精度对于家用完全足够
  • SYN6288语音模块:比普通蜂鸣器高级多了,能合成真人语音

2. 硬件设计详解

2.1 核心电路设计

主控板我用的是最小系统板,自己画PCB的话注意这几个关键点:

  • 给ESP8266单独供电,实测3.3V电流峰值能达到200mA
  • STM32的NRST引脚要留出调试接口
  • OLED最好用I2C接口,节省IO资源

电源部分有个坑要注意:DHT11对电压波动敏感,我在VCC并联了100μF电容才稳定。下面是实测可用的原理图片段:

// STM32与ESP8266连接示意图
PA9(TX)  —— ESP8266 RX
PA10(RX) —— ESP8266 TX
PB6      —— OLED SCL
PB7      —— OLED SDA
PC13     —— 用户按键
PA0      —— DHT11数据线

2.2 模块选型建议

语音模块我对比过三种方案:

  1. SYN6288:成本约25元,支持GB2312编码,音质较好
  2. LD3320:能本地识别简单指令,但需要大量训练样本
  3. 科大讯飞模块:效果最好但价格超预算

最终选择SYN6288+按键触发的方式,既控制成本又保证实用性。时钟模块用DS1302就行,比DS3231便宜不少,走时精度每天误差在2秒左右,通过联网校准完全够用。

3. 软件架构设计

3.1 程序流程图

整个系统跑在裸机上,没有上RTOS。主循环这样设计:

while(1) {
  读取DHT11数据();
  if(网络连接正常){
    获取网络时间();
    每30分钟更新天气();
  }
  检测按键();
  刷新OLED显示();
  处理语音队列();
}

3.2 关键代码实现

天气API我用的是心知天气的免费版,HTTP请求要这样构造:

// 示例请求URL
char url[128];
sprintf(url, "https://api.seniverse.com/v3/weather/now.json?key=你的密钥&location=%s&language=zh-Hans", 
        city_name);

JSON解析推荐用cJSON库,STM32上跑起来毫无压力:

cJSON *root = cJSON_Parse(response);
cJSON *now = cJSON_GetObjectItem(root, "now");
temperature = cJSON_GetObjectItem(now, "temperature")->valuestring;

4. 云端数据对接

4.1 天气API配置

免费API有调用次数限制,我的解决方案是:

  • 本地缓存最近查询结果
  • 非必要不频繁更新
  • 多个城市共用同一个API key

在STM32上实现HTTPS有点吃力,可以改用HTTP或者通过路由器做反向代理。实测发现,用ESP8266的AT指令直接请求HTTP,代码最简洁:

AT+CIPSTART="TCP","api.seniverse.com",80
AT+CIPSEND=98
GET /v3/weather/now.json?key=xxx&location=beijing HTTP/1.1\r\nHost: api.seniverse.com\r\n\r\n

4.2 数据同步策略

设计了一个智能更新策略:

  1. 首次上电强制更新
  2. 之后每30分钟更新一次
  3. 用户主动查询时立即更新
  4. 网络异常时使用最近有效数据

在Flash里开辟了一个存储区,保存最后一次成功获取的数据:

typedef struct {
    char temp[8];
    char weather[16];
    uint32_t timestamp;
} WeatherCache;

5. 语音交互实现

5.1 语音合成控制

SYN6288通过串口控制,发送文本前要先组包:

void speak(const char *text) {
    uint8_t head[] = {0xFD, 0x00, 0x00, 0x01, 0x00};
    head[2] = strlen(text) + 2;
    head[4] = 0x04; // GB2312编码
    
    HAL_UART_Transmit(&huart2, head, 5, 100);
    HAL_UART_Transmit(&huart2, (uint8_t*)text, strlen(text), 100);
}

5.2 交互逻辑设计

设计了几个常用语音指令:

  • "现在时间":播报当前时间
  • "天气情况":朗读温度和天气状况
  • "打开灯光":预留的智能家居接口

为了避免误触发,采用长按按键激活语音功能。在代码里用状态机实现:

if(按键按下时间 > 2秒) {
    播放提示音();
    开始录音();
    while(!超时){
        检测语音指令();
    }
}

6. APP远程控制

6.1 通信协议选择

对比了三种方案后选择MQTT:

  • HTTP轮询:实现简单但耗电
  • WebSocket:需要持续连接
  • MQTT:最适合物联网场景

用ESP8266内置的MQTT功能,代码量大幅减少:

AT+MQTTUSERCFG=0,1,"clientID","","",0,0,""
AT+MQTTCONN=0,"broker.emqx.io",1883,0

6.2 控制指令设计

定义了简单的JSON协议:

{
  "cmd": "set_alarm",
  "time": "07:30",
  "repeat": [1,1,1,1,1,0,0]
}

APP端用Android Studio开发,关键是用好MQTT的异步回调:

client.setCallback(new MqttCallback() {
    @Override
    public void messageArrived(String topic, MqttMessage message) {
        String payload = new String(message.getPayload());
        // 更新UI
    }
});

7. 常见问题解决

在开发过程中遇到过几个典型问题:

  1. ESP8266频繁断连 解决方法:在AT指令后加500ms延时,电源并联470μF电容

  2. DHT11读取失败 解决方法:将数据线改为开漏输出,上拉电阻改为4.7K

  3. 语音模块杂音大 解决方法:在电源端加π型滤波电路,地线单独走线

  4. OLED显示闪烁 解决方法:改用硬件I2C,时钟频率降到100kHz

这个项目最让我满意的是它的扩展性——后续可以轻松加入更多功能,比如通过GPIO控制智能插座,或者增加PM2.5检测模块。所有源码和PCB文件我都整理好了,需要的话可以留言区告诉我。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐