Как стать автором
Обновить

Subaru и Arduino: протокол SSM1. Рукопожатие

Время на прочтение4 мин
Количество просмотров44K

Исходные данные




Приехала ко мне недавно плата под названием Carduino Nano v7. И как-то так вдруг совпало, что примерно в это же время на панели своего авто я с удивлением обнаружил горящую лампочку Check Engine. «Это жжжжжж неспроста» — подумал я, и углубился в поиски. Оказалось, что ошибки можно прочитать без дополнительных устройств — самодиагностика в Форестерах очень проста и доступна любому. Причем здесь тогда Arduino? А при том, что прямым следствием поисков информации по диагностики явилось обнаружение существования такой забавной штуки, как Subaru Select Monitor версии 1, поддержкой протокола которой (забавной штуки) оснащены старые автомобили Subaru, еще до того, как в них появилась K-линия. Именно к таким автомобилям и относится мой Форестер. Вот и появилась у меня озорная мыслишка — реализовать некое подобие бортового компьютера на Arduino. Подобные проекты с обращением к K-линии я видел, а вот с SSM1 — только пара видео на YouTube, никакой конкретики. Поэтому придется делать все самому.

Краткое описание протокола SSM1


Скорость обмена 1953 бод (1953-8E1). ЭБУ принимает команду чтения данных и начинает сыпать в ответ данными из запрошенного адреса, пока не получит команду остановиться. Чтобы записать данные по адресу, необходимо сначала прочитать данные по этому же адресу. Чтобы запросить идентификатор ЭБУ, необходимо сначала прочитать данные с любого адреса.

Команды




В ответ на команды чтения и записи данных приходит следующий пакет данных:


И снова Форестер


Несмотря на то, что в моем авто присутствует разъем OBD2, в нем имеется полное наличие отсутствия контакта «K-линия».

  • Контакт 4 — заземление кузова
  • Контакт 5 — сигнальное заземление
  • Контакт 6 — линия CAN-High, J-2284
  • Контакт 7 — K-линия диагностики (ISO 9141-2 и ISO/DIS 14230-4)
  • Контакт 9 — тактовый сигнал SSM
  • Контакт 11 — тактовый сигнал SSM
  • Контакт 12 — SSM to ECM — линия передачи данных от дилерского диагностического прибора SSM в ЭБУ
  • Контакт 13 — ECM to SSM — линия передачи данных от ЭБУ в дилерский диагностический прибор SSM
  • Контакт 14 — линия CAN-Low, J-2284
  • Контакт 15 — L-линия диагностики (ISO 9141-2 и ISO/DIS 14230-4)
  • Контакт 16 — питание +12 от АКБ

В разъеме моего авто задействовано 4 контакта — 4,5,12,13 и 16.

Раз, два, три, четыре, пять — начинаем сопрягать


Донором разъема OBD2 послужил адаптер ELM327, купленный у братьев-китайцев, но абсолютно бесполезный для моего Форестера. Вскрытие показало, что почти все ноги, кроме тех, что нужно, распаяны для использования в самом ELM'е. Чтобы не спалить порты на ардуине, ноги с данными (12 и 13) будем подключать через резисторы — я использовал резисторы на 240 Ом. Землю подключим к обеим ногам с землей (4 и 5). Питать Arduino пока что будем от прикуривателя через USB-адаптер.

Итак, используем 3 провода:


По идее, на этом можно было бы остановиться, зашить в Arduino скетч типа

void setup()
	{
	    pinMode(0, INPUT);
	    pinMode(1, OUTPUT);
	}
	 
	void loop() 
	{
	}
	

и, подключив Arduino к ноутбуку, использовать его как обычный шнурок USB-Subaru с программами типа EvoScan и SelectMonitor. Но основной идеей был именно бортовой компьютер, так что продолжим.

Сочиняем скетч


Поскольку в заголовок данной статьи вынесено слово «рукопожатие», ограничимся именно рукопожатием между Форестером и Arduino — прочитаем ROM ID автомобиля. Дабы ограничиться уже собранной конструкцией, прочитанный ROM ID «промигаем» встроенным светодиодом Arduino, обозначив 1 как длинную вспышку, а 0 как две коротких вспышки.

Итак, по действиям:
  1. Устанавливаем режим обмена UART 1953-8E1
  2. На 3 секунды зажигаем светодиод
  3. Гасим светодиод и ждем 2 секунды
  4. Запрашиваем ROM ID
  5. Промигиваем ROM ID встроенным светодиодом Arduino

Пришлось еще немного погуглить, как объяснить Arduino, что мне нужен контроль четности, и, в результате, получился вот такой скетч:

int led = 13;

void setup()  {
  byte romid[3];

  // Устанавливаем скорость обмена
  Serial.begin(1953);
  // Включаем контроль четности: Even Parity
  UCSR0C = ( UCSR0C & ~_BV(UPM00) | _BV(UPM01) ); 

  pinMode(led, OUTPUT);

  // Зажигаем светодиод на 3 секунды
  digitalWrite(led, HIGH);
  delay(3000);
  // Гасим светодиод и ждем 2 секунды
  digitalWrite(led, LOW); 
  delay(2000);
  
  
  if (ECU_GetROMID(romid))  // Если ROM ID все-таки прочитали,
  {
    for (int i=0;i<3;i++) {
      show_byte(romid[i]);  // То промигаем его светодиодом
    }
  }
}

void loop() {
  // а здесь нам ничего не надо
}

void show_byte(byte b) {
  for (int i=7;i>=0;i--) {
    if (bitRead(b,i)==1) {     
      digitalWrite(led, HIGH);
      delay(1000);
      digitalWrite(led, LOW);
      delay(1000);
    } else {
      digitalWrite(led, HIGH);
      delay(300);
      digitalWrite(led, LOW);
      delay(200);
      digitalWrite(led, HIGH);
      delay(300);
      digitalWrite(led, LOW);
      delay(1200);
    }
  }
}

void ECU_Stop() {
  byte txbuf[4]={0x12,0x00,0x00,0x00};
  
  Serial.write(txbuf[0]);
  Serial.write(txbuf[1]);
  Serial.write(txbuf[2]);
  Serial.write(txbuf[3]);
  
  delay(50);
  
  Serial.flush();
}

boolean ECU_GetROMID(byte * buffer) {
  char readCmd[4] ={0x78,0x00,0x00,0x00};
  char romidCmd[4]={0x00,0x46,0x48,0x49};
  char romid[3]   ={0};

  ECU_Stop();

  Serial.write(readCmd[0]);
  Serial.write(readCmd[1]);
  Serial.write(readCmd[2]);
  Serial.write(readCmd[3]);
  
  
  int retries = 0;
  while (retries<8) {
    int nbytes = Serial.readBytes(romid,3);
    
    if ((nbytes == 3) && (romid[0]!=0x00))
      break;
    
    Serial.write(romidCmd[0]);
    Serial.write(romidCmd[1]);
    Serial.write(romidCmd[2]);
    Serial.write(romidCmd[3]);
    ++retries;
  }
  ECU_Stop();
  
  buffer[0] = romid[0];
  buffer[1] = romid[1];
  buffer[2] = romid[2];
  
  if (romid[0] == 0x00) {
    return false;
  }
  
  return true;
}


Результат


Заливаем данный скетч в Arduino, спускаемся в машину, подключаем разъем OBD2, включаем зажигание, подаем питание на Arduino и… начинаем вбивать нолики и единички в телефон, поскольку больше с собой ничего не взяли. Arduino весело проморгал мне комбинацию 1010 0011 0000 0001 0001 0111, что в переводе на общечеловеческий означает 0xA30117 — это и есть ROM ID моего Форестера. «Ну, здравствуй, 0xA30117! А меня Роман зовут.»

Послесловие


К сожалению, для большинства автомобилей с поддержкой SSM1 известно в лучшем случае только 18 адресов важных параметров — таких, как напряжение аккумулятора, обороты двигателя и прочее. Поэтому работы еще непаханное поле — считывание дампа памяти, поиск адресов с ошибками и т.д. Ну что ж — есть еще, к чему стремиться…

Используемые материалы


Информация о протоколе SSM1
Обсуждение SSM1, информация о схемах переходников RS232-SSM1 и USB-SSM1
Теги:
Хабы:
+24
Комментарии10

Публикации