Welcome to TrainThing’s software documentation!

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.

Todo

command All Signals instructions are stubs returning <”failed”> response.

Need to build data structure and files for saved data.

Calls to signals to change aspect of signals.

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

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__()

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.

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

cmd_other

(dictionary) - Of other commands

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)

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

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

class command.Sensor

All methods to access stored sensor data

VERBOSE = True
__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: bool) None
store_file() str

store sensor data file

class command.Signal

All methods to access stored signal data

__init__()

read table from memory

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

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

Class Fan runs as a thread in the background as long as TrainThing is running. Using FanLib functions to monitor the cooling fan cpu temperature & frequency, fan status and mode. Information is displayed on the unicorns.

  • 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. The 5 step temperature ladder 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:

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

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

UPDATE_TIME = 2.0

(float) Time between display updates

VERBOSE = False

(boolean) If True extra print statements are output.

__init__() None

Initialization starts the FanLib passing ON_threshold and OFF_threshold 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 the same. Makes unicorn display methods available and starts the main method as a thread.

main() None

This method sets up initial display values and then runs forever checking and updating the display for

  • The fan is on or off

  • The fan is running manually or auto mode

  • The cpu frequency is Max, Min or in between

  • the cpu temperature is display with a 5 step ladder

Constant:

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 module 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 also used for 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 frequency if enabled

  • Monitor the cpu temperature

  • Monitor the fan control button if enabled

If in the auto mode, method _run_auto 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 thread running the fan in the auto mode.

Class fan passes on_threshold, off_threshold, to coordinate temperatures. auto_time is passed to coordinate refresh rates.

If the fan button is enabled, an interrupt is established to monitor the button.

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

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

  • 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

  • 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

_interrupt() None

If the fan control switch is enabled (default no_button = False) an interrupt is established to monitor the fan control switch connected to pin_button , watching for the falling edge.

The button has 2 functions based on the length of time the button is held down

  • If in the auto mode and the button is held for more than the param button_hold_time

    • The fan will be switch to the manual mode of operation

  • If in the manual mode

    • If the button is held for less than the button_hold_time , the fan will be toggled on or off

    • If the button is held for more than the button_hold_time , the fan will be switched to the auto mode

_run_auto() None

Runs the fan in the auto mode as a thread exiting when the fan is switched to the manual mode

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

  • If enabled checks the cpu frequency and keeps the fan running if the frequency is not the max freq.

_start_auto() None

starts procedure _run_auto as a thread

static get_cpu_freq()

Gets the current CPU frequencies. Named tuples, current, min and max

Returns

(dict) Frequencies in MHz

static get_cpu_temp() float

Gets the current CPU temperature

Returns

(float) degrees C

get_fan() bool

Get current fan state.

Returns

(Boolean) True if the fan is running

get_fan_auto() bool

Get the current mode of the fan

:returns (Boolean) True if fan in auto mode

set_fan(fan_state: bool) bool

Set the fan on/off.

Parameters

fan_state (boolean) – Desired state of the fan

Returns

(Boolean) The new state of the fan

toggle_fan() bool

Toggle fan state.

Returns

(Boolean) The new state of the fan

Todo

signals semaphore set needed LED done

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.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:

PINS

Assign GPIO pins for signals red, yellow, green

VERBOSE = True

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

__init__() None

Defines GPIO pin interface to the LED signals

Constant:

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

class signals.Signals

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

Constant:

VERBOSE = False

(boolean) more print statements for troubleshooting and development of the class Signals.

__init__() None

Defines the semaphore hardware interface

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

Set the semaphore aspect

Parameters
  • row (int) – which PWM HAT/Bonnet o - 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

Todo

traffic Output interrupt driven sensor changes to the laptop

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.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:

DIV = 30

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

VERBOSE = False

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

__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_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

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_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

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

class traffic.Traffic

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 by Arduino.

SERVER_IP = '192.168.1.128'

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

VERBOSE = True

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

__init__() 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

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

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 “>”

static stop_servers(self) None

end all wifi and serial connections

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

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.

Todo

unicorn code and docstring done.

Module unicorn

This module provides: an interface to the two Unicorn HAT HD 16x16 displays used to show the TrainThing signals aspect 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:

GREEN = 3

(int) Signal aspect green

OFF = 0

(int) LED Signal off

RED = 1

(int) Signal aspect red

YELLOW = 2

(int) Signal aspect yellow

__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

static off() None

Clears the unicorn buffer and display

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

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

Todo

wabbitFB Change response from “print” to <Q x> or <q x> sent to Laptop

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:

VERBOSE = False

(boolean) If true provides additional output for troubleshooting

__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

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

_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) 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

output(port: int, changed: int, value: int, delay: bool = True)

Called by _interrupt() and tell_all() to Report which sensors/bits changed. If bit goes high, call delayed() to report

Parameters
  • port (int) – which GPIO pin caused the interrupt i.e. which port

  • changed (byte) – byte 1 if bit changed else 0

  • value (byte) – New value of the reporting port

  • delay (boolean) – delay reporting going high or not

Constants:

static tell_all(self) None

Report status of all sensors

Indices and tables