"""
红外语音控制管家 - ESP32-S3 开发板
=====================================
引脚接线 (直接在板子上接好这些线即可):
红外接收头 (VS1838B):
OUT -> GPIO33
VCC -> 3.3V
GND -> GND
无源蜂鸣器:
正极 -> GPIO18
负极 -> GND
OLED 显示屏 (SSD1306 I2C):
SDA -> GPIO21
SCL -> GPIO22
VCC -> 3.3V
GND -> GND
红外遥控器:使用 NEC 协议,地址 0x00
上:0x18 / 下:0x52 / 左:0x08 / 右:0x5A / OK:0x1C / 返回:0x45
=====================================
"""
from machine import Pin, PWM, I2C, Timer
import ssd1306
import time
# ========== 引脚定义(与上方接线一致) ==========
IR_PIN = 14
BUZZER_PIN = 18
OLED_SAO = 20
OLED_CS = 21
# 遥控器键值映射
KEY_MAP = {
0x18: 'UP',
0x52: 'DOWN',
0x08: 'LEFT',
0x5A: 'RIGHT',
0x1C: 'OK',
0x45: 'BACK'
}
# ========== NEC 红外解码类 ==========
class NEC_IR:
def __init__(self, pin):
self.pin = Pin(pin, Pin.IN)
self.edges = []
self.pin.irq(handler=self._cb, trigger=Pin.IRQ_FALLING | Pin.IRQ_RISING)
def _cb(self, p):
self.edges.append(time.ticks_us())
if len(self.edges) > 200:
self.edges = self.edges[-100:]
def available(self):
return len(self.edges) >= 68
def decode(self):
if not self.available():
return None
edges = self.edges.copy()
self.edges.clear()
# 引导码检查
if not (8000 < time.ticks_diff(edges[1], edges[0]) < 10000):
return None
if not (4000 < time.ticks_diff(edges[2], edges[1]) < 5000):
return None
bits = []
for i in range(3, 67, 2):
low = time.ticks_diff(edges[i], edges[i-1])
high = time.ticks_diff(edges[i+1], edges[i])
if 400 < low < 800:
bits.append(1 if high > 1200 else 0)
else:
return None
if len(bits) < 32:
return None
addr = sum(bits[i] << i for i in range(8))
addr_inv = sum(bits[8+i] << i for i in range(8))
cmd = sum(bits[16+i] << i for i in range(8))
cmd_inv = sum(bits[24+i] << i for i in range(8))
if (addr ^ 0xFF) != addr_inv or (cmd ^ 0xFF) != cmd_inv:
return None
return (addr, cmd)
# ========== 蜂鸣器驱动 ==========
class Buzzer:
def __init__(self, pin):
self.pwm = PWM(Pin(pin), freq=1000, duty=0)
self.timer = Timer(-1)
def beep(self, freq=2000, duration=100):
self.pwm.freq(freq)
self.pwm.duty(512)
self.timer.deinit()
self.timer.init(mode=Timer.ONE_SHOT, period=duration,
callback=lambda t: self.pwm.duty(0))
# ========== OLED 菜单系统 ==========
class Menu:
def __init__(self, oled):
self.oled = oled
self.state = 'main'
self.main_items = ["TV 控制", "空调控制", "灯光控制", "设置"]
self.main_cur = 0
self.sub_items = []
self.sub_cur = 0
def draw(self):
self.oled.fill(0)
if self.state == 'main':
self.oled.text("-- 主菜单 --", 0, 0, 1)
for i, name in enumerate(self.main_items):
prefix = "> " if i == self.main_cur else " "
self.oled.text(f"{prefix}{name}", 0, (i+1)*12+2, 1)
elif self.state == 'sub':
title = self.main_items[self.main_cur]
self.oled.text(f"-- {title} --", 0, 0, 1)
if self.main_cur == 0:
subs = ["音量+", "音量-", "静音"]
elif self.main_cur == 1:
subs = ["开/关", "温度+", "温度-"]
elif self.main_cur == 2:
subs = ["开灯", "关灯", "调亮", "调暗"]
else:
subs = ["学习红外", "恢复默认", "关于"]
self.sub_items = subs
for i, name in enumerate(subs):
prefix = "> " if i == self.sub_cur else " "
self.oled.text(f"{prefix}{name}", 0, (i+1)*12+2, 1)
self.oled.show()
def handle(self, key):
if key is None:
return False
if self.state == 'main':
if key == 'UP':
self.main_cur = (self.main_cur - 1) % len(self.main_items)
elif key == 'DOWN':
self.main_cur = (self.main_cur + 1) % len(self.main_items)
elif key == 'OK':
self.state = 'sub'
self.sub_cur = 0
else:
return False
return True
elif self.state == 'sub':
if key == 'UP':
self.sub_cur = (self.sub_cur - 1) % len(self.sub_items)
elif key == 'DOWN':
self.sub_cur = (self.sub_cur + 1) % len(self.sub_items)
elif key == 'OK':
print("执行命令:", self.sub_items[self.sub_cur])
buzzer.beep(1500, 50) # 执行反馈音
# 在这里添加实际控制代码
elif key == 'BACK':
self.state = 'main'
self.sub_cur = 0
else:
return False
return True
return False
# ========== 初始化硬件 ==========
i2c = I2C(0, scl=Pin(OLED_SCL), sda=Pin(OLED_SDA))
oled = ssd1306.SSD1306_I2C(128, 64, i2c)
ir = NEC_IR(IR_PIN)
buzzer = Buzzer(BUZZER_PIN)
menu = Menu(oled)
menu.draw()
last_key_time = 0
last_key = None
# ========== 主循环 ==========
while True:
cmd = ir.decode()
if cmd:
addr, code = cmd
key = KEY_MAP.get(code)
if key:
now = time.ticks_ms()
# 简单消抖:同一个键 200ms 内不重复触发
if key != last_key or time.ticks_diff(now, last_key_time) > 200:
buzzer.beep(2000, 30) # 按键提示音
menu.handle(key)
menu.draw()
last_key_time = now
last_key = key
time.sleep_ms(10)Loading
esp32-s3-devkitc-1
esp32-s3-devkitc-1