Приложение
Все обновления прошивок смотрите на сайте ПР
Листинг 1 - Сигнальная лампа на стойке управления
cpp
#include <Ethernet.h> //библиотека для работы с Ethernet-модулем
#include <EthernetUdp.h> //библиотека для обмена данными по протоколу UDP
#include <EEPROM.h> //библиотека для работы с энергонезависимой памятью
#include <Wire.h> //библиотека для работы с интерфейсом I2C
//период отправки текущего состояния светодиодов
uint8_t time_delay = 100;
//локальный и удалённый UDP-порты
unsigned int localPort = 8888;
unsigned int UDPremotePort = 8888;
//скорость обмена данными через последовательный порт
unsigned long baud_serial = 115200;
//IP-адрес контроллера и IP-адрес принимающего устройства
IPAddress MyIP(192, 168, 2, 15);
IPAddress ServerIP(192, 168, 2, 3);
//пины, которыми управляются светодиоды
#define yellow_led 2
#define green_led 3
#define blue_led 4
#define red_led 5
//шаблон буферов для обмена данными
char packetBuffer[UDP_TX_PACKET_MAX_SIZE]; //буфер для хранения входящего UDP-пакета
char ReplyBuffer[50]; //буфер для формирования ответного сообщения
//объект для обмена данными по протоколу UDP
EthernetUDP Udp;
//MAC-адрес Ethernet-модуля
byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xAA, 0xDD};
//строки для хранения данных, полученных через последовательный порт
String inputString;
String inputIPString;
//таймеры для периодической отправки данных
unsigned long timer_print_serial;
unsigned long timer_print_udp;
unsigned long timer_print_dxl;
void setup() {
//инициализируем последовательный порт
Serial.begin(baud_serial);
//задаём начальные значения таймеров
timer_print_serial = millis();
timer_print_udp = millis();
//настраиваем пины светодиодов на работу в режиме выхода
pinMode(red_led, OUTPUT);
pinMode(blue_led, OUTPUT);
pinMode(green_led, OUTPUT);
pinMode(yellow_led, OUTPUT);
//проверяем работу светодиодов
checkLeds();
//формируем MAC-адрес на основе IP-адреса контроллера
mac[0] = MyIP[0];
mac[1] = MyIP[1];
mac[2] = MyIP[2];
mac[3] = MyIP[3];
mac[4] = MyIP[2];
mac[5] = MyIP[3];
//сообщаем о готовности контроллера к работе
Serial.println("Ready");
//запускаем Ethernet-соединение
Ethernet.begin(mac, MyIP);
//выводим сетевые настройки в последовательный порт
Serial.print("My IP: ");
Serial.println(MyIP);
Serial.print("Server IP: ");
Serial.println(ServerIP);
//запускаем UDP-соединение на заданном локальном порту
Udp.begin(localPort);
}
void loop() {
//если в последовательный порт поступили данные,
//посимвольно считываем их во входную строку
while (Serial.available()) {
char data = (char)Serial.read();
inputString += data;
delay(10);
}
//проверяем наличие входящего UDP-пакета
int packetSize = Udp.parsePacket();
if (packetSize) {
Serial.print("-> Udp: ");
//считываем полученный UDP-пакет в буфер
Udp.read(packetBuffer, UDP_TX_PACKET_MAX_SIZE);
//выводим полученные данные в последовательный порт
Serial.println(packetBuffer);
//устанавливаем состояния светодиодов
setValue(packetBuffer);
}
//обрабатываем данные, полученные через последовательный порт
if (inputString.length() > 0) {
Serial.print("-> ");
Serial.println(inputString);
//устанавливаем состояния светодиодов
setValue(inputString);
//очищаем строку после обработки команды
inputString = "";
}
//периодически отправляем текущее состояние светодиодов
if (millis() - timer_print_serial > time_delay) {
timer_print_serial = millis();
//выводим состояние светодиодов в последовательный порт
printSerialValue();
//отправляем состояние светодиодов по UDP
printUdpValue();
}
}
//функция отправки состояния светодиодов по UDP
void printUdpValue() {
//преобразуем состояния светодиодов в символьные массивы
char led_red[2];
String(digitalRead(red_led)).toCharArray(led_red, 2);
char led_blue[2];
String(digitalRead(blue_led)).toCharArray(led_blue, 2);
char led_green[2];
String(digitalRead(green_led)).toCharArray(led_green, 2);
char led_yellow[2];
String(digitalRead(yellow_led)).toCharArray(led_yellow, 2);
//начинаем формирование UDP-пакета
Udp.beginPacket(ServerIP, UDPremotePort);
//записываем в пакет состояния всех светодиодов
Udp.write("leds: ");
Udp.write("r: ");
Udp.write(led_red);
Udp.write(" b: ");
Udp.write(led_blue);
Udp.write(" g: ");
Udp.write(led_green);
Udp.write(" y: ");
Udp.write(led_yellow);
Udp.write("\n");
//завершаем формирование и отправляем UDP-пакет
Udp.endPacket();
}
//функция установки состояния светодиодов
void setValue(String str) {
//команда должна содержать не менее четырёх символов:
//красный, синий, зелёный и жёлтый светодиоды
if (str.length() >= 4) {
for (int i = 0; i < str.length(); i++) {
switch (i) {
//первый символ управляет красным светодиодом
case 0:
digitalWrite(red_led, String(str[i]).toInt());
break;
//второй символ управляет синим светодиодом
case 1:
digitalWrite(blue_led, String(str[i]).toInt());
break;
//третий символ управляет зелёным светодиодом
case 2:
digitalWrite(green_led, String(str[i]).toInt());
break;
//четвёртый символ управляет жёлтым светодиодом
case 3:
digitalWrite(yellow_led, String(str[i]).toInt());
break;
}
}
}
}
//функция вывода состояния светодиодов в последовательный порт
void printSerialValue() {
//считываем текущие состояния светодиодов
uint8_t led_red = digitalRead(red_led);
uint8_t led_blue = digitalRead(blue_led);
uint8_t led_green = digitalRead(green_led);
uint8_t led_yellow = digitalRead(yellow_led);
//формируем и выводим строку с состояниями светодиодов
Serial.println(
"leds: r: " + String(led_red) + " " +
"b: " + String(led_blue) + " " +
"g: " + String(led_green) + " " +
"y: " + String(led_yellow));
}
//функция проверки работоспособности светодиодов
void checkLeds() {
//задержка между переключениями светодиодов
uint8_t time_sleep = 100;
//последовательно включаем все светодиоды
digitalWrite(red_led, HIGH);
delay(time_sleep);
digitalWrite(blue_led, HIGH);
delay(time_sleep);
digitalWrite(green_led, HIGH);
delay(time_sleep);
digitalWrite(yellow_led, HIGH);
delay(time_sleep);
//последовательно выключаем все светодиоды
digitalWrite(red_led, LOW);
delay(time_sleep);
digitalWrite(blue_led, LOW);
delay(time_sleep);
digitalWrite(green_led, LOW);
delay(time_sleep);
digitalWrite(yellow_led, LOW);
delay(time_sleep);
//дважды одновременно включаем и выключаем все светодиоды
for (int i = 0; i < 2; i++) {
digitalWrite(red_led, HIGH);
digitalWrite(blue_led, HIGH);
digitalWrite(green_led, HIGH);
digitalWrite(yellow_led, HIGH);
delay(time_sleep);
digitalWrite(red_led, LOW);
digitalWrite(blue_led, LOW);
digitalWrite(green_led, LOW);
digitalWrite(yellow_led, LOW);
delay(time_sleep);
}
}Листинг 2 - Сигнальная лампа внутри ячейки
cpp
//скорость обмена данными, идентификатор устройства и время ожидания ответа
#define DXL_BAUD 1000000
#define DXL_ID 1
#define RTD_TIME 50
//пины управления направлением передачи данных
#define TX_PIN 42
#define RX_PIN 43
//пины, которыми управляются светодиоды
#define RED_PIN 2
#define GREEN_PIN 4
#define BLUE_PIN 5
#define ORANGE_PIN 3
//адреса регистров управления светодиодами
#define RED_DXL_REGISTER 26
#define GREEN_DXL_REGISTER 28
#define BLUE_DXL_REGISTER 29
#define ORANGE_DXL_REGISTER 27
//буфер для хранения принятого пакета Dynamixel
uint8_t dxl_buffer[8] = {0, 0, 0, 0, 0, 0, 0, 0};
//шаблон корректного ответного пакета Dynamixel
uint8_t dxl_good_response[6] = {0xFF, 0xFF, DXL_ID, 0x02, 0x00, 0x00};
//счётчик принятых байтов
uint8_t byte_counter = 0;
void setup() {
//инициализируем последовательный порт для отладки
Serial.begin(115200);
//инициализируем последовательный порт для обмена по протоколу Dynamixel
Serial1.begin(DXL_BAUD);
Serial1.setTimeout(RTD_TIME);
//настраиваем пины управления направлением передачи данных
pinMode(TX_PIN, OUTPUT);
pinMode(RX_PIN, OUTPUT);
//переводим интерфейс в режим приёма данных
setReadMode();
//настраиваем пины светодиодов на работу в режиме выхода
pinMode(RED_PIN, OUTPUT);
pinMode(GREEN_PIN, OUTPUT);
pinMode(BLUE_PIN, OUTPUT);
pinMode(ORANGE_PIN, OUTPUT);
}
void loop() {
//если в последовательный порт поступили данные,
//последовательно записываем их в буфер
while (Serial1.available()) {
dxl_buffer[byte_counter] = Serial1.read();
byte_counter++;
//после получения полного пакета обрабатываем его
if (byte_counter == 8) {
processDxl1Protocol();
//сбрасываем счётчик принятых байтов
byte_counter = 0;
//выводим содержимое буфера в последовательный порт
printBuffer();
//очищаем буфер после обработки
clearBuffer();
//считываем и удаляем оставшиеся данные из последовательного порта
Serial1.readString();
}
}
//если был получен неполный пакет, очищаем буфер и счётчик
if (byte_counter != 0) {
byte_counter = 0;
printBuffer();
clearBuffer();
Serial1.readString();
}
//небольшая задержка между циклами обработки
delay(20);
}
//функция обработки пакета протокола Dynamixel 1.0
void processDxl1Protocol() {
//проверяем заголовок пакета
if (!isItDynamixel()) return;
//проверяем идентификатор устройства
if (!isItMyDynamixelId()) return;
//проверяем длину пакета для записи одного байта
if (!isIt1ByteWrite()) return;
//проверяем инструкцию записи
if (!isItWriteInstruction()) return;
//изменяем состояние соответствующего светодиода
changeRegister();
}
//функция выбора светодиода по адресу регистра
void changeRegister() {
//управляем красным светодиодом
if (dxl_buffer[5] == RED_DXL_REGISTER) {
useLight(RED_PIN);
}
//управляем зелёным светодиодом
else if (dxl_buffer[5] == GREEN_DXL_REGISTER) {
useLight(GREEN_PIN);
}
//управляем синим светодиодом
else if (dxl_buffer[5] == BLUE_DXL_REGISTER) {
useLight(BLUE_PIN);
}
//управляем оранжевым светодиодом
else if (dxl_buffer[5] == ORANGE_DXL_REGISTER) {
useLight(ORANGE_PIN);
}
}
//функция включения или выключения выбранного светодиода
void useLight(int pin) {
//если значение регистра больше или равно единице,
//включаем светодиод
if (dxl_buffer[6] >= 1) {
digitalWrite(pin, HIGH);
}
//если значение регистра равно нулю,
//выключаем светодиод
else {
digitalWrite(pin, LOW);
}
}
//функция проверки инструкции записи
bool isItWriteInstruction() {
//код инструкции записи в протоколе Dynamixel
if (dxl_buffer[4] == 0x03) {
return true;
}
//выводим сообщение об ошибке инструкции
Serial.println("4");
return false;
}
//функция проверки длины пакета для записи одного байта
bool isIt1ByteWrite() {
//длина пакета с инструкцией записи одного байта
if (dxl_buffer[3] == 0x05) {
return true;
}
return false;
}
//функция проверки идентификатора устройства
bool isItMyDynamixelId() {
//проверяем, что пакет предназначен данному устройству
if (dxl_buffer[2] == DXL_ID) {
return true;
}
return false;
}
//функция проверки заголовка пакета Dynamixel
bool isItDynamixel() {
//пакет Dynamixel 1.0 начинается с двух байтов 0xFF
if (dxl_buffer[0] == 0xFF && dxl_buffer[1] == 0xFF) {
return true;
}
return false;
}
//функция переключения интерфейса в режим чтения
void setReadMode() {
digitalWrite(TX_PIN, LOW);
digitalWrite(RX_PIN, HIGH);
}
//функция переключения интерфейса в режим записи
void setWriteMode() {
digitalWrite(TX_PIN, HIGH);
digitalWrite(RX_PIN, LOW);
}
//функция вывода содержимого буфера в последовательный порт
void printBuffer() {
for (uint8_t i = 0; i < 8; i++) {
Serial.print(String(dxl_buffer[i]) + " ");
}
Serial.println();
}
//функция очистки буфера принятого пакета
void clearBuffer() {
for (uint8_t i = 0; i < 8; i++) {
dxl_buffer[i] = 0;
}
}Листинг 3 - Управление конвейером через DXL-IOT
cpp
#include "DxlMaster2.h" //библиотека для управления сервоприводами Dynamixel
//объект сервопривода Dynamixel с идентификатором 5
DynamixelMotor motor2(5);
//объект дополнительного сервопривода Dynamixel с идентификатором 3
//DynamixelMotor motor3(3);
void setup() {
//инициализируем интерфейс Dynamixel со скоростью 1 Мбит/с
DxlMaster.begin(1000000);
//инициализируем последовательный порт для вывода отладочной информации
Serial.begin(115200);
//инициализируем сервопривод с идентификатором 5
motor2.init();
//устанавливаем первую версию протокола Dynamixel
motor2.protocolVersion(1);
//переводим сервопривод в режим непрерывного вращения
motor2.wheelMode();
//инициализируем дополнительный сервопривод
//motor3.init();
//переводим дополнительный сервопривод в режим непрерывного вращения
//motor3.wheelMode();
//выводим сообщение об успешном запуске программы
Serial.println("started");
}
void loop() {
//задаём скорость и направление вращения сервопривода
motor2.speed(1300);
//задаём скорость дополнительного сервопривода
//motor3.speed(300);
}Листинг 4 - Основной код для управления всеми DXL - совместимыми периферийными устройствами через DXL-IOT (конвейер, сигнальная лампа внутри ячейки, SVcam, TrackingCam3)
cpp
#include "DxlMaster2.h"
#include "TrackingCamDxl.h"
// === Скорости и ID ===
const unsigned long serial_baudrate = 115200;
const unsigned long main_baudrate = 1000000;
const unsigned long svcam_baudrate = 115200;
const uint8_t lamp_id = 1;
const uint8_t cam_id = 2;
const uint8_t motor_id = 5;
const uint8_t svcam_id = 3;
// === Регистры лампы ===
const uint8_t red_dxl_register = 26;
const uint8_t green_dxl_register = 28;
const uint8_t blue_dxl_register = 29;
const uint8_t orange_dxl_register = 27;
const int16_t conveyor_speed = 1300;
// === Регистры SVCAM ===
#define DIST_ADDR 24
#define U_ADDR 25
#define V_ADDR 26
#define X_ADDR 28
#define Y_ADDR 29
#define Z_ADDR 30
#define COLOR_ADDR 31
// === Объекты устройств ===
DynamixelDevice lampa(lamp_id);
DynamixelMotor motor2(motor_id);
TrackingCamDxl trackingCam(cam_id);
DynamixelDevice svcam(svcam_id);
// === Таймеры ===
unsigned long previousCamMillis = 0;
unsigned long previousSvcamMillis = 0;
unsigned long lastCommandTime = 0;
// === Переменные SVCAM ===
uint32_t dist_mm = 0, u_pix = 0, v_pix = 0;
uint32_t x_raw = 0, y_raw = 0, z_raw = 0;
uint32_t color_id = 0;
void setup() {
Serial.begin(serial_baudrate);
DxlMaster.begin(main_baudrate);
motor2.init();
motor2.wheelMode();
delay(5000);
Serial.println("Ready. Send command: 0, 1, 2, 3, or 4.");
}
void applyCommand(char command) {
lastCommandTime = millis();
switch (command) {
case '0':
lampa.write(blue_dxl_register, 1); delay(100);
lampa.write(green_dxl_register, 0); delay(100);
lampa.write(red_dxl_register, 0); delay(100);
lampa.write(orange_dxl_register, 0); delay(100);
motor2.speed(0); delay(100);
Serial.println("0: blue, conveyor stopped");
break;
case '1':
lampa.write(green_dxl_register, 1); delay(100);
lampa.write(red_dxl_register, 0); delay(100);
lampa.write(orange_dxl_register, 0); delay(100);
lampa.write(blue_dxl_register, 0); delay(100);
motor2.speed(0); delay(100);
Serial.println("1: green, conveyor stopped");
break;
case '2':
lampa.write(green_dxl_register, 1); delay(100);
lampa.write(red_dxl_register, 0); delay(100);
lampa.write(orange_dxl_register, 0); delay(100);
lampa.write(blue_dxl_register, 0); delay(100);
motor2.speed(conveyor_speed); delay(100);
Serial.println("2: green, conveyor is working");
break;
case '3':
lampa.write(orange_dxl_register, 1); delay(100);
lampa.write(red_dxl_register, 0); delay(100);
lampa.write(green_dxl_register, 0); delay(100);
lampa.write(blue_dxl_register, 0); delay(100);
motor2.speed(0); delay(100);
Serial.println("3: orange, conveyor stopped");
break;
case '4':
lampa.write(red_dxl_register, 1); delay(100);
lampa.write(green_dxl_register, 0); delay(100);
lampa.write(orange_dxl_register, 0); delay(100);
lampa.write(blue_dxl_register, 0); delay(100);
motor2.speed(0); delay(100);
Serial.println("4: red, conveyor emergency stop");
break;
default:
Serial.println("Unknown command. Use 0, 1, 2, 3, or 4.");
break;
}
}
const char* blobColorName(uint8_t type) {
if (type == 0) return "Red";
if (type == 1) return "Blue";
if (type == 2) return "Green";
return nullptr;
}
void readCamera() {
uint8_t rawCount = trackingCam.readBlobs(5); // read data about first 5 blobs
Serial.println("=== TrackingCam ===");
Serial.println("All blobs");
Serial.println(rawCount); // print numbers of blobs
for(int i = 0; i < rawCount; i++) // print information about all blobs
{
const char* color = blobColorName(trackingCam.blob[i].type);
if (color == nullptr) continue;
Serial.println(color);
Serial.print(trackingCam.blob[i].cx, DEC);
Serial.print(" ");
Serial.print(trackingCam.blob[i].cy, DEC);
Serial.println(" ");
Serial.println(trackingCam.blob[i].area, DEC);
Serial.print(" ");
}
// wait for the next frame
long previousMillis = millis();
while(millis() - previousMillis < 33){
delay(1);
}
}
void readSvcam() {
// Переключаем шину на скорость SVCAM
DxlMaster.begin(svcam_baudrate);
delay(20);
// Читаем все регистры
svcam.read(DIST_ADDR, dist_mm);
svcam.read(U_ADDR, u_pix);
svcam.read(V_ADDR, v_pix);
svcam.read(X_ADDR, x_raw);
svcam.read(Y_ADDR, y_raw);
svcam.read(Z_ADDR, z_raw);
svcam.read(COLOR_ADDR, color_id);
// Возвращаем основную скорость
DxlMaster.begin(main_baudrate);
delay(50);
Serial.println("=== SVCAM ===");
if (dist_mm > 0) {
// Определяем цвет
String color_name = "NONE";
if (color_id == 1) color_name = "RED";
else if (color_id == 2) color_name = "GREEN";
else if (color_id == 3) color_name = "BLUE";
// Преобразуем X, Y, Z к знаковым
int32_t x_mm = (int32_t)x_raw;
int32_t y_mm = (int32_t)y_raw;
int32_t z_mm = (int32_t)z_raw;
// Отправляем данные в формате:
// 1
// COLOR U V DIST X Y Z
Serial.println("1");
Serial.print(color_name);
Serial.print(" ");
Serial.print(u_pix);
Serial.print(" ");
Serial.print(v_pix);
Serial.print(" ");
Serial.print(dist_mm);
Serial.print(" ");
Serial.print(x_mm);
Serial.print(" ");
Serial.print(y_mm);
Serial.print(" ");
Serial.println(z_mm);
} else {
// Если объект не найден
Serial.println("0");
}
delay(40); // ~25 Hz
}
void loop() {
if (Serial.available()) {
char command = (char)Serial.read();
if (command != '\n' && command != '\r' && command != ' ') {
applyCommand(command);
}
while (Serial.available()) Serial.read();
return;
}
unsigned long now = millis();
// Опрос TrackingCam каждые 500мс
if (now - previousCamMillis >= 500 && now - lastCommandTime >= 300) {
readCamera();
previousCamMillis = now;
}
// Опрос SVCAM каждые 500мс
if (now - previousSvcamMillis >= 500) {
readSvcam();
previousSvcamMillis = now;
}
}