Пульт управления МехБокс
1. О пульте
Данная инструкция позволяет самостоятельно собрать пульт для управления контроллером MBX-A1.
- Управление газом — аналоговый джойстик KY-023
- Управление рулём — магнитный энкодер AS5600
- Контроллер — ESP32 C3 mini
Все компоненты можно приобрести на Ozon, общая стоимость обычно не превышает 1000 рублей.
2. Функции
- Считывание угла поворота с энкодера AS5600 по I2C
- Считывание положения джойстика (2 оси, ADC)
- Формирование и отправка BLE-команды с параметрами
- Индикация состояния соединения встроенным LED
- Автоматический поиск и подключение к BLE-серверу
- Отладочный вывод в Serial
3. Пиновая карта
| Назначение | Пин ESP32 | Описание |
|---|---|---|
| AS5600 SDA | GPIO4 | I2C SDA |
| AS5600 SCL | GPIO5 | I2C SCL |
| Джойстик VRx (X) | GPIO20 | ADC1, ось X — мотор 1 |
| Джойстик VRy (Y) | GPIO21 | ADC1, ось Y — мотор 2/серво |
4. Прошивка контроллера
Версия прошивки: 1.04 — инверсия направления рулевого управления через энкодер AS5600, добавлена чувствительность руля (STEERING_SENSITIVITY)
#include <Wire.h>
#include <ESP32Servo.h>
#include <BLEDevice.h>
#include <BLEUtils.h>
#include <AS5600.h>
// --- Пины ---
#define AS5600_SDA 4
#define AS5600_SCL 5
#define VRX_PIN 0 // X-ось джойстика (ADC1_CH0, GPIO0)
#define VRY_PIN 1 // Y-ось джойстика (ADC1_CH1, GPIO1)
#define LED_PIN 8 // Встроенный LED на ESP32-C3-DevKitM-1
// --- Настройки чувствительности руля ---
#define STEERING_SENSITIVITY 2.0f // 1.0 — стандартно, 2.0 — более острый
// --- Режим отладки ---
#define DEBUG_MODE 1 // 1 — включён, 0 — выключен
// --- BLE UUID ---
#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"
// --- Глобальные переменные ---
AS5600 as5600;
BLEClient* pClient = nullptr;
BLERemoteCharacteristic* pRemoteCharacteristic = nullptr;
BLEAdvertisedDevice* myDevice = nullptr;
bool deviceConnected = false;
// --- Настройки джойстика ---
const int joyCenter = 2048; // для 12-бит ADC ESP32-C3
const int deadZone = 200; // мёртвая зона ±200
// --- BLE scan callback ---
class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
void onResult(BLEAdvertisedDevice advertisedDevice) {
if (advertisedDevice.haveServiceUUID() &&
advertisedDevice.isAdvertisingService(BLEUUID(SERVICE_UUID))) {
myDevice = new BLEAdvertisedDevice(advertisedDevice);
}
}
};
void connectToServer() {
pClient = BLEDevice::createClient();
pClient->connect(myDevice);
BLERemoteService* pRemoteService = pClient->getService(SERVICE_UUID);
if (pRemoteService == nullptr) {
Serial.println("Failed to find our service UUID");
pClient->disconnect();
return;
}
pRemoteCharacteristic = pRemoteService->getCharacteristic(CHARACTERISTIC_UUID);
if (pRemoteCharacteristic == nullptr) {
Serial.println("Failed to find our characteristic UUID");
pClient->disconnect();
return;
}
deviceConnected = true;
Serial.println("Connected to BLE server!");
}
void setup() {
Serial.begin(115200);
Serial.println("MBX_transmitter v1.04");
// --- I2C и AS5600 ---
Wire.begin(AS5600_SDA, AS5600_SCL);
as5600.begin();
// --- LED ---
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, LOW);
// --- BLE ---
BLEDevice::init("");
BLEScan* pBLEScan = BLEDevice::getScan();
pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
pBLEScan->setActiveScan(true);
Serial.println("Scanning for BLE server...");
while (myDevice == nullptr) {
pBLEScan->start(1, false);
digitalWrite(LED_PIN, !digitalRead(LED_PIN));
delay(500);
}
BLEDevice::getScan()->stop();
connectToServer();
}
void loop() {
static unsigned long lastBlink = 0;
static bool ledState = false;
if (!deviceConnected) {
Serial.println("Not connected. Trying to reconnect...");
connectToServer();
unsigned long now = millis();
if (now - lastBlink > 500) {
ledState = !ledState;
digitalWrite(LED_PIN, ledState ? LOW : HIGH);
lastBlink = now;
}
delay(1000);
return;
} else {
digitalWrite(LED_PIN, LOW);
}
// --- Считываем угол с AS5600 ---
int rawAngle = as5600.readAngle();
int a = map(rawAngle, 0, 4095, 0, 180);
a = 180 - a; // Инверсия
a = 90 + (a - 90) * STEERING_SENSITIVITY;
if (a < 0) a = 0;
if (a > 180) a = 180;
// --- Считываем джойстик ---
int joyX = analogRead(VRX_PIN);
int joyY = analogRead(VRY_PIN);
// --- Обработка X (мотор 1) ---
int dX = 0, cX = 0;
if (joyX > joyCenter + deadZone) {
cX = 1;
dX = map(joyX, joyCenter + deadZone, 4095, 0, 255);
} else if (joyX < joyCenter - deadZone) {
cX = 0;
dX = map(joyX, joyCenter - deadZone, 0, 0, 255);
}
int motorA = (dX == 0) ? 0 : (cX == 1 ? dX : -dX);
// --- Обработка Y (мотор 2) ---
int dY = 0, cY = 0;
if (joyY > joyCenter + deadZone) {
cY = 1;
dY = map(joyY, joyCenter + deadZone, 4095, 0, 255);
} else if (joyY < joyCenter - deadZone) {
cY = 0;
dY = map(joyY, joyCenter - deadZone, 0, 0, 255);
}
int motorB = (dY == 0) ? 0 : (cY == 1 ? dY : -dY);
// --- Формируем и отправляем BLE команду ---
char bleMsg[32];
snprintf(bleMsg, sizeof(bleMsg), "%d,0,%d,%d,0", a, motorA, motorB);
if (deviceConnected && pRemoteCharacteristic != nullptr) {
pRemoteCharacteristic->writeValue((uint8_t*)bleMsg, strlen(bleMsg), false);
}
#if DEBUG_MODE
Serial.print("BLE CMD: ");
Serial.println(bleMsg);
#endif
delay(50); // 20 Гц
}