Last active
June 5, 2021 22:10
-
-
Save klalle/ddf22bc67aa7a29e6f15 to your computer and use it in GitHub Desktop.
AUX Hack on Volvo HU-xxxx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* Melbus CDCHGR Emulator | |
* Program that emulates the MELBUS communication from a CD-changer (CD-CHGR) in a Volvo V70 (HU-xxxx) to enable AUX-input through the 8-pin DIN-contact. | |
* Tis setup is using an Arduino Nano 5v clone | |
* | |
* The HU enables the CD-CHGR in its source-menue after a successful initialization procedure is accomplished. | |
* The HU will remove the CD-CHGR everytime the car starts if it wont get an response from CD-CHGR (second init-procedure). | |
* | |
* Karl Hagström 2015-11-04 | |
* | |
* This project went realy smooth thanks to these sources: | |
* http://volvo.wot.lv/wiki/doku.php?id=melbus | |
* https://github.com/festlv/screen-control/blob/master/Screen_control/melbus.cpp | |
* http://forums.swedespeed.com/showthread.php?50450-VW-Phatbox-to-Volvo-Transplant-(How-To)&highlight=phatbox | |
*/ | |
const uint8_t MELBUS_CLOCKBIT_INT = 1; //interrupt numer (INT1) on DDR3 | |
const uint8_t MELBUS_CLOCKBIT = 3; //Pin D3 - CLK | |
const uint8_t MELBUS_DATA = 4; //Pin D4 - Data | |
const uint8_t MELBUS_BUSY = 5; //Pin D5 - Busy | |
volatile uint8_t melbus_ReceivedByte = 0; | |
volatile uint8_t melbus_LastReadByte[8] = {0, 0, 0, 0 ,0, 0, 0, 0}; | |
volatile uint8_t melbus_Bitposition = 7; | |
uint8_t ByteToSend = 0; | |
volatile long Counter = 0; | |
volatile bool InitialSequence = false; | |
volatile bool ByteIsRead = false; | |
volatile bool sending_byte = false; | |
volatile bool melbus_MasterRequested = false; | |
volatile bool melbus_MasterRequestAccepted = false; | |
volatile bool Connected = false; | |
volatile bool testbool = false; | |
volatile bool AllowInterruptRead = true; | |
//Startup seequence | |
void setup() { | |
//Data is deafult input high | |
pinMode(MELBUS_DATA, INPUT_PULLUP); | |
//Activate interrupt on clock pin (INT1, D3) | |
attachInterrupt(MELBUS_CLOCKBIT_INT, MELBUS_CLOCK_INTERRUPT, RISING); | |
//Set Clockpin-interrupt to input | |
pinMode(MELBUS_CLOCKBIT, INPUT_PULLUP); | |
//Initiate serial communication to debug via serial-usb (arduino) | |
Serial.begin(115200); | |
Serial.println("Initiating contact with Volvo-Melbus:"); | |
//Call function that tells HU that we want to register a new device | |
melbus_Init_CDCHRG(); | |
} | |
//Main loop | |
void loop() { | |
//Waiting for the clock interrupt to trigger 8 times to read one byte before evaluating the data | |
if (ByteIsRead) { | |
//Reset bool to enable reading of next byte | |
ByteIsRead=false; | |
Counter++; | |
//If we failed to connect, reset and retry the init procedure | |
if(Counter>100 && Connected == false){ | |
Serial.print("\nRetrying to connect...\n"); | |
Counter=0; | |
melbus_MasterRequested = false; | |
melbus_MasterRequestAccepted = false; | |
melbus_Init_CDCHRG(); | |
} | |
if(Counter>100) | |
Counter = 0; | |
//Check if this is the very first initial sequence (07 1A EE ....) | |
if(melbus_LastReadByte[2] == 0x07 && melbus_LastReadByte[1] == 0x1A && melbus_LastReadByte[0] == 0xEE) | |
{ | |
InitialSequence = true; | |
Serial.println("Initiating CD-CHGR..."); | |
} | |
//Now check if this is the car ignition startup sequence. | |
//The HU performes a check everytime the car starts, to evaluate if the initiated applications are still present: (00 00 1C ED ....) | |
//This function is not necessary since the Arduino reconnects if not connected by calling the first init-procedure! | |
else if(melbus_LastReadByte[3] == 0x00 && melbus_LastReadByte[2] == 0x00 && melbus_LastReadByte[1] == 0x1C && melbus_LastReadByte[0] == 0xED){ | |
Serial.println("Initiating ignition CD-CHGR..."); | |
InitialSequence = true; | |
} | |
//If this is the initial sequense and the HU is now asking if the CD-CHGR (0xE8) is prsent? | |
if(melbus_LastReadByte[0] == 0xE8 && InitialSequence == true){ | |
InitialSequence = false; | |
//Returning the expected byte to the HU, to confirm that the CD-CHGR is present (0xEE)! see "ID Response"-table here http://volvo.wot.lv/wiki/doku.php?id=melbus | |
SendByteToMelbus(0xEE); | |
Serial.println("\nConnected!"); | |
//Make sure the bit-counter is reset before we go back to reading bytes... | |
melbus_Bitposition=7; | |
} | |
//Check if the HU is asking for current track information (E9 1B E0 01 08 .......) - about once a second | |
//The HU is writing out CD ERROR if it wont get a response on this... the AUX works anyway! | |
else if(melbus_LastReadByte[4] == 0xE9 && melbus_LastReadByte[3] == 0x1B && melbus_LastReadByte[2] == 0xE0 && melbus_LastReadByte[1] == 0x01 && melbus_LastReadByte[0] == 0x08 ){ | |
Connected = true; | |
Serial.println("\nTrack info requested!"); | |
/* This is where you could request master mode and send the HU your cartridge and track info to display instead of "CD ERROR": | |
* 1. Wait for Busy-pin to go high again (HU not currently using melbus) | |
* 2. Pull datapin low for 2ms | |
* 3. listen for the "Master request broadcast" and then respond to your address followed by 8 bits (track info) | |
* see http://volvo.wot.lv/wiki/doku.php?id=melbus for further info | |
* | |
* For some reson I didn't get the HU to send out the Master request broadcast... | |
* I gave up since I don't care that the display sais CD Error (legit message since my smartphone lacks a CD!) | |
*/ | |
} | |
} | |
//If BUSYPIN is HIGH => HU is in between transmissions | |
if (digitalRead(MELBUS_BUSY)==HIGH) | |
{ | |
//Make sure we are in sync when reading the bits by resetting the clock reader | |
melbus_Bitposition = 7; | |
} | |
} | |
//Notify HU that we want to trigger the first initiate procedure to add a new device (CD-CHGR) by pulling BUSY line low for 1s | |
void melbus_Init_CDCHRG() { | |
//Disabel interrupt on INT1 quicker then: detachInterrupt(MELBUS_CLOCKBIT_INT); | |
EIMSK &= ~(1<<INT1); | |
// Wait untill Busy-line goes high (not busy) before we pull BUSY low to request init | |
while(digitalRead(MELBUS_BUSY)==LOW){} | |
delayMicroseconds(10); | |
pinMode(MELBUS_BUSY, OUTPUT); | |
digitalWrite(MELBUS_BUSY, LOW); | |
delay(1200); | |
digitalWrite(MELBUS_BUSY, HIGH); | |
pinMode(MELBUS_BUSY, INPUT_PULLUP); | |
//Enable interrupt on INT1, quicker then: attachInterrupt(MELBUS_CLOCKBIT_INT, MELBUS_CLOCK_INTERRUPT, RISING); | |
EIMSK |= (1<<INT1); | |
} | |
//This is a function that sends a byte to the HU - (not using interrupts) | |
void SendByteToMelbus(uint8_t byteToSend){ | |
//Disabel interrupt on INT1 quicker then: detachInterrupt(MELBUS_CLOCKBIT_INT); | |
EIMSK &= ~(1<<INT1); | |
//Convert datapin to output | |
//pinMode(MELBUS_DATA, OUTPUT); //To slow, use DDRD instead: | |
DDRD |= (1<<MELBUS_DATA); | |
//For each bit in the byte | |
for(int i=7;i>=0;i--) | |
{ | |
//If bit [i] is "1" - make datapin high | |
if(byteToSend & (1<<i)){ | |
//digitalWrite(, HIGH); //To slow, use AVR-style instead: | |
PORTD |= (1<<MELBUS_DATA); | |
} | |
//if bit [i] is "0" - make databpin low | |
else{ | |
PORTD &= ~(1<<MELBUS_DATA); | |
} | |
//dummyloop until clk goes low and then high again (I think HU is recording the value on datapin when clk goes back high again) | |
while(PIND & (1<<MELBUS_CLOCKBIT)){} | |
while(!(PIND & (1<<MELBUS_CLOCKBIT))){} | |
//while(digitalRead(MELBUS_CLOCKBIT)==HIGH){} | |
//while(digitalRead(MELBUS_CLOCKBIT)==LOW){} | |
} | |
//Reset datapin to high and return it to an input | |
pinMode(MELBUS_DATA, INPUT_PULLUP); | |
//Enable interrupt on INT1, quicker then: attachInterrupt(MELBUS_CLOCKBIT_INT, MELBUS_CLOCK_INTERRUPT, RISING); | |
EIMSK |= (1<<INT1); | |
} | |
//Global external interrupt that triggers when clock pin goes high after it has been low for a short time => time to read datapin | |
void MELBUS_CLOCK_INTERRUPT() { | |
//Read status of Datapin and set status of current bit in recv_byte | |
if (digitalRead(MELBUS_DATA)==HIGH){ | |
melbus_ReceivedByte |= (1<<melbus_Bitposition); //set bit nr [melbus_Bitposition] to "1" | |
} | |
else { | |
melbus_ReceivedByte &=~(1<<melbus_Bitposition); //set bit nr [melbus_Bitposition] to "0" | |
} | |
//if all the bits in the byte are read: | |
if (melbus_Bitposition==0) { | |
//Move every lastreadbyte one step down the array to keep track of former bytes | |
for(int i=7;i>0;i--){ | |
melbus_LastReadByte[i] = melbus_LastReadByte[i-1]; | |
} | |
//Insert the newly read byte into first position of array | |
melbus_LastReadByte[0] = melbus_ReceivedByte; | |
//set bool to true to evaluate the bytes in main loop | |
ByteIsRead = true; | |
//Reset bitcount to first bit in byte | |
melbus_Bitposition=7; | |
} | |
else { | |
//set bitnumber to address of next bit in byte | |
melbus_Bitposition--; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment