Implement dynamic hardware auto-detection in board_config.py to support both Hosyond and Waveshare RLCD boards out-of-the-box

This commit is contained in:
Adolfo Reyna
2026-06-17 22:35:47 -04:00
parent 75475723fa
commit c63712e3aa
5 changed files with 350 additions and 148 deletions
+73 -50
View File
@@ -1,6 +1,7 @@
import time
import machine
from machine import Pin, I2C, I2S
import board_config
class ES7210:
"""MicroPython driver for the ES7210 4-Channel Audio ADC (Microphone Array).
@@ -79,32 +80,41 @@ def record_audio(duration_seconds=5, filename='recording.pcm'):
duration_seconds (int): How long to record in seconds.
filename (str): Name of output raw PCM file on the device.
"""
# 1. Start I2C Control Bus (SDA=16, SCL=15)
i2c = I2C(0, sda=Pin(16), scl=Pin(15))
# 1. Use I2C Control Bus from board_config
i2c = board_config.i2c_bus
# 1a. Setup Master Clock (MCLK) on GPIO 4 using PWM (needed for ES8311 ADC)
mclk_pin = Pin(4, Pin.OUT)
mclk_pwm = machine.PWM(mclk_pin)
mclk_pwm.freq(6144000)
mclk_pwm.duty_u16(32768)
# 1a. Setup Master Clock (MCLK) using PWM if configured
mclk_pwm = None
if board_config.audio_mclk_pin is not None:
mclk_pin = Pin(board_config.audio_mclk_pin, Pin.OUT)
mclk_pwm = machine.PWM(mclk_pin)
mclk_pwm.freq(board_config.audio_mclk_freq)
mclk_pwm.duty_u16(32768)
# 2. Configure I2S Receiver
# Pins: sck=BCLK (GPIO 5), ws=WS/LRCK (GPIO 7), sd=DIN (GPIO 6)
i2s = I2S(1,
sck=Pin(5),
ws=Pin(7),
sd=Pin(6),
sck=Pin(board_config.audio_i2s_sck),
ws=Pin(board_config.audio_i2s_ws),
sd=Pin(board_config.audio_i2s_rx_sd),
mode=I2S.RX,
ibuf=16000,
rate=16000,
bits=16,
format=I2S.STEREO)
# 3. Wake up and configure the ES8311 codec chip
mic_adc = ES8311(i2c)
if not mic_adc.init(sample_rate=16000):
i2s.deinit()
return False
# 3. Wake up and configure the microphone chip (ES7210 vs ES8311)
if board_config.audio_mic_codec == "ES7210":
mic_adc = ES7210(i2c)
if not mic_adc.init(sample_rate=16000, bit_width=16):
i2s.deinit()
if mclk_pwm: mclk_pwm.deinit()
return False
else:
mic_adc = ES8311(i2c)
if not mic_adc.init(sample_rate=16000):
i2s.deinit()
if mclk_pwm: mclk_pwm.deinit()
return False
print(f"Recording {duration_seconds} seconds of audio...")
@@ -130,8 +140,15 @@ def record_audio(duration_seconds=5, filename='recording.pcm'):
return False
finally:
# Always release the I2S peripheral resources
i2s.deinit()
mclk_pwm.deinit()
try:
i2s.deinit()
except:
pass
if mclk_pwm:
try:
mclk_pwm.deinit()
except:
pass
print("I2S receiver and MCLK deinitialized.")
@@ -226,37 +243,40 @@ def play_tone(frequency=440, duration_ms=1000, volume=50):
print(f"Playing tone: {frequency}Hz for {duration_ms}ms (vol={volume})...")
# 1. Setup Master Clock (MCLK) on GPIO 4 using PWM
mclk_pin = Pin(4, Pin.OUT)
mclk_pwm = machine.PWM(mclk_pin)
mclk_pwm.freq(6144000)
mclk_pwm.duty_u16(32768)
# 1. Setup Master Clock (MCLK) using PWM if configured
mclk_pwm = None
if board_config.audio_mclk_pin is not None:
mclk_pin = Pin(board_config.audio_mclk_pin, Pin.OUT)
mclk_pwm = machine.PWM(mclk_pin)
mclk_pwm.freq(board_config.audio_mclk_freq)
mclk_pwm.duty_u16(32768)
# 2. Start I2C Control Bus (SDA=16, SCL=15)
i2c = I2C(0, sda=Pin(16), scl=Pin(15))
# 2. Use dynamic I2C Control Bus from board_config
i2c = board_config.i2c_bus
# 3. Initialize the ES8311 DAC
dac = ES8311(i2c)
if not dac.init(sample_rate=16000):
mclk_pwm.deinit()
if mclk_pwm: mclk_pwm.deinit()
return False
dac.set_volume(volume)
# 4. Configure I2S TX
# Pins: sck=BCLK (GPIO 5), ws=WS/LRCK (GPIO 7), sd=DOUT (GPIO 8)
i2s = I2S(1,
sck=Pin(5),
ws=Pin(7),
sd=Pin(8),
sck=Pin(board_config.audio_i2s_sck),
ws=Pin(board_config.audio_i2s_ws),
sd=Pin(board_config.audio_i2s_tx_sd),
mode=I2S.TX,
ibuf=8000,
rate=16000,
bits=16,
format=I2S.STEREO)
# 5. Enable Speaker Amplifier (GPIO 1, Active Low)
amp_pin = Pin(1, Pin.OUT, value=0)
# 5. Enable Speaker Amplifier
on_val = 0 if board_config.audio_amp_active_level == 0 else 1
off_val = 1 if board_config.audio_amp_active_level == 0 else 0
amp_pin = Pin(board_config.audio_amp_pin, Pin.OUT, value=on_val)
# 6. Generate sine wave cycle
# Approximate frequency to make integer number of samples per cycle
@@ -284,9 +304,9 @@ def play_tone(frequency=440, duration_ms=1000, volume=50):
# 7. Clean up
time.sleep_ms(100) # Let the remaining buffer play out
amp_pin.value(1) # Disable amp
amp_pin.value(off_val) # Disable amp
i2s.deinit()
mclk_pwm.deinit()
if mclk_pwm: mclk_pwm.deinit()
print("Tone playback complete.")
return True
@@ -344,39 +364,42 @@ def play_wav(filename, volume=50):
print(f"WAV Info: {sample_rate}Hz, {bits} bits, {'Mono' if channels == 1 else 'Stereo'}")
# 2. Setup Master Clock (MCLK) on GPIO 4 using PWM
mclk_pin = Pin(4, Pin.OUT)
mclk_pwm = machine.PWM(mclk_pin)
mclk_pwm.freq(6144000)
mclk_pwm.duty_u16(32768)
# 2. Setup Master Clock (MCLK) using PWM if configured
mclk_pwm = None
if board_config.audio_mclk_pin is not None:
mclk_pin = Pin(board_config.audio_mclk_pin, Pin.OUT)
mclk_pwm = machine.PWM(mclk_pin)
mclk_pwm.freq(board_config.audio_mclk_freq)
mclk_pwm.duty_u16(32768)
# 3. Start I2C Control Bus (SDA=16, SCL=15)
i2c = I2C(0, sda=Pin(16), scl=Pin(15))
# 3. Use dynamic I2C Control Bus from board_config
i2c = board_config.i2c_bus
# 4. Initialize the ES8311 DAC
dac = ES8311(i2c)
if not dac.init(sample_rate=16000):
mclk_pwm.deinit()
if mclk_pwm: mclk_pwm.deinit()
f.close()
return False
dac.set_volume(volume)
# 5. Configure I2S TX
# Pins: sck=BCLK (GPIO 5), ws=WS/LRCK (GPIO 7), sd=DOUT (GPIO 8)
i2s_format = I2S.MONO if channels == 1 else I2S.STEREO
i2s = I2S(1,
sck=Pin(5),
ws=Pin(7),
sd=Pin(8),
sck=Pin(board_config.audio_i2s_sck),
ws=Pin(board_config.audio_i2s_ws),
sd=Pin(board_config.audio_i2s_tx_sd),
mode=I2S.TX,
ibuf=4096,
rate=sample_rate,
bits=bits,
format=i2s_format)
# 6. Enable Speaker Amplifier (GPIO 1, Active Low)
amp_pin = Pin(1, Pin.OUT, value=0)
# 6. Enable Speaker Amplifier
on_val = 0 if board_config.audio_amp_active_level == 0 else 1
off_val = 1 if board_config.audio_amp_active_level == 0 else 0
amp_pin = Pin(board_config.audio_amp_pin, Pin.OUT, value=on_val)
# 7. Read and stream chunks to I2S
buf = bytearray(2048)
@@ -388,9 +411,9 @@ def play_wav(filename, volume=50):
# 8. Clean up
time.sleep_ms(100) # Let the remaining buffer play out
amp_pin.value(1) # Disable amp
amp_pin.value(off_val) # Disable amp
i2s.deinit()
mclk_pwm.deinit()
if mclk_pwm: mclk_pwm.deinit()
f.close()
print("WAV playback complete.")
return True
+191
View File
@@ -0,0 +1,191 @@
import sys
import machine
from machine import Pin, I2C, SPI
# Global configuration variables
BOARD_TYPE = None # 'HOSYOND', 'WAVESHARE_RLCD', or 'RP2'
DISPLAY_TYPE = None # 'ILI9341', 'RLCD', or 'ST7796'
DISPLAY_WIDTH = 320
DISPLAY_HEIGHT = 240
# Bus and display references
spi_bus = None
i2c_bus = None
display_instance = None
touch = None
# Component support flags
has_sensor = False
has_rtc = False
# WS2812 NeoPixel pin
led_pin = None
# Audio pins, clocks, and codecs config
audio_mclk_pin = None
audio_mclk_freq = 6144000
audio_i2c_sda = None
audio_i2c_scl = None
audio_i2s_sck = None
audio_i2s_ws = None
audio_i2s_tx_sd = None
audio_i2s_rx_sd = None
audio_amp_pin = None
audio_amp_active_level = 0 # 0 = Active Low, 1 = Active High
audio_mic_codec = "ES8311" # "ES7210" or "ES8311"
def detect_board():
global BOARD_TYPE, DISPLAY_TYPE, DISPLAY_WIDTH, DISPLAY_HEIGHT
global spi_bus, i2c_bus, display_instance, touch
global has_sensor, has_rtc, led_pin
global audio_mclk_pin, audio_mclk_freq, audio_i2c_sda, audio_i2c_scl
global audio_i2s_sck, audio_i2s_ws, audio_i2s_tx_sd, audio_i2s_rx_sd
global audio_amp_pin, audio_amp_active_level, audio_mic_codec
# 1. Check if running on RP2 platform (e.g. Raspberry Pi Pico / RP2350)
if sys.platform == 'rp2':
print("Auto-detected: Raspberry Pi RP2 platform")
BOARD_TYPE = 'RP2'
DISPLAY_TYPE = 'ST7796'
DISPLAY_WIDTH = 480
DISPLAY_HEIGHT = 320
led_pin = 25
# Setup SPI
spi_bus = SPI(1, baudrate=32000000, polarity=0, phase=0, sck=Pin(10), mosi=Pin(11))
# Setup Display: ST7796
import st7796
display_instance = st7796.ST7796(spi_bus, cs=Pin(7), dc=Pin(4), rst=Pin(9), bl=Pin(6))
# Setup Touch: FT6336U on I2C(1)
try:
touch_i2c = I2C(1, sda=Pin(2), scl=Pin(3), freq=400000)
from ft6336u import FT6336U
touch = FT6336U(touch_i2c, rst_pin=28, int_pin=25)
except Exception as e:
print("Failed to initialize touch on RP2:", e)
return
# 2. We are on ESP32 platform.
# Try scanning I2C(0) on Hosyond pins (SDA=16, SCL=15)
try:
test_i2c = I2C(0, sda=Pin(16), scl=Pin(15))
devices = test_i2c.scan()
# FT6336U touchscreen is at 0x38 on Hosyond
if 0x38 in devices:
print("Auto-detected: Hosyond ESP32-S3 Touchscreen board")
BOARD_TYPE = 'HOSYOND'
DISPLAY_TYPE = 'ILI9341'
DISPLAY_WIDTH = 320
DISPLAY_HEIGHT = 240
has_sensor = False
has_rtc = False
led_pin = 48
i2c_bus = test_i2c
# Setup SPI
spi_bus = SPI(1, baudrate=40000000, polarity=0, phase=0, sck=Pin(12), mosi=Pin(11), miso=Pin(13))
# Setup Display: ILI9341
import ili9341
display_instance = ili9341.ILI9341(spi_bus, cs=Pin(10), dc=Pin(46), bl=Pin(45), rst=None)
# Setup Touch: FT6336U
try:
from ft6336u import FT6336U
touch = FT6336U(i2c_bus, rst_pin=18, int_pin=17, width=320, height=240, swap_xy=True, invert_x=False, invert_y=True)
except Exception as te:
print("Failed to initialize touchscreen:", te)
# Audio pins & clocks configuration
audio_mclk_pin = 4
audio_mclk_freq = 6144000
audio_i2c_sda = 16
audio_i2c_scl = 15
audio_i2s_sck = 5
audio_i2s_ws = 7
audio_i2s_tx_sd = 8
audio_i2s_rx_sd = 6
audio_amp_pin = 1
audio_amp_active_level = 0 # Active Low (0 = enabled)
audio_mic_codec = "ES8311"
return
except Exception as e:
print("I2C scan on Hosyond pins failed:", e)
# 3. Try scanning I2C(0) on Waveshare RLCD pins (SDA=13, SCL=14)
try:
test_i2c = I2C(0, sda=Pin(13), scl=Pin(14))
devices = test_i2c.scan()
# SHTC3 is at 0x70, PCF85063 RTC is at 0x51
if 0x70 in devices or 0x51 in devices:
print("Auto-detected: Waveshare ESP32-S3-RLCD-4.2 board")
BOARD_TYPE = 'WAVESHARE_RLCD'
DISPLAY_TYPE = 'RLCD'
DISPLAY_WIDTH = 400
DISPLAY_HEIGHT = 300
has_sensor = True
has_rtc = True
led_pin = 38
i2c_bus = test_i2c
# Setup SPI
spi_bus = SPI(1, baudrate=20000000, polarity=0, phase=0, sck=Pin(11), mosi=Pin(12))
# Setup Display: RLCD
import rlcd
display_instance = rlcd.RLCD(spi_bus, cs=Pin(40), dc=Pin(5), rst=Pin(41))
# Audio pins & clocks configuration
audio_mclk_pin = 16
audio_mclk_freq = 12288000
audio_i2c_sda = 13
audio_i2c_scl = 14
audio_i2s_sck = 9
audio_i2s_ws = 45
audio_i2s_tx_sd = 8
audio_i2s_rx_sd = 10
audio_amp_pin = 46
audio_amp_active_level = 1 # Active High (1 = enabled)
audio_mic_codec = "ES7210"
return
except Exception as e:
print("I2C scan on Waveshare RLCD pins failed:", e)
# 4. Fallback default if auto-detection failed completely
print("Auto-detection failed. Falling back to Hosyond ESP32-S3 defaults...")
BOARD_TYPE = 'HOSYOND'
DISPLAY_TYPE = 'ILI9341'
DISPLAY_WIDTH = 320
DISPLAY_HEIGHT = 240
led_pin = 48
try:
i2c_bus = I2C(0, sda=Pin(16), scl=Pin(15))
spi_bus = SPI(1, baudrate=40000000, polarity=0, phase=0, sck=Pin(12), mosi=Pin(11), miso=Pin(13))
import ili9341
display_instance = ili9341.ILI9341(spi_bus, cs=Pin(10), dc=Pin(46), bl=Pin(45), rst=None)
from ft6336u import FT6336U
touch = FT6336U(i2c_bus, rst_pin=18, int_pin=17, width=320, height=240, swap_xy=True, invert_x=False, invert_y=True)
except Exception as e:
print("Fallback hardware setup failed:", e)
audio_mclk_pin = 4
audio_mclk_freq = 6144000
audio_i2c_sda = 16
audio_i2c_scl = 15
audio_i2s_sck = 5
audio_i2s_ws = 7
audio_i2s_tx_sd = 8
audio_i2s_rx_sd = 6
audio_amp_pin = 1
audio_amp_active_level = 0
audio_mic_codec = "ES8311"
# Run dynamic detection immediately on module load
detect_board()