#include #include #include #define HOVER_BAUD 115200 #define START_FRAME 0xABCD #define HoverSerial Serial typedef struct __attribute__((packed)) { uint16_t start; // 0xABCD int16_t steer; // поворот (-..+) int16_t speed; // скорость (-..+) uint16_t checksum;// XOR(start, steer, speed) } SerialCommand; static inline void sendCmd(int16_t steer, int16_t speed) { SerialCommand cmd; cmd.start = START_FRAME; cmd.steer = steer; cmd.speed = speed; cmd.checksum = (uint16_t)(cmd.start ^ cmd.steer ^ cmd.speed); HoverSerial.write((uint8_t*)&cmd, sizeof(cmd)); } // Ограничения по "железу" #define SPEED_MAX_HW 300 #define STEER_MAX_HW 300 // Командные значения #define SPEED_CMD 250 #define STEER_CMD 200 // Период отправки последней команды (для поддержания движения) #define CMD_PERIOD_MS 50 // Текущее состояние int16_t lastSteer = 0; int16_t lastSpeed = 0; unsigned long lastFrameTS = 0; const char* AP_SSID = "HoverESP32"; const char* AP_PASS = "12345678"; WebServer server(80); // Обновлённый красивый интерфейс const char HTML_PAGE[] PROGMEM = R"HTML( Hover Control

Hover Controller

Select direction and the robot will move until you press STOP
Drive state
Stopped
Last command: S
)HTML"; void handleRoot() { server.send(200, "text/html", HTML_PAGE); } void applyCommand(char c) { int16_t steer = 0; int16_t speed = 0; switch (c) { case 'F': speed = SPEED_CMD; break; case 'B': speed = -SPEED_CMD; break; case 'L': steer = -STEER_CMD; break; case 'R': steer = STEER_CMD; break; case 'S': default: steer = 0; speed = 0; break; } // Ограничения if (speed > SPEED_MAX_HW) speed = SPEED_MAX_HW; if (speed < -SPEED_MAX_HW) speed = -SPEED_MAX_HW; if (steer > STEER_MAX_HW) steer = STEER_MAX_HW; if (steer < -STEER_MAX_HW) steer = -STEER_MAX_HW; lastSteer = steer; lastSpeed = speed; sendCmd(lastSteer, lastSpeed); sendCmd(lastSpeed , lastSteer); } void handleCmd() { if (!server.hasArg("c")) { server.send(400, "text/plain", "Usage: /cmd?c=F|B|L|R|S"); return; } char c = server.arg("c")[0]; applyCommand(c); server.sendHeader("Access-Control-Allow-Origin", "*"); server.send(200, "text/plain", String("OK: ") + c); } #define LED_PIN 2 void setup() { pinMode(LED_PIN, OUTPUT); digitalWrite(LED_PIN, LOW); HoverSerial.begin(HOVER_BAUD); WiFi.mode(WIFI_AP); WiFi.softAP(AP_SSID, AP_PASS); IPAddress ip = WiFi.softAPIP(); server.on("/", handleRoot); server.on("/cmd", handleCmd); server.begin(); Serial.begin(115200); Serial.print("AP IP: "); Serial.println(ip); } void loop() { server.handleClient(); // Периодически дублируем последнюю команду, // чтобы робот продолжал движение, пока пользователь не нажмёт Stop unsigned long now = millis(); if (now - lastFrameTS >= CMD_PERIOD_MS) { sendCmd(lastSteer, lastSpeed); lastFrameTS = now; } // LED: мигает, если робот движется, выключен при Stop if (lastSpeed != 0 || lastSteer != 0) { // мигает digitalWrite(LED_PIN, (now / 300) % 2); } else { digitalWrite(LED_PIN, LOW); } }