[CentralOH] 2017-08-28 會議 Scribbles 落書/惡文?: Neil Ludban Python vs the Hardware; freebsd raspberry pi2 cnc real I/O
jep200404 at columbus.rr.com
jep200404 at columbus.rr.com
Fri Sep 29 18:21:53 EDT 2017
Thanks to christoph baker and pillar for their generous hospitality
there was pizza, salad, cookies, and beverages for all
devopsdays ohio in columbus 2017-11-08 and 2017-11-09
https://www.devopsdays.org/events/2017-ohio/welcome/
29 folks
columbus state
software development
python
database admin work for state of ohio
powershell
python
interested in anyone who has used ironpython
for integration with C#/.net
bioinformatics at ohio state
python is for 90% of what I do
R is for other 10%
september
erik welch will be presenting tips and tricks for jupyter notebooks
october
len and mike handler
python data analysis
temperature and humidity sensor
graph it in real time
len did back end
mike did front end
dallas one-wire interface sensor
first monday in december for social
maybe meet at smokehouse brewing company
january
travis will talk about async await and curio library
which was written by david beazley (dabeaz)
###############################################################################
Python vs the Hardware
by Neil Ludban
https://bitbucket.com/nludban/python-vs-the-hardware/
private repo?
theme: python and embedded systems
several small python / freebsd / raspberry pi projects
He used Raspberry Pi 2, because that version is best supported by FreeBSD.
usb hid
i2c
gpio
part 1 usb hid
build cnc machine
free usb game controller
12 buttons
got it to work with python
https://pypi.python.org/pypi/libusb1
adafruit circuitpython
https://circuitpython.readthedocs.io
beta
micropython derivative
attaches as USB device
keyboard
mouse
midi
feather m0 express
metro m0 express
the "right way" == the "wrong way"
supposed to
- get a device driver
- find existing
- modify it
- write from scratch
- configure kernel
- one of
- write python module to interface to kernel device
- configure x11 to ignore device
or
- configure x11 to enable device
- write python module to interface to x11 device
- application now revolves around x11
1-bit message for a button click
sudo usbhidctl -f /dev/uhid1 -a -v -l
import usb1
import libusb1
part 1
freebsd.org 11.1 july 2017
- production servers
- high end embedded systems
products based on freebsd
- mac os x
- juniper routers
- netflix
- game consoles: ps3, ps4, switch
freebsd raspbian
all i/o works on RPi2, dunno about RPi3
x11 not running yet
ARM6/7 ports confusion
no official (RPi) pre-built packages
native builds are slow
#hardware build tools
usb data capture www.saleae.com
mixed signal oscilloscope www.rigolna.com
agilent 54622D mixed signal oscilloscope
- circa 2000
- 2 analog
- 16 digital
- some serial protocol support
- good parallel bus support
- excellent real-time hi-res display
- https://www.youtube.com/watch?v=aVNfFewFn_Y
#part 3 - i2c for python on freebsd
found at microcenter
pimoroni four-letter-phat $12
four 15-segment LED displays
f007347
pimoroni
- pure python driver from the vendor
https://github.com/pimoroni/fourletter-phat
- uses https://github.com/pimoroni/py-smbus
- which uses linux #include <linux/i2c-dev.h>
freebsd
- wp:SMB
- SMB is a subset of I2C
- no python i2c or smbus for freebsd
- freebsd i2c is spelled iic
man 4 iic
only did single byte reads and writes
did not need to do multi-byte reads or writes
plan #1
- python module iic.py
- class IIC implemented with
- file descriptor from os.open
- I/O using os.read, os.write
- C extension module (_bsdiic.c) for ioctl
- I2CSADDR
- dummy module (smbus.py)
- class SMBus with methods:
- open()
- set_address()
- write_i2c_block_data()
_bsdiic.c
#include <Python.h>
#includ <dev/iicbus/iic.h>
static
PyObject * foo(
PyObject *self,
PyObject *args
) {
PyArg_ParseTuple(args, "ii", &fd, &addr);
PyErr_SetFromErrno(PyExc_IOError);
Py_INCREF(Py_None);
return Py_None;
}
static PyMethodDef foo_methods[] = {
{
"set_slave_address",
foo,
METH_VARARGS,
"I2CSADDR"
}, {
0
}
};
static struct PyModuleDef foo_module =
{
PyModuleDef_HEAD_INIT,
"_bsdiic",
NULL,
-1,
foo_methods,
0
};
PyObject *
PyInit__bsdiic(void)
{
PyObject *m = PyModule_Create(&bsdiic_module);
}
smbus.py
from iic ipmort IIC
class SMBus:
setup.py
#!/usr/bin/env python3.6
from distutils.core import setup, Extension
/usr/src/sys/arm/broadcom/bcm2835/bcm2835_bsc.c
NOTE: Important detail uncovered by web searches:
this kernel driver does not implement read and write.
static device_method_t bcm_bsc_methods[] = {
...
/* iicbus interface */
DEVMETHOD(iicbus_reset, bcm_bsc_iicbus_reset),
DEVMETHOD(iicbus_callback, iicbus_null_callback),
DEVMETHOD(iicbus_transfer, bcm_bsc_transfer),
...
(what does my C code read talking to thermal sensor use?)
Plan #2
- Python class IIC
- Verify data is bytes
- remember slave_address
- C extension adds read and write functions using I2CRDWR ioctl
iic.py
_bsdiic.c
static
PyObject *
bsdiic_write(
PyObject *self,
PyObject *args
) {
PyObject *obj;
!PyArg_ParseTuple(args, "ii0", &fd, &addr, &obj))
PyBytes_Check(obj)
PyBytes_Size(obj)
PyErr_SetString(PyExc_ValueError, "hello world");
PyBytes_Size(obj);
PyBytes_AsString(obj);
PyErr_SetFromErrno(PyExc_OSError);
Py_INCREF(Py_None);
}
_bsdiic.c
had to shift I2C address one bit left
PyBytes(obj)
(uint8_t *)PyBytes_AsString(Obj)
demo has pirate theme
https://github.com/pimoroni/fourletter-phat/tree/master/examples
https://github.com/pimoroni/fourletter-phat/blob/master/examples/demo.py
https://github.com/pimoroni/fourletter-phat/blob/master/examples/countdown.py
# Part 4 GPIO for Python on FreeBSD
- found pimoroni displayotron hat at microcenter for about $25
https://shop.pimoroni.com/products/display-o-tron-hat
https://github.com/pimoroni/displayotron
- lcd driver (spi data; gpio select and reset)
- capacitive touch interface (i2c data; gpio ???)
- backlight driver (i2c data)
- already have i2c
- need gpio
- can implement (slow) SPI over GPIO
existing python packages
- https://pypi.python.org/pypi/RPi.GPIO
- Raspberry Pi and Linux specific
- memory mapped I/O device access
- mostly C code (~3k lines)
- https://github.com/gonzoua/freebsd-gpio
- not maintained, suggests following:
- https://github.com/evadot/fbsd_gpio_py
- wrapper for libgpio
- uses CFFI (C foreign Function Interface for Python)
https://cffi.readthedocs.io/
looks like write by someone trying out a bunch of new things in Python
without catching on to what one is really supposed to do
as opposed to what one can do
neil hopes he never sees CFFI again
- throws exceptions while handling exceptions
- clunky interface
pin = getattr(controller, 'pin 42')
(arduinoesque)
- properties with major side effects:
pin.name = 'foobar'
freebsd options
- search for detected devices
- kernel supplied devices
- system header files
dmesg | grep -i gpio
ls -l /dev/gpio*
/dev/gpioc0
man 8 gpioctl ;# note wrong section
sudo gpioctl -l -v
man 3 gpio
came out of netbsd originally?
more questions
- sysctl or libgpio?
- sysctl is low level
- libgpio requires gpio_handle_t
- result of setting conflicting flags?
- last takes precedence?
- error?
- after more searching:
- nothing gained with libgpio
- very thin wrapper over ioctl()s
- kernel device driver has implicit priorities
additional cpio knobs supported through sysctl(8)
/usr/src/lib/libgpio/gpio.c
/usr/src/sys/arm/broadcom/bcm2835/bcm2835_gpio.c
sysctl -a | grep -i gpio
came from intel
so he wrote is own interface
a gpio example
NOTE: The biggest requirement is a clean Pythonic API.
Test first by writing a small but complete application.
(Everything should be built top-down, except the first time.)
("Facade" design pattern.)
import gpio
c = gpio.open('/dev/gpioc0')
for pin in c.list_pins():
print(pin)
bcm2 = c.get_pin(number=2) # (i2c SDA / header pin 3)
bcm3 - c.get_pin(name='pin 3') # (i2c SCL / header pin 5)
# bug in following?
# should write initial value before configuring for output?
with bcm2.configure() as pin_config:
pin_config.output = True
pin_config.initial = 0 # Why not 1?
with bcm3.configure() as pin_config:
pin_config.output = True
pin_config.initial = 0 # Why not 1?
while True:
bcm2.set(1) # 1,0
bcm3.set(1) # 1,1
bcm2.set(0) # 1,0
bcm3.set(0) # 0,0
# above loop ran at 10.7 kHz (four pin.set() calls)
# neil did not notice jitter
but first, some refactoring
NOTE: Created a top-level "pybsd" package to contain iic and gpio
sub-projects. To avoid changing application code smbus and RPi stay
at the top level
pybsd/gpio/__init__.py
NOTE: The package __init__ exports the public interfaces.
pybsd/gpio/constants.py
NOTE: Copying constant definitions from C to a more convenient
namespace. XXX import _bsdgpio.constants as ?
pybsd/gpio/controller.py
NOTE: The GPIO Controller manages the file descriptor and
provides access to individual pins.
pybsd/gpio/pin.py
NOTE: The Pin class is supported by Flags, Capabilities,
and Configuration.
NOTES: Configuration extends the read-only Flags properties with
setter properties.
class Flags
@property
class Capabilities(Flags)
class Configuration(Flags)
@Flags.input.setter
def input(self, yes_no)
class Pin
_bsdgpio.c
NOTE: Mostly boilerplate; abridged to highlight new techniques.
#include <Python.h>
#include <sys/gpio.h>
static
PyObject *
bsdgpio_get_pin_config(
PyObject *self,
PyObject *args
) {
PyArg_ParseTuple(args, "ii", &fd, &pin_number)
PyErr_SetFromErrno(PyExc_IOError)
PyObject *t = PyTuple_New(3);
PyTuple_SetItem(t, 0, PyBytes_FromString(gp.gp_name));
PyTuple_SetItem(t, 1, PyLong_FromLong(gp.gp_caps));
PyTuple_SetItem(t, 2, PyLong_FromLong(gp.gp_flags));
}
PyObject *rv = (gr.gp_value ? Py_True : Py_False);
Py_INCREF(rv);
return rv;
PyObject *
PyInit__bsdgpio(void)
{
PyObject *m = PyModule_Create(&bsdgpio_module);
PyModule_AddIntConstant(m, "PIN_INPUT", GPIO_PIN_INPUT);
PyModule_AddIntConstant(m, "PIN_OUTPUT", GPIO_PIN_OUTPUT);
PyModule_AddIntConstant(m, "PIN_OPEN_DRAIN", GPIO_PIN_OPEN_DRAIN);
PyModule_AddIntConstant(m, "PIN_PUSHPULL", GPIO_PIN_PUSHPULL);
PyModule_AddIntConstant(m, "PIN_TRISTATE", GPIO_PIN_TRISTATE);
}
More information about the CentralOH
mailing list