Вопрос по ISO-14230-4

Aku
Offline
Зарегистрирован: 04.12.2018

Скажу честно, не пробовал. Авто на улице в снегу)). Бегаю с ноутом для отладки. Не очень удобно. Может быть в выходной попробую.

Aku
Offline
Зарегистрирован: 04.12.2018

Заметил, что протокол очень чувствителен к временным интервалам.

MaksVV
Offline
Зарегистрирован: 06.08.2015

В моем скетче если что регулируйте задержки строка 62 и 66 . Одна из низ это между отправкой байт на эбу. У меня там стоит 1мс , в ваших скетчах 5мс. Но может и 1 будет работать.

MaksVV
Offline
Зарегистрирован: 06.08.2015

Плюсы моего скетча это: отсутствие delay. Переподключение при обрыве связи, проверка КС , а значит если будет мусор , то на индикацию не пройдут некорректные данные. Также легко добавлять новые пиды.

Aku
Offline
Зарегистрирован: 04.12.2018

Предлагаю свой вариант бортового МК. Добавил немного функционал.

1. Выбор параметра кнопкой.

2. Если температура двигателя меньше 40 градусов, 3 параметра перебираются автоматически.

3. При срабатывании какого-нибудь порога,  моргает индикатор и пикает зуммер.

#define F_CPU 16000000UL

#include "din_ind.h"
//#include "ds18b20.h"

#define TX 1
#define SPEAKER 2
#define BUTTON 3
#define MODEDISPMAX 6 // количество режимов 

//#define TIMEDISP 300000UL // время отображения параметра 5 минут
#define TIMEDISP 60000UL // время отображения параметра 1 минут
//#define TIMEDISP 5000UL // время отображения параметра 5сек

#define TIMEKLINE 200 // время между опросами Kline //100

#define BEEPON 70 // длительность включения BEEP
#define BEEPOFF 50 // длительность выключения BEEP
#define BEEPPAUSE 10000 // длительность между пачками BEEP

#define TIME_DS18B20 1000 //время чтения и отображения DS18B20 (температуры АЦП)
#define TIMEADC 500 //время чтения и отображения напряжения АЦП

#define ADC_AVERAGE 10 // кол-во замеров ADC (max 255)
#define ADC_REF 1022 // опорное напряжение ADC //1035

#define COUNTTOOGLELED 500 // кол-во прерываний для отключения индикации
#define REFRESHMODE 5000 // время смены показаний

char NotInit = false; // флаг инициализации

unsigned int Iso = 0; // стандарт по ISO

unsigned char ModeDisp = 0; // текущий режим

unsigned long ForceBeepCount = 0; // команда на включение бипера на заданное количество Beep

unsigned long PrevButtMillis = 0;  

unsigned char  RxBuffer[11] = {0};

signed char    Temperature = 0;
unsigned int   Cel = 0;
unsigned char  Speed = 0;
unsigned short Rpm = 0;

unsigned short Volt = 0;
signed short mVoltTemp = 0;

char ForceDisplayVoltage = false;  // флаг принудительного отображения напряжения
char ForceDisplayTemper = false;   // флаг принудительного отображения температуры
char ForceDisplayCel = false;      // флаг принудительного отображения нагрузки
char ForceDisplaySpeed = false;    // флаг принудительного отображения скорости
char ForceDisplayRPM = false;      // флаг принудительного отображения оборотов

char ForceToogleLed = false; // флаг принудительного моргания индикатора

char ModeToogle = false; // флаг моргания при достижении 40 градусов

void setup() {
  InitPorts(); 
  InitTimer2(); 
  InitDinInd(); 
  InitKL_5baud();
  ReadTrouble();
  PrevButtMillis = millis();   // запоминаем первое нажатие кнопки 
}

void loop() {
  InitKL_5baud(); 
  ProcessKLine();
  ProcessDisplay();
  //Temperature++; 
  //if (Temperature>=70) Temperature = 70;
  //Speed++;
  //if (Speed>=100) Speed = 100;
  delay(1000);
  //ForceToogleLed = 1;
}

///////////////////////////////
//Функция инициализации Портов
///////////////////////////////
void InitPorts() {
  pinMode(TX,OUTPUT);
  digitalWrite(TX,HIGH);   
  pinMode(BUTTON, INPUT_PULLUP);   
  pinMode(SPEAKER, OUTPUT);
  digitalWrite(SPEAKER,LOW);
  ACSR |= (1 << ACD); // Analog Comparator: Off
  //pinMode(13,OUTPUT);
  //PORTB &= ~(1<<5);
  //PORTB ^= (1<<5); 
}

////////////////////////
//функция опроса кнопки
////////////////////////
void ProcessButton() { 
  static char PrevButtonState = false; // предыдущее состояние кнопки
  static char ButtonState = false;     // текущее состояние кнопки по отжатию
  ButtonState = (!digitalRead(BUTTON)); // читаем текущие состояние кнопки по нажатию
  // ButtonState = digitalRead(BUTTON); // читаем текущие состояние кнопки по отжатию  
  if (!PrevButtonState && ButtonState && (millis() - PrevButtMillis > 200)) { // была не нажата, стала нажата и с прошлого нажатия прошло 200ms
    PrevButtMillis = millis(); // Запоминаем время первого срабатывания
    ButtonState = true; // Запоминаем состояние кнопки по нажатию
    // ButtonState = false; // Запоминаем состояние кнопки по отжатию
    if (++ModeDisp >= MODEDISPMAX) ModeDisp = 0; 
  } else PrevButtonState = ButtonState; // не нажата   
}

////////////////////////////
// Функция обработки данных
////////////////////////////
void ProcessData() {    
  if ((Temperature >= 110)&&(Temperature <= 115)) {ForceBeepCount = 5; ForceDisplayTemper = true; ForceToogleLed = true;} // моргаем инд. и переходим на 0 режим
  //if ((Temperature >= 39)&&(Temperature <= 40)) {ForceBeepCount = 4; ForceDisplayTemper = true; ForceToogleLed = true;}  // моргаем инд. и переходим на 0 режим
  if (Temperature == 40) {ForceBeepCount = 4; ForceDisplayTemper = true; ForceToogleLed = true;}  // моргаем инд. и переходим на 0 режим
  //if ((Temperature >= 38)&&(Temperature <= 39)) {ModeToogle = true;} // перестаем перебирать режим
  if (Temperature == 39) {ModeToogle = true;} // перестаем перебирать режим
  
  if ((Volt >= 55)&&(Volt <= 110)||(Volt >= 150)) {ForceBeepCount = 2; ForceToogleLed = true;} // если напряжение меньше 5,5В и 11В или больше 15В то не пикаем  
  if ((Speed >= 75)&&(Speed <= 76)) {ForceBeepCount = 3; ForceDisplaySpeed = true; ForceToogleLed = true;} // моргаем инд. и переходим на 0 режим
  if ((Rpm >= 4000)&&(Rpm >= 4500)){ForceBeepCount = 1; ForceDisplayRPM = true; ForceToogleLed = true;} // моргаем инд. и переходим на 0 режим
}

//////////////////////////////////
// Функция отображения параметров
//////////////////////////////////
void ProcessDisplay() {
unsigned long CurrentStateMillis = millis();
static char StateModeStart = false;

static unsigned long PrevModeDispMillis;
  if (!ModeToogle) { // если достигли 40 градусов то больше не перебираем
    ModeToogle = false;   // обнуляем чтоб больше не заходить, если не выбрали  
    if ((millis() - PrevModeDispMillis >= REFRESHMODE)) { // меняем показания через 5 секунд
      if (++ModeDisp >= 3) ModeDisp = 0;         
      PrevButtMillis = millis(); // запоминаем время первого срабатывания чтоб не переходить на 0 режим через время    
      PrevModeDispMillis = millis();
    }
  } 

  if (!(StateModeStart && (CurrentStateMillis - PrevButtMillis) < TIMEDISP))//если не 0 режим и после нажатия прошло время отображения параметра
    ModeDisp = 0; //0 //переходим на 0 режим, температура салона

  if (ForceDisplayVoltage) {    //принудительно отображаем напряжение
    ForceDisplayVoltage = false; // обнуляем чтоб больше не заходить, если не выбрали 
    ModeDisp = 1;
    PrevButtMillis = millis(); // запоминаем время первого срабатывания 
    StateModeStart = false; // после моргания переходим на 0 режим
  }
      
  if (ForceDisplayTemper) {    //принудительно отображаем температуру
    ForceDisplayTemper = false; // обнуляем чтоб больше не заходить, если не выбрали 
    ModeDisp = 2;
    PrevButtMillis = millis(); // запоминаем время первого срабатывания
    StateModeStart = false; // после моргания переходим на 0 режим
  }

  if (ForceDisplayCel) {    //принудительно отображаем нагрузку
    ForceDisplayCel = false; // обнуляем чтоб больше не заходить, если не выбрали 
    ModeDisp = 3;
    PrevButtMillis = millis(); // запоминаем время первого срабатывания
    StateModeStart = false; // после моргания переходим на 0 режим
  }

  if (ForceDisplaySpeed) {    //принудительно отображаем скорость
    ForceDisplaySpeed = false; // обнуляем чтоб больше не заходить, если не выбрали 
    ModeDisp = 4;
    PrevButtMillis = millis(); // запоминаем время первого срабатывания
    StateModeStart = false; // после моргания переходим на 0 режим
  } 

  if (ForceDisplayRPM) {       //принудительно отображаем обороты
    ForceDisplayRPM = false;  // обнуляем чтоб больше не заходить, если не выбрали 
    ModeDisp = 5;
    PrevButtMillis = millis(); // запоминаем время первого срабатывания
    StateModeStart = false; // после моргания переходим на 0 режим
  }
  
  switch (ModeDisp) {
//температура DS18B20
    case 0:
      StateModeStart = true; // обнуляем чтоб больше не заходить, если не выбрали 
      static unsigned long PrevReadDS18B20Millis;
      if ((millis() - PrevReadDS18B20Millis >= TIME_DS18B20)) { // читаем и выводим через некоторое время   
        Digit[0] = 16;          
        //ReadTemperDs18B20();
        mVoltTemp = (Read_ADC_Temp()*ADC_REF/1024UL/10)-60;
        ADC_Temp_Ind();      
        PrevReadDS18B20Millis = millis();       
      } 
      break;  

//напряжение борт. сети
    case 1:
      static unsigned long PrevADCMillis;   
      if ((millis() - PrevADCMillis >= TIMEADC)) {  // выводим через некоторое время  
        //Volt = Read_ADC()*20*ADC_REF/1024UL/100; //99 чуть больше         
        ADC_Volt_Ind();
        PrevADCMillis = millis();      
      }
      break; 
    
//температура K-Line
    case 2:   
      unsigned char temper_module;     
      if (Temperature >=0) temper_module = Temperature;
      else temper_module = - Temperature;
             
      if (Temperature < 0) Digit[0] = 18;       // "прочерк" 
      else {
        if ((temper_module%1000/100) == 0) Digit[0] = 16; // если 0 то пусто
        else Digit[0] = temper_module%1000/100; // Сотни 
      }        
      Digit[1] = temper_module%100/10; // Десятки 
      Digit[2] = temper_module%10;     // Единицы 
      Digit[3] = 19;                   // "Градус" 
      Dp0 = 0; 
      Dp1 = 0; 
      Dp2 = 0;
      Dp3 = 1; 
      break;  
    
//нагрузка K-Line
    case 3:      
      if ((Cel%1000/100) == 0) Digit[0] = 16; // если 0 то пусто
      else Digit[0] = Cel%1000/100;  // Сотни           
      Digit[1] = Cel%100/10;         // Десятки 
      Digit[2] = Cel%10;             // Единицы 
      Digit[3] = 24;                 // "L" 
      Dp0 = 0; 
      Dp1 = 0; 
      Dp2 = 0; 
      Dp3 = 0; 
      break; 
      
// скорость K-Line
    case 4:       
      if (((Speed)%1000/100) == 0) Digit[0] = 16; // если 0 то пусто
      else Digit[0] = Speed%1000/100;  // Сотни           
      Digit[1] = Speed%100/10;         // Десятки 
      Digit[2] = Speed%10;             // Единицы 
      Digit[3] = 31;                   // "c" 
      Dp0 = 0; 
      Dp1 = 0; 
      Dp2 = 0; 
      Dp3 = 0; 
      break;    
       
//обороты K-Line
    case 5:      
      if (Rpm % 10000/1000 == 0) Digit[0] = 16; // если 0 то пусто 
      else Digit[0] = Rpm%10000/1000;         // Тысячи
      Digit[1] = Rpm%1000/100;                // Сотни 
      Digit[2] = Rpm%100/10;                  // Десятки 
      Digit[3] = Rpm%10/1;                    // Единицы 
      Dp0 = 0; 
      Dp1 = 0; 
      Dp2 = 0; 
      Dp3 = 0; 
      break;
  }
}

////////////////////////////////////
// Функция чтения параметров K-Line
////////////////////////////////////
void ProcessKLine() {
  static unsigned char index;            
  static unsigned long PrevKlineMillis;
  if ((millis() - PrevKlineMillis >= TIMEKLINE)) {
             
    switch (index) {     
//температура
      case 0:     
        if (Iso == 9141) {         
          static unsigned char EngCoolTemp[6] = {0x68, 0x6A, 0xF1, 0x01, 0x05, 0xC9};
          TxBuf(EngCoolTemp,sizeof(EngCoolTemp)); 
        }
        
        if (Iso == 14230) {         
          static unsigned char EngCoolTemp[6] = {0xC2, 0x33, 0xF1, 0x01, 0x05, 0xEC};
          TxBuf(EngCoolTemp,sizeof(EngCoolTemp)); 
        }                 
        delay(50);                      
        RxBuf(); //записываем в RxBuffer         
        if (RxBuffer[4]==0x05) {
          Temperature = RxBuffer[5];
          Temperature = Temperature - 40;  
        } else {
          NotInit = false; // инициализируем снова
          Serial.end();
          digitalWrite(TX,HIGH);
        }
        break;   

//Нагрузка
      case 1:
        if (Iso == 9141) { 
          static unsigned char CalcEngLoad[6] = {0x68, 0x6A, 0xF1, 0x01, 0x04, 0xC8};
          TxBuf(CalcEngLoad,sizeof(CalcEngLoad)); 
        }
        
        if (Iso == 14230) { 
          static unsigned char CalcEngLoad[6] = {0xC2, 0x33, 0xF1, 0x01, 0x04, 0xEB};  
          TxBuf(CalcEngLoad,sizeof(CalcEngLoad));   
        }
        
        delay(50);  
        RxBuf(); //записываем в RxBuffer             
        if (RxBuffer[4]==0x04) {
          Cel = RxBuffer[5] * 100;   
          Cel = Cel / 255;
        } else {
          NotInit = false; // инициализируем снова
          Serial.end(); 
          digitalWrite(TX,HIGH); 
        }
        break;
            
//скорость 
      case 2:
        if (Iso == 9141) { 
          static unsigned char VehicleSpeed[6] = {0x68, 0x6A, 0xF1, 0x01, 0x0D, 0xD1};
          TxBuf(VehicleSpeed,sizeof(VehicleSpeed));
        }
         
        if (Iso == 14230) { 
          static unsigned char VehicleSpeed[6] = {0xC2, 0x33, 0xF1, 0x01, 0x0D, 0xF4};
          TxBuf(VehicleSpeed,sizeof(VehicleSpeed));
        } 
        
        delay(50);  
        RxBuf(); //записываем в RxBuffer             
        if (RxBuffer[4]==0x0D) {  
          Speed = RxBuffer[5];
        } else { 
          NotInit = false; // инициализируем снова
          Serial.end();
          digitalWrite(TX,HIGH); 
        }
        break;
        
//обороты  
      case 3:
       if (Iso == 9141) { 
         static unsigned char EngineRPM[6] = {0x68, 0x6A, 0xF1, 0x01, 0x0C, 0xD0};
         TxBuf(EngineRPM,sizeof(EngineRPM));  
       }

       if (Iso == 14230) { 
         static unsigned char EngineRPM[6] = {0xC2, 0x33, 0xF1, 0x01, 0x0C, 0xF3};
         TxBuf(EngineRPM,sizeof(EngineRPM)); 
       }
          
        delay(50);                
        RxBuf(); //записываем в RxBuffer            
        if (RxBuffer[4]==0x0C) {        
          Rpm = (256*RxBuffer[5]) + RxBuffer[6]; 
          Rpm = Rpm/4;      
        } else {
            NotInit = false; // инициализируем снова
            Serial.end();
            digitalWrite(TX,HIGH);
        }
        break;

//Напряжение ADC  
      case 4:
       Volt = Read_ADC()*20*ADC_REF/1024UL/100; //99 чуть больше          
       break;
       
//Напряжение термодатчика  
      //case 5:
       //mVoltTemp = (Read_ADC_Temp()*ADC_REF/1024UL/10)-60;
       //break;
    } 
  if (++index>=5) index = 0; //4 + ADC 
  PrevKlineMillis = millis();
  }
}



//////////////////////////
// Функция обработки Beep
//////////////////////////
void ProcessBeeper() {
  static unsigned char index;
  static unsigned long BeepMillis;
  static unsigned long PrevBeepMillis; 
  static char count;
    
  switch (index) {
    case 0: 
      if (!((millis()-PrevBeepMillis) >= BeepMillis)) break; 
        if (ForceBeepCount) {
          count = ForceBeepCount;
          //ForceBeepCount = 0;    
          BeepMillis = 0;       
          index = 1;
          PrevBeepMillis = millis(); 
        }    
      break;    
   
    case 1:
      if (!((millis()-PrevBeepMillis) >= BeepMillis)) break;
        Beeper_ON();       
        BeepMillis = BEEPON;      
        index = 2;
        PrevBeepMillis = millis();
      break;
    
    case 2: 
      if (!((millis()-PrevBeepMillis) >= BeepMillis)) break; 
      Beeper_OFF();  
        if (--count>0) {       
          BeepMillis = BEEPOFF; 
          index = 1; 
          PrevBeepMillis = millis();   
        }
        else {        
          BeepMillis = BEEPPAUSE; 
          index = 0; 
          PrevBeepMillis = millis();                    
        }
      break;
   }
  ForceBeepCount = 0;   
}

///////////////////////////////////////////////
//Функция инициализации Таймера2 для Дин. Инд.
///////////////////////////////////////////////
void InitTimer2() {
  cli();// Глобально запрещаю прерывания
  TCCR2A = 0;
  TCCR2B = 0;
  TIMSK2 = 0; 
  TCCR2A |= (1<<COM2A0); // Toggle OC2A,No CTC
  TCCR2B |= (1<<CS21)|(1<<CS20);              // Presc. = 32
  //TCCR2B |= (1<<CS22);                      // Presc. = 64
  TIMSK2 |= (1<<TOIE2); // enable timer overflow interrupt
  sei(); // Глобально разрешаю прерывания 
}

//////////////////////////////////
// Обработчик прерывания дин. инд.
//////////////////////////////////
ISR (TIMER2_OVF_vect) {
  static short  i = 0;
  i++; 
  if (ForceToogleLed) {
    ForceToogleLed = false;  // обнуляем чтоб больше не заходить, если не выбрали 
    if (i>=COUNTTOOGLELED) {     
      i = 0;
      if (OnLed) OnLed = false; 
      else OnLed = true; 
    }    
  } else {
    OnLed = true;
    i = 0;  
  }

/*
static short  z = 0;
static char On = 0;
  z++;    
  if (z>=700) {
    z = 0;
    if (On) {
    On = false; 
    PORTB &= ~(1<<5);
    } else {
      On = true;
      PORTB |= (1<<5);
    }
  }      
*/   
  RefreshDinInd();  
  ProcessButton(); 
  ProcessData();
  ProcessBeeper(); 
}

///////////////////////////////////////////////
// Функция чтения байта с выходом по тайм ауту
///////////////////////////////////////////////
char getSerial(unsigned char &retVal, unsigned long timeout) {
  unsigned long start = millis();
  while (!Serial.available()) { // ждем байт
    if ((millis() - start) > timeout) {
      return true; // не дождались, вышли по тайм ауту
    }
  }
  retVal = Serial.read();
  return false; // дождались байт
}

/////////////////////////////////
//Функция чтения данных по KLine
/////////////////////////////////
void RxBuf() {
  char bByteTimeout = false; 
  unsigned char byteNum = 0;   
  while (!bByteTimeout) {   
      bByteTimeout = getSerial(RxBuffer[byteNum],20); // сохранить ответ
      if (bByteTimeout) {        
        RxBuffer[byteNum] = 0; // текущий байт в 0, если тайм-аут произошел до того, как он мог быть записан
      }        
   byteNum++;
  }
 
  for (unsigned char i = byteNum; byteNum < 11; byteNum++) { // заполнение остатка текущего сообщения нулями
    RxBuffer[byteNum] = 0;
  } 
}

///////////////////////////////////
//Функция отправки данных по KLine
///////////////////////////////////
void TxBuf(unsigned char *array, unsigned char bufSizeTx) {
  for (unsigned char i = 0; i < bufSizeTx; i++) {
    Serial.write(array[i]);
    unsigned char Clear;
    getSerial(Clear,5);
    delay(5); // 5ms <= P4 <= 20ms
  }
}

///////////////////////////////
//Функция инициализации K-Line
///////////////////////////////
void InitKL_5baud() {
  if (!NotInit) { // если не было инициализации, то инициализируем 
    delay(2500); 
    digitalWrite(TX, LOW );
    delay(200);              
    digitalWrite(TX, HIGH ); 
    delay(400);
    digitalWrite(TX, LOW ); 
    delay(400);
    digitalWrite(TX, HIGH );
    delay(400);
    digitalWrite(TX, LOW ); 
    delay(400);
    digitalWrite(TX, HIGH );
    delay(200);
    
    Serial.begin(10400);
  
    unsigned char InitError = 0;  // номер ошибки       
    unsigned char Sync = 0;
    unsigned char Key1 = 0;
    unsigned char Key2 = 0;
    unsigned char InvKey2 = 0;
    unsigned char Clear = 0;
    unsigned char InvAddress = 0;
    if (!getSerial(Sync,300)) {
      if (Sync == 0x55) {
        if (!getSerial(Key1,20)) { // если успешно прочитали key1
          if(!getSerial(Key2,20)) { // если успешно прочитали key2
            delay(25);  // 25ms <= W4 <= 50ms 
            InvKey2 = ~Key2;
            Serial.write(InvKey2);   
            getSerial(Clear,5); // читаем эхо invKey2          
            if (!getSerial(InvAddress,50)) { // если успешно прочитали инверсию байта
              if (InvAddress == 0xCC) { // если получили 0xCC                
                if ((Key1 == 0x08) && (Key2 == 0x08)) Iso = 9141;
                if (Key2 == 0x8F) Iso = 14230;              
                //MsbToLed(Key1); // отображаем key1, key2
                //LsbToLed(Key2);  
                NotInit = true; // инит успешно, больше не инициализируем                                                                             
              } else {    
                InitError = 6; // байт не равен 0xCC
              }
            } else {    
              InitError = 5; // не получили инверсию байта 0x33
            }
          } else {    
            InitError = 4; // не получили key2
          }
        } else {    
          InitError = 3; // не получили key1
        }
      } else {    
        InitError = 2; // байт sync не равен 0x55
      }
    } else {    
      InitError = 1; // не получили sync
    }
    
    if (InitError) { // выводим ошибки, если есть
      Digit[0] = 14; // E
      Digit[1] = 22; // r
      Digit[2] = 30; // r.
      Digit[3] = InitError; 
      Dp0 = 0; 
      Dp1 = 0; 
      Dp2 = 0; 
      Dp3 = 0; 
      delay(5000); //5000
    }                    
    delay(TIMEKLINE);  // 55ms <= P3 (wait between messages) <= 5s 
    //delay(2000); // если отображаем key1, key2
  } // NotInit
}

/////////////////////////
//Функция включения BEEP
/////////////////////////
void Beeper_ON() {
  digitalWrite(SPEAKER,HIGH);  
}

//////////////////////////
//Функция выключения BEEP
//////////////////////////
void Beeper_OFF() {
  digitalWrite(SPEAKER,LOW);  
}

////////////////////////
//Функция чтения ошибок
////////////////////////
void ReadTrouble() {

  if (Iso == 9141) { 
    static unsigned char Trouble[5] = {0x68, 0x6A, 0xF1, 0x03, 0xC6};
    TxBuf(Trouble,sizeof(Trouble));
  }

  if (Iso == 14230) { 
    static unsigned char Trouble[5] = {0xC2, 0x33, 0xF1, 0x03, 0xE9};
    TxBuf(Trouble,sizeof(Trouble));
  }
          
  delay(50);    
  RxBuf(); //записываем в RxBuffer
  
  //RxBuffer[5] = 0xFF;
  //RxBuffer[6] = 0x12;
 
  unsigned char dtc1 = (RxBuffer[5] & 0xC0) | (dtc1 >> 6);    
  unsigned char dtc2 = (RxBuffer[5] & 0x30) >> 4;
  unsigned char dtc3 =  RxBuffer[5] & 0x0F;
  unsigned char dtc4 = (RxBuffer[6] & 0xF0) >> 4;
  unsigned char dtc5 =  RxBuffer[6] & 0x0F;
  
  if (dtc1||dtc2||dtc3||dtc4||dtc5) { // если есть ошибки  
    Digit[0] = 14; // E
    Digit[1] = 22; // r
    Digit[2] = 30; // r.
      
    switch (dtc1) {
      case 0x00 :
         Digit[3] = 26;
        break;
      case 0x40 :
         Digit[3] = 12;
        break;
      case 0x80 :
         Digit[3] = 11;
        break;
      case 0xC0 :
         Digit[3] = 28;
        break;
    }
    Dp0 = 0; 
    Dp1 = 0; 
    Dp2 = 0; 
    Dp3 = 0;   
    delay(2000);      
    Digit[0] = dtc2;
    Digit[1] = dtc3;
    Digit[2] = dtc4;
    Digit[3] = dtc5;     
    delay(2000); 
  }
}

//////////////////////////
//Функция удаления ошибок
//////////////////////////
/*
void CLRTrouble() {
  if (Iso == 9141) { 
    static unsigned char ClearTrouble[5] = {0x68, 0x6A, 0xF1, 0x04, 0xC7};
    TxBuf(ClearTrouble,sizeof(ClearTrouble));  
  }

  if (Iso == 14230) { 
    static unsigned char ClearTrouble[5] = {0xC2, 0x33, 0xF1, 0x04, 0xEA};
    TxBuf(ClearTrouble,sizeof(ClearTrouble));  
  }     
  delay(50);    
}
*/

//////////////////////
// Функция чтения ADC
//////////////////////
unsigned long Read_ADC() {
unsigned long Result = 0;  
// Настраиваю  АЦП
  DIDR0 |= (1 << 7); // отключаем цифровой вход ADC7 напряжение
  ADMUX |= (1 << REFS1)|(1 << REFS0) // Int. 1.1V Ref. with ext. cap. at AREF.   
      |(1 << MUX2)|(1 << MUX1)|(1 << MUX0); // ADC7 volt                        
      //|(1 << MUX3)|(1 << MUX2)|(1 << MUX1); // 1.1V
              
  ADCSRA |= (1 << ADEN) // ADC enable
      |(1 << ADPS2)|(1 << ADPS1)|(1 << ADPS0); // CK/128

  delay(1);
  
  for (unsigned char i=0;i<ADC_AVERAGE;i++) {  
    do {ADCSRA |= (1 << ADSC);} // Старт преобразования
    while ((ADCSRA & (1 << ADIF)) == 0); // ждем флаг ADIF 
    Result = Result + ADCW; // Result + (ADCL|ADCH << 8);     
    ADCSRA |= (1 << ADIF);    // сбрасываем флаг ADIF      
    ADCSRA &= ~(1 << ADSC);   // Стоп преобразования
  }
  Result = Result / ADC_AVERAGE;
  DIDR0 = 0;
  ADMUX = 0;
  ADCSRA = 0;

  return Result;
}

void ADC_Volt_Ind() { 
  if ((Volt%1000/100)==0) Digit[0] = 16;  
  else Digit[0] = Volt%1000/100; // Десятки вольт
  Digit[1] = Volt%100/10;  // Единицы вольт
  Digit[2] = Volt%10;    // 1/10 вольт            
  Digit[3] = 28;  // U
  Dp0 = 0;
  Dp1 = 1;
  Dp2 = 0;
  Dp3 = 0;
}

///////////////////////////
// Функция чтения ADC темп
///////////////////////////
unsigned long Read_ADC_Temp() {
unsigned long Result = 0;  
// Настраиваю  АЦП
  DIDR0 |= (1 << 6); // отключаем цифровой вход ADC6 температура
  ADMUX |= (1 << REFS1)|(1 << REFS0) // Int. 1.1V Ref. with ext. cap. at AREF.  
      |(1 << MUX2)|(1 << MUX1);  // ADC6 temp                        
      //|(1 << MUX3)|(1 << MUX2)|(1 << MUX1); // 1.1V
              
  ADCSRA |= (1 << ADEN) // ADC enable
      |(1 << ADPS2)|(1 << ADPS1)|(1 << ADPS0); // CK/128

  delay(1);
  
  for (unsigned char i=0;i<ADC_AVERAGE;i++) {  
    do {ADCSRA |= (1 << ADSC);} // Старт преобразования
    while ((ADCSRA & (1 << ADIF)) == 0); // ждем флаг ADIF 
    Result = Result + ADCW; // Result + (ADCL|ADCH << 8);     
    ADCSRA |= (1 << ADIF);    // сбрасываем флаг ADIF      
    ADCSRA &= ~(1 << ADSC);   // Стоп преобразования
  }
  Result = Result / ADC_AVERAGE;
  DIDR0 = 0;
  ADMUX = 0;
  ADCSRA = 0;
  return Result;
}

///////////////////////////
// Функция чтения ADC темп
///////////////////////////
void ADC_Temp_Ind() { 
  if (mVoltTemp < 0) {
  // Отрицательная температура
    mVoltTemp =-mVoltTemp;    // Перевожу отрицательное число в положительное
    Digit[1] = 18;       // Символ "-"
    if (mVoltTemp < 10) {
      // -9*...-1*
      Digit[2] = mVoltTemp % 10;  // Единицы градусов
      Digit[3] = 19;    // Символ градуса
    }
    else {
      // -55...-10
      Digit[2] = mVoltTemp % 100 / 10;  // Десятки градусов
      Digit[3] = mVoltTemp % 10;      // Единицы градусов
    }
  }
  else {
  // Положительная температура
    if (mVoltTemp > 99) {
      // 100...125
      Digit[1] = mVoltTemp % 1000 / 100;  // Сотни градусов
      Digit[2] = mVoltTemp % 100 / 10;  // Десятки градусов
      Digit[3] = mVoltTemp % 10;      // Единицы градусов
    }
    else {
      if (mVoltTemp > 9) {
        // 10*...99*
        Digit[1] = mVoltTemp % 100 / 10;  // Десятки градусов
        Digit[2] = mVoltTemp % 10;      // Единицы градусов
        Digit[3] = 19;        // Символ градуса
      }
      else {
        // 0*...9*
        Digit[1] = 16;       // Пустой символ
        Digit[2] = mVoltTemp % 10;    // Единицы градусов
        Digit[3] = 19;      // Символ градуса
      }
    }
  }  
  Dp0 = 0;
  Dp1 = 0; 
  Dp2 = 0;
  Dp3 = 0;              
}


 

MaksVV
Offline
Зарегистрирован: 06.08.2015

а все таки #45 пробовали?

Aku
Offline
Зарегистрирован: 04.12.2018

MaksVV,Спасибо за помощь. Нет. Не дошло до этого. Когда у меня заработало, смысл пропал. Но если очень нужно, то попробую.

MaksVV
Offline
Зарегистрирован: 06.08.2015

в вашем скетче delay-и присутсвтуют и нет проверки корректности полученных от PCM данных, поэтому на экране возможно будут проскакивать нереальные значения и подтормаживания, а также плохое реагирования на кнопку. Так для прикола проверьте мой мой скетч. 

Aku
Offline
Зарегистрирован: 04.12.2018

Если говорить про Delay(1000), которая в в основном цикле, то она там совсем не нужна. Её нужно закомментировать. Я её использовал для отладки. А в остальных случаях я так думаю без задержек не получится. Да и максимум там задержка на 50-100мс.

MaksVV
Offline
Зарегистрирован: 06.08.2015

100 это уже очень прилично я вам скажу. Нормальные программы так не пишут. Например , обороты двс скачками будут отображаться. Мой скетч тоже далеко не идеал. Но delay больших нет. Максимум 5 вроде гдето было. Подобные пару скетчей у меня отлично работают. Посмотрите принцип получения байт от PCM.

lev2606
Offline
Зарегистрирован: 19.06.2019

Привет, MaksVV. А почему в скетче №45 Вы считываете напряжение из одного байта SysVolt = buf[n+2]/10.0 ?

В документации на Pid-ы другая формула 

 

PID
(hex)
PID
(Dec)
Data bytes returned Description Min value Max value Units Formula

 

42 66 2 Control module voltage
0  
65.535 V {\displaystyle {\frac {256A+B}{1000}}}
 

 

MaksVV
Offline
Зарегистрирован: 06.08.2015

Aku пишет:

Добрый день MaksVV. Спасибо за скетч. У меня работает свой сейчас. Читаю параметры температуры, скорости и обороты двигателя. Но с напряжением по адресу 0x42h почему-то не получается. Возвращает 0x83 0xF1 0x10 0x7F 0x01 0x12 0x16.

 

MaksVV пишет:

вот исправленный скетч, в том есть косяки . Видимо такой PID 01 42   ЭБУ не поддерживает.

stas_sychev
Offline
Зарегистрирован: 16.04.2020

Добрый день! Пытаюсь повторить Ваше творение ! не могу найти библиотеку данную ! Если не сложно поделитесь . Спасибо! #include "din_ind.h"

Aku
Offline
Зарегистрирован: 04.12.2018

Добрый! Библиотека моя личная. Могу скинуть.  Моя почта "elektronshik.kudzinсобакаgmail.com"

Mersovod
Offline
Зарегистрирован: 09.03.2022

Ребят, только учусь. Мне нужно тоже получить из машинки температуру. Машина Mercedes W168.

Перебрал почти все скетчи тут, в итоге, для наглядности, по скетчу в сообщении #36 (от MaksVV) выдаёт следующее:

Delayu zapros na PCM. Adress: 33
5 baud Init
 55 8 8
 
Подскажите, пожалуйста, куда дальше копать? А ещё можно узнать, через какой адаптер подключались тут? 
 
На данный момент я использую эту схему на L9637 .
 
 
 
Я ещё новичок и, возможно, вопросы тупые будут, за что заранее прошу прощения ) 
Komandir
Komandir аватар
Offline
Зарегистрирован: 18.08.2018

Какие-нибудь программы видят авто через шнурок ?

Mersovod
Offline
Зарегистрирован: 09.03.2022

С программами не работал, опыта нет (если подскажете куда копать в этом плане, то тоже буду признателен). Сразу пишу скетч для Ардуино. Если к OBD2 разъёму подключить ELM 327, то данные можно считывать через тот же Car Scanner, например. Но я хочу вывести на панель значение температуры, от этого и пришлось этим заняться.

Aku
Offline
Зарегистрирован: 04.12.2018

Могу выложить свои творения которые делал в 2019 году. НО по мимо программы, нужно еще чтоб аппаратная часть была рабочей.

Komandir
Komandir аватар
Offline
Зарегистрирован: 18.08.2018

Mersovod оптимально через Advanced Serial Port Monitor заснифить обмен этой программы через ELM и потом уже нужный обмен повторить через ардуино.

Mersovod
Offline
Зарегистрирован: 09.03.2022

Aku пишет:

Могу выложить свои творения которые делал в 2019 году. НО по мимо программы, нужно еще чтоб аппаратная часть была рабочей.

Выложи, пожалуйста, буду очень признателен. По поводу аппаратной части - вот как раз я тоже выше вопрос про это задал. Хотел бы узнать, кто какие ещё варианты использует? 

Видел такой вот ещё вариант и на lm393 , вот не понятно, какой в итоге использовать и может ли поменяться результат, если буду использовать эти схемы (пока не собирал, но детали все есть под рукой для сборки).

Mersovod
Offline
Зарегистрирован: 09.03.2022

Komandir пишет:

Mersovod оптимально через Advanced Serial Port Monitor заснифить обмен этой программы через ELM и потом уже нужный обмен повторить через ардуино.

Правильно ли я понимаю, что если не использовать сторонний софт, то надо будет параллельно к сканеру подключиться к K-линии моей же собранной аппаратной схемой, и через SoftwareSerial поставить порт на чтение и вывести все шестнадцатиричные значения через монитор Serial порта? 

А если через Advanced Serial Port Monitor, то какая аппаратная часть нужна для этого? Что-то типа "BM9213M Универсальный автомобильный адаптер K-L-линии USB" ? И также надо будет параллельно сканеру подключаться? 

* сканером я называю ELM или официальное диагностическое оборудование Mercedes (которое тоже у меня присутствует)

Komandir
Komandir аватар
Offline
Зарегистрирован: 18.08.2018

Advanced Serial Port Monitor перехватывает обмен на COM порту компьютера и по этому ничего дополнительно цеплять на K-line не надо !

Просто цепляем elm, запускаем Advanced Serial Port Monitor, запускаем ПО от elm и общаемся с ЭБУ. Потом смотрим в логе Advanced Serial Port Monitor - что посылалось-принималось через COM порт и уже пробуем посылать/принимать эти данные через ваш девайс.

Mersovod
Offline
Зарегистрирован: 09.03.2022

Не до конца понял. 

Как сейчас у меня:

мозги ==> аппаратная часть собранная мной (подсоединённая к K-линии) ==> арудина ==> USB (COM порт)

Ты говоришь что при использовании Advanced Serial Port Monitor ничего цеплять не нужно. Ок, я втыкаю ELM в мозги. ELM работает по блютузу, запускаю на андройде приложение для коннекта к ELM. А COM порт-то на компе как будет слушаться программой Advanced Serial Port Monitor? Если он никуда не подключён. Туплю пока ) 

Komandir
Komandir аватар
Offline
Зарегистрирован: 18.08.2018

Я про elm в виде шнурка в USB/COM компьютера. А мерседесовская приблуда куда включается ?

Mersovod
Offline
Зарегистрирован: 09.03.2022

Komandir пишет:

Я про elm в виде шнурка в USB/COM компьютера. А мерседесовская приблуда куда включается ?

А, понял, хорошо, попробую через мерседесовский сканер, он с проводом. Спасибо!

Komandir
Komandir аватар
Offline
Зарегистрирован: 18.08.2018
#define BAUD_200  0
#define BAUD_5    1

bool Protocol = BAUD_5;   // тут выбираем протокол 200 baud или 5 baud
byte ADDRESS = 0x33;      // тут выбираем адрес ЭБУ


byte StartCommunication [] =   {0xc1, ADDRESS, 0xf1, 0x81, 0x66};

byte delaybyte_TX  = 1; //задержка между отправкой байт, мс

int  bit_time = 0;
uint32_t prev = 0;
#include <SoftwareSerial.h>

#define RX 2
#define TX 3
SoftwareSerial K_line(RX, TX);

bool InitGauge = 0;    // флаг инита панели

void setup() {
  Serial.begin (9600); //открываем соединение терминала для отладки

  pinMode  (TX, OUTPUT);
  digitalWrite  (TX, 1); // BUS idle
  delay (300);

  Serial.print("Delayu zapros na PCM. Adress: "); Serial.println(ADDRESS, HEX);

  Serial.println("Fast Init");
  digitalWrite  (TX, 0);
  delay (25);
  digitalWrite  (TX, 1);
  delay (25);
  K_line.begin(10400); //
}


void loop() {

  byte Checksum = 0;
  for (byte i = 0; i < sizeof(StartCommunication); i++) {
    if (i != sizeof(StartCommunication) - 1) Checksum += StartCommunication[i];
    else StartCommunication[i] = Checksum;
    K_line.write (StartCommunication[i]);
    delay (delaybyte_TX);
  }
  Serial.println ("Send StartCommunication");

  while (K_line.available()) {
    Serial.print(K_line.read(), HEX);
    Serial.print (" ");


  }
  while (1);



}

Так ещё можно попробовать ...

Mersovod
Offline
Зарегистрирован: 09.03.2022

Komandir, эти скетчи, по сути, вроде то же самое, что в начале темы были, но попробую.

Вопрос сейчас в другом - я попробовал использовать Advanced Serial Port Monitor, но если я запускаю одновременно мерсовский софт сканирования ЭБУ и открываю в Advanced Serial Port Monitor, то я тем самым занимаю порт и мерсовский софт не работает, т.к. порт занят. Может быть какие-то конкретные настройки нужно сделать в Advanced Serial Port Monitor? 

Komandir
Komandir аватар
Offline
Зарегистрирован: 18.08.2018

Я точно не помню - может порядок запуска важен. Вообще  Advanced Serial Port Monitor не должен занимать порт. Он как бы встраивается между ... Может софт видит что  Advanced Serial Port Monitor "подгляывает" и не хочет раскрывать секреты ...

Mersovod
Offline
Зарегистрирован: 09.03.2022

Что вот эти настройки обозначают? 

У меня сканер подключается с физическому COM порту №2, через переходник RS232 TO RS485 (RS485 выходом в COM порт). Должен ли я эту галочку "Режим интерфейса RS485" поставить? 

Komandir
Komandir аватар
Offline
Зарегистрирован: 18.08.2018

С такими переходниками не сталкивался.

Может не тот снифер ... я брал тут - https://advanced-serial-port-monitor.soft112.com/

У меня он сейчас не работает - бесплатно работает ограниченное время. Запускать надо в режиме Наблюдатель.

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

Вашу тему не знаю, но у себя применяю VSPE сплиттер, программа для 32 битной версии бесплатна

Mersovod
Offline
Зарегистрирован: 09.03.2022

А что означают вот эти две красные полоски? Просто они кликабельные и надпись при наведении ни о чем мне не говорит ) Не понятно, должны ли они быть нажаты во время мониторинга или нет.

Komandir
Komandir аватар
Offline
Зарегистрирован: 18.08.2018

Я вроде не нажимал там ничего. Просто в HEX обмен смотрел и все.

Выложу тут ссылку на инициализацию - https://russianblogs.com/article/33311624928/

Mersovod
Offline
Зарегистрирован: 09.03.2022

В общем попробовал я промониторить, выдаёт очень много данных, там порт постоянно общается со сканером, даже до инициализации, поэтому очень сложно было разобраться. Пошёл по другому пути - попробовал разобраться в скетче, описанном в посте #55 от Aku, в итоге вроде как общение с ЭБУ началось, по крайне мере ошибок, которые там заложены в коде не вылезло и я дошёл за запросов температуры. Мозги стандарта ISO 9141, соответственно для запроса температуры ОЖ используется пакет 

{0x68, 0x6A, 0xF1, 0x01, 0x05, 0xC9}

в ответ ЭБУ мне присылает такое:

48 6B 1 41 5 0 FA 0 0 0 0

По коду используется 5 байт, тут он = 0, но если смотреть через сканер то должно быть 27 градусов (ЭБУ такое значение отдаёт). 

В итоге я чёт не пойму, где тут температура? 

Komandir
Komandir аватар
Offline
Зарегистрирован: 18.08.2018

В ответе контрольная сумма верная. Как вариант это ответ от другого узла ЭБУ.

Вы бы все-таки выложили перехваченный массив данных в HEX формате. Можно нажать Clear и сразу после нажатия подключить разъем OBD к авто. Посмотрим что там видно ... Если монитор отображает температуру, то должно быть видно посылки-ответы типа 0x68, 0x6A, 0xF1, 0x01, 0x05, 0xC9

Mersovod
Offline
Зарегистрирован: 09.03.2022

Выкладываю ссылку на лог обмена сканера с ЭБУ на том моменте, где я смотрел температуру ОЖ (но там на экране ещё было два или три других параметра, которые не убрать). На момент сканирования температура была 26 градусов. В логе сразу всё скопом - отправка и приём данных. Что-то мне подсказывает, что он ничем не поможет.

Komandir
Komandir аватар
Offline
Зарегистрирован: 18.08.2018

Там есть кнопочка ASCII/HEX - можно в HEX ???

Mersovod
Offline
Зарегистрирован: 09.03.2022
Komandir
Komandir аватар
Offline
Зарегистрирован: 18.08.2018

Насколько я вижу тут нет протокола OBD ... Это какой то свой протокол MB.

Mersovod
Offline
Зарегистрирован: 09.03.2022

Получается, нужен всё-таки шнур с Элемкой? Там точно OBD будет ....

Komandir
Komandir аватар
Offline
Зарегистрирован: 18.08.2018

Скорее всего. MB не делится своими протоколами.

Mersovod
Offline
Зарегистрирован: 09.03.2022

> в ответ ЭБУ мне присылает такое:

> 48 6B 1 41 5 0 FA 0 0 0 0

В итоге решил подключить собранную сборку и написанный, загруженный скетч в ардуинку к самой машине (до этого тестировал на купленнм с разборки ЭБУ дома без подсоединения датчиков) и ВОУЛЯ! Всё заработало на реальной машине!

Это дома:

 

А это в машине:

 

Komandir
Komandir аватар
Offline
Зарегистрирован: 18.08.2018

Ok

Aku
Offline
Зарегистрирован: 04.12.2018

Кидаю. Это пример, на его основе соберете свой.

#define F_CPU 16000000UL

#include "din_ind.h"
#include "ds18b20.h"

//https://www.drive2.ru/c/1195541/

#include <avr/io.h>

#define TX 1
#define SPEAKER 2
#define BUTTON 3
#define MODEDISPMAX 6 //количество режимов 

#define W1Syncro 50  // 20-300 Ожидание байта SYNC(300-timeOut)
#define W2byteOut 5 // 5-20 //10(20-timeOut)
#define W3byteIn 15  // 0-20
//#define W4KeyTo 30  // 25 мс <= W4 <= 50 мс (время между завершенным получением ключа 2 и началом отправки инвертированного ключа 2)
#define TxToRx 100   // от отправки до приема 55-5000

#define TIMEDISP 60000*5 //время отображения параметра 5 минуту
#define TIMEKLINE 100 //время между опросами Kline

#define BEEPON 60 //длительность включения BEEP
#define BEEPOFF 50 //длительность выключения BEEP
#define BEEPPAUSE 1000 //длительность между пачками BEEP

#define ADC_AVERAGE 250 // кол-во замеров ADC

#define TIMEADC 1000 //время между опросами температуры DS18b20 и АЦП

char ButtonState = false; // состояние кнопки
char PrevButtonState = false; //предыдущее состояние кнопки
unsigned long PrevButtMillis = 0; //время последнего нажатия кнопки

char ModeDisp = 0; // текущий режим

char StateSelectMode = false; // состояние кнопки для начала отсчета времени возврата 
unsigned long CurrentStateMillis = 0; // время текущего состояния кнопки

unsigned long PrevKlineMillis = 0; 
unsigned long PrevADCMillis = 0;

unsigned long BeepMillis = 0;
unsigned long PrevBeepMillis = 0; 
unsigned long ForceBeepCount; //команда на включение бипера на заданное количество Beep

unsigned char EngCoolTemp[6]  = {0xC2, 0x33, 0xF1, 0x01, 0x05, 0xEC};
unsigned char CalcEngLoad[6]  = {0xC2, 0x33, 0xF1, 0x01, 0x04, 0xEB};
unsigned char VehicleSpeed[6] = {0xC2, 0x33, 0xF1, 0x01, 0x0D, 0xF4};
unsigned char EngineRPM[6]    = {0xC2, 0x33, 0xF1, 0x01, 0x0C, 0xF3};
unsigned char Trouble[5]      = {0xC2, 0x33, 0xF1, 0x03, 0xE9};

unsigned char KeysBuf[4]; 
unsigned char Syncro= 0;
unsigned char Key2 = 0;
unsigned char Clear = 0;
unsigned char Ready = 0;

unsigned char RxBuffer[10] = {0};
signed char   Temperature = 0;
unsigned int  Cel = 0;
unsigned char Speed = 0;
unsigned short Rpm = 0;

unsigned int Volts = 0; //Напряжение в вольтах

char ForceDisplayVoltage = false;  // флаг принудительного отображения напряжения
char ForceDisplayTemper = false;   // флаг принудительного отображения температуры
char ForceDisplayCel = false;      // флаг принудительного отображения нагрузки
char ForceDisplaySpeed = false;    // флаг принудительного отображения скорости
char ForceDisplayRPM = false;      // флаг принудительного отображения оборотов

const int SoundPin = 2;
const byte COUNT_NOTES = 39;

int DelaySound = 1000; 

int tones[COUNT_NOTES] = {
 392, 392, 392, 311, 466, 392, 311, 466, 392,
 587, 587, 587, 622, 466, 369, 311, 466, 392,
 784, 392, 392, 784, 739, 698, 659, 622, 659,
 415, 554, 523, 493, 466, 440, 466,
 311, 369, 311, 466, 392
};
/*
int durations[COUNT_NOTES] = {
 350, 350, 350, 250, 100, 350, 250, 100, 700,
 350, 350, 350, 250, 100, 350, 250, 100, 700,
 350, 250, 100, 350, 250, 100, 100, 100, 450,
 150, 350, 250, 100, 100, 100, 450,
 150, 350, 250, 100, 750
};
*/

int durations[COUNT_NOTES] = {
 300, 300, 300, 200, 50, 300, 200, 50, 650,
 300, 300, 300, 200, 500, 300, 200, 500, 650,
 300, 200, 500, 300, 200, 500, 500, 500, 400,
 100, 300, 200, 50, 50, 50, 400,
 100, 300, 200, 50, 700
};

void sound() {
    for (int i = 0; i <= COUNT_NOTES; i++ ) {
        tone( SoundPin, tones[i], durations[i] * 2 );
        delay( durations[i] * 2 );
        noTone( SoundPin );
    }
}

//////////////////////
// Функция чтения ADC
//////////////////////
unsigned long Read_ADC() {
 if ((millis() - PrevADCMillis >= TIMEADC)) { 
    
   DIDR0 |=0x80; // отключаем цифровой вход ADC7
  
   unsigned char i;
   unsigned long result = 0;
  
   // Настраиваю  АЦП
    ADMUX |= (1 << REFS1)|(1 << REFS0) // Int. 1.1V Ref. with ext. cap. at AREF.       
        |(1 << MUX0)|(1 << MUX1)|(1 << MUX2);  // ADC7                                
         // |(1 << MUX3); // temper ADC
              
    ADCSRA |= (1 << ADEN) // ADC enable
        |(1 << ADPS2)|(1 << ADPS1)|(1 << ADPS0); // CK/128
  
    for (i=0; i<ADC_AVERAGE;i++) {  
      do {ADCSRA |= (1 << ADSC);} // Старт преобразования
      while ((ADCSRA & (1 << ADIF)) == 0); // ждем флаг ADIF 
      result = result + ADCW;
      //result = result + (ADCL|ADCH << 8);
      //_delay_us(10);
      ADCSRA |= (1 << ADIF);    // сбрасываем флаг ADIF      
      ADCSRA &= ~(1 << ADSC);   // Стоп преобразования
    }
    return result / ADC_AVERAGE;
  PrevADCMillis = millis();
 }
}

void setup() {
  InitPorts(); 
  InitDinInd();
  InitTimer2(); 
  ///InitKL_5baud();
  //ReadTrouble(); 

  ///tone(SoundPin, 2014);
  ///delay(DelaySound);
  ///noTone(SoundPin); // Выключаем звук

  sound();
}

void loop() {
  ProcessButton();
  ReadTemperDs18B20();
  Read_ADC();
  ///ProcessKLine(); 
  ProcessDisplay(); 
  ProcessData(); 
  ProcessBeeper();
}

///////////////////////////////
//Функция инициализации Портов
///////////////////////////////
void InitPorts() {
  pinMode(TX,OUTPUT);
  digitalWrite(TX,HIGH);   
  pinMode(BUTTON, INPUT_PULLUP);   
  pinMode(SPEAKER, OUTPUT);
  digitalWrite(SPEAKER,LOW);  
}

////////////////////////
//функция опроса кнопки
////////////////////////
void ProcessButton() {
  PrevButtonState = false; //предыдущее состояние кнопки
    ButtonState = (!digitalRead(BUTTON)); // читаем текущие состояние кнопки по нажатию
    //ButtonState = digitalRead(BUTTON); // читаем текущие состояние кнопки по отжатию
    if (!PrevButtonState && ButtonState && (millis() - PrevButtMillis > 200)) { // была не нажата, стала нажата и с прошлого нажатия прошло 200ms
      PrevButtMillis = millis(); // Запоминаем время первого срабатывания
      ButtonState = true;
      if (++ModeDisp >= MODEDISPMAX) ModeDisp = 0; 
    }
     else  
        PrevButtonState = ButtonState; // не нажата   
}

//////////////////////////////////
// Функция отображения параметров
//////////////////////////////////
void ProcessDisplay() {
CurrentStateMillis = millis(); //записали время текущего отображения параметра

  if (!(StateSelectMode && (CurrentStateMillis - PrevButtMillis) < TIMEDISP))//если не 0 режим и после нажатия прошло время отображения параметра
    ModeDisp = 0; //переходим на 0 режим, температура салона

  if (ForceDisplayVoltage) {    //принудительно отображаем напряжение
    ForceDisplayVoltage = false;
    ModeDisp = 1;
  }
      
  if (ForceDisplayTemper) {    //принудительно отображаем температуру
    ForceDisplayTemper = false;
    ModeDisp = 2;
  }

  if (ForceDisplayCel) {    //принудительно отображаем нагрузку
    ForceDisplayCel = false;
    ModeDisp = 3;
  }

  if (ForceDisplaySpeed) {    //принудительно отображаем скорость
    ForceDisplaySpeed = false;
    ModeDisp = 4;
  }

  if (ForceDisplayRPM) {       //принудительно отображаем обороты
    ForceDisplayRPM = false;
    ModeDisp = 5;
  }
  
  switch (ModeDisp) {
    //температура DS18B20
    case 0:
      StateSelectMode = true;     
      DispTemperDs18B20();
      Digit[0] = 16;
      Dp1 = 0;
      Dp3 = 0; 
      break;  

//напряжение борт. сети
    case 1:
      Volts = Read_ADC() * 100 / 475;   // Read_ADC() * 1024 / 1.1; 
      //Volts = Read_ADC() * (10240 / 11);   // Read_ADC() * 1024 / 1.1;
      //Volts = Volts / 1100;  
      ///Volts = Read_ADC() * 11; 
      ///Volts = Volts / 1024;
      ///Volts = Volts * 22;
       
      if ((Volts%1000/100)==0) Digit[0] = 16;  
      else Digit[0] = Volts%1000/100; // Десятки вольт
      Digit[1] = Volts%100/10;  // Единицы вольт
      Digit[2] = Volts%10;    // 1/10 вольт 
      Digit[3] = 28;  
      Dp1 = 1;
      Dp3 = 0; 
      break; 
    
//температура K-Line
    case 2:   
      unsigned char temper_module;
      temper_module = Temperature >=0 ? Temperature : - Temperature;       
      if (Temperature < 0) Digit[0] = 18;       // "прочерк" 
      else {
        if ((temper_module%1000/100) == 0) Digit[0] = 16; // если 0 то пусто
        else Digit[0] = temper_module%1000/100; // Сотни 
      }        
      Digit[1] = temper_module%100/10; // Десятки 
      Digit[2] = temper_module%10;     // Единицы 
      Digit[3] = 19;                   // "Градус"  
      Dp1 = 0;
      Dp3 = 1;  
      break;  
    
//нагрузка K-Line
    case 3:
      if ((Cel%1000/100) == 0) Digit[0] = 16; // если 0 то пусто
      else Digit[0] = Cel%1000/100;  // Сотни           
      Digit[1] = Cel%100/10;         // Десятки 
      Digit[2] = Cel%10;             // Единицы 
      Digit[3] = 24;                 // "L" 
      Dp1 = 0;
      Dp3 = 0;    
      break; 
      
// скорость K-Line
    case 4:
      if (((Speed)%1000/100) == 0) Digit[0] = 16; // если 0 то пусто
      else Digit[0] = Speed%1000/100;  // Сотни           
      Digit[1] = Speed%100/10;         // Десятки 
      Digit[2] = Speed%10;             // Единицы 
      Digit[3] = 21;                   // "c" 
      Dp1 = 0; 
      Dp3 = 0;   
      break;    
       
//обороты K-Line
    case 5:
      if (Rpm % 10000/1000 == 0) Digit[0] = 16; // если 0 то пусто 
      else Digit[0] = Rpm%10000/1000;         // Тысячи
      Digit[1] = Rpm%1000/100;                // Сотни 
      Digit[2] = Rpm%100/10;                  // Десятки 
      Digit[3] = Rpm%10/1;                    // Единицы 
      Dp1 = 0; 
      Dp3 = 0;  
      break;
  }
}

////////////////////////////////////
// Функция чтения параметров K-Line
////////////////////////////////////
void ProcessKLine() {
  static char index;            //номер команды
  
  if (!(millis() - PrevKlineMillis >= TIMEKLINE)) return;
  
    if (++index>=4) index = 0;
    switch (index) {
      
//температура
      case 0:
        TxBuf(EngCoolTemp,sizeof(EngCoolTemp));           
        delay(TxToRx);          
        if (Serial.available()) {
          for (int i = 0; i < 7; i++) {    
            unsigned char byte_in = Serial.read();
            RxBuffer[i] = byte_in;  
            delay(W3byteIn); //5ms
          }
        } 
        if (RxBuffer[0]==0x83 && RxBuffer[1]==0xF1 && RxBuffer[2]==0x10
          && RxBuffer[3]==0x41 && RxBuffer[4]==0x05) {
          Temperature = RxBuffer[5];
          //Temperature = 0;
          Temperature = Temperature - 0x28;     
        } else {
          RxBuffer[5] = 0;
          Temperature = 0;
        }
        break;   

//Нагрузка
      case 1:
        TxBuf(CalcEngLoad,sizeof(CalcEngLoad)); 
        delay(TxToRx);  
        if (Serial.available()) {
          for (byte i = 0; i < 7; i++) {    
            unsigned char byte_in = Serial.read();
            RxBuffer[i] = byte_in;  
            delay(W3byteIn);
          }
        }      
        if (RxBuffer[0]==0x83 && RxBuffer[1]==0xF1 && RxBuffer[2]==0x10
          && RxBuffer[3]==0x41 && RxBuffer[4]==0x04) {
          //RxBuffer[5] = 128;
          Cel = RxBuffer[5] * 100;   
          Cel = Cel / 255;
          RxBuffer[5] = 0; 
        } else {
          RxBuffer[5] = 0; 
          Cel = 0;  
        }
        break;
            
//скорость 
      case 2:
        TxBuf(VehicleSpeed,sizeof(VehicleSpeed)); 
        delay(TxToRx);  
        if (Serial.available()) {
          for (byte i = 0; i < 7; i++) {    
            unsigned char byte_in = Serial.read();
            RxBuffer[i] = byte_in;  
            delay(W3byteIn);
          }
        }      
        if (RxBuffer[0]==0x83 && RxBuffer[1]==0xF1 && RxBuffer[2]==0x10
          && RxBuffer[3]==0x41 && RxBuffer[4]==0x0D) {  
          Speed = RxBuffer[5];
          //Speed = 79;
          RxBuffer[5] = 0; 
        } else {
          RxBuffer[5] = 0; 
          Speed = 0;  
        }
        break;
        
//обороты  
      case 3:
        TxBuf(EngineRPM,sizeof(EngineRPM));    
        delay(TxToRx);     
        if (Serial.available()) {
          for (byte i = 0; i < 8; i++) {    //7byte
            unsigned char byte_in = Serial.read();
            RxBuffer[i] = byte_in;  
            delay(W3byteIn);
          }
        }     
        if (RxBuffer[0]==0x84 && RxBuffer[1]==0xF1 && RxBuffer[2]==0x10 
            && RxBuffer[3]==0x41 && RxBuffer[4]==0x0C) {        
          Rpm = (256*RxBuffer[5]) + RxBuffer[6]; 
          Rpm = Rpm/4; 
          RxBuffer[5] = 0;
          RxBuffer[6] = 0;         
        } else {
            RxBuffer[5] = 0;
            RxBuffer[6] = 0; 
            Rpm = Rpm/4; 
        }
        break;
      //default: indexKLine = 0;
    }   
    PrevKlineMillis = millis();
}

////////////////////////////
// Функция обработки данных
////////////////////////////
void ProcessData() {    
  if (Temperature >= 110) {ForceBeepCount = 15; ForceDisplayTemper = true;}
  if ((Volts <= 10)&&(Volts >= 15)) {ForceBeepCount = 1; ForceDisplayVoltage = true;}
  if (Temperature == 40) {ForceBeepCount = 5; ForceDisplayTemper = true;}
  if (Speed == 75) {ForceBeepCount = 3; ForceDisplaySpeed = true;}
  if (Rpm >= 3800) {ForceBeepCount = 10; ForceDisplayRPM = true;} 
}

//////////////////////////
// Функция обработки Beep
//////////////////////////
void ProcessBeeper() {
  static char index;
  static char count;
  
  switch (index) {
    case 0: 
      if (!((millis()-PrevBeepMillis) >= BeepMillis)) break; 
        if (ForceBeepCount) {
          count = ForceBeepCount;
          ForceBeepCount = 0;
          PrevBeepMillis = millis(); 
          BeepMillis = 0;       
          index = 1;
        }    
      break;    
   
    case 1:
      if (!((millis()-PrevBeepMillis) >= BeepMillis)) break;
        BEEPER_ON();
        PrevBeepMillis = millis(); 
        BeepMillis = BEEPON;      
        index = 2;
      break;
    
    case 2: 
      if (!((millis()-PrevBeepMillis) >= BeepMillis)) break; 
      BEEPER_OFF();  
        if (--count>0) {
          PrevBeepMillis = millis(); 
          BeepMillis = BEEPOFF; 
          index = 1;   
        }
        else {
          PrevBeepMillis = millis(); 
          BeepMillis = BEEPPAUSE; 
          index = 0;                    
        }
      break;
   }
}

///////////////////////////////////////////////
//Функция инициализации Таймера2 для Дин. Инд.
///////////////////////////////////////////////
void InitTimer2() {
  cli();// Глобально запрещаю прерывания
  TCCR2A = 0;
  TCCR2B = 0;
  TIMSK2 = 0; 
  TCCR2A |= (1<<COM2A0); // Toggle OC2A,No CTC
  TCCR2B |= (1<<CS21)|(1<<CS20);              // Presc. = 32
  //TCCR2B |= (1<<CS22);                      // Presc. = 64
  TIMSK2 |= (1<<TOIE2); // enable timer overflow interrupt
  sei();// Глобально разрешаю прерывания 
}

//////////////////////////////////
// Обработчик прерывания дин. инд.
//////////////////////////////////
ISR(TIMER2_OVF_vect) { 
  RefreshDinInd();
}

///////////////////////////////
//Функция инициализации K-Line
///////////////////////////////
void InitKL_5baud() {
  delay(4000); //ждем перед инициализацией
  digitalWrite(TX,LOW); // start
  delay(200);
  digitalWrite(TX,HIGH); // 1,2
  delay(400);
  digitalWrite(TX,LOW); // 3,4
  delay(400);
  digitalWrite(TX,HIGH); // 5,6
  delay(400);
  digitalWrite(TX,LOW); // 7,8
  delay(400);
  digitalWrite(TX,HIGH); // stop
  delay(200);
//end 5-baud
  
  Serial.begin(10400); //60ms минимум - время настройки 10400
  delay(W1Syncro);     // 50ms (300-timeOut) 
  
//wait syncro 
  while (!(Serial.available()));
  Syncro = Serial.read();   
  if (Syncro == 0x55) MsbToLed(Syncro); 
  else while (1);
  
//wait keys  
  while (!(Serial.available()));
  for (byte i = 0; i < 2; i++) {    //2байта
    KeysBuf[i] = Serial.read();
    delay(W3byteIn); //15
  }
  MsbToLed(KeysBuf[0]); 
  LsbToLed(KeysBuf[1]);  
  delay(W2byteOut); //5 (20-timeOut) key2 0-24работает
  
//send key2   
  Key2 = KeysBuf[1];  
  if (Key2 == 0x8F) Serial.write(~Key2);    //ok            
  else while (1);
  
//получаем эхо отправленного байта Key2
  while (!(Serial.available()));
  unsigned char Clear = Serial.read();  

  while (!(Serial.available()));
  Ready = Serial.read(); 
    
  if (Ready == 0xCC) { 
  }  
  else while (1);
  delay(2000);
}

///////////////////////////////////
//Функция отправки данных по KLine
///////////////////////////////////
void TxBuf(unsigned char *array, unsigned int bufSizeTx) {
  for (int i = 0; i < bufSizeTx; i++) {     
    Serial.write(array[i]); 
    while (!(Serial.available()));
    unsigned char Clear = Serial.read();  
    delay(W2byteOut); 
  }
}
/////////////////////////
//Функция включения BEEP
/////////////////////////
void BEEPER_ON() {
  digitalWrite(SPEAKER,HIGH);  
}

//////////////////////////
//Функция выключения BEEP
//////////////////////////
void BEEPER_OFF() {
  digitalWrite(SPEAKER,LOW);  
}

////////////////////////
//Функция чтения ошибок
////////////////////////
void ReadTrouble() {
  TxBuf(Trouble,sizeof(Trouble));           
  delay(TxToRx);    
  if (Serial.available()) {
    for (int i = 0; i < 11; i++) {    //всего 10
      unsigned char byte_in = Serial.read();
      RxBuffer[i] = byte_in;  
      delay(W3byteIn);
    }
  } 

  char dtc1 = (RxBuffer[5] && 0x0C) >> 6;  
  char dtc2 = (RxBuffer[5] && 0x30) >> 4;
  char dtc3 =  RxBuffer[5] && 0x0F;
  char dtc4 = (RxBuffer[6] && 0xF0) >> 4;
  char dtc5 =  RxBuffer[6] && 0x0F;

  RxBuffer[5] = 0;
  RxBuffer[6] = 0;
  
  if (dtc2||dtc3||dtc4||dtc5) {
    Digit[0] = 14;
    Digit[1] = 22;
    Digit[2] = 30;
  }
  
  //char code = ' ';
  switch (dtc1) {
    case 0x00 :
      //code = 'P';
      Digit[3] = 26;
    break;
    case 0x01 :
      //code = 'C';
      Digit[3] = 12;
    break;
    case 0x02 :
      //code = 'B';
       Digit[3] = 11;
    break;
    case 0x03 :
      //code = 'U';
      Digit[3] = 28;
    break;
  }
  
  delay(1500);  
  Digit[0] = dtc2;
  Digit[1] = dtc3;
  Digit[2] = dtc4;
  Digit[3] = dtc5;
  delay(1500);
}

//////////////////////////
//Функция удаления ошибок
//////////////////////////
/*
void CLRTrouble() {
    TxBuf(ClearTrouble,sizeof(ClearTrouble));           
    delay(TxToRx);    
}
*/
Mersovod
Offline
Зарегистрирован: 09.03.2022
unsigned char EngCoolTemp[6]  = {0xC2, 0x33, 0xF1, 0x01, 0x05, 0xEC};
static unsigned char VehicleSpeed[6] = {0x68, 0x6A, 0xF1, 0x01, 0x0D, 0xD1};

А откуда узнать подобные запросы на параметры, которые описаны тут

Например я хочу получить уровень топлива, вернуть мозги должны PID = 2F, но какой запрос я должен сделать и как самому мне понимать какие делать для соответствующих PID'ов?

Aku
Offline
Зарегистрирован: 04.12.2018

Нужно отправить PID 01 00. Будет ответ всех поддерживаемых.

Mersovod
Offline
Зарегистрирован: 09.03.2022

А как узнать какой за что отвечает?

Feofan
Offline
Зарегистрирован: 28.05.2017

Mersovod пишет:
А как узнать какой за что отвечает?

Запрос 0100 только для диапазона 0x01-0x20.

0100     //  Supported PIDs [01 - 20]
0120     //  Supported PIDs [21 - 40]
0140     //  Supported PIDs [41 - 60]
0160     //  Supported PIDs [61 - 80]
0180     //  Supported PIDs [81 - A0]
01A0     //  Supported PIDs [A1 - C0]
01C0     //  Supported PIDs [C1 - E0]

Ответ содержит 4 байта. Для запроса 0100, к примеру, приходит ответ:10111110 00011111 10101000 00010011

Каждый бит указывает на поддержку определенного PID'а диапазона. Единица в самом правом бите указывает на наличие поддерживаемых PID'ов в следующем диапазоне. Standard PID's

Mersovod
Offline
Зарегистрирован: 09.03.2022

Это я вроде понял, не пойму откуда берётся последнее значение?

{0xC2, 0x33, 0xF1, 0x01, 0x05, 0xEC}; 

Т.е. 0хЕС

Komandir
Komandir аватар
Offline
Зарегистрирован: 18.08.2018

Это контрольная сумма - в данном протоколе младший байт суммы всех остальных !

0xC2+0x33+0xF1+0x01+0x05=0x01EC

В ответе от ЭБУ последний байт считается по той же схеме и если он не совпадает - произошла ошибка передачи !!!