*** Project: Arduino & BMW K/I-Bus Interface ***
** Intro located here ***
The examples below are from version 0.0.1-alpha. This is an older version of the code, and requires and outdated Arduino IDE, as well as some modifications to the IDE core files. I suggest looking at the latest versions (which do not require core modifications and can use the latest Arduino IDE), available: here.
All of the Arduino I/K-Bus code is available on my BitBucket page, as well as Google Drive (embedded below).
Like I mentioned earlier, the coding was mostly done by ian332isport, and only slightly modified for projects’ my purposes.
Most of the code is fairly well commented, but I will provide a general description here anyway.
There are two slight modifications that need to be made to the Arduino core files in order for this branch of code to work properly.
The files are “HardwareSerial.cpp” and “HardwareSerial.h”, located in the following directory:
“C:\Program Files (x86)\Arduino\hardware\arduino\cores\arduino”
This is due to the modification of function “Serial.peekn(n)”. Both of the modified files are included on my BitBucket page.
The Arduino IDE version that must be used is version 1.0.5 – this is due to modifying the two core files mentioned above.
I have not checked if these files have changed in any versions after 1.0.5.
⚠ Module cannot be rendered as the requested content is not (longer) accessible. Contact the administrator to get access.
Code description:
• E46_KBus.ino
This is the main program file (The one that must be opened with the Arduino IDE). The first section below is simply setting up variables we will use throughout the code.
E46_KBus.ino
// v0.0.1-alpha branch
#include <SoftwareSerial.h>
// Setting up our variables
volatile boolean clearToSend = false;
boolean goodPacket; //boolean value set true if good IBUS message detected
int ibusEvent = 0;
int EventID = 0; // Event number (for case select)
byte ibusByte[40]; // Byte array to store incoming messages.. Max message length per BMW design should be 32, plus the source and length bytes, makes a total of 34. Sticking to 40 for safety - don't want to overrun the array.
byte inByte[40];
byte source;
byte length;
byte destination;
byte databytes[36]; // Byte array to store message data bytes. ibusByte array of 40 - 4 for (Source, length, destination, checksumByte) = 36
byte checksumByte;
byte outgoingMsg[32];
int outgoingSize;
const int EN = 4; // HIGH enables Melexix TH3122 chip
const int senSta = 3; // SEN/STA output from Melexix TH3122 - Needs to be on pin 3 for interrupt 1 to work (on UNO/NANO - probably different on Mega)
const byte packetGap = 10; // Length of gap between messages we send
unsigned long currentTime;
unsigned long packetTimer;
unsigned long ibusSleepTimer;
// Debug - Used for sending debug messages to terminal window.
SoftwareSerial mySerial(7, 8);
// 7 is Rx, 8 is Tx
// All these messages are either compared with incoming messages, or can be sent out onto the bus. Add or
// remove as required, but you need to make the necessary changes further down as well.
byte SEND_MESSAGE[32] = {
0xC8 , 0x00 , 0x80 , 0x23 , 0x42 , 0x32 };
byte KEY_IN [7] PROGMEM = {
0x44 , 0x05 , 0xBF , 0x74 , 0x04 , 0x00 , 0x8E }; // Ignition key in
byte KEY_OUT [7] PROGMEM = {
0x44 , 0x05 , 0xBF , 0x74 , 0x00 , 0xFF , 0x75 }; // Ignition key out
byte CD_STOP [7] PROGMEM = {
0x68 , 0x05 , 0x18 , 0x38 , 0x01 , 0x00 , 0x4C }; // CD Stop command
byte CD_PLAY [7] PROGMEM = {
0x68 , 0x05 , 0x18 , 0x38 , 0x03 , 0x00 , 0x4E }; // CD Play command
byte CD_PAUSE [7] PROGMEM = {
0x68 , 0x05 , 0x18 , 0x38 , 0x02 , 0x00 , 0x4F }; // CD Pause command
byte MFL_VOL_UP [6] PROGMEM = {
0x50 , 0x04 , 0x68 , 0x32, 0x11 , 0x1F }; // Steering wheel Volume Up
byte MFL_VOL_DOWN [6] PROGMEM = {
0x50 , 0x04 , 0x68 , 0x32, 0x10 , 0x1E }; // Steering wheel Volume Down
byte MFL_RT [5] PROGMEM = {
0x50 , 0x03 , 0xC8 , 0x01, 0x9A }; // Steering wheel R/T
byte DSP_VOL_UP_1 [6] PROGMEM = {
0x68 , 0x04 , 0x6A , 0x32, 0x11 , 0x25 }; // Rotary Volume Up 1 step
byte DSP_VOL_DOWN_1 [6] PROGMEM = {
0x68 , 0x04 , 0x6A , 0x32, 0x10 , 0x24 }; // Rotary Volume Down 1 step
byte DSP_SRCE_CD [6] PROGMEM = {
0x68 , 0x04 , 0x6A , 0x36, 0xA0 , 0x90 }; // DSP Source = CD
The section below is the setup and the main loop functions. The main loop runs millions of times per second – it first checks if the I/K-Bus has been idle for over 60 seconds in order to put the TH3122 IC into sleep mode, and shutdown the Arduino. The TH3122 will wake up automatically when it receives K-Bus voltage once again. The main loop then sends any messages that are awaiting in the circular buffer. And finally, the loop reads the serial buffer and passes the bytes to the readIbus() function.
void setup()
{
Serial.begin(9600, SERIAL_8E1); // Set hardware serial for 9600 baud, Even parity and one stop bit
mySerial.begin(9600); // Set debug serial speed
mySerial.println(F("--IBUS reader--"));
pinMode (EN, OUTPUT); // initialize pin.
pinMode(senSta, INPUT); // Configure pin as input
digitalWrite (EN, HIGH); // write pin high to enable TH3122.
senStaTimer(); // Start contention timer
packetTimer = currentTime;
ibusSleepTimer = millis();
}
void loop()
{
// Sleep Timer - Check if I-Bus has been inactive for 60 seconds
if (millis() - ibusSleepTimer >= 60000) {
digitalWrite (EN, LOW); // Shutdown TH3122
}
// Check if we have packets waiting to be sent onto the I-bus in our circular Buffer.. and send them.
// Inter packet gap timer on send
if(clearToSend && cbAvailable() > 0 ) { // If clear to send, and messages waiting in circular buffer, send them.
//if(cbAvailable() > 0 ) { // BENCH TESTING - NO clearToSend
if (millis() - packetTimer >= packetGap) {
sendIbusPacket();
packetTimer = millis();
}
}
if (Serial.available() >= 2) { // if 2 or more bytes in serial buffer, go and check for bus message
readIbus();
}
}