Оживляем дисплей от приборной панели автобуса

в 8:05, , рубрики: CAN, j1939, konsberg automotive, mcp2515, timeweb_статьи, автобус, Блок питания, дисплей, лиаз, приборная панель

Приветствую всех!

Насколько сложно запустить приборную панель от автобуса, грузовика или ещё какой-то спецтехники и вывести на неё какие-нибудь данные? Возможно ли вообще это сделать? Самое время выяснить это, попробовав провернуть всё самому.

Оживляем дисплей от приборной панели автобуса - 1

Итак, сегодня мы попробуем запустить дисплейный блок от приборной панели старого автобуса. Разберёмся, по какому протоколу он работает и что с ним ещё можно сделать. Как водится, будет много интересного.

❯ Суть такова

Началось всё это довольно просто: ко мне в руки попал дисплейный модуль Kongsberg Automotive KAntrak 2710, снятый с какого‑то теперь уже неведомого автобуса на авторазборке.

Я давно интересовался, как работает электроника на подобном транспорте и какие у неё отличия в протоколе обмена или чём‑то ещё от того железа, что можно встретить на легковых автомобилях. Поэтому, заполучив девайс, было решено его запустить и попытаться что‑то отобразить на нём. Пригодиться это может много где, например, можно будет попробовать подключить железку к какому‑нибудь автосимулятору или использовать как блок индикации в каком‑нибудь следующем проекте.

❯ Что это вообще за KAntrak?

Для начала разберёмся с тем, что это вообще за штука.

Оживляем дисплей от приборной панели автобуса - 2

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

У нас такие штуки ставилась на некоторые автобусы ЛиАЗ с двигателем MAN. Живьём мне довелось его увидеть на инструкторском пульте учебного автобуса ЛиАЗ 5292.22.

Источник

А вот этот же аппарат на приборной панели ЛиАЗ 5292.21 (справа от спидометра).

❯ Протоколы

Теперь разберёмся с тем, что вообще хочет на вход эта железка.

На легковом транспорте какого‑то стандарта коммуникации нет. Разные автопроизводители используют свои проприетарные протоколы. Существует, конечно, стандарты диагностики (например, знакомый многим OBD‑II), но к данным, которые отправляются на приборную панель, они не имеют отношения. Таким образом, чтобы заставить работать цифровой щиток приборов от какой‑то машины, надо либо подобрать адреса CAN, либо найти информацию о них (на тематических сайтах или путём записи обмена на шине).

Иначе обстоят дела на коммерческом транспорте — грузовиках, автобусах и спецтехнике. Там существует единый отраслевой стандарт — J1939. Для некоторого транспорта существуют другие протоколы, базирующиеся на нём же, но это редкая экзотика, в данный момент нас не интересующая. Более подробно о структуре данных рассказано тут.

Как следует из документации, KAntrak как раз поддерживает этот протокол. Помимо него упоминается ещё J1708/J1587 — ещё один стандартизированный протокол, использующий в качестве линии связи RS-485.

В J1939 сообщения передаются одним или несколькими пакетами CAN. Все пакеты имеют расширенный (29-битный) ID. Он представляет собой группу из трёх параметров: приоритет, PGN (Parameter Group Number) - код типа пакета или группы параметров (к примеру, PGN 65262 - температурные параметры), SA (Souce Address) — адрес блока, который послал это сообщение (к примеру, 0 — ЭБУ двигателя, 3 — блок управления КПП, 11 — ABS, 255 — зарезервированный код для широковещательной отправки).

Конкретный параметр определяет SPN (Suspect Parameter Number). Он не передаётся явно, спецификация J1939 (Digital Annex) для каждого PGN определяет в каких битах какого байта блока данных лежит этот параметр, в каком диапазоне он может лежать и какое разрешение имеет. Также имеется возможность передачи значений длиннее восьми байт путём использования специального транспортного протокола, но, скорее всего, нам всё это сейчас не понадобится.

❯ Обзор оборудования

Рассмотрим наш девайс поподробнее.

Оживляем дисплей от приборной панели автобуса - 4

А вот и он.

Сходу его вообще можно принять за часы или метеостанцию, но масса, качество пластика и разъём сзади вызывают интерес.

Оживляем дисплей от приборной панели автобуса - 5

Обратная сторона.

По центру разъём подключения (явно какой-то специфический, предназначенный специально для автомобильной промышленности). Крышка держится на четырёх саморезах. Один из них заклеен пломбой, которую мы, конечно же, отковыряем.

Оживляем дисплей от приборной панели автобуса - 6

Ещё одна наклейка.

Оживляем дисплей от приборной панели автобуса - 7

Ответная часть разъёма.

Оживляем дисплей от приборной панели автобуса - 8

Это DEUTSCH DT06-12S от TE Connectivity, применяемый на коммерческом автотранспорте.

Оживляем дисплей от приборной панели автобуса - 9

Конструкция довольно интересная, напоминает соединители СНЦ или Amphenol, где тоже в резиновый уплотнитель вставляются обжимные контакты.

Оживляем дисплей от приборной панели автобуса - 10

Провода промаркированы печатью на них.

❯ Внутренности

Что мы делаем, когда к нам в руки попадает какая-то интересная железка? Правильно: мы её разбираем.

Оживляем дисплей от приборной панели автобуса - 11

Открутив четыре самореза, снимаем переднюю часть.

Оживляем дисплей от приборной панели автобуса - 12

К рамке дисплея приклеен термистор.

Оживляем дисплей от приборной панели автобуса - 13

Всё самое интересное расположилось на обратной стороне платы.

Оживляем дисплей от приборной панели автобуса - 14

Девайс построен на чипе Infineon SAK-C164CI-LM.

Это специализированный микроконтроллер на архитектуре C166, предназначенный для использования в промышленности и на автотранспорте.

Оживляем дисплей от приборной панели автобуса - 15

EEPROM 93LC86.

Оживляем дисплей от приборной панели автобуса - 16

Чипы памяти.

Оживляем дисплей от приборной панели автобуса - 17

Импульсный преобразователь питания на чипе LM2574HVM-ADJ.

Оживляем дисплей от приборной панели автобуса - 18

CAN-трансивер PCA82C251T/YM.

❯ Первый запуск

Зачищаем провода и подаём питание.

Оживляем дисплей от приборной панели автобуса - 19

Девайс крякнул и показал заставку.

Оживляем дисплей от приборной панели автобуса - 20

Через пару секунд железка загружается и показывает прочерки. Хорошее начало.

Из всех приборов работает только указатель напряжения зарядки батареи — по сути просто вольтметр напряжения питания этой коробочки.

Оживляем дисплей от приборной панели автобуса - 21

Нажатием кнопок можно переключать «раскладку» приборов. При нажатии кратковременно включается тусклая зелёная подсветка.

Оживляем дисплей от приборной панели автобуса - 22

Можно отображать разные показания в виде графиков, чтобы смотреть на них в динамике.

Оживляем дисплей от приборной панели автобуса - 23

Список ошибок, явно подгружаемый откуда-то извне.

Оживляем дисплей от приборной панели автобуса - 24

При отсутствии данных на экране отображается вот такой значок.

❯ Подключаем CAN

Теперь попробуем подать какие-то данные.

Оживляем дисплей от приборной панели автобуса - 25

Если у вас нет ответной части от разъёма, нумерация контактов здесь такая.

Подключать будем по CAN, так как именно эти провода имелись у меня. Конечно, можно разобрать разъём и переставить пины, но делать этого я пока не буду.

Оживляем дисплей от приборной панели автобуса - 26

Конвертер USB-CAN, куда лучше подходящий для подобных опытов, я пока не раздобыл, поэтому экспериментировать будем с платой CAN на чипе MCP2515 и платой Arduino Nano. Связка из двух этих модулей у меня уже давно используется в качестве CAN-адаптера.

Оживляем дисплей от приборной панели автобуса - 27

Нашёл первый попавшийся скетч для имитации данных J1939.

Девайс явно увидел обмен, ранее упомянутый транспарант пропал. Правда, стрелки всё так же безмолвствовали. Странно.

Скорость CAN оказалась угадана верно: 250 кбит/с. С другой поведение блока ничем не отличалось от такового с отключенной шиной.

Оживляем дисплей от приборной панели автобуса - 28

Немного подкрутил тайминги, и вот первый результат.

Конечно, что-то тут явно не так и 119 передачи точно не бывает, но начало очень хорошее: девайс действительно хочет на вход J1939 со скоростью 250 кбит/с. С форматом данных разберёмся чуть позже.

Но почему же другие индикаторы не оживают? Дело в том, что KAntrak 2710 — не просто цифровой показометр, а по сути целый микрокомпьютер. Для него существуют какие-то утилиты конфигурации и даже SDK, позволяющий писать для этого дисплейного модуля приложения на Си. Всё это звучит достаточно грустно, так как никакого софта ожидаемо не ищется, а сама возможность что-то где-то настраивать намекает на то, что адреса, возможно, были изменены на какие-то свои.

Поскольку возможности записать обмен на рабочей машине у меня нет, выходов тут несколько: подобрать адреса вручную, отреверсить прошивку девайса или формат конфигурации в его EEPROM, либо попытаться найти ещё какую-то информацию, дающую понять, что ему вообще надо.

❯ Системные меню

Оказалось, что у девайса есть несколько меню с настройками.

Оживляем дисплей от приборной панели автобуса - 29

Нажатие крайней правой кнопки позволяет изменить яркость и контрастность.

Оживляем дисплей от приборной панели автобуса - 30

Удержание же отображает кое-что более интересное.

Оживляем дисплей от приборной панели автобуса - 31

В настройках ничего интересного: пределы, язык, единицы измерения.

Сразу переключил подсветку в постоянный режим. Пункт «КОР. СИГН.» выключает писк кнопок. TIER4 POPOUT — какая‑то штука для AdBlue.

Оживляем дисплей от приборной панели автобуса - 32

Также тут можно настроить уведомление о техобслуживании.

Оживляем дисплей от приборной панели автобуса - 33

Следующий кадр интереснее.

Что за «Показ» со значениями 0 и 1 — понятия не имею. А вот далее идут уже напрямую относящиеся к нашей цели пункты.

Оживляем дисплей от приборной панели автобуса - 34

Во-первых, это просмотр того, что происходит на шине.

Оживляем дисплей от приборной панели автобуса - 35

Можно в реальном времени посмотреть, что приходит на вход прибора, и убедиться, что данные верные.

Оживляем дисплей от приборной панели автобуса - 36

А вот этот пункт меню — самый важный. Здесь нас интересуют некие «АДР. ИС. ДАНН.».

Что это такое? Это адреса источников данных. Эти два параметра задают то, данные от каких устройств (проще говоря, пакеты с какими SA) вообще следует мониторить. Как мы помним, значение 3 в стандартном варианте J1939 соответствует адресу трансмиссии, именно поэтому номер передачи отображается верно. А вот в качестве первого источника указан какой-то адрес 17, тогда как двигатель обычно располагается на нулевом. Похоже, в этом и кроется причина того, почему другие данные не отображаются.

❯ Запускаем

С причиной глюков разобрались. Поэтому меняем в скетче SA двигателя с 0 на 17, загружаем в плату...

Оживляем дисплей от приборной панели автобуса - 37

...и оно работает!

Оживляем дисплей от приборной панели автобуса - 38

Можно зайти в третий пункт меню — Database Viewer. Здесь в реальном времени отображаются вообще все поддерживаемые значения.

Оживляем дисплей от приборной панели автобуса - 39

Ещё один монитор CAN, судя по всему, какой-то низкоуровневый. Выйти из него можно только перезагрузкой блока.

Оживляем дисплей от приборной панели автобуса - 40

Из интересного — если отправить ему напряжение по CAN, он будет показывать его вместо того, что измерил сам.

А вот тот самый текстовый скетч

J1939
#include <SPI.h>
#include <mcp2515.h>

#define CAN_CS_PIN   3
#define CAN_INT_PIN  2

// Адреса источников (SA)
#define SA_ENGINE        17   // 0x11
#define SA_TRANSMISSION   3   // 0x03

MCP2515 mcp2515(CAN_CS_PIN);

unsigned long lastSend = 0;
const unsigned long sendInterval = 100;

// --- Параметры двигателя ---
uint16_t engineRpm      = 800;
uint8_t  coolantTemp    = 80;
int8_t   gear           = 125; //125 = N; < 125 = R; > 125 = F
uint16_t vehicleSpeed   = 0;
uint8_t  fuelLevel      = 75;
uint16_t oilPressure    = 400;

// --- Электрика ---
uint16_t batteryVoltage    = 480;   // 24.00 V (SPN 168)
int16_t  netBatteryCurrent = 0;     // А (SPN 114, 1 A/bit, знаковый)
uint16_t alternatorCurrent = 50;    // А (SPN 115, 1 A/bit)
uint16_t chargingVoltage   = 480;  // 24.00 V (SPN 167)

// --- Температуры ---
uint8_t  oilTemp          = 90;   // SPN 175, °C (+40 offset)
uint8_t  turboOilTemp     = 120;  // SPN 176, °C (+40 offset)
uint8_t  transmissionTemp = 70;   // SPN 177, °C (+40 offset)
uint8_t  fuelTemp         = 25;   // SPN 174, °C (+40 offset)
int16_t  ambientTempRaw   = 2931; // SPN 171, 0.03125 °C/bit, -273 offset

bool addressClaimedSent = false;

void setup() {
  mcp2515.reset();
  mcp2515.setBitrate(CAN_250KBPS, MCP_8MHZ);
  mcp2515.setNormalMode();
}

void loop() {
  if (!addressClaimedSent) {
    sendAddressClaimed(SA_ENGINE);
    sendAddressClaimed(SA_TRANSMISSION);
    addressClaimedSent = true;
    delay(50);
  }

  if (millis() - lastSend >= sendInterval) {
    lastSend = millis();

    // Имитация изменений
    engineRpm    = constrain(engineRpm + random(-20, 20), 600, 2500);
    coolantTemp  = constrain(coolantTemp + random(-1, 1), 70, 95);
    gear         = random(125, 134);
    vehicleSpeed = map(engineRpm, 600, 2500, 0, 80);
    fuelLevel    = constrain(fuelLevel + random(-1, 1), 0, 100);
    oilPressure  = constrain(oilPressure + random(-10, 10), 200, 600);

    batteryVoltage    = constrain(batteryVoltage + random(-5, 5), 420, 480);
    netBatteryCurrent = constrain(netBatteryCurrent + random(-2, 2), -30, 30);
    alternatorCurrent = constrain(alternatorCurrent + random(-2, 2), 30, 80);
    chargingVoltage   = constrain(chargingVoltage + random(-5, 5), 420, 480);

    oilTemp          = constrain(oilTemp + random(-1, 1), 80, 110);
    turboOilTemp     = constrain(turboOilTemp + random(-2, 2), 100, 150);
    transmissionTemp = constrain(transmissionTemp + random(-1, 1), 60, 95);
    fuelTemp         = constrain(fuelTemp + random(-1, 1), 15, 45);
    ambientTempRaw   = constrain(ambientTempRaw + random(-10, 10), 2800, 3100);

    // Отправка сообщений
    sendEEC1(engineRpm);
    sendET1(coolantTemp, oilTemp, turboOilTemp, fuelTemp);
    sendTC1(gear);
    sendCCVS(vehicleSpeed);
    sendFLI(fuelLevel);
    sendEOP(oilPressure);
    sendVEP1(batteryVoltage, netBatteryCurrent, alternatorCurrent, chargingVoltage);
    sendTRF1(transmissionTemp);
    sendAMB(ambientTempRaw);
  }
}

// ------------------------------------------------------------------
// Address Claimed (PGN 60928)
// ------------------------------------------------------------------
void sendAddressClaimed(uint8_t sourceAddress) {
  struct can_frame frame;
  frame.can_id = (6UL << 26) | (60928UL << 8) | sourceAddress;
  frame.can_id |= CAN_EFF_FLAG;
  frame.can_dlc = 8;

  frame.data[0] = 0x01;
  frame.data[1] = 0x00;
  frame.data[2] = 0xAB;
  frame.data[3] = 0x00;
  frame.data[4] = 0x00;
  frame.data[5] = 0x00;
  frame.data[6] = 0x00;
  frame.data[7] = 0x00;

  mcp2515.sendMessage(&frame);
}

// ------------------------------------------------------------------
// EEC1 (PGN 61444) — обороты двигателя (SPN 190), SA = 17
// ------------------------------------------------------------------
void sendEEC1(uint16_t rpm) {
  struct can_frame frame;
  frame.can_id = (6UL << 26) | (61444UL << 8) | SA_ENGINE;
  frame.can_id |= CAN_EFF_FLAG;
  frame.can_dlc = 8;

  uint16_t rpmRaw = rpm / 0.125;
  frame.data[3] = rpmRaw & 0xFF;
  frame.data[4] = (rpmRaw >> 8) & 0xFF;

  frame.data[0] = 0xFF;
  frame.data[1] = 0xFF;
  frame.data[2] = 0xFF;
  frame.data[5] = 0xFF;
  frame.data[6] = 0xFF;
  frame.data[7] = 0xFF;

  mcp2515.sendMessage(&frame);
}

// ------------------------------------------------------------------
// ET1 (PGN 65262) — температуры двигателя, SA = 17
// SPN 110: байт 1, SPN 174: байт 2, SPN 175: байты 3-4, SPN 176: байты 5-6
// ------------------------------------------------------------------
void sendET1(uint8_t coolant, uint8_t oil, uint8_t turboOil, uint8_t fuel) {
  struct can_frame frame;
  frame.can_id = (6UL << 26) | (65262UL << 8) | SA_ENGINE;
  frame.can_id |= CAN_EFF_FLAG;
  frame.can_dlc = 8;

  frame.data[0] = coolant + 40;           // SPN 110, байт 1 (ОЖ)
  frame.data[1] = fuel + 40;              // SPN 174, байт 2 (топливо)
  frame.data[2] = (oil + 40) & 0xFF;      // SPN 175, байт 3 (масло ДВС, младший)
  frame.data[3] = 0xFF;                   // SPN 175, байт 4 (старший, 0xFF = не используется)
  frame.data[4] = (turboOil + 40) & 0xFF; // SPN 176, байт 5 (масло турбины, младший)
  frame.data[5] = 0xFF;                   // SPN 176, байт 6 (старший)
  frame.data[6] = 0xFF;
  frame.data[7] = 0xFF;

  mcp2515.sendMessage(&frame);
}

// ------------------------------------------------------------------
// TC1 (PGN 61445) — выбранная передача (SPN 524), SA = 3
// ------------------------------------------------------------------
void sendTC1(int8_t gear) {
  struct can_frame frame;
  frame.can_id = (6UL << 26) | (61445UL << 8) | SA_TRANSMISSION;
  frame.can_id |= CAN_EFF_FLAG;
  frame.can_dlc = 8;

  if (gear == 0) {
    frame.data[0] = 0;
  } else if (gear > 0) {
    frame.data[0] = gear;
  } else {
    frame.data[0] = 256 + gear;
  }

  for (int i = 1; i < 8; i++) frame.data[i] = 0xFF;
  mcp2515.sendMessage(&frame);
}

// ------------------------------------------------------------------
// CCVS (PGN 65265) — скорость (SPN 84), SA = 17
// ------------------------------------------------------------------
void sendCCVS(uint16_t speed) {
  struct can_frame frame;
  frame.can_id = (6UL << 26) | (65265UL << 8) | SA_ENGINE;
  frame.can_id |= CAN_EFF_FLAG;
  frame.can_dlc = 8;

  uint16_t speedRaw = speed * 256;
  frame.data[1] = speedRaw & 0xFF;
  frame.data[2] = (speedRaw >> 8) & 0xFF;

  frame.data[0] = 0xFF;
  for (int i = 3; i < 8; i++) frame.data[i] = 0xFF;
  mcp2515.sendMessage(&frame);
}

// ------------------------------------------------------------------
// FLI (PGN 65276) — уровень топлива (SPN 96), SA = 17
// ------------------------------------------------------------------
void sendFLI(uint8_t fuelLevel) {
  struct can_frame frame;
  frame.can_id = (6UL << 26) | (65276UL << 8) | SA_ENGINE;
  frame.can_id |= CAN_EFF_FLAG;
  frame.can_dlc = 8;

  frame.data[1] = fuelLevel / 0.4;

  frame.data[0] = 0xFF;
  for (int i = 2; i < 8; i++) frame.data[i] = 0xFF;
  mcp2515.sendMessage(&frame);
}

// ------------------------------------------------------------------
// EOP (PGN 65263) — давление масла (SPN 100), SA = 17
// ------------------------------------------------------------------
void sendEOP(uint16_t pressure) {
  struct can_frame frame;
  frame.can_id = (6UL << 26) | (65263UL << 8) | SA_ENGINE;
  frame.can_id |= CAN_EFF_FLAG;
  frame.can_dlc = 8;

  frame.data[3] = pressure / 4; // разрешение 4 кПа/бит

  frame.data[0] = 0xFF;
  frame.data[1] = 0xFF;
  frame.data[2] = 0xFF;
  frame.data[4] = 0xFF;
  frame.data[5] = 0xFF;
  frame.data[6] = 0xFF;
  frame.data[7] = 0xFF;

  mcp2515.sendMessage(&frame);
}

// ------------------------------------------------------------------
// VEP1 (PGN 65271) — электрическая система, SA = 17
// SPN 114: байты 1-2, SPN 115: байты 3-4, SPN 167: байты 5-6, SPN 168: байты 7-8
// ------------------------------------------------------------------
void sendVEP1(uint16_t battVolt, int16_t netCurr, uint16_t altCurr, uint16_t chgVolt) {
  struct can_frame frame;
  frame.can_id = (6UL << 26) | (65271UL << 8) | SA_ENGINE;
  frame.can_id |= CAN_EFF_FLAG;
  frame.can_dlc = 8;

  // SPN 114: Net Battery Current, байты 1-2, 1 A/bit, знаковый
  frame.data[0] = netCurr & 0xFF;
  frame.data[1] = (netCurr >> 8) & 0xFF;

  // SPN 115: Alternator Current, байты 3-4, 1 A/bit
  frame.data[2] = altCurr & 0xFF;
  frame.data[3] = (altCurr >> 8) & 0xFF;

  // SPN 167: Charging System Potential, байты 5-6
  frame.data[4] = chgVolt & 0xFF;
  frame.data[5] = (chgVolt >> 8) & 0xFF;

  // SPN 168: Battery Potential / Power Input 1, байты 7-8
  frame.data[6] = battVolt & 0xFF;
  frame.data[7] = (battVolt >> 8) & 0xFF;

  mcp2515.sendMessage(&frame);
}

// ------------------------------------------------------------------
// TRF1 (PGN 65272) — температура масла в КПП (SPN 177), SA = 3
// SPN 177 находится в байте 2 (data[1]), 1 °C/bit, -40 смещение
// ------------------------------------------------------------------
void sendTRF1(uint8_t temp) {
  struct can_frame frame;
  frame.can_id = (6UL << 26) | (65272UL << 8) | SA_TRANSMISSION;
  frame.can_id |= CAN_EFF_FLAG;
  frame.can_dlc = 8;

  frame.data[0] = 0xFF;      // SPN 126 (oil pressure) — не используется
  frame.data[1] = temp + 40; // SPN 177, байт 2
  frame.data[2] = 0xFF;      // SPN 161 (oil level) — не используется
  frame.data[3] = 0xFF;
  frame.data[4] = 0xFF;
  frame.data[5] = 0xFF;
  frame.data[6] = 0xFF;
  frame.data[7] = 0xFF;

  mcp2515.sendMessage(&frame);
}

// ------------------------------------------------------------------
// AMB (PGN 65269) — температура окружающего воздуха (SPN 171), SA = 17
// SPN 171: байты 4-5, 0.03125 °C/bit, смещение -273 °C
// ------------------------------------------------------------------
void sendAMB(int16_t tempRaw) {
  struct can_frame frame;
  frame.can_id = (6UL << 26) | (65269UL << 8) | SA_ENGINE;
  frame.can_id |= CAN_EFF_FLAG;
  frame.can_dlc = 8;

  frame.data[0] = 0xFF;                   // SPN 108 — байт 1
  frame.data[1] = 0xFF;                   // SPN 108 — байт 2
  frame.data[2] = 0xFF;                   // SPN 170 — байт 3
  frame.data[3] = tempRaw & 0xFF;         // SPN 171, байт 4 (младший)
  frame.data[4] = (tempRaw >> 8) & 0xFF;  // SPN 171, байт 5 (старший)
  frame.data[5] = 0xFF;                   // SPN 172 — байт 6
  frame.data[6] = 0xFF;                   // SPN 173 — байт 7
  frame.data[7] = 0xFF;                   // SPN 79 — байт 8

  mcp2515.sendMessage(&frame);
}

Для взаимодействия использовал эту библиотеку.

❯ Что же в итоге?

Итак, с помощью довольно простых манипуляций этот прибор удалось оживить.

Оживляем дисплей от приборной панели автобуса - 41

За исключением нестандартного адреса особых проблем не возникло. Теперь девайс можно использовать в каком-то из своих проектов.

Один из виртуальных приборов я так и не смог запустить. Судя по иконке, это какая-то температура, но пакет, который её передаёт, я не нашёл. Также интересно, что разрешение некоторых величин несколько отличается от принятого в J1939, поэтому некоторые приборы изначально показывали чрезмерные или околонулевые значения. Но, само собой, исправить всё это не составляет труда.

Такие дела.

Может быть интересно:

Автор: MaFrance351

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js