1. 项目缘起:为什么用micro:bit做温湿度计?
最近在整理工作室的电子元件,翻出来几块闲置的micro:bit开发板和几个DHT11温湿度传感器。看着它们,我就在想,能不能用最简单、最直观的方式,把它们组合成一个能实时显示环境数据的“桌面小仪表”?这个想法其实挺实用的,比如放在书桌上监控一下工作环境的舒适度,或者放在花盆旁边看看植物是否需要浇水。micro:bit本身自带LED点阵屏,虽然分辨率不高,但显示几个数字和简单图标绰绰有余,正好省去了外接复杂显示屏的麻烦。而DHT11作为一款经典的数字温湿度复合传感器,价格低廉、接口简单,和micro:bit简直是绝配。
市面上很多教程要么过于简单,只讲接线和显示数字,要么一下子跳到物联网上传数据,对中间的核心原理和可能遇到的坑一笔带过。这次,我想从一个有实际项目经验的开发者角度,从头到尾、掰开揉碎地讲清楚如何用micro:bit和DHT11搭建一个稳定可靠的温湿度计。我们不仅要让它“跑起来”,更要理解它“为什么能跑”,以及当它“跑不动”的时候该怎么排查。你会发现,即使是这样一个小项目,也蕴含着数字传感器通信、电源管理、代码健壮性设计等多个嵌入式开发的核心知识点。
2. 核心器件选型与工作原理深潜
在动手之前,我们必须先吃透手头的两个“主角”:micro:bit主控板和DHT11传感器。理解它们的能力边界和工作原理,是后续一切顺利的基础。
2.1 BBC micro:bit:不只是个玩具开发板
很多人对micro:bit的第一印象是面向教育的图形化编程工具,但实际上,它的硬件配置对于此类传感器应用是绰绰有余的。其核心是一颗Nordic nRF51822芯片,这是一款ARM Cortex-M0内核的蓝牙低功耗SoC。对我们这个项目而言,需要关注以下几个关键特性:
GPIO(通用输入输出引脚):micro:bit边缘的金手指连接器提供了多个GPIO,其中一些支持模拟输入、PWM输出等特殊功能。DHT11使用的是单总线数字协议,我们只需要任意一个普通的数字IO口即可。但这里有一个细节:micro:bit的IO口逻辑电平是3.3V,而DHT11的工作电压范围是3.3V-5.5V。这意味着在供电上,我们可以直接用micro:bit的3.3V输出给DHT11供电,两者电平匹配,无需电平转换电路。
电源管理:micro:bit可以通过USB供电,也可以用外部电池盒供电。当使用电池供电时,整机的功耗变得非常重要。nRF51822本身功耗极低,而DHT11在测量间隙处于休眠模式,功耗也很小。因此,这个温湿度计完全可以做成一个便携的、电池续航时间很长的设备。
板载资源:5x5的LED点阵是我们天然的显示器。虽然小,但通过滚动显示或分屏显示(例如,上半部分显示温度,下半部分显示湿度),完全可以清晰呈现数据。板载的A/B按钮可以作为功能切换键,比如切换显示单位(摄氏/华氏)、查看历史最大值/最小值等。
2.2 DHT11温湿度传感器:单总线协议详解
DHT11是一款含有已校准数字信号输出的温湿度复合传感器。它采用单总线数据格式进行通信,这意味着只需要一根数据线(加上电源和地线)就能完成双向通信。这是它简单易用的原因,也是初学者最容易出错的地方。
电气连接:DHT11有四个引脚(从正面看,网格面朝自己,从左到右):
- VCC:电源正极(3.3V-5.5V)。我们接micro:bit的3.3V引脚。
- DATA:单总线数据引脚。需要接一个上拉电阻(通常4.7KΩ-10KΩ)到VCC。这是关键!很多模块已经内置了这个上拉电阻,如果是单独的传感器元件,你必须自己外接。
- NC:空脚,不连接。
- GND:电源地。接micro:bit的GND。
通信协议时序(重点与难点): DHT11的通信过程由微处理器(micro:bit)发起,传感器响应。一次完整的数据传输为40bit(5字节),格式为:8bit湿度整数 + 8bit湿度小数 + 8bit温度整数 + 8bit温度小数 + 8bit校验和。
通信步骤分解:
- 主机发送开始信号:micro:bit将数据线引脚拉低至少18毫秒(ms),然后拉高20-40微秒(µs),随后释放总线(设置为输入模式,由上拉电阻拉高)。这个“先拉低再拉高”的组合拳,是告诉DHT11:“我要读数据了,你准备好。”
- 从机响应信号:DHT11检测到开始信号后,会先将总线拉低80µs作为应答,再拉高80µs,表示它准备好了要发送数据。
- 数据传输:随后,DHT11开始发送40bit数据。每一位数据都以一个50µs的低电平起始位开始,随后的高电平持续时间决定了数据是0还是1:
- ‘0’ bit:高电平持续约26-28µs。
- ‘1’ bit:高电平持续约70µs。 因此,核心在于精确测量这个高电平的持续时间。在micro:bit这种级别的MCU上,我们需要用微秒级的延时函数和引脚状态读取来实现。
校验和:校验和是前四个字节(湿度整数、湿度小数、温度整数、温度小数)相加后的低8位。接收完数据后,必须计算校验和并与接收到的第五个字节比较,如果不匹配,说明本次通信数据有误,应该丢弃。
为什么读取数据常为0?这是网络热词中“stm32f103c8t6读取dht11数据为0”问题的核心。通常有以下几个原因:
- 时序不满足:开始信号或读取位数据的延时时间不精确,特别是从机响应后的等待时间或位数据高电平的判断阈值设置不当。
- 上拉电阻缺失:总线处于浮空状态,电平不稳定。
- 电源问题:传感器供电不足或电压不稳。确保VCC电压在范围内,且电流足够(DHT11工作电流约2.5mA)。
- 接线错误或接触不良:这是最容易被忽略的硬件问题。
- 代码逻辑错误:例如没有正确释放总线、没有将引脚模式在输出和输入之间正确切换。
3. 硬件连接与电路搭建实操
理解了原理,动手连接就很简单了。这里我提供两种连接方案:一种使用带内置上拉电阻的DHT11模块(最推荐),另一种使用裸传感器。
3.1 方案一:使用集成模块(推荐给初学者)
这是最省事、成功率最高的方法。市面上常见的蓝色DHT11模块通常已经集成了上拉电阻和电源滤波电容。
所需材料:
- BBC micro:bit 一块
- DHT11温湿度传感器模块(蓝色板) 一个
- 公对母杜邦线 3根
连接步骤:
- 将micro:bit的3.3V引脚用杜邦线连接到DHT11模块的VCC引脚。
- 将micro:bit的GND引脚用杜邦线连接到DHT11模块的GND引脚。
- 将micro:bit的任意一个数字IO口(例如我选择P0)用杜邦线连接到DHT11模块的DATA引脚。 这就完成了!模块上的“板载上拉电阻”已经帮我们处理了最易错的一环。
3.2 方案二:使用裸传感器(深入理解)
如果你想更底层地理解电路,或者手头只有裸传感器,可以按此方案操作。
所需材料:
- BBC micro:bit 一块
- DHT11传感器(裸体) 一个
- 公对母杜邦线 4根
- 4.7KΩ 或 10KΩ 电阻 一个(用作上拉电阻)
- 面包板 一块(方便连接)
连接步骤:
- 在面包板上,将micro:bit的3.3V连接到电源正极轨,GND连接到电源负极轨。
- 将DHT11的VCC引脚连接到正极轨,GND引脚连接到负极轨。
- 将DHT11的DATA引脚连接到micro:bit的P0引脚。
- 关键一步:将一个4.7KΩ电阻的一端连接到正极轨(3.3V),另一端连接到DATA引脚所在的线上。这个电阻就是上拉电阻,确保总线在空闲时保持高电平。 这种连接方式让你对整个单总线电路的构成一目了然。
注意:无论哪种方案,接线务必牢固。接触不良是导致通信失败、数据乱跳的罪魁祸首之一。建议使用面包板或焊接,避免杜邦线虚接。
4. 软件实现:从MakeCode图形化到Python代码解析
编程环境我们选择微软的MakeCode for micro:bit在线编辑器。它支持图形化积木块和JavaScript代码,对于进阶用户,我们也可以切换到Python模式进行更底层的控制。我将分别讲解两种方式。
4.1 使用MakeCode图形化编程(快速上手)
MakeCode的扩展库生态非常丰富,有社区贡献的DHT11驱动积木,极大简化了操作。
步骤:
- 打开 MakeCode for micro:bit 网站,新建一个项目。
- 点击“高级” -> “扩展”,在搜索框中输入“dht11”,选择“dht11/dht22”这个扩展并添加。
- 添加后,左侧积木区会出现“DHT11”类别。主要会用到两个积木:
从引脚 P0 读取 DHT11 温湿度值:这是一个命令积木,执行一次读取操作。DHT11 温度(℃)和DHT11 湿度(%):这是两个报告积木,用于获取最近一次成功读取的温度和湿度值。
- 编写逻辑:我们通常会在“无限循环”中,每隔一段时间(如2秒)读取一次传感器,然后将数据显示在LED屏上。由于屏幕小,我们可以用“显示数字”积木轮流显示温度和湿度,中间用暂停隔开。
图形化代码示例逻辑:
当开机时 设置数字引脚 P0 为输入模式(有些扩展库要求) 无限循环 从引脚 P0 读取 DHT11 温湿度值 如果读取成功 则 显示字符串“T:” 显示数字 DHT11 温度(℃) 暂停 1000 毫秒 显示字符串“H:” 显示数字 DHT11 湿度(%) 暂停 1000 毫秒 否则 显示图标“X” (表示读取失败) 暂停 2000 毫秒 (等待下一次读取)这种方式非常简单,但封装度太高,不利于我们理解底层通信和进行错误处理。
4.2 使用MicroPython编程(推荐进阶,掌握核心)
我强烈建议使用Python模式来编写。micro:bit的MicroPython固件对硬件控制能力很强,我们可以自己实现DHT11的驱动,从而完全掌控通信过程。
步骤:
- 在MakeCode编辑器中,点击右上角齿轮图标,选择“Python”,切换到Python代码编辑器。
- 我们将编写一个完整的、带错误处理和校验的DHT11驱动函数。
核心代码解析与实现:
首先,我们需要导入必要的模块,并定义引脚和常量。
from microbit import * import utime # 配置 DHT_PIN = pin0 # 假设数据线接在P0接下来是核心的read_dht11函数。这个函数将返回一个元组(temperature, humidity),如果失败则返回(None, None)。
def read_dht11(): # 1. 主机发送开始信号 DHT_PIN.write_digital(0) # 拉低总线 sleep_ms(18) # 保持低电平至少18ms DHT_PIN.write_digital(1) # 拉高总线 utime.sleep_us(20) # 保持高电平20-40us,这里取20us # 2. 准备接收,将引脚设置为输入,由上拉电阻拉高 DHT_PIN.set_pull(DHT_PIN.PULL_UP) # 等待从机响应低电平 if wait_pin_state(0, 100) is False: # 100us内没等到低电平 return None, None # 等待从机响应高电平 if wait_pin_state(1, 100) is False: # 100us内没等到高电平 return None, None # 3. 接收40位数据 data = [] for i in range(40): # 等待每个位开始的50us低电平结束 if wait_pin_state(0, 60) is False: return None, None # 测量高电平持续时间 t = pulse_width_us(1, 100) # 测量高电平,超时100us if t < 0: # 超时 return None, None # 根据高电平时间判断是0还是1 data.append(1 if t > 40 else 0) # 阈值取40us左右,介于26-28和70之间 # 4. 解析数据字节 humidity_high = bits_to_byte(data[0:8]) humidity_low = bits_to_byte(data[8:16]) temp_high = bits_to_byte(data[16:24]) temp_low = bits_to_byte(data[24:32]) checksum = bits_to_byte(data[32:40]) # 5. 校验 if (humidity_high + humidity_low + temp_high + temp_low) & 0xFF != checksum: return None, None # DHT11湿度小数位常为0,温度小数位也常为0 humidity = humidity_high + humidity_low / 10.0 temperature = temp_high + temp_low / 10.0 return temperature, humidity # 辅助函数1:等待引脚达到指定状态,带超时 def wait_pin_state(state, timeout_us): start = utime.ticks_us() while DHT_PIN.read_digital() != state: if utime.ticks_diff(utime.ticks_us(), start) > timeout_us: return False return True # 辅助函数2:测量引脚高电平脉冲宽度,带超时 def pulse_width_us(state, timeout_us): start = utime.ticks_us() while DHT_PIN.read_digital() == state: if utime.ticks_diff(utime.ticks_us(), start) > timeout_us: return -1 return utime.ticks_diff(utime.ticks_us(), start) # 辅助函数3:将位列表转换为一个字节 def bits_to_byte(bits): byte = 0 for bit in bits: byte = (byte << 1) | bit return byte主循环与显示逻辑:
while True: temp, humi = read_dht11() if temp is not None and humi is not None: # 在LED屏上滚动显示,信息更清晰 display.scroll("T:{:.1f}C".format(temp), delay=80, wait=False) sleep(500) display.scroll("H:{:.1f}%".format(humi), delay=80) else: display.show(Image.SAD) # 读取失败显示哭脸 sleep(2000) # 每2秒读取一次这段代码完整实现了DHT11协议,包含了超时判断和校验和验证,健壮性远高于简单的图形化模块。你可以清晰地看到“开始信号”、“等待响应”、“逐位读取”、“数据解析”的完整流程。
5. 深度调试与常见问题排查指南
即使代码和接线都正确,在实际操作中你仍可能遇到问题。下面是我总结的排查清单,按照从易到难的顺序进行。
5.1 现象:LED屏无显示或一直显示错误图标(如“X”或哭脸)
排查步骤:
- 检查电源:用万用表测量DHT11的VCC和GND之间电压,确保在3.2V-3.4V之间。micro:bit的USB口供电能力足够,但劣质USB线可能导致电压跌落。
- 检查接线:这是最高频的问题。逐根捏紧杜邦线与micro:bit金手指和传感器引脚的连接处。强烈建议:将杜邦线直接插在micro:bit的引脚上,而不是通过扩展板,以排除扩展板接触不良的可能。
- 检查引脚定义:确认代码中的
DHT_PIN = pin0与你实际连接的物理引脚(P0)一致。micro:bit的引脚编号印在板子背面。 - 检查上拉电阻:如果使用裸传感器,确认4.7KΩ上拉电阻已正确连接在DATA线和3.3V之间。可以用万用表测量DATA引脚在空闲时的电压,应该是稳定的3.3V左右,如果电压很低或飘忽不定,说明上拉电阻没接好或电阻值太大。
5.2 现象:能显示数据,但数值固定为0、255或明显错误(如湿度95%不变)
排查步骤:
- 校验和错误:首先在代码中打印出接收到的5个原始字节和计算出的校验和。如果校验和不匹配,说明数据传输过程受到干扰,数据整体错误。这通常指向时序问题或电气干扰。
- 时序问题:这是“stm32f103c8t6读取dht11数据为0”类问题的核心。MicroPython的
utime.sleep_us()和utime.ticks_us()函数精度有限,且会被中断影响。可以尝试:- 调整阈值:在判断数据位0/1时,代码中用的阈值是40µs。你可以尝试微调这个值,比如调整到35µs或45µs,看看数据是否恢复正常。
- 增加延时:在主机发送开始信号后,释放总线并切换到输入模式后,可以增加一个短暂的等待(如
utime.sleep_us(5)),再开始检测从机响应。给传感器一个更稳定的准备时间。 - 关闭中断:在读取DHT11的整个关键时序期间,可以尝试关闭全局中断,但MicroPython标准库不直接支持,操作复杂,一般不推荐。
- 传感器损坏或型号不符:DHT11有几种封装,确保你使用的是正品。用万用表测量传感器VCC和GND之间的电阻,不应短路。尝试更换一个传感器测试。
- 电源噪声:在DHT11的VCC和GND之间并联一个100nF(0.1uF)的陶瓷电容,可以非常有效地滤除电源线上的高频噪声,这是提升通信稳定性的经典做法,成本极低但效果显著。
5.3 现象:数据偶尔跳变或读取间隔必须很长
排查步骤:
- 遵守传感器休息时间:DHT11两次测量之间需要至少1秒的间隔。如果你在循环中频繁调用读取函数(比如间隔小于1秒),会导致读取失败或返回旧数据。确保主循环中的
sleep(2000)或类似延时存在且不少于1000毫秒。 - 电气干扰:确保信号线(DATA)不要与电机、继电器等高功率器件线路平行走线。如果无法避免,可以使用双绞线或屏蔽线。
- 代码健壮性:确保你的
read_dht11函数在每次调用时,都严格遵循“输出模式发起信号->输入模式接收数据”的引脚模式切换流程。残留的电平状态可能影响下一次通信。
6. 功能扩展与项目优化思路
一个基础的温湿度计已经完成了,但我们可以让它变得更聪明、更有用。
6.1 增加显示模式与用户交互
利用micro:bit的A/B按钮,我们可以增加更多功能:
- 模式切换:按A键切换显示温度(℃),按B键切换显示湿度(%),长按A键切换温度单位(℃/℉)。
- 数据显示优化:由于LED屏小,可以设计更紧凑的显示方式。例如,用点阵绘制一个简易的温度计图标和湿度水滴图标,配合数字。
- 历史记录:在内存中记录一段时间内的最高/最低温湿度,并通过特定按钮组合(如同时按下A+B)来显示。
6.2 数据记录与离线分析
虽然micro:bit本身存储空间有限,但我们可以实现简单的数据记录。
- 间隔记录:每小时记录一次数据到micro:bit的“文件系统”中(需要启用MicroPython的文件支持)。
- 数据导出:通过USB连接电脑,将记录的数据文件复制出来,用Excel或Python进行图表分析,观察一天内的温湿度变化趋势。
6.3 阈值报警功能
这是一个非常实用的功能。
- 声光报警:当温度超过30℃或湿度低于30%时,让micro:bit板载的蜂鸣器(通过引脚P0模拟,需外接无源蜂鸣器)发出警报声,同时LED屏幕闪烁特定图案。
- 代码实现:在主循环中,读取到数据后,加入
if判断语句,触发music.play()和display.show()报警序列。
6.4 迈向物联网(进阶)
如果你想将数据上传到网络,micro:bit也完全有能力。
- 蓝牙传输:利用micro:bit内置的蓝牙功能,编写一个手机App(例如用MIT App Inventor)来接收并显示实时温湿度数据。
- 外接Wi-Fi/射频模块:通过串口连接ESP8266等Wi-Fi模块,将数据发送到MQTT服务器(如ThingsBoard、Home Assistant)或云平台(需注意网络访问合规性),实现远程监控。这需要更复杂的硬件连接和编程,但可玩性极高。
通过这个从硬件原理到软件实现,再到调试排错和功能扩展的完整流程,你收获的不仅仅是一个能显示数字的小装置,更是一套应对嵌入式传感器项目的通用方法论。下次当你遇到其他I2C、SPI传感器时,你会发现思路是相通的:理解协议、确保电气连接正确、编写稳健的驱动、处理异常情况。这个小巧的micro:bit温湿度计项目,正是打开嵌入式世界大门的一把绝佳钥匙。