Arduino in car

Hi everyone, my 1st message so don’t be angry if I’m wrong.
So as title says I was thinking about putting my Latte Panda with integrated Arduino Leonardo in my car (Audi A3 8p) that actually got Audi Concert 2 as radio and it’s ok for me as sound quality and loudness.
So my project was initially based on this board to get all functions of pc and put in car without major mods on car itself. Initially I was writing on LP forum but 0 help from them. I can link the page if needed there will be some scratch code etc… So starting from beginning, plan is that or I will use on-board arduino leonardo or mini pro to communicate with radio, I need to communicate between them to enable aux and use of radio buttons to manage music, so for radio arduino will be as cd changer and will co op with player (winamp, wmp and so on). then I would like to connect also GPS antenna (actually bought an neo-6m gps but can not get it collab with my mini pro) maybe later will add function of reverse camera.
So after plan I got some sure things I managed so far:

Hardware:

  • LattePanda feat. x32bit proccessor and 2gig ram/7" touch display (30cm ribbon cable → annoying for placement, mean of running miles of cables or use pro mini and bt ad2c (or somethink like it) for communication)
  • GPS neo 6m
  • Arduino pro mini
  • 10000mah powerbank
  • 5V 3A power supply
  • other minor stuff

Software:

  • some scratches but all to bin I think
  • checked async and spi protocols, how they work etc…
  • all info about radio transmitting data itself and codes sent

Progress: none xD

So radio’s working on spi protocol radio → arduino

#include <SPI.h>

#define CDC_PREFIX1 0x53
#define CDC_PREFIX2 0x2C

#define CDC_END_CMD 0x14
#define CDC_PLAY 0xE4
#define CDC_NEXT 0xF8
#define CDC_PREV 0x78
#define CDC_SEEK_FWD 0xD8
#define CDC_SEEK_RWD 0x58
#define CDC_CD1 0x0C
#define CDC_CD2 0x8C
#define CDC_CD3 0x4C
#define CDC_CD4 0xCC
#define CDC_CD5 0x2C
#define CDC_CD6 0xAC
#define CDC_SCAN 0xA0
#define CDC_SFL 0x60
#define CDC_PLAY_NORMAL 0x08
 
#define MODE_PLAY 0xFF
#define MODE_SHFFL 0x55
#define MODE_SCAN 0x00


void Send(byte a,byte b,byte c,byte d,byte e,byte f,byte g,byte h);

void setup() {
  SPI.beginTransaction(SPISettings(62500,MSBFIRST,SPI_MODE0));
  SPI.begin();
  Send(0x74,0xBE,0xFE,0xFF,0xFF,0xFF,0x8F,0x7C);
}

void loop() {
}

void Send(byte a,byte b,byte c,byte d,byte e,byte f,byte g,byte h){
  byte Val;
  Val=SPI.transfer(a);
  delay(50);
  Val=SPI.transfer(b);
  delay(50);
  Val=SPI.transfer(c);
  delay(50);
  Val=SPI.transfer(d);
  delay(50);
  Val=SPI.transfer(e);
  delay(50);
  Val=SPI.transfer(f);
  delay(50);
  Val=SPI.transfer(g);
  delay(50);
  Val=SPI.transfer(h);
  delay(50);
  
}

and async arduino → radio

#include <Wire.h>
#include <SPI.h>
#include <stdio.h>

int val,vala,valb,valc;

void setup() 
{ 
  Serial.begin(1818); 
  while (!Serial) 
     {printf("not ready /n");}  // wait for Serial comms to become ready
  printf("reading /n");
  val = Serial.read();
  vala = Serial.read();
  valb = Serial.read();
  valc = Serial.read();
  printf("%i /n",&val);
  printf("%i /n",&vala);
  printf("%i /n",&valb);
  printf("%i /n",&valc);
}
void loop(){}

(I know that should be in loop and can create the function for it)
Created them as separate programs but in final versions will be as functions.

Now here a couple of links that I refer to how it should work:
Protocol: protocol is in german but most important is what is on images.
Codes, general info, etc… : vag.
Don’t wanted to use the code that’s inside that webpage, cuz not fun at all and is coded for rapsberry using phyton.
LP forum post: here

So in what I need a help?
Coding, still I’m not sure if it’s correct how I code the protocol, especially spi (bit 0 is shorter than longer?? what do you mean?? :o :o ) how to command the player, I used to program using dev compiler but now started to use visual studio due to wmp.h library that as far as I know is needed to communicate with the WMP.
Case for it, still don’t know the exact place. or where is centre speaker on dash or where are the lights overhead.
Maybe a bit with electric diagram for wiring PSU.

Electronic part: I managed to get circuit like power bank will do as standby battery when car is not driven, when car is on will get an input from 5v 3a power supply (or if you prefer step down something, don’t remember all names) while cranking thinked to use a capacitor (anyway I was calculating that I need 5V 2.5A that mean 12.5W for 4 seconds /more than needed to crank my car) and automate power supply.

Hopefully didn’t missed anything xD

So you described all the stuff you have but I didn't see a clear description of what the stuff is supposed to do. Control the car radio? Like just volume control or more than that?

  val = Serial.read();

vala = Serial.read();
  valb = Serial.read();
  valc = Serial.read();

So you read three values without first checking if there's anything to read. I don't know what while(!Serial) does on the Lattepanda but on Arduinos such as the Leonardo, that waits until there is a serial monitor connected on the PC side. It doesn't have anything to do with 'real' hardware serial.

Thanks for fast reply.
Ah sorry for being not clear at all :confused: , so I was about full control, I mean all playing functions like back forward next song next folder etc… however the volume control isn’t supported by radio, also all functions supported by radio you can find in spi code, ah ok, I was sure (of what I understood of arduino section about async and spi communication explained on forum) that while(!Serial) automatically check if something incoming or not, then how I need to check if got some impulses from Radio? Like very basic language
if(input == high){capture bytes}?
As said I’m not expert in programming as got the best teacher on the world of c++ in school (just to tell you he was still using C printf, scanf instead of e.g. cin, cout to show messages on screen), however I’m not familiar with serial communication as never done that (tried to understand it a bit with my gps module but as said without luck, I was using reference code and trying all possible frequencies from datasheet).
Anyway Radio need at start get sequence of 8 bytes to enable aux and “see the cd changer ← will call it CDC”, after that for each button pressed will receive the code and need to get sent the one as confirmation from CDC (2x 4 bytes async messages ).
So to be clear initially I want to build up the code to fully communicate LattePanda <-> Radio using on board Leonardo. Then will probably incorporate this to run with GPS module (another function) and probably some more options.
The Leonardo that is on board is actually permanently plugged to usb (com1) to LP, there is a possibility to upload firmata on Leonardo and manage it directly from Windows level or like stand alone (but permanently plugged to pc) Leonardo.

bump, looked on few tutorials and yt videos but still no clue how to get it communicated, however seen that might be helpful allserialsoftware to manage few serial ports "at once".

OK, the first thing to understand about serial is that it is:

S

L

O

W

The slowest Arduino can do thousands of instructions in between each character arriving. So you must always check that there's a character in the buffer before you attempt to read anything. You probably noticed Serial.available() in lots of examples? That's what that does.

"Firmata" is a thing for Arduinos which is only marginally useful. Did you mean "firmware"? Usually we just call it a "program" although sometimes we will accede to the Arduino way of calling it a "sketch".

If you are going to have multiple serial ports all talking at once then you are going to need an Arduino with more than one hardware serial port. The Teensy 3.2 is great for this and it's also very small. You can have one software serial on a Leonardo but there's only a limited number of pins that it can use.

Hi, as said I was about firmata that according to LP documents is forwarding the ports of Leonardo to windows, so instead of uploading the sketch file to arduino it’s read by windows, about the multiple ports, I thinking to get Radio I/O + GPS Input for now, so something like in line loop will be fine for me, with interrupt for radio commands, anyway reading or sending commands will be some milliseconds right? GPS, for me can be read also only every 0.5s
So the reading of radio commands will be like that

#include <Wire.h>
#include <SPI.h>
#include <stdio.h>

int val,vala,valb,valc;

void setup() 
{ 
  Serial.begin(1818); 
  while (!Serial.available()){
    printf("not ready /n");
  }                            // wait for Serial comms to become ready
  printf("reading /n");
  val = Serial.read();
  vala = Serial.read();
  valb = Serial.read();
  valc = Serial.read();
  printf("%i /n",&val);
  printf("%i /n",&vala);
  printf("%i /n",&valb);
  printf("%i /n",&valc);
}
void loop(){}

or if it was with checking incoming bytes will be like that:

#include <Wire.h>
#include <SPI.h>
#include <stdio.h>

byte val[10];                        //aray of bytes containing 10 bytes

void setup() 
{ 
  Serial.begin(1818); 
  while (!Serial.available()){      //meanwhile nothing incoming 
    printf("not ready /n");
  }                                 // wait for Serial comms to become ready
  printf("reading /n");
 int x =Serial.available();         //checking how mayny bytes in buffer
 while((x+1) > 0){                  //meanwhile x isn't null
   x--;                             //decreasing x so got right value
   val[x]=Serial.read();            //sending byte to array, array should receive MSB on pointer 0
 }
}
void loop(){}

right?

S

L

O

W

E

R

 while (!Serial.available()){

printf("not ready /n");
 }                            // wait for Serial comms to become ready
 printf("reading /n");
 val = Serial.read();
 vala = Serial.read();
 valb = Serial.read();
 valc = Serial.read();

When the first character is available, you can't read 4 characters. The second one is still coming down the pipe and won't get here for several thousand clock cycles.

Ok, so instead should be like:

#include <Wire.h>
#include <SPI.h>
#include <stdio.h>

byte val[10];                        //aray of bytes containing 10 bytes
int pointer;

void setup() 
{ 
  Serial.begin(1818);
  pointer=0;
}
void loop(){
  while (!Serial.available()){      //meanwhile nothing incoming 
    printf("not ready /n");         //normally will skip to next function
  }                                 // wait for Serial comms to become ready
  printf("reading /n");             //just for now to know that is working
 int x = Serial.available();        //checking how mayny bytes in buffer
 if(x){                             //if x different than null
     val[pointer]=Serial.read();    //sending byte to array
     pointer++;
 }
 if(pointer>3){                           //if got 4 bytes in array
    pointer=0;                            //reset of pointer
                                    //then send the command to player but don't know that commands
 }  
}

right?

Getting there.

The next thing you need to know about serial is that it's unreliable. A glitch like the A/C compressor coming on in the car might broadcast a spike that eliminates one bit in your communication. That one bit might be in the middle of a byte or it might be the start or stop bit, which would eliminate one entire byte.

Now you're waiting for 4 characters to come but you only got 3. The sender sends again. You get the first byte of that and add it to the 3 you have. That's an invalid command so you ignore it and go back to waiting for 4. But you will only get 3 this time around. How do you get back in sync?

All of these issues are covered in Serial Input Basics

I see, I will read it, however the 4 bytes are register register (and are always the same) command !command
so I can check it with pre loaded values from spi scetch like :

if((val[0]==CDC_PREFIX01)&&(val[1]==CDC_PREFIX02)){
  if( val[3] == 0xE4 ){                   //check values of byte 3 and 4 in this case with PLAY cmd in hex values
    if( val[4] ==  0x1B ){
      if(pause==0){
        play; pause=1;
      }                                     //play the music and trigger the pause var
      else{                               //that will declare as global
        pause; pause=0;
      }
    }
  }
}

That decodes the command but it doesn't get you back in sync if you have an extra char or a missing char.

Yes, but anyway I will get x bytes in, assuming that some of them will be lost, it won’t do anything, anyway it will be short cable that actually I can shield (using e.g. shielded rj-45 cable, it was STP isn’t?) so shouldn’t be any problems with interference, however still some bytes can be missed due to timing, and always need to be 4, so I can do an If (pointer < 3) (as comprehensive of index 0 to 3) check the command, else do other stuff.

O
W
E
R
S
L

If you wait for 4 chars, you will get 4 chars. You don't get any indication to tell you that there's another one in the pipe. To get back in sync, you need to identify the beginning of a valid message and discard all characters before that.

I see, is the same thing I seen on this code:

// 12 Mar 2014
// this works with ComArduino.py and ComArduinoA4e.rb
// this version uses a start marker 254 and an end marker of 255
//  it uses 253 as a special byte to be able to reproduce 253, 254 and 255
// it also sends data to the PC using the same system
//   if the number of bytes is 0 the PC will assume a debug string and just print it to the screen

//================

#define startMarker 254
#define endMarker 255
#define specialByte 253
#define maxMessage 16

// the program could be rewritten to use local variables instead of some of these globals
//  however globals make the code simpler
//  and simplify memory management

byte bytesRecvd = 0;
byte dataSentNum = 0; // the transmitted value of the number of bytes in the package i.e. the 2nd byte received
byte dataRecvCount = 0;


byte dataRecvd[maxMessage]; 
byte dataSend[maxMessage];  
byte tempBuffer[maxMessage];

byte dataSendCount = 0; // the number of 'real' bytes to be sent to the PC
byte dataTotalSend = 0; // the number of bytes to send to PC taking account of encoded bytes

boolean inProgress = false;
boolean startFound = false;
boolean allReceived = false;

//================

void setup() {
  pinMode(13, OUTPUT); // the onboard LED
  Serial.begin(57600);
  debugToPC("Arduino Ready from ArduinoPC.ino");
  
  delay(500);
  blinkLED(5); // just so we know it's alive
}

//================

void loop() {

  getSerialData();
  
  processData();

}

//================

void getSerialData() {

     // Receives data into tempBuffer[]
     //   saves the number of bytes that the PC said it sent - which will be in tempBuffer[1]
     //   uses decodeHighBytes() to copy data from tempBuffer to dataRecvd[]
     
     // the Arduino program will use the data it finds in dataRecvd[]

  if(Serial.available() > 0) {

    byte x = Serial.read();
    if (x == startMarker) { 
      bytesRecvd = 0; 
      inProgress = true;
      // blinkLED(2);
      // debugToPC("start received");
    }
      
    if(inProgress) {
      tempBuffer[bytesRecvd] = x;
      bytesRecvd ++;
    }

    if (x == endMarker) {
      inProgress = false;
      allReceived = true;
      
        // save the number of bytes that were sent
      dataSentNum = tempBuffer[1];
  
      decodeHighBytes();
    }
  }
}

//============================

void processData() {

    // processes the data that is in dataRecvd[]

  if (allReceived) {
  
      // for demonstration just copy dataRecvd to dataSend
    dataSendCount = dataRecvCount;
    for (byte n = 0; n < dataRecvCount; n++) {
       dataSend[n] = dataRecvd[n];
    }

    dataToPC();

    delay(100);
    allReceived = false; 
  }
}

//============================

void decodeHighBytes() {

  //  copies to dataRecvd[] only the data bytes i.e. excluding the marker bytes and the count byte
  //  and converts any bytes of 253 etc into the intended numbers
  //  Note that bytesRecvd is the total of all the bytes including the markers
  dataRecvCount = 0;
  for (byte n = 2; n < bytesRecvd - 1 ; n++) { // 2 skips the start marker and the count byte, -1 omits the end marker
    byte x = tempBuffer[n];
    if (x == specialByte) {
       // debugToPC("FoundSpecialByte");
       n++;
       x = x + tempBuffer[n];
    }
    dataRecvd[dataRecvCount] = x;
    dataRecvCount ++;
  }
}

//====================

void dataToPC() {

      // expects to find data in dataSend[]
      //   uses encodeHighBytes() to copy data to tempBuffer
      //   sends data to PC from tempBuffer
    encodeHighBytes();

    Serial.write(startMarker);
    Serial.write(dataSendCount);
    Serial.write(tempBuffer, dataTotalSend);
    Serial.write(endMarker);
}

//============================

void encodeHighBytes() {
  // Copies to temBuffer[] all of the data in dataSend[]
  //  and converts any bytes of 253 or more into a pair of bytes, 253 0, 253 1 or 253 2 as appropriate
  dataTotalSend = 0;
  for (byte n = 0; n < dataSendCount; n++) {
    if (dataSend[n] >= specialByte) {
      tempBuffer[dataTotalSend] = specialByte;
      dataTotalSend++;
      tempBuffer[dataTotalSend] = dataSend[n] - specialByte;
    }
    else {
      tempBuffer[dataTotalSend] = dataSend[n];
    }
    dataTotalSend++;
  }
}

//=========================

void debugToPC( char arr[]) {
    byte nb = 0;
    Serial.write(startMarker);
    Serial.write(nb);
    Serial.print(arr);
    Serial.write(endMarker);
}

//=========================

void debugToPC( byte num) {
    byte nb = 0;
    Serial.write(startMarker);
    Serial.write(nb);
    Serial.print(num);
    Serial.write(endMarker);
}

//=========================

void blinkLED(byte numBlinks) {
    for (byte n = 0; n < numBlinks; n ++) {
      digitalWrite(13, HIGH);
      delay(200);
      digitalWrite(13, LOW);
      delay(200);
    }
}

just in my case should be stopped on end byte and the length should be of 2 bytes (+ start and end bytes) + don’t need part of sending it back to pc or de/coding.