2024-12-12 15:38:11 +08:00
|
|
|
|
#include <Arduino.h>
|
2024-12-16 00:16:06 +08:00
|
|
|
|
#include <BLEDevice.h>
|
|
|
|
|
#include <BLEServer.h>
|
|
|
|
|
#include <BLEUtils.h>
|
|
|
|
|
#include <BLE2902.h>
|
2024-12-18 16:10:26 +08:00
|
|
|
|
#include <SPIFFS.h>
|
2024-12-18 14:16:02 +08:00
|
|
|
|
#include "consts.h"
|
2024-12-15 18:09:40 +08:00
|
|
|
|
|
2024-12-12 15:38:11 +08:00
|
|
|
|
// 使用 2 片 TB6612 控制 4 个电机
|
|
|
|
|
// 使用差速控制转向
|
|
|
|
|
|
2024-12-15 18:09:40 +08:00
|
|
|
|
#define PACKET_R_HEAD 0x00
|
|
|
|
|
#define PACKET_R_TAIL 0xFF
|
|
|
|
|
#define PACKET_T_HEAD 0x01
|
|
|
|
|
#define PACKET_T_TAIL 0xFE
|
|
|
|
|
#define PACKET_MAX_LENGTH 32 // 数据包最大长度
|
|
|
|
|
|
|
|
|
|
// 指令定义
|
2024-12-15 21:42:43 +08:00
|
|
|
|
#define CMD_GET_BT_STATUS 0x10
|
2024-12-18 16:10:26 +08:00
|
|
|
|
#define CMD_GET_SPIFFS_STATUS 0x11
|
|
|
|
|
#define CMD_GET_DISTANCE 0x12
|
2024-12-15 18:09:40 +08:00
|
|
|
|
#define CMD_MOTOR_MOVE_CONTROL 0x20
|
2024-12-19 01:08:39 +08:00
|
|
|
|
#define CMD_MOTOR_STEER_CONTROL 0x21
|
2024-12-18 17:31:35 +08:00
|
|
|
|
#define CMD_MOTOR_SINGLE_CONTROL 0x22
|
2024-12-19 01:08:39 +08:00
|
|
|
|
#define CMD_MOTOR_ROTATE_CONTROL 0x23
|
2024-12-18 21:52:05 +08:00
|
|
|
|
#define CMD_DEMO_PID 0xf0
|
2024-12-19 01:08:39 +08:00
|
|
|
|
#define CMD_DEMO_PATH 0xf1
|
|
|
|
|
|
|
|
|
|
#define CMD_STATUS_MOTOR 0xE0
|
|
|
|
|
|
2024-12-16 00:16:06 +08:00
|
|
|
|
// 全局变量
|
|
|
|
|
int currentSpeed = 0; // 当前速度
|
|
|
|
|
int turnOffset = 0; // 转向偏移量 (-100 到 100)
|
|
|
|
|
bool isMoving = false; // 运动状态
|
|
|
|
|
bool isTurning = false; // 转向状态
|
|
|
|
|
|
|
|
|
|
// BLE 相关
|
|
|
|
|
BLEServer *pServer = nullptr;
|
|
|
|
|
BLECharacteristic *pTxCharacteristic = nullptr;
|
|
|
|
|
bool deviceConnected = false;
|
|
|
|
|
|
2024-12-15 18:09:40 +08:00
|
|
|
|
// 数据包缓冲区
|
|
|
|
|
uint8_t packetBuffer[PACKET_MAX_LENGTH];
|
|
|
|
|
int packetIndex = 0;
|
|
|
|
|
bool isReceivingPacket = false;
|
|
|
|
|
|
|
|
|
|
// 在全局变量定义区域添加
|
|
|
|
|
#define LED_FLASH_INTERVAL 1000 // LED闪烁间隔(ms)
|
|
|
|
|
unsigned long lastLedToggle = 0; // 上次LED切换状态的时间
|
|
|
|
|
bool ledState = false; // LED当前状态
|
|
|
|
|
|
2024-12-19 01:08:39 +08:00
|
|
|
|
#define MOTOR_STATUS_INTERVAL 200 // 发送电机状态的时间间隔(ms)
|
|
|
|
|
unsigned long lastMotorStatusUpdate = 0; // 上次发送电机状态的时间
|
|
|
|
|
|
|
|
|
|
// 在全局变量区域添加电机方向状态跟踪
|
|
|
|
|
struct MotorStatus
|
|
|
|
|
{
|
|
|
|
|
bool in1;
|
|
|
|
|
bool in2;
|
|
|
|
|
unsigned char pwm;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 定义四个电机的状态
|
|
|
|
|
MotorStatus motorA = {false, false, 0};
|
|
|
|
|
MotorStatus motorB = {false, false, 0};
|
|
|
|
|
MotorStatus motorC = {false, false, 0};
|
|
|
|
|
MotorStatus motorD = {false, false, 0};
|
|
|
|
|
|
2024-12-16 00:16:06 +08:00
|
|
|
|
void processSerialIncomingByte(uint8_t incomingByte, BLECharacteristic &characteristic);
|
|
|
|
|
|
|
|
|
|
class CarBLEServerCallbacks : public BLEServerCallbacks
|
|
|
|
|
{
|
|
|
|
|
void onConnect(BLEServer *pServer)
|
|
|
|
|
{
|
|
|
|
|
deviceConnected = true;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
void onDisconnect(BLEServer *pServer)
|
|
|
|
|
{
|
|
|
|
|
deviceConnected = false;
|
|
|
|
|
// 重新开始广播
|
|
|
|
|
pServer->getAdvertising()->start();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class CarBLECharacteristicCallbacks : public BLECharacteristicCallbacks
|
|
|
|
|
{
|
|
|
|
|
void onWrite(BLECharacteristic *pCharacteristic)
|
|
|
|
|
{
|
|
|
|
|
std::string rxValue = pCharacteristic->getValue();
|
|
|
|
|
if (rxValue.length() > 0)
|
|
|
|
|
{
|
|
|
|
|
for (int i = 0; i < rxValue.length(); i++)
|
|
|
|
|
{
|
|
|
|
|
processSerialIncomingByte((uint8_t)rxValue[i], *pTxCharacteristic);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2024-12-15 21:42:43 +08:00
|
|
|
|
void floatToBytes(float val, uint8_t *bytes)
|
|
|
|
|
{
|
|
|
|
|
union
|
|
|
|
|
{
|
|
|
|
|
float f;
|
|
|
|
|
uint8_t bytes[4];
|
|
|
|
|
} u;
|
|
|
|
|
u.f = val;
|
|
|
|
|
|
|
|
|
|
// 考虑大小端问题
|
|
|
|
|
for (int i = 0; i < 4; i++)
|
|
|
|
|
{
|
|
|
|
|
bytes[i] = u.bytes[i];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-19 01:08:39 +08:00
|
|
|
|
void pwmControl(char motor, unsigned char pwm)
|
|
|
|
|
{
|
|
|
|
|
int pwmPin;
|
|
|
|
|
switch (motor)
|
|
|
|
|
{
|
|
|
|
|
case 'A':
|
|
|
|
|
pwmPin = MOTOR_A_PWMA;
|
|
|
|
|
motorA.pwm = pwm;
|
|
|
|
|
break;
|
|
|
|
|
case 'B':
|
|
|
|
|
pwmPin = MOTOR_B_PWMB;
|
|
|
|
|
motorB.pwm = pwm;
|
|
|
|
|
break;
|
|
|
|
|
case 'C':
|
|
|
|
|
pwmPin = MOTOR_C_PWMA;
|
|
|
|
|
motorC.pwm = pwm;
|
|
|
|
|
break;
|
|
|
|
|
case 'D':
|
|
|
|
|
pwmPin = MOTOR_D_PWMB;
|
|
|
|
|
motorD.pwm = pwm;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
analogWrite(pwmPin, pwm);
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-12 15:38:11 +08:00
|
|
|
|
// 电机控制函数
|
2024-12-19 01:08:39 +08:00
|
|
|
|
void motorControl(char motor, int speed)
|
2024-12-15 18:09:40 +08:00
|
|
|
|
{
|
2024-12-19 01:08:39 +08:00
|
|
|
|
int in1Pin, in2Pin;
|
|
|
|
|
MotorStatus *status;
|
|
|
|
|
|
|
|
|
|
switch (motor)
|
|
|
|
|
{
|
|
|
|
|
case 'A':
|
|
|
|
|
in1Pin = MOTOR_A_AIN1;
|
|
|
|
|
in2Pin = MOTOR_A_AIN2;
|
|
|
|
|
status = &motorA;
|
|
|
|
|
break;
|
|
|
|
|
case 'B':
|
|
|
|
|
in1Pin = MOTOR_B_BIN2;
|
|
|
|
|
in2Pin = MOTOR_B_BIN1;
|
|
|
|
|
status = &motorB;
|
|
|
|
|
break;
|
|
|
|
|
case 'C':
|
|
|
|
|
in1Pin = MOTOR_C_AIN2;
|
|
|
|
|
in2Pin = MOTOR_C_AIN1;
|
|
|
|
|
status = &motorC;
|
|
|
|
|
break;
|
|
|
|
|
case 'D':
|
|
|
|
|
in1Pin = MOTOR_D_BIN1;
|
|
|
|
|
in2Pin = MOTOR_D_BIN2;
|
|
|
|
|
status = &motorD;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-15 18:09:40 +08:00
|
|
|
|
if (speed > 0)
|
|
|
|
|
{
|
2024-12-12 15:38:11 +08:00
|
|
|
|
digitalWrite(in1Pin, HIGH);
|
|
|
|
|
digitalWrite(in2Pin, LOW);
|
2024-12-19 01:08:39 +08:00
|
|
|
|
if (motor == 'A' || motor == 'D')
|
|
|
|
|
{
|
|
|
|
|
status->in1 = true;
|
|
|
|
|
status->in2 = false;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
status->in1 = false;
|
|
|
|
|
status->in2 = true;
|
|
|
|
|
}
|
2024-12-15 18:09:40 +08:00
|
|
|
|
}
|
|
|
|
|
else if (speed < 0)
|
|
|
|
|
{
|
2024-12-12 15:38:11 +08:00
|
|
|
|
digitalWrite(in1Pin, LOW);
|
|
|
|
|
digitalWrite(in2Pin, HIGH);
|
2024-12-19 01:08:39 +08:00
|
|
|
|
if (motor == 'A' || motor == 'D')
|
|
|
|
|
{
|
|
|
|
|
status->in1 = false;
|
|
|
|
|
status->in2 = true;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
status->in1 = true;
|
|
|
|
|
status->in2 = false;
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-12 15:38:11 +08:00
|
|
|
|
speed = -speed;
|
2024-12-15 18:09:40 +08:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2024-12-12 15:38:11 +08:00
|
|
|
|
digitalWrite(in1Pin, LOW);
|
|
|
|
|
digitalWrite(in2Pin, LOW);
|
2024-12-19 01:08:39 +08:00
|
|
|
|
status->in1 = false;
|
|
|
|
|
status->in2 = false;
|
2024-12-12 15:38:11 +08:00
|
|
|
|
}
|
2024-12-19 01:08:39 +08:00
|
|
|
|
pwmControl(motor, speed);
|
2024-12-12 15:38:11 +08:00
|
|
|
|
}
|
|
|
|
|
|
2024-12-15 21:42:43 +08:00
|
|
|
|
float getDistance()
|
|
|
|
|
{
|
|
|
|
|
digitalWrite(HC_SR04_TRIG, HIGH);
|
|
|
|
|
delayMicroseconds(1);
|
|
|
|
|
digitalWrite(HC_SR04_TRIG, LOW);
|
|
|
|
|
float distance = pulseIn(HC_SR04_ECHO, HIGH); // 计数接收高电平时间
|
|
|
|
|
distance = distance * 340 / 2 / 10000; // 计算距离 1:声速:340M/S 2:实际距离为1/2声速距离 3:计数时钟为1US//温补公式:c=(331.45+0.61t/℃)m•s-1 (其中331.45是在0度)
|
|
|
|
|
return distance;
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-18 16:10:26 +08:00
|
|
|
|
class CarStorage
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
static bool isMounted;
|
|
|
|
|
|
|
|
|
|
static void init()
|
|
|
|
|
{
|
|
|
|
|
if (!SPIFFS.begin(true))
|
|
|
|
|
{
|
|
|
|
|
Serial.println("SPIFFS Mount Failed");
|
|
|
|
|
isMounted = false;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
isMounted = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static unsigned int getSensitivity()
|
|
|
|
|
{
|
|
|
|
|
if (!isMounted)
|
|
|
|
|
return 0xFF;
|
|
|
|
|
|
|
|
|
|
File file = SPIFFS.open("/sensitivity.txt", "r");
|
|
|
|
|
if (!file)
|
|
|
|
|
return 0xFF;
|
|
|
|
|
|
|
|
|
|
String sensitivity = file.readStringUntil('\n');
|
|
|
|
|
file.close();
|
|
|
|
|
|
|
|
|
|
return sensitivity.toInt();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void setSensitivity(unsigned int sensitivity)
|
|
|
|
|
{
|
|
|
|
|
if (!isMounted)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
File file = SPIFFS.open("/sensitivity.txt", "w");
|
|
|
|
|
file.println(sensitivity);
|
|
|
|
|
file.close();
|
|
|
|
|
}
|
|
|
|
|
};
|
2024-12-18 17:31:35 +08:00
|
|
|
|
bool CarStorage::isMounted = false;
|
2024-12-18 16:10:26 +08:00
|
|
|
|
|
2024-12-12 15:38:11 +08:00
|
|
|
|
void setup()
|
|
|
|
|
{
|
2024-12-19 01:08:39 +08:00
|
|
|
|
// 初始化<E5A78B><E58C96><EFBFBD>口
|
2024-12-12 15:38:11 +08:00
|
|
|
|
Serial.begin(115200);
|
2024-12-15 18:09:40 +08:00
|
|
|
|
|
2024-12-16 00:16:06 +08:00
|
|
|
|
// 初始化 BLE
|
2024-12-18 14:12:46 +08:00
|
|
|
|
BLEDevice::init(DEVICE_NAME);
|
2024-12-16 00:16:06 +08:00
|
|
|
|
pServer = BLEDevice::createServer();
|
|
|
|
|
pServer->setCallbacks(new CarBLEServerCallbacks());
|
|
|
|
|
|
|
|
|
|
BLEService *pService = pServer->createService(SERVICE_UUID);
|
|
|
|
|
|
|
|
|
|
// 创建 RX 特征值 (用于接收数据)
|
|
|
|
|
BLECharacteristic *pRxCharacteristic = pService->createCharacteristic(
|
|
|
|
|
CHARACTERISTIC_UUID_RX,
|
|
|
|
|
BLECharacteristic::PROPERTY_WRITE);
|
|
|
|
|
pRxCharacteristic->setCallbacks(new CarBLECharacteristicCallbacks());
|
|
|
|
|
|
|
|
|
|
// 创建 TX 特征值 (用于发送数据)
|
|
|
|
|
pTxCharacteristic = pService->createCharacteristic(
|
|
|
|
|
CHARACTERISTIC_UUID_TX,
|
|
|
|
|
BLECharacteristic::PROPERTY_NOTIFY);
|
|
|
|
|
pTxCharacteristic->addDescriptor(new BLE2902());
|
|
|
|
|
|
|
|
|
|
pService->start();
|
|
|
|
|
pServer->getAdvertising()->start();
|
2024-12-15 18:09:40 +08:00
|
|
|
|
|
2024-12-18 16:10:26 +08:00
|
|
|
|
// 初始化 EEPROM
|
|
|
|
|
CarStorage::init();
|
|
|
|
|
|
2024-12-12 15:38:11 +08:00
|
|
|
|
// 设置引脚模式
|
2024-12-15 18:09:40 +08:00
|
|
|
|
pinMode(STATUS_LED, OUTPUT);
|
|
|
|
|
digitalWrite(STATUS_LED, HIGH);
|
|
|
|
|
|
2024-12-12 15:38:11 +08:00
|
|
|
|
pinMode(MOTOR_A_PWMA, OUTPUT);
|
|
|
|
|
pinMode(MOTOR_A_AIN1, OUTPUT);
|
|
|
|
|
pinMode(MOTOR_A_AIN2, OUTPUT);
|
2024-12-15 18:09:40 +08:00
|
|
|
|
|
2024-12-12 15:38:11 +08:00
|
|
|
|
pinMode(MOTOR_B_PWMB, OUTPUT);
|
|
|
|
|
pinMode(MOTOR_B_BIN1, OUTPUT);
|
|
|
|
|
pinMode(MOTOR_B_BIN2, OUTPUT);
|
2024-12-15 18:09:40 +08:00
|
|
|
|
|
2024-12-12 15:38:11 +08:00
|
|
|
|
pinMode(MOTOR_C_PWMA, OUTPUT);
|
|
|
|
|
pinMode(MOTOR_C_AIN1, OUTPUT);
|
|
|
|
|
pinMode(MOTOR_C_AIN2, OUTPUT);
|
2024-12-15 18:09:40 +08:00
|
|
|
|
|
2024-12-12 15:38:11 +08:00
|
|
|
|
pinMode(MOTOR_D_PWMB, OUTPUT);
|
|
|
|
|
pinMode(MOTOR_D_BIN1, OUTPUT);
|
|
|
|
|
pinMode(MOTOR_D_BIN2, OUTPUT);
|
2024-12-15 18:09:40 +08:00
|
|
|
|
|
2024-12-15 21:42:43 +08:00
|
|
|
|
pinMode(HC_SR04_TRIG, OUTPUT);
|
2024-12-15 18:09:40 +08:00
|
|
|
|
pinMode(HC_SR04_ECHO, INPUT);
|
2024-12-12 15:38:11 +08:00
|
|
|
|
}
|
|
|
|
|
|
2024-12-16 00:16:06 +08:00
|
|
|
|
void handleSerialPacket(uint8_t *packet, int length, BLECharacteristic &characteristic)
|
2024-12-15 18:09:40 +08:00
|
|
|
|
{
|
2024-12-16 00:16:06 +08:00
|
|
|
|
if (!deviceConnected || length < 4)
|
|
|
|
|
return;
|
2024-12-15 18:09:40 +08:00
|
|
|
|
|
|
|
|
|
uint8_t packetLength = packet[1]; // 获取长度
|
|
|
|
|
uint8_t cmd = packet[2]; // 获取指令
|
2024-12-19 01:08:39 +08:00
|
|
|
|
uint8_t direction, speed, time, wheel, diffSpeed;
|
2024-12-15 21:42:43 +08:00
|
|
|
|
float distance;
|
2024-12-16 00:16:06 +08:00
|
|
|
|
uint8_t buffer[PACKET_MAX_LENGTH]; // 用于存储要发送的数据
|
|
|
|
|
int bufferIndex = 0;
|
2024-12-15 18:09:40 +08:00
|
|
|
|
|
|
|
|
|
switch (cmd)
|
|
|
|
|
{
|
2024-12-15 21:42:43 +08:00
|
|
|
|
case CMD_GET_BT_STATUS:
|
|
|
|
|
Serial.println("CMD_GET_BT_STATUS");
|
2024-12-16 00:16:06 +08:00
|
|
|
|
// 构建响应数据
|
|
|
|
|
buffer[0] = PACKET_T_HEAD;
|
|
|
|
|
buffer[1] = 0x05;
|
|
|
|
|
buffer[2] = CMD_GET_BT_STATUS;
|
|
|
|
|
buffer[3] = (uint8_t)(deviceConnected ? 0x01 : 0x00);
|
|
|
|
|
buffer[4] = PACKET_T_TAIL;
|
|
|
|
|
characteristic.setValue(buffer, 5);
|
|
|
|
|
characteristic.notify();
|
2024-12-15 18:09:40 +08:00
|
|
|
|
break;
|
|
|
|
|
|
2024-12-18 16:10:26 +08:00
|
|
|
|
case CMD_GET_SPIFFS_STATUS:
|
|
|
|
|
Serial.println("CMD_GET_SPIFFS_STATUS");
|
|
|
|
|
// 构建响应数据
|
|
|
|
|
buffer[0] = PACKET_T_HEAD;
|
|
|
|
|
buffer[1] = 0x05;
|
|
|
|
|
buffer[2] = CMD_GET_SPIFFS_STATUS;
|
|
|
|
|
buffer[3] = (uint8_t)(CarStorage::isMounted ? 0x01 : 0x00);
|
|
|
|
|
buffer[4] = PACKET_T_TAIL;
|
|
|
|
|
characteristic.setValue(buffer, 5);
|
|
|
|
|
characteristic.notify();
|
|
|
|
|
break;
|
|
|
|
|
|
2024-12-15 21:42:43 +08:00
|
|
|
|
case CMD_GET_DISTANCE:
|
|
|
|
|
distance = getDistance();
|
2024-12-16 00:16:06 +08:00
|
|
|
|
Serial.println("CMD_GET_DISTANCE, distance: " + String(distance));
|
|
|
|
|
// 构建响应数据
|
|
|
|
|
buffer[0] = PACKET_T_HEAD;
|
|
|
|
|
buffer[1] = 0x08;
|
|
|
|
|
buffer[2] = CMD_GET_DISTANCE;
|
|
|
|
|
floatToBytes(distance, &buffer[3]);
|
|
|
|
|
buffer[7] = PACKET_T_TAIL;
|
|
|
|
|
characteristic.setValue(buffer, 8);
|
|
|
|
|
characteristic.notify();
|
2024-12-15 21:42:43 +08:00
|
|
|
|
break;
|
|
|
|
|
|
2024-12-15 18:09:40 +08:00
|
|
|
|
case CMD_MOTOR_MOVE_CONTROL:
|
|
|
|
|
direction = packet[3];
|
|
|
|
|
speed = packet[4];
|
2024-12-16 00:16:06 +08:00
|
|
|
|
Serial.println("CMD_MOTOR_MOVE_CONTROL, direction: " + String(direction) + ", speed: " + String(speed));
|
2024-12-15 18:09:40 +08:00
|
|
|
|
// 移动
|
|
|
|
|
if (direction == 0x00)
|
|
|
|
|
{
|
2024-12-19 01:08:39 +08:00
|
|
|
|
motorControl('A', 0);
|
|
|
|
|
motorControl('B', 0);
|
|
|
|
|
motorControl('C', 0);
|
|
|
|
|
motorControl('D', 0);
|
2024-12-15 18:09:40 +08:00
|
|
|
|
}
|
|
|
|
|
// 前进
|
2024-12-16 00:16:06 +08:00
|
|
|
|
else if (direction == 0x01)
|
2024-12-15 18:09:40 +08:00
|
|
|
|
{
|
2024-12-19 01:08:39 +08:00
|
|
|
|
motorControl('A', speed);
|
|
|
|
|
motorControl('B', speed);
|
|
|
|
|
motorControl('C', speed);
|
|
|
|
|
motorControl('D', speed);
|
2024-12-15 18:09:40 +08:00
|
|
|
|
}
|
|
|
|
|
// 后退
|
2024-12-16 00:16:06 +08:00
|
|
|
|
else if (direction == 0x02)
|
2024-12-15 18:09:40 +08:00
|
|
|
|
{
|
2024-12-19 01:08:39 +08:00
|
|
|
|
motorControl('A', -speed);
|
|
|
|
|
motorControl('B', -speed);
|
|
|
|
|
motorControl('C', -speed);
|
|
|
|
|
motorControl('D', -speed);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case CMD_MOTOR_STEER_CONTROL:
|
|
|
|
|
direction = packet[3];
|
|
|
|
|
diffSpeed = packet[4];
|
|
|
|
|
Serial.println("CMD_MOTOR_STEER_CONTROL, direction: " + String(direction) + ", diffSpeed: " + String(diffSpeed));
|
|
|
|
|
// 左转
|
|
|
|
|
if (direction == 0x00)
|
|
|
|
|
{
|
|
|
|
|
motorControl('A', motorA.pwm - diffSpeed);
|
|
|
|
|
motorControl('B', motorB.pwm - diffSpeed);
|
|
|
|
|
}
|
|
|
|
|
// 右转
|
|
|
|
|
else if (direction == 0x01)
|
|
|
|
|
{
|
|
|
|
|
motorControl('C', motorC.pwm - diffSpeed);
|
|
|
|
|
motorControl('D', motorD.pwm - diffSpeed);
|
2024-12-15 18:09:40 +08:00
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case CMD_MOTOR_ROTATE_CONTROL:
|
|
|
|
|
direction = packet[3];
|
|
|
|
|
time = packet[4];
|
2024-12-16 00:16:06 +08:00
|
|
|
|
Serial.println("CMD_MOTOR_ROTATE_CONTROL, direction: " + String(direction) + ", time: " + String(time));
|
2024-12-15 18:09:40 +08:00
|
|
|
|
// 顺时针
|
|
|
|
|
if (direction == 0x00)
|
|
|
|
|
{
|
2024-12-19 01:08:39 +08:00
|
|
|
|
motorControl('A', 255);
|
|
|
|
|
motorControl('B', 255);
|
|
|
|
|
motorControl('C', -255);
|
|
|
|
|
motorControl('D', -255);
|
2024-12-15 18:09:40 +08:00
|
|
|
|
}
|
|
|
|
|
// 逆时针
|
2024-12-16 00:16:06 +08:00
|
|
|
|
else if (direction == 0x01)
|
2024-12-15 18:09:40 +08:00
|
|
|
|
{
|
2024-12-19 01:08:39 +08:00
|
|
|
|
motorControl('A', -255);
|
|
|
|
|
motorControl('B', -255);
|
|
|
|
|
motorControl('C', 255);
|
|
|
|
|
motorControl('D', 255);
|
2024-12-15 18:09:40 +08:00
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
2024-12-18 17:31:35 +08:00
|
|
|
|
case CMD_MOTOR_SINGLE_CONTROL:
|
|
|
|
|
wheel = packet[3];
|
|
|
|
|
direction = packet[4];
|
|
|
|
|
speed = packet[5];
|
|
|
|
|
Serial.println("CMD_MOTOR_SINGLE_CONTROL, wheel: " + String(wheel) + ", direction: " + String(direction) + ", speed: " + String(speed));
|
|
|
|
|
// 单轮控制
|
|
|
|
|
switch (wheel)
|
|
|
|
|
{
|
|
|
|
|
case 0x00:
|
2024-12-19 01:08:39 +08:00
|
|
|
|
motorControl('A', speed);
|
2024-12-18 17:31:35 +08:00
|
|
|
|
break;
|
|
|
|
|
case 0x01:
|
2024-12-19 01:08:39 +08:00
|
|
|
|
motorControl('B', speed);
|
2024-12-18 17:31:35 +08:00
|
|
|
|
break;
|
|
|
|
|
case 0x02:
|
2024-12-19 01:08:39 +08:00
|
|
|
|
motorControl('C', speed);
|
2024-12-18 17:31:35 +08:00
|
|
|
|
break;
|
|
|
|
|
case 0x03:
|
2024-12-19 01:08:39 +08:00
|
|
|
|
motorControl('D', speed);
|
2024-12-18 17:31:35 +08:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
2024-12-18 21:52:05 +08:00
|
|
|
|
case CMD_DEMO_PID:
|
|
|
|
|
Serial.println("CMD_DEMO_PID");
|
|
|
|
|
|
2024-12-19 01:08:39 +08:00
|
|
|
|
// PID 参数
|
|
|
|
|
float Kp = 2.0;
|
|
|
|
|
float Ki = 0.5;
|
|
|
|
|
float Kd = 0.1;
|
|
|
|
|
|
|
|
|
|
// PID 变量
|
|
|
|
|
float targetSpeed = 255; // 目标速度
|
|
|
|
|
float currentSpeed = 0; // 当前速度
|
|
|
|
|
float lastError = 0; // 上一次误差
|
|
|
|
|
float integral = 0; // 积分项
|
|
|
|
|
unsigned long lastTime = millis();
|
|
|
|
|
|
|
|
|
|
// 加速阶段 - PID控制
|
|
|
|
|
while (millis() - lastTime < 2000)
|
|
|
|
|
{ // 2秒加速过程
|
|
|
|
|
float error = targetSpeed - currentSpeed;
|
|
|
|
|
integral += error;
|
|
|
|
|
float derivative = error - lastError;
|
|
|
|
|
|
|
|
|
|
float output = Kp * error + Ki * integral + Kd * derivative;
|
|
|
|
|
output = constrain(output, 0, 255); // 限制输出范围
|
|
|
|
|
|
|
|
|
|
currentSpeed = output;
|
|
|
|
|
|
|
|
|
|
// 应用到所有电机
|
|
|
|
|
motorControl('A', output);
|
|
|
|
|
motorControl('B', output);
|
|
|
|
|
motorControl('C', output);
|
|
|
|
|
motorControl('D', output);
|
|
|
|
|
|
|
|
|
|
lastError = error;
|
|
|
|
|
delay(10); // 控制周期
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 停止阶段 - PID控制
|
|
|
|
|
lastTime = millis();
|
|
|
|
|
targetSpeed = 0; // 目标速度设为0
|
|
|
|
|
|
|
|
|
|
while (millis() - lastTime < 1000)
|
|
|
|
|
{ // 1秒减速过程
|
|
|
|
|
float error = targetSpeed - currentSpeed;
|
|
|
|
|
integral += error;
|
|
|
|
|
float derivative = error - lastError;
|
|
|
|
|
|
|
|
|
|
float output = Kp * error + Ki * integral + Kd * derivative;
|
|
|
|
|
output = constrain(output, 0, 255);
|
|
|
|
|
|
|
|
|
|
currentSpeed = output;
|
|
|
|
|
|
|
|
|
|
// 应用到所有电机
|
|
|
|
|
motorControl('A', output);
|
|
|
|
|
motorControl('B', output);
|
|
|
|
|
motorControl('C', output);
|
|
|
|
|
motorControl('D', output);
|
|
|
|
|
|
|
|
|
|
lastError = error;
|
|
|
|
|
delay(10);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 确保完全停止
|
|
|
|
|
motorControl('A', 0);
|
|
|
|
|
motorControl('B', 0);
|
|
|
|
|
motorControl('C', 0);
|
|
|
|
|
motorControl('D', 0);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case CMD_DEMO_PATH:
|
|
|
|
|
Serial.println("CMD_DEMO_PATH");
|
2024-12-18 21:52:05 +08:00
|
|
|
|
|
2024-12-19 01:08:39 +08:00
|
|
|
|
// 第一段:全速前进1秒
|
|
|
|
|
currentSpeed = 255;
|
|
|
|
|
unsigned long startTime = millis();
|
|
|
|
|
while (millis() - startTime < 1000)
|
|
|
|
|
{
|
|
|
|
|
motorControl('A', currentSpeed);
|
|
|
|
|
motorControl('B', currentSpeed);
|
|
|
|
|
motorControl('C', currentSpeed);
|
|
|
|
|
motorControl('D', currentSpeed);
|
|
|
|
|
delay(10);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 第二段:左转90度
|
|
|
|
|
startTime = millis();
|
|
|
|
|
while (millis() - startTime < 500)
|
|
|
|
|
{ // 假设500ms可以转90度
|
|
|
|
|
// 左侧电机反转,右侧电机正转
|
|
|
|
|
motorControl('A', 200);
|
|
|
|
|
motorControl('B', 200);
|
|
|
|
|
motorControl('C', 200);
|
|
|
|
|
motorControl('D', 200);
|
|
|
|
|
delay(10);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 第三段:全速前进1秒
|
|
|
|
|
startTime = millis();
|
|
|
|
|
while (millis() - startTime < 1000)
|
|
|
|
|
{
|
|
|
|
|
motorControl('A', currentSpeed);
|
|
|
|
|
motorControl('B', currentSpeed);
|
|
|
|
|
motorControl('C', currentSpeed);
|
|
|
|
|
motorControl('D', currentSpeed);
|
|
|
|
|
delay(10);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 最后停止所有电机
|
|
|
|
|
motorControl('A', 0);
|
|
|
|
|
motorControl('B', 0);
|
|
|
|
|
motorControl('C', 0);
|
|
|
|
|
motorControl('D', 0);
|
2024-12-18 21:52:05 +08:00
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
2024-12-15 18:09:40 +08:00
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-16 00:16:06 +08:00
|
|
|
|
void processSerialIncomingByte(uint8_t incomingByte, BLECharacteristic &characteristic)
|
2024-12-12 15:38:11 +08:00
|
|
|
|
{
|
2024-12-15 18:09:40 +08:00
|
|
|
|
static uint8_t expectedLength = 0;
|
|
|
|
|
|
|
|
|
|
packetIndex++;
|
|
|
|
|
|
|
|
|
|
if (incomingByte == PACKET_R_HEAD && !isReceivingPacket)
|
|
|
|
|
{
|
|
|
|
|
isReceivingPacket = true;
|
|
|
|
|
packetIndex = 0;
|
|
|
|
|
packetBuffer[packetIndex] = incomingByte;
|
|
|
|
|
}
|
|
|
|
|
else if (isReceivingPacket)
|
|
|
|
|
{
|
|
|
|
|
if (packetIndex < PACKET_MAX_LENGTH)
|
|
|
|
|
{
|
|
|
|
|
packetBuffer[packetIndex] = incomingByte;
|
|
|
|
|
|
|
|
|
|
// 第二个字节是包体长度
|
|
|
|
|
if (packetIndex == 1)
|
|
|
|
|
{
|
|
|
|
|
expectedLength = incomingByte;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 到达预期长度时检查包尾
|
|
|
|
|
if (packetIndex == expectedLength - 1)
|
|
|
|
|
{
|
|
|
|
|
isReceivingPacket = false;
|
|
|
|
|
if (incomingByte == PACKET_R_TAIL)
|
|
|
|
|
{
|
2024-12-16 00:16:06 +08:00
|
|
|
|
handleSerialPacket(packetBuffer, packetIndex + 1, *pTxCharacteristic);
|
2024-12-15 18:09:40 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
isReceivingPacket = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void updateStatusLED()
|
|
|
|
|
{
|
2024-12-16 00:16:06 +08:00
|
|
|
|
if (deviceConnected)
|
2024-12-15 18:09:40 +08:00
|
|
|
|
{
|
|
|
|
|
digitalWrite(STATUS_LED, HIGH);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
unsigned long currentMillis = millis();
|
|
|
|
|
if (currentMillis - lastLedToggle >= LED_FLASH_INTERVAL)
|
|
|
|
|
{
|
|
|
|
|
lastLedToggle = currentMillis;
|
|
|
|
|
ledState = !ledState;
|
|
|
|
|
digitalWrite(STATUS_LED, ledState);
|
2024-12-12 15:38:11 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
2024-12-15 18:09:40 +08:00
|
|
|
|
}
|
|
|
|
|
|
2024-12-19 01:08:39 +08:00
|
|
|
|
void sendMotorStatus()
|
|
|
|
|
{
|
|
|
|
|
unsigned long currentMillis = millis();
|
|
|
|
|
if (currentMillis - lastMotorStatusUpdate >= MOTOR_STATUS_INTERVAL)
|
|
|
|
|
{
|
|
|
|
|
lastMotorStatusUpdate = currentMillis;
|
|
|
|
|
|
|
|
|
|
uint8_t buffer[PACKET_MAX_LENGTH];
|
|
|
|
|
buffer[0] = PACKET_T_HEAD;
|
|
|
|
|
buffer[1] = 0x0C;
|
|
|
|
|
buffer[2] = CMD_STATUS_MOTOR;
|
|
|
|
|
buffer[3] = (motorA.in2 << 1) | motorA.in1;
|
|
|
|
|
buffer[4] = motorA.pwm;
|
|
|
|
|
buffer[5] = (motorB.in2 << 1) | motorB.in1;
|
|
|
|
|
buffer[6] = motorB.pwm;
|
|
|
|
|
buffer[7] = (motorC.in2 << 1) | motorC.in1;
|
|
|
|
|
buffer[8] = motorC.pwm;
|
|
|
|
|
buffer[9] = (motorD.in2 << 1) | motorD.in1;
|
|
|
|
|
buffer[10] = motorD.pwm;
|
|
|
|
|
buffer[11] = PACKET_T_TAIL;
|
|
|
|
|
pTxCharacteristic->setValue(buffer, 12);
|
|
|
|
|
pTxCharacteristic->notify();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-15 18:09:40 +08:00
|
|
|
|
void loop()
|
|
|
|
|
{
|
2024-12-16 00:16:06 +08:00
|
|
|
|
updateStatusLED();
|
2024-12-19 01:08:39 +08:00
|
|
|
|
if (deviceConnected)
|
|
|
|
|
{
|
|
|
|
|
sendMotorStatus();
|
|
|
|
|
}
|
2024-12-12 15:38:11 +08:00
|
|
|
|
}
|