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

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

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

На универсальном вычислительном контроллере DXL-IoT имеется светодиод (Рис 1.1, позиция 7), линия управления которого заведена на линию PB7 (D13) управляющего микроконтроллера Atmega2560. Такой светодиод имеется у большинства Arduino-совместимых плат и может быть использован как для тестирования работоспособности микроконтроллера, так и для индикации состояния выполнения программы. Для управления им можно воспользоваться простейшим скетчем, приведенном в примере: «Файлы» –> «Примеры» –> «01.Basic» –> «Blink»:

void setup() { pinMode(LED_BUILTIN, OUTPUT);

}

void loop() { digitalWrite(LED_BUILTIN, HIGH); delay(1000); digitalWrite(LED_BUILTIN, LOW); delay(1000);

}

В результате загрузки кода данного примера в контроллер, светодиод начнет мигать с периодичностью в 1 секунду (Рис. 3.1).

Рис. 3.1. Управление встроенным светодиодом

Подключение УЗ-дальномера

Программируемый контроллер DXL-IoT является Arduino-подобной платформой, то к нему можно подключать популярные Arduino-совместимые датчики таким же образом, как они подключаются к платам типа Arduino Mega2560 или Arduino Uno. В качестве примера рассмотрим взаимодействие с типовым датчиком расстояния. Проводное подключение УЗ дальномера к контроллеру (Рис. 3.2) выполняется в соответствии со схемой распиновки контроллера, показанной на рисунке 3.2:

Рис. 3.2. Подключение УЗ-дальномера к контроллеру DXL-IoT

#define PIN_TRIG 9

#define PIN_ECHO 8 
long duration, cm; 
void setup() {

// Инициализируем взаимодействие по последовательному порту 
Serial.begin (115200);

// Определяем вводы и выводы 
pinMode(PIN_TRIG, OUTPUT); 
pinMode(PIN_ECHO, INPUT);

}

void loop() { 
digitalWrite(PIN_TRIG, LOW); 
delayMicroseconds(5); 
digitalWrite(PIN_TRIG, HIGH); 
delayMicroseconds(10); 
digitalWrite(PIN_TRIG, LOW);

duration = pulseIn(PIN_ECHO, HIGH); 
cm = (duration / 2) / 29.1; 
Serial.print("Расстояние до объекта: "); 
Serial.print(cm);
Serial.println(" см."); delay(250);

}
В результате, в монитор порта будет выведена следующая информация (Рис. 3.3):

Рис. 3.3. Вывод данных в монитор порта

Использование модуля беспроводной связи Bluetooth

Модуль беспроводной связи, установленный на контроллере DXL-IoT может работать как в режиме Wi-Fi, так и в режиме Bluetooth 4 (он же Bluetooth LE или BLE). Для активации требуемого режима работы необходимо загрузить определенный программный код в модуль с помощью среды разработки Arduino IDE.

В качестве основного примера для настройки работы модуля в качестве Bluetooth устройства, будет использоваться стандартный пример

«BLE_uart.» Данный пример находится, по умолчанию, после установки программной поддержки модуля в среде Arduino IDE в меню «Файл» –>

«Примеры» –> ESP32 BLE Arduino –> BLE_uart. Для отображения данного примера в среде Arduino IDE должна быть выбрана плата «ESP32 Dev Module». Данный пример необходимо модернизировать таким образом, чтобы задать необходимое наименование нашего BLE-устройства и добавить код, передающий получаемые по Bluetooth каналу данные в вычислительный микроконтроллер atmega2560.

Для изменения наименования устройства необходимо внести изменения в строку:

BLEDevice::init(“UART Service”);

где вместо “UART Service“ необходимо указать требуемое наименование, например, “My Robot“:

BLEDevice::init(“My Robot”);

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

#include <BLEDevice.h> 
#include <BLEServer.h> 
#include <BLEUtils.h> 
#include <BLE2902.h>

BLEServer *pServer = NULL; 
BLECharacteristic * pTxCharacteristic; 
bool deviceConnected = false;

bool oldDeviceConnected = false; 
uint8_t txValue = 0;

// Перейдите на сайт для получения информации о генерации UUIOs: ht[tps://www.uuidgener](http://www.uuidgenerator.net/)ator[.net/](http://www.uuidgenerator.net/)

#define SERVICE_UUID “6E400001-B5A3-F393-E0A9-E50E24DCCA9E“

// UART service UUID

#define CHARACTERISTIC_UUID_RX “6E400002-B5A3-F393-E0A9- E50E24DCCA9E“

#define CHARACTERISTIC_UUID_TX “6E400003-B5A3-F393-E0A9- E50E24DCCA9E“


class MyServerCallbacks: public BLEServerCallbacks { 
void onConnect(BLEServer* pServer) { deviceConnected = true;


};

void onDisconnect(BLEServer* pServer) { deviceConnected = false;

}

};

class MyCallbacks: public BLECharacteristicCallbacks { void onWrite(BLECharacteristic *pCharacteristic) { std::string rxValue = pCharacteristic->getValue();

if (rxValue.length() > 0) { 
Serial.println(*********); 
Serial.print(Received Value: );

for (int i = 0; i < rxValue.length(); i++) Serial.print(rxValue[i]);

Serial.println(); 
Serial.println(*********);

}

}

};

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

// создать устройство 
BLE BLEDevice::init(My Robot);

// создать BLE-сервер

pServer = BLEDevice::createServer();

pServer->setCallbacks(new MyServerCallbacks());

// создать сервис BLE

BLEService *pService = pServer->createService(SERVICE_UUID);

// создать характеристику BLE

pTxCharacteristic = pService->createCharacteristic( CHARACTERISTIC_UUID_TX, BLECharacteristic::PROPERTY_NOTIFY);

pTxCharacteristic->addDescriptor(new BLE2902());


BLECharacteristic * pRxCharacteristic = pService->createCharacteristic(CHARACTERISTIC_UUID_RX,BLECharacteristic::PROPERTY_WRITE);

pRxCharacteristic->setCallbacks(new MyCallbacks());

// Запустить службу pService->start();

// Запустить службу

pServer->getAdvertising()->start(); Serial.println(Waiting a client connection to notify...);

}

void loop() {

if (deviceConnected) {

pTxCharacteristic->setValue(&txValue, 1); pTxCharacteristic->notify();

txValue++;

delay(10); // Bluetooth модуль перезагрузится, если будет отправлено много пакетов

}

// отключение

if (!deviceConnected && oldDeviceConnected) { 
delay(500); // подготовка к работе модуля Bluetooth 
pServer->startAdvertising(); // перезапуск 
Serial.println(start advertising);

oldDeviceConnected = deviceConnected;

}

// подключение

if (deviceConnected && !oldDeviceConnected) {

// программное подключение 
oldDeviceConnected = deviceConnected;

}

}

Для загрузки данного кода в модуль беспроводной связи необходимо подключить его к компьютеру через соответствующий miniUSB порт, переместить переключатель в положение «ESP», нажать в среде Arduino IDE кнопку загрузки и, по завершении процесса компиляции, когда в терминал будет выведена строка «Connecting….» (Рис. 3.4), зажать на 2-3 секунды обе кнопки, затем отпустить RST и потом отпустить Boot на модуле.

Рис. 3.4. Процесс начала загрузки кода в модуль беспроводной связи

После окончания загрузки в терминал будет выведена информация о том, что загрузка завершена, и появится сообщение о необходимости аппаратной перезагрузки (Рис. 3.5.).

Рис. 3.5. Успешное завершение загрузки кода

Для выполнения аппаратной перезагрузки необходимо отключить модуль от питания, после чего снова подключить его. После этого в окружении появится Bluetooth – устройство «My Robot» (Рис. 3.6). После выполнения настройки модуля беспроводной связи его можно переключить на работу с микроконтроллером. Для этого необходимо переместить переключатель на контроллере в положение MCU.

Рис. 3.6. Устройство «My Robot» в списке доступных Bluetoot-устройств

Для получения данных микроконтроллером, необходимо реализовать следующий скетч и загрузить его в Atmega2560 обычным способом – как в обычную плату Mega2560:

char c;

void setup() {

// Инициализируем взаимодействие по последовательному порту с ПК

Serial.begin (115200);

// Инициализируем взаимодействие по последовательному порту с модулем беспроводной связи 
Serial2.begin (115200);

}

void loop() { while(Serial2.available()){ 
c=Serial2.read(); 
Serial.print(c);

}

delay(10);

}

В результате, микроконтроллер будет ожидать поступление данных и, в случае их получения, будет выводить их в монитор порта (Рис. 3.7-3.8):

Рис. 3.7. Отправка данных из приложения

Рис. 3.8. Получение данных контроллером

Таким образом, в зависимости от поступающих данных, можно реализовывать ветвление программы – например, использовать входящие данные для управления мобильной платформой. В таком случае, на мобильном устройстве (телефоне или планшете) необходимо выбрать пульт управления, поддерживающий обмен данными по интерфейсу BLE, посмотреть какие данные он передает при нажатии на кнопки и отслеживать их в управляющей программе.

Использование Wi-Fi адаптера

Работа в качестве Wi-Fi клиента

В данном режиме работы модулю требуется наличие внешней точки доступа и развернутой Wi-Fi сети для подключения к ней. Рассмотрим пример, в котором будет произведено подключение к существующей сети Wi-Fi и организован обмен данными между контроллером DXL-IoT и каким-либо еще устройством, подключенным к этой же Wi-Fi сети. В качестве базового примера, на основе которого будет создаваться код будет использоваться стандартный пример, по умолчанию входящий в библиотеку для работы с модулем и содержащийся в меню «Файл» –> «Примеры» –> «Примеры для ESP32 Dev Module» –> «WiFi» –> «SimpleWiFiServer».

В первую очередь, необходимо подключить библиотеку «WiFi.h»:

#include <WiFi.h>

Затем задаем ssid и пароль сети, к которой будет выполняться подключение:

const char* ssid = “Wifi_for_Robots“; const char* password = “1234567890“;

после выполняется инициализация сервера на 80 порту:

WiFiServer server(80);

в основной функции setup() проинициализируем соединение по последовательному порту на скорости 115200 бод:

Serial.begin(115200);

делаем небольшую паузу

delay(10);

инициализируем выполнение подключения к WiFi сети с ранее заданными параметрами:

WiFi.begin(ssid, password);

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

while (WiFi.status() != WL_CONNECTED) { 

delay(500);
Serial.print(.);

}

при успешном соединении в последовательный интерфейс выводится информация об успешном соединении и выводится присвоенный модулю IP адрес:

Serial.println(““); 
Serial.println(WiFi connected.); 
Serial.println(IP address: ); 
Serial.println(WiFi.localIP());

после инициализируем сервер:

server.begin();

Теперь модуль готов к обмену данными. В функции void loop() добавляем ожидание подключения клиента. И в случае подключившегося клиента, выполняем чтение данных, поступающих от него. В случае, если клиент отключается, выводится соответствующее сообщение:

int value = 0; void loop(){


WiFiClient client = server.available(); 
if (client) {
Serial.println("New Client."); 
String currentLine = "";

while (client.connected()) { while (client.available()) {
В результате, получившийся код будет выглядеть следующим образом:
#include <WiFi.h>

const char* ssid = "Wifi_for_Robots"; 
const char* password = "1234567890";

WiFiServer server(80); 
void setup()

{

Serial.begin(115200); 
delay(10);

WiFi.begin(ssid, password);

while (WiFi.status() != WL_CONNECTED) { 

delay(500);
Serial.print(".");

}

Serial.println(""); 
Serial.println("WiFi connected."); 
Serial.println("IP address: "); 
Serial.println(WiFi.localIP()); 
server.begin();

}

int value = 0; 
void loop(){

WiFiClient client = server.available();

if (client) { 
Serial.println("New Client."); 
String currentLine = ""; 
while (client.connected()) { 
    while (client.available()) { 
    char c;

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

}

void loop() { 
while(Serial2.available()){ 
c=Serial2.read(); 
Serial.print(c);

}

delay(1000); 
Serial2.println("2345");

}

Код для работы модуля в режиме Wi-Fi готов, и его можно загрузить стандартным способом, рассмотренным ранее.

В качестве примера получения данных в микроконтроллер загружаем такой же код, как тот, что мы использовали в предыдущем примере. Перед обменом данными с контроллером необходимо узнать, какой ему присваивается IP адрес в сети. Данный адрес может быть не постоянным, поэтому необходимо периодически его проверять. Для этого необходимо, загрузив программный код в модуль, открыть терминал и дождаться подключения его к сети – в случае успешного подключения будет выведен присвоенный модулю IP адрес (Рис. 3.9).

Рис. 3.9. Инициализация модуля в сети

Для обмена данными воспользуемся приложением TCP Client, где требуется произвести настройку сервера (на рисунке 3.10 это testsq) и подключаемся к нему.

Рис. 3.10. Подключение к TCP серверу и отправка ему данных

В случае успешного подключения, откроется окно с терминалом, в котором можно отправлять и получать данные от удаленного модуля. Отправив значение «178», оно будет выведено в монитор порта.

Для передачи данных со стороны контроллера вносим изменения в прошивку контроллера таким образом, чтобы данные передавались, например, каждую секунду:

char c;

void setup() {

// Инициализируем взаимодействие по последовательному порту с ПК

Serial.begin (115200);

// Инициализируем взаимодействие по последовательному порту с модулем беспроводной связи 
Serial2.begin (115200);

}

void loop() { 
while(Serial2.available()){ 
c=Serial2.read(); 
Serial.print(c);

}

delay(1000); Serial2.println(2345);

}

Загрузив данный код в контроллер и подключившись в сеть в окне терминала TCP Client отобразятся передаваемые данные (Рис. 3.11).

Рис. 3.11. Прием данных со стороны контроллера

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

Работа в качестве Wi-Fi точки доступа

При необходимости, модуль беспроводной связи может сам стать источником Wi-Fi сети. Для этого необходимо внести следующие изменения в функцию setup() написанной ранее программы, после которых она должна принять следующий вид:

#include <WiFi.h>


const char* ssid = "Wifi_for_Robots"; 
const char* password = "1234567890"; 
WiFiServer server(80);

void setup()

{

Serial.begin(115200); 
delay(10);

WiFi.begin(ssid, password);

while (WiFi.status() != WL_CONNECTED) {
delay(500);

Serial.print(".");

}

Serial.println(""); 
Serial.println("WiFi connected.");
Serial.println("IP address: "); 
Serial.println(WiFi.localIP()); 

server.begin();

}

int value = 0; 
void loop() {

WiFiClient client = server.available(); 
if (client) {

Serial.println("New Client."); 
String currentLine = ""; 
while (client.connected()) { 
while (client.available()) {

char c = client.read(); 
Serial.write(c);

}

}

client.stop();

Serial.println("Client Disconnected.");

}

}
Загрузим измененный код в программу, убедимся в создании новой сети, проверив IP адрес способом, рассмотренным ранее.

Используя вышеописанный метод, была создана новая Wi-Fi сеть «Wi-Fi», к которой можно подключиться и выполнить все те же задачи, которые были рассмотрены ранее (Рис. 3.12)

Рис. 3.12. Работа в качестве точки доступа

Модуль беспроводной связи, встроенный в программируемый контроллер DXL-IoT может быть использован и как клиент Wi-Fi сети, и как ее источник, что дает широкие возможности при организации обмена данными между программируемым контроллером и произвольным удаленным устройством.

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

Для подключения универсального вычислительного контроллера DXL-IoT к сети проводным образом существует плата расширения с адаптером Ethernet, позволяющая подключить контроллер в сеть на скорости до 100 Мб/сек. Помимо этого, на модуле находится слот для установки microSD карты памяти, которую можно использовать как хранилище файлов. Для программной реализации Ethernet-соединения в среде разработки Arduino IDE существует набор готовых примеров во вкладке

«Файл» –> «Примеры» –> «Ethernet».

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

#include <SPI.h> // Подключаем библиотеку SPI

#include <Ethernet.h> // Подключаем библиотеку Ethernet

byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED}; // Вводим MAC адрес 
IPAddress ip(169, 254, 21, 70); // Указываем статический IP 
EthernetServer server(80); // Инициализация библиотеки Ethernet “server“ на порту 80

Затем задаем скорость передачи данных и ждем, пока не откроется монитор порта, после чего запускаем сервер:

void setup()

{

Serial.begin(115200); // Задаем скорость передачи данных 
while (!Serial) {
;
}

Ethernet.begin(mac, ip); // Запускаем сервер 
server.begin();

Serial.print(server is at ); Serial.println(Ethernet.localIP());

}

В функции loop() ожидаем подключение клиента (открытие вкладки браузера с указанием в адресной строки IP:port). Начинается передача данных, которые в данном примере представлены значениями с аналоговых портов:

void loop()

{EthernetClient client = server.available();

// Принимаем данные, посылаемые клиентом 
if (client) {

Serial.println(new client); 
boolean currentLineIsBlank = true; 
while (client.connected()) {

if (client.available()) { 
char c = client.read(); 
Serial.write(c);

if (c == ‘\n && currentLineIsBlank) { 
client.println(HTTP/1.1 200 OK); 
client.println(Content-Type: text/html);
client.println(Connection: close); // закрыть соединение 
client.println(Refresh: 5v); // обновлять страницу каждые 5 сек 
client.println();
client.println(<!DOCTYPE HTML>); client.println(<html>);

for (int analogChannel = 10; analogChannel < 16; analogChannel++)

{int sensorReading = analogRead(analogChannel); 
client.print(analog input );
client.print(analogChannel); 
client.print( is ); 
client.print(sensorReading); 
client.println(<br />);}
client.println(</html>); 
break;}

if (c == ‘\n) { 
currentLineIsBlank = true;
} else if (c != ‘\r) { 
currentLineIsBlank = false;
}

}

}

delay(1); 
client.stop();

Serial.println(client disconnected);

}

}

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

Использование силовой платы расширения

Силовая плата расширения для программируемого контроллера DXL-IoT предназначена для управления силовой нагрузкой. Такой нагрузкой могут выступать популярные двигатели постоянного тока, светодиодные ленты, пневмонасосы и гидронасосы. Данная плата может одновременно управлять двигателями постоянного тока (задавая направление и скорость вращения), в количестве до четырех штук, а также коммутировать питание до четырех устройств.

Здесь необходимо понимать, что чем больше устройств будет подключено к плате расширения, тем больше линий будет зарезервировано для них, и тем меньше линий на штыревых разъемах будут доступны пользователю для работы.

Подключение двигателей постоянного тока

Внешний вид типовой схемы подключения двух двигателей постоянного тока к плате показан на рисунке 3.13:

Рис. 3.13. Подключение двигателей постоянного тока к силовой плате расширения

В данном примере контроллер питается от источника 12В. И джамперы на плате расширения стоят таким образом, что эти 12В пойдут на питание двигателей.

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

#define M1_dir 2

#define M1_Speed 3

#define M2_dir 8

#define M2_Speed 9

volatile int val = 50; 
void setup() {

pinMode(M1_dir, OUTPUT); 
pinMode(M1_Speed, OUTPUT); 
pinMode(M2_dir, OUTPUT); 
pinMode(M2_Speed, OUTPUT);

digitalWrite(M1_dir, LOW); 
analogWrite(M1_Speed, val);

digitalWrite(M2_dir, LOW); 
analogWrite(M2_Speed, val);

delay(2000);

digitalWrite(M1_dir, HIGH); 
analogWrite(M1_Speed, val);

digitalWrite(M2_dir, HIGH); 
analogWrite(M2_Speed, val); 
delay(2000);

val=0; 
analogWrite(M1_Speed, val); 
analogWrite(M2_Speed, val);

}

void loop() {

}

Важно! В последних версиях Arduino IDE существует баг, не позволяющий корректно отрабатывать функции analogWrite. Для того, чтобы избежать этой ошибки, необходимо значение скорости писать не напрямую, как аргумент функции, а передавать в функцию через переменную int c квалификатором volatile.

Подключение нескольких устройств к силовой плате

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

Для подачи силового питания на клемники 1-4 можно воспользоваться следующим примером:

#define K1 10
#define K2 11
#define K3 12
#define K4 13

void setup() { 
pinMode(K1, OUTPUT); 
pinMode(K2, OUTPUT); 
pinMode(K3, OUTPUT); 
pinMode(K4, OUTPUT);
digitalWrite(K1, HIGH); delay(2000); digitalWrite(K1, LOW);
digitalWrite(K2, HIGH); delay(2000); digitalWrite(K2, LOW);
digitalWrite(K3, HIGH); delay(2000); digitalWrite(K3, LOW);
digitalWrite(K4, HIGH); delay(2000); digitalWrite(K4, LOW);

}

void loop() {

}

Здесь последовательно активируются клеммники с 1 по 4, и через две секунды деактивируются. Индикационный светодиод на плате показывает состояние клеммника.

Использование шагового двигателя

Контроллер DXL-IoT позволяет управлять двигателями несколькими способами. Например, через драйвер для двигателя или через силовую плату расширения. Оба способа применимы для подключения шагового двигателя, но и имеют свои особенности. Вращение вала осуществляется поочередной последовательной подачей тока на обмотки мотора. Благодаря этому, вал способен поворачиваться на определённый угол и фиксироваться в новом положении.

Шаговые двигатели применимы в механических системах точного позиционирования – ЧПУ станках, 3d-принтерах, автоматизированных манипуляторах и других промышленнх и робототехнических комплексах. Шаговые двигатели преобразуют электрические импульсы в перемещение вала на определенный угол. Шагом называется минимально возможный угол перемещения двигателя.

Подключение шагового двигателя через драйвер

Рассмотрим пример подключения шагового двигателя через драйвер. Шаговый двигатель подключается к драйверу в разъем. От драйвера 1-4 ПИНы ведут к DXL-IoT в 8-11 цифровые ПИНы платы, соответственно. Для облегчения управления шаговыми двигателями были реализованы библиотеки, но вращение валом двигателя возможно и без них.

Обратите внимание, в примере кода вращение двигателя осуществляется по часовой стрелке. В случае, если необходимо изменить направление вращения двигателя в другую сторону, то требуется изменить фазы. Все значения «LOW» изменяются на «HIGH» и наоборот, все значения «HIGH» на изменяются на «LOW». Для регулировки скорости вращения двигателя, изменяется величина переменной «dl» это задержка между переключениями обмоток. Чем больше это число тем медленнее крутится шаговый двигатель.

Внешний вид схемы подключения шагового двигателя через драйвер показан на рисунке 3.14:

Рис. 3.14. (а) Подключение шагового двигателя через драйвер

Рис. 3.14. (б) Подключение шагового двигателя через драйвер

Пример кода:

#define in1 8 //объявление ПИНов драйвера 
#define in2 9
#define in3 10
#define in4 11

int dl = 5; // время задержки между переключением 
void setup() {

pinMode(in1, OUTPUT); 
pinMode(in2, OUTPUT); 
pinMode(in3, OUTPUT); 
pinMode(in4, OUTPUT);

}

void loop() {

//переключение обмоток 
digitalWrite(in1, HIGH); //первая фаза 
digitalWrite(in2, LOW); 
digitalWrite(in3, LOW); 
digitalWrite(in4, HIGH);
delay(dl);
digitalWrite(in1, HIGH); //вторая фаза 
digitalWrite(in2, HIGH); 
digitalWrite(in3, LOW); 
digitalWrite(in4, LOW);
delay(dl);
digitalWrite(in1, LOW); //третья фаза 
digitalWrite(in2, HIGH); 
digitalWrite(in3, HIGH); 
digitalWrite(in4, LOW);
delay(dl);
digitalWrite(in1, LOW); //четвертая фаза 
digitalWrite(in2, LOW);
digitalWrite(in3, HIGH); 
digitalWrite(in4, HIGH); 
delay(dl);

}

Подключение шагового двигателя через силовую плату расширения

Подключение шагового двигателя к плате DXL-IoT через силовую плату происходит путем закрепления 4 ПИНов двигателя к клеммам на плате расширения. Каждый полумост используется в качестве входного напряжения для обмоток шагового двигателя. Принцип их переключения тот же, что в примере с использованием драйвера.

Обратите внимание, что для корректной работы силовой платы необходимо подключение внешнего дополнительного питания. Для обеспечения необходимым питанием рекомендуется использовать преобразователь DXL-PWR или SMPS2Dynamixel.

Внешний вид схемы подключения шагового двигателя через силовую плату расширения показан на рисунке 3.15:

Рис. 3.15. (а) Подключение шагового двигателя через силовую плату расширения

Рис. 3.15. (б) Подключение шагового двигателя через силовую плату расширения

Пример кода:

#define M1_dir 2 //объявление ПИНов клемм 
#define M1_Speed 3
#define M2_dir 4
#define M2_Speed 5
#define M3_dir 6
#define M3_Speed 7
#define M4_dir 8
#define M4_Speed 9

int dl = 10; //время задержки между переключением volatile 
int val = 50;

void setup() {

pinMode(M1_dir, OUTPUT); //настройка ПИНов на выход 
pinMode(M1_Speed, OUTPUT);

pinMode(M2_dir, OUTPUT); 
pinMode(M2_Speed, OUTPUT); 
pinMode(M3_dir, OUTPUT); 
pinMode(M3_Speed, OUTPUT); 
pinMode(M4_dir, OUTPUT); 
pinMode(M4_Speed, OUTPUT);

}

void loop() {

digitalWrite(M1_dir, HIGH); //первая фаза 
analogWrite(M1_Speed, 0); 
digitalWrite(M2_dir, LOW); 
analogWrite(M2_Speed, val); 
digitalWrite(M3_dir, LOW); 
analogWrite(M3_Speed, val); 
digitalWrite(M4_dir, HIGH); 
analogWrite(M4_Speed, 0);

delay(dl);

digitalWrite(M1_dir, HIGH); //вторая фаза 
analogWrite(M1_Speed, 0); 
digitalWrite(M2_dir, HIGH); 
analogWrite(M2_Speed, 0); 
digitalWrite(M3_dir, LOW);
analogWrite(M3_Speed, val); 
digitalWrite(M4_dir, LOW);
analogWrite(M4_Speed, val);

delay(dl);

digitalWrite(M1_dir, LOW); //третья фаза 
analogWrite(M1_Speed, val); 
digitalWrite(M2_dir, HIGH); 
analogWrite(M2_Speed, 0); 
digitalWrite(M3_dir, HIGH); 
analogWrite(M3_Speed, 0); 
digitalWrite(M4_dir, LOW); 
analogWrite(M4_Speed, val);

delay(dl);

digitalWrite(M1_dir, LOW); //четвертая фаза 
analogWrite(M1_Speed, val); 
digitalWrite(M2_dir, LOW);
analogWrite(M2_Speed, val); 
digitalWrite(M3_dir, HIGH); 
analogWrite(M3_Speed, 0); 
digitalWrite(M4_dir, HIGH); 
analogWrite(M4_Speed, 0);

delay(dl);

}

Управление Dynamixel-совместимыми устройствами

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

Одной из особенностей программируемого контроллера DXL-IoT является возможность подключения и управления сервоприводами DYNAMIXEL. Используемые в образовательных наборах ООО «Прикладная робототехника ПРО» сервоприводы, работающие по протоколу Dynamixel, являются уменьшенной копией промышленных приводов и обладают всеми свойствами, характерными для промышленных сервоприводов. В первую очередь, это выражено в наличии у сервопривода различных обратных связей. Помимо обратных связей, которые используются в сервоприводах для самостоятельного контроля своего состояния, при необходимости, можно получать данные о работе сервопривода на внешний управляющий контроллер. Таким образом, возможно узнавать такую информацию, как: с какой скоростью в данный момент вращается сервопривод, в каком положении он находится, какую нагрузку испытывает, какая температура внутри корпуса сервопривода и т.д. Эту информацию можно использовать в работе управляющей программы для максимально точного управления исполнительными системами робота. Обмен данными с сервоприводами осуществляется в соответствии с протоколом Dynamixel путем обмена пакетами данных. Пакеты данных в данном случае бывают двух типов:

  • Пакет Инструкций - пакет данных, содержащий команды управления сервоприводами, посылаемые внешним управляющим контроллером.
  • Пакет Состояний - пакет данных, содержащий ответы от сервопривода, посылаемые внешнему управляющему контроллеру.

Для программной реализации системы управления понадобится библиотека DxlMaster или DxlMaster2, рассмотренные ранее. В качестве примера рассмотрим схему подключения и процесс управления сервоприводами AR-S430-01, XL431-T250-T и AX-12A.

Поскольку сервопривод является устройством, которое потребляет достаточное количество тока и рекомендуется его питать напряжением 12В, то необходимо использовать внешний источник питания. Схема подключения устройств выглядит так: внешний источник питания подключается к плате распределения питания (DXL-PWR или SMPS2Dynamixel), и уже к ней подключается контроллер DXL-IoT и сервопривод (Рис. 3.16)

Таким образом, питание 12В раздастся на все устройства, подключенные в цепь от платы распределения питания. При этом, программируемый контроллер также можно подключить к ПК с помощью USB кабеля. Если требуется подключение дополнительных устройств, то их можно подключать как к сервоприводу, так и к контроллеру в свободные 3х пиновые порты.

Для управления сервоприводом реализуем следующий скетч на основе примера joint_mode из раздела «Файл» -> «Примеры» -> «Примеры из пользовательских библиотек» -> «DxlMaster» или «DxlMaster2» ->

«joint_mode»:

Рис. 3.16. Пример подключения сервопривода к контроллеру DXL-IoT

Пример программы управления сервоприводами по протоколу Dynamixel 1.0:

#include «DxlMaster.h»

// задаем id сервопривода 
const uint8_t id = 4;

// скорость, от 0 до 1023 
int16_t speed = 512;

// скорость передачи данных

const long unsigned int baudrate = 1000000;

DynamixelMotor motor(id);


void setup()

{

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

// проверяем, можем ли мы связаться с двигателем

// если нет, то останавливаем 
uint8_t status = motor.init(); 
if(status != DYN_STATUS_OK)

{

while(1);

}

motor.enableTorque();

// установлен в режим соединения с диапазоном углов 180°

// смотреть информацию для расчета значений углов 
motor.jointMode(204, 820);

motor.speed(speed);

}

void loop()

{

// перейти в среднее положение 
motor.goalPosition(512); 
delay(500);

// двигаться на 45° против часовой стрелки 
motor.goalPosition(666);

delay(500);

// перейти в среднее положение 
motor.goalPosition(512); 
delay(500);

// двигаться на 45° по часовой стрелке 
motor.goalPosition(358);

delay(500);

}

Пример рабочего кода для сервоприводов, производства ООО «Прикладная робототехника ПРО», AR-S430-01 и DYNAMIXEL XL431-T250-T, управляемые через протокол Dynamixel 2.0:

#include «DxlMaster2.h»

// ID сервопривода 
const uint8_t id = 4;

// скорость, от 0 до 1023

int16_t speed = 512;

const float DXL_PROTOCOL_VERSION = 2.0;

// скорость соединения

const long unsigned int baudrate = 1000000; 
DynamixelMotor motor(id);

void setup()

{

DxlMaster.begin(baudrate); 
motor.protocolVersion(DXL_PROTOCOL_VERSION); 
delay(100);

// ожидание статуса сервопривода 
uint8_t status = motor.init();

if(status != DYN_STATUS_OK)

{

while(1);

}

motor.enableTorque(); 
motor.write(11, (uint8_t)3);

// установка режима joint с углом поворота 300°

// see doc to compute angle values

//
motor.jointMode(0, 1024);
 motor.write(52, (uint32_t)0); 
 motor.write(48, (uint32_t)2); 
 motor.speed(speed);

}

void loop()

{

// установка средней позиции 
motor.write(116, (uint32_t)(512*16)); 
delay(500);

// поворот на 45° против часовой стрелки 
motor.write(116, (uint32_t)(666*16)); 
delay(500);

// установкка средней позиции 
motor.write(116, (uint32_t)(512*16)); 
delay(500);

// поворот на 45° по часовой стрелке 
motor.write(116, (uint32_t)(358*16)); 
delay(500);

}

Пример рабочего кода для сервопривода DYNAMIXEL XL430-W250-T по протоколу Dynamixel 2.0:

#include «DxlMaster2.h»

// ID сервопривода 
const uint8_t id = 4;

// скорость, от 0 до 1023 
int16_t speed = 512;


const float DXL_PROTOCOL_VERSION = 2.0;


// скорость соединения

const long unsigned int baudrate = 1000000; 
DynamixelMotor motor(id);

void setup()

{

DxlMaster.begin(baudrate); 
motor.protocolVersion(DXL_PROTOCOL_VERSION); 
delay(100);

// ожидание статуса сервопривода 
uint8_t status = motor.init();

if(status != DYN_STATUS_OK)

{

while(1);

}

motor.enableTorque(); 
motor.write(11, (uint8_t)3);

// установка режима joint с углом поворота 300°

// see doc to compute angle values

//motor.jointMode(0, 1024); 
motor.write(52, (uint32_t)0); 
motor.write(48, (uint32_t)2); 
motor.speed(speed);

}

void loop()

{

// установка средней позиции 
motor.write(116, (uint32_t)(512)); 
delay(500);

// поворот на 45° против часовой стрелки 
motor.write(116, (uint32_t)(666)); 
delay(500);

// установкка средней позиции 
motor.write(116, (uint32_t)(512)); 
delay(500);

// поворот на 45° по часовой стрелке 
motor.write(116, (uint32_t)(358)); 
delay(500);

}

Управление Dynamixel-совместимыми периферийными модулями

Помимо сервоприводов, работающих по протоколу Dynamixel, с помощью универсального вычислительного контроллера DXL-IoT можно также управлять различными периферийными модулями, обладающими интерфейсом Dynamixel. Такие модули производятся, например, компанией «Прикладная робототехника ПРО» и о них более подробно рассказывается в соответствующем учебном пособии. В данном случае, в качестве примера, рассмотрим процесс управления модулем «Трехцветный светодиод» (Рис. 3.17).

#include "DxlMaster.h"

const unsigned long dynamixel_baudrate = 57600; 
const unsigned long serial_baudrate = 57600; 
const uint8_t id = 21;

DynamixelDevice rgb(id); 
void setup() {

DxlMaster.begin(dynamixel_baudrate); 
rgb.init(); 
Serial.begin(serial_baudrate);

}

void loop() { 
rgb.write(26, 255); 
delay(1000); 
rgb.write(27, 255); 
delay(1000); 
rgb.write(28, 255); 
delay(1000); 
rgb.write(26, 0); 
delay(1000); 
rgb.write(27, 0); 
delay(1000); 
rgb.write(28, 0); 
delay(1000);

}

Рис. 3.17. Подключение периферийного модуля к контроллеру DXL-IoT

Опрос Dynamixel-совместимого периферийного модуля

Опрос Dynamixel-совместимых периферийных модулей выполняется образом, аналогичным управлению такими модулями. У каждого модуля существуют регистры, в которых содержится значимая информация. И весь опрос сводится к тому, что этот регистр необходимо опросить. Рассмотрим пример опроса кнопки, при нажатии на которую будет включаться светодиод на модуле «Трехцветный светодиод» (Рис. 3.18):

Рис. 3.18. Одновременное подключение нескольких периферийных модулей к контроллеру DXL-IoT

#include «DxlMaster.h»

const unsigned long dynamixel_baudrate = 57600; 
const unsigned long serial_baudrate = 57600; 
const uint8_t idRGB = 21;

const uint8_t idBUT = 3; 
uint8_t a;

DynamixelDevice rgb(idRGB); 
DynamixelDevice but(idBUT);

void setup() { 
DxlMaster.begin(dynamixel_baudrate); 
rgb.init();

but.init(); 
Serial.begin(serial_baudrate);

}

void loop() { 
but.read(27, a); 
if (a==1){

rgb.write(26, 255);
}

else {
rgb.write(26, 0);

}

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

Конфигурирование контроллера, как Dynamixel-совместимое устройство

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

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

device model 1234

software version 12

dxl id 1

baud rate 1 (1000000 bps)

Данный пример расписан в «Файл» –> «Примеры» –> «Примеры из пользовательских библиотек» –> «DxlSlave» –> «allRegs»

Исходя из резервирования ячеек таблицы регистров под системные нужды, необходимо первым делом корректно сконфигурировать device model, software version, dxl id и baud rate:

DxlSlave.begin(1234, 12); 
Serial.begin(9600);


// инициализация EEPPOM 
if(!DxlSlave.get(IS_INITED_REG))

{

Serial.println(First boot); 
DxlSlave.set(IS_INITED_REG, 1);

DxlSlave.set_id(1); // the same as 
DxlSlave.set(3, 1); 
DxlSlave.set_baud(1); // the same as DxlSlave.set(4, 1);

// заполнение регистров 
values = addr 
for(int i = 6; i < 23; i++)

DxlSlave.set(i, i);

}

Причем, при первой конфигурации устройства необходимо убедиться, что параметры были корректно записаны в EEPROM (проверяется состояние флага в регистре IS_INITED_REG).

После инициирования основных регистров можно переходить к заполнению произвольных. Например, далее регистры до 200 заполняются значениями своих адресов, и при этом адресам выше 100 ставится режим «Только для чтения».

for(int i = 24; i < 200; i++)

{

DxlSlave.set(i, i);

// установка режима «только чтение» для выбранных регистров 
if(i >= 100)

DxlSlave.set_mode(i, 0);

}
В качестве примера показан способ заполнения регистров 16-битными значениями (одной командой заполняется сразу 2 регистра – текущий, к которому выполняется обращение, и следующий):

// записьрегистров в режиме 16 бит 
for(int i = 200; i < 255; i += 2)

{

DxlSlave.set16(i, 1000 + i);

// установка режима «только чтение» для выбранных регистров 
if(i >= 240)

DxlSlave.set_mode16(i, 0);

}

Модифицируем имеющийся пример allRegs таким образом, чтобы в один из регистров, например, 51, будут последовательно записываться значения от 0 до 255 с периодом в 0.5 с.

#define IS_INITED_REG 23 
uint32_t cur_time = millis(); 
void setup() {

// установочный код для запуска:

// инициализация dxl slave с model number 1234 и версией ПО 12 
DxlSlave.begin(1234, 12);

Serial.begin(9600);

// init EEPROM only on first boot 
if(!DxlSlave.get(IS_INITED_REG))

{

Serial.println(First boot); 
DxlSlave.set(IS_INITED_REG, 1);

DxlSlave.set_id(1); // такой же как 
DxlSlave.set(3, 1); 
DxlSlave.set_baud(1); // такой же как 
DxlSlave.set(4, 1);

// заполняем регистры значениями = addr 
for(int i = 6; i < 23; i++)

DxlSlave.set(i, i);

}

}

void loop() {

// заполнение RAM регистров 
for(int i = 0; i < 255; i++)

{

DxlSlave.set(51, i); 
delay(500);

}

}
Считаем это значение через контроллер CM-530. В результате, в терминал будут выведены значения, содержащиеся в регистре 51 (Рис. 3.19):

Рис. 3.19. Чтение данных с контроллера DXL-IoT

Управление мобильной платформой через Web-интерфейс

Пример управления мобильной платформой дифференциального типа, используя сетевое соединение и веб-интерфейс.

Рассмотрим, как работает контроллер DXL-IoT с интернет-архитектурой. В среде Arduino IDE имеется пример по развертыванию веб-сервера на базе контроллера Arduino. Для того, чтобы проверить работу нашего контроллера, необходимо подключить Ethernet кабель к роутеру и программируемому контроллеру, используя для этого Ethernet-модуль расширения. На контроллер потребуется загрузить скетч WebService из примера Ethernet. В этом скетче можно найти поле, где указывается IP адрес веб-страницы.

Важно! Чтобы ПК был подключен к той же сети, что и программируемый контроллер, а также, чтобы IP адрес компьютера и контроллера были в одной подсети. Подключив ПК к той же сети и перейдя по указанному IP адресу, можно увидеть веб-страницу, развернутую на контроллере.

В данную веб-страницу контроллер передает данные с аналоговых датчиков.

В качестве объекта управления будет рассматриваться мобильная платформа дифференциального типа с установленными на ней сервоприводами, работающими по протоколу Dynamixel. Управление данной платформой осуществляется с помощью веб-интерфейса, генерируемого на стороне контроллера и доступного в окне браузера любого устройства, подключенного к сети с платформой.

Инициализация системы:

#include <SPI.h> 
#include <Ethernet.h> 
#include <DxlMaster2.h> 
const uint8_t id = 1; 
const uint8_t id1 = 3; 
int16_t speed = 512;

const long unsigned int baudrate = 1000000; 
DynamixelMotor motor(id);

DynamixelMotor motor1(id1); 
boolean newInfo = 0;

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; 
IPAddress ip(192, 168, 1, 177);

EthernetServer server(80);

В этой части, как и во всех скетчах, объявляются используемые библиотеки: библиотека для работы с SPI, библиотека для работы с Ethernet, библиотека для работы с интерфейсом Dynamixel. Далее объявляются ID сервоприводов. В данном случае, ID равны 1 и 2. Также объявляется переменная, содержащая значение скорости speed. Задается скорость обмена сообщениями с сервоприводами, далее создаются экземпляры класса для работы с сервоприводами. Также необходимо создать логическую переменную, которая будет использоваться для отслеживания состояния обновилась ли информации или нет. Выдается mac адрес(обычно он тот же, менять его не надо). Далее указывается IP адрес, на котором будет находиться наш сервер (он должен находиться в одной подсети с ПК). Происходит инициализация сервера на заданном порту.

void setup() { 
Ethernet.begin (mac, ip); 
Serial.begin (1000000); 
server. begin(); 
DxlMaster.begin(baudrate); 
delay(100);

uint8_t status = motor.init(); 
motor.protocolVersion(2.0); 
motor.enableTorque(0);

motor.write(DYN2_ADDR_OPERATION_MODE, (uint8_t)1); 
motor.enableTorque(1);

motor1.protocolVersion(2.0); 
motor1.enableTorque(0);

motor1.write(DYN2_ADDR_OPERATION_MODE, (uint8_t)1); 
motor1.enableTorque(1);

}

В этой части скетча инициализируется и запускается сервер с указанными ранее параметрами. Так же происходит инициализация сервоприводов и задание режимов их работы.

Основная работа происходит в функции loop().

void loop() {

EthernetClient client = server.available(); 
if (client){

boolean currentLineIsBlank = true; 
while (client.connected()) {

if (client.available()) { 
char c = client.read(); 
if (newInfo && c ==  ){ 
newInfo = 0;

}

if (c == $){ 
newInfo = 1;

}

if (newInfo == 1){ 
Serial.println (c); 
if (c == 1){

Serial.println ("Вперед"); 
speed =100; 
motor.speed(speed); 
motor1.speed(-1 * speed);

}

if (c == 2){

Serial.println ("Разворот"); 
speed =-100; 
motor.speed(speed); 
motor1.speed(speed);

}

if (c == 3){

Serial.println ("Стоп"); 
speed =0; 
motor.speed(speed); 
motor1.speed(speed);

}

}

В начале происходит проверка наличия соединения между клиентом и сервером (принимаются ли данные от клиента). Если соединение есть, то программа входит в цикл while и не выходит из него, пока соединение не оборвется. Далее, если клиент активен, то происходит считывание передаваемой информации и записывание ее в переменную «с». Затем происходит обработка полученной информации и проведение анализа, насколько эта информация нова. Если переменная новой информации = 1 и «с», в которой записан запрос, равен пустой строке, то обнуляется переменная поступления новой информации. Если переменная «с», несущая отправленный запрос, содержит символ $, то пришла новая информация, ставится метка новой информации.

После проверяется содержание URL. В данном случае можно получить значения от 1 до 3. При получении значения «1», контроллер передает команду на вращение сервоприводами в разные стороны, но с одинаковой скоростью. За счет того, что сервопривода располагаются зеркально, мобильная платформа едет вперед. При получении значения

«2», мобильная платформа будет совершать поворот на месте, вокруг своей оси. При получении значения «3», мобильная платформа остановится.

if (c == ‘\n) { currentLineIsBlank = true;


}

else if (c != ‘\r) { currentLineIsBlank = false;

}

if (c == ‘\n && currentLineIsBlank) {

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

client. println ("HTTP/1.1 200 OK"); 
client. println ("Content-Type: text/html"); 
client. println ("Connection: close"); 
client. println ("Refresh: 5");

client. println ();

client. println ("<!DOCTYPE HTML>"); 
client. println ("<html>");

client. println ("<head>");

client. println ("<meta http-equiv=’Content-Type’ content=’text/html; charset=utf-8’/>");

client. print ("<title>IoT Web Server</title>"); 
client. println ("</head>");

client. println ("<body>");

client. print ("<H1>IoT Web Server</H1>");

client. print ("<a href=\"/$1\"><button>Прямо</button></a>"); 
client. print ("<a href=\"/$2\"><button>Разворот</button></a>"); 
client. print ("<a href=\"/$3\"><button>Стоп</button></a>"); 
client. println ("</body>");

client. println ("</html>"); 
break;

}

}

}

Вывод страницы html выглядит следующим образом: Здесь:

В этой части программы: заголовочная информация веб-страницы. Обновление страницы каждые 5 секунд. Задаем тип документа (html), происходит открытие тега html и Head. Далее идет название страницы, заголовочная информация и заголовок на самой странице. А также создаются кнопки «Прямо», «Разворот» и «Стоп». После чего происходит закрытие тегов и выход.

delay (1); client. stop();

}

}

Реализована задержка по времени на получение новых данных с веб-страницы и закрытие соединения

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

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

#include <SPI.h> 
#include <Ethernet.h> 
#include <DxlMaster2.h> 
const uint8_t id = 1; 
const uint8_t id1 = 3; 
int16_t speed = 512;

const long unsigned int baudrate = 1000000; 
DynamixelMotor motor(id);

DynamixelMotor motor1(id1); 
boolean newInfo = 0;

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; 
IPAddress ip(192, 168, 1, 177);

EthernetServer server(80); 
void setup() { 
Ethernet.begin (mac, ip); 
Serial.begin (1000000); 
server. begin();

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


uint8_t status = motor.init(); 
motor.protocolVersion(2.0); 
motor.enableTorque(0);

motor.write(DYN2_ADDR_OPERATION_MODE, (uint8_t)1); 
motor.enableTorque(1);

motor1.protocolVersion(2.0); 
motor1.enableTorque(0);

motor1.write(DYN2_ADDR_OPERATION_MODE, (uint8_t)1); 
motor1.enableTorque(1);

}

void loop() {

EthernetClient client = server.available(); 
if (client){

boolean currentLineIsBlank = true; 
while (client.connected()) {

if (client.available()) { 
char c = client.read(); 
if (newInfo && c ==  ){ 
newInfo = 0;

}

if (c == $){ 
newInfo = 1;

}

if (newInfo == 1){ 
Serial.println (c); 
if (c == 1){

Serial.println ("Вперед"); 
speed =100; 
motor.speed(speed); 
motor1.speed(-1 * speed);

}

if (c == 2){

Serial.println ("Разворот"); 
speed =-100; 
motor.speed(speed); 
motor1.speed(speed);

}

if (c == 3){

Serial.println ("Стоп"); 
speed =0; 
motor.speed(speed); 
motor1.speed(speed);
}

}

if (c == ‘\n) { 
currentLineIsBlank = true;

}

else if (c != ‘\r) { 
currentLineIsBlank = false;

}

if (c == ‘\n && currentLineIsBlank) { 
client. println ("HTTP/1.1 200 OK"); 
client. println ("Content-Type: text/html"); 
client. println ("Connection: close"); 
client. println ("Refresh: 5");

client. println ();

client. println ("<!DOCTYPE HTML>"); 
client. println ("<html>");

client. println ("<head>");

client. println ("<meta http-equiv=’Content-Type’ content=’text/html ; charset=utf-8’/>");

client. print ("<title>IoT Web Server</title>"); 
client. println ("</head>");

client. println ("<body>");

client. print ("<H1>IoT Web Server</H1>");

client. print ("<a href=\"/$1\"><button>Прямо</button></a>"); 
client. print ("<a href=\"/$2\"><button>Разворот</button></a>"); 
client. print ("<a href=\"/$3\"><button>Стоп</button></a>");

client. println ("</body>"); 
client. println ("</html>"); 
break;

}

}

}

delay (1); 
client. stop();

}

}