Hey guys,
I'm having some trouble with my current project where I wanna use an RPi in combination with an Arduino mini pro. I want to use the Arduino to manage my dc-motors via an L298N controller, since the RPi only has 2 PWM PINs and I need at least 4.
Now getting to my current problem: I need to send 3 floats from the RPi to the Arduino via I2C, so it can make calculations on how to drive the motors for the mecanum wheels. I've scraped the internet on how to do it and so far I'm close to the point where I can send a float represented as an array of integers from my RPi to the Arduino, but somehow every time the RPI sends an array I'm missing the last integer.
My RPI master code:
import smbus
import struct
# device adress
arduino = 0x03
# initialize serial commumication
bus = smbus.SMBus(1)
def writeNumber(value):
# sending a float (4byte) to the arduino
ba = list(struct.pack('!f', value))
print(ba)
bus.write_i2c_block_data(arduino, ba[0], ba[1:3])
while True:
number = float(input('Give me a number: '))
writeNumber(number)
My Arduino slave code:
#include <Wire.h>
#define SLAVE_ADDRESS 0x03
uint8_t index = 0;
union floatToBytes {
int valueBuffer[4];
float valueReading;
} converter;
void setup() {
Serial.begin(9600); // start serial for output
Wire.begin(SLAVE_ADDRESS); // initialize i2c as slave
Wire.onReceive(receiveData); // receive interrupt callback (triggered by I2C-Master)
Serial.println("Ready!");
}
void loop() {
delay(100);
}
void receiveData(int byteCount){
while(Wire.available()){
converter.valueBuffer[index] = Wire.read();
Serial.println(converter.valueBuffer[index]);
index++;
}
index = 0;
Serial.print("The number is: ");
Serial.println(converter.valueReading);
}
I would really appreciate if someone could help me out and maybe point me into a direction of how to send more then just one float to the arduino!!!
Take a look at pigpio (pigpio library) - it will let you have as many pwms as you have gpio pins, and a whole lot of other functions as well. It is the best I/O library I've seen for RPi.
1. One correction has been proposed in Post#1 for the Slave sketch.
2. Here is the proposal for another correction in the Slave sketch.
void receiveData() is an interrupt context where print() method is not permitted. Do the print job in the loop() function based on a globally declared "volatile type flag variable" to be set in the receiveData() routine.
void loop()
{
if(flag == true)
{
Place codes to do the printing job using print() method
flag = false;
}
}
void receiveData(int byteCount)
{
for(int index = 0; index<byteCount; index++)
{
converter.valueBuffer[index] = Wire.read();
}
flag = true;
}
Big thanks, I've implemented both corrections and it works now!!!
FYI, I did use the Arduino as my motor driver, as it has hardware PWM on 6 PINs, which the RPi does only have for 2. Using the recommended RPi library would apply software PWM, which would increase the processor load and limit my computer vision computations on the RPi.
I didn't want to do the parsing computations, because the way it's working now is way more elegant in my opinion. Also wanna keep latency down as much as possible.
For the interested one, this is the corresponding code
RPi Master:
import smbus
import struct
# device adress
arduino = 0x03
# initialize serial commumication
bus = smbus.SMBus(1)
def writeNumbers(values):
# sending an float to the arduino
# consider changing d to f for float precision
byteList = []
for value in values:
byteList += list(struct.pack('f', value))
byteList.append(0) # fails to send last byte over I2C, hence this needs to be added
bus.write_i2c_block_data(arduino, byteList[0], byteList[1:12])
if __name__ == '__main__':
while True:
numbers = []
for i in range(3):
numbers.append(float(input(f'Give me number {i}: ')))
writeNumbers(numbers)
Arduino:
#include <Wire.h>
#define SLAVE_ADDRESS 0x03
bool flag = false;
union BytesToFloat {
// 'converts' incoming bytes to a float array
// valueReading: [0] wheelSpeed; [1] wheelAngle; [2] wheelRotation
byte valueBuffer[12];
float valueReading[3];
} converter;
void setup() {
Serial.begin(9600); // start serial for output
Wire.begin(SLAVE_ADDRESS); // initialize i2c as slave
Wire.onReceive(receiveData); // receive interrupt callback (triggered by I2C-Master)
Serial.println("Ready!");
}
void loop() {
if(flag) printInfo();
delay(100);
}
void printInfo(){
for(uint8_t index = 0; index<3; index++){
Serial.print("The number is: ");
Serial.println(converter.valueReading[index]);
}
for(uint8_t index = 0; index<12; index++){
Serial.print("Number ");
Serial.print(index);
Serial.print(" is: ");
Serial.println(converter.valueBuffer[index]);
}
flag = false;
}
void receiveData(int byteCount){
for(uint8_t index = 0; index<byteCount; index++){
converter.valueBuffer[index] = Wire.read();
}
flag = true;
}