M5Atom icon indicating copy to clipboard operation
M5Atom copied to clipboard

Atom Serial TX is full of errors when it simultaneously receives Serial data

Open thomasfredericks opened this issue 1 year ago • 0 comments

Describe the bug

When the Atom Lite is NOT receiving data, it transmits data to the computer without an error. serial-receive

When the Atom Lite IS simultaneously receiving data, many errors appear in the data sent to the computer. serial-send-receive

To reproduce

Here is an Arduino sketch. Serial is configured at baud 115200. It simply puts any received data in a array and it sends out the values 0 to 255 in a loop constantly.

//#include <M5Atom.h>

unsigned long monChronoMessages;

int inBuffer[256];
int inBufferIndex;
int outBufferIndex;

void setup() {
  // put your setup code here, to run once:
 // M5.begin(false, false, false);

  Serial.begin(115200);

  delay(5000);
}



void loop() {
  // put your main code here, to run repeatedly:

  // À CHAQUE 20 MS I.E. 50x PAR SECONDE
  if (millis() - monChronoMessages >= 20) {
    monChronoMessages = millis();

    for (int i = 0; i < 33; i++) {
      Serial.write(outBufferIndex);
      Serial.flush();
      outBufferIndex = (outBufferIndex + 1) % 256;
    }
  }

  while (Serial.available()) {
    inBuffer[inBufferIndex] = Serial.read();
    inBufferIndex = (inBufferIndex + 1) % 256;
  }
}

Here is a Python test program that checks that all incoming data is the sequence of numbers 0 to 255. If a number falls out of sequence it means that there is was an error while transmitting the data.

Install Serial for Python first: pip install pyserial

import serial
import time
import threading
import sys
import select

def send_sequence(ser, batch_size=33, delay=0.1, stop_event=None):
    try:
        while not stop_event.is_set():
            for start in range(0, 256, batch_size):
                # Generate the batch of 33 numbers (or fewer if at the end of the sequence)
                batch = list(range(start, min(start + batch_size, 256)))
                ser.write(bytes(batch))
                #print(f"Sent batch: {batch}")
                time.sleep(delay)
                if stop_event.is_set():
                    break

    except serial.SerialException as e:
        print(f"Serial error in sender: {e}")
    except KeyboardInterrupt:
        print("Stopping sender...")

def check_sequence(ser, stop_event=None):
    try:
        expected_value = 0  # Start expecting from 0
        ignore_first = True
        while not stop_event.is_set():
            if ser.in_waiting > 0:
                byte = ser.read(1)
                received_value = int.from_bytes(byte, byteorder='big')
                if ignore_first == True:
                    expected_value = (received_value + 1) % 256
                    ignore_first = False
                else:
                    if received_value == expected_value:
                        #print(f"Received correct value: {received_value}")
                        expected_value = (expected_value + 1) % 256
                    else:
                        print(f"Sequence error! Expected {expected_value} but got {received_value}")
                        expected_value = (received_value + 1) % 256

    except serial.SerialException as e:
        print(f"Serial error in receiver: {e}")
    except KeyboardInterrupt:
        print("Stopping receiver...")

def main():
    serial_port = 'COM3'  # Replace with your actual serial port
    baud_rate = 115200
    stop_event = threading.Event()  # Create an event to control stopping the threads

    try:
        # Instantiate the serial connection once
        ser = serial.Serial(serial_port, baud_rate)
        print(f"Connected to {serial_port} at {baud_rate} baud.")

        # Create and start the sending and receiving threads with the shared ser instance
        send_thread = threading.Thread(target=send_sequence, args=(ser, 63, 0.1, stop_event))
        receive_thread = threading.Thread(target=check_sequence, args=(ser, stop_event))

        send_thread.start()
        receive_thread.start()

        # Wait for any key press to stop the threads
        print("Press ctrl+C to stop...")
        input()  # Wait for user input

        # Set the stop event to signal both threads to exit
        stop_event.set()

        # Join threads to wait for them to finish
        send_thread.join()
        receive_thread.join()

    except serial.SerialException as e:
        print(f"Could not open serial port {serial_port}: {e}")
    except KeyboardInterrupt:
        print("Exiting program...")
    finally:
        ser.close()
        print("Serial port closed.")

if __name__ == "__main__":
    main()

Expected behavior

Replace COM3 with your serial port in the Python code:

serial_port = 'COM3'  # Replace with your actual serial port`

The expected behavior is that the Python code should receive all the values 0 to 255 in a loop sequentially. If you run the Python code you will see that some values received are out of sequence. serial-send-receive

What is strange is that if you disable the sending of values from Python (by commenting the following lines) THERE ARE NO MORE ERRORS IN THE VALUES RECEIVED:

#send_thread = threading.Thread(target=send_sequence, args=(ser, 63, 0.1, stop_event))
#send_thread.start()
#send_thread.join()

serial-receive

Screenshots

No response

Environment

  • OS: Tested on Windows an Mac OS
  • IDE &IDE Version: 2.3.3
  • Repository Version: Latest

Additional context

No response

Issue checklist

  • [X] I searched for previous reports in the issue tracker
  • [X] My report contains all necessary details

thomasfredericks avatar Nov 12 '24 22:11 thomasfredericks