VOTOL serial communication protocol

@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;
}
 
Last edited:
I have tested it on Votol EM-70 and get rpm, current (A), regen, etc.

Notice current switches between charge/discharge at 128.
 
It compiled ok, but i cant see anything in my serial monitor,,, i have connected the line to my RX on nano... with a simple serial input i can read something so i know it is reading, but with your code nothing shows up, what can i have wrong?


this is my serial with a sinle code using 9600

if(Serial.available()){
c = Serial.read();
Serial.println(c,HEX);

FFFFFFFF
30
FFFFFF8C
FFFFFF8E
FFFFFF8C
FFFFFFCC
FFFFFF8E
FFFFFF8C
FFFFFFCC
...
 
but the votol controller is talking at 9600 right? should i change with pc soft app? i have changed just in code and still nothing.. I have connected the single line wire , to my ARDUINO NANO pin labelled RX , is this ok right?
 
Last edited:
Edit: it's actually 667 Hz.
---, but trufinv's code uses timing, to detect high/low bits.

Did you set SIF_PIN to the input you use.

Did you see "Begin" on your serial output.
 
Last edited:
Guys, this is not serial communication. Just use any port that supports hardware interrupts. The actual bits are being determined by the algorithm. If you're getting bad results when the controller is in PWM mode (wheel moving) keep in mind that these controllers give huge EMI. Shielding, shorter cables, proper solders, etc fixes these issues.

Look at the protocol documentation that I posted here to get a better understanding of how this works.

Also keep in mind that ESP32 and arduino handle data types and hardware interrupts a bit differently. I remember having to do some slight changes when moving to ESP32. Try to limit the interrupt function just for getting the data and do the processing in the loop elsewhere.
 
Oh yes, the EMI is through the roof here. Kind of hard to collect reliable data for a Odometer.
had issues displaying the data on an external display also. Fixed by using usb cable with shielding for data transfer. Also instead of checking like this: "if (ratio > 1.5)", you could just compare the values like lastDuration > duration. It seems to provide more accurate data. Also avoid any kind of float operations in the interrupt if you're using ESP32.
 
attached is my teraterm log, it seems to be receiving data, can anyone tell me if its the same data you would expect?
LOGGED SESION IS: turned on, accelerated and brake then turned off.
 

Attachments

  • teratermonelin.log
    24.4 KB · Views: 35
attached is my teraterm log, it seems to be receiving data, can anyone tell me if its the same data you would expect?
LOGGED SESION IS: turned on, accelerated and brake then turned off.
I'm not familiar with Teraterm, so can't read the log-output as it is.

Remember trufinv's implementation is not using the regular serial-reader. It might be possible to implement with a serial UART input, but I haven't had success with my (few) experiments. A software-defined serial implementation is probably the best way to do it.

I see some possibilities to improve trufinv's excellent proof-of-concept.
- sync on >900 Tosc Low, followed by 32 Tosc High.
- filter EMI where period is below 32 Tosc.
- filter EMI where period is above 64 Tosc.

@trufinv With 32us <Tosc <320us, Can you clarify wether Tosc is a constant time of 1/9600 (104 microseconds) or 32*Tosc ?
 
i do not think is that complex... i do not know what is 32*Tosc and the like..... the log is pretty strightforward a lot or FFFFFFF BUT nothing useful....
Maybe some port on votol page to redirect signal to my wires? because my log is not having any data i can parse.....
 
Hello all, I tried implementing the code provided by @trufinv. I'm using an arduino nano with interrupt pin set at digital pin 2 (d2). got a very wonky reading on the Serial Monitor, found some CRC failure and the data frame bytes are mostly all zeroes. In most cases my nano are unable to determine whether the bit are 1 or 0. I think this happens the ratio parameters never seems to get beyond 1.5. I dont understand why this happens since i think the code are already correct. I post some of my serial monitor reading below:

20:21:26.783 -> No bit sampled.
20:21:27.000 -> 8 61 A 80 2 0 0 0 0 0 0 E1
20:21:27.000 -> Battery %: 0 Current %: 0 Current A: 0 RPM: 0
20:21:27.874 -> No bit sampled.
20:21:27.874 -> No bit sampled.
20:21:27.927 -> No bit sampled.
20:21:28.042 -> CRC FAILURE: 240-112
20:21:28.042 -> No bit sampled.
20:21:28.042 -> No bit sampled.
20:21:28.076 -> No bit sampled.
20:21:28.076 -> No bit sampled.
20:21:28.076 -> No bit sampled.
20:21:28.076 -> No bit sampled.
20:21:28.076 -> No bit sampled.
20:21:28.076 -> No bit sampled.
20:21:28.111 -> No bit sampled.
20:21:28.111 -> No bit sampled.
20:21:28.225 -> CRC FAILURE: 124-28

Thank you!
 
Last edited:
had issues displaying the data on an external display also. Fixed by using usb cable with shielding for data transfer. Also instead of checking like this: "if (ratio > 1.5)", you could just compare the values like lastDuration > duration. It seems to provide more accurate data. Also avoid any kind of float operations in the interrupt if you're using ESP32.
Can you share what your current setup is? I'd love to try this on my FarDriver and have a nicer external display. Thanks!
 
Can you share what your current setup is? I'd love to try this on my FarDriver and have a nicer external display. Thanks!
Well I have 3 of them, one ebike and 2 electric motorcycles. The displays are 0.96" SSD1306, 2" ST7789V and 3.2" ILI9341. The 0.96" is in a simple voltmeter/ignition enclosure and I use the KT display buttons to scroll between screens, for the other 2 I've designed and 3d printed enclossures (they also have a button on the side to scroll between screens). All are driven with esp32s
 

Attachments

  • 20240802_103122.jpg
    20240802_103122.jpg
    1.5 MB · Views: 44
  • 20240802_103126.jpg
    20240802_103126.jpg
    1.7 MB · Views: 44
  • WhatsApp Image 2024-07-14 at 20.22.14_2bf14c06.jpg
    WhatsApp Image 2024-07-14 at 20.22.14_2bf14c06.jpg
    200.6 KB · Views: 43
  • WhatsApp Image 2024-07-31 at 14.25.25_7aed00dc.jpg
    WhatsApp Image 2024-07-31 at 14.25.25_7aed00dc.jpg
    91.8 KB · Views: 38
I see some possibilities to improve trufinv's excellent proof-of-concept.
- sync on >900 Tosc Low, followed by 32 Tosc High.
- filter EMI where period is below 32 Tosc.
- filter EMI where period is above 64 Tosc.

@trufinv With 32us <Tosc <320us, Can you clarify wether Tosc is a constant time of 1/9600 (104 microseconds) or 32*Tosc ?
Finally received my new oscilloscope and did some measurements.

992Tosc = 60 ms.
64Tosc = 1 ms.
32Tosc = 0.5 ms.

Restricting bits to fit these timings, might improve accuracy in a noisy environment.


1000006899.jpg
 
Last edited:
hello #TRUFINV

I'm trying to display Battery Volt like your picture in the OLED 0.96 display, but i find only 0 in that byte.... RPM, brake, and current seems to be working fine, but cant find any data for battery (despite that Volt is not present in the datasheet you were given) that is why I ask you how did you determine Volts in your OLDE picture post #69
my controller is 96v EM100 , OCT 2024 , maybe they changed something?
 
Last edited:
hello #TRUFINV

I'm trying to display Battery Volt like your picture in the OLED 0.96 display, but i fin only 0 in that byte.... RPM, brake, and current seems to be working fine, but cant find any data for battery (despite that Volt is not present in the datasheet you were given) that is why I ask you how did you determine Volts in your OLDE picture port #69
my controller is 96v EM100 , OCT 2024 , maybe they changed something?
i actually have it dual mode. most of the time I get my data (voltage included) via serial. when i connect my phone or laptop to the esp32 via bloetooth serial then I use the sif (display output) and forward the serial packages from the phone/pc to the votol so it can be programmed
 
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];
 
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 where outdated beause come 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];
fardrivers allow you to customize the sif packets up to a point whereas votols don't. however for me the votol is better as I can use the serial protocol that is well documented and works great and provides better data than the very limited sif packages.
 
@marce002
The Votol do not output battery-volt over "one-lin" on my tests. (It makes sense to add those values when passing data through a BMS).

The protocol from @trufinv is for a Votol-compatible display. It might be capable of more data, than a Votol-controller can provide.
 
Back
Top