Python & Arduino. Просто, быстро и красиво

Очень часто, у начинающих и не только разработчиков возникают вопросы. Как управлять с Arduino с компьютера? А если компьютер — не Raspberry Pi, а обычный домашний компьютер? А если не хочется мучится с ESP8266 и управлять через веб интерфейс? Неужели надо каждый раз открывать Arduino IDE и отправлять команды через Монитор порта? Именно о том, как создать своё собственное приложение с графическим интерфейсом для управления Arduino я сейчас и расскажу.

Оборудование


Недавно я заполучил очень интересную плату: Arduino SS Micro. Эта плата, внешне напоминающая Digispark Attiny 85, тем не менее является китайской версией Arduino Micro, с выведенным выходом USB.

aawu2yvapatjuclkye5hfyxkdxs.png

Подробно рассказывать о ней я не буду, ведь это уже сделал пользователь YouTube с ником iomoio, и его обзор можно посмотреть здесь.

Как мне кажется — это довольно крутое и удобное устройство для небольших домашних проектов, ведь у проводов есть супер-свойство: теряться в самый неподходящий момент.

В качестве управляющего компьютера был использован MacBook Pro с операционной системой macOS Mojave, но не надо закрывать статью, если вы используете Windows или Linux — всё описанное в статье будет работать без изменений на любой операционной системе.

Скетч для Arduino


В качестве примера будет использоваться скетч, включающий и выключающий светодиод, по команде из Serial-порта.

WARNING

Светодиод в Arduino SS Micro весит на порте SS, и поэтому он автоматически выключается. Не смотря на это, стандартный пример Blink — мигающий светодиод работает.

char inChar;
#define LED_PIN SS

void setup() {
  pinMode(LED_PIN, OUTPUT); // Инициализация светодиода
  Serial.begin(115200); // Инициализация Serial - порта
}

void loop() {
  if (Serial.available() > 0)
  {
    inChar = Serial.read();
    if (inChar=='e') // e - Enable - включить
    {
      digitalWrite(LED_PIN,HIGH);
    }
  }
  
    else if (inChar=='d') // d - Disable - выключить
    {
      digitalWrite(LED_PIN,LOW);
    }
  
    else if (inChar=='b')  // b - Blink - выключить режим мигания
    {
      while (true){
      digitalWrite(LED_PIN,HIGH);
      delay(1000);
      digitalWrite(LED_PIN,LOW);
      delay(1000);
    }
    }
}


Если вы будете использовать другую Arduino — не забудьте сменить пин светодиода.

Код для компьютера


Одним из достоинств Python, кроме его кроссплатформенности — наличие гигантского числа библиотек. Нам понадобятся:

  • PySerial — библиотека для работы с Serial-портом
  • PyQT5 — библиотека для создания графического интерфейса


Установка


Для установки, воспользуемся встроенным менеджером пакетов — pip.

pip install pyserial pyqt5


Для удобства создания GUI можно установить программу QTDesigner.

Интерфейс


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

iyga62zgwjt9p5zbr8ebw-r6xr4.png

Исходный код


Вся работа с устройством происходит благодаря библиотеке PySerial. Но есть несколько нюансов. Например, как узнать, в какой из портов подключено устройство?

На всем прекрасно известном сайте stackoverflow, пользователь с ником Thomas предложил уже готовое решение, которое я и использовал.

def serial_ports():
    """ Lists serial port names

        :raises EnvironmentError:
            On unsupported or unknown platforms
        :returns:
            A list of the serial ports available on the system
    """
    if sys.platform.startswith('win'):
        ports = ['COM%s' % (i + 1) for i in range(256)]
    elif sys.platform.startswith('linux') or sys.platform.startswith('cygwin'):
        # this excludes your current terminal "/dev/tty"
        ports = glob.glob('/dev/tty[A-Za-z]*')
    elif sys.platform.startswith('darwin'):
        ports = glob.glob('/dev/tty.*')
    else:
        raise EnvironmentError('Unsupported platform')

    result = []
    for port in ports:
        try:
            s = serial.Serial(port)
            s.close()
            result.append(port)
        except (OSError, serial.SerialException):
            pass
    return result


Кроме этого необходимо хранить список доступных скоростей:

speeds = ['1200','2400', '4800', '9600', '19200', '38400', '57600', '115200']


А теперь соберём вместе дизайн(созданный в QtDesigner и сконвертированный с помощью утилиты pyuic5 в .py файл), функции для сканирования портов и основной код программы.

Основной класс, содержащий в себе всю логику программы


class LedApp(QtWidgets.QMainWindow, design.Ui_Form):
    def __init__(self):
        super().__init__()
        self.setupUi(self)
        self.Port.addItems(serial_ports())
        self.Speed.addItems(speeds)
        self.realport = None
        self.ConnectButton.clicked.connect(self.connect)
        self.EnableBtn.clicked.connect(self.send)

    def connect(self):
        try:
            self.realport = serial.Serial(self.Port.currentText(),int(self.Speed.currentText()))
            self.ConnectButton.setStyleSheet("background-color: green")
            self.ConnectButton.setText('Подключено')
        except Exception as e:
            print(e)

    def send(self):
        if self.realport:
            self.realport.write(b'b')


Переменные self.Port и self.Speed — это выпадающие списки, содержащие в себе значения доступных портов и скоростей.

При нажатии на кнопку self.ConnectButton вызывается функция connect, в которой производится попытка подключения к заданному порту с заданной скоростью. Если подключение успешно, то кнопка окрашивается в зелёный цвет, и меняется надпись.

gk6yf27-ftwz7k_omji4nuxrraw.png

Функция send отправляет в наш порт байтовую строку — заставляющую включить режим мигания.

Таким образом можно управлять различными устройствами, подключёнными к USB.

Данная статья является вводной и обзорной, более полную информацию можно найти например тут:


Полный исходный код, как скетча для Arduino, так и программы размещён на GitHub.

© Habrahabr.ru