For a robotics project, I have to connect a Raspberry Pi together with two Arduino's. Each Arduino controls two stepper stepper motors. The raspberry pi is sort of the "brain" for the project and thus needs to control the arudino's actions, often multiple in a row. They communicate over the serial port, therefore in python I use the "pyserial" framework. My problem is, that I can't know when exactly an arduino has completed powering the stepper motor so I can run the next action. Often it is crucial for me to know when the Arduino is finished. What I need is the following:
def control_arduino(command, nextcommand):
serial.write(command) # a command for the arduino to move motor x y steps
serial.wait_for_string("end") # a command initiated by the arduino stating that he completed moving the motor
serial2.write(nextcommand) # next command for the other arduino, but the first command has to be completed for this
serial2.wait_for_string("end")
print("ended doing x")
As of the current program, arduino 2 starts his action even if arduino 1 hasn't even completed his actions due to the missing "wait_for" method.
read_until
didn't help me that much.
This tutorial provided a good way for solving the problem.
I settled on this code for the pi-side:
class Arduino:
def __init__(self, port: int):
self.port = port
def send_command(self, cmd: str) -> bool:
with serial.Serial(self.port, 9600, timeout=40) as arduino:
sleep(0.1)
if arduino.is_open:
print("Connected")
try:
arduino.write(cmd.encode())
while arduino.in_waiting == 0:
pass
if arduino.in_waiting > 0:
answer = str(arduino.readline())
print(answer)
return True
except: # needed because of unknown error
return False
In my case, self.port
is /dev/ttyACM0
and /dev/ttyACM1
for the other arduino.
The command itself is just a number that is sent by the pi as a string and then parsed by the arduino, although I am sure that one could also directly send an int to the arduino.
The arduino code looks like this:
String msg;
void setup() {
Serial.begin(9600);
}
void loop() {
readSerialPort();
if (msg != "") {
int cmd = msg.toInt();
// do something with the int, e.g. steering the motors
// e.g. 300 means 300 steps forward, 10300 means 300 steps backward and so on
Serial.println("done");
}
delay(500);
}
void readSerialPort() {
msg = "";
if (Serial.available()) {
delay(10);
while (Serial.available() > 0) {
msg += (char)Serial.read();
}
Serial.flush();
}
}
Maybe there is a way of doing that more elegantly, but this works.