# ============================================
# 完整项目:NTC 温度监测器(带 OLED 显示和 LED 指示)
# 智能物联网设计与实验
# ============================================
# 功能:
# - 使用 ADC 读取 NTC 热敏电阻温度
# - 在 OLED 屏幕上实时显示温度
# - 显示 5 秒平均温度
# - LED 颜色指示:蓝色(<20°C),绿色(20-28°C),红色(>28°C)
# - 使用 Steinhart-Hart 方程进行精确温度转换
# ============================================
from machine import ADC, Pin, I2C
import math
import time
import ssd1306
# ========== OLED 显示屏设置 (I2C) ==========
i2c = I2C(0, sda=Pin(4), scl=Pin(5), freq=400000)
oled = ssd1306.SSD1306_I2C(128, 64, i2c)
# ========== LED 设置 ==========
led_red = Pin(3, Pin.OUT) # 红色 LED - 高温指示 (>28°C)
led_green = Pin(2, Pin.OUT) # 绿色 LED - 正常温度指示 (20-28°C)
led_blue = Pin(6, Pin.OUT) # 蓝色 LED - 低温指示 (<20°C)
# ========== NTC 热敏电阻设置 ==========
ntc_adc = ADC(Pin(26)) # GP26 = ADC0
# NTC 参数(与传感器配置匹配)
BETA = 3950 # B 常数(10K NTC 典型值为 3950K)
R_NOMINAL = 10000 # 标称温度下的电阻值 (10KΩ)
T_NOMINAL = 25 # 标称温度(摄氏度)
R_SERIES = 10000 # NTC 模块内部串联电阻 (10KΩ)
# ========== 温度计算函数 ==========
def calculate_temperature(adc_value):
"""
将 ADC 读数转换为摄氏度温度。
使用 B 参数方程(简化的 Steinhart-Hart 方程)。
电路:3.3V -- [R_SERIES] -- ADC -- [NTC] -- GND
"""
if adc_value <= 0 or adc_value >= 65535:
return None
# 从分压电路计算 NTC 电阻
r_ntc = R_SERIES * adc_value / (65535 - adc_value)
# Steinhart-Hart 方程(B 参数版本)
# 1/T = 1/T0 + (1/B) * ln(R/R0)
t_kelvin = 1.0 / (
1.0 / (T_NOMINAL + 273.15) +
(1.0 / BETA) * math.log(r_ntc / R_NOMINAL)
)
# 开尔文转摄氏度
t_celsius = t_kelvin - 273.15
return t_celsius
# ========== 基于温度的 LED 控制 ==========
def update_led_indicator(temp_c):
"""根据温度范围点亮相应的 LED"""
# 先关闭所有 LED
led_red.off()
led_green.off()
led_blue.off()
if temp_c is None:
return
if temp_c < 20.0:
led_blue.on() # 寒冷:蓝色 LED
elif temp_c <= 28.0:
led_green.on() # 正常:绿色 LED
else:
led_red.on() # 炎热:红色 LED
# ========== 显示更新函数 ==========
def update_display(current_temp, avg_temp):
"""更新 OLED 屏幕显示温度信息"""
oled.fill(0) # 清屏
# 标题
oled.text("NTC 温度监测", 0, 0)
oled.text("===============", 0, 10)
# 当前温度
if current_temp is not None:
oled.text("当前: {:.1f} C".format(current_temp), 0, 22)
else:
oled.text("当前: --- C", 0, 22)
# 平均温度
if avg_temp is not None:
oled.text("平均(5秒): {:.1f} C".format(avg_temp), 0, 34)
else:
oled.text("平均(5秒): --- C", 0, 34)
# 温度状态
if current_temp is not None:
if current_temp < 20:
status = "状态: 寒冷"
elif current_temp <= 28:
status = "状态: 正常"
else:
status = "状态: 炎热"
oled.text(status, 0, 46)
# LED 指示图例
oled.text("LED: 蓝<20 绿20-28 红>28", 0, 56)
oled.show()
# ========== 移动平均滤波(平滑数据) ==========
class MovingAverage:
def __init__(self, window_size=5):
self.window_size = window_size
self.values = []
self.sum = 0.0
def add_value(self, value):
if value is None:
return None
self.values.append(value)
self.sum += value
if len(self.values) > self.window_size:
self.sum -= self.values.pop(0)
return self.sum / len(self.values)
def get_average(self):
if not self.values:
return None
return self.sum / len(self.values)
# ========== 主程序 ==========
print("=" * 45)
print(" NTC 温度监测器已启动")
print("=" * 45)
print(" ADC | 电阻(Ω) | 温度(℃) | LED 状态")
print("-" * 45)
# 初始化移动平均(5 个样本 ≈ 5 秒,间隔 1 秒)
temp_avg = MovingAverage(window_size=5)
# 显示启动画面
oled.fill(0)
oled.text("NTC 监测器", 20, 20)
oled.text("启动中...", 30, 35)
oled.text("v1.0", 50, 50)
oled.show()
time.sleep(2)
# 主循环
while True:
# 读取 ADC 值 (0-65535)
adc_val = ntc_adc.read_u16()
# 计算温度
temperature = calculate_temperature(adc_val)
# 更新移动平均
avg_temp = temp_avg.add_value(temperature)
# 控制 LED 指示灯
update_led_indicator(temperature)
# 更新 OLED 显示
update_display(temperature, avg_temp)
# 输出到串口控制台,便于调试
if temperature is not None:
# 计算电阻值用于显示
if adc_val > 0 and adc_val < 65535:
r_ntc = R_SERIES * adc_val / (65535 - adc_val)
else:
r_ntc = 0
# 确定 LED 状态
if temperature < 20:
led_state = "蓝色"
elif temperature <= 28:
led_state = "绿色"
else:
led_state = "红色"
print(" {:5d} | {:10.0f} | {:6.1f} | {}".format(
adc_val, r_ntc, temperature, led_state
))
else:
print(" {:5d} | {:10s} | {:6s} | {}".format(
adc_val, "无效", "---", "关闭"
))
# 等待 1 秒再进行下一次读取
time.sleep(1)
# ============================================
# 扩展练习(在此项目基础上扩展):
# 1) 添加蜂鸣器,温度超过 35°C 时报警
# 2) 记录最高/最低温度并显示在 OLED 上
# 3) 添加按钮,用于切换摄氏度和华氏度显示
# 4) 使用 Pico W 通过 MQTT 将数据发送到云端
# ============================================