VOTOL serial communication protocol

Wow thks for you fast response!, But having a votol em100 and a proper connection to one line wire, my stm32 device is not showing up any data related to BATTERY V or BAT %, like if the datasheet would be wrong or outdated , retrieves always as 0 no data for battery, even less for VOLTS.... all the rest is fine like REGEN, brake, RPM and so on.... do you know what can it be? i mean nothing here:
battery = data[9];
bottom line is that either the controller is not sending that info or they are being sent at a different offset. you can compare 2 idle packages at 2 different enough voltage levels to see what changes in the received packages. also note that you might have voltage as increments of 0.5v or 0.1v... 2v. if you have a look at the fardriver documentation you will see how fardrivers do it. good luck
 
perfect thanks... (the datashet in fact was not a votol document but a spedometer seeler maybe...that is why read 0) ... i will compare packages yes thks// (and yes Michael i know i have volt in the other usb/bt protocol but wanted to be here to free that side for parameters and not online view) thks//
 
Also have developed a simple bluetooth old android phone20240923_162202.jpg app to show important values in a cool DIY speedometer like app, when i had an EM50s (now bricked cant connect anymore) and was working fine.
Now I have Em100 but as soon as the motor starts to spin it turns off BT communication... and my app does not rx data. in votol app this does not happen. any idea.... thks
 
tha
Also have developed a simple bluetooth old android phoneView attachment 360027 app to show important values in a cool DIY speedometer like app, when i had an EM50s (now bricked cant connect anymore) and was working fine.
Now I have Em100 but as soon as the motor starts to spin it turns off BT communication... and my app does not rx data. in votol app this does not happen. any idea.... thks
that is emi. first of all make sure the ground you are using to power up your esp32 comes directly from the votol. I am still having some emi issues with my 72680 fardriver when drawing big phase currents.
 
A little update. I played around a bit with the "one wire" display output and was able to decode the data with a simple arduino nano.
Here's some rough test code, nothing fancy, just a simple proof of concept but it works well for me.
Protocol documentation in previous post.



#define sif_pin 2
short battery = 0;
short current = 0;
short currentPercent = 0;
short rpm = 0;
long faultCode = 0;
bool regen = false;
bool brake = false;
unsigned long lastTime;
unsigned long lastDuration = 0;
byte lastCrc = 0;
byte data[12];
int bitIndex = -1;
void setup() {
Serial.begin(115200);
pinMode(sif_pin, INPUT);
lastTime = micros();
attachInterrupt(digitalPinToInterrupt(sif_pin), sifChange, CHANGE);
}
void loop() {
// put your main code here, to run repeatedly:
}
void sifChange() {
int val = digitalRead(sif_pin);
unsigned long duration = micros() - lastTime;
lastTime = micros();
if (val == LOW) {
if (lastDuration > 0) {
bool bitComplete = false;
float ratio = float(lastDuration) / float(duration);
if (round(lastDuration / duration) >= 31) {
bitIndex = 0;
} else if (ratio > 1.5) {
// bit value 0
bitClear(data[bitIndex / 8], 7 - (bitIndex % 8));
bitComplete = true;
} else if (1 / ratio > 1.5) {
// bit value 0
bitSet(data[bitIndex / 8], 7 - (bitIndex % 8));
bitComplete = true;
} else {
Serial.println(String(duration) + "-" + String(lastDuration));
}
if (bitComplete) {
bitIndex++;
if (bitIndex == 96) {
bitIndex = 0;
byte crc = 0;
for (int i = 0; i < 11; i++) {
crc ^= data;
}
if (crc != data[11]) {
Serial.println("CRC FAILURE: " + String(crc) + "-" + String(data[11]));
}
if (crc == data[11] && crc != lastCrc) {
lastCrc = crc;
for (int i = 0; i < 12; i++) {
Serial.print(data, HEX);
Serial.print(" ");
}
Serial.println();
battery = data[9];
battery = data[9];
current = data[6];
currentPercent = data[10];
rpm = ((data[7] << 8) + data[8]) * 1.91;
brake = bitRead(data[4], 5);
regen = bitRead(data[4], 3);

Serial.print("Battery %: " + String(battery));
Serial.print(" Current %: " + String(currentPercent));
Serial.print(" Current A: " + String(current));
Serial.print(" RPM: " + String(rpm));
if (brake) Serial.print(" BRAKE");
if (regen) Serial.print(" REGEN");
Serial.println();
}
}
}
}
}
lastDuration = duration;
}
Hi can you please guide the setup for arduino and the votol controller. it would be much appreciated
 
I'm using a ESP32C6 to capture the data sent by my em150 Votol controller but i'm having some issues:
- its not super stable and since the CRC is very basic i sometime have bad data with a correct CRC
- the data specified in the vendor documentation does not seem to be correct (drawings certainly are not).

I took some capture with a logic analyzer and the bit value is 0 for a duty cycle lower than 50% and 1 for a duty cycle greater than 50%. The start symbol has a duty cycle lower than 1%. I have modified the code to be the following which works for me.

C:
int8_t bitIndex = -1;

bool wait_reset = true;

void IRAM_ATTR sifChange() {

  int val = digitalRead(SIF_PIN);

  unsigned long now = micros();

  unsigned long duration = now - lastTime;

  lastTime = now;

  if (val == LOW) {

    if (lastDuration > 0) {

      bool bitComplete = false;

      float duty_cycle = float(duration) / (float(duration + lastDuration));

      if (duty_cycle < 0.1) {

        bitIndex = 0;  //Reset condition

        wait_reset = false;

      } else if (duty_cycle < 0.45 && !wait_reset) {

        // bit value 0

        bitClear(data[bitIndex / 8], 7 - (bitIndex % 8));

        bitComplete = true;

      } else if (duty_cycle > 0.55 && !wait_reset) {

        // bit value 1

        bitSet(data[bitIndex / 8], 7 - (bitIndex % 8));

        bitComplete = true;

      }

      if (bitComplete) {

        bitIndex++;

        if (bitIndex >= 96) {

          decode_frame(&votol_state);

          bitIndex = 0;

          wait_reset = true;

        }

      }

    }

  }

  lastDuration = duration;

}



I'm still trying to figure out which bit indicates reverse since the documentation does not seem to be correct.
 

Attachments

  • Screenshot from 2024-10-23 22-49-54.png
    Screenshot from 2024-10-23 22-49-54.png
    83 KB · Views: 29
  • Screenshot from 2024-10-23 22-49-48.png
    Screenshot from 2024-10-23 22-49-48.png
    83.1 KB · Views: 29
  • Screenshot from 2024-10-23 22-49-25.png
    Screenshot from 2024-10-23 22-49-25.png
    81.7 KB · Views: 29
I'm using a ESP32C6 to capture the data sent by my em150 Votol controller but i'm having some issues:
- its not super stable and since the CRC is very basic i sometime have bad data with a correct CRC
- the data specified in the vendor documentation does not seem to be correct (drawings certainly are not).

I took some capture with a logic analyzer and the bit value is 0 for a duty cycle lower than 50% and 1 for a duty cycle greater than 50%. The start symbol has a duty cycle lower than 1%. I have modified the code to be the following which works for me.

C:
int8_t bitIndex = -1;

bool wait_reset = true;

void IRAM_ATTR sifChange() {

  int val = digitalRead(SIF_PIN);

  unsigned long now = micros();

  unsigned long duration = now - lastTime;

  lastTime = now;

  if (val == LOW) {

    if (lastDuration > 0) {

      bool bitComplete = false;

      float duty_cycle = float(duration) / (float(duration + lastDuration));

      if (duty_cycle < 0.1) {

        bitIndex = 0;  //Reset condition

        wait_reset = false;

      } else if (duty_cycle < 0.45 && !wait_reset) {

        // bit value 0

        bitClear(data[bitIndex / 8], 7 - (bitIndex % 8));

        bitComplete = true;

      } else if (duty_cycle > 0.55 && !wait_reset) {

        // bit value 1

        bitSet(data[bitIndex / 8], 7 - (bitIndex % 8));

        bitComplete = true;

      }

      if (bitComplete) {

        bitIndex++;

        if (bitIndex >= 96) {

          decode_frame(&votol_state);

          bitIndex = 0;

          wait_reset = true;

        }

      }

    }

  }

  lastDuration = duration;

}



I'm still trying to figure out which bit indicates reverse since the documentation does not seem to be correct.
Hey, I wanted to ask you used the In LIN wire from votol controller to the esp or the you used the rx tx from the controller? Can you please clarify the connections?
 
I'm using a ESP32C6 to capture the data sent by my em150 Votol controller but i'm having some issues:
- its not super stable and since the CRC is very basic i sometime have bad data with a correct CRC
- the data specified in the vendor documentation does not seem to be correct (drawings certainly are not).

I took some capture with a logic analyzer and the bit value is 0 for a duty cycle lower than 50% and 1 for a duty cycle greater than 50%. The start symbol has a duty cycle lower than 1%. I have modified the code to be the following which works for me.

C:
int8_t bitIndex = -1;

bool wait_reset = true;

void IRAM_ATTR sifChange() {

  int val = digitalRead(SIF_PIN);

  unsigned long now = micros();

  unsigned long duration = now - lastTime;

  lastTime = now;

  if (val == LOW) {

    if (lastDuration > 0) {

      bool bitComplete = false;

      float duty_cycle = float(duration) / (float(duration + lastDuration));

      if (duty_cycle < 0.1) {

        bitIndex = 0;  //Reset condition

        wait_reset = false;

      } else if (duty_cycle < 0.45 && !wait_reset) {

        // bit value 0

        bitClear(data[bitIndex / 8], 7 - (bitIndex % 8));

        bitComplete = true;

      } else if (duty_cycle > 0.55 && !wait_reset) {

        // bit value 1

        bitSet(data[bitIndex / 8], 7 - (bitIndex % 8));

        bitComplete = true;

      }

      if (bitComplete) {

        bitIndex++;

        if (bitIndex >= 96) {

          decode_frame(&votol_state);

          bitIndex = 0;

          wait_reset = true;

        }

      }

    }

  }

  lastDuration = duration;

}



I'm still trying to figure out which bit indicates reverse since the documentation does not seem to be correct.
sport = bitRead(p.data[4], 7);
cruise = bitRead(p.data[3], 2);
reverse = bitRead(p.data[5], 2);

This works well on my fardrivers. I don't use reverse on my votol (as it's an ebike). I use a circular queue and push a copy of the data there and do all processing outside the ISR, in the loop. Computing in ISRs should be avoided.
 
Hey, I wanted to ask you used the In LIN wire from votol controller to the esp or the you used the rx tx from the controller? Can you please clarify the connections?
rx/tx are used for serial. lin uses the separate extra digital output single wire.
 
rx/tx are used for serial. lin uses the separate extra digital output single wire.
WhatsApp Image 2024-10-24 at 6.34.41 PM.jpegWhatsApp Image 2024-10-24 at 6.33.57 PM.jpeg
See these battery , current etc readings they don't change.
And if I press or turn the throttle
Crc error shows up
And no reading is recorded until I release the throttle.
But when I don't give any throttle and just press the brake.
Brake is printed with the 12 bit of data..I look forward to your feedback. Thanks
 
View attachment 361105View attachment 361106
See these battery , current etc readings they don't change.
And if I press or turn the throttle
Crc error shows up
And no reading is recorded until I release the throttle.
But when I don't give any throttle and just press the brake.
Brake is printed with the 12 bit of data..I look forward to your feedback. Thanks
you are getting EMI that corrupts the signal. having a proper ground (from the controller preferably) and good connectors can combat that. also when determining if the bit is 1 or 0, just checking if one interval is greater than the other helps
 
you are getting EMI that corrupts the signal. having a proper ground (from the controller preferably) and good connectors can combat that. also when determining if the bit is 1 or 0, just checking if one interval is greater than the other helps
Thanks alot for the feedback. I am still working to resolve this issue. I have a VOTOL EM-50-4 controller and 60V 1500W QS hub motor, for regenerative breaking what is the maximum safe % EBS value i can set to, currently for my motorcycle i have set it to 20%.
 
Thanks alot for the feedback. I am still working to resolve this issue. I have a VOTOL EM-50-4 controller and 60V 1500W QS hub motor, for regenerative breaking what is the maximum safe % EBS value i can set to, currently for my motorcycle i have set it to 20%.
I also have an em-50-4 on my ebike but I use the serial protocol to get the data as it's far better suited for my application. I use an esp32 that has classic bluetooth serial to passthrough the connection when I want to program the controller using phone or pc, and at that point only I use the lin signal for display data.
 
Hello,

Could you please help me / share your files for communication from controller to super soco tsx display? I have zero programming experience but I hope that I could put your code to chat gpt and get it a bit personalized.. but basically I'm ok just with battery percentage and speed on the tsx display.

Thanks a lot
 
Hi all!! i have a very weird issue here... i have arduino nano working with a round display with one lin protocol to show speed amps brake etc... but it works only with led lights turned off.... as soon as i turn on the led lights (parking lights) the information is slow or wrong or does not come at all...even worse if i turn on full beams lights, seems to reset arduino..
tried different ways to power arduino VIn usb etc and also external power same results... start to think about noise in the line but works fine with no lights ...so what would you do here?20241205_083952.jpg
 
Hi all!! i have a very weird issue here... i have arduino nano working with a round display with one lin protocol to show speed amps brake etc... but it works only with led lights turned off.... as soon as i turn on the led lights (parking lights) the information is slow or wrong or does not come at all...even worse if i turn on full beams lights, seems to reset arduino..
tried different ways to power arduino VIn usb etc and also external power same results... start to think about noise in the line but works fine with no lights ...so what would you do here?View attachment 362711
First of all nice display. If not already the case, the ground for the arduino should come from the controller. Also in order to minimize noise and EMI it's best to use good quality connectors and wiring (perhaps even shielded). For my displays I use shielded USB cables (4 or 8 wires, depending on your display). Good luck!
 
Thanks trufinv... do you mean shield the single wire from the votol to the arduino? or shield the lines from the rounded display to the arduino nano...
 
Thanks trufinv... do you mean shield the single wire from the votol to the arduino? or shield the lines from the rounded display to the arduino nano...
if your signal from arduino to the display gets corrupted then you can try using shielded wires for the rounded display. If you feel the issue is dropped packages from the controller you might look into shielding the single wire from the controller, and specially use good connectors. On my motorcycle I soldered the input wire to the pin on my esp32.
But first of all make sure the arduino ground comes from the controller directly
 
ok thks... looks perfect from arduino to display... the single wire from votol to arduino is from stock speedmeter and might be standar wire for analog signal... maybe i will replace with shielded wire and report back but this is weird... i though i would need maybe pull up resistors or change something in the code....
 
turned out to.be the inverter case attached to chasis and generating noise! ... i have attached to another place and used plastic screws... the wires remained as twisted pairs no shield..thks
 
@marce002
Yes it's a bitwise XOR, that is part of the C language, so it is already included.

Update:
I fixed the code parts that were changed by forum formatting.

C++:
/*
* Original code from trufinv. See:
* https://www.endless-sphere.com/sphere/threads/using-fardrivers-lin-protocol.120345/#post-1805274
* https://www.endless-sphere.com/sphere/threads/votol-serial-communication-protocol.112970/page-2#post-1805276
*/

const unsigned int SIF_PIN = 2;
short battery = 0;
short current = 0;
short currentPercent = 0;
short rpm = 0;
long faultCode = 0;
bool regen = false;
bool brake = false;
unsigned long lastTime;
unsigned long lastDuration = 0;
byte lastCrc = 0;
byte data[12];
int bitIndex = -1;

void setup()
{
  Serial.begin(115200);
  Serial.println("Begin.");

  pinMode(SIF_PIN, INPUT);
  lastTime = micros();
  attachInterrupt(digitalPinToInterrupt(SIF_PIN), sifChange, CHANGE);
}

void loop()
{
  // put your main code here, to run repeatedly:
}

void sifChange()
{
  int val = digitalRead(SIF_PIN);
  unsigned long duration = micros() - lastTime;
  lastTime = micros();
  if (val == LOW)
  {
    if (lastDuration > 0)
    {
      bool bitComplete = false;
      float ratio = float(lastDuration) / float(duration);

      if (round(lastDuration / duration) >= 31)
      {
        bitIndex = 0;
      }
      else if (ratio > 1.5)
      {
        // bit value 0
        bitClear(data[bitIndex / 8], 7 - (bitIndex % 8));
        bitComplete = true;
      }
      else if (1 / ratio > 1.5)
      {
        // bit value 0
        bitSet(data[bitIndex / 8], 7 - (bitIndex % 8));
        bitComplete = true;
      }
      else
      {
        Serial.println(String(duration) + "-" + String(lastDuration));
      }

      if (bitComplete)
      {
        bitIndex++;
        if (bitIndex == 96)
        {
          bitIndex = 0;
          byte crc = 0;

          for (int i = 0; i < 11; i++)
          {
            crc ^= data[i];
          }

          if (crc != data[11])
          {
            Serial.println("CRC FAILURE: " + String(crc) + "-" + String(data[11]));
          }

          if (crc == data[11] && crc != lastCrc)
          {
            lastCrc = crc;
            for (int i = 0; i < 12; i++)
            {
              Serial.print(data[i], HEX);
              Serial.print(" ");
            }
            Serial.println();
            battery = data[9];
            battery = data[9];
            current = data[6];
            currentPercent = data[10];
            rpm = ((data[7] << 8) + data[8]);  // * 1.91; TODO: Change ratio to match wheel-size/motor gearing.
            brake = bitRead(data[4], 5);
            regen = bitRead(data[4], 3);

            Serial.print("Battery %: " + String(battery));
            Serial.print(" Current %: " + String(currentPercent));
            Serial.print(" Current A: " + String(current));
            Serial.print(" RPM: " + String(rpm));
            if (brake) Serial.print(" BRAKE");
            if (regen) Serial.print(" REGEN");
            Serial.println();
          }
        }
      }
    }
  }
  lastDuration = duration;
}
Hello! I connected my Votol EM-50-4 to Arduino nano to pin D3 (I only changed the pin to D3 in the sketch you provided). Everything works fine except: Battery %, Current %, Current A. Please help me figure it out. This is what aruino returns to the serial
 

Attachments

  • 1.png
    1.png
    28.5 KB · Views: 63
  • 2.png
    2.png
    29.9 KB · Views: 64
if your signal from arduino to the display gets corrupted then you can try using shielded wires for the rounded display. If you feel the issue is dropped packages from the controller you might look into shielding the single wire from the controller, and specially use good connectors. On my motorcycle I soldered the input wire to the pin on my esp32.
But first of all make sure the arduino ground comes from the controller directly
I think I fixed the battery percentage, but there are problems with the current measurement. Can you help me please?
 

Attachments

  • 3.png
    3.png
    26.8 KB · Views: 37
  • 4.png
    4.png
    16.8 KB · Views: 36
Forgive me for my madness. The current detection seems to work fine. Zero values are shown because the motor was not under load. When give it a load, the amperes are shown roughly but correctly. The controller does not transmit voltage, I used this data from a board I designed myself. Here is my code works fine.
 

Attachments

  • 1.png
    1.png
    17.7 KB · Views: 51
Last edited:
Back
Top