Issues with PySerial and Arduino

I am trying to send a 2D python list (called grid in the code) of bits to an esp32 (specifically the ESP32-WROOM-DA MODULE) as a part of a larger project. (Project explained at bottom if interested)

I have managed to get the grid into seperate bytes. Effectivly each row of the grid is a seperate bytes object with 4 bytes. I am then trying to send these byte objects to the arduino.

The python and arduino code are currently just trying to send one row (4 bytes, one byte object, 32 bits). But the arduino seems to be only reading or only writing 0. It currently returns the row sent. I know it is at least running lines 37 and 38 in the arduino script because I truned the onboard LED on inside of the if statement.

I have been trying a lot of things and have had some success with online examples. I'm guessing I'm missing something with regards serial communication but I'm not sure what. I'm sure there are better libraries out there than pySerial but it seems straitforward and like it should be able to do the job. I am headed into my Junior year of Computer Engineering so I have some experience and I do want to learn.

//Onboard LED
#define LED_BUILTIN 2

//same as the data in the python script
uint32_t grid[16] = {
  0b00000000000000000000000000000000, 
  0b00000000000011110000000000000000, 
  0b00000000001111111110000000000000, 
  0b01100000001111111110000000000000, 
  0b11111000001111111110000000000000, 
  0b11111110001111111111000000000000, 
  0b11111111001111111111000000000000, 
  0b11111111111111111111000000000000, 
  0b11111111111111111111000000000000, 
  0b11111111111111111111000000000000, 
  0b11111001111111111111000000000000, 
  0b11110000001111111110000000000000, 
  0b11100000000111111110000000000000, 
  0b11000000000111111110000000000000, 
  0b10000000001111111111111000000000, 
  0b10000000111111111111111100000000
};

//bool check=0;
uint32_t input = 0;
void setup() {
  Serial.begin(115200);
  Serial.setTimeout(1); 
  pinMode(LED_BUILTIN, OUTPUT);

  //Blink the LED to show the script started
  digitalWrite(LED_BUILTIN, HIGH);
  delay(500);
  digitalWrite(LED_BUILTIN, LOW);
}


void loop() {
  //tries to read the 4 bytes sent by the python script into a Long
  if(Serial.available()){
    //shifts the first set of input 16 bits
    input = (Serial.parseInt()<<16);
    input = input|(Serial.parseInt());
  }

  //if the row was read correctly set the debug LED high
  if(input == grid[15]){
    digitalWrite(LED_BUILTIN, HIGH);
  }
  
  //outputs 8 bits at a time
  Serial.write(input&0xFF);
  Serial.write(input&0xFF00);
  Serial.write(input&0xFF0000);
  Serial.write(input&0xFF000000);
}


Python Script:

import serial
import time

#I know I should use if name=main but this is just a test script

#Initialize
arduino = serial.Serial(port='COM8', baudrate=115200, timeout=1)

#Grid of bits, 32 in each list, 16 lists
testGrid = [
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0]
]

#Sleep for a second for the esp32 to blink
time.sleep(1)

#convert every intiger in the grid to a str of 0 or 1
for j, row in enumerate(testGrid):
    for i, val in enumerate(row):
        row[i]=str(val)
    testGrid[j] = row


#convert each row of the grid to a list of 4 bytes (for the 32 bits in each row)
for i, row in enumerate(testGrid):
    #convert row into a string of binary values
    sb = ''.join(row)
    #This could flip each row if desired[::-1]
    
    row_updated = []

    #seperate the row into 4 chunks of 8 bits each
    #At one point considered adding this as a signal byte
    #row_updated.append(int('11111111', 2))
    row_updated.append(int(sb[0:8],2))
    row_updated.append(int(sb[8:16],2))
    row_updated.append(int(sb[16:24],2))
    row_updated.append(int(sb[24:32],2))

    #Actually convert to bytes
    testGrid[i] = bytes(row_updated)
    print(testGrid[i])

#Tried this, probably not nescesary
while not arduino.is_open:
    None
arduino.reset_input_buffer()
arduino.reset_output_buffer()

print("open")

while(True):
    #Actually sends bytes to the esp32
    arduino.write(testGrid[15])
    #Maybe a carrage return is nescesary?
    #arduino.write(b'\n')
    #Gives time for the arduino to process
    time.sleep(0.5)
    #Reads four bytes from arduino
    print(arduino.read(4))
    

Output of Python Script:
b'\x00\x00\x00\x00'
b'\x00\x0f\x00\x00'
b'\x00?\xe0\x00'
b'`?\xe0\x00'
b'\xf8?\xe0\x00'
b'\xfe?\xf0\x00'
b'\xff?\xf0\x00'
b'\xff\xff\xf0\x00'
b'\xff\xff\xf0\x00'
b'\xff\xff\xf0\x00'
b'\xf9\xff\xf0\x00'
b'\xf0?\xe0\x00'
b'\xe0\x1f\xe0\x00'
b'\xc0\x1f\xe0\x00'
b'\x80?\xfe\x00'
b'\x80\xff\xff\x00'
open
b'\x00\x00\x00\x00'
b'\x00\x00\x00\x00'
b'\x00\x00\x00\x00'
b'\x00\x00\x00\x00'
b'\x00\x00\x00\x00'

The b'\x00\x00\x00\x00' line repeated every 0.05 seconds (the delay in the while loop).

Larger Project Explained:
I am not sure exactly where I want this project to end up but so far I have managed to use googles mediapipe library to created a pixilated version of a person that updates in real time (uses pygame to draw the pixels in the video below). It uses a 2D grid list similar to the one above to draw the pixels.

I have seperatley created an LED matrix panel with 16x32 pixels with the WS2812B strip that can be controlled by an esp32. I want to send the pixel data to the esp32 and maybe have the esp32 do some sort of LED effect where there is a person vs where there isnt.

Demo Video:

I've written a small tutorial on interfacing with Python. See Two ways communication between Python3 and Arduino

May be that can give you ideas on how to approach this (I use '\n' to denote the end of a message in the tutorial, if you use a binary protocol you'll need to devise another strategy to sync the writer and the reader)

I apreciate the help a lot. The links were excellent. I cobled together a python script and an arduino script that at least sent one grid. I'll spend some more time cleaning things up and putting back in some of the things I took out. I might also try to get the grid into a single bytes object so I can send it over all at once.

// Example 3 - Receive with start- and end-markers
//Onboard LED
#define LED_BUILTIN 2
#define numChars 4

const uint32_t END  = 1162757120;
const uint32_t GRID = 1196575044;

char receivedChars[numChars];

boolean newData = false;
boolean sameGrid = false;
boolean gridActive = false;

uint32_t grid[16] = {
  0b00000000000000000000000000000000, 
  0b00000000000011110000000000000000, 
  0b00000000001111111110000000000000, 
  0b01100000001111111110000000000000, 
  0b11111000001111111110000000000000, 
  0b11111110001111111111000000000000, 
  0b11111111001111111111000000000000, 
  0b11111111111111111111000000000000, 
  0b11111111111111111111000000000000, 
  0b11111111111111111111000000000000, 
  0b11111001111111111111000000000000, 
  0b11110000001111111110000000000000, 
  0b11100000000111111110000000000000, 
  0b11000000000111111110000000000000, 
  0b10000000001111111111111000000000, 
  0b10000000111111111111111100000000
};

uint32_t gridIn[16] =  {};

void setup() {
    pinMode(LED_BUILTIN, OUTPUT);

    //Blink the LED to show the script started
    digitalWrite(LED_BUILTIN, HIGH);
    delay(500);
    digitalWrite(LED_BUILTIN, LOW);
    
    Serial.begin(115200);
    Serial.setTimeout(1); 
    Serial.println("OK");
}


void loop() {
    recvWithStartEndMarkers();
    showNewData();
    
    //check if data was transfered successfully
    sameGrid = true;
    for(int i=0; i<16; i++){
      if(grid[i] != gridIn[i]){
        sameGrid = false;
        //Serial.println(i);
      }
    }
    if(sameGrid){
      digitalWrite(LED_BUILTIN, HIGH);
    }

}

void recvWithStartEndMarkers() {
    static boolean recvInProgress = false;
    static byte ndx = 0;
    char startMarker = '<';
    char endMarker = '>';
    char rc;

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

        if (recvInProgress == true) {
            if (rc != endMarker) {
                receivedChars[ndx] = rc;
                ndx++;
                if (ndx > numChars) {
                    ndx = numChars - 1;
                }
            }
            else {
                recvInProgress = false;
                ndx = 0;
                newData = true;
            }
        }

        else if (rc == startMarker) {
            recvInProgress = true;
        }
        rc = 0;
    }
    if(ndx < numChars){
      if(ndx==1){
        receivedChars[1] = 0;
        receivedChars[2] = 0;
        receivedChars[3] = 0;
      }
      else if(ndx==2){
        receivedChars[2] = 0;
        receivedChars[3] = 0;
      }
      else if(ndx==3){
        receivedChars[3] = 0;
      }
    }
}

void showNewData() {
    static uint8_t rowIter = 0;
    static uint32_t row = 0;

    if (newData == true) {
        //similar to before
        //Serial.println(receivedChars);
        newData = false;
        row = (receivedChars[0]<<24)|(receivedChars[1]<<16)|(receivedChars[2]<<8)|(receivedChars[3]);
        Serial.println(row);
        //updates grid
        //grid send start
        //grid send end
        //checks if row =="END" using ascii
        if(row == END){
          gridActive = false;
        }
        //while the grid is being sent
        if(gridActive){
          if(rowIter<16){
            gridIn[rowIter] = row;
            rowIter++;
          }
        }
        //checks if row == "grid" using ascii
        if(row == GRID){
          rowIter = 0;
          gridActive = true;
        }
    }
}
import sys, threading, queue, serial, time

arduinoQueue = queue.Queue()
localQueue = queue.Queue()


#Grid of bits, 32 in each list, 16 lists
testGrid = [
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0]
]

#convert every intiger in the grid to a str of 0 or 1
for j, row in enumerate(testGrid):
    for i, val in enumerate(row):
        row[i]=str(val)
    testGrid[j] = row


#convert each row of the grid to a list of 4 bytes (for the 32 bits in each row)
for i, row in enumerate(testGrid):
    #convert row into a string of binary values
    sb = ''.join(row)
    #This could flip each row if desired[::-1]

    row_updated = []

    #seperate the row into 4 chunks of 8 bits each
    #At one point considered adding this as a signal byte
    row_updated.append(int(sb[0:8],2))
    row_updated.append(int(sb[8:16],2))
    row_updated.append(int(sb[16:24],2))
    row_updated.append(int(sb[24:32],2))

    #Actually convert to bytes
    testGrid[i] = bytes(row_updated)


def listenToArduino():
    message = b''
    while True:
        incoming = arduino.read()
        if (incoming == b'\n'):
            #arduinoQueue.put(message.decode('utf-8').strip().upper())
            arduinoQueue.put(message)
            message = b''
        else:
            if ((incoming != b'') and (incoming != b'\r')):
                message += incoming

#Sends Data to Arduino
def listenToLocal():
    while True:
        #command = sys.stdin.readline().strip().upper()
        #localQueue.put(command)
        
        localQueue.put(b'GRID')
        for row in testGrid:
            localQueue.put(row)
        localQueue.put(b'END')


def configureUserInput():
    localThread = threading.Thread(target=listenToLocal, args=())
    localThread.daemon = True
    localThread.start()


def configureArduino():
    global arduino
    arduino = serial.Serial(port='COM8', baudrate=115200, timeout=0.1)
    arduinoThread = threading.Thread(target=listenToArduino, args=())
    arduinoThread.daemon = True
    arduinoThread.start()


# ---- CALLBACKS UPON MESSAGES -----
def handleLocalMessage(aMessage):
    #print("=> [" + aMessage + "]")
    #arduino.write(aMessage.encode('utf-8'))
    arduino.write(b'<')
    arduino.write(aMessage)
    arduino.write(b'>')
    arduino.write(bytes('\n', encoding='utf-8'))

def handleArduinoMessage(aMessage):
    #print("<= [" + aMessage + "]")
    print(aMessage)


# ---- MAIN CODE -----

configureArduino()                                      # will reboot AVR based Arduinos
configureUserInput()                                    # handle stdin 


print("Waiting for Arduino")
"""
# --- A good practice would be to wait for a know message from the Arduino
# for example at the end of the setup() the Arduino could send "OK"
while True:
    if not arduinoQueue.empty():
        if arduinoQueue.get().decode('utf-8').strip() == "OK":
            break
"""
time.sleep(1)
print("Arduino Ready")


# --- Now you handle the commands received either from Arduino or stdin
while True:
    if not arduinoQueue.empty():
        handleArduinoMessage(arduinoQueue.get())

    if not localQueue.empty():
        handleLocalMessage(localQueue.get())

I'll make sure to source your code in the final revision too and definitly wherever I post it.

1 Like

Thanks again for the link and the more basic rundown ascociated. I got things working but I'm hoping to get a little more help. I got the python script to send data to the esp32 controlling a 16x32 Matrix panel as well as update continuously with the image recognition on the PC.

However the serial communication takes over a second to send the data. I'm hoping it can be sped up and I'm not running into limitations of the pyserial library. It's sending less than 1000 bits and the baud rate is 115200 so in theory it should be able to communicate much faster if the python script sends the information fast enough.

Demo Video
The image recognition isn't as good with the background I had but it mostly worked.

Here is the code. I tried to keep things seperated so it's easier to follow.

Notes about Arduino Script:
There is an I2C display. It is a part of this project but right now is just used to show how long different proceeses take.

The two functions doing serial input are:
recvWithStartEndMarkers();
showNewData();

They're not copies from https://forum.arduino.cc/t/serial-input-basics-updated/382007 but are based off of it.

Notes about Python Script:
I switched to multiprocessing instead of multithreading because the image recognition is pretty computationally intensive. (Helpful Source) I tried to keep things seperate so hopefully it's easier to weigh in on the serial part of it.

There are 3 tasks:
P1: Image Processing
P2: Send Serial to Arduino
P3: Check for quit command (had some other functionality removed)

There are 3 Queues:
arduinoQueue (currently unused)
bytesQueue (used to send an array of bytes objects from P1 to P2)
quitQueue (used to end all tasks safely)

I am not collecting serial information from the arduino (it made it easier to only access the com port in one process).

The image recoginition updates the bytesQueue once every 1.1 seconds (look for DTIME). This is just done so the bytesQueue doesn't stack up. If you set DTIME to say 0.5, the display still only updates once per second.

Python Code Sample Output:

Waiting for Arduino

Arduino Ready

INFO: Created TensorFlow Lite XNNPACK delegate for CPU.

Update

0.8003032207489014

Update

0.7430198192596436

Update

0.8796906471252441

Update

0.6871297359466553

Update

0.8116204738616943

Arduino Code

//MAIN
#include "FastLED.h"
#include <Adafruit_SSD1306.h>

//OLED SETUP
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 32 // OLED display height, in pixels

#define OLED_RESET     -1 // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

//Onboard LED
#define LED_BUILTIN 2

//FAST LED SETUP
#define LED_PIN     5
#define NUM_LEDS    512
#define COLOR_ORDER         GRB
#define MAX_POWER_MILLIAMPS 3500

//creates an array of length 512 to store LED info.
CRGBArray<NUM_LEDS> leds;

//Grid for Human Display
uint32_t gridIn[16] = {
  0b00000000000000000000000000000000,
  0b00000000000000000000000000000000,
  0b00000000000000111110000000000000,
  0b00000000000011111111000000000000,
  0b00000000000111111111100000000000,
  0b00000000000111111111100000000000,
  0b00000000000111111111100000000000,
  0b00000000000111111111100000000000,
  0b00000000000111111111100000000000,
  0b00000000001111111111100000000000,
  0b00000000000111111111000000000000,
  0b00000000000011111110000000000000,
  0b00000000000001111110000000000000,
  0b00000000000001111110000000000000,
  0b00000000000001111111100000000000,
  0b00000000000111111111111100000000,
};

//for calculating time b/w
uint32_t prevTime = 0;
long dTime = 0;

//used to update matrix
bool bit=0;
uint32_t row = 0;

//For Serial Communication
#define numChars 4

const uint32_t END  = 1162757120;
const uint32_t GRID = 1196575044;

char receivedChars[numChars];

boolean newData = false;
boolean sameGrid = false;
boolean gridActive = false;
boolean newGrid = false;


void setup() {
  setup_OLED_FastLED(); //sets up OLED screen and FastLED

  //Blink the Onboard LED to show the script started
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, HIGH);
  delay(500);
  digitalWrite(LED_BUILTIN, LOW);

  //Send OK to show script ready
  Serial.begin(115200);
  Serial.setTimeout(0.01); 
  Serial.println("OK");

  human_setup();
}

void loop() {

  //Send and recieve serial instructions
  recvWithStartEndMarkers();
  showNewData();

  if(newGrid){
    prevTime = millis();
    newGrid = false;
    //Serial.println("Update");
    for (int j=0; j<16; j++){
      row = gridIn[j];
      for (int i=0; i<32; i++){
        bit = row&1;
        if(bit){
          leds[XY(i, 15-j)] = 0x00003D;
        }
        else{
          leds[XY(i, j)] = 0;
        }
        row >>= 1;
      }
    }
    FastLED.show();
  }
}

void recvWithStartEndMarkers() {
    static boolean recvInProgress = false;
    static byte ndx = 0;
    char startMarker = '<';
    char endMarker = '>';
    char rc;

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

        if (recvInProgress == true) {
            if (rc != endMarker) {
                receivedChars[ndx] = rc;
                ndx++;
                if (ndx > numChars) {
                    ndx = numChars - 1;
                }
            }
            else {
                recvInProgress = false;
                ndx = 0;
                newData = true;
            }
        }

        else if (rc == startMarker) {
            recvInProgress = true;
        }
        rc = 0;
    }
    if(ndx < numChars){
      if(ndx==1){
        receivedChars[1] = 0;
        receivedChars[2] = 0;
        receivedChars[3] = 0;
      }
      else if(ndx==2){
        receivedChars[2] = 0;
        receivedChars[3] = 0;
      }
      else if(ndx==3){
        receivedChars[3] = 0;
      }
    }
}

void showNewData() {
    static uint8_t rowIter = 0;
    static uint32_t row = 0;

    if (newData == true) {

        newData = false;
        row = (receivedChars[0]<<24)|(receivedChars[1]<<16)|(receivedChars[2]<<8)|(receivedChars[3]);

        //checks if row =="END" using ascii
        if(row == END){
          gridActive = false;
          newGrid=true;

          display.clearDisplay();
          display.setCursor(0, 0);
          dTime = millis()-prevTime;
          display.print(dTime);
          display.display();
        }
        //while the grid is being sent
        if(gridActive){
          if(rowIter<16){
            gridIn[rowIter] = row;
            rowIter++;
          }
        }
        //checks if row == "grid" using ascii
        if(row == GRID){
          prevTime = millis();
          rowIter = 0;
          gridActive = true;
        }
    }
}

uint16_t XY(uint8_t x, uint8_t y)
{
  if(x<=31 && y<=15){
    uint16_t i;
    //check for first or second matrix 
    bool addAtEnd = false;
    if (x>15){
      x -= 16;
      addAtEnd = true;
    }
    //&0x01 checks if odd
    if(y & 0x01){
      // Even rows run backwards
      uint8_t reverseX = 15 - x;
      i = (y * 16) + reverseX;
    } 
    else {
      // odd rows run forwards
      i = (y * 16) + x;
    }
    if(addAtEnd){
      i+=256;
    }
    /*
    Serial.print("x: ");
    Serial.print(x);
    Serial.print(" y: ");
    Serial.print(y);
    Serial.print(" i: ");
    Serial.println(i);*/
    return i;
  }
  else{
    //Serial.println(" bad val");
    return NUM_LEDS;
  }
}

void setup_OLED_FastLED(){
  //Setup I2C Display**********************************************************
  // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
  if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
    Serial.println("SSD1306 allocation failed");
    for(;;); // Don't proceed, loop forever
  }
  // Show initial display buffer contents on the screen --
  // the library initializes this with an Adafruit splash screen.
  display.display();
  delay(500); // Pause for 2 seconds
  
  // Clear the buffer
  display.clearDisplay();
  display.setTextColor(SSD1306_WHITE);
  display.setTextSize(3); // Draw 3X-scale text

  //Setup FastLED************************************************************
  FastLED.addLeds<WS2812B,LED_PIN,COLOR_ORDER>(leds, NUM_LEDS);
    //.setCorrection(TypicalLEDStrip);
  FastLED.setDither(150 < 255);
  FastLED.setMaxPowerInVoltsAndMilliamps( 5, MAX_POWER_MILLIAMPS);
  FastLED.setBrightness(180);
  /*https://fastled.io/docs/group___color_enums.html#gadf6bcba67c9573665af20788c4431ae8
  https://fastled.io/docs/color_8h_source.html*/ 
  FastLED.setCorrection(0xE696DC);/*230, 150, 220*/
}

void human_setup(){
  FastLED.clear();
  display.setCursor(0, 0);
  display.stopscroll();
  display.clearDisplay();
  display.setTextSize(2);
  
  /*
  display.println(F("Human "));
  display.print(F("CV"));
  display.setTextSize(3);
  display.display();
  display.startscrollright(0x00, 0x0F); 
  */
  draw_human();
}

void draw_human(){
  for (int j=0; j<16; j++){
    row = gridIn[j];
    for (int i=0; i<32; i++){
      bit = row&1;
      if(bit){
        leds[XY(i, 15-j)] = 0x00003D;
      }
      else{
        leds[XY(i, j)] = 0;
      }
      row >>= 1;
    }
  }
  FastLED.show();
}

Warning
This code may continue to run tasks after you run it so be careful.

Python Code

from multiprocessing import Process, Manager
import serial, time
import cv2
from mediapipe import solutions
import keyboard

def P1_buildGrid(bytesQueue, quitQueue):
    #Grid of bits, 32 in each list, 16 lists
    startGrid = [
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0]
    ]
    
    def gridToBytes(grid_f):
        grid_func = []
        #convert each row of the grid to a list of 4 bytes (for the 32 bits in each row)
        for i, row in enumerate(grid_f):
            #convert row into a string of binary values
            sb = ''.join(row)[::-1]
            #This could flip each row if desired[::-1]

            row_updated = []

            #seperate the row into 4 chunks of 8 bits each
            #At one point considered adding this as a signal byte
            row_updated.append(int(sb[0:8],2))
            row_updated.append(int(sb[8:16],2))
            row_updated.append(int(sb[16:24],2))
            row_updated.append(int(sb[24:32],2))

            #Actually convert to bytes
            grid_func.append(bytes(row_updated))

        return grid_func
    
    
    #print("Waiting for Arduino")
    time.sleep(1)
    #print("Arduino Ready")
    
    #MEDIAPIPE CONSTANTS------------------------------------
    #CHANGE THESE
    NumPixelsX = 32
    NumPixelsY = 16

    PX_ACCEPTANCE=0.5 #B/W 0-1, less means more pixels will turn on, more means less pixels will turn on
    PX_CHOICE=0.2

    #Defines update speed of bytes to arduino
    DTIME = 1.1

    #These must match the ratio of numPixels
    #Actual Camera Dimensions are 480x640
    CAM_HEIGHT = 320
    CAM_LENGTH = 640
    CAM_OFFSET_H = 80
    PX_LEN = int(320/NumPixelsY)
    PX_MIN = int((PX_LEN*PX_LEN)*PX_ACCEPTANCE)


    #Previous Time
    prevTime = time.time()

    #Set Up Mediapipe------------------------
    mp_seg = solutions.selfie_segmentation

    # For webcam input:
    cap = cv2.VideoCapture(0)

    #model_selection: 0 or 1. 0 to select a general-purpose model, and 1 to
    #          select a model more optimized for landscape images.

    with mp_seg.SelfieSegmentation(model_selection=0) as pose:
        grid = startGrid
        prevTime = time.time()
        
        #MAIN LOOP---------------------------------------------------
        while cap.isOpened():
            #Update IMAGEPIPE --------------------------------------
            success, image = cap.read()

            if not success:
                print("Ignoring empty camera frame.")
                # If loading a video, use 'break' instead of 'continue'.
                continue

            # To improve performance, optionally mark the image as not writeable to
            # pass by reference.
            image.flags.writeable = False
            image = cv2.flip(image, 1)
            image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)

            #Generate Mask
            results = pose.process(image)

            #Called to check a pixel from the mask
            def check_mask(x,y):
                px_x_end = (x+1)*PX_LEN
                px_y = (y*PX_LEN) + CAM_OFFSET_H
                px_y_end = (y+1)*PX_LEN + CAM_OFFSET_H
                px_x=x*PX_LEN
                px_count=0

                try:
                    while(px_y<px_y_end):
                        px_x=x*PX_LEN
                        while(px_x<px_x_end):
                            if(results.segmentation_mask[px_y][px_x] > PX_CHOICE):
                                px_count+=1
                            px_x+=1
                        px_y+=1

                    if(px_count>PX_MIN):
                        return "1"
                    else:
                        return "0"
                except:
                    return "0"


            #UPDATE GRID
            #Update only if enough time has passed
            if(time.time()-prevTime >= DTIME):
                for y, row in enumerate(grid):
                    for x, px in enumerate(row):
                        prev=grid[y][x]
                        grid[y][x]=check_mask(x,y)

                bytesQueue.put(gridToBytes(grid))
                #print(gridBytes)
                prevTime = time.time()
            
            if not quitQueue.empty():
                break

    cap.release()


#Update Serial-----------------------------------------
def P2_updateSerial(bytesQueue, arduinoQueue, quitQueue):
    arduino = serial.Serial(port='COM8', baudrate=115200, timeout=0.01)

    print("Waiting for Arduino")
    time.sleep(2)
    print("Arduino Ready")
    
    def write(data):
        arduino.write(b'<')
        time.sleep(0.001)
        arduino.write(data)
        time.sleep(0.001)
        arduino.write(b'>')
        time.sleep(0.001)
    
    while True:
        #Output
        if(not bytesQueue.empty()):
            print("Update")
            gridBytes = bytesQueue.get()
            write(b'<')
            write(b'GRID')
            write(b'>')
            prevTime = time.time()
            for row in gridBytes:
                write(row)
                arduino.write(bytes('\n', encoding='utf-8'))
                time.sleep(0.001)
            print(time.time()-prevTime)
            write(b'END')

        #Input
        if not arduinoQueue.empty():
            print(arduinoQueue.get())
        
        if not quitQueue.empty():
            break


#Listents to arduino for input
def P3_listenToArduinoAndQuit(arduinoQueue, quitQueue):
    #arduino = serial.Serial(port='COM8', baudrate=115200, timeout=0.1)
    message = b''
    while True:
        """
        incoming = arduino.read()
        if (incoming == b'\n'):
            #arduinoQueue.put(message.decode('utf-8').strip().upper())
            arduinoQueue.put(message)
            message = b''
        else:
            if ((incoming != b'') and (incoming != b'\r')):
                message += incoming
        """
        if keyboard.is_pressed('q'):
            for i in range(30):
                quitQueue.put(1)
            break


if __name__ == '__main__':
    """
    Processes:
    P1_buildGrid(bytesQueue)
    P2_updateSerial(bytesQueue, arduinoQueue)
    P3_listenToArduino(arduinoQueue)
    """

    # Create an instance of the Manager
    with Manager() as manager:
        # Create a queue within the context of the manager
        arduinoQueue = manager.Queue()
        bytesQueue   = manager.Queue()
        quitQueue    = manager.Queue()

        #Create two instances of the Process class, one for each function
        p1 = Process(target=P1_buildGrid, args=(bytesQueue, quitQueue,))
        p2 = Process(target=P2_updateSerial, args=(bytesQueue, arduinoQueue, quitQueue,))
        p3 = Process(target=P3_listenToArduinoAndQuit, args=(arduinoQueue, quitQueue,))

        #Start processes
        p1.start()
        p2.start()
        p3.start()
        
        # Wait for processes to finish
        p1.join()
        p2.join()
        p3.join()

I would take a binary communication approach to ensure everything goes fast

here is an example where I send a 64 byte array to the arduino based on the code from my tutorial.

I set the baud rate to 1 million as both an arduino and your Mac/PC should be fine at that speed.

To mimic your code, I added a task generateRandomGrid which builds the 64 byte grid with random data except the first 4 bytes which are an incrementing "grid frame counter", and submit it to the bytesQueue and then sleeps for 40s.

The main code checks that queue and calls sendGrid() with the oldest grid if the queue is not empty in a similar way as the other queues.

the sendGrid() functions sends first a 4 byte header (0xDEADBEEF) that is used as a way to sync the Serial line and then sends the 64 bytes.

The arduino code uses a state machine to receive the 4 byte header (to ensure it's in sync) and then gets the 64 bytes. Upon reception of a payload, the arduino extracts the first 4 bytes (which was the "grid frame counter" we build) and prints that back to the python code.

The task listening to the Arduino sees the incoming message and prints it with also a ∆t information (time elapsed since the last Arduino message).

➜ you'll see that after a first buffering, where ∆t is inconsistent you then get a steady flow of messages printed out every 40/50ms and notice that the count is actually incrementing nicely showing that no frame was lost on the arduino side

python3 grid.py
PORT	DEVICE			MANUFACTURER
2 	 cu.usbmodem142301 	 Arduino (www.arduino.cc)
➜ Select your port: 2
selecting:  /dev/cu.usbmodem142301
Waiting for Arduino
Arduino Ready
GOT [0] @ ∆T=8 ms
GOT [1] @ ∆T=0 ms
GOT [2] @ ∆T=0 ms
GOT [3] @ ∆T=0 ms
GOT [4] @ ∆T=4 ms
GOT [5] @ ∆T=0 ms
GOT [6] @ ∆T=0 ms
GOT [7] @ ∆T=0 ms
GOT [8] @ ∆T=4 ms
GOT [9] @ ∆T=0 ms
GOT [10] @ ∆T=0 ms
GOT [11] @ ∆T=0 ms
GOT [12] @ ∆T=4 ms
GOT [13] @ ∆T=0 ms
GOT [14] @ ∆T=0 ms
GOT [15] @ ∆T=0 ms
GOT [16] @ ∆T=3 ms
GOT [17] @ ∆T=0 ms
GOT [18] @ ∆T=0 ms
GOT [19] @ ∆T=0 ms
GOT [20] @ ∆T=4 ms
GOT [21] @ ∆T=0 ms
GOT [22] @ ∆T=0 ms
GOT [23] @ ∆T=0 ms
GOT [24] @ ∆T=0 ms
GOT [25] @ ∆T=18 ms
GOT [26] @ ∆T=0 ms
GOT [27] @ ∆T=19 ms
GOT [28] @ ∆T=0 ms
GOT [29] @ ∆T=0 ms
GOT [30] @ ∆T=7 ms
GOT [31] @ ∆T=0 ms
GOT [32] @ ∆T=0 ms
GOT [33] @ ∆T=0 ms
GOT [34] @ ∆T=0 ms
GOT [35] @ ∆T=13 ms
GOT [36] @ ∆T=45 ms
GOT [37] @ ∆T=50 ms
GOT [38] @ ∆T=57 ms
GOT [39] @ ∆T=52 ms
GOT [40] @ ∆T=54 ms
GOT [41] @ ∆T=34 ms
GOT [42] @ ∆T=55 ms
GOT [43] @ ∆T=44 ms
GOT [44] @ ∆T=40 ms
GOT [45] @ ∆T=67 ms
GOT [46] @ ∆T=35 ms
GOT [47] @ ∆T=48 ms
GOT [48] @ ∆T=58 ms
GOT [49] @ ∆T=49 ms
GOT [50] @ ∆T=53 ms
GOT [51] @ ∆T=43 ms
GOT [52] @ ∆T=49 ms
GOT [53] @ ∆T=43 ms
GOT [54] @ ∆T=57 ms
GOT [55] @ ∆T=49 ms
GOT [56] @ ∆T=41 ms
GOT [57] @ ∆T=56 ms
...
click to see the code
#!/usr/bin/python3

# ============================================
# code is placed under the MIT license
#  Copyright (c) 2023 J-M-L
#  For the Arduino Forum : https://forum.arduino.cc/u/j-m-l
#
#  Permission is hereby granted, free of charge, to any person obtaining a copy
#  of this software and associated documentation files (the "Software"), to deal
#  in the Software without restriction, including without limitation the rights
#  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
#  copies of the Software, and to permit persons to whom the Software is
#  furnished to do so, subject to the following conditions:
#
#  The above copyright notice and this permission notice shall be included in
#  all copies or substantial portions of the Software.
#
#  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
#  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
#  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
#  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
#  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
#  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
#  THE SOFTWARE.
#  ===============================================


import sys, threading, queue, serial, random, time
import serial.tools.list_ports

baudRate = 1000000
arduinoQueue = queue.Queue()
localQueue = queue.Queue()
bytesQueue = queue.Queue()
lastTime = time.time()

def selectArduino():
    ports = serial.tools.list_ports.comports()
    choices = []
    print('PORT\tDEVICE\t\t\tMANUFACTURER')
    for index, value in enumerate(sorted(ports)):
        if value.hwid != 'n/a':
            choices.append(index)
            print(index, '\t', value.name, '\t', value.manufacturer)

    choice = -1
    while choice not in choices:
        answer = input("➜ Select your port: ")
        if answer.isnumeric() and int(answer) <= int(max(choices)):
            choice = int(answer)
    print('selecting: ', ports[choice].device)
    return ports[choice].device

def listenToArduino():
    message = b''
    while True:
        incoming = arduino.read()
        if incoming == b'\n':
            arduinoQueue.put(message.decode('utf-8').strip().upper())
            message = b''
        else:
            if incoming != b'' and incoming != b'\r':
                message += incoming

def listenToLocal():
    while True:
        command = sys.stdin.readline().strip().upper()
        localQueue.put(command)

def configureUserInput():
    localThread = threading.Thread(target=listenToLocal, args=())
    localThread.daemon = True
    localThread.start()

def configureArduino():
    global arduinoPort
    arduinoPort = selectArduino()
    global arduino
    arduino = serial.Serial(arduinoPort, baudrate=baudRate, timeout=.1)
    arduinoThread = threading.Thread(target=listenToArduino, args=())
    arduinoThread.daemon = True
    arduinoThread.start()

def generateRandomGrid():
    counter = 0
    while True:
        # Create the grid
        grid = [[random.getrandbits(8) for _ in range(4)] for _ in range(16)]
        # Replace the first 4 bytes with a counter to check if all works
        counter_bytes = counter.to_bytes(4, byteorder='big')
        grid[0] = list(counter_bytes)
        bytesQueue.put(grid)
        counter += 1
        time.sleep(0.04)  #  40 ms interval ==> 25Hz

def sendGrid(aGrid):
    header = b'\xDE\xAD\xBE\xEF'
    arduino.write(header)
    for row in aGrid:
        arduino.write(bytes(row))

def configureRandomGrid():
    gridThread = threading.Thread(target=generateRandomGrid, args=())
    gridThread.daemon = True
    gridThread.start()

def handleLocalMessage(aMessage):
    print("=> [" + aMessage + "]")
    arduino.write(aMessage.encode('utf-8'))
    arduino.write(bytes('\n', encoding='utf-8'))

def handleArduinoMessage(aMessage):
    global lastTime
    currentTime = time.time()
    deltaT = int((currentTime - lastTime) * 1000)
    lastTime = currentTime
    print("GOT [{}] @ ∆T={} ms".format(aMessage, deltaT))

configureArduino()
configureUserInput()
configureRandomGrid()

print("Waiting for Arduino")

while True:
    if not arduinoQueue.empty():
        if arduinoQueue.get() == "OK":
            break
print("Arduino Ready")
lastTime = time.time()

while True:
    if not arduinoQueue.empty():
        handleArduinoMessage(arduinoQueue.get())

    if not localQueue.empty():
        handleLocalMessage(localQueue.get())

    if not bytesQueue.empty():
        sendGrid(bytesQueue.get())

and sketch

/* ============================================
  code is placed under the MIT license
  Copyright (c) 2024 J-M-L
  For the Arduino Forum : https://forum.arduino.cc/u/j-m-l

  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  in the Software without restriction, including without limitation the rights
  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  copies of the Software, and to permit persons to whom the Software is
  furnished to do so, subject to the following conditions:

  The above copyright notice and this permission notice shall be included in
  all copies or substantial portions of the Software.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  THE SOFTWARE.
  ===============================================
*/

uint8_t gridIn[64];
enum ReceiveState : byte {HEADER, GRID} currentState = HEADER;


bool gridReceived() {
  static const uint8_t headerBuffer[] = {0xDE, 0xAD, 0xBE, 0xEF};
  static uint8_t currentIndex = 0;
  bool gridIsReady = false;
  int incoming = Serial.read();
  if (incoming == -1) return false;

  switch (currentState) {
    case HEADER:
      if (incoming != headerBuffer[currentIndex++]) {
        currentIndex = 0;
      } else {
        if (currentIndex >= sizeof headerBuffer) {
          currentIndex = 0;
          currentState = GRID;
        }
      }
      break;

    case GRID:
      gridIn[currentIndex++] = incoming;
      if (currentIndex >= sizeof gridIn) {
        gridIsReady = true;
        currentIndex = 0;
        currentState = HEADER;
      }
      break;
  }
  return gridIsReady;
}

void setup() {
  Serial.begin(1000000);
  Serial.println();
  Serial.println("OK"); // let the Python code know we are ready
}

void loop() {
  if (gridReceived()) {
    uint32_t counter = uint32_t(gridIn[0]) << 24 |  uint32_t(gridIn[1]) << 16 |  uint32_t(gridIn[2]) << 8 |  uint32_t(gridIn[3]);
    Serial.println(counter);
  }
}

hope this gives you some ideas to adapt to your process

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.