Connecting my Arduino Uno to two python programs

Hi! I am trying to connect my Arduino Uno to two Python programs.
The main python program opens the serial.port communication with the Arduino and once it wants to execute the second code, it closes the serial.port communication so that the second code can open it.

When executing the first code, I am able to send data to my arduino and, the arduino is able to execute the desired commands. However, when closing the serial.port in order to execute the second code, I am able to send the data but Arduino is unable to execute the commands. Nevertheless, if I close my second code and open again the serial.port communication for the first code, everything works correctly.

My codes are really simple.

First code:

import serial  # Para comunicación serial
import time  # Para pausas temporales
from pynput import keyboard  # Para escuchar eventos de teclado
import subprocess  # Para ejecutar comandos del sistema
import re
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler



# Configurar la conexión serial con Arduino
puerto_serial = serial.Serial('COM10', 9600)  
time.sleep(2)  # Esperar para que Arduino se inicie
finalizar = False  # Para verificar si el programa debe finalizar

# Variable para almacenar la última tecla conocida
ultima_tecla = None

def on_press(key):
    global finalizar
    global ultima_tecla

    try:
        # Verificar si es una tecla imprimible
        if hasattr(key, 'char') and key.char is not None:
            char = key.char

            # Finalizar si se presiona 'q'
            if char == 'q':
                print("Presionaste 'q', terminando...")
                finalizar = True
                return False
                if  ultima_tecla != 'q':
                    puerto_serial.write(b'q\n')
                ultima_tecla = char
                
            if char == 'a':
                puerto_serial.write(b'a\n')
                print("Flecha hacia arriba")                
                if ultima_tecla != 'a':
                    puerto_serial.write(b'a\n')
                ultima_tecla = char
                
            elif char == 'b':
                puerto_serial.write(b'b\n')    
                print("Flecha hacia abajo")            
                if ultima_tecla != 'b':
                    puerto_serial.write(b'b\n')
                ultima_tecla = char
                
            elif char == 'l':
                puerto_serial.write(b'l\n')
                print("Flecha hacia la izquierda")
                if ultima_tecla != 'l':
                    puerto_serial.write(b'l\n')
                ultima_tecla = char
                
            elif char == 'r':
                puerto_serial.write(b'r\n')
                print("Flecha hacia la derecha")
                if ultima_tecla != 'r':
                    puerto_serial.write(b'r\n')
                ultima_tecla = char
                
            elif char == 'p':
                puerto_serial.write(b'p\n')
                print("Flecha para parar")
                if ultima_tecla != 'p':
                    puerto_serial.write(b'p\n')
                ultima_tecla = char

            elif char == 'w':
                puerto_serial.write(b'm\n')
                if puerto_serial.is_open:
                    puerto_serial.close()
                    print("Puerto serial cerrado.")

                archivo_a_ejecutar = r'C:/Users/Lara\Desktop/TFG/comprobar_lectura_x.py'
                process = subprocess.Popen(['python', archivo_a_ejecutar])
                process.wait()  # Esperar a que el proceso termine

                if process.returncode is not None:  # Verificar si el proceso ha terminado
                    puerto_serial.open()
    
    except Exception as e:
        print(f"Error al procesar tecla: {e}")

# Iniciar el Listener para capturar eventos de teclado
with keyboard.Listener(on_press=on_press) as listener:
    listener.join()  # Mantener el Listener hasta que se presione 'q'

# Cerrar el puerto serial al finalizar
if puerto_serial.is_open:
    puerto_serial.close()
    print("Puerto serial cerrado.")

Second code that constants the comprobar_lectura_x.py:

import time
import subprocess
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
import serial 
import cv2
import keyboard

puerto_serial = serial.Serial('COM10', 9600) 


# Variable para almacenar el último número del archivo
ultimo_numero = ""
ultimo_valor = ""


def imprimir_ultimo_numero(data):
    global ultimo_numero, ultimo_valor
    if data and data != ultimo_numero:  # Verificar si hay datos y si son diferentes al último número
        ultimo_numero = obtener_ultimo_numero(data)  
        print("Último número:", ultimo_numero)  
        
        if ultimo_numero == '1':
            print("Flecha hacia arriba")
            puerto_serial.write(b'a\n')
            if ultimo_valor != '1':
                puerto_serial.write(b'a\n')
            ultimo_valor = ultimo_numero
        elif ultimo_numero == '2':
            print("Flecha hacia abajo")
            puerto_serial.write(b'b\n')
            if ultimo_valor != '2':
                puerto_serial.write(b'b\n')
            ultimo_valor = ultimo_numero
        elif ultimo_numero == '3':
            print("Flecha hacia izquierda")
            puerto_serial.write(b'l\n')
            if ultimo_valor != '3':
                puerto_serial.write(b'l\n')
            ultimo_valor = ultimo_numero
        elif ultimo_numero == '4':
            print("Flecha hacia derecha")
            puerto_serial.write(b'r\n')
            if ultimo_valor != '4':
                puerto_serial.write(b'r\n')
            ultimo_valor = ultimo_numero
        elif ultimo_numero == '0':
            print("PARAR")
            puerto_serial.write(b'p\n')
            if ultimo_valor != '0':
                puerto_serial.write(b'p\n')
            ultimo_valor = ultimo_numero
        else:
            print("Posición no reconocida")
            puerto_serial.write(b'p\n')
            ultimo_valor = ultimo_numero

def obtener_ultimo_numero(data):
    # Eliminar no numéricos a la derecha
    data = ''.join(filter(str.isdigit, data))
    if not data:
        return '0'
    # último número de la cadena
    return data[-1]

class MyHandler(FileSystemEventHandler):
    def on_modified(self, event):
        if event.src_path.endswith("x_value.txt"):
            data = leer_archivo(event.src_path)
            imprimir_ultimo_numero(data)

def leer_archivo(filename):
    with open(filename) as fh:
        line = fh.read()
    return line

archivo_a_ejecutar = r'C:/Users/Lara\Desktop/TFG/Control_Arduino_witmotion.py'
#archivo_a_ejecutar = r'C:/Users/Lara\Desktop/TFG/comprobar_generar_lectura.py'
process = subprocess.Popen(['python', archivo_a_ejecutar])

ultimo_valor = '0'

event_handler = MyHandler()
observer = Observer()
observer.schedule(event_handler, path='.', recursive=False)
observer.start()

try:
    while True:
        if keyboard.is_pressed('q'):
            print("Exiting program...")
            # Terminate the subprocess
            process.terminate()
            break
        time.sleep(0.1)  # Add a small delay to reduce CPU usage
except KeyboardInterrupt:
    observer.stop()


# Cerrar el puerto serial al finalizar
if puerto_serial.is_open:
    puerto_serial.close()
    print("Puerto serial cerrado.")

where the Control_Arduino_witmotion simply generates the values to be read on the x_value.txt file.

The arduino code generated PWM signals depending on the received character. For both python programs, the Arduino is able to read the characters but, for the second one, it does not generate the signals.

My arduino code is the following:

/ Definición de pines
const int pwmPin1 = 6;
const int pwmPin2 = 5;
const int pinX = A0; // Para el eje X del joystick
const int pinY = A1; // Para el eje Y del joystick
const int pinControl1 = 3; // Control de dirección
const int pinControl2 = 10; // Control de dirección

// Definición de modos
enum Mode { NONE, EYE, VOICE, MOVEMENT, JOYSTICK, STOP };
Mode currentMode = STOP;

// Velocidad predeterminada
int velocidad_fija = 20;



void move_backward() {
    analogWrite(pwmPin1, velocidad_fija); 
    digitalWrite(pinControl1, HIGH); // Control hacia atrás
}

void move_forward() {
    analogWrite(pwmPin1, velocidad_fija); 
    digitalWrite(pinControl1, LOW); // Control hacia adelante
}

void move_left() {
    analogWrite(pwmPin2, velocidad_fija); 
    digitalWrite(pinControl2, LOW); // Control hacia la izquierda
}

void move_right() {
    analogWrite(pwmPin2, velocidad_fija); 
    digitalWrite(pinControl2, HIGH); // Control hacia la derecha
}

void move_stop() {
    analogWrite(pwmPin1, 100); // Detener PWM
    analogWrite(pwmPin2, 100); 
    digitalWrite(pinControl1, LOW); 
    digitalWrite(pinControl2, LOW); 
}

void setup() {
    Serial.begin(9600); // Comunicación serial

    // Configuración de pines
    pinMode(pwmPin1, OUTPUT);
    pinMode(pwmPin2, OUTPUT);
    pinMode(pinX, INPUT);
    pinMode(pinY, INPUT);
    pinMode(pinControl1, OUTPUT);
    pinMode(pinControl2, OUTPUT);

    move_stop(); // Detener por defecto
}

void loop() {
    if (Serial.available() > 0) {
        String command = Serial.readStringUntil('\n'); // Leer comando serie
        command.trim(); // Limpiar caracteres extra
        
        if (command.startsWith("f")) {
            String speedValue = command.substring(1); // Obtener valor después de 'v'
            int newSpeed = speedValue.toInt(); // Convertir a número entero
            
            if (newSpeed >= 0 && newSpeed <= 255) { // Validar rango para PWM
                velocidad_fija = newSpeed; // Actualizar velocidad fija
                Serial.println("Velocidad establecida a " + String(velocidad_fija));
            } else {
                Serial.println("Valor de velocidad fuera de rango.");
            }
            return; // Retornar para evitar otros comandos
        }
        // Cambiar modo según comando recibido
        char cmd = command.charAt(0);
        switch (cmd) {
            case 'j':
                currentMode = JOYSTICK;
                break; 

            case 'v':
                currentMode = VOICE;
                break; 

            case 'a':
              move_forward(); // Mover hacia adelante
              Serial.println("Moving Forward");
              break;

            case 'b':
              move_backward(); // Mover hacia atrás            
              Serial.println("Moving Backward");
              break;

            case 'l':
              move_left(); // Mover hacia la izquierda
              Serial.println("Moving Left");
              break;

            case 'r':
              move_right(); // Mover hacia la derecha
              Serial.println("Moving Right");
              break;

            case 'p':
              move_stop(); // Detener
              Serial.println("Stop");
              break;               

            case 'm': // Modo de movimiento
                currentMode = MOVEMENT;
                break;
            
            default:
                break;
        }
    }


    if (currentMode == JOYSTICK) {
        handle_joystick(); // Control por joystick

     } else if (currentMode == MOVEMENT) {
        handle_movement('p'); // Control por movimiento

    } else if (currentMode == STOP) {
        move_stop(); // Detener movimiento
    }
}

void handle_joystick() {
    int xValue = analogRead(pinX); 
    int yValue = analogRead(pinY); 

    if (yValue > 400 && yValue < 600 && xValue < 600 && xValue > 500) {
        move_stop(); // Detener
    }
    if (yValue > 1000 && xValue < 700 && xValue > 300) {
        move_backward(); // Mover hacia atrás
    } else if (yValue < 100 && xValue < 700 && xValue > 300) {
        move_forward(); // Mover hacia adelante
    }
    if (xValue < 50 && yValue < 700 && yValue > 300) {
        move_left(); // Mover hacia la izquierda
    } else if (xValue > 1000 && yValue < 700 && yValue > 300) {
        move_right(); // Mover hacia la derecha
    }
}

void handle_movement(char command) {
    switch (command) {
        case 'a':
            move_forward(); // Mover hacia adelante
            Serial.println("Moving Forward");
            break;

        case 'b':
            move_backward(); // Mover hacia atrás            
            Serial.println("Moving Backward");
            break;

        case 'l':
            move_left(); // Mover hacia la izquierda
            Serial.println("Moving Left");
            break;

        case 'r':
            move_right(); // Mover hacia la derecha
            Serial.println("Moving Right");
            break;

        case 'p':
            move_stop(); // Detener
            Serial.println("Stop");
            break;
    }
}

When you open the serial port from python, your Arduino UNO reboots. so once you are done with the first program and want to launch the second one, that will reboot the arduino.

I've written a small tutorial on interfacing with Python. See Two ways communication between Python3 and Arduino - may be that can help.

Do you really need two different programs ?

1 Like

Try disabling the reset in the second python code by setting dtr false.

eg:

puerto_serial = serial.Serial('COM10', 9600)
puerto_serial.dtr = False

I made a mistake, the way it is the port will open automatically before the reset is disabled. The following will work

puerto_serial=serial.Serial()
puerto_serial.port="COM10"
puerto_serial.baudrate=9600
puerto_serial.dtr=False
puerto_serial.open()

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