├── LICENSE ├── README.md ├── examples ├── BasicReceive │ └── BasicReceive.ino ├── SteeringWheelControls │ └── SteeringWheelControls.ino └── ToggleDomeLight │ └── ToggleDomeLight.ino ├── extras ├── basic-ibus-receive-interface.png └── basic-ibus-transmit-receive-interface.png ├── keywords.txt ├── library.json ├── library.properties └── src ├── IbusMessage.cpp ├── IbusMessage.h ├── IbusNames.h ├── IbusTrx.cpp └── IbusTrx.h /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 D. van Gent 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # arduino-ibustrx 2 | 3 | Arduino library for sending and receiving messages over the BMW infotainment bus (IBUS). 4 | 5 | 6 | ### IBUS Introduction 7 | 8 | IBUS, or infotainment bus, is a BMW-specific controller network that enables infotainment and convenience systems to exchange data with each other. 9 | 10 | Every time you interact with the vehicle, whether it be pressing a button on the steering wheel, opening a window or turning on the AC, data gets exchanged over the IBUS. In addition to that the IBUS is also used to exchange diagnostic messages and read fault codes. 11 | 12 | The IBUS can be found on most BMW models produced between the late 90s and early 00s (most notably on the E46 3-series and E39 5-series). 13 | 14 | ##### Cool, now what can I do with this? 15 | *Basic projects:* interpreting steering wheel control commands, remote keyfob instructions and broadcast messages like road speed and engine RPM. 16 | 17 | *Intermediate projects:* triggering stuff based on state changes (door/window has been opened/closed, wipers have been turned to level two, a key has been inserted into the ignition, left turn signal is on, etc.). 18 | 19 | *Advanced projects:* manipulating inputs and outputs, for example: emulating a press of the "lock/unlock" button in order to automatically unlock the doors after the key has been removed (*a feature that for some reason isn't available on the E46!*). 20 | 21 | --- 22 | 23 | ### Installation 24 | 25 | ##### Arduino library manager 26 | This library can be found in the Arduino library manager. Simply search for *IbusTrx* and click install. 27 | 28 | ##### Manual installation 29 | 1. Download this repository as a ZIP file 30 | 1. Open the Arduino environment 31 | 1. Go to Sketch -> Include Library -> Add .ZIP library... 32 | 1. Install the ZIP file that you just downloaded 33 | 1. Open the *BasicReceive* example to get a good understanding of how this library works 34 | 35 | 36 | ### Schematics 37 | 38 | **Warning**: the IBUS operates at 12-15 volts, level shifting is required in order to make it work with the Arduino. Connecting the IBUS directly to your Arduino will let the magic smoke out. 39 | 40 | **Warning**: the resistor values shown in these schematics are only suitable for Arduino boards that operate at a logic level of 5 volts. 41 | 42 | **Note**: both schematics do not include any form of isolation or input protection. If you're worried about spikes on the IBUS causing damage to your Arduino you should definitely use an opto-isolater (or other form of isolation device). 43 | 44 | ##### Basic receive only interface 45 | 46 | ![schematic](https://raw.githubusercontent.com/just-oblivious/arduino-ibustrx/master/extras/basic-ibus-receive-interface.png) 47 | 48 | ##### Basic transmit and receive interface 49 | 50 | ![schematic](https://raw.githubusercontent.com/just-oblivious/arduino-ibustrx/master/extras/basic-ibus-transmit-receive-interface.png) 51 | 52 | 53 | ### IbusTrx example sketch 54 | 55 | This quick sketch shows you how to receive your first IBUS message, more elaborate examples are included with the library. 56 | 57 | ```cpp 58 | #include 59 | IbusTrx ibusTrx; // create a new IbusTrx instance 60 | 61 | void setup(){ 62 | ibusTrx.begin(Serial); // begin listening on the first hardware serial port 63 | } 64 | void loop(){ 65 | if (ibusTrx.available()) { // if there's a message waiting, do something with it 66 | IbusMessage message = ibusTrx.readMessage(); // grab the message 67 | unsigned int sourceID = message.source(); // read the source id 68 | unsigned int destinationID = message.destination(); // read the destination id 69 | unsigned int length = message.length(); // read the length of the payload 70 | unsigned int payloadFirstByte = message.b(0); // read the first byte of the payload 71 | // do something with this message 72 | if (sourceID == M_MFL) { 73 | // this message was sent by the steering wheel controls 74 | } 75 | if (destinationID == M_ALL) { 76 | // this is a broadcast message 77 | } 78 | // etc. 79 | } 80 | } 81 | ``` 82 | 83 | 84 | ### IBUS module addresses 85 | 86 | To make life easier, I defined the following IBUS addresses in this library: 87 | 88 | |Name|Description| 89 | |-|-| 90 | |M_GM5|body control module| 91 | |M_DIA|diagnostic computer| 92 | |M_EWS|immobilizer| 93 | |M_MFL|steering wheel controls| 94 | |M_IHKA|climate control panel| 95 | |M_RAD|radio module| 96 | |M_IKE|instrument cluster| 97 | |M_ALL|broadcast message|| 98 | |M_TEL|telephone module| 99 | |M_LCM|light control module| 100 | 101 | 102 | ### GM5 control addresses 103 | 104 | The following diagnostic IO-addresses have been included for tinkering with the GM5 body control module: 105 | 106 | |Name|Description| 107 | |-|-| 108 | |GM5_SET_IO | "set IO" diagnostic command| 109 | |GM5_BTN_DOME_LIGHT | dome light button| 110 | |GM5_BTN_CENTER_LOCK | center console lock/unlock button| 111 | |GM5_BTN_TRUNK_OPEN | interior trunk unlock button| 112 | |GM5_BTN_WINDOW_DRIVER_DOWN | driver window down button| 113 | |GM5_BTN_WINDOW_DRIVER_UP | driver window up button| 114 | |GM5_BTN_WINDOW_PASSENGER_DOWN | passenger window down button| 115 | |GM5_BTN_WINDOW_PASSENGER_UP | passenger window up button| 116 | |GM5_LED_ALARM_WARNING | red LED under interior mirror ("clown nose")| 117 | |GM5_INPUT_STATE_DIGITAL | request digital IO states| 118 | |GM5_INPUT_STATE_ANALOG | request analog IO states| 119 | 120 | **Warning:** several safeguards are disabled when directly setting outputs, forcing a component to turn on may cause damage to mechanisms and/or electrical components. It's much safer to control inputs instead. 121 | 122 | For example: telling the GM5 module that a window switch has been pressed is safer than directly sending power to the window motor. 123 | -------------------------------------------------------------------------------- /examples/BasicReceive/BasicReceive.ino: -------------------------------------------------------------------------------- 1 | /* 2 | example IBUS message: 3 | 50 04 68 32 11 1F (volume up button pressed on the steering wheel) 4 | | | | | | | 5 | | | | | | checksum (xorsum of all previous bytes) 6 | | | | | one or more data fields 7 | | | | message type/command type 8 | | | destination address 9 | | length of message (including destination address and checksum) 10 | source address 11 | */ 12 | 13 | // include the IbusTrx library 14 | #include 15 | 16 | // create a new IbusTrx instance 17 | IbusTrx ibusTrx; 18 | 19 | void setup(){ 20 | // begin listening for IBUS messages 21 | // timing is critical, software-based serial ports are absolutely not recommended for receiving IBUS data 22 | ibusTrx.begin(Serial); 23 | pinMode(13, OUTPUT); 24 | } 25 | void loop(){ 26 | // available() has to be called repeatedly, with no delay() in between 27 | // this function returns true if a new message is available for reading 28 | bool messageWaiting = ibusTrx.available(); 29 | 30 | // if there's a message waiting, check it out 31 | if (messageWaiting) { 32 | // read the incoming message (this copies the message and clears the receive buffer) 33 | IbusMessage message = ibusTrx.readMessage(); 34 | 35 | // every module on the IBUS has its own 8-bit address. 36 | // the following addresses are defined in the IbusTrx library: 37 | // M_GM5: body control module 38 | // M_DIA: diagnostic computer 39 | // M_EWS: immobilizer 40 | // M_MFL: steering wheel controls 41 | // M_IHKA: climate control panel 42 | // M_RAD: radio module 43 | // M_IKE: instrument cluster 44 | // M_ALL: broadcast message 45 | // M_TEL: telephone module 46 | // M_LCM: light control module 47 | 48 | // these two functions return the source and destination addresses of the IBUS message: 49 | unsigned int messageSource = message.source(); 50 | unsigned int messageDestination = message.destination(); 51 | 52 | // filtering example: 53 | // in this case we're only interested in messages sent by the steering wheel controls to the radio 54 | if (messageSource == M_MFL && messageDestination == M_RAD) { 55 | // the length of the message payload, including the checksum 56 | // this function is rarely needed, 57 | // in most cases the number of payload fields is already known based on the type of message 58 | unsigned int messageLength = message.length(); 59 | 60 | // the b(n) function returns the n'th byte of the message payload 61 | // b(0) will return the first byte, b(1) returns the second byte, etc. 62 | unsigned int messageCommand = message.b(0); // the first byte usually identifies what type of message it is 63 | 64 | // command 0x32 happens to be related to the volume controls 65 | if (messageCommand == 0x32) { 66 | // in this case, the least significant bit of the second payload byte tells us whether this is a "volume up" or a "volume down" instruction 67 | // several fields are often packed into a single byte, playing around with bitwise operators is recommended when working with IBUS data 68 | if (message.b(1) & 0x01){ 69 | // volume up pressed, turn LED on 70 | digitalWrite(13, HIGH); 71 | } 72 | else{ 73 | // volume down pressed, turn LED off 74 | digitalWrite(13, LOW); 75 | } 76 | } 77 | // etc. 78 | } 79 | // etc. 80 | } 81 | // rest of your program goes here... 82 | 83 | // remember to never use a blocking function like delay() in your program, 84 | // always use millis() or micros() if you have to implement a delay somewhere 85 | } -------------------------------------------------------------------------------- /examples/SteeringWheelControls/SteeringWheelControls.ino: -------------------------------------------------------------------------------- 1 | /* 2 | example IBUS message: 3 | 50 04 68 32 11 1F (volume up button pressed on the steering wheel) 4 | | | | | | | 5 | | | | | | checksum (xorsum of all previous bytes) 6 | | | | | one or more data fields 7 | | | | message type/command type 8 | | | destination address 9 | | length of message (including destination address and checksum) 10 | source address 11 | */ 12 | 13 | 14 | // this example shows how to interpret all messages related to the steering wheel controls 15 | 16 | #include // include the IbusTrx library 17 | 18 | IbusTrx ibusTrx; // create a new IbusTrx instance 19 | 20 | void setup(){ 21 | ibusTrx.begin(Serial); // begin listening for messages 22 | } 23 | 24 | void loop(){ 25 | if (ibusTrx.available()) { 26 | IbusMessage m = ibusTrx.readMessage(); // grab incoming messages 27 | 28 | if (m.source() == M_MFL) { 29 | switch (m.b(0)) { 30 | case 0x32: // volume controls 31 | if (m.b(1) & 0x01) { 32 | // volume up pressed 33 | } 34 | else { 35 | // volume down pressed 36 | } 37 | break; 38 | case 0x3B: // other controls 39 | if (m.b(1) & 0x10) { 40 | if (m.b(1) & 0x01) { 41 | // track up (held down for 1 second) 42 | } 43 | else if (m.b(1) & 0x08) { 44 | // track down (held down for 1 second) 45 | } 46 | else if (m.b(1) & 0x80) { 47 | // talk (held down for 1 second) 48 | } 49 | } 50 | else if (m.b(1) & 0x20) { 51 | if (m.b(1) & 0x01) { 52 | // track up (released) 53 | } 54 | else if (m.b(1) & 0x08) { 55 | // track down (released) 56 | } 57 | else if (m.b(1) & 0x80) { 58 | // talk (released) 59 | } 60 | } 61 | else{ 62 | if (m.b(1) & 0x01) { 63 | // track up (pressed) 64 | } 65 | else if (m.b(1) & 0x08) { 66 | // track down (pressed) 67 | } 68 | else if (m.b(1) & 0x80) { 69 | // talk (pressed) 70 | } 71 | } 72 | break; 73 | } 74 | } 75 | } 76 | // remember to never use a blocking function like delay() in your program, 77 | // always use millis() or micros() if you have to implement a delay somewhere 78 | } -------------------------------------------------------------------------------- /examples/ToggleDomeLight/ToggleDomeLight.ino: -------------------------------------------------------------------------------- 1 | /* 2 | example IBUS message: 3 | 50 04 68 32 11 1F (volume up button pressed on the steering wheel) 4 | | | | | | | 5 | | | | | | checksum (xorsum of all previous bytes) 6 | | | | | one or more data fields 7 | | | | message type/command type 8 | | | destination address 9 | | length of message (including destination address and checksum) 10 | source address 11 | */ 12 | 13 | // this example shows how to transmit a message over the IBUS 14 | 15 | #include // include the IbusTrx library 16 | 17 | IbusTrx ibusTrx; // create a new IbusTrx instance 18 | 19 | // define the message that we want to transmit 20 | // the message must be defined as an array of uint8_t's (unsigned 8-bit integers) 21 | uint8_t toggleDomeLight[6] = { 22 | M_DIA, // sender ID (diagnostic interface) 23 | 0x05, // length of the message payload (including destination ID and checksum) 24 | M_GM5, // destination ID (body control module) 25 | GM5_SET_IO, // the type of message (IO manipulation) 26 | GM5_BTN_DOME_LIGHT, // the first parameter (the IO line that we want to manipulate) 27 | 0x01 // second parameter 28 | // don't worry about the checksum, the library automatically calculates it for you 29 | }; 30 | 31 | void setup(){ 32 | ibusTrx.begin(Serial); // begin listening for messages 33 | } 34 | 35 | void loop(){ 36 | if (ibusTrx.available()) { 37 | IbusMessage m = ibusTrx.readMessage(); // grab incoming messages 38 | 39 | // trigger based on a message from the steering wheel controls 40 | if (m.source() == M_MFL) { 41 | 42 | // if "talk" is held down for 1 second: simulate a press of the dome light button 43 | if (m.b(1) == 0x90) { 44 | // write the message to the transmit buffer 45 | ibusTrx.write(toggleDomeLight); 46 | 47 | } 48 | } 49 | } 50 | // remember to never use a blocking function like delay() in your program, 51 | // always use millis() or micros() if you have to implement a delay somewhere 52 | } -------------------------------------------------------------------------------- /extras/basic-ibus-receive-interface.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/just-oblivious/arduino-ibustrx/55838670420d8e350d2aeb70c557101f10114756/extras/basic-ibus-receive-interface.png -------------------------------------------------------------------------------- /extras/basic-ibus-transmit-receive-interface.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/just-oblivious/arduino-ibustrx/55838670420d8e350d2aeb70c557101f10114756/extras/basic-ibus-transmit-receive-interface.png -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map For IbusTrx 3 | ####################################### 4 | 5 | ####################################### 6 | # Datatypes (KEYWORD1) 7 | ####################################### 8 | 9 | IbusTrx KEYWORD1 10 | IbusMessage KEYWORD1 11 | 12 | ####################################### 13 | # Methods and Functions (KEYWORD2) 14 | ####################################### 15 | 16 | available KEYWORD2 17 | readmessage KEYWORD2 18 | begin KEYWORD2 19 | end KEYWORD2 20 | transmitWaiting KEYWORD2 21 | source KEYWORD2 22 | destination KEYWORD2 23 | length KEYWORD2 24 | b KEYWORD2 25 | 26 | ####################################### 27 | # Constants (LITERAL1) 28 | ####################################### 29 | 30 | M_GM5 LITERAL1 31 | M_DIA LITERAL1 32 | M_EWS LITERAL1 33 | M_MFL LITERAL1 34 | M_IHKA LITERAL1 35 | M_RAD LITERAL1 36 | M_IKE LITERAL1 37 | M_ALL LITERAL1 38 | M_TEL LITERAL1 39 | M_LCM LITERAL1 40 | GM5_SET_IO LITERAL1 41 | GM5_BTN_DOME_LIGHT LITERAL1 42 | GM5_BTN_CENTER_LOCK LITERAL1 43 | GM5_BTN_TRUNK_OPEN LITERAL1 44 | GM5_BTN_WINDOW_DRIVER_DOWN LITERAL1 45 | GM5_BTN_WINDOW_DRIVER_UP LITERAL1 46 | GM5_BTN_WINDOW_PASSENGER_DOWN LITERAL1 47 | GM5_BTN_WINDOW_PASSENGER_UP LITERAL1 48 | GM5_LED_ALARM_WARNING LITERAL1 49 | GM5_INPUT_STATE_DIGITAL LITERAL1 50 | GM5_INPUT_STATE_ANALOG LITERAL1 -------------------------------------------------------------------------------- /library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "IbusTrx", 3 | "description": "Arduino library for sending and receiving messages over the BMW infotainment bus (IBUS).", 4 | "keywords": "BMW,IBUS,E46,E39,SWC,steering wheel controls,vehicle,car", 5 | "version": "2.4.0", 6 | "authors": 7 | [ 8 | { 9 | "name": "D. van Gent", 10 | "email": "arduino@12q.nl", 11 | "url": "https://0x7b.nl/ibus" 12 | } 13 | ], 14 | "repository": 15 | { 16 | "type": "git", 17 | "url": "https://github.com/just-oblivious/arduino-ibustrx.git" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=IbusTrx 2 | version=2.4.0 3 | author=D. van Gent 4 | maintainer=D. van Gent 5 | sentence=Arduino library for sending and receiving messages over the BMW infotainment bus (IBUS). 6 | paragraph=IBUS, or infotainment bus, is a BMW-specific controller network that enables all infotainment and convenience systems in the vehicle to exchange data with each other. 7 | category=Communication 8 | url=https://0x7b.nl/ibus 9 | architectures=* 10 | -------------------------------------------------------------------------------- /src/IbusMessage.cpp: -------------------------------------------------------------------------------- 1 | #include "IbusMessage.h" 2 | 3 | IbusMessage::IbusMessage(uint8_t *buffer){ 4 | uint8_t m_length = buffer[1]+1; 5 | // create a new buffer to hold the message 6 | m_buffer = new uint8_t[m_length]; 7 | // copy part of message buffer into the new buffer 8 | for (uint8_t i = 0; i < m_length; i++){ 9 | m_buffer[i] = buffer[i]; 10 | } 11 | } 12 | // clean up 13 | IbusMessage::~IbusMessage() { 14 | delete[] m_buffer; 15 | m_buffer = NULL; 16 | } 17 | // returns the source byte of the message 18 | uint8_t IbusMessage::source() { 19 | return m_buffer[0]; 20 | } 21 | // returns the payload length of the message 22 | uint8_t IbusMessage::length() { 23 | return m_buffer[1]; 24 | } 25 | // returns the destination byte of the message 26 | uint8_t IbusMessage::destination() { 27 | return m_buffer[2]; 28 | } 29 | // returns the n'th byte of the message payload 30 | uint8_t IbusMessage::b(uint8_t i) { 31 | if (i > m_buffer[1]-2){ 32 | return 0x00; // return 0x00 if an out of bound value has been requested 33 | } 34 | else { 35 | return m_buffer[3+i]; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/IbusMessage.h: -------------------------------------------------------------------------------- 1 | #ifndef IbusMessage_h 2 | #define IbusMessage_h 3 | 4 | #include "Arduino.h" 5 | 6 | class IbusMessage{ 7 | public: 8 | uint8_t source(); 9 | uint8_t length(); 10 | uint8_t destination(); 11 | uint8_t b(uint8_t n); 12 | IbusMessage(uint8_t *buffer); 13 | ~IbusMessage(); 14 | 15 | private: 16 | uint8_t *m_buffer; // temporary message buffer 17 | }; 18 | #endif 19 | -------------------------------------------------------------------------------- /src/IbusNames.h: -------------------------------------------------------------------------------- 1 | #ifndef IbusNames_h 2 | #define IbusNames_h 3 | 4 | // IBUS module ID's 5 | static const uint8_t M_GM5 = 0x00; // GM5: body control module 6 | static const uint8_t M_DIA = 0x3F; // DIA: diagnostic computer 7 | static const uint8_t M_EWS = 0x44; // EWS: immobilizer 8 | static const uint8_t M_MFL = 0x50; // MFL: steering wheel controls 9 | static const uint8_t M_IHKA = 0x5B; // IHKA: climate control panel 10 | static const uint8_t M_RAD = 0x68; // RAD: radio module 11 | static const uint8_t M_IKE = 0x80; // IKE: instrument cluster 12 | static const uint8_t M_ALL = 0xBF; // ALL: broadcast message 13 | static const uint8_t M_TEL = 0xC8; // TEL: telephone module 14 | static const uint8_t M_LCM = 0xD0; // LCM: light control module 15 | 16 | // GM5 input/output addresses 17 | static const uint8_t GM5_SET_IO = 0x0C; // "set IO" diagnostic command 18 | static const uint8_t GM5_BTN_DOME_LIGHT = 0x01; // dome light button 19 | static const uint8_t GM5_BTN_CENTER_LOCK = 0x03; // center console lock/unlock button 20 | static const uint8_t GM5_BTN_TRUNK_OPEN = 0x05; // interior trunk unlock button 21 | static const uint8_t GM5_BTN_WINDOW_DRIVER_DOWN = 0x0A; // driver window down button 22 | static const uint8_t GM5_BTN_WINDOW_DRIVER_UP = 0x0B; // driver window up button 23 | static const uint8_t GM5_BTN_WINDOW_PASSENGER_DOWN = 0x0C; // passenger window down button 24 | static const uint8_t GM5_BTN_WINDOW_PASSENGER_UP = 0x0D; // passenger window up button 25 | static const uint8_t GM5_LED_ALARM_WARNING = 0x4E; // red LED under interior mirror ("clown nose") 26 | 27 | // GM5 state groups 28 | static const uint8_t GM5_INPUT_STATE_DIGITAL = 0x00; // request digital IO states 29 | static const uint8_t GM5_INPUT_STATE_ANALOG = 0x01; // request analog IO states 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /src/IbusTrx.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | IbusTrx (v2.4.0) 3 | Arduino library for sending and receiving messages over the BMW infotainment bus (IBUS). 4 | Author: D. van Gent 5 | More info: https://0x7b.nl/ibus 6 | 7 | THIS PROGRAM IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. 8 | */ 9 | 10 | #include "IbusTrx.h" 11 | // open serial port 12 | void IbusTrx::begin(HardwareSerial &userPort) { 13 | serialPort = &userPort; 14 | serialPort->begin(9600, SERIAL_8E1); 15 | } 16 | 17 | // close serial port 18 | void IbusTrx::end() { 19 | serialPort->end(); 20 | clearBuffer(); 21 | } 22 | 23 | // transmit and receive 24 | // returns true if a valid IBUS message has been buffered 25 | bool IbusTrx::available() { 26 | // clear old message before attempting to read new data 27 | if (rx_msg_waiting) { 28 | clearBuffer(); 29 | } 30 | // prevent receive buffer from overflowing 31 | if (rx_bytes > 0xFE) { 32 | clearBuffer(); 33 | } 34 | // discard buffer if time since last byte timer has expired 35 | if (rx_bytes > 0 && millis()-t_last_rx_byte >= 8) { 36 | clearBuffer(); 37 | } 38 | // if data is available, buffer it up 39 | if (serialPort->available()) { 40 | uint8_t rx_byte = serialPort->read(); 41 | // ignore loopback bytes coming in right after transmitting 42 | if (tx_bytes > 0 && !tx_msg_waiting) { 43 | tx_bytes--; 44 | } 45 | // buffer incoming data 46 | else { 47 | rx_buffer[rx_bytes] = rx_byte; 48 | rx_bytes++; 49 | } 50 | t_last_rx_byte = millis(); 51 | } 52 | // assume bus is clear for sending after a short period of inactivity 53 | if (tx_msg_waiting && millis()-t_last_rx_byte >= 32) { 54 | // send all bytes in the transmit buffer 55 | for (uint8_t b = 0; b < tx_bytes; b++) { 56 | serialPort->write(tx_buffer[b]); 57 | } 58 | tx_msg_waiting = false; // clear tx wait flag 59 | } 60 | // check if the receive buffer contains a valid IBUS message 61 | if (checkMessage()) { 62 | rx_msg_waiting = true; // set rx wait flag 63 | return true; 64 | } 65 | return false; 66 | } 67 | 68 | // returns true if the receive buffer contains a valid IBUS message 69 | // example message: 80 05 bf 18 00 00 22 (sender, length, destination, data(*3), checksum) 70 | bool IbusTrx::checkMessage() { 71 | // check if all bytes have been received 72 | if (rx_bytes > 4 && rx_bytes == rx_buffer[1]+2) { 73 | uint8_t sender = rx_buffer[0]; 74 | uint8_t length = rx_buffer[1]; 75 | uint8_t chksum = sender ^ length; 76 | for (uint16_t i = 2; i < length+1; i++) { 77 | chksum = chksum ^ rx_buffer[i]; 78 | } 79 | if (rx_buffer[length+1] == chksum) { 80 | // checksums match, buffer contains a valid message 81 | return true; 82 | } 83 | else { 84 | // message received with invalid checksum: discard buffer 85 | clearBuffer(); 86 | return false; 87 | } 88 | } 89 | else { 90 | // message incomplete 91 | return false; 92 | } 93 | } 94 | 95 | // creates and returns an IbusMessage object containing the contents of the message 96 | IbusMessage IbusTrx::readMessage() { 97 | IbusMessage ibusMessage(rx_buffer); 98 | clearBuffer(); // clear receive buffer after reading the message 99 | return ibusMessage; 100 | } 101 | 102 | // returns the number of bytes stored in the rx buffer 103 | uint8_t IbusTrx::length() { 104 | return rx_bytes; 105 | } 106 | 107 | // reset receive buffer 108 | void IbusTrx::clearBuffer() { 109 | rx_buffer[1] = 0x00; 110 | rx_bytes = 0; 111 | rx_msg_waiting = false; 112 | } 113 | 114 | // returns tx wait flag 115 | bool IbusTrx::transmitWaiting() { 116 | return tx_msg_waiting; 117 | } 118 | 119 | // prepare message for transmission 120 | void IbusTrx::write(uint8_t message[]) { 121 | // copy message to transmit buffer 122 | for (uint8_t p = 0; p <= message[1]; p++) { 123 | tx_buffer[p] = message[p]; 124 | } 125 | // calculate checksum 126 | uint8_t chksum = tx_buffer[0] ^ tx_buffer[1]; 127 | for (uint8_t i = 2; i < tx_buffer[1]+1; i++) { 128 | chksum = chksum ^ tx_buffer[i]; 129 | } 130 | tx_buffer[tx_buffer[1]+1] = chksum; 131 | // set tx wait flag 132 | tx_msg_waiting = true; 133 | tx_bytes = tx_buffer[1] + 2; 134 | } 135 | -------------------------------------------------------------------------------- /src/IbusTrx.h: -------------------------------------------------------------------------------- 1 | /* 2 | IbusTrx (v2.4.0) 3 | Arduino library for sending and receiving messages over the BMW infotainment bus (IBUS). 4 | Author: D. van Gent 5 | More info: https://0x7b.nl/ibus 6 | 7 | THIS PROGRAM IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. 8 | */ 9 | 10 | #ifndef IbusTrx_h 11 | #define IbusTrx_h 12 | 13 | #include "Arduino.h" 14 | #include "IbusMessage.h" 15 | #include "IbusNames.h" 16 | 17 | 18 | class IbusTrx{ 19 | public: 20 | void begin(HardwareSerial &userPort); 21 | void end(); 22 | void write(uint8_t message[]); 23 | bool available(); 24 | bool transmitWaiting(); 25 | uint8_t length(); 26 | IbusMessage readMessage(); 27 | 28 | private: 29 | HardwareSerial* serialPort; 30 | void clearBuffer(); 31 | bool checkMessage(); 32 | bool tx_msg_waiting = false; // message waiting in transmit buffer 33 | bool rx_msg_waiting = false; // message waiting in receive buffer 34 | uint8_t rx_buffer[0xFF] = {0x00}; // receive bufer 35 | uint8_t tx_buffer[0x10] = {0x00}; // transmit buffer 36 | uint8_t rx_bytes = 0; // number of bytes in receive buffer 37 | uint8_t tx_bytes = 0; // number of bytes in transmit buffer 38 | uint32_t t_last_rx_byte = 0; // timestamp of last byte received 39 | }; 40 | 41 | #endif 42 | --------------------------------------------------------------------------------