Двусторонний обмен данными между компьютером и Arduino по UART

Сегодня я хочу поделиться с вами достаточно важным проектом, несмотря на то, что это опять «Мигание светодиодами».Мы все знаем, что объединение программирования, электроники и механики открывает бесконечные возможности, и именно поэтому мы занимаемся робототехникой! Сегодня мы разработаем двунаправленный обмен данными между Python скриптом и Arduino!

Двусторонний обмен данными между компьютером и Arduino по UART

Проект предполагает создание двух частей: программы на Python, которая обеспечивает графический интерфейс для управления светодиодами, и скетча для Arduino, который реагирует на команды от Python и управляет светодиодами. Взаимодействие между этими частями осуществляется через Serial порт по протоколу UART.

Двусторонний обмен данными между компьютером и Arduino по UART

Важно, что нажатия физических и виртуальных кнопок синхронизируют события, происходящие в двух программах!Синхронизация данных - ключевой аспект в системах, где программное обеспечение взаимодействует с аппаратным. Это процесс, при котором данные передаются между программой и физическим устройством (например, между компьютером и микроконтроллером) таким образом, чтобы обеспечивать актуальность и точность информации с обеих сторон.

Эффективная синхронизация данных важна не только в учебных проектах, но и в реальных задачах, таких как робототехника, автоматизированные производственные линии, медицинское оборудование и многих других областях, где программное обеспечение взаимодействует с физическим миром.

Подключения светодиодов и кнопок к Arduino

Двусторонний обмен данными между компьютером и Arduino по UART
  • Подключение светодиодов

Светодиоды подключаются к цифровым пинам Arduino. В нашем проекте используются пины 5, 6 и 7. Каждый светодиод подключается одним выводом к пину Arduino, а другим - к земле (GND) через резистор для ограничения тока. Это предотвращает повреждение светодиодов из-за чрезмерного тока.

Важно учитывать полярность светодиода: длинная ножка (анод) подключается к пину Arduino, а короткая (катод) - к земле через резистор.

  • Подключение кнопок

Кнопки подключаются к цифровым пинам Arduino (в проекте используются пины 2, 3 и 4). Один контакт кнопки подключается к пину, а другой - к земле.

В коде Arduino используется режим INPUT_PULLUP, который активирует внутренний подтягивающий резистор к питанию. Это обеспечивает стабильное высокое (HIGH) состояние при отпущенной кнопке и низкое (LOW) при нажатии.

  • Управление светодиодами

Состояние всех трех светодиодов хранится в одной переменной `ledStates`, что оптимизирует использование памяти. Каждый бит этой переменной соответствует состоянию одного светодиода.

Битовые операции используются для изменения и проверки состояния каждого светодиода. Например, операция `(ledStates >> i) & 1` возвращает состояние i-го светодиода.

  • Переключение светодиодов

При изменении состояния светодиода используется операция XOR (`^=`), которая переключает соответствующий бит в `ledStates`.Например, `ledStates ^= (1 << i)` переключит i-й светодиод.

  • Чтение состояния кнопок

В основном цикле `loop()` Arduino постоянно проверяет состояние кнопок. Когда кнопка нажата, её пин переходит в низкое состояние (LOW).

Для каждой кнопки используются переменные, которые хранят время последнего изменения состояния и последнее зафиксированное состояние.

  • Антидребезг

Антидребезг необходим, чтобы избежать ложных срабатываний из-за естественного "дребезга" контактов при нажатии кнопки. Это состояние, когда кнопка еще нажата не до конца и нет точного режима (нажата или отпущена).Если текущее состояние кнопки отличается от последнего зафиксированного и прошло достаточно времени (задержка антидребезга), состояние кнопки обновляется, и происходит изменение состояния соответствующего светодиода.

// Определение пинов, к которым подключены светодиоды и кнопки const int ledPins[] = { 5, 6, 7 }; // Пины светодиодов const int buttonPins[] = { 2, 3, 4 }; // Пины кнопок int ledStates = 0; // Состояния светодиодов хранятся в одном байте // Настройка начальных параметров void setup() { Serial.begin(9600); // Инициализация порта // Настройка пинов светодиодов на вывод и пинов кнопок на ввод с подтяжкой к питанию for (int i = 0; i < 3; i++) { pinMode(ledPins[i], OUTPUT); pinMode(buttonPins[i], INPUT_PULLUP); } } // Основной цикл выполнения void loop() { // Проверка на наличие данных в порту if (Serial.available() > 0) { ledStates = Serial.read(); // Чтение полученных данных // Перебор светодиодов и обновление их состояния for (int i = 0; i < 3; i++) { digitalWrite(ledPins[i], (ledStates >> i) & 1); // Установка состояния светодиода } } // Перебор кнопок для обработки их состояний for (int i = 0; i < 3; i++) { static unsigned long lastDebounceTime[3] = { 0, 0, 0 }; // Таймеры для антидребезга static bool lastButtonState[3] = { HIGH, HIGH, HIGH }; // Последние состояния кнопок bool currentButtonState = !digitalRead(buttonPins[i]); // Чтение текущего состояния кнопки // Проверка на изменение состояния кнопки и антидребезг if (currentButtonState != lastButtonState[i] && (millis() - lastDebounceTime[i] > 50)) { lastDebounceTime[i] = millis(); // Обновление таймера антидребезга lastButtonState[i] = currentButtonState; // Обновление состояния кнопки // Обработка нажатия кнопки if (currentButtonState == HIGH) { // Если кнопка отпущена ledStates ^= (1 << i); // Переключение бита состояния светодиода digitalWrite(ledPins[i], (ledStates >> i) & 1); // Обновление состояния светодиода // Отправка обновленных состояний светодиодов через порт Serial.write(ledStates); } } } }

Подробнее об обработке нажатия кнопки можно прочитать в этом материале:

Однако метод описанный выше не позволяет эффективно взаимодействовать с множеством кнопок, поэтому предлагаю вам метод, который легко интегрировать в код на Arduino с любым количеством кнопок, увеличенным функционалом - теперь удобно контролировать не только момент нажатия, но и удержание, а также отпускание кнопки.
📦 Всё это собрано в удобном классе!
🔗 Статья на VC.ru
🔗 Проект в TinkerCad , где можно сразу опробовать
Если у вас будут затруднения с входом в TinkerCad, метод входа описан в одном из уроков БЕСПЛАТНОГО курса по электронике !
В том курсе также подробно разбирается (в прямом смысле этого слова) устройство кнопок и выключателей:

Двусторонний обмен данными между компьютером и Arduino по UART

Программирование на Python

  • Обнаружение Arduino

Скрипт начинается с поиска доступных COM-портов, чтобы найти подключенную Arduino. Это достигается с помощью библиотеки `serial.tools.list_ports`.

Функция `find_arduino_port()` перебирает все доступные порты, пытаясь открыть их с помощью `serial.Serial`. Успешное открытие порта со скоростью 9600 бит/с говорит о том, что Arduino найдена.

# Импортируем необходимые библиотеки import serial import serial.tools.list_ports import cv2 import numpy as np import time # Функция для автоматического поиска и подключения к Arduino def find_arduino_port(): # Получаем список всех доступных COM-портов ports = serial.tools.list_ports.comports() for port in ports: print(port, end=" ") try: # Пытаемся открыть каждый порт ser = serial.Serial(port.device, 9600, timeout=1) print("opened") return ser # Возвращаем объект порта, если успешно except serial.SerialException: # Продолжаем поиск, если порт не подходит continue # Выводим сообщение, если Arduino не найдена print("Arduino не найдена.") return None # Ищем Arduino и подключаемся ser = find_arduino_port() if ser is None: exit() # Завершаем программу, если подключение не удалось time.sleep(2) #Ждем открытия порта # Переменная для хранения состояний светодиодов ledStates = 0 # Функция для отрисовки интерфейса управления светодиодами def draw_interface(): # Создаем черное изображение img = np.zeros((220, 310, 3), dtype=np.uint8) for i in range(3): # Определяем центральные точки светодиодов и кнопок center = (50 + i*100, 40) bottom_center = (50 + i*100, 60) # Выбираем цвет светодиода в зависимости от его состояния led_color = (0, 255, 0) if ledStates & (1 << i) else (0, 50, 0) # Рисуем полукруг и прямоугольник, имитируя форму светодиода cv2.ellipse(img, center, (20, 20), 0, 180, 360, led_color, -1) cv2.rectangle(img, (center[0] - 20, center[1]), (center[0] + 20, bottom_center[1]), led_color, -1) # Рисуем "ножки" светодиода cv2.line(img, (center[0] - 10, bottom_center[1]), (center[0] - 10, bottom_center[1] + 30), (200, 200, 200), 2) cv2.line(img, (center[0] + 10, bottom_center[1]), (center[0] + 10, bottom_center[1] + 40), (200, 200, 200), 2) # Рисуем кнопки button_center = (50 + i*100, 170) button_color = (200, 200, 200) cv2.rectangle(img, (30 + i*100, 150), (70 + i*100, 190), button_color, -1) cv2.circle(img, button_center, 10, (0, 0, 0), -1) # Добавляем подписи к светодиодам cv2.putText(img, f'LED {i+1}', (30 + i*100, 189), cv2.FONT_HERSHEY_SIMPLEX, 0.4, (0, 0, 0), 1) # Отображаем созданное изображение cv2.imshow('LED Control', img) # Функция для обработки событий мыши def mouse_callback(event, x, y, flags, param): global ledStates # Проверяем нажатие левой кнопки мыши if event == cv2.EVENT_LBUTTONDOWN: # Перебираем координаты кнопок for i in range(3): if 30 + i*100 < x < 70 + i*100 and 150 < y < 190: # Изменяем состояние светодиода и отправляем новое состояние в Arduino ledStates ^= (1 << i) ser.write(bytearray([ledStates])) return # Настройка окна OpenCV и привязка функции обработки событий мыши cv2.namedWindow('LED Control') cv2.setMouseCallback('LED Control', mouse_callback) # Основной цикл программы while True: # Чтение данных с Arduino if ser.in_waiting > 0: ledStates = ord(ser.read(1)) # Обновляем состояния светодиодов draw_interface() # Рисуем интерфейс if cv2.waitKey(1) == 27: break # Закрытие окна и освобождение ресурсов cv2.destroyAllWindows() ser.close()
  • Установление соединения

После обнаружения Arduino скрипт устанавливает соединение. Это обеспечивает двустороннюю связь между Python-скриптом и Arduino.

После подключения программа ждет две секунды, чтобы убедиться, что соединение стабильно и готово к передаче данных.

  • Создание графического интерфейса с помощью OpenCV для управления светодиодами

С помощью библиотеки OpenCV создается простой графический интерфейс, который включает в себя визуальное представление светодиодов и кнопок управления.
Почему мы рисуем интерфейс при помощь OpenCV?
Потому что этот проект - часть большого курса по робототехнике, который, конечно же, включает в себя изучение Python и компьютерного зрения!
Бесплатный курс по основам Python ( по завершении выдается сертификат)

Двусторонний обмен данными между компьютером и Arduino по UART

Ну а тем, кто хочет больше, рекомендую расширенный курс на Stepik (и дарю промокод на 20% скидку до 11 ноября 2024 года):
Записаться на курс и получить скидку

В курсе есть несколько бесплатных уроков, чтобы каждый мог попробовать свои силы!

Для каждого светодиода на интерфейсе отображается его текущее состояние (включен или выключен), а также кнопки для управления ими.

  • Отрисовка элементов управления

Используя функции OpenCV, такие как `cv2.ellipse` и `cv2.rectangle`, рисуются элементы управления: полукруги для светодиодов и прямоугольники для кнопок.

Двусторонний обмен данными между компьютером и Arduino по UART

Цвета и размеры элементов выбираются так, чтобы четко отображать состояние каждого светодиода и обеспечивать понятный интерфейс.

  • Обработка событий мыши и синхронизация состояний светодиодов

Для взаимодействия пользователя с интерфейсом применяется обработчик событий мыши `mouse_callback`.

При клике на кнопку интерфейса скрипт определяет, какой светодиод должен быть переключен, изменяя переменную `ledStates`.

  • Синхронизация состояний

После изменения состояния светодиода, обновленное состояние отправляется в Arduino через последовательный порт, используя функцию `ser.write`.

Arduino, получив данные, обновляет состояние светодиодов соответственно. Таким образом, достигается синхронизация состояний между графическим интерфейсом на компьютере и физическими светодиодами.

Правило действительно и в обратную сторону, когда пользователь нажимает кнопку, подключенную к Arduino, светодиод переключает состояние, и с помощью команды Serial.write(ledStates) передает новые состояния в Python-скрипт.

Конечно же, весь материал доступен для подробного изучения и самостоятельного применения!

Это лишь один из способов создавать проекты, объединяющие Python, Arduino, электронику, разработку интерфейсов, работу с библиотеками для компьютерного зрения и много другое!

Удачи и успехов в освоении!

Начать дискуссию