保姆级教程—ESP32链接温度传感器和屏幕,实现温度的实时显示
接线:
-
显示屏接线:
- 确认 SPI 引脚连接:SCK=18, MOSI=23, MISO=19
- 确认控制引脚:RESET=15, DC=2, CS=5, BACKLIGHT=21
- 检查显示屏供电是否正常(3.3V)
-
DS18B20 接线:
- 确认 DATA 引脚连接到 GPIO4 并接 4.7kΩ 上拉电阻
- 确认 VCC 和 GND 连接正确
- 尝试更换传感器或上拉电阻
下面是相关代码:
from machine import Pin, SoftSPI, RTC
import st7789
import vga1_16x32_1 as font
import time
# 屏幕参数配置
SCREEN_WIDTH = 240
SCREEN_HEIGHT = 240
ROTATION = 0 # 旋转方向:0=默认,1=90度,2=180度,3=270度
# 温度传感器配置(软件模拟单总线)
DS18B20_PIN = 4 # DS18B20数据引脚连接到ESP32的GPIO4
class DS18B20:
"""纯软件实现DS18B20温度传感器驱动"""
def __init__(self, pin):
self.pin = Pin(pin, Pin.OUT)
def _write_bit(self, bit):
"""写一位数据到DS18B20"""
self.pin(0)
# 拉低总线至少1us
time.sleep_us(1)
self.pin(bit)
# 保持位状态60-120us
time.sleep_us(60)
self.pin(1)
time.sleep_us(1)
def _read_bit(self):
"""从DS18B20读一位数据"""
self.pin(0)
time.sleep_us(1)
self.pin(1)
time.sleep_us(1)
# 读取总线状态
bit = self.pin()
time.sleep_us(60)
return bit
def _write_byte(self, byte):
"""写一个字节到DS18B20"""
for i in range(8):
self._write_bit(byte & 0x01)
byte >>= 1
def _read_byte(self):
"""从DS18B20读一个字节"""
byte = 0
for i in range(8):
byte >>= 1
if self._read_bit():
byte |= 0x80
return byte
def reset(self):
"""重置DS18B20"""
self.pin(0)
# 拉低总线至少480us
time.sleep_us(500)
self.pin(1)
time.sleep_us(60)
# 等待DS18B20响应
response = self.pin()
time.sleep_us(440)
return not response # 返回True表示设备响应
def read_temp(self):
"""读取温度值"""
if not self.reset():
print("DS18B20未响应")
return None
self._write_byte(0xCC) # 跳过ROM命令
self._write_byte(0x44) # 启动温度转换
time.sleep_ms(750) # 等待转换完成
if not self.reset():
return None
self._write_byte(0xCC) # 跳过ROM命令
self._write_byte(0xBE) # 读取暂存寄存器
# 读取温度数据(低字节在前,高字节在后)
low = self._read_byte()
high = self._read_byte()
# 组合温度值(16位有符号数)
temp = (high << 8) | low
# 转换为浮点数(0.5℃精度)
return round((temp * 0.0625), 1)
# 颜色定义(RGB565格式)
def color565(r, g, b):
"""将RGB888颜色转换为RGB565格式"""
return (r & 0xf8) << 8 | (g & 0xfc) << 3 | b >> 3
# 预定义常用颜色
BLACK = color565(0, 0, 0)
WHITE = color565(255, 255, 255)
RED = color565(255, 0, 0)
GREEN = color565(0, 255, 0)
BLUE = color565(0, 0, 255)
YELLOW = color565(255, 255, 0)
CYAN = color565(0, 255, 255)
MAGENTA = color565(255, 0, 255)
ORANGE = color565(255, 165, 0) # 新增橙色用于温度显示
class DisplayController:
"""ST7789显示屏控制器"""
def __init__(self):
"""初始化显示屏控制器"""
self.tft = None
self.backlight = None
self.rtc = RTC() # 初始化RTC
self.last_time = (0, 0, 0, 0, 0, 0) # 记录上次显示的 时,分,秒,年,月,日
self.last_temp = -99.9 # 记录上次显示的温度
self.ds18b20 = DS18B20(DS18B20_PIN) # 初始化DS18B20
def initialize(self):
"""初始化SPI总线和显示屏"""
try:
# 初始化SPI总线
spi = SoftSPI(
baudrate=40000000,
polarity=1,
phase=0,
sck=Pin(18),
mosi=Pin(23),
miso=Pin(19)
)
# 初始化控制引脚
reset = Pin(15, Pin.OUT)
dc = Pin(2, Pin.OUT)
cs = Pin(5, Pin.OUT)
self.backlight = Pin(21, Pin.OUT)
# 初始化ST7789显示屏
self.tft = st7789.ST7789(
spi,
SCREEN_WIDTH,
SCREEN_HEIGHT,
reset=reset,
dc=dc,
cs=cs,
backlight=self.backlight,
rotation=ROTATION
)
# 开启背光
self.backlight.on()
# 设置RTC时间(可选,若需要准确时间可通过NTP同步)
# self.rtc.datetime((2025, 6, 29, 0, 12, 30, 0, 0)) # 年-月-日-周,时-分-秒-毫秒
return True
except Exception as e:
print(f"显示屏初始化失败: {str(e)}")
return False
def test_pattern(self):
"""显示测试图案"""
if not self.tft:
print("显示屏未初始化")
return
try:
# 填充背景
self.tft.fill(BLACK)
# 绘制边框
self.tft.rect(0, 0, SCREEN_WIDTH-1, SCREEN_HEIGHT-1, WHITE)
# 绘制对角线
self.tft.line(0, 0, SCREEN_WIDTH-1, SCREEN_HEIGHT-1, RED)
self.tft.line(0, SCREEN_HEIGHT-1, SCREEN_WIDTH-1, 0, GREEN)
# 绘制彩色矩形
self.tft.fill_rect(10, 10, 40, 40, BLUE)
self.tft.rect(10, 10, 40, 40, WHITE)
# 使用矩形替代圆形
self.tft.rect(SCREEN_WIDTH//2-30, SCREEN_HEIGHT//2-30, 60, 60, YELLOW)
# 显示文字
self.tft.text(font, "ST7789 Clock & Temp", 10, 100, WHITE)
self.tft.text(font, f"{SCREEN_WIDTH}x{SCREEN_HEIGHT}", 10, 140, CYAN)
except Exception as e:
print(f"显示测试图案失败: {str(e)}")
def read_temperature(self):
"""读取DS18B20温度值"""
return self.ds18b20.read_temp()
def show_time_and_temp(self, update_all=False):
"""显示时间和温度(智能更新变化部分)"""
if not self.tft:
print("显示屏未初始化")
return
try:
# 获取当前时间
year, month, day, weekday, hour, minute, second, _ = self.rtc.datetime()
current_time = (hour, minute, second, year, month, day)
# 读取温度
temp = self.read_temperature()
if temp is not None and abs(temp - self.last_temp) > 0.1 or update_all:
need_update_temp = True
self.last_temp = temp
else:
need_update_temp = False
# 首次显示或强制更新时全屏绘制
if self.last_time == (0, 0, 0, 0, 0, 0) or update_all:
self._draw_full_screen(year, month, day, hour, minute, second, temp)
self.last_time = current_time
return
# 判断哪些部分需要更新
need_update_second = (current_time[2] != self.last_time[2])
need_update_minute = (current_time[1] != self.last_time[1])
need_update_hour = (current_time[0] != self.last_time[0])
need_update_date = (current_time[3:6] != self.last_time[3:6])
# 仅更新变化的部分
if need_update_date:
self._draw_date(year, month, day)
if need_update_hour or need_update_minute or need_update_second:
self._draw_time(hour, minute, second)
if need_update_temp:
self._draw_temperature(temp)
# 更新最后一次记录的时间
self.last_time = current_time
except Exception as e:
print(f"显示时间和温度失败: {str(e)}")
def _draw_full_screen(self, year, month, day, hour, minute, second, temp):
"""绘制完整屏幕内容(首次或强制更新时使用)"""
# 清屏
self.tft.fill(BLACK)
# 绘制日期(年月日)
date_str = f"{year:04d}-{month:02d}-{day:02d}"
date_width = len(date_str) * font.WIDTH
date_x = (SCREEN_WIDTH - date_width) // 2
date_y = (SCREEN_HEIGHT // 3) - font.HEIGHT
self.tft.text(font, date_str, date_x, date_y, WHITE)
# 绘制时间(时分秒)
time_str = f"{hour:02d}:{minute:02d}:{second:02d}"
time_width = len(time_str) * font.WIDTH
time_x = (SCREEN_WIDTH - time_width) // 2
time_y = (SCREEN_HEIGHT // 3) * 2 - font.HEIGHT
self.tft.text(font, time_str, time_x, time_y, WHITE)
# 绘制温度
temp_str = f"Temp: {temp}°C" if temp is not None else "Temp: --.-°C"
temp_width = len(temp_str) * font.WIDTH
temp_x = (SCREEN_WIDTH - temp_width) // 2
temp_y = (SCREEN_HEIGHT // 3) * 2 + 10
self.tft.text(font, temp_str, temp_x, temp_y, ORANGE)
def _draw_date(self, year, month, day):
"""仅更新日期部分"""
date_str = f"{year:04d}-{month:02d}-{day:02d}"
date_width = len(date_str) * font.WIDTH
date_x = (SCREEN_WIDTH - date_width) // 2
date_y = (SCREEN_HEIGHT // 3) - font.HEIGHT
# 先绘制黑色背景覆盖旧日期
self.tft.fill_rect(date_x, date_y, date_width, font.HEIGHT, BLACK)
# 再绘制新日期
self.tft.text(font, date_str, date_x, date_y, WHITE)
def _draw_time(self, hour, minute, second):
"""仅更新时间部分(时分秒)"""
time_str = f"{hour:02d}:{minute:02d}:{second:02d}"
time_width = len(time_str) * font.WIDTH
time_x = (SCREEN_WIDTH - time_width) // 2
time_y = (SCREEN_HEIGHT // 3) * 2 - font.HEIGHT
# 先绘制黑色背景覆盖旧时间
self.tft.fill_rect(time_x, time_y, time_width, font.HEIGHT, BLACK)
# 再绘制新时间
self.tft.text(font, time_str, time_x, time_y, WHITE)
def _draw_temperature(self, temp):
"""仅更新温度部分"""
temp_str = f"Temp: {temp}°C"
temp_width = len(temp_str) * font.WIDTH
temp_x = (SCREEN_WIDTH - temp_width) // 2
temp_y = (SCREEN_HEIGHT // 3) * 2 + 10
# 先绘制黑色背景覆盖旧温度
self.tft.fill_rect(temp_x, temp_y, temp_width, font.HEIGHT, BLACK)
# 再绘制新温度(橙色)
self.tft.text(font, temp_str, temp_x, temp_y, ORANGE)
def clear(self):
"""清屏"""
if self.tft:
self.tft.fill(BLACK)
def turn_off(self):
"""关闭显示屏和背光"""
if self.tft:
self.clear()
if self.backlight:
self.backlight.off()
def main():
"""主程序"""
display = DisplayController()
# 初始化显示屏
if not display.initialize():
print("显示屏初始化失败,程序退出")
return
try:
# 显示测试图案
display.test_pattern()
time.sleep(2)
# 清屏并显示时间和温度
display.clear()
display.show_time_and_temp(update_all=True) # 首次全屏绘制
print("时间和温度显示启动,按Ctrl+C退出")
last_second = -1
while True:
# 获取当前秒数
_, _, _, _, _, _, second, _ = display.rtc.datetime()
# 每秒更新一次显示
if second != last_second:
display.show_time_and_temp()
last_second = second
time.sleep(0.1) # 小延迟减少CPU占用
except KeyboardInterrupt:
# 清屏并关闭背光
display.turn_off()
print("程序已停止")
except Exception as e:
# 发生其他异常时关闭显示屏
print(f"发生错误: {str(e)}")
display.turn_off()
if __name__ == "__main__":
main()
其中有需要st7789.py和vga1_16x32.py,付费,请知晓
更多推荐



所有评论(0)