Compare commits
24 Commits
b7d0ebfe38
...
802ae35166
| Author | SHA1 | Date | |
|---|---|---|---|
| 802ae35166 | |||
| 11a0457b64 | |||
| 2c1bb34ce9 | |||
| c61d2b8d27 | |||
| 4d83cf9de7 | |||
| 263570d45a | |||
| 5a07913067 | |||
| 00dcbe6afe | |||
| 42b0b8b81b | |||
| 197e1360d4 | |||
| 3d26efd6ea | |||
| 2c1e20ee0a | |||
| 895fdb264e | |||
| 759805c8c8 | |||
| 57c07b07db | |||
| 7e8c58828d | |||
| e2966de1cc | |||
| 84827216c1 | |||
| c06794458c | |||
| 8bbdef87ec | |||
| 880e52df5c | |||
| f43ba6a359 | |||
| 8a0149efca | |||
| e65c347deb |
10
src/config.h
10
src/config.h
@ -1,5 +1,15 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
// ===== Robot Info =====
|
||||||
|
#define ROBOT_NAME "OLEG-02"
|
||||||
|
#define FIRMWARE_NAME "ESP32 Robot"
|
||||||
|
#define FIRMWARE_VERSION "1.0.0"
|
||||||
|
|
||||||
|
// ===== Joystick (PS2 analog) =====
|
||||||
|
#define JOY_X_PIN 34 // VRX → ADC1_CH6
|
||||||
|
#define JOY_Y_PIN 35 // VRY → ADC1_CH7
|
||||||
|
#define JOY_SW_PIN 14 // SW → кнопка
|
||||||
|
|
||||||
// ===== Wi-Fi =====
|
// ===== Wi-Fi =====
|
||||||
#define WIFI_SSID "Capybara"
|
#define WIFI_SSID "Capybara"
|
||||||
#define WIFI_PASS "qq1234567890"
|
#define WIFI_PASS "qq1234567890"
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "actuators.h"
|
#include "actuators.h"
|
||||||
|
|
||||||
void actuatorsInit() {
|
bool actuatorsInit() {
|
||||||
pinMode(PIN_LB, OUTPUT);
|
pinMode(PIN_LB, OUTPUT);
|
||||||
pinMode(PIN_LF, OUTPUT);
|
pinMode(PIN_LF, OUTPUT);
|
||||||
pinMode(PIN_RB, OUTPUT);
|
pinMode(PIN_RB, OUTPUT);
|
||||||
@ -14,8 +14,15 @@ void actuatorsInit() {
|
|||||||
ledcAttachPin(L_PWM_PIN, PWM_CH_L);
|
ledcAttachPin(L_PWM_PIN, PWM_CH_L);
|
||||||
ledcAttachPin(R_PWM_PIN, PWM_CH_R);
|
ledcAttachPin(R_PWM_PIN, PWM_CH_R);
|
||||||
|
|
||||||
|
Serial.println("Actuators initialized");
|
||||||
|
Serial.printf(" L_PWM → GPIO%d\n", L_PWM_PIN);
|
||||||
|
Serial.printf(" R_PWM → GPIO%d\n", R_PWM_PIN);
|
||||||
|
Serial.printf(" L_DIR → GPIO%d, %d\n", PIN_LB, PIN_LF);
|
||||||
|
Serial.printf(" R_DIR → GPIO%d, %d\n", PIN_RB, PIN_RF);
|
||||||
|
|
||||||
actuatorsSetSpeed(150, 150);
|
actuatorsSetSpeed(150, 150);
|
||||||
actuatorsStop();
|
actuatorsStop();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void actuatorsStop() {
|
void actuatorsStop() {
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
void actuatorsInit();
|
bool actuatorsInit();
|
||||||
void actuatorsStop();
|
void actuatorsStop();
|
||||||
|
|
||||||
void actuatorsForward();
|
void actuatorsForward();
|
||||||
|
|||||||
318
src/core/display/body/body.cpp
Normal file
318
src/core/display/body/body.cpp
Normal file
@ -0,0 +1,318 @@
|
|||||||
|
#include <Arduino.h>
|
||||||
|
#include <Wire.h>
|
||||||
|
#include <Adafruit_GFX.h>
|
||||||
|
#include <Adafruit_SSD1306.h>
|
||||||
|
#include "body.h"
|
||||||
|
#include "../../../config.h"
|
||||||
|
#include "../../../server/web_server.h"
|
||||||
|
#include "../../../core/ultrasonic/ultrasonic.h"
|
||||||
|
#include "../../../core/robot_state/robot_state.h"
|
||||||
|
#include <WiFi.h>
|
||||||
|
|
||||||
|
// ===== OLED CONFIG =====
|
||||||
|
// ESP32 ESP-WROOM-32D с OLED 0.96" (SSD1306)
|
||||||
|
#define SCREEN_WIDTH 128
|
||||||
|
#define SCREEN_HEIGHT 64
|
||||||
|
#define OLED_RESET -1
|
||||||
|
#define SCREEN_ADDR 0x3C
|
||||||
|
|
||||||
|
// SDA/SCL пины для CH340 с OLED
|
||||||
|
#define SDA_PIN 21
|
||||||
|
#define SCL_PIN 22
|
||||||
|
|
||||||
|
static Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
|
||||||
|
|
||||||
|
// ===== Main Display =====
|
||||||
|
|
||||||
|
static int currentStage = 0;
|
||||||
|
static int totalStages = 5;
|
||||||
|
static bool systemReady = false;
|
||||||
|
static bool debugMode = false;
|
||||||
|
|
||||||
|
// Display update tracking
|
||||||
|
static unsigned long lastDisplayUpdate = 0;
|
||||||
|
static const unsigned long DISPLAY_UPDATE_INTERVAL = 500; // ms
|
||||||
|
|
||||||
|
static void drawHeader() {
|
||||||
|
// Заголовок (инверсия)
|
||||||
|
display.fillRect(0, 0, SCREEN_WIDTH, 16, SSD1306_WHITE);
|
||||||
|
display.setTextColor(SSD1306_BLACK);
|
||||||
|
display.setCursor(2, 4);
|
||||||
|
display.setTextSize(1);
|
||||||
|
display.print(ROBOT_NAME);
|
||||||
|
|
||||||
|
// Версия (правый угол) - только если система не готова
|
||||||
|
if (!systemReady && totalStages > 0) {
|
||||||
|
char stageStr[8];
|
||||||
|
snprintf(stageStr, sizeof(stageStr), "%d/%d", currentStage, totalStages);
|
||||||
|
int textWidth = strlen(stageStr) * 6; // ~6px на символ
|
||||||
|
display.setCursor(SCREEN_WIDTH - textWidth - 2, 4);
|
||||||
|
display.print(stageStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Версия ПО (правый угол) - только если система готова
|
||||||
|
if (systemReady) {
|
||||||
|
int textWidth = strlen(FIRMWARE_VERSION) * 6;
|
||||||
|
display.setCursor(SCREEN_WIDTH - textWidth - 2, 4);
|
||||||
|
display.print(FIRMWARE_VERSION);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void drawFooter() {
|
||||||
|
// Разделительная линия (опущена ниже)
|
||||||
|
// display.drawLine(0, SCREEN_HEIGHT - 10, SCREEN_WIDTH, SCREEN_HEIGHT - 10, SSD1306_WHITE);
|
||||||
|
|
||||||
|
display.setTextColor(SSD1306_WHITE);
|
||||||
|
display.setTextSize(1);
|
||||||
|
|
||||||
|
if (systemReady && debugMode) {
|
||||||
|
// Инвертированный фон для DEBUG режима
|
||||||
|
display.fillRect(0, SCREEN_HEIGHT - 9, SCREEN_WIDTH, 9, SSD1306_WHITE);
|
||||||
|
display.setTextColor(SSD1306_BLACK);
|
||||||
|
|
||||||
|
// Кнопка Menu (левый угол)
|
||||||
|
const char* menuBtn = "[MENU]";
|
||||||
|
display.setCursor(2, SCREEN_HEIGHT - 8);
|
||||||
|
display.print(menuBtn);
|
||||||
|
|
||||||
|
// Uptime (правый угол)
|
||||||
|
uint32_t uptime = webServerGetUptime();
|
||||||
|
char uptimeStr[12];
|
||||||
|
snprintf(uptimeStr, sizeof(uptimeStr), "UP=%lus", uptime);
|
||||||
|
int textWidth = strlen(uptimeStr) * 6;
|
||||||
|
display.setCursor(SCREEN_WIDTH - textWidth - 2, SCREEN_HEIGHT - 8);
|
||||||
|
display.print(uptimeStr);
|
||||||
|
} else if (systemReady) {
|
||||||
|
// Uptime (правый угол)
|
||||||
|
uint32_t uptime = webServerGetUptime();
|
||||||
|
char uptimeStr[12];
|
||||||
|
snprintf(uptimeStr, sizeof(uptimeStr), "UP=%lus", uptime);
|
||||||
|
int textWidth = strlen(uptimeStr) * 6;
|
||||||
|
display.setCursor(SCREEN_WIDTH - textWidth - 2, SCREEN_HEIGHT - 8);
|
||||||
|
display.print(uptimeStr);
|
||||||
|
} else {
|
||||||
|
// Версия (правый угол)
|
||||||
|
int textWidth = strlen(FIRMWARE_VERSION) * 6;
|
||||||
|
display.setCursor(SCREEN_WIDTH - textWidth - 2, SCREEN_HEIGHT - 8);
|
||||||
|
display.print(FIRMWARE_VERSION);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void oledSetStage(int current, int total) {
|
||||||
|
currentStage = current;
|
||||||
|
totalStages = total;
|
||||||
|
}
|
||||||
|
|
||||||
|
void oledSetSystemReady(bool ready, bool debug) {
|
||||||
|
systemReady = ready;
|
||||||
|
debugMode = debug;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool oledIsSystemReady() {
|
||||||
|
return systemReady;
|
||||||
|
}
|
||||||
|
|
||||||
|
void oledShowMain(const char* line1, const char* line2, const char* line3, const char* line4) {
|
||||||
|
display.clearDisplay();
|
||||||
|
|
||||||
|
// Заголовок
|
||||||
|
drawHeader();
|
||||||
|
|
||||||
|
// Основной контент (4 строки)
|
||||||
|
display.setTextColor(SSD1306_WHITE);
|
||||||
|
display.setCursor(0, 20);
|
||||||
|
if (line1) display.println(line1);
|
||||||
|
display.setCursor(0, 30);
|
||||||
|
if (line2) display.println(line2);
|
||||||
|
display.setCursor(0, 40);
|
||||||
|
if (line3) display.println(line3);
|
||||||
|
display.setCursor(0, 50);
|
||||||
|
if (line4) display.println(line4);
|
||||||
|
|
||||||
|
// Подвал
|
||||||
|
drawFooter();
|
||||||
|
|
||||||
|
display.display();
|
||||||
|
}
|
||||||
|
|
||||||
|
void oledInit() {
|
||||||
|
Wire.begin(SDA_PIN, SCL_PIN);
|
||||||
|
|
||||||
|
if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDR)) {
|
||||||
|
Serial.println("SSD1306 allocation failed");
|
||||||
|
for (;;);
|
||||||
|
}
|
||||||
|
|
||||||
|
display.clearDisplay();
|
||||||
|
display.setTextSize(1);
|
||||||
|
display.setTextColor(SSD1306_WHITE);
|
||||||
|
display.display();
|
||||||
|
|
||||||
|
oledShowBoot();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== Screens =====
|
||||||
|
|
||||||
|
void oledShowBoot() {
|
||||||
|
display.clearDisplay();
|
||||||
|
|
||||||
|
// Заголовок
|
||||||
|
drawHeader();
|
||||||
|
|
||||||
|
// Контент
|
||||||
|
display.setTextColor(SSD1306_WHITE);
|
||||||
|
display.setCursor(0, 25);
|
||||||
|
display.setTextSize(1);
|
||||||
|
display.println("Robot booting");
|
||||||
|
display.setCursor(0, 35);
|
||||||
|
display.println("Please wait...");
|
||||||
|
|
||||||
|
// Подвал
|
||||||
|
drawFooter();
|
||||||
|
|
||||||
|
display.display();
|
||||||
|
}
|
||||||
|
|
||||||
|
void oledShowWiFi(const char* ssid, int rssi) {
|
||||||
|
display.clearDisplay();
|
||||||
|
|
||||||
|
// Заголовок
|
||||||
|
drawHeader();
|
||||||
|
|
||||||
|
// Контент
|
||||||
|
display.setTextColor(SSD1306_WHITE);
|
||||||
|
display.setCursor(0, 25);
|
||||||
|
display.setTextSize(1);
|
||||||
|
display.print("SSID: ");
|
||||||
|
display.println(ssid);
|
||||||
|
display.setCursor(0, 35);
|
||||||
|
display.print("RSSI: ");
|
||||||
|
display.print(rssi);
|
||||||
|
display.println(" dBm");
|
||||||
|
|
||||||
|
// Подвал
|
||||||
|
drawFooter();
|
||||||
|
|
||||||
|
display.display();
|
||||||
|
}
|
||||||
|
|
||||||
|
void oledShowMode(const char* mode) {
|
||||||
|
display.clearDisplay();
|
||||||
|
|
||||||
|
// Заголовок
|
||||||
|
drawHeader();
|
||||||
|
|
||||||
|
// Контент
|
||||||
|
display.setTextColor(SSD1306_WHITE);
|
||||||
|
display.setCursor(0, 25);
|
||||||
|
display.setTextSize(1);
|
||||||
|
display.print("MODE: ");
|
||||||
|
display.println(mode);
|
||||||
|
display.setCursor(0, 35);
|
||||||
|
display.println("Ready");
|
||||||
|
|
||||||
|
// Подвал
|
||||||
|
drawFooter();
|
||||||
|
|
||||||
|
display.display();
|
||||||
|
}
|
||||||
|
|
||||||
|
void oledShowText(const char* line1, const char* line2) {
|
||||||
|
display.clearDisplay();
|
||||||
|
|
||||||
|
// Заголовок
|
||||||
|
drawHeader();
|
||||||
|
|
||||||
|
// Контент
|
||||||
|
display.setTextColor(SSD1306_WHITE);
|
||||||
|
display.setCursor(0, 25);
|
||||||
|
display.setTextSize(1);
|
||||||
|
display.println(line1);
|
||||||
|
display.setCursor(0, 35);
|
||||||
|
display.println(line2);
|
||||||
|
|
||||||
|
// Подвал
|
||||||
|
drawFooter();
|
||||||
|
|
||||||
|
display.display();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== Calibration =====
|
||||||
|
|
||||||
|
void oledShowCalibration() {
|
||||||
|
display.clearDisplay();
|
||||||
|
|
||||||
|
// Заголовок (верхняя строка)
|
||||||
|
display.fillRect(0, 0, SCREEN_WIDTH, 16, SSD1306_WHITE);
|
||||||
|
display.setTextColor(SSD1306_BLACK);
|
||||||
|
display.setCursor(2, 4);
|
||||||
|
display.setTextSize(1);
|
||||||
|
display.println("CALIBRATION");
|
||||||
|
|
||||||
|
// Основная область
|
||||||
|
display.setTextColor(SSD1306_WHITE);
|
||||||
|
display.setCursor(0, 16);
|
||||||
|
display.println("Line 1: y=16");
|
||||||
|
display.setCursor(0, 26);
|
||||||
|
display.println("Line 2: y=26");
|
||||||
|
display.setCursor(0, 36);
|
||||||
|
display.println("Line 3: y=36");
|
||||||
|
display.setCursor(0, 46);
|
||||||
|
display.println("Line 4: y=46");
|
||||||
|
display.setCursor(0, 56);
|
||||||
|
display.println("Line 5: y=56");
|
||||||
|
|
||||||
|
// Границы
|
||||||
|
display.drawRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, SSD1306_WHITE);
|
||||||
|
|
||||||
|
display.display();
|
||||||
|
}
|
||||||
|
|
||||||
|
void oledDrawMainMenu(const char* ip, const char* mode) {
|
||||||
|
display.clearDisplay();
|
||||||
|
|
||||||
|
// Заголовок
|
||||||
|
drawHeader();
|
||||||
|
|
||||||
|
// Основной контент (центрирован между шапкой и подвалом)
|
||||||
|
display.setTextColor(SSD1306_WHITE);
|
||||||
|
display.setTextSize(1);
|
||||||
|
|
||||||
|
// Строка 1: IP адрес (y=17)
|
||||||
|
display.setCursor(0, 17);
|
||||||
|
display.print("IP: ");
|
||||||
|
display.println(ip);
|
||||||
|
|
||||||
|
// Строка 2: Режим (y=27)
|
||||||
|
display.setCursor(0, 27);
|
||||||
|
display.print("Mode: ");
|
||||||
|
display.println(mode);
|
||||||
|
|
||||||
|
// Строка 3: Дистанция и RSSI (y=37)
|
||||||
|
display.setCursor(0, 37);
|
||||||
|
int dist = ultrasonicReadCm();
|
||||||
|
int rssi = WiFi.RSSI();
|
||||||
|
display.printf("DIST: %dcm RSSI: %d", dist, rssi);
|
||||||
|
|
||||||
|
// Строка 4: Скорости (y=47)
|
||||||
|
display.setCursor(0, 47);
|
||||||
|
display.printf("L: %d R: %d", robot.speedL, robot.speedR);
|
||||||
|
|
||||||
|
// Подвал
|
||||||
|
drawFooter();
|
||||||
|
|
||||||
|
display.display();
|
||||||
|
}
|
||||||
|
|
||||||
|
void oledUpdateDisplay() {
|
||||||
|
if (!systemReady) return;
|
||||||
|
|
||||||
|
unsigned long now = millis();
|
||||||
|
if (now - lastDisplayUpdate >= DISPLAY_UPDATE_INTERVAL) {
|
||||||
|
lastDisplayUpdate = now;
|
||||||
|
|
||||||
|
const char* ip = webServerGetIP();
|
||||||
|
const char* mode = webServerGetMode();
|
||||||
|
oledDrawMainMenu(ip, mode);
|
||||||
|
}
|
||||||
|
}
|
||||||
26
src/core/display/body/body.h
Normal file
26
src/core/display/body/body.h
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
void oledInit();
|
||||||
|
|
||||||
|
// базовые экраны
|
||||||
|
void oledShowBoot();
|
||||||
|
void oledShowWiFi(const char* ssid, int rssi);
|
||||||
|
void oledShowMode(const char* mode);
|
||||||
|
|
||||||
|
// универсально (2 строки)
|
||||||
|
void oledShowText(const char* line1, const char* line2);
|
||||||
|
|
||||||
|
// калибровка
|
||||||
|
void oledShowCalibration();
|
||||||
|
|
||||||
|
// основная отрисовка
|
||||||
|
void oledSetStage(int current, int total);
|
||||||
|
void oledShowMain(const char* line1, const char* line2, const char* line3, const char* line4);
|
||||||
|
|
||||||
|
// состояние системы
|
||||||
|
void oledSetSystemReady(bool ready, bool debugMode = false);
|
||||||
|
bool oledIsSystemReady();
|
||||||
|
|
||||||
|
// меню
|
||||||
|
void oledDrawMainMenu(const char* ip, const char* mode);
|
||||||
|
void oledUpdateDisplay();
|
||||||
109
src/core/joystick/joystick.cpp
Normal file
109
src/core/joystick/joystick.cpp
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
#include <Arduino.h>
|
||||||
|
#include "joystick.h"
|
||||||
|
#include "../../config.h"
|
||||||
|
|
||||||
|
// ===== ADC CONFIG =====
|
||||||
|
// ADC1: 12 бит (0-4095)
|
||||||
|
// Центр джойстика ≈ 2048 (но может отличаться)
|
||||||
|
|
||||||
|
#define ADC_CENTER_X 1870 // калибровка X (подбирается опытно)
|
||||||
|
#define ADC_CENTER_Y 1850 // калибровка Y (подбирается опытно)
|
||||||
|
#define ADC_DEADZONE 150 // мёртвая зона
|
||||||
|
#define ADC_MIN 0
|
||||||
|
#define ADC_MAX 4095
|
||||||
|
|
||||||
|
// Нормализованные значения
|
||||||
|
#define NORM_MIN -100
|
||||||
|
#define NORM_MAX 100
|
||||||
|
|
||||||
|
// Порог логирования (чтобы не шумело)
|
||||||
|
#define LOG_THRESHOLD 10 // минимальное изменение для лога
|
||||||
|
|
||||||
|
static int joyX = 0;
|
||||||
|
static int joyY = 0;
|
||||||
|
static bool swPressed = false;
|
||||||
|
static int lastLoggedXNorm = 0;
|
||||||
|
static int lastLoggedYNorm = 0;
|
||||||
|
static bool lastLoggedSw = false;
|
||||||
|
|
||||||
|
bool joyInit() {
|
||||||
|
pinMode(JOY_SW_PIN, INPUT_PULLUP);
|
||||||
|
|
||||||
|
// ADC1 каналы (пины 34, 35)
|
||||||
|
analogReadResolution(12); // 12 бит (0-4095)
|
||||||
|
analogSetAttenuation(ADC_11db); // 0-3.3V
|
||||||
|
|
||||||
|
// Проверяем наличие джойстика
|
||||||
|
int x = analogRead(JOY_X_PIN);
|
||||||
|
int y = analogRead(JOY_Y_PIN);
|
||||||
|
|
||||||
|
// Если значения в разумных пределах - джойстик подключен
|
||||||
|
bool connected = (x > 100 && x < 4000) && (y > 100 && y < 4000);
|
||||||
|
|
||||||
|
Serial.println("Joystick initialized");
|
||||||
|
Serial.printf(" VRX → GPIO%d (ADC1_CH6)\n", JOY_X_PIN);
|
||||||
|
Serial.printf(" VRY → GPIO%d (ADC1_CH7)\n", JOY_Y_PIN);
|
||||||
|
Serial.printf(" SW → GPIO%d\n", JOY_SW_PIN);
|
||||||
|
Serial.printf(" Center: X=%d, Y=%d\n", ADC_CENTER_X, ADC_CENTER_Y);
|
||||||
|
Serial.printf(" Status: %s (X=%d, Y=%d)\n",
|
||||||
|
connected ? "OK" : "NOT FOUND", x, y);
|
||||||
|
|
||||||
|
return connected;
|
||||||
|
}
|
||||||
|
|
||||||
|
void joyUpdate() {
|
||||||
|
joyX = analogRead(JOY_X_PIN);
|
||||||
|
joyY = analogRead(JOY_Y_PIN);
|
||||||
|
swPressed = (digitalRead(JOY_SW_PIN) == LOW); // LOW = нажата
|
||||||
|
|
||||||
|
// Логирование только при значимых изменениях
|
||||||
|
int xNorm = joyGetXNormalized();
|
||||||
|
int yNorm = joyGetYNormalized();
|
||||||
|
|
||||||
|
bool xChanged = abs(xNorm - lastLoggedXNorm) >= LOG_THRESHOLD;
|
||||||
|
bool yChanged = abs(yNorm - lastLoggedYNorm) >= LOG_THRESHOLD;
|
||||||
|
bool swChanged = (swPressed != lastLoggedSw);
|
||||||
|
|
||||||
|
if (xChanged || yChanged || swChanged) {
|
||||||
|
Serial.printf("[JOY] X=%4d (%4d) | Y=%4d (%4d) | SW=%s\n",
|
||||||
|
joyX, xNorm, joyY, yNorm, swPressed ? "PRESSED" : "released");
|
||||||
|
|
||||||
|
lastLoggedXNorm = xNorm;
|
||||||
|
lastLoggedYNorm = yNorm;
|
||||||
|
lastLoggedSw = swPressed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int joyGetX() {
|
||||||
|
return joyX;
|
||||||
|
}
|
||||||
|
|
||||||
|
int joyGetY() {
|
||||||
|
return joyY;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool joyIsPressed() {
|
||||||
|
return swPressed;
|
||||||
|
}
|
||||||
|
|
||||||
|
int joyGetXNormalized() {
|
||||||
|
if (joyX < ADC_CENTER_X - ADC_DEADZONE) {
|
||||||
|
// влево
|
||||||
|
return map(joyX, ADC_MIN, ADC_CENTER_X - ADC_DEADZONE, NORM_MIN, 0);
|
||||||
|
} else if (joyX > ADC_CENTER_X + ADC_DEADZONE) {
|
||||||
|
// вправо
|
||||||
|
return map(joyX, ADC_CENTER_X + ADC_DEADZONE, ADC_MAX, 0, NORM_MAX);
|
||||||
|
}
|
||||||
|
return 0; // мёртвая зона
|
||||||
|
}
|
||||||
|
|
||||||
|
int joyGetYNormalized() {
|
||||||
|
if (joyY < ADC_CENTER_Y - ADC_DEADZONE) {
|
||||||
|
// вверх (инвертировано)
|
||||||
|
return map(joyY, ADC_MIN, ADC_CENTER_Y - ADC_DEADZONE, NORM_MAX, 0);
|
||||||
|
} else if (joyY > ADC_CENTER_Y + ADC_DEADZONE) {
|
||||||
|
// вниз
|
||||||
|
return map(joyY, ADC_CENTER_Y + ADC_DEADZONE, ADC_MAX, 0, NORM_MIN);
|
||||||
|
}
|
||||||
|
return 0; // мёртвая зона
|
||||||
|
}
|
||||||
15
src/core/joystick/joystick.h
Normal file
15
src/core/joystick/joystick.h
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
bool joyInit();
|
||||||
|
void joyUpdate();
|
||||||
|
|
||||||
|
// возвращает значения 0-4095
|
||||||
|
int joyGetX();
|
||||||
|
int joyGetY();
|
||||||
|
|
||||||
|
// возвращает true, если кнопка нажата
|
||||||
|
bool joyIsPressed();
|
||||||
|
|
||||||
|
// возвращает нормализованные значения -100..100
|
||||||
|
int joyGetXNormalized();
|
||||||
|
int joyGetYNormalized();
|
||||||
@ -38,10 +38,16 @@ static uint32_t angleToDuty(int logicalAngle) {
|
|||||||
SERVO_MIN, SERVO_MAX);
|
SERVO_MIN, SERVO_MAX);
|
||||||
}
|
}
|
||||||
|
|
||||||
void servoInit() {
|
bool servoInit() {
|
||||||
ledcSetup(SERVO_CH, servoFreq, servoRes);
|
ledcSetup(SERVO_CH, servoFreq, servoRes);
|
||||||
ledcAttachPin(SERVO_PIN, SERVO_CH);
|
ledcAttachPin(SERVO_PIN, SERVO_CH);
|
||||||
|
|
||||||
|
Serial.println("Servo initialized");
|
||||||
|
Serial.printf(" PIN → GPIO%d\n", SERVO_PIN);
|
||||||
|
Serial.printf(" CH → %d\n", SERVO_CH);
|
||||||
|
|
||||||
servoCenter();
|
servoCenter();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void servoCenter() {
|
void servoCenter() {
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
void servoInit();
|
bool servoInit();
|
||||||
void servoCenter();
|
void servoCenter();
|
||||||
void servoSetAngle(int angle);
|
void servoSetAngle(int angle);
|
||||||
int servoGetAngle();
|
int servoGetAngle();
|
||||||
|
|||||||
@ -8,11 +8,17 @@
|
|||||||
// таймаут эха (30 мс ≈ 5 м)
|
// таймаут эха (30 мс ≈ 5 м)
|
||||||
static const uint32_t ECHO_TIMEOUT_US = 30000;
|
static const uint32_t ECHO_TIMEOUT_US = 30000;
|
||||||
|
|
||||||
void ultrasonicInit() {
|
bool ultrasonicInit() {
|
||||||
pinMode(TRIG_PIN, OUTPUT);
|
pinMode(TRIG_PIN, OUTPUT);
|
||||||
pinMode(ECHO_PIN, INPUT);
|
pinMode(ECHO_PIN, INPUT);
|
||||||
|
|
||||||
digitalWrite(TRIG_PIN, LOW);
|
digitalWrite(TRIG_PIN, LOW);
|
||||||
|
|
||||||
|
Serial.println("Ultrasonic initialized");
|
||||||
|
Serial.printf(" TRIG → GPIO%d\n", TRIG_PIN);
|
||||||
|
Serial.printf(" ECHO → GPIO%d\n", ECHO_PIN);
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// измерение расстояния
|
// измерение расстояния
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
void ultrasonicInit();
|
bool ultrasonicInit();
|
||||||
|
|
||||||
// возвращает расстояние в см
|
// возвращает расстояние в см
|
||||||
// -1 = нет эхо / ошибка
|
// -1 = нет эхо / ошибка
|
||||||
|
|||||||
68
src/main.cpp
68
src/main.cpp
@ -1,10 +1,11 @@
|
|||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
|
|
||||||
#include "core/deprecate/lcd_status/lcd_status.h"
|
#include "core/display/body/body.h"
|
||||||
#include "core/servo/servo.h"
|
#include "core/servo/servo.h"
|
||||||
#include "core/ultrasonic/ultrasonic.h"
|
#include "core/ultrasonic/ultrasonic.h"
|
||||||
#include "core/display/face/face.h"
|
#include "core/display/face/face.h"
|
||||||
#include "core/robot_state/robot_state.h"
|
#include "core/robot_state/robot_state.h"
|
||||||
|
#include "core/joystick/joystick.h"
|
||||||
|
|
||||||
#include "core/actuators/actuators.h"
|
#include "core/actuators/actuators.h"
|
||||||
#include "server/web_server.h"
|
#include "server/web_server.h"
|
||||||
@ -12,17 +13,62 @@
|
|||||||
#include "core/controller/controller.h"
|
#include "core/controller/controller.h"
|
||||||
// #include "core/deprecate/ir_input/ir_input.h"
|
// #include "core/deprecate/ir_input/ir_input.h"
|
||||||
|
|
||||||
|
#define TOTAL_STAGES 5
|
||||||
|
#define DELAY_FOR_STARTUP_CHECKS 1000
|
||||||
|
|
||||||
|
void runStartupChecks() {
|
||||||
|
delay(DELAY_FOR_STARTUP_CHECKS);
|
||||||
|
|
||||||
|
// Этап 1: Проверка джойстика
|
||||||
|
oledSetStage(1, TOTAL_STAGES);
|
||||||
|
bool joyOk = joyInit();
|
||||||
|
// irInit();
|
||||||
|
oledShowMain(joyOk ? "Joystick: OK" : "Joystick: NOT FOUND",
|
||||||
|
"IR: NOT FOUND",
|
||||||
|
"", "");
|
||||||
|
delay(DELAY_FOR_STARTUP_CHECKS);
|
||||||
|
|
||||||
|
// Этап 2: Проверка Face дисплея
|
||||||
|
oledSetStage(2, TOTAL_STAGES);
|
||||||
|
bool faceOk = false; // faceInit();
|
||||||
|
oledShowMain("Face display: FAIL",
|
||||||
|
"", "", "");
|
||||||
|
delay(DELAY_FOR_STARTUP_CHECKS);
|
||||||
|
|
||||||
|
// Этап 3: Проверка серво и ультразвука
|
||||||
|
oledSetStage(3, TOTAL_STAGES);
|
||||||
|
bool servoOk = servoInit();
|
||||||
|
bool ultrasonicOk = ultrasonicInit();
|
||||||
|
oledShowMain(servoOk ? "Servo: OK" : "Servo: FAIL",
|
||||||
|
ultrasonicOk ? "Ultrasonic: OK" : "Ultrasonic: FAIL",
|
||||||
|
"", "");
|
||||||
|
delay(DELAY_FOR_STARTUP_CHECKS);
|
||||||
|
|
||||||
|
// Этап 4: Проверка моторов (actuators)
|
||||||
|
oledSetStage(4, TOTAL_STAGES);
|
||||||
|
bool actuatorsOk = actuatorsInit();
|
||||||
|
oledShowMain(actuatorsOk ? "Motors: OK" : "Motors: FAIL",
|
||||||
|
"", "", "");
|
||||||
|
delay(DELAY_FOR_STARTUP_CHECKS);
|
||||||
|
|
||||||
|
// Этап 5: Инициализация WiFi и WebSocket
|
||||||
|
oledSetStage(5, TOTAL_STAGES);
|
||||||
|
bool wifiOk = webServerInit();
|
||||||
|
bool wsOk = wsInit();
|
||||||
|
delay(DELAY_FOR_STARTUP_CHECKS);
|
||||||
|
|
||||||
|
// Все проверки пройдены
|
||||||
|
// joyOk = true означает debug mode (джойстик найден)
|
||||||
|
oledSetSystemReady(wifiOk, joyOk);
|
||||||
|
}
|
||||||
|
|
||||||
void setup() {
|
void setup() {
|
||||||
Serial.begin(115200);
|
Serial.begin(115200);
|
||||||
|
|
||||||
lcdInit();
|
oledInit();
|
||||||
servoInit();
|
|
||||||
ultrasonicInit();
|
// Запуск проверок
|
||||||
faceInit();
|
runStartupChecks();
|
||||||
actuatorsInit();
|
|
||||||
webServerInit();
|
|
||||||
wsInit();
|
|
||||||
// irInit();
|
|
||||||
|
|
||||||
robot.lastCmdMs = millis();
|
robot.lastCmdMs = millis();
|
||||||
}
|
}
|
||||||
@ -30,6 +76,10 @@ void setup() {
|
|||||||
void loop() {
|
void loop() {
|
||||||
webServerLoop();
|
webServerLoop();
|
||||||
wsLoop();
|
wsLoop();
|
||||||
|
joyUpdate();
|
||||||
controllerUpdate();
|
controllerUpdate();
|
||||||
// irPoll();
|
// irPoll();
|
||||||
|
|
||||||
|
// Обновляем дисплей с IP и режимом
|
||||||
|
oledUpdateDisplay();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,10 +6,17 @@
|
|||||||
#include "core/controller/controller.h"
|
#include "core/controller/controller.h"
|
||||||
#include "core/robot_state/robot_state.h"
|
#include "core/robot_state/robot_state.h"
|
||||||
#include "core/ultrasonic/ultrasonic.h"
|
#include "core/ultrasonic/ultrasonic.h"
|
||||||
#include "core/deprecate/lcd_status/lcd_status.h"
|
#include "core/display/body/body.h"
|
||||||
|
|
||||||
static WebServer server(80);
|
static WebServer server(80);
|
||||||
|
|
||||||
|
// Static buffers for getters
|
||||||
|
static char ipStr[16] = "No IP";
|
||||||
|
static char modeStr[16] = "IDLE";
|
||||||
|
|
||||||
|
#define WIFI_RETRY_MAX 5
|
||||||
|
#define WIFI_RETRY_DELAY_MS 800
|
||||||
|
|
||||||
void handleRoot() {
|
void handleRoot() {
|
||||||
server.send_P(200, "text/html; charset=utf-8", INDEX_HTML);
|
server.send_P(200, "text/html; charset=utf-8", INDEX_HTML);
|
||||||
}
|
}
|
||||||
@ -65,7 +72,8 @@ void handleMode() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
lcdShowMode(modeToStr(robot.mode));
|
snprintf(modeStr, sizeof(modeStr), "%s", modeToStr(robot.mode));
|
||||||
|
// oledShowMode(modeStr);
|
||||||
// lcdShowText("SERVICE MODE", "Waiting...");
|
// lcdShowText("SERVICE MODE", "Waiting...");
|
||||||
server.send(200, "text/plain",
|
server.send(200, "text/plain",
|
||||||
String("OK MODE=") + modeToStr(robot.mode));
|
String("OK MODE=") + modeToStr(robot.mode));
|
||||||
@ -88,30 +96,87 @@ void handleStatus() {
|
|||||||
server.send(200, "application/json", json);
|
server.send(200, "application/json", json);
|
||||||
}
|
}
|
||||||
|
|
||||||
void webServerInit() {
|
bool webServerInit() {
|
||||||
WiFi.mode(WIFI_STA);
|
WiFi.mode(WIFI_STA);
|
||||||
WiFi.begin(WIFI_SSID, WIFI_PASS);
|
WiFi.begin(WIFI_SSID, WIFI_PASS);
|
||||||
Serial.print("\nConnecting to Wi-Fi");
|
Serial.print("\nConnecting to Wi-Fi");
|
||||||
|
|
||||||
while (WiFi.status() != WL_CONNECTED) {
|
// Показываем экран подключения
|
||||||
|
char statusBuf[32];
|
||||||
|
snprintf(statusBuf, sizeof(statusBuf), "Retry 0/%d", WIFI_RETRY_MAX);
|
||||||
|
oledShowText("Connecting to WiFi", "");
|
||||||
|
|
||||||
|
int retryCount = 0;
|
||||||
|
while (WiFi.status() != WL_CONNECTED && retryCount < WIFI_RETRY_MAX) {
|
||||||
|
// Serial.print("\nRetry connecting to Wi-Fi");
|
||||||
|
// delay(WIFI_RETRY_DELAY_MS);
|
||||||
|
|
||||||
|
// if (retryCount >= 3){
|
||||||
|
// retryCount = 0;
|
||||||
|
// } else {
|
||||||
|
// retryCount++;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Обновляем дисплей с номером попытки
|
||||||
|
// snprintf(statusBuf, sizeof(statusBuf), "Retry %d/%d", retryCount, WIFI_RETRY_MAX);
|
||||||
|
// oledShowText("Connecting to WiFi...", statusBuf);
|
||||||
|
|
||||||
Serial.print("\nRetry connecting to Wi-Fi");
|
Serial.print("\nRetry connecting to Wi-Fi");
|
||||||
delay(300);
|
delay(WIFI_RETRY_DELAY_MS);
|
||||||
|
|
||||||
|
// Считаем точки: 1, 2, 3, затем сброс в 0
|
||||||
|
retryCount = (retryCount + 1) % 4; // 0, 1, 2, 3 точки
|
||||||
|
|
||||||
|
// Формируем строку с точками
|
||||||
|
char dots[4] = "";
|
||||||
|
for (int i = 0; i < retryCount; i++) {
|
||||||
|
dots[i] = '.';
|
||||||
|
}
|
||||||
|
dots[retryCount] = '\0';
|
||||||
|
|
||||||
|
// Обновляем дисплей: "Connecting to WiFi" + точки
|
||||||
|
char statusBuf[32];
|
||||||
|
snprintf(statusBuf, sizeof(statusBuf), "Connecting to WiFi%s", dots);
|
||||||
|
oledShowText(statusBuf, ""); // вторая строка пустая, если не нужна
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (WiFi.status() == WL_CONNECTED) {
|
if (WiFi.status() == WL_CONNECTED) {
|
||||||
lcdShowWiFi(WIFI_SSID, WiFi.RSSI());
|
// Показываем успешное подключение
|
||||||
|
snprintf(ipStr, sizeof(ipStr), "%s", WiFi.localIP().toString().c_str());
|
||||||
|
oledShowText("WiFi: CONNECTED", ipStr);
|
||||||
Serial.print("\nConnected. IP: ");
|
Serial.print("\nConnected. IP: ");
|
||||||
Serial.println(WiFi.localIP());
|
Serial.println(WiFi.localIP());
|
||||||
|
|
||||||
|
// Настраиваем маршруты
|
||||||
|
server.on("/", handleRoot);
|
||||||
|
server.on("/cmd", handleCmd);
|
||||||
|
server.on("/speed", handleSpeed);
|
||||||
|
server.on("/mode", handleMode);
|
||||||
|
server.on("/status", handleStatus);
|
||||||
|
server.begin();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
server.on("/", handleRoot);
|
// Не удалось подключиться
|
||||||
server.on("/cmd", handleCmd);
|
oledShowText("WiFi: FAILED", "Check router/network");
|
||||||
server.on("/speed", handleSpeed);
|
Serial.println("\nWiFi connection failed");
|
||||||
server.on("/mode", handleMode);
|
return false;
|
||||||
server.on("/status", handleStatus);
|
|
||||||
server.begin();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void webServerLoop() {
|
void webServerLoop() {
|
||||||
server.handleClient();
|
server.handleClient();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char* webServerGetIP() {
|
||||||
|
return ipStr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* webServerGetMode() {
|
||||||
|
return modeStr;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t webServerGetUptime() {
|
||||||
|
return millis() / 1000; // seconds
|
||||||
|
}
|
||||||
|
|||||||
@ -1,3 +1,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
void webServerInit();
|
bool webServerInit();
|
||||||
void webServerLoop();
|
void webServerLoop();
|
||||||
|
|
||||||
|
// Getters for display
|
||||||
|
const char* webServerGetIP();
|
||||||
|
const char* webServerGetMode();
|
||||||
|
uint32_t webServerGetUptime();
|
||||||
|
|||||||
@ -4,11 +4,12 @@
|
|||||||
#include "ws_server.h"
|
#include "ws_server.h"
|
||||||
#include "core/controller/controller.h"
|
#include "core/controller/controller.h"
|
||||||
#include "core/robot_state/robot_state.h"
|
#include "core/robot_state/robot_state.h"
|
||||||
#include "core/display/face/face.h"
|
#include "core/display/body/body.h"
|
||||||
|
|
||||||
static WebSocketsServer ws(81); // порт 81
|
static WebSocketsServer ws(81); // порт 81
|
||||||
|
|
||||||
static uint32_t lastWsCmdMs = 0;
|
static uint32_t lastWsCmdMs = 0;
|
||||||
|
static bool wsReady = false;
|
||||||
|
|
||||||
void wsEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) {
|
void wsEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
@ -54,16 +55,16 @@ void wsEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (msg.startsWith("FACE:")) {
|
// else if (msg.startsWith("FACE:")) {
|
||||||
String v = msg.substring(5);
|
// String v = msg.substring(5);
|
||||||
v.toUpperCase();
|
// v.toUpperCase();
|
||||||
|
|
||||||
if (v == "HAPPY") faceSet(FACE_HAPPY);
|
// if (v == "HAPPY") faceSet(FACE_HAPPY);
|
||||||
else if (v == "ANGRY") faceSet(FACE_ANGRY);
|
// else if (v == "ANGRY") faceSet(FACE_ANGRY);
|
||||||
else if (v == "BORED") faceSet(FACE_BORED);
|
// else if (v == "BORED") faceSet(FACE_BORED);
|
||||||
else if (v == "SURPRISED") faceSet(FACE_SURPRISED);
|
// else if (v == "SURPRISED") faceSet(FACE_SURPRISED);
|
||||||
else faceSet(FACE_NEUTRAL);
|
// else faceSet(FACE_NEUTRAL);
|
||||||
}
|
// }
|
||||||
|
|
||||||
else if (msg.startsWith("SPEED:")) {
|
else if (msg.startsWith("SPEED:")) {
|
||||||
int v = msg.substring(6).toInt();
|
int v = msg.substring(6).toInt();
|
||||||
@ -77,9 +78,20 @@ void wsEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void wsInit() {
|
bool wsInit() {
|
||||||
|
// Показываем статус инициализации WebSocket
|
||||||
|
// oledShowText("WebSocket: INIT...", "Queue pending");
|
||||||
|
|
||||||
ws.begin();
|
ws.begin();
|
||||||
ws.onEvent(wsEvent);
|
ws.onEvent(wsEvent);
|
||||||
|
|
||||||
|
wsReady = true;
|
||||||
|
|
||||||
|
// Показываем успешную инициализацию
|
||||||
|
// oledShowText("WebSocket: READY", "Listening on :81");
|
||||||
|
Serial.println("WebSocket initialized on port 81");
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void wsLoop() {
|
void wsLoop() {
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
void wsInit();
|
bool wsInit();
|
||||||
void wsLoop();
|
void wsLoop();
|
||||||
void wsBroadcastStatus();
|
void wsBroadcastStatus();
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user