Sunday, 16 February 2025

2025 Update

 


After a few years and roughly 25 thousand kms of almost daily driving this car I think its time to go over the changes and challenges of this conversion since the last post, and why I've stopped driving it recently.

 


1. Motor coupler:

Initially I had the coupler floating in the bell housing, connected only to the motor spline and gearbox input shaft.

This coupler worked well for anything under 3k rpm, then it started to rattle and shake the whole car!
It almost felt like a rattly 4 cylinder engine was under the bonnet.

This noise was caused by two issues.

The first being the motor and gearbox were nearly 2mm out of alignment... ouch, not good.

The second was the gearbox input shaft, which needed to be supported, rather than floating, as the coupler I used allows for some misalignment.

To fix these issues I had to remake the motor adapter plate, since you cant just move threaded holes 2mm, and added in support for the input shaft with a bearing.








 

 


3. Heater

 I got the MiEVs heater system mounted under the back seats where the fuel tank used to be, with the HV cables running into the boot through a grommet in the spare wheel well.
After running the coolant hoses up the gearbox tunnel to the front I connected the hoses into the beamers factory heater core. 

On start up the heater draws about 20A from the battery, for about a minute or so, until the coolant is up to temperature, then it maintains that temperature automatically (about 65°C)

Even though that temperature is cooler than an engine would run, it's more than enough to heat the cabin quickly.

I haven't yet sorted the CAN information to run the heater system standalone, its currently running attached to the MiEVs heater controls hidden in the glovebox.
Hooked up with the button above the gear stick to supply the controls with 12v, which turns the heater on.
  




4. Airbag light reset

Now this was an annoying problem. While installing some soundproofing on the floor of the car I unknowingly triggered the airbag light by turning the ignition on while the passenger seat was removed, which I now know has a occupancy sensor in it, and is tied into the SRS system.

Normally this wouldn't be a big deal, just get a scan tool, plug it into the diagnostic port under the bonnet, and hit clear codes...

But that port has never existed under my ownership, the previous owner must have cut it out thinking it was useless, which it is, with no engine. Except when you need to clear the airbag light. Sheeeit.

I read countless forums and guides, none of which helped me at all because they all either mention the port I no longer had or an OBD connector which was on cars sold in the US at the time.
Some mentioned a pin you short to ground on the airbag module itself. Nope, not the answer.


After trawling through BMW wiring diagrams I figured out the two communication, pins 15 (RxD), and 20 (TxD) that ran to the diagnostic port, also run to the dashboard.

On connector X16 (the white one connected to the dash) I connected the scanner to pins 11 (RxD), 12 (TxD), and 22 (+12v).

Connector X17 (the blue one connected to the dash) has ground on pin 21.
I also grounded pin 15 on this connector for a few seconds to reset the service interval.



 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

5. 12v battery:

One morning, this horrible smell came from inside the car as I opened the door.
At first I thought something had died under my seat! It turned out to be the 12v battery which the DC-DC had cooked overnight while the car was charging.

The battery had spewed its sulfur smelling battery acid all over the boot and spare wheel well...
Not good... I can still occasionally get a whiff of it when I get in the car over 6 months later!

I think this was because the car had been sitting idle for months before this, and the 12v battery had gone flat and been recharged multiple times.

I've since got a new battery for the car and a trickle charger for when projects sit around to avoid flat batteries.



 

 

 

 

 

 

 

 

 6. Aero "Mods" and Range:

To help improve the drag of the car I've put a few big piece of clear acrylic behind the kidney grills, and  behind the bumper, making sure to leave a little gap under the number plate for air flow.
Plus, being clear acrylic, you can still see the radiator behind the grill, so it doesn't look too out of place.

Range wise I can get upto 120kms with flat motorway driving, which weirdly decreases the more city driving I do. Probably because its a heavy, but relatively low and aerodynamic car.
Generally I get about 90-100kms of "usable" range before I start to worry about finding a charger.

This picture is just after some flooding the car was in, it shows the acrylic, and how high the floating bark got!



7.Under the Bonnet:

Added a fiberglass cover to go over all the spicy relays and bus bars, I couldn't put a flat sheet over everything as it would interfere with the bonnet closing.

Also managed to score some Bilstein shocks, and a strut bar to add some rigidity in the front end.




8. Interior/Dash setup:

All the gauges except the speedo are controlled by Arduino's snooping the MiEVs can bus, Fuel, RPM, Temp and MPG.
The two buttons above the gear stick turn the heater on/off, and motor regen on/off. 



9. More power and range?

Now this, is ultimately why I have stopped driving the car recently.

Power was just not enough for me, even with gears, its borderline dangerous when you cant accelerate quickly.

I also got a new job where i need to travel 100+ kms a day, Which was just a little too far for comfortable daily use.

Sometime in the future I'm planning to swap in a different, more powerful motor/inverter combo, and new Chinese cells for better range.
For that I need more money and time, Not something I'm expecting to get anytime soon.

 

 

10. New Daily

So while this project is on hold, I've bought a new daily. A Leaf!

Sound deadened, 160kW inverter, 40kWh battery, Lightweight wheels and lowered on coilovers.
Definitely a fun drive with about 170-200kms range.






Saturday, 2 October 2021

Arduino CAN spoofing i-MiEV ABS signals

 Usually in the i-MiEV the ABS module will detect the wheel speed from wheel speed sensors to calculate and change the accelerator pedal map depending on speed. ie the higher the speed the more regen is applied when taking your foot off the accelerator.

However, now that the i-MiEVs guts are in the BMW the ABS module doesn't have the sensors to detect speed hooked up. 

There are two ways to solve this, either splice into the BMWs wheels speed sensors and hope that the signal from them is compatible with the i-MiEV system, or...

Use an Arduino with a CAN-BUS Module to simulate the CAN signals usually sent from the ABS module to spoof the ECU into thinking it has a properly functioning ABS system.

 

To start this process me and a friend started by taking a log of an unmodified i-MiEVs CAN stream using Savvy CAN, after this going through the CAN PiDs one by one to find possible data streams and record them. 

To do this under the RE Tools menu we used Frame Data Analysis to find these.

 

The easiest way to find the data is by looking at the Bytes Graph (Bottom Right) which shows line zig-zagging up and down. 

If the data here isnt encrypted or has a large scale it will usually be in two bytes, usually 0-1, 2-3, 4-5, 6-7. 

As shown below the graph these bytes are colour coded on the graph, 0 = Blue, 1 = Green etc. 

 

When zoomed into the graph you can see the Red byte 3 drop from a value of  255 to 0  at the same time the Black byte 2 increases by 1, the same goes for bytes 4 and 5 slightly after that. 

This indicates that these two pairs of bytes must have a large scale which is multiplied by the High byte (2 with 3 and 4 with 5 in this case)

The next step is to make a graph using the values found to collect and gather all the usable data streams, this is listed under the RE Tools menu (Graph Data)

 

With the blank graph showing right click it and select add a new graph. 

I have already filled this graph setting out with the graph found just before, 0x200 as the ID, and selecting bytes 2 and 3. each byte is made up of 8 bits, so when selecting the first bit of byte 2 we type a data length of 16 which then selects the whole of byte 2 and 3 seen in green in the screenshot.

 

When this is added play around with the Bias which moves that specific graph up and down the value axis, as well as Scale which allows you to.. well scale the graph ie enter 10 and the graph will multiply all values in the graph by 10. (name it once you've figured out what its for)

Going through all the IDs one by one eventually gave us this.

 The humps right at the top are both RPM, with the similarly shaped humps at the bottom being wheel speed. The darker looking hump second from the bottom is actually all 4 wheel speeds with the same Bias and Scale as they all read similar values.

Using these graphs with the Scale and Bias function I found the RPM to Wheel speed equation which was "(RPM / 3) + 49152"

 Now the wheel speed sensors can be "locked" to the speed of the motors RPM allowing for the i-MiEV to think it has a functioning ABS system allowing for full regen off the accelerator and full power when flooring. 

I have not tested this with the brake pedal sensor so I am only getting accelerator lift off regen, but when combined with the i-MiEVs "B" driving mode there is heaps of regen avaliable.

The Arduino code I used to do this: (This has been cut from the full code I use to show just the ABS spoofing part)

#include <defaults.h>
#include <global.h>
#include <mcp_can.h>

    int cRPM;  
    int wRPM;
    int dRPM;

long unsigned int rxId;
unsigned char len = 0;
unsigned char rxBuf[8];
char msgString[128];                        // Array to store serial string

#define CAN0_INT 2                              // Set INT to pin 2
MCP_CAN CAN0(10);                               // Set CS to pin 10


void setup() {

CAN0.begin(MCP_ANY, CAN_500KBPS, MCP_16MHZ); // Initialize MCP2515 running at 16MHz with a baudrate of 500kb/s and the masks and filters disabled.

 
  CAN0.setMode(MCP_NORMAL);                     // Set operation mode to normal so the MCP2515 sends acks to received data.

  pinMode(CAN0_INT, INPUT);                            // Configuring pin for /INT input

}

void loop() {

CAN0.readMsgBuf(&rxId, &len, rxBuf);             // Read data: len = data length, buf = data byte(s)
 
         //Serial.println(rxId);
         if(rxId == 0x288){     //Reads motor rpm from CAN
       
           cRPM = rxBuf[2] * 256; //Adjusts RPM to actual decimal numbers
           cRPM += rxBuf[3];
           cRPM -= 10000;

  
           wRPM = cRPM / 3; //Adjusts RPM to wheel speed (Spoofs ABS signals)
           wRPM += 49152;

           //seperate speed signal (For dash?) with different ratio
           dRPM = cRPM * 2;

           byte high = highByte(wRPM); //converts decimal number into two HEX bytes
           byte low = lowByte(wRPM);
           byte high0 = highByte(dRPM); //converts decimal number into two HEX bytes
           byte low0 = lowByte(dRPM);
          
           byte data200[8] = {0x00, 0x03, high, low, high, low, 0xFF, 0xFF};  //loads data into arrays
           byte data208[8] = {0x00, 0x20, 0x60, 0x01, high, low, high, low};
           byte data215[8] = {high0, low0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
          
           CAN0.sendMsgBuf(0x200, 0, 8, data200); //sends out CAN messages
           CAN0.sendMsgBuf(0x208, 0, 8, data208);
           CAN0.sendMsgBuf(0x215, 0, 8, data215);

}