visualapproach / Volvo-melbus

Volvo MELBUS Bluetooth audio input and remote control with Arduino Nano
66 stars 18 forks source link

Power on BT module only if HU in CD-changer mode #17

Open BK-Poland opened 5 years ago

BK-Poland commented 5 years ago

Great job! Thank you! I have only one "little" dream: Is it possible to switch power on the BT module only when I switch to the CD changer in HU? BT turned off when I listen to FM radio.

archi commented 5 years ago

On the hardware side you could add a relay or a suitable NPN transistor to control the power of the module. Software-wise: The protocol allows for this for sure, but @visualapproach would need to add some code to handle that (I didn't look at the code, so no idea how possible that is).

BK-Poland commented 5 years ago

Hardware for me is no problem. The Arduino output controls the transistor that powers the BT module. The question is: it can be programmed? @visualapproach ? Does Arduino get a message from HU that the CD-changer is on?

visualapproach commented 5 years ago

Sure thing, I Will have a look at it and post somerhing soon.

BK-Poland commented 5 years ago

I will be waiting for a good news. :-)

visualapproach commented 5 years ago

Code removed and reposted for readability

visualapproach commented 5 years ago

check lines 43 and 713-740 That should do it, I think

BK-Poland commented 5 years ago

I have connected according to: older_stuff/schematics_melbus_hack_v2. All I need to do is change the PLAY PIN from 12 to 10? And connect Arduino PIN 5 to base of NPN transistor?

visualapproach commented 5 years ago

No, you shouldn't have to change PLAY PIN. What you wanna do is to capture POWERON-message and switch on the transistor. If POWEROFF is received, you turn off the transistor. That's all you need to change.

If you use the old code (v7i line 255 onwards), look at the following snippets and adjust according to my suggestion:

         //6, power up. resp ack (0x00), not verified
          case 6:
            byteToSend = 0x00;
            SendByteToMelbus();
            trackInfo[1] = startByte;
            trackInfo[8] = startByte;
            -->   digitalWrite(5, HIGH); //change "5" to whatever pin you have connected to the transistor.
            break;
          //7, power down. ack (0x00), not verified
          case 7:
            byteToSend = 0x00;
            SendByteToMelbus();
            trackInfo[1] = stopByte;
            trackInfo[8] = stopByte;
            -->   digitalWrite(5, LOW); //change "5" to whatever pin you have connected to the transistor
            break;

If you are using the newest code, do the same, but at lines 713 onwards:

        //CDC_PUP
          case 27:
            byteToSend = 0x00;
            SendByteToMelbus();
            trackInfo[1] = startByte;
            trackInfo[8] = startByte;
            -->   digitalWrite(5, HIGH); //change "5" to whatever pin you have connected to the transistor.
            break;

          //CDC_PDN
          case 28:
            byteToSend = 0x00;
            SendByteToMelbus();
            trackInfo[1] = stopByte;
            trackInfo[8] = stopByte;
            -->   digitalWrite(5, LOW); //change "5" to whatever pin you have connected to the transistor
            break;

Ofc remove the arrows. They are just for your attention.

BK-Poland commented 5 years ago

And in the "void setup ()" section I have to add: pinMode (5, OUTPUT); digitalWrite (5, LOW); ???

visualapproach commented 5 years ago

Yes. Wish you luck, and let us know if it works.

archi commented 5 years ago

@visualapproach Maybe you can patch this in the repo; I think the feature doesn't do any harm if it goes unused.

BK-Poland commented 5 years ago

Wait. First, I'll do the tests. Then I will draw a connection diagram. OK? :-)

visualapproach commented 5 years ago

Yeah sure. If it works for BK-Poland I'll update the repo

visualapproach commented 5 years ago

And thanks all for keeping this project alive!

BK-Poland commented 5 years ago

The first test (older stuff code) was quite good. There is only one problem. Output D5 is in a high state also when I listen to the internal CD (HU). When I switch to FM, BT turns off. Perfect! When I turn on the CD-Changer, BT turns on. Perfect! But when I turn on the Internal-HU-CD, BT also turns on. It looks like Internal-HU-CD = CD-Changer.

visualapproach commented 5 years ago

Ok I’ll have a look at it.

BK-Poland commented 5 years ago

And thanks all for keeping this project alive! Thank you for a great job!

Ok I’ll have a look at it. Ok, I will wait.

visualapproach commented 5 years ago

@BK-Poland I couldn't find anything obvious. What happens when you go from CDC->FM->CD ? Does the D5 go high->low->high?

BK-Poland commented 5 years ago

What happens when you go from CDC->FM->CD ? Does the D5 go high->low->high?

Yes, exactly.

BK-Poland commented 5 years ago

There is one more interesting thing. When I switch CDC-> CD, the BT module will restart. So Arduino turns off D5 at 100ms or maybe max 200ms. You can see how the LED in the BT module goes out for a moment.

visualapproach commented 5 years ago

Can you hook up a computr via USB and log serial output?

visualapproach commented 5 years ago

(Don’t forget to uncomment the transmission logging in Main loop)

BK-Poland commented 5 years ago

Can you hook up a computr via USB and log serial output?

Sure. But... How can I do this? :-)

visualapproach commented 5 years ago

Arduino in car, hooked up to computer via USB. Use arduino IDE to Reprogram arduino with new software (uncomment the logging part by removing the double slashes). Then turn on radio and watch the serial monitor in IDE. Switch between sources and then copy text

BK-Poland commented 5 years ago

OK. I need 30 minutes. I'll be back... :-D

BK-Poland commented 5 years ago

I do not know what I'm doing wrong, but the serial monitor does not work. Communication is correct, but nothing is there. Empty serial monitor window.

visualapproach commented 5 years ago

Need to sleep now. I get back to you. Check correct port and baudrate 115Kbps (i think)

BK-Poland commented 5 years ago

Check correct port and baudrate 115Kbps (i think)

They are correct. I need to learn Arduino IDE :-)

BK-Poland commented 5 years ago

I have a stupid question: Do I have to remove all double slashes from all lines with "Serial.println" and "Serial.print"?

visualapproach commented 5 years ago

Edit: wrong code in previous post.

No stupid question. Try to uncomment this part:

//Do other stuff here if you want. MELBUS lines are free now. BUSY = IDLE (HIGH) //Don't take too much time though, since BUSY might go active anytime, and then we'd better be ready to receive. //Printing transmission log (incoming, before responses) if (flag) { for (byte b = 0; b < byteCounter; b++) { Serial.print(melbus_log[b], HEX); Serial.print(" "); } Serial.println(); }

visualapproach commented 5 years ago

and in setup() uncomment this:

//Serial.begin(115200);

to

Serial.begin(115200);

BK-Poland commented 5 years ago

` (connect to Arduino, HU is on FM) 7 1A EE F8 7F E8 1E EF

E9 1B E0 1 8 (FM to CDC) -> E8 19 2F E9 1B E0 1 8

E9 1B E0 1 8

E9 1B E0 1 8

E9 1B E0 1 8

E9 1B E0 1 8

E9 1B E0 1 8

E9 1B E0 1 8

E9 1B E0 1 8

E9 1B E0 1 8

E9 1B E0 1 8

E9 1B E0 1 8

E9 1B E0 1 8

E9 1B E0 1 8

E9 1B E0 1 8

E9 1B E0 1 8 E8 19 22 81 1B E0 1 8 10 2 1 1 80 1 C CC CC E9 1B E0 1 8 80 19 2F 0 E 76 43 C3 FC C0 70 A4 0 80 C0 0 87 FF FF (CDC to CD) -> 81 1B E0 1 8 10 48 1 1 80 1 F FF FF

81 1B E0 1 8 10 48 1 1 80 1 F FF FF

81 1B E0 1 8 10 48 1 1 80 1 F FF FF

81 1B E0 1 8 10 48 1 1 80 1 F FF FF

81 1B E0 1 8 10 48 1 1 80 1 F FF FF

81 1B E0 1 8 10 48 1 1 80 1 F FF FF 0 1C EC

81 1B E0 1 8 10 8 1 1 80 1 0 5 32 0 1C EC 81 1B E0 1 8 10 8 1 1 80 1 0 5 33

81 1B E0 1 8 10 8 1 1 80 1 0 5 33 0 1C EC 81 1B E0 1 8 10 8 1 1 80 1 0 5 34

81 1B E0 1 8 10 8 1 1 80 1 0 5 34 0 1C EC 81 1B E0 1 8 10 8 1 1 80 1 0 5 35

81 1B E0 1 8 10 8 1 1 80 1 0 5 35 0 1C EC 81 1B E0 1 8 10 8 1 1 80 1 0 5 36

81 1B E0 1 8 10 8 1 1 80 1 0 5 36 0 1C EC 81 1B E0 1 8 10 8 1 1 80 1 0 5 37 80 19 22 0 E 76 43 C3 FC C0 70 A1 0 80 C0 0 86 66 66 21:10:46.682 -> E9 1B E0 1 8 81 1B E0 1 8 10 42 1 1 80 1 C CC CC E8 19 2F (CD to CDC) -> E9 1B E0 1 8 0 1C EC E9 1B E0 1 8

E9 1B E0 1 8

E9 1B E0 1 8

E9 1B E0 1 8

E9 1B E0 1 8

E9 1B E0 1 8

E9 1B E0 1 8

E9 1B E0 1 8

E9 1B E0 1 8

E9 1B E0 1 8

E9 1B E0 1 8

E9 1B E0 1 8

E9 1B E0 1 8

E9 1B E0 1 8

E9 1B E0 1 8

E9 1B E0 1 8

E9 1B E0 1 8

E9 1B E0 1 8

E9 1B E0 1 8 E8 19 22 (CDC to FM) -> E9 1B E0 1 8

80 19 22 0 E 76 43 C3 FC C0 70 81 0 C0 C0 0 86 66 66 (Shut down HU) -> E8 19 22 81 1B E0 1 8 10 2 1 81 80 1 C CC CC 21:11:04.113 -> E9 1B E0 1 8

0 18 12 `

visualapproach commented 5 years ago

@BK-Poland Good, you got serial monitoring to work!

Here is a "translation" of the log:

` (connect to Arduino, HU is on FM) 7 1A EE F8 7F E8 1E EF (Cartridge info request)

E9 1B E0 1 8 track info request) (FM to CDC) -> E8 19 2F (CDC Power UP) E9 1B E0 1 8 track info request)

E9 1B E0 1 8 track info request)

E9 1B E0 1 8 track info request)

E9 1B E0 1 8 track info request)

E9 1B E0 1 8 track info request)

E9 1B E0 1 8 track info request)

E9 1B E0 1 8 track info request)

E9 1B E0 1 8 track info request)

E9 1B E0 1 8 track info request)

E9 1B E0 1 8 track info request)

E9 1B E0 1 8 track info request)

E9 1B E0 1 8 track info request)

E9 1B E0 1 8 track info request)

E9 1B E0 1 8 track info request)

E9 1B E0 1 8 track info request) E8 19 22 (CDC Power DOWN) 81 1B E0 1 8 10 2 1 1 80 1 C CC CC E9 1B E0 1 8 track info request) 80 19 2F 0 E 76 43 C3 FC C0 70 A4 0 80 C0 0 87 FF FF (CDC to CD) -> 81 1B E0 1 8 10 48 1 1 80 1 F FF FF

81 1B E0 1 8 10 48 1 1 80 1 F FF FF

81 1B E0 1 8 10 48 1 1 80 1 F FF FF

81 1B E0 1 8 10 48 1 1 80 1 F FF FF

81 1B E0 1 8 10 48 1 1 80 1 F FF FF

81 1B E0 1 8 10 48 1 1 80 1 F FF FF 0 1C EC

81 1B E0 1 8 10 8 1 1 80 1 0 5 32 0 1C EC 81 1B E0 1 8 10 8 1 1 80 1 0 5 33

81 1B E0 1 8 10 8 1 1 80 1 0 5 33 0 1C EC 81 1B E0 1 8 10 8 1 1 80 1 0 5 34

81 1B E0 1 8 10 8 1 1 80 1 0 5 34 0 1C EC 81 1B E0 1 8 10 8 1 1 80 1 0 5 35

81 1B E0 1 8 10 8 1 1 80 1 0 5 35 0 1C EC 81 1B E0 1 8 10 8 1 1 80 1 0 5 36

81 1B E0 1 8 10 8 1 1 80 1 0 5 36 0 1C EC 81 1B E0 1 8 10 8 1 1 80 1 0 5 37 80 19 22 0 E 76 43 C3 FC C0 70 A1 0 80 C0 0 86 66 66 21:10:46.682 -> E9 1B E0 1 8 track info request) 81 1B E0 1 8 10 42 1 1 80 1 C CC CC E8 19 2F (CDC Power UP) (CD to CDC) -> E9 1B E0 1 8 track info request) 0 1C EC E9 1B E0 1 8 track info request)

E9 1B E0 1 8 track info request)

E9 1B E0 1 8 track info request)

E9 1B E0 1 8 track info request)

E9 1B E0 1 8 track info request)

E9 1B E0 1 8 track info request)

E9 1B E0 1 8 track info request)

E9 1B E0 1 8 track info request)

E9 1B E0 1 8 track info request)

E9 1B E0 1 8 track info request)

E9 1B E0 1 8 track info request)

E9 1B E0 1 8 track info request)

E9 1B E0 1 8 track info request)

E9 1B E0 1 8 track info request)

E9 1B E0 1 8 track info request)

E9 1B E0 1 8 track info request)

E9 1B E0 1 8 track info request)

E9 1B E0 1 8 track info request)

E9 1B E0 1 8 track info request) E8 19 22 (CDC Power DOWN) (CDC to FM) -> E9 1B E0 1 8 track info request)

80 19 22 0 E 76 43 C3 FC C0 70 81 0 C0 C0 0 86 66 66 (Shut down HU) -> E8 19 22 (CDC Power DOWN) 81 1B E0 1 8 10 2 1 81 80 1 C CC CC 21:11:04.113 -> E9 1B E0 1 8 track info request)

0 18 12 `

It appears the communication is good and the HU does the right thing. That narrows it down to program error or hardware error. Please add following lines to your code and reupload, then check serial output again.

after case 6, directly after the line "digitalWrite(5, HIGH);" add this

Serial.println("D5 ON");

after case 7, directly after the line "digitalWrite(5, LOW);" add this

Serial.println("D5 OFF");

visualapproach commented 5 years ago

You are also welcome to post that part of your code if you want me to check for errors.

BK-Poland commented 5 years ago

I also have an emergency plan: connect a 4700uF capacitor to the BT module. Then temporary power outages will not cause BT restart. Capacitor like 1-second UPS. ;-)

It would be nice if you could switch BT off when the CD (not CDC) works. So OK, I'll add new lines to my code and tomorrow I'll show you LOG with "D5 ON" and "D5 OFF". See you! :-)

BK-Poland commented 5 years ago

7 1A EE F8 7F E8 1E EF E9 1B E0 1 8 D5 ON E8 19 2F E9 1B E0 1 8 E9 1B E0 1 8 E9 1B E0 1 8 E9 1B E0 1 8 E9 1B E0 1 8 E9 1B E0 1 8 E9 1B E0 1 8 E9 1B E0 1 8 E9 1B E0 1 8 D5 OFF E8 19 22 81 1B E0 1 8 10 2 1 1 80 1 C CC CC E9 1B E0 1 8 D5 ON 80 19 2F 0 E 76 43 C3 FC C0 70 A4 0 80 C0 0 80 2 A0 81 1B E0 1 8 10 48 1 1 80 1 0 5 40 81 1B E0 1 8 10 48 1 1 80 1 0 5 40 81 1B E0 1 8 10 48 1 1 80 1 0 5 40 81 1B E0 1 8 10 48 1 1 80 1 0 5 40 81 1B E0 1 8 10 48 1 1 80 1 0 5 40 81 1B E0 1 8 10 8 1 1 80 1 0 5 40 0 1C EC 81 1B E0 1 8 10 8 1 1 80 1 0 5 40 0 1C EC 81 1B E0 1 8 10 8 1 1 80 1 0 5 41 D5 OFF 80 19 22 0 E 76 43 C3 FC C0 70 A1 0 80 C0 0 86 66 66 E9 1B E0 1 8 81 1B E0 1 8 10 42 1 1 80 1 C CC CC D5 ON E8 19 2F E9 1B E0 1 8 0 1C EC E9 1B E0 1 8 E9 1B E0 1 8 E9 1B E0 1 8 E9 1B E0 1 8 E9 1B E0 1 8 D5 OFF E8 19 22 E9 1B E0 1 8 D5 OFF 80 19 22 0 C 91 0 81 1B E0 1 8 10 2 1 1 80 1 C CC CC E9 1B E0 1 8

visualapproach commented 5 years ago

yeah it's code related. I'll investigate. Could you send me your code by email?

BK-Poland commented 5 years ago

/* new: Press cd1 or cd2 to change bluetooth volume. Cd change is resetting track number.

By Thomas Landahl, 2017-04-25

*/

define MELBUS_CLOCKBIT (byte)2 //Pin D2 - CLK

define MELBUS_DATA (byte)3 //Pin D3 - Data

define MELBUS_BUSY (byte)4 //Pin D4 - Busy

const byte prevPin = 8; const byte nextPin = 9; const byte upPin = 10; //volume up const byte downPin = 11; //volume down const byte playPin = 12;

//volatile variables used inside and outside of ISP volatile byte melbus_ReceivedByte = 0; volatile byte melbus_Bitposition = 7; volatile bool byteIsRead = false;

byte byteToSend = 0; //global to avoid unnecessary overhead unsigned long Connected = 0;

//preset parameters byte track = 0x01; //Display show HEX value, not DEC. (A-F not "allowed") byte cd = 0x01; //1-10 is allowed (in HEX. 0A-0F and 1A-1F is not allowed) byte powerup_ack = 0x00;

//Base adresses. //const byte MD_ADDR = 0x70; //internal //const byte CD_ADDR = 0x80; //internal //const byte TV_ADDR = 0xA8; //might be A9 during main init sequence //const byte DAB_ADDR = 0xB8; //const byte SAT_ADDR = 0xC0; //const byte MDC_ADDR = 0xD8; //const byte CDC_ADDR = 0xE8; //const byte RESPONSE = 0x06; //add this to base adress when responding to HU //const byte MASTER = 0x07; //add this to base adress when requesting/beeing master

//change theese definitions if you wanna emulate another device. //My HU-650 don't accept anything but a CD-C (so far).

define RESPONSE_ID 0xEE //ID while responding to init requests (which will use base_id)

define MASTER_ID 0xEF //ID while requesting/beeing master

define BASE_ID 0xE8 //ID when getting commands from HU

define ALT_ID 0xE9 //Alternative ID when getting commands from HU

//This list can't be too long. We only have so much time between the received bytes. (approx 500 us) const byte commands[][5] = { {BASE_ID, 0x1E, 0xEF}, //0, Cartridge info request. Respond with 6 bytes (confirmed) {ALT_ID, 0x1B, 0xE0, 0x01, 0x08}, //1, track info req. resp 9 bytes {BASE_ID, 0x1B, 0x2D, 0x40, 0x01}, //2, next track. {BASE_ID, 0x1B, 0x2D, 0x00, 0x01}, //3, prev track {BASE_ID, 0x1A, 0x50}, //4, change cd {BASE_ID, 0x1A, 0x50}, //5, not used {BASE_ID, 0x19, 0x2F}, //6, power up. resp ack (0x00), 0x19 could be 0x49?? {BASE_ID, 0x19, 0x22}, //7, power down. ack (0x00), not verified {BASE_ID, 0x19, 0x29}, //8, FFW. ack, not verified {BASE_ID, 0x19, 0x26}, //9, FRW. ack, not verified {BASE_ID, 0x19, 0x2E}, //10 scan mode. ack, cmd verified! {BASE_ID, 0x19, 0x52}, //11 random mode. ack, not verified {0x07, 0x1A, 0xEE}, //12 main init seq. wait for BASE_ID and respond with RESPONSE_ID. {0x00, 0x00, 0x1C, 0xED}, //13 secondary init req. wait for BASE_ID and respond with RESPONSE_ID. {0x00, 0x1C, 0xEC} //14 master req broadcast. wait for MASTER_ID and respond with MASTER_ID. }; //keep track of the length of each command. (The two dimensional array above have fixed width (padded with 0x00)) const byte listLen = 15; byte cmdLen[listLen] = {3, 5, 5, 5, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3};

//arrays to send to HU when requested

//TRACK INFO /*According to internet resources, HEX 10 88 01 01 80 03 00 02 22 should be a valid answer. another source: 0x00, 0x02, 0x00, 0x01, 0x80, 0x01, 0xff, 0x60, 0x60 All comments in HEX

  1. 10, 14, 20, 24, 30, 34, 80, 84, 90, 94 => HU is OK, else error

  2. Status message 01 = able to select cd by pushing HU-buttons but it resets to 01 after a couple of secs 00, 10, 20, 40 = HU display: cd load cartridge (decimal numbers here??) 22 = HU asking for status rapidly 11 = HU display: CD cartridge empty 44 = HU display: CD number is blinking 81 = HU display: cd error

  3. 8 = HU display: random

  4. HU display: CD number (1-10). 0x10 shows "CD ". 0x0A (DEC 10) does not work? HU displays each nibble, and only values < A is valid.

  5. Unknown meaning.

  6. TRACK number

  7. Hours not displayed on HU-650

  8. Minutes not displayed on HU-650

  9. Status Power?

*/ byte trackInfo[] = {0x00, 0x02, 0x00, cd, 0x80, track, 0xC7, 0x0A, 0x02}; //9 bytes byte startByte = 0x08; //on powerup - change trackInfo[1] & [8] to this byte stopByte = 0x02; //same on powerdown

//CARTRIDGE INFO //According to internet resources, HEX 00 08 01 4A 0C CC is a valid answer. //another source: 0x00, 0x0f, 0xff, 0x4a, 0xfc, 0xff byte cartridgeInfo[] = {0x00, 0xFC, 0xFF, 0x4A, 0xFC, 0xFF}; //6 bytes // 0xFC = B1111 1100; first six bits for each CD in 6-CD cartridge??

//MASTER MODE INFO byte masterInfo[] = {0xF8, 0x85, 0xE2, 0x80, 0x03, 0x00, 0x02, 0x02}; //8 bytes

void setup() { //Disable timer0 interrupt. It's is only bogging down the system. We need speed! TIMSK0 &= ~_BV(TOIE0);

//All lines are idle HIGH pinMode(MELBUS_DATA, INPUT_PULLUP); pinMode(MELBUS_CLOCKBIT, INPUT_PULLUP); pinMode(MELBUS_BUSY, INPUT_PULLUP); pinMode(nextPin, OUTPUT); pinMode(prevPin, OUTPUT); pinMode(playPin, OUTPUT); pinMode (5, OUTPUT); digitalWrite (5, LOW); digitalWrite(nextPin, LOW); digitalWrite(prevPin, LOW); digitalWrite(playPin, LOW);

//LED indicates connected status. pinMode(13, OUTPUT); digitalWrite(13, LOW);

//Activate interrupt on clock pin attachInterrupt(digitalPinToInterrupt(MELBUS_CLOCKBIT), MELBUS_CLOCK_INTERRUPT, RISING);

//Initiate serial communication to debug via serial-usb (arduino) //For debugging purpose. Better off without it when things work. //Serial printing take a lot of time!! Serial.begin(115200);

//Call function that tells HU that we want to register a new device melbusInitReq(); }

//Main loop void loop() { static byte byteCounter = 0; //keep track of how many bytes is sent in current command static byte lastByte = 0; //used to copy volatile byte to register variable. See below static byte matching[listLen]; //Keep track of every matching byte in the commands array //static byte msgCount = 0; //inc every time busy line goes idle. //static byte masterCount = 0; byte melbus_log[99]; //main init sequence is 61 bytes long... bool flag = false; bool BUSY = PIND & (1 << MELBUS_BUSY);

Connected++; //check BUSY line active (active low) while (!BUSY) { //Transmission handling here... //debug //PORTB |= 1 << 5; if (byteIsRead) { //debug //PORTB &= ~(1 << 5); byteIsRead = false; lastByte = melbus_ReceivedByte; //copy volatile byte to register variable //Well, since HU is talking to us we might call it a conversation. Connected = 0; melbus_log[byteCounter] = lastByte; //Loop though every command in the array and check for matches. (No, don't go looking for matches now) for (byte cmd = 0; cmd < listLen; cmd++) { //check if this byte is matching if (lastByte == commands[cmd][byteCounter]) { matching[cmd]++; //check if a complete command is received, and take appropriate action if (matching[cmd] == cmdLen[cmd]) { switch (cmd) { //0, Cartridge info request. Respond with 6 bytes case 0: SendCartridgeInfo(); break; //1, track info req. resp 9 bytes case 1: SendTrackInfo(); //Serial.println(" trk info sent"); break; //2, next track. case 2: track++; fixTrack(); trackInfo[5] = track; nextTrack(); break; //3, prev track case 3: track--; fixTrack(); trackInfo[5] = track; prevTrack(); break; //4, change cd case 4: //wait for next byte to get CD number while (!(PIND & (1 << MELBUS_BUSY))) { if (byteIsRead) { byteIsRead = false; switch (melbus_ReceivedByte) { case 0x81: cd = 1; volumeDown(); //internal to BT-module, not BT source track = 1; break; case 0x82: cd = 2; volumeUp(); //internal to BT-module, not BT source track = 1; break; case 0x83: cd = 3; track = 1; break; case 0x84: cd = 4; track = 1; break; case 0x85: cd = 5; track = 1; break; case 0x86: cd = 6; track = 1; break; case 0x41: cd++; track = 1; break; case 0x01: cd--; track = 1; break; default: track = 1; break; } } } trackInfo[3] = cd; trackInfo[5] = track; break; //5, not used case 5: break; //6, power up. resp ack (0x00), not verified case 6: byteToSend = 0x00; SendByteToMelbus(); trackInfo[1] = startByte; trackInfo[8] = startByte; digitalWrite(5, HIGH); Serial.println("D5 ON"); break; //7, power down. ack (0x00), not verified case 7: byteToSend = 0x00; SendByteToMelbus(); trackInfo[1] = stopByte; trackInfo[8] = stopByte; digitalWrite(5, LOW); Serial.println("D5 OFF"); break; //8, FFW. ack, not verified case 8: byteToSend = 0x00; SendByteToMelbus(); break; //9, FRW. ack, not verified case 9: byteToSend = 0x00; SendByteToMelbus(); break; //10 scan mode. //Used as a PLAY button here case 10: byteToSend = 0x00; SendByteToMelbus(); play(); //trackInfo[0]++; //debug break; //11 random mode. //Used as a PLAY button here case 11: byteToSend = 0x00; SendByteToMelbus(); play(); break; //12 main init seq. wait for BASE_ID and respond with RESPONSE_ID. case 12: //wait for base_id and respond with response_id while (!(PIND & (1 << MELBUS_BUSY))) { if (byteIsRead) { byteIsRead = false; //debug get whole message //byteCounter++; //melbus_log[byteCounter] = melbus_ReceivedByte; //end debug if (melbus_ReceivedByte == BASE_ID) { byteToSend = RESPONSE_ID; SendByteToMelbus(); break; } } } break; //13 secondary init req. wait for BASE_ID and respond with RESPONSE_ID. case 13: //wait for base_id and respond response_id //digitalWrite(13, HIGH); while (!(PIND & (1 << MELBUS_BUSY))) { if (byteIsRead) { byteIsRead = false; //debug get whole message //byteCounter++; //melbus_log[byteCounter] = melbus_ReceivedByte; //end debug if (melbus_ReceivedByte == BASE_ID) { byteToSend = RESPONSE_ID; SendByteToMelbus(); break; } } } break; //14 master req broadcast. wait for MASTER_ID and respond with MASTER_ID. (not used in this sketch) case 14: while (!(PIND & (1 << MELBUS_BUSY))) { if (byteIsRead) { byteIsRead = false; if (melbus_ReceivedByte == MASTER_ID) { byteToSend = MASTER_ID; SendByteToMelbus(); //do stuff here to send message to HU, like masterSend(); break; } } } break; // //15 // case 15: // byteToSend = 0x00; // SendByteToMelbus(); // break; // //16 // case 16: // byteToSend = 0x00; // SendByteToMelbus(); // break; // //17 // case 17: // byteToSend = 0x00; // SendByteToMelbus(); // break; } break; //bail for loop. (Not meaningful to search more commands if one is already found) } //end if command found } //end if lastbyte matches } //end for cmd loop byteCounter++; } //end if byteisread //Update status of BUSY line, so we don't end up in an infinite while-loop. BUSY = PIND & (1 << MELBUS_BUSY); if (BUSY) { flag = true; //used to execute some code only once between transmissions } }

//Do other stuff here if you want. MELBUS lines are free now. BUSY = IDLE (HIGH) //Don't take too much time though, since BUSY might go active anytime, and then we'd better be ready to receive. //Printing transmission log (incoming, before responses) if (flag) { for (byte b = 0; b < byteCounter; b++) { Serial.print(melbus_log[b], HEX); Serial.print(" "); } Serial.println(); }

//Reset stuff byteCounter = 0; melbus_Bitposition = 7; for (byte i = 0; i < listLen; i++) { matching[i] = 0; } if (Connected > 2000000) { melbusInitReq(); Connected = 0; }

//Incoming serial data is supposed to look like this: //index, databyte: "3, 5" //No error checking here since we're just hacking // if (Serial.available() > 0) { // int index = Serial.parseInt(); // trackInfo[index] = (byte) Serial.parseInt(); // Serial.readStringUntil('\n'); // for (byte b = 0; b < 9; b++) { // Serial.print(trackInfo[b], HEX); // Serial.print("-"); // } // Serial.println(); // }

// I haven't seen any advantages from sending messages to HU. //Therefore this section is disabled. // if (flag) { // if(some timed interval etc) // reqMaster(); // }

flag = false; //don't print during next loop. Wait for new message to arrive first. }

//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 melbusInitReq() { //Serial.println("conn"); //Disable interrupt on INT0 quicker than: detachInterrupt(MELBUS_CLOCKBIT_INT); EIMSK &= ~(1 << INT0);

// Wait until Busy-line goes high (not busy) before we pull BUSY low to request init while (digitalRead(MELBUS_BUSY) == LOW) {} delayMicroseconds(20);

pinMode(MELBUS_BUSY, OUTPUT); digitalWrite(MELBUS_BUSY, LOW); //timer0 is off so we have to do a trick here for (unsigned int i = 0; i < 12000; i++) delayMicroseconds(100);

digitalWrite(MELBUS_BUSY, HIGH); pinMode(MELBUS_BUSY, INPUT_PULLUP); //Enable interrupt on INT0, quicker than: attachInterrupt(MELBUS_CLOCKBIT_INT, MELBUS_CLOCK_INTERRUPT, RISING); EIMSK |= (1 << INT0); }

//This is a function that sends a byte to the HU - (not using interrupts) //SET byteToSend variable before calling this!! void SendByteToMelbus() { //Disable interrupt on INT0 quicker than: detachInterrupt(MELBUS_CLOCKBIT_INT); EIMSK &= ~(1 << INT0);

//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--) { while (PIND & (1 << MELBUS_CLOCKBIT)) {} //wait for low clock //If bit [i] is "1" - make datapin high if (byteToSend & (1 << i)) { PORTD |= (1 << MELBUS_DATA); } //if bit [i] is "0" - make datapin low else { PORTD &= ~(1 << MELBUS_DATA); } while (!(PIND & (1 << MELBUS_CLOCKBIT))) {} //wait for high clock }

//Reset datapin to high and return it to an input //pinMode(MELBUS_DATA, INPUT_PULLUP); PORTD |= 1 << MELBUS_DATA; DDRD &= ~(1 << MELBUS_DATA);

//Enable interrupt on INT0, quicker than: attachInterrupt(MELBUS_CLOCKBIT_INT, MELBUS_CLOCK_INTERRUPT, RISING); EIMSK |= (1 << INT0); }

//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) { if ((PIND & (1 << MELBUS_DATA))) { 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) { //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--; } }

void SendTrackInfo() { noInterrupts(); for (byte i = 0; i < 9; i++) { byteToSend = trackInfo[i]; SendByteToMelbus(); } interrupts(); }

void SendCartridgeInfo() { noInterrupts(); for (byte i = 0; i < 6; i++) { byteToSend = cartridgeInfo[i]; SendByteToMelbus(); } interrupts(); }

void reqMaster() { DDRD |= (1 << MELBUS_DATA); //output PORTD &= ~(1 << MELBUS_DATA);//low delayMicroseconds(700); delayMicroseconds(700); delayMicroseconds(800); PORTD |= (1 << MELBUS_DATA);//high DDRD &= ~(1 << MELBUS_DATA); //back to input }

void masterSend() { noInterrupts(); for (byte i = 0; i < 8; i++) { byteToSend = masterInfo[i]; SendByteToMelbus(); } interrupts(); }

void fixTrack() { //cut out A-F in each nibble, and skip "00" byte hn = track >> 4; byte ln = track & 0xF; if (ln == 0xA) { ln = 0; hn += 1; } if (ln == 0xF) { ln = 9; } if (hn == 0xA) { hn = 0; ln = 1; } if ((hn == 0) && (ln == 0)) { ln = 0x9; hn = 0x9; } track = (hn << 4) + ln; }

//Simulate button presses on the BT module. 200 ms works good. Less is not more in this case... void nextTrack() { digitalWrite(nextPin, HIGH); for (byte i = 0; i < 200; i++) delayMicroseconds(1000); digitalWrite(nextPin, LOW); }

void prevTrack() { digitalWrite(prevPin, HIGH); for (byte i = 0; i < 200; i++) delayMicroseconds(1000); digitalWrite(prevPin, LOW); }

void play() { digitalWrite(playPin, HIGH); for (byte i = 0; i < 200; i++) delayMicroseconds(1000); digitalWrite(playPin, LOW); }

void volumeDown() { digitalWrite(downPin, HIGH); for (byte i = 0; i < 200; i++) delayMicroseconds(1000); digitalWrite(downPin, LOW);

}

void volumeUp() { digitalWrite(upPin, HIGH); for (byte i = 0; i < 200; i++) delayMicroseconds(1000); digitalWrite(upPin, LOW);

}

//Happy listening, hacker!

visualapproach commented 5 years ago

change line 182 to

if ((matching[cmd] == cmdLen[cmd]) && (byteCounter+1 == commands[cmd][0])) {

and try again. (It's friday night so my mental skills are degraded now ;-) but I have good hope this works)

BK-Poland commented 5 years ago

Something is wrong. Now HU does not detect the CDC. So... It's Friday night. Time for play ;-) We'll be back to the topic after the weekend.

visualapproach commented 5 years ago

if ((matching[cmd] == cmdLen[cmd]) && (byteCounter+1 ==cmdLen[cmd] )) {

BK-Poland commented 5 years ago

It works! Perfectly! A big beer for you. Thank you!

BK-Poland commented 5 years ago

OK. Time to sharezone my hardware. I use the MOSFET IRL540N (logic level). I use gate resistor 1Mohm. Its simple to make a new function :-)

visualapproach commented 5 years ago

Awesome! Cheers

BK-Poland commented 5 years ago

Do you need me to draw my wiring diagram? If I can help, please write. You can publish my idea in the official code.

visualapproach commented 5 years ago

Please do! I’ll update the code also. May I ask why you are using the old code? Legacy or doesn’t the newer code work in your application? Just curious.

BK-Poland commented 5 years ago

Why do I use the old code? It's simpler and it's enough for me. It's easier for me to analyze it. It meets all my requirements. BT works in my old Volvo. Thank you very much for that! When I was doing my BT-Volvo-Car according to your instructions and with your code it was still an old code. Maybe I will try a new code in the future. In what program do you draw your electrical diagrams?

visualapproach commented 5 years ago

Ok, I understand. If you are happy then no need to switch code :-) I've used Eagle. Supposedly you can import your PCBs into Fusion360 wich is an advantage. But it is a rather complicated program for what I do. I have also peeked at easyEDA, an online PCB editor.

BK-Poland commented 5 years ago

I remember that I had to draw a diagram. I'm working on one more improvement. I am waiting for a shipment (with some electronics parts) from China. A month or two. Please be patient. :-)

visualapproach commented 5 years ago

No worries. I will update the code also. Some day :-)