Documenting for publication

This commit is contained in:
Reid 'arrdem' McKenzie 2021-11-05 12:18:45 -06:00
parent db08760964
commit ed633c9ab0
2 changed files with 98 additions and 70 deletions

View file

@ -1,13 +1,106 @@
# clusterctrl
This project is a rewrite of the `clusterctrl` tool provided by the 8086 consultancy for interacting with their ClusterCTRL and ClusterHAT line of Raspberry Pi backplane products.
This project is a clean-sheet rewrite of the `clusterctrl` tool and underlying device driver provided by the 8086 consultancy for interacting with their ClusterCTRL and ClusterHAT line of Raspberry Pi backplane products.
It serves to factor out the underlying i²c driver common to the ClusterCTRL family of products, and aims to implement a far more idiomatic and better documented and far more consistent CLI tool.
## Usage & driver API
``` python
from clusterctrl.driver import ClusterCTRLv2Driver as Driver
from smbus2 import SMBus
hat = Driver(SMBus(3)) # Note that 3 here is the number of the i2c device the HAT is on
```
A quick API overview -
The CTRL/HAT products "order" themselves (identify boards) using an EEPROM stored value called 'order'.
The official clients use "order" to enable for somewhat stable sequential addressing of "pi6" as being board 2 pi 1.
However it's far more general purpose and predictable to directly expose and model the device tree.
Note that the firmware default for "order" is `20` and this driver will automatically assign random IDs to boards with orders of 20.
This behavior can be disabled by subclassing the driver and overloading `_post_init()`.
This API is built atop a `PiRef(board_id, pi_id)` tuple which is intended to allow for the construction of cluster management APIs which allow for automatic but predictable mapping of requests (eg. `power_on`, `power_status`) to a given device.
``` python
hat.fw_version # => (1, 6)
hat.min_pi # => <PiRef XXX-01>
hat.max_pi # => <PiRef XXX-05>
hat.pis() # => Iterable[PiRef] (iterate over all Pis on this device in order)
hat.type # => BoardType
hat.max_adc # => int (ADC support is incomplete)
```
### Power status
```
hat.power_on(<int | PiRef>)
hat.power_off(<int | PiRef>)
hat.power_status(<int | PiRef>)
hat.power_all_off()
hat.power_all_on()
hat.eeprom_save_powerstate()
```
### Board identification
``` python
hat.get_order()
hat.set_order(<order: uint8>)
hat.eeprom_save_order()
```
### Alert lights
``` python
hat.alert_on()
hat.alert_off()
hat.led_on()
hat.led_off()
hat.eeprom_save_leds()
```
### Fan control
``` python
hat.fan_on()
hat.fan_off()
hat.fan_status()
hat.read_temp() # Temp in integer kelvin
```
### USB hub control
``` python
hat.hub_on()
hat.hub_off()
hat.hub_reset()
```
### USB booting
``` python
hat.usbboot_on()
hat.usbboot_off()
hat.usbboot_status()
hat.eeprom_save_usbboot()
```
#### Driver.eeprom_reset
Reset all EEPROM stored settings to their 'factory' firmware defaults.
#### Driver.eeprom_save_all
Save all values back to EEPROM.
Note that this does not update the defaults restored by `Driver.eeprom_reset`.
## license
Copyright (c) 2018 Chris Burton (8086 consultancy)
Copyright (c) 2021 Reid McKenzie
This software is published under the MIT License
This software is published under the terms of the MIT License

View file

@ -1,65 +0,0 @@
import smbus
# Registers
# Direction
IN = 0x00
OUT = 0x01
DIR = 0x03
PUR = 0x04
INPUT = 1
OUTPUT = 0
class Xra1200:
bus = -1
address = -1
port = -1
def __init__(self, bus=0, address=0x39, port=0, dir="Null"):
self.bus = smbus.SMBus(bus)
self.address = address
self.port = port
if dir == 1:
self.set_input()
elif dir == 0:
self.set_output()
def set_dir(self, dir):
self.bus.write_byte_data(self.address, DIR, dir)
def get_dir(self):
return self.bus.read_byte_data(self.address, DIR)
def set_pur(self, pur):
self.bus.write_byte_data(self.address, PUR, pur)
def get_pur(self):
try:
reg = self.bus.read_byte_data(self.address, PUR)
except IOError as err:
return -1
return reg
def set_input(self):
state = self.bus.read_byte_data(self.address, DIR)
self.bus.write_byte_data(self.address, DIR, state | 1 << self.port)
def write_byte(self, data):
self.bus.write_byte_data(self.address, OUT, data)
def read_byte(self):
return self.bus.read_byte_data(self.address, IN)
def on(self):
state = self.bus.read_byte_data(self.address, OUT)
self.bus.write_byte_data(self.address, OUT, state | 1 << self.port)
def off(self):
state = self.bus.read_byte_data(self.address, OUT)
self.bus.write_byte_data(self.address, OUT, state & (255 - (1 << self.port)))
def get(self):
state = self.bus.read_byte_data(self.address, IN)
return (state >> self.port) & 1