Sunday,15-May-2016

Xantia Mk2 Instrument Panel Backlight Bluetooth controlled

BG Описано на български: тук
EN Hi,
In that topic will try to describe one idea for my Xantia instrument cluster back-light, but it can be used/set to any other car or to be used for other project as well.
Parts been used : WS2812B LEDs , Arduino Nano, HC-06 serial(with board) and some small electronic elements.
But before start any proper description, let me say that all credits goes to: josh.com Without his clean and simple code that will be bad going project, or lets say with Adafruit library it come little overweight.

Electrical diagram is like that:
dash_ws2812_3.jpg
As you see nothing special. If you need to know what is the RPM signal wire number check my other topic about “Trip computer and Backlight ”
LM7805 need heat sink because otherwise will get too hot! Keep that in mind! Hardware look like that(with one test ring)
dash_ws2812_4.jpg

and pretest in the car:

dash_ws2812_5.jpg

I used normal bulb holder and first cut the bottom of it, second the metal tweezers that keep the bulb bottom and that make room for the cable to go. After, cut the plastic edges on top to the level of first metal pin. Solider the LED and add shrink tube. That give me easy way of glue it(red circle) and last add 100nF SMD capacitor:

dash_ws2812_8.jpg dash_ws2812_7.jpg

Whole loom is like that:

dash_ws2812_9.jpg

And finally on spot:

dash_ws2812_6.jpg

Code:

//Based on : http://wp.josh.com/2014/05/11/ws2812-neopixels-made-easy/ 
#include "EEPROM.h"
#define PIXELS 7  // Number of pixels in the string

//digital 13
#define PIXEL_PORT  PORTB  // Port of the pin the pixels are connected to 
#define PIXEL_DDR   DDRB   // Port of the pin the pixels are connected to 
#define PIXEL_BIT   5      // Bit of the pin the pixels are connected to 

#define T1H  900    // Width of a 1 bit in ns 
#define T1L  400    // Width of a 1 bit in ns 
#define T0H  400    // Width of a 0 bit in ns 
#define T0L  900    // Width of a 0 bit in ns 
#define RES 6000    // Width of the low gap between bits to cause a frame to latch 
// Here are some convience defines for using nanoseconds specs to generate actual CPU delays 
#define NS_PER_SEC (1000000000L)  // Note that this has to be SIGNED since we want to be able to check for negative values of derivatives 
#define CYCLES_PER_SEC (F_CPU) 
#define NS_PER_CYCLE ( NS_PER_SEC / CYCLES_PER_SEC ) 
#define NS_TO_CYCLES(n) ( (n) / NS_PER_CYCLE ) 

void ledsetup() { 
   bitSet( PIXEL_DDR , PIXEL_BIT ); 
 } 

void sendBit( boolean bitVal ) { 
    if (  bitVal ) {				    // 0 bit 
		asm volatile ( 
			"sbi %[port], %[bit] \n\t"	// Set the output bit 
			".rept %[onCycles] \n\t"    // Execute NOPs to delay exactly the specified number of cycles 
			"nop \n\t" 
			".endr \n\t" 
			"cbi %[port], %[bit] \n\t"  // Clear the output bit 
			".rept %[offCycles] \n\t"   // Execute NOPs to delay exactly the specified number of cycles 
			"nop \n\t" 
			".endr \n\t" 
			:: 
			[port]		"I" (_SFR_IO_ADDR(PIXEL_PORT)), 
			[bit]		"I" (PIXEL_BIT), 
			[onCycles]	"I" (NS_TO_CYCLES(T1H) - 2),    // 1-bit width less overhead  for the actual bit setting, note that this delay could be longer and everything would still work 
			[offCycles] 	"I" (NS_TO_CYCLES(T1L) - 2)	// Minimum interbit delay. Note that we probably don't need this at all since the loop overhead will be enough, but here for correctness 

		); 
                                   
    } else {					// 1 bit 

		// ************************************************************************** 
		// This line is really the only tight goldilocks timing in the whole program! 
		// ************************************************************************** 
		asm volatile ( 
			"sbi %[port], %[bit] \n\t"	// Set the output bit 
			".rept %[onCycles] \n\t"	// Now timing actually matters. The 0-bit must be long enough to be detected but not too long or it will be a 1-bit 
			"nop \n\t"                  // Execute NOPs to delay exactly the specified number of cycles 
			".endr \n\t"                
			"cbi %[port], %[bit] \n\t" // Clear the output bit 
			".rept %[offCycles] \n\t"  // Execute NOPs to delay exactly the specified number of cycles 
			"nop \n\t" 
			".endr \n\t" 
			:: 
			[port]		"I" (_SFR_IO_ADDR(PIXEL_PORT)), 
			[bit]		"I" (PIXEL_BIT), 
			[onCycles]	"I" (NS_TO_CYCLES(T0H) - 2), 
			[offCycles]	"I" (NS_TO_CYCLES(T0L) - 2) 

		); 
       
    } 
 }   

void sendByte( long byte ) { 
      for( int bits = 0 ; bits < 8 ; bits++ ) { 
          sendBit( bitRead( byte , 7 ) );  // Neopixel wants bit in highest-to-lowest order 
                                        // so send highest bit (bit #7 in an 8-bit byte since they start at 0) 
       byte <<= 1;                      // and then shift left so bit 6 moves into 7, 5 moves into 6, etc 
     }            
 }  

void sendPixel(int r,int g , int b )  {
// Neopixel wants colors in green then red then blue order   
      sendByte(g);           
      sendByte(r); 
      sendByte(b); 
} 

// Just wait long enough without sending any bots to cause the pixels to latch and display the last sent frame 
 void show() { 
 	delayMicroseconds((RES/1000UL) + 1);				// Round up since the delay must be <em>at</em>least_ this long (too short might not work, too long not a problem) 
 } 

 void colorSpinCW(int rb , int gb, int bb, int waitb ) { 
   for(int i=0; i<PIXELS; i++ ) { 
      cli(); 
           int p=0;
           while(p< PIXELS){
                if(p == i){
                      sendPixel(rb,gb,bb);
                }else{  
                      sendPixel(0,0,0);
                }               
             p++;
           }
      sei(); 
      show(); 
      delay(waitb); 
    } 
 } 
 void colorSpinCCW(int rc , int gc, int bc, int waitc ) { 
   for(int i=PIXELS ; i>=0; i--) { 
       cli(); 
           int p=0;
           while(p<PIXELS){
                if(p == i){
                     sendPixel(rc,gc,bc);
                }else{  
                     sendPixel(0,0,0);
            }               
             p++;
           }
      sei(); 
      show(); 
      delay(waitc); 
    } 
 } 
 
void flipFlop(){
    colorSpinCW(255, 255, 0, 40); 
   colorSpinCCW(0, 255, 255, 60); 
    colorSpinCW(255, 0, 255, 80); 
   colorSpinCCW(0, 0, 0, 150); 
} 
 
//P0-fuel/P1-RPM red/P2-RPM top/P3-KM/RPM common/P4-KMH top/P5-KMH left bottom/P6-temp - type: R,G,B

int dasha[21]={ //--- full green STANDARD one --- (1)  
255,255,0,//P0 - fuel
  0,255,0,//P1 - RPM red zone
255,255,0,//P2 - rpm top zone
255,255,0,//P3 - KM/H RPM common
255,255,0,//P4 - KMH top
255,255,0,//P5 - KMH left bottom
255,255,0 //P6 - temp gauge
};

int dashb[21]={ //------ full blue ------- (2)
255,0,255, //P0 - fuel
 40,0,255, //P1 - RPM red zone
160,0,255, //P2 - rpm top zone
255,0,255, //P3 - KM/H RPM common
255,0,255, //P4 - KMH top
255,0,255, //P5 - KMH left bottom
255,0,255  //P6 - temp gauge
};

int dashc[21]={ //---- blue top / green bottom (3) 
255,255,0, //P0 - fuel
255,255,0, //P1 - RPM red zone
255,0,255, //P2 - rpm top zone
255,255,0, //P3 - KM/H RPM common
255,0,255, //P4 - KMH top
255,255,0, //P5 - KMH left bottom
255,255,0//P6 - temp gauge
};  

int dashd[21]={ //---- blue bottom / green top (4)
180,0,255,//P0 - fuel
127,0,255,//P1 - RPM red zone
255,255,0,//P2 - rpm top zone
255,0,255,//P3 - KM/H RPM common
255,255,0,//P4 - KMH top
255,0,255,//P5 - KMH left bottom
180,0,255 //P6 - temp gauge
};

int dashe[21]={ //-------- blue Fuel/Temp - other green (5)
255,0,255, //P0 - fuel
  0,160,0, //P1 - RPM red zone
255,160,0, //P2 - rpm top zone
255,160,0, //P3 - KM/H RPM common
255,160,0, //P4 - KMH top
255,160,0, //P5 - KMH left bottom
255,0,255  //P6 - temp gauge
};

int dashf[21]={ //---- night mode -------- (6)
180,20,0,//P0 - fuel
100,20,0,//P1 - RPM red zone
180,20,0,//P2 - rpm top zone
180,20,0,//P3 - KM/H RPM common
180,20,0,//P4 - KMH top
180,20,0,//P5 - KMH left bottom
180,30,0 //P6 - temp gauge
};

long previousMillis;
 int tmpDash[21];
 int pixPrnt[21];
 int hz=0;
 int devider; 
 int mode=1; 
 int tmpMode = 1;
 int printOut;
boolean nightMode;
boolean rpmColChange;
long int pulseHigh,pulseLow;
byte rpmLogic;

long int mili =0;

void setup() { 
     pinMode(2, INPUT); // rpm-Low
     pinMode(3, INPUT); // rpm-High

     ledsetup();
     devider = EEPROM.read(3);
     if(devider< 1 || devider >2){
          EEPROM.write(3,1); //set the devider
     } 
delay(1000); // to reduce error when turn ON ignition key.And is more fun
      arrayFit();  
           flyIn();
nightMode = false;
   mode = 1;
tmpMode = 1;
rpmLogic = B0000;
   Serial.begin(38400);
   Serial.println("DASH is connected!");  
  if(devider == 1){
     Serial.println("Bright. is 100%");  
  }else if(devider == 2){
     Serial.println("Bright. is 50%");  
  }  
}//end SETUP 

void loop(){
while(Serial.available()>0) {
          char serialBuffer = Serial.read();
          
          if(serialBuffer != '#'){
                if(serialBuffer == 'a'){ // dash 1 green
                    printOut = 1;
                }
                else if(serialBuffer == 'b'){ //dash 2 blue passat
                    printOut = 2;
                }
               else if(serialBuffer == 'c'){ //dash 3  blugree
                     printOut = 3;
                }
               else if(serialBuffer == 'd'){ //dash 4 greboll
                     printOut = 4;
                }
               else if(serialBuffer == 'e'){ //dash 5 mix
                     printOut = 5;
                } 
               else if(serialBuffer == 'f'){ //dash nightmode
                     printOut = 6;
                } 
               else if(serialBuffer == 'g'){ //dash 50%
                     printOut = 7;
                } 
               else if(serialBuffer == 'h'){ //dash 100%
                     printOut = 8;
                } 

          }          
          if(serialBuffer == '#'){ //run command
                 switch(printOut){
                     case 1: 
                        Serial.println("DASH:Standard green");
                          EEPROM.write(0,1);
                          if(EEPROM.read(10) == 0){EEPROM.write(10,1);} 
                          arrayFit();
                          flipFlop();
                          flyIn();
                          nightMode = false;
                     break;
                     case 2: 
                        Serial.println("DASH:Passat Blue");
                          EEPROM.write(0,2);
                          if(EEPROM.read(10) == 0){EEPROM.write(10,1);} 
                          arrayFit();
                          flipFlop();
                          flyIn();
                          nightMode = false;                          
                     break;
                     case 3: 
                        Serial.println("DASH:Blue top&green");
                          EEPROM.write(0,3);
                          if(EEPROM.read(10) == 0){EEPROM.write(10,1);} 
                          arrayFit();
                          flipFlop();
                          flyIn();
                          nightMode = false;                          
                     break;
                     case 4: 
                        Serial.println("DASH:Green top&blue");
                          EEPROM.write(0,4);
                          if(EEPROM.read(10) == 0){EEPROM.write(10,1);} 
                          arrayFit();
                          flipFlop();
                          flyIn();
                          nightMode = false;                          
                     break;
                     case 5: 
                        Serial.println("DASH:Mix");
                          EEPROM.write(0,5);
                          if(EEPROM.read(10) == 0){EEPROM.write(10,1);} 
                          arrayFit();
                          flipFlop();
                          flyIn();
                          nightMode = false;                          
                     break;
                     case 6: 
                        Serial.println("DASH:Night mode");
                          EEPROM.write(0,6);
                          if(EEPROM.read(10) == 0){EEPROM.write(10,1);} 
                          arrayFit();
                          flipFlop();
                          flyIn();
                          nightMode = true;
                     break;
                     case 7: 
                        Serial.println("DASH:50% mode");
                         EEPROM.write(3,2); //set the devider
                         arrayFit();
                         diodeUpdatePercent();
                     break;
                     case 8: 
                        Serial.println("DASH:100% mode");
                         EEPROM.write(3,1); //set the devider
                         arrayFit();
                         diodeUpdatePercent();
                     break;
                 }  
                 printOut=0;
                   
         }
}
//------------------------------------------------- RPM counter -------
  pulseLow = pulseIn(2,LOW);
 pulseHigh = pulseIn(3,HIGH);

float tmpRpm = (pulseHigh+pulseLow)/1000.0; 
float tmpRpmA = (1000.0/tmpRpm)+0.5;  
      hz = tmpRpmA;  // return rpm as Hz
//==========================================================
if(rpmColChange == true){
            devider = EEPROM.read(3);
    if(hz<100){  //<3000
         if(bitRead(rpmLogic,0)==0){
             for(int u=0;u<21;u++){
                  pixPrnt[u] = tmpDash[u];
             }
             diodeUpdate();
             rpmLogic = B0001;
         }
    } 
    else if(hz>100 && hz<150){ //3000-4500 
       if(bitRead(rpmLogic,1)==0){
           pixPrnt[0] = tmpDash[0];
           pixPrnt[1] = tmpDash[1];
           pixPrnt[2] = tmpDash[2];
           pixPrnt[3] = 180/devider;
           pixPrnt[4] = 50/devider;
           pixPrnt[5] = 220/devider;
           pixPrnt[6] = 255/devider;
           pixPrnt[7] = 0;
           pixPrnt[8] = 100/devider;
         for(int v=9;v<22;v++){
           pixPrnt[v] = tmpDash[v];
         }
        diodeUpdate();
            rpmLogic = B0010;
        }
    }
    else if(hz>150 && hz<186){ //4500-5580
      if(bitRead(rpmLogic,2)==0){
          pixPrnt[0] = tmpDash[0];
          pixPrnt[1] = tmpDash[1];
          pixPrnt[2] = tmpDash[2];    
          pixPrnt[3] = 120/devider;
          pixPrnt[4] = 0;
          pixPrnt[5] = 220/devider;
          pixPrnt[6] = 255/devider;
          pixPrnt[7] = 0;
          pixPrnt[8] = 200/devider;
        for(int vw=9;vw<22;vw++){
          pixPrnt[vw] = tmpDash[vw];
        }
            diodeUpdate();
            rpmLogic = B0100;
      }
    }
    else if(hz>186){         //>5580
      if(bitRead(rpmLogic,3)==0){
          pixPrnt[0] = tmpDash[0];
          pixPrnt[1] = tmpDash[1];
          pixPrnt[2] = tmpDash[2];    
          pixPrnt[3] = 255/devider;
          pixPrnt[4] = 0;
          pixPrnt[5] = 150/devider;
          pixPrnt[6] = 255/devider;
          pixPrnt[7] = 0;
          pixPrnt[8] = 255/devider;
         for(int vw=9;vw<22;vw++){
          pixPrnt[vw] = tmpDash[vw];
         }
           diodeUpdate();
           rpmLogic = B1000;
      }
    }
}
//========================================================
}//end loop

void flyIn(){
      int boom[21];
      int cc = 8;
      while(cc >= 1){
          for(int vv=0;vv<21;vv++){
              if(tmpDash[vv] != 0){
                   boom[vv] = tmpDash[vv]/cc;
              }else{
                   boom[vv] = 0;
              }
          }
        cli();  
        for(int x=0;x<21;x+=3){
             sendPixel(boom[x],boom[x+1],boom[x+2]);
        }
        sei();
       show();
       delay(150);
       cc=cc/2;
     } 
  if(EEPROM.read(0)==1 || EEPROM.read(0)==5){
          rpmColChange = true;
  }else{
         rpmColChange = false;
  }     
}

void diodeUpdate(){
   cli();
   for(int x=0;x<21;x+=3){
        sendPixel(pixPrnt[x],pixPrnt[x+1],pixPrnt[x+2]);
   }
   sei();
   show();
}

void diodeUpdatePercent(){
   cli();
   for(int ax=0;ax<21;ax+=3){
        sendPixel(tmpDash[ax],tmpDash[ax+1],tmpDash[ax+2]);
   }
   sei();
   show();
}

 

void arrayFit(){
   int vas = EEPROM.read(0);
   devider = EEPROM.read(3);
   switch(vas){
   case 1:
         for(int a=0;a<21;a++){ tmpDash[a] = dasha[a]/devider ;}
    break;
   case 2:
         for(int b=0;b<21;b++){ tmpDash[b] = dashb[b]/devider ;}
    break;
   case 3:
         for(int c=0;c<21;c++){ tmpDash[c] = dashc[c]/devider ;}
    break;
   case 4:
         for(int d=0;d<21;d++){ tmpDash[d] = dashd[d]/devider ;}
    break;
   case 5:
         for(int e=0;e<21;e++){ tmpDash[e] = dashe[e]/devider ;}
    break;
   case 6:
         for(int f=0;f<21;f++){ tmpDash[f] = dashf[f]/devider ;}
    break;
    }
}

And how all that work with video:

More about how to set commands in BT Terminal APP can be found on
my site in article: RGB LED control via Arduino and BT serial.