Перейти к содержанию

Глава 3. Практическая часть

Подключаемые устройства можно разделить на следующие категории:

  • электронные модули ввода данных, производства ООО «Прикладная робототехника ПРО» (кнопки, потенциометры, концевой выключатель)

  • электронные сенсорные модули, производства ООО «Прикладная робототехника ПРО» (датчики линии, инерциальный датчик и магнетометр, датчик шума, датчик атмосферного давления, датчик освещенности, датчик температуры и влажности)

  • электронные индикационные модули, производства ООО «Прикладная робототехника ПРО» (светодиоды, трехцветные светодиоды, звуковой излучатель)

  • электронные управляющие модули, производства ООО «Прикладная робототехника ПРО» (силовой ключ)

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

Помимо вышеперечисленных модулей, можно использовать Arduino

-совместимые комплектующие:

  • двигатели постоянного тока;

  • сервоприводы;

  • набор светодиодов;

  • набор резисторов;

  • LCD-дисплей;

  • УЗ-дальномер;

  • эластичная клавиатура.

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

Работа с модулями ввода данных

Рассмотрим процесс опроса модулей тактовой кнопки, потенциометра и концевого выключателя одновременно. Поскольку каждый тип модуля имеет свой индивидуальный идентификационный номер, то можно без боязни включать в цепь устройств модули различного типа. Например, как в данном случае – в одной цепи находятся модуль "Тактовая кнопка" (ID 3), модуль "Потенциометр" ( ID 19) и модуль "Концевой выключатель" (ID 23) (Рисунок 3.1). Для того чтобы подключить в одну цепь несколько одинаковых модулей (например, несколько кнопок) необходимо выполнить изменение их ID таким образом, чтобы у всех модулей, включенных в цепь, были разные ID. Такой пример будет рассмотрен далее.

Рисунок 3.1. Последовательное подключение модулей ввода данных к программируемому контроллеру IoT ESP-JS-AR

Пример для Arduino IDE

Код для опроса модулей будет выглядеть следующим образом:

#include "DxlMaster.h" 
#include "JsAr.h"

// указываем ID модулей в цепи 

const uint8_t id_but = 3;
const uint8_t id_pot = 19; 
const uint8_t id_usw = 23;

// указываем скорость обмена данными 
const long unsigned int baudrate = 57600;

// создаем экземпляры класса для каждого модуля 
DynamixelDevice device_but(id_but); 
DynamixelDevice device_pot(id_pot); 
DynamixelDevice device_usw(id_usw);


// объявляем переменные для работы 

uint8_t data_but = 0;
uint8_t data_pot_L = 0;
uint8_t data_pot_H = 0; 
uint8_t data_usw = 0;
void setup()

{

JsAr.begin(); 
DxlMaster.begin(baudrate); 
delay(100);

// инициализируем модули 

device_but.init();
delay(50); 
device_pot.init(); 
delay(50); 
device_usw.init();

// открываем соединение с ПК
// для отображения показаний 
Serial.begin(115200);

}

void loop() {

// опрашиваем необходимые регистры модулей device_but.read(27, data_but);

device_pot.read(26, data_pot_L); // малый регистр от 0 до 255 
device_pot.read(27, data_pot_H); // большой регистр от 1*256 до 3*256 
device_usw.read(27, data_usw);

// выводим показания в терминал 
if(data_but) 
Serial.println("But is pressed"); 
Serial.print("Pot position: ");

Serial.print((data_pot_L + 256*data_pot_H)/10.23); 
Serial.print("%\n");

if(data_usw) Serial.println("USW is pressed"); 
delay(100);

}

В результате, в окне терминала можно будет увидеть подобную картину (Рисунок 3.2):

Рисунок 3.2. Результат опроса подключенных модулей ввода данных в среде Arduino IDE

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

Пример для Mongoose OS

В случае использования среды разработки Mongoose OS при использовании JavaScript, код, реализующий аналогичный пример, будет выглядеть следующим образом:

load('api_config.js'); // Загрузка конфигурации платы 
load('api_gpio.js');        // Работа с GPIO 
load('api_timer.js'); // Работа с таймером 
load('api_sys.js');  // Системные функции 
load('api_jsar.js');    // Работа с платой ESP-JS-AR 
load('api_dxl.js');  // Работа с Dynamixel
print('Start js app');

// ID устройств Dynamixel 
let id_but = 3; // Кнопка
let id_pot = 19; // Потенциометр
let id_usw = 23; // Концевой выключатель

// Скорость передачи для шины Dynamixel 
let baudrate = 57600;

// Переменные для хранения данных с устройств 
let data_but = 0;
let data_pot = 0; 
let data_usw = 0;

// Создание объектов устройств

let but = DynamixelDevice.create(id_but); 
let pot = DynamixelDevice.create(id_pot); 
let usw = DynamixelDevice.create(id_usw);

// Инициализация шины и устройств 

DxlMaster.begin(baudrate);
but.init();
pot.init();
usw.init();

// Таймер для считывания данных каждые 0.5 секунды 
Timer.set(500, Timer.REPEAT, function() {

data_but = but.read(27);    // Чтение состояния кнопки

data_pot = pot.read16(26);  // Чтение значения потенциометра (16- бит)

data_usw = usw.read(27);    // Чтение состояния концевого выключателя

// Вывод значений в консоль 
print('But:', data_but);

print('POT:', data_pot);

print('USW:', data_usw);

},null);
Результатом работы кода будет следующий вывод в окно терминала (Рисунок 3.3):

Рисунок 3.3. Результат опроса подключенных модулей ввода данных в среде Mongoose OS

Работа с сенсорными модулями

Рассмотрим аналогичную предыдущей конфигурацию модулей – в одну цепь подключим модуль "Датчик атмосферного давления" (ID 20), модуль "Датчик температуры и влажности воздуха" (ID 22) и модуль "Датчик освещённости" (ID 25) (Рисунок 3.4):

Рисунок 3.4. Последовательное подключение сенсорных модулей к программируемому контроллеру IoT ESP-JS-AR

Пример для Arduino IDE

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

#include "DxlMaster.h" 
#include "JsAr.h"

// указываем ID модулей в цепи 
const uint8_t id_bmp = 20;
const uint8_t id_temp = 22; 
const uint8_t id_light = 25;

// указываем скорость обмена данными

const long unsigned int baudrate = 57600;

// создаем экземпляры класса для каждого модуля 
DynamixelDevice device_bmp(id_bmp); 
DynamixelDevice device_temp(id_temp); 
DynamixelDevice device_light(id_light);

// объявляем переменные для работы 

uint8_t data_light_L = 0;
uint8_t data_light_H = 0;
uint32_t data_bmp_press_32 = 0; 
uint16_t data_bmp_press_16 = 0; 
uint32_t data_bmp_alt_32 = 0; 
uint16_t data_bmp_alt_16 = 0; 
uint16_t data_temp_RH_int = 0; 
uint8_t data_temp_RH_dec = 0; 
uint16_t data_temp_temp_int = 0; 
uint8_t data_temp_temp_dec = 0;

void setup()

{

JsAr.begin(); 
DxlMaster.begin(baudrate); 
delay(100);

// инициализируем модули 
device_bmp.init(); 
delay(50); 
device_temp.init(); 
delay(50);

device_light.init();

// открываем соединение с ПК

// для отображения показаний 
Serial.begin(115200);

}

void loop() {

// опрашиваем необходимые регистры модулей 
device_light.read(26, data_light_L); // малый регистр от 0 до 255

device_light.read(27, data_light_H); // большой регистр от 1*256 до 3*256

// считываем значение давления 
device_bmp.read(24, data_bmp_press_32); 
device_bmp.read(32, data_bmp_press_16);

// считываем значение альтитуды 
device_bmp.read(28, data_bmp_alt_32); 
device_bmp.read(34, data_bmp_alt_16);

// считываем значение влажности 
device_temp.read(24, data_temp_RH_int); 
device_temp.read(26, data_temp_RH_dec);

// считываем значение температуры

device_temp.read(28, data_temp_temp_int); 
device_temp.read(30, data_temp_temp_dec);

// выводим показания в терминал 
Serial.print("Light: ");

Serial.print((data_light_L + 256*data_light_H)/10.23); 
Serial.print("%\n");

Serial.print("Pres_32: "); 
Serial.print(data_bmp_press_32); 
Serial.print("\n"); 
Serial.print("Pres_16: "); 
Serial.print(data_bmp_press_16); 
Serial.print("\n");

Serial.print("Alt_32: "); 
Serial.print(data_bmp_alt_32); 
Serial.print("\n"); 
Serial.print("Alt_16: "); 
Serial.print(data_bmp_alt_16); 
Serial.print("\n");

Serial.print("RH_int: "); 
Serial.print(data_temp_RH_int); 
Serial.print("\n"); 
Serial.print("RH_dec: "); 
Serial.print(data_temp_RH_dec); 
Serial.print("\n");

Serial.print("Temp_int: "); 
Serial.print(data_temp_temp_int); 
Serial.print("\n"); 
Serial.print("Temp_dec: "); 
Serial.print(data_temp_temp_dec); 
Serial.print("\n");

delay(500);

}

В результате, вывод в терминал выглядит следующим образом (Рисунок 3.5):

Рисунок 3.5. Результат опроса подключенных сенсорных модулей в среде Arduino IDE

Таким образом, можно получать данные с сенсорных модулей. В рассматриваемом случае получаемые данные следующие:

data_bmp_press_32 – давление в Па. Т.е. 1000 = 1 кПа.

data_bmp_press_16 - давление в даПа (в декапаскалях), т.е. 100 = 1 кПа.

data_bmp_alt_32 и data_bmp_alt_16 - высота в дм. Например, 567 = 56.7 м над уровнем моря. В data_bmp_alt_16 не помещается большая высота – предел - 6553.5 м.

data_temp_RH_int - целая часть влажности (относительная) в %, при этом data_temp_RH_dec – десятичная. Аналогично и для температуры: data_temp_temp_int - целая часть, т.е. 25.5С => 25, а data_temp_temp_ dec - десятичная часть, т.е. 25.5С => 5.

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

Важно! В случае отсутствия получаемых данных после загрузки управляющей программы, иногда требуется нажать на кнопку RST контроллера ESP-JS-AR для реинициализации подключенных модулей.

Пример для Mongoose OS

В случае использования среды разработки Mongoose OS при использовании JavaScript, код, реализующий аналогичный пример, будет выглядеть следующим образом:

load('api_config.js'); // Загрузка конфигурации платы 
load('api_gpio.js');        // Работа с GPIO 
load('api_timer.js'); // Работа с таймером 
load('api_sys.js');  // Системные функции 
load('api_jsar.js');    // Работа с платой ESP-JS-AR 
load('api_dxl.js');  // Работа с Dynamixel

print('Start js app');

// ID устройств Dynamixel

let id_bmp = 20; // Барометр BMP

let id_temp = 22; // Датчик температуры и влажности 
let id_light = 25; // Датчик освещенности

// Скорость передачи для шины Dynamixel 
let baudrate = 57600;

// Переменные для хранения данных с датчиков 
let data_light = 0;

let data_bmp_press_16 = 0; 
let data_bmp_alt_16 = 0; 
let data_temp_RH_int = 0; 
let data_temp_RH_dec = 0;

let data_temp_temp_int = 0; 
let data_temp_temp_dec = 0;

// Создание объектов устройств

let bmp = DynamixelDevice.create(id_bmp); 
let temp = DynamixelDevice.create(id_temp); 
let light = DynamixelDevice.create(id_light);

// Инициализация шины и устройств 
DxlMaster.begin(baudrate); 
bmp.init();

temp.init();

light.init();

// Таймер для чтения данных каждые 1.5 секунды

Timer.set(1500, Timer.REPEAT, function() {

data_light = light.read16(26);  // Считываем значение освещенности 
data_bmp_press_16 = bmp.read16(32); // Считываем давление (целое) 
data_bmp_alt_16 = bmp.read16(34);   // Считываем высоту (целое) 
data_temp_RH_int = temp.read16(24); // Считываем целую часть влажности 
data_temp_RH_dec = temp.read(26);       // Считываем дробную часть влажности туры

data_temp_temp_int = temp.read16(28); // Считываем целую часть темпераратуры
data_temp_temp_dec = temp.read(30);  // Считываем дробную часть температуры

// Вывод данных в консоль 
print('Light: ', data_light); 
print('Pres16: ', data_bmp_press_16); 
print('ALT16: ', data_bmp_alt_16);

print('RH: ', data_temp_RH_int, ',', data_temp_RH_dec); 
print('Temp: ', data_temp_temp_int, ',', data_temp_temp_dec);

}, null);

По сравнению с примером для Arduino IDE, в данном примере отсутствует вывод 32-х битных значений параметров. Результатом работы кода станет следующий вывод в окно терминала (Рисунок 3.6):

Рисунок 3.6. Результат опроса подключенных сенсорных модулей в среде Mongoose OS

Работа с индикационными модулями

Единственной отличительной особенностью работы с индикационными модулями, производства ООО "Прикладная робототехника ПРО", является то, что в процессе работы происходит не чтение регистров, а изменение их состояния. Рассмотрим этот механизм на примере цепи, в которую включены модуль "Светодиод" (ID 9), модуль "Трехцветный светодиод" (ID 21) и модуль "Звуковой пьезоизлучатель 4 кГц" (ID 24) (Рисунок 3.7):

Рисунок 3.7. Последовательное подключение индикационных модулей к программируемому контроллеру IoT ESP-JS-AR

Пример для Arduino IDE

Код для управления данными модулями будет выглядеть следующим образом:

#include "DxlMaster.h" 
#include "JsAr.h"

// указываем ID модулей в цепи 
const uint8_t id_rgb = 21;

const uint8_t id_led = 9; 
const uint8_t id_zum = 24;

// указываем скорость обмена данными 
const long unsigned int baudrate = 57600;

// создаем экземпляры класса для каждого модуля 
DynamixelDevice device_rgb(id_rgb);


DynamixelDevice device_led(id_led); 
DynamixelDevice device_zum(id_zum);

// объявляем переменные для работы 
uint8_t data_but = 0;

uint8_t data_pot_L = 0; 
uint8_t data_pot_H = 0; 
uint8_t data_usw = 0;

void setup()

{

JsAr.begin(); 
DxlMaster.begin(baudrate); 
delay(100);

// инициализируем модули 
device_rgb.init();

delay(50); 
device_led.init(); 
delay(50); 
device_zum.init();

}

void loop() {

// записываем значения в требуемые регистры

// задаем скважность сигнала 
device_zum.write(28, 127);

// задаем частоту сигнала 
device_zum.write(26, (uint16_t) 512);

// включаем светодиод 
device_led.write(26, 1); delay(1000);

// выключаем светодиод 
device_led.write(26, 0);

// далее включаем каждую составляющую

// на RGB светодиоде через 1 секунду 
device_rgb.write(26, 1);

delay(1000); 
device_rgb.write(26, 0);

device_rgb.write(27, 1); 
delay(1000); 
device_rgb.write(27, 0);

device_rgb.write(28, 1); 
delay(1000); 
device_rgb.write(28, 0); 
delay(1000);

}

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

Пример для Mongoose OS

В случае использования среды разработки Mongoose OS при использовании JavaScript , код, реализующий аналогичный пример, будет выглядеть следующим образом:

load('api_config.js'); // Загрузка конфигурации платы 
load('api_gpio.js');        // Работа с GPIO 
load('api_timer.js'); // Работа с таймером 
load('api_sys.js');  // Системные функции 
load('api_jsar.js');    // Работа с платой ESP-JS-AR 
load('api_dxl.js');  // Работа с Dynamixel

print('Start js app');

// ID устройств Dynamixel

let id_rgb = 21; // RGB светодиод

let id_led = 9; // Индикаторный светодиод 
let id_zum = 24; // Зуммер

// Скорость передачи для шины Dynamixel 
let baudrate = 57600;

// Создание объектов устройств

let rgb = DynamixelDevice.create(id_rgb); 
let led = DynamixelDevice.create(id_led); 
let zum = DynamixelDevice.create(id_zum);

// Инициализация шины Dynamixel 
DxlMaster.begin(baudrate);

// Инициализация устройств 
rgb.init();

led.init();

zum.init();

// Таймер, который каждые 1.5 секунды управляет устройствами 
Timer.set(1500, Timer.REPEAT, function() {

zum.write(28, 127); // Управление зуммером (мощность/скорость) 
zum.write(26, 512); // Дополнительная настройка зуммера 
led.write(26, 1); // Включение светодиода

Sys.usleep(2000000); // Задержка 2 секунды 
led.write(26, 0); // Выключение светодиода 
rgb.write(26, 1); // Включение RGB (канал 1) 
Sys.usleep(2000000);

rgb.write(26, 0); // Выключение RGB (канал 1) 
Sys.usleep(2000000);

rgb.write(27, 1); // Включение RGB (канал 2) 
Sys.usleep(2000000);

rgb.write(27, 0); // Выключение RGB (канал 2) 
Sys.usleep(2000000);

rgb.write(28, 1); // Включение RGB (канал 3) 
Sys.usleep(2000000);

rgb.write(28, 0); // Выключение RGB (канал 3) 
Sys.usleep(2000000);

}, null);

Работа с управляющими модулями

В качестве управляющего модуля в набор входит модуль "Силовой ключ" (ID 17), с помощью которого можно коммутировать силовую нагрузку – например, управлять вентилятором, насосом, электромагнитным ключом и т.п. В качестве демонстрации рассмотрим пример управления двигателем постоянного тока (Рисунок 3.8):

Рисунок 3.8. Подключение управляющего модуля к программируемому контроллеру IoT ESP-JS-AR

Пример для Arduino IDE

Код управления током, приходящим на нагрузку (управление осуществляется по принципу ШИМ), выглядит следующим образом:

#include "DxlMaster.h" 
#include "JsAr.h"

// указываем ID модулей в цепи 
const uint8_t id_mos = 17;

// указываем скорость обмена данными 
const long unsigned int baudrate = 57600;

// создаем экземпляры класса для каждого модуля 
DynamixelDevice device_mos(id_mos);

void setup()

{

JsAr.begin(); 
DxlMaster.begin(baudrate); 
delay(100);

// инициализируем модули 
device_mos.init();

delay(50);

}

void loop() {

// записываем значения в требуемые регистры

// регулируем скважность сигнала 
device_mos.write(28, 0); 
delay(2000);

device_mos.write(28, 127); 
delay(2000); 
device_mos.write(28, 255); 
delay(2000);

}

Пример для Mongoose OS

В случае использования среды разработки Mongoose OS при использовании JavaScript, код, реализующий аналогичный пример, будет выглядеть следующим образом:

load('api_config.js'); // Загрузка конфигурации платы 
load('api_gpio.js');         // Работа с GPIO 
load('api_timer.js');       // Работа с таймером 
load('api_sys.js');  // Системные функции 
load('api_jsar.js');    // Работа с платой ESP-JS-AR 
load('api_dxl.js');  // Работа с Dynamixel

print('Start js app');

// ID устройства MOSFET (Dynamixel) 
let id_mos = 17;

// Скорость передачи для шины Dynamixel 
let baudrate = 57600;

// Создаём объект устройства

let mos = DynamixelDevice.create(id_mos);


// Инициализация шины Dynamixel 
DxlMaster.begin(baudrate);

// Инициализация устройства 
mos.init();

// Таймер, который каждые 1.5 секунды переключает MOSFET 
Timer.set(1500, Timer.REPEAT, function() {

mos.write(28, 0);   // Устанавливаем минимальное значение 
Sys.usleep(2000000); // Пауза 2 секунды

mos.write(28, 127); // Устанавливаем среднее значение 
Sys.usleep(2000000); // Пауза 2 секунды

mos.write(28, 255); // Устанавливаем максимальное значение 
Sys.usleep(2000000); // Пауза 2 секунды

}, null);

Работа с одинаковыми модулями

Рассмотрим ситуацию, когда в цепь модулей необходимо включить одинаковые модули. В качестве примера, пусть это будут два модуля "Тактовая кнопка" и два модуля "Светодиод". По умолчанию, у всех тактовых кнопок ID=3, а у всех светодиодов ID=9. Таким образом, необходимо у одного модуля "Тактовая кнопка" и у одного модуля "Светодиод" изменить их ID на другие. Это можно сделать следующим образом:

Подключаем к программируемому контроллеру модуль, у которого необходимо изменить ID. Внимание! Модуль должен быть подключен один.

Из примеров библиотеки "JsAr" – "Dynamixel" необходимо загрузить в контроллер пример "DxlConsole", при этом, не забыть изменить скорость обмена данными между модулем и контроллером. По умолчанию, это значение равно 57600, а в скетче скорость по умолчанию указана 1000000. В результате, необходимо внести изменения в значение параметра "dynamixel_baudrate":

const unsigned long dynamixel_baudrate = 57600;

После загрузки скетча с измененным значением скорости в контроллер, требуется открыть монитор порта. Далее рассмотрим на примере модуля "Светодиод":

отправляем команду:

ping 9

Получаем статус "ОК" – это значит, что модуль "Светодиод" корректно идентифицировался.

затем отправляем команду на смену ID. Для примера изменим ID модуля с 9 на 50:

write 9 3 50

Получаем сообщение об ошибке – это нормально. Перезагружаем модуль и контроллер по питанию. После перезагрузки можно убедиться в изменении ID модуля, путем отправки команды

ping 50.

Процесс смены ID показан на рисунке 3.9:

Рисунок 3.9. Процесс смены ID периферийного модуля

Аналогичным образом изменим ID модуля "Тактовая кнопка" c 3 на 60 (Рисунок 3.10):

Рисунок 3.10. Процесс смены ID тактовой кнопки

Соберем цепь из четырех модулей и подключим ее к программируемому контроллеру ESP-JS-AR. Примерный внешний вид собранной цепи представлен на следующем рисунке (Рисунок 3.11):

Рисунок 3.11. Последовательного подключение нескольких одинаковых модулей к программируемому контроллеру IoT ESP-JS-AR

Пример для Arduino IDE

Реализуем управляющую программу, в которой при нажатии на модуль "Тактовая кнопка" с ID 60 будет включаться светодиод с ID 50. И аналогично, при нажатии на модуль "Тактовая кнопка" с ID 3 будет включаться светодиод с ID 9:

#include "DxlMaster.h" 
#include "JsAr.h"

// указываем ID модулей в цепи 
const uint8_t id_but_1 = 3;
const uint8_t id_but_2 = 60; 
const uint8_t id_led_1 = 9; 
const uint8_t id_led_2 = 50;

// указываем скорость обмена данными 
const long unsigned int baudrate = 57600;

// создаем экземпляры класса для каждого модуля 
DynamixelDevice device_but_1(id_but_1); 
DynamixelDevice device_but_2(id_but_2); 
DynamixelDevice device_led_1(id_led_1);
DynamixelDevice device_led_2(id_led_2);

// объявляем переменные для работы 
uint8_t data_but_1 = 0;
uint8_t data_but_2 = 0;

void setup()
{

JsAr.begin(); 
DxlMaster.begin(baudrate); 
delay(100);

// инициализируем модули 
device_but_1.init(); 
delay(50); 
device_but_2.init(); 
delay(50); 
device_led_1.init(); 
delay(50); 
device_led_2.init();

}

void loop() {

// опрашиваем необходимые регистры модулей 
device_but_1.read(27, data_but_1); 
device_but_2.read(27, data_but_2);

// управляем светодиодами 
if(data_but_1)
 device_led_1.write(26, 1);
else 
device_led_1.write(26, 0);
if(data_but_2) 
device_led_2.write(26, 1);
else 
device_led_2.write(26, 0); 
delay(10);

}

Пример для Mongoose OS

В случае использования среды разработки Mongoose OS при использовании JavaScript, код, реализующий аналогичный пример, будет выглядеть следующим образом:

load("api_config.js");
load('api_config.js');          // Загрузка конфигурации платы 
load('api_gpio.js');         // Работа с GPIO 
load('api_timer.js');       // Работа с таймером 
load('api_sys.js');  // Системные функции 
load('api_jsar.js');    // Работа с платой ESP-JS-AR 
load('api_dxl.js');  // Работа с Dynamixel
print('Start js app');  

// ID устройств Dynamixel 

let id_led_1 = 9;
let id_led_2 = 50; 
let id_but_1 = 3; 
let id_but_2 = 60;

// Переменные для хранения состояний кнопок 

let data_but_1 = 0;
let data_but_2 = 0;

// Скорость передачи для шины Dynamixel 
let baudrate = 57600;

// Создаём объекты устройств

let led_1 = DynamixelDevice.create(id_led_1); 
let led_2 = DynamixelDevice.create(id_led_2); 
let but_1 = DynamixelDevice.create(id_but_1); 
let but_2 = DynamixelDevice.create(id_but_2);

// Инициализация шины Dynamixel 
DxlMaster.begin(baudrate);

// Инициализация устройств
led_1.init();

led_2.init();

but_1.init();

but_2.init();

// Таймер, который каждые 250 мс проверяет кнопки и включает/выключает светодиоды

Timer.set(250, Timer.REPEAT, 
function() {

data_but_1 = but_1.read(27); // Чтение состояния кнопки 1 
data_but_2 = but_2.read(27); // Чтение состояния кнопки 2

if(data_but_1) 
led_1.write(26, 1); // Включаем светодиод 1 
else led_1.write(26, 0);    // Выключаем светодиод 1

if(data_but_2) 
led_2.write(26, 1); // Включаем светодиод 2 
else led_2.write(26, 0);    // Выключаем светодиод 2

}, 

null);

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

Управление двигателями постоянного тока

В контроллере ESP-JS-AR имеются встроенные драйвера моторов, с помощью которых можно управлять двигателями постоянного тока. Контроллер поддерживает одновременное подключение до двух двигателей постоянного тока в двустороннем режиме управления (Рисунок 3.12), либо до четырех двигателей в одностороннем управлении.

Рисунок 3.12. Подключение двигателей постоянного тока к программируемому контроллеру IoT ESP-JS-AR

Пример для Arduino IDE

Рассмотрим простейший пример:

#include <JsAr.h> 
#include <JsArMotors.h>

void setup()

{

JsAr.begin(); 
JsArMotors.begin(); 
JsArMotors.powerWrite(1, 255); 
delay(5000);


JsArMotors.powerWrite(1, 0);
JsArMotors.powerWrite(1, -255); 
delay(5000);

}

void loop()

{

}

Здесь показано управление двигателем постоянного тока по мощности. Аргументами к функции powerWrite() являются номер двигателя постоянного тока ("1" - если подключение выполнено к выводам H1 и H2, и "2" - если подключение выполнено к выводам H3 иH4), а также значение ШИМ в диапазоне от 0 до 1023. Реверс задается указанием знака " - " перед значением ШИМ.

Пример для Mongoose OS

В случае использования среды разработки Mongoose OS при использовании JavaScript, код, реализующий аналогичный пример, будет выглядеть следующим образом:

// Подключаем необходимые библиотеки API 
load('api_config.js'); // Настройки конфигурации 
load('api_gpio.js');        // Работа с GPIO 
load('api_timer.js'); // Работа с таймерами 
load('api_sys.js');  // Системные функции 
load('api_jsar.js');    // Работа с платой ESP-JS-AR 
load('api_dxl.js');  // Работа с моторами Dynamixel

print('Start js app'); // Сообщение в консоль о старте программы
JsArMotors.begin(); // Инициализация моторов JsAr

// Включаем мотор 1 на максимальную мощность вперёд 
JsArMotors.powerWrite(1, 255);

Sys.usleep(2000000);    // Ждём 2 секунды

// Останавливаем мотор 1 
JsArMotors.powerWrite(1, 0); 
Sys.usleep(2000000);    // Ждём 2 секунды

// Включаем мотор 1 на максимальную мощность назад 
JsArMotors.powerWrite(1, -255);

Sys.usleep(2000000);    // Ждём 2 секунды

// Останавливаем мотор 1 
JsArMotors.powerWrite(1, 0);

Таким образом, становится возможно управлять двигателями постоянного тока напрямую с контроллера ESP-JS-AR. В случае необходимости подключения сразу четырех двигателей или двигателей с энкодерами, рекомендуется рассмотреть примеры, содержащиеся в соответствующей вкладке.

Управление сервоприводами

Помимо двигателей постоянного тока, в качестве исполнительных механизмов так же широко используются сервоприводы, управлять положением фланца которых можно с помощью ШИМ сигнала. В качестве примера рассмотрим управление двумя простейшими сервоприводами, подключенными к контроллеру ESP-JS-AR через плату расширения для подключения периферийных модулей. Такая схема подключения выбрана, во-первых, для удобства подключения, а во-вторых, на плате расширения находится свой стабилизатор питания, который будет отвечать за питание сервоприводов стабилизированными 5В (Рисунок 3.13):

Рисунок 3.13. Подключение сервоприводов к программируемому контроллеру IoT ESP-JS-AR с помощью платы расширения

В приведенной выше схеме подключение выполнено к колодкам, содержащим сигнальные линии под номером 12 и 13. Однако, надо понимать, что соответствующие им линии управления на контроллере ESP-JS-AR будут 12и 14, соответственно.

Пример для Arduino IDE

Для удобства управления сервоприводами рекомендуется установить библиотеку "ESP32Servo" (Рисунок 3.14):

Рисунок 3.14. Установка библиотеки для управления сервоприводами ESP32Servo

Используя установленную библиотеку, код для управления положением фланца сервоприводов выглядит следующим образом:

// Подключаем необходимые библиотеки

#include <ESP32Servo.h> // Библиотека для управления сервоприводами на ESP32

#include "JsAr.h"   // Библиотека для работы с платой ESP-JS-AR

// Объявляем объекты сервоприводов 
Servo myservo1;
Servo myservo2;

int pos = 0; // Переменная для задания положения сервоприводов

void setup() {

JsAr.begin();   // Инициализация платы ESP-JS-AR

// Инициализация сервоприводов на конкретных пинах 
myservo1.attach(12);

myservo2.attach(14);

}

void loop() {

// Плавное движение сервоприводов от 0 до 180 градусов 
for (pos = 0; pos <= 180; pos += 1) {

myservo1.write(pos); 
myservo2.write(pos);

delay(30); // Задержка для плавного движения

}

// Плавное движение сервоприводов от 180 до 0 градусов 
for (pos = 180; pos >= 0; pos -= 1) {

myservo1.write(pos); 
myservo2.write(pos);

delay(30); // Задержка для плавного движения

}

}
Таким образом с помощью контроллера ESP-JS-AR становится возможным управлять простейшими сервоприводами.

Пример для Mongoose OS

В случае использования среды разработки Mongoose OS при использовании JavaScript, код, реализующий аналогичный пример, будет выглядеть следующим образом:

load('api_config.js');          // Конфигурация платы 
load('api_gpio.js');         // Работа с GPIO 
load('api_timer.js');       // Работа с таймерами 
load('api_sys.js');  // Системные функции 
load('api_jsar.js');    // Работа с платой ESP-JS-AR
load('api_jsar_servo.js');  // Работа с сервоприводами через JsAr

let led_state = false;  // Состояние светодиода

let servo1 = Servo.create(); // Создаем объект сервопривода 1 
let servo2 = Servo.create(); // Создаем объект сервопривода 2

servo1.attach(12);  // Подключаем servo1 к пину 12

servo2.attach_full(19, 1000, 2000); // Подключаем servo2 к пину 19 с рабочим диапазоном 1000-2000 мкс

Timer.set(2000, Timer.REPEAT, function() {

// Каждые 2 секунды переключаем состояние светодиода и сервоприводов 
if (led_state === false) {

led_state = true;

JsAr.expanderWriteLed(1); // Включаем светодиод на плате 
servo1.write(45);   // Устанавливаем угол сервопривода 1 
servo2.write(45);   // Устанавливаем угол сервопривода 2

} else {

led_state = false;

JsAr.expanderWriteLed(0); // Выключаем светодиод 
servo1.write(135);  // Устанавливаем другой угол сервопривода 1 
servo2.write(135);  // Устанавливаем другой угол сервопривода 2


}

print(servo1.read());   // Выводим угол servo1 в консоль 
print(servo2.read_us());        // Выводим значение в микросекундах servo2

}, null);
В данном примере происходит управление двумя сервоприводами, один из которых подключен к линии 12, а другой к линии **19 ** (на плате расширения это пин 4). Во время работы происходит изменение положения сервоприводов с 45 градусов до 135, причем, второй сервопривод имеет заданные ограничения по диапазону перемещения за счет функции attach_full().

Подключение LCD экрана

В состав набора входит LCD дисплей со встроенным I2C драйвером. Сам дисплей представляет собой классический символьный экран в 2 строки по 16 символов каждый и может быть использован как инструмент для вывода текстовой информации. Подключается данный экран по шине I2C, сигнальные линии которой, по умолчанию, находятся на следующих выводах: SCL – 22, SDA – 21 (но при необходимости это можно изменить). Так для работы дисплея требуется подать на него питание в 5В, и с помощью потенциометра, расположенного на драйвере экрана, установить подходящую контрастность символов. Таким образом, схема подключения будет выглядеть примерно следующим образом (Рисунок 3.15):

Рисунок 3.15. Подключение LCD экрана к программируемому контроллеру IoT ESP-JS-AR

Пример для Arduino IDE

Для более удобного управления экраном рекомендуется установить библиотеку "LiquidCrystal I2C" (Рисунок 3.16):

Рисунок 3.16. Установка библиотеки для управления LCD дисплеем LiquidCrystal I2C

После установки библиотеки код для вывода данных на экран выглядит следующим образом:

#include <Wire.h>   // Библиотека для работы с I2C

#include <LiquidCrystal_I2C.h> // Библиотека для работы с LCD по I2C 
#include "JsAr.h"   // Библиотека для платы ESP-JS-AR

LiquidCrystal_I2C lcd(0x27, 16, 2); // Адрес LCD 0x27, 16 символов на 2 строки

void setup() {

JsAr.begin();       // Инициализация платы ESP-JS-AR 
lcd.init(); // Инициализация LCD

lcd.backlight();    // Включение подсветки дисплея

}

void loop() {

// Первая строка и первая надпись 
lcd.setCursor(0, 0); 
lcd.print("Applied Robotics"); 
lcd.setCursor(0, 1);

lcd.print("KPMIS IoT");

delay(1000);        // Задержка 1 секунда 
lcd.clear();    // Очистка экрана

// Вторая надпись 
lcd.setCursor(0, 0); 
lcd.print("TEXT TEXT"); 
delay(1000);

lcd.setCursor(0, 1); 
lcd.print("I2C Display"); 
delay(1000);

}
В данном примере происходит следующее: после инициализации всех необходимых библиотек и указания, с каким размером экрана происходит работа, на экране последовательно выводятся строки: "Applied Robotics", "KPMIS IoT", "TEXT TEXT", "I2C Display". Таким образом, можно выводить на экран как информационные фразы, так и результаты показаний датчиков и состояния системы.

Пример для Mongoose OS

В случае использования среды разработки Mongoose OS при использовании JavaScript, код, реализующий аналогичный пример, будет выглядеть следующим образом:

load("api_config.js"); 
load("api_sys.js"); 
load("api_gpio.js"); 
load("api_timer.js"); 
load("api_lcd_i2c.js"); 
load("api_jsar.js");

// I2C: SDA - 32, SDC - 33

print("Start user js app at", Sys.uptime()); 
let lcd = LCD_I2C.create();

lcd.begin(2, 10);

lcd.home(); 
lcd.print("Hello World!"); 
lcd.setCursor(0, 1); 
lcd.print("Uptime: ");

Timer.set(1000, Timer.REPEAT, function() { lcd.setCursor(8, 1); lcd.print(Sys.uptime() |0)
}, null);
В данном примере выводится сообщение "Hello World!" и считается время с момента запуска в секундах.

Подключение эластичной клавиатуры

В состав набора входит эластичная клавиатура на 16 (4х4) или на 12 (3х4) кнопок. Данная клавиатура может быть подключена к контроллеру и использована для ввода данных. Несмотря на отсутствие полноценного алфавита, данная клавиатура может быть использована для моделирования ввода простейших команд, либо же неких символьных последовательностей, которые могут быть использованы в качестве кодов доступа. Для более удобной работы с клавиатурой рекомендуется скачать и установить соответствующую библиотеку, например, "iarduino_KB". Подключение клавиатуры к контроллеру выполняется перемычками, которые можно подключать к любым цифровым линиям (однако, надо помнить, что в системе некоторые линии могут быть задействованы другим оборудованием). В результате, подключение клавиатуры к контроллеру может выглядеть следующим образом (Рисунок 3.17):

Рисунок 3.17. Подключение эластичной клавиатуры к программируемому контроллеру IoT ESP-JS-AR

Благодаря установленной библиотеке, код работы с клавиатурой будет выглядеть следующим образом:

#include "JsAr.h"   // Библиотека для работы с платой ESP-JS-AR 
#include <iarduino_KB.h>    // Библиотека для работы с эластичной клавиатурой

// Инициализация клавиатуры с указанием выводов (пины для рядов и колонок)

iarduino_KB KB(14, 12, 13, 15, 4, 2, 22, 21);

void setup() {

JsAr.begin();   // Инициализация платы ESP-JS-AR


KB.begin(KB1);  // Инициализация клавиатуры (режим KB1) 
Serial.begin(115200);       // Инициализация Serial для вывода данных

}

void loop() {

// Проверка нажатия клавиши 
if (KB.check(KEY_DOWN)) {

// Вывод номера нажатой клавиши и символа в 
Serial Serial.print(KB.getNum());

Serial.print(" = \""); 
Serial.print(KB.getChar()); 
Serial.println("\"");

}

}

В данном примере, после инициализации всех необходимых библиотек, происходит инициализация подключенной клавиатуры, путем указания линий, к которым подключен шлейф. Указание линий производится, начиная с левого вывода шлейфа. Затем указывается тип клавиатуры: KB1 (16ти кнопочная) или KB3 (12ти кнопочная). После чего, в бесконечном цикле происходит опрос кнопок клавиатуры, и в случае их нажатия, вывод номера кнопки и его символа в терминал (Рисунок 3.18):

Рисунок 3.18. Результат опроса эластичной клавиатуры

Работа с Arduino-совместимыми компонентами

В качестве примера работы контроллера ESP-JS-AR с классическими Arduino-совместимыми комплектующими, такими как: светодиоды, кнопки, различные простейшие датчики, рассмотрим процесс взаимодействия с собранной на макетной плате системы из 3х светодиодов, кнопки и датчика расстояния (Рисунок 3.19):

Рисунок 3.19. Подключение Arduino-совместимых компонентов к программируемому контроллеру IoT ESP-JS-AR

Схема сборки данной системы изображена на рисунке 3.20. Здесь номера линий указы в нотации платы для подключения периферийных модулей. Т.е. при разработке кода номера линий, указанные на схеме и указываемые в коде, совпадать не будут (смотрите описание платы для подключения периферийных модулей).

Рисунок 3.20. Схема подключения Arduino-совместимых компонентов к программируемому контроллеру IoT ESP-JS-AR

Пример для Arduino IDE

Код управляющей программы выглядит следующим образом:

#include "JsAr.h"

// указываем пины светодиодов 
int LedG = 2;

int LedY = 4; 
int LedR = 15;

// указываем пин кнопки 
int But = 22;

// указываем пины дальномера 
int Trig = 21;

int Echo = 18;

void setup() { 
JsAr.begin(); 
Serial.begin(115200);

pinMode(LedG, OUTPUT); 
pinMode(LedY, OUTPUT); 
pinMode(LedR, OUTPUT); 
pinMode(But, INPUT); 
pinMode(Trig, OUTPUT); 
pinMode(Echo, INPUT);

// желтый светодиод всегда включен 
digitalWrite(LedY, HIGH); 
digitalWrite(LedR, LOW);

}

void loop() {

int duration, cm;

// если кнопка нажата, выводим сообщение 
if(!digitalRead(But)){

Serial.println("But pressed");

// включаем зеленый светодиод 
digitalWrite(LedG, HIGH); 
delay(100);

}

else digitalWrite(LedG, LOW);

// формируем импульс для дальномера 
digitalWrite(Trig, LOW); 
delayMicroseconds(2); 
digitalWrite(Trig, HIGH); 
delayMicroseconds(10); 
digitalWrite(Trig, LOW);

// вычисляем расстояние 
duration = pulseIn(Echo, HIGH); 
cm = duration / 58; 
Serial.print(cm);

Serial.println(" cm");

// если расстояние меньше 10 см,

// то включается красный светодиод 
if(cm < 10){ digitalWrite(LedR, HIGH);

}

else digitalWrite(LedR, LOW); 
delay(100);

}

В данном примере происходит следующее. После инициализации всех необходимых библиотек, происходит включение желтого светодиода, который в примере включен постоянно. Затем, в непрерывном цикле происходит опрос кнопки и ультразвукового датчика расстояния. Если кнопка нажата, то включается зеленый светодиод. А если УЗ датчик расстояния фиксирует расстояние до объекта, менее 10 см, то включается красный светодиод. Таким образом, совместно с контроллером ESP-JS-AR могут быть использованы стандартные Arduino-совместимые комплектующие.

Пример для Mongoose OS

В случае использования среды разработки Mongoose OS при использовании JavaScript, код, реализующий аналогичный пример, но без функционала дальномера будет выглядеть следующим образом:

// Подключаем необходимые модули Mongoose OS 
load('api_config.js'); // Работа с конфигурацией 
load('api_gpio.js'); // Работа с GPIO 
load('api_timer.js'); // Работа с таймерами 
load('api_sys.js'); // Системные функции
load('api_jsar.js'); // Специфичные функции для платы ESP-JS-AR

// Определение пинов

let Gpin = 2; // Зелёный светодиод 
let Ypin = 4; // Жёлтый светодиод 
let Rpin = 15; // Красный светодиод 
let Bpin = 22; // Кнопка/датчик
let Trig = 21; // Триггер ультразвукового датчика 
let Echo = 18; // Эхо ультразвукового датчика

// Переменные для работы с ультразвуковым датчиком


let Time_start = 0; 
let Time_stop = 0; 
let duration = 0;

// Настройка пинов

GPIO.setup_input(Echo, GPIO.PULL_NONE); // Эхо как вход без подтяжки 
GPIO.setup_output(Gpin, 0); // Зелёный светодиод как выход, начальное состояние LOW

GPIO.setup_output(Ypin, 0); // Жёлтый светодиод как выход, начальное состояние LOW

GPIO.setup_output(Rpin, 0); // Красный светодиод как выход, начальное состояние LOW

GPIO.setup_input(Bpin, GPIO.PULL_NONE); // Кнопка как вход без подтяжки

// Таймер с периодом 500 мс 
Timer.set(500, Timer.REPEAT, function() {

GPIO.write(Gpin, 1);            // Включаем зелёный светодиод 
if (GPIO.read(Bpin)) {      // Если кнопка нажата
 GPIO.write(Ypin, 0);   // Выключаем жёлтый светодиод

} else {
GPIO.write(Ypin, 1);    // Иначе включаем жёлтый светодиод
}
}, null);

Отправка данных с платы в веб-интерфейс

Рассмотрим способ отправки данных с микроконтроллера в веб-интерфейс. Для работы нам понадобится 2 файла – основной файл .ino, который содержит логику устройства (платы), и файл .h с разметкой HTML для создания интерфейса веб-страницы.

Сторона веб-интерфейса

Создаем файл html_base.h и открываем его. В нем создаем переменную, которая будет содержать в себе всю разметку:

char html[] PROGMEM = R"rawliteral()rawliteral";

Весь дальнейший код пишется в скобках. Объявляем тип документа как HTML, указываем русский язык:

<!DOCTYPE html>

<html lang="ru">

Тег <head> содержит метаинформацию о документе, то есть данные для браузера. Это техническая информация о веб-странице, которая не отображается напрямую пользователю, но необходима браузерам, поисковым системам и другим сервисам для правильной обработки документа:

<head>

<meta charset="UTF-8" />

<meta name="viewport" content="width=device-width, initial-scale=1.0" />

<title>Example</title>

В теге <style> находится секция CSS. Это язык стилей, который определяет внешний вид HTML-документа. После этого тег <head> закрывается:

<style>

…

</style>

</head>

Тег<body> – это тело документа. Здесь находится видимое содержимое страницы. Интересует объект <p>, в котором будут выводится данные. Зададим ему id – text:

<p id="text" style="margin: 0 0 0 10px;">text</p>

Необходимо написать скрипт, который будет принимать значения и выводить их в нужный объект. Создаем функцию getText. Внутри создаем новый объект xhr – новый запрос на плату, который будет экземпляром XMLHttpRequest:

var xhr = new XMLHttpRequest;

Теперь откроем запрос с методом “GET” по адресу "/getData", третий элемент отвечает за асинхронную работу – пишем true:

xhr.open("GET", "/getData", true);

Далее пишем обработчик на изменение состояния запроса:

xhr.onreadystatechange = function() {}

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

if (xhr.readyState === 4) {

Добавляем внутрь изменение объекта <p>. Запишем в него значение, которое пришло:

document.getElementById("text").innerHTML = xhr.responseText;

В конце необходимо отправить запрос:

xhr.send();

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

const intervalID = setInterval(getText,1000);

Полный код файла html_base.h:

char html[] PROGMEM = R"rawliteral(

<!DOCTYPE html>

<html lang="ru">

<head>

<meta charset="UTF-8" />

<meta name="viewport" content="width=device-width, initial-scale=1.0" />

<title>Example</title>

<style>

:root {

--bg: #638be9; /* фон страницы */

--card: #3c75cf; /* фон карточек */

--text: #ffffff; /* цвет текста */

--accent: #3b82f6; /* акцентный цвет */

}

* { box-sizing: border-box; } html, body { height: 100%; } body {

margin: 0;

font-family: system-ui, -apple-system, Micra, Roboto, Inter, Tahoma, sans-serif; background: var(--bg);

color: var(--text);

}

.grid {

display: grid;

min-height: 80svh; /* минимальная высота */

place-content: center; /* центрирование содержимого */ gap: 16px; /* расстояние между элементами */

grid-template-columns: repeat(1, 320px); /* 1 колонка по 320px */ grid-auto-rows: 280px; /* высота строк */

padding: 16px;

}

.card {

display: grid;

place-items: center; background: var(--card); border: 1px solid #334155; border-radius: 16px; padding: 16px;

box-shadow: 0 6px 20px rgb(0 0 0 / .2);

user-select: none; /* запрет выделения текста */

}

.card span {

font-weight: 600; letter-spacing: 0.3px; text-align: center; display: flex;

}

.innerDivFlex{

# 73


**Программируемый контроллер IoT ESP-JS-AR. **Учебное пособие

display: flex;

flex-direction: column; /* элементы по колонке */

}

.onlyFlex{ display: flex;

align-items: center;

justify-content: center; /* выравнивание по центру */ margin-bottom: 10px;

}

</style>

</head>

<body>

<main class="grid">

<div class="card">

<span>Случайное значение</span>

<span>

<div class="innerDivFlex">

<div class="onlyFlex">

<p id="text" style="margin: 0 0 0 10px;">text</p> <!-- сюда будет выводиться значение с ESP -->

</div>

</div>

</span>

</div>

</main>

<script>

// Функция для получения данных с ESP function getText(){

var xhr = new XMLHttpRequest;

xhr.open("GET", "/getData", true); // GET-запрос на ESP xhr.onreadystatechange = function() {

if (xhr.readyState === 4) { // когда получен ответ document.getElementById("text").innerHTML = xhr.responseText; // выво-

дим данные

}

}

xhr.send(); // отправляем запрос

}

// Интервал вызова функции каждую секунду const intervalID = setInterval(getText, 1000);

</script>

</body>

</html>

)rawliteral";

Сторона платы

Для работы помимо уже известной нам "JsAr" необходимо использовать библиотеки "WiFi" для управления WiFi модулем платы и "ESPAsyncWebServer", позволяющей создавать веб-сервер и обрабатывать запросы без блокировки основного кода. Эти библиотеки можно найти в репозиториях Arduino IDE. Также необходимо подключить HTML верстку:

#include <WiFi.h> // Модуль для Wi-Fi

#include "ESPAsyncWebServer.h" // Библиотека для создания Web-server"а #include "html_base.h" // html

#include <JsAr.h> // Подключение библиотеки для работы с платой ESP.

Пропишем логин и пароль которые присвоим Wi-Fi:

const char *ssid = "testCase"; // ssid Wi-Fi

const char *password = "12345678"; // Пароль к Wi-Fi

Далее напишем ip, маску и шлюз веб-сервера. Также необходимо присвоить порт веб-серверу. Пропишем стандартный порт, который умеет обрабатывать http запросы – 80. И создадим переменную, в которую будем генерировать случайное число:

IPAddress local_ip(192, 168, 0, 5); // IР на Web страничку 

IPAddress gateway(192, 168, 0, 5);

IPAddress subnet(255, 255, 255, 0); // Маска подсети

AsyncWebServer server(80); // Стандартный порт для обработки HTTP запросов uint8_t randNumber = 0; // Число для вывода на веб-сервер

Переходим в функцию setup, где инициализируем Wi-Fi модуль с нашими данными:

WiFi.softAP(ssid, password); WiFi.softAPConfig(local_ip, gateway, subnet);

Теперь пропишем стандартный обработчик для начальной странички. Внутри него пропишем ответ с выводом html:

server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {

request->send(200, "text/html", html); // Обязательный ответ на запрос

});

Добавим ещё один обработчик, который будет отправлять данные с платы на веб-сервер. Уже создан запрос по адресу "/getData". Будем отправлять в ответ на него данные. Внутри генерируем случайное число,

используя встроенную функцию random, в свойствах которой прописываем минимум и максимум, между которыми будет генерироваться число. Далее отправляем ответ с числом, преобразованным в строку, и выводим в Serial для проверки значений с веб-cервером:

#include <WiFi.h> // Модуль для работы с Wi-Fi

#include "ESPAsyncWebServer.h" // Библиотека для создания асинхронного веб-сервера

#include "html_base.h" // HTML-шаблон страницы #include <JsAr.h> // Библиотека для работы с платой ESP

// Логин и пароль для Wi-Fi

const char *ssid = "testCase"; // SSID сети

const char *password = "12345678"; // Пароль сети

// Настройки локального IP

IPAddress local_ip(192, 168, 0, 5); // IP-адрес ESP

IPAddress gateway(192, 168, 0, 5); // Шлюз

IPAddress subnet(255, 255, 255, 0); // Маска подсети 
AsyncWebServer server(80); // Порт веб-сервера

uint8_t randNumber = 0; // Переменная для случайного числа

void setup() {

JsAr.begin(); // Инициализация платы ESP-JS-AR

Serial.begin(115200); // Запуск Serial для вывода отладочной информации

// Инициализация Wi-Fi в режиме точки доступа 
WiFi.softAP(ssid, password); 
WiFi.softAPConfig(local_ip, gateway, subnet);

// Обработчик главной страницы

server.on("/", HTTP_GET, 
[](AsyncWebServerRequest *request) {

request->send(200, "text/html", html); // Отправляем HTML-страницу

});

// Обработчик запроса для получения случайного числа 
server.on("/getData", HTTP_GET, 
[](AsyncWebServerRequest *request) {

randNumber = random(10, 100); // Генерируем случайное число от 10 до 99 
request->send(200, "text/plain", (String)randNumber); // Отправляем число клиенту

Serial.println(randNumber); // Выводим число в Serial для отладки

});

server.begin(); // Запуск веб-сервера

}

void loop() {

// Пустой loop, так как сервер работает асинхронно

}
В результате при работе кода можно увидеть, как появилась сеть testСase. Подключимся к ней с установленным паролем. После успешного подключения перейдем по адресу, который прописали в коде. Откроется сайт, где начинают приходить случайные числа.

Рисунок 3.21. Генерация случайного числа в веб-интерфейсе

Отправка данных с веб-интерфейса на плату

Теперь рассмотрим способ отправки данных с веб-интерфейса на микроконтроллер. Для работы нам также понадобится два файла – основной файл .ino и файл .h для создания интерфейса веб-страницы.

Сторона веб-интерфейса

Создаем файл html_base.h, открываем его. Так же, как в предыдущей работе, создаем переменную, которая будет содержать в себе всю разметку. Объявляем тип документа, указываем русский язык. Прописываем метаданные в теге <head>.

В теге <body> интересует <input>, и кнопка, которая будет отправлять значение. У <input> пропишем id – inputValue, а у <button> – кнопки отправки данных – пропишем ивент onclick и присвоим функцию sendData(), которая будет вызываться при нажатии на кнопку:

<div class="onlyFlex">

<input id="inputValue" type="text" class="input-text" placeholder="" />

</div>

<div class="onlyFlex">

<button class="btn" onclick="sendData()">Отправить</button>

</div>

Теперь напишем скрипт, который будет отправлять значения на плату. Создаем функцию sendData, которую использовали раньше. Внутри создаем новый объект xhr – новый запрос на плату, который будет экземпляром XMLHttpRequest:

var xhr = new XMLHttpRequest; Получим значение с input"а:

var text = document.getElementById("inputValue").value;

Теперь откроем запрос с методом “GET” по адресу /NewData, а также добавим параметр для отправки data, используя символ "?": ""NewData?data=" + text"; третий элемент отвечает за асинхронную работу – пишем true:

xhr.open("GET", "NewData?data=" + text, true);

В конце необходимо отправить запрос: xhr.send();

Полный код файла html_base.h:

char html[] PROGMEM = R"rawliteral(

<!DOCTYPE html>

<html lang="ru">

<head>

<meta charset="UTF-8" />

<meta name="viewport" content="width=device-width, initial-scale=1.0" />

<title>Example 2</title>

<style>

:root {

--bg: #638be9; /* фон */

--card: #3c75cf; /* карточки */

--text: #ffffff; /* текст */

--accent: #3b82f6; /* акцентный цвет */

}

* { box-sizing: border-box; }

html, body { height: 100%; margin: 0; } body {

font-family: system-ui, -apple-system, Micra, Roboto, Inter, Tahoma, sans-serif; background: var(--bg);

color: var(--text);

}

.grid {

display: grid;

min-height: 80svh; place-content: center; gap: 16px;

grid-template-columns: repeat(1, 320px); grid-auto-rows: 280px;

padding: 16px;

}

.card {

display: grid;

place-items: center; background: var(--card); border: 1px solid #334155; border-radius: 16px; padding: 16px;

box-shadow: 0 6px 20px rgb(0 0 0 / .2);

user-select: none; /* запрет выделения текста */

}

.card span {

font-weight: 600; letter-spacing: 0.3px; text-align: center; display: flex;

}

.innerDivFlex{ display: flex;

flex-direction: column;

}

.onlyFlex{ display: flex;

align-items: center; justify-content: center; margin-bottom: 10px;

}

.input-text {

margin-left: 15px; width: 100%; padding: 10px 14px;

background: var(--card); border: 1px solid #334155; border-radius: 12px;

color: var(--text); font-size: 14px; outline: none;

transition: border-color 0.2s ease, box-shadow 0.2s ease;

}

.input-text::placeholder{ color: var(--text);

}

.input-text:focus {

border-color: var(--accent);

box-shadow: 0 0 0 3px rgb(59 130 246 / 0.3);

}

.btn {

display: inline-block; padding: 10px 14px; background: var(--card); border: 1px solid #334155; border-radius: 12px;

color: var(--text); font-size: 14px; font-weight: 500; cursor: pointer;

transition: background 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease;

}

.btn:hover {

background: #273449;

}

.btn:focus {

outline: none;

border-color: var(--accent);

box-shadow: 0 0 0 3px rgb(59 130 246 / 0.3);

}

</style>

</head>

<body>

<main class="grid">

<div class="card">

<span>Введите текст</span>

<span>

<div class="innerDivFlex">

<div class="onlyFlex">

<input id="inputValue" type="text" class="input-text" placeholder=""/>

</div>

<div class="onlyFlex">

<button class="btn" onclick="sendData()">Отправить</button>

</div>

</div>

</span>

</div>

</main>

<script>

// Функция отправки данных на ESP function sendData(){

var xhr = new XMLHttpRequest;

var text = document.getElementById("inputValue").value; // получаем значение из поля

xhr.open("GET", "NewData?data=" + text, true); // формируем GET-запрос xhr.send(); // отправляем запрос

}

</script>

</body>

</html>

)rawliteral";

Сторона платы

Для работы используем библиотеки "JsAr", "WiFi" для управления WiFi модулем платы и "ESPAsyncWebServer", позволяющую создавать веб-сервер и обрабатывать запросы без блокировки основного кода. Также подключаем HTML верстку:

#include <WiFi.h> //Модуль для Wi-Fi

#include "ESPAsyncWebServer.h" //Библиотека для создания Web-server"а #include "html_base.h" //html

#include <JsAr.h> // Подключение библиотеки для работы с платой ESP.

Пропишем логин и пароль для Wi-Fi. Далее напишем ip, маску и шлюз веб-сервера. Прописываем порт для обработки http запросов и создаем переменную для записи приходящего значения:

const char *ssid = "testCase2"; //ssid Wi-Fi

const char *password = "12345678"; //Пароль к Wi-Fi

//Выставляем настройки Wi-Fi

IPAddress local_ip(192, 168, 0, 5); //Ip на Web страничку

IPAddress gateway(192, 168, 0, 5);

IPAddress subnet(255, 255, 255, 0); //Маска подсети

AsyncWebServer server(80); //Стандартный порт для обработки HTTP запросов 
String text = ""; //Переменная для записи приходящего значения В функции setup инициализируем Wi-Fi модуль с данными и прописываем обработчик для начальной страницы:

WiFi.softAP(ssid, password); 
WiFi.softAPConfig(local_ip, gateway, subnet);

server.on("/", HTTP_GET, 
[](AsyncWebServerRequest *request) {

request->send(200, "text/html", html); //Обязательный ответ на запрос

});

Добавим ещё один обработчик, который будет принимать значения с веб-сервера. Уже создан запрос по адресу "/NewData". Теперь будем принимать значения с него. Внутри записываем в переменную text параметр, который прописали в запросе. Далее выводим его в Serial для проверки и отправляем ответ об успешном запросе:

server.on("/NewData", HTTP_GET, [](AsyncWebServerRequest *request) {

text = request->getParam("data")->value();  //Получаем информация с запроса

Serial.println(text);   //Выводим для проверки

request->send(200, "text/html", html);  //Отправляем 200, об успешном получении

});

В конце функции setup запускаем сервер командой begin: server.begin(); //Инициализация и запуск Web-сервера

Полный код выглядит следующим образом:

#include <WiFi.h>   // Модуль для работы с Wi-Fi

#include "ESPAsyncWebServer.h" // Библиотека для асинхронного веб-сервера

#include "html_base.h"  // HTML страница

#include <JsAr.h>   // Библиотека для работы с платой ESP

// Логин и пароль для Wi-Fi точки доступа 
const char *ssid = "testCase2"; // Имя сети Wi-Fi 
const char *password = "12345678"; // Пароль

// Настройки сети

IPAddress local_ip(192, 168, 0, 5); // Статический IP для веб-страницы 
IPAddress gateway(192, 168, 0, 5); // Шлюз

IPAddress subnet(255, 255, 255, 0); // Маска подсети

AsyncWebServer server(80); // Создание сервера на стандартном 
HTTP-порту String text = ""; // Переменная для хранения данных, полученных с веб-сервера

void setup() {

JsAr.begin();   // Инициализация платы ESP-JS-AR 
Serial.begin(115200); // Инициализация Serial для отладки

// Настройка Wi-Fi в режиме точки доступа 
WiFi.softAP(ssid, password); 
WiFi.softAPConfig(local_ip, gateway, subnet);

// Обработчик для начальной страницы

server.on("/", HTTP_GET, 
[](AsyncWebServerRequest *request) {

request->send(200, "text/html", html); // Отправляем HTML страницу

});

// Обработчик для получения данных с веб-страницы 
server.on("/NewData", HTTP_GET, 
[](AsyncWebServerRequest *request) {

// Считываем значение параметра "data" из запроса 
if (request->hasParam("data")) {

text = request->getParam("data")->value(); 
Serial.println(text); // Выводим значение для проверки

} else {

Serial.println("Параметр 'data' отсутствует в запросе");

});

}

// Отправляем HTML страницу в ответ 
request->send(200, "text/html", html);

server.begin(); // Запуск веб-сервера

}

void loop() {

// Основной цикл пустой, обработка запросов асинхронная

}

В результате при работе кода можно увидеть, как появилась сеть testСase. Подключимся к ней. После успешного подключения перейдем по адресу, который прописали в коде, чтобы посмотреть сайт. Напишем любую строку ввода и нажмем на кнопку для отправки. Проверим, что строка дошла до платы. При открытии монитора порта, отобразилась переменная.

Рисунок 3.22. Получение данных с веб-интерфейса на плату, с отображением результата на мониторе порта

Работа с модулями с использованием веб-интерфейса

Аналогично предыдущим работам, воспользуемся способом получения и управления данными через веб-интерфейс. Для этого примера подключаем несколько модулей: модуль "Датчик температуры и влажности воздуха" (ID 22), модуль "Трёхцветный светодиод" (ID 21), модуль "Концевой выключатель" (ID 23) и LCD-экран.

Рассмотрим обработчики на стороне платы:

При открытии веб-страницы происходит запрос на путь "/" и веб-сервер в ответ на этот запрос отправляет html разметку.

Для пути "/getData" вызывается метод "setJson", который в свою очередь считывает данные с датчиков, таких как температура, влажность и состояние концевого выключателя, формирует их в формат JSON и отправляет на веб-страницу. При отправке запроса на путь "/RGB" обработчик принимает параметры значений красного, зелёного и синего цветов и использует их для управления светодиодом. Путь "/lcdPrint" позволяет управлять дисплеем с помощью полученных параметров: текст, строка и позиция. Путь "/lcdClear" очищает дисплей.

Полный код выглядит следующим образом:

#include <LiquidCrystal_I2C.h> // Библиотека для работы с LCD дисплеем 
#include <WiFi.h>   // Модуль Wi-Fi

#include "ESPAsyncWebServer.h" // Библиотека для асинхронного Web-сервера

#include "html_base.h"  // HTML страница

#include <JsAr.h>   // Библиотека для работы с платой ESP

#include "DxlMaster2.h" // Библиотека для управления Dynamixel устройствами

// Логин и пароль Wi-Fi

const char *ssid = "ESP_JS_AR"; 
const char *password = "12345678";

// Настройки сети

IPAddress local_ip(192, 168, 0, 5);

IPAddress gateway(192, 168, 0, 5);

IPAddress subnet(255, 255, 255, 0);

AsyncWebServer server(80); // Порт для HTTP сервера

// Объявляем LCD-дисплей (адрес, количество столбцов, количество строк) 
LiquidCrystal_I2C lcd(0x27, 20, 4);

// Dynamixel устройства

DynamixelDevice deviceRGB(21); // Трёхцветный светодиод DynamixelDevice deviceTemp(22); // Датчик температуры и влажности 
DynamixelDevice deviceUsw(23); // Концевой выключатель

// Дополнительные переменные

String json = ""; // Для формирования JSON объекта

uint8_t uswData = 0;    // Данные концевого выключателя

uint8_t HumInt = 0, 
HumDot = 0; // Целая и дробная части влажности 
uint8_t TempInt = 0, 
TempDot = 0;        // Целая и дробная части температуры

void setup() {

JsAr.begin();   // Инициализация платы ESP-JS-AR 
Serial.begin(115200);   // Инициализация Serial для отладки 
DxlMaster.begin(57600);  // Инициализация шины Dynamixel

// Настройка Wi-Fi в режиме точки доступа 
WiFi.softAP(ssid, password); 
WiFi.softAPConfig(local_ip, gateway, subnet);

// Инициализация LCD дисплея 
lcd.init();

lcd.backlight();    // Включаем подсветку 
lcd.clear();    // Очищаем дисплей

// Обработчик начальной страницы

server.on("/", HTTP_GET, 
[](AsyncWebServerRequest *request) {

request->send(200, "text/html", html); // Отправляем HTML страницу

});

// Обработчик для получения данных с датчиков 
server.on("/getData", HTTP_GET, 
[](AsyncWebServerRequest *request){

setJson();  // Формируем JSON объект 
request->send(200, "text/plain", json);

json = "";  // Очищаем для следующей отправки

});

// Обработчик для управления RGB светодиодом 
server.on("/RGB", HTTP_GET, 
[](AsyncWebServerRequest *request){

String red = request->getParam("red")->value(); 
String green = request->getParam("green")->value(); 
String blue = request->getParam("blue")->value(); 
writeRGB(red.toInt(), green.toInt(), blue.toInt()); 
request->send(200, "text/html", html);

});

// Обработчик очистки LCD дисплея

server.on("/lcdClear", HTTP_GET, 
[](AsyncWebServerRequest *request){ 
lcd.clear();

request->send(200, "text/html", html);

});

// Обработчик для вывода текста на LCD

server.on("/lcdPrint", HTTP_GET, [](AsyncWebServerRequest *request){ 
String row = request->getParam("row")->value();

String pos = request->getParam("pos")->value(); 
String text = request->getParam("text")->value(); 
writeLCD(row.toInt(), pos.toInt(), text);

request->send(200, "text/html", html);

});

server.begin(); // Запуск сервера

}

void loop() {

delay(20); // Минимальная задержка

}

// Управление RGB светодиодом

void writeRGB(int red, int green, int blue){ 
deviceRGB.write(26, (uint8_t)green); 
deviceRGB.write(27, (uint8_t)red); 
deviceRGB.write(28, (uint8_t)blue);

}

// Управление LCD дисплеем

void writeLCD(int row, int pos, String text){ 
lcd.setCursor(pos, row);

lcd.print(text);

}

// Формирование JSON объекта с данными датчиков 
void setJson(){

readData(); // Чтение данных с датчиков json = "[{";

json += "\"temp\":" + getTempValue() + ","; json += "\"humi\":" + getHumidityValue() + ","; json += "\"usw\":" + String(uswData);

json += "}]";

}

// Получение значения температуры в формате строки String getTempValue(){

return String(TempInt) + "." + String(TempDot);

}

// Получение значения влажности в формате строки String getHumidityValue(){

return String(HumInt) + "." + String(HumDot);

}

// Чтение данных с датчиков void readData(){

deviceTemp.read(24, HumInt); // Целая часть влажности 
deviceTemp.read(26, HumDot); // Дробная часть влажности 
deviceTemp.read(28, TempInt); // Целая часть температуры 
deviceTemp.read(30, TempDot); // Дробная часть температуры 
deviceUsw.read(27, uswData); // Концевой выключатель

}

Рассмотрим работу на стороне веб-страницы:

Обмен данными между страницей и платой происходит асинхронно, чтобы интерфейс оставался отзывчивым. Для получения данных страница каждые 200 мс отправляет GET-запрос с помощью метода setInterval на "/getData" с использованием XMLHttpRequest. Страница разбирает полученный JSON и обновляет значения в карточках интерфейса, обеспечивая отображение в реальном времени. Для отправки данных пользователь вводит значения в поля ввода (например, цвета для RGB или текст для LCD) и нажимает кнопку. JavaScript формирует GET-запрос с параметрами в URL, например, "/RGBred=255&green=0&blue=0" или "/lcdPrint?r ow=0&pos=0&text=Hello", и отправляет его.

Полный код файла html_base.h:
Julia, [11.09.2025 13:38]

char html[] PROGMEM = R"rawliteral(

<!DOCTYPE html>

<html lang="ru">

<head>

<meta charset="utf-8" />

<meta name="viewport" content="width=device-width, initial-scale=1" />

<title>ESP-JS-AR</title>

<style>

:root {

--bg: #638be9; /* фон страницы */

--card: #3c75cf; /* фон карточек */

--text: #ffffff; /* цвет текста */

--accent: #3b82f6; /* цвет акцента */

}

* { box-sizing: border-box; } html, body { height: 100%; } body {

margin: 0;

font-family: system-ui, -apple-system, Micra, Roboto, Inter, Tahoma, sans-serif; background: var(--bg);

color: var(--text);

}

h1 {

text-align: center; margin-top: 20px; font-size: 28px; font-weight: bold;

}

/* Сетка для карточек */

.grid {

display: grid;

min-height: 80svh; place-content: center; gap: 16px;

grid-template-columns: repeat(2, 360px); grid-auto-rows: 280px;

padding: 16px;

}

/* Стили карточек */

.card {

display: grid;

place-items: center; background: var(--card); border: 1px solid #334155; border-radius: 16px; padding: 16px;

box-shadow: 0 6px 20px rgb(0 0 0 / .2);

user-select: none; /* запрет выделения текста */

}

.card span {

font-weight: 600; letter-spacing: .3px; text-align: center; display: flex;

}

.innerDivFlex{ display: flex;

flex-direction: column;

}

.onlyFlex{ display: flex;

align-items: center; justify-content: center; margin-bottom: 10px;

}

.valueOffset{ margin-left: 15px;

}

.input-text {

margin-left: 15px; width: 100%; padding: 10px 14px;

background: var(--card); border: 1px solid #334155; border-radius: 12px;

color: var(--text); font-size: 14px; outline: none;

transition: border-color 0.2s ease, box-shadow 0.2s ease;

}

.input-text::placeholder{ color: var(--text);

}

.input-text:focus {

border-color: var(--accent);

box-shadow: 0 0 0 3px rgb(59 130 246 / 0.3);

}

.btn {

display: inline-block; padding: 10px 14px; background: var(--card); border: 1px solid #334155; border-radius: 12px;

color: var(--text); font-size: 14px;

font-weight: 500; cursor: pointer;

transition: background 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease;

}

.btn:hover {

background: #273449;

}

.btn:focus { outline: none;

border-color: var(--accent);

box-shadow: 0 0 0 3px rgb(59 130 246 / 0.3);

}

/* Адаптив для мобильных устройств */ @media (max-width: 480px) {

.grid {

grid-template-columns: repeat(1, 300px); grid-auto-rows: 280px;

gap: 12px;

}

}

</style>

</head>

<body>

<h1>ESP JS AR</h1>

<main class="grid">

<!-- Карточка концевого выключателя -->

<div class="card">

<span>Концевой выключатель</span>

<span>

<p>Значение: </p>

<p id="uswData" class="valueOffset">0</p>

</span>

</div>

<!-- Карточка RGB светодиода -->

<div class="card">

<span>Трехцветный светодиод</span>

<div class="innerDivFlex">

<div class="onlyFlex">

<div>Зеленый</div>

<input id="rgb_G" type="text" class="input-text" placeholder="0" />

</div>

<div class="onlyFlex">

<div>Красный</div>

<input id="rgb_R" type="text" class="input-text" placeholder="0" />

</div>

<div class="onlyFlex">

<div>Синий</div>

<input id="rgb_B" type="text" class="input-text" placeholder="0" />

</div>

<div class="onlyFlex">

<button class="btn" onclick="sendRGB()">Отправить</button>

</div>

</div>

</div>

<!-- Карточка датчика температуры и влажности -->

<div class="card">

<span>Датчик температуры и влажности</span>

<span>

<div class="innerDivFlex">

<div class="onlyFlex">

<p>Температура: </p>

<p id="temp" class="valueOffset">0</p>

</div>

<div class="onlyFlex">

<p>Влажность: </p>

<p id="humidity" class="valueOffset">0</p>

</div>

</div>

</span>

</div>

Julia, [11.09.2025 13:38]

<!-- Карточка LCD дисплея -->

<div class="card">

<span>LCD экран</span>

<div class="innerDivFlex">

<div class="onlyFlex">

<div>Строка</div>

<input id="LCD_row" type="text" class="input-text" placeholder="0" />

</div>

<div class="onlyFlex">

<div>Позиция</div>

<input id="LCD_point" type="text" class="input-text" placeholder="0" />

</div>

<div class="onlyFlex">

<div>Текст</div>

<input id="LCD_text" type="text" class="input-text" placeholder="Пример"

/>

</div>

<div class="onlyFlex">

<button class="btn" onclick="sendLCD()">Отправить</button>

<button class="btn" onclick="clearLCD()" style="margin-left: 15px;">Очи-

стить</button>

</div>

</div>

</div>

</main>

<script>

// Получаем элементы для обновления данных с ESP 
let tempVal = document.getElementById("temp");

let humiVal = document.getElementById("humidity"); 
let uswVal = document.getElementById("uswData");

// Функция отправки значений RGB на плату function 
sendRGB(){

let green = document.getElementById("rgb_G").value; 
let red = document.getElementById("rgb_R").value; 
let blue = document.getElementById("rgb_B").value;

let uri = RGB?red=${red}&green=${green}&blue=${blue}; 
var xhr = new XMLHttpRequest();

xhr.open("GET", uri, true); 
xhr.send();

}

// Функция очистки LCD дисплея 
function clearLCD(){

var xhr = new XMLHttpRequest(); 
xhr.open("GET", "lcdClear", true); 
xhr.send();

}

// Функция отправки текста на LCD дисплей 
function sendLCD(){

let row = document.getElementById("LCD_row").value; 
let pos = document.getElementById("LCD_point").value; 
let text = document.getElementById("LCD_text").value;

let uri = lcdPrint?row=${row}&pos=${pos}&text=${text}; 
var xhr = new XMLHttpRequest();

xhr.open("GET", uri, true); 
xhr.send();

}

// Функция периодического получения данных с платы function getData(){

var xhr = new XMLHttpRequest(); 
xhr.open("GET", "/getData", true);
xhr.onreadystatechange = function() {

if (xhr.readyState === 4 && xhr.status === 200) { 
let jsonObject = JSON.parse(xhr.responseText);

tempVal.innerHTML = jsonObject[0].temp;     // Обновляем температуру 
humiVal.innerHTML = jsonObject[0].humi;     // Обновляем влажность 
uswVal.innerHTML = jsonObject[0].usw;   // Обновляем значение концевого выключателя

}

};

xhr.send();

}

// Запуск периодического опроса каждые 200 мс 
const intervalID = setInterval(getData, 200);

</script>

</body>

</html>

)rawliteral";

Рисунок 3.23. Внешний вид веб-страницы