Приветствую всех!
Давным-давно, больше года назад, я рассказывал о подключении блока индикации скорости от метровагона «Яуза». Но тогда, несмотря на то, что я почти полностью разобрал протокол, у меня возникли проблемы: при попытке обновить показания табло гасло. И что-то совершенно не давало мне покоя, отчего я решил, что девайс надо заставить работать во что бы то ни стало.



Итак, в сегодняшней статье узнаем, как же на самом деле работает данный прибор. Попутно заставим его отображать что-нибудь полезное. Традиционно будет много интересного.

Суть такова


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

После этого железка осталась у меня лежать просто как память об истории отечественного подвижного состава метро: собирать тренажёр «Яузы» или «Русича» у меня планов не было, а для чего-то ещё панель было не применить ввиду упомянутого недостатка. Но сделать это хотелось, так что было решено разобраться окончательно.

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


Так получилось, что существуют две версии данного скоростемера.



Более старая ставится на «Яузы» с коллекторными двигателями. Иногда бывает, что вместо «Рекомендуемая скорость» написано «Предупредительная скорость», разницы здесь никакой. Зелёная шкала здесь отображает текущую скорость, красная — максимально допустимую (при её превышении произойдёт сработка системы автоматического регулирования скорости), жёлтая — предупредительную (в АРС «Днепр» это допустимая скорость на следующем участке, то есть та, с которой можно ехать, чтобы точно не превысить ограничение).



Более новая — на «Русичи» и асинхронные «Яузы». Вместо шкалы ускорения здесь шесть индикаторов:

  • ЛН — лампа направления, признак восприятия направления системой АРС.
  • Ход — лампа разрешения хода.
  • Днепр — работа АРС «Днепр».
  • ДАУ — работа резервного комплекта АРС.
  • ИСПР — признак исправности системы АРС.
  • А ОСТ — абсолютная остановка, она же «электронный автостоп». Сигнал запрещает проезд даже с зажатой педалью бдительности.

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

В чём сложность?


Признаться, у меня самого были мысли просто выпаять из платы ПЗУшку, прочитать её программатором и посмотреть, как там реализован алгоритм приёма данных. Но делать этого решительно не хотелось: подумал, что слишком высок риск запороть плату этого, считай, уникального девайса. Поэтому было решено разбираться чисто программными методами.

Внешний сигнал сброса и break


Ещё одно интересное предположение сделал товарищ kafeman: разделителем пакетов может служить так называемый break — логический ноль на линии передачи UART большей длительности, чем один кадр. Я попробовал импульсы разной длины, а также (мало ли что?) серии импульсов, однако это ничего не дало. Панель определённо реагировала на эти манипуляции (в момент подачи этого импульса шкалы подмигивали), однако толку от этого не было вообще никакого.

Также tormozedison предложил попробовать подать сигнал сброса по отдельной линии. Впрочем, как оказалось, ни один из оставшихся неиспользованными пинов на эту роль не подходил. Так что и эту версию пришлось отбросить. Вывод прост: обновление производится исключительно путём отправки каких-то данных на вход. Что же это за данные, найти ожидаемо не удалось. Я уже думал записать обмен с настоящим блоком АРС и даже познакомился с несколькими работниками метро, но у них такой возможности не было.

АРС


Об устройстве системы автоматического регулирования скорости «Яузы» или «Русича» найти удалось совсем немного. Известно, что на этих поездах используется так называемая бесконтактная АРС. Скорее всего, скоростемер получает данные из двух источников: блоков БАРС-1 и БАРС-2 (основного и резервного блока автоматического регулирования скорости) и измерительного преобразователя, непосредственно обрабатывающего сигнал с датчика вращения шестерни. Этим и объясняется, что индикаторы управляются по своим проводам, а шкалы — через UART. Блок управления достать у меня не вышло, поэтому придётся разбираться самому.



Вот так выглядят эти блоки в аппаратном отсеке. У меня не нашлось фото именно для «Яузы» (если вдруг кто-то из вас сможет найти, прошу выложить в комментариях), поэтому взял для «Русича». Над блоками АРС находится тот самый измеритель скорости, выдающий данные для цифрового индикатора и аналоговый сигнал для других блоков. В самом низу установлен блок управления поездом — по сути бортовой компьютер системы управления «Витязь».

Побеждаем распиновку




Для чего же нужны остальные контакты, если внешнего сброса среди них нет? Сейчас узнаем. Ещё немного отреверсив плату, удалось получить практически полную распиновку:

  1. D0 первого индикатора
  2. D1 первого индикатора
  3. D2 первого индикатора
  4. D3 первого индикатора
  5. D0 второго индикатора
  6. D1 второго индикатора
  7. D2 второго индикатора
  8. D3 второго индикатора
  9. Вперёд
  10. Назад
  11. Вспомогательный канал (-)
  12. Вспомогательный канал (+)
  13. Корпус
  14. Основной канал (+)
  15. Основной канал (-)
  16. Основной канал (+)
  17. Основной канал (-)
  18. Неизвестно
  19. Неизвестно
  20. 5 В (инд.)
  21. Земля (инд.)
  22. Ничего
  23. Ничего
  24. Ничего

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

Начинаем смотреть протокол


Помните, с чего начинались изыскания по части протокола? Если кучу раз отправить байт 01h, то можно увидеть зажигание сегмента на шкале ускорения. Если делать это в цикле с интервалом около 0,1 с, то можно увидеть, как этот сегмент будет мигать. Примерно так:

Это значит, что либо существует некая «магическая» комбинация байт, после получения которой блок вновь готов к приёму данных, либо происходит банальный сброс из-за переполнения буфера.
Следующим моим предположением было то, что блок требует инициализации, для чего ему необходимо что-то отправить перед тем, как начать вывод данных. И действительно: отправка некоторых байт не оказывала влияния на приём пакета извне. Впрочем, больше на этом ничего выяснить так и не удалось, поспрашивав у товарищей, также не узнал ничего интересного, поэтому я выложил все имеющиеся у меня наработки, а про девайс с тех пор мало вспоминал.

Тише едешь — получишь результат


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

Самым необычным открытием оказалось то, что данные должны поступать с интервалом между байтами порядка 1500-2000 мкс. Он может быть больше (до десятков секунд), но ни в коем случае не меньше, иначе работать блок не будет. Таймаута на приём нет, то есть отправлять можно даже вручную из окна терминала. Слишком частыми посылками можно отправить блок индикации в полный сброс (то же состояние, в котором он находится сразу после подачи питания, когда на несколько секунд загораются все шкалы). О том, что это именно сброс, а не какая-то команда для включения режима самотестирования, свидетельствует то, что загнать девайс в такое состояние можно вообще любыми данными, если слать их много и без интервала. Задержка эта должна быть между каждым из байтов, это не разделитель пакетов. Если даже в одном месте убрать её, блок работать уже не будет.

Инициализация


После подачи питания блок не готов к работе. Чтобы он мог показать что-то не единоразово, требуется отправить пять нулевых байт. Опыты показали, что отправлять можно не только нули, но и вообще что угодно, на дальнейшее поведение это не влияет совершенно никак. Возможно, что на рабочем составе этой посылкой были либо некие служебные данные, которые никак не обрабатывались скоростемером, но использовались какими-либо другими устройствами (например, регистратором параметров движения), либо признак исправности системы АРС, что заставляет блок индикации считать, что пакеты, которые придут далее, действительны, а не представляют собой просто помехи. К «начальным» байтам применяются всё те же требования, что и к «рабочим»: интервал между отправкой каждого из них обязателен.

Протокол


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

<00h><00h><FFh><FFh><00h><00h><00h><00h><00h><00h><00h><00h><magic byte><green><00h><red><acceleration><yellow>

Вначале идёт заголовок пакета, после него — восемь нулевых байт. Следом за ними — тот самый «магический» байт, отвечающий за сброс. Он должен быть строго равен FFh, иначе при обновлении показаний блок так же будет гаснуть. Также стоит заметить, что формат той части пакета, которая отвечает непосредственно за шкалы, отличается от приведённого в первой моей статье. Заставить блок постоянно отображать данные в том формате не вышло.

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

Что с ним делать?


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


Само собой, таких проектов есть уже масса, так что софт для компьютера писать с нуля не придётся.


После некоторых изысканий выбор был сделан в пользу Libre Hardware Monitor. Понадобится форк этой программы, про который рассказывал небезызвестный AlexGyver в ролике про аппаратный монитор железа на ардуино:

Экранов на HD44780 разных фирм и годов я повидал массу, а вот монитора железа прямиком из метро пока что ни у кого не было. Само собой, дисплей как на видео нам не понадобится, а вот софт и скетч данных позаимствуем как раз у этого проекта.



А вот и схема подключения. Из-за отсутствия у прибора внешнего сброса она чуть сложнее, чем «тестовая»: добавлено реле (любое маломощное пятивольтовое, лишь бы не герконовое. Можно было использовать китайский релейный модуль, но его у меня не было), а также контроль цепей питания. При запуске устройство будет ждать, пока поднимется напряжение на преобразователе и включится блок индикации, а уже потом слать данные. Если питание пропало (например, переткнули блок питания), железка перезагрузится.

А вот и скетч для данного девайса
#include <string.h>
#include <SoftwareSerial.h>

//ИРПС
#define PANEL_RX 4
#define PANEL_TX 5

//Реле и оптопара
#define PANEL_POWER 2
#define PANEL_POWER_DETECTION 3

//Транспаранты
#define FORWARD 6
#define BACKWARD 7

//Первый семисегментник
#define D01 8
#define D11 9
#define D12 10
#define D13 11

//Второй семисегментник
#define D02 12
#define D21 13
#define D22 A0
#define D23 A1

//Задержка между байтами
int x = 1300;

SoftwareSerial panel(PANEL_RX, PANEL_TX);

//Переменные для обмена
char inData[82];       // массив входных значений (СИМВОЛЫ)
int PCdata[20];        // массив численных значений показаний с компьютера
byte index = 0;
unsigned long timeout;
bool restoreConnectToPC = 0, timeOut_flag = 1, updateDisplay_flag = 0;
String string_convert;

int8_t acceleration = 0xF5;

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  panel.begin(9600);
  pinMode(D01, OUTPUT);
  pinMode(D11, OUTPUT);
  pinMode(D12, OUTPUT);
  pinMode(D13, OUTPUT);
  pinMode(D02, OUTPUT);
  pinMode(D21, OUTPUT);
  pinMode(D22, OUTPUT);
  pinMode(D23, OUTPUT);
  pinMode(FORWARD, OUTPUT);
  pinMode(BACKWARD, OUTPUT);
  pinMode(PANEL_POWER, OUTPUT);
  pinMode(PANEL_POWER_DETECTION, INPUT);
  while (!digitalRead(PANEL_POWER_DETECTION));;
  digitalWrite(PANEL_POWER, HIGH);
  panelBegin();
}

void parsing() {
  while (Serial.available() > 0) {
    char aChar = Serial.read();
    if (aChar != 'E') {
      inData[index] = aChar;
      index++;
      inData[index] = '\0';
    } else {
      char *p = inData;
      char *str;
      index = 0;
      String value = "";
      while ((str = strtok_r(p, ";", &p)) != NULL) {
        string_convert = str;
        PCdata[index] = string_convert.toInt();
        index++;
      }
      index = 0;
    }
    timeout = millis();
  }
}

void callTimeout() {
  while (!Serial.available()) {
    if (millis() - timeout > 5000) {
      showNumber1(0xFF); //погасить первый сегмент
      showNumber2(0x0E); //"t"
    }
  }
}

void showNumber1(int num) {
  digitalWrite(D01, (num >> 0) & 1);
  digitalWrite(D11, (num >> 1) & 1);
  digitalWrite(D12, (num >> 2) & 1);
  digitalWrite(D13, (num >> 3) & 1);
}

void showNumber2(int num) {
  digitalWrite(D02, (num >> 0) & 1);
  digitalWrite(D21, (num >> 1) & 1);
  digitalWrite(D22, (num >> 2) & 1);
  digitalWrite(D23, (num >> 3) & 1);
}

void showNumber(int num) {
  if (num / 10 == 0 || num / 10 > 10) {
    showNumber1(0xFF);
    showNumber2(num % 10);
  }
  else if (num == 100) {
    showNumber1(9);
    showNumber2(9);
  }
  else {
    showNumber1(num / 10);
    showNumber2(num % 10);
  }
}

void panelBegin() {
  delay(3000);
  for (int i = 0; i < 5; i++) {
    panel.write((uint8_t)0x00);
    delayMicroseconds(x);
  }
  delay(100);
}

void updateValues(uint8_t red, uint8_t green, uint8_t yellow, uint8_t acceleration) {
  uint8_t packet[] = {0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00};
  packet[12] = green;
  packet[14] = red;
  packet[15] = acceleration;
  packet[16] = yellow;
  for (int i = 0; i < 17; i++) {
    panel.write(packet[i]);
    delayMicroseconds(x);
  }
}

void callPower() {
  if (digitalRead(PANEL_POWER_DETECTION)) return;
  else {
    digitalWrite(PANEL_POWER, LOW);
    while (!digitalRead(PANEL_POWER_DETECTION));;
    digitalWrite(PANEL_POWER, HIGH);
    delay(3000);
    panelBegin();
  }
}

void loop() {
  // put your main code here, to run repeatedly:
  parsing();
  callTimeout();
  callPower();
  showNumber(PCdata[4]);
  updateValues(map(PCdata[5], 0, 100, 0, 53) * 4, map(PCdata[4], 0, 100, 0, 53) * 4, map(PCdata[6], 0, 100, 0, 53) * 4, acceleration);
}


Обмен данными с софтом мониторинга полностью взят из кода AlexGyver. Загружаем скетч, запускаем мониторинг, и вот результат:

Работает, однако!
Шкала ускорения и транспаранты «Вперёд»/«Назад» остались зарезервированными. Что отображать на них, я пока не придумал.

Вот как-то так




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

Новости, обзоры продуктов и конкурсы от команды Timeweb.Cloud — в нашем Telegram-канале





? Читайте также:

Комментарии (6)


  1. dlinyj
    06.09.2024 08:19
    +1

    Очень прикольно получилось. Классная концепция. Мигание не раздражает при долгой работе?


    1. MaFrance351 Автор
      06.09.2024 08:19
      +2

      Нет, не раздражает.

      Интересно, как это выглядело в настоящем поезде. Видео того, как там меняются показания, я так и не нашёл...


  1. AlexNixon
    06.09.2024 08:19
    +3

    Слышал, вроде, весь вагон был непонятно как сделанный и ненадежный, потому они и пришли и ушли. Вот и протоколы такие же, видать. Хотя жаль, раньше часто на них ездил по Каховской линии, мне нравились, что-то такое "робокопное" в дизайне было.


    1. MaFrance351 Автор
      06.09.2024 08:19
      +1

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

      А так по тем временам очень много прорывных решений было там. Многое из этого до сих пор используется на метро и ЖД (конструкция метропоезда Юбилейный и рельсового автобуса РА1, электрооборудование русака, бортовые компьютеры, система управления "Витязь" и ещё многое...). Жаль, что ни одного ходового состава так и не сохранили.


  1. S-trace
    06.09.2024 08:19
    +1

    Прикольный индикатор, респект!

    Шкала ускорения и транспаранты «Вперёд»/«Назад» остались зарезервированными. Что отображать на них, я пока не придумал.

    Пока нет более интересной идеи - можно на шкале ускорения показывать сетевой трафик (или точнее соотношение трафика up/downlink за период, возможно с логарифмическим масштабированием, чтобы малые значения не терялись из видимости). Преобладает передача - ускорение в плюс, преобладает скачивание - в минус.


    1. MaFrance351 Автор
      06.09.2024 08:19

      Я изначально думал на этой шкале сделать динамику загрузки ЦП (что-то интенсивное запустили - шкала пошла в плюс, комп освободился - пошла в минус). Но получилось не очень красиво.