Подружить ардуино и микас 7.1

Вот … в загашнике лежат , там размерность , скорость и формат посылки есть … разбирайтесь… Хотя если у Вас 9600 то скорее всего 9141 … т.к его скорость 10000, а 2000 повыше

Это китайский 9141 но на английском
ТАм пиды стандартные , но на микас7 они совпадали…обороты двигла
 Each PID was translated into meaningful
parameter the predefined formula
e.g., Engine RPM PID = 0x0C,
2 bytes (A and B) are returned
The predefined formula is
((A * 256) + B) / 4
Units : rpm


вот здесь есть исследование протокола микас 5.4 и 7.1, там все написано

это запрос списка параметров , перед этим нужно список сформировать командой 0х64
вы этот длинный запрос отправляли в сетапе (Nepon8), но эбу ничего не ответил

и FF FF это не ответ от блока , это вы видите эхо своей отправки 63 9D 0D блоку . Если бы у вас был аппаратный сериал а не софт , вы бы реальные эти байты и видели
Посмотрите в своем логе , у вас перед каждым ответом эбу есть некое количество FF и это количество коррелирует с количеством байт в запросах.

Клайн это однопроводная шина , и все что вы отправляете - оно возвращается в приемник (эхо называется), просто софт сериал (и/или ваш скетч) коряво это отрабатывает

1 лайк

микруха L9637D является лишь преобразователем физической линии сигнала (драйвер шины - ТТL превращает в клайн). То есть девайс (узел), с точки зрения сети, при этом остается один и тот же, поэтому в данном случае подключается Tx к Tx и Rx к Rx

Спасибо, да в этом было дело. Сам до этого допер, правда почти 8 часов на это ушло.
Надо было команду из ОпенДиаг скопировать, а я её ручками переписывал, вот видимо и ошибся где то.


Вот этот момент ещё не разобрал. То есть если в ответе будут попадаться 0х40 и 0х0D, то общее количество байт увеличивается? То есть если у меня обороты будут на 10 байте, а на 3 байте выпало 0х40, то в данном случае обороты будут на 11 байте?

нет, обороты будут результатом суммы 10 и 11 байта. в логике программы делается просто, если например ваш 10й байт равен 0x40 , то значение берем как 10байт+11байт


А это опечатка? Или в теле вообще не может быть 0х0D? И если будет последовательно 0х40 0хСD то это следует заменить на 0x0D?

это не опечатка. 0D используется как метка конца сообщения, поэтому в теле сообщение такой байт применять нельзя. Но если в теле сообщения (в данных) встретится 0x0D, его заменяют суммой двух других байт 0x40+0xCD , это будет как раз 0x0D (отбрасывая переполнение). А если в теле встречается 0х40 то также делится на два байта 0x40+0x00, что в сумме даст тот же 0x40.
То есть если мы встречаем в теле сообщения 0х40, то обязательно прибавляем следующий байт к нему .

примерно можно такой код применить


#include <SoftwareSerial.h>

SoftwareSerial K_LINE(7, 8); // RX, TX


byte answ_01[]={0x0B};
byte answ_6101[] = {0x3D, 0xC0, 0x23, 0xC0, 0x03, 0x04};
byte answ_6109[] = {0x01};
byte answ_63[] = {0x0D, 0xB0, 0x0, 0x40, 0x79, 0x0, 0x0, 0x0,
0x1B, 0x28, 0x28, 0xCC, 0x75, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 ,0x0, 0x0,
0xFF, 0xFF, 0x0, 0x0, 0x80, 0x0, 0x5A, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0xB2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0x16,
0x0, 0xFB, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xD};


void setup() {
  
  Serial.begin(19200);
  K_LINE.begin(9600);
  
}

void loop() { 
  

K_LINE_Read();
  
}


void K_LINE_Read(){
  
  static byte messageReveived = 0; 
  static byte Buffer[100];
  
  static byte Length = 0 ;
  static uint32_t TimerReset = 0;
  
  if (K_LINE.available()) {
       
  byte inbyte = K_LINE.read();
  TimerReset = millis(); //сброс таймера сброса сообщения (для случая, если данные по середине сообщения перестали поступать)
  if (inbyte==0x0D)
  {

Serial.print ("Received:  ");
for (byte i=0; i<Length-1; i++) {if (Buffer[i]<0x0F)Serial.print ("0"); Serial.print (Buffer[i], HEX);Serial.print (" ");}
byte checksumm = 0;
for (byte i=0; i<Length-1; i++){ checksumm+=Buffer[i];}
checksumm = 0-checksumm;
Serial.print ("  CS is ");
  
  if (checksumm==Buffer[Length-1]) // тут если получили валидное сообщение , что то делаем 
   {Serial.println ("OK!"); 
         if (Buffer[0] == 0x01) K_LINE_Send(answ_01, sizeof(answ_01));
    else if (Buffer[0] == 0x61 && Buffer[1] == 0x01 && Buffer[2] == 0x02 && Buffer[3] == 0x03 && Buffer[4] == 0x04 && Buffer[5] == 0x05 && Buffer[6] == 0x06) K_LINE_Send(answ_6101, sizeof(answ_6101));
    else if (Buffer[0] == 0x61 && Buffer[1] == 0x09) K_LINE_Send(answ_6109, sizeof(answ_6109)); 
    else if (Buffer[0] == 0x63) K_LINE_Send(answ_63, sizeof(answ_63));  
   }
else Serial.println ("FAIL!!!"); 


Serial.println();
  Length = 0 ; messageReveived = 0; 
  }
  else if (Length < 100 && inbyte!=0x0D) {Buffer[Length]=inbyte; Length++; messageReveived=1;}
  if (Length==100){messageReveived = 0; Length = 0 ;}
  
  }//end available

  // ниже таймер сброса сообщения (для случая, если данные по середине сообщения перестали поступать)
  if (messageReveived && millis() - TimerReset >=500) {TimerReset = millis(); messageReveived = 0; Length = 0 ;}
  
  }


 void K_LINE_Send(const byte *command, const size_t size) 
 {
  
  Serial.print ("      Send:   ");
  byte Checksum = 0;
  for(byte i=0; i<size; i++) 
  {
        Checksum+=command[i];
        
        
     if  (command[i]!=0x40 && command[i]!=0x0D)  {if (command[i]<0x0F)Serial.print ("0"); Serial.print(command[i],HEX); Serial.print(" ");  K_LINE.write (command[i]); } 
     else {K_LINE.write (0x40); Serial.print(0x40, HEX); Serial.print(" ");
        if (command[i]==0x40){K_LINE.write ((byte)0x00); Serial.print("00"); }
        if (command[i]==0x0D){K_LINE.write (0xCD); Serial.print(0xCD, HEX); }
         Serial.print(" ");
         }
        
  }
    Checksum = 0 - Checksum;
    K_LINE.write (Checksum);    Serial.print(Checksum,HEX); Serial.print(" ");  
    K_LINE.write (0x0D);  Serial.print("0D"); 
  Serial.println();
}

это код эмулятора ЭБУ микас. т.е. Подключить нужно ардуино+клайн к компу+клайн .
если не будет работать, то после каждого K_LINE.write(бла бла) добавить K_LINE.read();

1 лайк

тут не внимательно прочитал ваш вопрос, да вы правы, обороты сдвигаются на 1 байт , то есть 11й
но в любом случае, при таком раскладе , вам нужно логику такую в программу заложить, что когда будете парсить данные из сообщения от эбу, привязываться не к номеру байта в сообщении , а к последовательности , что за чем считывать, например первый параметр температура - считали один байт (или два если попалось 40 или 0D), сдвинули маркер на один байт или два(если попалось 40 или 0D) , и дальше например должен идти расход воздуха, считываем аналогично и так далее.

1 лайк

не было работы на работе, решил мозг потренировать. Вот скетч опроса микас 7.1. Скетч не проверял. хз заработает ли. (надеюсь ТС не пропадет, интересно же)

// ниже выбираем сериал порт, на котором подключена шина Клайн , раскомментировать естественно только одну строку, если все закомментированы - будет софт сериал
// #define K_LINE Serial
// #define K_LINE Serial1
// #define K_LINE Serial2
// #define K_LINE Serial3

#ifndef K_LINE
#include <SoftwareSerial.h>
SoftwareSerial K_LINE(10, 11); // (RX, TX) не забываем выбрать правильные пины софтсериал , если используем его
#endif

#define K_LINE_BITRATE   9600  // выбираем скорость шины клайн 
#define TERMINAL_BITRATE 19200 // выбираем скорость терминала для отладки 

#define BUFFER_SIZE 200    // размер буфера принятого сообщения
#define REQUEST_PERIOD 600 // выбираем периодичность запросов на ЭБУ, мс

// Ниже раскомментировать одну строку - что хотим видеть в терминале - лог байт общения или список полученных переменных. 
// Закомментировать обе строки в конце работы над проектом, когда отладка уже не нужна 
  #define DEBUG    
//#define DEBUG_PARAMETER

const byte answerStartSession =  0x0B; // тут пишем какой байт нужно получить от ЭБУ на запрос начала диагностической сессии 

// ниже заполняем массив параметров, которые хотим получать от эбу  (пишем заглавными буквами)
enum Data_quantity_                                    {ENG_TEMP,    AIR_TEMP,  IDLE_STEP,   ONBOARD_VOLT,  ENG_RPM,   UOZ,    TPS,    MAF,  FUEL_CONS,  INJ_TIME, Data_quantity}; //тут перечисляем какие параметры (PIDs) вы хотите получать от ЭБУ, в конце должно быть Data_quantity
const byte DatalistRequest[2][Data_quantity+1]={{0x64,     0x1A,       0x1C,       0x5C,        0x1E,        0x29,     0x26,   0x20,  0x21,    0x40,       0x3F                 }, //тут пишем байт запроса (PID) под каждым параметром
                                                {0x00,     0x01,       0x01,       0x01,        0x01,        0x01,     0x01,   0x01,  0x02,    0x02,       0x02                }}; //тут пишем под каждым пидом сколько байт его размерность (сколько байт в ответе от эбу на этот пид)

//ниже создаем переменные каждого параметра с нужным типом данных (переменные пишем прописными буквами)
int8_t engTemp;
int8_t airTemp;
byte idle_step;
float onb_volt;
uint32_t eng_rpm;
float uoz;
byte tps;
float maf;
float fuel_cons;
float inj_time;


//остальное ниже не трогаем 

const byte StartSession[] = {0x01}; // запрос начала диагностической сессии
const byte DataRequest[]  = {0x63}; // запрос предоставления параметров (которые указаны в массиве парамтеров выше)
const byte DTC_Read[]     = {0x02}; // запрос чтения ошибок
const byte DTC_erase_1[] = {0x62, 0x0E, 0x08}; // запрос1 на удаление ошибок 
const byte DTC_erase_2[] = {0x62, 0x0E, 0x00}; // запрос2 на удаление ошибок 

// статус запроса (что именно в данный момент запрашиваем от блока)
enum currentRequest_  {STARTSESSION, DATALIST_WRITE, DATA_REQUEST, DTC_READ, DTC_ERASE_1, DTC_ERASE_2};
byte currentRequest = STARTSESSION; 

byte CurrentByteNumber = 0; 
uint32_t requestTimer = 0;
bool requestTimerflag = 0 ; 
uint32_t pauseTimer = 0;
bool pauseTimerflag = 0 ; 
byte echo = 0 ; 

void setup() {
  //ниже выбор наличия эхо на шине , в зависимости от того, хард или софт сериал используем 
  #ifdef K_LINE
  echo = 1;
  #else 
  echo = 0;
  #endif

  
  #ifdef DEBUG or DEBUG_PARAMETER
  Serial.begin(TERMINAL_BITRATE);
  #endif

  for (byte i =1; i<=Data_quantity; i++) {if (DatalistRequest[1][i]>4) { 
  #ifdef DEBUG or DEBUG_PARAMETER
  Serial.print (F("  DatalistRequest -  the number of answer bytes is out of range !!!! SKETCH STOP !!! "));
  #endif
  // если в массиве параметров написали количество байт в ответе от эбу для какого-либо параметра больше 4, то скетч останавливается!!!!
  while(1); 
  }}
  
  K_LINE.begin(K_LINE_BITRATE);



// если нужно сделать чтение  ошибок из ЭБУ, в нужном месте скетча ставим:   currentRequest=DTC_READ;  
// если нужно сделать удаление ошибок из ЭБУ, в нужном месте скетча ставим:  currentRequest=DTC_ERASE_1;
}

void loop() { 


static uint32_t prevmillis = 0;
if (!pauseTimerflag && millis()- prevmillis>=REQUEST_PERIOD) // периодически посылаем на ЭБУ различные запросы
{ 
  
  prevmillis = millis(); 
     if (currentRequest == STARTSESSION)   K_LINE_Send (StartSession, sizeof(StartSession));
else if (currentRequest == DATALIST_WRITE) K_LINE_Send (DatalistRequest[0], sizeof(DatalistRequest[0]));   
else if (currentRequest == DATA_REQUEST)   K_LINE_Send (DataRequest, sizeof(DataRequest));  
else if (currentRequest == DTC_READ)       K_LINE_Send (DTC_Read, sizeof(DTC_Read));  
else if (currentRequest == DTC_ERASE_1)    K_LINE_Send (DTC_erase_1, sizeof(DTC_erase_1));  
else if (currentRequest == DTC_ERASE_2)    K_LINE_Send (DTC_erase_2, sizeof(DTC_erase_2));   


// ниже распечатка в терминал списка переменных, правим в соответсвии со своим массивом параметров
#ifdef DEBUG_PARAMETER
static byte n=0;
if (n==4){
n=0;
Serial.println();
Serial.print (F("engTemp  ")); Serial.println (engTemp);
Serial.print (F("airTemp  ")); Serial.println (airTemp);
Serial.print (F("idle_step  ")); Serial.println (idle_step);
Serial.print (F("onb_volt  ")); Serial.println (onb_volt);
Serial.print (F("eng_rpm  ")); Serial.println (eng_rpm);
Serial.print (F("uoz  ")); Serial.println (uoz);
Serial.print (F("tps  ")); Serial.println (tps);
Serial.print (F("maf  ")); Serial.println (maf);
Serial.print (F("fuel_cons  ")); Serial.println (fuel_cons);
Serial.print (F("inj_time  ")); Serial.println (inj_time);
  }
n++;
#endif
}

K_LINE_Read(); // процедура чтения шины клайн

// ниже работа таймера перезапуска диагностической сессии при неответах эбу
if (requestTimerflag && millis()-requestTimer>=5000){requestTimerflag=0; currentRequest = STARTSESSION; pauseTimerflag = 1; pauseTimer = millis();}
// ниже работа таймера паузы перед перезапуском диагностической сессии при неответах эбу
if (pauseTimerflag   && millis()-pauseTimer>=5000) {pauseTimerflag = 0 ;}
}


void K_LINE_Read(){ // процедура чтения шины клайн 
  
  static byte messageReveived = 0; 
  static byte Buffer[BUFFER_SIZE];
  
  static byte Length = 0 ;
  static uint32_t TimerReset = 0;
  
  if (K_LINE.available()) {
       
  byte inbyte = K_LINE.read();
  TimerReset = millis(); //сброс таймера сброса сообщения (для случая, если данные по середине сообщения перестали поступать)
  
  if (inbyte==0x0D) // если получили маркер конца сообщения запускаем процедуру парсинга сообщения (сама правит , если попадается 0x40 или 0x0D в теле сообщения) 
  {
    #ifdef DEBUG
    Serial.print ("Received:  ");
    for (byte i=0; i<Length-1; i++) {if (Buffer[i]<0x0F)Serial.print ("0"); Serial.print (Buffer[i], HEX);Serial.print (" ");}
    #endif
byte checksumm = 0;
for (byte i=0; i<Length-1; i++){ checksumm+=Buffer[i];}
checksumm = 0-checksumm;
 #ifdef DEBUG
Serial.print ("  CS is ");
#endif
  
  if (checksumm==Buffer[Length-1]) // тут если получили валидное сообщение , что то делаем 
   {
     #ifdef DEBUG
    Serial.println ("OK!"); 
    #endif
    
        if (currentRequest == STARTSESSION)   
          {
          if (Buffer[0]==answerStartSession) {currentRequest++;} 
          
          }
else if (currentRequest == DATALIST_WRITE) 
          {
           if (Buffer[0]==0x00) {currentRequest++;}
          }
else if (currentRequest == DATA_REQUEST) //-----------------------Если получили от ЭБУ валидное сообщение с текущими данными
          {
  
for (byte i=0; i<Data_quantity; i++){  //создаем цикл фор для парсинга каждого параметра из массива параметров  
  
  byte Byte[5]={0};
  for (byte p=1; p<DatalistRequest[1][i+1]+1; p++) // цикл фор для записи нужного количества байт каждого параметра (до  4х байт)
  {
  if (Buffer[CurrentByteNumber]==0x40) // если попался байт 0x40 , читаем еще байт за ним , чтобы понять какой байт в итоге получить
            {
                  if (Buffer[CurrentByteNumber+1]==0x00) {Byte[p] = 0x40;} 
             else if (Buffer[CurrentByteNumber+1]==0xCD) {Byte[p] = 0x0D;}
             CurrentByteNumber++; // если был байт 0x40 сдвигаем счетчик текущего номера байта на один вправо 
            }
   else {Byte[p] = Buffer[CurrentByteNumber];}        
  CurrentByteNumber++; // после считывания очередного байта параметра сдвигаем счетчик текущего номера байта на один вправо 
  }

// ниже пишем формулы для расчета каждого параметра 
     if (i == ENG_TEMP)    { engTemp = Byte[1]-40;}
else if (i == AIR_TEMP)    { airTemp = Byte[1]-40;}
else if (i == IDLE_STEP)   { idle_step = Byte[1]; }
else if (i == ONBOARD_VOLT){ onb_volt = Byte[1]/10.0; }
else if (i == ENG_RPM)     { eng_rpm  = Byte[1]*40; }
else if (i == UOZ)         { uoz  = Byte[1]/2.0; }
else if (i == TPS)         { tps  = Byte[1]; }
else if (i == MAF)         { maf  = ((uint32_t)Byte[2]*256ul+Byte[1])/100.00; }
else if (i == FUEL_CONS)   { fuel_cons = ((uint32_t)Byte[2]*256ul+Byte[1])/10.00; }
else if (i == INJ_TIME)    { inj_time  = ((uint32_t)Byte[2]*256ul+Byte[1])/125.00; }
  
}
CurrentByteNumber = 0;
          }

          
else if (currentRequest == DTC_READ)    
          {
          currentRequest=DATA_REQUEST; // тут сами допишете парсинг DTC 
          }
else if (currentRequest == DTC_ERASE_1)    
          {
           if (Buffer[0]==0x00) {currentRequest=DTC_ERASE_2;} 
          }
else if (currentRequest == DTC_ERASE_2)   
          {
           if (Buffer[0]==0x00) {currentRequest=DATA_REQUEST;} 
          }
   
   requestTimer = millis(); //так как приняли валидный ответ от эбу сбрасываем таймер перезапуска диагностической сессии при неответах эбу
   }

else // если в ответе эбу не совпала контрольная  сумма
  {
    #ifdef DEBUG
    Serial.println ("FAIL!!!"); 
    #endif
  }

#ifdef DEBUG
Serial.println();
#endif
  Length = 0 ; messageReveived = 0; 
  }
  else if (Length < BUFFER_SIZE && inbyte!=0x0D) {Buffer[Length]=inbyte; Length++; messageReveived=1;}
  if (Length==BUFFER_SIZE){messageReveived = 0; Length = 0 ;}
  
  }//end available

  // ниже таймер сброса сообщения (для случая, если данные по середине сообщения перестали поступать)
  if (messageReveived && millis() - TimerReset >=400) {TimerReset = millis(); messageReveived = 0; Length = 0 ;}
  
}


 void K_LINE_Send(const byte *command, const size_t size)   // процедура отправки сообщения на ЭБУ (сама правит , если попадается 0x40 или 0x0D в теле сообщения)
 {
  #ifdef DEBUG
  Serial.print ("      Send:   ");
  #endif
  byte Checksum = 0;
  for(byte i=0; i<size; i++) 
  {
        Checksum+=command[i];
        
        
     if  (command[i]!=0x40 && command[i]!=0x0D)  
            {
            #ifdef DEBUG
            if (command[i]<0x0F)Serial.print ("0"); Serial.print(command[i],HEX); Serial.print(" ");  
            #endif
            K_LINE.write (command[i]); if (echo)K_LINE.read();  
            } 
     else {
      K_LINE.write (0x40); if (echo)K_LINE.read(); 
       #ifdef DEBUG
      Serial.print(0x40, HEX); Serial.print(" ");
       #endif
      
        if (command[i]==0x40)
        {
          K_LINE.write ((byte)0x00); if (echo)K_LINE.read(); 
          #ifdef DEBUG 
          Serial.print("00"); 
          #endif
        }
        if (command[i]==0x0D)
        {
        K_LINE.write (0xCD); if (echo)K_LINE.read(); 
        #ifdef DEBUG 
        Serial.print(0xCD, HEX); 
        #endif 
        }
          #ifdef DEBUG
         Serial.print(" ");
         #endif
         }
        
  }
    Checksum = 0 - Checksum;
    K_LINE.write (Checksum);  if (echo)K_LINE.read();  
    K_LINE.write (0x0D);      if (echo)K_LINE.read(); 
    #ifdef DEBUG
    Serial.print(Checksum,HEX); Serial.print(" ");  
    Serial.print("0D"); 
    Serial.println();
    #endif
 if (!requestTimerflag) {requestTimerflag = 1; requestTimer = millis();} // запускаем таймер после запроса 
}

скетч в сообщении #33 корявый. Вот исправленный скетч. проверил его работу на эмуляторе эбу. Все работает. По идее и в реале должен работать .

// ниже выбираем сериал порт, на котором подключена шина Клайн , раскомментировать естественно только одну строку, если все закомментированы - будет софт сериал
// #define K_LINE Serial
 #define K_LINE Serial1
// #define K_LINE Serial2
// #define K_LINE Serial3

#ifndef K_LINE
#include <SoftwareSerial.h>
SoftwareSerial K_LINE(10, 11); // (RX, TX) не забываем выбрать правильные пины софтсериал , если используем его (если выше все дефайны закомментированы)
#endif

#define K_LINE_BITRATE   9600  // выбираем скорость шины клайн 
#define TERMINAL_BITRATE 19200 // выбираем скорость терминала для отладки 
#define WAIT_ECHO 3            // задержка ожидания эхо, мс.  играемся этой задержкой, начиная с 0 и выше, пока не заработает стабильно связь 

#define BUFFER_SIZE 200    // размер буфера принятого сообщения
#define REQUEST_PERIOD 600 // выбираем периодичность запросов на ЭБУ, мс

// Ниже раскомментировать строки - в зависимости что хотим видеть в терминале - лог байт общения и/или список полученных переменных. 
// Закомментировать обе строки в конце работы над проектом, когда отладка уже не нужна 
#define DEBUG    
#define DEBUG_PARAMETER

const byte answerStartSession =  0x0B; // тут пишем какой байт нужно получить от ЭБУ на запрос начала диагностической сессии 

//ниже создаем список параметров , которые хотим получать от ЭБУ (по феншую пишем заглавными буквами, а в конце должно быть Data_quantity ) 
enum Data_quantity_  {
  ENG_TEMP,    
  AIR_TEMP,   
  IDLE_STEP,   
  ONBOARD_VOLT,   
  ENG_RPM,   
  UOZ,   
  TPS,   
  MAF,  
  FUEL_CONS,   
  INJ_TIME, 
  Data_quantity
  };

struct Data_ {char Name[16]; float value; byte RequestPIDbyte; byte Numberbytes;};

// ниже заполняем таблицу, где строки это сами параметры в соответствии со списком выше (порядок не менять!), 
// а столбцы слева направо: ||имя параметра (НЕ БОЛЕЕ 15 СИМВОЛОВ!!!) || значение параметра (оставить 0) ||   байт запроса PID || количество байт,  которое содерджит ответ ЭБУ на запрос этого параметра (НЕ БОЛЕЕ 4 !!!) 
Data_ Data[Data_quantity]=
{
  {"ENG_TEMP",     0,  0x1A, 1},  
  {"AIR_TEMP",     0,  0x1C, 1},
  {"IDLE_STEP",    0,  0x5C, 1},
  {"ONBOARD_VOLT", 0,  0x1E, 1},
  {"ENG_RPM",      0,  0x29, 1},
  {"UOZ",          0,  0x26, 1},
  {"TPS",          0,  0x20, 1},
  {"MAF",          0,  0x21, 2},
  {"FUEL_CONS",    0,  0x40, 2}, 
  {"INJ_TIME",     0,  0x3F, 2},
};  

//остальное ниже не трогаем 

const byte StartSession[] = {0x01}; // запрос начала диагностической сессии
const byte DataRequest[]  = {0x63}; // запрос предоставления параметров (которые указаны в массиве парамтеров выше)
const byte DTC_Read[]     = {0x02}; // запрос чтения ошибок
const byte DTC_erase_1[]  = {0x62, 0x0E, 0x08}; // запрос1 на удаление ошибок 
const byte DTC_erase_2[]  = {0x62, 0x0E, 0x00}; // запрос2 на удаление ошибок 
byte DataLISTwrite[Data_quantity+1]; // запрос на запись списка параметров (0x64)


// статус запроса (что именно в данный момент запрашиваем от блока)
enum currentRequest_  {STARTSESSION, DATALIST_WRITE, DATA_REQUEST, DTC_READ, DTC_ERASE_1, DTC_ERASE_2};
byte currentRequest = STARTSESSION; 

byte CurrentByteNumber = 0; 
uint32_t requestTimer = 0;
bool requestTimerflag = 0 ; 
uint32_t pauseTimer = 0;
bool pauseTimerflag = 0 ; 
byte echo = 0 ; 



void setup() 
{

DataLISTwrite[0] = 0x64; // формирование запроса на запись списка параметров  
for (byte i =1; i<=Data_quantity; i++) DataLISTwrite[i] = Data[i-1].RequestPIDbyte; // формирование запроса на запись списка параметров  

//ниже выбор наличия эхо на шине , в зависимости от того, хард или софт сериал используем 
  #ifdef K_LINE
  echo = 1;
  #else 
  echo = 0;
  #endif

  
  #if defined DEBUG or defined DEBUG_PARAMETER
  Serial.begin(TERMINAL_BITRATE);
  #endif
  K_LINE.begin(K_LINE_BITRATE);
  
  // если нужно сделать чтение   ошибок из ЭБУ, в нужном месте скетча ставим:  currentRequest=DTC_READ;
  // если нужно сделать удаление ошибок из ЭБУ, в нужном месте скетча ставим:  currentRequest=DTC_ERASE_1;
}


void K_LINE_Read(){ // процедура чтения шины клайн 
  
  static byte messageReveived = 0; 
  static byte Buffer[BUFFER_SIZE];
  
  static byte Length = 0 ;
  static uint32_t TimerReset = 0;
  
  if (K_LINE.available()) {
       
  byte inbyte = K_LINE.read();
  TimerReset = millis(); //сброс таймера сброса сообщения (для случая, если данные по середине сообщения перестали поступать)
  
  if (inbyte==0x0D) // если получили маркер конца сообщения запускаем процедуру парсинга сообщения (сама правит , если попадается 0x40 или 0x0D в теле сообщения) 
  {
    #ifdef DEBUG
    Serial.print ("Received:  ");
    for (byte i=0; i<Length-1; i++) {if (Buffer[i]<0x0F)Serial.print ("0"); Serial.print (Buffer[i], HEX);Serial.print (" ");}
    #endif
byte checksumm = 0;
for (byte i=0; i<Length-1; i++){ checksumm+=Buffer[i];}
checksumm = 0-checksumm;
 #ifdef DEBUG
Serial.print ("  CS is ");
#endif
  
  if (checksumm==Buffer[Length-1]) // тут если получили валидное сообщение , что то делаем 
   {
     #ifdef DEBUG
    Serial.println ("OK!"); 
    #endif
    
        if (currentRequest == STARTSESSION)   
          {
          if (Buffer[0]==answerStartSession) {currentRequest++;} 
          
          }
else if (currentRequest == DATALIST_WRITE) 
          {
           if (Buffer[0]==0x00) {currentRequest++;}
          }
else if (currentRequest == DATA_REQUEST) //-----------------------Если получили от ЭБУ валидное сообщение с текущими данными
          {
  
for (byte i=0; i<Data_quantity; i++){  //создаем цикл фор для парсинга каждого параметра из массива параметров  
  
  byte Byte[Data[i].Numberbytes]={0};
  for (byte p=1; p<=Data[i].Numberbytes; p++) // цикл фор для записи нужного количества байт каждого параметра (до  4х байт)
  {
  if (Buffer[CurrentByteNumber]==0x40) // если попался байт 0x40 , читаем еще байт за ним , чтобы понять какой байт в итоге получить
            {
                  if (Buffer[CurrentByteNumber+1]==0x00) {Byte[p] = 0x40;} 
             else if (Buffer[CurrentByteNumber+1]==0xCD) {Byte[p] = 0x0D;}
             CurrentByteNumber++; // если был байт 0x40 сдвигаем счетчик текущего номера байта на один вправо 
            }
   else {Byte[p] = Buffer[CurrentByteNumber];}        
  CurrentByteNumber++; // после считывания очередного байта параметра сдвигаем счетчик текущего номера байта на один вправо 
  }

// ниже пишем формулы для расчета каждого параметра 
     if (i == ENG_TEMP)    { Data[i].value = Byte[1]-40;}
else if (i == AIR_TEMP)    { Data[i].value = Byte[1]-40;}
else if (i == IDLE_STEP)   { Data[i].value = Byte[1]; }
else if (i == ONBOARD_VOLT){ Data[i].value = Byte[1]/10.0; }
else if (i == ENG_RPM)     { Data[i].value = Byte[1]*40; }
else if (i == UOZ)         { Data[i].value = Byte[1]/2.0; }
else if (i == TPS)         { Data[i].value = Byte[1]; }
else if (i == MAF)         { Data[i].value = ((uint32_t)Byte[2]*256ul+Byte[1])/100.00; }
else if (i == FUEL_CONS)   { Data[i].value = ((uint32_t)Byte[2]*256ul+Byte[1])/10.00; }
else if (i == INJ_TIME)    { Data[i].value = ((uint32_t)Byte[2]*256ul+Byte[1])/125.00; }
  
}
CurrentByteNumber = 0;
          }

          
else if (currentRequest == DTC_READ)    
          {
          currentRequest=DATA_REQUEST; // тут сами допишете парсинг DTC 
          }
else if (currentRequest == DTC_ERASE_1)    
          {
           if (Buffer[0]==0x00) {currentRequest=DTC_ERASE_2;} 
          }
else if (currentRequest == DTC_ERASE_2)   
          {
           if (Buffer[0]==0x00) {currentRequest=DATA_REQUEST;} 
          }
   
   requestTimer = millis(); //так как приняли валидный ответ от эбу сбрасываем таймер перезапуска диагностической сессии при неответах эбу
   }

else // если в ответе эбу не совпала контрольная  сумма
  {
    #ifdef DEBUG
    Serial.println ("FAIL!!!"); 
    #endif
  }

#ifdef DEBUG
Serial.println();
#endif
  Length = 0 ; messageReveived = 0; 
  }
  else if (Length < BUFFER_SIZE && inbyte!=0x0D) {Buffer[Length]=inbyte; Length++; messageReveived=1;}
  if (Length==BUFFER_SIZE){messageReveived = 0; Length = 0 ;}
  
  }//end available

  // ниже таймер сброса сообщения (для случая, если данные по середине сообщения перестали поступать)
  if (messageReveived && millis() - TimerReset >=400) {TimerReset = millis(); messageReveived = 0; Length = 0 ;}
  
}


 void K_LINE_Send(const byte *command, const size_t size)   // процедура отправки сообщения на ЭБУ (сама правит , если попадается 0x40 или 0x0D в теле сообщения)
 {
  #ifdef DEBUG
  Serial.print ("      Send:   ");
  #endif
  byte Checksum = 0;
  for(byte i=0; i<size; i++) 
  {
        Checksum+=command[i];
        
        
     if  (command[i]!=0x40 && command[i]!=0x0D)  
            {
            #ifdef DEBUG
            if (command[i]<0x0F)Serial.print ("0"); Serial.print(command[i],HEX); Serial.print(" ");  
            #endif
            K_LINE.write (command[i]); if (echo){delay (WAIT_ECHO); K_LINE.read(); }
            } 
     else {
      K_LINE.write (0x40); if (echo){delay (WAIT_ECHO); K_LINE.read(); }
       #ifdef DEBUG
      Serial.print(0x40, HEX); Serial.print(" ");
       #endif
      
        if (command[i]==0x40)
        {
          K_LINE.write ((byte)0x00); if (echo){delay (WAIT_ECHO); K_LINE.read(); }
          #ifdef DEBUG 
          Serial.print("00"); 
          #endif
        }
        if (command[i]==0x0D)
        {
        K_LINE.write (0xCD); if (echo){delay (WAIT_ECHO); K_LINE.read(); }
        #ifdef DEBUG 
        Serial.print(0xCD, HEX); 
        #endif 
        }
          #ifdef DEBUG
         Serial.print(" ");
         #endif
         }
        
  }
    Checksum = 0 - Checksum;
    K_LINE.write (Checksum);  if (echo){delay (WAIT_ECHO); K_LINE.read(); }
    K_LINE.write (0x0D);      if (echo){delay (WAIT_ECHO); K_LINE.read(); }
    #ifdef DEBUG
    Serial.print(Checksum,HEX); Serial.print(" ");  
    Serial.print("0D"); 
    Serial.println();
    #endif
 if (!requestTimerflag) {requestTimerflag = 1; requestTimer = millis();} // запускаем таймер после запроса 
}


void K_LINE_Request()
{ 
static uint32_t prevmillis = 0;
if (!pauseTimerflag && millis()- prevmillis>=REQUEST_PERIOD) // периодически посылаем на ЭБУ различные запросы
  { 

    
         prevmillis = millis(); 
        if (currentRequest == STARTSESSION)   K_LINE_Send (StartSession, sizeof(StartSession));
   else if (currentRequest == DATALIST_WRITE) K_LINE_Send (DataLISTwrite, sizeof(DataLISTwrite));   
   else if (currentRequest == DATA_REQUEST)   K_LINE_Send (DataRequest, sizeof(DataRequest));  
   else if (currentRequest == DTC_READ)       K_LINE_Send (DTC_Read, sizeof(DTC_Read));  
   else if (currentRequest == DTC_ERASE_1)    K_LINE_Send (DTC_erase_1, sizeof(DTC_erase_1));  
   else if (currentRequest == DTC_ERASE_2)    K_LINE_Send (DTC_erase_2, sizeof(DTC_erase_2));   


   // ниже распечатка в терминал списка переменных
    #ifdef DEBUG_PARAMETER
    static byte n=0;
    if (n==4)
   {
    n=0; 
    Serial.println();
    for (byte i=0; i<Data_quantity; i++){Serial.print(Data[i].Name);Serial.print(F("   ")); Serial.println (Data[i].value, 2);}
    Serial.println();
   }
    n++;
   #endif
  }

// ниже работа таймера перезапуска диагностической сессии при неответах эбу
if (requestTimerflag && millis()-requestTimer>=5000){requestTimerflag=0; currentRequest = STARTSESSION; pauseTimerflag = 1; pauseTimer = millis();}
// ниже работа таймера паузы перед перезапуском диагностической сессии при неответах эбу
if (pauseTimerflag   && millis()-pauseTimer>=5000) {pauseTimerflag = 0 ;}
}


void loop() 
{ 
K_LINE_Request(); // процедура посылки запросов на ЭБУ
K_LINE_Read();    // процедура чтения шины клайн

// здесь добавляем свой проект 
}



1 лайк

Вот спасибо, поразбираюсь, возьму на вооружение как надо писать.
А так я написал по своему, немного по колхозному, но пока на столе все работает. +подключил Виртуино по блютуз для коррекции октанкорректора на ходу.
Сейчас не менее длительный этап оформления как это уже в машину в приборку затолкать

Кстати, по большей части проект затевался для считывания скорости авто. Вот запрос на скорость (его не было в документации):
0x67

только не забудьте отписаться заработал ли скетч #34. Если не заработал, пишите, найдем причину.

нашел ошибку в скетче #34. строку 152 заменить на

byte Byte[Data[i].Numberbytes+1]={0};

для этого скетч эмулятора и выложил , с помощью эмулятора можно подключить к нему программу диагностики и обратно инженерить протокол . Выявить какой параметр в каком байте передается, подобрать для него формулу.

Подскажите не пойму какая логика заложена в признаки, конкретно интересует признак наличия детонации: