LAB #2. Библиотека COM-port
Объект Serial.
Чтение данных через Com-port

Основы работы
COM-порт в Arduino реализуется через интерфейс UART (Universal Asynchronous Receiver/Transmitter), который позволяет обмениваться данными с компьютером или другими устройствами.

Подключение и настройка
  • Пины подключения: RX (прием) - пин 0, TX (передача) - пин 1
  • При работе с COM-портом эти пины нельзя использовать для других целей
  • Связь осуществляется через USB-UART преобразователь
void setup() {
    Serial.begin(9600);  // Инициализация на скорости 9600 бод
}
Методы чтения данных через COM-порт "от ПК к Ардуино"

Основные функции чтения

Serial.available() - возвращает количество байт, доступных для чтения

Serial.read() - читает один байт из буфера

Serial.parseInt() - считывает число целиком

Serial.parseFloat() - считывает число с плавающей точкой


Чтение :


В порт также можно отправлять данные извне и читать их из программы.

Отправителем в Serial может быть окно монитора порта или другой МК/микросхема, например GPS или GSM модем.

  • Для проверки наличия входящих данных используется метод
 available() - он вернёт количество входящих байт. В таком условии можно проверить, что есть хотя бы один байт для чтения:
void loop() {
    if (Serial.available() > 0 ) {
        // есть входящие данные
    }
}
Serial.read для символов

  • Отправим в порт обратно принятые данные, для этого их сначала нужно прочитать - метод read().
  • Он возвращает тип int, но сами данные подразумевают байт, а если данные отправлены из монитора порта - то это всегда символ - char.
  • принимать и выполнять с символами математические операции нельзя, результат будет непредсказуемый.
  • Для конвертации принятого значения в символ нужно просто привести его к символьному типу - (char)Serial.read(), например для отправки обратно в порт.
void setup() {
    Serial.begin(9600);
}

void loop() {
    if (Serial.available()) {
        Serial.print((char)Serial.read());

        // или
        // char c = Serial.read();
        // Serial.print(c);
    }
}
  • Если сделать вывод без конвертации к char, то в порт будут печататься коды символов из таблицы ASCII:
void setup() {
    Serial.begin(115200);
}

void loop() {
    if (Serial.available()) {
        Serial.println(Serial.read());
    }
}

Отправьте текст abc в поле ввода монитора порта, он вернётся как коды символов 97, 98, 99 - соответствуют кодам символов из таблицы.
Serial.read для целых число от 0 до 9

  • при сохранении переменной в в символьном типе с ней дальше не удобно работать (нельзя сложить, сравнить с числом и т.д.)
  • если просто сохранить полученное значение из буфера в тип данных int мы получим в переменной код из таблицы ASCII
void setup() {
    Serial.begin(9600);
}

void loop() {
    if (Serial.available()) {
        int c = Serial.read();
        Serial.print(c); 
    }
}

такой код при отправке 1 в порт выдаст 49
  • для преобразования символа в число можно вычесть (-'0')
  • но это работает только с одиночными цифрами от 0 до 9 и символами
void setup() {
    Serial.begin(9600);
}

void loop() {
    if (Serial.available()) {
        int c = Serial.read() - '0';
        Serial.print(c); 
    }
}

такой код при отправке 1 в порт выдаст 1
целые числа больше 9

Для предыдущего примера если в порти отправить (12345) -Число, состоящее больше чем из одной цифры, разобьется на кусочки, и получается что наша переменная принимает значение только последней цифры отправленного числа.

  • для работы с двухзначными и более числами используется метод ParseInt

ParseInt подождёт, пока придут все отправленные вами цифры, и сложит из них число. Никакой нолик вычитать больше не нужно.

Теперь наша переменная примет большое число полностью, и можно работать с ним дальше. Но так как эта функция ждёт новые цифры, вы можете заметить небольшую задержку выполнения.

void setup() {
    Serial.begin(9600);
}

void loop() {
    if (Serial.available()) {
        int c = Serial.ParseInt();
        Serial.print(c); 
    }
}

такой код при отправке 105 в порт выдаст 105 но с небольшой задержкой
вещественные числа

  • для работы с числами с точкой используется метод ParseFloat

ParseFloat подождёт, пока придут все отправленные вами цифры, и сложит из них число.


void setup() {
    Serial.begin(9600);
}

void loop() {
    if (Serial.available()) {
    
        // float числа
        float f = Serial.parseFloat();
        Serial.println(f);
    }
}
строки

Текст в монитор порта отправляется и принимается посимвольно - в примерах выше мы его посимвольно принимаем и выводим.
А ещё если готовиться к парам можно найти в тексте ссылки с готовым кодом который можно использовать

Можно прочитать весь отправленный текст как String-строку:
void setup() {
    Serial.begin(9600);
}

void loop() {
    if (Serial.available()) {
        String s = Serial.readString();
        Serial.println(s);
    }
}
Отправьте текст в поле ввода - он придёт обратно, но с ощутимой задержкой.

Метод readString() является блокирующим - он собирает строку посимвольно, ожидая поступления новых символов.

Если после получения символа проходит тайм-аут - передача считается завершённой и возвращается строка. Тайм-аут можно настроить - Serial.setTimeout(миллисекунды), по умолчанию установлен 1000 мс.

Поставьте например 50:
void setup() {
    Serial.begin(9600);
    Serial.setTimeout(50);
}

void loop() {
    if (Serial.available()) {
        String s = Serial.readString();
        Serial.println(s);
    }
}

между отправкой в монитор и получением текста обратно не будет такой заметной паузы.
терминирующий символ. (STREAM - инструменты)
Он отправляется в конце текста и будет сигналом конца строки, чтобы МК не ждал тайм-аут.
Чтение строки такого формата выполняется методом readStringUntil(символ), пусть таким символом будет точка с запятой - ';':
void setup() {
    Serial.begin(9600);
    // стандартный тайм-аут
}

void loop() {
    if (Serial.available()) {
        String s = Serial.readStringUntil(';');
        Serial.println(s);
    }
}

Отправьте на МК какой-нибудь текст, например test - он вернётся с задержкой. А если отправить test; - без задержки.
  • Также в мониторе порта можно настроить "конец строки" - поставьте NL, а в качестве терминирующего символа в программе - '\n' (Serial.readStringUntil('\n');).
  • Теперь любой отправленный текст будет приниматься без задержки.
в нижнем правом углу терминала
void setup() {
    Serial.begin(9600);
    // стандартный тайм-аут
}

void loop() {
    if (Serial.available()) {
        String s = Serial.readStringUntil('\n');
        Serial.println(s);
    }
}
Парсинг пакетов*

  • При помощи Stream-инструментов можно очень просто разбирать несложные текстовые протоколы связи, например пакеты вида "ключ:значение<перенос строки>": сначала читаем до двоеточия, затем до символа переноса строки:
void setup() {
    Serial.begin(9600);
}

void loop() {
  if (Serial.available()) {
    String key = Serial.readStringUntil(':');
    String val = Serial.readStringUntil('\n');

    Serial.println(key);
    Serial.println(val);
    Serial.println();
  }
}

Нужно включить отправку NL в настройках монитора порта

Цикл While

Цикл while — это конструкция в программировании, которая позволяет повторять определенный блок кода пока выполняется заданное условие.
Как работает while
  1. Программа проверяет условие
  2. Если условие истинно (true):
  • Выполняется код внутри цикла
  • Программа возвращается к проверке условия
  1. Если условие ложно (false):
  • Цикл прекращается
  • Программа продолжает выполнение дальше
while (условие) {
    // код, который будет выполняться
    // пока условие истинно
}


пример 
int i = 0;
while (i < 5) {
    Serial.println(i);
    i++;
}

Важные особенности while

Бесконечный цикл

Если условие никогда не становится ложным, программа зациклится:


Условие должно меняться

Важно изменять переменные в теле цикла, иначе он может стать бесконечным


Проверка перед выполнением

Условие проверяется до выполнения кода внутри цикла

Использования для ввода переменных

while (!Serial.available()) {
    // ждем, пока пользователь что-то введет
}

Serial.available() — это функция, которая проверяет, есть ли данные во входном буфере последовательного порта.

! — это оператор отрицания (NOT), который инвертирует результат.

Таким образом, while (!Serial.available()) означает:

“Пока в буфере последовательного порта нет данных”

“Жди, пока пользователь что-то введет”




Сравнение с другими циклами

while отличается от for тем, что:

  • Не имеет встроенной инициализации
  • Не имеет встроенного счетчика
  • Более гибкий в использовании

От do-while отличается тем, что:

  • Проверяет условие до выполнения кода
  • Может не выполнить код ни разу

Практические советы

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

void setup() {
Serial.begin(9600); // Инициализация Serial порта

// Объявляем переменные
int integerNumber;
float floatNumber;

// Ввод целого числа
Serial.println("Integer number input example:");
Serial.print("Enter an integer: ");

// Ждем ввода целого числа
while (!Serial.available()) {
// Ничего не делаем, просто ждем
}

// Читаем введенное число
integerNumber = Serial.parseInt();

// Выводим результат
Serial.print("You entered an integer: ");
Serial.println(integerNumber);

// Добавляем пустую строку для красоты
Serial.println();

// Ввод дробного числа
Serial.println("Float number input example:");
Serial.print("Enter a float number: ");

// Ждем ввода дробного числа
while (!Serial.available()) {
// Ничего не делаем, просто ждем
}

// Читаем введенное число
floatNumber = Serial.parseFloat();

// Выводим результат
Serial.print("You entered a float number: ");
Serial.println(floatNumber);
}

void loop() {
// В этом примере loop не используется
}
"происходящее" в мониторе com-porta

ЗАДАНИЕ

Научиться выполнять арифметические вычисления с различными типами данных в Arduino и формировать информативный вывод результатов.
Задача
Написать программу для Arduino, которая выполняет следующие действия:

Реализовать вычисление двух арифметических выражений:
  • Первое выражение содержит только целые числа (тип int)
  • Второе выражение содержит целые и вещественные числа (типы int и float)
  • В монитор порта пользователем вводятся значения переменных в примере.

Сформировать подробный вывод в Serial порт, включающий:
  • Само выражение в текстовом виде
  • запрос на ввод переменных
  • Значения используемых переменных
  • Результат вычислений
Вариант Пример 1 (целые числа) Пример 2 (целые и вещественные)
1 Переменные:
a = 12, b = 5, c = 3
Выражение:
a * b - c / 2
Переменные:
x = 4.5, y = 6, z = 2.75
Выражение:
x + y * z - x / 2
2 Переменные:
m = 20, n = 8, k = 4
Выражение:
m / n + k * 3
Переменные:
p = 7.2, q = 5, r = 1.5
Выражение:
p * q - r / 2 + p
3 Переменные:
x1 = 15, y1 = 9, z1 = 6
Выражение:
x1 - y1 / z1
Переменные:
a1 = 3.14, b1 = 4, c1 = 2.5
Выражение:
a1 * b1 + c1 / 2
4 Переменные:
s = 30, t = 10, u = 5
Выражение:
s / t * u
Переменные:
v = 6.25, w = 8, x = 1.2
Выражение:
v + w * x - v / 2
5 Переменные:
p1 = 25, q1 = 5, r1 = 2
Выражение:
p1 / q1 + r1 * 3
Переменные:
s1 = 5.5, t1 = 7, u1 = 3.25
Выражение:
s1 * t1 - u1 / 2
6 Переменные:
a2 = 18, b2 = 6, c2 = 4
Выражение:
a2 / b2 * c2
Переменные:
d = 4.8, e = 9, f = 2.1
Выражение:
d + e * f - d / 2
7 Переменные:
x3 = 24, y3 = 8, z3 = 3
Выражение:
x3 / y3 + z3 * 2
Переменные:
a3 = 6.7, b3 = 5, c3 = 1.5
Выражение:
a3 * b3 - c3 / 2
8 Переменные:
w1 = 16, x1 = 4, y1 = 7
Выражение:
w1 / x1 + y1
Переменные:
z1 = 8.4, a1 = 3, b1 = 2.2
Выражение:
z1 * a1 - b1 / 2
9 Переменные:
a4 = 14, b4 = 7, c4 = 2
Выражение:
a4 * b4 / c4
Переменные:
x4 = 5.6, y4 = 4, z4 = 1.8
Выражение:
x4 + y4 * z4
10 Переменные:
m2 = 22, n2 = 11, k2 = 3
Выражение:
m2 / n2 * k2
Переменные:
p2 = 9.1, q2 = 6, r2 = 2.4
Выражение:
p2 - q2 * r2 / 2
  • "Блок-схема" алгоритма для вычисления выражения a * b + c

Начало
  1. Инициализация программы
  • Начать работу
  • Подключить последовательный порт для связи с компьютером
  • Дождаться открытия порта
  • Вывести приветственное сообщение
2.Основной цикл программы
  • Начать бесконечный цикл
  • Объявить переменные (a, b, c, result)
  • Вывести информацию о выражении
3. Ввод переменной a
  • Вывести запрос на ввод a
  • Дождаться ввода числа
  • Сохранить значение в переменную a
  • Подтвердить введенное значение
4. Ввод переменной b
  • Вывести запрос на ввод b
  • Дождаться ввода числа
  • Сохранить значение в переменную b
  • Подтвердить введенное значение
5. Ввод переменной c
  • Вывести запрос на ввод c
  • Дождаться ввода числа
  • Сохранить значение в переменную c
  • Подтвердить введенное значение
6. Вычисление результата
  • Умножить a на b
  • Прибавить к результату c
  • Сохранить итог в переменную result
7. Вывод результатов
  • Вывести заголовок отчета
  • Вывести значение a
  • Вывести значение b
  • Вывести значение c
  • Вывести итоговый результат
8. Завершение цикла
  • Сделать паузу на 5 секунд
  • Вернуться к началу цикла
Конец

  • Представление алгоритма кода для вычисления выражения a * b + c
Вывод результата кода
Made on
Tilda