pythonarduinopyserial

How can I wait for a command over serial in python?


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.


Solution

  • 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.