from machine import UART, Pin
import time

# ----------------------------
# CONFIG (change to your pins)
# ----------------------------
UART_ID = 1                 # ESP32-C6 typically has UART(0) + UART(1)
TX_PIN = 20                 # ESP32 TX  -> DFPlayer RX
RX_PIN = 21                 # ESP32 RX  <- DFPlayer TX
BUSY_PIN = 22             # Optional: DFPlayer BUSY pin -> ESP32 GPIO (set int like 4), or None

BAUD = 9600

# ----------------------------
# DFPlayer protocol helpers
# ----------------------------
_START = 0x7E
_VER   = 0xFF
_LEN   = 0x06
_ACK   = 0x00               # 0x01 = request ACK (some clones get flaky). Keep 0x00 for test jig.
_END   = 0xEF

# Commands (common)
CMD_SET_VOLUME   = 0x06
CMD_PLAY_TRACK   = 0x03     # Play track number (0001..2999) from root (depends on SD structure)
CMD_PLAY_FOLDER  = 0x0F     # Play folder/file (01..99 folder, 001..255 file)
CMD_STOP         = 0x16
CMD_RESET        = 0x0C
CMD_SET_EQ       = 0x07
CMD_SET_SOURCE   = 0x09     # (rarely needed)
CMD_QUERY_STATUS = 0x42     # not always supported consistently

def _checksum(cmd, p1, p2):
    # checksum = 0 - (ver + len + cmd + ack + 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 = None
        if busy_pin is not None:
            self.busy = Pin(busy_pin, Pin.IN, Pin.PULL_UP)  # BUSY is often LOW when playing (depends on module)
        # Small settle time after power-up
        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)  # DFPlayer needs a moment after reset

    def volume(self, level: int):
        # 0..30
        level = max(0, min(30, level))
        self._send(CMD_SET_VOLUME, level)

    def stop(self):
        self._send(CMD_STOP, 0)

    def play_track(self, track_num: int):
        # Track numbers: 1..2999 typically
        self._send(CMD_PLAY_TRACK, track_num)

    def play_folder_file(self, folder: int, file: int):
        # folder: 1..99, file: 1..255
        folder = max(1, min(99, folder))
        file = max(1, min(255, file))
        param = (folder << 8) | file
        self._send(CMD_PLAY_FOLDER, param)

    def is_playing(self):
        if self.busy is None:
            return None
        # Many modules: BUSY = 0 while playing, 1 when idle
        return self.busy.value() == 0

# ----------------------------
# UART + Test Jig
# ----------------------------
uart = UART(
    UART_ID,
    baudrate=BAUD,
    bits=8,
    parity=None,
    stop=1,
    tx=Pin(TX_PIN),
    rx=Pin(RX_PIN),
    timeout=100
)

player = DFPlayer(uart, busy_pin=BUSY_PIN)

def demo():
    print("DFPlayer test jig starting...")
    print("UART:", UART_ID, "TX:", TX_PIN, "RX:", RX_PIN, "BUSY:", BUSY_PIN)

    # Optional: Reset (sometimes helpful on clones; comment out if it causes issues)
    # player.reset()

    player.volume(20)
    time.sleep_ms(200)


    # English
    # Temp
    player.play_folder_file(1, 101)
    #time.sleep(2)
    while busy.value() == 1:
        print("Gate1",busy.value())
    # now wait until playback finishes
    while busy.value() == 0:
        print("Gate2",busy.value())
    # 30
    player.play_folder_file(1, 30)
    #time.sleep(1)
    while busy.value() == 1:
        print("Gate1",busy.value())
    # now wait until playback finishes
    while busy.value() == 0:
        print("Gate2",busy.value())
    # 7
    player.play_folder_file(1, 7)
    #time.sleep(1.5)
    while busy.value() == 1:
        print("Gate1",busy.value())
    # now wait until playback finishes
    while busy.value() == 0:
        print("Gate2",busy.value())
    # Vibration Warning
    player.play_folder_file(1, 102)
    #time.sleep(3)
    while busy.value() == 1:
        print("Gate1",busy.value())
    # now wait until playback finishes
    while busy.value() == 0:
        print("Gate2",busy.value())
    time.sleep(0.5)
    #Spanish
    # Temp
    player.play_folder_file(2, 101)
    #time.sleep(2)
    while busy.value() == 1:
        print("Gate1",busy.value())
    # now wait until playback finishes
    while busy.value() == 0:
        print("Gate2",busy.value())
    # 30
    player.play_folder_file(2, 30)
    #time.sleep(1)
    while busy.value() == 1:
        print("Gate1",busy.value())
    # now wait until playback finishes
    while busy.value() == 0:
        print("Gate2",busy.value())
    # 7
    player.play_folder_file(2, 7)
    time.sleep(1.5)
    # Vibration Warning
    player.play_folder_file(2, 102)
    while busy.value() == 1:
        print("Gate1",busy.value())
    # now wait until playback finishes
    while busy.value() == 0:
        print("Gate2",busy.value())


    print("Stop.")
    player.stop()


busy = Pin(BUSY_PIN, Pin.IN, Pin.PULL_UP)
demo()
quit()
