Связь по последовательному порту
У многих микроконтроллеров есть интерфейс UART, позволяющий передавать и принимать различные данные. У интерфейса есть два вывода на плате – пины TX и RX. На большинстве Arduino-плат к этим пинам подключен USB-UART-преобразователь (расположен на плате), при помощи которого плата может определяться компьютером при подключении USB-кабеля и обмениваться с ним информацией. На компьютере создается виртуальный COM-порт (последовательный порт), к которому можно подключиться при помощи программ-терминалов и принимать/отправлять текстовые данные. Таким же образом, кстати, работают некоторые принтеры и большинство станков с ЧПУ. В самой Arduino IDE есть встроенная “консоль” – монитор порта, кнопка с иконкой лупы в правом верхнем углу программы. Нажав на эту кнопку, мы откроем сам монитор порта, в котором будут настройки:

Если с отправкой, автопрокруткой, отметками времени и кнопкой очистить вывод все и так понятно, то конец строки и скорость мы рассмотрим подробнее: Конец строки: тут есть несколько вариантов на выбор, чуть позже вы поймете, на что они влияют. Лучше поставить нет конца строки, так как это позволит избежать непонятных ошибок на первых этапах знакомства с платформой.
- Нет конца строки –-- никаких дополнительных символов в конце введённых символов после нажатия на кнопку отправка или клавишу Enter.
- NL –-- символ переноса строки в конце отправленных данных.
- CR –-- символ возврата каретки в конце отправленных данных.
- NL+CR –-- и то, и то.
Скорость --– тут на выбор нам дается целый список скоростей, т. к. общение по Serial может осуществляться на разных скоростях, измеряемых в бод (baud), и, если скорости приема и отправки не совпадают, – данные будут получены некорректно. По умолчанию скорость стоит 9600, ее и оставим.
Объект Serial
Начнем знакомство с одним из самых полезных инструментов Arduino-разработчика — Serial, который идет в комплекте со стандартными библиотеками. Serial позволяет как просто принимать и отправлять данные через последовательный порт, так и наследует из класса Stream кучу интересных возможностей и фишек. Давайте сразу их все рассмотрим, а потом перейдем к конкретным примерам.
Serial.begin(speed) --- запустить связь по Serial на скорости speed.
Serial.end() --- прекратить связь по Serial. Также освобождает пины RX и TX.
Serial.available() --- возвращает количество байт, находящихся в буфере приема и доступных для чтения.
Serial.availableForWrite() --- возвращает количество байт, которые можно записать в буфер последовательного порта, не блокируя при этом функцию записи.
Serial.print(val) --- отправляет в порт значение val –-- число или строку, фактически печатает.
Serial.print(val, format) --- format позволяет настраивать вывод данных: BIN, OCT, DEC, HEX выведут число в соответствующей системе исчисления.
Serial.read() --- читает и возвращает крайний символ из буфера.
Serial.setTimeout(time) --- устанавливает time (миллисекунды) таймаут ожидания приема данных для следующих ниже функций. По умолчанию равен 1000 мс (1 секунда).
Serial.parseInt() --- читает целочисленное значение из порта и возвращает его (тип long). Заканчивает работу по таймауту.
Serial.parseFloat()--- читает дробное значение из порта и возвращает его (тип long). Заканчивает работу по таймауту.
Отправка в порт
Рассмотрим самый классический пример для всех языков программирования: Hello World! Для этого используем следующий код:
void setup() {
Serial.begin(9600);
Serial.print(Hellow world);
}
void loop() {
}Отправка в порт позволяет узнать значение переменной в нужном месте программы, этот процесс называется отладка. Когда код работает не так, как нужно, начинаем смотреть, где какие переменные какие значения принимают. Или выводим текст из разных мест программы, чтобы наблюдать за порядком ее работы. Во взрослых средах разработки и более серьезных микроконтроллерах есть аппаратная отладка, которая позволяет наблюдать за ходом выполнения программы и значениями любых переменных без вывода в порт.
Чтение из порта
Проблемы возникают при попытке принять данные в порт. Дело в том, что метод read() читает один символ, а если вы отправите длинное число или строку –-- программа получит его по одному символу. Чтобы не нагружать программу чтением пустого буфера, нужно использовать конструкцию:
if (Serial.available()) {
// тут читаем
}Таким образом, чтение будет осуществляться только в том случае, если в буфере есть какие-то данные.
Парсинг цифр
Для чтения целых цифр используем Serial.parseInt(), для дробных –-- Serial.parseFloat(). Пример, который читает целое число и отправляет его обратно:
void setup() {
Serial.begin(9600);
}
void loop() {
if (Serial.available()) {
int val = Serial.parseInt();
Serial.println(val);
}
}Совет: Если при парсинге у вас появляются лишние цифры – поставьте “Нет конца строки” в настройках монитора порта
Вы заметите, что после отправки проходит секунда, прежде чем плата ответит в порт. Эта секунда является таймаутом, о котором мы говорили чуть выше. Программа ждет секунду после принятия последнего символа, чтобы все данные успели прийти. Секунда --- это очень много, лучше ожидать, скажем, 50 миллисекунд. Это можно сделать при помощи метода setTimeout().
void setup() {
Serial.begin(9600);
Serial.setTimeout(50);
}
void loop() {
if (Serial.available()) {
int val = Serial.parseInt();
Serial.println(val);
}
}Теперь после отправки цифры программа будет ждать всего 50 мс и ответит гораздо быстрее.