2022-09-13 22:52:21

Компьютерное управление макетом железной дороги

Хобби Raspberry Pi Python

  Пульт управления макетом железной дороги и командная станция

В качестве компьютера, управляющего моим макетом выступает малютка Raspberry Pi 3. Само по себе "компьютерное управление" делится на две части: взаимодействие с аппаратным пультом управления макетом (экран, кнопки) и автоматика в движении поездов на макете.


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

Подключение к GPIO

Физически - то три разъема dupont: кнопки со светом, i2c дисплей, последовательный порт и еще один провод, который туда не вписался. Для того, чтоб это все могло работать нужно провести настройки на Raspbery. 

sudo raspi-config

В меню выбираем "Interfacing Options" 

И там включаем три пункта: SSH, I2C, Serial. SSH понадобится, чтоб крутить что-то без монитора и клавиатуры по сети, I2C для работы LCD экрана, Serial - для RocoBus.

А для того, чтоб подключить гребенку GPIO в стандартный разъем DB9 на рокобасе, потребуется RS232 to TTL Serial Port Converter с али. По непонятным пока причинам конструкция с последовательным портом на гребенке GPIO в связке с RocRail не взлетела, но, на удивление, заработало через самый бюджетный кабель HL-340 USB, приобретенный вместе конвертером так же на али. И еще удивительно, что не заработало через более дорогой кабель USB-RS232 на (якобы) чипе FTDI купленный в РФ, через который прописывал номера модулям обратной связи.

И раз уж затронул тему модулей обратной связи, то в процессе присвоения им номеров столкнулся с некоторыми трудностями. На MacOS не удалось подключиться к модулям FB2 через фирменный софт "Диспетчер", запущенный в эмуляторе CrossOver, даже руками прописав в wine порт, который появляется после подключения USB-Serial кабеля к маку. Порт становился доступен, но ничего не работает. Зато прекрасно все удалось сделать через Windows XP, запущенный в VirtualBox. 

Возвращаясь к малине, так же рекомендую для экономии ресурсов и уменьшения тепловыделения отключить Wi-fi, Bluetooth и звук. Для этого идем в файл /boot/config.txt и находим там строчку

# Additional overlays and parameters are documented /boot/overlays/README

ниже нее добавляем

dtoverlay=disable-wifi
dtoverlay=disable-bt

а еще ниже комментируем или меняем

# dtparam=audio=on
dtparam=audio=off

После всех этих действий придется перезагрузиться.

Работа с пультом управления

Экран и кнопки пульта управления макетом

Здесь придется немного освоить лже-язык программирования Python. Почему "лже", это мое субъективное мнение и останется за пределами этого поста. Для начала доставим немного софта из интернета

sudo apt-get install git i2c-tools python3-smbus python3-pip

Затем узнаем i2c-адрес нашего дисплея с помощью

i2cdetect -y 1

Если он отличается от 27, то нужно будет править в исходниках далее.

Теперь ставим библиотеку для работы с дисплеем 

sudo pip install RPLCD

И напишем коротенькую программу, которая будет показывать температуру и IP-адрес на экране, а так же отображать спиннер-индикатор, что все не повисло:

import os
from time import sleep 
from RPLCD.i2c import CharLCD

def get_ip():
    out = os.popen("hostname -I").readline()
    return out.strip()

def get_temp():
    out = os.popen("vcgencmd measure_temp").readline()
    return out.replace("temp=", "").strip()

backslash = (
        0b00000,
        0b10000,
        0b01000,
        0b00100,
        0b00010,
        0b00001,
        0b00000,
        0b00000
        )
lcd = CharLCD('PCF8574', 0x27, rows=2, cols=16)
lcd.create_char(0, backslash)
lcd.clear()

ip = old_ip = temp = old_temp = ''
star = ["|", "/", "-", '\x00']
s = 0

while True:
    if s >= len(star): s = 0
    lcd.cursor_pos = (0, 15)
    lcd.write_string(star[s])
    s += 1

    if s % 2:
        temp = get_temp()
        if old_temp != temp:
            old_temp = temp
            lcd.cursor_pos = (0, 0)
            lcd.write_string(temp)

        ip = get_ip()
        if old_ip != ip:
            old_ip = ip
            lcd.cursor_pos = (1, 0)
            lcd.write_string(ip)

    sleep(1)

lcd.clear()

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

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

Теперь кнопки. Для начала просто сделаем возможность включать и выключать подсветку кнопки при нажатии на нее. Программу так же можно запускать при старте системы и играться в кнопки :)

Ставим прослойку для работы с GPIO 

sudo apt-get install python3-gpiozero

И пишем простую программу, которая будет переключать светодиоды в кнопках по нажатию:

from gpiozero import LED, Button
from time import sleep 
from signal import pause

YL = 13
YB = 16
GL = 19
GB = 20
RL = 26
RB = 21

debounce = 0.3

red_led = LED(RL)
red_btn = Button(RB)
green_led = LED(GL)
green_btn = Button(GB)
yellow_led = LED(YL)
yellow_btn = Button(YB)

def on_red(btn):
    red_led.toggle()
    print("red", red_led.value)
    sleep(debounce)

def on_green(btn):
    green_led.toggle()
    print("green", green_led.value)
    sleep(debounce)

def on_yellow(btn):
    yellow_led.toggle()
    print("yellow", yellow_led.value)
    sleep(debounce)


red_btn.when_pressed = on_red
green_btn.when_pressed = on_green
yellow_btn.when_pressed = on_yellow

pause()

Я специально сделал три почти одинаковых обработчика нажатия на кнопки для того, чтобы потом, при включении и выключении подсветки кнопки, добавить выполнение команд в системе. Желтая кнопка - при включении запуск вещания картинки с камеры в сеть, при выключении - остановка вещания. Зеленая кнопка - при включении запустить RocRail (если еще не запущен) и включить автоматический режим движения поездов на макете, при выключении отключить автоматический режим. Красная кнопка - при включении остановить все на макете на случай аварии, при отключении выключить режим "stop". Так же следует добавить периодическую проверку происходящего, чтоб состояния индикаторов на кнопках соответствовали происходящему, например, чтоб красная загоралась так же при нажатии "stop" на рокомышке, а зеленая выключалась, если рокрейл по каким-то причинам вышел из автоматического режима. Обо всем этом будет отдельный пост по мере развития.

Автоматизация движения

Схема макета в RocRail

Для автоматизации движения поездов на макете буду использовать RocRail. Точнее его серверную часть, установленную на Raspberry и клиентскую часть на своем ноутбуке.

Все дальнейшее описание будет относительно моей конфигурации: командная станция Roco Multimaus (не путать с Z21) и 9 модулей обратной связи от Modelldepo (8 из которых FB2 и один RocoBus2 для связи с рокрейл). Перед началом во все модули были прописаны адреса по порядку, модуль рокобас получил номер 4.

На малине качаем и распаковываем рокрейл по инструкции. Затем запускаем сервер, для этого есть готовый rocrail.sh в папке куда распаковали. Теперь идем на ноутбук, там открываем рокрейл и подключаемся к серверу. Теперь весь вывод данных от сервера мы получим в области слева внизу окна.

Теперь согласно инструкции от рокобас добавляем нашу командную станцию. Некоторые замечания, относительно Raspberry pi: порт /dev/ttyS0 - это тот что на GPIO, а /dev/usbTTY0 - это usb-переходник. Так же в списке будет еще порт - это последовательный порт bluetooth, который никак не пригодится.

В том же диалоге свойств интерфейса в блоке Sensors поле Number - это количество модулей обратной связи (в моем случае 9), а не количество датчиков на них. Теперь можно переходить к рисованию схемы.

Добавление датчиков обратной связи

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

Адрес = (Номер модуля на шине - 1) * 8 + Номер порта + 1

Например для 5-го порта на 3 модуле получится адрес 22, а для 0-го порта на 4 модуле - 25.

Управление стрелками

Поскольку я использую собственные декодеры для управления стрелками, то готового рецепта адресации в рокрейл для них нет. Мои декодеры реализуют максимально простую адресацию, где один адрес = одна стрелка. Это вполне очевидно работает на рокомышке, а вот в рокрейле все сложнее, но вполне решаемо, если применить FADA-адресацию (когда порт всегда 0) и адрес стрелки вычислять по формуле

Адрес = (Номер на рокомыши - 1) * 2 + 1

Таким образом стрелка 1 имеет адрес 1, а стрелка 5 - адрес 9. Далее на вкладке Wiring надо указать датчики обратной связи, через которые рокрейл будет понимать положение и занятость стрелки.

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

Блоки

Теперь можно собирать блоки. В блоках следует использовать как можно больше датчиков для начала, а далее можно будет скорректировать, в зависимости от целей автоматизации движения. На моей схеме получились блоки, содержащие от 3 до 5 датчиков. Если блок не тупиковый, то важно описать все датчики как в прямом направлении (+) так и в обратном (-), на вкладке Routing в свойствах блока для этого есть два первых маршрута. Каждому датчику надо присвоить событие, которое будет происходить при попадании поезда на него. 

Локомотивы

Локомотивы добавляются через список в меню Tables. В принципе, для них достаточно задать лишь адрес, но при настройках по-умолчанию на моем маленьком макете локомотив Herkules от Piko несется крайне быстро и выглядит это не очень. Поэтому на вкладке Speed я уменьшил максимальную скорость до 40, а минимальную наоборот поднял до 11. Остальные значение выставил примерно пропорционально. Теперь локомотив едет красиво между блоками и ползет без рывков перед остановкой. Для других локомотивов, естественно, будут другие параметры, например, Breuer Traktor от Rivarossi начинает нормально ехать без рывков только на 30% мощности, а при 100% ползет совсем не быстро.

Поехали!

Теперь в меню File -> Routing -> Routing запускаем генерацию маршрутов для автоматики, дожидаемся, пока он все построит. На моей схеме получилось аж 44 маршрута! Включаем питание и автоматический режим. Назначаем локомотив в блок в котором он физически находится, перетаскиваем его мышкой в другой блок и... если все сделано правильно, наблюдаем чудо, сначала на схеме строится маршрут следования, затем начинают переключаться стрелки и поезд поехал! Прибыв в нужный блок, он плавно тормозит, медленно подползает к концу и останавливается. А если этот блок не был конечным, то снова переключаются стрелки и начинается движение дальше...

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