# ESP32-S3 MicroPython: Modbus RTU (RS485) + DFPlayer Mini (HW UART) merged # RS485: UART1 TX=GPIO9 RX=GPIO10 DIR=GPIO18 # DFPlayer: UART2 TX=GPIO40 RX=GPIO41 BUSY=GPIO42 (BUSY typically LOW while playing) from machine import UART, Pin import time, struct # ========================= # DFPLAYER (UART2) # ========================= DF_UART_ID = 2 DF_TX_PIN = 21 # ESP32 TX -> DFPlayer RX DF_RX_PIN = 20 # ESP32 RX <- DFPlayer TX (optional, but harmless) DF_BUSY_PIN = 47 # DFPlayer BUSY -> ESP32 GPIO (usually LOW when playing) DF_BAUD = 9600 _START = 0x7E _VER = 0xFF _LEN = 0x06 _ACK = 0x00 _END = 0xEF CMD_SET_VOLUME = 0x06 CMD_PLAY_TRACK = 0x03 CMD_PLAY_FOLDER = 0x0F CMD_STOP = 0x16 CMD_RESET = 0x0C def _checksum(cmd, p1, p2): s = _VER + _LEN + cmd + _ACK + p1 + p2 c = (0 - s) & 0xFFFF return (c >> 8) & 0xFF, c & 0xFF class DFPlayer: def __init__(self, uart: UART, busy_pin=None): self.uart = uart self.busy = Pin(busy_pin, Pin.IN, Pin.PULL_UP) if busy_pin is not None else None time.sleep_ms(800) def _send(self, cmd, param=0): p1 = (param >> 8) & 0xFF p2 = param & 0xFF ch, cl = _checksum(cmd, p1, p2) frame = bytes([_START, _VER, _LEN, cmd, _ACK, p1, p2, ch, cl, _END]) self.uart.write(frame) def reset(self): self._send(CMD_RESET, 0) time.sleep_ms(1200) def volume(self, level): level = max(0, min(30, int(level))) self._send(CMD_SET_VOLUME, level) def stop(self): self._send(CMD_STOP, 0) def play_track(self, track_num): self._send(CMD_PLAY_TRACK, int(track_num)) def play_folder_file(self, folder, file): folder = max(1, min(99, int(folder))) file = max(1, min(255, int(file))) self._send(CMD_PLAY_FOLDER, (folder << 8) | file) def is_playing(self): if self.busy is None: return None # Typical DFPlayer: BUSY LOW = playing, HIGH = idle return self.busy.value() == 0 def wait_done(self, timeout_ms=20000): """Wait for BUSY to go LOW (start) then HIGH (done).""" if self.busy is None: return False t0 = time.ticks_ms() # wait for playback to begin (BUSY goes LOW) while self.busy.value() == 1: if time.ticks_diff(time.ticks_ms(), t0) > 1000: return False time.sleep_ms(5) # wait for playback to finish (BUSY goes HIGH) while self.busy.value() == 0: if time.ticks_diff(time.ticks_ms(), t0) > timeout_ms: return False time.sleep_ms(10) return True df_uart = UART( DF_UART_ID, baudrate=DF_BAUD, bits=8, parity=None, stop=1, tx=Pin(DF_TX_PIN), rx=Pin(DF_RX_PIN), timeout=100 ) player = DFPlayer(df_uart, busy_pin=DF_BUSY_PIN) # ========================= # MODBUS RTU (RS485 UART1) # ========================= MB_UART_ID = 1 MB_TX_PIN = 45 MB_RX_PIN = 0 MB_BAUD = 9600 SLAVE_ID = 10 # RS485 direction pin (DE/RE tied together) RS485_DIR_PIN = 18 rs485_dir = Pin(RS485_DIR_PIN, Pin.OUT) rs485_dir.value(0) # receive mode mb_uart = UART( MB_UART_ID, baudrate=MB_BAUD, bits=8, parity=None, stop=1, tx=Pin(MB_TX_PIN), rx=Pin(MB_RX_PIN), timeout=200 ) def crc16(data): crc = 0xFFFF for b in data: crc ^= b for _ in range(8): crc = (crc >> 1) ^ 0xA001 if (crc & 1) else (crc >> 1) return crc def read_holding(start_reg, count): frame = bytearray(6) frame[0] = SLAVE_ID frame[1] = 3 # FC03 frame[2] = start_reg >> 8 frame[3] = start_reg & 0xFF frame[4] = count >> 8 frame[5] = count & 0xFF frame += struct.pack("h", struct.pack(">H", regs[4]))[0] / 100 rh = regs[5] / 100 pressure = regs[6] / 10 iaq = regs[7] print("------------") print("Counter :", counter) print("Status :", bin(status)) print("Mode :", "SIM" if mode else "LIVE") print("Errors :", errors) print("Temp :", temp, "C") print("RH :", rh, "%") print("Press :", pressure, "hPa") print("IAQ :", iaq) print("------------\n") # Example trigger: if IAQ > 10, play one file once if iaq > 100 and not RunOnceOver: player.volume(50) time.sleep_ms(50) print("play") # folder=2, file=105 must exist as numeric file in /02/ player.play_folder_file(2, 105) ok = player.wait_done(timeout_ms=25000) time.sleep(1) print("Audio done:", ok) RunOnceOver = True RunOnceOver1 = False if iaq < 100 and not RunOnceOver1: player.volume(15) time.sleep_ms(50) print("play") # folder=2, file=105 must exist as numeric file in /02/ player.play_folder_file(2, 106) ok = player.wait_done(timeout_ms=25000) time.sleep(1) print("Audio done:", ok) RunOnceOver = False RunOnceOver1 = True # ========================= # MAIN LOOP # ========================= while True: #player.play_folder_file(2, 106) #time.sleep(5) regs = read_holding(0, 8) if regs: decode_env(regs) #time.sleep_ms(100)