Спасибо.
Подправил
Вид для печати
Спасибо.
Подправил
11111
Итак...нынче закончил с индикатором включённой передачи, работать он будет с резистивным датчиком (ДПДЗ от ВАЗа) если кому интересно есть одна из записок сумашедшего по этому поводу, хоть как то поясняющая как это работает. https://yadi.sk/i/ZHWqcPxlkJozf
Аналогичное меню калибровки изображенное на мятом листе, предстанет взору на LCD дисплее если всё таки начать калибровку ...
Так же индикатор передачи имеет блинкер, который начинает блинковыть через заданное время, напоминая, что пора бы переключить передачу, активирован на 3 и 4 передаче. С этим блинкером было не всё так просто, отдельный код с таймером написаный отдельно прекрасно работал, однако если его добавить в основной код получались тормоза - слишком много было вызовов millis в разных участках кода. Решил переделать и сделать один таймер для всего, изменить пришлось не маленькую часть кода. Индикатор резерва (мигающий прямоугольник, видно на видео) тоже мигает через отдельный вызов таймера ;)
Видео - https://youtu.be/P9oFztnLAIU
Вот с этим кодом воевал дня 4 по вечерам :D, это конечно вершина айсберга, сами таймеры работаю тихо себе не заметно.
PHP код:
void _gear_switch_send(){
if (gear_first_start == 1) {
static byte g;
switch (GEAR_CURENT) {
case GEAR_3:
if (g != GEAR_3) {
time_gear_blinker = 0;
g = GEAR_3;
}
if (time_gear_blinker > timer_gear3) {
_Wire_Send(GEAR_3_BLINK);
}
else _Wire_Send(GEAR_3);
break;
case GEAR_4:
if (g != GEAR_4) {
time_gear_blinker = 0;
g = GEAR_4;
}
if (time_gear_blinker > timer_gear4) {
_Wire_Send(GEAR_4_BLINK);
}
else _Wire_Send(GEAR_4);
break;
default:
_Wire_Send(GEAR_CURENT);
g = GEAR_CURENT;
}
}
}
Пришла мне посылка, прислали много чего интересного, в том числе и монохроный дисплей SHARP, как оказалось он без подсветки. Место и выводы под светодиод есть, нужно только впаять светодиод нужного цвета. Дисплей абсолютно читаем без подсветки днём!
Вложение 18747
Возник нюанс с классами, как мне это провернуть ?
PHP код:
class CppStudio {
public:
void message() {
byte numder = numder ++;
}
};
void setup() {}
void loop() {
CppStudio objMessage;
objMessage.message();
// Как получить значение numder, не декларируя переменную в глобальную ?
}
Что именно нужно провернуть?
В коде комментарий, как в этом месте кода получить значение numder, не декларируя переменную в глобальную ?
Компилятор выдаёт ошибку error: 'numder' was not declared in this scope 'numder' was not declared in this scope
Кстати код готов, остались не значительные мелочи, можно лицезреть ... объективная критика приветствуется :D
PHP код:
#include <EEPROM.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include "RTClib.h"
// Префикс ( с )-const означает константы только в разделе настроек, далее в коде ( d )-define, остальные префиксы образованы от первой буквы
// или аббревиатуры типа данных переменной.
// Пины Ардуины не имеют прификса, принадлежность к пинам отражена в названии
//
// Настройки
#define cADDRESS_OF_SLAVE 0x7d // адрес ардуины - SLAVE, индикатор включённой скорости. У обоих ардуино обязательно должно быть общее питание +5В!
#define cTIME_MAIN_LOOP 2000 //mc, таймер запуска функций мониторинга
#define cARITHMETIC_AVERAGE_RATIO 10 // Средне арифметический коэффициент, уровня топлива в баке.
// интервал считывания уровня топлива в мили секундах = cTIME_MAIN_LOOP * cARITHMETIC_AVERAGE_RATIO
#define cTIMER_BLINK_FUEL 250 //мс, время одного соcтояния индикатора резерва
#define cTIMER_BLINK_TRIP 500 //
#define cTIMER_GEAR3 10 /2 // сек/2 блинкер третьей передачи, через сколько секунд индикатор начнёт мигать
#define cTIMER_GEAR4 15 /2 // сек/2 блинкер четвертой передачи, через сколько секунд индикатор начнёт мигать
#define cMIN_ABC_FUEL_LEVEL 0 // АЦП минимального показания датчика уровня топлива (устанавливается подстроеччным резистором) можно расчитать Uдатчика / 0.0048828125
#define cMAX_ABC_FUEL_LEVEL 1010// 238 // АЦП максимального показания датчика уровня топлива, расчитывается: Uдатчика / 0.0048828125
#define cADC_LIMIT_FUEL_LEVEL 1000 // Максимальный лимит АЦП датчика уровня топлива!
#define cTEMP_SNOW 4 // Tемпература при которой появится снежинка вместо градусника на дисплее
#define cCOEF_ABC_VOLT 0.03107 // Коэфициент АЦП контроля напряжения бортовой сети
#define cLOW_VOLTAGE 12 // Минимальное напряжение в борт сети до предупреждения
#define cHIGH_VOLTAGE 15 // Максимальное напряжение в борт сети до предупреждения
#define cSTART_ODOMETR 2000 // По истечения скольки импульсов ничинает считать время в пути, максимальное значение в dPULSES_TO_METERS
#define cENGINE_COLD 40 // Температура при которой с дисплея исчезнет символ " ! ", означающий что двигатель прогрелся
#define cTIMER_DELAY_EXIT 1000 * 1 // Сколько секунд микроконтроллер остаётся включённым, после отключения питания с IGN_ON__ANALOG_PIN
#define cERROR_TEMP_OIL 100 // Максимальная температура масла до предумпреждения
//
//
// Выводы платы ************************************************************************************************
// 0 Внешнее прерывание, датчик скорости 5 вольт *
// 1 *
// 2 // I2C *
// 3 // I2C *
#define GEAR_NEITRAL__DIGITAL_PIN 4 // Минус при включении нейтрали *
#define FAN_CONTROL_RUN__DIGITAL_PIN 5 // Минус при включении вентилятора *
// 6 *
// 7 Внещнее прерывание *
// 8 *
#define MONITOR_BRIGHTNESS__DIGITAL_PIN 9 // Яркость дисплея, ШИМ *
#define DS18B20_TEMP 10 // Сигнальный провод датчиков DS18B20 *
#define FAN_RELAY__DIGITAL_PIN 11 // Реле CH1 - включение реле вентилятора охлаждения *
#define RELAY_DELAY__DIGITAL_PIN 12 // Реле CH2 - задержка выключения *
#define LED_ERROR__DIGITAL_PIN 13 // Светодиод неисправности *
// *
#define FUEL_LEVEL__ANALOG_PIN 0 // Датчик уровня топлива *
#define GEAR__ANALOG_PIN 1 // Датчик положения передачи *
#define MONITOR_5VDC__ANALOG_PIN 2 // Контроль DC преобразователя, 5В *
// 3 // *
#define MENU_BUTTON__ANALOG_PIN 4 // Делитель напряжения *
#define IGN_ON__ANALOG_PIN 5 // Плюс при включении зажигания,контроль напряжения *
// *************************************************************************************************************
// Внешнее прерывание
//**********************************************************
//Leonardo-Pin * 3 * 2 * 0 * 1 * 7 *
//Номер прерывания * int.0 * int.1 * int.2 * int.3 * int.4 *
//**********************************************************
#define dINTERRUPT_SPEED 2
#define dINTERRUPT_BUTTON 4
// Массив с настройками для сохраниения в панять
#define UBOUND_EEPROM_ARRAY 14
int aEEPROM_DATA[UBOUND_EEPROM_ARRAY];
/*
0 - вентилятор отключение,
1 - вентилятор включение
2 - перегрев
3 - по температуре окр. воздуха
4 - уровень топлива проценты или литры
5 - ? температура включения вентилятора в зависимости от температуры [7]
6 - яркость дисплея
7 - коррекция температуры окр. воздуха
8 - коррекция темературы двигателя
9 - ацп нейтрали
10 - ацп верхней точки
11 - ацп нижней точки
12 - коррекция темературы масла
13 - ?
*/
int aExpon[UBOUND_EEPROM_ARRAY] = {
2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28};
//Дисплей
//Кастомные символы
uint8_t temp_ico[8] = {
B00100, B01010, B01010, B01110, B11111, B11111, B01110};
uint8_t akb1[8] = {
B00100, B11111, B10010, B10111, B10010, B10000, B11111};
uint8_t akb2[8] = {
B00100, B11111, B00001, B11101, B00001, B00001, B11111};
uint8_t fuel_empty[8] = {
B11111, B00000, B00000, B00000, B00000, B00000, B11111};
uint8_t fuel[8] = {
B11111, B11111, B11111, B11111, B11111, B11111, B11111};
uint8_t temp[8] = {
B00110, B01001, B01001, B00110, B00000, B00000, B00000};
#if defined(ARDUINO) && ARDUINO >= 100
#define printByte(args) write(args);
#else
#define printByte(args) print(args,BYTE);
#endif
//
LiquidCrystal_I2C lcd(0x27, 16, 2);
// RTC timer
RTC_DS1307 RTC;
//System Timer
unsigned long ulTimer_one_trip;
byte bDelay_exit;
unsigned long ulMillis_delay_exit;
byte bTimer_first_action;
//FAN, OUT_TEMP - DALLAS TEMPERATYRE
byte bRELAY_CURENT; // Текущее состояние реле
int iTEMP_CURENT_ENGIN; // Температура двигателя
int iOUT_TEMP_CURENT; // Температура наружного воздуха
int iOIL_TEMP_CURENT; // Температура масла
int tO, tE, tOil; // Промежуточные значения
OneWire oneWire(DS18B20_TEMP);
DallasTemperature ds(&oneWire);
DeviceAddress Thermometer1 = {
0x28, 0xFF, 0x34, 0x17, 0x52, 0x14, 0x00, 0x8E};
DeviceAddress Thermometer2 = {
0x28, 0xFF, 0xF9, 0x8B, 0x52, 0x14, 0x00, 0xB3};
DeviceAddress Thermometer3 = {
0x28, 0xFF, 0xF9, 0x8B, 0x52, 0x14, 0x00, 0xB3}; // Масло
// Константы для вывода ошибки на дисплей
#define dSYSTEM_ERROR_LOW_VOLTAGE 1
#define dSYSTEM_ERROR_HIGH_VOLTAGE 2
#define dSYSTEM_ERROR_HIGH_TEMPERARURE 3
#define dSYSTEM_ERROR_HIGH_TEMPERARURE_OIL 4
byte bSYSTEM_ERROR = 0; // прерывание цикла дисплея если ошибка в системе, 0 - нет ошибок
// Меню
#define dBUTTON_RESET_ERROR 670 // АЦП кнопок меню
#define dBUTTON_MENU 400
#define dBUTTON_DUBLE 337
#define dBUTTON_NOT_PRESSED 1023
byte bBOUNCE_BUTTON_RESET_ERROR, bBOUNCE_BUTTON_MENU; // Для исключения переключения меню при удержании кнопки, дребезга контактов.
byte bKEY; // Код нажатой клавиши, срабатыввает при отпускании кнопки
byte bMENU_ACTIV; // При нажатии на кнопку KEY_MENU_LIST - bMENU_ACTIV = 1, KEY_MENU_OK - bMENU_ACTIV = 0
byte bDISPLAY_2;
//
byte bINDEX_MENU; // Номер страницы меню
#define dMENU_ITEM_OIL_TEMP 1
#define dMENU_ITEM_ENG_TEMP 2
#define dMENU_ITEM_BATT 3
#define dMENU_ITEM_FAN_MANUAL 4 // 1 - 4 Выход из меню двумя кнопками без сохранения в eeprom
#define dMENU_ITEM_DISPLAY_BRIGHTNESS 5 // 5 ... Выход из меню двумя кнопками c сохранением в eeprom
#define dMENU_ITEM_COR_OIL_TEMP 6
#define dMENU_ITEM_COR_OUT_TEMP 7
#define dMENU_ITEM_COR_ENG_TEMP 8
#define dMENU_ITEM_FAN_ON 9
#define dMENU_ITEM_FAN_OFF 10
#define dMENU_ITEM_ERROR_TEMP 11
#define dMENU_ITEM_FUEL_PERSENT_AND_LITER 12
#define dMENU_ITEM_CALIBR_GEAR_POS 13
//#define 14
#define dMENU_ITEM_END 15
byte menu_gear_calibration; // Многоуровневое меню калибровки передач
byte fan_manual = 0; // Ручное включение вентилятора из меню
byte persent_and_liter; // Проценты или литры на главном экране
byte DISPLAY_BRIGHTNESS; // Яркость дисплея 0 - 255
//Контроль напряжения
byte bSYSTEM_VOLTAGE;
float fDIS_SYSTEM_VOLTAGE; // Вывод на дисплей
byte bDC_VOLTAGE; // Контроль напряжения 5в линии DC преобразователя
// Константы вентилятора охлаждения
#define dLOW_COLING_FAN 2
#define dHIGH_COLING_FAN 3
#define dERROR_COLING_FAN 4
// Уровень топлива
byte bFUEL_LEVEL = 101, bIndex_mean;
int iArithmetic_mean; // Средне арифметическое уровня топлива
// Пройдения дистанция
boolean bolOdometr_menu_blink; // Мигание пустых делений, времени в пути
byte bOdometr_one_run;
volatile word vwDistance_one_trip; // +1 с каждым внешним прерыванием
#define dPULSES_TO_METERS 5625 // 5625 импульсов - 100 метров
volatile word vwInteger, vwAfter_point; // Делим число TRIP на две части (vwInteger.vwAfter_point)
//Индикатор включенной передачи
int iUP_GEAR, iN_GEAR, iDOWN_GEAR;
byte bGEAR_CURENT = 1;
int iUp_or_down = 1;
byte bGear_first_start = 0;
boolean fb;
byte time_gear_blinker;
// Константы для передачи значения включённой передачи по i2c шине.
#define dGEAR_H 0
#define dGEAR_1 1
#define dGEAR_2 2
#define dGEAR_3 3
#define dGEAR_4 4
#define dGEAR_5 5
#define dGEAR_H_BLINK 6
#define dGEAR_3_BLINK 7
#define dGEAR_4_BLINK 8
#define dGEAR_5_BLINK 9
//_______________________________________________________________________________________________________________
boolean bolTimer_state1, bolTimer_state2, bolTimer_state3, bolTimer_state4;
class _ClassTimer
{
// Переменные - члены класса
// Инициализируются при запуске
int number_timer; // номер таймера
long OnTime; // время включения в миллисекундах
long OffTime; // время, когда светодиод выключен
// Текущее состояние
int timer_state; // состояние ВКЛ/ВЫКЛ
unsigned long previousMillis; // последний момент смены состояния
// Конструктор создает экземпляр _ClassTimer и инициализирует
// переменные-члены класса и состояние
public:
_ClassTimer(int timer, long on, long off)
{
number_timer = timer;
pinMode(number_timer, OUTPUT);
OnTime = on;
OffTime = off;
timer_state = LOW;
previousMillis = 0;
}
int Update()
{
unsigned long currentMillis = millis(); // текущее время в миллисекундах
if ((timer_state == HIGH) && (currentMillis - previousMillis >= OnTime))
{
timer_state = LOW; // выключаем
previousMillis = currentMillis; // запоминаем момент времени
if (number_timer == 1) bolTimer_state1 = !bolTimer_state1;
else if (number_timer == 2) bolTimer_state2 = !bolTimer_state2;
else if (number_timer == 3) bolTimer_state3 = !bolTimer_state3;
else if (number_timer == 4) bolTimer_state4 = !bolTimer_state4;
}
else if ((timer_state == LOW) && (currentMillis - previousMillis >= OffTime))
{
timer_state = HIGH; // выключаем
previousMillis = currentMillis ; // запоминаем момент времени
}
}
};
// Устанавливаем параметры для таймера
_ClassTimer TimeR1(1, cTIME_MAIN_LOOP, cTIME_MAIN_LOOP); // Таймер считывания функций мониторинга
_ClassTimer Timer2(2, cTIMER_BLINK_TRIP, cTIMER_BLINK_TRIP); // TRIP мигание пустых делений
_ClassTimer Timer3(3, cTIMER_BLINK_FUEL, cTIMER_BLINK_FUEL); // Резерв топлива
_ClassTimer Timer4(4, 1000, 1000); // Таймер блинкера передач, счётчик приращения (time_gear_blinker) работает только по ВКЛЮЧЕНИЮ таймера
void setup() {
attachInterrupt(dINTERRUPT_SPEED, _Interrupt_Speed, FALLING); // FALLING HIGH -> LOW
//attachInterrupt(dINTERRUPT_BUTTON, _Interrupt_Button, FALLING ); // FALLING HIGH -> LOW
pinMode(FAN_RELAY__DIGITAL_PIN, OUTPUT);
digitalWrite(FAN_RELAY__DIGITAL_PIN, !LOW);
pinMode(RELAY_DELAY__DIGITAL_PIN, OUTPUT);
digitalWrite(RELAY_DELAY__DIGITAL_PIN, LOW);
pinMode(GEAR_NEITRAL__DIGITAL_PIN, INPUT); // Минус при включении нейтрали
digitalWrite(GEAR_NEITRAL__DIGITAL_PIN, HIGH);
pinMode(FAN_CONTROL_RUN__DIGITAL_PIN, INPUT); // Минус при включении вентилятора
digitalWrite(FAN_CONTROL_RUN__DIGITAL_PIN, HIGH);
pinMode(MONITOR_BRIGHTNESS__DIGITAL_PIN, OUTPUT); // Яркость дисплея
pinMode(LED_ERROR__DIGITAL_PIN, OUTPUT); // Светодиод неисправности
pinMode(FUEL_LEVEL__ANALOG_PIN, INPUT);
Serial.begin(9600);
Wire.begin();
RTC.begin();
lcd.init();
lcd.backlight();
lcd.createChar(1, temp_ico);
lcd.createChar(2, akb1);
lcd.createChar(3, akb2);
lcd.createChar(4, fuel);
lcd.createChar(5, temp);
lcd.createChar(6, fuel_empty);
for (int i = 0; i <= (UBOUND_EEPROM_ARRAY - 1); i++)aEEPROM_DATA[i] = _eeprom_read(aExpon[i]);
//Заполняем переменные из памяти
persent_and_liter = aEEPROM_DATA[4];
iUP_GEAR = aEEPROM_DATA[13];
iN_GEAR = aEEPROM_DATA[9];
iDOWN_GEAR = aEEPROM_DATA[11];
if (aEEPROM_DATA[6] < 10) DISPLAY_BRIGHTNESS = 255;
else DISPLAY_BRIGHTNESS = aEEPROM_DATA[6];
}
void loop() {
TimeR1.Update();
Timer2.Update();
Timer3.Update();
Timer4.Update();
if (bolTimer_state1 == false) { // Таймер считывания функций мониторинга
//Serial.println("bolTimer_state1");
if (bTimer_first_action == 0) { // Первый запуск
bTimer_first_action = 1;
for (int i = 0; i <= cARITHMETIC_AVERAGE_RATIO + 1; i++) _fuel_level();
}
_delay_read_sensor();
bolTimer_state1 = !bolTimer_state1;
}
if (bolTimer_state2 == false) {
//Serial.println("bolTimer_state2"); // TRIP мигание пустых делений
bolTimer_state2 = !bolTimer_state2;
}
if (bolTimer_state3 == false) { // Резерв топлива
fb = !fb;
//_fuel_level_blink();
//Serial.println("bolTimer_state3");
bolTimer_state3 = !bolTimer_state3;
}
if (bolTimer_state4 == false) { // Таймер блинкера передач
time_gear_blinker ++;
bolTimer_state4 = !bolTimer_state4;
}
if (! RTC.isrunning()) {
//Serial.println("RTC is NOT running!");
RTC.adjust(DateTime(__DATE__, __TIME__));
}
analogWrite(MONITOR_BRIGHTNESS__DIGITAL_PIN, DISPLAY_BRIGHTNESS);
ds.requestTemperatures(); // считываем температуру с датчиков температуры
iOUT_TEMP_CURENT = ds.getTempC(Thermometer2) - aEEPROM_DATA[7] + 5; // Температуру окр.воздуха
iTEMP_CURENT_ENGIN = ds.getTempC(Thermometer1) - aEEPROM_DATA[8] + 5; // Температуру двигателя
iOIL_TEMP_CURENT = ds.getTempC(Thermometer1) - aEEPROM_DATA[12] + 5; // Температуру масла
bSYSTEM_VOLTAGE = analogRead(IGN_ON__ANALOG_PIN) * cCOEF_ABC_VOLT ;
//bDC_VOLTAGE = analogRead(MONITOR_5VDC__ANALOG_PIN);
if (bMENU_ACTIV == 0) _input_dusplay_SUSTEM_ERROR(bSYSTEM_ERROR);// Обрабатываем переменную bSYSTEM_ERROR, выводим сообщение на дисплей
_gear_switch(); // записываем в bGEAR_CURENT текущую передачу
if (vwDistance_one_trip > cSTART_ODOMETR) bOdometr_one_run = 1;
DateTime now = RTC.now();
if (ulTimer_one_trip == 0 && vwDistance_one_trip > cSTART_ODOMETR) ulTimer_one_trip = ( ((23 - now.hour()) * 3600) + ((59 - now.minute()) * 60) + (60 - now.second()) );
DateTime future (now.unixtime() + ulTimer_one_trip);
static long g = 100500; // Зачем здесь это ? +100500 баллов за внимательность :)!
// Включённая передача
// Нейтральная передача не изменяет номер передачи в bGEAR_CURENT, она является лишь "маской"
if (bGear_first_start == 0 && digitalRead(GEAR_NEITRAL__DIGITAL_PIN) == HIGH) _Wire_Send(dGEAR_H_BLINK);
if (digitalRead(GEAR_NEITRAL__DIGITAL_PIN) == LOW) {
_Wire_Send(dGEAR_H);
g = dGEAR_H;
bGear_first_start = 1; // Сигнал к началу индикации передач
}
else {
if (g == dGEAR_H && bGEAR_CURENT == dGEAR_3) bGEAR_CURENT = 2; // Эмулируем выход из нейтрали на вторую передачу.
else {
_gear_switch_send(); // запускаем счётчик передач, отправляем в порт номер передачи
g = 100500;
}
}
_treat_abc_button(); //Считываем нажатую клавишу
_delay_exit(); // Задержка выключения
_menu_lcd();
// ------------------------------------------------------------ Главный экран --------------------------------------------------------
if (bSYSTEM_ERROR == 0 && bMENU_ACTIV == 0) { // Если нет ошибок и статус меню не активно! выводм текшие данные на дисплей
if (bDISPLAY_2 == 1) { // Дисплей 2
char ch_trip[15], ch_time[10];
sprintf(ch_time, "%i:%02i:%02i ", now.hour(), now.minute(), now.second());
lcd.setCursor(0, 0);
lcd.print(ch_time);
lcd.setCursor(0, 1);
sprintf(ch_trip, "%s - %i.%i ", "Trip", vwInteger, vwAfter_point);
lcd.print(ch_trip);
for (int i = 0; i <= cARITHMETIC_AVERAGE_RATIO + 1; i++) _fuel_level(); // Обновляем уровень топлива без задержки
}
else { // Основной дисплей
char ch_ch[5];
lcd.setCursor(12, 1);
if (aEEPROM_DATA[4] == 1) { // Литры
sprintf(ch_ch, "%01i%s", _persent_to_liter(), "L");
lcd.print(ch_ch);
_Fuel_Progress_Bar();
}
else { // Проценты
sprintf(ch_ch, "%03i%s", bFUEL_LEVEL, "%");
lcd.print(ch_ch);
_Fuel_Progress_Bar();
}
char ch_time[10];
//if (bOdometr_one_run == 0 && stat_timer1 == !LOW) { // Отображаем на дисплее пустые деления
// if (bolOdometr_menu_blink == true) {lcd.setCursor(0, 0);lcd.print("-:--:--");}
// else if (bolOdometr_menu_blink == false) {lcd.setCursor(0, 0); lcd.print(" ");}
// bolOdometr_menu_blink = !bolOdometr_menu_blink;
// stat_timer1 = !stat_timer1;
// }
//else
if (bOdometr_one_run == 1) { // Запускаем таймер
sprintf(ch_time, "%i:%02i:%02i", future.hour(), future.minute(), future.second());
lcd.setCursor(0, 0);
lcd.print(ch_time);
_voltage_scan(bSYSTEM_VOLTAGE); // Считываем напряжение, записываем в переменную bSYSTEM_ERROR статус системы
}
lcd.setCursor(10, 0); // 10 символ, если включен вентилятор
if (digitalRead(FAN_CONTROL_RUN__DIGITAL_PIN) == LOW) lcd.print("F");
else lcd.print(" ");
lcd.setCursor(11, 0); // 11 символ, двигатель не прогрет
if (iTEMP_CURENT_ENGIN < cENGINE_COLD) lcd.print("!");
else lcd.print(" ");
lcd.setCursor(12, 0); // 12 ... символ, температура наружного воздуха
if (tO > cTEMP_SNOW) lcd.write(1); //t
else if (tO <= cTEMP_SNOW) lcd.print("*"); //*
lcd.print(tO);
lcd.write(5);
}
}
}
//Функции ****************************************************************************************************************************************************************
// Функции состоящие из прописных букв запускаются только в одном участке кода
//void _Interrupt_Button() {
//}
void _delay_read_sensor() {
tE = iTEMP_CURENT_ENGIN;
tO = iOUT_TEMP_CURENT;
tOil = iOIL_TEMP_CURENT;
if (bINDEX_MENU != dMENU_ITEM_FAN_MANUAL) _engine_temp_monitoring(iTEMP_CURENT_ENGIN);// Считываем температуру, включаем или выключаем реле- функцией _RelayColingFan
// Если активен пунк меню dMENU_ITEM_FAN_MANUAL не запускаем функцию, во избежании постоянного переключения реле!
_oil_temp_monitoring(iOIL_TEMP_CURENT);
fDIS_SYSTEM_VOLTAGE = analogRead(IGN_ON__ANALOG_PIN) * cCOEF_ABC_VOLT ; // Считываем для дисплея напряжение в формате 10.00
_fuel_level();
}
void _treat_abc_button() {
bKEY = _read_button_key(analogRead(MENU_BUTTON__ANALOG_PIN)); // Считываем АЦП входа, обрабатываем нажатую кнопку
Serial.println(analogRead(MENU_BUTTON__ANALOG_PIN));
if (bKEY == 1) { // dBUTTON_RESET_ERROR кнопка сброса ошибок
if (bSYSTEM_ERROR) { // Если была ошибка, сбрасываем
bSYSTEM_ERROR = 0;
digitalWrite(LED_ERROR__DIGITAL_PIN, LOW); // Выключаем светодиод ошибки
lcd.clear();
bDISPLAY_2 = 0;
}
else if (bSYSTEM_ERROR == 0 && bMENU_ACTIV == 0) { // Отображаем дополнительное меню
bDISPLAY_2 = 1;
lcd.clear();
}
}
else if (bKEY == 2) { // dBUTTON_MENU
if (bDISPLAY_2 == 1) {
bMENU_ACTIV = 0;
bDISPLAY_2 = 0;
lcd.clear();
}
else if (bDISPLAY_2 == 0) {
bMENU_ACTIV = 1;
lcd.clear();
bDISPLAY_2 = 0;
}
}
}
int _read_button_key(int key) {
if (key > dBUTTON_RESET_ERROR && key < dBUTTON_RESET_ERROR + 40) bBOUNCE_BUTTON_RESET_ERROR = 1;
else if (key > dBUTTON_MENU && key < dBUTTON_MENU + 40) bBOUNCE_BUTTON_MENU = 1;
else if (key > dBUTTON_DUBLE && key < dBUTTON_DUBLE + 40) {
bBOUNCE_BUTTON_RESET_ERROR = 0;
return 3;
}
if (analogRead(MENU_BUTTON__ANALOG_PIN) == dBUTTON_NOT_PRESSED && bBOUNCE_BUTTON_RESET_ERROR + bBOUNCE_BUTTON_MENU == 1) { // Исли ацп = 1023 и нажата хоть одна кнопка
if (bBOUNCE_BUTTON_RESET_ERROR == 1) {
bBOUNCE_BUTTON_RESET_ERROR = 0;
return 1;
}
else if (bBOUNCE_BUTTON_MENU == 1) {
bBOUNCE_BUTTON_MENU = 0;
return 2;
}
}
}
void _delay_exit() {
if (analogRead(IGN_ON__ANALOG_PIN) > 100 && bDelay_exit == 2) {
bDelay_exit == 0;
ulMillis_delay_exit = millis() + cTIMER_DELAY_EXIT;
lcd.setCursor(9, 0);
lcd.print(" ");
}
if (analogRead(IGN_ON__ANALOG_PIN) < 100) { // Напряжение на контрольном пине упало до нуля
if (bSYSTEM_ERROR == 0 && bMENU_ACTIV == 0) {
lcd.setCursor(9, 0);
lcd.print("E");
}
if (bDelay_exit == 0) {
bDelay_exit = 2; // Значение 2 возможно только из этой строки
ulMillis_delay_exit = millis() + cTIMER_DELAY_EXIT;
Serial.println(ulMillis_delay_exit);
}
if (millis() > ulMillis_delay_exit && bDelay_exit == 2) digitalWrite(RELAY_DELAY__DIGITAL_PIN, HIGH); // выключаем реле
}
}
void _Wire_Send(byte data) {
Wire.beginTransmission(cADDRESS_OF_SLAVE);
Wire.write(data);
Wire.endTransmission();
}
void _gear_switch_send() {
if (bGear_first_start == 1) {
static byte g;
switch (bGEAR_CURENT) {
case dGEAR_3:
if (g != dGEAR_3) {
time_gear_blinker = 0;
g = dGEAR_3;
}
if (time_gear_blinker > cTIMER_GEAR3) {
_Wire_Send(dGEAR_3_BLINK);
}
else _Wire_Send(dGEAR_3);
break;
case dGEAR_4:
if (g != dGEAR_4) {
time_gear_blinker = 0;
g = dGEAR_4;
}
if (time_gear_blinker > cTIMER_GEAR4) {
_Wire_Send(dGEAR_4_BLINK);
}
else _Wire_Send(dGEAR_4);
break;
default:
_Wire_Send(bGEAR_CURENT);
g = bGEAR_CURENT;
}
}
}
void _Interrupt_Speed() {
vwDistance_one_trip++;
if (vwDistance_one_trip >= dPULSES_TO_METERS) {
vwDistance_one_trip = 0;
vwAfter_point ++;
if (vwAfter_point > 9) {
vwAfter_point = 0;
vwInteger ++;
}
}
}
void _gear_switch() {
int adc = analogRead(GEAR__ANALOG_PIN);
if (adc <= iDOWN_GEAR) iUp_or_down = iDOWN_GEAR;
if (adc >= iUP_GEAR) iUp_or_down = iUP_GEAR;
int diap = constrain(adc, aEEPROM_DATA[9] - 10, aEEPROM_DATA[9] + 10); // диапазон нейтральной передачи, считываем из памяти
if (adc == diap && iUp_or_down == iDOWN_GEAR && bGEAR_CURENT != 1) {
bGEAR_CURENT--;
iUp_or_down = 1;
}
else if (adc == diap && iUp_or_down == iUP_GEAR && bGEAR_CURENT < 5) {
bGEAR_CURENT++;
iUp_or_down = 1;
}
}
void _Save_Settings_in_Eeprom() {
if (bINDEX_MENU > dMENU_ITEM_FAN_MANUAL) {
for (int i = 0; i <= (UBOUND_EEPROM_ARRAY - 1); i++) _eeprom_write(aEEPROM_DATA[i], aExpon[i]); // Пишем в память настройки.
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Save settings");
fan_manual = 0;
delay(2000);
}
bMENU_ACTIV = 0;
bINDEX_MENU = 0;
bDISPLAY_2 = 0;
lcd.clear();
}
byte _persent_to_liter() { // Перерасчёт из процентов в литры
#define UBOUND 15 // Колличество элементов в массиве
byte ArrayLiterTank[UBOUND] = {
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
byte ArrayPersent[UBOUND] = {
4, 8, 15, 20, 20, 35, 48, 55, 65, 70, 72, 78, 82, 93, 100 };
for (int i = 0; i <= UBOUND; i++) {
if (ArrayPersent[i] >= bFUEL_LEVEL) return ArrayLiterTank[i];
}
}
void _Fuel_Progress_Bar() { // Прогресс бар
#define INDEX 10 // количество делений уровня топлива
int f = bFUEL_LEVEL / INDEX;
if (bFUEL_LEVEL <= INDEX) {
if (fb == false) {
lcd.setCursor(0, 1);
lcd.write(4);
}
else {
lcd.setCursor(0, 1);
lcd.print(" ");
}
for (int ii = 1; ii <= 9; ii++) { // пустые деления
lcd.setCursor(ii, 1);
lcd.write(6);
}
}
else if (bFUEL_LEVEL > INDEX) {
for (int i = 0; i <= f; i++) { // Цикл целые деления
lcd.setCursor(i - 1, 1);
lcd.write(4);
}
for (int ii = f; ii <= 9; ii++) { // пустые деления
lcd.setCursor(ii, 1);
lcd.write(6);
}
}
}
void _fuel_level() {
int val = analogRead(FUEL_LEVEL__ANALOG_PIN);
if (val > cADC_LIMIT_FUEL_LEVEL) val = cADC_LIMIT_FUEL_LEVEL; // ограничение уровня напряжения АЦП
iArithmetic_mean += map(val, cMIN_ABC_FUEL_LEVEL, cMAX_ABC_FUEL_LEVEL, 100, 0);
bIndex_mean ++;
if (bIndex_mean >= cARITHMETIC_AVERAGE_RATIO) {
bFUEL_LEVEL = iArithmetic_mean / cARITHMETIC_AVERAGE_RATIO;
bIndex_mean = 0;
iArithmetic_mean = 0;
}
}
byte _input_dusplay_SUSTEM_ERROR(byte e) { // Обрабытываем переменную bSYSTEM_ERROR, выводим сообщение на дисплей
switch (e) {
case dSYSTEM_ERROR_LOW_VOLTAGE:
lcd.clear();
digitalWrite(LED_ERROR__DIGITAL_PIN, HIGH);
lcd.clear();
lcd.setCursor(2, 1);
lcd.print("LOW VOLTAGE");
break;
case dSYSTEM_ERROR_HIGH_VOLTAGE:
lcd.clear();
digitalWrite(LED_ERROR__DIGITAL_PIN, HIGH);
lcd.clear();
lcd.setCursor(2, 1);
lcd.print("HIGH VOLTAGE") ;
break;
case dSYSTEM_ERROR_HIGH_TEMPERARURE:
lcd.clear();
digitalWrite(LED_ERROR__DIGITAL_PIN, HIGH);
lcd.clear();
lcd.setCursor(2, 1);
lcd.print("HIGH TEMP") ;
break;
case dSYSTEM_ERROR_HIGH_TEMPERARURE_OIL:
lcd.clear();
digitalWrite(LED_ERROR__DIGITAL_PIN, HIGH);
lcd.clear();
lcd.setCursor(2, 1);
lcd.print("OIL HIGH TEMP") ;
break;
}
}
byte _voltage_scan(byte volt) { // Считываем напряжение, записываем в переменную bSYSTEM_ERROR статус системы
if (volt <= cLOW_VOLTAGE && volt > 2) {
bSYSTEM_ERROR = dSYSTEM_ERROR_LOW_VOLTAGE;
return 0;
}
if (volt >= cHIGH_VOLTAGE) {
bSYSTEM_ERROR = dSYSTEM_ERROR_HIGH_VOLTAGE;
return 0;
}
if (volt < cHIGH_VOLTAGE && volt > cLOW_VOLTAGE) {
return 0;
}
}
int _engine_temp_monitoring(int adc) { // Считываем температуру, включаем или выключаем реле- функцией _RelayColingFan
if (adc > aEEPROM_DATA[2] ) { // Если перегрев двигателя записываем в переменную bSYSTEM_ERROR статус системы
_RelayColingFan(dERROR_COLING_FAN);
bSYSTEM_ERROR = dSYSTEM_ERROR_HIGH_TEMPERARURE;
return dERROR_COLING_FAN;
}
if (adc > aEEPROM_DATA[1]) {
_RelayColingFan(dHIGH_COLING_FAN); // Включаем
return dHIGH_COLING_FAN;
}
if (adc < aEEPROM_DATA[0]) {
_RelayColingFan(dLOW_COLING_FAN); // Выключаем
return dLOW_COLING_FAN;
}
}
int _oil_temp_monitoring(int adc) { // Считываем температуру масла
if (adc > cERROR_TEMP_OIL) { // Если перегрев масла записываем в переменную bSYSTEM_ERROR статус системы
//_RelayColingFan(dERROR_COLING_FAN);
bSYSTEM_ERROR = dSYSTEM_ERROR_HIGH_TEMPERARURE_OIL;
return 1;
}
}
int _RelayColingFan(int atr) { // Включение реле согласно принятому значению температуры
switch (atr)
{
case dLOW_COLING_FAN:
digitalWrite(FAN_RELAY__DIGITAL_PIN, !LOW); // Выключено
bRELAY_CURENT = digitalRead(FAN_RELAY__DIGITAL_PIN);
break;
case dHIGH_COLING_FAN:
digitalWrite(FAN_RELAY__DIGITAL_PIN, !HIGH); // Включено
bRELAY_CURENT = digitalRead(FAN_RELAY__DIGITAL_PIN);
break;
case dERROR_COLING_FAN:
digitalWrite(FAN_RELAY__DIGITAL_PIN, !HIGH); // Включено
bRELAY_CURENT = digitalRead(FAN_RELAY__DIGITAL_PIN);
break;
default: //
//
break;
}
}
unsigned int _eeprom_read(int cell) { // Чтение из еепром
unsigned int res = 0;
res = (EEPROM.read(cell) << 8) | EEPROM.read(cell - 1);
return res;
}
void _eeprom_write(unsigned int val, int cell) { // Запись в еепром
EEPROM.write(cell - 1, (val & 0xFF)); //пишем младший байт
EEPROM.write(cell, ((val & 0xFF00) >> 8));//пишем старший байт
}
void _menu_lcd() {
if (bMENU_ACTIV == 1) { // ----------------------------------------- Mеню 1 --------------------------------------------------------------------------
if (bKEY == 3) _Save_Settings_in_Eeprom(); // dBUTTON_DUBLE выходим из меню НАСТРОЕК, двойное нажатие клавиш
if (bKEY == 2) bINDEX_MENU += 1; // dBUTTON_MENU, листаем страницы меню
lcd.setCursor(0, 0);
if (bINDEX_MENU == dMENU_ITEM_OIL_TEMP) { // Текушие данные без настроек
lcd.print(bINDEX_MENU + String("Oil temp" ));
lcd.setCursor(0, 1);
lcd.write(1);
lcd.print(tOil);
lcd.write(5);
lcd.setCursor(0, 1);
}
else if (bINDEX_MENU == dMENU_ITEM_ENG_TEMP) {
lcd.print(bINDEX_MENU + String(":Engine temp"));
lcd.setCursor(0, 1);
lcd.write(1);
lcd.print(tE);
lcd.write(5);
lcd.setCursor(7, 1);
}
else if (bINDEX_MENU == dMENU_ITEM_BATT) {
lcd.print(bINDEX_MENU + String("BATT"));
lcd.setCursor(0, 1);
lcd.write(2);
lcd.write(3);
lcd.print(fDIS_SYSTEM_VOLTAGE);
lcd.print("V");
}
else if (bINDEX_MENU == dMENU_ITEM_FAN_MANUAL) { // Управление вентилятором
if (bKEY == 1) {
fan_manual++;
if (fan_manual == 2) fan_manual = 0;
lcd.clear();
}
lcd.print(bINDEX_MENU + String(":Fan manual" ));
lcd.setCursor(0, 1);
if (fan_manual == 1) {
lcd.print("[ON]");
_RelayColingFan(dHIGH_COLING_FAN);
if (digitalRead(FAN_CONTROL_RUN__DIGITAL_PIN) == LOW ) {
lcd.setCursor(7, 1);
lcd.print("<Run>");
}
else lcd.setCursor(7, 1);
lcd.print(" ");
}
if (fan_manual == 0) {
lcd.print("[OFF]");
_RelayColingFan(dLOW_COLING_FAN);
}
}
else if (bINDEX_MENU == dMENU_ITEM_DISPLAY_BRIGHTNESS) { // ------------------------------------------------------------
if (bKEY == 1) {
DISPLAY_BRIGHTNESS += 16 ;
if (DISPLAY_BRIGHTNESS > 255) DISPLAY_BRIGHTNESS = 16;
analogWrite(MONITOR_BRIGHTNESS__DIGITAL_PIN, DISPLAY_BRIGHTNESS);
aEEPROM_DATA[6] = DISPLAY_BRIGHTNESS;
lcd.clear();
}
lcd.print(bINDEX_MENU + String(":BRIGHTNESS"));
lcd.setCursor(0, 1);
int f = map(DISPLAY_BRIGHTNESS, 0, 255, 0, 16);
for (int i = 0; i <= f; i++) { // Цикл целые деления
lcd.setCursor(i, 1);
lcd.write(4);
}
for (int ii = f; ii <= 16; ii++) { // пустые деления
lcd.setCursor(ii, 1);
lcd.write(6);
}
}
else if (bINDEX_MENU == dMENU_ITEM_COR_OIL_TEMP) {
if (bKEY == 1) {
aEEPROM_DATA[12]++;
if (aEEPROM_DATA[12] > 10) aEEPROM_DATA[12] = 0;
lcd.clear();
}
lcd.print(bINDEX_MENU + String(":Oil temp correct" ));
lcd.setCursor(0, 1);
lcd.write(1);
lcd.print(String(" +[") + aEEPROM_DATA[12] + String("]-" ));
}
else if (bINDEX_MENU == dMENU_ITEM_COR_OUT_TEMP) {
if (bKEY == 1) {
aEEPROM_DATA[7]++;
if (aEEPROM_DATA[7] > 10) aEEPROM_DATA[7] = 0;
lcd.clear();
}
lcd.print(bINDEX_MENU + String(":Outdoor temp" ));
lcd.setCursor(0, 1);
lcd.write(1);
lcd.print(String(" +[") + aEEPROM_DATA[7] + String("]-" ));
}
else if (bINDEX_MENU == dMENU_ITEM_COR_ENG_TEMP) {
if (bKEY == 1) {
aEEPROM_DATA[8]++;
if (aEEPROM_DATA[8] > 10) aEEPROM_DATA[8] = 0;
lcd.clear();
}
lcd.print(bINDEX_MENU + String(":Engine temp" ));
lcd.setCursor(0, 1);
lcd.write(1);
lcd.print(String(" +[") + aEEPROM_DATA[8] + String("]-"));
}
else if (bINDEX_MENU == dMENU_ITEM_FAN_ON) {
if (bKEY == 1) {
aEEPROM_DATA[1]++;
if (aEEPROM_DATA[1] < 80) aEEPROM_DATA[1] = 80;
if (aEEPROM_DATA[1] > 106) aEEPROM_DATA[1] = 80;
lcd.clear();
}
lcd.print(bINDEX_MENU + String(":Fan on"));
lcd.setCursor(0, 1);
lcd.write(1);
lcd.print(String(" +[") + aEEPROM_DATA[1] + String("]-"));
}
else if (bINDEX_MENU == dMENU_ITEM_FAN_OFF) {
if (bKEY == 1) {
aEEPROM_DATA[0]++;
if (aEEPROM_DATA[0] < 80) aEEPROM_DATA[0] = 80;
if (aEEPROM_DATA[0] > 100) aEEPROM_DATA[0] = 80;
lcd.clear();
}
lcd.print(bINDEX_MENU + String(":Fan off"));
lcd.setCursor(0, 1);
lcd.write(1);
lcd.print(String(" -[") + aEEPROM_DATA[0] + String("]-"));
}
else if (bINDEX_MENU == dMENU_ITEM_ERROR_TEMP) {
if (bKEY == 1) {
aEEPROM_DATA[2]++;
if (aEEPROM_DATA[2] < 80) aEEPROM_DATA[2] = 80;
if (aEEPROM_DATA[2] > 120) aEEPROM_DATA[2] = 100;
lcd.clear();
}
lcd.print(bINDEX_MENU + String(":Error temp")); // PROGMEM !
lcd.setCursor(0, 1);
lcd.write(1);
lcd.print(String(" +[") + aEEPROM_DATA[2] + String("]"));
}
else if (bINDEX_MENU == dMENU_ITEM_FUEL_PERSENT_AND_LITER) {
if (bKEY == 1) {
persent_and_liter++;
if (persent_and_liter == 2) persent_and_liter = 0;
lcd.clear();
}
lcd.print(bINDEX_MENU + String(":Persent/liter"));
lcd.setCursor(0, 1);
if (persent_and_liter == 1) {
aEEPROM_DATA[4] = persent_and_liter;
lcd.print("[L]");
}
else if (persent_and_liter == 0) {
aEEPROM_DATA[4] = persent_and_liter;
lcd.print("[%]");
}
lcd.setCursor(11, 1) ; // Выводим реальный уровень топлива без задержки
lcd.print(map(analogRead(FUEL_LEVEL__ANALOG_PIN), 0, 1023, 0, 100));
}
else if (bINDEX_MENU == dMENU_ITEM_CALIBR_GEAR_POS) {
if (menu_gear_calibration == 0) { // -------- 0 <<<<< ----- первый шаг, спрашиваем - калибровать ?
//lcd.setCursor(0, 0);
lcd.print(bINDEX_MENU + String(":Calibrate gear"));
lcd.setCursor(0, 1);
lcd.print("[Calibrate]");
lcd.setCursor(13, 1);
lcd.print( String("G:") + bGEAR_CURENT );
if (bKEY == 1) {
menu_gear_calibration = 1;
lcd.clear();
return;
}
} // -------- 0
if (menu_gear_calibration == 1) { // -------- 1 <<<<< ----- второй шаг, просим установить коробу в N ?
//lcd.setCursor(0, 0);
lcd.print("Set gear - N");
if ( digitalRead(GEAR_NEITRAL__DIGITAL_PIN) == LOW) {
lcd.setCursor(0, 1);
lcd.print("[Ok]" + String(analogRead(GEAR__ANALOG_PIN)));
if (bKEY == 1) {
menu_gear_calibration = 2; // <<<<< ----- третий шаг, сохраняем ацп средней точки
aEEPROM_DATA[9] = analogRead(GEAR__ANALOG_PIN);
Serial.println(aEEPROM_DATA[9] + String("- N save"));
lcd.clear();
return;
}
}
else {
lcd.setCursor(0, 1);
lcd.print(" ");
}
} // -------- 1
if (menu_gear_calibration == 2) { // -------- 2
//lcd.setCursor(0, 0);
lcd.print("Hold gear - Up");
lcd.setCursor(0, 1);
lcd.print("[Ok]" + String(analogRead(GEAR__ANALOG_PIN)));
if (bKEY == 1) {
menu_gear_calibration = 3; // <<<<< ----- четвёртый шаг, сохраняем ацп верхней точки
aEEPROM_DATA[13] = analogRead(GEAR__ANALOG_PIN);
Serial.println(aEEPROM_DATA[13] + String("- Up save"));
lcd.clear();
return;
}
} // -------- 2
if (menu_gear_calibration == 3) { // -------- 3
//lcd.setCursor(0, 0);
lcd.print("Hold gear - Down");
lcd.setCursor(0, 1);
lcd.print("[Ok]" + String(analogRead(GEAR__ANALOG_PIN)));
if (bKEY == 1) {
menu_gear_calibration = 4; // <<<<< ----- пятый шаг, сохраняем ацп нижней точки
aEEPROM_DATA[11] = analogRead(GEAR__ANALOG_PIN);
Serial.println(aEEPROM_DATA[11] + String("- Down save"));
lcd.clear();
return;
}
} // -------- 3
if (menu_gear_calibration == 4) { // -------- 4
//lcd.setCursor(0, 0);
lcd.print("Save in eeprom ?");
lcd.setCursor(0, 1);
lcd.print("[Ok]");
if (bKEY == 1) {
Serial.println("Eeprom save"); // <<<<< ----- пятый шаг, сохраняем в память
menu_gear_calibration = 0;
_Save_Settings_in_Eeprom();
}
} // -------- 4
} // -------- dMENU_ITEM_CALIBR_GEAR_POS
else if (bINDEX_MENU == dMENU_ITEM_END) {
bINDEX_MENU = 0;
fan_manual = 0;
menu_gear_calibration = 0;
}
} // Конец - bMENU_ACTIV = 1
}
Для рисования схем рекомендую Fritzing, очень просто, удобно и красиво.
Дерево работы основных функций ...
Вложение 18761
Приветствую товарищи!
Не осилил последние 6 страниц, начал ковыряться с аналогичным девайсом, только у меня немного другие требования. надо мерять 4 температуры (за бортом, движка в двух местах и выхлопа), обороты и часы чтоб показывал, ибо родных нету.
Думаю дело пойдет веселее в кругу единомышленников, а то детали-то все заказал, а начать собирать не могу себя заставить)
для измерения температуры до 5 датчиков применяется библиотека DallasTemperature - работает с датчиками DS18B20
Что бы измерить температуру выхлопных газов понадобится - EGT Sensor она же термопара, сигнал с неё завести на микроконтроллер через датчик термопары.
Вам в отдельную тему надо.
Что уж сразу прогонять-то? Я не столько за помощью пришёл, сколько за вдохновением и поддержкой!
Термопару с интерфейсом уже взял, Даллас будет только один, он будет мерить погоду, в головках один штатный, второй вазовский.
Реалтайм приставку для часов взял, 16х2 дисплей сгодится. Тахометр хочу катушкой с провода ловить.
Это не первый мой колхоз, опыту немного есть, вполне в состоянии собрать самостоятельно, но мешает лень)
Ну вот и приехали) в гугле схем получения данных термопары с драйвером max6675 только одна, через библиотеку.
В итоге врет безбожно, причем нелинейно, при реальных 24 показывает 27, при 350 - 140 при 400 - 160, как теперь калибровать?
Просто нужные значения проиндексировать и все, или есть другая процедура калибровки?
Замечательная статья, которая выручала меня не раз, но к данному устройству она неприменима, потомучто MAX6675 имеет SPI интерфейс, подключается к цифровому порту и если перехватывать данные без библиотеки, то это будет очень не маленький код.
Вот что передает драйвер, уже готовую температуру
А напрямую на аналоговый порт термопару не присрать, ибо очень маленький ток в термопаре, ардуина его не отловит.Код:Serial Interface The Typical Application Circuit shows the MAX6675
interfaced with a microcontroller. In this example, the
MAX6675 processes the reading from the thermocou-
ple and transmits the data through a serial interface.
Force CS low and apply a clock signal at SCK to read
the results at SO. Forcing CS low immediately stops
any conversion process. Initiate a new conversion
process by forcing CS high. Force CS low to output the first bit on the SO pin. A
complete serial interface read requires 16 clock cycles.
Read the 16 output bits on the falling edge of the clock.
The first bit, D15, is a dummy sign bit and is always
zero. Bits D14–D3 contain the converted temperature
in the order of MSB to LSB. Bit D2 is normally low and
goes high when the thermocouple input is open. D1 is
low to provide a device ID for the MAX6675 and bit D0
is three-state
В итоге надо или откалибровать, либо посчитать нелинейный коэффицент и корректировать полученные данные.
Может термо пара не подходит для драйвера?
Хз, термопара безымянная, заказывал комплектом пара+драйвер, все в одной упаковке пришло, мне кажется библиотека неправильно преобразует, ибо термопары на 400 градусов очень распространены и в статьях везде их используют, а я заказывал до тысячи градусов, вот в чем проблема, в том что с тысячной еще не ковырялись, ибо такая температура ни в одном бытовом приборе не используется :)
зачем EGT мерить ? турбо что ли ? там не более 800 градусов, дальше поршни потекут
Мотоцикл достался с неродными карбюраторами, и я хочу по ШДК оценить зависимость оборотов\дросселя\температур от состава смеси и впоследствии по температуре выхлопа и полученным данным, подобрать жиклеры и настроить иглы. С ШДК постоянно ездить нет возможности. Карбы стоят как полмотоцикла, из-за этого мне и достался с неродными.
Я понимаю что идея почти бредовая, но надо девайс, и я его ковыряю :)
Вложение 18890
Вот что получилось, сразу напишу что это прототип, дисплей с кнопками колхоз полный ;) только что бы проверить функционал, больше я делать так не буду, встрою большой графический дисплей вместо спидометра всю электрику запихну в панель.
Что он умеет.
Показывать температуру - двигателя, масла, наружного воздуха, напряжение сети
Предупреждать о превышении температуры : масла, двигателя и о перезаряде или не до заряде АКБ, всплывает сообщение на дисплее и горит светодиод.
Включать и выключать вентилятор охлажения по заданной температуре (выключить раньше чем заложено в штатный датчик не может, что бы не вторгаться в штатную проводку)
Показывать литраж в баке в процентах или литрах, датчик в баке поплавковый собственного изготовления.
Считает пробег одной поездки, при выключении зажигания остаётся включеным на заданное время,что бы счётчик не сбрасывался. (дабы не мучить EEPROM!)
Из меню можно принудительно включить вентилятор охлаждения
В панели приборов установлена PRO MINI которая управляет восьми сегментным индикатором это датчик включенной скорости, связь с основным блоком через I2C шину
Индикатор включенной скорости снабжен блинкером, что бы не забыть переключить скорость на повышенную (дался мне тяжело, затык с таймерами)
Плюс много полезных функций, для исключения ложных срабатываний меню и устройств
https://youtu.be/sGv7toyiIVo
Сегодня прошли ходовые испытания, в результате которых выявилось куча косяков, один из них: Датчик уровня топлива (резистивный) показывает точное показание только стоя на месте, тк мой алгоритм предполагает средне-арифметическую выборку за 20 секунд. На деле я получил среднее показание.
Как сделать точную выборку с датчика, что бы показания на дисплее не прыгали (бензин плещется в баке) ?? есть какой нибудь алгоритм ?
Как вариант собрать в массив 100 - 300 замеров и вычислить при помощи функции find_similar часто повторяющиеся значения
И самое главное керамический конденсатор 1мкФ паралельно входу АЦП поставить нужно
Конденсатор это гуд, а насчёт библиотеки непонял
Функция find_similar это фильтр шума
Эта функция есть в библиотеке CyberLib
Придумал свой алгоритм
1. На нейтральной передаче, обновляем сразу (напрямую с датчика без задержки) уровень топлива, в большинстве случаев на нейтральной передаче мотоцикл стоит прямо и не двигается.
2. Если передача не нейтральная начинаем высчитывать минимальное и максимальное значение значение с датчика уровня топлива (если появляется значение больше или меньше существующих заменяем) Может нужно выбирать наименьшее значение из максимального и минимального ?
3. Каждые 200 метров записываем это значение в массив (потому что если мы не едем, топливо не расходуется) ;)
4. Каждый километр высчитываем средне арифметическое из ячеек массива
Вот код, не на С++ под рукой больше ничего небыло, для проверки самое то, ардуинки стоят на мотоцикле. Думаю всё понятно, вместо километража таймеры и счётчики
Как это работает - Вложение 18908
PHP код:
#NoTrayIcon
Global $Max = 0, $Min = 500, $Random = 0, $index = 0, $sa = 0
Global $metr = 0 ; 200 метровый счетчик, примерно по времени 5 секунд =)
Dim $Array[6]
$Form1 = GUICreate("", 241, 226, 649, 150)
$Label1 = GUICtrlCreateLabel("", 48, 20, 156, 25)
$Label2 = GUICtrlCreateLabel("", 48, 40, 156, 25)
$Label3 = GUICtrlCreateLabel("", 48, 60, 156, 25)
GUISetState(@SW_SHOW)
While 1
$nMsg = GUIGetMsg()
Switch $nMsg
Case -3
Exit
Case Else
If _timer(@MSEC) = 1 Then ; таймер раз в секунду
GUICtrlSetData($Label1, "Mаксимальное : " & $Max)
GUICtrlSetData($Label2, "Минимальное : " & $Min)
$Random = Random(100, 250, 1) ; Имитируем АЦП датчика уровня топлива
If $Random > $Max Then $Max = $Random
If $Random < $Min Then $Min = $Random
If $index > 5 Then $index = 0
If $metr > 5 Then $metr = 0
$Array[$index] = ($Max + $Min) / 2 ; Раз в секунду записываем значения в массив
$index += 1
$metr += 1
If $metr > 5 Then ; проехали 200 метров (типа 5 секунд прошло =) )
$sa = ($Array[0] + $Array[1] + $Array[2] + $Array[3] + $Array[4]) / 5
GUICtrlSetData($Label3, "Среднее значение : " & $sa)
$Max = 0
$Min = 250
EndIf
EndIf
EndSwitch
WEnd
Func _timer($sec)
For $i = 500 To 520 Step 1
If $sec = $i Then
Return 1
EndIf
Next
EndFunc ;==>_timer
У меня какие то проблемы с библиотекой CyberLib.h
Ошибку такую выдаёт,что я сделал не так ?
"абстрактная часть кода "Цитата:
vc1.ino: In function 'void _fuel_level()':
vc1.ino:723:49: error: 'find_similar' was not declared in this scope
Ошибка компиляции.
PHP код:
#include <CyberLib.h>
#define cFt 250 // Размер массива для считывания уровня топлива
byte bFUEL_LEVEL;
int iFuelTripArray = 0; // Колличество проходов для записи в массив показаний датчика, максимальное значение в cFt
int aFuelLevel[cFt + 1];
void _fuel_level(){
if (iFuelTripArray > cFt) {
iFuelTripArray = 0;
bFUEL_LEVEL = find_similar(aFuelLevel, cFt,0);
}
iFuelTripArray ++;
int val = analogRead(FUEL_LEVEL__ANALOG_PIN);
if (val > cADC_LIMIT_FUEL_LEVEL) val = cADC_LIMIT_FUEL_LEVEL; // ограничение уровня напряжения АЦП
aFuelLevel[iFuelTripArray] = map(val, cMIN_ABC_FUEL_LEVEL, cMAX_ABC_FUEL_LEVEL, 100, 0);
}
А где секция setup и loop?
Так то ничего не меняется, как не работал так и не работает :(
PHP код:
#include <CyberLib.h>
#define cFt 250 // Размер массива для считывания уровня топлива
byte bFUEL_LEVEL;
int iFuelTripArray = 0; // Колличество проходов для записи в массив показаний датчика, максимальное значение в cFt
int aFuelLevel[cFt + 1];
void setup() {
}
void loop() {
_fuel_level();
}
void _fuel_level() {
if (iFuelTripArray > cFt) {
iFuelTripArray = 0;
bFUEL_LEVEL = find_similar(aFuelLevel, cFt, 0);
}
iFuelTripArray ++;
int val = analogRead(3);
if (val > 1000) val = 1000; // ограничение уровня напряжения АЦП
aFuelLevel[iFuelTripArray] = map(val, 100, 800, 100, 0);
}
Подправил тип вместо int нужно было unsignet int
Теперь компилируется
Цитата:
#include <CyberLib.h>
#define cFt 250 // Размер массива для считывания уровня топлива
byte bFUEL_LEVEL;
int iFuelTripArray = 0; // Колличество проходов для записи в массив показаний датчика, максимальное значение в cFt
uint16_t aFuelLevel[cFt + 1];
void setup() {
}
void loop() {
_fuel_level();
}
void _fuel_level() {
if (iFuelTripArray > cFt) {
iFuelTripArray = 0;
bFUEL_LEVEL = find_similar(aFuelLevel, cFt, 0);
}
iFuelTripArray ++;
int val = analogRead(3);
if (val > 1000) val = 1000; // ограничение уровня напряжения АЦП
aFuelLevel[iFuelTripArray] = map(val, 100, 800, 100, 0);
}
Ничего не изменилось опять error: 'find_similar' was not declared in this scope
значит библиотеку на положили в папку libraries
Или название папки не соответствует названию библиотеки
Возможно что положили в нескольких вложенных папках, что тоже не допустимо
Вложение 18910
всё совсем не так. Пример Blink работает, остальные функции не работают даже на reset() ругается error: 'reset' was not declared in this scope
Пробовал даже так: #include <C:\arduino1.6.2\libraries\CyberLib\CyberLib.h> :(
Тогда возможно что разрабы опять о совместимости библиотек не особо заботятся
Опробуйте версию IDE более старую
Пробовал 1.5.0 ... чего-то там, не работает. Что за чудо библиотека!!!
У меня работает на 3-х ПК
Я думаю что всетаки проблема не в либе.
А какая ардуина выбрана в настройках?
Пробовал все платы, если например выбрать Arduino Mini - ошибка будет такая
avr-gcc: error: C:\Users\08A4~1\AppData\Local\Temp\build5104382067 642245081.tmp/core.a: No such file or directory
С остальными Arduino всё тоже самое что и было.
Библиотека написана для плат с контроллером ATmega328
Библиотека заработала.
Сначала пытался откатить версию Arduino AVR boards на 1.6.2 (именно на ту в которой писал весь код) видать раньше когда пробовал другие IDE версию я всё таки обновил на более новую, в итоге конфликт произошел с библиотекой CyberLib ... ничего из отката версии хорошего не получилось, произошла "путаница" файлов.
В итоге пришлось в ручную очистить папку C:\Users\Олег\AppData\Roaming\Arduino15\, при запуске IDE установил заново файлы в эту папку о чем сообщил в начальном окне загрузки. Версия IDE стала соответствовать версии AVR boards.
Как установить сторожевой таймер ?