bafang-esp32/src/main.cpp

296 lines
7.7 KiB
C++

#include <Arduino.h>
#include <WiFi.h>
#include <ArduinoOTA.h>
#include <BluetoothSerial.h>
#include "DummyStream.h"
#include "bafang.h"
#include "commands.h"
#include "utils.h"
#include "main.h"
#define DISPLAY_RX 18
#define DISPLAY_TX 19
#define MOTOR_RX 16
#define MOTOR_TX 17
#define THROTTLE_GPIO 32
#define BRAKE_GPIO 25
WiFiServer server(23);
WiFiClient serverClient;
BluetoothSerial SerialBT;
DummyStream SerialDummy;
const char g_WIFI_SSID[] = "BotoX";
const char g_WIFI_Passphrase[] = "";
namespace Main
{
int g_CurrentChannel = CHANNEL_HWSERIAL;
int g_Debug[NUM_CHANNELS];
char g_SerialBuffer[NUM_CHANNELS][255];
int g_SerialBufferPos[NUM_CHANNELS];
void init()
{
setCpuFrequencyMhz(80);
Serial.begin(115200);
Serial.println("BOOTED!");
pinMode(THROTTLE_GPIO, INPUT);
pinMode(BRAKE_GPIO, INPUT);
Serial1.begin(1200, SERIAL_8N1, DISPLAY_RX, DISPLAY_TX); // Display
Serial2.begin(1200, SERIAL_8N1, MOTOR_RX, MOTOR_TX); // Motor
Bafang::DisplaySerial = Serial1;
Bafang::MotorSerial = Serial2;
ArduinoOTA.onStart([]() {
String type;
if (ArduinoOTA.getCommand() == U_FLASH)
type = "sketch";
else // U_SPIFFS
type = "filesystem";
// NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
channel()->println("Start updating " + type);
})
.onEnd([]() {
channel()->println("\nEnd");
})
.onProgress([](unsigned int progress, unsigned int total) {
channel()->printf("Progress: %u%%\r", (progress / (total / 100)));
})
.onError([](ota_error_t error) {
channel()->printf("Error[%u]: ", error);
if (error == OTA_AUTH_ERROR) channel()->println("Auth Failed");
else if (error == OTA_BEGIN_ERROR) channel()->println("Begin Failed");
else if (error == OTA_CONNECT_ERROR) channel()->println("Connect Failed");
else if (error == OTA_RECEIVE_ERROR) channel()->println("Receive Failed");
else if (error == OTA_END_ERROR) channel()->println("End Failed");
});
}
Stream* channel(int num)
{
if(num == -1)
num = g_CurrentChannel;
if(num == CHANNEL_BTSERIAL && SerialBT.hasClient())
return &SerialBT;
else if(num == CHANNEL_TCPSERIAL && serverClient)
return &serverClient;
else if(num == CHANNEL_HWSERIAL)
return &Serial;
return &SerialDummy;
}
void loop()
{
Bafang::tick();
if(WiFi.isConnected())
{
ArduinoOTA.handle();
if(server.hasClient())
{
if(serverClient) // disconnect current client if any
serverClient.stop();
serverClient = server.available();
}
if(!serverClient)
serverClient.stop();
}
for(int i = 0; i < NUM_CHANNELS; i++)
{
while(channel(i)->available())
{
g_CurrentChannel = i;
int c = channel(i)->read();
if(c == '\r' || c == '\n' || g_SerialBufferPos[i] == sizeof(*g_SerialBuffer))
{
g_SerialBuffer[i][g_SerialBufferPos[i]] = 0;
if(g_SerialBufferPos[i])
Commands::parseLine(g_SerialBuffer[i]);
g_SerialBufferPos[i] = 0;
continue;
}
g_SerialBuffer[i][g_SerialBufferPos[i]] = c;
++g_SerialBufferPos[i];
}
}
SecretFunction();
}
void SecretFunction()
{
static bool s_bThrottle = false;
static unsigned long s_firstThrottleTime = 0;
static int s_throttleCount = 0;
static bool s_bTriggered = false;
bool bBrake = !digitalRead(BRAKE_GPIO);
uint16_t throttleVal = analogRead(THROTTLE_GPIO);
if(bBrake)
{
if(throttleVal > 1100 && !s_bThrottle)
{
s_bThrottle = true;
if(!s_throttleCount)
s_firstThrottleTime = millis();
s_throttleCount++;
}
else if(throttleVal < 1000 && s_bThrottle)
{
s_bThrottle = false;
}
if(!s_bTriggered && s_throttleCount && millis() - s_firstThrottleTime > 3000)
{
s_bTriggered = true;
SecretAction(s_throttleCount);
}
}
else
{
if(!s_bTriggered && s_throttleCount)
SecretAction(s_throttleCount);
s_bTriggered = false;
s_firstThrottleTime = 0;
s_throttleCount = 0;
}
}
void SecretAction(int code)
{
if(g_Debug[g_CurrentChannel])
channel()->printf("SecretAction: %d\n", code);
switch(code)
{
case 3:
case 4: {
motorProfile(1);
} break;
case 5: {
toggleBluetooth();
} break;
case 6: {
toggleWiFi();
} break;
}
}
void toggleBluetooth(int mode)
{
if(mode == -1)
mode = !btStarted();
if(mode == 1)
{
if(btStarted())
{
channel()->println("Bluetooth already enabled.");
return;
}
channel()->println("Enabling bluetooth...");
SerialBT.begin("ESP32-BAFANG");
}
else if(mode == 0)
{
if(!btStarted())
{
channel()->println("Bluetooth already disabled.");
return;
}
channel()->println("Disabling bluetooth...");
SerialBT.end();
}
}
void toggleWiFi(int mode)
{
if(mode == -1)
mode = !(WiFi.getMode() == WIFI_MODE_NULL);
if(mode == 1)
{
if(WiFi.getMode() != WIFI_MODE_NULL)
{
channel()->println("WiFi already enabled.");
return;
}
channel()->println("Enabling WiFi...");
WiFi.begin(g_WIFI_SSID, g_WIFI_Passphrase);
WiFi.setHostname("ESP32-BAFANG");
ArduinoOTA.begin();
server.begin();
server.setNoDelay(true);
}
else if(mode == 0)
{
if(WiFi.getMode() == WIFI_MODE_NULL)
{
channel()->println("WiFi already disabled.");
return;
}
channel()->println("Disabling WiFi...");
ArduinoOTA.end();
server.end();
WiFi.disconnect(true, true);
}
}
void motorProfile(int profile)
{
switch(profile)
{
case 0: { // Unlimited
const uint8_t basic[] = {0x16,0x52,0x18,0x26,0x1E,0x01,0x14,0x19,0x1E,0x23,0x28,0x2D,0x32,0x37,0x64,0x01,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x3A,0x01,0xFF};
const uint8_t pedal[] = {0x16,0x53,0x0B,0x03,0xFF,0x28,0x0A,0x04,0x04,0xFF,0x0A,0x08,0x00,0x50,0xFB};
const uint8_t throttle[] = {0x16,0x54,0x06,0x0B,0x2A,0x01,0x09,0x28,0x0F,0xD0};
Bafang::QueueMotor(basic, sizeof(basic), Bafang::PrintCallback, (void *)g_CurrentChannel, 500);
Bafang::QueueMotor(pedal, sizeof(pedal), Bafang::PrintCallback, (void *)g_CurrentChannel, 500);
Bafang::QueueMotor(throttle, sizeof(throttle), Bafang::PrintCallback, (void *)g_CurrentChannel, 500);
} break;
case 1: { // Limited 10A 26km/h
const uint8_t basic[] = {0x16,0x52,0x18,0x26,0x0A,0x01,0x14,0x19,0x1E,0x23,0x28,0x2D,0x32,0x37,0x64,0x01,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x3A,0x01,0xEB};
const uint8_t pedal[] = {0x16,0x53,0x0B,0x03,0xFF,0x1A,0x0A,0x04,0x04,0xFF,0x0A,0x08,0x00,0x50,0xED};
const uint8_t throttle[] = {0x16,0x54,0x06,0x0B,0x2A,0x01,0x09,0x1A,0x0F,0xC2};
Bafang::QueueMotor(basic, sizeof(basic), Bafang::PrintCallback, (void *)g_CurrentChannel, 500);
Bafang::QueueMotor(pedal, sizeof(pedal), Bafang::PrintCallback, (void *)g_CurrentChannel, 500);
Bafang::QueueMotor(throttle, sizeof(throttle), Bafang::PrintCallback, (void *)g_CurrentChannel, 500);
} break;
}
}
}
void setup()
{
Main::init();
}
void loop()
{
Main::loop();
}