0
votes

I have some problems with serial communication between Raspberry Pi (Python Script) and Arduino nano/uno. I have both devices connected by USB port and when I send command directly from arduino IDE serial monitor, the arduino sketch always response correctly:

<Arduino is ready>
<MSG:GDI,RESPONSE:EMD-1707-1993,Time:7>
<MSG:GDI,RESPONSE:EMD-1707-1993,Time:23>
<MSG:GDI,RESPONSE:EMD-1707-1993,Time:26>
<MSG:GDI,RESPONSE:EMD-1707-1993,Time:30>
<MSG:GDI,RESPONSE:EMD-1707-1993,Time:34>
<MSG:GDI,RESPONSE:EMD-1707-1993,Time:38>

But, when I run the python script, and he send same command, the sketch response in a random way.

Raspberry terminal:

pi@raspberrypi:~/test/raspberry $ python test.py 
Sending GDI command to device...
<GDI>
Traceback (most recent call last):
  File "test.py", line 47, in <module>
    response = read_from_device(serial_connection)
  File "test.py", line 15, in read_from_device
    while ord(current_char) != MSG_START_CHAR: 
TypeError: ord() expected a character, but string of length 0 found

Arduino serial monitor:

<Arduino is ready>
MGIRSPi35

Mi code is:

Arduino sketch:

#include <EEPROM.h>

#define DEVICE_BAUD_RATE        9600

#define EEPROM_SIZE             1024

#define ID_PREFIX               "EMD-"

#define MSG_START_CHAR          '<'
#define MSG_END_CHAR            '>'

#define MSG_GET_DEVICE_ID       "GDI"

const byte buffSize = 40;
char inputBuffer[buffSize];
byte bytesRecvd = 0;
boolean readInProgress = false;
boolean newMsg = false;

char cmd[buffSize] = {0};
int pin = 0;
int value = 0;

unsigned long curMillis;

void setup() {
  if(getDeviceId() == "") {
    setDeviceId();
  }

  Serial.begin(DEVICE_BAUD_RATE);

  while(!Serial) {
    ;
  }

  Serial.println("<Arduino is ready>");
}

void loop() {
  curMillis = millis();
  readMsg();
  processCommand();
}

void readMsg() {
  if(Serial.available() > 0) {
    char x = Serial.read();

    if(x == MSG_END_CHAR) {
      readInProgress = false;
      newMsg = true;
      inputBuffer[bytesRecvd] = 0;
      strcpy(cmd, inputBuffer);
    }

    if(readInProgress) {
      inputBuffer[bytesRecvd] = x;
      bytesRecvd++;

      if(bytesRecvd == buffSize) {
        bytesRecvd = buffSize - 1;
      }
    }

    if(x == MSG_START_CHAR) { 
      bytesRecvd = 0; 
      readInProgress = true;
    }
  }
}

void processCommand() {
  if(strcmp(cmd, MSG_GET_DEVICE_ID) == 0) {
    sendMsg(getDeviceId());
  } else {
    sendMsg("Command Not Found");
  }
}

void sendMsg(String response) {
  if(newMsg) {
    newMsg = false;
    Serial.print("<MSG:");
    Serial.print(cmd);
    Serial.print(",RESPONSE:");
    Serial.print(response);
    Serial.print(",Time:");
    Serial.print(curMillis >> 9);
    Serial.println(">");
  }
}

String getDeviceId() {
  String id = "";

  for(int i=0; i<EEPROM_SIZE; i++) {
    int value = EEPROM.read(i);

    if(value == 0xFF) {
      return id;
    }

    id += char(value);
  }

  return id;
}

void setDeviceId() {
  randomSeed(analogRead(0));

  String id = ID_PREFIX + String(random(1000, 10000)) + "-" + String(random(1000, 10000));

  for(int i=0; i<EEPROM_SIZE; i++) {
    EEPROM.write(i, i<id.length() ? id.charAt(i) : 0xFF);
  }
}

Python script:

#!/usr/bin/python
import os
import serial

MSG_START_CHAR              = '<'
MSG_END_CHAR                = '>'

MSG_GET_DEVICE_ID           = 'GDI'


def read_from_device(serial_connection):  
    response = ""
    current_char = "z"

    while ord(current_char) != MSG_START_CHAR: 
        current_char = serial_connection.read()

    while ord(current_char) != MSG_START_CHAR:
        if ord(current_char) != MSG_START_CHAR:
            response = response + current_char 

        current_char = serial_connection.read()

    return(response)

def write_to_device(serial_connection, msg):
    cmd = MSG_START_CHAR + msg + MSG_END_CHAR

    print(cmd)

    serial_connection.write(cmd)

with serial.Serial('/dev/ttyACM0', 9600, timeout=10) as serial_connection:
    waiting_for_reply = False

    if waiting_for_reply == False:
        print('Sending {0} command to device...'.format(MSG_GET_DEVICE_ID))

        write_to_device(serial_connection, MSG_GET_DEVICE_ID)

        waiting_for_reply = True

    if waiting_for_reply == True:
        while serial_connection.inWaiting() == 0:
            pass

        response = read_from_device(serial_connection)

        print('Reply Received: {0}'.format(response))

        waiting_for_reply = False

    serial_connection.close()
1
Where in your Arduino sketch do you actually write anything out through the serial port. Don't you need a function call like Serial.println(...) in order to cause a serial port write to occur? That's how I did it. The behavior of your Python script is consisent with no serial data being present (read() returns an empty string if there is no data to be read).Paul Cornelius
Hi Paul, thanks for your response. I print to serial port in the sendMsg function of the sketch, and when i send the command via serial monitor, the sketch write into the serial port the correct response <MSG:GDI,RESPONSE:EMD-1707-1993,Time:7>, but when the python script send the same command, the sketch write random strings into the serial portMetallick

1 Answers

0
votes

The problem is that you're comparing an int to a string. In the function read_from_device, this line:

    while ord(current_char) != MSG_START_CHAR: 
  • current_char is a string.
  • ord(current_char) is an integer (in effect, the ASCII value of the one-character string).
  • MSG_START_CHAR is a string.

Thus the comparison is between an int and a string. That will always be False, so the while loop will never exit. Eventually there are no more characters from Arduino, at which point you will be performing ord() on an empty string. That's not allowed so you get a traceback at that point.

You don't need the ord function at all. In Python you just work directly with strings.

Try this:

def read_from_device(serial_connection):  
    while serial_connection.read() != MSG_START_CHAR:
        pass

    response = ""
    while True:
        current_char = serial_connection.read()
        if current_char == MSG_END_CHAR:
            break
        response = response + current_char 
    return response

One thing I don't know here: the Arduino is sending single-byte characters. In Python3 (if that's what you are using), all characters are unicode. I don't know how the Serial library will handle that.