There are a number of ways to tackle this problem, and the best solution depends on exactly what data you're sending back and forth.
The simplest solution is to represent commands a single bytes (e.g., M for MOVE or R for REQUEST_DATA), because this way you only need to read a single byte on the arduino side to determine the command. Once you know that, you should know how much additional data you need to read in order to get the necessary parameters.
For example, here's a simple program that understands two commands:
- A command to move to a given position
- A command to turn the built-in LED on or off
The code looks like this:
#define CMD_MOVE 'M'
#define CMD_LED 'L'
struct Position {
int8_t xpos, ypos;
};
struct LEDState {
byte state;
};
void setup() {
Serial.begin(9600);
pinMode(LED_BUILTIN, OUTPUT);
// We need this so our Python code knows when the arduino is
// ready to receive data.
Serial.println("READY");
}
void loop() {
char cmd;
size_t nb;
if (Serial.available()) {
cmd = Serial.read();
switch (cmd) {
case CMD_MOVE:
struct Position pos;
nb = Serial.readBytes((char *)&pos, sizeof(struct Position));
Serial.print("Moving to position ");
Serial.print(pos.xpos);
Serial.print(",");
Serial.println(pos.ypos);
break;
case CMD_LED:
struct LEDState led;
nb = Serial.readBytes((char *)&led, sizeof(struct LEDState));
if (led.state) {
digitalWrite(LED_BUILTIN, HIGH);
} else {
digitalWrite(LED_BUILTIN, LOW);
}
Serial.print("LED is ");
Serial.println(led.state ? "on" : "off");
break;
}
}
}
A fragment of Python code that interacts with the above might look like this (assuming that port is a serial.Serial object):
print("waiting for arduino...")
line=b""
while not b"READY" in line:
line = port.readline()
port.write(struct.pack('bbb', ord('M'), 10, -10))
res = port.readline()
print(res)
for i in range(10):
port.write(struct.pack('bb', ord('L'), i%2))
res = port.readline()
print(res)
time.sleep(0.5)
port.write(struct.pack('bbb', ord('M'), -10, 10))
res = port.readline()
print(res)
Running the above Python code, with the Arduino code loaded on my Uno, produces:
waiting for arduino...
b'Moving to position -10,10\r\n'
b'LED is off\r\n'
b'LED is on\r\n'
b'LED is off\r\n'
b'LED is on\r\n'
b'LED is off\r\n'
b'LED is on\r\n'
b'LED is off\r\n'
b'LED is on\r\n'
b'LED is off\r\n'
b'LED is on\r\n'
b'Moving to position 10,-10\r\n'
This is simple to implement and doesn't require much in the way of decoding on the Arduino side.
For more complex situations, you may want to investigate more complex serialization solutions: for example, you can send JSON to the arduino and use something like https://arduinojson.org/ to deserialize it on the Arduino side, but that's going to be a much more complex solution.
In most cases, the speed at which this works is going to be limited by the speed of the serial port: the default speed of 9600bps is relatively slow, and you're going to notice that with larger amounts of data. Using higher serial port speeds will make things noticeably faster: I'm too lazy to look up the max. speed supported by the Arduino, but my UNO works at least as fast as 115200bps.