Тахометр
- Войдите на сайт для отправки комментариев
Всем привет!
Спешу поделиться дебютной работой: на Arduino Nano собрал тахометр для Шевроле Ланос. Задача была сделать минимальный по размеру, не цифровой (легче воспринимается).

Маленьких диодов Шотки не нашёл, пришлось ставить большие.
Разъём припаял к плате комбинации приборов, блок разместил в закрытом отсеке для четырёх батареек АА (выключатель не задействовал). Индикатор выполнил на полоске стеклотекстолита (убавил по толщине) и SMD-светодиодах. Корпус - из гелевой ручки, шкала напечатана на бумаге (планирую потом на прозрачной плёнке для лазерных принтеров).




Цвета светодиодов выбрал по оптимуму для двигателя. При включении габаритов яркость снижается.
При заглушенном двигателе моргает индикатор "0".
Индикатор пока никак не закрепил, после доработки приклею пористым скотчем к накладке панели приборов.
Видео разместил здесь:
https://www.youtube.com/watch?v=ieEQ1Reh_ys
Покатался, вроде удобно.
А чем плох родной? Стрелочный?
А чем плох родной? Стрелочный?
Ланос достался без тахометра, а покупать новую комбинацию приборов посчитал дорого. Читал, что при замене могут быть проблемы с указателем уровня топлива (к разным приборам ставятся разные датчики).
Тем более это была первая практическая работа с Arduino.
Здравствуйте. Вот так же задумал сделать себе тахометр, ланос так же без тахометра. Можете поделиться скетчем и схемой? Заранее спасибо.
Добрый вечер.
Выкладываю схему и плату тахометра. Если интересно про скетч - пишите, размещу позднее (сейчас нет возможности).
Подключал: Земля, +12, габарит - припаивал провода непосредственно на плату комбинации приборов.
Провод от блока управления двигателем (зелёный с чёрной полосой) приходит на разъём комбинации приборов, но в ней отсутствует контакт - припаялся прямо к проводу жгута.
Вид со стороны деталей
Резисторы R5-R8 SMD, припаяны со стороны дорожек.
Вид со стороны дорожек:
Изначально планировалась установка на плату винтового разъёма, но в итоге провода просто припаял.
На схеме показаны цвета компьютерного кабеля. Он оказался слишком толстым и не поместился вместе с платой светодиодов в корпус ручки. Жгут к светодиодам сделал из проводов МГТФ в трубке - более гибкий и тоньше.
На плате не показано отверстие для крепления ЕН5 - после пайки её "положил" на плату и просверлил по месту (площадку дорожек сделал для лучшего теплоотвода).
спасибо большое, код тоже бы хотелось посмотреть, есть вопрос, что за элемент БУД А5 и как происходит съем сигнала
//Имена для светодиодов #define LED0 0 #define LED1 1 #define LED2 2 #define LED3 3 #define LED4 4 #define LED5 5 #define LED6 6 #define LED7 7 #define LED8 8 #define LED9 9 #define LED10 10 #define LED11 11 #define LED12 12 #define LED13 13 // Признак мерцания для зоны #define FlashNo false #define FlashYes true //Кодировка цвета светодиодов #define Red 0 #define Yellow 1 #define Green 2 //Выводы для светодиодов #define Anod1 2 //Коричневый с белой полосой #define Anod2 3 //Коричневый #define Anod3 4 //Оранжевый #define Anod4 7 //Оранжевый с белой полосой #define Katod1 5 //Голубой с белой полосой #define Katod2 6 //Зелёный #define Katod3 9 //Зелёный с белой полосой #define Katod4 10 //Голубой #define LEDpin 13 //Вывод на встроенный светодиод /* Таблица яркости светодиодов Цвет, Яркость дневная, Яркость ночная */ const byte TableColor[] = { Red, 50, 200, Yellow, 50, 200, Green, 230, 240 }; /* Таблица светодидов Номер светодиода, Вывод анода, Вывод катода, Цвет */ const byte TableLED[] = { Anod1, Katod1, Red, //LED0 Anod4, Katod1, Red, //LED1 Anod4, Katod2, Yellow, //LED2 Anod1, Katod2, Yellow, //LED3 Anod1, Katod3, Green, //LED4 Anod2, Katod3, Green, //LED5 Anod2, Katod4, Green, //LED6 Anod3, Katod4, Green, //LED7 Anod3, Katod2, Green, //LED8 Anod2, Katod2, Yellow, //LED9 Anod2, Katod1, Yellow, //LED10 Anod3, Katod1, Red, //LED11 Anod3, Katod3, Red, //LED12 Anod4, Katod3, Red //LED13 }; /* Таблица зон Светодиод, Мерцание, Нижняя граница */ const byte TableZone[] = { LED0, FlashYes, 0, //Зона 0 LED1, FlashNo, 8, //Зона 1 LED2, FlashNo, 16, //Зона 2 LED3, FlashNo, 25, //Зона 3 LED4, FlashNo, 33, //Зона 4 LED5, FlashNo, 41, //Зона 5 LED6, FlashNo, 50, //Зона 6 LED7, FlashNo, 58, //Зона 7 LED8, FlashNo, 66, //Зона 8 LED9, FlashNo, 75, //Зона 9 LED10, FlashNo, 83, //Зона 10 LED11, FlashNo, 91, //Зона 11 LED12, FlashNo, 100, //Зона 12 LED13, FlashYes, 108 //Зона 13 }; #define LightPin 11 //Вывод для контроля включения габаритов (HIGH – включены габариты) #define EnginePin 8 //Вывод для приёма синхросигнала #define MeasureTime 500 //Период измерения синхроимпульсов (0,5 сек) #define MinSignalTime 50 //Длительность защиты от антидребезга (микросекунды) #define FlashTime 300 //Длительность горения светодиода при мерцании (0,3 сек) #define StartShowTime 3000 //Длительность стартового отображения (2 сек) #define LEDShowTime 100 //Длительность горения светодиода (динамическая индикация – микросекунды) #define EngineCountMax 255 //Максимально допустимое значение счётчика синхроимпульсов byte Zone; //Номер зоны частоты void SwitchOff() //Подпрограмма гашения всех светодиодов { //Перебор всех светодиодов от нулевого до размера таблицы под светодиоды for (byte CurrentLED = 0; CurrentLED <= (sizeof(TableLED) / 3 - 1); CurrentLED++) { SwitchOffLED(CurrentLED); //Отключение светодиода } } void SwitchOffLED(byte CurrentLED) //Подпрограмма гашения светодиода { digitalWrite(TableLED[(CurrentLED ) * 3], LOW); //Отключение анода светодиода pinMode(TableLED[(CurrentLED ) * 3 + 1], INPUT); //Настройка вывода для катода как вход } /*Подпрограмма зажигания светодиода Входная переменная - номер светодиода */ void SwitchOnLED(byte CurrentLED) { byte Brightness; //Яркость byte CurrentAnod = TableLED[(CurrentLED * 3)]; //Вывод для анода указанного светодиода byte CurrentKatod = TableLED[(CurrentLED * 3) + 1]; //Вывод для катода указанного светодиода byte CurrentColor = TableLED[(CurrentLED * 3) + 2]; //Цвет указанного светодиода //Serial.print("LED: "); //Serial.println(CurrentLED); //Serial.print("AnodPin: "); //Serial.println(CurrentAnod); //Serial.print("KatodPin: "); //Serial.println(CurrentKatod); //Serial.print("ColorCode: "); //Serial.println(CurrentColor); //Поиск строки яркости для текущего цвета for (Brightness = 0; TableColor[Brightness * 3] != CurrentColor; Brightness++); if (digitalRead(LightPin) == LOW) //Проверка включения габаритов { Brightness = TableColor[(Brightness * 3) + 1]; // Выбор скважности «день» } else { Brightness = TableColor[(Brightness * 3) + 2]; // Выбор скважности «ночь» } //Serial.print("Brightness: "); //Serial.println(Brightness); //Serial.println(); digitalWrite(CurrentAnod, HIGH); //Включение анода светодиода analogWrite(CurrentKatod, Brightness); //Включение катода светодиода } /* Подпрограмма стартовой индикации (динамическая индикация всех светодиодов) */ void StartLight() { byte CurrentLED = 0; //Текущий светодиод unsigned long StartTime = millis(); //Запоминание времени начала индикации //Serial.println("StartLight On"); //Пока длительность СтартШоу меньше заданной while ((millis() - StartTime) <= StartShowTime) { //Serial.print("LED "); //Serial.print(CurrentLED); //Serial.print(" On "); SwitchOnLED(CurrentLED); //Включить текущий светодиод //delay(LEDShowTime); //Задержка на время горения светодиода delayMicroseconds(LEDShowTime); //Задержка на время горения светодиода SwitchOffLED(CurrentLED); //Погасить текущий светодиод if (CurrentLED == (sizeof(TableLED) / 3 - 1)) //Если текущим был последний светодиод: { CurrentLED = 0; //Сделать текущим нулевой светодиод } else //Иначе: { CurrentLED++; //Сделать текущим следующий светодиод } //Serial.println(" Off"); } /* for (byte CurrentLED = 0; CurrentLED <= (sizeof(TableLED) / 3 - 1); CurrentLED++) { SwitchOnLED(CurrentLED); //Включить текущий светодиод delay(300); SwitchOffLED(CurrentLED); //Отключение светодиода } */ //Serial.println("StartLight Off"); } void setup() //Подпрограмма инициализации { //pinMode(2, INPUT_PULLUP); //Serial.begin(9600); for (byte CurrentLED = 0; CurrentLED <= (sizeof(TableLED) / 3 - 1); CurrentLED++) //Перебрать все светодиоды { //Serial.print("LED: "); //Serial.println(CurrentLED); //Serial.print("AnodPin: "); //Serial.println(TableLED[(CurrentLED * 3)]); //Serial.print("KatodPin: "); //Serial.println(TableLED[(CurrentLED * 3)+1]); //Serial.print("ColorCode: "); //Serial.println(TableLED[(CurrentLED * 3)+2]); //Serial.println(); pinMode(TableLED[(CurrentLED * 3)], OUTPUT); //Настройка вывода для анода как выход digitalWrite(TableLED[(CurrentLED * 3)], LOW); //Отключение анода светодиода //Настройка вывода для катода как вход (гашение через катоды) pinMode(TableLED[(CurrentLED * 3) + 1] , INPUT); //pinMode(LEDpin, OUTPUT); //Настройка вывода встроенного светодиода как выход //digitalWrite(LEDpin, LOW); //Отключение встроенного светодиода } /*digitalWrite(2, HIGH); analogWrite(9, 100); Serial.println("On LED1"); delay(5000); digitalWrite(2, LOW); Serial.println("Off LED1");*/ pinMode(LightPin, INPUT_PULLUP); //Настройка вывода для контроля включения габаритов на ввод //digitalWrite(LightPin, HIGH); //Включение подтягивающего резистора pinMode(EnginePin, INPUT_PULLUP); //Настройка вывода для приёма синхроимпульсов на ввод //digitalWrite(EnginePin, HIGH); //Включение подтягивающего резистора //Serial.begin(9600); StartLight(); //Подпрограмма стартовой индикации Serial.begin(9600); } void loop() //Основная программа { /* Определение количества синхроимпульсов за интервал измерения Гашение светодиодов, если текущая зона мерцающая Определение зоны Гашение светодиодов Зажигание светодиода, соответствующего текущей зоне Выдача в UART числа синхроимпульсов и номера зоны*/ /* while (1) { Serial.print("EnginePin: "); Serial.println(digitalRead(EnginePin)); delay(1000); }*/ byte TempZone = CurrentZone(EngineCounter()); byte TempLED = TableZone[TempZone * 3]; //Serial.print("CurrentLED: "); //Serial.print(TempLED); SwitchOff(); //Гашение светодиодов SwitchOnLED(TempLED); //SwitchOn(ZoneLight[(CurrentZone(EngineCounter()) - 1) * 2]); } /*Подпрограмма счёта синхроимпульсов и гашения при мерцании Счёт производится по переднему фронту синхроимпуса Реализована защита от дребезга */ byte EngineCounter() { boolean LastState = HIGH; //Задание "прошлого" состояния синхросигнала boolean TempState = HIGH; //Задание "временного" состояния синхросигнала boolean StartFlag = LOW; //Сброс признака начала отсчёта byte EngineCount = 0; //Обнуление счётчика синхроимпульсов unsigned long StartMeasureTime = millis(); //Запоминание времени начала отсчёта unsigned long StartChangeTime = StartMeasureTime; //Запоминание времени последнего изменения синхросигнала //Подсчёт синхроимпульсов - пока интервал измерения не вышел while ((millis() - StartMeasureTime) <= MeasureTime) { boolean NewState = digitalRead(EnginePin); //Запоминание нового состояния синхросигнала //digitalWrite(LEDpin, NewState); //Вывод синхросигнала на встроенный светодиод if (TempState != NewState) //Если синхросигнал изменился { // Serial.print("TempState "); // Serial.print(TempState); // Serial.print(" NewState "); // Serial.println(NewState); TempState = NewState; //Запоминание нового состояния синхросигнала //StartChangeTime = millis(); //Запоминание времени изменения синхросигнала StartChangeTime = micros(); //Запоминание времени изменения синхросигнала } else { if ((micros() - StartChangeTime) > MinSignalTime) //Если время антидребезга вышло { if ((LastState == LOW) && (NewState == HIGH)) //Если пришёл и держался передний фронт { if (StartFlag == LOW) //Если отсчёт ещё не начинался { StartFlag = HIGH; //Установить признак начала отсчёта StartMeasureTime = millis(); //Запоминание времени начала отсчёта } else //Если отсчёт начат { EngineCount++; //Приращение счётчика синхроимпульсов } } LastState = NewState; //Запоминание державшегося состояния } } //Serial.print("Zone "); //Serial.print(Zone ); //Serial.print(" FlashAttribute "); //Serial.println(ZoneLight[(Zone) * 2 + 1]); //Гашение светодиодов, если время горения вышло и зона мерцающая if (((millis() - StartMeasureTime) >= FlashTime) && (TableZone[Zone * 3 + 1])) { SwitchOffLED(TableZone[Zone * 3 ]); //Погасить светодиод текущей зоны } } //digitalWrite(LEDpin, LOW); //Отключение встроенного светодиода return EngineCount; //Вернуть количество синхроимпульсов } //Альтернативная программа определения зоны byte CurrentZoneA(byte EngineCount) { for (Zone = 0; Zone <= sizeof(TableZone) / 3 - 1; Zone++) //Перебор всех зон { /* Если нижний предел текущей зоны превышает количество синхроимпульсов */ if (TableZone[(Zone ) * 3 + 2] > EngineCount) { Zone--; //Возврат к предыдущей зоне break; //Выход из цикла } } /* Serial.print("\n\nPulses: "); Serial.println(EngineCount); Serial.print("Zone: "); Serial.println(Zone); */ return Zone; } byte CurrentZone(byte EngineCount) //Подпрограмма определения зоны { //Serial.print("CurrentZone begining... "); if (EngineCount >= TableZone[6 * 3 + 2]) //Test_6 { if (EngineCount >= TableZone[10 * 3 + 2]) //Test_10 { if (EngineCount >= TableZone[12 * 3 + 2]) //Test_12 { if (EngineCount >= TableZone[13 * 3 + 2]) //Test_13 Zone = 13; else //Test_13 Zone = 12; } else //Test_12 { if (EngineCount >= TableZone[11 * 3 + 2]) //Test_11 Zone = 11; else //Test_11 Zone = 10; } } else //Test_10 { if (EngineCount >= TableZone[8 * 3 + 2]) //Test_8 { if (EngineCount >= TableZone[9 * 3 + 2]) //Test_9 Zone = 9; else //Test_9 Zone = 8; } else //Test_8 { if (EngineCount >= TableZone[7 * 3 + 2]) //Test_7 Zone = 7; else //Test_7 Zone = 6; } } } else //Test_6 { if (EngineCount >= TableZone[2 * 3 + 2]) //Test_2 { if (EngineCount >= TableZone[4 * 3 + 2]) //Test_4 { if (EngineCount >= TableZone[5 * 3 + 2]) //Test_5 Zone = 5; else //Test_5 Zone = 4; } else //Test_4 { if (EngineCount >= TableZone[3 * 3 + 2]) //Test_3 Zone = 3; else //Test_3 Zone = 2; } } else //Test_2 { if (EngineCount >= TableZone[1 * 3 + 2]) //Test_1 Zone = 1; else //Test_1 Zone = 0; } } SwitchOff(); //Гашение светодиодов // Serial.print("\n\nPulses: "); // Serial.println(EngineCount); // Serial.print("Zone: "); // Serial.println(Zone); //Serial.println(String("Pulses: ") + EngineCount + String("/ Zone: ") + Zone); return Zone; } [/code]О подключении я уже написал выше: синхросигнал для тахометра формирует блок управления двигателем (БУД). Он приходит к комбинаци приборов на разъёме жгута проводов - зелёный провод с чёрной полосой. На разъёме комбинации приборов контакта нет, так как отсутствует тахометр.
Вот собственно код.
Жду море критики :)
большое спасибо, потеплеет, буду пробовать)
Коллеги-ардуинщики, приветствую! Нужна ваша помощь. Делаю тахометр на лодочный мотор. Принцып простой: сигнальный провод наматывается на высоковольтный провод и подключается к контакту с прерываниями. Скетч рабочий, в тестовом режиме все работает. Но вот когда доходит до практики, один разряд искры дает сразу несколько срабатываний, что увеличивает частоту в н раз. Я так понимаю, это связано с колебательным характером сигнала. Может кто подскадет, какую обвязку из эл.деталей нужно сделать, чтобы сигнал был с одним пиком? Может какие-то RC-фильтры? Заранее спасибо за любую инфу или тык пальцем, где копать.
софтовую задержку дать на последующее измерение после первого сигнала, но не длиннее чем время между 2-я импульсами на мах оборотах
зажигание контактное?
evgta, спасибо за ответ! Думал об этом, но внутри прерывания функция delay() не работает, конечно может я что-то не так прочтал. :(. Зажигание контактное.
значит дребезжат контакты.
необязательно через делей делать, через миллис(микрос), если импульс пришел раньше установленного времени после первого импульса то его просто не обрабатывать
или цикл через миллис в прерывании сделать на определенное время для задержки
или цикл через миллис в прерывании сделать на определенное время для задержки
millis в обработчике прерывания не изменяет своего значения и всегда равен тому же, чему был равен в момент прерывания. Чтобы он менялся нужно разрешить прерывания, но я далёк от мысли советовать такие трюки новичкам.
Нормально работает delayMicroseconds или как там его зовут.
Я правильно понял, что делейМикросекондс внутри прерывания работает? Заранее спасибо за ответ!
Привет!
Весеннее солнце стало ярче светить и выявился недостаток тахометра - под лучами совсем ничего не видно. Яркостью думаю компенсировать не стоит, планирую сделать что-то вроде козырька над индикатором, чтобы получилась щель, через которую будет видно светодиодную полоску.
Насчёт дребезга, правильно подсказали: после обработки первого импульса нужно сделать ожидание "длинного нуля" (например, как в моём скетче).