Question(s) about the pololu-rpi-slave-arduino-library

I have a 32u4 Robot Controller LV. I mounted it on a Raspberry Pi 3. I successfully loaded the AStar32U4 and PololuRPiSlave libraries in my Arduino dev environment and loaded the AStarRPiSlaveDemo onto the Controller. I also pulled the Python3 AStar class and the blink, beep, and benchmark examples onto the Pi. The blink and benchmark examples work just fine. The beep may or may not; it makes noise, but I am not sure what it is supposed to sound like (I should probably note that when testing the Controller independent of the Pi BuzzerBasics example ran just fine).

So far, so good.

I looked at the code because I will need to add (and probably subtract) some functions; for example, I’ll want to drive a pair of servos. I started examining the Data structure in AStarRPiSlaveDemo and the implementation of the AStar class in a_star.py. It became obvious that the “address” parameter in read_unpack and write_pack methods refers to the byte address in the buffer created from the Data structure. Trying to do the formatting and math, etc. I get the following addresses:

0 = the 3 LEDs (each 1 byte) – used by the leds method
3 = the 3 buttons (each 1 byte) – used by the read_buttons method
6 = the 2 motors (each 2 bytes) – used by the motors method
10 = the battery voltage (2 bytes) – used by the read_battery_millivolts method
12 = the 6 analog inputs (each 2 bytes) – used by the read_analog method
24 = the playNotes indicator (1 byte) – used by the play_notes method
25 = the 14 notes (each 1 byte) – used by the play_notes method
39 = the 2 encoder values (each 2 bytes) – used by the read_encoders method

So from what I can see, all the addresses line up nicely with the byte structure created using the struct.pack method.

However, I cannot understand the implementation of the play_notes method, which I copy here for discussion:

def play_notes(self, notes):
self.write_pack(24, ‘B15s’, 1, notes.encode(“ascii”))

If I understand it, the format ‘B15s’ should map the 1 as an unsigned char (a byte) and place it in address 24. Then format should map the next 15 “things” as char[] (a byte array) starting at 25. Since the Data structure has notes[14], I would have expected the format to be ‘B14s’. At least in theory, the 15 could cause an overwrite into the encoder values. What am I missing?

Thanks.

Hello.

That does look like a bug - thanks for pointing this out! However, even with ‘B14s’ there is still a problem, because the string is not guaranteed to be null terminated. If notes[14] is filled, the string will be truncated leaving off the null termination character.

We discussed the issue and think changing the code for the play_notes function to self.write_pack(25, 'B13sB', 1, notes.encode('ascii'), 0) would be a good way to go, ensuring that the string will always be null terminated. We will try that solution out and get the code updated soon.

- Amanda

I looked at PololuBuzzer.cpp and it appears that anything that is not one of the characters it recognizes as meaningful for the buzzer will terminate play; thus I think a “null” (0) or even a newline (\n), etc. will terminate playing. I am, however, no C++ expert. The bottom line is I think your proposal will work.

However, to me this raises a couple of more questions.

  1. What if the incoming string is 14 char? The last one will get ignored. That I guess could be handled by documentation say “really only 13 char possible”, or by allowing the char[] array to be 14 char long, and adding another char after that for the terminator. Or maybe allow the 14 and then create a temp array and add the terminator to send to buzzer.play().

  2. What if the string is less than 14 char (as in the example)? The terminator needs to be placed after the last char. It appears from my testing the struct.pack() method will copy a string terminator. So, this case is covered, I think.

You are correct. With our proposed change, the play_notes code will silently truncate the incoming string to 13 characters.

If the string is less than the specified size, it is padded with null bytes to make it fit. This case is stated in the official Python documentation for the struct module.

- Amanda