Опубликовано

Взаимодействие Arduino с компьютерной программой Processing

По материалам книги Джереми Блума “Изучаем Arduino: инструменты и методы технического волшебства. 2-е изд.: пер. с англ.“(Глава 7. Последовательный интерфейс USB)

Изучаем Arduino: инструменты и методы технического волшебства. 2-е изд.

Прежде чем рассмотреть взаимодействие Arduino с компьютерной программой, рассмотрим как такое взаимодействие организовано посредством программы Монитора порта.

Отправка одиночных символов для управления светодиодом

Прежде чем приступать к обработке чисел, состоящих из нескольких цифр, разработаем скетч для управления светодиодом с помощью одного символа. В частности, при получении символа “1” скетч будет включать светодиод, а символа “0” — выключать его. Для работы этого скетча нужно подключить светодиод к контакту 9 платы Arduino, как показано на рис. 7.10.

C:\Users\Anansi\AppData\Local\Temp\FineReader10\media\image10.jpeg

Рис. 7.10. Подключение светодиода к контакту 9 платы Arduino (Рисунок создан в программе Fritzing)

Как мы разобрались в предыдущем разделе, при отправке одиночного символа проще всего сравнить его с целевым символом в операторе условия if. Каждый помещаемый в буфер символ сравнивается с символом ‘0’ или ‘1’, и в зависимости от того, с каким символом он совпадает, предпринимается соответствующее действие. Создайте новый скетч, скопируйте в него код из листинга 7.4, загрузите его в свою плату Arduino и экспериментируйте, отправляя одиночные символы 0 и 1 из монитора порта.

Листинг 7.4. Программа single_char_control.ino для управления светодиодом с помощью одиночных символов

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// Управление светодиодом с помощью одиночных символов
const int LED=9;
char data; // Переменная для хранения получаемого символа
void setup()
{
Serial.begin(9600); // Запускаем последовательный порт
// со скоростью в бодах = 9600
pinMode(LED, OUTPUT);
}
 
void loop()
{
// Предпринимаем действие только при наличии данных в буфере
if (Serial.available() > 0)
{
data = Serial.read(); // Считываем входящий байт данных
// Включаем светодиод
if (data == '1')
{
digitalWrite(LED, HIGH);
Serial.println("LED ON");
}
 
// Выключаем светодиод
else if (data == '0')
{
digitalWrite(LED, LOW);
Serial.println("LED OFF");
}
}
}

Обратите внимание на то, что вместо простого оператора условия if…else в коде присутствует его вариант if…else…if. Поскольку в каждой передаче вместе с символами управления светодиодом монитор порта также отправляет и символ новой строки, важно удалить этот символ из буфера. Считываемый функцией Serial.read() символ новой строки не проходит проверку на превышение символа ‘0’, и в скетче не предпринимается никакого действия по его обработке. Обрабатываются только получаемые символы ‘0’ и ‘1’. Если же в коде использовался бы вариант if…else, то светодиод выключался бы при получении как символа ‘0’, так и символа новой строки ‘\n’. Более того, даже при получении символа ‘1’ скетч сразу же после включения светодиода выключал бы его при получении символа новой строки ‘\n’, следующего за символом ‘1’.

Управление разноцветным светодиодом с помощью списка значений

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

C:\Users\Anansi\AppData\Local\Temp\FineReader10\media\image11.jpeg

Рис. 7.11. Подключение разноцветного светодиода к плате Arduino (Рисунок создан в программе Fritzing)

Для управления разноцветным светодиодом нам нужно посылать плате Arduino три значения в диапазоне 0–100, задающие яркость каждого из составляющих его трех светодиодов. Например, чтобы задать полную яркость для всех трех составляющих светодиодов, нам нужно отправить значения 100,100,100. Решение этой задачи сопряжено с несколькими проблемами:

  • Нам нужно как-то отличить числа от запятых.
  • Последовательность символов нужно преобразовать в целые числа и сопоставить эти числа значениям в диапазоне 0–255 для управления светодиодами, используя функции analogWrite().
  • Нам также нужно учитывать, что мы работаем со светодиодом с общим анодом, и сигналы управления подаются на катоды составляющих его светодиодов. Иными словами, значение 255 выключит светодиод, а значение 0 включит его на полную яркость.
  • Необходимо учитывать возможность, что значения яркости могут состоять из одной, двух или трех цифр.
  • Наша программа должна быть достаточно надежной, способной обрабатывать полученные данные, форматирование которых не соответствует норме, таким образом, чтобы они не искажали данные последующих передач (в разумных пределах).

К счастью среда Arduino IDE содержит очень удобную функцию Serial.parseInt() для определения и извлечения целых чисел. Функция ожидает, пока во входной буфер последовательного порта не поступит нецифровое значение, после чего она преобразовывает все предыдущие цифры в целое число. В нашем случае, первые два значения считываются по обнаружению следующих за ними запятых, а последнее — после обнаружения символа перевода строки.

Чтобы увидеть работу этой функции, загрузите код из листинга 7.5 в свою плату Arduino.

Листинг 7.5. Программа list_control.ino для управления разноцветным светодиодом

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
// Отправляем одновременно несколько значений
// Данные отправляются в следующем формате: <0-100>,<0-100>,<0,100)>\n
// Здесь числа представляют яркость в процентах красного,
// зеленого и синего светодиодов
// Контакты платы Arduino, к которым подключаются катоды светодиодов
const int RED = 11;
const int GREEN = 10;
const int BLUE = 9;
void setup()
{
Serial.begin(9600); // Запускаем последовательный порт
// со скоростью в бодах = 9600
Serial.setTimeout(10); // Таймаут, в течение которого
// ожидать целое число
// Задаем выходной режим работы для контактов управления светодиодами
pinMode(RED, OUTPUT);
pinMode(GREEN, OUTPUT);
pinMode(BLUE, OUTPUT);
// Выключаем светодиоды
// Поскольку светодиод имеет общий анод, для выключения светодиода
// на катод подается сигнал высокого уровня
digitalWrite(RED, HIGH);
digitalWrite(GREEN, HIGH);
digitalWrite(BLUE, HIGH);
}
void loop()
{
// Считываем данные, когда они присутствуют в буфере
if (Serial.available() > 0)
{
// Ожидаем получить три целых числа по последовательному каналу связи
// Функция parseInt() заблокирует исполнение следующего кода
// до тех пора, пока не будет получено действительное целое число
// Целое число определяется по завершающей запятой
// или символу новой строки
// Недействительные символы удаляются перед найденным целым числом,
// а не после него
int val1 = Serial.parseInt();
int val2 = Serial.parseInt();
int val3 = Serial.parseInt();
// После считывания всех целых чисел, очищаем буфер
while (Serial.available())
{
Serial.read();
}
// Ограничиваем полученные значения диапазоном 0–100%
int val1c = constrain(val1,0,100);
int val2c = constrain(val2,0,100);
int val3c = constrain(val3,0,100);
// Сопоставляем аналоговые значения процентным значениям
int rval = map(val1c,0,100,255,0); // Первое действительное
// целое число
int gval = map(val2c,0,100,255,0); // Второе действительное
// целое число
int bval = map(val3c,0,100,255,0); // Третье действительное
// целое число
// Устанавливаем яркость светодиода
analogWrite(RED, rval);
analogWrite(GREEN, gval);
analogWrite(BLUE, bval);
// Выводим на экран использованные значения яркости
Serial.println("Red: " + String(val1c) + "%");
Serial.println("Green: " + String(val2c) + "%");
Serial.println("Blue: " + String(val3c) + "%\n");
}
}

В функции setup() запускаем последовательный интерфейс, а затем с помощью функции setTimeout() задаем величину таймаута, равную 10 мс для функции parseInt(). Если в течение 10 мс по последовательному каналу не будет получен следующий символ, эта функция считает завершенным извлечение текущего целого числа. Это просто мера предосторожности, чтобы не допустить зависания программы в случае отправки неполного списка значений.

Вспомним, что поскольку наш разноцветный светодиод с общим анодом, мы управляем составляющими светодиодами, подключая их катоды на землю. Таким образом, подача высокого уровня на катод светодиода препятствует протеканию тока через него, т. е. выключает светодиод. Подобным образом необходимо инвертировать значения аргументов функции analogWrite(), т. е. значение 255 выключает светодиод, а значение 0 включает его на полную яркость. Код главного цикла loop() ожидает наличия данных в буфере последовательного порта и извлекает из него первые три целых числа. Если буфер содержит какие-либо дополнительные данные, все они удаляются функцией Serial.read(), оставляя буфер пустым. Затем вызывается функция constrain(), ограничивающая все значения диапазоном 0–100. Далее функция map() сопоставляет 0 процентов значению 255, а 100 процентов значению 0, чтобы обеспечить использование функции analogWrite(). Наконец, устанавливаются уровни яркости составляющих цветных светодиодов, значения которых также дополнительно выводятся в окно монитора порта. Затем цикл ожидает поступления следующего набора команд.

Загрузите эту программу в свою плату Arduino и откройте окно монитора последовательного порта. Наберите в строке ввода монитора порта три числа со значениями в диапазоне 0–100, например, 80,10,80, и нажмите кнопку Отправить. Попробуйте немного поэкспериментировать, устанавливая разные цвета для светодиода.

Взаимодействие с компьютерной программой

Со временем вам надоест взаимодействовать с платой Arduino посредством программы монитора порта. К счастью, это не единственная программа, с помощью которой можно обмениваться информацией между компьютером и платой Arduino: практически любой язык программирования содержит библиотеки, позволяющие работать с последовательным портом компьютера. Из наиболее популярных языков программирования, обладающих обширными, хорошо задокументированными библиотеками последовательной связи, можно назвать, например, Python, Java, C, Node.js и другие. Таким образом, если вы знаете какой-либо из этих языков программирования, то сможете разрабатывать на нем программы для двустороннего обмена данными через последовательный интерфейс между компьютером и платой Arduino.

В этой книге мы будем использовать для этого язык Processing, поскольку он очень похож на язык программирования Arduino, с которым вы уже знакомы. Более того, язык программирования Arduino разработан на основе языка Processing. Сначала мы разработаем программу на языке Processing для считывания данных, получаемых в последовательный порт, а затем создадим простой графический интерфейс пользователя для отправки команд плате Arduino.

Интерфейс программирования языка Processing достаточно простой, похожий на интерфейс среды Arduino IDE. В этом разделе мы установим язык Processing на компьютер, а затем разработаем на нем простой графический интерфейс для визуального представления данных, получаемых с платы Arduino по последовательному каналу. Разобравшись с работой этой программы, мы создадим другую: графический интерфейс для передачи данных с компьютера на плату Arduino.

Установка Processing

Чтобы разрабатывать программы на языке Processing, нам нужно установить этот язык на свой компьютер. Загрузите установочный архив для своей операционной системы со страницы загрузки Processing (https://processing.org/download/) на свой компьютер. Для установки просто распакуйте загруженный архив в какую-либо папку. Запустите приложение Processing, выполнив двойной щелчок мышью по значку приложения. Должно открыться главное окно среды IDE, которое выглядит, как показано на рис. 7.12.

C:\Users\Anansi\AppData\Local\Temp\FineReader10\media\image12.jpeg

Рис. 7.12. Главное окно среды Processing IDE

Управление приложением Processing посредством платы Arduino

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

Создайте новый скетч, вставьте в него код из листинга 7.6 и загрузите его в свою плату Arduino. Эта программа каждые 50 мс отправляет текущее значение величины сигнала с потенциометра на последовательный порт компьютера. Период передачи длительностью 50 мс важен. Если передавать значения с максимально возможной скоростью, то скетч Processing не сможет обрабатывать все поступающие значения, и со временем произойдет переполнение входного буфера последовательного порта компьютера.

Листинг 7.6. Программа Arduino arduino_read_pot.ino для передачи данных на компьютер

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Передача значения уровня сигнала с потенциометра на компьютер
const int POT=0; // Сигнал с потенциометра подается
// на аналоговый контакт A0
int val; // Переменная для хранения сопоставленного
// значения сигнала с потенциометра
void setup()
 
{
Serial.begin(9600); // Запускаем последовательную передачу данных
}
 
void loop()
{
val = map(analogRead(POT), 0, 1023, 0, 255); // Считываем сигнал
// с потенциометра и сопоставляем его
// значению в диапазоне 0–255
Serial.println(val); // Отправляем значение на компьютер
delay(50); // Выдерживаем паузу, чтобы
// не переполнить входной буфер
}

А теперь следует интересная часть задачи: создание скетча Processing для приема и обработки данных, передаваемых платой Arduino. Программа, приведенная в листинге 7.7, считывает данные из входного буфера последовательного порта компьютера, и изменяет яркость одного из основных цветов созданного им окна на основе значений, получаемых от Arduino. Создайте новый скетч Processing и скопируйте в него код из листинга 7.7. Затем вам нужно будет внести в скопированный код одну важную корректировку. В частности, скетчу Processing необходимо знать, на каком последовательном порту ожидать поступающие от Arduino данные. Это будет тот же самый порт, к которому подключена ваша плата Arduino. Поэтому в листинге 7.7 замените COM3 номером этого последовательного порта. (В системах под Linux и Mac OS обозначение этого порта будет выглядеть наподобие /dev/ttyUSB0). Чтобы избежать ошибок при ручном вводе, номер последовательного порта можно скопировать из среды Arduino IDE.

Листинг 7.7. Скетч processing_display_color.pde для приема данных по последовательному каналу и изменения цвета окна

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// Скетч Processing для приема данных по последовательному каналу
// и изменения цвета окна
// Импортируем и инициализируем библиотеку последовательного порта
import processing.serial.*;
Serial port;
float brightness = 0; // Переменная для хранения значения сигнала
// с потенциометра
 
void setup()
{
size(500,500); // Размер окна
port = new Serial(this, "COM3", 9600); // Инициализируем
// последовательный порт
port.bufferUntil('\n'); // Конфигурируем порт для чтения
// данных до символа новой строки
}
 
void draw()
{
background(0,0,brightness); // Обновляем окно
}
void serialEvent (Serial port)
{
brightness = float(port.readStringUntil('\n')); // Получаем значение
// от Arduino
}

Скопировав код в свой скетч и указав в нем правильный номер последовательного порта, проверьте, что программа монитора порта среды Arduino IDE не открыта. Последовательный порт компьютера может одновременно использоваться только одной программой. Нажмите кнопку Run окна Processing IDE (левая кнопка со значком треугольника в ней, расположенная в верхнем левом углу окна). Откроется небольшое окно (рис. 7.13). Вращайте подключенный к плате Arduino потенциометр; цвет окна должен плавно изменяться с черного на синий.

C:\Users\Anansi\AppData\Local\Temp\FineReader10\media\image13.jpeg

Increasing analog values

Увеличивающиеся аналоговые значения

Рис. 7.13. Пример исполнения скетча Processing для изменения цвета окна

Убедившись, что наш скетч работает должным образом, рассмотрим пошагово его код, чтобы разобраться, как он работает. В отличие от скетчей Arduino, библиотека для работы с последовательным портом не импортируется автоматически в программы на Processing. Эту задачу выполняет оператор import processing.serial.*, а оператор Serial port создает объект последовательной связи, называющийся port.

Подобно скетчам Arduino, программы на Processing содержат функцию setup(), которая исполняется один раз в начале кода. В данном коде Processing эта функция выполняет настройки последовательного порта и посредством команды size(500,500) создает окно размером 500 × 500 пикселов. Оператор port = new Serial(this, “COM3”, 9600) предоставляет Processing всю необходимую информацию для создания экземпляра объекта последовательного порта. При исполнении в этом скетче данный экземпляр порта (называющийся port) будет поддерживать связь через порт COM3 (или через другой конкретный порт) со скоростью 9600 бод. Скорость последовательной передачи данных должна быть одинаковой, как в скетче Arduino, так и в скетче Processing; в противном случае передаваемые символы будут искажаться в процессе приема. Оператор port.bufferUntil(‘\n’) дает указание Processing помещать полученные данные во входной буфер и не предпринимать никаких действий до тех пор, пока не будет получен символ новой строки.

В Processing вместо цикла loop() предусмотрены другие специальные функции, в частности, функции draw() и serialEvent(). Функция draw() Processing похожа на функцию loop() Arduino: она исполняется в бесконечном цикле и обновляет дисплей. Функция background() устанавливает цвет окна, задавая интенсивность составляющих цветов: красного, зеленого и синего (значения которых передаются функции в аргументах). В данном случае выходной сигнал от потенциометра управляет интенсивностью синего цвета, а для красного и зеленого цветов установлены нулевые значения. Можно изменить управляемый потенциометром цвет, просто разместив аргумент brightness в соответствующей позиции списка аргументов. Значения красного, зеленого и синего цветов задаются значениями в диапазоне от 0 до 255, поэтому значения выходного сигнала с потенциометра перед их передачей сопоставляются значениям в этом диапазоне.

Функция serialEvent() вызывается всякий раз, когда выполняется условие bufferUntil(), заданное в функции setup(). Таким условием является получение символа новой строки. Поступающие по последовательному каналу данные считываются в строку оператором port.readStringUntil(‘\n’). Строку можно рассматривать, как массив текстовых символов. Содержимое строки преобразуется в число с плавающей запятой посредством функции float(). Результат этой функции присваивается переменной brightness, в итоге изменяется цвет фона окна приложения.

Чтобы остановить исполнение приложения Processing, нажмите кнопку Stop в окне среды Processing IDE (круглая кнопка со значком квадрата внутри нее, расположенная справа от кнопки Run).

Передача скетчем Processing данных на плату Arduino

Очевидным следующим шагом будет передача данных в обратном направлении: с компьютера на Arduino. Для этого эксперимента нам нужно будет подключить к плате Arduino разноцветный светодиод с общим анодом, как показано на монтажной схеме на рис. 7.11 и загрузить в плату Arduino программу из листинга 7.5. Только теперь вместо отправки плате Arduino трех числовых значений через монитор порта, мы будем отправлять цвет, выбираемый с помощью палитры цветов, встроенной в Processing.

Создайте в Processing новый скетч, скопируйте в него код, приведенный в листинге 7.8, и запустите программу на исполнение. (Не забудьте изменить номер порта в коде на номер порта, используемый вашей платой Arduino, как это нужно было сделать в предыдущем скетче.)

Листинг 7.8. Скетч processing_control_RGB.pde для управления разноцветным светодиодом на Arduino

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import processing.serial.*; // Импортируем библиотеку serial
PImage img; // Объект изображения
Serial port; // Объект последовательного порта
void setup()
{
size(640,256); // Размер изображения в цветовой модели HSV
img = loadImage("hsv.jpg"); // Загружаем фоновое изображение
port = new Serial(this, "COM3", 9600); // Открываем последовательный
// порт
}
void draw()
{
background(0); // Черный фон
image(img,0,0); // Накладываемое изображение
}
void mousePressed()
{
color c = get(mouseX, mouseY); // Получаем цвет RGB в точке,
// где находится курсор мыши
int r = int(map(red(c), 0, 255, 0, 100));
int g = int(map(green(c), 0, 255, 0, 100));
int b = int(map(blue(c), 0, 255, 0, 100));
String colors = r+","+g+","+b+"\n"; // Извлекаем значения из цвета
print(colors); // Выводим значения цветов
// на экран для целей отладки
port.write(colors); // Отправляем значения
// на плату Arduino
}

Скетчи Processing автоматически загружают сопутствующие файлы из папки data, вложенной в папку скетча. Файл hsv.jpg входит в состав кода для этой главы. Загрузите его и поместите в папку data в папке своего скетча. По умолчанию Processing сохраняет скетчи в папке Processing, находящейся в папке Мои документы текущего пользователя, автоматически создавая папку для скетча с таким же самым названием, как и скетч. Структура папок проекта выглядит подобно показанной на рис. 7.14. Изображение в папке data будет использоваться в качестве цветовой палитры.

C:\Users\Anansi\AppData\Local\Temp\FineReader10\media\image14.jpeg

Рис. 7.14. Структура папок проекта Processing

Запустите скетч Processing на исполнение; отроется окно приложения, заполненное палитрой, как на рис. 7.15.

7

Рис. 7.15. Окно программы Processing для выбора цвета

Щелкайте мышью по разным цветам; в консоли окна среды Processing IDE будут выводиться значения составных цветов выбранного цвета, которые передаются на плату Arduino, вызывая соответствующее изменение цвета подключенного к ней разноцветного светодиода.

Убедившись, что все составляющие проекта функционируют должным образом, рассмотрим код скетча Processing более подробно, чтобы разобраться, как он работает. Как и в предыдущем скетче Processing, импортируется библиотека последовательного интерфейса serial и создается объект последовательного порта с названием port. Кроме этого создается объект типа PImage с названием img, который будет содержать фоновое изображение. В функции setup() выполняется инициализация последовательного порта, устанавливается размер окна приложения, равный размеру выводимого в нем изображения, и осуществляется импорт изображения в объект изображения, с помощью оператора img = loadImage(“hsv.jpg”). Операция импорта предполагает, что файл hsv.jpg находится в папке data текущего скетча Processing.

В функции draw() изображение загружается в окно посредством оператора image(img,0,0). В данном случае аргумент img обозначает изображение, которое мы хотим отображать в окне приложения, а аргументы 0,0 обозначают координаты, в которых нужно начинать выводить это изображение. Эти координаты представляют верхний левый угол окна приложения.

При каждом щелчке мышью по изображению палитры в окне приложения вызывается функция mousePressed(). Цвет пиксела, по которому пришелся щелчок, сохраняется в виде объекта типа color с названием c. Сам цвет извлекается методом get(), который предоставляет приложению информацию, откуда получить значения цвета (в данном случае, координаты X и Y щелчка мыши). Затем вызывается функция map(), которая сопоставляет значения составных цветов процентным значениям, ожидаемых скетчем Arduino. Полученные процентные значения далее конкатенируются в строку, которая отправляется по последовательному каналу на плату Arduino. Для справки эти значения также выводятся в консоли главного окна Processing IDE.

Подключите свою плату Arduino (с подсоединенным к ней разноцветным светодиодом) к компьютеру и загрузите в нее код из листинга 7.5. Запустите на исполнение скетч Processing (предварительно исправив в нем номер последовательного порта должным образом), и щелкайте по разным цветам в палитре в открывшемся окне приложения. Выбранный таким образом цвет должен устанавливаться на разноцветном светодиоде, подключенном к плате Arduino.

Резюме

В этой главе мы узнали следующее:

  • Платы Arduino подключаются к компьютеру через преобразователь USB/RS-232.
  • Для преобразования USB/RS-232 в разных платах Arduino может быть предусмотрена специальная микросхема или реализована встроенная поддержка функциональности USB.
  • Плата Arduino может отправлять данные на компьютер через интерфейс USB.
  • Для форматирования текста, выводимого в окне монитора порта, можно использовать символы новой строки и табуляции.
  • Данные передаются по последовательному каналу в виде символов, которые можно преобразовать в целые числа несколькими способами.
  • По последовательному каналу с компьютера в скетч Arduino можно отправлять наборы целых чисел, разделенных запятыми. В скетче Arduino составляющие числа можно извлечь из набора с помощью специальных функций и использовать их в качестве команд для скетча.
  • По последовательному каналу можно отправлять данные из скетча Arduino в программу на Processing, исполняющуюся на компьютере.
  • Из программы на Processing можно отравлять данные в скетч Arduino для управления подключенными к плате Arduino периферийными устройствами.
Опубликовано

Графопостроитель FlexiPlot

По материалам книги В. Яценкова  «Здоровье, спорт и окружающая среда в проектах Arduino» (глава 5. «Визуализация данных»)

Здоровье

Графопостроитель FlexiPlot находится в процессе разработки, но уже сейчас обладает большим количеством настроек и функций. Скачайте архив файлов программы по адресу https://github.com/xcoder123/FlexiPlot/releases. Программа не требует установки. Распакуйте архив в удобное место.

FlexiPlot позволяет открыть несколько рабочих окон графопостроителя (Chart), и в каждом окне можно независимо и одновременно строить до 256-ти графиков. Каждому окну присвоено уникальное имя (индекс окна). Этот индекс необходимо указать в пакете данных, чтобы программа поняла, в какое окно направить пакет.

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

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

{Index|Name_1|Colour_1|Value_1|Name_2|Colour_2|Value_2}

где:

  • Index — имя окна, в которое отправлен пакет;
  • Name_1 — подпись первой кривой в легенде графика;
  • Colour_1 — цвет первой кривой графика в формате R,G,B (например, 255,0,0);
  • Value_1 — текущее значение для первой кривой графика;
  • Name_2 — подпись второй кривой в легенде графика;
  • Colour_2 — цвет второй кривой графика в формате R,G,B;
  • Value_2 — текущее значение для второй кривой графика,

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

Пакет должен завершаться символом перевода строки.

Такой объемный пакет данных позволяет очень гибко настраивать отображение графиков. Например, можно на ходу менять цвет линии графика в зависимости от величины отображаемого параметра. Но большая длина пакета вынуждает использовать высокую скорость передачи данных в порт (115200 бод) и выдерживать паузу не менее 5 мс между пакетами, чтобы избежать переполнения буфера порта и потерю данных.

Загрузите в плату Arduino демонстрационный скетч из листинга 5.3. Этот скетч отправляет текущее значение аргумента angle в окно P0, а значения тригонометрических функций sin() и cos() — в окно P1.

Листинг 5.3. Пример работы с графопостроителем FlexiPlot

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#define DEG_TO_RAD 0.01745329
void setup() {
Serial.begin(115200);
}
 
void loop() {
for (float angle = 0; angle < 360; angle++)
{
// Пакет данных для окна P0
Serial.print("{P0|Angle|0,0,255|");
Serial.print(angle);
Serial.println("}");
 
 
// Пакет данных для окна P1
Serial.print("{P1|Sin|255,0,0|");
Serial.print(sin(DEG_TO_RAD * angle)*10);
Serial.print("|Cos|0,255,0|");
Serial.print(cos(DEG_TO_RAD * angle));
Serial.println("}");
}
}
  1. Запустите программу FlexiPlot. Настройте номер последовательного порта (опция Port) и установите скорость обмена 115200 бод (опция Baudrate).
  2. Выберите пункт меню Graph | Add Line Chart или нажмите кнопку графического меню с изображением графика — на экране появится рабочее окно графопостроителя, которое надо настроить. Растяните это окно на всю ширину окна программы и на половину его высоты.
  3. Перейдите на вкладку Settings и введите в поле Title название окна — например, Аргумент функций.
  4. В поле Buffer введите значение 1000 — это размер буфера. С такой настройкой буфер будет хранить 1000 выборок. Вновь поступающие значения вытесняют устаревшие данные.
  5. В поле Refresh Rate введите значение 10 — это интервал между обновлениями изображения в миллисекундах. Чем меньше его значение, тем плавнее движется график, но выше нагрузка на процессор компьютера.
  6. Уберите флажок Auto Scaling Y (автомасштабирование по оси Y). В нашем случае мы заранее знаем, что отображаемое значение изменяется в диапазоне от 0 до 360, поэтому можем настроить масштаб вручную. Для этого в поле Min Y введите 0, а в поле Max Y — 400.
  7. Добавьте еще одно рабочее окно и расположите его под предыдущим окном. Введите заголовок окна — например, Значения функций. Значения Buffer и Refresh Rate скопируйте из настроек первого окна, чтобы графики прорисовывались синхронно.
  8. Уберите флажок Auto Scaling Y. В поле Min Y введите -10, в поле Max Y — 10.
  9. Для сохранения всех настроек и расположения рабочих окон нажмите кнопку с изображением дискеты или выберите пункт меню File | Save.
  10. Вернитесь к вкладкам Chart. Выберите пункт меню Serial | Connect. Плата Arduino перезагрузится и начнется прорисовка графиков (рис. 5.3). Если этого не произошло, проверьте настройку номера и скорости порта, а также настройки рабочих окон.

5-3

Рис. 5.3. Рабочее окно программы FlexiPlot с демонстрационными графиками

exclamation При большой нагрузке на процессор компьютера может искажаться прорисовка графиков. В ответственных случаях лучше не запускать на компьютере одновременно с FlexiPlot какие-либо ресурсоемкие программы.

Дополнительные настройки рабочего окна графика

Вы можете также задать следующие дополнительные настройки:

  • Date Format — формат меток реального времени по оси X (часы:минуты:секунды). Можно оставить только нужную метку или полностью убрать метки времени;
  • Auto Filling — автоматическая заливка цветом областей под кривой графика;
  • Auto Fill Opacity — интенсивность цвета заливки. По умолчанию — 20%.

Встроенный терминал

В программе имеется встроенный терминал последовательного порта, доступный в меню Serial | Terminal. Это обычный терминал, который позволяет отправлять символы и шестнадцатеричные числа в плату Arduino и отображать поступающие данные. Опция Filter Plotting Packets отсекает пакеты данных и пропускает только символы, не входящие в пакеты. Эта опция позволяет отправлять в графопостроитель отладочные сообщения в интервале между пакетами данных.

Рисование столбчатых диаграмм

FlexiPlot может в режиме реального времени рисовать наглядные столбчатые диаграммы по данным, поступающим от платы контроллера Arduino. Каждый новый пакет полностью обновляет диаграмму.

Формат пакетов данных для диаграммы представлен в двух вариантах: с автоматическим выбором цвета столбцов и с явным указанием цвета.

Пример пакета данных с автоматическим выбором цвета имеет следующий вид:

{P0|Night;Morning;Day;Evening|Inside|12 15 17 14|Outside|8 15 23 19}

Сначала, как обычно идет индекс (имя) рабочего окна. Далее через точку с запятой задаются имена групп столбцов. Их может быть сколь угодно много. В нашем примере это четыре группы: ночь, утро, день и вечер. Допустим, мы измеряем температуру внутри помещения (Inside) и снаружи (Outside) четыре раза в сутки (то есть получаем по четыре группы значений для каждого типа замеров). Соответственно, после названия величины типа замера в пакете следуют четыре значения, разделенные пробелами. Затем идет название следующей величины и ее значения. Этот шаблон повторяется нужное количество раз.

В строке пакета нельзя использовать кириллицу и специальные символы.

Воспользуемся встроенным инструментом отладки FlexiPlot — Packet Injector — который позволяет имитировать получение пакетов через последовательный порт. Благодаря этому инструменту мы можем экспериментировать с содержимым пакетов и вариантами прорисовки диаграммы без программирования контроллера Arduino.

  1. Выберите пункт меню Graph | Add Bar Graph — чтобы открыть новое окно рисования столбчатых диаграмм.
  2. Выберите пункт меню Tools | Debug Tools — чтобы запустить инструмент отладки. Введите в поле Packet пример пакета данных c автоматическим выбором цвета и нажмите клавишу <Enter>. В рабочем окне должна появиться диаграмма (рис. 5.4). Если ничего не произошло, проверьте правильность ввода пакета данных. Чтобы повторить отправку пакета, выделите нужную строку в поле History.
  3. Перейдите на вкладку Settings в рабочем окне. В вашем распоряжении богатый выбор настроек внешнего вида диаграммы:
  • Name of X Axis и Name Of Y Axis — подписи осей координат. Можно использовать кириллицу;
  • Bar Chart Type — выбор типа диаграммы: обычная (Normal), с наложением столбцов (Stacked), с наложением в процентах (Percent), горизонтальная (Horizontal), горизонтальная с наложением (Horizontal Stacked), горизонтальная в процентах (Horizontal Percent);
  • Animation — анимация при прорисовке диаграммы: без анимации (No Animation), анимация осевой разметки (Grid/Axis Animation), анимация столбцов (Series Animation), анимация всех элементов (All Animations);
  • Theme — выбор темы оформления диаграммы;
  • Anti-Aliasing — сглаживание изображения;
  • Legend — настройка отображения, расположения и шрифта текста легенды диаграммы;
  • Labels — настройка отображения и расположения текстовых меток на диаграмме. По умолчанию числовое значение величины (@value) отображается в середине столбца;
  • Scaling — управление автоматическим масштабированием. Если диапазон значений известен заранее, можно задать минимальное и максимальное значение в полях Min и Max.

5-4

Рис. 5.4. Пример построения столбчатой диаграммы из четырех групп с автовыбором цвета

Попробуйте менять параметры настроек и наблюдайте за изменениями на вкладке Chart. Выберите вариант настройки, который вам больше нравится.

Цвет столбцов в формате R,G,B можно указать непосредственно в пакете. Для этого после названия величины надо вставить в пакет значение цвета:

{P0|Night;Morning;Day;Evening|Inside|255,0,0|12 15 17 14|Outside|0,255,0|8 15 23 19}

В этом примере параметр Inside будет отображен красными столбцами, а параметр Outside — зелеными.

Динамические диаграммы

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

Загрузите в плату Arduino скетч примера из листинга 5.4.

Листинг 5.4. Пример отображения динамической диаграммы в FlexiPlot

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int i, j;
void setup() {
Serial.begin(115200);
}
 
void loop() {
for (i = 0; i &lt; 255; i = i + 5)
{
j = 255 - i;
Serial.println((String)"{P0|S|d1|" + i + ",0," + j +"|" + i + "|d2|0,255,0|" + j + "}");
delay(100);
}
 
for (int i = 255; i &gt; 0; i = i - 5)
{
j = 255 - i;
Serial.println((String)"{P0|S|d1|" + i + ",0," + j +"|" + i + "|d2|0,255,0|" + j + "}");
delay(100);
}
}

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

В настройках графика отключите анимацию, отображение легенды и автомасштабирование. Задайте диапазон значений Min/Max от 0 до 300. Попробуйте менять тип диаграммы Bar Chart Type и выберите наиболее подходящий для ваших задач.