I just thought I'd post on update on how I've progressed with fitting the auto-dimming mirror to my F22. I really struggled to find the information I needed on the internet, so what follows is information that I would have really appreciated finding a year ago...
Just to quickly recap, my F22 didn't have the electrochromic mirror factory fitted. I assumed that BMW would build all cars with the necessary wiring and I just needed to buy a mirror off ebay and connect it. I was wrong, my car only has 2 wires running to the mirror, and the the electrochromic mirror has 4 pins.
BMW said there was no way to retrofit the self-dimming mirror, and various independents I contacted were only interested in fitting official BMW retrofit kits. Phil J at Peter Van Der Veer, near Huddersfield, said they could take on the job, but quoted £300. This seemed like a steep price and Phil J didn't seem willing to explain the reason. Either, I'd badly misjudged the amount of work involved, or they didn't actually know how to fit it (and were basing their quote on having to spend time working it out), or they were simply charging a really high labour rate. Whichever it was I didn't feel inclined to trust them.
I thought of the mirror as being like a dashcam - it's a self-contained device with all the electronics required to do its job. It's not the car that determines how much the mirror should dim, all the car does is supply the power and an indication of when the dimming logic should be active i.e. monitoring when to dim or not. In the scenario where the car is reversing, or the interior lights are on, the car instructs the mirror to remain undimmed. But to me that's not especially important as I don't often reverse towards bright lights in the dark, and I never need to look at myself in the mirror at night!
The mirror I bought from ebay worked when plugged in to a F30 3 Series, so on that basis all I needed to do was replicate the functionality of the 2 extra wires that my car lacked.
With a simple multimeter and the mirror connected to the F30 I was able to ascertain that the 4 pins on the mirror are:
10 = Ground.
9 = 12v to illuminate the LED on bottom of mirror. The car switches the voltage on and off to make the LED flash.
3 = 12v to power the electrochromic function, basically goes live when ignition is on, and powers down when the car powers down.
6 = a mystery control pin with a voltage that varies a lot.
All the information about electrochromic mirrors that I found on the internet suggested that the control pin is a simple dual state e.g. voltage high when the mirror should be active, and low when the mirror should stay undimmed. It was clear that my BMW mirror was more complicated than that. Wiring my mirror to 12v and messing about with the voltage to pin 6 failed to make the mirror active.
So I used a logic analyser (Saleae Logic 4) to capture and study what is happening on pin 6. I initially thought it might be CAN bus, but eventually worked out that it's a LIN bus line using a data rate of 20,000 bits per second. A good introduction to LIN bus is here:
There's information on the internet about the BMW i-bus and people hacking it on older BMW models. The information says that i-bus uses the LIN protocol with asynchronous serial comms settings of 9600 bps, 8 data bits, even parity, 1 stop bit. Based on the data I captured it looks like BMW have moved on in recent years and now use 20kbps, 8 data bits, no parity, 1 stop bit.
Having understood that the mirror monitors a LIN bus, my general plan of attack was to power my mirror from a 12v socket in my F22, and use an arduino to send the LIN bus messages that I'd captured from the F30. The assumption that the mirror was not the LIN bus master was a pretty safe assumption. The assumption that the mirror doesn't generate any responses to the LIN bus master was a marginally less safe assumption.
The arduino is able to write data to a serial port with the 20kbps 8N1 settings, but in order to interface the 5v signal to the 12v required I used this:
I'm sure someone with decent electronic design skills could easily build themselves a circuit to do the equivalent.
My logic analyser captured around 1600 messages during the time it took the mirror to dim when connected to the F30. With a bit of scripting I was able to import all of those messages in to an arduino program and "play" them to the mirror. The only slight complication is that the LIN protocol starts each message with a Header Break that is defined in the LIN specification to be at least 13 bits long. It is not possible to generate this condition by writing data to the arduino serial port, so the port has to be disabled for a 680 microseconds and the pin forced low, then re-enabled.
After a bit of puzzling I managed to work out that the only 2 messages required to activate the mirror and make it active are:
Message 1:
PID(hex) = 07
Data(hex) = 00 EF 79 6A 00 F0 0A 00
Checksum(hex) = E9
Message 2:
PID(hex) = 07
Data(hex) = 00 EF 7A 6A 00 F0 0A 00
Checksum(hex) = E8
Each message is preceeded by the Header Break, and a Header Sync (which is 55 hex)
I discovered that the second message needs to be sent every 50ms or else the mirror wil deactivate.
The other gotcha is that the LIN protocol message IDs are actually only 6 bits, and the upper 2 bits of the byte are parity bits. So to send PID 07h you actually write 47h to the serial port.
After all the above, the conclusion is that I finally have an auto-dimming rear-view mirror in my F22! Both the mirror and arduino (a nano) are powered from the 12v socket under the glove compartment, although to be safe I use a 12v-5v convertor to power the arduino rather than rely on it's onboard regulator. When the car activates the 12v socket the mirror is activated then just sits and does what it's meant to do. The mirror is nothing more than an independent device, so there shouldn't ever be any issues with the BMW warranty - I was concerned that a £300 job at Peter Van Der Veer would involve something that could invalidate the warranty. When I come to sell the car I'll simply put the original mirror back in and sell my auto-dimming mirror.
I'm happy with the setup, but perhaps I'll look at taking power from the fuse box rather than the 12v socket in the passenger footwell, then the whole installation can be completely hidden. As mentioned earlier, the only slight problem with having a mirror that isn't actually interfaced to the car is that it won't undim in scenarios such as the car being put in to reverse, or the interior light being switched on. But I can live with that. I suppose if it really bothered me I could fit a small switch somewhere to stop the arduino sending the message every 50ms.
My car only has 2 wires running to the mirror (ground and 12v to illuminate the LED). Given that I now know that the missing wires are ignition-activated-12v and LIN-bus, I do wonder how hard it would be to install those wires. It's possible that there's already a ignition-activated-12v wire running to the interior light console, and I wouldn't be surprised if there's also a LIN-bus wire. Presumably the mirror can be added as an extra device on that LIN-bus. Unfortunately I don't have access to wiring diagrams, and wouldn't fancy hacking my wiring whilst the car is still under warranty, so I'll just leave it. The setup I have will suffice until I'm in a position to replace my current car, at which point I'll definitely make sure the replacement has the auto-dimming mirror factory fitted!
For reference, here's my arduino code. Some of the delays were determined experimentally and might not meet the exact LIN specification, but they're good enough to fool my mirror and that's enough for me!
Code:
#define LIN_BUS_SPEED (20000)
#define OUTPUT_PIN (1)
#define INTER_BYTE_SPACE (100) // Delay between bytes, in microseconds
// Messages are in format: PID, 8 data bytes, Checksum
// Note: Actual PID is 0x07, but bits 6 and 7 are parity bits, so need to send 0x47
const byte linMsgs[2][10] = {{0x47,0x00,0xEF,0x79,0x6A,0x00,0xF0,0x0A,0x00,0xE9},
{0x47,0x00,0xEF,0x7A,0x6A,0x00,0xF0,0x0A,0x00,0xE8}};
void syncBreak()
{
Serial.end(); //End Serial
pinMode(OUTPUT_PIN, OUTPUT);
digitalWrite(OUTPUT_PIN, LOW);
delayMicroseconds(680); // LIN spec says Sync Break should be >=13 bit length. At 20,000 bps each bit is 650 microseconds. BMW F30 measured at 680us
Serial.begin(LIN_BUS_SPEED, SERIAL_8N1);
delayMicroseconds(INTER_BYTE_SPACE); // Allow Serial to pull Tx High
}
void sendMsg(byte msgIndex)
{
byte i;
// Message starts with the Sync Break
syncBreak();
// Sync Break is followed by the Sync Field value - always 0x55 (binary 01010101)
Serial.write(0x55);
Serial.flush();
// Delay before sending PID
delayMicroseconds(INTER_BYTE_SPACE);
// Protected ID is composed of a 6 bit address and 2 parity bits
Serial.write(linMsgs[msgIndex][0]); // PID
Serial.flush();
delayMicroseconds(INTER_BYTE_SPACE);
// Send the 8 data bytes
for(i=1; i<=8; i++)
{
Serial.write(linMsgs[msgIndex][i]);
Serial.flush();
delayMicroseconds(INTER_BYTE_SPACE);
}
// Send the checksum
Serial.write(linMsgs[msgIndex][9]); //CHK
Serial.flush();
delayMicroseconds(INTER_BYTE_SPACE);
}
void setup()
{
Serial.begin(LIN_BUS_SPEED,SERIAL_8N1);
delay(2000);
// Send both messages from linMsgs array
sendMsg(0);
delayMicroseconds(3000);
sendMsg(1);
delayMicroseconds(3000);
}
void loop()
{
// Send message 1 every 50ms
delay(50);
sendMsg(1);
}