Skip to main content
2 of 4
added 2297 characters in body

Debugging different serial receive / send behavior between Uno & Mega

I have a simple text parser (as a component of a larger project) coded up and seemingly working correctly. It parses a key-value delimited command like the following: <key1=value1;key2=value2;key3=value3>.

At present, the functionality is very silly just to test it out: when the Arduino finds a key named "text", it composes a response of a similar form, but just with one key "text_return" and the same value, and sends it back via serial. That is, when the command <key1=value1;text=123;key3=value3> is sent, I expect a response from the Arduino of <text_return=123>.

I then am connecting the board to my laptop via USB, and testing it out, both with the Serial Monitor in Arduino's IDE, and with a simple program coded in python.

So here's the confusing part:

  1. Serial Monitor with Uno: works
  2. Python with Uno: works
  3. Serial Monitor with Mega: works
  4. Python with Mega: does NOT work

Specifically, by "does not work", I mean that the Mega seems to receive the serial input from the program, but does not issue a response.

Here's the terminal output from test #2 as displayed by the python program, showing the command-response I'd expect (I parsed the received command into a dict):

SENT: <text=1953>
SENT: <text=1954>
SENT: <text=1954>
SENT: <text=1954>
SENT: <text=1954>
SENT: <text=1954>
SENT: <text=1954>
SENT: <text=1954>
RECD: {'text_return': 1954}
SENT: <text=1954>
RECD: {'text_return': 1954}
SENT: <text=1954>
RECD: {'text_return': 1954}
SENT: <text=1954>
RECD: {'text_return': 1954}

And here's the terminal output from the non-working test#4 - i.e.: no response.

SENT: <text=2001>
SENT: <text=2002>
SENT: <text=2002>
SENT: <text=2002>
SENT: <text=2002>
SENT: <text=2002>
SENT: <text=2002>
SENT: <text=2002>
SENT: <text=2002>
SENT: <text=2002>
SENT: <text=2002>
SENT: <text=2003>
SENT: <text=2003>
SENT: <text=2003>
SENT: <text=2003>
SENT: <text=2003>
SENT: <text=2003>
SENT: <text=2003>
SENT: <text=2003>

In all cases, I'm using 9600 Baud; the only change between the two tests is that I select the Uno vs. Mega / Mega 2560 board in the Arduino IDE.

The Mega board is an Elegoo Mega2560 R3. The code may be extraneous, but to help:

Opening the port to the arduino from python

def FindArduino(baud=9600, timeout=0):
  initial_time = time.time()
  arduino_found = False
  attempted = False
  while not attempted or time.time() - initial_time < timeout and not arduino_found:
    attempted = True
    ports = serial.tools.list_ports.comports(include_links=False)
    for port in ports:
      manufacturer = port.manufacturer
      if manufacturer and 'arduino' in manufacturer.lower():
        arduino_port = port
        arduino_found = True

  if arduino_found:
    try:
      arduino = serial.Serial(arduino_port.device, baud, timeout=0)
      arduino.reset_input_buffer()
      arduino.reset_output_buffer()
    except serial.SerialException:
      arduino_found = False
  
  if arduino_found:
    return arduino
  else:
    return None

Sending the serial in python, to Arduino

  cmd = '<text=%d>' % (time.time() - 1586218411)
  print('SENT: %s' %cmd)
  cmd = bytes(cmd, 'ascii')
  arduino.write(cmd)
  time.sleep(.1)

Receiving the serial in python, from Arduino

  cmds = []

  if (arduino.in_waiting>0):
    buffer += arduino.read(arduino.in_waiting).decode('ascii')
    while COMMAND_END_CHAR in buffer:
      end_char_pos = buffer.find(COMMAND_END_CHAR)
      potential_command = buffer[:end_char_pos]
      if COMMAND_START_CHAR in potential_command:
        cmds.append(potential_command[potential_command.find(COMMAND_START_CHAR)+1:])
      buffer = buffer[end_char_pos+1:]

  return (buffer, cmds)

And the full code on the Arduino itself

// command indicators
char START_MARKER = '<';
char END_MARKER = '>';

const int MAX_KEY_VALUE_PAIRS = 3;  // maximum number of key-value pairs in message
const int MAX_ELEMENT_CHARS = 30;  // the maximum number of characters (+1 for terminator) in a key or a value

// message format: <key1=value1;key2=value2;key3=value3>
const int MAX_MESSAGE_CHARS = (MAX_KEY_VALUE_PAIRS * (MAX_ELEMENT_CHARS + 1)) * 2 + (MAX_KEY_VALUE_PAIRS - 1) + 2;  // maximum message size

char received_chars[MAX_MESSAGE_CHARS];
bool new_data = false;
char written_chars[MAX_MESSAGE_CHARS];

char *text =  {'\0'};


void ParseData(char *str) {
  // This picks off the ;-delimited key-value pairs and assigns them to a multi-dim array
  char * pch;
  int pairs_count = 0;
  char config[MAX_KEY_VALUE_PAIRS][2][MAX_ELEMENT_CHARS];

  pch = strtok(str, "=");

  while (pch != NULL)
  {
    strcpy(config[pairs_count][0], pch);
    pch = strtok(NULL, ";");
    if (pch == NULL) break;

    strcpy(config[pairs_count][1], pch);
    pairs_count++;
    pch = strtok(NULL, "=");
    if (pch == NULL) break;
  }

  for(int i=0;i<pairs_count;i++) {
    if (strcmp(config[i][0], "text")==0) 
      strcpy(text, config[i][1]);
    WriteSerial();
  }
}

void ReadSerial(){
  // After calling ReceiveText to empty the buffer, if a complete command has been found,
  // parse that command.
  new_data = ReceiveText();
  if (new_data == true) {
    char temp_chars[MAX_MESSAGE_CHARS];  // temporary array for use when parsing
    strcpy(temp_chars, received_chars);
    received_chars[0] = '\0';
    ParseData(temp_chars);
  }
}

void WriteSerial(){
  sprintf(written_chars, "<text_return=%s>\n", text);
  Serial.write(written_chars);
  written_chars[0] = '\0';
}

boolean ReceiveText() {
  // This dumps the characters on the buffer so far received_chars, searching for an END_MARKER
  // along the way; if it finds one, it goes back to find a START_MARKER; if that is also found,
  // the string within is the ;-delimited set of key value pairs
  static boolean recv_in_progress = false;
  static byte ndx = 0;
  char rc;

  boolean new_data = false;
  while (Serial.available() > 0 && new_data == false) {
    rc = Serial.read();

    if (recv_in_progress == true) {
      if (rc != END_MARKER) {
        received_chars[ndx] = rc;
        ndx++;
        if (ndx >= MAX_MESSAGE_CHARS) {
          ndx = MAX_MESSAGE_CHARS - 1;
        }
      } else {
        received_chars[ndx] = '\0'; // terminate the string
        recv_in_progress = false;
        ndx = 0;
        new_data = true;
      }
    } else if (rc == START_MARKER) {
        recv_in_progress = true;
    }
  }
  return new_data;
}

void setup() {  
  Serial.begin(9600);
  text = (char*)malloc(25);
 
  received_chars[0] = '\0';
  
}

void loop() {
  ReadSerial();
}

UPDATE

Using the suggested libraries, I updated the code. The comms in both directions for simple test data are working, though I can't seem to convert the packet received by the Arduino into a string correctly. Here is the output I see from my python terminal; I'd expect to receive back the first character of what was sent (i.e.: the "0", then "1"), rather than just the initialized value of "S".

SENT: 0.20
RCVD: S
SENT: 0.41
RCVD: S
SENT: 0.61
RCVD: S
SENT: 0.82
RCVD: S
SENT: 1.02
RCVD: S
SENT: 1.23
RCVD: S
SENT: 1.43
RCVD: S
SENT: 1.64
RCVD: S

Python code import time from pySerialTransfer import pySerialTransfer as txfer

if name == 'main': try: link = txfer.SerialTransfer('/dev/cu.usbmodem14201')

link.open()
time.sleep(2) # allow some time for the Arduino to completely reset
base = time.time()

while True:
  time.sleep(0.2)
  s = '%.2f' % (time.time() - base)
  l = len(s)
  for i in range(l):
    link.txBuff[i] = s[i]

  link.send(l)

  while not link.available():
    if link.status < 0:
      print('ERROR: {}'.format(link.status))


  response = ''
  for index in range(link.bytesRead):
    response += chr(link.rxBuff[index])

  print('SENT: %s' % s)
  print('RCVD: %s' % response)

except KeyboardInterrupt: link.close()

**Arduino code**

#include "SerialTransfer.h"

char str[1000];

SerialTransfer myTransfer;

void setup() { str[0] = 'S'; str[1] = '\n'; Serial.begin(115200); myTransfer.begin(Serial); }

void loop() {

// send bytes myTransfer.txBuff[0] = str[0]; myTransfer.sendData(1); delay(100);

if(myTransfer.available()) { // receive bytes byte bytes_to_read = myTransfer.bytesRead; for(byte i = 0; i < bytes_to_read; i++) str[i] = myTransfer.rxBuff[i]; str[bytes_to_read] = '\0';

} else if(myTransfer.status < 0) { Serial.print("ERROR: ");

if(myTransfer.status == -1)
  Serial.println(F("CRC_ERROR"));
else if(myTransfer.status == -2)
  Serial.println(F("PAYLOAD_ERROR"));
else if(myTransfer.status == -3)
  Serial.println(F("STOP_BYTE_ERROR"));

} }



Thank you!