Documenting for publication
This commit is contained in:
parent
6999e35a91
commit
89610a3568
2 changed files with 98 additions and 70 deletions
|
@ -1,13 +1,106 @@
|
||||||
# clusterctrl
|
# 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
|
## license
|
||||||
|
|
||||||
Copyright (c) 2018 Chris Burton (8086 consultancy)
|
|
||||||
Copyright (c) 2021 Reid McKenzie
|
Copyright (c) 2021 Reid McKenzie
|
||||||
|
|
||||||
This software is published under the MIT License
|
This software is published under the terms of the MIT License
|
||||||
|
|
||||||
|
|
|
@ -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
|
|
Loading…
Reference in a new issue