Module lib.plugins.ambientsound.dfplayer

Class to drive the dfplayer or yx5300 module

Expand source code
# Distributed under Pycameresp License
# Copyright (c) 2023 Remi BERTHOLET
""" Class to drive the dfplayer or yx5300 module """ 
# pylint:disable=consider-using-f-string
# https://github.com/0xcafed00d/yx5300/blob/master/docs/Serial%20MP3%20Player%20v1.0%20Manual.pdf
from binascii import hexlify
import machine

# Status
TF_INSERT        = 0x3A  # TF Card was inserted (unsolicited message).
TF_REMOVE        = 0x3B  # TF card was removed (unsolicited message).
PLAY_ENDED       = 0x3D  # Track/file has ended (unsolicited message). Data is the index number of the file just completed.
INIT             = 0x3F  # Initialization complete (unsolicited message). Data is the file store types available (0x02 for TF).
ERR_FILE         = 0x40  # Error file not found. Data is error code (no definition).
ACK_OK           = 0x41  # Message acknowledged ok
STATUS           = 0x42  # Current status. Data high byte is file store (2 for TF); low byte 0=stopped, 1=play, 2=paused.
VOLUME           = 0x43  # Current volume level. Data is volume level [0..30].
EQUALIZER        = 0x44  # Equalizer status. Data is equalizer mode types  0=Normal, 1=Pop, 2=Rock, 3=Jazz, 4=Classic or 5=Base.
TOT_FILES        = 0x48  # TF Total file count
PLAYING          = 0x4C  # Current file playing
FLDR_FILES       = 0x4E  # Total number of files in the folder. Data is the number of files.
TOT_FLDR         = 0x4F  # Total number of folders. Data is the number of folders.

# Command
PLAY_NEXT        = 0x01  # Play next song.
PLAY_PREVIOUS    = 0x02  # Play previous song.
PLAY_WITH_INDEX  = 0x03  # Play song with index number. Data is the index of the file.
VOLUME_UP        = 0x04  # Volume increase by one.
VOLUME_DOWN      = 0x05  # Volume decrease by one.
SET_VOLUME       = 0x06  # Set the volume to level specified. Data is the volume level [0..30].
SET_EQUALIZER    = 0x07  # Set the equalizer to specified level. Data is [0..5] ? 0=Normal, 1=Pop, 2=Rock, 3=Jazz, 4=Classic or 5=Base.
SNG_CYCL_PLAY    = 0x08  # Loop play (repeat) specified track. Data is the track number.
SEL_DEV          = 0x09  # Select file storage device. The only valid choice for data is TF (0x02).
SLEEP_MODE       = 0x0A  # Chip enters sleep mode.
WAKE_UP          = 0x0B  # Chip wakes up from sleep mode.
RESET            = 0x0C  # Chip reset.
PLAY             = 0x0D  # Playback restart.
PAUSE            = 0x0E  # Pause playback.
PLAY_FOLDER_FILE = 0x0F  # Play the file with the specified folder and index number
STOP_PLAY        = 0x16  # Stop playback.
FOLDER_CYCLE     = 0x17  # Loop playback within specified folder. Data is the folder index.
SHUFFLE_PLAY     = 0x18  # Playback shuffle mode. Data is 0 to enable, 1 to disable.
SET_SNGL_CYCL    = 0x19  # Set loop play (repeat) on/off for current file. Data is 0 to enable, 1 to disable.
SET_DAC          = 0x1A  # DAC on/off control (mute sound). Data is 0 to enable DAC, 1 to disable DAC (mute).
PLAY_W_VOL       = 0x22  # Play track at the specified volume. Data hi byte is the track index, low byte is the volume level [0..30].
SHUFFLE_FOLDER   = 0x28  # Playback shuffle mode for folder specified. Data high byte is the folder index.
QUERY_STATUS     = 0x42  # Query Device Status.
QUERY_VOLUME     = 0x43  # Query Volume level.
QUERY_EQUALIZER  = 0x44  # Query current equalizer (disabled in hardware).
QUERY_TOT_FILES  = 0x48  # Query total files in all folders.
QUERY_PLAYING    = 0x4C  # Query which track playing
QUERY_FLDR_FILES = 0x4E  # Query total files in folder. Data is the folder index number.
QUERY_TOT_FLDR   = 0x4F  # Query number of folders.

class DFPlayer:
        """ Dfplayer """
        def __init__(self, rx=13, tx=4, display=False):
                """ Open dfplayer instance """
                self.uart = machine.UART(1, baudrate=9600, rx=rx, tx=tx)
                self.display = display

        def __del__(self):
                """ Close dfplayer """
                if self.uart is not None:
                        self.uart.deinit()

        def checksum(self, frame):
                """ Compute the frame checksum """
                result = 0
                for byte in frame:
                        result -= byte
                return result

        def send(self, command, param=0, response=False):
                """ Send command to dfplayer """
                buf = bytearray(10)
                buf[0] = 0x7E
                buf[1] = 0xFF
                buf[2] = 6
                buf[3] = command
                buf[4] = 1 if response else 0
                buf[5] = (param & 0xFF00) >> 8
                buf[6] = (param & 0xFF)
                checksum = self.checksum(buf[1:6])
                buf[7] = (checksum & 0xFF00) >> 8
                buf[8] = (checksum & 0xFF)
                buf[9] = 0xEF
                if self.display:
                        print("Snd=",hexlify(buf," ").upper().decode("utf8"))
                self.uart.write(buf)
                return buf

        def receive(self):
                """ Receive query response """
                # The reception is complicated, because it happens that bytes are lost,
                # and then there is a byte shift on the reception.
                # In this case, the entire reception buffer is emptied
                if self.uart.any() > 0:
                        pos = 0
                        command = None
                        data = None
                        fback = None
                        flush = False
                        buffer = b""
                        while self.uart.any() > 0:
                                char = self.uart.read(1)
                                buffer += char
                                if flush is False:
                                        # Start
                                        if pos == 0:
                                                if char[0] == 0x7E:
                                                        pos += 1
                                                else:
                                                        flush = True
                                        # Version
                                        elif pos == 1:
                                                if char[0] == 0xFF:
                                                        pos += 1
                                                else:
                                                        flush = True
                                        # Length
                                        elif pos == 2:
                                                if char[0] == 6:
                                                        pos += 1
                                        # Command
                                        elif pos == 3:
                                                command = char[0]
                                                pos += 1
                                        # Fback
                                        elif pos == 4:
                                                fback = char[0]
                                                pos += 1
                                        # Data high
                                        elif pos == 5:
                                                data = char[0] << 8
                                                pos += 1
                                        # Data low
                                        elif pos == 6:
                                                data |= char[0]
                                                pos += 1
                                        # Checksum high
                                        elif pos == 7:
                                                checksum = char[0] << 8
                                                pos += 1
                                        # Checksum low
                                        elif pos == 8:
                                                checksum |= char[0]
                                                pos += 1
                                        # Stop
                                        elif pos == 9:
                                                if char[0] == 0xEF:
                                                        pos += 1
                                                        break
                                                else:
                                                        flush = True
                        if pos == 10:
                                if self.display:
                                        print("Rcv= %02X -> %d"%(command, data))
                                return command, data
                        else:
                                if self.display:
                                        print("Bad data received ! %s"%(hexlify(buffer," ").upper().decode("utf8")))
                return None

        def play(self, response=False):
                """ Play """
                if self.display:
                        print("Play")
                self.send(PLAY, response=response)

        def play_track(self, folder, track, response=False):
                """ Play """
                if self.display:
                        print("Play track")
                self.send(PLAY_FOLDER_FILE, (folder&0xFF) << 8| track, response=response)

        def stop(self, response=False):
                """ Stop """
                if self.display:
                        print("Stop")
                self.send(STOP_PLAY, response=response)

        def pause(self, response=False):
                """ Pause """
                if self.display:
                        print("Pause")
                self.send(PAUSE, response=response)

        def reset(self, response=False):
                """ Reset """
                if self.display:
                        print("Reset")
                self.send(RESET, response=response)

        def play_next(self, response=False):
                """ Play next """
                if self.display:
                        print("Next")
                self.send(PLAY_NEXT, response=response)

        def play_previous(self, response=False):
                """ Play previous """
                if self.display:
                        print("Previous")
                self.send(PLAY_PREVIOUS, response=response)

        def get_status(self):
                """ Send the get status command """
                if self.display:
                        print("Get status")
                self.send(QUERY_STATUS, response=True)

Classes

class DFPlayer (rx=13, tx=4, display=False)

Dfplayer

Open dfplayer instance

Expand source code
class DFPlayer:
        """ Dfplayer """
        def __init__(self, rx=13, tx=4, display=False):
                """ Open dfplayer instance """
                self.uart = machine.UART(1, baudrate=9600, rx=rx, tx=tx)
                self.display = display

        def __del__(self):
                """ Close dfplayer """
                if self.uart is not None:
                        self.uart.deinit()

        def checksum(self, frame):
                """ Compute the frame checksum """
                result = 0
                for byte in frame:
                        result -= byte
                return result

        def send(self, command, param=0, response=False):
                """ Send command to dfplayer """
                buf = bytearray(10)
                buf[0] = 0x7E
                buf[1] = 0xFF
                buf[2] = 6
                buf[3] = command
                buf[4] = 1 if response else 0
                buf[5] = (param & 0xFF00) >> 8
                buf[6] = (param & 0xFF)
                checksum = self.checksum(buf[1:6])
                buf[7] = (checksum & 0xFF00) >> 8
                buf[8] = (checksum & 0xFF)
                buf[9] = 0xEF
                if self.display:
                        print("Snd=",hexlify(buf," ").upper().decode("utf8"))
                self.uart.write(buf)
                return buf

        def receive(self):
                """ Receive query response """
                # The reception is complicated, because it happens that bytes are lost,
                # and then there is a byte shift on the reception.
                # In this case, the entire reception buffer is emptied
                if self.uart.any() > 0:
                        pos = 0
                        command = None
                        data = None
                        fback = None
                        flush = False
                        buffer = b""
                        while self.uart.any() > 0:
                                char = self.uart.read(1)
                                buffer += char
                                if flush is False:
                                        # Start
                                        if pos == 0:
                                                if char[0] == 0x7E:
                                                        pos += 1
                                                else:
                                                        flush = True
                                        # Version
                                        elif pos == 1:
                                                if char[0] == 0xFF:
                                                        pos += 1
                                                else:
                                                        flush = True
                                        # Length
                                        elif pos == 2:
                                                if char[0] == 6:
                                                        pos += 1
                                        # Command
                                        elif pos == 3:
                                                command = char[0]
                                                pos += 1
                                        # Fback
                                        elif pos == 4:
                                                fback = char[0]
                                                pos += 1
                                        # Data high
                                        elif pos == 5:
                                                data = char[0] << 8
                                                pos += 1
                                        # Data low
                                        elif pos == 6:
                                                data |= char[0]
                                                pos += 1
                                        # Checksum high
                                        elif pos == 7:
                                                checksum = char[0] << 8
                                                pos += 1
                                        # Checksum low
                                        elif pos == 8:
                                                checksum |= char[0]
                                                pos += 1
                                        # Stop
                                        elif pos == 9:
                                                if char[0] == 0xEF:
                                                        pos += 1
                                                        break
                                                else:
                                                        flush = True
                        if pos == 10:
                                if self.display:
                                        print("Rcv= %02X -> %d"%(command, data))
                                return command, data
                        else:
                                if self.display:
                                        print("Bad data received ! %s"%(hexlify(buffer," ").upper().decode("utf8")))
                return None

        def play(self, response=False):
                """ Play """
                if self.display:
                        print("Play")
                self.send(PLAY, response=response)

        def play_track(self, folder, track, response=False):
                """ Play """
                if self.display:
                        print("Play track")
                self.send(PLAY_FOLDER_FILE, (folder&0xFF) << 8| track, response=response)

        def stop(self, response=False):
                """ Stop """
                if self.display:
                        print("Stop")
                self.send(STOP_PLAY, response=response)

        def pause(self, response=False):
                """ Pause """
                if self.display:
                        print("Pause")
                self.send(PAUSE, response=response)

        def reset(self, response=False):
                """ Reset """
                if self.display:
                        print("Reset")
                self.send(RESET, response=response)

        def play_next(self, response=False):
                """ Play next """
                if self.display:
                        print("Next")
                self.send(PLAY_NEXT, response=response)

        def play_previous(self, response=False):
                """ Play previous """
                if self.display:
                        print("Previous")
                self.send(PLAY_PREVIOUS, response=response)

        def get_status(self):
                """ Send the get status command """
                if self.display:
                        print("Get status")
                self.send(QUERY_STATUS, response=True)

Methods

def checksum(self, frame)

Compute the frame checksum

Expand source code
def checksum(self, frame):
        """ Compute the frame checksum """
        result = 0
        for byte in frame:
                result -= byte
        return result
def get_status(self)

Send the get status command

Expand source code
def get_status(self):
        """ Send the get status command """
        if self.display:
                print("Get status")
        self.send(QUERY_STATUS, response=True)
def pause(self, response=False)

Pause

Expand source code
def pause(self, response=False):
        """ Pause """
        if self.display:
                print("Pause")
        self.send(PAUSE, response=response)
def play(self, response=False)

Play

Expand source code
def play(self, response=False):
        """ Play """
        if self.display:
                print("Play")
        self.send(PLAY, response=response)
def play_next(self, response=False)

Play next

Expand source code
def play_next(self, response=False):
        """ Play next """
        if self.display:
                print("Next")
        self.send(PLAY_NEXT, response=response)
def play_previous(self, response=False)

Play previous

Expand source code
def play_previous(self, response=False):
        """ Play previous """
        if self.display:
                print("Previous")
        self.send(PLAY_PREVIOUS, response=response)
def play_track(self, folder, track, response=False)

Play

Expand source code
def play_track(self, folder, track, response=False):
        """ Play """
        if self.display:
                print("Play track")
        self.send(PLAY_FOLDER_FILE, (folder&0xFF) << 8| track, response=response)
def receive(self)

Receive query response

Expand source code
def receive(self):
        """ Receive query response """
        # The reception is complicated, because it happens that bytes are lost,
        # and then there is a byte shift on the reception.
        # In this case, the entire reception buffer is emptied
        if self.uart.any() > 0:
                pos = 0
                command = None
                data = None
                fback = None
                flush = False
                buffer = b""
                while self.uart.any() > 0:
                        char = self.uart.read(1)
                        buffer += char
                        if flush is False:
                                # Start
                                if pos == 0:
                                        if char[0] == 0x7E:
                                                pos += 1
                                        else:
                                                flush = True
                                # Version
                                elif pos == 1:
                                        if char[0] == 0xFF:
                                                pos += 1
                                        else:
                                                flush = True
                                # Length
                                elif pos == 2:
                                        if char[0] == 6:
                                                pos += 1
                                # Command
                                elif pos == 3:
                                        command = char[0]
                                        pos += 1
                                # Fback
                                elif pos == 4:
                                        fback = char[0]
                                        pos += 1
                                # Data high
                                elif pos == 5:
                                        data = char[0] << 8
                                        pos += 1
                                # Data low
                                elif pos == 6:
                                        data |= char[0]
                                        pos += 1
                                # Checksum high
                                elif pos == 7:
                                        checksum = char[0] << 8
                                        pos += 1
                                # Checksum low
                                elif pos == 8:
                                        checksum |= char[0]
                                        pos += 1
                                # Stop
                                elif pos == 9:
                                        if char[0] == 0xEF:
                                                pos += 1
                                                break
                                        else:
                                                flush = True
                if pos == 10:
                        if self.display:
                                print("Rcv= %02X -> %d"%(command, data))
                        return command, data
                else:
                        if self.display:
                                print("Bad data received ! %s"%(hexlify(buffer," ").upper().decode("utf8")))
        return None
def reset(self, response=False)

Reset

Expand source code
def reset(self, response=False):
        """ Reset """
        if self.display:
                print("Reset")
        self.send(RESET, response=response)
def send(self, command, param=0, response=False)

Send command to dfplayer

Expand source code
def send(self, command, param=0, response=False):
        """ Send command to dfplayer """
        buf = bytearray(10)
        buf[0] = 0x7E
        buf[1] = 0xFF
        buf[2] = 6
        buf[3] = command
        buf[4] = 1 if response else 0
        buf[5] = (param & 0xFF00) >> 8
        buf[6] = (param & 0xFF)
        checksum = self.checksum(buf[1:6])
        buf[7] = (checksum & 0xFF00) >> 8
        buf[8] = (checksum & 0xFF)
        buf[9] = 0xEF
        if self.display:
                print("Snd=",hexlify(buf," ").upper().decode("utf8"))
        self.uart.write(buf)
        return buf
def stop(self, response=False)

Stop

Expand source code
def stop(self, response=False):
        """ Stop """
        if self.display:
                print("Stop")
        self.send(STOP_PLAY, response=response)