CAN Network Reverse Engineering | Creating DBC | iMiEV

Mitsubishi i-MiEV Forum

Help Support Mitsubishi i-MiEV Forum:

This site may earn a commission from merchant affiliate links, including eBay, Amazon, and others.

Mathias V

New member
Joined
Jan 22, 2025
Messages
3
Hi everyone!

I'm currently reverse engineering the CAN network of a Mitsubishi iMiEV (aka Citroën C-Zero and Peugeot iOn) and trying to make a DBC. As you know, this is quite time-consuming, so I would like to share the information I find. This is still a work in progress. When finished I will share all my findings together with a full DBC!

Note: All messages on CAN 1 (VCU, radio, combination meter, EPS...) also appear in CAN 2 (inverter, DC-DC, A/C...) and vice versa.

- When the ignition is not on, CAN 1 and CAN 2 do not send the same data. Does anyone know anything about this?
- Does anyone have useful information to avoid double work?


Hexadecimal numbers are preceded by ‘0x’, otherwise they are decimal. All formulas are in decimal. Everything is read MSB first.
A 'B' represents a byte, a 'b' represents a bit.
Note: constants can also be a variable that did not change during logging → example: no charging in our log → constant is not necessarily a heartbeat!
IDcommentsformula
0x101always constant → B0 = 0x04

heartbeat? → no, would be key status
0x119B5 increases by value 1 every so often
B6 increases by 1 value with each frame.
B7 no idea

I suspect a kind of timer, can also be a complicated heartbeat.
0x149clear correlation with driving
B2: constant 0
B3: constant 0xFF
B6: increases from 0 to 0xF → index? → explains the erratic behavior of B0, B4, B7

B1: yaw sensor
B5: yaw sensor
0x156B0, B1, B2, B3: constant 0
B4: could be an index (increases per value of 2)
B5: no idea, not a measurement
B6: could be an index from 0x10 to 0x1F → explains jagged graph of B7
0x200B0: constant 0
B1: constant 0x03
B2-3: RPM wheel front left
B4-5: RPM wheel front right
B6: constant 0xFF
B7: constant 0xFF
RPM = (value * 0.5) - 24576
(RPM = (value * 0x0.8) - 0x6000)
0x208B0: constant 0
B1: constant 0x20
B2: constant 0x60
B3: brake pedal → bias = 0, scale = 0.39216 (= 100/255) → full press = 100% (not
sure, further testing needed)
B4-5: wheel speed rear right
B6-7: speed wheel rear left
pedal % = TO BE MEASURED
0x210B0: constant 0
B1: constant 1
B2: accelerator pedal
B3: is 0 or 0x80 → strong correlation with B2
B4: twice 00, then twice 0x80
B5: constant 0
B6: constant 0
pedal % = value * 0.4
(full press = 0xFA = 250 → 100%)
0x212B4b4-7-B5 → very strong correlation with current/voltage traction battery
0x215B0-1: just under speed and always positive
I suspect actual speed, 0x412 (is then for combimeter (speed slightly higher and
always integers)

B2–3: bias = –20, scale = 0x0.0A (rough estimate!) → distance traveled since start?
B4–5: bias = –20, scale = 0x0.0A (rough estimate!) → distance traveled since start? →
deviates more and more under B2–3 → left/right?

B6: constant 0
B7: constant 0
km/h = value * 0.0078125
(0.0078125 = 0x0.02)

km = (value * 0.0390625) - 20 → rough estimate
(0.0390625 = 0x0.0A)
0x231B0: constant 0
B1: constant 0
B2: constant 0
B3: constant 0
B4b1: brake pedal switch (you can also view per byte)
B5: constant 0
B6: constant 0
B7: constant 0
1 = on
0 = off
0x236B0–1: steering wheel position
B2–3: EPS → is this the torque across the steering wheel (in that case →
find data of 2nd sensor) or is it the voltage across the steering servo?
B4b0–3 adds from 0x0 to 0xF
B5: constant 0
B6: constant 0
B7: no idea
steering wheel % = (value * 0.078125) - 320
steering wheel angle = TO BE MEASURED
0x285B1–B2b2–7: acceleration
no idea for the rest
0x286B0: constant 0
B1: constant 0
B2: constant 0
B3: gear selection? → only difference in signal at the start and end of the ride
B4: constant 0
B5: constant 0
B6: constant 0
B7: constant 0
0x288B0–B1b2–7: acceleration (corresponds exactly to the same bits in 0x285)
B2–3: motor RPM → with the same scale and bias it is close to the motor RPM of 0x298
B4: very similar to current through traction battery BUT less accurate
B5: constant 0
B6: constant 0
B7: only change at the end of the ride?
0x298B6–7: motor RPM according to online sources
B0, B1, B2, B3: clear measurement results, of what?
B5: constant 0
RPM = (value * 1) - 10000 → according to online sources
0x29Athe following sequence is repeated:
AD_4nXe6nHzN0N1x5EHCOuzwrb0uIxK2cNP0pcEUG5EUktz41ZaG0eI7nZagfrja6mcWosD2INNF0DuEApvACVub2CfxOVK2dnjH3EOYvZGkH5tALJEhIjH-I3XPjsvqg181ociC1nSK

index for information in another frame?
or heartbeat?
0x2F2B0: clear measurement result, of what?
B1: constant 0
B2: constant 0x03
0x300everything constant besides:
B4: is 0x07 or 0x87 i.e. B4b4-7 is also constant
0x308completely constant
probably heartbeat
0x325completely constant
probably heartbeat
0x346B0–1: very similar to current through traction battery → maybe current through
motor alone? Or vice versa?
Note: Short peaks in the middle of stopping to enter the road:
AD_4nXckj2kv9G2pva1dGlsoA4DCnzYvMrj1yu5Wf4qDRDSvmcWCoX_cxVTGMPRKGbMqnaatpv7CI9x7A4eyN2cilC906OmQVKOjIHtoOMY0t5nt_fLaxFofk3aV34kFFh46PufXE1mkMQ

B2: constant 0x5D
B3: constant 0x20
B4: constant 0x0
B5: constant 0
B6: constant 0
B7: bias = 0, scale = 1 → range according to online sources → possible because
in general the graph is decreasing, some strange jumps?
0x373B0b0–6: bias = 3, scale = 0.01 → max cell voltage according to online sources
B1b0–6: bias = 3, scale = 0.01 → min cell voltage according to online sources
B2–3: bias = -328, scale = 0.01 → traction battery current according to online sources
B4–5: bias = 0, scale = 0.1 → traction battery voltage according to online sources
B6–7: constant 0016
0x374B0 and B1: have something to do with battery SOC (online sources contradict).
Process B0-1? Or would B0 and B1 each refer to specific number of modules?

B2 constant 0
B3 constant 0
B6 constant 0x31
B7 constant 0
0x375completely constant
probably heartbeat
0x384B0–1: AC according to online sources
B3: current to 12V battery according to online sources
B4: heating according to online sources
rest: no idea, probably also something to do with AC
AC = value * 0.001 → according to online sources

12V current = value * 0.01 → according to online sources
0x385B1: change at the end of the ride (like 0x384)
rest: constant 0
0x3A4B0b0: 1 = AC on, 0 = AC off
B0b1: 1 = recirculation on, 0 = recirculation off
B0b2: 1 = MAX btn on, 0 = MAX btn off
B0b5-7: bias = 0, scale = 1 → heating level
B1b0-3: bias = 0, scale = 1 → vent direction 1 or 2 = face, 3 = legs + face,
4 or 5 = legs, 6 = legs + windshield, 7 or 8 or 9 = windshield
B1b5-7: bias = 0, scale = 12.5 → fan speed

everything according to online sources, not entirely sure, further testing needed
0x408whole message constant 0

Thanks for your help!
 
Thanks for your help!
PID 149:
Rotation = (PID[5] * 256 + PID[4] - 32934) / 32.934;

PID 200:
if (PID[2] < 255)
Speed1 = (PID[2] * 256 + PID[3] - 49152) / 19.0;
if (PID[4] < 255)
Speed2 = (PID[4] * 256 + PID[5] - 49152) / 19.0;

PID 208:
Brake = PID[3];
if (PID[4] < 255)
Speed3 = (PID[4] * 256 + PID[5] - 49152) / 19.0;
if (PID[6] < 255)
Speed4 = (PID[6] * 256 + PID[7] - 49152) / 19.0;

PID 210:
Pedal = 100 * PID[2] / 256.0;

PID 215:
When PID[0] < 255
Speed0 = (256 * PID[0] + PID[1]) / 128.0;

PID 231:
BrakeOn = PID[4];

PID 236:
Steering = (PID[0] * 256 + PID[1] - 4096) / 30.0;

PID 285:
Acceleration = ((PID[0] * 256 + PID[1]) - 2000) / 400.0;
calcGear(PID);

PID 286:
When PID[3] > 0 AirSensor = PID[3] - 50.0;

PID 298:
RPM = PID[6] * 256 + PID[7] - 10000;
MotorTemp0 = PID[0] - 50;
MotorTemp1 = PID[1] - 50;
MotorTemp2 = PID[2] - 50;
MotorTemp3 = PID[3] - 50;

PID 6FA:
calcVIN(PID);

PID 346:
RRshown = PID[7];
if (RangeUnits.equals(miles))
RestRange = KmPerMile * PID[7];
else RestRange = PID[7];

PID 373:
BatVmaxCell = (PID[0] + 210) / 100.0;
BatVminCell = (PID[1] + 210) / 100.0;
Amps = (PID[2] * 256 + PID[3] - 32768) / 100.0;
if (Amps < 200 && Amps > -200)
Amps = -Amps;
AmpsCal = -(Amps + 0.68);

if (PID[4] > 9)
BatterytotalVolts = (PID[4] * 256 + PID[5]) / 10.0;
Watts = AmpsCal * Volts;

PID 374:
if (PID[0] > 10) SoC1 = (PID[0] - 10.0) / 2.0;
if (PID[1] > 10) SoC2 = (PID[1] - 10.0) / 2.0;
BatTmax = (PID[4] - 50.0);
BatTmin = (PID[5] - 50.0);
if (PID[6] > 0) CapAh = PID[6] / 2.0;

PID 384:
when (PID[0] < 255)
Amps = (PID[0] * 256 + PID[1]) / 1000.0;
when (PID[0] >= 255)
if (On.in() == 1 && (_Fan.in() > 0 || _Max.in() > 0))
Amps = 1;

Watts = Amps * Volts;
12vAmps = PID[3] / 100.0;
12vWatts = 12vAmps * Volts;
h_Amps = PID[4] / 10.0;
h_Watts = h_Amps * Volts;

PID 389:
ChargeVDC = 2 * (PID[0] + 0.5);
ChargeVAC = PID[1];
ChargeADC = PID[2] / 10.0;
ChargeTemp1 = PID[3] - 50.0;
ChargeTemp2 = PID[4] - 50.0;
ChargeAAC = PID[6] / 10.0;

PID 3A4:
calcAir(PID);

PID 412:
SpdShown = PID[1];
if (PID[2] < 255)
OdoShown = 256 * (256 * PID[2] + PID[3]) + PID[4];
if (i_OdoUnits.equals(miles)) Odo = KmPerMile * OdoShown;
else Odo = OdoShown;
if (PID[0] == 254) KeyOn = 1;
else KeyOn = 0;

PID 418:
if (PID[0] < 255)
Gear = PID[0];
PID 418 contains the gear shifter position.
The first byte (B0) translated to ASCII is the gear shifter position. The rest (B1-B7) just seem like constants.
0x50: P 80 : P
0x52: R 82 : R
0x4E: N 78 : N
0x44: D 68 : D
0x83: B 131: B
0x32: C 50 : C


PID 424:
calcLights(PID);
calcRearDefrost(PID);
calcWipers(PID);

PID 696:
if (PID[2] > 0 && PID[2] < 7)
MotorA = (256 * PID[2] + PID[3] - 500) / 20.0;
if (MotorA < 0) MotorA = 0;
if (PID[6] > 37 && PID[6] < 40)
RegA = (256 * PID[6] + PID[7] - 10000) / 5.0;
else
RegA = 0;


PID 697:
QuickCharge = PID[0];
QCprocent = PID[1];
QCAmps = PID[2];

PID 6E1:
PID 6E2:
PID 6E3:
PID 6E4:
cellsData = true;// 2.1 + (256 * b4 + b5)/200
calcCells(PID); // 2.1 + (256 * b6 + b7)/200

PID 762:
if (PID[0] == 36)
if (PID[3] > 0 || PID[4] > 0)
BMUCapAh = (PID[3] * 256 + PID[4]) / 10.0;
if (PID[5] > 0 || PID[6] > 0)
BMURemAh = (PID[5] * 256 + PID[6]) / 10.0;
if (BMUCapAh > 0)
BMUSoC = 100 * BMURemAh / BMUCapAh;
 
I have a Peugeot Partner EV, master bms and traction control units are inside the car, under the driver seat, both units are marked Mitsubishi, CAN traffic (values and addresses) is the same of iMiev until their CAN line arrive to another device (I still never found where and what) and out of this line the original Mitsubishi traffic dispare.
I suppose the body computer or another device have the function to make possible the communication between two CAN BUS network wich don't use the same protocol.
So all bms data are available in Peugeot standard diagnostic but with different adresses.
It seems Peugeot instead to develop a own traction chain from zéro preferred to buy a whole block from Mitsubishi (good choice I think).
It was enough to realise a "CAN traslator" to make two different systems in communication
 
Hello Mathias, great project. I am preparing to do similar in order to try to run the imiev propulsion system without various components - steering column, wheel speed sensors, abs, a/c, G+yaw rate sensor, eps... The plan, is to try to capture 'straight ahead' messages and proportional wheel speed messages to inject into the canbus as required to keep the propulsion system happy enough to behave just like a good old fashioned engine. I don't know if this will be possible. Anyone who thinks not should probably tell me now and save a whole lot of pain :).

So to start with, does this look like a correct interpretation of the two canbus schematics from the ru database?

canbus combined.jpg

As in they are all part of the same network? At least when the ignition is on following Mathias comment on all messages visible on both systems only when ignition on? And all accessible from obd2 socket pins 6 and 14 (with ignition on)?

As such, all I'd need to start logging and potentially injecting would be something like this: arduino uno canbus shield on an Uno, connected to an obd2 cable etc?

Mathias, I'd like to say yeah, I'll help... but whilst I will be with you in spirit, I will be mostly a hinderance as this is a little (lot) outside my comfort zone. Of course if you are willing to help me get up to speed, then I may be able to reduce your workload, maybe, a little, maybe. :cool:
 
That diagram is helpful to show how the EV-ECU is the gateway device between two CAN Busses.

But it is missing two other CAN Buss, namely the Battery Pack Buss between the BMU and the Traction Pack (CMU Boards on all the cell modules). And the CAN between the DC Quick Charging and the EV-ECU.
 
Thanks Kiev, yes, luckily I hope that I won't have to disturb those two.

And Mickey, there is indeed the will, but whilst that is all it took when I was a youth, there are now other responsabilités, so the question is will I write my will before getting this done? :)

318iev seemed to be doing well, but then dropped off the face of the planet.
 
I don't see a site linked. So is the imiev into a bmw guy the same as he who did the electric tractor conversion? (Ive been planning to put my spare Nissan leaf power plant into a broken mini digger one day). If so then he's definitely still alive, just not communicating with old project stuff, shame, he seems mighty capable.
 
As in they are all part of the same network? At least when the ignition is on following Mathias comment on all messages visible on both systems only when ignition on? And all accessible from obd2 socket pins 6 and 14 (with ignition on)?
I checked it again and now I have doubts... When the key is inserted there is movement on the network, as well as when you turn on the ignition. Sometimes data continues to be sent for a while, sometimes it stops quite quickly. I have never read the buses at the same time, so I am afraid I made a mistake there. Sorry for the confusion!

As such, all I'd need to start logging and potentially injecting would be something like this: arduino uno canbus shield on an Uno, connected to an obd2 cable etc?
I am using an Arduino Due with two CAN transceivers (https://openinverter.org/wiki/CAN_bus_with_Arduino_Due). I needed the GVRET firmware updater for it to compile: https://openinverter.org/forum/viewtopic.php?f=19&p=77814#p77814
I removed the 120ohm resistors from the transceivers to not disturb the correct resistance over the bus (so don't forget to add terminators again if necessary). I don't know how necessary this step was. I can read and process messages perfectly in SavvyCan. Further calculations can always be done in Excel. I can also send messages but then at least one of the nodes on the network has to overwrite the ACK, otherwise the transceiver will keep sending that message (this is not shown in SavvyCan).
But of course you can use whatever you want.

I don't know if this will be possible.
I hope so, because that is indeed exactly what we are trying to do😅.
 
that is indeed exactly what we are trying to do😅
Well there's a coincidence. Where are you based?

So with your due and dual tranceivers you've made a canbus man in the middle?

I already have the uno and can us shield, but there doesn't seem to be anybody using these online. That's annoying.

But I suppose it doesn't really matter because you seem to be pretty good at this so I just have to put my feet up and wait until you've finished 😉

I'll keep trying to get to the point of being useful...

In the meantime do keep us updated because I see this as being a vital step to these little cars being a fantastic source of hardware for converting '50s and 60's classics... Without the desire to make them firebreathing tesla powered beasts.
 
So with your due and dual tranceivers you've made a canbus man in the middle?
Dual transceivers are only required if you want (need) to change data
I already have the uno and can us shield, but there doesn't seem to be anybody using these online. That's annoying.
No excuse 😉, it’s perfectly adequate to log CAN data, I use my UNO to test CMU boards..
In the meantime do keep us updated because I see this as being a vital step to these little cars being a fantastic source of hardware for converting '50s and 60's classics...
I would have thought a 1st Gen Leaf to be a better option given the vast amount of knowledge available?
 
Thanks Mickey,

OK, great, I'll persevere with the Uno solution and see if I can get anywhere. And I do have two sets, so I believe there is a solution for connecting one to the other if the need to translate arrives.. Just a bit cumbersome.

I think the Leaf is a great package, but it is a solid lump of dcdc, charger, inverter and motor (assuming avoiding the very first version that did have frailties)... They can of course be separated, but that does add some HV complications. And as a big lump they won't fit easily in many European classics. Add to that the motor speed and the original 8:1 ratio that is impossible to find in an oem diff. So you need to keep the gearbox or another custion solution. And finally they produce a lot more power 108 to 215bhp in standard tune) . Which is generally desirable (I am all for more power 😀) but converting a European classic from this era often means removing a 40hp to 65hp engine, that's a lot less. So then you're thinking about suspension mods, drive train mods, brake mods even chassis mods... And then you don't have much left of the classic car... Which is often a good thing! But does change the project, and opens it up to more complex regulations in the UK at least.

The limitations of the imiev platform make it ideal for this task... If we can eradicate the modern fancy gizmos like power steering, abs traction control etc. And if we can get a bit of freedom to potentially increase battery capacity.

The other notable advantage they have is that when some numpty drives one into a wall the important bits remain unscathed, unlike the Leaf. So buying a really ugly wreck for peanuts can still lead to a perfectly working vehicle.

Just a couple of weekend chores 😉
 
Ok, I get your reasoning; at a bare minimum, what parts do you actually need for the conversation?

It might be easier to control the drivetrain/charging circuit with a 3rd party ECU than simulating all the bits you don’t have?
 
Well, for the conversation probably a cup of tea, a pencil and a calculator would do 😉

But for the conversion I need motor, inverter, charger, dcdc, bms (bmu and cmus contacters etc) cabin water heater, motor/inverter cooling system.
But then all that has to be managed, so EV-ecu also. And I believe EV-ecu is going to need etacs etc. I don't need airbags, but I like the idea of keeping the HV shutdown in event of crash feature so will be keeping the SRS ecu and sensors. Also keeping chademo (for what it's worth today).

Sounds pretty terrifying when I lay it out like that... not to mention everything I've probably forgotten.
 
No excuse 😉, it’s perfectly adequate to log CAN data, I use my UNO to test CMU boards..
Mickey, I've been researching but I can't find a solution for getting data from Uno into savvycan. Have you done that? I think I'm going to have to go either esp32ret, or a Due like Mathias. The Due seems expensive for what it is. But the process of getting it running is very well established and documented. The esp32ret is cheap, but a bit more of a challenge setup wise. God I wish I knew everything already.
 
We are working on this but let's not overflow this thread. This is exactly our intention. Today, we got the car running with ASC/ABS and airbag ECU disconnected. Everything still working so far. We also had some funny experiment with rear axle in the air, one side with wheel and tire, other side without.

We try not to post too often to make sure people can find back useful information efficiently. Also, try not to sidetrack in this thread. We try to report reverse engineering Peugeot iOn 2013 here
 
General guideline:

CAN sniffing:
Follow OpenInverter setup with Arduino Due. All is described there

RESTORING THE CAR after a XMas tree
Make sure you have a DiagBox
- we bought it on AliExpress (full version)
- we used this link for software Diagbox 9 Install & Operators Guide | Peugeot Forums
(we did not manage to get the drivers for the included DiagBox 7 DVD on a 32 bit Windows 7)

Please don't use this thread to ask how we got it running but post a separate message or send a direct message.
Above way worked flawlessly for us (after spending a few nights trying different things)
 
Back
Top