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
#1
Not only am I a beginner to Python3 but I’m a beginner to reading and writing via serial port on a Raspberry PI (dev/ttyS0). I’m trying to communicate to a MP3 player board that is suppose to use UART Communication Command to play particular MP3s and to return status of the MP3, like if it is playing or finished playing.

The MP3 requires a unique set of values to include a summation byte for it to recognize what to do.

With much trial, error & many Internet searches, I was able to come up with some code that almost works.

It will play the requested MP3 and write the request for status some of the time. When it does return the status, the program doesn’t work like I thought it would when using the try: statement. I wanted it to keep on looping and I would figure out a way to break out when the status was zero. But it doesn’t loop.

Also, after the program ends by it’s self, the next time I try the program, the expected read doesn’t work. But, if I stop the program with a ^C, the next time I run the program, I get expected results in state_ret. I want it to loop till I get a state_ret equal to zero. I think I need to do some sort of reset at the beginning of the program to be sure .

I hope I’m clear about the above and the problems I’m having.

I can use suggestions and pointers on how reading & writing to serial really works, so I can be somewhat proficient and what to change in my program so it will work.

Here is my program (I use NANO as my editor).

#// Sends a  message via serial & receives back information via serial.

import time
import serial

ser = serial.Serial('/dev/ttyS0', baudrate=9600, parity=serial.PARITY_NONE,
	stopbits=serial.STOPBITS_ONE, bytesize=serial.EIGHTBITS)

print (ser.isOpen())

track_req = bytearray([170,7,2,0,20,180])
state_req = bytearray([170,1,0,171])
state_ret = bytearray([0,0,0,0,0])
expected0 = bytearray([170,1,1,0,172])
expected1 = bytearray([170,1,1,1,173])
expected2 = bytearray([170,1,1,2,174])

track_req[5] = track_req[0]+ track_req[1]+ track_req[2]+ track_req[3]+ track_req[4] 

print ("Script Started", track_req)
ser.write(track_req)
time.sleep(2)

try:
	print ("Query Requested")
	print ("--->  ", ser.isOpen())
	ser.write(state_req)
	time.sleep(1)
	if (ser.inWaiting()>0): 
		print ("ReadLine", ser.inWaiting())
		print ("===>  ", ser.isOpen())
		state_ret = ser.read(ser.inWaiting())
		print("RX ", state_ret)

except OSError:
# ignore or log...  Let the loop retry.
	print ("SOError ", OSError)
#	pass
These are the 2 different outputs when I run without a ^c then after a ^c:

Output:
pi@raspberrypi:~ $ sudo python3 SerialTest.py True Script Started bytearray(b'\xaa\x07\x02\x00\x14\xc7') Query Requested ---> True ReadLine 5 ===> True RX b'xxxxx' pi@raspberrypi:~ $ ^C pi@raspberrypi:~ $ sudo python3 SerialTest.py True Script Started bytearray(b'\xaa\x07\x02\x00\x14\xc7') Query Requested ---> True ReadLine 4 ===> True RX b'\x01\x01\x01\xad' pi@raspberrypi:~ $ ^C
Reply
#2
To loop you need a loop; for or while. try/except catch errors that accur while executing the code in the body of the try statement. try is not a loop.
Reply
#3
Thanks, That fixed the looping problem but there is still the problem of reading from the serial port again and again till the MP3 board sends back a zero indicating the MP3 has finished playing. All I get it a null after each serial write after the first.

Also, without the try: how do I trap for errors?
Reply
#4
Working more and more on this code, I was able to fix the looping issue (thanks Deanhystad), but still having an issue of receiving back from the serial port.

I'm was getting inconsistent returns from the serial port each time I run the program (see output).

I noticed that I do not get return information each time the program does a ser.read(ser.inWaiting(), only the first time even though I think I am doing repeated ser.write. Also ser.isOpen value changes between program runs. I don't understand this.

If the program receives RX b'\x01\x01\x01\xad' on the first read, then the program ends as expected, but, if the first read is something like RX b'\x1exxxx' then it won't end well.

Please help.

Here is my code:

#// Sends a  message via serial & receives back information via serial.
 
import time
import serial
 
ser = serial.Serial('/dev/ttyS0', baudrate=9600, parity=serial.PARITY_NONE,
    stopbits=serial.STOPBITS_ONE, bytesize=serial.EIGHTBITS)
 
print (ser.isOpen())
 
track_req = bytearray([170,7,2,0,4,180])
state_req = bytearray([170,1,0,171])
state_ret = bytearray([0,0,0,0,0])

Zeros = bytearray([0,0,0])
Playing = True
track_req[5] = track_req[0]+ track_req[1]+ track_req[2]+ track_req[3]+ track_req[4] 
 
print ("Script Started", track_req)
ser.write(track_req)
time.sleep(2)
 
while Playing:
	print ("Loop Query Requested")
	print ("--->  ", ser.isOpen())
	ser.write(state_req)
	time.sleep(1)
	if (ser.inWaiting()>0):
		print ("ReadLine", ser.inWaiting())
		print ("===>  ", ser.isOpen())
		state_ret = ser.read(ser.inWaiting())
		print("RX ", state_ret)
		if (state_ret == Zeros):
			Playing = False 

print ("Done")
ser.close()
Output:
pi@raspberrypi:~ $ sudo python3 SerialTest2.py True Script Started bytearray(b'\xaa\x07\x02\x00\x04\xb7') Loop Query Requested ---> True ReadLine 4 ===> True RX b'\x01\x01\x01\xad' Loop Query Requested ---> True Loop Query Requested ---> True Loop Query Requested ---> True ReadLine 3 ===> True RX b'\x00\x00\x00' Done pi@raspberrypi:~ $ sudo python3 SerialTest2.py True Script Started bytearray(b'\xaa\x07\x02\x00\x04\xb7') Loop Query Requested ---> True ReadLine 5 ===> True RX b'\x1exxxx' Loop Query Requested ---> True Loop Query Requested ---> True Loop Query Requested ---> True Loop Query Requested ---> True ^CTraceback (most recent call last): File "SerialTest2.py", line 30, in <module> time.sleep(1) KeyboardInterrupt pi@raspberrypi:~ $
Reply
#5
Can you provide a link to the mp3 serial protocol?
Reply
#6
UART Protocol Control documentation is on the vendor's site. There are several modes to this board. I chose to use the UART serial interface because it requires very few wires to set it up and still is able to access 500+ MP3s (if I can get it to work). Documentation is WAY down the page, but it is there.

Please know that using the UART commands, I can currently access (play) any MP3 on my SD chip. What my Raspbarry Pi need to know is when the MP3 is finished playing. This documentation is suppose to be able to report this with one of its Query commands.

UART Protocol Control

In my example of the output of my program in one of the previous postings, the format and value of the received information changes when I run the same program the second time. Something isn't resetting after program end I feel one of my issues is in how I open or close the serial port. But, I'm very new to accessing serial port information.

Thanks for any help you can give me.
Reply
#7
I could not follow the link, so this advise of the uninformed guessing type. I think 170 (0xAA) is a start of packet marker and that the packet ends with some sort of checksum. I am accepting that the play track command is 7 and the read status command is 1. I also accept that the read status command returns 3 bytes.

Instead of looping and peeking at the read buffer I would specify a read timeout when opening the serial port.
"""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].extend(command)  # Command packet starts with 0xAA
    command.append(sum(command) % 256)  # Append checksum
    port.write(bytearray(command))

def read_status(port):
    """Read status?  Returns 3L bytearray"""
    write_command(port, [1, 0])
    return port.read(3)

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

ser = serial.Serial(
    '/dev/ttyS0',
    baudrate=9600,
    parity=serial.PARITY_NONE,
    stopbits=serial.STOPBITS_ONE,
    bytesize=serial.EIGHTBITS,
    timeout=1)  # 1 second timeout for read

play_track(ser, [2, 0, 4])  # Track info?
playing = None
while playing != PLAYING_DONE:
    time.sleep(1)
    playing = read_status(ser)
    print(f'Status = f{playing}')
ser.close()
Reply
#8
Thanks Deanhystad,

You are correct that the board is expecting x/aa to start the command and a check sum byte to end it. Sorry you could not access the URL. I'll try to see what I can do about that.

There is some of this code that I do not understand being a beginner and all.

Just in attempting to learn, I ran your code and got errors. Something about not iterable. Error has something to do with for loops, in your case the 'while', I'm assuming. So I tested and that was not it.

Something to do with the '[]s'. I don't know how to fix.

Output:
$ sudo python3 SerialTest3.py Traceback (most recent call last): File "SerialTest3.py", line 31, in <module> play_track(ser, [2, 0, 4]) # Track info? File "SerialTest3.py", line 21, in play_track write_command(port, [7].extend(track_info)) File "SerialTest3.py", line 10, in write_command command = [170].extend(command) # Command packet starts with 0xAA TypeError: 'NoneType' object is not iterable
Reply
#9
Why do you think my code throws the error? Do you know what a "TypeError" is? Why is this error raised when "command" is a NoneType? How do you think a NoneType got passed to "write_command()"?

All the above questions are things a new Python programmer should be able to answer with a little bit of digging.

A few hints. The error has nothing to do with the while loop. Look at what the list.extend() command takes as an argument and what it returns.

Corrected code is below. There was also an error in the print command.
"""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 = ', command)  # For debugging purposes
    port.write(bytearray(command))

def read_status(port):
    """Read status?  Returns 3L bytearray"""
    write_command(port, [1, 0])
    return port.read(3)

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

ser = serial.Serial(
    'COM3:',   # Serial port on my computer
    baudrate=9600,
    parity=serial.PARITY_NONE,
    stopbits=serial.STOPBITS_ONE,
    bytesize=serial.EIGHTBITS,
    timeout=1)  # 1 second timeout for read

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}')
ser.close()
Reply
#10
Thanks again for your help Deanhystad.

The corrected code did start the MP3 player playing then there was a bunch of information listed but no return on the query.

Is there a way to reset the port to insure all buffers are cleared and there are no other accesses to it?

I'll have to figure a way to get you the documentation of this board.

In the mean time, here is some important information:
Query Play Status = AA 01 00 AB Expects return of AA 01 01 ?? SumByte where ?? should be 00 for Stopped, 01 for playing & 02 for paused. SumByte like the request, is the sum of the of the bytes in the command.

Output:
$ sudo python3 SerialTest3.py Command = [170, 7, 2, 0, 4, 183] Command = [170, 1, 0, 171] 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 36, in <module> playing = read_status(ser) File "SerialTest3.py", line 18, 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


Possibly Related Threads…
Thread Author Replies Views Last Post
  Last record in file doesn't write to newline gonksoup 3 365 Jan-22-2024, 12:56 PM
Last Post: deanhystad
  UART & I2C slow down a loop trix 4 548 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,314 Nov-09-2023, 10:56 AM
Last Post: mg24
Question Special Characters read-write Prisonfeed 1 582 Sep-17-2023, 08:26 PM
Last Post: Gribouillis
  pyserial/serial "has no attribute 'Serial' " gowb0w 9 3,326 Aug-24-2023, 07:56 AM
Last Post: gowb0w
  Why doesn't calling a parent constructor work with arbitrary keyword arguments? PurposefulCoder 4 870 Jun-24-2023, 02:14 PM
Last Post: deanhystad
  How do I read and write a binary file in Python? blackears 6 6,018 Jun-06-2023, 06:37 PM
Last Post: rajeshgk
  Python Serial: How to read the complete line to insert to MySQL? sylar 1 787 Mar-21-2023, 10:06 PM
Last Post: deanhystad
  Read text file, modify it then write back Pavel_47 5 1,501 Feb-18-2023, 02:49 PM
Last Post: deanhystad
  Why doesn't this code work? What is wrong with path? Melcu54 7 1,681 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