Raspberry Pi I2C connection to Romi 32U4

Hello,

I followed the step in this blog:

Now, I would like to control a servo motor connected to data pin 5 on the Romi through the web app created using the server.py. All these files are in the following GitHub repository: GitHub - pololu/pololu-rpi-slave-arduino-library: An Arduino library that helps establish I2C communication between an A-Star 32U4 Robot Controller and a Raspberry Pi, with the Arduino acting as the I2C slave.

Through this, I have access to three LEDs, three buttons, the motor encoders, and the buzzer. How do I expand what I have access to? I would like to be able to know how to control all the pins on the Romi. Initially, I was running the robot using C++ in VScode. This way I was able to control all the pins and their outputs with the help of a large library. When I added the Raspberry Pi, it seems that all that work is thrown out the window.

The end goal is to use a Pi camera and TensorFlow object detection to operate the robot. The robot will have to pickup a bag using the servo motor with an attached arm.

Hello.

If you have not done so already, a good starting point would probably be to get a servo working on the Romi 32U4 Control Board independently from the Raspberry Pi. The Romi 32U4 user’s guide has some guidance for how to do that in the “Controlling a servo” section.

From there it should be possible to modify our Raspberry Pi slave library for Arduino to add the functionality you want. We do not have specific instructions for doing that, but if you try it and have trouble then you can post about your attempt and we might be able to offer some guidance.

By the way, it sounds like your project might eventually involve controlling several servos. If that is the case, then you might consider adding one of our Maestro USB Servo Controllers to your robot. The Maestro has a serial interface that you could control with your Raspberry Pi, such as by using this Python class. However, please note that the Maestro’s graphical configuration program (the Maestro Control Center) does not work on ARM-based Linux machines such as the Raspberry Pi, so it would be ideal if you have a different Linux computer or a Windows computer to help set up the Maestro.

- Patrick

Thank you Patrick for the help.

I am able to move the servo but for the wrong reasons. On the server webpage, once I play the buzzer, the servo fully extends upwards. This may be an issue with the addition data I put into the struct.

I am using the WPI library which sets the servo motor to a Timer_3 on Pin 5. I did some sanity checks and it seems the slave file to the Romi is acting properly. I believe the issue is from unpacking the struct in the a_star.py file to the Raspberry Pi. With that being said, I have create a GitHub repository with all the code. The pi folder contains the files for the RPi and the Romi folder contains the files for the Romi 32U4. Here is the link to the repository: GitHub - mohamedhadrami/BU-romi32U4-RPi-robot: Code for Raspberry Pi mounted on Romi 32U4

Again, my main problem seems to be with the i2c connection between the RPi and the Romi. when unpack and packing the struct how do I ensure I am communicating with the write pin on the Romi?

Thank you,
Mohamed

Hi, Mohamed.

As you guessed, it looks like your Romi and RPi programs are not packing and unpacking the data structure consistently. Here is the struct from your Romi program with comments added to label the byte offsets of each member:

struct Data
{
  bool yellow, green, red;           // 0, 1, 2
  bool buttonA, buttonB, buttonC;    // 3, 4, 5

  int16_t leftMotor, rightMotor;     // 6-7, 8-9
  uint16_t batteryMillivolts;        // 10-11
  uint16_t analog[6];                // 12-23
  uint16_t setServo;                 // 24-25

  bool playNotes;                    // 26
  char notes[14];                    // 27-40

  int16_t leftEncoder, rightEncoder; // 41-42, 43-44
};

In a-star.py on the RPi, the first numeric argument to the read_unpack and write_pack methods is the address or offset inside the data structure where each variable should be accessed. So to match where the Romi program is expecting the servo position, your servo method should be writing at address 24, not 5 as it does in your current code, and all of the methods that access variables after the servo position need to be updated (since you shifted all of them 2 bytes when you inserted the servo position in the middle of the data). Also, the servo position is a single unsigned short, so the format character should be H. Here are the changes you need to make (with comments):

  def leds(self, red, yellow, green):
    self.write_pack(0, 'BBB', red, yellow, green)
  
  # This method probably won't do what you want unless you use a new variable that doesn't overlap with existing ones and add support for it in the Romi code.
  def buzz(self, beep):
    self.write_pack(0, 'B', beep)

  def play_notes(self, notes):
    self.write_pack(26, 'B14s', 1, notes.encode("ascii")) # address changed from 24 to 26

  def motors(self, left, right):
    self.write_pack(6, 'hh', left, right)
  
  def servo(self, setServo):
    self.write_pack(24, 'H', setServo) # address changed from 5 to 24; format changed from hh to H

  def read_buttons(self):
    return self.read_unpack(3, 3, "???")

  def read_battery_millivolts(self):
    return self.read_unpack(10, 2, "H")

  def read_analog(self):
    return self.read_unpack(12, 12, "HHHHHH")

  def read_encoders(self):
    return self.read_unpack(41, 4, 'hh') # address changed from 39 to 41

Could you try these changes to see if they give the behavior you want?

Kevin

1 Like

Yes, that fixed all my problems. Thank you immensely.

Just out of curiosity, how are able to determine, for example, that each integer needs two address or the bools only need one? Is this just how the bytes are read once they are unpacked? I understand the struct module much more thanks to the link you provided. I am novice at this, so excuse me if I am asking these basic question.

-Mohamed

I’m glad it’s working for you now.

The int16_t and uint16_t types come from stdint.h and are defined to be the specified width (16 bits, or 2 bytes). A bool is 1 byte (this depends on the compiler but is true for almost all implementations), and a char is always 1 byte (according to the C++ standard).

One way you can determine the width of a particular type is by checking what the sizeof operator says for that type (for example, Serial.print(sizeof(bool))).

Kevin

1 Like