Перейти к основному содержимому

Устройства с COM-портом и Arduino

Встроенные функции Serial* работают со всем, что отображается как COM-порт: Arduino, ESP32 или Raspberry Pi Pico через USB, адаптер USB — последовательный порт, GPS-приёмник, сканер штрихкодов в режиме последовательного порта, чековый принтер или принтер этикеток, 3D-принтер или контроллер ЧПУ, принимающий G-код, лабораторный прибор или самодельный кнопочный пульт, макропанель, поворотная ручка или контроллер светодиодов. Устройство отправляет и принимает строки текста; ваши скрипты превращают эти строки во что угодно, что умеет Input.Observer.

Несколько идей:

  • Кнопочный пульт или макропанель. Arduino считывает свои кнопки и выводит B1, B2, ... Монитор COM-порта превращает каждую строку в сочетание клавиш, мультимедийную клавишу или команду окна.
  • Ручка громкости или прокрутки. Потенциометр или энкодер выводит своё положение; монитор устанавливает громкость или прокручивает.
  • Индикаторы состояния. Жест или горячая клавиша отправляет LED 255 0 0 плате, управляющей светодиодной лентой, и свет показывает, что идёт запись или сборка завершилась ошибкой.
  • Датчики. Таймер запрашивает у платы температуру или освещённость и показывает её, записывает в файл или затемняет экран.
  • Сканеры и считыватели. Каждый штрихкод или RFID-метка, которые присылает считыватель, становится набранным текстом, поисковым запросом или поиском в файле.
  • Станки. Отправляйте строки G-кода принтеру или плоттеру, по одному SerialWriteTextLine на команду.

Поиск порта​

Windows даёт каждому устройству имя вроде COM3. Диспетчер устройств показывает его в разделе Порты (COM и LPT); Arduino IDE — в меню выбора платы. Скрипт тоже может их перечислить:

n = SerialEnumeratePorts();
if (n == 0) UtilityPrint("No COM ports. Is the board plugged in?");
for (i = 0; i < n; i = i + 1) {
UtilityPrint(SerialGetEnumeratedPortAt(i));
}

Список отсортирован по номеру, поэтому COM3 идёт перед COM10. Номер может измениться, если подключить устройство к другому USB-разъёму.

Скорость, формат кадра и окончания строк​

  • Скорость (baudRate) должна совпадать со скоростью устройства: числом в Serial.begin(...) скетча Arduino, обычно 9600 или 115200.
  • Формат кадра почти всегда 8 бит данных, без контроля чётности, один стоповый бит: 8, SerialParity.None, SerialStopBits.One. Пишите константы: простое число 1 в качестве аргумента стоповых битов означает полтора стоповых бита.
  • Окончания строк. Принятая строка заканчивается на заданном вами terminator, который удаляется из строки. Пустой текст означает CR LF — то, что отправляет Serial.println в Arduino. Для устройства, завершающего строки одним LF, используйте "\n"; для одного CR — "\r". SerialWriteTextLine завершает отправляемое тем же завершителем или через CR LF, если порт открывает сам этот вызов. С CR LF скетч Arduino, читающий через Serial.readStringUntil('\n'), получает строку с завершающим CR, который удаляет trim(); с "\n" удалять нечего.
  • Сигналы. При открытии порта включаются его сигналы DTR и RTS, как это делает Arduino IDE. Это перезапускает большинство плат Arduino Uno и Nano, а также другие платы с такой же схемой сброса.

Три способа общаться с устройством​

  • Один вопрос — один ответ. SerialWriteTextLine и SerialGetTextLine для неоткрытого порта открывают его на скорости baudRate из вызова, 8-N-1, на время одного вызова. Если порт уже открыт, аргумент baudRate не учитывается. Подходит для устройства, которое не перезапускается при открытии порта. См. Задать вопрос устройству на COM-порту.

  • Постоянно открытый порт. SerialOpenPort открывает порт с вашими настройками до вызова SerialClosePort и собирает каждую строку, которую отправляет устройство, для чтения через SerialGetTextLine. Большинство плат Arduino Uno и Nano перезапускаются при каждом открытии порта, поэтому откройте порт один раз, в глобальном событии Load, а жесты и горячие клавиши пусть пишут в него. SerialOpenPort возвращает false для порта, который отсутствует, отключён или открыт в другой программе; для порта, который Input.Observer уже открыл или который занимает монитор, он останавливает действие с ошибкой.

    // Load global event: open the port once, at the sketch's speed. An Uno or Nano
    // restarts every time its port opens, so a port opened per command would lose
    // the command while the board boots.
    if (!SerialOpenPort("COM4", 115200, SerialParity.None, 8, SerialStopBits.One, "")) {
    UtilityPrint("COM4 isn't available: unplugged, or open in the Arduino IDE's Serial Monitor?");
    }

    // Any gesture or hotkey: send a command line and read the board's reply.
    // (The sketch reads a line with Serial.readStringUntil('\n') and answers OK.)
    if (SerialWriteTextLine("COM4", "LED 255 0 0", 115200)) {
    reply = SerialGetTextLine("COM4", 1, 115200);
    if (reply != "OK") UtilityPrint(StringFormat("Board said: {0}", reply, ""));
    }
  • Монитор. SerialMonitorCreate выполняет скрипт для каждой строки, которую отправляет устройство, в фоне, до вызова SerialMonitorDelete. Строка — это ContextGetSerialTextLine(). См. Кнопки устройства на COM-порту как мультимедийные клавиши и:

    // Arduino side: a potentiometer on A0, one reading per line.
    // void setup() { Serial.begin(115200); }
    // void loop() { Serial.println(analogRead(A0)); delay(50); }
    // Empty text as the terminator means CR LF, which Serial.println sends.
    SerialMonitorCreate("knob", "COM4", 115200, SerialParity.None, 8, SerialStopBits.One, "",
    "line = ContextGetSerialTextLine(); if (StringIsNumber(line, true)) MultimediaSetVolume(AudioEndpoint.Playback, StringToNumber(line, true) / 1023.0);");
примечание

Что нужно знать о мониторах.

  • Строки обрабатываются по одной, в порядке поступления: скрипт для следующей строки начинается, когда скрипт для предыдущей завершился. Ожидать могут до 256 строк; сверх этого отбрасываются самые старые.
  • Остановка всех действий останавливает скрипт, выполняющийся для строки, и отбрасывает ожидающие строки. Сам монитор продолжает работать. Горячая клавиша остановки всех действий идёт дальше: она удаляет все мониторы и закрывает их порты.
  • Любой скрипт, включая собственный скрипт монитора, может отправлять данные в отслеживаемый порт через SerialWriteTextLine, поэтому монитор может отвечать своему устройству. SerialGetTextLine не может читать отслеживаемый порт: каждая строка достаётся монитору.
  • Каждое имя монитора принадлежит одному порту. SerialMonitorCreate с именем монитора этого порта заменяет этот монитор. Он останавливает действие с ошибкой, если это имя уже отслеживает другой порт, если порт открыт через SerialOpenPort или если порт занимает другой монитор. Порт, который отсутствует, отключён или открыт в другой программе, даёт false.
  • Отключение устройства завершает монитор и записывает строку на вкладку Система консоли. Ничто не перезапускает монитор, когда устройство возвращается. Выполните SerialMonitorCreate снова (из горячей клавиши или из события Load при следующем запуске).
warning

COM-порт может быть открыт только одной программой. Пока в Arduino IDE открыт Монитор порта или Плоттер по последовательному соединению, Input.Observer не может открыть порт, а пока порт открыт в Input.Observer, IDE не может загрузить скетч. Закройте порт (SerialClosePort или SerialMonitorDelete) перед загрузкой.