Станция обслуживания дрона — это комплексный автоматизированный модуль, выполняющий функции замены аккумуляторов и обработки грузов (загрузка/выгрузка) в полностью автономном режиме. Управление ею реализовано с помощью микроконтроллера Arduino Uno, связанного с модулем ESP8266, который подключается к удалённому серверу по WebSocket и обменивается командами в формате JSON.
🔧 Архитектура управления станцией
Станция состоит из нескольких механических компонентов, управляемых шаговыми двигателями и отслеживаемых с помощью концевых датчиков. Управляющая логика разбита на два уровня:
1. Arduino Uno — логика управления станцией
Arduino отвечает за:
Поворотную платформу для грузов и аккумуляторов;
Подъём/опускание лифта;
Фиксацию/освобождение дрона;
Механизм загрузки/выгрузки груза;
Калибровку всех компонентов;
Отправку телеметрии на сервер;
Приём JSON-команд через UART от ESP8266 и их выполнение.
Каждый механизм снабжён шаговым двигателем, а его положение контролируется концевыми выключателями. Arduino обрабатывает команды вида:
{ "command": "move_lift", "value": 50, "operation_id": "abc123" }
...и, выполнив их, возвращает результат, например:
{ "type": "operation_result", "success": true, "message": "Lift moved", "operation_id": "abc123" }
Также отправляется телеметрия о текущем состоянии всех систем.
2. ESP8266 — шлюз между сервером и Arduino
ESP8266 подключается к Wi-Fi и устанавливает WebSocket-соединение с сервером. Его роль — посредник:
Принимает команды с сервера и передаёт их Arduino через UART.
Считывает ответы от Arduino и пересылает обратно на сервер.
Автоматически повторно подключается при потере связи.
Отвечает на
PING
-запросы сервера для поддержания heartbeat и передачи общего статуса работы.
3. Сервер управления
Сервер принимает WebSocket-соединения от устройств и предоставляет пользователю веб-интерфейс (временное решение для тестирования). Он:
Хранит список зарегистрированных устройств и пользователей.
Обеспечивает аутентификацию устройств.
Принимает и маршрутизирует команды, отправленные с веб-интерфейса.
Обрабатывает телеметрию и ошибки от устройств.
Показывает актуальное состояние системы через веб-интерфейс.
В дальнейшем планируется, что сервер управления будет лишён пользовательского интерфейса и будет действовать как промежуточное звено между системами высокого уровня и станцией, получая команды от внешнего управляющего сервера.
🔑 Принцип работы в реальном времени
Пользователь отправляет команду через веб-интерфейс сервера.
Сервер отправляет JSON-команду устройству
esp02
(станция).ESP8266 передаёт JSON по UART на Arduino Uno.
Arduino выполняет команду и возвращает JSON-результат.
ESP8266 передаёт результат обратно на сервер.
Сервер отображает результат пользователю.
📊 Пример телеметрии
{
"type": "telemetry",
"status": "работает",
"lift_position": 50,
"platform_position": 90,
"is_drone_locked": true,
"has_cargo": false,
"is_error": false,
"last_operation": "Drone locked",
"switches": [true, false, false, true, false, false, true, false]
}
🧩 Преимущества архитектуры
🔌 Модульность: Arduino Uno отделяет механику от сетевой логики.
🌐 Удалённое управление: весь обмен данными через WebSocket.
🔁 Устойчивость: повторные подключения, heartbeat, обработка ошибок.
🔄 Автоматизация: полностью автоматическое выполнение команд.
Код WIFI модуля
#include <ESP8266WiFi.h>
#include <WebSocketsClient.h>
#include <ArduinoJson.h>
const char* ssid = "ASUS";
const char* password = "123456789098";
WebSocketsClient webSocket;
const char* deviceName = "esp02";
const char* devicePassword = "5678";
const int LED_PIN = LED_BUILTIN;
unsigned long previousMillis = 0;
unsigned long lastConnectionAttempt = 0;
const long connectionInterval = 10000; // 10 секунд между попытками подключения
int ledState = LOW;
int connectionState = 0; // 0 - нет подключения, 1 - WiFi подключен, 2 - WebSocket подключен
void updateLed() {
unsigned long currentMillis = millis();
unsigned long interval;
switch(connectionState) {
case 0: // Нет подключения - быстрое мигание (500ms)
interval = 500;
break;
case 1: // WiFi подключен - медленное мигание (1000ms)
interval = 1000;
break;
case 2: // WebSocket подключен - постоянно включен
digitalWrite(LED_PIN, LOW);
return;
}
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis;
ledState = (ledState == LOW) ? HIGH : LOW;
digitalWrite(LED_PIN, ledState);
}
}
void sendToArduino(const String& message) {
Serial.println(message); // Просто перенаправляем сообщение на Arduino
}
void webSocketEvent(WStype_t type, uint8_t* payload, size_t length) {
switch (type) {
case WStype_CONNECTED:
//Serial.println("[WS] Connected");
connectionState = 2; // WebSocket подключен
updateLed();
// Отправляем информацию об устройстве при подключении
{
StaticJsonDocument<256> doc;
doc["type"] = "auth";
doc["name"] = deviceName;
doc["password"] = devicePassword;
String json;
serializeJson(doc, json);
webSocket.sendTXT(json);
}
break;
case WStype_DISCONNECTED:
//Serial.println("[WS] Disconnected");
connectionState = (WiFi.status() == WL_CONNECTED) ? 1 : 0;
break;
case WStype_TEXT:
//Serial.printf("[WS] Received: %s\n", payload);
// Обработка PING
if (String((char*)payload) == "PING") {
StaticJsonDocument<128> doc;
doc["type"] = "telemetry";
doc["status"] = "работает";
String json;
serializeJson(doc, json);
webSocket.sendTXT(json);
}
// Обработка команды reboot
else if (String((char*)payload) == "reboot") {
//Serial.println("Rebooting...");
ESP.restart();
}
// Все остальные сообщения просто перенаправляем на Arduino
else {
//Serial.printf("Сообщения отправлено на arduino\n");
sendToArduino(String((char*)payload));
}
break;
}
}
void checkUART() {
if (Serial.available() > 0) {
String received = Serial.readStringUntil('\n');
received.trim();
if (received.length() > 0) {
// Serial.printf("Received from Arduino: %s\n", received.c_str());
// Просто перенаправляем полученные данные на сервер
if (webSocket.isConnected()) {
webSocket.sendTXT(received);
}
}
}
}
void setup() {
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, HIGH);
Serial.begin(115200); // Инициализация UART для связи с другим Arduino
WiFi.begin(ssid, password);
// Начальное состояние - нет подключения
connectionState = 0;
}
void loop() {
unsigned long currentMillis = millis();
// Обновляем состояние подключения WiFi
if (WiFi.status() != WL_CONNECTED) {
connectionState = 0;
if (currentMillis - lastConnectionAttempt >= connectionInterval) {
//Serial.println("Connecting to WiFi...");
WiFi.begin(ssid, password);
lastConnectionAttempt = currentMillis;
}
}
else if (connectionState == 0) {
connectionState = 1;
//Serial.println("\nWiFi connected");
//Serial.print("IP address: ");
//Serial.println(WiFi.localIP());
}
updateLed();
webSocket.loop();
// Если WiFi подключен, но WebSocket еще нет - пытаемся подключиться
if (WiFi.status() == WL_CONNECTED && !webSocket.isConnected() &&
currentMillis - lastConnectionAttempt >= connectionInterval) {
connectionState = 1;
//Serial.println("Connecting to WebSocket server...");
webSocket.begin("178.66.182.97", 8765, "/");
webSocket.onEvent(webSocketEvent);
webSocket.setReconnectInterval(5000);
lastConnectionAttempt = currentMillis;
}
// Проверяем входящие сообщения от Arduino
checkUART();
delay(10); // Небольшая задержка для стабильности
}