Serial Communication Between arduino and python

I need to receive and send information between arduino and python. I have a project that I must correctly separate parts with a robotic arm.

I started trying to send a start character ('3') from arduino to python when a button is pressed indicating a detection of a sensor (so it is interrupt pin). When python reads this character, it must process some data and send back the value '1' or '2' and expects to receive data 3 again to process the information again.

But the programming does not work, most of the time the arduino receives only the first data.

Can someone help me ?

Arduino program

int modo = 0;
int led1 = 5;
int led2 = 6;

char option;

void setup() {
   
  pinMode(led1, OUTPUT);
  pinMode(led2,OUTPUT);
  attachInterrupt(digitalPinToInterrupt(Botao), botaoAcionado , FALLING);
  
  Serial.begin(9600);

}

void loop() {
   
  if(modo == 1){
      digitalWrite(LED_BUILTIN,HIGH);
       Serial.print('3');
       modo = 0;
       while(Serial.available() == 0){ 
        char lido = Serial.read(); 
       
        if(lido == '1'){
          digitalWrite(led1,HIGH);
          delay(2000);
          }
        if(lido == '2'){
         digitalWrite(led2,HIGH);
         delay(2000);

          }
        }
      }
     else if (!modo) {digitalWrite(LED_BUILTIN,LOW);}
    }

Python code

import serial

arduino = serial.Serial('COM12', baudrate=9600, timeout=3)

while True:

    if arduino.in_waiting == 0 :

        lido = arduino.readline()

        val = int(input(" Enter 1 or 2: "))
        if val == 1:
            com = b'1'
            print(" 1")

        elif val == 2:
            com = b'2'
            print(" 2")

        else:
            print("Enter a valid amount")

        if lido == b'3':
            arduino.write(com)
            print(f"Send {com}")
            lido = b'0'

Set python aside for a while. Open the serial monitor. Type the same stuff that python would send. What does the Arduino do?

PaulS:
Set python aside for a while. Open the serial monitor. Type the same stuff that python would send. What does the Arduino do?

sends value 3 only once to the monitor. I type in the number 1 and it runs correctly. However, after running, the program does nothing else, does not send the value 3 again and if I type 1 or 2 it does not do anything else

It would be much faster and reliable to interface Python with your Arduino using the compatible libraries: pySerialTransfer and SerialTransfer.h.

pySerialTransfer is pip-installable and cross-platform compatible. SerialTransfer.h runs on the Arduino platform and can be installed through the Arduino IDE's Libraries Manager.

Both of these libraries have highly efficient and robust packetizing/parsing algorithms with easy to use APIs.

Example Python Script:

import time
from pySerialTransfer import pySerialTransfer as txfer


if __name__ == '__main__':
    try:
        link = txfer.SerialTransfer('COM17')
        
        link.open()
        time.sleep(2) # allow some time for the Arduino to completely reset
        
        while True:
            send_size = 0
            
            ###################################################################
            # Send a list
            ###################################################################
            list_ = [1, 3]
            list_size = link.tx_obj(list_)
            send_size += list_size
            
            ###################################################################
            # Send a string
            ###################################################################
            str_ = 'hello'
            str_size = link.tx_obj(str_, send_size) - send_size
            send_size += str_size
            
            ###################################################################
            # Send a float
            ###################################################################
            float_ = 5.234
            float_size = link.tx_obj(float_, send_size) - send_size
            send_size += float_size
            
            ###################################################################
            # Transmit all the data to send in a single packet
            ###################################################################
            link.send(send_size)
            
            ###################################################################
            # Wait for a response and report any errors while receiving packets
            ###################################################################
            while not link.available():
                if link.status < 0:
                    if link.status == -1:
                        print('ERROR: CRC_ERROR')
                    elif link.status == -2:
                        print('ERROR: PAYLOAD_ERROR')
                    elif link.status == -3:
                        print('ERROR: STOP_BYTE_ERROR')
            
            ###################################################################
            # Parse response list
            ###################################################################
            rec_list_  = link.rx_obj(obj_type=type(list_),
                                     obj_byte_size=list_size,
                                     list_format='i')
            
            ###################################################################
            # Parse response string
            ###################################################################
            rec_str_   = link.rx_obj(obj_type=type(str_),
                                     obj_byte_size=str_size,
                                     start_pos=list_size)
            
            ###################################################################
            # Parse response float
            ###################################################################
            rec_float_ = link.rx_obj(obj_type=type(float_),
                                     obj_byte_size=float_size,
                                     start_pos=(list_size + str_size))
            
            ###################################################################
            # Display the received data
            ###################################################################
            print('SENT: {} {} {}'.format(list_, str_, float_))
            print('RCVD: {} {} {}'.format(rec_list_, rec_str_, rec_float_))
            print(' ')
    
    except KeyboardInterrupt:
        link.close()
    
    except:
        import traceback
        traceback.print_exc()
        
        link.close()

Example Arduino Sketch:

#include "SerialTransfer.h"


SerialTransfer myTransfer;


void setup()
{
  Serial.begin(115200);
  myTransfer.begin(Serial);
}


void loop()
{
  if(myTransfer.available())
  {
    // send all received data back to Python
    for(uint16_t i=0; i < myTransfer.bytesRead; i++)
      myTransfer.txBuff[i] = myTransfer.rxBuff[i];
    
    myTransfer.sendData(myTransfer.bytesRead);
  }
}

On the Arduino side, you can use myTransfer.txObj() and myTransfer.rxObj() to copy values to the library's RX buffer and parse multi-byte variables out of the library's TX buffer.

For theory behind robust serial communication, check out the tutorials Serial Input Basics and Serial Input Advanced.

If (like me) you like to see how things go on "under the hood" have a look at this Simple Python - Arduino demo

The Python code should work on Windows if you edit it to use the Windows style of COM ports.

...R

You set modo to 0 initially. You reset it to 0 in loop. You test that modo equals 1, but you never set it to 1. I can't see why the Arduino sends the '3' once. But, it's obvious why it never sends it again.

Power_Broker:
It would be much faster and reliable to interface Python with your Arduino using the compatible libraries: pySerialTransfer and SerialTransfer.h.

pySerialTransfer is pip-installable and cross-platform compatible. SerialTransfer.h runs on the Arduino platform and can be installed through the Arduino IDE's Libraries Manager.

Both of these libraries have highly efficient and robust packetizing/parsing algorithms with easy to use APIs.

Example Python Script:

import time

from pySerialTransfer import pySerialTransfer as txfer

if name == 'main':
    try:
        link = txfer.SerialTransfer('COM17')
       
        link.open()
        time.sleep(2) # allow some time for the Arduino to completely reset
       
        while True:
            send_size = 0
           
            ###################################################################
            # Send a list
            ###################################################################
            list_ = [1, 3]
            list_size = link.tx_obj(list_)
            send_size += list_size
           
            ###################################################################
            # Send a string
            ###################################################################
            str_ = 'hello'
            str_size = link.tx_obj(str_, send_size) - send_size
            send_size += str_size
           
            ###################################################################
            # Send a float
            ###################################################################
            float_ = 5.234
            float_size = link.tx_obj(float_, send_size) - send_size
            send_size += float_size
           
            ###################################################################
            # Transmit all the data to send in a single packet
            ###################################################################
            link.send(send_size)
           
            ###################################################################
            # Wait for a response and report any errors while receiving packets
            ###################################################################
            while not link.available():
                if link.status < 0:
                    if link.status == -1:
                        print('ERROR: CRC_ERROR')
                    elif link.status == -2:
                        print('ERROR: PAYLOAD_ERROR')
                    elif link.status == -3:
                        print('ERROR: STOP_BYTE_ERROR')
           
            ###################################################################
            # Parse response list
            ###################################################################
            rec_list_  = link.rx_obj(obj_type=type(list_),
                                    obj_byte_size=list_size,
                                    list_format='i')
           
            ###################################################################
            # Parse response string
            ###################################################################
            rec_str_  = link.rx_obj(obj_type=type(str_),
                                    obj_byte_size=str_size,
                                    start_pos=list_size)
           
            ###################################################################
            # Parse response float
            ###################################################################
            rec_float_ = link.rx_obj(obj_type=type(float_),
                                    obj_byte_size=float_size,
                                    start_pos=(list_size + str_size))
           
            ###################################################################
            # Display the received data
            ###################################################################
            print('SENT: {} {} {}'.format(list_, str_, float_))
            print('RCVD: {} {} {}'.format(rec_list_, rec_str_, rec_float_))
            print(' ')
   
    except KeyboardInterrupt:
        link.close()
   
    except:
        import traceback
        traceback.print_exc()
       
        link.close()





**Example Arduino Sketch:**


#include "SerialTransfer.h"

SerialTransfer myTransfer;

void setup()
{
  Serial.begin(115200);
  myTransfer.begin(Serial);
}

void loop()
{
  if(myTransfer.available())
  {
    // send all received data back to Python
    for(uint16_t i=0; i < myTransfer.bytesRead; i++)
      myTransfer.txBuff[i] = myTransfer.rxBuff[i];
   
    myTransfer.sendData(myTransfer.bytesRead);
  }
}





On the Arduino side, you can use `myTransfer.txObj()` and `myTransfer.rxObj()` to copy values to the library's RX buffer and parse multi-byte variables out of the library's TX buffer.


For theory behind robust serial communication, check out the tutorials [Serial Input Basics](https://forum.arduino.cc/index.php?topic=396450.0) and [Serial Input Advanced](https://forum.arduino.cc/index.php?topic=662346.0).

I already tried to use this library, however, I didn't find much reference about it. I think you could help me with that.
I don't speak English very well, it makes me a little difficult to understand some things that I only find in English.
Well, I tried to start again by sending information from Arduino to Python when the button was pressed on the interrupt pin. It worked, however, python does not read the data correctly, it is empty data.

Arduino

#include "SerialTransfer.h"
SerialTransfer myTransfer;

int Botao = 3;
int modo = 0;

void setup()
{
  Serial.begin(115200);
  myTransfer.begin(Serial);
  attachInterrupt(digitalPinToInterrupt(Botao), botaoAcionado , RISING);
  pinMode(13, OUTPUT);
  digitalWrite(13, LOW);
}
void loop()
{
   if (modo == 1){
    digitalWrite(LED_BUILTIN, HIGH);
    myTransfer.rxBuff[0] = '2';
    myTransfer.sendData(1);
    delay(100);  
    modo = 0;
    }
}
void botaoAcionado(){
  modo = 1;
}

Python

import time
from pySerialTransfer import pySerialTransfer as txfer

try:
    link = txfer.SerialTransfer('COM12')
    link.open()
    time.sleep(2)
    while True:
        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(f'Response received:{response}')

except KeyboardInterrupt:
    link.close()

Python - sometimes send more than one data when the button is pressed

Response received:

PaulS:
You set modo to 0 initially. You reset it to 0 in loop. You test that modo equals 1, but you never set it to 1. I can't see why the Arduino sends the '3' once. But, it's obvious why it never sends it again.

Sorry, in the final of the program has a function that i forgot to put, but, it was already in this program

void botaoAcionado(){
  modo = 1;
}

I solved the problem of the date being read twice in python, putting a 200 ms delay in place of the 100 ms delay.
About not being able to read correctly in python, I can leave it out, it is not necessary to check if the data is really 2, I just need to check if something has arrived from Arduino, and its ok.
The problem now is that the arduino receives the data from the python and interprets it correctly.

How to interpret arduino data to be able to use within an 'if' ??

when the arduino receives the data, what format does it receive? I am confused about it
what do i need to make this program work?

#include "SerialTransfer.h"
SerialTransfer myTransfer;

int Botao = 3;
int modo = 0;
String val;
void setup()
{
  Serial.begin(115200);
  myTransfer.begin(Serial);
  attachInterrupt(digitalPinToInterrupt(Botao), botaoAcionado , FALLING);
  pinMode(13, OUTPUT);
  digitalWrite(13, LOW);
}
void loop()
{
   if (modo == 1){
    
    myTransfer.rxBuff[0] = '2';
    myTransfer.sendData(1);
    delay(200);
    if(myTransfer.available()){
    for(uint16_t i=0; i < myTransfer.bytesRead; i++)
      val = myTransfer.rxBuff[i];
    if (val == '1'){
      digitalWrite(LED_BUILTIN, HIGH);
      modo = 0;
      }
    if (val == '2'){
      digitalWrite(LED_BUILTIN, LOW);
      modo=0;
    }
   }
  }
 }

void botaoAcionado(){
  modo = 1;
}

My code in python for this program is

import time
from pySerialTransfer import pySerialTransfer as txfer

try:
    link = txfer.SerialTransfer('COM12')
    link.open()
    time.sleep(2)
    while True:
        while not link.available():
            if link.status < 0:
                print('ERROR: {}'.format(link.status))
        response = ''
        if link.bytesRead > 0:
            val = str(input("enter 1 or 2 :"))
            link.txBuff[0] = val
            link.txBuff[1] = '\n'

            link.send(2)


except KeyboardInterrupt:
    link.close()

I'm glad to help, but right now it's not clear what your end goal is with the software. What exactly are you trying to accomplish with this project? The description should be high-level, no small details.

Something like, "I want to use this to build a line following robot that communicates it's position to a Python script upon user input".

Once we know what it's all supposed to do, then we might be able to help fix the root problems.

I am designing a robotic arm that will separate parts. In the system, it will have a presence sensor (infrared) that will detect a piece, when it detects it will send a signal to the python and the python with the aid of computer vision will recognize which piece it is and send back a value to the arduino that will interpret and order the robotic arm to position the piece in its proper place.
The robotic arm is ready and with the positions already marked. The computer vision system is also ready. I just lack the communication between the computer and the Arduino.

Ok, cool. Now I understand what's going on.

What sort of data does the Python program need from the Arduino? Is it an array of IR values?

Likewise, what sort of data does the Arduino need from the Python program in order to properly command the arm?

The arduino only needs to send any data for the python to start identifying, it doesn't necessarily have to be an array, just a character or an integer value, anything.
Python must send a number 1 or 2 indicating which part was identified. It can be in String format, integer, byte, whatever.
The arduino when receiving '1' must give the commands to the arm to take the piece and put it in pot 1, and when receiving 2, it must give the commands to the arm to place the piece in pot 2.
The arm commands are already in the arduino program, there is no need for a very complex data exchange at this time, the data exchange can be simple, just a String or an entire value.

I was able to whip up something that will get you started. The Arduino first sends a packet to Python with the "IR value". Once Python receives such a packet, it responds with a "command" that then controls the board's LED.

Note that the Arduino sketch is blocking and doesn't use an interrupt (that can be changed, however).

Arduino:

#include "SerialTransfer.h"


SerialTransfer myTransfer;


int32_t txVal; // 32-bit int to be compatible with Python int size
int32_t rxVal; // 32-bit int to be compatible with Python int size


void setup()
{
  Serial.begin(115200);
  myTransfer.begin(Serial);
  
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, LOW);

  delay(10000); // wait for Python to initialize (this assumes Python opening the Arduino's serial port will cause the board to power-cycle)
}


void loop()
{
  // <-- do IR stuff here to figure out what `txVal` should really be
  txVal = 2;
  
  myTransfer.txObj(txVal, sizeof(txVal));
  myTransfer.sendData(sizeof(txVal));
  
  while(!myTransfer.available()); // <-- I know this is blocking and usually a big no-no, but it seems permissible in your situation (unless you have further project requirments requiring non-blocking code)
  
  myTransfer.rxObj(rxVal, sizeof(rxVal));
    
  if (rxVal == 1)
    digitalWrite(LED_BUILTIN, HIGH);
  else if (rxVal == 2)
    digitalWrite(LED_BUILTIN, LOW);
}

Python:

import time
from random import randint
from pySerialTransfer import pySerialTransfer as txfer


if __name__ == '__main__':
    try:
        link = txfer.SerialTransfer('COM12')
        link.open()
        time.sleep(2)
        
        while True:
            while not link.available():
                if link.status < 0:
                    print('ERROR: {}'.format(link.status))
            
            rx_val = link.rx_obj(obj_type=int, obj_byte_size=4) # 4 bytes for an int
            
            print('Received: {}'.format(rx_val))
            
            tx_val = randint(1, 2) # <-- Do stuff here to figure out what command to send to Arduino
            
            send_size = link.tx_obj(tx_val)
            link.send(send_size)
            
            print('Sent: {}'.format(tx_val))
            print(' ')
    
    except KeyboardInterrupt:
        link.close()
        
    except:
        import traceback
        traceback.print_exc()
        
        link.close()

Thank you !! Your help was extremely important! I made some changes and it is already working as I wanted.
In the future, maybe I will come back to answer more questions about serial communication, I really wanted to go deeper into it, but for now, the questions already answered are enough to complete my project. Thank you again !!