Пожалуй, самой желанной для меня железкой на транспортную тему был контроллер машиниста от какого-нибудь поезда. Но, как это часто бывает, достать такой экземпляр не так уж и просто.
И вот наконец в мои руки попал не просто какой-то контроллер, а очень примечательный экземпляр — от «концептуальной» электрички ЭМ2И, коих в наши дни уже не осталось.
Итак, в сегодняшней статье поговорим о том, как устроен такой контроллер и как его подключить. Традиционно будет много интересного.
❯ Суть такова
Так получилось, что ко мне в руки попал контроллер от старого электропоезда ЭМ2И. И, само собой, напрашивалось единственное верное решение — подключить его к Trainz или «Метрострою».
Удивительно, что при наличии огромного количества железнодорожных симуляторов нет практически никакой периферии для них. Если вспомнить, сколько всякой амуниции выпускается для фанатов авиации, то окажется, что для желающих покататься на поезде у себя дома по сути нет ничего.
Безусловно, кое-что всё же существует. Есть такая штука как Raildriver, которая даже нативно поддерживается в Trainz, однако в России оказалось реальнее достать настоящий контроллер, нежели её.
Немного про электропоезда ЭМ2И
Прежде чем начать разбираться с подключением, поговорим о том, что это вообще была за электричка.
ЭМ2И являются детищем более старых проектов — ЭМ1 и ЭМ2. Все эти поезда — глубокие модернизации куда более старых ЭР2. Серия была предназначена для эксплуатации на линии в Домодедово в качестве экспрессов.
Электропоезд обладал целым рядом нововведений — новые сцепные устройства, полупантографы, электронная система управления. Выпускались они на Московском локомотиворемонтном заводе, а запчасти для них поставляло некое ЗАО «Спецремонт».
Модель была многообещающей — тут вам и скорость аж в сто шестьдесят километров в час, и мягкие сиденья, и салон повышенной комфортности, всё как в дорогих иномарках японских поездах.
Увы, с самого начала история этих электричек пошла не в их пользу — уже на испытаниях выяснилось, что многие характеристики не соответствовали заявленным, а в конструкции имелось немало фатальных недостатков. Эксплуатировались необычные поезда совсем недолго, в первой половине нулевых, а большую часть своего существования они стояли в депо или на базах запаса и тихонечко гнили. Проект вышел крайне неоднозначным: стоили машины очень дорого (практически как новые ЭД4М), имели небольшой срок службы (ведь ходовая часть осталась от старых ЭРок), а также были крайне ненадёжными и неудобными в ремонте. В итоге от их использования довольно быстро отказались, а в 2019-2020 годах все машины (точнее, то, что от них осталось за долгие годы стояния на открытом воздухе и постепенного разграбления вандалами) были порезаны на металлолом.
Впрочем, многие (как и я) до сих пор жалеют, что не успели прокатиться на этом неудачном, но всё же весьма интересном МВПС.
Обзор оборудования
Ну что же, переходим к нашей железке. Ко мне в руки попал контроллер машиниста КМ-4А, снятый с одного из списанных таких поездов. Скручен он был ещё много лет назад, отчего он не успел сгнить, пострадать от рук вандалов или стать собственностью охотников за драгметаллами.
А вот и подопытный экземпляр. На нём две ручки — реверс и непосредственно управление. Также имеется тумблер.
Позиции.
Вид снизу. Видно сердце контроллера — кулачки. В нынешних электронных вагонах применяются бесконтактные контроллеры машиниста (БКМы), где вместо кулачков и контактных групп используются датчики (оптические или магнитные). Чуть позже я расскажу и про этот тип контроллеров, ну а пока что будем разбираться с нашим.
Кулачки переключают контактные группы, в качестве которых здесь используются обычные концевые микрики. Один из них почему-то не задействован, видимо, зарезервирован для другой модели или для будущего использования.
Разъём для связи девайса с внешним миром — DB37F. Закреплён он на обычном куске фанерки.
Наклейка того самого «Спецремонта».
А вот механизм. Дискретное перемещение ручки обеспечивается за счёт детали сложной формы, в которую упирается подпружиненный ролик. Шестерни обмазаны густой липкой смазкой.
Вдалеке видно тот самый прижимной механизм.
Нужное усилие обеспечивается двумя мощными пружинами.
По бокам находятся толстые стальные пластины, в которые вкручиваются стойки и вставляются валы. Из-за их толщины (десять миллиметров) скромный по размерам контроллер имеет весьма внушительную массу — чуть больше семи килограммов.
Кулачки
Контроллер внутри очень похож на командоаппарат.
В самом деле, в нём имеются всё те же самые контактные элементы, которые переключаются расположенными на вращающемся валу кулачками. Разница лишь в том, что в командоаппарате вал вращается электромотором, а тут он связан с ручкой контроллера машиниста.
Вот для примера положение кулачков в разных позициях ручки.
Реверс
Начнём, наверное, с самого простого — с ручки реверса.
Для неё выделено сразу три кулачка, один из которых не используется. От оставшихся контактных групп идут четыре провода — по одной на каждое из двух «рабочих» положений.
Помимо этого ручка реверса блокирует перемещение основного вала. Когда она стоит на нуле, ручка контроллера заблокирована. В то же время нельзя переключить реверс на ходу. Достигается это за счёт использования диска с прорезью на основном валу и ролика на реверсоре. Когда вал находится в положении, отличном от нуля, ролик не может встать в прорезь, отчего реверс не переключается. При этом, когда ролик находится на своём месте, диск не может повернуться, что обеспечивает блокировку ручки контроллера.
Вот положение этих двух деталей при нулевой позиции реверса.
А вот — когда включён реверс, а ручка повёрнута на ход или тормоз.
Распиновка
Чтобы разобраться с работой самого контроллера, пришлось выяснить распиновку, благо проблем с её определением не возникло. Она здесь оказалась вот такая:
- Пятый кулачок (нормально разомкнутый контакт)
- Четвёртый кулачок (нормально разомкнутый контакт)
- Третий кулачок (нормально разомкнутый контакт)
- Второй кулачок (нормально разомкнутый контакт)
- Первый кулачок (нормально разомкнутый контакт)
- Третий кулачок (нормально замкнутый контакт)
- Второй кулачок (нормально замкнутый контакт)
- Назад
- Вперёд
- Ничего
- Вперёд
- Назад
- Второй кулачок (нормально замкнутый контакт)
- Третий кулачок (нормально замкнутый контакт)
- Первый кулачок (нормально разомкнутый контакт)
- Второй кулачок (нормально разомкнутый контакт)
- Третий кулачок (нормально разомкнутый контакт)
- Четвёртый кулачок (нормально разомкнутый контакт)
- Пятый кулачок (нормально разомкнутый контакт)
- Ничего
- Ничего
- Ничего
- Ничего
- Ничего
- Ничего
- Ничего
- Ничего
- ЗАП/ОТП
- ЗАП/ОТП
- Ничего
- Ничего
- Ничего
- Ничего
- Ничего
- Ничего
- Ничего
- Ничего
Далее берём мультиметр и выясняем, какие контакты в какой позиции замкнуты.
Далее объединяем всё в одну таблицу:
Посмотрев на список контактов, решил задействовать их все — лишние несколько занятых выводов контроллера ничего не меняют, зато вероятность глюков из-за пропавшего контакта в одном из концевиков существенно снизится.
Подключение к ПК
Для того, чтобы связать контроллер с компьютером, понадобится всего-ничего — плата на самом дешёвом МК с поддержкой USB HID в лице Raspberry Pico, вилка DB37 и немного проводков.
Безусловно, можно было использовать Arduino Pro Micro, STM32 или вовсе сделать программную реализацию USB на AVR или STM8, но RP2040 очень дешёв (RPi Pico можно отхватить рублей за триста, что в разы меньше той же Pro Micro), у меня имелась целая пачка таких плат, так что я без раздумий взял именно эту платформу.
Немного пайки, и получаем вот такой переходник. Да, разъём для установки на плату был не лучшим решением, но другого в местном лабазе в наличии не было.
Запуск
Теперь очередь прошивки контроллера. Тут всё очень просто.
В случае с Метростроем управление вот такое:
Соответственно, надо отправлять эти символы при перемещении ручки.
Помня об этом, пишем вот такой скетч:
#include "PluggableUSBHID.h"
#include "USBKeyboard.h"
#define POSITION_3 0x54
#define POSITION_2 0x1C
#define POSITION_1 0x38
#define POSITION_M 0x70
#define POSITION_0 0x60
#define POSITION_P 0x62
#define POSITION_EPT 0x63
#define REVERSE_0 2
#define REVERSE_FORWARD 3
#define REVERSE_BACKWARD 1
#define UPPER 1
#define LOWER 0
USBKeyboard Keyboard;
void setup() {
// put your setup code here, to run once:
for (int i = 13; i <= 22; i++) {
pinMode(i, OUTPUT);
digitalWrite(i, HIGH);
}
for (int i = 3; i <= 12; i++) pinMode(i, INPUT_PULLDOWN);
Serial.begin(115200);
}
uint8_t getPosition() {
uint8_t switches = 0x00;
for (int i = 0; i < 7; i++) switches |= (digitalRead(i + 3) << i);
return switches;
}
uint8_t getReverse() {
if (digitalRead(11)) return REVERSE_FORWARD;
if (digitalRead(10)) return REVERSE_BACKWARD;
return REVERSE_0;
}
void processPositionMetrostroi(uint8_t position) {
switch (position) {
case POSITION_3:
Keyboard.key_code('3', 0);
break;
case POSITION_2:
Keyboard.key_code('2', 0);
break;
case POSITION_1:
Keyboard.key_code('1', 0);
break;
case POSITION_M:
break;
case POSITION_0:
Keyboard.key_code('4', 0);
break;
case POSITION_P:
Keyboard.key_code('5', 0);
break;
case POSITION_EPT:
Keyboard.key_code('7', 0);
break;
}
}
void processReverseMetrostroi(uint8_t direction) {
if (direction == UPPER) Keyboard.key_code('0', 0);
else if (direction == LOWER) Keyboard.key_code('9', 0);
}
void loop() {
// put your main code here, to run repeatedly:
uint8_t previousPosition = POSITION_0, currentPosition = POSITION_0, previousReverse = REVERSE_0, currentReverse = REVERSE_0;
while (1) {
currentPosition = getPosition();
currentReverse = getReverse();
if (currentPosition != previousPosition) {
processPositionMetrostroi(currentPosition);
previousPosition = currentPosition;
}
if (currentReverse > previousReverse) {
processReverseMetrostroi(UPPER);
previousReverse = currentReverse;
}
if (currentReverse < previousReverse) {
processReverseMetrostroi(LOWER);
previousReverse = currentReverse;
}
delay(50);
}
}
С реверсом всё чуть сложнее — фиксированных клавиш для каждой его позиции нет. Поэтому отслеживаем предыдущее положение этой ручки и отправляем «0» или «9» в зависимости от того, куда её передвинули.
С Trainz всё куда проще — там можно назначить клавиши для большинства действий. Программа при этом получается вот такая:
#include "PluggableUSBHID.h"
#include "USBKeyboard.h"
#define POSITION_3 0x54
#define POSITION_2 0x1C
#define POSITION_1 0x38
#define POSITION_M 0x70
#define POSITION_0 0x60
#define POSITION_P 0x62
#define POSITION_EPT 0x63
#define REVERSE_0 2
#define REVERSE_FORWARD 3
#define REVERSE_BACKWARD 1
#define UPPER 1
#define LOWER 0
USBKeyboard Keyboard;
void setup() {
// put your setup code here, to run once:
for (int i = 13; i <= 22; i++) {
pinMode(i, OUTPUT);
digitalWrite(i, HIGH);
}
for (int i = 3; i <= 12; i++) pinMode(i, INPUT_PULLDOWN);
Serial.begin(115200);
}
uint8_t getPosition() {
uint8_t switches = 0x00;
for (int i = 0; i < 7; i++) switches |= (digitalRead(i + 3) << i);
return switches;
}
uint8_t getReverse() {
if (digitalRead(11)) return REVERSE_FORWARD;
if (digitalRead(10)) return REVERSE_BACKWARD;
return REVERSE_0;
}
void processPositionTrainz(uint8_t position) {
switch (position) {
case POSITION_3:
Keyboard.key_code('4', KEY_SHIFT);
break;
case POSITION_2:
Keyboard.key_code('3', KEY_SHIFT);
break;
case POSITION_1:
Keyboard.key_code('2', KEY_SHIFT);
break;
case POSITION_M:
Keyboard.key_code('1', KEY_SHIFT);
break;
case POSITION_0:
Keyboard.key_code('Q', 0);
break;
case POSITION_P:
Keyboard.key_code('7', KEY_SHIFT);
break;
case POSITION_EPT:
Keyboard.key_code('9', KEY_SHIFT);
break;
}
}
void processReverseTrainz(uint8_t direction) {
switch (direction) {
case REVERSE_FORWARD:
Keyboard.key_code('F', KEY_SHIFT);
break;
case REVERSE_BACKWARD:
Keyboard.key_code('R', KEY_SHIFT);
break;
case REVERSE_0:
Keyboard.key_code('N', KEY_SHIFT);
break;
}
}
void loop() {
// put your main code here, to run repeatedly:
uint8_t previousPosition = POSITION_0, currentPosition = POSITION_0, previousReverse = REVERSE_0, currentReverse = REVERSE_0;
while (1) {
currentPosition = getPosition();
currentReverse = getReverse();
if (currentPosition != previousPosition) {
processPositionTrainz(currentPosition);
previousPosition = currentPosition;
}
if (currentReverse != previousReverse) {
processReverseTrainz(currentReverse);
previousReverse = currentReverse;
}
delay(50);
}
}
Распиновку и обе прошивки я также залил сюда.
Играем
Заливаем прошивку и пробуем покататься с новым контроллером.
На просторах нашёл видео другого товарища, который также некогда смог запустить такой контроллер, так что своё собственное снимать уже не стал.
Да, никакой джойстик и уж тем более клавиатура не дают тех же ощущений, что может предоставить контроллер, даже при игре на слабом компьютере реалистичность возрастает на несколько порядков. Ручка контроллера очень тугая, с непривычки тяжело сразу поставить её в нужную позицию, что, безусловно, делает езду интереснее.
Но и тут не без своих нюансов: если в Trainz всё весьма неплохо, то в метрострое куда сложнее — четыре ходовых позиции на городском транспорте есть только в трамваях. В «метрошном» же контроллере их по три, что тормозных, что ходовых. В итоге получается, что с одной стороны имеется одна лишняя позиция, которую никак не задействовать, а с другой — одной не хватает. Позиция «ЭПТ», к слову, не фиксируется, если поставить контроллер в неё и отпустить, то он вернётся в «П».
Вот как-то так
Как удалось выяснить, подключить к компьютеру контроллер от настоящего поезда совершенно реально. Более того, реальность от его использования превзошла все ожидания, думаю, если собрать полноценный пульт, то эффект будет ещё круче.
Но всё же есть пара косяков и по аппаратной части: во-первых, сам контроллер очень тяжёлый, но спереди имеет небольшой скос, отчего при попытке перевести ручку на позицию ниже он опрокидывается. Во-вторых, из-за того, что ручка тугая, при переключении он болтается. Уверен, проблему можно решить, если прикрутить его к столу (благо, у товарища есть побитый жизнью экземпляр, который не жалко пустить на опыты) или приделать к нему какие-нибудь ножки, делающие его более устойчивым. Но это уже совсем другая история — про создание полноценного тренажёра машиниста.
Такие дела.
Автор: Лев