Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
ca81b28
Added docstrings using Google syntax and Sphinx support to generate t…
dplanella Dec 1, 2017
cfdf755
Made kernel version check to happen only if running on a beaglebone. …
dplanella Dec 1, 2017
1913471
Use the default readthedocs theme
dplanella Dec 1, 2017
7de4ccb
Use readthedocs theme if building docs there, remove redundand search…
dplanella Dec 1, 2017
c37cf6a
Readthedocs theme tweaks
dplanella Dec 1, 2017
7d33f1d
Removed redundant TOC, added global description
dplanella Dec 1, 2017
d1608b4
Added UART documentation
dplanella Dec 1, 2017
152678a
Added documentation badge
dplanella Dec 1, 2017
6dc5313
Added ADC API docs, fixed UART module definition
dplanella Dec 1, 2017
93b2a23
API docs: added SPI module
dplanella Dec 1, 2017
746c22c
Added SPI module attribute docs
dplanella Dec 2, 2017
c379e30
Added Python badges to README file
dplanella Dec 2, 2017
c84bc31
Added SPI pins table and first shot at GPIO module. Functions still n…
dplanella Dec 2, 2017
59e4375
Merge branch 'readthedocs' of https://github.com/dplanella/adafruit-b…
dplanella Dec 2, 2017
2b7de7f
Documented the API docs build process
dplanella Dec 2, 2017
32e63c9
Added docstrings using Google syntax and Sphinx support to generate t…
dplanella Dec 1, 2017
47aa372
Made kernel version check to happen only if running on a beaglebone. …
dplanella Dec 1, 2017
fa5a786
Use the default readthedocs theme
dplanella Dec 1, 2017
2433729
Use readthedocs theme if building docs there, remove redundand search…
dplanella Dec 1, 2017
be0a908
Readthedocs theme tweaks
dplanella Dec 1, 2017
f9212b2
Removed redundant TOC, added global description
dplanella Dec 1, 2017
0dc0ca7
Added UART documentation
dplanella Dec 1, 2017
f0487a7
Added documentation badge
dplanella Dec 1, 2017
330b0f2
Added ADC API docs, fixed UART module definition
dplanella Dec 1, 2017
191b214
API docs: added SPI module
dplanella Dec 1, 2017
2769792
Added SPI module attribute docs
dplanella Dec 2, 2017
e955585
Added Python badges to README file
dplanella Dec 2, 2017
4ab134b
Added SPI pins table and first shot at GPIO module. Functions still n…
dplanella Dec 2, 2017
6e0cf94
Documented the API docs build process
dplanella Dec 2, 2017
38f152c
Merge branch 'readthedocs' of https://github.com/dplanella/adafruit-b…
dplanella Dec 2, 2017
3079497
Update README.md
dplanella Dec 2, 2017
a86300f
Added some more API doc content
dplanella Dec 2, 2017
0b49916
Sync from upstream master
dplanella Dec 2, 2017
6e9ea92
Minor documentation and configuration improvements
dplanella Dec 2, 2017
f12715b
Finished documenting GPIO
dplanella Dec 2, 2017
55aa0a9
rST fixes
dplanella Dec 2, 2017
9d6aa46
Update README.md
dplanella Dec 2, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,10 @@ m4/lt~obsolete.m4
missing
source/Makefile.in
test-driver
docs/_build
docs/_static
docs/_templates

# vim temp files
*~
*.swp
137 changes: 101 additions & 36 deletions Adafruit_BBIO/Encoder.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,72 @@
#!/usr/bin/python

"""Quadrature Encoder Pulse interface.

This module enables access to the enhanced Quadrature Encoder Pulse (eQEP)
channels, which can be used to seamlessly interface with rotary encoder
hardware.

The channel identifiers are available as module variables :data:`eQEP0`,
:data:`eQEP1`, :data:`eQEP2` and :data:`eQEP2b`.

======= ======= ======= ===================================================
Channel Pin A Pin B Notes
======= ======= ======= ===================================================
eQEP0 P9.27 P9.92
eQEP1 P8.33 P8.35 Only available with video disabled
eQEP2 P8.11 P8.12 Only available with eQEP2b unused (same channel)
eQEP2b P8.41 P8.42 Only available with video disabled and eQEP2 unused
======= ======= ======= ===================================================

Example:
To use the module, you can connect a rotary encoder to your Beaglebone
and then simply instantiate the :class:`RotaryEncoder` class to read its
position::

from Adafruit_BBIO.Encoder import RotaryEncoder, eQEP2

# Instantiate the class to access channel eQEP2, and initialize
# that channel
myEncoder = RotaryEncoder(eQEP2)

# Get the current position
cur_position = myEncoder.position

# Set the current position
next_position = 15
myEncoder.position = next_position

# Reset position to 0
myEncoder.zero()

# Change mode to relative (default is absolute)
# You can use setAbsolute() to change back to absolute
# Absolute: the position starts at zero and is incremented or
# decremented by the encoder's movement
# Relative: the position is reset when the unit timer overflows.
myEncoder.setRelative()

# Read the current mode (0: absolute, 1: relative)
# Mode can also be set as a property
mode = myEncoder.mode

# Get the current frequency of update in Hz
freq = myEncoder.frequency

# Set the update frequency to 1 kHz
myEncoder.frequency = 1000

# Disable the eQEP channel
myEncoder.disable()

# Check if the channel is enabled
# The 'enabled' property is read-only
# Use the enable() and disable() methods to
# safely enable or disable the module
isEnabled = myEncoder.enabled

"""

from subprocess import check_output, STDOUT, CalledProcessError
import os
import logging
Expand All @@ -8,20 +75,28 @@
import platform

(major, minor, patch) = platform.release().split("-")[0].split(".")
if not (int(major) >= 4 and int(minor) >= 4):
if not (int(major) >= 4 and int(minor) >= 4) \
and platform.node() == 'beaglebone':
raise ImportError(
'The Encoder module requires Linux kernel version >= 4.4.x.\n'
'Please upgrade your kernel to use this module.\n'
'Your Linux kernel version is {}.'.format(platform.release()))


# eQEP module channel identifiers
# eQEP 2 and 2b are the same channel, exposed on two different sets of pins,
# which are mutually exclusive
eQEP0 = 0
'''eQEP0 channel identifier, pin A-- P9.92, pin B-- P9.27 on Beaglebone
Black.'''
eQEP1 = 1
'''eQEP1 channel identifier, pin A-- P9.35, pin B-- P9.33 on Beaglebone
Black.'''
eQEP2 = 2
'''eQEP2 channel identifier, pin A-- P8.12, pin B-- P8.11 on Beaglebone Black.
Note that there is only one eQEP2 module. This is one alternative set of pins
where it is exposed, which is mutually-exclusive with eQEP2b'''
eQEP2b = 3
'''eQEP2(b) channel identifier, pin A-- P8.41, pin B-- P8.42 on Beaglebone
Black. Note that there is only one eQEP2 module. This is one alternative set of
pins where it is exposed, which is mutually-exclusive with eQEP2'''

# Definitions to initialize the eQEP modules
_OCP_PATH = "/sys/devices/platform/ocp"
Expand All @@ -37,7 +112,7 @@
]


class eQEP(object):
class _eQEP(object):
'''Enhanced Quadrature Encoder Pulse (eQEP) module class. Abstraction
for either of the three available channels (eQEP0, eQEP1, eQEP2) on
the Beaglebone'''
Expand All @@ -51,7 +126,7 @@ def fromdict(cls, d):
return cls(**df)

def __init__(self, channel, pin_A, pin_B, sys_path):
'''Initialize the eQEP module
'''Initialize the given eQEP channel

Attributes:
channel (str): eQEP channel name. E.g. "eQEP0", "eQEP1", etc.
Expand Down Expand Up @@ -79,21 +154,11 @@ class RotaryEncoder(object):
'''
Rotary encoder class abstraction to control a given QEP channel.

Constructor:
eqep_num: QEP object that determines which channel to control

Properties:
position: current position of the encoder
frequency: frequency at which the encoder reports new positions
enabled: (read only) true if the module is enabled, false otherwise
mode: current mode of the encoder (absolute: 0, relative: 1)

Methods:
enable: enable the QEP channel
disable: disable the QEP channel
setAbsolute: shortcut for setting the mode to absolute
setRelative: shortcut for setting the mode to relative
zero: shortcut for setting the position to 0
Args:
eqep_num (int): determines which eQEP pins are set up.
Allowed values: EQEP0, EQEP1, EQEP2 or EQEP2b,
based on which pins the physical rotary encoder
is connected to.
'''

def _run_cmd(self, cmd):
Expand All @@ -117,15 +182,8 @@ def _config_pin(self, pin):
self._run_cmd(["config-pin", pin, "qep"])

def __init__(self, eqep_num):
'''Creates an instance of the class RotaryEncoder.
'''Creates an instance of the class RotaryEncoder.'''

Arguments:
eqep_num: determines which eQEP pins are set up.
Allowed values: EQEP0, EQEP1, EQEP2 or EQEP2b,
based on which pins the physical rotary encoder
is connected to.

'''
# nanoseconds factor to convert period to frequency and back
self._NS_FACTOR = 1000000000

Expand All @@ -134,7 +192,7 @@ def __init__(self, eqep_num):
self._logger.addHandler(logging.NullHandler())

# Initialize the eQEP channel structures
self._eqep = eQEP.fromdict(_eQEP_DEFS[eqep_num])
self._eqep = _eQEP.fromdict(_eQEP_DEFS[eqep_num])
self._logger.info(
"Configuring: {}, pin A: {}, pin B: {}, sys path: {}".format(
self._eqep.channel, self._eqep.pin_A, self._eqep.pin_B,
Expand All @@ -154,8 +212,8 @@ def __init__(self, eqep_num):
def enabled(self):
'''Returns the enabled status of the module:

true: module is enabled
false: module is disabled
Returns:
bool: True if the eQEP channel is enabled, False otherwise.
'''
isEnabled = bool(int(self._eqep.node.enabled))

Expand All @@ -164,7 +222,11 @@ def enabled(self):
def _setEnable(self, enabled):
'''Turns the eQEP hardware ON or OFF

value (int): 1 represents enabled, 0 is disabled
Args:
enabled (int): enable the module with 1, disable it with 0.

Raises:
ValueError: if the value for enabled is < 0 or > 1

'''
enabled = int(enabled)
Expand All @@ -189,8 +251,11 @@ def disable(self):

@property
def mode(self):
'''Returns the mode the eQEP hardware is in (absolute or relative).
'''Returns the mode the eQEP hardware is in.

Returns:
int: 0 if the eQEP channel is configured in absolute mode,
1 if configured in relative mode.
'''
mode = int(self._eqep.node.mode)

Expand Down Expand Up @@ -264,14 +329,13 @@ def position(self, position):
self._logger.debug("Set position: Channel {}, position: {}".format(
self._eqep.channel, position))


@property
def frequency(self):
'''Sets the frequency in Hz at which the driver reports
new positions.

'''
frequency = self._NS_FACTOR / int(self._eqep.node.period)
frequency = self._NS_FACTOR / int(self._eqep.node.period)

self._logger.debug(
"Set frequency(): Channel {}, frequency: {} Hz, "
Expand All @@ -298,3 +362,4 @@ def zero(self):
'''Resets the current position to 0'''

self.position = 0

8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
# Adafruit BeagleBone I/O Python Library (Adafruit_BBIO)
# Adafruit Beaglebone I/O Python API

* Adafruit_BBIO is a set of Python tools to allow [GPIO](README.md#gpio-setup), [PWM](README.md#pwm), [ADC](README.md#adc), [UART](README.md#uart) and [eQEP](README.md#eqep) (Quadrature Encoder) access on the BeagleBone
[![Documentation Status](https://readthedocs.org/projects/adafruit-beaglebone-io-python/badge/?version=latest)](http://adafruit-beaglebone-io-python.readthedocs.io/en/latest/?badge=latest)
[![PyPI version](https://badge.fury.io/py/Adafruit_BBIO.svg)](https://badge.fury.io/py/Adafruit_BBIO)
[![PyPI pyversions](https://img.shields.io/pypi/pyversions/Adafruit_BBIO.svg)](https://pypi.python.org/pypi/Adafruit_BBIO/)

Adafruit BBIO is an API to enable [GPIO](README.md#gpio-setup), [PWM](README.md#pwm), [ADC](README.md#adc), [UART](README.md#uart), [SPI](README.md#spi) and [eQEP](README.md#eqep) (Quadrature Encoder) hardware access from Python applications running on the Beaglebone.

* It is recommended to use an [official BeagleBoard.org Debian image](https://beagleboard.org/latest-images)
* **Currently recommended image: [Debian 9.2 "Stretch" iot (2017-10-29)](https://elinux.org/Beagleboard:BeagleBoneBlack_Debian#microSD.2FStandalone:_.28stretch-iot.29_.28All_BeagleBone_Variants_.26_PocketBeagle.29)**
Expand Down
20 changes: 20 additions & 0 deletions docs/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Minimal makefile for Sphinx documentation
#

# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
SPHINXPROJ = Adafruit-BBIO
SOURCEDIR = .
BUILDDIR = _build

# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

.PHONY: help Makefile

# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
68 changes: 68 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# Generating API documentation

This folder contains the required files to automatically generate the Adafruit Beaglebone I/O Python API documentation, partly from the code docstrings and partly from a reStructuredText-formatted `index.rst` file.

```
├── conf.py <- Sphinx configuration file
├── index.rst <- Documentation will be generated based on this
└── Makefile <- Auxiliary Makefile to build documentation
```

The tools used are [Sphinx](http://www.sphinx-doc.org) to extract the documentation and publish it in HTML format for online viewing, in combination with [Readthedocs](http://readthedocs.io), which automatically executes Sphinx via webhooks triggered by Github commits, and publishes the resulting docs for all tracked branches. Generally Readthedocs will be set up to track stable release branches and master.

## Building the documentation

The documentation can also be built on a local checkout of the project:

First ensure you've got sphinx installed:

```
sudo pip install sphinx
```

Then you can build the HTML docs:

```
cd docs
make html
```

Once Sphinx has built the documentation, you can open the main index file with your browser: `_build/html/index.html`

Notes:

- The build process will create three additional temporary directories: `_build`, `_static` and `_templates` that will not be version-controlled. You can use `make clean` to remove their contents if you wish to do so.
- The html theme from files built locally is different from the online readthedocs theme. See the `docs/config.py` `html_theme` variable. The main reason is not to introduce another dependency to install the readthedocs theme, but as a side effect, it also helps visually distinguishing the locally-built documentation from the online version.

## Readthedocs maintenance

At every release that includes documenation (most probably 1.0.10 will be the first one), the release's branch needs to be selected in the web UI and marked as active.

After this, documentation will automatically be generated and published for that release. It will be available at the same URL as the main documentation, and a link with the version number will be shown, where it can be accessed from.

Optionally, the 'stable' URL slug can be pointed to that release branch. Otherwise, the 'stable' slug can also be deactivated for less maintenance overhead.

The 'latest' URL slug will always be pointing at the repo's master branch.

## Notes

Ideally, all API documentation would be written in the source files as Python docstrings, and sphinx would simply extract it. This is actually the case with the `Encoder` module, which is pure Python.

However, most of the code is written as C extensions. While they do provide docstrings once they are built, Sphinx does not natively support extracting them. There is [a workaround](https://stackoverflow.com/a/30110104/9022675) to do this, but it involves first building the extensions, installing them and hardcoding a path. While it might work for locally-built documentation, it's unlikely that readthedocs support this option.

For the sake of keeping things simple and with less maintenance, the approach of documenting the C-generated API in the `index.rst` file has been taken.

This has the advantage of having a definition of the API in one place, but it also poses the disadvantage of some duplication, as the C modules do define some docstrings for their objects. Then again, the API itself has hardly changed in the last few years, and the Beaglebone is a mature platform, so it's unlikely that this will pose a significant maintenance overhead.

- The documentation in the `index.rst` file is written in [reStructuredText](http://docutils.sourceforge.net/rst.html), extended with Sphinx markup for defining the objects.
- The documentation in Python modules follows the Google readable docstring markup, which also builds upon reStructuredText and is fully supported by Sphinx.

## Further reference

- [Google readable docstring markup](https://google.github.io/styleguide/pyguide.html?showone=Comments#Comments)
- [Google docstring examples](http://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_google.html)
- [More Google docstring examples](http://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_google.html)
- [Sphinx docstring markup](http://www.sphinx-doc.org/en/stable/domains.html#the-python-domain)
- [reStructuredText primer](http://www.sphinx-doc.org/en/stable/rest.html#rst-primer)


Loading