Python Forum
UART Serial Read & Write to MP3 Player Doesn't Work
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
UART Serial Read & Write to MP3 Player Doesn't Work
#11
Is the response to read_status is 4 bytes (0xAA, ??, ??, checksum) or 3 bytes (0xAA, status, checksum)?

A failed read appears to raise an exception. According to pyserial documentation that should not happen UNLESS the port is closed or there are multiple devices trying to read the serial port. You should catch the exception and test if the serial port is still open.
Reply
#12
I'm running this code on a Raspberry Pi 4. If the program fails to close the port when it ends properly, the next time it runs it think there are more devices accessing the port.

Also, the documentation says to expect a status of AA 01 01 00 AC when MP3 has finished playing but the return appears to be 00 00 00, but the program is coded to accept the 00 00 00 to end.

I added print(" port ", ser.isOpen) in the read routine but how would I check to see if it thinks more devices are attempting to use the port?

What I saw after running this the second time is important! Shutting the RPi down and starting it fresh, I ran the program the first time with the print("port Open? ", isOpen) and the program ran and ended well printing the 00 00 00 return after the MP3 finished playing only. Makes me believe that the chip on the MP3 board can process only one thing at a time.

But the next observance is the real problem. When I ran the program again with no changes, I got the errors indicating that the device disconnected or multiple access on port. See output below.
Something needs to be closed or reset but at the end of the program is the ser.close() statement.

Output:
pi@raspberrypi:~ $ sudo python3 SerialTest3.py Command = [170, 7, 2, 0, 4, 183] Command = [170, 1, 0, 171] port Open? <bound method SerialBase.isOpen of Serial<id=0xb66e7bb0, open=True>(port='/dev/ttyS0', baudrate=9600, bytesize=8, parity='N', stopbits=1, timeout=1, xonxoff=False, rtscts=False, dsrdtr=False)> Status = b'\xaa' Command = [170, 1, 0, 171] port Open? <bound method SerialBase.isOpen of Serial<id=0xb66e7bb0, open=True>(port='/dev/ttyS0', baudrate=9600, bytesize=8, parity='N', stopbits=1, timeout=1, xonxoff=False, rtscts=False, dsrdtr=False)> Status = b'' Command = [170, 1, 0, 171] port Open? <bound method SerialBase.isOpen of Serial<id=0xb66e7bb0, open=True>(port='/dev/ttyS0', baudrate=9600, bytesize=8, parity='N', stopbits=1, timeout=1, xonxoff=False, rtscts=False, dsrdtr=False)> Status = b'' Command = [170, 1, 0, 171] port Open? <bound method SerialBase.isOpen of Serial<id=0xb66e7bb0, open=True>(port='/dev/ttyS0', baudrate=9600, bytesize=8, parity='N', stopbits=1, timeout=1, xonxoff=False, rtscts=False, dsrdtr=False)> Status = b'' Command = [170, 1, 0, 171] port Open? <bound method SerialBase.isOpen of Serial<id=0xb66e7bb0, open=True>(port='/dev/ttyS0', baudrate=9600, bytesize=8, parity='N', stopbits=1, timeout=1, xonxoff=False, rtscts=False, dsrdtr=False)> Status = b'' Command = [170, 1, 0, 171] port Open? <bound method SerialBase.isOpen of Serial<id=0xb66e7bb0, open=True>(port='/dev/ttyS0', baudrate=9600, bytesize=8, parity='N', stopbits=1, timeout=1, xonxoff=False, rtscts=False, dsrdtr=False)> Status = b'' Command = [170, 1, 0, 171] port Open? <bound method SerialBase.isOpen of Serial<id=0xb66e7bb0, open=True>(port='/dev/ttyS0', baudrate=9600, bytesize=8, parity='N', stopbits=1, timeout=1, xonxoff=False, rtscts=False, dsrdtr=False)> Status = b'\x00\x00\x00' pi@raspberrypi:~ $ sudo python3 SerialTest3.py Command = [170, 7, 2, 0, 4, 183] Command = [170, 1, 0, 171] port Open? <bound method SerialBase.isOpen of Serial<id=0xb664ebb0, open=True>(port='/dev/ttyS0', baudrate=9600, bytesize=8, parity='N', stopbits=1, timeout=1, xonxoff=False, rtscts=False, dsrdtr=False)> Traceback (most recent call last): File "/usr/lib/python3/dist-packages/serial/serialposix.py", line 501, in read 'device reports readiness to read but returned no data ' serial.serialutil.SerialException: device reports readiness to read but returned no data (device disconnected or multiple access on port?) During handling of the above exception, another exception occurred: Traceback (most recent call last): File "SerialTest3.py", line 37, in <module> playing = read_status(ser) File "SerialTest3.py", line 19, in read_status return port.read(3) File "/usr/lib/python3/dist-packages/serial/serialposix.py", line 509, in read raise SerialException('read failed: {}'.format(e)) serial.serialutil.SerialException: read failed: device reports readiness to read but returned no data (device disconnected or multiple access on port?)
Reply
#13
The read_status command has to read 5 bytes to get 3 status bytes. In the code below I added a check for the start of message character (often referred to as a sentinel) and a checsum check.
"""Sends a  message via serial & receives back information via serial."""

import serial
import time

PLAYING_DONE = bytearray([0, 0, 0])

def write_command(port, command):
    """Write command to serial port"""
    command = [170] + command  # Command packet starts with 0xAA
    command.append(sum(command) % 256)  # Append checksum
    print(command)
    port.write(bytearray(command))

def read_status(port):
    """Read status?  Returns 3L bytearray"""
    write_command(port, [1, 0])
    bytes = port.read(5)
    print('Reply ', bytes)
    if bytes is None:
        raise ValueError('Read timeout')
    if bytes[0] != 0xAA:
        raise ValueError('Serial sentinel error')
    if bytes[4] != (sum(bytes[:-1]) % 256):
        raise ValueError('Serial read checksum error')
    return bytes[1:-1]

def play_track(port, track_info):
    """Start playing track.  Track is??"""
    write_command(port, [7] + track_info)

with serial.Serial(
        'COM3:',
        baudrate=9600,
        parity=serial.PARITY_NONE,
        stopbits=serial.STOPBITS_ONE,
        bytesize=serial.EIGHTBITS,
        timeout=1) as ser:
    # Start of serial context
    play_track(ser, [2, 0, 4])  # Track info?
    playing = None
    while playing != PLAYING_DONE:
        time.sleep(1) 
        playing = read_status(ser)
        print(f'Status = {playing}')
    # end of serial context
print("I am done")
To prevent leaving the serial port open the code uses a context manager. "with serial.Serial(...)" creates a context for the serial port. When the program exits this context (the indented instructions below the "with") the serial port is automatically closed. It doesn't matter if the context exits because the program is done using the serial port or if there is an exception.
Reply
#14
Again, it started playing the MP3 requested but then got a run error. See below:

The Internet suggests leading the statement with try: and following up with except and pass.

What do you think?

Output:
$ sudo python3 SerialTest4.py [170, 7, 2, 0, 4, 183] [170, 1, 0, 171] Traceback (most recent call last): File "SerialTest4.py", line 41, in <module> playing = read_status(ser) File "SerialTest4.py", line 20, in read_status raise ValueError('Serial sentinel error') ValueError: Serial sentinel error
Reply
#15
Not sure if this is a patch or fix, but I started increasing the timeout parameter in the serial open statement and now, it doesn't give the error

serial.serialutil.SerialException: read failed: device reports readiness to read but returned no data (device disconnected or multiple access on port?)

I went up to 20 before it stopped giving an error, started at 2. I suppose, that the chip on the MP3 board doesn't support a very fast serial port.

This just stopped the above error. I still have work to do to get the whole return code back. At this point, all I get back are zeros, no leading or trailing information like the documentation suggests.
Reply
#16
Deanhystad,

With your help and others, I was able to come up with something that works consistently well.

Thanks to all!

Seems there were several issues. One was timing. The timeout parameter in the open statement needed to be long enough for the MP3 board to compile the answer to the query. Another issue was timing between the request and the response. 3rd was to not ask for the read till the inWaiting was equal to the length of the response.

Also, I had trouble when I kept the serial port open through the whole program. Opening and closing probably causes a bunch of overhead but worked best for me. I tried only opening once in the program after I was successful opening and closing many times and it failed with time out issues.

Please consider this issue closed.

Here is the resultant code:

"""Sends a  message via serial & receives back information via serial."""
 
import serial
import time
to = 30    #<-- timeout value 
PLAYING_DONE = bytearray([170, 1, 1, 0, 172])

def write_command(port, command):
    """Write command to serial port"""
    command = [170] + command           # Command packet starts with 0xAA
    command.append(sum(command) % 256)  # Append checksum
#    print('Command = ', command)        # For debugging purposes
    port.write(bytearray(command))

def read_status(port):
    """Read status?  Returns  bytearray"""
    write_command(port, [1, 0])
    time.sleep(0.5)
    if port.inWaiting() > 4:
         return port.read(5)

def play_track(port, track_info):
    """Start playing track.  Track is??"""
    write_command(port, [7] + track_info)

ser = serial.Serial(
    '/dev/ttyS0',   # Serial port on my computer
    baudrate=9600,
    parity=serial.PARITY_NONE,
    stopbits=serial.STOPBITS_ONE,
    bytesize=serial.EIGHTBITS,
    timeout=to)     #  timeout for read
 
play_track(ser, [2, 0, 9])  # Track info?
ser.close()

playing = None

while playing != PLAYING_DONE:
    ser = serial.Serial(
        '/dev/ttyS0',   # Serial port on my computer
        baudrate=9600,
        parity=serial.PARITY_NONE,
        stopbits=serial.STOPBITS_ONE,
        bytesize=serial.EIGHTBITS,
        timeout=to)  # timeout for read

    playing = read_status(ser)
#    print(f'Return = {playing}')
    ser.close()


ser.close()
print("Done and closed")
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  Last record in file doesn't write to newline gonksoup 3 433 Jan-22-2024, 12:56 PM
Last Post: deanhystad
  UART & I2C slow down a loop trix 4 620 Dec-28-2023, 05:14 PM
Last Post: trix
  python Read each xlsx file and write it into csv with pipe delimiter mg24 4 1,458 Nov-09-2023, 10:56 AM
Last Post: mg24
Question Special Characters read-write Prisonfeed 1 623 Sep-17-2023, 08:26 PM
Last Post: Gribouillis
  pyserial/serial "has no attribute 'Serial' " gowb0w 9 4,062 Aug-24-2023, 07:56 AM
Last Post: gowb0w
  Why doesn't calling a parent constructor work with arbitrary keyword arguments? PurposefulCoder 4 944 Jun-24-2023, 02:14 PM
Last Post: deanhystad
  How do I read and write a binary file in Python? blackears 6 6,643 Jun-06-2023, 06:37 PM
Last Post: rajeshgk
  Python Serial: How to read the complete line to insert to MySQL? sylar 1 827 Mar-21-2023, 10:06 PM
Last Post: deanhystad
  Read text file, modify it then write back Pavel_47 5 1,616 Feb-18-2023, 02:49 PM
Last Post: deanhystad
  Why doesn't this code work? What is wrong with path? Melcu54 7 1,795 Jan-29-2023, 06:24 PM
Last Post: Melcu54

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020