EFeru / hoverboard-firmware-hack-FOC

With Field Oriented Control (FOC)
GNU General Public License v3.0
1.06k stars 884 forks source link

USART with Arduino and Bluetooth #273

Open zeehead opened 2 years ago

zeehead commented 2 years ago

Your System

Hi, I'm amazed by the work you guys have done with this!

I'm trying to make a little 2 Wheel Drive car and want to control it via Arduino with an HC-05 Bluetooth module. I created a joystick app via RemoteXY and it worked with other projects.

Now, while uploading the FW to the Hoverboard worked fine, I'm not sure how to modify the example code, so I can use it for my project. I tried simply copy pasting the example and then adding my Arduino code. But it won't connect to the app. What I'm trying to do is simply control the speed and steering with the joystick app. For this, I wanted to place them on pins 10 and 11, and the serial pins are 2 and 3, like they are in your code.

I guess there's something wrong with the serial stuff since they are in there twice, but I'm not sure how to program them right in this case to make it work with the Firmware.

It's pretty messed up, but here's my code:

//////////////////////////////////////////////
//        RemoteXY include library          //
//////////////////////////////////////////////

// RemoteXY select connection mode and include library
#define REMOTEXY_MODE__SOFTSERIAL
#include <SoftwareSerial.h>

#include <RemoteXY.h>

// RemoteXY connection settings
#define REMOTEXY_SERIAL_RX 2
#define REMOTEXY_SERIAL_TX 3
#define REMOTEXY_SERIAL_SPEED 9600

// RemoteXY configurate
#pragma pack(push, 1)
uint8_t RemoteXY_CONF[] = { 255, 2, 0, 0, 0, 12, 0, 14, 31, 1,
                            5, 32, 16, 62, 30, 30, 2, 26, 31 };

// this structure defines all the variables and events of your control interface
struct {

  // input variables
  int8_t joystick_1_x;  // =-100..100 x-coordinate joystick position
  int8_t joystick_1_y;  // =-100..100 y-coordinate joystick position

  // other variable
  uint8_t connect_flag;  // =1 if wire connected, else =0

} RemoteXY;
#pragma pack(pop)

/////////////////////////////////////////////
//           END RemoteXY include          //
/////////////////////////////////////////////

#define HOVER_SERIAL_BAUD   9600      // [-] Baud rate for HoverSerial (used to communicate with the hoverboard)
#define SERIAL_BAUD         9600      // [-] Baud rate for built-in Serial (used for the Serial Monitor)
#define START_FRAME         0xABCD      // [-] Start frme definition for reliable serial communication
#define TIME_SEND           100         // [ms] Sending time interval
#define SPEED_MAX_TEST      300         // [-] Maximum speed for testing
#define SPEED_STEP          20          // [-] Speed step
// #define DEBUG_RX                        // [-] Debug received data. Prints all bytes to serial (comment-out to disable)

#include <SoftwareSerial.h>
SoftwareSerial HoverSerial(2,3);        // RX, TX

// Global variables
uint8_t idx = 0;                        // Index for new data pointer
uint16_t bufStartFrame;                 // Buffer Start Frame
byte *p;                                // Pointer declaration for the new received data
byte incomingByte;
byte incomingBytePrev;

typedef struct{
   uint16_t start;
   int16_t  steer;
   int16_t  speed;
   uint16_t checksum;
} SerialCommand;
SerialCommand Command;

typedef struct{
   uint16_t start;
   int16_t  cmd1;
   int16_t  cmd2;
   int16_t  speedR_meas;
   int16_t  speedL_meas;
   int16_t  batVoltage;
   int16_t  boardTemp;
   uint16_t cmdLed;
   uint16_t checksum;
} SerialFeedback;
SerialFeedback Feedback;
SerialFeedback NewFeedback;

#include <Servo.h>

Servo speedwire;
Servo steerwire;

void Speed(int val) {
  if (Serial.available() > 0) {
    val = Serial.read();
  }

  Serial.println(val);
  val = map(val, -100, 100, -1000, 1000);

  speedwire.writeMicroseconds(val);
}

void Steer(int val) {
  if (Serial.available() > 0) {
    val = Serial.read();
  }

  Serial.println(val);
  val = map(val, -100, 100, -1000, 1000);

  steerwire.writeMicroseconds(val);
}

// ########################## SETUP ##########################
void setup() 
{
  RemoteXY_Init();
  speedwire.attach(10);
  steerwire.attach(11);
  Serial.begin(SERIAL_BAUD);
  Serial.println("Hoverboard Serial v1.0");

  HoverSerial.begin(HOVER_SERIAL_BAUD);
  pinMode(LED_BUILTIN, OUTPUT);
}

// ########################## SEND ##########################
void Send(int16_t uSteer, int16_t uSpeed)
{
  // Create command
  Command.start    = (uint16_t)START_FRAME;
  Command.steer    = (int16_t)uSteer;
  Command.speed    = (int16_t)uSpeed;
  Command.checksum = (uint16_t)(Command.start ^ Command.steer ^ Command.speed);

  // Write to Serial
  HoverSerial.write((uint8_t *) &Command, sizeof(Command)); 
}

// ########################## RECEIVE ##########################
void Receive()
{
    // Check for new data availability in the Serial buffer
    if (HoverSerial.available()) {
        incomingByte      = HoverSerial.read();                                   // Read the incoming byte
        bufStartFrame   = ((uint16_t)(incomingByte) << 8) | incomingBytePrev;       // Construct the start frame
    }
    else {
        return;
    }

  // If DEBUG_RX is defined print all incoming bytes
  #ifdef DEBUG_RX
        Serial.print(incomingByte);
        return;
    #endif

    // Copy received data
    if (bufStartFrame == START_FRAME) {                     // Initialize if new data is detected
        p       = (byte *)&NewFeedback;
        *p++    = incomingBytePrev;
        *p++    = incomingByte;
        idx     = 2;    
    } else if (idx >= 2 && idx < sizeof(SerialFeedback)) {  // Save the new received data
        *p++    = incomingByte; 
        idx++;
    }   

    // Check if we reached the end of the package
    if (idx == sizeof(SerialFeedback)) {
        uint16_t checksum;
        checksum = (uint16_t)(NewFeedback.start ^ NewFeedback.cmd1 ^ NewFeedback.cmd2 ^ NewFeedback.speedR_meas ^ NewFeedback.speedL_meas
                            ^ NewFeedback.batVoltage ^ NewFeedback.boardTemp ^ NewFeedback.cmdLed);

        // Check validity of the new data
        if (NewFeedback.start == START_FRAME && checksum == NewFeedback.checksum) {
            // Copy the new data
            memcpy(&Feedback, &NewFeedback, sizeof(SerialFeedback));

            // Print data to built-in Serial
            Serial.print("1: ");   Serial.print(Feedback.cmd1);
            Serial.print(" 2: ");  Serial.print(Feedback.cmd2);
            Serial.print(" 3: ");  Serial.print(Feedback.speedR_meas);
            Serial.print(" 4: ");  Serial.print(Feedback.speedL_meas);
            Serial.print(" 5: ");  Serial.print(Feedback.batVoltage);
            Serial.print(" 6: ");  Serial.print(Feedback.boardTemp);
            Serial.print(" 7: ");  Serial.println(Feedback.cmdLed);
        } else {
          Serial.println("Non-valid data skipped");
        }
        idx = 0;    // Reset the index (it prevents to enter in this if condition in the next cycle)
    }

    // Update previous states
    incomingBytePrev = incomingByte;
}

// ########################## LOOP ##########################

unsigned long iTimeSend = 0;
int iTest = 0;
int iStep = SPEED_STEP;

void loop(void)
{ 
  unsigned long timeNow = millis();

  // Check for new received data
  Receive();

  // Send commands
  if (iTimeSend > timeNow) return;
  iTimeSend = timeNow + TIME_SEND;
  Send(0, iTest);

  // Calculate test command signal
  iTest += iStep;

  // invert step if reaching limit
  if (iTest >= SPEED_MAX_TEST || iTest <= -SPEED_MAX_TEST)
    iStep = -iStep;

  // Blink the LED
  digitalWrite(LED_BUILTIN, (timeNow%2000)<1000);

  RemoteXY_Handler();

  Speed(RemoteXY.joystick_1_x);
  Steer(RemoteXY.joystick_1_y);
}

Could you help me out here? I'd appreciate any input on this!

Candas1 commented 2 years ago

Hi,

But have you tried the example arduino code without changing it ? Is it working ?

zeehead commented 2 years ago

Yes, if I dont' change it, the wheel spins for a short time and then stops, without giving me any beeps, except for the back beep. So I guess it's working, right?

If using RemoteXY is too messy, maybe you could give me some hints how to do it with a Blynk legacy joystick. If that's somehow easier. Or just instruct me in general, how to implement Bluetooth in this Arduino code, so I can modify it? As I said, I'd appreciate any input.

Candas1 commented 2 years ago

You need to find a working example with blink online, there are many. And then you can combine with the arduino code here.

There are more details here how the Usart/Serial variant works. https://github.com/EFeru/hoverboard-firmware-hack-FOC/wiki/Variant-USART

ApacheBlack commented 2 years ago

Hey! Did you solve it? May i see the code to install in the arduino? I am up to the same as you. Thanks!

zeehead commented 2 years ago

Hey! Did you solve it? May i see the code to install in the arduino? I am up to the same as you. Thanks!

Unfortunately no, I can't seem to find the mistake.

What do you have so far? Maybe we can figure it out together.

ApacheBlack commented 2 years ago

Did you try the board with other human input to check is still working fine? Did you try this: https://github.com/salviador/Hoverboard-Board-Hack ? They say this one is easier and its just about plug the BT module and then control it with the app. My problem with this one is i Cant compile it. I got an error. So i am exploring different ways.

EFeru commented 2 years ago

Hi @zeehead, at a very first look the baud rate is different in your code than what the main board expects.

ApacheBlack commented 2 years ago

@EFeru Thanks for the advice! should be also changed in hoverboard mine.c to 9600 bouds ? or just change it in the arduino code like:

define HOVER_SERIAL_BAUD 115200

define SERIAL_BAUD 9600

Could work like this for example?

EFeru commented 2 years ago

I think is better to do it on the Arduino side. Your suggestion should be ok.

ApacheBlack commented 2 years ago

Thanks! but I see also here ( but as far as I know), the RemoteXY tool wont give you the choice of selecting that serial Boud rate. and maybe by just changing it in arduino it means you have to change something in the app. you get me?

EFeru commented 2 years ago

I never used RemoteXY app so I'm not sure what are the options there. I only used Blink app and there as far as i know the baud rate is configurable.

zeehead commented 2 years ago

Hi, sorry for my late reply. First off, thanks for your input!

Yeah, I gave up on RemoteXY and started using Blynk legacy, since it's a lot simpler to use.

Now, what I don't quite understand is this: So there's a Hover_Serial_Baud and a Serial_Baud in the example code. Are those two like linked together in the HoverSerial below?

As I mentioned, I want to use a Bluetooth module. So, if I connect the Bluetooth module to Pins 2 and 3: What do I do with the wires from the Hoverboard? As shown in my previous sketch, I'd like to connect the speed and steering to 10 and 11. Can i just connect them then?

But my question is basically, how do I merge these two below:

This is the relevant part, copied/pasted from the example:

#define HOVER_SERIAL_BAUD   115200      // [-] Baud rate for HoverSerial (used to communicate with the hoverboard)
#define SERIAL_BAUD         9600      // [-] Baud rate for built-in Serial (used for the Serial Monitor)
#define START_FRAME         0xABCD      // [-] Start frme definition for reliable serial communication
#define TIME_SEND           100         // [ms] Sending time interval
#define SPEED_MAX_TEST      300         // [-] Maximum speed for testing
#define SPEED_STEP          20          // [-] Speed step
// #define DEBUG_RX                        // [-] Debug received data. Prints all bytes to serial (comment-out to disable)

#include <SoftwareSerial.h>
SoftwareSerial HoverSerial(2,3);        // RX, TX

And this ist the Blynk template for the HC-05/06 module:

#define BLYNK_USE_DIRECT_CONNECT

// You could use a spare Hardware Serial on boards that have it (like Mega)
#include <SoftwareSerial.h>
SoftwareSerial DebugSerial(2, 3); // RX, TX

#define BLYNK_PRINT DebugSerial

/* Fill-in your Template ID (only if using Blynk.Cloud) */
//#define BLYNK_TEMPLATE_ID   "YourTemplateID"

#include <BlynkSimpleSerialBLE.h>

// You should get Auth Token in the Blynk App.
// Go to the Project Settings (nut icon).
char auth[] = "YourAuthToken";

void setup()
{
  // Debug console
  DebugSerial.begin(9600);

  DebugSerial.println("Waiting for connections...");

  // Blynk will work through Serial
  // 9600 is for HC-06. For HC-05 default speed is 38400
  // Do not read or write this serial manually in your sketch
  Serial.begin(9600);
  Blynk.begin(Serial, auth);
}

void loop()
{
  Blynk.run();
}

So, do I just need to replace the HoverSerial with the DebugSerial? And how do I set up the Hover_Serial_Baud and Serial_Baud in the Blynk sketch? Or do I need to set up two seperate serial connections somehow?

Sorry, if I'm being a total noob here. I'm kind of stuck exactly on this issue.

I hope I could explain my problem properly.

P.S. I tried changing the Baudrate on the Mainboard config.h, to 9600, that's why I had a 9600 Baudrate in my previous sketch. But since you're saying, it's better to change it on the Arduino side, I went and changed it back again in the config file. So my only challenge is merging these two sketches.

ApacheBlack commented 2 years ago

I am also unable to make it work and I am looking for someone in Fiverr to do it for me so i can finish with this step of my project and keeps going on. I share the code when i find someone to do the job, but i will recommend keep learning C like i will so one day we can do it. @EFeru you probably dont need my money but i am will to pay you instead if you want to, I wont judge you if you wont, you already made alot for us 💪🏼thanks.

zeehead commented 2 years ago

Hey, so, I finally got around to test a bit, but I can't seem to make this work.

This is how I tried to combine my sketches, but I get 3 beeps (serial connection timeout). What does that exactly mean? Does it mean my serial connection is wrong somewhere? Do you have any suggestions for improvement for me?


#define HOVER_SERIAL_BAUD   115200 // [-] Baud rate for HoverSerial (used to communicate with the hoverboard)
#define SERIAL_BAUD         115200 // [-] Baud rate for built-in Serial (used for the Serial Monitor)
#define START_FRAME         0xABCD      // [-] Start frme definition for reliable serial communication
#define TIME_SEND           100         // [ms] Sending time interval
#define SPEED_MAX_TEST      300         // [-] Maximum speed for testing
#define SPEED_STEP          20          // [-] Speed step
// #define DEBUG_RX                        // [-] Debug received data. Prints all bytes to serial (comment-out to disable)

// #define BLYNK_TEMPLATE_ID           "TMPLxxxxxx"
#define BLYNK_DEVICE_NAME           "Arduino UNO"
#define BLYNK_AUTH_TOKEN            "xxxzzzz"
#define BLYNK_PRINT HoverSerial
#define BLYNK_USE_DIRECT_CONNECT

#include <SoftwareSerial.h>
SoftwareSerial HoverSerial(2,3);        // RX, TX

#include <BlynkSimpleSerialBLE.h>

char auth[] = BLYNK_AUTH_TOKEN;

BLYNK_WRITE(V1) {
  int val1 = param[0].asInt();
  int val2 = param[1].asInt();
}

// Global variables
uint8_t idx = 0;                        // Index for new data pointer
uint16_t bufStartFrame;                 // Buffer Start Frame
byte *p;                                // Pointer declaration for the new received data
byte incomingByte;
byte incomingBytePrev;

typedef struct{
   uint16_t start;
   int16_t  steer;
   int16_t  speed;
   uint16_t checksum;
} SerialCommand;
SerialCommand Command;

typedef struct{
   uint16_t start;
   int16_t  cmd1;
   int16_t  cmd2;
   int16_t  speedR_meas;
   int16_t  speedL_meas;
   int16_t  batVoltage;
   int16_t  boardTemp;
   uint16_t cmdLed;
   uint16_t checksum;
} SerialFeedback;
SerialFeedback Feedback;
SerialFeedback NewFeedback;

#include <Servo.h>

Servo speedwire;
Servo steerwire;

void Speed(int val1) {
  if (Serial.available()) {
    val1 = Serial.read();
  }

  Serial.println(val1);
  val1 = map(val1, 0, 255, -1000, 1000);

  speedwire.writeMicroseconds(val1);
}

void Steer(int val2) {
  if (Serial.available()) {
    val2 = Serial.read();
  }

  Serial.println(val2);
  val2 = map(val2, 0, 255, -1000, 1000);

  steerwire.writeMicroseconds(val2);
}

// ########################## SETUP ##########################
void setup() 
{
  speedwire.attach(10);
  steerwire.attach(11);
  Serial.begin(SERIAL_BAUD);
  Serial.println("Hoverboard Serial v1.0");

  HoverSerial.begin(HOVER_SERIAL_BAUD);

  Blynk.begin(HoverSerial, auth);
  Serial.println("Waiting for connections...");
  pinMode(LED_BUILTIN, OUTPUT);
}

// ########################## SEND ##########################
void Send(int16_t uSteer, int16_t uSpeed)
{
  // Create command
  Command.start    = (uint16_t)START_FRAME;
  Command.steer    = (int16_t)uSteer;
  Command.speed    = (int16_t)uSpeed;
  Command.checksum = (uint16_t)(Command.start ^ Command.steer ^ Command.speed);

  // Write to Serial
  HoverSerial.write((uint8_t *) &Command, sizeof(Command));
}

// ########################## RECEIVE ##########################
void Receive()
{
    // Check for new data availability in the Serial buffer
    if (HoverSerial.available()) {
        incomingByte      = HoverSerial.read();                                   // Read the incoming byte
        bufStartFrame   = ((uint16_t)(incomingByte) << 8) | incomingBytePrev;       // Construct the start frame
    }
    else {
        return;
    }

  // If DEBUG_RX is defined print all incoming bytes
  #ifdef DEBUG_RX
        Serial.print(incomingByte);
        return;
    #endif

    // Copy received data
    if (bufStartFrame == START_FRAME) {                     // Initialize if new data is detected
        p       = (byte *)&NewFeedback;
        *p++    = incomingBytePrev;
        *p++    = incomingByte;
        idx     = 2;    
    } else if (idx >= 2 && idx < sizeof(SerialFeedback)) {  // Save the new received data
        *p++    = incomingByte; 
        idx++;
    }   

    // Check if we reached the end of the package
    if (idx == sizeof(SerialFeedback)) {
        uint16_t checksum;
        checksum = (uint16_t)(NewFeedback.start ^ NewFeedback.cmd1 ^ NewFeedback.cmd2 ^ NewFeedback.speedR_meas ^ NewFeedback.speedL_meas
                            ^ NewFeedback.batVoltage ^ NewFeedback.boardTemp ^ NewFeedback.cmdLed);

        // Check validity of the new data
        if (NewFeedback.start == START_FRAME && checksum == NewFeedback.checksum) {
            // Copy the new data
            memcpy(&Feedback, &NewFeedback, sizeof(SerialFeedback));

            // Print data to built-in Serial
            Serial.print("1: ");   Serial.print(Feedback.cmd1);
            Serial.print(" 2: ");  Serial.print(Feedback.cmd2);
            Serial.print(" 3: ");  Serial.print(Feedback.speedR_meas);
            Serial.print(" 4: ");  Serial.print(Feedback.speedL_meas);
            Serial.print(" 5: ");  Serial.print(Feedback.batVoltage);
            Serial.print(" 6: ");  Serial.print(Feedback.boardTemp);
            Serial.print(" 7: ");  Serial.println(Feedback.cmdLed);
        } else {
          Serial.println("Non-valid data skipped");
        }
        idx = 0;    // Reset the index (it prevents to enter in this if condition in the next cycle)
    }

    // Update previous states
    incomingBytePrev = incomingByte;
}

// ########################## LOOP ##########################

void loop(void)
{ 
  Blynk.run();
}
zeehead commented 2 years ago

So, I think I might have gotten a little further, but that didn't work either, I still get the 3 beeps, unfortunately. I also tried another baudrate again, changed it on the firmware as well. But no matter which baud i choose, it's always 3 beeps.


#define HOVER_SERIAL_BAUD   9600      // [-] Baud rate for HoverSerial (used to communicate with the hoverboard)
#define SERIAL_BAUD         9600      // [-] Baud rate for built-in Serial (used for the Serial Monitor)
#define START_FRAME         0xABCD      // [-] Start frme definition for reliable serial communication
#define TIME_SEND           100         // [ms] Sending time interval
#define SPEED_MAX_TEST      300         // [-] Maximum speed for testing
#define SPEED_STEP          20          // [-] Speed step
#define DEBUG_RX                        // [-] Debug received data. Prints all bytes to serial (comment-out to disable)

// #define BLYNK_TEMPLATE_ID           "TMPLxxxxxx"
#define BLYNK_DEVICE_NAME           "Arduino UNO"
#define BLYNK_AUTH_TOKEN            "xxxzzzzzz"
#define BLYNK_PRINT Serial
#define BLYNK_USE_DIRECT_CONNECT

#include <SoftwareSerial.h>
SoftwareSerial HoverSerial(2,3);        // RX, TX

#include <BlynkSimpleSerialBLE.h>

char auth[] = BLYNK_AUTH_TOKEN;

BLYNK_WRITE(V1) {
  int val1 = param[0].asInt();
  int val2 = param[1].asInt();
}

// Global variables
uint8_t idx = 0;                        // Index for new data pointer
uint16_t bufStartFrame;                 // Buffer Start Frame
byte *p;                                // Pointer declaration for the new received data
byte incomingByte;
byte incomingBytePrev;

typedef struct{
   uint16_t start;
   int16_t  steer;
   int16_t  speed;
   uint16_t checksum;
} SerialCommand;
SerialCommand Command;

typedef struct{
   uint16_t start;
   int16_t  cmd1;
   int16_t  cmd2;
   int16_t  speedR_meas;
   int16_t  speedL_meas;
   int16_t  batVoltage;
   int16_t  boardTemp;
   uint16_t cmdLed;
   uint16_t checksum;
} SerialFeedback;
SerialFeedback Feedback;
SerialFeedback NewFeedback;

#include <Servo.h>

Servo speedwire;
Servo steerwire;

void Speed(int val1) {
  if (HoverSerial.available()) {
    val1 = HoverSerial.read();
  }

  HoverSerial.println(val1);
  val1 = map(val1, 0, 255, -1000, 1000);

  speedwire.writeMicroseconds(val1);
}

void Steer(int val2) {
  if (HoverSerial.available()) {
    val2 = HoverSerial.read();
  }

  HoverSerial.println(val2);
  val2 = map(val2, 0, 255, -1000, 1000);

  steerwire.writeMicroseconds(val2);
}

// ########################## SETUP ##########################
void setup() 
{
  speedwire.attach(10);
  steerwire.attach(11);
  Serial.begin(SERIAL_BAUD);
  Serial.println("Hoverboard Serial v1.0");

  HoverSerial.begin(HOVER_SERIAL_BAUD);

  Blynk.begin(HoverSerial, auth);
  Serial.println("Waiting for connections...");
  pinMode(LED_BUILTIN, OUTPUT);
}

// ########################## SEND ##########################
void Send(int16_t steerwire, int16_t speedwire)
{
  // Create command
  Command.start    = (uint16_t)START_FRAME;
  Command.steer    = (int16_t)steerwire;
  Command.speed    = (int16_t)speedwire;
  Command.checksum = (uint16_t)(Command.start ^ Command.steer ^ Command.speed);

  // Write to Serial
  HoverSerial.write((uint8_t *) &Command, sizeof(Command)); 
}

// ########################## RECEIVE ##########################
void Receive()
{
    // Check for new data availability in the Serial buffer
    if (HoverSerial.available()) {
        incomingByte      = HoverSerial.read();                                   // Read the incoming byte
        bufStartFrame   = ((uint16_t)(incomingByte) << 8) | incomingBytePrev;       // Construct the start frame
    }
    else {
        return;
    }

  // If DEBUG_RX is defined print all incoming bytes
  #ifdef DEBUG_RX
        Serial.print(incomingByte);
        return;
    #endif

    // Copy received data
    if (bufStartFrame == START_FRAME) {                     // Initialize if new data is detected
        p       = (byte *)&NewFeedback;
        *p++    = incomingBytePrev;
        *p++    = incomingByte;
        idx     = 2;    
    } else if (idx >= 2 && idx < sizeof(SerialFeedback)) {  // Save the new received data
        *p++    = incomingByte; 
        idx++;
    }   

    // Check if we reached the end of the package
    if (idx == sizeof(SerialFeedback)) {
        uint16_t checksum;
        checksum = (uint16_t)(NewFeedback.start ^ NewFeedback.cmd1 ^ NewFeedback.cmd2 ^ NewFeedback.speedR_meas ^ NewFeedback.speedL_meas
                            ^ NewFeedback.batVoltage ^ NewFeedback.boardTemp ^ NewFeedback.cmdLed);

        // Check validity of the new data
        if (NewFeedback.start == START_FRAME && checksum == NewFeedback.checksum) {
            // Copy the new data
            memcpy(&Feedback, &NewFeedback, sizeof(SerialFeedback));

            // Print data to built-in Serial
            Serial.print("1: ");   Serial.print(Feedback.cmd1);
            Serial.print(" 2: ");  Serial.print(Feedback.cmd2);
            Serial.print(" 3: ");  Serial.print(Feedback.speedR_meas);
            Serial.print(" 4: ");  Serial.print(Feedback.speedL_meas);
            Serial.print(" 5: ");  Serial.print(Feedback.batVoltage);
            Serial.print(" 6: ");  Serial.print(Feedback.boardTemp);
            Serial.print(" 7: ");  Serial.println(Feedback.cmdLed);
        } else {
          Serial.println("Non-valid data skipped");
        }
        idx = 0;    // Reset the index (it prevents to enter in this if condition in the next cycle)
    }

    // Update previous states
    incomingBytePrev = incomingByte;
}

// ########################## LOOP ##########################

void loop(void)
{ 
  Receive();
  Blynk.run();
}

Do you guys have some more advice on this? If I leave the example untouched, it works perfectly. So, the error must be somewhere with the commands, right?

Candas1 commented 2 years ago

Hi,

Of course you have to use the send function. This is what sends the serial command to the board.

zeehead commented 2 years ago

Oh, Okay, great. I somehow just realized that. Could you help me out to improve my sketch maybe? I mean, which part could cause the serial connection timeout?

Candas1 commented 2 years ago

The firmware expects a proper serial frame. Here I describe how a timeout happens.

If you don't SEND frequently enough, it will timeout.

zeehead commented 2 years ago

I see, does that mean that this part in the example sketch is important to keep?

unsigned long timeNow = millis();

  // Check for new received data
  Receive();

  // Send commands
  if (iTimeSend > timeNow) return;
  iTimeSend = timeNow + TIME_SEND;
  Send(0, iTest);

If yes, what exactly should be the two arguments for SEND? Because iTest is only for testing, I guess

Candas1 commented 2 years ago

Steer and speed https://github.com/EFeru/hoverboard-firmware-hack-FOC/blob/bd4a34dabf655ba8a9a48e9974ebb0b619796fde/Arduino/hoverserial/hoverserial.ino#L79

ApacheBlack commented 2 years ago

Yeah right! Thanks! So then in the void lood we should place something like: BtscanF("%i,&Usteer") BtScanF("%i, &uSpeed") ?? Is it something like that? I am learning at the moment. Sorry for my ignorance.

Candas1 commented 2 years ago

You only need to call the Send function in the loop with the Steer and Speed you want.

zeehead commented 2 years ago

Hey, so I tried and tried but I still get the 3 beeps.

Well, this is my code so far, and I'm REALLY not sure, what else might be wrong. I thought I got the "Send( ... )" right this time. But still 3 beeps.

Could you help me out and give me another hint for my sketch maybe? What I want to do: I want to make "void Send" read the values in "void Speed" and "void Steer". But I can't seem to figure out how to do that the right way here.

#define HOVER_SERIAL_BAUD   9600      // [-] Baud rate for HoverSerial (used to communicate with the hoverboard)
#define SERIAL_BAUD         9600      // [-] Baud rate for built-in Serial (used for the Serial Monitor)
#define START_FRAME         0xABCD      // [-] Start frme definition for reliable serial communication
#define TIME_SEND           100         // [ms] Sending time interval
#define SPEED_MAX_TEST      300         // [-] Maximum speed for testing
#define SPEED_STEP          20          // [-] Speed step
#define DEBUG_RX                        // [-] Debug received data. Prints all bytes to serial (comment-out to disable)
#define steerwire 10
#define speedwire 11

// #define BLYNK_TEMPLATE_ID           "TMPLxxxxxx"
#define BLYNK_DEVICE_NAME           "Arduino UNO"
#define BLYNK_AUTH_TOKEN            "xxxxxxzzzzz"
#define BLYNK_PRINT Serial
#define BLYNK_USE_DIRECT_CONNECT

#include <SoftwareSerial.h>
SoftwareSerial HoverSerial(2,3);        // RX, TX

#include <BlynkSimpleSerialBLE.h>

char auth[] = BLYNK_AUTH_TOKEN;

BLYNK_WRITE(V1) {
  int x = param[0].asInt();
  int y = param[1].asInt();
  Speed(x);
  Steer(y);
}

// Global variables
uint8_t idx = 0;                        // Index for new data pointer
uint16_t bufStartFrame;                 // Buffer Start Frame
byte *p;                                // Pointer declaration for the new received data
byte incomingByte;
byte incomingBytePrev;

typedef struct{
   uint16_t start;
   int16_t  steer;
   int16_t  speed;
   uint16_t checksum;
} SerialCommand;
SerialCommand Command;

typedef struct{
   uint16_t start;
   int16_t  cmd1;
   int16_t  cmd2;
   int16_t  speedR_meas;
   int16_t  speedL_meas;
   int16_t  batVoltage;
   int16_t  boardTemp;
   uint16_t cmdLed;
   uint16_t checksum;
} SerialFeedback;
SerialFeedback Feedback;
SerialFeedback NewFeedback;

//-----------------------------------------------------------------------------
void Speed(int x) {
    x = map(x, 0, 255, -1000, 1000);
}

void Steer(int y) {   
  y = map(y, 0, 255, -1000, 1000);
}

// ########################## SETUP ##########################
void setup() 

{ Serial.begin(SERIAL_BAUD);
  Serial.println("Hoverboard Serial v1.0");

  HoverSerial.begin(HOVER_SERIAL_BAUD);

  Blynk.begin(HoverSerial, auth);
  Serial.println("Waiting for connections...");
  pinMode(LED_BUILTIN, OUTPUT);
}

// ########################## SEND ##########################
void Send(int16_t uSteer, int16_t uSpeed)
{
  // Create command
  Command.start    = (uint16_t)START_FRAME;
  Command.steer    = (int16_t)uSteer;
  Command.speed    = (int16_t)uSpeed;
  Command.checksum = (uint16_t)(Command.start ^ Command.steer ^ Command.speed);

  // Write to Serial
  HoverSerial.write((uint8_t *) &Command, sizeof(Command)); 
}

// ########################## RECEIVE ##########################
void Receive()
{
    // Check for new data availability in the Serial buffer
    if (HoverSerial.available()) {
        incomingByte      = HoverSerial.read();                                   // Read the incoming byte
        bufStartFrame   = ((uint16_t)(incomingByte) << 8) | incomingBytePrev;       // Construct the start frame
    }
    else {
        return;
    }

  // If DEBUG_RX is defined print all incoming bytes
  #ifdef DEBUG_RX
        Serial.print(incomingByte);
        return;
    #endif

    // Copy received data
    if (bufStartFrame == START_FRAME) {                     // Initialize if new data is detected
        p       = (byte *)&NewFeedback;
        *p++    = incomingBytePrev;
        *p++    = incomingByte;
        idx     = 2;    
    } else if (idx >= 2 && idx < sizeof(SerialFeedback)) {  // Save the new received data
        *p++    = incomingByte; 
        idx++;
    }   

    // Check if we reached the end of the package
    if (idx == sizeof(SerialFeedback)) {
        uint16_t checksum;
        checksum = (uint16_t)(NewFeedback.start ^ NewFeedback.cmd1 ^ NewFeedback.cmd2 ^ NewFeedback.speedR_meas ^ NewFeedback.speedL_meas
                            ^ NewFeedback.batVoltage ^ NewFeedback.boardTemp ^ NewFeedback.cmdLed);

        // Check validity of the new data
        if (NewFeedback.start == START_FRAME && checksum == NewFeedback.checksum) {
            // Copy the new data
            memcpy(&Feedback, &NewFeedback, sizeof(SerialFeedback));

            // Print data to built-in Serial
            Serial.print("1: ");   Serial.print(Feedback.cmd1);
            Serial.print(" 2: ");  Serial.print(Feedback.cmd2);
            Serial.print(" 3: ");  Serial.print(Feedback.speedR_meas);
            Serial.print(" 4: ");  Serial.print(Feedback.speedL_meas);
            Serial.print(" 5: ");  Serial.print(Feedback.batVoltage);
            Serial.print(" 6: ");  Serial.print(Feedback.boardTemp);
            Serial.print(" 7: ");  Serial.println(Feedback.cmdLed);
        } else {
          Serial.println("Non-valid data skipped");
        }
        idx = 0;    // Reset the index (it prevents to enter in this if condition in the next cycle)
    }

    // Update previous states
    incomingBytePrev = incomingByte;
}

// ########################## LOOP ##########################

void loop(void)
{ 

  int x;
  int y;

  Receive();
  Send(map(y, 0, 255, -1000, 1000), map(x, 0, 255, -1000, 1000));

  Blynk.run();
}
EFeru commented 2 years ago

Hi, i come back with some advices i hope they will be useful.

zeehead commented 2 years ago

Hi, wow, nice! Thank you, this helped me soooo much! I'm using two softserials (+serial monitor) now, as you suggested, and now it does get connected as it should and the arduino reacts to my bluetooth commands by flashing Tx. This is great!

It works almost like it should now! The motor runs on my commands.

The only thing is, it seems a bit unstable. Because sometimes when I try, it sends 3 beeps, when I send a command, but when I leave the joystick at 0, it's quiet. Then sometimes it works and just beeps in between, so the connection is away for half a second and then comes back. (backbeeps are turned off in config)

But really, you guys are doing a great job with this stuff and I really appreciate your support, @Candas1 and @EFeru !!!

By the way @ApacheBlack, did you get further with your sketch?

This is my code right now by the way, just in case someone might need it (I changed the baud again here, and also in the Firmware):


#define HOVER_SERIAL_BAUD   38400      // [-] Baud rate for HoverSerial (used to communicate with the hoverboard)
#define SERIAL_BAUD         38400      // [-] Baud rate for built-in Serial (used for the Serial Monitor)
#define START_FRAME         0xABCD      // [-] Start frme definition for reliable serial communication
#define TIME_SEND           100         // [ms] Sending time interval
#define SPEED_MAX_TEST      300         // [-] Maximum speed for testing
#define SPEED_STEP          20          // [-] Speed step
#define DEBUG_RX                        // [-] Debug received data. Prints all bytes to serial (comment-out to disable)

// #define BLYNK_TEMPLATE_ID           "TMPLxxxxxx"
#define BLYNK_DEVICE_NAME           "Arduino UNO"
#define BLYNK_AUTH_TOKEN            "xxxxxzzzzz"
#define BLYNK_PRINT Serial
#define BLYNK_USE_DIRECT_CONNECT

#include <SoftwareSerial.h>
SoftwareSerial HoverSerial(10,11);        // RX, TX
SoftwareSerial BTSerial(2,3);
#include <BlynkSimpleSerialBLE.h>

char auth[] = BLYNK_AUTH_TOKEN;

int valx;
int valy;

// Global variables
uint8_t idx = 0;                        // Index for new data pointer
uint16_t bufStartFrame;                 // Buffer Start Frame
byte *p;                                // Pointer declaration for the new received data
byte incomingByte;
byte incomingBytePrev;

typedef struct{
   uint16_t start;
   int16_t  steer;
   int16_t  speed;
   uint16_t checksum;
} SerialCommand;
SerialCommand Command;

typedef struct{
   uint16_t start;
   int16_t  cmd1;
   int16_t  cmd2;
   int16_t  speedR_meas;
   int16_t  speedL_meas;
   int16_t  batVoltage;
   int16_t  boardTemp;
   uint16_t cmdLed;
   uint16_t checksum;
} SerialFeedback;
SerialFeedback Feedback;
SerialFeedback NewFeedback;

//------------------------------------------------------------
BLYNK_WRITE(V1) {
  valx = param[0].asInt();
  valy = param[1].asInt();
}

// ########################## SETUP ##########################
void setup() 

{ 

  Serial.begin(SERIAL_BAUD);
  Serial.println("Hoverboard Serial v1.0");

  HoverSerial.begin(HOVER_SERIAL_BAUD);

  BTSerial.begin(38400);
  Blynk.begin(BTSerial, auth);
  Serial.println("Waiting for connections...");
  pinMode(LED_BUILTIN, OUTPUT);
}

// ########################## SEND ##########################
void Send(int16_t uSteer, int16_t uSpeed)
{
  // Create command
  Command.start    = (uint16_t)START_FRAME;
  Command.steer    = (int16_t)uSteer;
  Command.speed    = (int16_t)uSpeed;
  Command.checksum = (uint16_t)(Command.start ^ Command.steer ^ Command.speed);

  // Write to Serial
  HoverSerial.write((uint8_t *) &Command, sizeof(Command)); 
}

// ########################## RECEIVE ##########################
void Receive()
{
    // Check for new data availability in the Serial buffer
    if (HoverSerial.available()) {
        incomingByte      = HoverSerial.read();                                   // Read the incoming byte
        bufStartFrame   = ((uint16_t)(incomingByte) << 8) | incomingBytePrev;       // Construct the start frame
    }
    else {
        return;
    }

  // If DEBUG_RX is defined print all incoming bytes
  #ifdef DEBUG_RX
        Serial.print(incomingByte);
        return;
    #endif

    // Copy received data
    if (bufStartFrame == START_FRAME) {                     // Initialize if new data is detected
        p       = (byte *)&NewFeedback;
        *p++    = incomingBytePrev;
        *p++    = incomingByte;
        idx     = 2;    
    } else if (idx >= 2 && idx < sizeof(SerialFeedback)) {  // Save the new received data
        *p++    = incomingByte; 
        idx++;
    }   

    // Check if we reached the end of the package
    if (idx == sizeof(SerialFeedback)) {
        uint16_t checksum;
        checksum = (uint16_t)(NewFeedback.start ^ NewFeedback.cmd1 ^ NewFeedback.cmd2 ^ NewFeedback.speedR_meas ^ NewFeedback.speedL_meas
                            ^ NewFeedback.batVoltage ^ NewFeedback.boardTemp ^ NewFeedback.cmdLed);

        // Check validity of the new data
        if (NewFeedback.start == START_FRAME && checksum == NewFeedback.checksum) {
            // Copy the new data
            memcpy(&Feedback, &NewFeedback, sizeof(SerialFeedback));

            // Print data to built-in Serial
            Serial.print("1: ");   Serial.print(Feedback.cmd1);
            Serial.print(" 2: ");  Serial.print(Feedback.cmd2);
            Serial.print(" 3: ");  Serial.print(Feedback.speedR_meas);
            Serial.print(" 4: ");  Serial.print(Feedback.speedL_meas);
            Serial.print(" 5: ");  Serial.print(Feedback.batVoltage);
            Serial.print(" 6: ");  Serial.print(Feedback.boardTemp);
            Serial.print(" 7: ");  Serial.println(Feedback.cmdLed);
        } else {
          Serial.println("Non-valid data skipped");
        }
        idx = 0;    // Reset the index (it prevents to enter in this if condition in the next cycle)
    }

    // Update previous states
    incomingBytePrev = incomingByte;
}

// ########################## LOOP ##########################

unsigned long iTimeSend = 0;

void loop(void)

{ 
  unsigned long timeNow = millis();

  if (iTimeSend > timeNow) return;
  iTimeSend = timeNow + TIME_SEND;

  Receive();
  Send(valy, valx);
  Blynk.run();
}
EFeru commented 2 years ago

Further you can try a few things:

{ unsigned long timeNow = millis();

if (iTimeSend > timeNow) return;

if (i < 1) { Receive(); i++; return; } else if (i < 2) { Send(valy, valx); i++; return; } else if (i < 3) { Blynk.run(); } iTimeSend = timeNow + TIME_SEND; i = 0; }


(I typed it on my phone, you can make it pretty in Arduino.)
- I am not sure if `Blynk.run();` has to be called all the time. Then you might need to put it at the top, before all the `return` calls.
zeehead commented 2 years ago

Hey, so I tested your suggestions.

The motors just spin sporadically on my commands. On the serial monitor I get the message "packet too big", as soon as I move the joystick and the board beeps 3 times every now and then, disconnets and connects again. From what I googled about this specific error (not much info or advice on that, unfortunately), one of the suggestions is to "keep the loop empty, except for Blynk.run(); So from what I understand, this really is about the computational stress on the arduino, just as you suggested. Someone somewhere mentioned the buffer size. But I have no idea what to do with that information.

Is there a way to manage Send(valy,valx); in the setup somehow maybe? Or do you maybe have some other suggestions how I could lower the comp effort or what I could try? If not, I'll just have to go by "trial and error".

Anyway, here's my code so far, maybe that can help:

#define HOVER_SERIAL_BAUD   9600      // [-] Baud rate for HoverSerial (used to communicate with the hoverboard)
#define SERIAL_BAUD         9600      // [-] Baud rate for built-in Serial (used for the Serial Monitor)
#define START_FRAME         0xABCD      // [-] Start frme definition for reliable serial communication
#define TIME_SEND           50         // [ms] Sending time interval
#define DEBUG_RX                        // [-] Debug received data. Prints all bytes to serial (comment-out to disable)

// #define BLYNK_TEMPLATE_ID           "TMPLxxxxxx"
#define BLYNK_DEVICE_NAME           "Arduino UNO"
#define BLYNK_AUTH_TOKEN            "xxxxxzzzz"
#define BLYNK_PRINT Serial
#define BLYNK_USE_DIRECT_CONNECT

#include <SoftwareSerial.h>
SoftwareSerial HoverSerial(10,11);        // RX, TX
SoftwareSerial BTSerial(2,3);
#include <BlynkSimpleSerialBLE.h>

char auth[] = BLYNK_AUTH_TOKEN;

int valx;
int valy;

// Global variables
uint8_t idx = 0;                        // Index for new data pointer
uint16_t bufStartFrame;                 // Buffer Start Frame
byte *p;                                // Pointer declaration for the new received data
byte incomingByte;
byte incomingBytePrev;

typedef struct{
   uint16_t start;
   int16_t  steer;
   int16_t  speed;
   uint16_t checksum;
} SerialCommand;
SerialCommand Command;

typedef struct{
   uint16_t start;
   int16_t  cmd1;
   int16_t  cmd2;
   int16_t  speedR_meas;
   int16_t  speedL_meas;
   int16_t  batVoltage;
   int16_t  boardTemp;
   uint16_t cmdLed;
   uint16_t checksum;
} SerialFeedback;
SerialFeedback Feedback;
SerialFeedback NewFeedback;

//------------------------------------------------------------
BLYNK_WRITE(V1) {
  valx = param[0].asInt();
  valy = param[1].asInt();
}

// ########################## SETUP ##########################
void setup() 
{ 
  Serial.begin(SERIAL_BAUD);
  Serial.println("Hoverboard Serial v1.0");

  HoverSerial.begin(HOVER_SERIAL_BAUD);

  BTSerial.begin(9600);
  Blynk.begin(BTSerial, auth);
  Serial.println("Waiting for connections...");
  pinMode(LED_BUILTIN, OUTPUT);
}

// ########################## SEND ##########################
void Send(int16_t uSteer, int16_t uSpeed)
{
  // Create command
  Command.start    = (uint16_t)START_FRAME;
  Command.steer    = (int16_t)uSteer;
  Command.speed    = (int16_t)uSpeed;
  Command.checksum = (uint16_t)(Command.start ^ Command.steer ^ Command.speed);

  // Write to Serial
  HoverSerial.write((uint8_t *) &Command, sizeof(Command)); 
}

// ########################## RECEIVE ##########################
void Receive()
{
    // Check for new data availability in the Serial buffer
    if (HoverSerial.available()) {
        incomingByte      = HoverSerial.read();                                   // Read the incoming byte
        bufStartFrame   = ((uint16_t)(incomingByte) << 8) | incomingBytePrev;       // Construct the start frame
    }
    else {
        return;
    }

  // If DEBUG_RX is defined print all incoming bytes
  #ifdef DEBUG_RX
        Serial.print(incomingByte);
        return;
    #endif

    // Copy received data
    if (bufStartFrame == START_FRAME) {                     // Initialize if new data is detected
        p       = (byte *)&NewFeedback;
        *p++    = incomingBytePrev;
        *p++    = incomingByte;
        idx     = 2;    
    } else if (idx >= 2 && idx < sizeof(SerialFeedback)) {  // Save the new received data
        *p++    = incomingByte; 
        idx++;
    }   

    // Check if we reached the end of the package
    if (idx == sizeof(SerialFeedback)) {
        uint16_t checksum;
        checksum = (uint16_t)(NewFeedback.start ^ NewFeedback.cmd1 ^ NewFeedback.cmd2 ^ NewFeedback.speedR_meas ^ NewFeedback.speedL_meas
                            ^ NewFeedback.batVoltage ^ NewFeedback.boardTemp ^ NewFeedback.cmdLed);

        // Check validity of the new data
        if (NewFeedback.start == START_FRAME && checksum == NewFeedback.checksum) {
            // Copy the new data
            memcpy(&Feedback, &NewFeedback, sizeof(SerialFeedback));

            // Print data to built-in Serial
            Serial.print("1: ");   Serial.print(Feedback.cmd1);
            Serial.print(" 2: ");  Serial.print(Feedback.cmd2);
            Serial.print(" 3: ");  Serial.print(Feedback.speedR_meas);
            Serial.print(" 4: ");  Serial.print(Feedback.speedL_meas);
            Serial.print(" 5: ");  Serial.print(Feedback.batVoltage);
            Serial.print(" 6: ");  Serial.print(Feedback.boardTemp);
            Serial.print(" 7: ");  Serial.println(Feedback.cmdLed);
        } else {
          Serial.println("Non-valid data skipped");
        }
        idx = 0;    // Reset the index (it prevents to enter in this if condition in the next cycle)
    }

    // Update previous states
    incomingBytePrev = incomingByte;
}

// ########################## LOOP ##########################

unsigned long iTimeSend = 0;

int i = 0;
void loop(void)

{ 
  Blynk.run();

  unsigned long timeNow = millis();

  if (iTimeSend > timeNow) return;

  if (i < 1) {
  Receive(); i++; return; }
  else if (i < 2) {
  Send(valx, valy); i++; return; } 

  iTimeSend = timeNow + TIME_SEND;
  i = 0;
}

I still appreciate all the support you guys have given me so far!

ApacheBlack commented 2 years ago

Ill try your code! I am still trying with the hoverboard board but i am also looking for alternatives. Ill have a try to your code and let you know my conclusion. Ill use MIT app inventor so i can add an ip cam and see it with the app 💪🏼 good job.

ApacheBlack commented 2 years ago

are you using the old or the new version of the Blynk app? i cant find how to configure the blynk app to interact via bluetooth.

zeehead commented 2 years ago

Ah, yes, I'm using Blynk legacy, the older version 2.27.24.

I still use it exactly because the new one doesn't offer Bluetooth.

But it should be possible with the MIT app too, it's just a bit more work for me to create a working joystick, that is why I prefer the old Blynk for now.

Only problem is, they don't accept new user for the old platform, only for the new Blynk IoT.

ApacheBlack commented 2 years ago

So, from the MIT App Inventor app that i just created i just need to send the values 1-255 for Speed and 1-255 for steer and do you think it will work with this same code as is working for you now? you get me?

zeehead commented 2 years ago

Yes, it should. At least it should connect, as I said, for me, the motors are not quite following my commands. But instead of "1 to 255" you have to use "-1000 to 1000".

But it would be best if you just show your code, it's easier to see what is going on. Maybe you're able to find something that I missed.

EFeru commented 2 years ago

@zeehead do you really need to use Arduino uno? Because there are other more powerful alternatives like ESP32, blue pill, black pill with not a big price difference.

ApacheBlack commented 2 years ago

@EFeru i heard ESP32 is a better choice. I got a few of them and ill be willing to follow instructions to make it work.

@zeehead,take a look to MIT app inventor. Is very easy to learn and very good for making joystick with all kind of connections.and for what i test its a bit faster (te lag for the ip cam is better at least for me) than with the Blynk app and your data is not stored in a 3th party server. The con. is that is only working very good with Android. But for a kick starter is a good way to start.

zeehead commented 2 years ago

Well, @EFeru , no, I just use the uno because I had it here. Do you think with an ESP32 it might work better? Can I leave the code the same then?

Edit: I ordered an ESP32, should arrive tomorrow. I'll give it a try and let you know if it works

And @ApacheBlack Yes I used MIT app already, it's really good, just a little more work for me because it doesn't have a ready-to-use joystick, i would have to create one myself there But for Ip cams and stuff it's great

zeehead commented 2 years ago

Hey, so it took me a bit until I figured out how to adapt some Arduino libraries to the ESP32, but it works now! Thank you so much for mentioning the ESP32, @EFeru, it's really much more powerful and practical to use.

I'm going off topic now, but how exactly do I use the Hovercar protocol so I can control it with the ESP32? Do I just need to change the protocol in platformio.ini/config.h and add the "type", etc commands to Send in the Arduino code?

I would love to try the different types and modes to see how different they work. And I think my project would work better using Torque mode.

BTW - for the last time - my recent code, adapted to the ESP32, in case someone needs it:'

#define HOVER_SERIAL_BAUD   115200      // [-] Baud rate for HoverSerial (used to communicate with the hoverboard)
#define SERIAL_BAUD         115200      // [-] Baud rate for built-in Serial (used for the Serial Monitor)
#define START_FRAME         0xABCD      // [-] Start frme definition for reliable serial communication
#define TIME_SEND           100         // [ms] Sending time interval
#define DEBUG_RX                        // [-] Debug received data. Prints all bytes to serial (comment-out to disable)

#define BLYNK_PRINT Serial
#define BLYNK_USE_DIRECT_CONNECT

#define RXD2 16
#define TXD2 17

#include <BlynkSimpleEsp32_BT.h>

char auth[] = "xxxxxzzzzzzzz";

int valx;
int valy;

// Global variables
uint8_t idx = 0;                        // Index for new data pointer
uint16_t bufStartFrame;                 // Buffer Start Frame
byte *p;                                // Pointer declaration for the new received data
byte incomingByte;
byte incomingBytePrev;

typedef struct{
   uint16_t start;
   int16_t  steer;
   int16_t  speed;
   uint16_t checksum;
} SerialCommand;
SerialCommand Command;

typedef struct{
   uint16_t start;
   int16_t  cmd1;
   int16_t  cmd2;
   int16_t  speedR_meas;
   int16_t  speedL_meas;
   int16_t  batVoltage;
   int16_t  boardTemp;
   uint16_t cmdLed;
   uint16_t checksum;
} SerialFeedback;
SerialFeedback Feedback;
SerialFeedback NewFeedback;

//------------------------------------------------------------
BLYNK_WRITE(V1) {
  valx = param[0].asInt();
  valy = param[1].asInt();
}

// ########################## SETUP ##########################
void setup() 

{ 
  Serial.begin(115200);
  Serial.println("Hoverboard Serial v1.0");
  Serial.println("Waiting for connections...");

  Blynk.setDeviceName("Blynk");
  Blynk.begin(auth); 

  Serial2.begin(115200, SERIAL_8N1, RXD2, TXD2);
}

// ########################## SEND ##########################
void Send(int16_t uSteer, int16_t uSpeed)
{
  // Create command
  Command.start    = (uint16_t)START_FRAME;
  Command.steer    = (int16_t)uSteer;
  Command.speed    = (int16_t)uSpeed;
  Command.checksum = (uint16_t)(Command.start ^ Command.steer ^ Command.speed);

  // Write to Serial
  Serial2.write((uint8_t *) &Command, sizeof(Command)); 
}

// ########################## RECEIVE ##########################
void Receive()
{
    // Check for new data availability in the Serial buffer
    if (Serial2.available()) {
        incomingByte      = Serial2.read();                                   // Read the incoming byte
        bufStartFrame   = ((uint16_t)(incomingByte) << 8) | incomingBytePrev;       // Construct the start frame
    }
    else {
        return;
    }

  // If DEBUG_RX is defined print all incoming bytes
  #ifdef DEBUG_RX
        Serial.print(incomingByte);
        return;
    #endif

    // Copy received data
    if (bufStartFrame == START_FRAME) {                     // Initialize if new data is detected
        p       = (byte *)&NewFeedback;
        *p++    = incomingBytePrev;
        *p++    = incomingByte;
        idx     = 2;    
    } else if (idx >= 2 && idx < sizeof(SerialFeedback)) {  // Save the new received data
        *p++    = incomingByte; 
        idx++;
    }   

    // Check if we reached the end of the package
    if (idx == sizeof(SerialFeedback)) {
        uint16_t checksum;
        checksum = (uint16_t)(NewFeedback.start ^ NewFeedback.cmd1 ^ NewFeedback.cmd2 ^ NewFeedback.speedR_meas ^ NewFeedback.speedL_meas
                            ^ NewFeedback.batVoltage ^ NewFeedback.boardTemp ^ NewFeedback.cmdLed);

        // Check validity of the new data
        if (NewFeedback.start == START_FRAME && checksum == NewFeedback.checksum) {
            // Copy the new data
            memcpy(&Feedback, &NewFeedback, sizeof(SerialFeedback));

            // Print data to built-in Serial
            Serial.print("1: ");   Serial.print(Feedback.cmd1);
            Serial.print(" 2: ");  Serial.print(Feedback.cmd2);
            Serial.print(" 3: ");  Serial.print(Feedback.speedR_meas);
            Serial.print(" 4: ");  Serial.print(Feedback.speedL_meas);
            Serial.print(" 5: ");  Serial.print(Feedback.batVoltage);
            Serial.print(" 6: ");  Serial.print(Feedback.boardTemp);
            Serial.print(" 7: ");  Serial.println(Feedback.cmdLed);
        } else {
          Serial.println("Non-valid data skipped");
        }
        idx = 0;    // Reset the index (it prevents to enter in this if condition in the next cycle)
    }

    // Update previous states
    incomingBytePrev = incomingByte;
}

// ########################## LOOP ##########################

unsigned long iTimeSend = 0;

void loop(void)

{ 
  unsigned long timeNow = millis();

  if (iTimeSend > timeNow) return;
  iTimeSend = timeNow + TIME_SEND;
  Blynk.run();
  Receive();
  Send(valx, valy);
}
Candas1 commented 2 years ago

Hi,

I had shared details about the sideboard protocol here

You will have to manipulate bits for switches value.