mirror of
https://github.com/xtacocorex/CHIP_IO
synced 2025-07-19 12:23:22 +00:00
Initial commit, working GPIO for all available CHIP GPIO pins, have not tested edge detection and callbacks
This commit is contained in:
7
CHANGELOG.rst
Normal file
7
CHANGELOG.rst
Normal file
@ -0,0 +1,7 @@
|
||||
0.0.4
|
||||
____
|
||||
* Initial Commit
|
||||
* GPIO working - untested callback and edge detection
|
||||
* Initial GPIO unit tests
|
||||
|
||||
|
0
CHIP_IO/__init__.py
Normal file
0
CHIP_IO/__init__.py
Normal file
19
Makefile
Normal file
19
Makefile
Normal file
@ -0,0 +1,19 @@
|
||||
time:
|
||||
/usr/bin/ntpdate -b -s -u pool.ntp.org
|
||||
|
||||
publish: clean
|
||||
python setup.py sdist upload
|
||||
|
||||
clean:
|
||||
rm -rf CHIP_IO.* build dist
|
||||
rm -f *.pyo
|
||||
rm -f *.egg
|
||||
rm -f overlays/*.pyo overlays/*.pyc
|
||||
tests:
|
||||
py.test
|
||||
|
||||
build:
|
||||
python setup.py build --force
|
||||
|
||||
install: build
|
||||
python setup.py install --force
|
94
README.rst
Normal file
94
README.rst
Normal file
@ -0,0 +1,94 @@
|
||||
# CHIP_IO
|
||||
A CHIP GPIO library
|
||||
|
||||
Manual::
|
||||
|
||||
sudo ntpdate pool.ntp.org
|
||||
sudo apt-get update
|
||||
sudo apt-get install build-essential python-dev python-pip -y
|
||||
git clone git://github.com/xtacocorex/CHIP_IO.git
|
||||
cd CHIP_IO
|
||||
sudo python setup.py install
|
||||
cd ..
|
||||
sudo rm -rf CHIP_IO
|
||||
|
||||
**Usage**
|
||||
|
||||
Using the library is very similar to the excellent RPi.GPIO library used on the Raspberry Pi. Below are some examples.
|
||||
|
||||
**GPIO Setup**
|
||||
|
||||
Import the library, and setup as GPIO.OUT or GPIO.IN::
|
||||
|
||||
import CHIP_IO.GPIO as GPIO
|
||||
GPIO.setup("CSID0", GPIO.OUT)
|
||||
|
||||
You can also refer to the pin number::
|
||||
|
||||
GPIO.setup("U14_31", GPIO.OUT)
|
||||
|
||||
**GPIO Output**
|
||||
|
||||
Setup the pin for output, and write GPIO.HIGH or GPIO.LOW. Or you can use 1 or 0.::
|
||||
|
||||
import CHIP_IO.GPIO as GPIO
|
||||
GPIO.setup("CSID0", GPIO.OUT)
|
||||
GPIO.output("CSID0", GPIO.HIGH)
|
||||
|
||||
**GPIO Input**
|
||||
|
||||
Inputs work similarly to outputs.::
|
||||
|
||||
import CHIP_IO.GPIO as GPIO
|
||||
GPIO.setup("CSID0", GPIO.IN)
|
||||
|
||||
Polling inputs::
|
||||
|
||||
if GPIO.input("CSID0"):
|
||||
print("HIGH")
|
||||
else:
|
||||
print("LOW")
|
||||
|
||||
Waiting for an edge (GPIO.RISING, GPIO.FALLING, or GPIO.BOTH::
|
||||
|
||||
GPIO.wait_for_edge(channel, GPIO.RISING)
|
||||
|
||||
Detecting events::
|
||||
|
||||
GPIO.add_event_detect("CSID0", GPIO.FALLING)
|
||||
#your amazing code here
|
||||
#detect wherever:
|
||||
if GPIO.event_detected("CSID0"):
|
||||
print "event detected!"
|
||||
|
||||
**PWM**::
|
||||
|
||||
Not implemented yet
|
||||
|
||||
**ADC**::
|
||||
|
||||
Not Implemented yet
|
||||
|
||||
**Running tests**
|
||||
|
||||
Install py.test to run the tests. You'll also need the python compiler package for py.test.::
|
||||
|
||||
opkg update && opkg install python-compiler
|
||||
#Either pip or easy_install
|
||||
pip install -U pytest
|
||||
easy_install -U pytest
|
||||
|
||||
Execute the following in the root of the project::
|
||||
|
||||
py.test
|
||||
|
||||
**Credits**
|
||||
|
||||
The CHIP IO Python library was originally forked from the Adafruit Beaglebone IO Python Library.
|
||||
The BeagleBone IO Python library was originally forked from the excellent MIT Licensed [RPi.GPIO](https://code.google.com/p/raspberry-gpio-python) library written by Ben Croston.
|
||||
|
||||
**License**
|
||||
|
||||
CHIP IO port by Robert Wolterman, released under the MIT License.
|
||||
Beaglebone IO Library Written by Justin Cooper, Adafruit Industries. BeagleBone IO Python library is released under the MIT License.
|
||||
|
556
distribute_setup.py
Normal file
556
distribute_setup.py
Normal file
@ -0,0 +1,556 @@
|
||||
#!python
|
||||
"""Bootstrap distribute installation
|
||||
|
||||
If you want to use setuptools in your package's setup.py, just include this
|
||||
file in the same directory with it, and add this to the top of your setup.py::
|
||||
|
||||
from distribute_setup import use_setuptools
|
||||
use_setuptools()
|
||||
|
||||
If you want to require a specific version of setuptools, set a download
|
||||
mirror, or use an alternate download directory, you can do so by supplying
|
||||
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 time
|
||||
import fnmatch
|
||||
import tempfile
|
||||
import tarfile
|
||||
import optparse
|
||||
|
||||
from distutils import log
|
||||
|
||||
try:
|
||||
from site import USER_SITE
|
||||
except ImportError:
|
||||
USER_SITE = None
|
||||
|
||||
try:
|
||||
import subprocess
|
||||
|
||||
def _python_cmd(*args):
|
||||
args = (sys.executable,) + args
|
||||
return subprocess.call(args) == 0
|
||||
|
||||
except ImportError:
|
||||
# will be used for python 2.3
|
||||
def _python_cmd(*args):
|
||||
args = (sys.executable,) + args
|
||||
# quoting arguments if windows
|
||||
if sys.platform == 'win32':
|
||||
def quote(arg):
|
||||
if ' ' in arg:
|
||||
return '"%s"' % arg
|
||||
return arg
|
||||
args = [quote(arg) for arg in args]
|
||||
return os.spawnl(os.P_WAIT, sys.executable, *args) == 0
|
||||
|
||||
DEFAULT_VERSION = "0.0.1"
|
||||
DEFAULT_URL = "http://pypi.python.org/packages/source/d/distribute/"
|
||||
SETUPTOOLS_FAKED_VERSION = "0.6c11"
|
||||
|
||||
SETUPTOOLS_PKG_INFO = """\
|
||||
Metadata-Version: 1.0
|
||||
Name: setuptools
|
||||
Version: %s
|
||||
Summary: xxxx
|
||||
Home-page: xxx
|
||||
Author: xxx
|
||||
Author-email: xxx
|
||||
License: xxx
|
||||
Description: xxx
|
||||
""" % SETUPTOOLS_FAKED_VERSION
|
||||
|
||||
|
||||
def _install(tarball, install_args=()):
|
||||
# extracting the tarball
|
||||
tmpdir = tempfile.mkdtemp()
|
||||
log.warn('Extracting in %s', tmpdir)
|
||||
old_wd = os.getcwd()
|
||||
try:
|
||||
os.chdir(tmpdir)
|
||||
tar = tarfile.open(tarball)
|
||||
_extractall(tar)
|
||||
tar.close()
|
||||
|
||||
# going in the directory
|
||||
subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0])
|
||||
os.chdir(subdir)
|
||||
log.warn('Now working in %s', subdir)
|
||||
|
||||
# installing
|
||||
log.warn('Installing Distribute')
|
||||
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
|
||||
finally:
|
||||
os.chdir(old_wd)
|
||||
shutil.rmtree(tmpdir)
|
||||
|
||||
|
||||
def _build_egg(egg, tarball, to_dir):
|
||||
# extracting the tarball
|
||||
tmpdir = tempfile.mkdtemp()
|
||||
log.warn('Extracting in %s', tmpdir)
|
||||
old_wd = os.getcwd()
|
||||
try:
|
||||
os.chdir(tmpdir)
|
||||
tar = tarfile.open(tarball)
|
||||
_extractall(tar)
|
||||
tar.close()
|
||||
|
||||
# going in the directory
|
||||
subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0])
|
||||
os.chdir(subdir)
|
||||
log.warn('Now working in %s', subdir)
|
||||
|
||||
# building an egg
|
||||
log.warn('Building a Distribute egg in %s', to_dir)
|
||||
_python_cmd('setup.py', '-q', 'bdist_egg', '--dist-dir', to_dir)
|
||||
|
||||
finally:
|
||||
os.chdir(old_wd)
|
||||
shutil.rmtree(tmpdir)
|
||||
# returning the result
|
||||
log.warn(egg)
|
||||
if not os.path.exists(egg):
|
||||
raise IOError('Could not build the egg.')
|
||||
|
||||
|
||||
def _do_download(version, download_base, to_dir, download_delay):
|
||||
egg = os.path.join(to_dir, 'distribute-%s-py%d.%d.egg'
|
||||
% (version, sys.version_info[0], sys.version_info[1]))
|
||||
if not os.path.exists(egg):
|
||||
tarball = download_setuptools(version, download_base,
|
||||
to_dir, download_delay)
|
||||
_build_egg(egg, tarball, to_dir)
|
||||
sys.path.insert(0, egg)
|
||||
import setuptools
|
||||
setuptools.bootstrap_install_from = egg
|
||||
|
||||
|
||||
def use_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL,
|
||||
to_dir=os.curdir, download_delay=15, no_fake=True):
|
||||
# making sure we use the absolute path
|
||||
to_dir = os.path.abspath(to_dir)
|
||||
was_imported = 'pkg_resources' in sys.modules or \
|
||||
'setuptools' in sys.modules
|
||||
try:
|
||||
try:
|
||||
import pkg_resources
|
||||
|
||||
# Setuptools 0.7b and later is a suitable (and preferable)
|
||||
# substitute for any Distribute version.
|
||||
try:
|
||||
pkg_resources.require("setuptools>=0.7b")
|
||||
return
|
||||
except (pkg_resources.DistributionNotFound,
|
||||
pkg_resources.VersionConflict):
|
||||
pass
|
||||
|
||||
if not hasattr(pkg_resources, '_distribute'):
|
||||
if not no_fake:
|
||||
_fake_setuptools()
|
||||
raise ImportError
|
||||
except ImportError:
|
||||
return _do_download(version, download_base, to_dir, download_delay)
|
||||
try:
|
||||
pkg_resources.require("distribute>=" + version)
|
||||
return
|
||||
except pkg_resources.VersionConflict:
|
||||
e = sys.exc_info()[1]
|
||||
if was_imported:
|
||||
sys.stderr.write(
|
||||
"The required version of distribute (>=%s) is not available,\n"
|
||||
"and can't be installed while this script is running. Please\n"
|
||||
"install a more recent version first, using\n"
|
||||
"'easy_install -U distribute'."
|
||||
"\n\n(Currently using %r)\n" % (version, e.args[0]))
|
||||
sys.exit(2)
|
||||
else:
|
||||
del pkg_resources, sys.modules['pkg_resources'] # reload ok
|
||||
return _do_download(version, download_base, to_dir,
|
||||
download_delay)
|
||||
except pkg_resources.DistributionNotFound:
|
||||
return _do_download(version, download_base, to_dir,
|
||||
download_delay)
|
||||
finally:
|
||||
if not no_fake:
|
||||
_create_fake_setuptools_pkg_info(to_dir)
|
||||
|
||||
|
||||
def download_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL,
|
||||
to_dir=os.curdir, delay=15):
|
||||
"""Download distribute from a specified location and return its filename
|
||||
|
||||
`version` should be a valid distribute 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.
|
||||
"""
|
||||
# making sure we use the absolute path
|
||||
to_dir = os.path.abspath(to_dir)
|
||||
try:
|
||||
from urllib.request import urlopen
|
||||
except ImportError:
|
||||
from urllib2 import urlopen
|
||||
tgz_name = "distribute-%s.tar.gz" % version
|
||||
url = download_base + tgz_name
|
||||
saveto = os.path.join(to_dir, tgz_name)
|
||||
src = dst = None
|
||||
if not os.path.exists(saveto): # Avoid repeated downloads
|
||||
try:
|
||||
log.warn("Downloading %s", url)
|
||||
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(saveto, "wb")
|
||||
dst.write(data)
|
||||
finally:
|
||||
if src:
|
||||
src.close()
|
||||
if dst:
|
||||
dst.close()
|
||||
return os.path.realpath(saveto)
|
||||
|
||||
|
||||
def _no_sandbox(function):
|
||||
def __no_sandbox(*args, **kw):
|
||||
try:
|
||||
from setuptools.sandbox import DirectorySandbox
|
||||
if not hasattr(DirectorySandbox, '_old'):
|
||||
def violation(*args):
|
||||
pass
|
||||
DirectorySandbox._old = DirectorySandbox._violation
|
||||
DirectorySandbox._violation = violation
|
||||
patched = True
|
||||
else:
|
||||
patched = False
|
||||
except ImportError:
|
||||
patched = False
|
||||
|
||||
try:
|
||||
return function(*args, **kw)
|
||||
finally:
|
||||
if patched:
|
||||
DirectorySandbox._violation = DirectorySandbox._old
|
||||
del DirectorySandbox._old
|
||||
|
||||
return __no_sandbox
|
||||
|
||||
|
||||
def _patch_file(path, content):
|
||||
"""Will backup the file then patch it"""
|
||||
f = open(path)
|
||||
existing_content = f.read()
|
||||
f.close()
|
||||
if existing_content == content:
|
||||
# already patched
|
||||
log.warn('Already patched.')
|
||||
return False
|
||||
log.warn('Patching...')
|
||||
_rename_path(path)
|
||||
f = open(path, 'w')
|
||||
try:
|
||||
f.write(content)
|
||||
finally:
|
||||
f.close()
|
||||
return True
|
||||
|
||||
_patch_file = _no_sandbox(_patch_file)
|
||||
|
||||
|
||||
def _same_content(path, content):
|
||||
f = open(path)
|
||||
existing_content = f.read()
|
||||
f.close()
|
||||
return existing_content == content
|
||||
|
||||
|
||||
def _rename_path(path):
|
||||
new_name = path + '.OLD.%s' % time.time()
|
||||
log.warn('Renaming %s to %s', path, new_name)
|
||||
os.rename(path, new_name)
|
||||
return new_name
|
||||
|
||||
|
||||
def _remove_flat_installation(placeholder):
|
||||
if not os.path.isdir(placeholder):
|
||||
log.warn('Unkown installation at %s', placeholder)
|
||||
return False
|
||||
found = False
|
||||
for file in os.listdir(placeholder):
|
||||
if fnmatch.fnmatch(file, 'setuptools*.egg-info'):
|
||||
found = True
|
||||
break
|
||||
if not found:
|
||||
log.warn('Could not locate setuptools*.egg-info')
|
||||
return
|
||||
|
||||
log.warn('Moving elements out of the way...')
|
||||
pkg_info = os.path.join(placeholder, file)
|
||||
if os.path.isdir(pkg_info):
|
||||
patched = _patch_egg_dir(pkg_info)
|
||||
else:
|
||||
patched = _patch_file(pkg_info, SETUPTOOLS_PKG_INFO)
|
||||
|
||||
if not patched:
|
||||
log.warn('%s already patched.', pkg_info)
|
||||
return False
|
||||
# now let's move the files out of the way
|
||||
for element in ('setuptools', 'pkg_resources.py', 'site.py'):
|
||||
element = os.path.join(placeholder, element)
|
||||
if os.path.exists(element):
|
||||
_rename_path(element)
|
||||
else:
|
||||
log.warn('Could not find the %s element of the '
|
||||
'Setuptools distribution', element)
|
||||
return True
|
||||
|
||||
_remove_flat_installation = _no_sandbox(_remove_flat_installation)
|
||||
|
||||
|
||||
def _after_install(dist):
|
||||
log.warn('After install bootstrap.')
|
||||
placeholder = dist.get_command_obj('install').install_purelib
|
||||
_create_fake_setuptools_pkg_info(placeholder)
|
||||
|
||||
|
||||
def _create_fake_setuptools_pkg_info(placeholder):
|
||||
if not placeholder or not os.path.exists(placeholder):
|
||||
log.warn('Could not find the install location')
|
||||
return
|
||||
pyver = '%s.%s' % (sys.version_info[0], sys.version_info[1])
|
||||
setuptools_file = 'setuptools-%s-py%s.egg-info' % \
|
||||
(SETUPTOOLS_FAKED_VERSION, pyver)
|
||||
pkg_info = os.path.join(placeholder, setuptools_file)
|
||||
if os.path.exists(pkg_info):
|
||||
log.warn('%s already exists', pkg_info)
|
||||
return
|
||||
|
||||
log.warn('Creating %s', pkg_info)
|
||||
try:
|
||||
f = open(pkg_info, 'w')
|
||||
except EnvironmentError:
|
||||
log.warn("Don't have permissions to write %s, skipping", pkg_info)
|
||||
return
|
||||
try:
|
||||
f.write(SETUPTOOLS_PKG_INFO)
|
||||
finally:
|
||||
f.close()
|
||||
|
||||
pth_file = os.path.join(placeholder, 'setuptools.pth')
|
||||
log.warn('Creating %s', pth_file)
|
||||
f = open(pth_file, 'w')
|
||||
try:
|
||||
f.write(os.path.join(os.curdir, setuptools_file))
|
||||
finally:
|
||||
f.close()
|
||||
|
||||
_create_fake_setuptools_pkg_info = _no_sandbox(
|
||||
_create_fake_setuptools_pkg_info
|
||||
)
|
||||
|
||||
|
||||
def _patch_egg_dir(path):
|
||||
# let's check if it's already patched
|
||||
pkg_info = os.path.join(path, 'EGG-INFO', 'PKG-INFO')
|
||||
if os.path.exists(pkg_info):
|
||||
if _same_content(pkg_info, SETUPTOOLS_PKG_INFO):
|
||||
log.warn('%s already patched.', pkg_info)
|
||||
return False
|
||||
_rename_path(path)
|
||||
os.mkdir(path)
|
||||
os.mkdir(os.path.join(path, 'EGG-INFO'))
|
||||
pkg_info = os.path.join(path, 'EGG-INFO', 'PKG-INFO')
|
||||
f = open(pkg_info, 'w')
|
||||
try:
|
||||
f.write(SETUPTOOLS_PKG_INFO)
|
||||
finally:
|
||||
f.close()
|
||||
return True
|
||||
|
||||
_patch_egg_dir = _no_sandbox(_patch_egg_dir)
|
||||
|
||||
|
||||
def _before_install():
|
||||
log.warn('Before install bootstrap.')
|
||||
_fake_setuptools()
|
||||
|
||||
|
||||
def _under_prefix(location):
|
||||
if 'install' not in sys.argv:
|
||||
return True
|
||||
args = sys.argv[sys.argv.index('install') + 1:]
|
||||
for index, arg in enumerate(args):
|
||||
for option in ('--root', '--prefix'):
|
||||
if arg.startswith('%s=' % option):
|
||||
top_dir = arg.split('root=')[-1]
|
||||
return location.startswith(top_dir)
|
||||
elif arg == option:
|
||||
if len(args) > index:
|
||||
top_dir = args[index + 1]
|
||||
return location.startswith(top_dir)
|
||||
if arg == '--user' and USER_SITE is not None:
|
||||
return location.startswith(USER_SITE)
|
||||
return True
|
||||
|
||||
|
||||
def _fake_setuptools():
|
||||
log.warn('Scanning installed packages')
|
||||
try:
|
||||
import pkg_resources
|
||||
except ImportError:
|
||||
# we're cool
|
||||
log.warn('Setuptools or Distribute does not seem to be installed.')
|
||||
return
|
||||
ws = pkg_resources.working_set
|
||||
try:
|
||||
setuptools_dist = ws.find(
|
||||
pkg_resources.Requirement.parse('setuptools', replacement=False)
|
||||
)
|
||||
except TypeError:
|
||||
# old distribute API
|
||||
setuptools_dist = ws.find(
|
||||
pkg_resources.Requirement.parse('setuptools')
|
||||
)
|
||||
|
||||
if setuptools_dist is None:
|
||||
log.warn('No setuptools distribution found')
|
||||
return
|
||||
# detecting if it was already faked
|
||||
setuptools_location = setuptools_dist.location
|
||||
log.warn('Setuptools installation detected at %s', setuptools_location)
|
||||
|
||||
# if --root or --preix was provided, and if
|
||||
# setuptools is not located in them, we don't patch it
|
||||
if not _under_prefix(setuptools_location):
|
||||
log.warn('Not patching, --root or --prefix is installing Distribute'
|
||||
' in another location')
|
||||
return
|
||||
|
||||
# let's see if its an egg
|
||||
if not setuptools_location.endswith('.egg'):
|
||||
log.warn('Non-egg installation')
|
||||
res = _remove_flat_installation(setuptools_location)
|
||||
if not res:
|
||||
return
|
||||
else:
|
||||
log.warn('Egg installation')
|
||||
pkg_info = os.path.join(setuptools_location, 'EGG-INFO', 'PKG-INFO')
|
||||
if (os.path.exists(pkg_info) and
|
||||
_same_content(pkg_info, SETUPTOOLS_PKG_INFO)):
|
||||
log.warn('Already patched.')
|
||||
return
|
||||
log.warn('Patching...')
|
||||
# let's create a fake egg replacing setuptools one
|
||||
res = _patch_egg_dir(setuptools_location)
|
||||
if not res:
|
||||
return
|
||||
log.warn('Patching complete.')
|
||||
_relaunch()
|
||||
|
||||
|
||||
def _relaunch():
|
||||
log.warn('Relaunching...')
|
||||
# we have to relaunch the process
|
||||
# pip marker to avoid a relaunch bug
|
||||
_cmd1 = ['-c', 'install', '--single-version-externally-managed']
|
||||
_cmd2 = ['-c', 'install', '--record']
|
||||
if sys.argv[:3] == _cmd1 or sys.argv[:3] == _cmd2:
|
||||
sys.argv[0] = 'setup.py'
|
||||
args = [sys.executable] + sys.argv
|
||||
sys.exit(subprocess.call(args))
|
||||
|
||||
|
||||
def _extractall(self, path=".", members=None):
|
||||
"""Extract all members from the archive to the current working
|
||||
directory and set owner, modification time and permissions on
|
||||
directories afterwards. `path' specifies a different directory
|
||||
to extract to. `members' is optional and must be a subset of the
|
||||
list returned by getmembers().
|
||||
"""
|
||||
import copy
|
||||
import operator
|
||||
from tarfile import ExtractError
|
||||
directories = []
|
||||
|
||||
if members is None:
|
||||
members = self
|
||||
|
||||
for tarinfo in members:
|
||||
if tarinfo.isdir():
|
||||
# Extract directories with a safe mode.
|
||||
directories.append(tarinfo)
|
||||
tarinfo = copy.copy(tarinfo)
|
||||
tarinfo.mode = 448 # decimal for oct 0700
|
||||
self.extract(tarinfo, path)
|
||||
|
||||
# Reverse sort directories.
|
||||
if sys.version_info < (2, 4):
|
||||
def sorter(dir1, dir2):
|
||||
return cmp(dir1.name, dir2.name)
|
||||
directories.sort(sorter)
|
||||
directories.reverse()
|
||||
else:
|
||||
directories.sort(key=operator.attrgetter('name'), reverse=True)
|
||||
|
||||
# Set correct owner, mtime and filemode on directories.
|
||||
for tarinfo in directories:
|
||||
dirpath = os.path.join(path, tarinfo.name)
|
||||
try:
|
||||
self.chown(tarinfo, dirpath)
|
||||
self.utime(tarinfo, dirpath)
|
||||
self.chmod(tarinfo, dirpath)
|
||||
except ExtractError:
|
||||
e = sys.exc_info()[1]
|
||||
if self.errorlevel > 1:
|
||||
raise
|
||||
else:
|
||||
self._dbg(1, "tarfile: %s" % e)
|
||||
|
||||
|
||||
def _build_install_args(options):
|
||||
"""
|
||||
Build the arguments to 'python setup.py install' on the distribute package
|
||||
"""
|
||||
install_args = []
|
||||
if options.user_install:
|
||||
if sys.version_info < (2, 6):
|
||||
log.warn("--user requires Python 2.6 or later")
|
||||
raise SystemExit(1)
|
||||
install_args.append('--user')
|
||||
return install_args
|
||||
|
||||
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 distribute package')
|
||||
options, args = parser.parse_args()
|
||||
# positional arguments are ignored
|
||||
return options
|
||||
|
||||
def main(version=DEFAULT_VERSION):
|
||||
"""Install or upgrade setuptools and EasyInstall"""
|
||||
options = _parse_args()
|
||||
tarball = download_setuptools(download_base=options.download_base)
|
||||
return _install(tarball, _build_install_args(options))
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
22
fix_py_compile.py
Normal file
22
fix_py_compile.py
Normal file
@ -0,0 +1,22 @@
|
||||
#!/usr/bin/env python2
|
||||
# Some Angstrom images are missing the py_compile module; get it if not
|
||||
# present:
|
||||
# Fix credit:https://github.com/alexanderhiam/PyBBIO/blob/master/setup.py
|
||||
import random, os
|
||||
python_lib_path = random.__file__.split('random')[0]
|
||||
if not os.path.exists(python_lib_path + 'py_compile.py'):
|
||||
print "py_compile module missing; installing to %spy_compile.py" %\
|
||||
python_lib_path
|
||||
import urllib2
|
||||
url = "http://hg.python.org/cpython/raw-file/4ebe1ede981e/Lib/py_compile.py"
|
||||
py_compile = urllib2.urlopen(url)
|
||||
with open(python_lib_path+'py_compile.py', 'w') as f:
|
||||
f.write(py_compile.read())
|
||||
print "testing py_compile..."
|
||||
try:
|
||||
import py_compile
|
||||
print "py_compile installed successfully"
|
||||
except Exception, e:
|
||||
print "*py_compile install failed, could not import"
|
||||
print "*Exception raised:"
|
||||
raise e
|
36
setup.py
Normal file
36
setup.py
Normal file
@ -0,0 +1,36 @@
|
||||
try:
|
||||
from overlays import builder
|
||||
builder.compile()
|
||||
builder.copy()
|
||||
except:
|
||||
pass
|
||||
|
||||
import distribute_setup
|
||||
distribute_setup.use_setuptools()
|
||||
from setuptools import setup, Extension, find_packages
|
||||
|
||||
classifiers = ['Development Status :: 3 - Alpha',
|
||||
'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 :: Home Automation',
|
||||
'Topic :: System :: Hardware']
|
||||
|
||||
setup(name = 'CHIP_IO',
|
||||
version = '0.0.2',
|
||||
author = 'Robert Wolterman',
|
||||
author_email = 'robert.wolterman@gmail.com',
|
||||
description = 'A module to control CHIP IO channels',
|
||||
long_description = open('README.rst').read() + open('CHANGELOG.rst').read(),
|
||||
license = 'MIT',
|
||||
keywords = 'CHIP NextThingCo IO GPIO PWM ADC',
|
||||
url = 'https://github.com/xtacocorex/CHIP_IO/',
|
||||
classifiers = classifiers,
|
||||
packages = find_packages(),
|
||||
ext_modules = [Extension('CHIP_IO.GPIO', ['source/py_gpio.c', 'source/event_gpio.c', 'source/constants.c', 'source/common.c'], extra_compile_args=['-Wno-format-security'])]) #,
|
||||
# Extension('CHIP_IO.PWM', ['source/py_pwm.c', 'source/c_pwm.c', 'source/constants.c', 'source/common.c'], extra_compile_args=['-Wno-format-security']),
|
||||
# Extension('CHIP_IO.ADC', ['source/py_adc.c', 'source/c_adc.c', 'source/constants.c', 'source/common.c'], extra_compile_args=['-Wno-format-security']),
|
||||
# Extension('CHIP_IO.SPI', ['source/spimodule.c', 'source/constants.c', 'source/common.c'], extra_compile_args=['-Wno-format-security'])])
|
309
source/common.c
Normal file
309
source/common.c
Normal file
@ -0,0 +1,309 @@
|
||||
/*
|
||||
Copyright (c) 2016 Robert Wolterman
|
||||
|
||||
Original BBIO Author Justin Cooper
|
||||
Modified for CHIP_IO Author Robert Wolterman
|
||||
|
||||
This file incorporates work covered by the following copyright and
|
||||
permission notice, all modified code adopts the original license:
|
||||
|
||||
Copyright (c) 2013 Adafruit
|
||||
|
||||
Original RPi.GPIO Author Ben Croston
|
||||
Modified for BBIO Author Justin Cooper
|
||||
|
||||
This file incorporates work covered by the following copyright and
|
||||
permission notice, all modified code adopts the original license:
|
||||
|
||||
Copyright (c) 2013 Ben Croston
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
#include "Python.h"
|
||||
#include <dirent.h>
|
||||
#include <time.h>
|
||||
#include "common.h"
|
||||
|
||||
int setup_error = 0;
|
||||
int module_setup = 0;
|
||||
|
||||
typedef struct pins_t {
|
||||
const char *name;
|
||||
const char *key;
|
||||
int gpio;
|
||||
int pwm_mux_mode;
|
||||
int ain;
|
||||
} pins_t;
|
||||
|
||||
// I have no idea if this table is correct, we shall see - Robert Wolterman
|
||||
pins_t table[] = {
|
||||
{ "GND", "U13_1", 0, -1, -1},
|
||||
{ "CHG-IN", "U13_2", 0, -1, -1},
|
||||
{ "VCC-5V", "U13_3", 0, -1, -1},
|
||||
{ "GND", "U13_4", 0, -1, -1},
|
||||
{ "VCC-3V3", "U13_5", 0, -1, -1},
|
||||
{ "TS", "U13_6", 0, -1, -1},
|
||||
{ "VCC-1V8", "U13_7", 0, -1, -1},
|
||||
{ "BAT", "U13_8", 0, -1, -1},
|
||||
{ "TWI1-SDA", "U13_9", 48, -1, -1},
|
||||
{ "PWRON", "U13_10", 0, -1, -1},
|
||||
{ "TWI1-SCK", "U13_11", 47, -1, -1},
|
||||
{ "GND", "U13_12", 0, -1, -1},
|
||||
{ "X1", "U13_13", 0, -1, -1},
|
||||
{ "X2", "U13_14", 0, -1, -1},
|
||||
{ "Y1", "U13_15", 0, -1, -1},
|
||||
{ "Y2", "U13_16", 0, -1, -1},
|
||||
{ "LCD-D2", "U13_17", 98, -1, -1},
|
||||
{ "PWM0", "U13_18", 34, -1, -1},
|
||||
{ "LCD-D4", "U13_19", 100, -1, -1},
|
||||
{ "LCD-D3", "U13_20", 99, -1, -1},
|
||||
{ "LCD-D6", "U13_21", 102, -1, -1},
|
||||
{ "LCD-D5", "U13_22", 101, -1, -1},
|
||||
{ "LCD-D10", "U13_23", 106, -1, -1},
|
||||
{ "LCD-D7", "U13_24", 103, -1, -1},
|
||||
{ "LCD-D12", "U13_25", 108, -1, -1},
|
||||
{ "LCD-D11", "U13_26", 107, -1, -1},
|
||||
{ "LCD-D14", "U13_27", 110, -1, -1},
|
||||
{ "LCD-D13", "U13_28", 109, -1, -1},
|
||||
{ "LCD-D18", "U13_29", 114, -1, -1},
|
||||
{ "LCD-D15", "U13_30", 111, -1, -1},
|
||||
{ "LCD-D20", "U13_31", 116, -1, -1},
|
||||
{ "LCD-D19", "U13_32", 115, -1, -1},
|
||||
{ "LCD-D22", "U13_33", 118, -1, -1},
|
||||
{ "LCD-D21", "U13_34", 117, -1, -1},
|
||||
{ "LCD-CLK", "U13_35", 120, -1, -1},
|
||||
{ "LCD-D23", "U13_36", 119, -1, -1},
|
||||
{ "LCD-VSYNC", "U13_37", 123, -1, -1},
|
||||
{ "LCD-HSYNC", "U13_38", 122, -1, -1},
|
||||
{ "GND", "U13_39", 0, -1, -1},
|
||||
{ "LCD-DE", "U13_40", 121, -1, -1},
|
||||
{ "GND", "U14_1", 0, -1, -1},
|
||||
{ "VCC-5V", "U14_2", 0, -1, -1},
|
||||
{ "UART1-TX", "U14_3", 195, -1, -1},
|
||||
{ "HPL", "U14_4", 0, -1, -1},
|
||||
{ "UART1-RX", "U14_5", 196, -1, -1},
|
||||
{ "HPCOM", "U14_6", 0, -1, -1},
|
||||
{ "FEL", "U14_7", 0, -1, -1},
|
||||
{ "HPR", "U14_8", 0, -1, -1},
|
||||
{ "VCC-3V3", "U14_9", 0, -1, -1},
|
||||
{ "MICM", "U14_10", 0, -1, -1},
|
||||
{ "LRADC", "U14_11", 0, -1, -1},
|
||||
{ "MICIN1", "U14_12", 0, -1, -1},
|
||||
{ "XIO-P0", "U14_13", 408, -1, -1},
|
||||
{ "XIO-P1", "U14_14", 409, -1, -1},
|
||||
{ "XIO-P2", "U14_15", 410, -1, -1},
|
||||
{ "XIO-P3", "U14_16", 411, -1, -1},
|
||||
{ "XIO-P4", "U14_17", 412, -1, -1},
|
||||
{ "XIO-P5", "U14_18", 413, -1, -1},
|
||||
{ "XIO-P6", "U14_19", 414, -1, -1},
|
||||
{ "XIO-P7", "U14_20", 415, -1, -1},
|
||||
{ "GND", "U14_21", 0, -1, -1},
|
||||
{ "GND", "U14_22", 0, -1, -1},
|
||||
{ "AP-EINT1", "U14_23", 193, -1, -1},
|
||||
{ "AP-EINT3", "U14_24", 35, -1, -1},
|
||||
{ "TWI2-SDA", "U14_25", 50, -1, -1},
|
||||
{ "TWI2-SCK", "U14_26", 49, -1, -1},
|
||||
{ "CSIPCK", "U14_27", 128, -1, -1},
|
||||
{ "CSICK", "U14_28", 129, 4, -1},
|
||||
{ "CSIHSYNC", "U14_29", 130, 1, -1},
|
||||
{ "CSIVSYNC", "U14_30", 131, -1, -1},
|
||||
{ "CSID0", "U14_31", 132, 1, -1},
|
||||
{ "CSID1", "U14_32", 133, -1, -1},
|
||||
{ "CSID2", "U14_33", 134, -1, 4},
|
||||
{ "CSID3", "U14_34", 135, -1, -1},
|
||||
{ "CSID4", "U14_35", 136, -1, 6},
|
||||
{ "CSID5", "U14_36", 137, -1, 5},
|
||||
{ "CSID6", "U14_37", 138, -1, 2},
|
||||
{ "CSID7", "U14_38", 139, -1, 3},
|
||||
{ "GND", "U14_39", 0, -1, 0},
|
||||
{ "GND", "U14_40", 0, -1, 1},
|
||||
{ NULL, NULL, 0 }
|
||||
};
|
||||
|
||||
int lookup_gpio_by_key(const char *key)
|
||||
{
|
||||
pins_t *p;
|
||||
for (p = table; p->key != NULL; ++p) {
|
||||
if (strcmp(p->key, key) == 0) {
|
||||
return p->gpio;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lookup_gpio_by_name(const char *name)
|
||||
{
|
||||
pins_t *p;
|
||||
for (p = table; p->name != NULL; ++p) {
|
||||
if (strcmp(p->name, name) == 0) {
|
||||
return p->gpio;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lookup_ain_by_key(const char *key)
|
||||
{
|
||||
pins_t *p;
|
||||
for (p = table; p->key != NULL; ++p) {
|
||||
if (strcmp(p->key, key) == 0) {
|
||||
if (p->ain == -1) {
|
||||
return -1;
|
||||
} else {
|
||||
return p->ain;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int lookup_ain_by_name(const char *name)
|
||||
{
|
||||
pins_t *p;
|
||||
for (p = table; p->name != NULL; ++p) {
|
||||
if (strcmp(p->name, name) == 0) {
|
||||
if (p->ain == -1) {
|
||||
return -1;
|
||||
} else {
|
||||
return p->ain;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int copy_pwm_key_by_key(const char *input_key, char *key)
|
||||
{
|
||||
pins_t *p;
|
||||
for (p = table; p->key != NULL; ++p) {
|
||||
if (strcmp(p->key, input_key) == 0) {
|
||||
//validate it's a valid pwm pin
|
||||
if (p->pwm_mux_mode == -1)
|
||||
return 0;
|
||||
|
||||
strncpy(key, p->key, 7);
|
||||
key[7] = '\0';
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int get_pwm_key_by_name(const char *name, char *key)
|
||||
{
|
||||
pins_t *p;
|
||||
for (p = table; p->name != NULL; ++p) {
|
||||
if (strcmp(p->name, name) == 0) {
|
||||
//validate it's a valid pwm pin
|
||||
if (p->pwm_mux_mode == -1)
|
||||
return 0;
|
||||
|
||||
strncpy(key, p->key, 7);
|
||||
key[7] = '\0';
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int get_gpio_number(const char *key, unsigned int *gpio)
|
||||
{
|
||||
*gpio = lookup_gpio_by_key(key);
|
||||
|
||||
if (!*gpio) {
|
||||
*gpio = lookup_gpio_by_name(key);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int get_pwm_key(const char *input, char *key)
|
||||
{
|
||||
if (!copy_pwm_key_by_key(input, key)) {
|
||||
return get_pwm_key_by_name(input, key);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int get_adc_ain(const char *key, unsigned int *ain)
|
||||
{
|
||||
*ain = lookup_ain_by_key(key);
|
||||
|
||||
if (*ain == -1) {
|
||||
*ain = lookup_ain_by_name(key);
|
||||
|
||||
if (*ain == -1) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int build_path(const char *partial_path, const char *prefix, char *full_path, size_t full_path_len)
|
||||
{
|
||||
DIR *dp;
|
||||
struct dirent *ep;
|
||||
|
||||
dp = opendir (partial_path);
|
||||
if (dp != NULL) {
|
||||
while ((ep = readdir (dp))) {
|
||||
// Enforce that the prefix must be the first part of the file
|
||||
char* found_string = strstr(ep->d_name, prefix);
|
||||
|
||||
if (found_string != NULL && (ep->d_name - found_string) == 0) {
|
||||
snprintf(full_path, full_path_len, "%s/%s", partial_path, ep->d_name);
|
||||
(void) closedir (dp);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
(void) closedir (dp);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int get_spi_bus_path_number(unsigned int spi)
|
||||
{
|
||||
char path[50];
|
||||
|
||||
build_path("/sys/devices", "ocp", ocp_dir, sizeof(ocp_dir));
|
||||
|
||||
if (spi == 0) {
|
||||
snprintf(path, sizeof(path), "%s/48030000.spi/spi_master/spi1", ocp_dir);
|
||||
} else {
|
||||
snprintf(path, sizeof(path), "%s/481a0000.spi/spi_master/spi1", ocp_dir);
|
||||
}
|
||||
|
||||
DIR* dir = opendir(path);
|
||||
if (dir) {
|
||||
closedir(dir);
|
||||
//device is using /dev/spidev1.x
|
||||
return 1;
|
||||
} else if (ENOENT == errno) {
|
||||
//device is using /dev/spidev2.x
|
||||
return 2;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
59
source/common.h
Normal file
59
source/common.h
Normal file
@ -0,0 +1,59 @@
|
||||
/*
|
||||
Copyright (c) 2016 Robert Wolterman
|
||||
|
||||
Original BBIO Author Justin Cooper
|
||||
Modified for CHIP_IO Author Robert Wolterman
|
||||
|
||||
This file incorporates work covered by the following copyright and
|
||||
permission notice, all modified code adopts the original license:
|
||||
|
||||
Copyright (c) 2013 Adafruit
|
||||
|
||||
Original RPi.GPIO Author Ben Croston
|
||||
Modified for BBIO Author Justin Cooper
|
||||
|
||||
This file incorporates work covered by the following copyright and
|
||||
permission notice, all modified code adopts the original license:
|
||||
|
||||
Copyright (c) 2013 Ben Croston
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
#define MODE_UNKNOWN -1
|
||||
#define BOARD 10
|
||||
#define BCM 11
|
||||
|
||||
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
|
||||
|
||||
#define FILENAME_BUFFER_SIZE 128
|
||||
|
||||
int gpio_mode;
|
||||
int gpio_direction[120];
|
||||
|
||||
char ctrl_dir[35];
|
||||
char ocp_dir[25];
|
||||
|
||||
int get_gpio_number(const char *key, unsigned int *gpio);
|
||||
int get_pwm_key(const char *input, char *key);
|
||||
int get_adc_ain(const char *key, unsigned int *ain);
|
||||
int build_path(const char *partial_path, const char *prefix, char *full_path, size_t full_path_len);
|
||||
int get_spi_bus_path_number(unsigned int spi);
|
||||
int setup_error;
|
||||
int module_setup;
|
81
source/constants.c
Normal file
81
source/constants.c
Normal file
@ -0,0 +1,81 @@
|
||||
/*
|
||||
Copyright (c) 2016 Robert Wolterman
|
||||
|
||||
Original BBIO Author Justin Cooper
|
||||
Modified for CHIP_IO Author Robert Wolterman
|
||||
|
||||
This file incorporates work covered by the following copyright and
|
||||
permission notice, all modified code adopts the original license:
|
||||
|
||||
Copyright (c) 2013 Adafruit
|
||||
|
||||
Original RPi.GPIO Author Ben Croston
|
||||
Modified for BBIO Author Justin Cooper
|
||||
|
||||
This file incorporates work covered by the following copyright and
|
||||
permission notice, all modified code adopts the original license:
|
||||
|
||||
Copyright (c) 2013 Ben Croston
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
#include "Python.h"
|
||||
#include "constants.h"
|
||||
#include "event_gpio.h"
|
||||
#include "common.h"
|
||||
|
||||
void define_constants(PyObject *module)
|
||||
{
|
||||
high = Py_BuildValue("i", HIGH);
|
||||
PyModule_AddObject(module, "HIGH", high);
|
||||
|
||||
low = Py_BuildValue("i", LOW);
|
||||
PyModule_AddObject(module, "LOW", low);
|
||||
|
||||
output = Py_BuildValue("i", OUTPUT);
|
||||
PyModule_AddObject(module, "OUT", output);
|
||||
|
||||
input = Py_BuildValue("i", INPUT);
|
||||
PyModule_AddObject(module, "IN", input);
|
||||
|
||||
alt0 = Py_BuildValue("i", ALT0);
|
||||
PyModule_AddObject(module, "ALT0", alt0);
|
||||
|
||||
pud_off = Py_BuildValue("i", PUD_OFF);
|
||||
PyModule_AddObject(module, "PUD_OFF", pud_off);
|
||||
|
||||
pud_up = Py_BuildValue("i", PUD_UP);
|
||||
PyModule_AddObject(module, "PUD_UP", pud_up);
|
||||
|
||||
pud_down = Py_BuildValue("i", PUD_DOWN);
|
||||
PyModule_AddObject(module, "PUD_DOWN", pud_down);
|
||||
|
||||
rising_edge = Py_BuildValue("i", RISING_EDGE);
|
||||
PyModule_AddObject(module, "RISING", rising_edge);
|
||||
|
||||
falling_edge = Py_BuildValue("i", FALLING_EDGE);
|
||||
PyModule_AddObject(module, "FALLING", falling_edge);
|
||||
|
||||
both_edge = Py_BuildValue("i", BOTH_EDGE);
|
||||
PyModule_AddObject(module, "BOTH", both_edge);
|
||||
|
||||
version = Py_BuildValue("s", "0.0.4");
|
||||
PyModule_AddObject(module, "VERSION", version);
|
||||
}
|
14
source/constants.h
Normal file
14
source/constants.h
Normal file
@ -0,0 +1,14 @@
|
||||
PyObject *high;
|
||||
PyObject *low;
|
||||
PyObject *input;
|
||||
PyObject *output;
|
||||
PyObject *alt0;
|
||||
PyObject *pud_off;
|
||||
PyObject *pud_up;
|
||||
PyObject *pud_down;
|
||||
PyObject *rising_edge;
|
||||
PyObject *falling_edge;
|
||||
PyObject *both_edge;
|
||||
PyObject *version;
|
||||
|
||||
void define_constants(PyObject *module);
|
660
source/event_gpio.c
Normal file
660
source/event_gpio.c
Normal file
@ -0,0 +1,660 @@
|
||||
/*
|
||||
Copyright (c) 2016 Robert Wolterman
|
||||
|
||||
Original BBIO Author Justin Cooper
|
||||
Modified for CHIP_IO Author Robert Wolterman
|
||||
|
||||
This file incorporates work covered by the following copyright and
|
||||
permission notice, all modified code adopts the original license:
|
||||
|
||||
Copyright (c) 2013 Adafruit
|
||||
|
||||
Original RPi.GPIO Author Ben Croston
|
||||
Modified for BBIO Author Justin Cooper
|
||||
|
||||
This file incorporates work covered by the following copyright and
|
||||
permission notice, all modified code adopts the original license:
|
||||
|
||||
Copyright (c) 2013 Ben Croston
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
#include <pthread.h>
|
||||
#include <sys/epoll.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include "event_gpio.h"
|
||||
#include "common.h"
|
||||
|
||||
const char *stredge[4] = {"none", "rising", "falling", "both"};
|
||||
|
||||
// file descriptors
|
||||
struct fdx
|
||||
{
|
||||
int fd;
|
||||
unsigned int gpio;
|
||||
int initial;
|
||||
unsigned int is_evented;
|
||||
struct fdx *next;
|
||||
};
|
||||
struct fdx *fd_list = NULL;
|
||||
|
||||
// event callbacks
|
||||
struct callback
|
||||
{
|
||||
unsigned int gpio;
|
||||
void (*func)(unsigned int gpio);
|
||||
struct callback *next;
|
||||
};
|
||||
struct callback *callbacks = NULL;
|
||||
|
||||
// gpio exports
|
||||
struct gpio_exp
|
||||
{
|
||||
unsigned int gpio;
|
||||
struct gpio_exp *next;
|
||||
};
|
||||
struct gpio_exp *exported_gpios = NULL;
|
||||
|
||||
pthread_t threads;
|
||||
int event_occurred[430] = { 0 };
|
||||
int thread_running = 0;
|
||||
int epfd = -1;
|
||||
|
||||
int gpio_export(unsigned int gpio)
|
||||
{
|
||||
int fd, len;
|
||||
char str_gpio[10];
|
||||
struct gpio_exp *new_gpio, *g;
|
||||
|
||||
if ((fd = open("/sys/class/gpio/export", O_WRONLY)) < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
len = snprintf(str_gpio, sizeof(str_gpio), "%d", gpio);
|
||||
write(fd, str_gpio, len);
|
||||
close(fd);
|
||||
|
||||
// add to list
|
||||
new_gpio = malloc(sizeof(struct gpio_exp));
|
||||
if (new_gpio == 0)
|
||||
return -1; // out of memory
|
||||
|
||||
new_gpio->gpio = gpio;
|
||||
new_gpio->next = NULL;
|
||||
|
||||
if (exported_gpios == NULL)
|
||||
{
|
||||
// create new list
|
||||
exported_gpios = new_gpio;
|
||||
} else {
|
||||
// add to end of existing list
|
||||
g = exported_gpios;
|
||||
while (g->next != NULL)
|
||||
g = g->next;
|
||||
g->next = new_gpio;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void close_value_fd(unsigned int gpio)
|
||||
{
|
||||
struct fdx *f = fd_list;
|
||||
struct fdx *temp;
|
||||
struct fdx *prev = NULL;
|
||||
|
||||
while (f != NULL)
|
||||
{
|
||||
if (f->gpio == gpio)
|
||||
{
|
||||
close(f->fd);
|
||||
if (prev == NULL)
|
||||
fd_list = f->next;
|
||||
else
|
||||
prev->next = f->next;
|
||||
temp = f;
|
||||
f = f->next;
|
||||
free(temp);
|
||||
} else {
|
||||
prev = f;
|
||||
f = f->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int fd_lookup(unsigned int gpio)
|
||||
{
|
||||
struct fdx *f = fd_list;
|
||||
while (f != NULL)
|
||||
{
|
||||
if (f->gpio == gpio)
|
||||
return f->fd;
|
||||
f = f->next;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int add_fd_list(unsigned int gpio, int fd)
|
||||
{
|
||||
struct fdx *new_fd;
|
||||
|
||||
new_fd = malloc(sizeof(struct fdx));
|
||||
if (new_fd == 0)
|
||||
return -1; // out of memory
|
||||
|
||||
new_fd->fd = fd;
|
||||
new_fd->gpio = gpio;
|
||||
new_fd->initial = 1;
|
||||
new_fd->is_evented = 0;
|
||||
if (fd_list == NULL) {
|
||||
new_fd->next = NULL;
|
||||
} else {
|
||||
new_fd->next = fd_list;
|
||||
}
|
||||
fd_list = new_fd;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int open_value_file(unsigned int gpio)
|
||||
{
|
||||
int fd;
|
||||
char filename[MAX_FILENAME];
|
||||
|
||||
// create file descriptor of value file
|
||||
snprintf(filename, sizeof(filename), "/sys/class/gpio/gpio%d/value", gpio);
|
||||
|
||||
if ((fd = open(filename, O_RDONLY | O_NONBLOCK)) < 0)
|
||||
return -1;
|
||||
add_fd_list(gpio, fd);
|
||||
return fd;
|
||||
}
|
||||
|
||||
int gpio_unexport(unsigned int gpio)
|
||||
{
|
||||
int fd, len;
|
||||
char str_gpio[10];
|
||||
struct gpio_exp *g, *temp, *prev_g = NULL;
|
||||
|
||||
close_value_fd(gpio);
|
||||
|
||||
if ((fd = open("/sys/class/gpio/unexport", O_WRONLY)) < 0)
|
||||
return -1;
|
||||
|
||||
len = snprintf(str_gpio, sizeof(str_gpio), "%d", gpio);
|
||||
write(fd, str_gpio, len);
|
||||
close(fd);
|
||||
|
||||
// remove from list
|
||||
g = exported_gpios;
|
||||
while (g != NULL)
|
||||
{
|
||||
if (g->gpio == gpio)
|
||||
{
|
||||
if (prev_g == NULL)
|
||||
exported_gpios = g->next;
|
||||
else
|
||||
prev_g->next = g->next;
|
||||
temp = g;
|
||||
g = g->next;
|
||||
free(temp);
|
||||
} else {
|
||||
prev_g = g;
|
||||
g = g->next;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gpio_set_direction(unsigned int gpio, unsigned int in_flag)
|
||||
{
|
||||
int fd;
|
||||
char filename[40];
|
||||
char direction[10] = { 0 };
|
||||
|
||||
snprintf(filename, sizeof(filename), "/sys/class/gpio/gpio%d/direction", gpio);
|
||||
if ((fd = open(filename, O_WRONLY)) < 0)
|
||||
return -1;
|
||||
|
||||
if (in_flag) {
|
||||
strncpy(direction, "out", ARRAY_SIZE(direction) - 1);
|
||||
} else {
|
||||
strncpy(direction, "in", ARRAY_SIZE(direction) - 1);
|
||||
}
|
||||
|
||||
write(fd, direction, strlen(direction));
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gpio_get_direction(unsigned int gpio, unsigned int *value)
|
||||
{
|
||||
int fd;
|
||||
char direction[4] = { 0 };
|
||||
char filename[40];
|
||||
|
||||
snprintf(filename, sizeof(filename), "/sys/class/gpio/gpio%d/direction", gpio);
|
||||
if ((fd = open(filename, O_RDONLY | O_NONBLOCK)) < 0)
|
||||
return -1;
|
||||
|
||||
lseek(fd, 0, SEEK_SET);
|
||||
read(fd, &direction, sizeof(direction) - 1);
|
||||
|
||||
if (strcmp(direction, "out") == 0) {
|
||||
*value = OUTPUT;
|
||||
} else {
|
||||
*value = INPUT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gpio_set_value(unsigned int gpio, unsigned int value)
|
||||
{
|
||||
int fd;
|
||||
char filename[MAX_FILENAME];
|
||||
char vstr[10];
|
||||
|
||||
snprintf(filename, sizeof(filename), "/sys/class/gpio/gpio%d/value", gpio);
|
||||
|
||||
if ((fd = open(filename, O_WRONLY)) < 0)
|
||||
return -1;
|
||||
|
||||
if (value) {
|
||||
strncpy(vstr, "1", ARRAY_SIZE(vstr) - 1);
|
||||
} else {
|
||||
strncpy(vstr, "0", ARRAY_SIZE(vstr) - 1);
|
||||
}
|
||||
|
||||
write(fd, vstr, strlen(vstr));
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gpio_get_value(unsigned int gpio, unsigned int *value)
|
||||
{
|
||||
int fd = fd_lookup(gpio);
|
||||
char ch;
|
||||
|
||||
if (!fd)
|
||||
{
|
||||
if ((fd = open_value_file(gpio)) == -1)
|
||||
return -1;
|
||||
}
|
||||
|
||||
lseek(fd, 0, SEEK_SET);
|
||||
read(fd, &ch, sizeof(ch));
|
||||
|
||||
if (ch != '0') {
|
||||
*value = 1;
|
||||
} else {
|
||||
*value = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gpio_set_edge(unsigned int gpio, unsigned int edge)
|
||||
{
|
||||
int fd;
|
||||
char filename[40];
|
||||
|
||||
snprintf(filename, sizeof(filename), "/sys/class/gpio/gpio%d/edge", gpio);
|
||||
|
||||
if ((fd = open(filename, O_WRONLY)) < 0)
|
||||
return -1;
|
||||
|
||||
write(fd, stredge[edge], strlen(stredge[edge]) + 1);
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned int gpio_lookup(int fd)
|
||||
{
|
||||
struct fdx *f = fd_list;
|
||||
while (f != NULL)
|
||||
{
|
||||
if (f->fd == fd)
|
||||
return f->gpio;
|
||||
f = f->next;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void exports_cleanup(void)
|
||||
{
|
||||
// unexport everything
|
||||
while (exported_gpios != NULL)
|
||||
gpio_unexport(exported_gpios->gpio);
|
||||
}
|
||||
|
||||
int add_edge_callback(unsigned int gpio, void (*func)(unsigned int gpio))
|
||||
{
|
||||
struct callback *cb = callbacks;
|
||||
struct callback *new_cb;
|
||||
|
||||
new_cb = malloc(sizeof(struct callback));
|
||||
if (new_cb == 0)
|
||||
return -1; // out of memory
|
||||
|
||||
new_cb->gpio = gpio;
|
||||
new_cb->func = func;
|
||||
new_cb->next = NULL;
|
||||
|
||||
if (callbacks == NULL) {
|
||||
// start new list
|
||||
callbacks = new_cb;
|
||||
} else {
|
||||
// add to end of list
|
||||
while (cb->next != NULL)
|
||||
cb = cb->next;
|
||||
cb->next = new_cb;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void run_callbacks(unsigned int gpio)
|
||||
{
|
||||
struct callback *cb = callbacks;
|
||||
while (cb != NULL)
|
||||
{
|
||||
if (cb->gpio == gpio)
|
||||
cb->func(cb->gpio);
|
||||
cb = cb->next;
|
||||
}
|
||||
}
|
||||
|
||||
void remove_callbacks(unsigned int gpio)
|
||||
{
|
||||
struct callback *cb = callbacks;
|
||||
struct callback *temp;
|
||||
struct callback *prev = NULL;
|
||||
|
||||
while (cb != NULL)
|
||||
{
|
||||
if (cb->gpio == gpio)
|
||||
{
|
||||
if (prev == NULL)
|
||||
callbacks = cb->next;
|
||||
else
|
||||
prev->next = cb->next;
|
||||
temp = cb;
|
||||
cb = cb->next;
|
||||
free(temp);
|
||||
} else {
|
||||
prev = cb;
|
||||
cb = cb->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void set_initial_false(unsigned int gpio)
|
||||
{
|
||||
struct fdx *f = fd_list;
|
||||
|
||||
while (f != NULL)
|
||||
{
|
||||
if (f->gpio == gpio)
|
||||
f->initial = 0;
|
||||
f = f->next;
|
||||
}
|
||||
}
|
||||
|
||||
int gpio_initial(unsigned int gpio)
|
||||
{
|
||||
struct fdx *f = fd_list;
|
||||
|
||||
while (f != NULL)
|
||||
{
|
||||
if ((f->gpio == gpio) && f->initial)
|
||||
return 1;
|
||||
f = f->next;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *poll_thread(void *threadarg)
|
||||
{
|
||||
struct epoll_event events;
|
||||
char buf;
|
||||
unsigned int gpio;
|
||||
int n;
|
||||
|
||||
thread_running = 1;
|
||||
while (thread_running)
|
||||
{
|
||||
if ((n = epoll_wait(epfd, &events, 1, -1)) == -1)
|
||||
{
|
||||
thread_running = 0;
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
if (n > 0) {
|
||||
lseek(events.data.fd, 0, SEEK_SET);
|
||||
if (read(events.data.fd, &buf, 1) != 1)
|
||||
{
|
||||
thread_running = 0;
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
gpio = gpio_lookup(events.data.fd);
|
||||
if (gpio_initial(gpio)) { // ignore first epoll trigger
|
||||
set_initial_false(gpio);
|
||||
} else {
|
||||
event_occurred[gpio] = 1;
|
||||
run_callbacks(gpio);
|
||||
}
|
||||
}
|
||||
}
|
||||
thread_running = 0;
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
|
||||
int gpio_is_evented(unsigned int gpio)
|
||||
{
|
||||
struct fdx *f = fd_list;
|
||||
while (f != NULL)
|
||||
{
|
||||
if (f->gpio == gpio)
|
||||
return 1;
|
||||
f = f->next;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gpio_event_add(unsigned int gpio)
|
||||
{
|
||||
struct fdx *f = fd_list;
|
||||
while (f != NULL)
|
||||
{
|
||||
if (f->gpio == gpio)
|
||||
{
|
||||
if (f->is_evented)
|
||||
return 1;
|
||||
|
||||
f->is_evented = 1;
|
||||
return 0;
|
||||
}
|
||||
f = f->next;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gpio_event_remove(unsigned int gpio)
|
||||
{
|
||||
struct fdx *f = fd_list;
|
||||
while (f != NULL)
|
||||
{
|
||||
if (f->gpio == gpio)
|
||||
{
|
||||
f->is_evented = 0;
|
||||
return 0;
|
||||
}
|
||||
f = f->next;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int add_edge_detect(unsigned int gpio, unsigned int edge)
|
||||
// return values:
|
||||
// 0 - Success
|
||||
// 1 - Edge detection already added
|
||||
// 2 - Other error
|
||||
{
|
||||
int fd = fd_lookup(gpio);
|
||||
pthread_t threads;
|
||||
struct epoll_event ev;
|
||||
long t = 0;
|
||||
|
||||
// check to see if this gpio has been added already
|
||||
if (gpio_event_add(gpio) != 0)
|
||||
return 1;
|
||||
|
||||
// export /sys/class/gpio interface
|
||||
gpio_export(gpio);
|
||||
gpio_set_direction(gpio, 0); // 0=input
|
||||
gpio_set_edge(gpio, edge);
|
||||
|
||||
if (!fd)
|
||||
{
|
||||
if ((fd = open_value_file(gpio)) == -1)
|
||||
return 2;
|
||||
}
|
||||
|
||||
// create epfd if not already open
|
||||
if ((epfd == -1) && ((epfd = epoll_create(1)) == -1))
|
||||
return 2;
|
||||
|
||||
// add to epoll fd
|
||||
ev.events = EPOLLIN | EPOLLET | EPOLLPRI;
|
||||
ev.data.fd = fd;
|
||||
if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev) == -1)
|
||||
return 2;
|
||||
|
||||
// start poll thread if it is not already running
|
||||
if (!thread_running)
|
||||
{
|
||||
if (pthread_create(&threads, NULL, poll_thread, (void *)t) != 0)
|
||||
return 2;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void remove_edge_detect(unsigned int gpio)
|
||||
{
|
||||
struct epoll_event ev;
|
||||
int fd = fd_lookup(gpio);
|
||||
|
||||
// delete callbacks for gpio
|
||||
remove_callbacks(gpio);
|
||||
|
||||
// delete epoll of fd
|
||||
epoll_ctl(epfd, EPOLL_CTL_DEL, fd, &ev);
|
||||
|
||||
// set edge to none
|
||||
gpio_set_edge(gpio, NO_EDGE);
|
||||
|
||||
// unexport gpio
|
||||
gpio_event_remove(gpio);
|
||||
|
||||
// clear detected flag
|
||||
event_occurred[gpio] = 0;
|
||||
}
|
||||
|
||||
int event_detected(unsigned int gpio)
|
||||
{
|
||||
if (event_occurred[gpio]) {
|
||||
event_occurred[gpio] = 0;
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void event_cleanup(void)
|
||||
{
|
||||
close(epfd);
|
||||
thread_running = 0;
|
||||
exports_cleanup();
|
||||
}
|
||||
|
||||
int blocking_wait_for_edge(unsigned int gpio, unsigned int edge)
|
||||
// standalone from all the event functions above
|
||||
{
|
||||
int fd = fd_lookup(gpio);
|
||||
int epfd, n, i;
|
||||
struct epoll_event events, ev;
|
||||
char buf;
|
||||
|
||||
if ((epfd = epoll_create(1)) == -1)
|
||||
return 1;
|
||||
|
||||
// check to see if this gpio has been added already, if not, mark as added
|
||||
if (gpio_event_add(gpio) != 0)
|
||||
return 2;
|
||||
|
||||
// export /sys/class/gpio interface
|
||||
gpio_export(gpio);
|
||||
gpio_set_direction(gpio, 0); // 0=input
|
||||
gpio_set_edge(gpio, edge);
|
||||
|
||||
if (!fd)
|
||||
{
|
||||
if ((fd = open_value_file(gpio)) == -1)
|
||||
return 3;
|
||||
}
|
||||
|
||||
// add to epoll fd
|
||||
ev.events = EPOLLIN | EPOLLET | EPOLLPRI;
|
||||
ev.data.fd = fd;
|
||||
if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev) == -1)
|
||||
{
|
||||
gpio_event_remove(gpio);
|
||||
return 4;
|
||||
}
|
||||
|
||||
// epoll for event
|
||||
for (i = 0; i<2; i++) // first time triggers with current state, so ignore
|
||||
if ((n = epoll_wait(epfd, &events, 1, -1)) == -1)
|
||||
{
|
||||
gpio_event_remove(gpio);
|
||||
return 5;
|
||||
}
|
||||
|
||||
if (n > 0)
|
||||
{
|
||||
lseek(events.data.fd, 0, SEEK_SET);
|
||||
if (read(events.data.fd, &buf, sizeof(buf)) != 1)
|
||||
{
|
||||
gpio_event_remove(gpio);
|
||||
return 6;
|
||||
}
|
||||
if (events.data.fd != fd)
|
||||
{
|
||||
gpio_event_remove(gpio);
|
||||
return 7;
|
||||
}
|
||||
}
|
||||
|
||||
gpio_event_remove(gpio);
|
||||
close(epfd);
|
||||
return 0;
|
||||
}
|
74
source/event_gpio.h
Normal file
74
source/event_gpio.h
Normal file
@ -0,0 +1,74 @@
|
||||
/*
|
||||
Copyright (c) 2016 Robert Wolterman
|
||||
|
||||
Original BBIO Author Justin Cooper
|
||||
Modified for CHIP_IO Author Robert Wolterman
|
||||
|
||||
This file incorporates work covered by the following copyright and
|
||||
permission notice, all modified code adopts the original license:
|
||||
|
||||
Copyright (c) 2013 Adafruit
|
||||
|
||||
Original RPi.GPIO Author Ben Croston
|
||||
Modified for BBIO Author Justin Cooper
|
||||
|
||||
This file incorporates work covered by the following copyright and
|
||||
permission notice, all modified code adopts the original license:
|
||||
|
||||
Copyright (c) 2013 Ben Croston
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
#define NO_EDGE 0
|
||||
#define RISING_EDGE 1
|
||||
#define FALLING_EDGE 2
|
||||
#define BOTH_EDGE 3
|
||||
|
||||
#define INPUT 0
|
||||
#define OUTPUT 1
|
||||
#define ALT0 4
|
||||
|
||||
#define HIGH 1
|
||||
#define LOW 0
|
||||
|
||||
#define MAX_FILENAME 50
|
||||
|
||||
#define PUD_OFF 0
|
||||
#define PUD_DOWN 1
|
||||
#define PUD_UP 2
|
||||
|
||||
int gpio_export(unsigned int gpio);
|
||||
int gpio_unexport(unsigned int gpio);
|
||||
void exports_cleanup(void);
|
||||
int gpio_set_direction(unsigned int gpio, unsigned int in_flag);
|
||||
int gpio_get_direction(unsigned int gpio, unsigned int *value);
|
||||
int gpio_set_value(unsigned int gpio, unsigned int value);
|
||||
int gpio_get_value(unsigned int gpio, unsigned int *value);
|
||||
|
||||
int add_edge_detect(unsigned int gpio, unsigned int edge);
|
||||
void remove_edge_detect(unsigned int gpio);
|
||||
int add_edge_callback(unsigned int gpio, void (*func)(unsigned int gpio));
|
||||
int event_detected(unsigned int gpio);
|
||||
int gpio_event_add(unsigned int gpio);
|
||||
int gpio_event_remove(unsigned int gpio);
|
||||
int gpio_is_evented(unsigned int gpio);
|
||||
int event_initialise(void);
|
||||
void event_cleanup(void);
|
||||
int blocking_wait_for_edge(unsigned int gpio, unsigned int edge);
|
554
source/py_gpio.c
Normal file
554
source/py_gpio.c
Normal file
@ -0,0 +1,554 @@
|
||||
/*
|
||||
Copyright (c) 2016 Robert Wolterman
|
||||
|
||||
Original BBIO Author Justin Cooper
|
||||
Modified for CHIP_IO Author Robert Wolterman
|
||||
|
||||
This file incorporates work covered by the following copyright and
|
||||
permission notice, all modified code adopts the original license:
|
||||
|
||||
Copyright (c) 2013 Adafruit
|
||||
|
||||
Original RPi.GPIO Author Ben Croston
|
||||
Modified for BBIO Author Justin Cooper
|
||||
|
||||
This file incorporates work covered by the following copyright and
|
||||
permission notice, all modified code adopts the original license:
|
||||
|
||||
Copyright (c) 2013 Ben Croston
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
#include "Python.h"
|
||||
#include "constants.h"
|
||||
#include "common.h"
|
||||
#include "event_gpio.h"
|
||||
|
||||
static int gpio_warnings = 1;
|
||||
|
||||
struct py_callback
|
||||
{
|
||||
char channel[32];
|
||||
unsigned int gpio;
|
||||
PyObject *py_cb;
|
||||
unsigned long long lastcall;
|
||||
unsigned int bouncetime;
|
||||
struct py_callback *next;
|
||||
};
|
||||
static struct py_callback *py_callbacks = NULL;
|
||||
|
||||
static int init_module(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0; i<430; i++)
|
||||
gpio_direction[i] = -1;
|
||||
|
||||
module_setup = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// python function cleanup()
|
||||
static PyObject *py_cleanup(PyObject *self, PyObject *args)
|
||||
{
|
||||
// clean up any exports
|
||||
event_cleanup();
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
// python function setup(channel, direction, pull_up_down=PUD_OFF, initial=None)
|
||||
static PyObject *py_setup_channel(PyObject *self, PyObject *args, PyObject *kwargs)
|
||||
{
|
||||
unsigned int gpio;
|
||||
char *channel;
|
||||
int direction;
|
||||
int pud = PUD_OFF;
|
||||
int initial = 0;
|
||||
static char *kwlist[] = {"channel", "direction", "pull_up_down", "initial", NULL};
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "si|ii", kwlist, &channel, &direction, &pud, &initial))
|
||||
return NULL;
|
||||
|
||||
if (!module_setup) {
|
||||
init_module();
|
||||
}
|
||||
|
||||
|
||||
if (direction != INPUT && direction != OUTPUT)
|
||||
{
|
||||
PyErr_SetString(PyExc_ValueError, "An invalid direction was passed to setup()");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (direction == OUTPUT)
|
||||
pud = PUD_OFF;
|
||||
|
||||
if (pud != PUD_OFF && pud != PUD_DOWN && pud != PUD_UP)
|
||||
{
|
||||
PyErr_SetString(PyExc_ValueError, "Invalid value for pull_up_down - should be either PUD_OFF, PUD_UP or PUD_DOWN");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (get_gpio_number(channel, &gpio))
|
||||
return NULL;
|
||||
|
||||
gpio_export(gpio);
|
||||
gpio_set_direction(gpio, direction);
|
||||
if (gpio < 408) {
|
||||
if (direction == OUTPUT) {
|
||||
gpio_set_value(gpio, initial);
|
||||
} else {
|
||||
gpio_set_value(gpio, pud);
|
||||
}
|
||||
}
|
||||
|
||||
gpio_direction[gpio] = direction;
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
// python function output(channel, value)
|
||||
static PyObject *py_output_gpio(PyObject *self, PyObject *args)
|
||||
{
|
||||
unsigned int gpio;
|
||||
int value;
|
||||
char *channel;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "si", &channel, &value))
|
||||
return NULL;
|
||||
|
||||
if (get_gpio_number(channel, &gpio))
|
||||
return NULL;
|
||||
|
||||
if (!module_setup || gpio_direction[gpio] != OUTPUT)
|
||||
{
|
||||
PyErr_SetString(PyExc_RuntimeError, "The GPIO channel has not been setup() as an OUTPUT");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
gpio_set_value(gpio, value);
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
// python function value = input(channel)
|
||||
static PyObject *py_input_gpio(PyObject *self, PyObject *args)
|
||||
{
|
||||
unsigned int gpio;
|
||||
char *channel;
|
||||
unsigned int value;
|
||||
PyObject *py_value;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "s", &channel))
|
||||
return NULL;
|
||||
|
||||
if (get_gpio_number(channel, &gpio))
|
||||
return NULL;
|
||||
|
||||
// check channel is set up as an input or output
|
||||
if (!module_setup || (gpio_direction[gpio] != INPUT && gpio_direction[gpio] != OUTPUT))
|
||||
{
|
||||
PyErr_SetString(PyExc_RuntimeError, "You must setup() the GPIO channel first");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
gpio_get_value(gpio, &value);
|
||||
|
||||
py_value = Py_BuildValue("i", value);
|
||||
|
||||
return py_value;
|
||||
}
|
||||
|
||||
static void run_py_callbacks(unsigned int gpio)
|
||||
{
|
||||
PyObject *result;
|
||||
PyGILState_STATE gstate;
|
||||
struct py_callback *cb = py_callbacks;
|
||||
struct timeval tv_timenow;
|
||||
unsigned long long timenow;
|
||||
|
||||
while (cb != NULL)
|
||||
{
|
||||
if (cb->gpio == gpio)
|
||||
{
|
||||
gettimeofday(&tv_timenow, NULL);
|
||||
timenow = tv_timenow.tv_sec*1E6 + tv_timenow.tv_usec;
|
||||
if (cb->bouncetime == 0 || timenow - cb->lastcall > cb->bouncetime*1000 || cb->lastcall == 0 || cb->lastcall > timenow) {
|
||||
|
||||
// save lastcall before calling func to prevent reentrant bounce
|
||||
cb->lastcall = timenow;
|
||||
|
||||
// run callback
|
||||
gstate = PyGILState_Ensure();
|
||||
result = PyObject_CallFunction(cb->py_cb, "s", cb->channel);
|
||||
|
||||
if (result == NULL && PyErr_Occurred())
|
||||
{
|
||||
PyErr_Print();
|
||||
PyErr_Clear();
|
||||
}
|
||||
Py_XDECREF(result);
|
||||
PyGILState_Release(gstate);
|
||||
}
|
||||
cb->lastcall = timenow;
|
||||
}
|
||||
cb = cb->next;
|
||||
}
|
||||
}
|
||||
|
||||
static int add_py_callback(char *channel, unsigned int gpio, unsigned int bouncetime, PyObject *cb_func)
|
||||
{
|
||||
struct py_callback *new_py_cb;
|
||||
struct py_callback *cb = py_callbacks;
|
||||
|
||||
// add callback to py_callbacks list
|
||||
new_py_cb = malloc(sizeof(struct py_callback));
|
||||
if (new_py_cb == 0)
|
||||
{
|
||||
PyErr_NoMemory();
|
||||
return -1;
|
||||
}
|
||||
new_py_cb->py_cb = cb_func;
|
||||
Py_XINCREF(cb_func); // Add a reference to new callback
|
||||
memset(new_py_cb->channel, 0, sizeof(new_py_cb->channel));
|
||||
strncpy(new_py_cb->channel, channel, sizeof(new_py_cb->channel) - 1);
|
||||
new_py_cb->gpio = gpio;
|
||||
new_py_cb->lastcall = 0;
|
||||
new_py_cb->bouncetime = bouncetime;
|
||||
new_py_cb->next = NULL;
|
||||
if (py_callbacks == NULL) {
|
||||
py_callbacks = new_py_cb;
|
||||
} else {
|
||||
// add to end of list
|
||||
while (cb->next != NULL)
|
||||
cb = cb->next;
|
||||
cb->next = new_py_cb;
|
||||
}
|
||||
add_edge_callback(gpio, run_py_callbacks);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// python function add_event_callback(gpio, callback, bouncetime=0)
|
||||
static PyObject *py_add_event_callback(PyObject *self, PyObject *args, PyObject *kwargs)
|
||||
{
|
||||
unsigned int gpio;
|
||||
char *channel;
|
||||
unsigned int bouncetime = 0;
|
||||
PyObject *cb_func;
|
||||
char *kwlist[] = {"gpio", "callback", "bouncetime", NULL};
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sO|i", kwlist, &channel, &cb_func, &bouncetime))
|
||||
return NULL;
|
||||
|
||||
if (!PyCallable_Check(cb_func))
|
||||
{
|
||||
PyErr_SetString(PyExc_TypeError, "Parameter must be callable");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (get_gpio_number(channel, &gpio))
|
||||
return NULL;
|
||||
|
||||
// check channel is set up as an input
|
||||
if (!module_setup || gpio_direction[gpio] != INPUT)
|
||||
{
|
||||
PyErr_SetString(PyExc_RuntimeError, "You must setup() the GPIO channel as an input first");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!gpio_is_evented(gpio))
|
||||
{
|
||||
PyErr_SetString(PyExc_RuntimeError, "Add event detection using add_event_detect first before adding a callback");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (add_py_callback(channel, gpio, bouncetime, cb_func) != 0)
|
||||
return NULL;
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
// python function add_event_detect(gpio, edge, callback=None, bouncetime=0
|
||||
static PyObject *py_add_event_detect(PyObject *self, PyObject *args, PyObject *kwargs)
|
||||
{
|
||||
unsigned int gpio;
|
||||
char *channel;
|
||||
int edge, result;
|
||||
unsigned int bouncetime = 0;
|
||||
PyObject *cb_func = NULL;
|
||||
char *kwlist[] = {"gpio", "edge", "callback", "bouncetime", NULL};
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "si|Oi", kwlist, &channel, &edge, &cb_func, &bouncetime))
|
||||
return NULL;
|
||||
|
||||
if (cb_func != NULL && !PyCallable_Check(cb_func))
|
||||
{
|
||||
PyErr_SetString(PyExc_TypeError, "Parameter must be callable");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (get_gpio_number(channel, &gpio))
|
||||
return NULL;
|
||||
|
||||
// check channel is set up as an input
|
||||
if (!module_setup || gpio_direction[gpio] != INPUT)
|
||||
{
|
||||
PyErr_SetString(PyExc_RuntimeError, "You must setup() the GPIO channel as an input first");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// is edge valid value
|
||||
if (edge != RISING_EDGE && edge != FALLING_EDGE && edge != BOTH_EDGE)
|
||||
{
|
||||
PyErr_SetString(PyExc_ValueError, "The edge must be set to RISING, FALLING or BOTH");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ((result = add_edge_detect(gpio, edge)) != 0) // starts a thread
|
||||
{
|
||||
if (result == 1)
|
||||
{
|
||||
PyErr_SetString(PyExc_RuntimeError, "Edge detection already enabled for this GPIO channel");
|
||||
return NULL;
|
||||
} else {
|
||||
PyErr_SetString(PyExc_RuntimeError, "Failed to add edge detection");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (cb_func != NULL)
|
||||
if (add_py_callback(channel, gpio, bouncetime, cb_func) != 0)
|
||||
return NULL;
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
// python function remove_event_detect(gpio)
|
||||
static PyObject *py_remove_event_detect(PyObject *self, PyObject *args)
|
||||
{
|
||||
unsigned int gpio;
|
||||
char *channel;
|
||||
struct py_callback *cb = py_callbacks;
|
||||
struct py_callback *temp;
|
||||
struct py_callback *prev = NULL;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "s", &channel))
|
||||
return NULL;
|
||||
|
||||
if (get_gpio_number(channel, &gpio))
|
||||
return NULL;
|
||||
|
||||
// remove all python callbacks for gpio
|
||||
while (cb != NULL)
|
||||
{
|
||||
if (cb->gpio == gpio)
|
||||
{
|
||||
Py_XDECREF(cb->py_cb);
|
||||
if (prev == NULL)
|
||||
py_callbacks = cb->next;
|
||||
else
|
||||
prev->next = cb->next;
|
||||
temp = cb;
|
||||
cb = cb->next;
|
||||
free(temp);
|
||||
} else {
|
||||
prev = cb;
|
||||
cb = cb->next;
|
||||
}
|
||||
}
|
||||
|
||||
remove_edge_detect(gpio);
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
// python function value = event_detected(channel)
|
||||
static PyObject *py_event_detected(PyObject *self, PyObject *args)
|
||||
{
|
||||
unsigned int gpio;
|
||||
char *channel;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "s", &channel))
|
||||
return NULL;
|
||||
|
||||
if (get_gpio_number(channel, &gpio))
|
||||
return NULL;
|
||||
|
||||
if (event_detected(gpio))
|
||||
Py_RETURN_TRUE;
|
||||
else
|
||||
Py_RETURN_FALSE;
|
||||
}
|
||||
|
||||
// python function py_wait_for_edge(gpio, edge)
|
||||
static PyObject *py_wait_for_edge(PyObject *self, PyObject *args)
|
||||
{
|
||||
unsigned int gpio;
|
||||
int edge, result;
|
||||
char *channel;
|
||||
char error[30];
|
||||
|
||||
if (!PyArg_ParseTuple(args, "si", &channel, &edge))
|
||||
return NULL;
|
||||
|
||||
if (get_gpio_number(channel, &gpio))
|
||||
return NULL;
|
||||
|
||||
// check channel is setup as an input
|
||||
if (!module_setup || gpio_direction[gpio] != INPUT)
|
||||
{
|
||||
PyErr_SetString(PyExc_RuntimeError, "You must setup() the GPIO channel as an input first");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// is edge a valid value?
|
||||
if (edge != RISING_EDGE && edge != FALLING_EDGE && edge != BOTH_EDGE)
|
||||
{
|
||||
PyErr_SetString(PyExc_ValueError, "The edge must be set to RISING, FALLING or BOTH");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Py_BEGIN_ALLOW_THREADS // disable GIL
|
||||
result = blocking_wait_for_edge(gpio, edge);
|
||||
Py_END_ALLOW_THREADS // enable GIL
|
||||
|
||||
if (result == 0) {
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
} else if (result == 2) {
|
||||
PyErr_SetString(PyExc_RuntimeError, "Edge detection events already enabled for this GPIO channel");
|
||||
return NULL;
|
||||
} else {
|
||||
sprintf(error, "Error #%d waiting for edge", result);
|
||||
PyErr_SetString(PyExc_RuntimeError, error);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
// python function value = gpio_function(gpio)
|
||||
static PyObject *py_gpio_function(PyObject *self, PyObject *args)
|
||||
{
|
||||
unsigned int gpio;
|
||||
unsigned int value;
|
||||
PyObject *func;
|
||||
char *channel;
|
||||
|
||||
|
||||
if (!PyArg_ParseTuple(args, "s", &channel))
|
||||
return NULL;
|
||||
|
||||
if (get_gpio_number(channel, &gpio))
|
||||
return NULL;
|
||||
|
||||
if (setup_error)
|
||||
{
|
||||
PyErr_SetString(PyExc_RuntimeError, "Module not imported correctly!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
gpio_get_direction(gpio, &value);
|
||||
func = Py_BuildValue("i", value);
|
||||
return func;
|
||||
}
|
||||
|
||||
// python function setwarnings(state)
|
||||
static PyObject *py_setwarnings(PyObject *self, PyObject *args)
|
||||
{
|
||||
if (!PyArg_ParseTuple(args, "i", &gpio_warnings))
|
||||
return NULL;
|
||||
|
||||
if (setup_error)
|
||||
{
|
||||
PyErr_SetString(PyExc_RuntimeError, "Module not imported correctly!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static const char moduledocstring[] = "GPIO functionality of a CHIP using Python";
|
||||
|
||||
PyMethodDef gpio_methods[] = {
|
||||
{"setup", (PyCFunction)py_setup_channel, METH_VARARGS | METH_KEYWORDS, "Set up the GPIO channel, direction and (optional) pull/up down control\nchannel - Either: RPi board pin number (not BCM GPIO 00..nn number). Pins start from 1\n or : BCM GPIO number\ndirection - INPUT or OUTPUT\n[pull_up_down] - PUD_OFF (default), PUD_UP or PUD_DOWN\n[initial] - Initial value for an output channel"},
|
||||
{"cleanup", py_cleanup, METH_VARARGS, "Clean up by resetting all GPIO channels that have been used by this program to INPUT with no pullup/pulldown and no event detection"},
|
||||
{"output", py_output_gpio, METH_VARARGS, "Output to a GPIO channel\ngpio - gpio channel\nvalue - 0/1 or False/True or LOW/HIGH"},
|
||||
{"input", py_input_gpio, METH_VARARGS, "Input from a GPIO channel. Returns HIGH=1=True or LOW=0=False\ngpio - gpio channel"},
|
||||
{"add_event_detect", (PyCFunction)py_add_event_detect, METH_VARARGS | METH_KEYWORDS, "Enable edge detection events for a particular GPIO channel.\nchannel - either board pin number or BCM number depending on which mode is set.\nedge - RISING, FALLING or BOTH\n[callback] - A callback function for the event (optional)\n[bouncetime] - Switch bounce timeout in ms for callback"},
|
||||
{"remove_event_detect", py_remove_event_detect, METH_VARARGS, "Remove edge detection for a particular GPIO channel\ngpio - gpio channel"},
|
||||
{"event_detected", py_event_detected, METH_VARARGS, "Returns True if an edge has occured on a given GPIO. You need to enable edge detection using add_event_detect() first.\ngpio - gpio channel"},
|
||||
{"add_event_callback", (PyCFunction)py_add_event_callback, METH_VARARGS | METH_KEYWORDS, "Add a callback for an event already defined using add_event_detect()\ngpio - gpio channel\ncallback - a callback function\n[bouncetime] - Switch bounce timeout in ms"},
|
||||
{"wait_for_edge", py_wait_for_edge, METH_VARARGS, "Wait for an edge.\ngpio - gpio channel\nedge - RISING, FALLING or BOTH"},
|
||||
{"gpio_function", py_gpio_function, METH_VARARGS, "Return the current GPIO function (IN, OUT, ALT0)\ngpio - gpio channel"},
|
||||
{"setwarnings", py_setwarnings, METH_VARARGS, "Enable or disable warning messages"},
|
||||
{NULL, NULL, 0, NULL}
|
||||
};
|
||||
|
||||
#if PY_MAJOR_VERSION > 2
|
||||
static struct PyModuleDef rpigpiomodule = {
|
||||
PyModuleDef_HEAD_INIT,
|
||||
"GPIO", // name of module
|
||||
moduledocstring, // module documentation, may be NULL
|
||||
-1, // size of per-interpreter state of the module, or -1 if the module keeps state in global variables.
|
||||
gpio_methods
|
||||
};
|
||||
#endif
|
||||
|
||||
#if PY_MAJOR_VERSION > 2
|
||||
PyMODINIT_FUNC PyInit_GPIO(void)
|
||||
#else
|
||||
PyMODINIT_FUNC initGPIO(void)
|
||||
#endif
|
||||
{
|
||||
PyObject *module = NULL;
|
||||
|
||||
#if PY_MAJOR_VERSION > 2
|
||||
if ((module = PyModule_Create(&rpigpiomodule)) == NULL)
|
||||
return NULL;
|
||||
#else
|
||||
if ((module = Py_InitModule3("GPIO", gpio_methods, moduledocstring)) == NULL)
|
||||
return;
|
||||
#endif
|
||||
|
||||
define_constants(module);
|
||||
|
||||
if (!PyEval_ThreadsInitialized())
|
||||
PyEval_InitThreads();
|
||||
|
||||
if (Py_AtExit(event_cleanup) != 0)
|
||||
{
|
||||
setup_error = 1;
|
||||
event_cleanup();
|
||||
#if PY_MAJOR_VERSION > 2
|
||||
return NULL;
|
||||
#else
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if PY_MAJOR_VERSION > 2
|
||||
return module;
|
||||
#else
|
||||
return;
|
||||
#endif
|
||||
}
|
36
source/setup.py
Normal file
36
source/setup.py
Normal file
@ -0,0 +1,36 @@
|
||||
try:
|
||||
from overlays import builder
|
||||
builder.compile()
|
||||
builder.copy()
|
||||
except:
|
||||
pass
|
||||
|
||||
import distribute_setup
|
||||
distribute_setup.use_setuptools()
|
||||
from setuptools import setup, Extension, find_packages
|
||||
|
||||
classifiers = ['Development Status :: 3 - Alpha',
|
||||
'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 :: Home Automation',
|
||||
'Topic :: System :: Hardware']
|
||||
|
||||
setup(name = 'CHIP_IO',
|
||||
version = '0.0.4',
|
||||
author = 'Robert Wolterman',
|
||||
author_email = 'robert.wolterman@gmail.com',
|
||||
description = 'A module to control CHIP IO channels',
|
||||
long_description = open('README.md').read() + open('CHANGELOG.rst').read(),
|
||||
license = 'MIT',
|
||||
keywords = 'CHIP NextThingCo IO GPIO PWM ADC',
|
||||
url = 'https://github.com/xtacocorex/CHIP_IO/',
|
||||
classifiers = classifiers,
|
||||
packages = find_packages(),
|
||||
ext_modules = [Extension('CHIP_IO.GPIO', ['source/py_gpio.c', 'source/event_gpio.c', 'source/constants.c', 'source/common.c'], extra_compile_args=['-Wno-format-security'])]) #,
|
||||
# Extension('CHIP_IO.PWM', ['source/py_pwm.c', 'source/c_pwm.c', 'source/constants.c', 'source/common.c'], extra_compile_args=['-Wno-format-security']),
|
||||
# Extension('CHIP_IO.ADC', ['source/py_adc.c', 'source/c_adc.c', 'source/constants.c', 'source/common.c'], extra_compile_args=['-Wno-format-security']),
|
||||
# Extension('CHIP_IO.SPI', ['source/spimodule.c', 'source/constants.c', 'source/common.c'], extra_compile_args=['-Wno-format-security'])])
|
BIN
test/__pycache__/test_gpio_input.cpython-27-PYTEST.pyc
Normal file
BIN
test/__pycache__/test_gpio_input.cpython-27-PYTEST.pyc
Normal file
Binary file not shown.
BIN
test/__pycache__/test_gpio_output.cpython-27-PYTEST.pyc
Normal file
BIN
test/__pycache__/test_gpio_output.cpython-27-PYTEST.pyc
Normal file
Binary file not shown.
BIN
test/__pycache__/test_gpio_setup.cpython-27-PYTEST.pyc
Normal file
BIN
test/__pycache__/test_gpio_setup.cpython-27-PYTEST.pyc
Normal file
Binary file not shown.
35
test/gptest.py
Executable file
35
test/gptest.py
Executable file
@ -0,0 +1,35 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
import CHIP_IO.GPIO as GPIO
|
||||
import time, os
|
||||
|
||||
print "SETUP CSIDO"
|
||||
GPIO.setup("CSID0", GPIO.OUT)
|
||||
|
||||
#print os.path.exists('/sys/class/gpio/gpio132')
|
||||
|
||||
print "SETUP XIO-P1"
|
||||
GPIO.setup("XIO-P1", GPIO.IN)
|
||||
#GPIO.setup("U14_13", GPIO.IN)
|
||||
|
||||
print "READING XIO-P1"
|
||||
GPIO.output("CSID0", GPIO.HIGH)
|
||||
print "HIGH", GPIO.input("XIO-P1")
|
||||
|
||||
GPIO.output("CSID0", GPIO.LOW)
|
||||
GPIO.output("CSID0", GPIO.LOW)
|
||||
time.sleep(1)
|
||||
print "LOW", GPIO.input("XIO-P1")
|
||||
|
||||
GPIO.output("CSID0", GPIO.HIGH)
|
||||
GPIO.output("CSID0", GPIO.HIGH)
|
||||
print "HIGH", GPIO.input("XIO-P1")
|
||||
time.sleep(1)
|
||||
|
||||
GPIO.output("CSID0", GPIO.LOW)
|
||||
GPIO.output("CSID0", GPIO.LOW)
|
||||
print "LOW", GPIO.input("XIO-P1")
|
||||
|
||||
print "CLEANUP"
|
||||
GPIO.cleanup()
|
||||
|
24
test/test_gpio_input.py
Executable file
24
test/test_gpio_input.py
Executable file
@ -0,0 +1,24 @@
|
||||
import pytest
|
||||
import os
|
||||
import time
|
||||
|
||||
import CHIP_IO.GPIO as GPIO
|
||||
|
||||
def teardown_module(module):
|
||||
GPIO.cleanup()
|
||||
|
||||
class TestGPIOInput:
|
||||
def test_input(self):
|
||||
GPIO.setup("CSID6", GPIO.IN)
|
||||
#returned as an int type
|
||||
input_value = GPIO.input("CSID6")
|
||||
#value read from the file will have a \n new line
|
||||
value = open('/sys/class/gpio/gpio138/value').read()
|
||||
assert int(value) == input_value
|
||||
time.sleep(30)
|
||||
GPIO.cleanup()
|
||||
|
||||
def test_direction_readback(self):
|
||||
GPIO.setup("CSID6", GPIO.IN)
|
||||
direction = GPIO.gpio_function("CSID6")
|
||||
assert direction == GPIO.IN
|
44
test/test_gpio_output.py
Normal file
44
test/test_gpio_output.py
Normal file
@ -0,0 +1,44 @@
|
||||
import pytest
|
||||
import os
|
||||
|
||||
import CHIP_IO.GPIO as GPIO
|
||||
|
||||
def teardown_module(module):
|
||||
GPIO.cleanup()
|
||||
|
||||
class TestGPIOOutput:
|
||||
def test_output_high(self):
|
||||
GPIO.setup("CSID6", GPIO.OUT)
|
||||
GPIO.output("CSID6", GPIO.HIGH)
|
||||
value = open('/sys/class/gpio/gpio138/value').read()
|
||||
assert int(value)
|
||||
GPIO.cleanup()
|
||||
|
||||
def test_output_low(self):
|
||||
GPIO.setup("CSID6", GPIO.OUT)
|
||||
GPIO.output("CSID6", GPIO.LOW)
|
||||
value = open('/sys/class/gpio/gpio138/value').read()
|
||||
assert not int(value)
|
||||
GPIO.cleanup()
|
||||
|
||||
def test_direction_readback(self):
|
||||
GPIO.setup("CSID6", GPIO.OUT)
|
||||
direction = GPIO.gpio_function("CSID6")
|
||||
assert direction == GPIO.OUT
|
||||
def test_output_greater_than_one(self):
|
||||
GPIO.setup("CSID6", GPIO.OUT)
|
||||
GPIO.output("CSID6", 2)
|
||||
value = open('/sys/class/gpio/gpio138/value').read()
|
||||
assert int(value)
|
||||
GPIO.cleanup()
|
||||
|
||||
def test_output_of_pin_not_setup(self):
|
||||
with pytest.raises(RuntimeError):
|
||||
GPIO.output("CSID7", GPIO.LOW)
|
||||
GPIO.cleanup()
|
||||
|
||||
def test_output_setup_as_input(self):
|
||||
GPIO.setup("CSID6", GPIO.IN)
|
||||
with pytest.raises(RuntimeError):
|
||||
GPIO.output("CSID6", GPIO.LOW)
|
||||
GPIO.cleanup()
|
72
test/test_gpio_setup.py
Normal file
72
test/test_gpio_setup.py
Normal file
@ -0,0 +1,72 @@
|
||||
import pytest
|
||||
import os
|
||||
|
||||
import CHIP_IO.GPIO as GPIO
|
||||
|
||||
def teardown_module(module):
|
||||
GPIO.cleanup()
|
||||
|
||||
class TestSetup:
|
||||
def test_setup_output_key(self):
|
||||
GPIO.setup("U14_37", GPIO.OUT)
|
||||
assert os.path.exists('/sys/class/gpio/gpio138')
|
||||
direction = open('/sys/class/gpio/gpio138/direction').read()
|
||||
assert direction == 'out\n'
|
||||
GPIO.cleanup()
|
||||
|
||||
def test_setup_output_name(self):
|
||||
GPIO.setup("CSID6", GPIO.OUT)
|
||||
assert os.path.exists('/sys/class/gpio/gpio138')
|
||||
direction = open('/sys/class/gpio/gpio138/direction').read()
|
||||
assert direction == 'out\n'
|
||||
GPIO.cleanup()
|
||||
|
||||
def test_setup_input_key(self):
|
||||
GPIO.setup("U14_37", GPIO.IN)
|
||||
assert os.path.exists('/sys/class/gpio/gpio138')
|
||||
direction = open('/sys/class/gpio/gpio138/direction').read()
|
||||
assert direction == 'in\n'
|
||||
GPIO.cleanup()
|
||||
|
||||
def test_setup_input_name(self):
|
||||
GPIO.setup("CSID6", GPIO.IN)
|
||||
assert os.path.exists('/sys/class/gpio/gpio138')
|
||||
direction = open('/sys/class/gpio/gpio138/direction').read()
|
||||
assert direction == 'in\n'
|
||||
GPIO.cleanup()
|
||||
|
||||
def test_setup_input_pull_up(self):
|
||||
GPIO.setup("U14_37", GPIO.IN, pull_up_down=GPIO.PUD_UP)
|
||||
assert os.path.exists('/sys/class/gpio/gpio138')
|
||||
direction = open('/sys/class/gpio/gpio138/direction').read()
|
||||
assert direction == 'in\n'
|
||||
GPIO.cleanup()
|
||||
|
||||
def test_setup_input_pull_down(self):
|
||||
GPIO.setup("U14_37", GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
|
||||
assert os.path.exists('/sys/class/gpio/gpio138')
|
||||
direction = open('/sys/class/gpio/gpio138/direction').read()
|
||||
assert direction == 'in\n'
|
||||
GPIO.cleanup()
|
||||
|
||||
def test_setup_cleanup(self):
|
||||
GPIO.setup("U14_37", GPIO.OUT)
|
||||
assert os.path.exists('/sys/class/gpio/gpio138')
|
||||
GPIO.cleanup()
|
||||
assert not os.path.exists('/sys/class/gpio/gpio138')
|
||||
|
||||
def test_setup_failed_type_error(self):
|
||||
with pytest.raises(TypeError):
|
||||
GPIO.setup("U14_37", "WEIRD")
|
||||
GPIO.cleanup()
|
||||
|
||||
def test_setup_failed_value_error(self):
|
||||
with pytest.raises(ValueError):
|
||||
GPIO.setup("U14_37", 3)
|
||||
GPIO.cleanup()
|
||||
|
||||
def test_setup_expanded_gpio(self):
|
||||
GPIO.setup("XIO-P1", GPIO.OUT)
|
||||
assert os.path.exists('/sys/class/gpio/gpio409')
|
||||
GPIO.cleanup()
|
||||
assert not os.path.exists('/sys/class/gpio/gpio409')
|
11
tox.ini
Normal file
11
tox.ini
Normal file
@ -0,0 +1,11 @@
|
||||
# Tox (http://tox.testrun.org/) is a tool for running tests
|
||||
# in multiple virtualenvs. This configuration file will run the
|
||||
# test suite on all supported python versions. To use it, "pip install tox"
|
||||
# and then run "tox" from this directory.
|
||||
|
||||
[tox]
|
||||
envlist = py27, py34
|
||||
|
||||
[testenv]
|
||||
commands =
|
||||
deps =
|
Reference in New Issue
Block a user