Welcome to TrainThing’s Raspberry Pi software documentation!

Note

Close to done.

Module TT_3

This is the software for the Raspberry Pi, part (3), of the TrainThing

TT_3.main()

This top level module creates an instance of the class fan.Run and Traffic to start the TrainThing.

A keyboard interrupt (<Ctrl> C) will shut things down.

Module traffic

This module provides:

  • A socket server for the WiFi connection to the Laptop running JMRI DCC++ software or TrainThing application.

  • A serial link to the Arduino running the DCC++ Base Station and

  • Blinks the 4 LEDs representing traffic to and from each.

By default the library module serial creates a serial link to the Arduino with:

  • EIGHTBITS = 8,

  • PARITY_NONE = N,

  • STOPBITS_ONE = 1

class traffic.Traffic(unicorn)

This class handles all TrainThing communication wirelessly to/from the Laptop and wired to/from the Arduino. read_lt() retains commands from the Laptop NOT intended for the Arduino Base Station. Calls to send_to_lt() and write_lt() are used to return status from the Raspberry Pi to the Laptop.

Constants:

BAUDRATE = 115200

(int) - Serial baud rate to the Arduino.

DEVICE_NAME = '/dev/ttyACM0'

(str) - Serial name for the Arduino.

FORMAT = 'utf-8'

(str) - Format for string/byte messages.

PORT = 2560

(int) - Serial port used between RPi and Laptop.

SERVER_IP = '192.168.1.149'

(str) - IP address of the RPi internet server used by Laptop.

VERBOSE = True

(boolean) - more print statements for troubleshooting of the class traffic.

NO_BS = False

(boolean) - Run Raspberry Pi without Arduino base station

__init__(unicorn) None
  • Defines and starts the WiFi socket to the Laptop.

  • Defines a serial link through a USB port to the Arduino Base Station.

  • Starts threads to read from the LapTop and Arduino.

  • Init class monitor.

Parameters

unicorn – Link to the instance of module unicorn created in module TT_3 and passed to module Command.

read_lt() None

This method is a thread started by __init__ to reads strings from the laptop each ending with “>” command.check is used to see if an instruction from the laptop is for the Arduino, RPi or both. Passes input to write_BS for the Arduino, Pass input to command.process for the RPi or both. Passes Monitor.from_LT the length of the message to blink the orange LED Method loops till shutdown

send_to_lt(responses: list) None

Send strings to write_lt one at a time from the list of responses Some commands return a list of more than one response.

Parameters

responses (list) – List of strings each bounded by “<” and “>”

write_lt(message: str) None

Writes to the Laptop the string passed as a parameter. Passes Monitor.to_LT the length of the message to blink the blue LED

Pram str message

Status returned for a laptop command or sensor update.

write_bs(message: str) None

Writes to the Arduino the string passed as a parameter from read_lt. Passes Monitor.to_BS the length of the message to blink the orange LED

Pram str message

Command from LT to Arduino base station

read_bs() None

This method is a thread started by __init__ to reads strings from the Arduino each ending with “>”. Strings from the Arduino are passed to write_lt Passes Monitor.from_BS the length of the message to blink the blue LED

static stop_servers(self) None

end all wifi and serial connections

class traffic.Monitor

This class is used to blink each of the 4 traffic monitoring LEDs in relationship to the traffic Out from the laptop (Orange) and Back to the laptop (Blue). Output to the Base Station (Orange) and Back from the Base Station (Blue).

Should be called only be Traffic

Constants:

VERBOSE = True

(boolean) - If True extra print statements are output.

DIV = 30

(int) - Divisor for the message size to determine the number of blink for the LED

__init__() None

Set up the IOPi HAT pins for traffic monitoring LEDs IOPi HAT Bus 2 using pins 9 - 12

Common routine to blink a LED number of blinks = 1 + num // DIV (integer divide).

Parameters
  • led (int) – output pin on IOPi bus2 of the LED to blink

  • num (int) – number of bytes in the message i.e. len(message)

from_lt(num: int) None

Creates a thread to Blink the ORANGE LED for traffic from the Laptop.

Parameters

num (int) – length of the message received i.e. len(message)

to_lt(num: int) None

Creates a thread to Blink the BLUE LED for traffic to the Laptop.

Parameters

num (int) – length of message sent to the laptop

to_bs(num: int) None

Creates a thread to Blink the ORANGE LED for traffic to the Base Station.

Parameters

num (int) – length of message sent to the Base Station

from_bs(num: int) None

Creates a thread to Blink the BLUE LED for traffic from the Base Station.

Parameters

num (int) – length of message received from the Base Station

Module command

This module manages configuration of data retained by the RPi and instructions passed from the Laptop.

All access to the configuration information is controlled by Classes Sensor and Signal.

Classes Sensor and Signal implement all DCC++ instructions (and addon commands) processed by the RPi using assets controlled by Classes wabbitFB and signals.

The aspect of each signal is mirrored on the unicorn LED matrix

class command.Command(unicorn)

This class processes all commands from the TrainThing laptop (4) intended for the Raspberry Pi (3)

Traffic.from_LP uses function command.check to identify which instruction go where.

If intended for the RPi command.process is passed the command

Constant:

VERBOSE = True

(boolean) - If True print statements are output for development and troubleshooting

__init__(unicorn)

Defines list of single letter commands sent by the laptop to be executed by the RPi.

Create an instance of Classes Sensor and Signal which execute appropriate commands and retain configuration data.

cmd_sensor

(dictionary) - Of sensor related commands and if intended for RPi only (2) or both (1)

cmd_signal

(dictionary) - Of signal related commands and if intended for RPi only (2) or both (1)

cmd_other

(dictionary) - Of other commands

check(msg: str) int

Uses the one letter command to determine whether an instruction is intended for the RPi or Arduino or both.

DCC++ command “S” is an exception.

  • <S ID PIN PULLUP> or <S ID>

    • IF ID <= 50 then command is passed to the Arduino only

    • IF ID > 50 then command is processed by RPi only

  • <S> Command processed by BOTH RPi and Arduino

Parameters

msg (str) – command message from the LapTop

Returns

(int) 0 - Arduino only (default), 1 - both, 2 RPI only

Raises
  • ValueError – If received command format is invalid or ID < 51

  • TypeError – If param are not digits

process(msg: str) list

Processes all commands to be handled by the RPi Function Signal.process , Sensor.process or other_process are called to manage each group of commands.

Parameters

msg (str) – command from the Laptop

Raises

ValueError – If command code is invalid

other_process(msg: str) list

Processes other commands for the RPi

Maybe only <s> current Raspberry Pi status

Parameters

msg (str) – Command from the laptop

Returns

Current status of the RPi. Several lines.

Raises

ValueError for invalid command letter

class command.Signal(unicorn)

this class processes all commands for signals added to the DCC++ structure.

data_file = 'signals.pkl'
__init__(unicorn)

Read from memory the file that containing all information defining signals. Initialize all signals and display on the unicorn

process(msg: str) list

Process all commands to define/maintain/set the aspect of all signals.

Function signals.Signals.set or signals.LED.set are called to change the physical signal.

Function unicorn.TT_Display.set_signal is called to update the appropriate pixel on the unicorn display.

Parameters

msg (str) – command sent from the Laptop

Returns

list of return string for the command

Raises

ValueError – If command is invalid

Todo

fan figure out why _yellow does not show as a constant.

Module fan

This module displays the status of the RPi cooling fan on the TrainThing unicorn LED matrix. Low lever hardware functions are called from the FanLib module and status is displayed by calls to module unicorn.

class fan.Run(unicorn)

This class runs as a thread in the background as long as TrainThing is running. Using FanLib functions to control the cooling fan, monitor the RPi cpu temperature & frequency, fan status and mode. Information is displayed on the TrainThing unicorns LED matrix.

  • Fan off - GREEN, Fan on RED

  • CPU Freq = MAX - GREEN (1500MHz), Freq = MIN (600MHz) - RED, else YELLOW (1000 or 750 MHz)

  • Fan auto mode - GREEN, Manual mode - YELLOW

Hardware has a hard Temperature threshold of 85 C and a soft threshold of 80 C. Using ON_threshold and OFF_threshold the fan on-off hysteresis is controlled. The 5 step temperature ladder displayed on the unicorn matrix is:

  • Temp >= ON_threshold

  • ON_threshold > Temp >= OFF_threshold + 3*(on-off Δ)/4

  • OFF_threshold + (on-off Δ)4 > Temp >= OFF_threshold + 2*(on-off Δ)/4

  • OFF_threshold + 2*(on-off Δ)/4 > Temp >= OFF_threshold + (on-off Δ)/4

  • OFF_threshold >= Temp

Raises

ValueError – If constant ON_threshold <= OFF_threshold.

Constants:

UPDATE_TIME = 2.0

(float) Time between display updates

ON_threshold = 65

(int) The degrees C when the fan is turned on in the auto mode. RPi hard threshold of 85 C, soft 80 C

OFF_threshold = 55

(int) The degrees C when the fan is turned off in the auto mode. OFF_threshold must be less than ON_threshold

__init__(unicorn) None

Initialization starts the FanLib passing the named parameters on_threshold, off_threshold and auto_time so both modules, fan and FanLib, have the same values so the unicorn display set by this module and the auto mode run by FanLib are synchronized. Makes unicorn display methods available and starts the _main() method as a thread.

Parameters

unicorn (link) – Link to instance of module unicorn created in TT_3. Used to display the current status of the fan.

Variables

run_forever (bool) – Set to True by _main() in the beginning. Set to False by function stop() to stop updating the fan status.

_main() None

This method sets up the initial display values and then every 2 seconds checks status and updating the display for:

  • The fan is on or off.

  • The fan is running in the manually or auto mode.

  • The cpu frequency is Max, Min or in between.

  • the cpu temperature 5 step display ladder.

until variable run_forever is set False by function stop()

Constant:

stop() None

This function is called to end updating of the fan status on the unicorn by setting run_forever False. FanLab is called to set_fan to False stopping the fan. And the fan status area of the unicorn is cleared.

Todo

FanLib Add option for PWM or GPIO fan control

Module FanLib

This library provides low level functions to manage the cooling fan for the Raspberry Pi. The Pimoroni FanShim FanLib library was used as a model to build my implementation to control a cooling fan powered by an output from a PWM on a 16-channel PWM/servo HAT and a separate control button connected to a GPIO pin.

the FanShim uses GPIO pins that are also mapped to the I2C interface which is heavily used by TrainThing.

__version__ = ‘0.0.3’

  • v 0.0.1 button

  • v 0.0.2 add fan and parameters

  • v 0.0.3 add temp and freq

  • v 0.0.4 PWM or GPIO fan control

class FanLib.Fan(pin_button: int = 19, button_bounce_delay: float = 0.05, button_hold_time: float = 4.0, no_button: bool = False, fan_i2c_addr: int = 64, fan_channel: int = 0, preempt: bool = True, auto_time: float = 2.0, on_threshold: int = 65, off_threshold: int = 55, verbose: bool = False)

This class handles the low level hardware interface to:

  • Run the fan

  • Monitor the cpu temperature

  • Monitor the cpu frequency if enabled

  • Monitor the fan control button if enabled

If in the auto mode (default), method _start_auto() is called to start method _run_auto() as a thread to updates the state of the fan based on cpu frequency, temperature and input parameters.

__init__(pin_button: int = 19, button_bounce_delay: float = 0.05, button_hold_time: float = 4.0, no_button: bool = False, fan_i2c_addr: int = 64, fan_channel: int = 0, preempt: bool = True, auto_time: float = 2.0, on_threshold: int = 65, off_threshold: int = 55, verbose: bool = False) None

Initializes the hardware and starts the function _run_auto() as a thread running the fan in the auto mode.

If the fan button is enabled (no_button: False), an interrupt is established to monitor the button, on GPIO pin pin_button. The function _interrupt may change variables _fan_auto (auto/manual mode) and _fanON (fan on/off).

Parameters
  • pin_button (int) – BCM pin # for fan control button. Default 19

  • button_bounce_delay (float) – Time in seconds delay to debounce the fan button. Default 0.05 second

  • button_hold_time (float) – Time in seconds to hold button to change fan mode. Default 4.0 seconds

  • no_button (boolean) – Disable button input. If True fan in auto mode. Default False

  • fan_i2c_addr (int) – I2C address of PWM controlling the fan. Default 0x40

  • fan_channel (int) – Sub address of PWM controlling the fan. Default 0

  • preempt (boolean) – Monitor CPU frequency and activate cooling preemptively. Default True

  • auto_time (float) – Time in seconds, between updates in auto mode. Default 2.0 seconds

  • on_threshold (int) – Temperature threshold in degrees C to auto start fan. Default 65

  • off_threshold (int) – Temperature threshold in degrees C to auto stop fan. Default 55

  • verbose (boolean) – Print Output temp and fan status messages. Default False

Raises

ValueError – IF argument on_threshold <= off_threshold

Variables
  • _fanON (bool) – True when the fan is running.

  • _fan_auto (bool) – True when the fan is in the auto mode.

get_fan() bool

returns the current value of _fanON, whether or not the fan is running.

Returns

(Boolean) True if the fan is running

toggle_fan() bool

Toggle fan state. Calls set_fan to turn the fan on or off and change the value of _fanON

Returns

(Boolean) The new state of the fan

set_fan(fan_state: bool) bool

Set the fan on/off. Based on fan_state change the state of the fan and _fanON

Parameters

fan_state (boolean) – Desired state of the fan

Returns

(Boolean) The new state of the fan (_fanON)

static get_cpu_freq()

Calls hardware utility to get the current CPU frequencies. Named tuples, current, min and max.

Returns

(dict) Frequencies in MHz

static get_cpu_temp() float

Calls hardware utility to get the current CPU temperature.

Returns

(float) degrees C

get_fan_auto() bool

Returns the current mode of the fan, _fan_auto

:returns (Boolean) True if fan in auto mode

_start_auto() None

Starts procedure _run_auto as a thread to run the fan in the auto mode.

_run_auto() None

Runs the fan in the auto mode as a thread while the fan is in the auto mode (_fan_auto True)

  • Checks the cpu temperature (get_cpu_temp()) to turn the fan on or off (toggle_fan()) based on on_threshold and off_threshold.

  • If enabled (_preempt True) checks the cpu frequency (get_cpu_freq()) and keeps the fan running if the frequency is not the max freq.

_interrupt() None

If the fan control switch is enabled (default no_button = False) an interrupt is established in __init__ to monitor the fan control switch connected to pin_button. When the button is pressed (falling edge of pin_button) this function is called.

The fan control button has 2 functions based on the status of variables _fan_auto, _fanON & button_hold_time and the length of time the button is held down.

  • If in the auto mode (_fan_auto True) and the button is held for more than the param button_hold_time

    • The fan will be switch to the manual mode of operation, (_fan_auto False) and the fan is stopped (_fanON False).

  • If in the manual mode (_fan_auto False)

    • If the button is held for less than the button_hold_time , the fan will be toggled on or off (_famON changed).

    • If the button is held for more than the button_hold_time , the fan will be switched to the auto mode, (_fan_auto True) and the fan is stopped, (_fanON False).

Module sensor

This description is to short. TODO: Need to put some words here in sensor

class sensor.Sensor

All methods to access stored sensor data

data_file = 'sensor.pkl'
__init__() None

read sensor table from memory

pickle = <module 'pickle' from '/usr/lib/python3.7/pickle.py'>
process(msg: str) list

Processes commands to define/maintain/report status of all turnout sensors

Parameters

msg (str) – Command from the LapTop

Returns

One or many status lines

Raises
  • ValueError – if command char/values in param msg are invalid

  • TypeError – if param includes not digits char

sensor_change(pin: str, value: int, hi: bool) None

Called by WabbitFB in response to a sensor change

Parameters
  • pin (str) – 1,2,3 byte of sensor hardware pins

  • value (int) – byte of sensor bits that changed

  • hi (bool) – sensors in value changed to high (true) or low (false)

Module signals

This module directly controls all the signals on the TrainThing layout. A command to change the aspect of a signal is sent from the LapTop (4) received by module traffic, passed to module command/Signal, combined with stored information and sent to this module which changes the aspect of a semaphore or LED signal.

A thread is created for each semaphore change so that the change is not “instantaneous” nor blocking of other operations.

class signals.Signals

This class defines the interface to the semaphore signal hardware and sets the aspect of each semaphore.

Constant:

VERBOSE = False

(bool) used for troubleshooting when True

__init__() None

Defines the semaphore hardware interface

set(row: int, col: int, old: int, new: int) None

Starts a thread to change a semaphore aspect. Calls _move()

Parameters
  • row (int) – which PWM HAT/Bonnet 0 - 3 {0x40 - 0x43}

  • col (int) – which output of the row 0 - 15

  • old (int) – current servo setting 0 - 180

  • new (int) – new servo setting 0 - 180

Raises

ValueError – If a parameter is out of range

class signals.LED

This class defines the hardware interface to the four tri-color LED signals in the parking lot and sets the aspect of the LED signals.

Constant:

VERBOSE = False

(boolean) more print statements for troubleshooting of the class LED.

PINS = {33: [12, 13, 16], 34: [25, 5, 6], 35: [22, 23, 24], 36: [17, 18, 27]}

Assign GPIO pins for signals red, yellow, green

__init__() None

Defines GPIO pin interface to the LED signals

set(signal: int, aspect: int) None

Set the LED signal aspect

Parameters
  • signal (int) – Signal to set 33 - 36

  • aspect (int) – 0-3, off, red, yellow, green

Raises

ValueError – If a parameter is out of range

Todo

traffic Output interrupt driven sensor changes to the laptop

Module unicorn

This module provides: an interface to the two Unicorn HAT HD 16x16 displays used to show the aspect of TrainThing signals in place of/ or in addition to, the actual semaphores and display the state of the Raspberry Pi cooling fan.

Functions are provided to simplify, for TrainThing, the interface with the UnicornHATHD2 library.

class unicorn.TT_Display(color: str = 'BLUE')

Procedure to display: Aspect of all TrainThing signals Status of the Raspberry Pi fan

Constants:

OFF = 0

(int) LED Signal off

RED = 1

(int) Signal aspect red

YELLOW = 2

(int) Signal aspect yellow

GREEN = 3

(int) Signal aspect green

__init__(color: str = 'BLUE') None

Initialize settings for the two Unicorn HAT HD matrix. By default, sets each pixel representing a TrainThing signal to BLUE. If color is set to anything else each pixel/signal is set to the expected initial aspect.

The eight pixel locations used to display the fan status are always initially BLUE

Parameters

color (str) – If NOT “BLUE” then expected initial signal aspects

set_signal(x: int, y: int, aspect: int) None

Update the displayed aspect of a signal at location {x,y}

Parameters
  • x (int) – coordinate of the pixel 2-31

  • y (int) – coordinate of the pixel 0-15

  • aspect (int) – 0-3 ~ OFF|RED|YELLOW|GREEN

Raises

ValueError – If a parameter is out of range

set_fan(x: int, y: int, r: int, g: int, b: int) None

Set a fan status pixel at location {x, y} with the given RGB levels.

Parameters
  • x (int) – coordinate of the pixel 0-1

  • y (int) – coordinate of the pixel 0-4

  • r (int) – intensity of red 0 - 255

  • g (int) – intensity of green 0 - 255

  • b (int) – intensity of blue 0 - 255

Raises

ValueError – If a parameter is out of range

static off() None

Clears the unicorn buffer and display

Module wabbitFB

This module provides the interface between Wabbit optic-isolators, the IOPi HAT, and the RPi.

  • The wabbits provide the status of each TrainThing turnouts to IOPi HAT.

  • The IOPi sends an interrupt to the RPi

  • The RPi then reads the IOPi inputs and reports changes in status of the turnouts to the Laptop.

Class WabbitFB Method Interrupt is called when any of the 24 optic-isolators/IOPi bits change.

class wabbitFB.WabbitFB

This class defines and handles the interrupts generated by the IOPi HAT in response to turnout status changes reported by the 6 Wabbit decoder boards part of TrainThing.

Defines a method to report the status of all defined IOPi pins.

Each of 6 Wabbit controls 2 turnouts reporting the CLEAR and THROWN status to (4) input pins on the IOPi HAT. In response the IOPi generates one of three hardware interrupt connected to Raspberry Pi GPIO pins 4, 0 or 1. The RPi generates interrupts in response to the GPIO pin changes calling method _interrupt within this class. The method reports the change in sensor status to the laptop.

Constant:

__init__() None

This method:

  • Initializes the IOPi bus1, port 0 & 1 and bus2 port 0 as inputs from the Wabbits. (NOT bus2 port 1.)

  • Initializes output interrupts for the 3 ports connected to GPIO pins

  • Initializes 3 GPIO pins as input interrupts to the RPi 4 0 1

static _bits(n: int) str

Change byte n to a binary string (Testing only)

Parameters

n (int) – A numeric byte

Returns

(str) A 9 character string representing the 8 bits in the byte. 8765 4321

static _fix0(old: int) int

Fix wiring error on Wabbit Feedback board, IOPi bus 1, port 1, bits 10 and 12 switched

Parameters

old – byte read from bus 1 port 1 xxxx AxBx

Returns

byte with bits switched xxxx BxAx

_interrupt(interrupt_pin: int, new: int, old: int) None

Function called when GPIO 0,1,4 goes low. Identifies sensors that changed

Parameters

interrupt_pin (int) – which GPIO pin caused the interrupt

Passes to output the interrupt_pin, which bits changed and the current value of the byte causing the interrupt.

Raises

ValueError – if parameter is invald

one_state(pin: str) list

Returns the state of one sensor called by command.Sensor - <Q> :pram str pin: 2 char byte and bit of sensor :returns: the pin requested and the current bool state 0/1 :raises ValueError: if pram value is not valid :raises TypeError: of pram is not 2 digits

_delayed(pin: int, went_hi: int) None

Thread to delay setting show bits going high

Parameters
  • pin (int) – interrupt-pin

  • went_hi (int) – byte of sensor that went high

Indices and tables