Статья: Python (Tkinter) работа с Arduino

Сегодня я сделал очень простой проект, основная идея которого заключается в создании приложения с графическим интерфейсом для управления платой arduino. Конечно, управление светодиодом было бы очень простым способом начать работу, а затем мы сможем применить его к более сложным проектам в будущем, когда наши навыки будут постоянно расти.

Я разделю этот проект на 2 части

Часть 1 связана с созданием приложения с графическим интерфейсом.

Часть 2 — написать код Arduino для получения входных данных из графического интерфейса, который я создал заранее.

Хорошо, давайте перейдем к части 1.

Часть 1 Проектирование графического интерфейса и написание кода для его создания.

Во-первых, мне нужно разработать кнопки для отправки данных в порт Arduino через UART.

 В этом проекте мой порт com7, а скорость передачи 96oo. (Вы можете вставить свои данные). Кстати, это обычные и подходящие значения платы Arduino. Если вы хотите подключить Wi-Fi с помощью ESP8266 или ESP32, вы всегда заметите, что эти платы используют 115200 в качестве подходящей скорости передачи.

Написание кода Python

Давайте посмотрим в коде Python, я импортировал 3 модуля; сериал, время, ткинтер

serial >>> Для связи между arduino и python.

time >>> Управляем временем.

tkinter >>> Создание приложением с графическим интерфейсом.

import serial
import time
import tkinter

Создание соединения

ser = serial.Serial('com7', 9600) 
print("Сброс Arduino") 
time.sleep(3) 
ser.write(bytes('L', 'UTF-8'))

Последовательную связи определили в переменную «ser» для хранения данных последовательной связи.

Установили com 7 и 9600 как номер com-порта и скорость передачи соответственно. (Здесь можем ввести свой номер порта).

Выводим текст в Python IDE, через print(“Сброс Arduino”)

« time.sleep(3) » задерживает на 3 секунды.

Создаём байтовую строку, и через «ser.write» отправляем «L» на Arduino для сброса включенного состояния, при первом подключении.

Создание приложение

tkTop = tkinter.Tk()
tkTop.geometry('300x400')
tkTop.title("IoT24hours")
label3 = tkinter.Label(text = 'Building Python GUI to interface an arduino,'
 '\n and control an LED',font=("Courier", 12,'bold')).pack()

Рассмотрим подробнее:

tkTop = tkinter.Tk()

Создаем Корневое окно tkinter и передаем данные в переменную tkTop.

tkTop.geometry(‘300×400’)

Создаем размеры окна.

tkTop.title(“IoT24hours”)

Создаем заголовок окна.

label3 = tkinter.Label(text = ‘Building Python GUI to interface an arduino,’ ‘\n and control an LED’,font=(“Courier”, 12,’bold’)).pack()

Создаем текст с переносом \n на новую строку. ВЫбираем шрифт и размер текста font=(“Courier”, 12,’bold’).

Для позиционирования виджетов в контейнере применяются различные способы. Один из них представляет вызов у виджета метода pack().

tkTop.counter = 0

Переменная хранится в корневом окне, и первое значение равно нулю.


b = tkTop.counter

Передаем значение переменной в корневом окне переменной b.

3 функции для 3 кнопок

quit (Кнопка Quit) >>>

Чтобы выйти из приложения, когда мы хотим

set_button1_state (кнопка ON)>>> 

Чтобы отправить символ «H» на Arduino, возможно, включение светодиода.

set_button2_state (кнопка OFF)>>> 

Чтобы отправить символ «L» в Arduino, возможно, выключение светодиода.

def quit(): 
    global tkTop 
    ser.write(bytes('L', 'UTF-8')) 
    tkTop.destroy() 

def set_button1_state(): 
        b += 1 
        varLabel.set("LED ON") 
        ser.write (bytes('H', 'UTF-8')) 
        varLabel2.set(b) 
        print(b) 

def set_button2_state(): 
        varLabel.set("LED OFF") 
        ser.write(bytes('L', 'UTF -8')

Разберем код функции def quit:

def quit():

Создание функции.

global tkTop

Определяем переменную tkTop как глобальную.

ser.write(bytes(‘L’, ‘UTF-8’))

Создаём байтовую строку, и через «ser.write» отправляем «L» на Arduino при работе функции.

tkTop.destroy()

Уничтожаем переменную tkTop, по сути закрываем приложение.

Разберем код функции def set_button1_state:

def set_button1_state():

Создание функции

b += 1

При каждой работе функции прибавляем единицу к переменной b.

varLabel.set(“LED ON”)

В переменную varLabel устанавливаем текст “LED ON”

ser.write (bytes(‘H’, ‘UTF-8’))

Создаём байтовую строку, и через «ser.write» отправляем «H» на Arduino при работе функции.

varLabel2.set(b)

В переменную varLabel2 вводим значение переменной b. Значение которой увеличивается на единицу.

print(b)

Печатаем значение переменной b.

Разберем код функции def set_button1_state:

def set_button2_state():

Создание функции

varLabel.set(“LED OFF”)

В переменную varLabel устанавливаем текст “LED OFF”

ser.write(bytes(‘L’, ‘UTF -8’)

Создаём байтовую строку, и через «ser.write» отправляем «L» на Arduino при работе функции.

Текстовое поле tkLabel

varLabel = tkinter.IntVar()
tkLabel = tkinter.Label(textvariable=varLabel, )
tkLabel.pack()

Рассмотрим код:

varLabel = tkinter.IntVar()

Переменная, определенная с помощью IntVar()функции, содержит целочисленные данные, где мы можем установить целочисленные данные, а также можем получить их, используя методы получения и установки. Эти переменные могут быть переданы в различные параметры виджета, например, переменный параметр Radio Button и CheckBox, текстовый параметр Label Widget и т. д., как мы увидим в примерах. Как только эти переменные подключаются к виджетам, соединение работает в обоих направлениях: если переменная IntVar() изменяется, значение виджета также автоматически обновляется новым значением.

Передаем значение в переменную varLabel.


tkLabel = tkinter.Label(textvariable=varLabel, )

Виджет Label представляет текстовую метку. Этот элемент позволяет выводить статический текст без возможности редактирования.

В данном случае определяется переменная tkLabel, которая представляет класс StringVar, то есть такая переменная, которая хранит некоторую строку.

С помощью параметра textvariable эта переменная привязана к текстовой метки.

textvariable: как следует из названия, она связана с переменной Tkinter (обычно StringVar) с меткой. Если переменная изменяется, текст метки обновляется.
И если мы изменим текст в поле Entry, автоматически синхронно изменится и значение привязанной переменной tkLabel. а поскольку к этой переменной также привязана текстовая метка, то автоматически также изменится текст метки.


tkLabel.pack()

Pack – это менеджер геометрии, который размещает виджеты по горизонтали и вертикали.

Текстовое поле tkLabel2

varLabel2 = tkinter.IntVar()
tkLabel2 = tkinter.Label(textvariable=varLabel2, )
tkLabel2.pack()

Рассмотрим код:

varLabel2 = tkinter.IntVar()

Переменная, определенная с помощью IntVar()функции, содержит целочисленные данные, где мы можем установить целочисленные данные, а также можем получить их, используя методы получения и установки. Эти переменные могут быть переданы в различные параметры виджета, например, переменный параметр Radio Button и CheckBox, текстовый параметр Label Widget и т. д., как мы увидим в примерах. Как только эти переменные подключаются к виджетам, соединение работает в обоих направлениях: если переменная IntVar() изменяется, значение виджета также автоматически обновляется новым значением.

Передаем значение в переменную varLabel2.


tkLabel2= tkinter.Label(textvariable=varLabel2, )

Виджет Label представляет текстовую метку. Этот элемент позволяет выводить статический текст без возможности редактирования.

В данном случае определяется переменная tkLabel2, которая представляет класс StringVar, то есть такая переменная, которая хранит некоторую строку.

С помощью параметра textvariable эта переменная привязана к текстовой метки.

textvariable: как следует из названия, она связана с переменной Tkinter (обычно StringVar) с меткой. Если переменная изменяется, текст метки обновляется.
И если мы изменим текст в поле Entry, автоматически синхронно изменится и значение привязанной переменной tkLabel2. а поскольку к этой переменной также привязана текстовая метка, то автоматически также изменится текст метки.


tkLabel2.pack()

Pack – это менеджер геометрии, который размещает виджеты по горизонтали и вертикали.

Кнопка button1

button1 = tkinter.IntVar()
button1state = tkinter.Button(tkTop,
    text="ON",
    command=set_button1_state,
    height = 4,
    fg = "black",
    width = 8,
    bd = 5,
    activebackground='green'
)
button1state.pack(side='top', ipadx=10, padx=10, pady=15);

Рассмотрим код:

button1 = tkinter.IntVar()

Переменная, определенная с помощью IntVar()функции, содержит целочисленные данные.


button1state = tkinter.Button(tkTop,
text=”ON”,
command=set_button1_state,
height = 4,
fg = “black”,
width = 8,
bd = 5,
activebackground=’green’
)

В переменную button1state записываем следующие значения:

tkTop: расположение кнопки в корневом окне tkTop .

text=”ON”: текст на кнопке.

command=set_button1_state: при нажатии, выполнение функции set_button1_state.

height = 4: высота кнопки.

fg = “black”: цвет кнопки.

width = 8: ширина кнопки.

bd = 5: ширина линии кнопки

activebackground=’green’: фон при нажатии.

И упаковщик кнопки


button1state.pack(side=’top’, ipadx=10, padx=10, pady=15)

Метод pack, который я понимаю, заключается в том, как разместить контейнер в вашем окне. В основном это концепция относительного положения, и самое главное, метод pack размещается построчно в порядке выполнения кода. Да, порядок имеет большое влияние на результат.
Прежде всего, атрибуты метода pack () следующие:
-after, -anchor, -before, -expand, -fill, -in, -ipadx, -ipady, -padx, -pady, -side
Метод pack можно применить ко всем контейнерам в Tkinter;

Более подробно здесь или здесь

У метода pack есть параметр side (сторона), который принимает одно из четырех значений-констант tkinter – TOPBOTTOMLEFTRIGHT (верх, низ, лево, право). По умолчанию, когда в pack не указывается side, его значение равняется TOP. Из-за этого виджеты располагаются вертикально.

Можно задавать внутренние (ipadx и ipady) и внешние (padx и pady) отступы:

Кнопка button1 и tkButtonQuit имеют такие же настройки как выше.

И последняя строчка кода.

tkinter.mainloop()

Для отображения окна и взаимодействия с пользователем у окна вызывается метод mainloop()

Полный код:

import serial
import time
import tkinter


def quit():
    global tkTop
    ser.write(bytes('L', 'UTF-8'))
    tkTop.destroy()

def set_button1_state():
        global b
        b += 1
        varLabel.set("LED ON ")
        ser.write(bytes('H', 'UTF-8'))
        varLabel2.set(b)
        print(b)

def set_button2_state():
        varLabel.set("LED OFF")
        ser.write(bytes('L', 'UTF-8'))

ser = serial.Serial('com1', 9600)
print("Reset Arduino")
time.sleep(3)
ser.write(bytes('L', 'UTF-8'))

tkTop = tkinter.Tk()
tkTop.geometry('300x400')
tkTop.title("IoT24hours")
label3 = tkinter.Label(text = 'Building Python GUI to interface an arduino,'
                      '\n and control an LED',font=("Courier", 12,'bold')).pack()
tkTop.counter = 0
b = tkTop.counter

varLabel = tkinter.IntVar()
tkLabel = tkinter.Label(textvariable=varLabel, )
tkLabel.pack()

varLabel2 = tkinter.IntVar()
tkLabel2 = tkinter.Label(textvariable=varLabel2, )
tkLabel2.pack()

button1 = tkinter.IntVar()
button1state = tkinter.Button(tkTop,
    text="ON",
    command=set_button1_state,
    height = 4,
    fg = "black",
    width = 8,
    bd = 5,
    activebackground='green'
)
button1state.pack(side='top', ipadx=10, padx=10, pady=15)

button2 = tkinter.IntVar()
button2state = tkinter.Button(tkTop,
    text="OFF",
    command=set_button2_state,
    height = 4,
    fg = "black",
    width = 8,
    bd = 5
)
button2state.pack(side='top', ipadx=10, padx=10, pady=15)

tkButtonQuit = tkinter.Button(
    tkTop,
    text="Quit",
    command=quit,
    height = 4,
    fg = "black",
    width = 8,
    bg = 'yellow',
    bd = 5
)
tkButtonQuit.pack(side='top', ipadx=10, padx=10, pady=15)

tkinter.mainloop()

Часть 2 Написание кода arduino

Я не использовал никаких светодиодных устройств, я просто использую свою плату arduino. Как вы уже знаете, если вы познакомитесь с arduino, вы увидите, что это встроенный светодиодный вывод, прикрепленный к выводу 13 на его плате, и, конечно же, я буду использовать его для этого проекта, потому что теперь у меня нет никаких светодиодов. . Но на самом деле я очень хочу создать этот пост сейчас, поэтому мне достаточно начать с использования светодиода на плате Arduino. Кстати, если у вас есть собственный светодиод, не проблема, вы тоже можете его использовать, но вам потребуется подключить к нему провод и иметь некоторые базовые знания схемы.

Код вначале скетча

const int ledPin = 13; // пин, к которому подключен светодиод 
int incomingByte; // переменная хранит последовательные данные

В настройке void не было ничего сложного, просто добавили 9600 в качестве скорости передачи и определили «ledPin» как выходной контакт.

void setup() { 
  // инициализируем последовательную связь: 
  Serial.begin(9600); 
  // инициализируем вывод светодиода как выход: 
  pinMode(ledPin, OUTPUT); 
}

Основная идея написания кода Arduino — открыть последовательный порт, ожидая получения входящих данных из графического интерфейса. Посмотрим в void loop.

// смотрим, есть ли входящие последовательные данные: 
if (Serial.available() > 0) { 
  // читаем самый старый байт в последовательном буфере: 
  incomingByte = Serial.read();

Когда я нажимаю кнопку «ON», приложение с графическим интерфейсом отправит символ «H» на Arduino через последовательную связь.

 Затем я напишу несколько кодов для получения этих данных, и я использую код ниже.

if (incomingByte == 'L') {
  digitalWrite(ledPin, LOW);
  Serial.println("Getting L"); //print out to serial monitor to check state
}

Полная версия кода Arduino

// Arduino IDE: 
// Файл -> Примеры -> 04. Связь -> PhysicalPixel 

const int ledPin = 13; // пин, к которому подключен светодиод 
int incomingByte; // переменная хранит последовательные данные 

void setup() { 
  // инициализируем последовательную связь: 
  Serial.begin(9600); 
  // инициализируем вывод светодиода как выход: 
  pinMode(ledPin, OUTPUT); 
} 

void loop() { 
  // смотрим, есть ли входящие последовательные данные: 
  if (Serial.available() > 0) { 
    // читаем самый старый байт в последовательном буфере: 
    incomingByte = Serial.read(); 
    // если это заглавная буква H (ASCII 72), включить светодиод: 
    if (incomingByte == 'H') { 
      digitalWrite(ledPin, HIGH);
      Serial.println("Получение H"); // вывод в последовательный монитор для проверки состояния 
    } 
    // если это L (ASCII 76), выключаем светодиод: 
    if (incomingByte == 'L') { 
      digitalWrite(ledPin, LOW); 
      Serial.println("Получение L"); // вывод в последовательный монитор для проверки состояния 
    } } 
  }

Заключение

Просто помните, что основные концепции этого проекта

  1. Нажатие кнопок из графического интерфейса для отправки данных на плату Arduino через последовательную связь.
  2. Получение входных данных от кнопок графического интерфейса, которые мы нажимали ранее, чтобы использовать эти данные для применения к условиям if-else для управления светодиодом или любыми устройствами, которые вы хотите.