ElectricMotorcycleForum.com
Makes And Models => Zero Motorcycles Forum | 2013+ => Topic started by: remmie on March 10, 2019, 09:47:00 PM
-
Impressed with the new dash of the Zero SR/F dashboard with the TFT screen I tried to create a similar screen for my 2018 Zero SR.
Hardware used :
* Adafruit ESP32 Feather (arduino like microcontroller) https://www.adafruit.com/product/3405 (https://www.adafruit.com/product/3405)
* Adafruit 3.5" TFT Featherwing (3.5" display specially tailored for the Adafruit Feather Series https://www.adafruit.com/product/3651 (https://www.adafruit.com/product/3651)
* SKPANG Can-Bus Feather http://skpang.co.uk/catalog/canbus-featherwing-for-esp32-p-1556.html (http://skpang.co.uk/catalog/canbus-featherwing-for-esp32-p-1556.html)
* OBD-II Cable to Dsub9 connector http://www.hobbytronics.co.uk/obd-ii-db9-cable (http://www.hobbytronics.co.uk/obd-ii-db9-cable)
* 3D printed housing that secures the unit to my TomTom Rider mount (Self designed and printed)
Special Thanks to user CrashCash in this Thread : http://electricmotorcycleforum.com/boards/index.php?topic=6809.msg56423#msg56423 (http://electricmotorcycleforum.com/boards/index.php?topic=6809.msg56423#msg56423) that gave me the idea to snoop the Canbus en getting me started on most of the CanId's. I found a few more than in this thread and in a following post i will sum up all the CanId's I've found and or used up till now so others can take advantage of it.
Upside of this project is that i don't need the Zero App any more to display some data i like to know during a ride.
Downsides are there is a heep of information on the screen now so distraction lurks :) and the display is nowhere near waterproof but i don't ride in the rain anyways.
This first (and second) post will probably be updated as i find more CanId's and their information
I've only ridden with this dash for 1 ride now, but it works very well. The speed and the colored "speed ring" update every 0.1 seconds and the other values every 0.5 seconds. This is more than fast enough. Sunlight glare is an issue so you have to position the display so the reflection of the display doesn't project the sky but it projects your body, that way the display stays readable.
I also introduced a few additional values by calcualtions from some of the gahtered values:
* Stint Time and Kilometers
* surplus Range. Which is the extra range i have left compared to a 150 km run using the Trip 1 meter. 150 km is an "average range" i want to get during a longer tour. so if the trip 1 reads 50 and the range is 120 kilometers the surplus is (150-120+50 = 20 kilometers.
* battery cell voltage. By dividing the Total battery voltage with 28 you get the individual cell voltage. It is not critical information but is easier to understand than what 102 Volt for the entire pack is.
* by using the Amps in and out of the pack I have made a "Coulomb Counter" to see if I can make the SOC "Guess-O-Meter" a bit more accurate. The 2018 14.4 kWh pack is supposed to have 124Ah of energy in it, so with this gauge I hope to get the actual SOC better.
To Do :
* To make the base for the surplus range adjustable through the touchscreen. This would allow me to make a route to the next charging station on the TomTom and set the surplus range calculator value to the kilometers to go of the TomTom. That way i only have to keep the surplus positive to be able to get there. (it avoids constant calculations in your head : Am i gonna make it, i have 54 km and the range is 65 so I have eeeeeh 11 km Surplus. and a minute later the same calculation with 52 and 61 km which yields only 9 kilometers surplus)
* to make a 3D printed housing that exactly covers the existing Dash.
Any other idea's you guys (and girls) have ??
P.S. I'm not writing anything to the Canbus, I'm just snooping values of it. Also i use the ODB connector which is accesable under the seat, So i don't have to cut into the wiring harness in any way.
Forgot to mention that the ESP32 has both bluetooth and WIFI capabilities, a 240 Mhz DUAL CORE processor so there is a LOT to explore there :)
-
CanID's (and a picture of the startup screen :)
Here's a snippet of my Arduino Code where the CanId's are converted to values
if (canId == 0x0188)
{
SOC2 = buf[0];
// ?? byte 1 (always 0)
// ?? byte 2 (always 66, but 3 while charging in another session)
ChargeCycles = buf[3] + 256 * buf[4]; // byte 3 is number of charge cycles (probably byte 4 as well (highbyte)
CellBalance = buf[5]; // byte 5 could be Cel Balance value
// ?? byte 6 (always 0)
// ?? byte 7 (always 4 maybe number of bricks ?)
}
if (canId == 0x01C0)
{
// ?? byte 0 (fixed at 32)
// ?? byte 1 (fixed at 0)
DashTimeHours = buf[2]; // byte 2 is Dash Time Hours
DashTimeMinutes = buf[3]; // byte 3 is Dash Time Minutes
DashTimeSeconds = buf[4]; // byte 4 is Dash Time Seconds
NumberOfCanMessageSets = buf[5] + 258 * buf[6] + 256 * 256 * buf[7]; // byte 5 - 7 is number of canbus message SETS received since power on ??
}
if (canId == 0x0206) // this canID only appears during charging !!
{
// ?? byte 0 (batterypack temperature ??)
// ?? byte 1 (fixed at 2 during charging)
ChargerVoltage = buf[2] * 0.001 + buf[3] * 0.256 + buf[4] * 65.535; // byte 2-3-4 Charger voltage in mV (division by 1000 gets Volts)
// ?? byte 5 (fixed at 1)
// ?? byte 6 (fixed at 0)
// ?? byte 7 (fixed at 0)
}
if (canId == 0x240)
{
if (bitRead(buf[0],2) == 1) Mode = "Sport "; //A5 // CanID 0x0240 byte 0 = Mode
if (bitRead(buf[0],3) == 1) Mode = "Eco "; // A9
if (bitRead(buf[0],4) == 1) Mode = "Custom"; // B1
// ?? byte 1 = always 0F : unknown
Speed = (buf[2] * 0.01) + (buf[3] * 2.56); // byte 2 and 3 is Speed in kph
PowerBar = buf[4]; // ?? byte 4 is Power bar on dash ?? (It is power related)
TorqueBar = buf[5]; // ?? byte 5 is Torque bar on dash ?? (It is Torque related)
SOC = buf[6]; // byte 6 is State Of Charge on dash in %
// no Byte 7
}
if (canId == 0x0281)
{
MotorRPM = buf[0] + 256 * buf[1]; // ?? byte 0, 1 is motor RPM (same as canID 0x0340 byte 4 and 5)
// ?? byte 2 (fixed at 0)
// ?? byte 3 (fixed at 0)
ThrottlePosition = buf[4] + 256 * buf[5]; // ?? byte 4, 5 is throttle position (around 125 is closed, aroud 1200 is fully open)
MotorTemp = buf[6]; // ?? byte 6 is motor temperature (second time) this time in 1 byte , not 2 as in canid 440
// ?? byte 7 (fixed at 0)
}
if (canId == 0x0288)
{
Power = buf[0] + 256 * buf[1]; // byte 0, 1 Power in Watts
// ?? byte 2 (fixed at 231)
// ?? byte 3 (fixed at 0)
// ?? byte 4 (fixed at 50)
// ?? byte 5 (fixed at 114)
// ?? byte 6 (fixed at 0)
// ?? byte 7 (fixed at 18 not any temperature))
}
if (canId == 0x340)
{
Trip1 = buf[0] * 0.01 + buf[1] * 2.56; // byte 0, 1 is Trip 1
// ?? byte 2 (always 0)
// ?? byte 3 (always 0)
RPM = buf[4] + 256 * buf[5]; // byte 4, 5 is motor RPM
ErrorCode = buf[6]; // byte 6 (error code on dash, 44 is killswitch, 45 is kickstand etc)
// ?? byte 7 (always 0)
}
if (canId == 0x0381)
{
// ?? byte 0
// ?? byte 1
ControllerTemp = buf[2];
// ?? byte 3
// ?? byte 4
// ?? byte 6
// ?? byte 7
}
if (canId == 0x0388)
{
// ?? byte 0 (very random ?? 0-25 when speed = 0 , goes higher/lower when powering or regen)
// ?? byte 1 (very reandom ?? 246-248 when speed = 0, goes higher/lower when powering or regen)
// ?? byte 2 (always 15)
Vbatt = 0.001 * buf[3] + 0.256 * buf[4] + 65.535 * buf[5]; // byte 3, 4 and 5 are Vbatt in units of mV (divide by 1000 to get Volts)
// ?? byte 6 (always zero)
// ?? byte 7 (increases with torque/power and is negative with regen : could be power/torque/battery or motor amps
}
if (canId == 0x0408)
{
// byte 0
BatteryTemp = buf[1]; // byte 1 is battery temp in celcius
// byte 2 (same as byte 1) could be second temp sensor ?? needs change in battery temp)
BatteryAmps = buf[3] + 256 * buf[4]; // byte 3 battery amps, both when charging and
// byte 5 (fixed at 131)
// byte 6 (fixed at 0)
// byte 7 (fixed at 255)
}
if (canId == 0x0440)
{
Trip2 = buf[0] * 0.01 + buf[1] * 2.56; // byte 0, 1 is Trip 2 in units of 10 mtrs (divided by 100 to get KM)
// byte 2 (fixed at 0)
// byte 3 (fixed at 0)
Range = buf[4] * 0.01 + buf[5] * 2.56; // byte 4, 5 is range in units of 10 mtrs (divided by 100 to get KM)
MotorTemperature = buf[6] * 0.01 + buf[7] * 2.56; // byte 6, 7 is motortemperature in 0.01 degrees but only 2200, 2300, 2400 (divsion by 100 is immediate integer value)
}
if (canId == 0x0508)
{
// ?? byte 0,1 ,2 are number of seconds used or maybe even lifetime possibly bytes 3 and on also included (at feb-2019 bytes are (xxx,223,114,92,10,0,75,0)
// ?? byte 1
// ?? byte 2
// ?? byte 3
// ?? byte 4
// ?? byte 5
// ?? byte 6
// ?? byte 7
}
if (canId == 0x02C0)
{
ODO = 0.1 * buf[0] + 25.6 * buf[1] + 6553.5 * buf[2]; // byte 0, 1 and 2 are ODO in units of 100 mtrs (divided by 10 to get KM)
// ?? byte 3 (fixed at 0)
// ?? byte 4 (fixed at 0)
// ?? byte 5 (fixed at 0)
// ?? byte 6 (fixed at 16)
// ?? byte 7 (fixed at 0)
}
}
-
Niiiccce!
-
I wish I had your brain!
Until that happens, I'd just like to be able swap out my 2017 DSR dash out for the sr/F dash.
-
Brilliant! Where/how do you tap into the CANBUS data?
-
Great work, and thanks for documenting your findings! I’ll try to have a deeper look later but this means a lot for increasing the visibility of the system state.
-
Brilliant! Where/how do you tap into the CANBUS data?
Under the seat there is a OBD-II Connector. By using a OBD to Dsub cable (4th item in the hardware list) the canbus is available on pins 3 (Can-high) and pin 5 (Can-Low) and pin 2 (Can GND)
Because the Featherwing display and the ESP 32 Feather and the Canbus Featherwing all have the same pinout and can be stacked there is just a little bit of basic soldering to do before the programming starts. In the picture you can see how the inside of the box looks like
Pin Description OBDII DB9
J1850 BUS+ 2 7
Chassis Ground 4 2
Signal Ground 5 1
CAN High J-2284 6 3
ISO 9141-2 K Line 7 4
J1850 BUS- 10 6
CAN Low J-2284 14 5
ISO 9141-2 L Line 15 8
Battery Power 16 9
So the canbus is the J-2284 bus
-
Nice work :) You could make a github repo for this, that would be helpful. :)
Also thinking about a project like this, in doubt whether I'm going the canbus route of the serial route, but I don't know if the serial is active during charging yet. My main goal is to control the charging by my domotica so the bike doesn't always charge to 100%. So report SOC and some other stuff through lora/wifi (if available) to Home Assistant and then control a relay.
-
Brilliant! Where/how do you tap into the CANBUS data?
Under the seat there is a OBD-II Connector. By using a OBD to Dsub cable (4th item in the hardware list) the canbus is available on pins 3 (Can-high) and pin 5 (Can-Low) and pin 2 (Can GND)
Thanks!
-
Re: GitHub, there is an organization I'd be glad to invite you to, which hosts forks or original repos for posterity and maintenance:
https://github.com/zero-motorcycle-community
-
Second iteration with a 3d printed housing which covers the original dash.
Also some improvements in the value and now includes a 0-100 kph meter :)
I’look into github to upload the entire code into the zero motorcycles section you pointed out Richard, thanks.
Today was the first ride with this new dash. Very good visibility for the speed. All other values like temperatures, voltages, distances and times are very nice to see.
Still to do is proper weatherproofing :) for the occasional rain shower we have here in the netherlands
Another thing added is the errorcodes and their (short) explanations are now also shown at the top
-
Very nice progress! I wonder whether a board with a premade weatherproof enclosure is available to program similarly.
I found that there are at least some tidy printable solutions like:
https://learn.adafruit.com/3d-printed-case-for-adafruit-feather/tft-feather-wing
-
This thing comes with a solar panel too! ;D
But hey it could work.
https://www.ebay.com/itm/GoalZero-Venture-30-Kit-Weatherproof-Phone-and-Tablet-Charger-and-Solar-Panel/273759762164?hash=item3fbd5a76f4:g:oogAAOSwv-JciY76
-
full code is now up on github
https://github.com/remmie1972/Zero-Dashboard/blob/master/Zero_CANBUS_Dash_V1_13_ESP32.ino (https://github.com/remmie1972/Zero-Dashboard/blob/master/Zero_CANBUS_Dash_V1_13_ESP32.ino)
-
A few general recommendations regarding your code...
Use a switch statement, something like :
switch(canId) {
case 0: break;
case 0x0181: {
memcpy(canID181, buf, 8);
break;
}
case 0x0188: {
// .....
break;
}
}
Define macros for common expressions. Instead of
outerRingColor = ((0 >> 3) << 11) | ((128 >> 2) << 5) | (0 >> 3);
innerRingColor = ((0 >> 3) << 11) | ((255 >> 2) << 5) | (0 >> 3);
Do something like :
#define COLOR_X (((0 >> 3) << 11) | ((128 >> 2) << 5) | (0 >> 3))
#define COLOR_Y (((0 >> 3) << 11) | ((255 >> 2) << 5) | (0 >> 3))
outerRingColor = COLOR_X;
innerRingColor = COLOR_Y;
Same for parameterized ones.
BatteryAmps = buf[3] + 256 * buf[4];
Becomes :
#define MAGIC(buf,x,y) ( buf[ x ] + 256 * buf[ y ])
BatteryAmps = MAGIC(buf, 3, 4);
And minor style things like
NumberOfCanMessageSets = buf[5] + 258 * buf[6] + 256 * 256 * buf[7];
to :
NumberOfCanMessageSets = buf[5] + (258 * buf[6]) + (256 * 256 * buf[7]);
Obviously no change in arithmetic precedence, but the reader clearly understands the intent and bugs will less likely sneak in
ChargerVoltage = buf[2] * 0.001 + buf[3] * 0.256 + buf[4] * 65.535;
I'd refactor that to use integer arithmetic, especially since your result is a long. This is both to reduce loss in precision and account for possibility of emulated floating point arithmetic.
-
Is it not possible to use the standard dash?
I (here we go) assume that it's a LCD screen?
-
Is it not possible to use the standard dash?
I (here we go) assume that it's a LCD screen?
The standard dash LCD is hardwired to display exactly what it already displays and not a single item more. DigiNow ‘s ability to display other parameters happens by sending messages of the same format with different values for the alternative parameters. It’s a bit of a hack, albeit reasonable.
-
Thanks very much for publishing the code. What kind of collaboration seems appropriate to you?
The code suggestions above seem reasonable to me. One of us could make a github pull request to implement them for you if it’s not fun for you to do those changes.
I’m recovering from a trip but will try to have a better look later.
-
Hi Richard, no problem sharing the code, Others could benefit from it making their own projects or just copying (bits of) this one :)
I have no idea what a pull request is or what it should do on github ;D . I'm a hobbyist programmer and I suspect pacificcricket is a programmer by profession.
I do appreciate suggestions. And as for his suggestions, I don't see real valid reasons to change the code. All the suggestions just add more lines of code and do not improve the readability. (in my opinion!)
Here's my view on things:
Using a switch instead of multiple if-statements could be valid but does not add any functionaltity or even clarity from my point of view. The If statements are very clearly for 1 single CANID and broken down byte by byte. Adding case and break lines just clutters the code (again MY opinon). I do agree that after the first if statement the rest would be better off using "else if" instead of a new "if" because then the routine is faster because when it has found the right canID it skips the rest. Maybe a switch case statement does the same, i wouldn't know. I'm just a (novice) hobbyist programmer.
The comment regarding the color definitions would ADD 12 definition lines at the top of the code for the same 12 lines it takes now to define the colors (every colour statement is just use once). I just dont see the point in that. it's adding (lines of) code with no real purpose (for ME).
The same for the grouping of the arithmetic "buf[1] + 256*buf[2] should be buf[1] + (256*Buf[1])". Well that is just basic arithmetic. If that is not clear enough that you must multiply before adding than the brackets are not going to help either. Again, it's more (lines of) code for no clear reason (again for ME)
next would be the macro definitions for the same line. WHY ? is it not clear enough that byte 1 and 256* byte 2 are added ?? Defining a macro "Magic(buf,1,2)" does not really make it any clearer (to ME). and again would just add some lines of code.
The last comment regarding integer arithmetic because of using a long could be valid. I don't know because i don't know what all that means. I have a general idea though. But in this way the displayed value is accurate for all values the parameter can get to in the real world, so.....
I would be very happy to add or change functionality for this project. But i'm not likely going to change the code to accomodate these (style) changes. I'd like to spend my (limited) time on adding things like :
* compass direction and distance to the nearest EVSE station and display that on the screen as well so i don't have to have my phone app ready all the time. I have ordered a small GPS receiver for this. Any suggestions for searching through a database of EVSE-POIS and selecting the nearest one with direction and distance would be extremely appreciated. 8) A warning when range minus distance to the nearest EVSE is below a certain threshold is also easy (making the range display in red for example)
* an altitude indicator. That would be an easy one with the addition of a GPS receiver. The higher the altitude the better the range because of the thinner air ;D (off course climbing to that higher altitude costs more battery, but still nice to know the altitude)
* GPS logging onto the SD card that can be put into the TFT screen. after a long trip it's always nice to have the route on a map inbetween the photo's of the trip ;)
Upon request i shared the code and eveyone is free to use it or bits from it. That benefits us all!!. After all the idea came from someone else on this forum. I expanded his good work with functionality that i liked and all the values are accurate and work well.
Greetz
P.S. This is not meant to be an angry post, not at all and i do think that the comments are meant to improve the code. It is merely my point of view on the proposed changes to be made. They don't add functionality, and that is my main objective. 8)
-
As for improved functionality :)
The dashboard cover i made previiouosly meant that the two buttons on the original dash were not accesable anymore :) So i redesigned the cover to include a small section that can be easily removed to access both buttons.
With all the data on the dash the only thing the buttons will be used for is setting the (daylight savings) time and resetting the trip 2 meter.
I'll post a picture of the new cover later.
-
And as for his suggestions, I don't see real valid reasons to change the code.
No worries, it's your project and you're free to run it in your own way :)
-
Agreed all around.
The point usually of code changes that don’t add functionality is to make functionality easier to add. One reason I haven’t augmented existing code bases that parse and display Zero logs is that they’re all very hard to extend.
This is why we have several log parsers and almost no data processing code.
Hell, we can’t even jump to points in a log by date. Every time I parse a log roadside I have to scroll manually through several thousand lines just to get to today’s logs.
So, you can bet I’m going to fork this and take pull requests.
-
The point usually of code changes that don’t add functionality is to make functionality easier to add.
Or make the code less bug prone. Or make it run faster (especially counts for microcontrollers).
-
Fuckkkkkk I'm in love! I want it!
-
Made some improvements among which a little lid on the 3D printed bracket so i can still reach the 2 pushbuttons on the original dash.
Also a gps module with which i can track a trip (logging every 30 seconds) and see the GPS altitude. Altitude is not varying very much here in the netherlands (highest is about 100 meters :) but still nice to see.
Attached is a new view when charging, the main dial then displays charging amps and I also found the canID's for chargetimetogo, momentary Ampshours left and the total amps hours as reported by the MBB. interestingly when the battery is full the momentary Amphours (124) is larger than the max amphours of 114.
-
Here's a screenshot of the GPS log made by GPSvisualizer
-
That is great progress!
I especially like that temperature is visualized with a little range, but it's a little hard to tell min/max/current values from each other.
Mainly, it's impressive that the layout is good and a healthy amount of information can be displayed in once screen, right down to cel balance.
I guess I might have a try at designing a layout concept in case it would be welcome... something to group values together so it's easier to summarize while riding.
-
> Impressed with the new dash of the Zero SR/F dashboard with the TFT screen I tried to create a similar screen for my 2018 Zero SR...
You and crashcash are inspiring!
-
Awesome work! Looks great.
-
What a big job ! :o impressed !
Do you know what is displayed on the SR/F with the circular colored bars ? This is the % of SoC.
On your pictures it's not, i don't understand what is related to on your's.
-
Speeeeeed! :)
-
> Impressed with the new dash of the Zero SR/F dashboard with the TFT screen I tried to create a similar screen for my 2018 Zero SR...
You and crashcash are inspiring!
We want pictures :) :) 8)
-
Speeeeeed! :)
Ok, why not :)
You can see quickly how close to the maximum speed you are.
You should make it turn into red after passing the legal speed limit, using GPS and maps data (a lot of work maybe)
-
Hi Remmie
I know this is a while ago but what chip was on SKPang board as the link is broken and the SKPang feather now has a SN65HVD231 CAN transceiver.
Trouble is that is runs at 16mhz and I beleive the Zero runs at 8Mhz, can you confirm.
I can read data with an MCP2515 set to 500KBPS and 8MHZ but I was hoping on using an esp32 feather.
-
Hi Remmie
I know this is a while ago but what chip was on SKPang board as the link is broken and the SKPang feather now has a SN65HVD231 CAN transceiver.
Trouble is that is runs at 16mhz and I beleive the Zero runs at 8Mhz, can you confirm.
I can read data with an MCP2515 set to 500KBPS and 8MHZ but I was hoping on using an esp32 feather.
Hello. I also recreated this great thing. had problems with canfeather. already tried everything and then simply (as you wrote) set the baud rate to 1000 in the sketch. then it works. is of course not the right solution.
-
Hey all. I've now derived the time and date from CAN address 0508. It works. I use the display (without the speedometer) to see all the Zero data. I have separate battery voltages for Accu 1/12.5 and Accu 2/7.2.
-
Great project. Thanks to everyone who has shared code and suggested/made improvements.
“If I have seen further, it is by standing on the shoulders of giants.” Sir Isaac Newton.