Python & Arduino communication with serial port

I have a small project to show how to communicate between Arduino and python project. The idea is to create python ap to control the traffic light. Arduino (UNO) part is just a small traffic light with only 3 LEDs (red - 2, yellow-3,green-4). Also the Python part is a simple tkinter app with 2 buttons: set 10 sec delay, and set 5 sec delay. They are communicating with serial port. All codes (py and arduino parts) will be below. An issue is when i press 'set 5 sec' button, in the next loop iteration, delay becomes 5 sec. But when i press 'set 10 sec' button, nothing happens. I am noob at arduino and c++, but know python very well. Also english is not my native language, so dont scold me for some mistakes.

python:

import serial
from time import sleep
import tkinter
from tkinter import ttk

# Укажите порт, к которому подключена Ваша плата Arduino.
ARDUINO_PORT = 'COM4'
BAUDRATE = 9600

def on():
    command = '10'
    arduino.write(command.encode())
    sleep(0.1)  # Добавляем небольшую задержку для обработки

def off():
    command = '5'
    arduino.write(command.encode())
    sleep(0.1)  # Добавляем небольшую задержку для обработки

main_window = tkinter.Tk()
main_window.title('Управление arduino')
main_window.geometry('800x600')

button1 = ttk.Button(text='Задержка 10 сек.', command=on)
button0 = ttk.Button(text='Задержка 5 сек.', command=off)
buttons = [button1, button0]

for button in buttons:
    button.pack()

status_label = ttk.Label()
status_label.pack()

try:
    arduino = serial.Serial(ARDUINO_PORT, BAUDRATE, timeout=1)
    sleep(2)
    status_label.config(text='Подключено к Arduino')
except serial.SerialException:
    status_label.config(text='Не удается подключиться')

main_window.mainloop()

arduino:

int main_delay = 10000;
int sign_delay = main_delay - 3000;
int yellow_delay = 3000;
int green_morg_delay = 1000;

void setup() {
    pinMode(2, OUTPUT);
    pinMode(3, OUTPUT);
    pinMode(4, OUTPUT);
    Serial.begin(9600);
}

void loop() {
    if (Serial.available() > 0) {
        char command = Serial.read();
        if (command == '10') {
            main_delay = 10000;
        } else if (command == '5') {
            main_delay = 5000;
        }
    }

    digitalWrite(2, HIGH);
    digitalWrite(3, LOW);
    digitalWrite(4, LOW);
    delay(main_delay);
    
    digitalWrite(2, HIGH);
    digitalWrite(3, HIGH);
    digitalWrite(4, LOW);
    delay(yellow_delay);
    
    digitalWrite(2, LOW);
    digitalWrite(3, LOW);
    digitalWrite(4, HIGH);
    delay(main_delay);
    
    digitalWrite(2, LOW);
    digitalWrite(3, LOW);
    digitalWrite(4, LOW);
    delay(green_morg_delay);
    
    digitalWrite(2, LOW);
    digitalWrite(3, LOW);
    digitalWrite(4, HIGH);
    delay(green_morg_delay);
    
    digitalWrite(2, LOW);
    digitalWrite(3, LOW);
    digitalWrite(4, LOW);
    delay(green_morg_delay);
    
    digitalWrite(2, LOW);
    digitalWrite(3, HIGH);
    digitalWrite(4, HIGH);
    delay(yellow_delay);
}

I already tried to talk with chat-gpt but it cant help me. Also all hardware is working correctly.

Multiple character commands are more difficult to use because of the timing of serial data transfer. If you change your “10” command into a single character (‘a’ perhaps?) it should work.

If you really need multiple character parsing look for the “serial input basics” tutorial.

1 Like

First off, thank you for posting your code inside code tags! +1.

Using single quotes in C/C++ specifies a single char and you are using 2 in that expression. If you change your preferences inside the IDE and increase your warning level, the compiler would have told you about that.

As @DaveX pointed out, it is much easier to just have each command be a single character.

2 Likes

Thank you! I can't make all commands be only 1 character, because in future, I want to let user choose any delay they want, just with entry. I made buttons just to test how it works.

Thank you!

You can send and parse numbers a character at a time, like "12345d" for a delay of 12345.

Look at the pcRead example:

and some of the linked Wokwi simulations:

This does a lot with single character-at-a-time processing:

1 Like

Why can't I just do something like that:

int main_delay = 10000;
int sign_delay = main_delay - 3000;
int yellow_delay = 3000;
int green_morg_delay = 1000;

void setup() {
    pinMode(2, OUTPUT);
    pinMode(3, OUTPUT);
    pinMode(4, OUTPUT);
    Serial.begin(9600);
}

void loop() {
    if (Serial.available() > 0) {
        int command = Serial.read();
        main_delay = command;
    }

    digitalWrite(2, HIGH);
    digitalWrite(3, LOW);
    digitalWrite(4, LOW);
    delay(main_delay);
    
    digitalWrite(2, HIGH);
    digitalWrite(3, HIGH);
    digitalWrite(4, LOW);
    delay(yellow_delay);
    
    digitalWrite(2, LOW);
    digitalWrite(3, LOW);
    digitalWrite(4, HIGH);
    delay(main_delay);
    
    digitalWrite(2, LOW);
    digitalWrite(3, LOW);
    digitalWrite(4, LOW);
    delay(green_morg_delay);
    
    digitalWrite(2, LOW);
    digitalWrite(3, LOW);
    digitalWrite(4, HIGH);
    delay(green_morg_delay);
    
    digitalWrite(2, LOW);
    digitalWrite(3, LOW);
    digitalWrite(4, LOW);
    delay(green_morg_delay);
    
    digitalWrite(2, LOW);
    digitalWrite(3, HIGH);
    digitalWrite(4, HIGH);
    delay(yellow_delay);
}

No, characters, no problems!
python:

import serial
from time import sleep
import tkinter
from tkinter import ttk

# Укажите порт, к которому подключена Ваша плата Arduino.
ARDUINO_PORT = 'COM4'
BAUDRATE = 9600

def test_delay():
    command = str(int(delay_entry.get())*1000)
    arduino.write(command.encode())

main_window = tkinter.Tk()
main_window.title('Управление arduino')
main_window.geometry('800x600')

delay_entry = ttk.Entry()
button_test = ttk.Button(text='Задать задержку', command=test_delay)
delay_entry.pack()
button_test.pack()

status_label = ttk.Label()
status_label.pack()

try:
    arduino = serial.Serial(ARDUINO_PORT, BAUDRATE, timeout=1)
    sleep(2)
    status_label.config(text='Подключено к Arduino')
except serial.SerialException:
    status_label.config(text='Не удается подключиться')

main_window.mainloop()

But in this case, nothing happens at all. WHY

look this over

const byte PinLeds [] = { 13, 12, 11, 10 };
const int  Nled       = sizeof(PinLeds);

enum { Off = HIGH, On = LOW };

// -----------------------------------------------------------------------------
void loop ()
{
    if (Serial.available ())  {
        char buf [90];
        int  n = Serial.readBytesUntil ('\n', buf, sizeof(buf)-1);
        buf [n] = '\0';     // terminate string with nul

        char cmd [10];
        int  val1;
        int  val2;

        sscanf (buf, "%s %d %d", cmd, &val1, &val2);

        Serial.println (cmd);
        if (! strcmp (cmd, "ledOn"))
            digitalWrite (PinLeds [val1], On);
        else if (! strcmp (cmd, "ledOff"))
            digitalWrite (PinLeds [val1], Off);
    }
}

// -----------------------------------------------------------------------------
void setup ()
{
    Serial.begin (9600);

    for (int n = 0; n < Nled; n++)
        pinMode (PinLeds [n], OUTPUT);
}

Because this Serial.read() reads one character at a time.

main_delay will be in the 0-255 range unless there was no character to read(), in which case (obviated by the availiable()>0) the returned -1 would cause a 2^32-1ms delay.

Did you read @Robin2's discussion of how the arduino is so much faster than 9600 baud?

Also, this bit makes the number into a string of characters:

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