1. 先搞清楚“模拟热敏传感器”在掌控板上到底怎么用
看到“模拟热敏传感器”和“掌控板”这两个词,很多刚接触硬件编程的朋友可能会有点懵。这到底是讲一个特定的传感器模块,还是在讲掌控板自带的某个功能?其实,在掌控板(通常指基于ESP32的掌控板)的语境下,“模拟热敏传感器”更多是指利用掌控板自带的模拟输入引脚,来读取一个热敏电阻(NTC或PTC)的电压值,从而计算出温度的一种方法。它不是一个现成的、插上就能用的“传感器模块”,而是一个需要你动手连接电路并编程实现的功能方案。
所以,这篇文章的核心是解决这个问题:当你手头只有一块掌控板、一个热敏电阻和几个基础元件时,如何通过编程,把它变成一个能测量温度的设备。这非常适合中小学创客教育、物联网入门项目,或者任何想低成本实现温度监测场景的爱好者。最关键的能力不是调用某个库,而是理解“模拟信号采集”到“物理量转换”的完整链路。
如果你期待的是直接调用一个DHT.read()那样的函数,那可能会失望。但如果你愿意花半小时,搞清楚从硬件连接到代码计算的全过程,那么你获得的将是一个可定制、可调试的底层技能,以后面对光敏、压力等任何模拟量传感器,思路都是相通的。
2. 动手前的准备:硬件连接与原理简述
在写代码之前,必须先把电路搭对。这是所有硬件项目的第一步,也是最容易出错的一步。
2.1 所需材料清单
你需要准备以下东西,大部分在入门套件里都能找到:
- 掌控板:一块,确保电量充足或连接USB供电。
- 热敏电阻(NTC 10K):这是最常用的型号,它的电阻值会随温度升高而降低(负温度系数)。
- 一个固定电阻(10KΩ):精度1%的金属膜电阻最好,普通5%的碳膜电阻也能用。
- 面包板和若干杜邦线:用于连接电路。
- USB数据线:用于供电和上传程序。
2.2 电路连接原理图(分压电路)
我们利用的是一个经典的分压电路。不要把热敏电阻直接接到模拟引脚上,那样读不到有用的值。
连接方式如下:
- 将热敏电阻的一端连接到掌控板的3.3V引脚。
- 将热敏电阻的另一端,与那个10KΩ的固定电阻的一端连接在一起。这个连接点,我们称之为“信号点”。
- 将这个“信号点”用杜邦线连接到掌控板的任意一个模拟输入引脚,例如P0、P1或P2(具体看你的掌控板型号,通常这些引脚旁会标有“ADC”字样)。
- 将固定电阻的另一端连接到掌控板的GND(地)引脚。
这样,热敏电阻(Rt)和固定电阻(R_fixed)就构成了一个分压器。模拟引脚测量的是“信号点”的电压。根据欧姆定律,这个电压V_signal = 3.3V * [R_fixed / (Rt + R_fixed)]。当温度变化导致Rt阻值变化时,V_signal也随之变化。我们通过ADC(模数转换器)读取这个电压的数字化值,就能反推出Rt,进而计算出温度。
注意:务必确认你的掌控板模拟输入引脚允许的电压范围。绝大多数掌控板的ADC引脚最大输入电压就是3.3V,所以从3.3V取电是安全的。切勿连接到5V!
2.3 软件环境准备
编程环境通常有两种选择:
- mPython X:官方图形化/代码编程软件,对初学者友好,自带掌控板库。
- Arduino IDE:需要安装ESP32开发板支持,适合喜欢纯代码编程、需要更底层控制的学习者。
本文以mPython X的代码模式为例进行讲解,因为它平衡了易用性和灵活性。请确保你已安装好mPython X,并能成功连接和上传程序到你的掌控板。
3. 从读取原始值到计算出温度:代码全流程拆解
环境准备好后,我们分三步走:读原始值、转换电阻、计算温度。
3.1 第一步:读取模拟原始值(ADC Raw Value)
首先,我们写一个最简单的程序,看看硬件连接是否正确。
from mpython import * # 导入掌控板库 import time # 初始化,假设热敏电阻信号线接在P0引脚 sensor_pin = MPythonPin(0, PinMode.ANALOG) while True: # 读取模拟值,范围通常是0-4095(12位ADC) adc_value = sensor_pin.read_analog() print("ADC Raw Value:", adc_value) time.sleep(1) # 每秒读一次将代码上传到掌控板,打开串口监视器(波特率115200)。你应该能看到一串不断变化的数字。
- 用手捏住热敏电阻,这个数值应该会显著变化(温度升高,NTC阻值变小,分压点电压升高,ADC值变大)。
- 如果数值始终为0或4095:检查电路是否接通,特别是GND和3.3V。如果是0,可能是信号点对地短路或引脚接错;如果是4095(或接近),可能是信号点直接接到了3.3V。
- 如果数值变化很小或不变:检查热敏电阻和固定电阻的接点是否接触良好。
这一步的目的是验证硬件链路和ADC读取功能是正常的。不要跳过这一步直接去算温度。
3.2 第二步:将ADC值转换为电阻值
得到ADC原始值(adc_value)后,我们需要把它转换成热敏电阻当前的阻值(Rt)。
公式推导如下:
- ADC值对应的电压:
V_signal = (adc_value / 4095.0) * 3.3 - 根据分压公式:
V_signal = 3.3 * (R_fixed / (Rt + R_fixed)) - 联立两个公式,可以解出
Rt:Rt = R_fixed * (4095.0 / adc_value - 1)
这里有一个关键点:4095是ADC的最大理论值(12位分辨率,2^12 - 1)。但有些ESP32的ADC可能有非线性,在实际要求不高的场合可以先用这个值。如果追求精度,可能需要校准。
代码实现:
from mpython import * import time sensor_pin = MPythonPin(0, PinMode.ANALOG) R_fixed = 10000.0 # 固定电阻阻值,10KΩ while True: adc_value = sensor_pin.read_analog() # 防止除零错误,当adc_value接近0时,电阻非常大,可以视为开路 if adc_value < 1: adc_value = 1 # 计算热敏电阻当前阻值 Rt = R_fixed * (4095.0 / adc_value - 1.0) print("ADC:", adc_value, "Resistance (Ω):", Rt) time.sleep(1)现在,串口监视器输出的就是热敏电阻的实时阻值了。用手加热它,阻值应该下降。
3.3 第三步:利用Steinhart-Hart公式计算温度
得到阻值Rt后,最后一步就是转换为温度。对于NTC热敏电阻,最常用的方法是Steinhart-Hart方程。它的简化形式(B参数方程)对于大多数应用来说足够精确:
1/T = 1/T0 + (1/B) * ln(Rt/R0)
其中:
T是我们要计算的绝对温度(单位:开尔文,K)。T0是参考温度(通常是25°C,即298.15K)。R0是热敏电阻在T0温度下的阻值(我们的NTC 10K就是在25°C时为10KΩ)。B是热敏电阻的B值(一个关键参数!)。这个值在你的热敏电阻规格书或卖家提供的参数里找,常见的有3435、3950等。用错B值,计算结果会偏差很大。ln是自然对数。
计算步骤:
- 将已知的
R0、T0、B和计算出的Rt代入公式,算出1/T。 - 取倒数得到开尔文温度
T。 - 减去273.15,转换为摄氏温度
°C。
完整代码示例:
from mpython import * import time import math # 需要用到math.log计算自然对数 sensor_pin = MPythonPin(0, PinMode.ANALOG) # 硬件参数 R_fixed = 10000.0 # 固定电阻,10KΩ # 热敏电阻参数 (必须根据你的元件修改!) R0 = 10000.0 # 在T0温度下的阻值,10K T0 = 298.15 # 参考温度,25°C对应的开尔文温度 B = 3950.0 # B值,这里是3950K,请替换为你的热敏电阻的实际B值 while True: adc_value = sensor_pin.read_analog() if adc_value < 1: adc_value = 1 # 1. 计算当前电阻Rt Rt = R_fixed * (4095.0 / adc_value - 1.0) # 2. 使用Steinhart-Hart公式计算温度(开尔文) # 先计算1/T inv_T = 1.0/T0 + (1.0/B) * math.log(Rt/R0) # 再计算T,并转换为摄氏度 T_kelvin = 1.0 / inv_T T_celsius = T_kelvin - 273.15 # 3. 输出结果 print("ADC:{:4d} Rt:{:.0f}Ω Temp:{:.2f}°C".format(adc_value, Rt, T_celsius)) # 可选:在掌控板OLED屏幕上显示 oled.fill(0) # 清屏 oled.DispChar("Temp:", 0, 0) oled.DispChar("{:.1f}C".format(T_celsius), 50, 0) oled.show() time.sleep(2) # 每2秒更新一次上传这个代码后,你应该能在串口监视器和OLED屏上看到当前的环境温度了。用手捏住热敏电阻,温度读数应该会上升。
4. 关键参数校准与常见问题排查
代码跑起来只是第一步,要让测量相对准确,必须关注以下几个点。
4.1 核心参数B值与校准
B值不准是温度误差的最大来源。卖家给的B值可能是一个范围(如3950±1%),或者根本就是错的。如果你发现测得的室温与实际情况相差好几度,就需要校准。
简易校准方法:
- 准备一个你知道准确温度的环境(比如冰水混合物约0°C,室内用另一个靠谱的温度计测)。
- 将热敏电阻置于该环境,等待读数稳定。
- 记录下此时程序计算出的电阻值
Rt_measured。 - 倒推B值。将已知的
T_cal(单位°C,转换为开尔文T_cal_k)、R0、T0和Rt_measured代入公式:B_cal = (1/T_cal_k - 1/T0) / math.log(Rt_measured/R0)计算出的B_cal替换掉代码中的B,再测试另一个温度点(如体温),看是否更接近。
4.2 ADC非线性与参考电压
我们代码中假设ADC是完美的12位,且参考电压是精确的3.3V。实际上ESP32的内部ADC可能存在非线性和电压波动。对于要求不高的教学或定性观察项目,可以忽略。如果希望提升精度,可以考虑:
- 使用外部基准电压源:为ADC提供一个更稳定的参考电压,但这超出了基础教程范围。
- 软件滤波:对ADC采样值进行多次读取求平均,可以平滑随机噪声。
def read_adc_average(pin, times=10): sum_val = 0 for i in range(times): sum_val += pin.read_analog() time.sleep_ms(5) return sum_val / times
4.3 常见问题与排查清单
当你发现程序不工作时,按这个顺序查:
完全没有输出/板子没反应:
- 检查USB连接和供电。
- 检查mPython X是否选择了正确的串口。
- 检查代码是否有语法错误,上传是否成功。
ADC值始终为0、4095或一个不变的值:
- 0:信号点可能对地短路,或引脚配置错误(不是模拟输入模式)。检查面包线连接。
- 4095(或接近):信号点可能直接接到3.3V,或者热敏电阻开路(断了)。检查热敏电阻两端是否焊好。
- 不变的值:程序可能卡住了,重启板子。或者ADC引脚损坏(换一个引脚试试)。
温度值明显不准(如室温显示50°C):
- 首先检查B值:这是最可能的原因。确认你用的热敏电阻的B值,并用校准法修正。
- 检查R0和T0:确认
R0是25°C(T0=298.15)时的阻值。有些热敏电阻的标称阻值是25°C下的,有些是其他温度下的。 - 检查固定电阻值:用万用表量一下你的“10K”固定电阻,实际可能是9.8K或10.2K,将这个实际值代入
R_fixed。
温度读数跳动剧烈:
- 进行软件滤波(如上文的多次平均)。
- 检查电源是否稳定,尝试用电池或手机充电器给掌控板供电,排除电脑USB口噪声干扰。
- 热敏电阻本身对温度极其敏感,空气流动、手靠近都会引起变化,这是正常的。观察长期趋势而非瞬时值。
5. 项目扩展:从单点测量到实用小系统
掌握了基础测温后,你可以把它融入更实用的项目中,这才是学习的最终目的。
5.1 案例:简易温度报警器
结合掌控板的OLED、RGB灯和蜂鸣器,做一个超过阈值就报警的装置。
from mpython import * import time import math sensor_pin = MPythonPin(0, PinMode.ANALOG) R_fixed = 10000.0 R0 = 10000.0 T0 = 298.15 B = 3950.0 HIGH_TEMP_ALARM = 30.0 # 高温报警阈值 LOW_TEMP_ALARM = 10.0 # 低温报警阈值 def read_temperature(): adc_val = sensor_pin.read_analog() if adc_val < 1: adc_val = 1 Rt = R_fixed * (4095.0 / adc_val - 1.0) inv_T = 1.0/T0 + (1.0/B) * math.log(Rt/R0) return (1.0 / inv_T) - 273.15 while True: temp = read_temperature() # OLED显示 oled.fill(0) oled.DispChar("Temp: {:.1f}C".format(temp), 0, 0) # 报警逻辑 if temp > HIGH_TEMP_ALARM: oled.DispChar("TOO HOT!", 0, 16) rgb.fill((50, 0, 0)) # RGB灯亮红色 buzzer.freq(1000) # 蜂鸣器响 buzzer.duty(512) elif temp < LOW_TEMP_ALARM: oled.DispChar("TOO COLD!", 0, 16) rgb.fill((0, 0, 50)) # RGB灯亮蓝色 buzzer.freq(800) buzzer.duty(512) else: oled.DispChar("Normal", 0, 16) rgb.fill((0, 10, 0)) # RGB灯亮绿色 buzzer.duty(0) # 关闭蜂鸣器 oled.show() time.sleep(1)5.2 扩展思路:数据记录与物联网
- 数据记录:利用掌控板的Flash存储模块,定期将温度和时间戳保存下来,形成简单的数据日志。
- Wi-Fi上传:利用掌控板的Wi-Fi功能,将温度数据发送到物联网平台(如SIoT、EasyIoT等),实现远程监控。
- 多传感器融合:结合光敏传感器(同样用模拟引脚读取),做一个“温光监测站”,同时在OLED上显示温度和光照强度。
5.3 关于“模拟传感器”编程的通用思维
通过这个项目,你应该建立起处理模拟传感器的通用流程:
- 硬件:确定传感器是电阻型还是电压型,设计合适的分压或接口电路。
- 信号采集:使用MCU的ADC读取模拟电压值。
- 标定转换:根据传感器特性公式(查数据手册!),将原始ADC值转换为物理量(温度、光照、压力等)。这一步最关键,往往需要校准。
- 应用逻辑:基于转换后的物理量,编写你的业务逻辑(显示、报警、控制)。
我个人更建议,在尝试任何新的模拟传感器时,都先重复“读原始值 -> 观察变化 -> 查公式 -> 写转换 -> 校准”这个流程。把热敏电阻吃透,其他如光敏电阻、MQ系列气体传感器、弯折传感器等,都是换一个公式而已,硬件连接和编程思维完全一样。不要一上来就找现成的库,理解底层原理会让你在调试和优化时更有把握。