hardware

Classes that manage hardware logic.

Each hardware class should be able to operate independently - ie. not be dependent on a particular task class, etc. Other than that there are very few design requirements:

  • Every class should have a .release() method that releases any system resources in use by the object, eg. objects that use pigpio must have their pigpio.pi client stopped; LEDs should be explicitly turned off.
  • The very minimal class attributes are described in the Hardware metaclass.
  • Hardware methods are typically called in their own threads, so care should be taken to make any long-running operations internally threadsafe.

Note

This software was primarily developed for the Raspberry Pi, which has two types of numbering schemes , “board” numbering based on physical position and “bcm” numbering based on the broadcom chip numbering scheme.

Board numbering is easier to use, but pigpio , which we use as a bridge between Python and the GPIOs, uses the BCM scheme. As such each class that uses the GPIOs takes a board number as its argument and converts it to a BCM number in the __init__ method.

If there is sufficient demand to make this more flexible, we can implement an additional pref to set the numbering scheme, but the current solution works without getting too muddy.

Warning

In order to use pigpio, the pigpio daemon must be running. See the docs Usually Pilot s should be started by the bash script or systemd service generated by setup.setup_pilot, which starts pigpiod.

Data

BCM_TO_BOARD dict – The inverse of BOARD_TO_BCM.
BOARD_TO_BCM dict – Mapping from board (physical) numbering to BCM numbering.

Classes

Beambreak(pin[, pull_ud, trigger_ud, event]) An IR Beambreak sensor.
Digital_Out(pin[, pulse_width, polarity]) Send digital output pulses over a GPIO pin.
Flag(pin) Trivial Reclass of the Beambreak class with the default directions reversed.
Hardware Generic class inherited by all hardware.
LED_RGB([pins, r, g, b, common, blink]) An RGB LED.
Pull(pin, pud) Pull a pin up or down.
Scale([model, vendor_id, product_id])
Solenoid(pin[, duration, vol]) Solenoid valves for water delivery.
Wheel([mouse_idx, fs, thresh, thresh_type, …]) A continuously measured mouse wheel.
autopilot.core.hardware.BOARD_TO_BCM = {3: 2, 5: 3, 7: 4, 8: 14, 10: 15, 11: 17, 12: 18, 13: 27, 15: 22, 16: 23, 18: 24, 19: 10, 21: 9, 22: 25, 23: 11, 24: 8, 26: 7, 29: 5, 31: 6, 32: 12, 33: 13, 35: 19, 36: 16, 37: 26, 38: 20, 40: 21}

dict – Mapping from board (physical) numbering to BCM numbering.

See this pinout.

Hardware objects take board numbered pins and convert them to BCM numbers for use with pigpio.

autopilot.core.hardware.BCM_TO_BOARD = {2: 3, 3: 5, 4: 7, 5: 29, 6: 31, 7: 26, 8: 24, 9: 21, 10: 19, 11: 23, 12: 32, 13: 33, 14: 8, 15: 10, 16: 36, 17: 11, 18: 12, 19: 35, 20: 38, 21: 40, 22: 15, 23: 16, 24: 18, 25: 22, 26: 37, 27: 13}

dict – The inverse of BOARD_TO_BCM.

class autopilot.core.hardware.Hardware[source]

Bases: object

Generic class inherited by all hardware. Should not be instantiated on its own (but it won’t do anything bad so go nuts i guess).

Primarily for the purpose of defining necessary attributes.

Also defines __del__ to call release() so objects are always released even if not explicitly.

Variables:
  • trigger (bool) – Is this object a discrete event input device? or, will this device be used to trigger some event? If True, will be given a callback by Task, and assign_cb() must be redefined.
  • pin (int) – The BCM pin used by this device, or None if no pin is used.
  • type (str) – What is this device known as in .prefs? Not required.
  • input (bool) – Is this an input device?
  • output (bool) – Is this an output device?

Methods

assign_cb(trigger_fn) Every hardware device that is a trigger must redefine this to accept a function (typically Task.handle_trigger()) that is called when that trigger is activated.
get_name() Usually Hardware is only instantiated with its pin number,
release() Every hardware device needs to redefine release(), and must

Attributes

input bool(x) -> bool
output bool(x) -> bool
trigger bool(x) -> bool
type str(object=’’) -> string
trigger = False
pin = None
type = ''
input = False
output = False
release()[source]

Every hardware device needs to redefine release(), and must

  • Safely unload any system resources used by the object, and
  • Return the object to a neutral state - eg. LEDs turn off.

When not redefined, a warning is given.

assign_cb(trigger_fn)[source]

Every hardware device that is a trigger must redefine this to accept a function (typically Task.handle_trigger()) that is called when that trigger is activated.

When not redefined, a warning is given.

get_name()[source]

Usually Hardware is only instantiated with its pin number, but we can get its name from prefs

class autopilot.core.hardware.Beambreak(pin, pull_ud='U', trigger_ud='D', event=None)[source]

Bases: autopilot.core.hardware.Hardware

An IR Beambreak sensor.

A phototransistor that changes voltage from ‘high’ to ‘low’ or vice versa when light is blocked.

Variables:
  • pig (pigpio.pi()) – The pigpio connection.
  • pin (int) – Broadcom-numbered pin, converted from the argument given on instantiation
  • callbacks (list) – A list of :meth:`pigpio.callback`s kept to clear them on exit
Parameters:
  • pin (int) – Board-numbered pin, converted to BCM numbering during instantiation.
  • pull_ud ('U', 'D', 'B') – Should this beambreak be pulled up or down?
  • trigger_ud ('U', 'D', 'B') – Is the trigger event up (low to high) or down (high to low)?
  • event (threading.Event) – We can be passed an Event object if we want to handle stage transition logic here instead of the Task object, as is typical.

Methods

assign_cb(callback_fn[, add, evented, …]) Sets callback_fn to be called when triggered.
clear_cb() Tries to call .cancel() on each of the callbacks in Beambreak.callbacks
release() Simply calls self.pig.stop() to release pigpio resources.

Attributes

input bool(x) -> bool
trigger bool(x) -> bool
type str(object=’’) -> string
trigger = True
type = 'POKES'
input = True
release()[source]

Simply calls self.pig.stop() to release pigpio resources.

assign_cb(callback_fn, add=False, evented=False, manual_trigger=None)[source]

Sets callback_fn to be called when triggered.

Parameters:
  • callback_fn (callable) – The function to be called when triggered
  • add (bool) – Are we adding another callback? If False, the previous callbacks are cleared.
  • evented (bool) – Should triggering this event also set the internal event? Note that Beambreak.event must have been passed.
  • manual_trigger ('U', 'D', 'B') – We can override Beambreak.trigger_ud if needed.
clear_cb()[source]

Tries to call .cancel() on each of the callbacks in Beambreak.callbacks

class autopilot.core.hardware.Flag(pin)[source]

Bases: autopilot.core.hardware.Beambreak

Trivial Reclass of the Beambreak class with the default directions reversed.

Attributes

trigger bool(x) -> bool

Todo

Need to add argument passing into hardware spec so we don’t need stuff like this

trigger = True
class autopilot.core.hardware.LED_RGB(pins=None, r=None, g=None, b=None, common='anode', blink=True)[source]

Bases: autopilot.core.hardware.Hardware

An RGB LED.

Variables:
  • pig (pigpio.pi()) – The pigpio connection.
  • flash_block (threading.Event) – An Event to wait on setting further colors if we are currently in a threaded flash train
  • end_thread (threading.Event) – Set this event to stop a flash train - usually called during object cleanup
  • pins (dict) –

    After init, pin numbers are kept in a dict like:

    {'r':bcm_number, 'g':...}
    
  • stored_color (dict) – A color we store to restore after we do a flash train.
Parameters:
  • pins (list) – A list of (board) pin numbers. Either pins OR all r, g, b must be passed.
  • r (int) – Board number of Red pin - must be passed with g and b
  • g (int) – Board number of Green pin - must be passed with r and b
  • b (int) – Board number of Blue pin - must be passed with r and g:
  • common ('anode', 'cathode') – Is this LED common anode (low turns LED on) or cathode (low turns LED off)
  • blink (bool) – Flash RGB at the end of init to show we’re alive.

Methods

color_series(colors, duration) Change color through a series for a fixed duration.
flash(duration[, frequency, colors]) Specify a color series by total duration and flash frequency.
release() Turns LED off and releases pigpio.
set_color([col, r, g, b, timed, stored, …]) Set the color of the LED.
threaded_color_series(colors, duration) Should only be called by LED_RGB.color_series() because it blocks.

Attributes

output bool(x) -> bool
type str(object=’’) -> string
output = True
type = 'LEDS'
release()[source]

Turns LED off and releases pigpio.

set_color(col=None, r=None, g=None, b=None, timed=None, stored=False, internal=False)[source]

Set the color of the LED.

Note

if called during a LED_RGB.color_series(), the color will be stashed and set when the train is over.

Parameters:
  • col (list, tuple) – an RGB color trio ranging from 0-255. Either col or all of r, g, b must be provided
  • r (int) – Red intensity 0-255. Must be passed with g and b
  • g (int) – Green intensity 0-255. Must be passed with r and b
  • b (int) – Blue intensity 0-255. Must be passed with r and g
  • timed (float) – Duration to change to this color before turning off in ms.
  • stored (bool) – Called internally to change back to the color that preceded a flash train. Restores LED_RGB.stored_color.
  • internal (bool) – True if being called inside a flash train.
flash(duration, frequency=10, colors=[[255, 255, 255], [0, 0, 0]])[source]

Specify a color series by total duration and flash frequency.

Largely a convenience function for on/off flashes.

Parameters:
  • duration (int, float) – Duration of flash in ms.
  • frequency (int, float) – Frequency of flashes in Hz
  • colors (list) –

    A list of RGB values 0-255 like:

    [[255,255,255],[0,0,0]]
    
color_series(colors, duration)[source]

Change color through a series for a fixed duration.

Wrapper around LED_RGB.threaded_color_series()

Parameters:
  • colors (list) –

    A list of RGB values 0-255 like:

    [[255,255,255],[0,0,0]]
    
  • duration (int, list) – Either a single duration (int, ms) or list of ints of equal length to colors to define duration for each.
threaded_color_series(colors, duration)[source]

Should only be called by LED_RGB.color_series() because it blocks.

Clears LED_RGB.flash_block , sets colors, sleeps, sets the block, and then sets any color that was passed during the train.

Parameters:
  • colors (list) –

    A list of RGB values 0-255 like:

    [[255,255,255],[0,0,0]]
    
  • duration (int, list) – Either a single duration (int, ms) or list of ints of equal length to colors to define duration for each.
class autopilot.core.hardware.Solenoid(pin, duration=20, vol=None)[source]

Bases: autopilot.core.hardware.Hardware

Solenoid valves for water delivery.

Only NC solenoids should be used, as there is no way to guarantee that a pin will maintain its voltage when it is released, and you will spill water all over the place.

Note

pigpio has a function to send waveforms, which would make solenoid opening far more accurate. If you are using an audio device, however, creating and sending waveforms disables it. Waveforms are thus not implemented here, but their implementation is left, skeleton-like, in the source should you do an experiment without audio that needs more precision.

It’s hard to see why submillisecond precision would matter all that much for reward delivery, but such is the obsessiveness of scientists.

Methods

dur_from_vol(vol) Given a desired volume, set our open duration.
open([duration]) Open the valve.
release() Simply releases the pigpio resources

Attributes

mode str(object=’’) -> string
output bool(x) -> bool
type str(object=’’) -> string
Parameters:
output = True
type = 'PORTS'
mode = 'DURATION'
dur_from_vol(vol)[source]

Given a desired volume, set our open duration.

Parameters:vol (float, int) – desired reward volume in uL

Returns:

release()[source]

Simply releases the pigpio resources

open(duration=None)[source]

Open the valve.

Parameters:duration (float) – If provided, open for this duration instead of the duration stored on instantiation.
class autopilot.core.hardware.Wheel(mouse_idx=0, fs=10, thresh=100, thresh_type='dist', start=True, digi_out=False, mode='vel_total', integrate_dur=5)[source]

Bases: autopilot.core.hardware.Hardware

A continuously measured mouse wheel.

Uses a USB computer mouse.

Warning

‘vel’ thresh_type not implemented

Attributes

MODES tuple() -> empty tuple
MOVE_DTYPE list() -> new empty list
THRESH_TYPES list() -> new empty list
input bool(x) -> bool
trigger bool(x) -> bool
type str(object=’’) -> string

Methods

assign_cb(trigger_fn) Every hardware device that is a trigger must redefine this to accept a function (typically Task.handle_trigger()) that is called when that trigger is activated.
calc_move(move[, thresh_type]) Calculate distance move depending on type (x, y, total dist)
check_thresh(move) Updates thresh_val and checks whether it’s above/below threshold
l_clear(value) Stop measuring!
l_measure(value) Task has signaled that we need to start measuring movements for a trigger
l_stop(value) Stop measuring and clear system resources
release() Every hardware device needs to redefine release(), and must
start()
thresh_trig()
Parameters:
  • mouse_idx (int) –
  • fs (int) –
  • thresh (int) –
  • thresh_type ('dist') –
  • start (bool) –
  • digi_out (Digital_Out, bool) –
  • mode ('vel_total') –
  • integrate_dur (int) –
input = True
type = 'Wheel'
trigger = False
THRESH_TYPES = ['dist', 'x', 'y', 'vel']
MODES = ('vel_total', 'steady', 'dist', 'timed')
MOVE_DTYPE = [('vel', 'i4'), ('dir', 'U5'), ('timestamp', 'f8')]
start()[source]
check_thresh(move)[source]

Updates thresh_val and checks whether it’s above/below threshold

Parameters:move (np.array) – Structured array with fields (‘vel’, ‘dir’, ‘timestamp’)

Returns:

calc_move(move, thresh_type=None)[source]

Calculate distance move depending on type (x, y, total dist)

Parameters:
  • () (thresh_type) –
  • ()

Returns:

thresh_trig()[source]
assign_cb(trigger_fn)[source]

Every hardware device that is a trigger must redefine this to accept a function (typically Task.handle_trigger()) that is called when that trigger is activated.

When not redefined, a warning is given.

l_measure(value)[source]

Task has signaled that we need to start measuring movements for a trigger

Parameters:() (value) –
l_clear(value)[source]

Stop measuring!

Parameters:() (value) –

Returns:

l_stop(value)[source]

Stop measuring and clear system resources :param value ():

Returns:

release()[source]

Every hardware device needs to redefine release(), and must

  • Safely unload any system resources used by the object, and
  • Return the object to a neutral state - eg. LEDs turn off.

When not redefined, a warning is given.

class autopilot.core.hardware.Scale(model='stamps.com', vendor_id=None, product_id=None)[source]

Bases: autopilot.core.hardware.Hardware

Note

Not implemented, working on using a digital scale to make weighing faster.

Attributes

MODEL dict() -> new empty dictionary
Parameters:
  • model
  • vendor_id
  • product_id
MODEL = {'stamps.com': {'vendor_id': 5190, 'product_id': 27251}}
class autopilot.core.hardware.Pull(pin, pud)[source]

Bases: autopilot.core.hardware.Hardware

Pull a pin up or down. Called by the Pilot instead of by a Task as is usual.

If a pin should be pulled up or down always, regardless of task, use this. For example, the otherwise wonderful HiFiBerry Amp 2 has an undocumented … feature … : the (board) pin 8 mutes output when low.

Parameters:
  • pin (int) – (Board) pin number
  • pud ('U', 'D') – Pull the pin ‘U’p or ‘D’own.

Methods

release() Simply releases the pigpio client.
release()[source]

Simply releases the pigpio client.

class autopilot.core.hardware.Digital_Out(pin, pulse_width=100, polarity=False)[source]

Bases: autopilot.core.hardware.Hardware

Send digital output pulses over a GPIO pin.

Parameters:
  • pin (int) –
  • pulse_width (int) – Width of digital output pulse (us). range: 1-100
  • polarity (bool) – Whether ‘off’ is Low (False, default) and pulses bring the voltage High, or vice versa (True)

Attributes

output bool(x) -> bool
type str(object=’’) -> string

Methods

pulse([duration])
release() Every hardware device needs to redefine release(), and must
output = True
type = 'DIGITAL_OUT'
pulse(duration=None)[source]
release()[source]

Every hardware device needs to redefine release(), and must

  • Safely unload any system resources used by the object, and
  • Return the object to a neutral state - eg. LEDs turn off.

When not redefined, a warning is given.