嵌入式项目之温湿度闹钟

在创客的世界里,最令人兴奋的莫过于用最少的成本,创造出既有技术含量又能解决实际问题的产品。今天,我们就来挑战一个项目:制作一个功能完备、外观精致的温湿度闹钟,总成本控制在50元以内。它不仅能显示时间、温度和湿度,还能设定闹钟,绝对是提升桌面幸福感的神器!

一、 核心思路:低成本与高溢值的平衡

"低成本"不等于"低品质",而"高溢价"则源于设计感和实用性。我们的策略是:

  • 核心廉价:选用成熟、廉价且资料丰富的单片机和传感器。
  • 显示升级:不用廉价的数码管,而选用带背光的液晶屏,瞬间提升产品档次。
  • 功能集成:将时钟、温湿度、闹钟等常用功能合而为一,提高实用性。
  • 外观用心:通过简单的3D打印外壳或亚克力板,让产品拥有完整的"产品形态"。

二、 BOM清单(物料清单)

元件名称 型号/规格 参考价格(元) 备注
主控 STM32F103C8T6 最小系统板 ~15 性能强大,性价比之王
显示 0.96寸 OLED屏 (I2C接口) ~10 自发光,对比度高,省电
传感器 DHT11 温湿度传感器模块 ~5 经典、稳定、易用
时钟 DS3231 高精度时钟模块 ~8 带备用电池,断电时间不丢失
输入 轻触按键 x 3 ~1 用于设置时间和闹钟
其他 杜邦线、面包板、蜂鸣器 ~5 用于连接和报警
外壳 3D打印或纸盒 0-5 可选,但强烈推荐
总计 ~49 完美控制在预算内!

三、 硬件连接

接线非常简单,我们主要使用I2C和单总线通信。

  1. OLED (I2C) :

    • VCC -> 3.3V
    • GND -> GND
    • SCL -> PB6 (STM32的I2C1_SCL)
    • SDA -> PB7 (STM32的I2C1_SDA)
  2. DS3231 (I2C) : 与OLED并联在同一组I2C总线上。

    • VCC -> 3.3V
    • GND -> GND
    • SCL -> PB6
    • SDA -> PB7
  3. DHT11:

    • VCC -> 3.3V
    • GND -> GND
    • DATA -> PA0 (任意GPIO即可)
  4. 按键 (设置/加/减) :

    • 一端分别接 PA1, PA2, PA3
    • 另一端接 GND (启用内部上拉电阻)
  5. 蜂鸣器:

    • 正极 -> PA4
    • 负极 -> GND

四、 软件与代码

我们将使用STM32CubeMX生成基础工程,然后编写应用逻辑。这里为了简化,我将核心逻辑整合到一个代码文件中,并假设你已经配置好了I2C、GPIO和时钟。

所需库

  • HAL 库 (STM32CubeMX生成)
  • DHT11 驱动库
  • DS3231 驱动库
  • SSD1306 OLED 驱动库

这些库在GitHub上都非常容易找到。

核心代码 (main.c)

scss 复制代码
/* USER CODE BEGIN Includes */
#include "dht11.h"
#include "ds3231.h"
#include "ssd1306.h"
#include <stdio.h>
/* USER CODE END Includes */

/* ... CubeMX生成的代码 ... */

/* USER CODE BEGIN PV */
// 定义按键GPIO
#define SET_BUTTON_PIN GPIO_PIN_1
#define SET_BUTTON_PORT GPIOA
#define UP_BUTTON_PIN GPIO_PIN_2
#define UP_BUTTON_PORT GPIOA
#define DOWN_BUTTON_PIN GPIO_PIN_3
#define DOWN_BUTTON_PORT GPIOA
#define BUZZER_PIN GPIO_PIN_4
#define BUZZER_PORT GPIOA

// 全局变量
DHT11_Data_t dht11_data;
DS3231_Time_t ds3231_time;
DS3231_Alarm1_t alarm1;
uint8_t set_mode = 0; // 0:正常显示, 1:设置时间, 2:设置闹钟
uint8_t cursor_pos = 0; // 设置时的光标位置
uint32_t last_tick = 0;
const uint16_t update_interval = 2000; // 2秒更新一次温湿度
/* USER CODE END PV */

// ... 其他函数 ...

int main(void)
{
  /* ... CubeMX初始化 ... */

  /* USER CODE BEGIN 2 */
  // 初始化外设
  SSD1306_Init();
  DS3231_Init();
  HAL_Delay(100); // 等待外设稳定

  // 设置一个默认闹钟 (例如 07:30:00)
  alarm1.seconds = 0;
  alarm1.minutes = 30;
  alarm1.hours = 7;
  alarm1.day_date = 1; // 不关心具体日期
  alarm1.mode = DS3231_ALARM1_EVERY_SECOND; // 先设为每秒触发,用于测试
  DS3231_SetAlarm1(&alarm1);
  DS3231_EnableAlarm1(1); // 开启闹钟1
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    uint32_t current_tick = HAL_GetTick();

    // 按键扫描
    handle_buttons();

    // 闹钟检测
    if (DS3231_CheckAlarm1Flag()) {
      DS3231_ClearAlarm1Flag();
      trigger_alarm();
    }

    // 定时更新温湿度
    if (current_tick - last_tick > update_interval) {
      last_tick = current_tick;
      DHT11_Read(&dht11_data);
    }

    // 显示逻辑
    update_display();

    HAL_Delay(100); // 主循环延时
    /* USER CODE END WHILE */
  }
}

void handle_buttons(void) {
  if (HAL_GPIO_ReadPin(SET_BUTTON_PORT, SET_BUTTON_PIN) == GPIO_PIN_RESET) {
    HAL_Delay(50); // 消抖
    if (HAL_GPIO_ReadPin(SET_BUTTON_PORT, SET_BUTTON_PIN) == GPIO_PIN_RESET) {
      set_mode = (set_mode + 1) % 3; // 循环切换模式
      cursor_pos = 0;
      while(HAL_GPIO_ReadPin(SET_BUTTON_PORT, SET_BUTTON_PIN) == GPIO_PIN_RESET); // 等待释放
    }
  }

  if (set_mode != 0) { // 只有在设置模式下才响应加减键
    if (HAL_GPIO_ReadPin(UP_BUTTON_PORT, UP_BUTTON_PIN) == GPIO_PIN_RESET) {
      HAL_Delay(50);
      if (HAL_GPIO_ReadPin(UP_BUTTON_PORT, UP_BUTTON_PIN) == GPIO_PIN_RESET) {
          adjust_value(1);
          while(HAL_GPIO_ReadPin(UP_BUTTON_PORT, UP_BUTTON_PIN) == GPIO_PIN_RESET);
      }
    }
    if (HAL_GPIO_ReadPin(DOWN_BUTTON_PORT, DOWN_BUTTON_PIN) == GPIO_PIN_RESET) {
      HAL_Delay(50);
      if (HAL_GPIO_ReadPin(DOWN_BUTTON_PORT, DOWN_BUTTON_PIN) == GPIO_PIN_RESET) {
          adjust_value(-1);
          while(HAL_GPIO_ReadPin(DOWN_BUTTON_PORT, DOWN_BUTTON_PIN) == GPIO_PIN_RESET);
      }
    }
  }
}

void adjust_value(int direction) {
    // 此处简化处理,实际应根据set_mode和cursor_pos调整time或alarm结构体
    if (set_mode == 1) { // 设置时间
        if (cursor_pos == 0) ds3231_time.hours = (ds3231_time.hours + direction + 24) % 24;
        if (cursor_pos == 1) ds3231_time.minutes = (ds3231_time.minutes + direction + 60) % 60;
        DS3231_SetTime(&ds3231_time);
    } else if (set_mode == 2) { // 设置闹钟
        if (cursor_pos == 0) alarm1.hours = (alarm1.hours + direction + 24) % 24;
        if (cursor_pos == 1) alarm1.minutes = (alarm1.minutes + direction + 60) % 60;
        DS3231_SetAlarm1(&alarm1);
    }
    cursor_pos = (cursor_pos + 1) % 2; // 自动切换到下一个设置项
}


void update_display(void) {
    char buffer[32];
    DS3231_GetTime(&ds3231_time);

    SSD1306_Clear();

    // 第一行:时间
    snprintf(buffer, sizeof(buffer), "%02d:%02d:%02d", ds3231_time.hours, ds3231_time.minutes, ds3231_time.seconds);
    SSD1306_GotoXY(0, 0);
    SSD1306_Puts(buffer, &Font_11x18, SSD1306_COLOR_WHITE);

    // 第二行:温湿度
    snprintf(buffer, sizeof(buffer), "T:%2dC H:%2d%%", (int)dht11_data.temperature, (int)dht11_data.humidity);
    SSD1306_GotoXY(0, 24);
    SSD1306_Puts(buffer, &Font_7x10, SSD1306_COLOR_WHITE);

    // 第三行:闹钟状态
    snprintf(buffer, sizeof(buffer), "Alarm: %02d:%02d", alarm1.hours, alarm1.minutes);
    SSD1306_GotoXY(0, 40);
    SSD1306_Puts(buffer, &Font_7x10, SSD1306_COLOR_WHITE);

    SSD1306_UpdateScreen();
}

void trigger_alarm(void) {
    // 简单的蜂鸣报警
    for(int i = 0; i < 10; i++) {
        HAL_GPIO_WritePin(BUZZER_PORT, BUZZER_PIN, GPIO_PIN_SET);
        HAL_Delay(200);
        HAL_GPIO_WritePin(BUZZER_PORT, BUZZER_PIN, GPIO_PIN_RESET);
        HAL_Delay(200);
    }
}
/* ... 其他用户代码 ... */

五、 打造"高溢价"的关键一步:外壳

一个裸露的电路板只是半成品,一个精致的外壳才能让它成为"产品"。

  • 3D打印:如果你有3D打印机,可以设计一个专属外壳,考虑按键的开孔、屏幕的视窗以及散热。
  • 亚克力激光切割:设计简洁的几何形状,用几层不同颜色的亚克力板堆叠起来,效果非常出众。
  • 废物利用:找一个漂亮的旧饼干盒或者小木盒,精心规划内部空间,也能达到意想不到的效果。

六、 总结与拓展

至此,一个成本不到50元,功能强大且外观精美的温湿度闹钟就完成了。它集成了时间显示、环境监测和闹钟功能,无论是自用还是作为礼物都非常有意义。

可以进一步拓展的功能:

  • 光敏传感器:根据环境光线自动调节OLED屏幕亮度。
  • 蓝牙模块:通过手机APP设置时间和闹钟。
  • 更多闹钟:支持设置多个闹钟。
  • 温度单位切换:支持摄氏度和华氏度切换。

这个项目完美诠释了创客精神:用技术赋能创意,用低成本实现高价值。现在,轮到你来动手,打造属于你自己的桌面小神器了!

相关推荐
用户2986985301412 小时前
C# 中如何从 URL 下载 Word 文档:基于 Spire.Doc 的高效解决方案
后端·c#·.net
小飞Coding12 小时前
你写的 equals() 和 hashCode(),正在悄悄吃掉你的数据!
java·后端
想用offer打牌12 小时前
一站式了解http1.1,http2.0和http3.0
后端·网络协议·面试
用户685453759776912 小时前
别再用低效方式读取数据了,这4种Pandas方法让你效率提升10倍
后端
用户685453759776912 小时前
Pandas数据清洗别再用fillna了,这些骚操作让你效率提升10倍
后端
小飞Coding12 小时前
🔍 你的 Java 应用“吃光”了内存?别慌,NMT 帮你揪出真凶!
jvm·后端
悟空码字12 小时前
Java短信验证码保卫战,当羊毛党遇上“铁公鸡”
java·后端
爱吃KFC的大肥羊12 小时前
Redis 基础完全指南:从全局命令到五大数据结构
java·开发语言·数据库·c++·redis·后端
用户21903265273512 小时前
Spring Boot4.0整合RabbitMQ死信队列详解
java·后端