Initial commit.
This commit is contained in:
parent
159fb07701
commit
a14ec51fde
7 changed files with 726 additions and 2 deletions
0
Adafruit_PureIO/__init__.py
Normal file
0
Adafruit_PureIO/__init__.py
Normal file
295
Adafruit_PureIO/smbus.py
Normal file
295
Adafruit_PureIO/smbus.py
Normal file
|
|
@ -0,0 +1,295 @@
|
||||||
|
# Copyright (c) 2016 Adafruit Industries
|
||||||
|
# Author: Tony DiCola
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
|
# in the Software without restriction, including without limitation the rights
|
||||||
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
# copies of the Software, and to permit persons to whom the Software is
|
||||||
|
# furnished to do so, subject to the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice shall be included in
|
||||||
|
# all copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
# THE SOFTWARE.
|
||||||
|
from ctypes import *
|
||||||
|
from fcntl import ioctl
|
||||||
|
import struct
|
||||||
|
|
||||||
|
# I2C C API constants (from linux kernel headers)
|
||||||
|
I2C_M_TEN = 0x0010 # this is a ten bit chip address
|
||||||
|
I2C_M_RD = 0x0001 # read data, from slave to master
|
||||||
|
I2C_M_STOP = 0x8000 # if I2C_FUNC_PROTOCOL_MANGLING
|
||||||
|
I2C_M_NOSTART = 0x4000 # if I2C_FUNC_NOSTART
|
||||||
|
I2C_M_REV_DIR_ADDR = 0x2000 # if I2C_FUNC_PROTOCOL_MANGLING
|
||||||
|
I2C_M_IGNORE_NAK = 0x1000 # if I2C_FUNC_PROTOCOL_MANGLING
|
||||||
|
I2C_M_NO_RD_ACK = 0x0800 # if I2C_FUNC_PROTOCOL_MANGLING
|
||||||
|
I2C_M_RECV_LEN = 0x0400 # length will be first received byte
|
||||||
|
|
||||||
|
I2C_SLAVE = 0x0703 # Use this slave address
|
||||||
|
I2C_SLAVE_FORCE = 0x0706 # Use this slave address, even if
|
||||||
|
# is already in use by a driver!
|
||||||
|
I2C_TENBIT = 0x0704 # 0 for 7 bit addrs, != 0 for 10 bit
|
||||||
|
I2C_FUNCS = 0x0705 # Get the adapter functionality mask
|
||||||
|
I2C_RDWR = 0x0707 # Combined R/W transfer (one STOP only)
|
||||||
|
I2C_PEC = 0x0708 # != 0 to use PEC with SMBus
|
||||||
|
I2C_SMBUS = 0x0720 # SMBus transfer
|
||||||
|
|
||||||
|
|
||||||
|
# ctypes versions of I2C structs defined by kernel.
|
||||||
|
class i2c_msg(Structure):
|
||||||
|
_fields_ = [
|
||||||
|
('addr', c_uint16),
|
||||||
|
('flags', c_uint16),
|
||||||
|
('len', c_uint16),
|
||||||
|
('buf', POINTER(c_uint8))
|
||||||
|
]
|
||||||
|
|
||||||
|
class i2c_rdwr_ioctl_data(Structure):
|
||||||
|
_fields_ = [
|
||||||
|
('msgs', POINTER(i2c_msg)),
|
||||||
|
('nmsgs', c_uint32)
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def make_i2c_rdwr_data(messages):
|
||||||
|
"""Utility function to create and return an i2c_rdwr_ioctl_data structure
|
||||||
|
populated with a list of specified I2C messages. The messages parameter
|
||||||
|
should be a list of tuples which represent the individual I2C messages to
|
||||||
|
send in this transaction. Tuples should contain 4 elements: address value,
|
||||||
|
flags value, buffer length, ctypes c_uint8 pointer to buffer.
|
||||||
|
"""
|
||||||
|
# Create message array and populate with provided data.
|
||||||
|
msg_data_type = i2c_msg*len(messages)
|
||||||
|
msg_data = msg_data_type()
|
||||||
|
for i, m in enumerate(messages):
|
||||||
|
msg_data[i].addr = m[0] & 0x7F
|
||||||
|
msg_data[i].flags = m[1]
|
||||||
|
msg_data[i].len = m[2]
|
||||||
|
msg_data[i].buf = m[3]
|
||||||
|
# Now build the data structure.
|
||||||
|
data = i2c_rdwr_ioctl_data()
|
||||||
|
data.msgs = msg_data
|
||||||
|
data.nmsgs = len(messages)
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
# Create an interface that mimics the Python SMBus API.
|
||||||
|
class SMBus(object):
|
||||||
|
"""I2C interface that mimics the Python SMBus API but is implemented with
|
||||||
|
pure Python calls to ioctl and direct /dev/i2c device access.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, bus=None):
|
||||||
|
"""Create a new smbus instance. Bus is an optional parameter that
|
||||||
|
specifies the I2C bus number to use, for example 1 would use device
|
||||||
|
/dev/i2c-1. If bus is not specified then the open function should be
|
||||||
|
called to open the bus.
|
||||||
|
"""
|
||||||
|
self._device = None
|
||||||
|
if bus is not None:
|
||||||
|
self.open(bus)
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
"""Clean up any resources used by the SMBus instance."""
|
||||||
|
self.close()
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
"""Context manager enter function."""
|
||||||
|
# Just return this object so it can be used in a with statement, like
|
||||||
|
# with SMBus(1) as bus:
|
||||||
|
# # do stuff!
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||||
|
"""Context manager exit function, ensures resources are cleaned up."""
|
||||||
|
self.close()
|
||||||
|
return False # Don't suppress exceptions.
|
||||||
|
|
||||||
|
def open(self, bus):
|
||||||
|
"""Open the smbus interface on the specified bus."""
|
||||||
|
# Close the device if it's already open.
|
||||||
|
if self._device is not None:
|
||||||
|
self.close()
|
||||||
|
# Try to open the file for the specified bus. Must turn off buffering
|
||||||
|
# or else Python 3 fails (see: https://bugs.python.org/issue20074)
|
||||||
|
self._device = open('/dev/i2c-{0}'.format(bus), 'r+b', buffering=0)
|
||||||
|
# TODO: Catch IOError and throw a better error message that describes
|
||||||
|
# what's wrong (i.e. I2C may not be enabled or the bus doesn't exist).
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
"""Close the smbus connection. You cannot make any other function
|
||||||
|
calls on the bus unless open is called!"""
|
||||||
|
if self._device is not None:
|
||||||
|
self._device.close()
|
||||||
|
self._device = None
|
||||||
|
|
||||||
|
def _select_device(self, addr):
|
||||||
|
"""Set the address of the device to communicate with on the I2C bus."""
|
||||||
|
ioctl(self._device.fileno(), I2C_SLAVE, addr & 0x7F)
|
||||||
|
|
||||||
|
def read_byte(self, addr):
|
||||||
|
"""Read a single byte from the specified device."""
|
||||||
|
assert self._device is not None, 'Bus must be opened before operations are made against it!'
|
||||||
|
self._select_device(addr)
|
||||||
|
return ord(self._device.read(1))
|
||||||
|
|
||||||
|
def read_byte_data(self, addr, cmd):
|
||||||
|
"""Read a single byte from the specified cmd register of the device."""
|
||||||
|
assert self._device is not None, 'Bus must be opened before operations are made against it!'
|
||||||
|
# Build ctypes values to marshall between ioctl and Python.
|
||||||
|
reg = c_uint8(cmd)
|
||||||
|
result = c_uint8()
|
||||||
|
# Build ioctl request.
|
||||||
|
request = make_i2c_rdwr_data([
|
||||||
|
(addr, 0, 1, pointer(reg)), # Write cmd register.
|
||||||
|
(addr, I2C_M_RD, 1, pointer(result)) # Read 1 byte as result.
|
||||||
|
])
|
||||||
|
# Make ioctl call and return result data.
|
||||||
|
ioctl(self._device.fileno(), I2C_RDWR, request)
|
||||||
|
return result.value
|
||||||
|
|
||||||
|
def read_word_data(self, addr, cmd):
|
||||||
|
"""Read a word (2 bytes) from the specified cmd register of the device.
|
||||||
|
Note that this will interpret data using the endianness of the processor
|
||||||
|
running Python (typically little endian)!
|
||||||
|
"""
|
||||||
|
assert self._device is not None, 'Bus must be opened before operations are made against it!'
|
||||||
|
# Build ctypes values to marshall between ioctl and Python.
|
||||||
|
reg = c_uint8(cmd)
|
||||||
|
result = c_uint16()
|
||||||
|
# Build ioctl request.
|
||||||
|
request = make_i2c_rdwr_data([
|
||||||
|
(addr, 0, 1, pointer(reg)), # Write cmd register.
|
||||||
|
(addr, I2C_M_RD, 2, cast(pointer(result), POINTER(c_uint8))) # Read word (2 bytes).
|
||||||
|
])
|
||||||
|
# Make ioctl call and return result data.
|
||||||
|
ioctl(self._device.fileno(), I2C_RDWR, request)
|
||||||
|
return result.value
|
||||||
|
|
||||||
|
def read_block_data(self, addr, cmd):
|
||||||
|
"""Perform a block read from the specified cmd register of the device.
|
||||||
|
The amount of data read is determined by the first byte send back by
|
||||||
|
the device. Data is returned as a bytearray.
|
||||||
|
"""
|
||||||
|
# TODO: Unfortunately this will require calling the low level I2C
|
||||||
|
# access ioctl to trigger a proper read_block_data. The amount of data
|
||||||
|
# returned isn't known until the device starts responding so an I2C_RDWR
|
||||||
|
# ioctl won't work.
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def read_i2c_block_data(self, addr, cmd, len=32):
|
||||||
|
"""Perform a read from the specified cmd register of device. Len number
|
||||||
|
of bytes (default of 32) will be read and returned as a bytearray.
|
||||||
|
"""
|
||||||
|
assert self._device is not None, 'Bus must be opened before operations are made against it!'
|
||||||
|
# Build ctypes values to marshall between ioctl and Python.
|
||||||
|
reg = c_uint8(cmd)
|
||||||
|
result = create_string_buffer(len)
|
||||||
|
# Build ioctl request.
|
||||||
|
request = make_i2c_rdwr_data([
|
||||||
|
(addr, 0, 1, pointer(reg)), # Write cmd register.
|
||||||
|
(addr, I2C_M_RD, len, cast(pointer(result), POINTER(c_uint8))) # Read data.
|
||||||
|
])
|
||||||
|
# Make ioctl call and return result data.
|
||||||
|
ioctl(self._device.fileno(), I2C_RDWR, request)
|
||||||
|
return result.value
|
||||||
|
|
||||||
|
def write_quick(self, addr):
|
||||||
|
"""Write a single byte to the specified device."""
|
||||||
|
# What a strange function, from the python-smbus source this appears to
|
||||||
|
# just write a single byte that initiates a write to the specified device
|
||||||
|
# address (but writes no data!). The functionality is duplicated below
|
||||||
|
# but the actual use case for this is unknown.
|
||||||
|
assert self._device is not None, 'Bus must be opened before operations are made against it!'
|
||||||
|
# Build ioctl request.
|
||||||
|
request = make_i2c_rdwr_data([
|
||||||
|
(addr, 0, 0, None), # Write with no data.
|
||||||
|
])
|
||||||
|
# Make ioctl call and return result data.
|
||||||
|
ioctl(self._device.fileno(), I2C_RDWR, request)
|
||||||
|
|
||||||
|
def write_byte(self, addr, val):
|
||||||
|
"""Write a single byte to the specified device."""
|
||||||
|
assert self._device is not None, 'Bus must be opened before operations are made against it!'
|
||||||
|
self._select_device(addr)
|
||||||
|
data = bytearray(1)
|
||||||
|
data[0] = val & 0xFF
|
||||||
|
self._device.write(data)
|
||||||
|
|
||||||
|
def write_byte_data(self, addr, cmd, val):
|
||||||
|
"""Write a byte of data to the specified cmd register of the device.
|
||||||
|
"""
|
||||||
|
assert self._device is not None, 'Bus must be opened before operations are made against it!'
|
||||||
|
# Construct a string of data to send with the command register and byte value.
|
||||||
|
data = bytearray(2)
|
||||||
|
data[0] = cmd & 0xFF
|
||||||
|
data[1] = val & 0xFF
|
||||||
|
# Send the data to the device.
|
||||||
|
self._select_device(addr)
|
||||||
|
self._device.write(data)
|
||||||
|
|
||||||
|
def write_word_data(self, addr, cmd, val):
|
||||||
|
"""Write a word (2 bytes) of data to the specified cmd register of the
|
||||||
|
device. Note that this will write the data in the endianness of the
|
||||||
|
processor running Python (typically little endian)!
|
||||||
|
"""
|
||||||
|
assert self._device is not None, 'Bus must be opened before operations are made against it!'
|
||||||
|
# Construct a string of data to send with the command register and word value.
|
||||||
|
data = struct.pack('=BH', cmd & 0xFF, val & 0xFFFF)
|
||||||
|
# Send the data to the device.
|
||||||
|
self._select_device(addr)
|
||||||
|
self._device.write(data)
|
||||||
|
|
||||||
|
def write_block_data(self, addr, cmd, vals):
|
||||||
|
"""Write a block of data to the specified cmd register of the device.
|
||||||
|
The amount of data to write should be the first byte inside the vals
|
||||||
|
string/bytearray and that count of bytes of data to write should follow
|
||||||
|
it.
|
||||||
|
"""
|
||||||
|
# Just use the I2C block data write to write the provided values and
|
||||||
|
# their length as the first byte.
|
||||||
|
data = bytearray(len(vals)+1)
|
||||||
|
data[0] = len(vals) & 0xFF
|
||||||
|
data[1:] = vals[0:]
|
||||||
|
self.write_i2c_block_data(addr, cmd, data)
|
||||||
|
|
||||||
|
def write_i2c_block_data(self, addr, cmd, vals):
|
||||||
|
"""Write a buffer of data to the specified cmd register of the device.
|
||||||
|
"""
|
||||||
|
assert self._device is not None, 'Bus must be opened before operations are made against it!'
|
||||||
|
assert vals is not None and len(vals) > 0, 'Vals must be a buffer with data!'
|
||||||
|
# Construct a string of data to send, including room for the command register.
|
||||||
|
data = bytearray(len(vals)+1)
|
||||||
|
data[0] = cmd & 0xFF # Command register at the start.
|
||||||
|
data[1:] = vals[0:] # Copy in the block data (ugly but necessary to ensure
|
||||||
|
# the entire write happens in one transaction).
|
||||||
|
# Send the data to the device.
|
||||||
|
self._select_device(addr)
|
||||||
|
self._device.write(data)
|
||||||
|
|
||||||
|
def process_call(self, addr, cmd, val):
|
||||||
|
"""Perform a smbus process call by writing a word (2 byte) value to
|
||||||
|
the specified register of the device, and then reading a word of response
|
||||||
|
data (which is returned).
|
||||||
|
"""
|
||||||
|
assert self._device is not None, 'Bus must be opened before operations are made against it!'
|
||||||
|
# Build ctypes values to marshall between ioctl and Python.
|
||||||
|
data = create_string_buffer(struct.pack('=BH', cmd, val))
|
||||||
|
result = c_uint16()
|
||||||
|
# Build ioctl request.
|
||||||
|
request = make_i2c_rdwr_data([
|
||||||
|
(addr, 0, 3, cast(pointer(data), POINTER(c_uint8))), # Write data.
|
||||||
|
(addr, I2C_M_RD, 2, cast(pointer(result), POINTER(c_uint8))) # Read word (2 bytes).
|
||||||
|
])
|
||||||
|
# Make ioctl call and return result data.
|
||||||
|
ioctl(self._device.fileno(), I2C_RDWR, request)
|
||||||
|
# Note the python-smbus code appears to have a rather serious bug and
|
||||||
|
# does not return the result value! This is fixed below by returning it.
|
||||||
|
return result.value
|
||||||
21
LICENSE
Normal file
21
LICENSE
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2016 Adafruit Industries
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
10
README.md
10
README.md
|
|
@ -1,2 +1,8 @@
|
||||||
# Adafruit_Python_PureIO
|
# Adafruit Python PureIO
|
||||||
Pure python (i.e. no native extensions) access to Linux IO including I2C and SPI. Drop in replacement for smbus and spidev modules.
|
|
||||||
|
Pure python (i.e. no native extensions) access to Linux IO including I2C and SPI.
|
||||||
|
Drop in replacement for smbus and spidev modules.
|
||||||
|
|
||||||
|
NOTE: This is a work in progress that's not yet ready for public consumption.
|
||||||
|
API signatures could change and all APIs are not yet implemented. Wait for a
|
||||||
|
1.x.x series release before depending on this code.
|
||||||
|
|
|
||||||
332
ez_setup.py
Normal file
332
ez_setup.py
Normal file
|
|
@ -0,0 +1,332 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
"""Bootstrap setuptools installation
|
||||||
|
|
||||||
|
To use setuptools in your package's setup.py, include this
|
||||||
|
file in the same directory and add this to the top of your setup.py::
|
||||||
|
|
||||||
|
from ez_setup import use_setuptools
|
||||||
|
use_setuptools()
|
||||||
|
|
||||||
|
To require a specific version of setuptools, set a download
|
||||||
|
mirror, or use an alternate download directory, simply supply
|
||||||
|
the appropriate options to ``use_setuptools()``.
|
||||||
|
|
||||||
|
This file can also be run as a script to install or upgrade setuptools.
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import sys
|
||||||
|
import tempfile
|
||||||
|
import zipfile
|
||||||
|
import optparse
|
||||||
|
import subprocess
|
||||||
|
import platform
|
||||||
|
import textwrap
|
||||||
|
import contextlib
|
||||||
|
|
||||||
|
from distutils import log
|
||||||
|
|
||||||
|
try:
|
||||||
|
from site import USER_SITE
|
||||||
|
except ImportError:
|
||||||
|
USER_SITE = None
|
||||||
|
|
||||||
|
DEFAULT_VERSION = "3.5.1"
|
||||||
|
DEFAULT_URL = "https://pypi.python.org/packages/source/s/setuptools/"
|
||||||
|
|
||||||
|
def _python_cmd(*args):
|
||||||
|
"""
|
||||||
|
Return True if the command succeeded.
|
||||||
|
"""
|
||||||
|
args = (sys.executable,) + args
|
||||||
|
return subprocess.call(args) == 0
|
||||||
|
|
||||||
|
|
||||||
|
def _install(archive_filename, install_args=()):
|
||||||
|
with archive_context(archive_filename):
|
||||||
|
# installing
|
||||||
|
log.warn('Installing Setuptools')
|
||||||
|
if not _python_cmd('setup.py', 'install', *install_args):
|
||||||
|
log.warn('Something went wrong during the installation.')
|
||||||
|
log.warn('See the error message above.')
|
||||||
|
# exitcode will be 2
|
||||||
|
return 2
|
||||||
|
|
||||||
|
|
||||||
|
def _build_egg(egg, archive_filename, to_dir):
|
||||||
|
with archive_context(archive_filename):
|
||||||
|
# building an egg
|
||||||
|
log.warn('Building a Setuptools egg in %s', to_dir)
|
||||||
|
_python_cmd('setup.py', '-q', 'bdist_egg', '--dist-dir', to_dir)
|
||||||
|
# returning the result
|
||||||
|
log.warn(egg)
|
||||||
|
if not os.path.exists(egg):
|
||||||
|
raise IOError('Could not build the egg.')
|
||||||
|
|
||||||
|
|
||||||
|
def get_zip_class():
|
||||||
|
"""
|
||||||
|
Supplement ZipFile class to support context manager for Python 2.6
|
||||||
|
"""
|
||||||
|
class ContextualZipFile(zipfile.ZipFile):
|
||||||
|
def __enter__(self):
|
||||||
|
return self
|
||||||
|
def __exit__(self, type, value, traceback):
|
||||||
|
self.close
|
||||||
|
return zipfile.ZipFile if hasattr(zipfile.ZipFile, '__exit__') else \
|
||||||
|
ContextualZipFile
|
||||||
|
|
||||||
|
|
||||||
|
@contextlib.contextmanager
|
||||||
|
def archive_context(filename):
|
||||||
|
# extracting the archive
|
||||||
|
tmpdir = tempfile.mkdtemp()
|
||||||
|
log.warn('Extracting in %s', tmpdir)
|
||||||
|
old_wd = os.getcwd()
|
||||||
|
try:
|
||||||
|
os.chdir(tmpdir)
|
||||||
|
with get_zip_class()(filename) as archive:
|
||||||
|
archive.extractall()
|
||||||
|
|
||||||
|
# going in the directory
|
||||||
|
subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0])
|
||||||
|
os.chdir(subdir)
|
||||||
|
log.warn('Now working in %s', subdir)
|
||||||
|
yield
|
||||||
|
|
||||||
|
finally:
|
||||||
|
os.chdir(old_wd)
|
||||||
|
shutil.rmtree(tmpdir)
|
||||||
|
|
||||||
|
|
||||||
|
def _do_download(version, download_base, to_dir, download_delay):
|
||||||
|
egg = os.path.join(to_dir, 'setuptools-%s-py%d.%d.egg'
|
||||||
|
% (version, sys.version_info[0], sys.version_info[1]))
|
||||||
|
if not os.path.exists(egg):
|
||||||
|
archive = download_setuptools(version, download_base,
|
||||||
|
to_dir, download_delay)
|
||||||
|
_build_egg(egg, archive, to_dir)
|
||||||
|
sys.path.insert(0, egg)
|
||||||
|
|
||||||
|
# Remove previously-imported pkg_resources if present (see
|
||||||
|
# https://bitbucket.org/pypa/setuptools/pull-request/7/ for details).
|
||||||
|
if 'pkg_resources' in sys.modules:
|
||||||
|
del sys.modules['pkg_resources']
|
||||||
|
|
||||||
|
import setuptools
|
||||||
|
setuptools.bootstrap_install_from = egg
|
||||||
|
|
||||||
|
|
||||||
|
def use_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL,
|
||||||
|
to_dir=os.curdir, download_delay=15):
|
||||||
|
to_dir = os.path.abspath(to_dir)
|
||||||
|
rep_modules = 'pkg_resources', 'setuptools'
|
||||||
|
imported = set(sys.modules).intersection(rep_modules)
|
||||||
|
try:
|
||||||
|
import pkg_resources
|
||||||
|
except ImportError:
|
||||||
|
return _do_download(version, download_base, to_dir, download_delay)
|
||||||
|
try:
|
||||||
|
pkg_resources.require("setuptools>=" + version)
|
||||||
|
return
|
||||||
|
except pkg_resources.DistributionNotFound:
|
||||||
|
return _do_download(version, download_base, to_dir, download_delay)
|
||||||
|
except pkg_resources.VersionConflict as VC_err:
|
||||||
|
if imported:
|
||||||
|
msg = textwrap.dedent("""
|
||||||
|
The required version of setuptools (>={version}) is not available,
|
||||||
|
and can't be installed while this script is running. Please
|
||||||
|
install a more recent version first, using
|
||||||
|
'easy_install -U setuptools'.
|
||||||
|
|
||||||
|
(Currently using {VC_err.args[0]!r})
|
||||||
|
""").format(VC_err=VC_err, version=version)
|
||||||
|
sys.stderr.write(msg)
|
||||||
|
sys.exit(2)
|
||||||
|
|
||||||
|
# otherwise, reload ok
|
||||||
|
del pkg_resources, sys.modules['pkg_resources']
|
||||||
|
return _do_download(version, download_base, to_dir, download_delay)
|
||||||
|
|
||||||
|
def _clean_check(cmd, target):
|
||||||
|
"""
|
||||||
|
Run the command to download target. If the command fails, clean up before
|
||||||
|
re-raising the error.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
subprocess.check_call(cmd)
|
||||||
|
except subprocess.CalledProcessError:
|
||||||
|
if os.access(target, os.F_OK):
|
||||||
|
os.unlink(target)
|
||||||
|
raise
|
||||||
|
|
||||||
|
def download_file_powershell(url, target):
|
||||||
|
"""
|
||||||
|
Download the file at url to target using Powershell (which will validate
|
||||||
|
trust). Raise an exception if the command cannot complete.
|
||||||
|
"""
|
||||||
|
target = os.path.abspath(target)
|
||||||
|
cmd = [
|
||||||
|
'powershell',
|
||||||
|
'-Command',
|
||||||
|
"(new-object System.Net.WebClient).DownloadFile(%(url)r, %(target)r)" % vars(),
|
||||||
|
]
|
||||||
|
_clean_check(cmd, target)
|
||||||
|
|
||||||
|
def has_powershell():
|
||||||
|
if platform.system() != 'Windows':
|
||||||
|
return False
|
||||||
|
cmd = ['powershell', '-Command', 'echo test']
|
||||||
|
devnull = open(os.path.devnull, 'wb')
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
subprocess.check_call(cmd, stdout=devnull, stderr=devnull)
|
||||||
|
except Exception:
|
||||||
|
return False
|
||||||
|
finally:
|
||||||
|
devnull.close()
|
||||||
|
return True
|
||||||
|
|
||||||
|
download_file_powershell.viable = has_powershell
|
||||||
|
|
||||||
|
def download_file_curl(url, target):
|
||||||
|
cmd = ['curl', url, '--silent', '--output', target]
|
||||||
|
_clean_check(cmd, target)
|
||||||
|
|
||||||
|
def has_curl():
|
||||||
|
cmd = ['curl', '--version']
|
||||||
|
devnull = open(os.path.devnull, 'wb')
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
subprocess.check_call(cmd, stdout=devnull, stderr=devnull)
|
||||||
|
except Exception:
|
||||||
|
return False
|
||||||
|
finally:
|
||||||
|
devnull.close()
|
||||||
|
return True
|
||||||
|
|
||||||
|
download_file_curl.viable = has_curl
|
||||||
|
|
||||||
|
def download_file_wget(url, target):
|
||||||
|
cmd = ['wget', url, '--quiet', '--output-document', target]
|
||||||
|
_clean_check(cmd, target)
|
||||||
|
|
||||||
|
def has_wget():
|
||||||
|
cmd = ['wget', '--version']
|
||||||
|
devnull = open(os.path.devnull, 'wb')
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
subprocess.check_call(cmd, stdout=devnull, stderr=devnull)
|
||||||
|
except Exception:
|
||||||
|
return False
|
||||||
|
finally:
|
||||||
|
devnull.close()
|
||||||
|
return True
|
||||||
|
|
||||||
|
download_file_wget.viable = has_wget
|
||||||
|
|
||||||
|
def download_file_insecure(url, target):
|
||||||
|
"""
|
||||||
|
Use Python to download the file, even though it cannot authenticate the
|
||||||
|
connection.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
from urllib.request import urlopen
|
||||||
|
except ImportError:
|
||||||
|
from urllib2 import urlopen
|
||||||
|
src = dst = None
|
||||||
|
try:
|
||||||
|
src = urlopen(url)
|
||||||
|
# Read/write all in one block, so we don't create a corrupt file
|
||||||
|
# if the download is interrupted.
|
||||||
|
data = src.read()
|
||||||
|
dst = open(target, "wb")
|
||||||
|
dst.write(data)
|
||||||
|
finally:
|
||||||
|
if src:
|
||||||
|
src.close()
|
||||||
|
if dst:
|
||||||
|
dst.close()
|
||||||
|
|
||||||
|
download_file_insecure.viable = lambda: True
|
||||||
|
|
||||||
|
def get_best_downloader():
|
||||||
|
downloaders = [
|
||||||
|
download_file_powershell,
|
||||||
|
download_file_curl,
|
||||||
|
download_file_wget,
|
||||||
|
download_file_insecure,
|
||||||
|
]
|
||||||
|
|
||||||
|
for dl in downloaders:
|
||||||
|
if dl.viable():
|
||||||
|
return dl
|
||||||
|
|
||||||
|
def download_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL,
|
||||||
|
to_dir=os.curdir, delay=15, downloader_factory=get_best_downloader):
|
||||||
|
"""
|
||||||
|
Download setuptools from a specified location and return its filename
|
||||||
|
|
||||||
|
`version` should be a valid setuptools version number that is available
|
||||||
|
as an egg for download under the `download_base` URL (which should end
|
||||||
|
with a '/'). `to_dir` is the directory where the egg will be downloaded.
|
||||||
|
`delay` is the number of seconds to pause before an actual download
|
||||||
|
attempt.
|
||||||
|
|
||||||
|
``downloader_factory`` should be a function taking no arguments and
|
||||||
|
returning a function for downloading a URL to a target.
|
||||||
|
"""
|
||||||
|
# making sure we use the absolute path
|
||||||
|
to_dir = os.path.abspath(to_dir)
|
||||||
|
zip_name = "setuptools-%s.zip" % version
|
||||||
|
url = download_base + zip_name
|
||||||
|
saveto = os.path.join(to_dir, zip_name)
|
||||||
|
if not os.path.exists(saveto): # Avoid repeated downloads
|
||||||
|
log.warn("Downloading %s", url)
|
||||||
|
downloader = downloader_factory()
|
||||||
|
downloader(url, saveto)
|
||||||
|
return os.path.realpath(saveto)
|
||||||
|
|
||||||
|
def _build_install_args(options):
|
||||||
|
"""
|
||||||
|
Build the arguments to 'python setup.py install' on the setuptools package
|
||||||
|
"""
|
||||||
|
return ['--user'] if options.user_install else []
|
||||||
|
|
||||||
|
def _parse_args():
|
||||||
|
"""
|
||||||
|
Parse the command line for options
|
||||||
|
"""
|
||||||
|
parser = optparse.OptionParser()
|
||||||
|
parser.add_option(
|
||||||
|
'--user', dest='user_install', action='store_true', default=False,
|
||||||
|
help='install in user site package (requires Python 2.6 or later)')
|
||||||
|
parser.add_option(
|
||||||
|
'--download-base', dest='download_base', metavar="URL",
|
||||||
|
default=DEFAULT_URL,
|
||||||
|
help='alternative URL from where to download the setuptools package')
|
||||||
|
parser.add_option(
|
||||||
|
'--insecure', dest='downloader_factory', action='store_const',
|
||||||
|
const=lambda: download_file_insecure, default=get_best_downloader,
|
||||||
|
help='Use internal, non-validating downloader'
|
||||||
|
)
|
||||||
|
parser.add_option(
|
||||||
|
'--version', help="Specify which version to download",
|
||||||
|
default=DEFAULT_VERSION,
|
||||||
|
)
|
||||||
|
options, args = parser.parse_args()
|
||||||
|
# positional arguments are ignored
|
||||||
|
return options
|
||||||
|
|
||||||
|
def main():
|
||||||
|
"""Install or upgrade setuptools and EasyInstall"""
|
||||||
|
options = _parse_args()
|
||||||
|
archive = download_setuptools(
|
||||||
|
version=options.version,
|
||||||
|
download_base=options.download_base,
|
||||||
|
downloader_factory=options.downloader_factory,
|
||||||
|
)
|
||||||
|
return _install(archive, _build_install_args(options))
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
sys.exit(main())
|
||||||
21
setup.py
Normal file
21
setup.py
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
from ez_setup import use_setuptools
|
||||||
|
use_setuptools()
|
||||||
|
from setuptools import setup, find_packages
|
||||||
|
|
||||||
|
classifiers = ['Development Status :: 4 - Beta',
|
||||||
|
'Operating System :: POSIX :: Linux',
|
||||||
|
'License :: OSI Approved :: MIT License',
|
||||||
|
'Intended Audience :: Developers',
|
||||||
|
'Programming Language :: Python :: 2.7',
|
||||||
|
'Programming Language :: Python :: 3',
|
||||||
|
'Topic :: Software Development',
|
||||||
|
'Topic :: System :: Hardware']
|
||||||
|
|
||||||
|
setup(name = 'Adafruit_PureIO',
|
||||||
|
version = '0.0.1',
|
||||||
|
author = 'Tony DiCola',
|
||||||
|
author_email = 'tdicola@adafruit.com',
|
||||||
|
description = 'Pure python (i.e. no native extensions) access to Linux IO including I2C and SPI. Drop in replacement for smbus and spidev modules.',
|
||||||
|
license = 'MIT',
|
||||||
|
classifiers = classifiers,
|
||||||
|
packages = find_packages())
|
||||||
49
tests/test_I2C.py
Normal file
49
tests/test_I2C.py
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
# Basic smbus test. This is pretty ugly and meant to be run against a ADS1x15
|
||||||
|
# and some output inspected by a Saleae logic analyzer. TODO: Refactor into
|
||||||
|
# something that can test without hardware?
|
||||||
|
import binascii
|
||||||
|
|
||||||
|
import Adafruit_PureIO.smbus as smbus
|
||||||
|
|
||||||
|
|
||||||
|
DEVICE_ADDR = 0x48
|
||||||
|
REGISTER = 0x01
|
||||||
|
|
||||||
|
|
||||||
|
# Test open and close.
|
||||||
|
i2c = smbus.SMBus()
|
||||||
|
i2c.open(1)
|
||||||
|
val = i2c.read_byte(DEVICE_ADDR)
|
||||||
|
print('read_byte from 0x{0:0X}: 0x{1:0X}'.format(REGISTER, val))
|
||||||
|
i2c.close()
|
||||||
|
|
||||||
|
# Test initializer open.
|
||||||
|
i2c = smbus.SMBus(1)
|
||||||
|
val = i2c.read_byte(DEVICE_ADDR)
|
||||||
|
print('read_byte from 0x{0:0X}: 0x{1:0X}'.format(REGISTER, val))
|
||||||
|
i2c.close()
|
||||||
|
|
||||||
|
# Test various data reads.
|
||||||
|
with smbus.SMBus(1) as i2c:
|
||||||
|
val = i2c.read_byte(DEVICE_ADDR)
|
||||||
|
print('read_byte from 0x{0:0X}: 0x{1:0X}'.format(REGISTER, val))
|
||||||
|
val = i2c.read_byte_data(DEVICE_ADDR, REGISTER)
|
||||||
|
print('read_byte_data from 0x{0:0X}: 0x{1:0X}'.format(REGISTER, val))
|
||||||
|
val = i2c.read_word_data(DEVICE_ADDR, REGISTER)
|
||||||
|
print('read_word_data from 0x{0:0X}: 0x{1:04X}'.format(REGISTER, val))
|
||||||
|
val = i2c.read_i2c_block_data(DEVICE_ADDR, REGISTER, 2)
|
||||||
|
print('read_i2c_block_data from 0x{0:0X}: 0x{1}'.format(REGISTER, binascii.hexlify(val)))
|
||||||
|
|
||||||
|
# Test various data writes.
|
||||||
|
with smbus.SMBus(1) as i2c:
|
||||||
|
i2c.write_byte(DEVICE_ADDR, REGISTER)
|
||||||
|
i2c.write_byte_data(DEVICE_ADDR, REGISTER, 0x85)
|
||||||
|
i2c.write_word_data(DEVICE_ADDR, REGISTER, 0x8385)
|
||||||
|
i2c.write_i2c_block_data(DEVICE_ADDR, REGISTER, [0x85, 0x83])
|
||||||
|
#i2c.write_block_data(DEVICE_ADDR, REGISTER, [0x85, 0x83])
|
||||||
|
i2c.write_quick(DEVICE_ADDR)
|
||||||
|
|
||||||
|
# Process call test.
|
||||||
|
with smbus.SMBus(1) as i2c:
|
||||||
|
val = i2c.process_call(DEVICE_ADDR, REGISTER, 0x8385)
|
||||||
|
print('process_call from 0x{0:0X}: 0x{1:04X}'.format(REGISTER, val))
|
||||||
Loading…
Reference in a new issue