Проверялось на (заточено на) Daewoo Nexia N150, но должно работать на любой машине с электронным спидометром (датчик скорости — 6 импульсов на метр, вроде как стандарт). Фотографии немного мутные (любительские):


а) мужским
б) женским
Голоса синтезированы на компьютере, записаны в виде набора mp3-файлов на микроСД карту. Это значит, что вы можете их поменять или записать.
2. Настройки выполняются с ПДУ от телевизора (или любого другого с достаточным количеством кнопок, настоятельно рекомендую ПДУ с кнопками 0-9). Настройки запоминаются в момент изменения.
3. В случае отказа/потери ПДУ, извещатель может быть оперативно переключен на режимы «нет звуков» или «нет звуков, кроме ручника». Для этого используется тройное включение габаритов (идея использовать мигание габаритами в качестве управляющего сигнала для настройки спёрта с какого-то устройства регулировки яркости ДХО).
— Включен дальний свет (мигание дальним игнорируется, предупреждает один раз после включения, далее не напоминает, пока дальний не переключат)
— Включите ходовые огни (если начато движение без включенных габаритов, у меня ДХО и ближний не работают, если не включены габариты).
— Напряжение бортовой сети (точность до десятой доли вольта)
— Превышение порога скорости 40, 50,… 120, 130 км/ч.
— Понижение порога скорости 40, 50,… 120, 130 км/ч.
— Изменение громкости, переключение голоса, отключение/включение каждого из озвучиваемых событий (включение вольтметра вызывает одновременно озвучивание напряжения).
Видео с демонтрацией:
2. К лампочкам — ручника, дальнего света, подсветки приборов (габариты).
Согласование уровней сделано довольно просто — выводы ардуины подтянуты внутренним резистором в «1», а сигналы 12В уровня из приборной панели идут через диоды. Когда с панели идет +12, диод закрыт — на ардуине «лог.1». Когда сигнал с панели идет землёй — диод открыт, внутренний подтягивающий резистор игнорируется, на выводе ардуино «лог.0».
Вольтметр — просто резистивный делитель на аналоговый вход. Отношение примерно 1:13, из того расчета, что при максимальном напряжении бортовой сети 16В (аварийный режим) АЦП достигнет своего предела при опорном сигнале 1,2В.
2. Модуль mp3 — DFplayer mini
Данный модуль в интернет-схемах подключается по tx/rx сигналам, я добавил анализ сигнала busy, для поимки момента окончания воспроизведения звука, чтоб новое оповещение не перебивало текущее, а вставало в очередь.
3. Усилитель PAM8403 (необязательный элемент)
4. Макетная плата, 5 резисторов (2 на АЦП вольтметра, и 3 на выводы DFplayer), 4 диода (согласование с +12В), а также пара конденсаторов и микросхема стабилизатора питания +5В типа LM7805 (у меня стоит DC/DC преобразователь AMSR 7805, потому что был в запасе и не греется), пъезозуммер (пищит при переполнении очереди мр3-файлов, больше для поиска ошибок при отладке), разъемы для подключения ИК-датчика (джек 2.5 в моем случае, но это непринципиально) и динамика (я взял RCA), ну и разъем для панели приборов (я взял IDC10, с защелками на папе — достаточно легкий и многоконтактный)
#include <Wire.h> 
//#include <LiquidCrystal_I2C.h>
#include <IRremote.h>
#include <EEPROM.h>
#include <SoftwareSerial.h>
#include <DFPlayer_Mini_Mp3.h>
int RECV_PIN = 6; //пин подключения IR приёмника
IRrecv irrecv(RECV_PIN);
decode_results results;
byte cfg[16] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
byte mp3stek[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
byte mp3stekpos = 0;
volatile byte busy = 1; //mp3 busy  0=play
volatile byte softbusy = 0; 
volatile byte nomer_mp3 = 1; 
volatile unsigned long micros_sp = 0;
volatile byte sz = 0; //счетчик обнуления
volatile unsigned int sp = 0; //скорость
volatile unsigned int sp20 = 0; //скорость*10
volatile boolean st = false; //триггер
volatile byte ruka = 1; //ручник
volatile byte dsvet = 0; //д.свет
volatile byte gaba = 0; //габариты
volatile byte lastspeed = 0; //последняя скорость
volatile byte lastdsvet = 0; //переменная для отсчета д.света
volatile byte lastgaba = 0; //переменная для отсчета DHO
volatile byte lastgabaonoff = 0; //переменная для отсчета щелчкоф габаритов
volatile word gabaonofftimer = 0; //переменная для отсчета времени щелчкоф габаритов
volatile byte gabaonoffcounter = 0; //переменная для отсчета времени щелчкоф габаритов
volatile byte speed40 = 38; // 40
volatile byte speed60 = 58; // 60
volatile byte speed80 = 78; // 80
volatile byte speed110 = 108; // 110
volatile byte unspeed40 = 36; // no 40
volatile byte unspeed60 = 56; // no 60
volatile byte unspeed80 = 76; // no 80
volatile byte unspeed110 = 106; // no 110
volatile byte speed50 = 48; // 50
volatile byte unspeed50 = 46; // no 50
volatile byte speed70 = 68; // 70
volatile byte unspeed70 = 66; // no 70
volatile byte speed90 = 88; // 90
volatile byte unspeed90 = 86; // no 90
volatile byte speed100 = 98; // 100
volatile byte unspeed100 = 96; // no 100
volatile byte speed120 = 118; // 120
volatile byte unspeed120 = 116; // no 120
volatile byte speed130 = 128; // 130
volatile byte unspeed130 = 126; // no 130
volatile byte s40 = 0;
volatile byte s50 = 0;
volatile byte s60 = 0;
volatile byte s70 = 0;
volatile byte s80 = 0;
volatile byte s90 = 0;
volatile byte s100 = 0;
volatile byte s110 = 0;
volatile byte s120 = 0;
volatile byte s130 = 0;
//LiquidCrystal_I2C lcd(0x3F, 16, 2);
float sensorValue = 0; //V
float outputValue = 0;  //V
void setup()
{
       Serial.begin(9600);
       Serial.println("start programm NEXIA 1.0");
       //start IR reciver 
        irrecv.enableIRIn(); // Start the receiver
	// initialize the LCD
	//lcd.begin();
	// Turn on the blacklight and print a message.
	//lcd.backlight();
//	lcd.print("Hello, world!");
        //tone (17,3000); //тестовый сигнал на выводе D3
        pinMode(2, INPUT);	//  пин будет работать на вход speed
        digitalWrite(2, HIGH);//подключаем подтягивающий резистор
        pinMode(3, INPUT);	//  пин будет работать на вход
        digitalWrite(3, HIGH);//подключаем подтягивающий резистор
        pinMode(4, INPUT);	//  пин будет работать на вход
        digitalWrite(4, HIGH);//подключаем подтягивающий резистор
        pinMode(5, INPUT);	//  пин будет работать на вход
        digitalWrite(5, HIGH);//подключаем подтягивающий резистор
//      pinMode(13, OUTPUT);	// 
        lastgabaonoff = digitalRead(5); //gaba
        attachInterrupt(0,speedometr, RISING);
        cfg[0]=EEPROM.read(5); //0=all sound  1=no sound(ruka) 2=no sound all
        cfg[1]=EEPROM.read(6); //volume mp3 0..30
        cfg[2]=EEPROM.read(7); //0 = dsvet sound? 1 = no dsvet sound
        cfg[3]=EEPROM.read(8); //0 = 40sound
        cfg[4]=EEPROM.read(9); //0 = 60sound
        cfg[5]=EEPROM.read(10);//0 = 80sound
        cfg[6]=EEPROM.read(11);//0 = 110 sound
        cfg[7]=EEPROM.read(12);//0 = Voltmetr sound 
        cfg[8]=EEPROM.read(13);//0 = DHO sound
        cfg[9]=EEPROM.read(14);//0 = 50sound
        cfg[10]=EEPROM.read(15);//0 = 70sound
        cfg[11]=EEPROM.read(16);//0 = 90sound
        cfg[12]=EEPROM.read(17);//0 = 100sound
        cfg[13]=EEPROM.read(18);//0 = 120sound
        cfg[14]=EEPROM.read(19);//0 = 130sound
        cfg[15]=EEPROM.read(20);//0 = голос0, 1=голос1
        //управление мр3
        pinMode(7, INPUT);  //busy
        mp3_set_serial (Serial);    
//      mp3_set_volume (25);
        mp3setvol();
        delay (100); 
        add_mp3(32); //включен голосовой режим
       // Вольтметр
       analogReference (INTERNAL);
    //    voltmeter();
        if (cfg[7]==0){voltmetertest();} // вольтметр
}
void loop()
{
  //индикация режимов датчиков
    dsvet = digitalRead(3); // Получить состояние пина : 
//  if (dsvet==1) {  lcd.setCursor(3, 0);  lcd.print("DD ");}
//  else{lcd.setCursor(3, 0); lcd.print("_s ");};
  
      ruka = digitalRead(4); // Получить состояние пина : 
//  if (ruka==1) {  lcd.setCursor(0, 0);  lcd.print("_r ");}
//  else{lcd.setCursor(0, 0); lcd.print("RR ");};
  
      gaba = digitalRead(5); // Получить состояние пина : 
//  if (gaba==1) {  lcd.setCursor(6, 0);  lcd.print("GG");}
//  else{lcd.setCursor(6, 0); lcd.print("_g");};
//voltmetertest(); //тест вольтметра
//    digitalWrite(13, !digitalRead(2));
  
  //измерение скорости
//  lcd.setCursor(10, 0);
//  lcd.print("     ");
//  lcd.setCursor(10, 0);
//  lcd.print(sp);
//  lcd.setCursor(0, 1);
//  lcd.print("     ");
//  lcd.setCursor(0, 1);
//  lcd.print(sp20);
  if (sz != 0){sz--;}else{sp20=0;sp=0;};
  delay(50);
  //  sdisplay();
//действия
gabamute(); //управление звуком по тройному щелчку габаритами
if ((sp20>0)&(ruka==0)) {rukabeep();}//пищит ручник
else
{ //обрабатываем д.свет и скорость только если не пищит ручник
if (dsvet==1) { if ( lastdsvet<200){lastdsvet=lastdsvet+1;};}
if (dsvet==1) { if ((lastdsvet>35)&(lastdsvet<38)){svetbeep();}; }
if (dsvet==0) {lastdsvet=0;};
//конец обработки д. света
//предупреждение о дхо
if ((gaba==0)&&(sp20>0)) { if ( lastgaba<150){lastgaba=lastgaba+1;};}
if ((gaba==0)&&(sp20>0)) { if ((lastgaba>20)&(lastgaba<23)){DHO_beep();}; }
if (gaba==1) {lastgaba=0;};
if (sp20==0) {lastgaba=0;};
//обрабатываем контроль превышения скорости 40, 60, 80, 110
if ((lastspeed<speed40)&(sp>=speed40)) {
 if (s40<1) { svet1beep(); s40=1; }};
if ((lastspeed>=unspeed40)&(sp<unspeed40)) {
 if (s40>0) { nsvet1beep(); s40=0; }};
if ((lastspeed<speed50)&(sp>=speed50)) {
 if (s50<1) { svet50beep(); s50=1; }};
if ((lastspeed>=unspeed50)&(sp<unspeed50)) {
 if (s50>0) { nsvet50beep(); s50=0; }};
if ((lastspeed<speed60)&(sp>=speed60)) {
 if (s60<1) { svet2beep(); s60=1; }};
if ((lastspeed>=unspeed60)&(sp<unspeed60)) {
 if (s60>0) { nsvet2beep(); s60=0; }};
if ((lastspeed<speed70)&(sp>=speed70)) {
 if (s70<1) { svet70beep(); s70=1; }}; 
if ((lastspeed>=unspeed70)&(sp<unspeed70)) {
 if (s70>0) { nsvet70beep(); s70=0; }}; //костыль. иначе на 66км/ч без конца твердит "нет 70"почему - непонятно. поставил костыли на все скорости.
if ((lastspeed<speed80)&(sp>=speed80)) {
 if (s80<1) { svet3beep(); s80=1; }};
if ((lastspeed>=unspeed80)&(sp<unspeed80)) {
 if (s80>0) { nsvet3beep(); s80=0; }};
if ((lastspeed<speed90)&(sp>=speed90)) {
 if (s90<1) { svet90beep(); s90=1; }};
if ((lastspeed>=unspeed90)&(sp<unspeed90)) {
 if (s90>0) { nsvet90beep(); s90=0; }};
if ((lastspeed<speed100)&(sp>=speed100)) {
 if (s100<1) { svet100beep(); s100=1; }};
if ((lastspeed>=unspeed100)&(sp<unspeed100)) {
 if (s100>0) { nsvet100beep(); s100=0; }};
if ((lastspeed<speed110)&(sp>=speed110)) {
 if (s110<1) { svet4beep(); s110=1; }};
if ((lastspeed>=unspeed110)&(sp<unspeed110)) {
 if (s110>0) { nsvet4beep(); s110=0; }};
if ((lastspeed<speed120)&(sp>=speed120)) {
 if (s120<1) { svet120beep(); s120=1; }};
if ((lastspeed>=unspeed120)&(sp<unspeed120)) {
 if (s120>0) { nsvet120beep(); s120=0; }};
if ((lastspeed<speed130)&(sp>=speed130)) {
 if (s130<1) { svet130beep(); s130=1; }};
if ((lastspeed>=unspeed130)&(sp<unspeed130)) {
 if (s130>0) { nsvet130beep(); s130=0; }};
lastspeed=sp;
delay(50);
//конец обработки превышения скорости
}//конец обработки при молчащем рчнике
// voltmeter();
  
//обработка очереди мр3
if (mp3stekpos != 0) 
{ busy = digitalRead(7); // Получить состояние пина 7: 
  if (softbusy > 0) softbusy = softbusy-1;//предварительная задержка, т.к. busy возникает не сразу
  if ((busy>0) && (softbusy ==0)) //если не занят мр3
  {mp3stekpos = mp3stekpos-1;
   nomer_mp3 = mp3stek[1];
   if (cfg[15]==0) {mp3_play (mp3stek[1]);} else {mp3_play (mp3stek[1]+100);} //выбор голоса
   softbusy = 25;   delay(50);
   for (int i=1; i < 9; i++){mp3stek[i]=mp3stek[i+1]; }
   mp3stek[9]= 0;  };}
//обработка ПДУ сигналов
/**/ if (irrecv.decode(&results)) 
  {
    Serial.println(results.value);
//    lcd.setCursor(6, 1); 
//    lcd.print(results.value);
    
    //проверяем кнопки
    //кнопка повер или муте  общий выкл звука 0-2
    if ((results.value==2255209021)|| (results.value==2255218711))
    {  cfg[0]=cfg[0]+1;
       if (cfg[0]>2) { cfg[0]=0; };    EEPROM.write(5,cfg[0]);
       if (cfg[0]==0) {add_mp3(5);}; //sound on
       if (cfg[0]==1) {add_mp3(3);}; //sound off krome ruka
       if (cfg[0]==2) {add_mp3(4);}; }//sound off all
    //кнопка V+ - прибавить громкость мр3
    if ((results.value==2255188111)||(results.value==2155866645))
    {   cfg[1]=cfg[1]+1;
    if (cfg[1]>4) {cfg[1]=4;add_mp3(41);}
    if (cfg[1]==1) {add_mp3(38);}
    if (cfg[1]==2) {add_mp3(39);}
    if (cfg[1]==3) {add_mp3(40);}
    if (cfg[1]==4) {add_mp3(41);}
    mp3setvol();   EEPROM.write(6,cfg[1]);  }
    
    //кнопка V- - убавить громкость мр3
    if ((results.value==2255210551)||(results.value==2155809015))  
    { if (cfg[1]<1)  {add_mp3(37); }  else  {cfg[1]=cfg[1]-1;}
      if (cfg[1]==1) {add_mp3(38);}
      if (cfg[1]==2) {add_mp3(39);}
      if (cfg[1]==3) {add_mp3(40);}
      mp3setvol();     EEPROM.write(6,cfg[1]);   }
    if (results.value==2255192191)  //кнопка sourse - переключить голос на +100
    { if (cfg[15]==0){  cfg[15]=1;}  
      else  {           cfg[15]=0;} add_mp3(42);    
      EEPROM.write(20,cfg[15]);    }
    if (results.value==2255222791)    //кнопка синяя - отключение д.свет
    { if (cfg[2]==0) {  cfg[2]=1;   add_mp3(18);} //no dsvet
      else {            cfg[2]=0;   add_mp3(19); }//dsvet on
      EEPROM.write(7,cfg[2]);    }
    if (results.value==2255220751)    //кнопка 4 - отключение скорости 40
    { if (cfg[3]==0) {  cfg[3]=1;   add_mp3(36); }//40 off
      else  {           cfg[3]=0;   add_mp3(35); }//40 on
      EEPROM.write(8,cfg[3]);   }
    
    if (results.value==2255200351)    //кнопка 5 - отключение скорости 50
    {  if (cfg[9]==0) { cfg[9]=1;   add_mp3(65); } //50 off
       else  {          cfg[9]=0;   add_mp3(66); }//50 on
       EEPROM.write(14,cfg[9]);    }
    
    if (results.value==2255184031)    //кнопка 6 - отключение скорости 60
    {  if (cfg[4]==0) { cfg[4]=1;   add_mp3(12); }//60 off
       else   {         cfg[4]=0;   add_mp3(13); }//60 on
       EEPROM.write(9,cfg[4]);   }
    
    if (results.value==2255171791)    //кнопка 7 - отключение скорости 70
    {  if (cfg[10]==0) {cfg[10]=1;  add_mp3(45);} //70 off
       else   {         cfg[10]=0;  add_mp3(46); }//70 on
       EEPROM.write(15,cfg[10]); }
    
    if (results.value==2255204431)   //кнопка 8 - отключение скорости 80
    { if (cfg[5]==0) {  cfg[5]=1;   add_mp3(14); }//80 off
      else  {           cfg[5]=0;   add_mp3(15); }//80 on
      EEPROM.write(10,cfg[5]);   }
    if (results.value==2255179951)    //кнопка 9 - отключение скорости 90
    { if (cfg[11]==0) {cfg[11]=1;  add_mp3(49);} //90 off
      else {           cfg[11]=0;  add_mp3(50);} //90 on
      EEPROM.write(16,cfg[11]);  }
    if (results.value==2255194231)    //кнопка 0 - отключение скорости 100
    { if (cfg[12]==0) {cfg[12]=1;  add_mp3(53);} //100 off
      else  {          cfg[12]=0;  add_mp3(54);} //100 on
      EEPROM.write(17,cfg[12]);  }
    if (results.value==2255167711)    //кнопка 1 - отключение скорости 110
    { if (cfg[6]==0) { cfg[6]=1;   add_mp3(16);} //110 off
      else   {         cfg[6]=0;   add_mp3(17);} //110 on
      EEPROM.write(11,cfg[6]);   }
    if (results.value==2255163631)    //кнопка 2 - отключение скорости 120
    { if (cfg[13]==0) { cfg[13]=1;  add_mp3(57);} //120 off
      else {            cfg[13]=0;  add_mp3(58);} //120 on
      EEPROM.write(18,cfg[13]);   }
    if (results.value==2255175871)    //кнопка 3 - отключение скорости 130
    { if (cfg[14]==0) { cfg[14]=1;  add_mp3(61);} //130 off
      else   {          cfg[14]=0;  add_mp3(62);} //130 on
      EEPROM.write(19,cfg[14]);   }
    if ((results.value==2255206471)||(results.value==2155829925)) //кнопка зеленая? - отключение вольтметра
    { if (cfg[7]==0)  { cfg[7]=1;   add_mp3(27);} //no Вольтметр
      else    {         cfg[7]=0;   add_mp3(28);  voltmetertest(); }//Вольтметр on
      EEPROM.write(12,cfg[7]);   }
    
    if (results.value==2255190151)   //кнопка желтая - отключение DHO
    { if (cfg[8]==0) {  cfg[8]=1;   add_mp3(31);} //no DHO
      else    {         cfg[8]=0;   add_mp3(30);} //DHO on
      EEPROM.write(13,cfg[8]);    }
     
    irrecv.resume(); // Receive the next value
  }
}
//вывод в монитор порта текущих параметров
void sdisplay(){
 if (ruka==1) {  Serial.print("ruka=off ");}  else{ Serial.print("RUKA=ON  ");}; 
 if (dsvet==1) {  Serial.print("dsvet=off ");}  else{ Serial.print("DSVET=ON  ");};  
 if (gaba==1) {  Serial.print("gaba=off ");}  else{ Serial.print("GABA=ON  ");};  
 Serial.println(" ");
 Serial.print(" speed=");
 Serial.println(sp);
 Serial.println("++++++++++++++++++ END ++++++");
}
void speedometr(){ if (!st){micros_sp = micros();} else {sp20 = (12000000/(micros() - micros_sp));}  sp=sp20/20;  st = !st;  sz=10;}
void rukabeep()    { if  (cfg[2]!=2) {mp3_play (1);} delay(1000);}
void svetbeep()    { if ((cfg[2]==0)&&(cfg[0]==0))   {add_mp3(2);}}
void DHO_beep()    { if ((cfg[8]==0)&&(cfg[0]==0))   {add_mp3(29);}}
void svet1beep()   { if ((cfg[3]==0)&&(cfg[0]==0))   {add_mp3(33);}} //40
void nsvet1beep()  { if ((cfg[3]==0)&&(cfg[0]==0))   {add_mp3(34);}} //n40
void svet50beep()  { if ((cfg[9]==0)&&(cfg[0]==0))   {add_mp3(63);}} //50
void nsvet50beep() { if ((cfg[9]==0)&&(cfg[0]==0))   {add_mp3(64);}} //n50
void svet2beep()   { if ((cfg[4]==0)&&(cfg[0]==0))   {add_mp3(6);}}  //60
void nsvet2beep()  { if ((cfg[4]==0)&&(cfg[0]==0))   {add_mp3(7);}}  //n60
void svet70beep()  { if ((cfg[10]==0)&&(cfg[0]==0))  {add_mp3(43);}} //70
void nsvet70beep() { if ((cfg[10]==0)&&(cfg[0]==0))  {add_mp3(44);}} //n70
void svet3beep()   { if ((cfg[5]==0)&&(cfg[0]==0))   {add_mp3(8);}}  //80
void nsvet3beep()  { if ((cfg[5]==0)&&(cfg[0]==0))   {add_mp3(9);}}  //n80
void svet90beep()  { if ((cfg[11]==0)&&(cfg[0]==0))  {add_mp3(47);}} //90
void nsvet90beep() { if ((cfg[11]==0)&&(cfg[0]==0))  {add_mp3(48);}} //n90
void svet100beep() { if ((cfg[12]==0)&&(cfg[0]==0))  {add_mp3(51);}} //100
void nsvet100beep(){ if ((cfg[12]==0)&&(cfg[0]==0))  {add_mp3(52);}} //n100
void svet4beep()   { if ((cfg[6]==0)&&(cfg[0]==0))   {add_mp3(10);}} //110
void nsvet4beep()  { if ((cfg[6]==0)&&(cfg[0]==0))   {add_mp3(11);}} //n110
void svet120beep() { if ((cfg[13]==0)&&(cfg[0]==0))  {add_mp3(55);}} //120
void nsvet120beep(){ if ((cfg[13]==0)&&(cfg[0]==0))  {add_mp3(56);}} //n120
void svet130beep() { if ((cfg[14]==0)&&(cfg[0]==0))  {add_mp3(59);}} //130
void nsvet130beep(){ if ((cfg[14]==0)&&(cfg[0]==0))  {add_mp3(60);}} //n130
//void svet1unbeep(){  tone (17,1000);  delay(200);  noTone(17);}
void add_mp3(byte nomermp3)
{  if ((mp3stekpos > 8)&&(cfg[0]<2))//если переполнение очереди то пищалка
  {  if (cfg[0]==0)  { tone (17,1000);  delay(200);  tone (17,1400);  delay(200);  noTone(17);  }}//пищалку не включать если все звуки отключены
     else   {  mp3stekpos = mp3stekpos+1;  mp3stek[mp3stekpos] = nomermp3;   }} //собственно постановка в очередь
void mp3setvol() //перекодировка уровней громкости из 0-4 в 0-30
{if(cfg[1]==0){mp3_set_volume(10);}if(cfg[1]==1){mp3_set_volume(15);}if(cfg[1]==2){mp3_set_volume(20);}if(cfg[1]==3){mp3_set_volume(25);}if(cfg[1]>3){mp3_set_volume(30);}}
void voltmetertest()
{ outputValue = 0; 
  int u_x10 =0 ; 
  int u_x1 =0 ; 
  int u_x0 =0 ;
  for (int iii=0; iii <10 ; iii++) {  outputValue =outputValue + float(analogRead(A0));  delay(100); }//усреднение нескольких измерений
  outputValue =  outputValue/620;//коэффициент прямого пересчета в вольты
  u_x10=round(outputValue*10); //напряжение х10 
  u_x1=floor(u_x10/10); //целая часть вольтов
  add_mp3(70+u_x1);
  u_x0=u_x10-(10*u_x1);   //десятые = 10U-10Uокруг
  add_mp3(90+u_x0);
}
void gabamute()
{  if (lastgabaonoff==gaba)
{//габариты не трогали
gabaonofftimer=gabaonofftimer+1;
if (gabaonofftimer>250 ) 
 {//давно не трогали габариты - обнуляем ожидание
 gabaonofftimer=250;
 gabaonoffcounter=0;
 }
}
else
{//габариты щелкнули
lastgabaonoff=gaba;
if (gabaonoffcounter==0) {gabaonofftimer=0;} //первый щелчок
// Serial.println(gabaonofftimer);
if (gabaonofftimer<25)
 {//щелчок недавно был
 gabaonoffcounter=gabaonoffcounter+1;
 gabaonofftimer=0;
 if (gabaonoffcounter>5) 
   {//блок отключения-включения звука по циклу
    gabaonoffcounter=0;
    cfg[0]=cfg[0]+1;
    if (cfg[0]>2) { cfg[0]=0; };
    EEPROM.write(5,cfg[0]);
    if (cfg[0]==0) {add_mp3(5);}; //sound on
    if (cfg[0]==1) {add_mp3(3);}; //sound off krome ruka
    if (cfg[0]==2) {add_mp3(4);}; //sound off all
   } }
 else  {   gabaonoffcounter=0;   gabaonofftimer=0; }
}}
Она умеет выдавать сигналы — габариты, дальний свет, ручник, и менять скорость с 0 до 150 примерно. Из недостатков — нет подавления дребезга контактов, но мне не особо мешает этот момент.
#include <Wire.h> // добавляем необходимые библиотеки
#include <LiquidCrystal.h>
LiquidCrystal lcd(8, 9, 4, 5, 6, 7 );
const int rukaPin =  2;      // пин = ручник
const int dsvetPin =  1;      // пинт = д.свет
const int speedPin =  0;      // пин = датчик скорости
const int gabaPin =  11;      // пин6 = габариты
int rukaState = 0;  // Переменная значения кнопки
int dsvetState = 0;  // Переменная значения кнопки
int speedState = 0;  // Переменная значения кнопки
int gabaState = 0;  // Переменная значения кнопки
int speedkm = 0;  // км/ч
int lowdelay = 250;  // задержка генератора молой частоты
// Нажатые кнопки
int button;
const int BUTTON_NONE   = 0;
const int BUTTON_RIGHT  = 1;
const int BUTTON_UP     = 2;
const int BUTTON_DOWN   = 3;
const int BUTTON_LEFT   = 4;
const int BUTTON_SELECT = 5;
int getPressedButton()
{
  int buttonValue = analogRead(0); // считываем значения с аналогового входа(A0) 
  if (buttonValue < 60) {
    return BUTTON_RIGHT;  
  }
  else if (buttonValue < 200) {
    return BUTTON_UP;
  }
  else if (buttonValue < 400){
    return BUTTON_DOWN;
  }
  else if (buttonValue < 600){
    return BUTTON_LEFT;
  }
  else if (buttonValue < 800){
    return BUTTON_SELECT;
  }
  return BUTTON_NONE;
}
void setup()
{
  pinMode(rukaPin, OUTPUT); // Настраиваем  порт на выход
  pinMode(dsvetPin, OUTPUT); // Настраиваем  порт на выход
  pinMode(speedPin, OUTPUT); // Настраиваем  порт на выход
  pinMode(gabaPin, OUTPUT); // Настраиваем  порт на выход
  lcd.begin(16, 2);             
  lcd.print("starting...");     
  delay (500);
     lcd.clear();
     lcd.setCursor(12, 0);
     if(gabaState == 1){lcd.print("G_ON");}else{lcd.print("GOFF");}
     digitalWrite(gabaPin,gabaState);
     lcd.setCursor(6, 0);     
     if(dsvetState == 1){lcd.print("D__ON");}else{lcd.print("D_OFF");}   
     digitalWrite(dsvetPin,dsvetState);
     lcd.setCursor(0, 0);     
     if(rukaState == 0){lcd.print("R__ON");}else{lcd.print("R_OFF");}   
     digitalWrite(rukaPin,rukaState);
 }
void loop()
{
  delay(50);
  if ((speedState<=36)&(speedState>0))
  {
  lowdelay=500/speedState;
  digitalWrite(speedPin,1);
  delay(lowdelay);
  digitalWrite(speedPin,0);
  delay(lowdelay);
  lcd.setCursor(12, 1);
  lcd.print(lowdelay);
  lcd.print(" ");
  }
   
  button = getPressedButton();
  switch (button)
  {
  case BUTTON_RIGHT: // при нажатии кнопки выводим следующий текст
    lcd.setCursor(12, 0);
    if (gabaState == 0) 
    { 
      gabaState = 1;
      lcd.print("G_ON ");
    }
    else
    {
      gabaState = 0;
      lcd.print("GOFF ");
    }
      digitalWrite(gabaPin,gabaState);
    break;
  case BUTTON_LEFT:
    lcd.setCursor(6, 0);
    if (dsvetState == 0) 
    { 
      dsvetState = 1;
      lcd.print("D__ON ");
    }
    else
    {
      dsvetState = 0;
      lcd.print("D_OFF ");
    }
    digitalWrite(dsvetPin,dsvetState);
    break;
  case BUTTON_UP:
    if (speedState<5){speedState=speedState+1;} else {if (speedState<250){speedState=speedState+5;};}
    lcd.setCursor(0, 1);
    lcd.print(speedState);
    speedkm=speedState*0.6;
    lcd.print("  ");
    lcd.print(speedkm);
    lcd.print("km/h ");
    if (speedState>35){ tone(speedPin,speedState);}
      else
      {
      noTone(speedPin);
     
      };
    break;
  case BUTTON_DOWN:
    if ((speedState<10)&(speedState>0)){speedState=speedState-1;} 
  else 
     {
   if ((speedState<255)&(speedState>0)){speedState=speedState-5;}
    }  ;
    lcd.setCursor(0, 1);
    lcd.print(speedState);
    speedkm=speedState*0.6;
    lcd.print("  ");
    lcd.print(speedkm);
    lcd.print("km/h ");
//    if (speedState==0){noTone(speedPin);} else {tone(speedPin,speedState);};
 if (speedState>35){ tone(speedPin,speedState);}
      else
      {
      noTone(speedPin);
           };
    break;
  case BUTTON_SELECT:
//    lcd.setCursor(0, 0); 
//    lcd.print("                 "); 
    lcd.setCursor(0, 0);
    if (rukaState == 0) 
    { 
      rukaState = 1;
      lcd.print("R_OFF ");
    }
    else
    {
      rukaState = 0;
      lcd.print("R__ON ");
    }
     digitalWrite(rukaPin,rukaState);
    break;
  }
}
Комментарии (15)
 - shtirlitsus20.02.2017 14:16- Посоветовал бы Вам озвучку взять с https://www.ivona.com 
 Повменяемей, на мой слух.
- artskep20.02.2017 18:52+1- Это, конечно, круто, но зачем голосом? Ну, кроме очевидной причины «потому что могу»? 
 По большому счету — весь функционал — предупреждение об опасной или потенциально опасной ситуации. В таких случаях подключать голову, чтобы слушать женский или мужской голос — не самая хорошая идея.
 Не лучше ли запилить разной степени «пикалки» в зависимости от проблемы.
 К примеру, на движение с ручником, без включенных огней — резкую пищалку (ибо надо).
 На дальний свет — че-то помягче (ибо, далеко не факт, что он просто так горит).
 На напряжение бортовой сети… даже не знаю.
 
 А для повышения/понижения порога скорости, я бы предложил посмотреть, как это сделано у вариометра у парапланеристов — не совсем хорошая аналогия, но все-таки, тон пикания при повышении, понижении скорости даст больше информации (особенно когда привыкнешь)
 - AlexKooper20.02.2017 22:34- Забыл добавить ссылку на голосовые и текстовые файлы. https://yadi.sk/d/Mk8eiMyP3Dwfv4 
 Насчет «пикалки» вместо звука — первые варианты были именно такие. От них и зуммер остался в плате. Но когда событий стало за десяток — сложно на слух воспринимать отличия между писками разной тональности и длительности. Заодно проговаривается изменение настроек — ПДУ может не всегда сработать.
 
 Кстати, звуки воспроизводятся с ощутимой задержкой, поэтому немного нелитературно сделана озвучка вольтметра в плане десятых долей (Напряжение двенадцать вольт… И девять десятых.). Заказал на али голосовой модуль, который должен быть свободен от таких задержек, WT588D-U 32 М называется. Поэкспериментирую.
 
 Ещё одна из проблем — неправильно измеряется скорость на скоростях выше 60 км/ч. В принципе, пороги забиты константами, и подобрать можно экспериментально, но я хочу разобраться, почему не подходят теоретические расчеты.
 
 Да, панель приборов у меня блеклая, лампу дальнего света днем не видно, а лампа ручника ещё и закрыта рулем. Поэтому езда «на слух».
 
 Несложно также вывести со свободной ноги сигнал включения ДХО (простая проверка скорости s>0), возможно комбинировать с проверкой включения габаритов.
 - bigbrotherwatchingyou20.02.2017 22:35- Эммм… Это для тех, кто управляет автомобилем, закрыв глаза? - artskep21.02.2017 16:41- Я полагаю, для тех, кто предпочитает управлять автомобилем смотря на дорогу, а не на панель управления. 
 
 - AlexKooper20.02.2017 23:44- Ссылка на голосовые файлы битая, вот правильная https://yadi.sk/d/3xnPrad-3EKGjy 
 - AlexKooper21.02.2017 20:37- Ещё у меня изменена библиотека ИК-датчика, с целью использовать другой таймер, вот только не помню, для этого проекта или другого менял. В общем, если будет на таймеры ругаться — надо в файле IRremoteInt.h найти и поменять: 
 было:
 // Arduino Duemilanove, Diecimila, LilyPad, Mini, Fio, Nano, etc
 #else
 //#define IR_USE_TIMER1 // tx = pin 9
 #define IR_USE_TIMER2 // tx = pin 3
 стало:
 // Arduino Duemilanove, Diecimila, LilyPad, Mini, Fio, Nano, etc
 #else
 #define IR_USE_TIMER1 // tx = pin 9
 //#define IR_USE_TIMER2 // tx = pin 3
 - AlexKooper22.02.2017 19:10- В нексии есть ODBII разъем, там у меня иногда живет elm327 адаптер. Других шин нет. 
 
           
 
serafims
А сигнал о скорости откуда брался? с OBD2 разъема?
Urvin
mefiato
К основному аккумулятору подключать или нужен съемный типа такой батарейки?
AlexKooper
В машине использует бортовую сеть, при отладке на столе питается от usb, при отладке вольтметра питался от внешнего блока питания с регулируемым выходом. Я ответил на Ваш вопрос?
mefiato
Спасибо, очень интересно)