Реверс протокола управления табло AKIS DW51R

В этой статье описал как я делал реверс протокола управления табло электронной очереди Akis DW51 (DW01). Реверс протокола делался в познавательных целях.

Данная модель табло представлена в двух вариантах, 3 символьное DW01 (04) и 5 символьное со стрелкой DM-01 (06). Из документации все что удалось найти это — напряжение питания 15 Вольт, управление по шине RS-485.

Внешний вид табло:

Разбираем табло, на лицевой стороне платы установлено 3 семисегментных индикатора YHD-1401SRA, даташит на который найти не удалось. Высота и ширина индикатора 121×90 мм соответственно, высота сегмента 100 мм.

На обратной стороне платы установлено несколько микросхем. Мозгом табло служит микроконтроллер Attiny2313, который управляет индикаторами через два драйвера STP16CP05.

С внешним миром табло общается по шине RS485 выполненной на микросхеме SN75176. Так же на плате есть два стабилизатора напряжения, для питание микросхем используется линейный регулятор L7805. Питание индикаторов выполнено на широко известном DC DC преобразователе MC33063A. На плате имеется наклейка на которой написана модель табло и его сетевой адрес.

Питание табло (15 В) и шина RS485 подключается через разъем RJ-45, распиновка на фото ниже. После подачи питания табло не подает никаких признаков жизни.

Распиновка разъема RJ-45 для подключения питания и шины RS-485

Для реверса протокола нужна реальная команда управления. Для этого установил программу «Конфигуратор модуля оповещения» от системы управления очередью (СУО) «Максима», добавил в настройках табло, сетевой адрес взял тот который указан на наклейке и выбрал COM порт к которому подключен преобразователь USB-UART-RS485.

При нажатии кнопки «Тест табло», табло ожило и показало номер 929, который содержится в сетевом адресе. Программа «Конфигуратор модуля оповещения», без полностью настроенного ПО СУО, других команд слать не может, но уже достаточно того, что она отправляет корректную команду для табло и оно реагирует на нее.

После того как я убедился, что табло вообще рабочее, подключаю логический анализатор к выходу преобразователя USB-UART. Логический анализатор я использую самодельный выполненный на плате EZ-USB FX2LP CY7C68013A, с залитой в нее прошивкой под анализатор Saleae Logic. Запускаем ПО для работы с логическим анализатором PulseView, нажимаем кнопку Run и нажимаем в «Конфигураторе» кнопку «Тест табло». В PulseView видим отправленную команду и то что команда управления состоит из двух фрагментов.

Для разбора команды нужно определить скорость обмена. Поставим на поле, из списка детекторов протокола, инструмент Guess bitrate, который поможет определить скорость обмена по UART. Он показал скорость 38461 бод, получается ближайшая стандартная скорость — 38400.

Добавляем анализатор протокола UART, указываем порт анализатора к которому подключена линия RX и указываем скорость Baude rate 38400. 

После этих настроек мы получаем расшифровку сообщения обмена в виде HEX значений. Первая посылка состоит из 14 байт, вторая из 9 байт.

Из полученных данных видно, что обе посылки содержат одинаковое значение 2E 99, я предположил что они скорее всего является сетевым адресом устройства. Запускаем калькулятор, выбираем вид Программист, выбираем режим HEX, пишем наше значение 2E99 и выбираем режим DEC, калькулятор перевел указанное число в десятичный вид и оно совпадает с сетевым адресом — 11929.

Теперь мы знаем, сетевой адрес передается вторым и третьим байтом. Первый байт стартовый. Так же из первой посылки мы видим значения F6 B3 F6, что наводит на мысли, что это скорее всего наше выведенное на табло значение — 929. Оставим эту теорию на потом.

Обычно различные протоколы обмена содержать контрольную сумму для проверки корректности переданных данных. Как правило контрольная сумма передается последними байтами и скорее всего это D8. Попробуем подобрать алгоритм подсчета контрольной суммы с помощью онлайн калькулятора контрольных сумм crccalc. Скопируем в калькулятор наш пакет без последнего байта. Нажимаем Calc CRC-8 и видим, что мы с первого раза нашли совпадение, используется алгоритм подсчета контрольной суммы CRC-8/MAXIM. Но у второй посылки контрольная сумма по этому алгоритму не бьется, что странно. Проверим теорию на практике.

Набросал в Ardiono IDE небольшой скетч, который будет подсчитывать контрольную сумму по алгоритму crc8 maxim и отправлять пакет данных на табло. Теория подтверждается практикой, табло отреагировало и отобразило число 929. Пробуем поменять 6, 7 и 8 байты, команда успешно принимается и на табло отображается уже что то другое. Пробуем определить какой бит в байте отвечает за отображение того или иного сегмента  на индикаторе.

void setup() {
  Serial.begin(38400);
}

byte cmd[13] = {0x0D, 0x2E, 0x99, 0x44, 0x00, 0xF6, 0xB3, 0xF6, 0x00, 0x00, 0x00, 0x00, 0x00};
byte shift[] = {0x08, 0x2E, 0x99, 0x50, 0xFF, 0x33, 0x33, 0x00, 0xA1};

static const uint8_t crc_table[] = {
  0x00, 0x07, 0x0e, 0x09, 0x1c, 0x1b, 0x12, 0x15, 0x38, 0x3f, 0x36, 0x31,
  0x24, 0x23, 0x2a, 0x2d, 0x70, 0x77, 0x7e, 0x79, 0x6c, 0x6b, 0x62, 0x65,
  0x48, 0x4f, 0x46, 0x41, 0x54, 0x53, 0x5a, 0x5d, 0xe0, 0xe7, 0xee, 0xe9,
  0xfc, 0xfb, 0xf2, 0xf5, 0xd8, 0xdf, 0xd6, 0xd1, 0xc4, 0xc3, 0xca, 0xcd,
  0x90, 0x97, 0x9e, 0x99, 0x8c, 0x8b, 0x82, 0x85, 0xa8, 0xaf, 0xa6, 0xa1,
  0xb4, 0xb3, 0xba, 0xbd, 0xc7, 0xc0, 0xc9, 0xce, 0xdb, 0xdc, 0xd5, 0xd2,
  0xff, 0xf8, 0xf1, 0xf6, 0xe3, 0xe4, 0xed, 0xea, 0xb7, 0xb0, 0xb9, 0xbe,
  0xab, 0xac, 0xa5, 0xa2, 0x8f, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9d, 0x9a,
  0x27, 0x20, 0x29, 0x2e, 0x3b, 0x3c, 0x35, 0x32, 0x1f, 0x18, 0x11, 0x16,
  0x03, 0x04, 0x0d, 0x0a, 0x57, 0x50, 0x59, 0x5e, 0x4b, 0x4c, 0x45, 0x42,
  0x6f, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7d, 0x7a, 0x89, 0x8e, 0x87, 0x80,
  0x95, 0x92, 0x9b, 0x9c, 0xb1, 0xb6, 0xbf, 0xb8, 0xad, 0xaa, 0xa3, 0xa4,
  0xf9, 0xfe, 0xf7, 0xf0, 0xe5, 0xe2, 0xeb, 0xec, 0xc1, 0xc6, 0xcf, 0xc8,
  0xdd, 0xda, 0xd3, 0xd4, 0x69, 0x6e, 0x67, 0x60, 0x75, 0x72, 0x7b, 0x7c,
  0x51, 0x56, 0x5f, 0x58, 0x4d, 0x4a, 0x43, 0x44, 0x19, 0x1e, 0x17, 0x10,
  0x05, 0x02, 0x0b, 0x0c, 0x21, 0x26, 0x2f, 0x28, 0x3d, 0x3a, 0x33, 0x34,
  0x4e, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5c, 0x5b, 0x76, 0x71, 0x78, 0x7f,
  0x6a, 0x6d, 0x64, 0x63, 0x3e, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2c, 0x2b,
  0x06, 0x01, 0x08, 0x0f, 0x1a, 0x1d, 0x14, 0x13, 0xae, 0xa9, 0xa0, 0xa7,
  0xb2, 0xb5, 0xbc, 0xbb, 0x96, 0x91, 0x98, 0x9f, 0x8a, 0x8d, 0x84, 0x83,
  0xde, 0xd9, 0xd0, 0xd7, 0xc2, 0xc5, 0xcc, 0xcb, 0xe6, 0xe1, 0xe8, 0xef,
  0xfa, 0xfd, 0xf4, 0xf3
};

byte CRC8(const byte *data, byte len) {
  byte crc = 0x00;
  while (len--) {
    byte extract = *data++;
    for (byte tempI = 8; tempI; tempI--) {
      byte sum = (crc ^ extract) & 0x01;
      crc >>= 1;
      if (sum) {
        crc ^= 0x8C;
      }
      extract >>= 1;
    }
  }
  return crc;
}

void sendPacket() {
  for (int i = 0; i < 13; i++) {
    Serial.write(cmd[i]);
  }
  Serial.write(CRC8(cmd, 13));
  for (int i = 0; i < 9; i++) {
    Serial.write(shift[i]);
  }
}

void loop() {
  sendPacket();
}

Подвигав биты в 6 байте команды, получил следующее соответствие битов сегментам: 

Составил массив для вывода цифр от 0 до 9 — [0x77, 0x14, 0xB3, 0xB6, 0xD4, 0xE6, 0xE7, 0x34, 0xF7, 0xF6].

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

С выводом цифр разобрались. попробуем определить за что отвечают остальные байты в первом пакете команды. Если внимательно посмотреть на обратную сторону платы то видно что под первым индикатором имеются отверстия под светодиоды для стрелки и заведены они на драйвер STP16CP05. Получается что у нас два драйвера могут управлять 4 семисегментными индакторами. И 5 байт команды вероятно отвечает за вывод стрелок, я не проверял, но на 99% уверен что это так.

Если присмотреться на 5 символьное табло со стрелкой и на плату табло, то становится понятно, что плата выполнена универсальной и в табло DM-01 (06) просто устанавливается две одинаковые платы на одной из которых вместо одного индикатора распаяны светодиоды для стрелок. Так же на плате присутствуют контактные площадки под разъем для соединения двух плат. 

Я припаял пины и соединил две платы вместе. Попробовал вывести данные, за вывод на второе табло отвечают 9, 10, 11 и 12 байт команды, где 9 байт для стрелок, 10, 11 и 12 для вывода на семисегментные индикаторы.

В процессе тестов выяснил что 13 байт команды отвечает за мигание индикаторов. Каждый бит этого байта соответствует позиции мигающего индикатора слева направо. Например для мигания второй цифры в 13 байт необходимо записать BIN 00100000 что соответствует HEX 0x40. 

Получился достаточно простой протокол:

Набросал итоговый тестовый скетч, пример его работы на видео ниже.

void setup() {
  Serial.begin(38400);
}

byte num[10] = {0x77, 0x14, 0xB3, 0xB6, 0xD4, 0xE6, 0xE7, 0x34, 0xF7, 0xF6};

byte cmd[13] = {0x0D, 0x2E, 0x99, 0x44, 0x00, 0x77, 0x77, 0x77, 0x00, 0x77, 0x77, 0x77, 0x00};
byte shift[] = {0x08, 0x2E, 0x99, 0x50, 0xFF, 0x33, 0x33, 0x00, 0xA1};

static const uint8_t crc_table[] = {
  0x00, 0x07, 0x0e, 0x09, 0x1c, 0x1b, 0x12, 0x15, 0x38, 0x3f, 0x36, 0x31,
  0x24, 0x23, 0x2a, 0x2d, 0x70, 0x77, 0x7e, 0x79, 0x6c, 0x6b, 0x62, 0x65,
  0x48, 0x4f, 0x46, 0x41, 0x54, 0x53, 0x5a, 0x5d, 0xe0, 0xe7, 0xee, 0xe9,
  0xfc, 0xfb, 0xf2, 0xf5, 0xd8, 0xdf, 0xd6, 0xd1, 0xc4, 0xc3, 0xca, 0xcd,
  0x90, 0x97, 0x9e, 0x99, 0x8c, 0x8b, 0x82, 0x85, 0xa8, 0xaf, 0xa6, 0xa1,
  0xb4, 0xb3, 0xba, 0xbd, 0xc7, 0xc0, 0xc9, 0xce, 0xdb, 0xdc, 0xd5, 0xd2,
  0xff, 0xf8, 0xf1, 0xf6, 0xe3, 0xe4, 0xed, 0xea, 0xb7, 0xb0, 0xb9, 0xbe,
  0xab, 0xac, 0xa5, 0xa2, 0x8f, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9d, 0x9a,
  0x27, 0x20, 0x29, 0x2e, 0x3b, 0x3c, 0x35, 0x32, 0x1f, 0x18, 0x11, 0x16,
  0x03, 0x04, 0x0d, 0x0a, 0x57, 0x50, 0x59, 0x5e, 0x4b, 0x4c, 0x45, 0x42,
  0x6f, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7d, 0x7a, 0x89, 0x8e, 0x87, 0x80,
  0x95, 0x92, 0x9b, 0x9c, 0xb1, 0xb6, 0xbf, 0xb8, 0xad, 0xaa, 0xa3, 0xa4,
  0xf9, 0xfe, 0xf7, 0xf0, 0xe5, 0xe2, 0xeb, 0xec, 0xc1, 0xc6, 0xcf, 0xc8,
  0xdd, 0xda, 0xd3, 0xd4, 0x69, 0x6e, 0x67, 0x60, 0x75, 0x72, 0x7b, 0x7c,
  0x51, 0x56, 0x5f, 0x58, 0x4d, 0x4a, 0x43, 0x44, 0x19, 0x1e, 0x17, 0x10,
  0x05, 0x02, 0x0b, 0x0c, 0x21, 0x26, 0x2f, 0x28, 0x3d, 0x3a, 0x33, 0x34,
  0x4e, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5c, 0x5b, 0x76, 0x71, 0x78, 0x7f,
  0x6a, 0x6d, 0x64, 0x63, 0x3e, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2c, 0x2b,
  0x06, 0x01, 0x08, 0x0f, 0x1a, 0x1d, 0x14, 0x13, 0xae, 0xa9, 0xa0, 0xa7,
  0xb2, 0xb5, 0xbc, 0xbb, 0x96, 0x91, 0x98, 0x9f, 0x8a, 0x8d, 0x84, 0x83,
  0xde, 0xd9, 0xd0, 0xd7, 0xc2, 0xc5, 0xcc, 0xcb, 0xe6, 0xe1, 0xe8, 0xef,
  0xfa, 0xfd, 0xf4, 0xf3
};

byte CRC8(const byte *data, byte len) {
  byte crc = 0x00;
  while (len--) {
    byte extract = *data++;
    for (byte tempI = 8; tempI; tempI--) {
      byte sum = (crc ^ extract) & 0x01;
      crc >>= 1;
      if (sum) {
        crc ^= 0x8C;
      }
      extract >>= 1;
    }
  }
  return crc;
}

void sendPacket() {
  for (int i = 0; i < 13; i++) {
    Serial.write(cmd[i]);
  }
  Serial.write(CRC8(cmd, 13));
  for (int i = 0; i < 9; i++) {
    Serial.write(shift[i]);
  }
}

void loop() {
  for (int a = 0; a < 10; a++) {
    cmd[5] = num[a];
    sendPacket();
    delay(150);
  }
  for (int i = 0; i < 10; i++) {
    cmd[6] = num[i];
    sendPacket();
    delay(150);
  }
  for (int i = 0; i < 10; i++) {
    cmd[7] = num[i];
    sendPacket();
    delay(150);
  }
  for (int i = 0; i < 10; i++) {
    cmd[9] = num[i];
    sendPacket();
    delay(150);
  }
  for (int i = 0; i < 10; i++) {
    cmd[10] = num[i];
    sendPacket();
    delay(150);
  }
  for (int i = 0; i < 10; i++) {
    cmd[11] = num[i];
    sendPacket();
    delay(150);
  }
  for (int i = 0; i < 8; i++) {
    cmd[12] = 1 << i;
    sendPacket();
    delay(2000);
  }
  cmd[12] = 0;
  sendPacket();
}
Подписаться
Уведомлять
guest
2 комментариев
Старые вначале
Новые вначале По голосам
Межтекстовые Отзывы
Посмотреть все комментарии
Владислав
Владислав
16 дней назад

Здравствуйте!
Не найдется ли у вас пароля от программного пульта. Документация утеряна, необходимо восстановить работу двух комплексов на работе. Требуется перенастроить программные пульты, пароля нету. Спасибо

2
0
Поделиться своими мыслямиx
()
x