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


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


Функционал:
1. Говорит голосом (5 уровней громкости) о различных событиях (события описаны далее)
а) мужским
б) женским
Голоса синтезированы на компьютере, записаны в виде набора mp3-файлов на микроСД карту. Это значит, что вы можете их поменять или записать.
2. Настройки выполняются с ПДУ от телевизора (или любого другого с достаточным количеством кнопок, настоятельно рекомендую ПДУ с кнопками 0-9). Настройки запоминаются в момент изменения.
3. В случае отказа/потери ПДУ, извещатель может быть оперативно переключен на режимы «нет звуков» или «нет звуков, кроме ручника». Для этого используется тройное включение габаритов (идея использовать мигание габаритами в качестве управляющего сигнала для настройки спёрта с какого-то устройства регулировки яркости ДХО).
а) мужским
б) женским
Голоса синтезированы на компьютере, записаны в виде набора mp3-файлов на микроСД карту. Это значит, что вы можете их поменять или записать.
2. Настройки выполняются с ПДУ от телевизора (или любого другого с достаточным количеством кнопок, настоятельно рекомендую ПДУ с кнопками 0-9). Настройки запоминаются в момент изменения.
3. В случае отказа/потери ПДУ, извещатель может быть оперативно переключен на режимы «нет звуков» или «нет звуков, кроме ручника». Для этого используется тройное включение габаритов (идея использовать мигание габаритами в качестве управляющего сигнала для настройки спёрта с какого-то устройства регулировки яркости ДХО).
Озвучиваемые события (отключаемые)
— Ручник (если горит лампочка ручника во время движения)
— Включен дальний свет (мигание дальним игнорируется, предупреждает один раз после включения, далее не напоминает, пока дальний не переключат)
— Включите ходовые огни (если начато движение без включенных габаритов, у меня ДХО и ближний не работают, если не включены габариты).
— Напряжение бортовой сети (точность до десятой доли вольта)
— Превышение порога скорости 40, 50,… 120, 130 км/ч.
— Понижение порога скорости 40, 50,… 120, 130 км/ч.
— Изменение громкости, переключение голоса, отключение/включение каждого из озвучиваемых событий (включение вольтметра вызывает одновременно озвучивание напряжения).
Видео с демонтрацией:
— Включен дальний свет (мигание дальним игнорируется, предупреждает один раз после включения, далее не напоминает, пока дальний не переключат)
— Включите ходовые огни (если начато движение без включенных габаритов, у меня ДХО и ближний не работают, если не включены габариты).
— Напряжение бортовой сети (точность до десятой доли вольта)
— Превышение порога скорости 40, 50,… 120, 130 км/ч.
— Понижение порога скорости 40, 50,… 120, 130 км/ч.
— Изменение громкости, переключение голоса, отключение/включение каждого из озвучиваемых событий (включение вольтметра вызывает одновременно озвучивание напряжения).
Видео с демонтрацией:
Подключение к авто
1. К спидометру — сразу три провода: земля, +12 (отсюда берём питание на устройство и данные для вольтметра), сигнал от датчика скорости.
2. К лампочкам — ручника, дальнего света, подсветки приборов (габариты).
Согласование уровней сделано довольно просто — выводы ардуины подтянуты внутренним резистором в «1», а сигналы 12В уровня из приборной панели идут через диоды. Когда с панели идет +12, диод закрыт — на ардуине «лог.1». Когда сигнал с панели идет землёй — диод открыт, внутренний подтягивающий резистор игнорируется, на выводе ардуино «лог.0».
Вольтметр — просто резистивный делитель на аналоговый вход. Отношение примерно 1:13, из того расчета, что при максимальном напряжении бортовой сети 16В (аварийный режим) АЦП достигнет своего предела при опорном сигнале 1,2В.
2. К лампочкам — ручника, дальнего света, подсветки приборов (габариты).
Согласование уровней сделано довольно просто — выводы ардуины подтянуты внутренним резистором в «1», а сигналы 12В уровня из приборной панели идут через диоды. Когда с панели идет +12, диод закрыт — на ардуине «лог.1». Когда сигнал с панели идет землёй — диод открыт, внутренний подтягивающий резистор игнорируется, на выводе ардуино «лог.0».
Вольтметр — просто резистивный делитель на аналоговый вход. Отношение примерно 1:13, из того расчета, что при максимальном напряжении бортовой сети 16В (аварийный режим) АЦП достигнет своего предела при опорном сигнале 1,2В.
Состав устройства
1. Arduino nano v3
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, с защелками на папе — достаточно легкий и многоконтактный)
Ссылка на Али
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, с защелками на папе — достаточно легкий и многоконтактный)
Прошивка:
Из текста прошивки можно легко увидеть, куда заведены все анализируемые сигналы, куда прописать коды вашего ИК-пульта (взять их можно с помощью демо-скетча IRRecvDump). Также константами явно прописаны моменты срабатывания озвучки скоростей (обратите внимание на то, что скорости озвучиваются несколько раньше, чем будут реально достигнуты — чтоб успеть сбавить, не превысив).
#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; } }}
Прошивка вспомогательной платы
Для отладки, доводки и демонстрации использовалась другая ардуина — arduino uno и модулем LCD-дисплея и кнопками.
Она умеет выдавать сигналы — габариты, дальний свет, ручник, и менять скорость с 0 до 150 примерно. Из недостатков — нет подавления дребезга контактов, но мне не особо мешает этот момент.
Она умеет выдавать сигналы — габариты, дальний свет, ручник, и менять скорость с 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; } }