84 Commits
Mk1 ... master

Author SHA1 Message Date
shrkey
fc7932dc16 change version 2016-10-17 15:48:18 +01:00
shrkey
53d9450dd2 changed release code 2016-10-17 15:45:53 +01:00
shrkey
f3bb28e576 messages on stepper example 2016-10-04 22:48:21 +01:00
shrkey
039ca68b27 redo examples 2016-10-04 22:41:57 +01:00
shrkey
fd9c62fc6a quick code test 2016-10-04 21:58:07 +01:00
shrkey
bca4e41e13 Merge remote-tracking branch 'origin/master'
# Conflicts:
#	640imutest.py
2016-09-30 00:03:26 +01:00
shrkey
a054b3ce77 python3 prints 2016-09-30 00:02:03 +01:00
shrkey
ec8186a1e6 Merge pull request #4 from darkwaterfoundation/dev
Dev
2016-09-29 23:57:09 +01:00
shrkey
3584447417 initial imu 2016-09-29 23:37:08 +01:00
shrkey
243af588da servo update 2016-09-29 22:46:26 +01:00
shrkey
a6281a309f typo 2016-09-29 22:41:17 +01:00
shrkey
f72925a04a back update 2016-09-29 22:40:05 +01:00
shrkey
7b86aca61f remove legacy 2016-09-29 22:36:20 +01:00
shrkey
ac31d90e5f grr 2016-09-29 22:32:19 +01:00
shrkey
865f8a2588 grrr 2016-09-29 22:30:27 +01:00
shrkey
6d3bb50e50 removed dodgy . 2016-09-29 22:29:04 +01:00
shrkey
1c5f1d160f test script update 2016-09-29 22:23:31 +01:00
shrkey
d08e939f40 updating for python 3 2016-09-29 22:19:47 +01:00
shrkey
9114eba70f Merge pull request #3 from darkwaterfoundation/dev
Dev
2016-09-25 23:45:36 +01:00
shrkey
27932106c1 Merge pull request #2 from darkwaterfoundation/master
Merge pull request #1 from darkwaterfoundation/dev
2016-09-25 23:43:58 +01:00
shrkey
7c9cfce771 changed function name for stepper speed 2016-09-25 23:41:01 +01:00
shrkey
c3a7b3204b missed a print 2016-09-18 21:19:18 +01:00
shrkey
d6d81fba91 change print statements 2016-09-18 21:16:57 +01:00
shrkey
9283b94bcb quick import test 2016-09-18 21:12:16 +01:00
shrkey
7ab733ae2c more debug stuff 2016-09-18 21:07:39 +01:00
shrkey
365eae3351 removed debug prints 2016-09-18 21:05:41 +01:00
shrkey
e23067e7fc Merge pull request #1 from darkwaterfoundation/dev
Update from Dev
2016-09-17 21:36:56 +01:00
shrkey
3cfca22056 another test 2016-09-17 21:25:45 +01:00
shrkey
8ca4d47d0c mode fix 2016-09-17 21:19:53 +01:00
shrkey
ab0b820d3b release bug 2016-09-17 21:12:57 +01:00
shrkey
171cdd37d1 setmode bug 2016-09-17 21:11:33 +01:00
shrkey
47bdab0e5a dodgy c synatx in there :) 2016-09-17 21:09:42 +01:00
shrkey
d5fbd166b8 updated stepper 2016-09-17 21:04:32 +01:00
shrkey
bf07ee7276 New motor code to handle IN/IN mode 2016-09-17 20:52:00 +01:00
shrkey
ab26bc2885 rejig 2016-09-13 23:16:38 +01:00
shrkey
78eea1026a remove pwm 2016-09-13 23:00:54 +01:00
shrkey
afe2d9c0b8 oops 2016-09-13 22:56:03 +01:00
shrkey
d382b174ff stepper test 2016-09-13 22:53:18 +01:00
shrkey
3d55fefa1a more indents 2016-09-13 22:47:41 +01:00
shrkey
c7db60bf6f indentation 2016-09-13 22:45:21 +01:00
shrkey
89b8f62feb stepper library testing 2016-09-13 22:38:54 +01:00
shrkey
1533b9cdf0 stepper hide for testing 2016-09-12 18:55:35 +01:00
shrkey
c9b5bcfba2 hide stepper for now 2016-09-12 18:48:54 +01:00
shrkey
8cb8d76f35 quick update test 2016-09-12 18:41:28 +01:00
shrkey
2c5d3217bc changed pin for new boards 2016-08-23 22:54:59 +01:00
shrkey
e01f2f2d7d changed mode pin 2016-08-23 22:19:06 +01:00
shrkey
e85a329ed5 updated for new boards 2016-08-23 21:55:33 +01:00
shrkey
d56cf2f666 A few changes
Added correction factor
Change motor test code
2016-08-15 21:21:43 +01:00
shrkey
95d9be87cd added correction factor 2016-08-15 20:30:58 +01:00
shrkey
6b2e637272 updated test 2016-08-15 19:40:32 +01:00
shrkey
3c22dafef4 change frequency 2016-08-15 19:35:30 +01:00
shrkey
68435b7d55 debug servo code 2016-08-15 19:05:19 +01:00
shrkey
937856da09 debug 2016-08-15 18:48:03 +01:00
shrkey
a9a91ec248 debug 2016-08-15 18:42:45 +01:00
shrkey
da1380602c test update 2016-08-15 18:37:24 +01:00
shrkey
8f93a7e049 debug 2016-08-15 16:16:33 +01:00
shrkey
3c951c5e21 debug 2016-08-15 16:14:53 +01:00
shrkey
93f7a539cd debug 2016-08-15 16:08:37 +01:00
shrkey
5bc3c09de4 debug 2016-08-15 16:04:53 +01:00
shrkey
7de6192a98 debug 2016-08-15 15:59:51 +01:00
shrkey
d74563afc3 updated for servos 2016-08-15 15:57:18 +01:00
shrkey
f678367dea Updated test script 2016-08-15 14:27:46 +01:00
shrkey
6f88dabdf3 added math 2016-08-15 14:10:26 +01:00
shrkey
d828daca57 frequency change 2016-08-15 14:09:11 +01:00
shrkey
29a1874f1d bug fix - indents 2016-08-15 13:58:33 +01:00
shrkey
234acb9ac6 update test 2016-08-15 13:48:34 +01:00
shrkey
b2f391f9fa changed methods 2016-08-15 13:17:02 +01:00
shrkey
f26dc9d572 Moved example code 2016-08-15 13:12:43 +01:00
shrkey
c70a4e71e0 change to method names 2016-08-13 20:48:11 +01:00
shrkey
0bb9e2a7c6 Update to code
Needs testing
2016-08-13 20:17:22 +01:00
shrkey
4bcc4ff404 Got rid of non-python files 2016-08-13 19:23:17 +01:00
shrkey
9a20c84464 Update README.md 2016-07-08 23:44:52 +01:00
shrkey
499deed69f Added CC 2016-07-08 23:41:41 +01:00
shrkey
581ff24fbd Mk4 hardware files 2016-07-08 23:38:16 +01:00
shrkey
5b2b5f081c Update README.md 2016-07-02 18:11:24 +01:00
shrkey
3c7cc20dd8 Added hardware directory 2016-07-02 18:10:40 +01:00
shrkey
e33451c5b8 readme files 2016-04-30 23:25:32 +01:00
shrkey
1cc99e86df Change structure 2016-04-30 22:52:49 +01:00
shrkey
b4a9998ca6 shorten test 2016-04-30 21:08:32 +01:00
shrkey
dfbdc0c658 Updated for mk3 2016-04-30 21:06:42 +01:00
shrkey
3d5543e135 Brought inline with escape code 2016-04-03 14:04:44 +01:00
shrkey
f05b4403be Added updates for getPWM and correctionFactor 2016-04-03 13:02:12 +01:00
shrkey
3511873a7c Removed Mk1 2016-02-28 21:46:41 +00:00
shrkey
ffeabbe0d8 Updated for Mk2 2016-02-28 21:46:00 +00:00
22 changed files with 2821 additions and 558 deletions

15
MANIFEST Normal file
View File

@@ -0,0 +1,15 @@
# file GENERATED by distutils, do NOT edit
setup.py
darkwater_640\GPIO.py
darkwater_640\I2C.py
darkwater_640\PCA9685.py
darkwater_640\Platform.py
darkwater_640\SPI.py
darkwater_640\__init__.py
darkwater_640\darkwater_640.py
darkwater_640\mpu9250.py
darkwater_640\smbus.py
examples\640imutest.py
examples\640motortest.py
examples\640servotest.py
examples\640steppertest.py

1
MANIFEST.in Normal file
View File

@@ -0,0 +1 @@
recursive-include examples *

View File

@@ -1,4 +1,4 @@
640 Mk1 Python driver 640 Python driver
======================= =======================
Based on the [Adafruit Python Library for DC + Stepper Motor HAT](https://github.com/adafruit/Adafruit-Motor-HAT-Python-Library) Based on the [Adafruit Python Library for DC + Stepper Motor HAT](https://github.com/adafruit/Adafruit-Motor-HAT-Python-Library)

426
darkwater_640/GPIO.py Normal file
View File

@@ -0,0 +1,426 @@
# Copyright (c) 2014 Adafruit Industries
# Author: Tony DiCola
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
import Platform as Platform
OUT = 0
IN = 1
HIGH = True
LOW = False
RISING = 1
FALLING = 2
BOTH = 3
PUD_OFF = 0
PUD_DOWN = 1
PUD_UP = 2
class BaseGPIO(object):
"""Base class for implementing simple digital IO for a platform.
Implementors are expected to subclass from this and provide an implementation
of the setup, output, and input functions."""
def setup(self, pin, mode, pull_up_down=PUD_OFF):
"""Set the input or output mode for a specified pin. Mode should be
either OUT or IN."""
raise NotImplementedError
def output(self, pin, value):
"""Set the specified pin the provided high/low value. Value should be
either HIGH/LOW or a boolean (true = high)."""
raise NotImplementedError
def input(self, pin):
"""Read the specified pin and return HIGH/true if the pin is pulled high,
or LOW/false if pulled low."""
raise NotImplementedError
def set_high(self, pin):
"""Set the specified pin HIGH."""
self.output(pin, HIGH)
def set_low(self, pin):
"""Set the specified pin LOW."""
self.output(pin, LOW)
def is_high(self, pin):
"""Return true if the specified pin is pulled high."""
return self.input(pin) == HIGH
def is_low(self, pin):
"""Return true if the specified pin is pulled low."""
return self.input(pin) == LOW
# Basic implementation of multiple pin methods just loops through pins and
# processes each one individually. This is not optimal, but derived classes can
# provide a more optimal implementation that deals with groups of pins
# simultaneously.
# See MCP230xx or PCF8574 classes for examples of optimized implementations.
def output_pins(self, pins):
"""Set multiple pins high or low at once. Pins should be a dict of pin
name to pin value (HIGH/True for 1, LOW/False for 0). All provided pins
will be set to the given values.
"""
# General implementation just loops through pins and writes them out
# manually. This is not optimized, but subclasses can choose to implement
# a more optimal batch output implementation. See the MCP230xx class for
# example of optimized implementation.
for pin, value in iter(pins.items()):
self.output(pin, value)
def setup_pins(self, pins):
"""Setup multiple pins as inputs or outputs at once. Pins should be a
dict of pin name to pin type (IN or OUT).
"""
# General implementation that can be optimized by derived classes.
for pin, value in iter(pins.items()):
self.setup(pin, value)
def input_pins(self, pins):
"""Read multiple pins specified in the given list and return list of pin values
GPIO.HIGH/True if the pin is pulled high, or GPIO.LOW/False if pulled low.
"""
# General implementation that can be optimized by derived classes.
return [self.input(pin) for pin in pins]
def add_event_detect(self, pin, edge):
"""Enable edge detection events for a particular GPIO channel. Pin
should be type IN. Edge must be RISING, FALLING or BOTH.
"""
raise NotImplementedError
def remove_event_detect(self, pin):
"""Remove edge detection for a particular GPIO channel. Pin should be
type IN.
"""
raise NotImplementedError
def add_event_callback(self, pin, callback):
"""Add a callback for an event already defined using add_event_detect().
Pin should be type IN.
"""
raise NotImplementedError
def event_detected(self, pin):
"""Returns True if an edge has occured on a given GPIO. You need to
enable edge detection using add_event_detect() first. Pin should be
type IN.
"""
raise NotImplementedError
def wait_for_edge(self, pin, edge):
"""Wait for an edge. Pin should be type IN. Edge must be RISING,
FALLING or BOTH."""
raise NotImplementedError
def cleanup(self, pin=None):
"""Clean up GPIO event detection for specific pin, or all pins if none
is specified.
"""
raise NotImplementedError
# helper functions useful to derived classes
def _validate_pin(self, pin):
# Raise an exception if pin is outside the range of allowed values.
if pin < 0 or pin >= self.NUM_GPIO:
raise ValueError('Invalid GPIO value, must be between 0 and {0}.'.format(self.NUM_GPIO))
def _bit2(self, src, bit, val):
bit = 1 << bit
return (src | bit) if val else (src & ~bit)
class RPiGPIOAdapter(BaseGPIO):
"""GPIO implementation for the Raspberry Pi using the RPi.GPIO library."""
def __init__(self, rpi_gpio, mode=None):
self.rpi_gpio = rpi_gpio
# Suppress warnings about GPIO in use.
rpi_gpio.setwarnings(False)
# Setup board pin mode.
if mode == rpi_gpio.BOARD or mode == rpi_gpio.BCM:
rpi_gpio.setmode(mode)
elif mode is not None:
raise ValueError('Unexpected value for mode. Must be BOARD or BCM.')
else:
# Default to BCM numbering if not told otherwise.
rpi_gpio.setmode(rpi_gpio.BCM)
# Define mapping of Adafruit GPIO library constants to RPi.GPIO constants.
self._dir_mapping = { OUT: rpi_gpio.OUT,
IN: rpi_gpio.IN }
self._pud_mapping = { PUD_OFF: rpi_gpio.PUD_OFF,
PUD_DOWN: rpi_gpio.PUD_DOWN,
PUD_UP: rpi_gpio.PUD_UP }
self._edge_mapping = { RISING: rpi_gpio.RISING,
FALLING: rpi_gpio.FALLING,
BOTH: rpi_gpio.BOTH }
def setup(self, pin, mode, pull_up_down=PUD_OFF):
"""Set the input or output mode for a specified pin. Mode should be
either OUTPUT or INPUT.
"""
self.rpi_gpio.setup(pin, self._dir_mapping[mode],
pull_up_down=self._pud_mapping[pull_up_down])
def output(self, pin, value):
"""Set the specified pin the provided high/low value. Value should be
either HIGH/LOW or a boolean (true = high).
"""
self.rpi_gpio.output(pin, value)
def input(self, pin):
"""Read the specified pin and return HIGH/true if the pin is pulled high,
or LOW/false if pulled low.
"""
return self.rpi_gpio.input(pin)
def input_pins(self, pins):
"""Read multiple pins specified in the given list and return list of pin values
GPIO.HIGH/True if the pin is pulled high, or GPIO.LOW/False if pulled low.
"""
# maybe rpi has a mass read... it would be more efficient to use it if it exists
return [self.rpi_gpio.input(pin) for pin in pins]
def add_event_detect(self, pin, edge, callback=None, bouncetime=-1):
"""Enable edge detection events for a particular GPIO channel. Pin
should be type IN. Edge must be RISING, FALLING or BOTH. Callback is a
function for the event. Bouncetime is switch bounce timeout in ms for
callback
"""
kwargs = {}
if callback:
kwargs['callback']=callback
if bouncetime > 0:
kwargs['bouncetime']=bouncetime
self.rpi_gpio.add_event_detect(pin, self._edge_mapping[edge], **kwargs)
def remove_event_detect(self, pin):
"""Remove edge detection for a particular GPIO channel. Pin should be
type IN.
"""
self.rpi_gpio.remove_event_detect(pin)
def add_event_callback(self, pin, callback):
"""Add a callback for an event already defined using add_event_detect().
Pin should be type IN.
"""
self.rpi_gpio.add_event_callback(pin, callback)
def event_detected(self, pin):
"""Returns True if an edge has occured on a given GPIO. You need to
enable edge detection using add_event_detect() first. Pin should be
type IN.
"""
return self.rpi_gpio.event_detected(pin)
def wait_for_edge(self, pin, edge):
"""Wait for an edge. Pin should be type IN. Edge must be RISING,
FALLING or BOTH.
"""
self.rpi_gpio.wait_for_edge(pin, self._edge_mapping[edge])
def cleanup(self, pin=None):
"""Clean up GPIO event detection for specific pin, or all pins if none
is specified.
"""
if pin is None:
self.rpi_gpio.cleanup()
else:
self.rpi_gpio.cleanup(pin)
class AdafruitBBIOAdapter(BaseGPIO):
"""GPIO implementation for the Beaglebone Black using the Adafruit_BBIO
library.
"""
def __init__(self, bbio_gpio):
self.bbio_gpio = bbio_gpio
# Define mapping of Adafruit GPIO library constants to RPi.GPIO constants.
self._dir_mapping = { OUT: bbio_gpio.OUT,
IN: bbio_gpio.IN }
self._pud_mapping = { PUD_OFF: bbio_gpio.PUD_OFF,
PUD_DOWN: bbio_gpio.PUD_DOWN,
PUD_UP: bbio_gpio.PUD_UP }
self._edge_mapping = { RISING: bbio_gpio.RISING,
FALLING: bbio_gpio.FALLING,
BOTH: bbio_gpio.BOTH }
def setup(self, pin, mode, pull_up_down=PUD_OFF):
"""Set the input or output mode for a specified pin. Mode should be
either OUTPUT or INPUT.
"""
self.bbio_gpio.setup(pin, self._dir_mapping[mode],
pull_up_down=self._pud_mapping[pull_up_down])
def output(self, pin, value):
"""Set the specified pin the provided high/low value. Value should be
either HIGH/LOW or a boolean (true = high).
"""
self.bbio_gpio.output(pin, value)
def input(self, pin):
"""Read the specified pin and return HIGH/true if the pin is pulled high,
or LOW/false if pulled low.
"""
return self.bbio_gpio.input(pin)
def input_pins(self, pins):
"""Read multiple pins specified in the given list and return list of pin values
GPIO.HIGH/True if the pin is pulled high, or GPIO.LOW/False if pulled low.
"""
# maybe bbb has a mass read... it would be more efficient to use it if it exists
return [self.bbio_gpio.input(pin) for pin in pins]
def add_event_detect(self, pin, edge, callback=None, bouncetime=-1):
"""Enable edge detection events for a particular GPIO channel. Pin
should be type IN. Edge must be RISING, FALLING or BOTH. Callback is a
function for the event. Bouncetime is switch bounce timeout in ms for
callback
"""
kwargs = {}
if callback:
kwargs['callback']=callback
if bouncetime > 0:
kwargs['bouncetime']=bouncetime
self.bbio_gpio.add_event_detect(pin, self._edge_mapping[edge], **kwargs)
def remove_event_detect(self, pin):
"""Remove edge detection for a particular GPIO channel. Pin should be
type IN.
"""
self.bbio_gpio.remove_event_detect(pin)
def add_event_callback(self, pin, callback, bouncetime=-1):
"""Add a callback for an event already defined using add_event_detect().
Pin should be type IN. Bouncetime is switch bounce timeout in ms for
callback
"""
kwargs = {}
if bouncetime > 0:
kwargs['bouncetime']=bouncetime
self.bbio_gpio.add_event_callback(pin, callback, **kwargs)
def event_detected(self, pin):
"""Returns True if an edge has occured on a given GPIO. You need to
enable edge detection using add_event_detect() first. Pin should be
type IN.
"""
return self.bbio_gpio.event_detected(pin)
def wait_for_edge(self, pin, edge):
"""Wait for an edge. Pin should be type IN. Edge must be RISING,
FALLING or BOTH.
"""
self.bbio_gpio.wait_for_edge(pin, self._edge_mapping[edge])
def cleanup(self, pin=None):
"""Clean up GPIO event detection for specific pin, or all pins if none
is specified.
"""
if pin is None:
self.bbio_gpio.cleanup()
else:
self.bbio_gpio.cleanup(pin)
class AdafruitMinnowAdapter(BaseGPIO):
"""GPIO implementation for the Minnowboard + MAX using the mraa library"""
def __init__(self,mraa_gpio):
self.mraa_gpio = mraa_gpio
# Define mapping of Adafruit GPIO library constants to mraa constants
self._dir_mapping = { OUT: self.mraa_gpio.DIR_OUT,
IN: self.mraa_gpio.DIR_IN }
self._pud_mapping = { PUD_OFF: self.mraa_gpio.MODE_STRONG,
PUD_UP: self.mraa_gpio.MODE_HIZ,
PUD_DOWN: self.mraa_gpio.MODE_PULLDOWN }
self._edge_mapping = { RISING: self.mraa_gpio.EDGE_RISING,
FALLING: self.mraa_gpio.EDGE_FALLING,
BOTH: self.mraa_gpio.EDGE_BOTH }
def setup(self,pin,mode):
"""Set the input or output mode for a specified pin. Mode should be
either DIR_IN or DIR_OUT.
"""
self.mraa_gpio.Gpio.dir(self.mraa_gpio.Gpio(pin),self._dir_mapping[mode])
def output(self,pin,value):
"""Set the specified pin the provided high/low value. Value should be
either 1 (ON or HIGH), or 0 (OFF or LOW) or a boolean.
"""
self.mraa_gpio.Gpio.write(self.mraa_gpio.Gpio(pin), value)
def input(self,pin):
"""Read the specified pin and return HIGH/true if the pin is pulled high,
or LOW/false if pulled low.
"""
return self.mraa_gpio.Gpio.read(self.mraa_gpio.Gpio(pin))
def add_event_detect(self, pin, edge, callback=None, bouncetime=-1):
"""Enable edge detection events for a particular GPIO channel. Pin
should be type IN. Edge must be RISING, FALLING or BOTH. Callback is a
function for the event. Bouncetime is switch bounce timeout in ms for
callback
"""
kwargs = {}
if callback:
kwargs['callback']=callback
if bouncetime > 0:
kwargs['bouncetime']=bouncetime
self.mraa_gpio.Gpio.isr(self.mraa_gpio.Gpio(pin), self._edge_mapping[edge], **kwargs)
def remove_event_detect(self, pin):
"""Remove edge detection for a particular GPIO channel. Pin should be
type IN.
"""
self.mraa_gpio.Gpio.isrExit(self.mraa_gpio.Gpio(pin))
def wait_for_edge(self, pin, edge):
"""Wait for an edge. Pin should be type IN. Edge must be RISING,
FALLING or BOTH.
"""
self.bbio_gpio.wait_for_edge(self.mraa_gpio.Gpio(pin), self._edge_mapping[edge])
def get_platform_gpio(**keywords):
"""Attempt to return a GPIO instance for the platform which the code is being
executed on. Currently supports only the Raspberry Pi using the RPi.GPIO
library and Beaglebone Black using the Adafruit_BBIO library. Will throw an
exception if a GPIO instance can't be created for the current platform. The
returned GPIO object is an instance of BaseGPIO.
"""
plat = Platform.platform_detect()
if plat == Platform.RASPBERRY_PI:
import RPi.GPIO
return RPiGPIOAdapter(RPi.GPIO, **keywords)
elif plat == Platform.BEAGLEBONE_BLACK:
import Adafruit_BBIO.GPIO
return AdafruitBBIOAdapter(Adafruit_BBIO.GPIO, **keywords)
elif plat == Platform.MINNOWBOARD:
import mraa
return AdafruitMinnowAdapter(mraa, **keywords)
elif plat == Platform.UNKNOWN:
raise RuntimeError('Could not determine platform.')

200
darkwater_640/I2C.py Normal file
View File

@@ -0,0 +1,200 @@
# Copyright (c) 2014 Adafruit Industries
# Author: Tony DiCola
# Based on Adafruit_I2C.py created by Kevin Townsend.
#
# 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.
import logging
import subprocess
import Platform as Platform
def reverseByteOrder(data):
"""Reverses the byte order of an int (16-bit) or long (32-bit) value."""
# Courtesy Vishal Sapre
byteCount = len(hex(data)[2:].replace('L','')[::2])
val = 0
for i in range(byteCount):
val = (val << 8) | (data & 0xff)
data >>= 8
return val
def get_default_bus():
"""Return the default bus number based on the device platform. For a
Raspberry Pi either bus 0 or 1 (based on the Pi revision) will be returned.
For a Beaglebone Black the first user accessible bus, 1, will be returned.
"""
plat = Platform.platform_detect()
if plat == Platform.RASPBERRY_PI:
if Platform.pi_revision() == 1:
# Revision 1 Pi uses I2C bus 0.
return 0
else:
# Revision 2 Pi uses I2C bus 1.
return 1
elif plat == Platform.BEAGLEBONE_BLACK:
# Beaglebone Black has multiple I2C buses, default to 1 (P9_19 and P9_20).
return 1
else:
raise RuntimeError('Could not determine default I2C bus for platform.')
def get_i2c_device(address, busnum=None, i2c_interface=None, **kwargs):
"""Return an I2C device for the specified address and on the specified bus.
If busnum isn't specified, the default I2C bus for the platform will attempt
to be detected.
"""
if busnum is None:
busnum = get_default_bus()
return Device(address, busnum, i2c_interface, **kwargs)
def require_repeated_start():
"""Enable repeated start conditions for I2C register reads. This is the
normal behavior for I2C, however on some platforms like the Raspberry Pi
there are bugs which disable repeated starts unless explicitly enabled with
this function. See this thread for more details:
http://www.raspberrypi.org/forums/viewtopic.php?f=44&t=15840
"""
plat = Platform.platform_detect()
if plat == Platform.RASPBERRY_PI:
# On the Raspberry Pi there is a bug where register reads don't send a
# repeated start condition like the kernel smbus I2C driver functions
# define. As a workaround this bit in the BCM2708 driver sysfs tree can
# be changed to enable I2C repeated starts.
subprocess.check_call('chmod 666 /sys/module/i2c_bcm2708/parameters/combined', shell=True)
subprocess.check_call('echo -n 1 > /sys/module/i2c_bcm2708/parameters/combined', shell=True)
# Other platforms are a no-op because they (presumably) have the correct
# behavior and send repeated starts.
class Device(object):
"""Class for communicating with an I2C device using the adafruit-pureio pure
python smbus library, or other smbus compatible I2C interface. Allows reading
and writing 8-bit, 16-bit, and byte array values to registers
on the device."""
def __init__(self, address, busnum, i2c_interface=None):
"""Create an instance of the I2C device at the specified address on the
specified I2C bus number."""
self._address = address
if i2c_interface is None:
# Use pure python I2C interface if none is specified.
import smbus
self._bus = smbus.SMBus(busnum)
else:
# Otherwise use the provided class to create an smbus interface.
self._bus = i2c_interface(busnum)
self._logger = logging.getLogger('Adafruit_I2C.Device.Bus.{0}.Address.{1:#0X}' \
.format(busnum, address))
def writeRaw8(self, value):
"""Write an 8-bit value on the bus (without register)."""
value = value & 0xFF
self._bus.write_byte(self._address, value)
self._logger.debug("Wrote 0x%02X",
value)
def write8(self, register, value):
"""Write an 8-bit value to the specified register."""
value = value & 0xFF
self._bus.write_byte_data(self._address, register, value)
self._logger.debug("Wrote 0x%02X to register 0x%02X",
value, register)
def write16(self, register, value):
"""Write a 16-bit value to the specified register."""
value = value & 0xFFFF
self._bus.write_word_data(self._address, register, value)
self._logger.debug("Wrote 0x%04X to register pair 0x%02X, 0x%02X",
value, register, register+1)
def writeList(self, register, data):
"""Write bytes to the specified register."""
self._bus.write_i2c_block_data(self._address, register, data)
self._logger.debug("Wrote to register 0x%02X: %s",
register, data)
def readList(self, register, length):
"""Read a length number of bytes from the specified register. Results
will be returned as a bytearray."""
results = self._bus.read_i2c_block_data(self._address, register, length)
self._logger.debug("Read the following from register 0x%02X: %s",
register, results)
return results
def readRaw8(self):
"""Read an 8-bit value on the bus (without register)."""
result = self._bus.read_byte(self._address) & 0xFF
self._logger.debug("Read 0x%02X",
result)
return result
def readU8(self, register):
"""Read an unsigned byte from the specified register."""
result = self._bus.read_byte_data(self._address, register) & 0xFF
self._logger.debug("Read 0x%02X from register 0x%02X",
result, register)
return result
def readS8(self, register):
"""Read a signed byte from the specified register."""
result = self.readU8(register)
if result > 127:
result -= 256
return result
def readU16(self, register, little_endian=True):
"""Read an unsigned 16-bit value from the specified register, with the
specified endianness (default little endian, or least significant byte
first)."""
result = self._bus.read_word_data(self._address,register) & 0xFFFF
self._logger.debug("Read 0x%04X from register pair 0x%02X, 0x%02X",
result, register, register+1)
# Swap bytes if using big endian because read_word_data assumes little
# endian on ARM (little endian) systems.
if not little_endian:
result = ((result << 8) & 0xFF00) + (result >> 8)
return result
def readS16(self, register, little_endian=True):
"""Read a signed 16-bit value from the specified register, with the
specified endianness (default little endian, or least significant byte
first)."""
result = self.readU16(register, little_endian)
if result > 32767:
result -= 65536
return result
def readU16LE(self, register):
"""Read an unsigned 16-bit value from the specified register, in little
endian byte order."""
return self.readU16(register, little_endian=True)
def readU16BE(self, register):
"""Read an unsigned 16-bit value from the specified register, in big
endian byte order."""
return self.readU16(register, little_endian=False)
def readS16LE(self, register):
"""Read a signed 16-bit value from the specified register, in little
endian byte order."""
return self.readS16(register, little_endian=True)
def readS16BE(self, register):
"""Read a signed 16-bit value from the specified register, in big
endian byte order."""
return self.readS16(register, little_endian=False)

167
darkwater_640/PCA9685.py Normal file
View File

@@ -0,0 +1,167 @@
# Copyright (c) 2016 Adafruit Industries
# Author: Tony DiCola
#
# Updated by: Dark Water
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
from __future__ import division
import logging
import time
import math
# Registers/etc:
PCA9685_ADDRESS = 0x60
MODE1 = 0x00
MODE2 = 0x01
SUBADR1 = 0x02
SUBADR2 = 0x03
SUBADR3 = 0x04
PRESCALE = 0xFE
LED0_ON_L = 0x06
LED0_ON_H = 0x07
LED0_OFF_L = 0x08
LED0_OFF_H = 0x09
ALL_LED_ON_L = 0xFA
ALL_LED_ON_H = 0xFB
ALL_LED_OFF_L = 0xFC
ALL_LED_OFF_H = 0xFD
# Bits:
RESTART = 0x80
SLEEP = 0x10
ALLCALL = 0x01
INVRT = 0x10
OUTDRV = 0x04
logger = logging.getLogger(__name__)
def software_reset(i2c=None, **kwargs):
"""Sends a software reset (SWRST) command to all servo drivers on the bus."""
# Setup I2C interface for device 0x00 to talk to all of them.
if i2c is None:
import I2C as I2C
i2c = I2C
self._device = i2c.get_i2c_device(0x00, **kwargs)
self._device.writeRaw8(0x06) # SWRST
class PCA9685(object):
"""PCA9685 PWM LED/servo controller."""
def __init__(self, address=PCA9685_ADDRESS, i2c=None, **kwargs):
"""Initialize the PCA9685."""
# Setup I2C interface for the device.
if i2c is None:
import I2C as I2C
i2c = I2C
self._device = i2c.get_i2c_device(address, **kwargs)
self.set_all_pwm(0, 0)
self._device.write8(MODE2, OUTDRV)
self._device.write8(MODE1, ALLCALL)
time.sleep(0.005) # wait for oscillator
mode1 = self._device.readU8(MODE1)
mode1 = mode1 & ~SLEEP # wake up (reset sleep)
self._device.write8(MODE1, mode1)
time.sleep(0.005) # wait for oscillator
def set_pwm_freq(self, freq_hz, correctionFactor=1.0):
"""Set the PWM frequency to the provided value in hertz."""
prescaleval = 25000000.0 # 25MHz
prescaleval /= 4096.0 # 12-bit
prescaleval /= float(freq_hz)
prescaleval -= 1.0
logger.debug('Setting PWM frequency to {0} Hz'.format(freq_hz))
logger.debug('Estimated pre-scale: {0}'.format(prescaleval))
prescale = round(prescaleval * correctionFactor + 0.5)
logger.debug('Final pre-scale: {0}'.format(prescale))
oldmode = self._device.readU8(MODE1);
newmode = (oldmode & 0x7F) | 0x10 # sleep
self._device.write8(MODE1, newmode) # go to sleep
self._device.write8(PRESCALE, int(math.floor(prescale)))
self._device.write8(MODE1, oldmode)
time.sleep(0.005)
self._device.write8(MODE1, oldmode | 0x80)
def set_pwm_freq_min(self, freq_hz, correctionFactor=1.0):
"Sets the PWM frequency"
prescaleval = 25000000.0 # 25MHz
prescaleval /= 4096.0 # 12-bit
prescaleval /= float(freq_hz)
prescaleval -= 1.0
if (self.debug):
print("Setting PWM frequency to %d Hz" % freq_hz)
print("Estimated pre-scale: %d" % prescaleval)
prescale = math.floor(prescaleval * correctionFactor + 0.5)
if (self.debug):
print("Final pre-scale: %d" % prescale)
oldmode = self.i2c.readU8(self.__MODE1);
newmode = (oldmode & 0x7F) | 0x10 # sleep
self.i2c.write8(self.__MODE1, newmode) # go to sleep
self.i2c.write8(self.__PRESCALE, int(math.floor(prescale)))
self.i2c.write8(self.__MODE1, oldmode)
time.sleep(0.005)
self.i2c.write8(self.__MODE1, oldmode | 0x80)
def set_pwm_freq_max(self, freq_hz, correctionFactor=1.0):
"Sets the PWM frequency"
prescaleval = 25000000.0 # 25MHz
prescaleval /= 4096.0 # 12-bit
prescaleval /= float(freq_hz)
prescaleval -= 1.0
if (self.debug):
print("Setting PWM frequency to %d Hz" % freq_hz)
print("Estimated pre-scale: %d" % prescaleval)
prescale = math.ceil(prescaleval * correctionFactor + 0.5)
if (self.debug):
print("Final pre-scale: %d" % prescale)
oldmode = self.i2c.readU8(self.__MODE1);
newmode = (oldmode & 0x7F) | 0x10 # sleep
self.i2c.write8(self.__MODE1, newmode) # go to sleep
self.i2c.write8(self.__PRESCALE, int(math.floor(prescale)))
self.i2c.write8(self.__MODE1, oldmode)
time.sleep(0.005)
self.i2c.write8(self.__MODE1, oldmode | 0x80)
def get_pwm_freq(self):
prescale = self.i2c.readU8(self.__PRESCALE)
calcfreq = 25000000.0 / 4096.0 / ( float(prescale) + 1 )
if (self.debug):
print("Got pre-scale: %d" % prescale)
print("Calculated Frequency: %d" % calcfreq)
return calcfreq
def set_pwm(self, channel, on, off):
"""Sets a single PWM channel."""
self._device.write8(LED0_ON_L+4*channel, on & 0xFF)
self._device.write8(LED0_ON_H+4*channel, on >> 8)
self._device.write8(LED0_OFF_L+4*channel, off & 0xFF)
self._device.write8(LED0_OFF_H+4*channel, off >> 8)
def set_all_pwm(self, on, off):
"""Sets all PWM channels."""
self._device.write8(ALL_LED_ON_L, on & 0xFF)
self._device.write8(ALL_LED_ON_H, on >> 8)
self._device.write8(ALL_LED_OFF_L, off & 0xFF)
self._device.write8(ALL_LED_OFF_H, off >> 8)

106
darkwater_640/Platform.py Normal file
View File

@@ -0,0 +1,106 @@
# Copyright (c) 2014 Adafruit Industries
# Author: Tony DiCola
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import platform
import re
# Platform identification constants.
UNKNOWN = 0
RASPBERRY_PI = 1
BEAGLEBONE_BLACK = 2
MINNOWBOARD = 3
def platform_detect():
"""Detect if running on the Raspberry Pi or Beaglebone Black and return the
platform type. Will return RASPBERRY_PI, BEAGLEBONE_BLACK, or UNKNOWN."""
# Handle Raspberry Pi
pi = pi_version()
if pi is not None:
return RASPBERRY_PI
# Handle Beaglebone Black
# TODO: Check the Beaglebone Black /proc/cpuinfo value instead of reading
# the platform.
plat = platform.platform()
if plat.lower().find('armv7l-with-debian') > -1:
return BEAGLEBONE_BLACK
elif plat.lower().find('armv7l-with-ubuntu') > -1:
return BEAGLEBONE_BLACK
elif plat.lower().find('armv7l-with-glibc2.4') > -1:
return BEAGLEBONE_BLACK
# Handle Minnowboard
# Assumption is that mraa is installed
try:
import mraa
if mraa.getPlatformName()=='MinnowBoard MAX':
return MINNOWBOARD
except ImportError:
pass
# Couldn't figure out the platform, just return unknown.
return UNKNOWN
def pi_revision():
"""Detect the revision number of a Raspberry Pi, useful for changing
functionality like default I2C bus based on revision."""
# Revision list available at: http://elinux.org/RPi_HardwareHistory#Board_Revision_History
with open('/proc/cpuinfo', 'r') as infile:
for line in infile:
# Match a line of the form "Revision : 0002" while ignoring extra
# info in front of the revsion (like 1000 when the Pi was over-volted).
match = re.match('Revision\s+:\s+.*(\w{4})$', line, flags=re.IGNORECASE)
if match and match.group(1) in ['0000', '0002', '0003']:
# Return revision 1 if revision ends with 0000, 0002 or 0003.
return 1
elif match:
# Assume revision 2 if revision ends with any other 4 chars.
return 2
# Couldn't find the revision, throw an exception.
raise RuntimeError('Could not determine Raspberry Pi revision.')
def pi_version():
"""Detect the version of the Raspberry Pi. Returns either 1, 2 or
None depending on if it's a Raspberry Pi 1 (model A, B, A+, B+),
Raspberry Pi 2 (model B+), or not a Raspberry Pi.
"""
# Check /proc/cpuinfo for the Hardware field value.
# 2708 is pi 1
# 2709 is pi 2
# Anything else is not a pi.
with open('/proc/cpuinfo', 'r') as infile:
cpuinfo = infile.read()
# Match a line like 'Hardware : BCM2709'
match = re.search('^Hardware\s+:\s+(\w+)$', cpuinfo,
flags=re.MULTILINE | re.IGNORECASE)
if not match:
# Couldn't find the hardware, assume it isn't a pi.
return None
if match.group(1) == 'BCM2708':
# Pi 1
return 1
elif match.group(1) == 'BCM2709':
# Pi 2
return 2
else:
# Something else, not a pi.
return None

336
darkwater_640/SPI.py Normal file
View File

@@ -0,0 +1,336 @@
# Copyright (c) 2014 Adafruit Industries
# Author: Tony DiCola
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
import operator
import time
import Adafruit_GPIO as GPIO
MSBFIRST = 0
LSBFIRST = 1
class SpiDev(object):
"""Hardware-based SPI implementation using the spidev interface."""
def __init__(self, port, device, max_speed_hz=500000):
"""Initialize an SPI device using the SPIdev interface. Port and device
identify the device, for example the device /dev/spidev1.0 would be port
1 and device 0.
"""
import spidev
self._device = spidev.SpiDev()
self._device.open(port, device)
self._device.max_speed_hz=max_speed_hz
# Default to mode 0.
self._device.mode = 0
def set_clock_hz(self, hz):
"""Set the speed of the SPI clock in hertz. Note that not all speeds
are supported and a lower speed might be chosen by the hardware.
"""
self._device.max_speed_hz=hz
def set_mode(self, mode):
"""Set SPI mode which controls clock polarity and phase. Should be a
numeric value 0, 1, 2, or 3. See wikipedia page for details on meaning:
http://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus
"""
if mode < 0 or mode > 3:
raise ValueError('Mode must be a value 0, 1, 2, or 3.')
self._device.mode = mode
def set_bit_order(self, order):
"""Set order of bits to be read/written over serial lines. Should be
either MSBFIRST for most-significant first, or LSBFIRST for
least-signifcant first.
"""
if order == MSBFIRST:
self._device.lsbfirst = False
elif order == LSBFIRST:
self._device.lsbfirst = True
else:
raise ValueError('Order must be MSBFIRST or LSBFIRST.')
def close(self):
"""Close communication with the SPI device."""
self._device.close()
def write(self, data):
"""Half-duplex SPI write. The specified array of bytes will be clocked
out the MOSI line.
"""
self._device.writebytes(data)
def read(self, length):
"""Half-duplex SPI read. The specified length of bytes will be clocked
in the MISO line and returned as a bytearray object.
"""
return bytearray(self._device.readbytes(length))
def transfer(self, data):
"""Full-duplex SPI read and write. The specified array of bytes will be
clocked out the MOSI line, while simultaneously bytes will be read from
the MISO line. Read bytes will be returned as a bytearray object.
"""
return bytearray(self._device.xfer2(data))
class SpiDevMraa(object):
"""Hardware SPI implementation with the mraa library on Minnowboard"""
def __init__(self, port, device, max_speed_hz=500000):
import mraa
self._device = mraa.Spi(0)
self._device.mode(0)
def set_clock_hz(self, hz):
"""Set the speed of the SPI clock in hertz. Note that not all speeds
are supported and a lower speed might be chosen by the hardware.
"""
self._device.frequency(hz)
def set_mode(self,mode):
"""Set SPI mode which controls clock polarity and phase. Should be a
numeric value 0, 1, 2, or 3. See wikipedia page for details on meaning:
http://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus
"""
if mode < 0 or mode > 3:
raise ValueError('Mode must be a value 0, 1, 2, or 3.')
self._device.mode(mode)
def set_mode(self,mode):
"""Set SPI mode which controls clock polarity and phase. Should be a
numeric value 0, 1, 2, or 3. See wikipedia page for details on meaning:
http://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus
"""
if mode < 0 or mode > 3:
raise ValueError('Mode must be a value 0, 1, 2, or 3.')
self._device.mode(mode)
def set_bit_order(self, order):
"""Set order of bits to be read/written over serial lines. Should be
either MSBFIRST for most-significant first, or LSBFIRST for
least-signifcant first.
"""
if order == MSBFIRST:
self._device.lsbmode(False)
elif order == LSBFIRST:
self._device.lsbmode(True)
else:
raise ValueError('Order must be MSBFIRST or LSBFIRST.')
def close(self):
"""Close communication with the SPI device."""
self._device.Spi()
def write(self, data):
"""Half-duplex SPI write. The specified array of bytes will be clocked
out the MOSI line.
"""
self._device.write(bytearray(data))
class BitBang(object):
"""Software-based implementation of the SPI protocol over GPIO pins."""
def __init__(self, gpio, sclk, mosi=None, miso=None, ss=None):
"""Initialize bit bang (or software) based SPI. Must provide a BaseGPIO
class, the SPI clock, and optionally MOSI, MISO, and SS (slave select)
pin numbers. If MOSI is set to None then writes will be disabled and fail
with an error, likewise for MISO reads will be disabled. If SS is set to
None then SS will not be asserted high/low by the library when
transfering data.
"""
self._gpio = gpio
self._sclk = sclk
self._mosi = mosi
self._miso = miso
self._ss = ss
# Set pins as outputs/inputs.
gpio.setup(sclk, GPIO.OUT)
if mosi is not None:
gpio.setup(mosi, GPIO.OUT)
if miso is not None:
gpio.setup(miso, GPIO.IN)
if ss is not None:
gpio.setup(ss, GPIO.OUT)
# Assert SS high to start with device communication off.
gpio.set_high(ss)
# Assume mode 0.
self.set_mode(0)
# Assume most significant bit first order.
self.set_bit_order(MSBFIRST)
def set_clock_hz(self, hz):
"""Set the speed of the SPI clock. This is unsupported with the bit
bang SPI class and will be ignored.
"""
pass
def set_mode(self, mode):
"""Set SPI mode which controls clock polarity and phase. Should be a
numeric value 0, 1, 2, or 3. See wikipedia page for details on meaning:
http://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus
"""
if mode < 0 or mode > 3:
raise ValueError('Mode must be a value 0, 1, 2, or 3.')
if mode & 0x02:
# Clock is normally high in mode 2 and 3.
self._clock_base = GPIO.HIGH
else:
# Clock is normally low in mode 0 and 1.
self._clock_base = GPIO.LOW
if mode & 0x01:
# Read on trailing edge in mode 1 and 3.
self._read_leading = False
else:
# Read on leading edge in mode 0 and 2.
self._read_leading = True
# Put clock into its base state.
self._gpio.output(self._sclk, self._clock_base)
def set_bit_order(self, order):
"""Set order of bits to be read/written over serial lines. Should be
either MSBFIRST for most-significant first, or LSBFIRST for
least-signifcant first.
"""
# Set self._mask to the bitmask which points at the appropriate bit to
# read or write, and appropriate left/right shift operator function for
# reading/writing.
if order == MSBFIRST:
self._mask = 0x80
self._write_shift = operator.lshift
self._read_shift = operator.rshift
elif order == LSBFIRST:
self._mask = 0x01
self._write_shift = operator.rshift
self._read_shift = operator.lshift
else:
raise ValueError('Order must be MSBFIRST or LSBFIRST.')
def close(self):
"""Close the SPI connection. Unused in the bit bang implementation."""
pass
def write(self, data, assert_ss=True, deassert_ss=True):
"""Half-duplex SPI write. If assert_ss is True, the SS line will be
asserted low, the specified bytes will be clocked out the MOSI line, and
if deassert_ss is True the SS line be put back high.
"""
# Fail MOSI is not specified.
if self._mosi is None:
raise RuntimeError('Write attempted with no MOSI pin specified.')
if assert_ss and self._ss is not None:
self._gpio.set_low(self._ss)
for byte in data:
for i in range(8):
# Write bit to MOSI.
if self._write_shift(byte, i) & self._mask:
self._gpio.set_high(self._mosi)
else:
self._gpio.set_low(self._mosi)
# Flip clock off base.
self._gpio.output(self._sclk, not self._clock_base)
# Return clock to base.
self._gpio.output(self._sclk, self._clock_base)
if deassert_ss and self._ss is not None:
self._gpio.set_high(self._ss)
def read(self, length, assert_ss=True, deassert_ss=True):
"""Half-duplex SPI read. If assert_ss is true, the SS line will be
asserted low, the specified length of bytes will be clocked in the MISO
line, and if deassert_ss is true the SS line will be put back high.
Bytes which are read will be returned as a bytearray object.
"""
if self._miso is None:
raise RuntimeError('Read attempted with no MISO pin specified.')
if assert_ss and self._ss is not None:
self._gpio.set_low(self._ss)
result = bytearray(length)
for i in range(length):
for j in range(8):
# Flip clock off base.
self._gpio.output(self._sclk, not self._clock_base)
# Handle read on leading edge of clock.
if self._read_leading:
if self._gpio.is_high(self._miso):
# Set bit to 1 at appropriate location.
result[i] |= self._read_shift(self._mask, j)
else:
# Set bit to 0 at appropriate location.
result[i] &= ~self._read_shift(self._mask, j)
# Return clock to base.
self._gpio.output(self._sclk, self._clock_base)
# Handle read on trailing edge of clock.
if not self._read_leading:
if self._gpio.is_high(self._miso):
# Set bit to 1 at appropriate location.
result[i] |= self._read_shift(self._mask, j)
else:
# Set bit to 0 at appropriate location.
result[i] &= ~self._read_shift(self._mask, j)
if deassert_ss and self._ss is not None:
self._gpio.set_high(self._ss)
return result
def transfer(self, data, assert_ss=True, deassert_ss=True):
"""Full-duplex SPI read and write. If assert_ss is true, the SS line
will be asserted low, the specified bytes will be clocked out the MOSI
line while bytes will also be read from the MISO line, and if
deassert_ss is true the SS line will be put back high. Bytes which are
read will be returned as a bytearray object.
"""
if self._mosi is None:
raise RuntimeError('Write attempted with no MOSI pin specified.')
if self._mosi is None:
raise RuntimeError('Read attempted with no MISO pin specified.')
if assert_ss and self._ss is not None:
self._gpio.set_low(self._ss)
result = bytearray(len(data))
for i in range(len(data)):
for j in range(8):
# Write bit to MOSI.
if self._write_shift(data[i], j) & self._mask:
self._gpio.set_high(self._mosi)
else:
self._gpio.set_low(self._mosi)
# Flip clock off base.
self._gpio.output(self._sclk, not self._clock_base)
# Handle read on leading edge of clock.
if self._read_leading:
if self._gpio.is_high(self._miso):
# Set bit to 1 at appropriate location.
result[i] |= self._read_shift(self._mask, j)
else:
# Set bit to 0 at appropriate location.
result[i] &= ~self._read_shift(self._mask, j)
# Return clock to base.
self._gpio.output(self._sclk, self._clock_base)
# Handle read on trailing edge of clock.
if not self._read_leading:
if self._gpio.is_high(self._miso):
# Set bit to 1 at appropriate location.
result[i] |= self._read_shift(self._mask, j)
else:
# Set bit to 0 at appropriate location.
result[i] &= ~self._read_shift(self._mask, j)
if deassert_ss and self._ss is not None:
self._gpio.set_high(self._ss)
return result

View File

@@ -0,0 +1,2 @@
from .darkwater_640 import dw_Motor, dw_Servo, dw_Stepper, dw_Controller
from .mpu9250 import MPU9250

View File

@@ -0,0 +1,431 @@
#!/usr/bin/python
import RPi.GPIO as GPIO
from .PCA9685 import PCA9685
import time
import math
# Stepper motor code based on Adafruit Python Library for DC + Stepper Motor HAT
# Written by Limor Fried for Adafruit Industries. MIT license.
class dw_Stepper:
MICROSTEPS = 8
MICROSTEP_CURVE = [0, 50, 98, 142, 180, 212, 236, 250, 255]
#MICROSTEPS = 16
# a sinusoidal curve NOT LINEAR!
#MICROSTEP_CURVE = [0, 25, 50, 74, 98, 120, 141, 162, 180, 197, 212, 225, 236, 244, 250, 253, 255]
def __init__(self, controller, num, steps=200, forcemode=False):
self.speed = 0
self.MC = controller
self.motornum = num
modepin = in1 = in2 = 0
self.revsteps = steps
self.sec_per_step = 0.1
self.steppingcounter = 0
self.currentstep = 0
if(self.MC.getMode() != dw_Controller.ININ and forcemode != True ):
raise NameError('Mode needs to be set to ININ for stepper mode operation')
else:
self.MC.setMode(dw_Controller.ININ)
if (num == 0):
ain1 = 2
ain2 = 3
bin1 = 4
bin2 = 5
elif (num == 1):
ain1 = 6
ain2 = 7
bin1 = 8
bin2 = 9
elif (num == 2):
ain1 = 10
ain2 = 11
bin1 = 12
bin2 = 13
else:
raise NameError('Stepper must be between 1 and 3 inclusive')
self.ain1 = ain1
self.ain2 = ain2
self.bin1 = bin1
self.bin2 = bin2
# switch off both drivers
self.run(dw_Controller.STOP, 0)
def run(self, command, speed = 0):
if not self.MC:
return
if (command == dw_Controller.STOP):
self.MC.setPin(self.ain1, 0)
self.MC.setPin(self.ain2, 0)
self.MC.setPin(self.bin1, 0)
self.MC.setPin(self.bin2, 0)
def off(self):
self.run(dw_Controller.STOP, 0)
def setMotorSpeed(self, rpm):
self.sec_per_step = 60.0 / (self.revsteps * rpm)
self.steppingcounter = 0
def oneStep(self, dir, style):
pwm_a = pwm_b = 255
# first determine what sort of stepping procedure we're up to
if (style == dw_Controller.SINGLE):
if ((self.currentstep/(self.MICROSTEPS/2)) % 2):
# we're at an odd step, weird
if (dir == dw_Controller.FORWARD):
self.currentstep += self.MICROSTEPS/2
else:
self.currentstep -= self.MICROSTEPS/2
else:
# go to next even step
if (dir == dw_Controller.FORWARD):
self.currentstep += self.MICROSTEPS
else:
self.currentstep -= self.MICROSTEPS
if (style == dw_Controller.DOUBLE):
if not (self.currentstep/(self.MICROSTEPS/2) % 2):
# we're at an even step, weird
if (dir == dw_Controller.FORWARD):
self.currentstep += self.MICROSTEPS/2
else:
self.currentstep -= self.MICROSTEPS/2
else:
# go to next odd step
if (dir == dw_Controller.FORWARD):
self.currentstep += self.MICROSTEPS
else:
self.currentstep -= self.MICROSTEPS
if (style == dw_Controller.MICROSTEP):
if (dir == dw_Controller.FORWARD):
self.currentstep += 1
else:
self.currentstep -= 1
# go to next 'step' and wrap around
self.currentstep += self.MICROSTEPS * 4
self.currentstep %= self.MICROSTEPS * 4
pwm_a = pwm_b = 0
if (self.currentstep >= 0) and (self.currentstep < self.MICROSTEPS):
pwm_a = self.MICROSTEP_CURVE[self.MICROSTEPS - self.currentstep]
pwm_b = self.MICROSTEP_CURVE[self.currentstep]
elif (self.currentstep >= self.MICROSTEPS) and (self.currentstep < self.MICROSTEPS*2):
pwm_a = self.MICROSTEP_CURVE[self.currentstep - self.MICROSTEPS]
pwm_b = self.MICROSTEP_CURVE[self.MICROSTEPS*2 - self.currentstep]
elif (self.currentstep >= self.MICROSTEPS*2) and (self.currentstep < self.MICROSTEPS*3):
pwm_a = self.MICROSTEP_CURVE[self.MICROSTEPS*3 - self.currentstep]
pwm_b = self.MICROSTEP_CURVE[self.currentstep - self.MICROSTEPS*2]
elif (self.currentstep >= self.MICROSTEPS*3) and (self.currentstep < self.MICROSTEPS*4):
pwm_a = self.MICROSTEP_CURVE[self.currentstep - self.MICROSTEPS*3]
pwm_b = self.MICROSTEP_CURVE[self.MICROSTEPS*4 - self.currentstep]
# go to next 'step' and wrap around
self.currentstep += self.MICROSTEPS * 4
self.currentstep %= self.MICROSTEPS * 4
# set up coil energizing!
coils = [0, 0, 0, 0]
if (style == dw_Controller.MICROSTEP):
if (self.currentstep >= 0) and (self.currentstep < self.MICROSTEPS):
coils = [1, 1, 0, 0]
elif (self.currentstep >= self.MICROSTEPS) and (self.currentstep < self.MICROSTEPS*2):
coils = [0, 1, 1, 0]
elif (self.currentstep >= self.MICROSTEPS*2) and (self.currentstep < self.MICROSTEPS*3):
coils = [0, 0, 1, 1]
elif (self.currentstep >= self.MICROSTEPS*3) and (self.currentstep < self.MICROSTEPS*4):
coils = [1, 0, 0, 1]
else:
step2coils = [ [1, 0, 0, 0],
[1, 1, 0, 0],
[0, 1, 0, 0],
[0, 1, 1, 0],
[0, 0, 1, 0],
[0, 0, 1, 1],
[0, 0, 0, 1],
[1, 0, 0, 1] ]
coils = step2coils[self.currentstep/(self.MICROSTEPS/2)]
self.MC.setPin(self.ain1, coils[0]) #ain2
self.MC.setPin(self.bin1, coils[1]) #bin1
self.MC.setPin(self.ain2, coils[2]) #ain1
self.MC.setPin(self.bin2, coils[3]) #bin2
return self.currentstep
def step(self, steps, direction, stepstyle):
s_per_s = self.sec_per_step
lateststep = 0
if (stepstyle == dw_Controller.MICROSTEP):
s_per_s /= self.MICROSTEPS
steps *= self.MICROSTEPS
for s in range(steps):
lateststep = self.oneStep(direction, stepstyle)
time.sleep(s_per_s)
if (stepstyle == dw_Controller.MICROSTEP):
# this is an edge case, if we are in between full steps, lets just keep going
# so we end on a full step
while (lateststep != 0) and (lateststep != self.MICROSTEPS):
lateststep = self.oneStep(dir, stepstyle)
time.sleep(s_per_s)
class dw_Motor:
def __init__(self, controller, num):
self.speed = 0
self.MC = controller
self.motornum = num
modepin = in1 = in2 = 0
if (num == 0):
in2 = 2 #phase
in1 = 3 #enable
elif (num == 1):
in2 = 4 #phase
in1 = 5 #enable
elif (num == 2):
in2 = 6 #phase
in1 = 7 #enable
elif (num == 3):
in2 = 8 #phase
in1 = 9 #enable
elif (num == 4):
in2 = 10 #phase
in1 = 11 #enable
elif (num == 5):
in2 = 12 #phase
in1 = 13 #enable
else:
raise NameError('Motors must be between 1 and 6 inclusive')
# for phase enable
self.PHpin = in2
self.ENpin = in1
# for in/in
self.IN2pin = in2
self.IN1pin = in1
# switch off
self.run(dw_Controller.STOP, 0)
def setMotorSpeed(self, value):
# Check for PWM values
if(value >= 1000) and (value < 1500):
self.run(dw_Controller.REVERSE, round(translate(value,1500,1000, 0, 255)))
if(value > 1500) and (value <= 2000):
self.run(dw_Controller.FORWARD, round(translate(value, 1500, 2000, 0, 255)))
if(value == 1500):
self.run(dw_Controller.STOP, 0)
if(value > 0) and (value <= 255):
self.run(dw_Controller.FORWARD, value)
if(value == 0):
self.run(dw_Controller.STOP, value)
if(value < 0) and (value >= -255):
self.run(dw_Controller.REVERSE, abs(value))
def run(self, command, speed = 0):
if not self.MC:
return
if (self.MC.getMode() == dw_Controller.PHASE):
if (command == dw_Controller.FORWARD):
self.MC.setPin(self.PHpin, 0)
self.MC._pwm.set_pwm(self.ENpin, 0, speed*16)
if (command == dw_Controller.REVERSE):
self.MC.setPin(self.PHpin, 1)
self.MC._pwm.set_pwm(self.ENpin, 0, speed*16)
if (command == dw_Controller.STOP):
self.MC.setPin(self.PHpin, 0)
self.MC.setPin(self.ENpin, 0)
else: ## IN/IN mode
if (command == dw_Controller.FORWARD):
self.MC.setPin(self.IN2pin, 0)
self.MC._pwm.set_pwm(self.IN1pin, 0, speed*16)
if (command == dw_Controller.REVERSE):
self.MC.setPin(self.IN1pin, 0)
self.MC._pwm.set_pwm(self.IN2pin, 0, speed*16)
if (command == dw_Controller.STOP):
self.MC.setPin(self.IN1pin, 1)
self.MC.setPin(self.IN2pin, 1)
if (command == dw_Controller.COAST):
self.MC.setPin(self.IN1pin, 0)
self.MC.setPin(self.IN2pin, 0)
def off(self):
self.run(dw_Controller.STOP, 0)
class dw_Servo:
def __init__(self, controller, num, freq):
_SERVO_MIN_MS = 1.250 #ms
_SERVO_MAX_MS = 1.750 #ms
self.speed = 0
self.MC = controller
self.cnum = num
self.pin = 0
self.freq = freq
self.servo_min = math.trunc( ( _SERVO_MIN_MS * 4096 ) / (1000.0 / self.freq ) - 1 )
self.servo_max = math.trunc( ( _SERVO_MAX_MS * 4096 ) / (1000.0 / self.freq ) - 1 )
self.servo_zero = math.trunc( ( self.servo_min + self.servo_max ) / 2 ) # halfway = 0 degrees
if (num == 0):
self.pin = 0
elif (num == 1):
self.pin = 1
else:
raise NameError('Port must be between 0 and 1 inclusive')
# switch off
self.off()
def off(self):
self.MC.setPin(self.pin, 0)
def setAngle(self, angle):
pulse = self.servo_zero + ( (self.servo_zero - self.servo_min ) * angle / 80 )
#print "angle=%s pulse=%s" % (angle, pulse)
#self.setPWMmS( pulse )
def setPWM(self, value):
if(value > 0):
self.MC._pwm.set_pwm(self.pin, 0, int(value) )
if(value == 0):
self.off()
def setPWMmS(self, length_ms):
self.setPWM( math.trunc( ( length_ms * 4096 ) / ( 1000.0 / self.freq ) ) - 1 )
def setPWMuS(self, length_us):
self.setPWM( math.trunc( ( length_us * 4096 ) / ( 1000000.0 / self.freq ) ) -1 )
class dw_Controller:
FORWARD = 1
REVERSE = 2
BRAKEFORWARD = 3
BRAKEREVERSE = 4
STOP = 5 #brake
COAST = 6 # in/in only
SINGLE = 1
DOUBLE = 2
INTERLEAVE = 3
MICROSTEP = 4
ININ = 0
PHASE = 1
def __init__(self, addr = 0x60, freq = 100, correctionFactor = 1.0):
self._i2caddr = addr # default addr on HAT
self._frequency = freq # default @1600Hz PWM freq
# self.steppers = [ Adafruit_StepperMotor(self, 1), Adafruit_StepperMotor(self, 2) ]
self._pwm = PCA9685(addr)
self._pwm.set_pwm_freq(self._frequency, correctionFactor)
# Just gonna default to high for now
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
GPIO.setup(27, GPIO.OUT)
self.setMode(self.ININ) # set high for en/phase mode - low = in/in mode
self.motors = [ dw_Motor(self, m) for m in range(6) ]
self.servos = [ dw_Servo(self, m, freq) for m in range(2) ]
self.steppers = [ dw_Stepper(self, m) for m in range(3) ]
def setMode(self, mode = 1):
if (mode == self.ININ):
self._mode = self.ININ
GPIO.output(27, GPIO.LOW)
else:
self._mode = self.PHASE
GPIO.output(27, GPIO.HIGH)
def getMode(self):
return self._mode
def setPin(self, pin, value):
if (pin < 0) or (pin > 15):
raise NameError('PWM pin must be between 0 and 15 inclusive')
if (value != 0) and (value != 1):
raise NameError('Pin value must be 0 or 1!')
if (value == 0):
self._pwm.set_pwm(pin, 0, 4096)
if (value == 1):
self._pwm.set_pwm(pin, 4096, 0)
def setAllPin(self, value):
if (pin < 0) or (pin > 15):
raise NameError('PWM pin must be between 0 and 15 inclusive')
if (value != 0) and (value != 1):
raise NameError('Pin value must be 0 or 1!')
if (value == 0):
self._pwm.set_all_pwm(0, 4096)
if (value == 1):
self._pwm.set_all_pwm(4096, 0)
def getMotor(self, num):
if (num < 1) or (num > 6):
raise NameError('Motors must be between 1 and 6 inclusive')
return self.motors[num-1]
def getServo(self, num):
if (num < 1) or (num > 2):
raise NameError('Servos must be between 1 and 2 inclusive')
return self.servos[num-1]
def getStepper(self, num):
if (num < 1) or (num > 3):
raise NameError('Stepper motors must be between 1 and 3 inclusive')
return self.steppers[num-1]
def setAllPWM(self, value):
if(value > 0):
self._pwm.set_all_pwm(0, value)
if(value == 0):
self.allOff()
def setAllPWMmS(self, value):
if(value > 0):
self._pwm.set_all_pwm(0, value)
if(value == 0):
self.allOff()
def setAllPWMuS(self, value):
if(value > 0):
self._pwm.set_all_pwm(0, value)
if(value == 0):
self.allOff()
def allOff(self):
this.setAllPin( 0 );
def translate(value, leftMin, leftMax, rightMin, rightMax):
# Figure out how 'wide' each range is
leftSpan = leftMax - leftMin
rightSpan = rightMax - rightMin
# Convert the left range into a 0-1 range (float)
valueScaled = float(value - leftMin) / float(leftSpan)
# Convert the 0-1 range into a value in the right range.
return rightMin + (valueScaled * rightSpan)

614
darkwater_640/mpu9250.py Normal file
View File

@@ -0,0 +1,614 @@
"""
MPU9250 driver code is placed under the BSD license.
Copyright (c) 2014, Emlid Limited, www.emlid.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Emlid Limited nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL EMLID LIMITED BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
import spidev
import time
import struct
import array
class MPU9250:
G_SI = 9.80665
PI = 3.14159
# MPU9250 registers
__MPUREG_XG_OFFS_TC = 0x00
__MPUREG_YG_OFFS_TC = 0x01
__MPUREG_ZG_OFFS_TC = 0x02
__MPUREG_X_FINE_GAIN = 0x03
__MPUREG_Y_FINE_GAIN = 0x04
__MPUREG_Z_FINE_GAIN = 0x05
__MPUREG_XA_OFFS_H = 0x06
__MPUREG_XA_OFFS_L = 0x07
__MPUREG_YA_OFFS_H = 0x08
__MPUREG_YA_OFFS_L = 0x09
__MPUREG_ZA_OFFS_H = 0x0A
__MPUREG_ZA_OFFS_L = 0x0B
__MPUREG_PRODUCT_ID = 0x0C
__MPUREG_SELF_TEST_X = 0x0D
__MPUREG_SELF_TEST_Y = 0x0E
__MPUREG_SELF_TEST_Z = 0x0F
__MPUREG_SELF_TEST_A = 0x10
__MPUREG_XG_OFFS_USRH = 0x13
__MPUREG_XG_OFFS_USRL = 0x14
__MPUREG_YG_OFFS_USRH = 0x15
__MPUREG_YG_OFFS_USRL = 0x16
__MPUREG_ZG_OFFS_USRH = 0x17
__MPUREG_ZG_OFFS_USRL = 0x18
__MPUREG_SMPLRT_DIV = 0x19
__MPUREG_CONFIG = 0x1A
__MPUREG_GYRO_CONFIG = 0x1B
__MPUREG_ACCEL_CONFIG = 0x1C
__MPUREG_ACCEL_CONFIG_2 = 0x1D
__MPUREG_LP_ACCEL_ODR = 0x1E
__MPUREG_MOT_THR = 0x1F
__MPUREG_FIFO_EN = 0x23
__MPUREG_I2C_MST_CTRL = 0x24
__MPUREG_I2C_SLV0_ADDR = 0x25
__MPUREG_I2C_SLV0_REG = 0x26
__MPUREG_I2C_SLV0_CTRL = 0x27
__MPUREG_I2C_SLV1_ADDR = 0x28
__MPUREG_I2C_SLV1_REG = 0x29
__MPUREG_I2C_SLV1_CTRL = 0x2A
__MPUREG_I2C_SLV2_ADDR = 0x2B
__MPUREG_I2C_SLV2_REG = 0x2C
__MPUREG_I2C_SLV2_CTRL = 0x2D
__MPUREG_I2C_SLV3_ADDR = 0x2E
__MPUREG_I2C_SLV3_REG = 0x2F
__MPUREG_I2C_SLV3_CTRL = 0x30
__MPUREG_I2C_SLV4_ADDR = 0x31
__MPUREG_I2C_SLV4_REG = 0x32
__MPUREG_I2C_SLV4_DO = 0x33
__MPUREG_I2C_SLV4_CTRL = 0x34
__MPUREG_I2C_SLV4_DI = 0x35
__MPUREG_I2C_MST_STATUS = 0x36
__MPUREG_INT_PIN_CFG = 0x37
__MPUREG_INT_ENABLE = 0x38
__MPUREG_ACCEL_XOUT_H = 0x3B
__MPUREG_ACCEL_XOUT_L = 0x3C
__MPUREG_ACCEL_YOUT_H = 0x3D
__MPUREG_ACCEL_YOUT_L = 0x3E
__MPUREG_ACCEL_ZOUT_H = 0x3F
__MPUREG_ACCEL_ZOUT_L = 0x40
__MPUREG_TEMP_OUT_H = 0x41
__MPUREG_TEMP_OUT_L = 0x42
__MPUREG_GYRO_XOUT_H = 0x43
__MPUREG_GYRO_XOUT_L = 0x44
__MPUREG_GYRO_YOUT_H = 0x45
__MPUREG_GYRO_YOUT_L = 0x46
__MPUREG_GYRO_ZOUT_H = 0x47
__MPUREG_GYRO_ZOUT_L = 0x48
__MPUREG_EXT_SENS_DATA_00 = 0x49
__MPUREG_EXT_SENS_DATA_01 = 0x4A
__MPUREG_EXT_SENS_DATA_02 = 0x4B
__MPUREG_EXT_SENS_DATA_03 = 0x4C
__MPUREG_EXT_SENS_DATA_04 = 0x4D
__MPUREG_EXT_SENS_DATA_05 = 0x4E
__MPUREG_EXT_SENS_DATA_06 = 0x4F
__MPUREG_EXT_SENS_DATA_07 = 0x50
__MPUREG_EXT_SENS_DATA_08 = 0x51
__MPUREG_EXT_SENS_DATA_09 = 0x52
__MPUREG_EXT_SENS_DATA_10 = 0x53
__MPUREG_EXT_SENS_DATA_11 = 0x54
__MPUREG_EXT_SENS_DATA_12 = 0x55
__MPUREG_EXT_SENS_DATA_13 = 0x56
__MPUREG_EXT_SENS_DATA_14 = 0x57
__MPUREG_EXT_SENS_DATA_15 = 0x58
__MPUREG_EXT_SENS_DATA_16 = 0x59
__MPUREG_EXT_SENS_DATA_17 = 0x5A
__MPUREG_EXT_SENS_DATA_18 = 0x5B
__MPUREG_EXT_SENS_DATA_19 = 0x5C
__MPUREG_EXT_SENS_DATA_20 = 0x5D
__MPUREG_EXT_SENS_DATA_21 = 0x5E
__MPUREG_EXT_SENS_DATA_22 = 0x5F
__MPUREG_EXT_SENS_DATA_23 = 0x60
__MPUREG_I2C_SLV0_DO = 0x63
__MPUREG_I2C_SLV1_DO = 0x64
__MPUREG_I2C_SLV2_DO = 0x65
__MPUREG_I2C_SLV3_DO = 0x66
__MPUREG_I2C_MST_DELAY_CTRL = 0x67
__MPUREG_SIGNAL_PATH_RESET = 0x68
__MPUREG_MOT_DETECT_CTRL = 0x69
__MPUREG_USER_CTRL = 0x6A
__MPUREG_PWR_MGMT_1 = 0x6B
__MPUREG_PWR_MGMT_2 = 0x6C
__MPUREG_BANK_SEL = 0x6D
__MPUREG_MEM_START_ADDR = 0x6E
__MPUREG_MEM_R_W = 0x6F
__MPUREG_DMP_CFG_1 = 0x70
__MPUREG_DMP_CFG_2 = 0x71
__MPUREG_FIFO_COUNTH = 0x72
__MPUREG_FIFO_COUNTL = 0x73
__MPUREG_FIFO_R_W = 0x74
__MPUREG_WHOAMI = 0x75
__MPUREG_XA_OFFSET_H = 0x77
__MPUREG_XA_OFFSET_L = 0x78
__MPUREG_YA_OFFSET_H = 0x7A
__MPUREG_YA_OFFSET_L = 0x7B
__MPUREG_ZA_OFFSET_H = 0x7D
__MPUREG_ZA_OFFSET_L = 0x7E
# ---- AK8963 Reg In MPU9250 -----------------------------------------------
__AK8963_I2C_ADDR = 0x0c #0x18
__AK8963_Device_ID = 0x48
# Read-only Reg
__AK8963_WIA = 0x00
__AK8963_INFO = 0x01
__AK8963_ST1 = 0x02
__AK8963_HXL = 0x03
__AK8963_HXH = 0x04
__AK8963_HYL = 0x05
__AK8963_HYH = 0x06
__AK8963_HZL = 0x07
__AK8963_HZH = 0x08
__AK8963_ST2 = 0x09
# Write/Read Reg
__AK8963_CNTL1 = 0x0A
__AK8963_CNTL2 = 0x0B
__AK8963_ASTC = 0x0C
__AK8963_TS1 = 0x0D
__AK8963_TS2 = 0x0E
__AK8963_I2CDIS = 0x0F
# Read-only Reg ( ROM )
__AK8963_ASAX = 0x10
__AK8963_ASAY = 0x11
__AK8963_ASAZ = 0x12
# Configuration bits MPU9250
__BIT_SLEEP = 0x40
__BIT_H_RESET = 0x80
__BITS_CLKSEL = 0x07
__MPU_CLK_SEL_PLLGYROX = 0x01
__MPU_CLK_SEL_PLLGYROZ = 0x03
__MPU_EXT_SYNC_GYROX = 0x02
__BITS_FS_250DPS = 0x00
__BITS_FS_500DPS = 0x08
__BITS_FS_1000DPS = 0x10
__BITS_FS_2000DPS = 0x18
__BITS_FS_2G = 0x00
__BITS_FS_4G = 0x08
__BITS_FS_8G = 0x10
__BITS_FS_16G = 0x18
__BITS_FS_MASK = 0x18
__BITS_DLPF_CFG_256HZ_NOLPF2 = 0x00
__BITS_DLPF_CFG_188HZ = 0x01
__BITS_DLPF_CFG_98HZ = 0x02
__BITS_DLPF_CFG_42HZ = 0x03
__BITS_DLPF_CFG_20HZ = 0x04
__BITS_DLPF_CFG_10HZ = 0x05
__BITS_DLPF_CFG_5HZ = 0x06
__BITS_DLPF_CFG_2100HZ_NOLPF = 0x07
__BITS_DLPF_CFG_MASK = 0x07
__BIT_INT_ANYRD_2CLEAR = 0x10
__BIT_RAW_RDY_EN = 0x01
__BIT_I2C_IF_DIS = 0x10
__READ_FLAG = 0x80
# ---- Sensitivity ---------------------------------------------------------
__MPU9250A_2g = (0.000061035156) # 0.000061035156 g/LSB
__MPU9250A_4g = (0.000122070312) # 0.000122070312 g/LSB
__MPU9250A_8g = (0.000244140625) # 0.000244140625 g/LSB
__MPU9250A_16g = (0.000488281250) # 0.000488281250 g/LSB
__MPU9250G_250dps = (0.007633587786) # 0.007633587786 dps/LSB
__MPU9250G_500dps = (0.015267175572) # 0.015267175572 dps/LSB
__MPU9250G_1000dps = (0.030487804878) # 0.030487804878 dps/LSB
__MPU9250G_2000dps = (0.060975609756) # 0.060975609756 dps/LSB
__MPU9250M_4800uT = (0.6) # 0.6 uT/LSB
__MPU9250T_85degC = (0.002995177763) # 0.002995177763 degC/LSB
__Magnetometer_Sensitivity_Scale_Factor = (0.15)
def __init__(self, spi_bus_number = 0, spi_dev_number = 1):
self.bus = spidev.SpiDev()
self.spi_bus_number = spi_bus_number
self.spi_dev_number = spi_dev_number
self.gyro_divider = 0.0
self.acc_divider = 0.0
self.calib_data = [0.0, 0.0, 0.0]
self.magnetometer_ASA = [0.0, 0.0, 0.0]
self.temperature = 0.0
self.gyroscope_data = [0.0, 0.0, 0.0]
self.accelerometer_data = [0.0, 0.0, 0.0]
self.magnetometer_data = [0.0, 0.0, 0.0]
# -----------------------------------------------------------------------------------------------
# REGISTER READ & WRITE
# usage: use these methods to read and write MPU9250 registers over SPI
# -----------------------------------------------------------------------------------------------
def WriteReg(self, reg_address, data):
self.bus.open(self.spi_bus_number, self.spi_dev_number)
tx = [reg_address, data]
rx = self.bus.xfer2(tx)
self.bus.close()
return rx
# -----------------------------------------------------------------------------------------------
def ReadReg(self, reg_address):
self.bus.open(self.spi_bus_number, self.spi_dev_number)
tx = [reg_address | self.__READ_FLAG, 0x00]
rx = self.bus.xfer2(tx)
self.bus.close()
return rx[1]
# -----------------------------------------------------------------------------------------------
def ReadRegs(self, reg_address, length):
self.bus.open(self.spi_bus_number, self.spi_dev_number)
tx = [0] * (length + 1)
tx[0] = reg_address | self.__READ_FLAG
rx = self.bus.xfer2(tx)
self.bus.close()
return rx[1:len(rx)]
# -----------------------------------------------------------------------------------------------
# TEST CONNECTION
# usage: call this function to know if SPI and MPU9250 are working correctly.
# returns true if mpu9250 answers
# -----------------------------------------------------------------------------------------------
def testConnection(self):
response = self.ReadReg(self.__MPUREG_WHOAMI)
if (response == 0x71):
return True
else:
return False
# -----------------------------------------------------------------------------------------------
# INITIALIZATION
# usage: call this function at startup, giving the sample rate divider (raging from 0 to 255) and
# low pass filter value; suitable values are:
# BITS_DLPF_CFG_256HZ_NOLPF2
# BITS_DLPF_CFG_188HZ
# BITS_DLPF_CFG_98HZ
# BITS_DLPF_CFG_42HZ
# BITS_DLPF_CFG_20HZ
# BITS_DLPF_CFG_10HZ
# BITS_DLPF_CFG_5HZ
# BITS_DLPF_CFG_2100HZ_NOLPF
# returns 1 if an error occurred
# -----------------------------------------------------------------------------------------------
def initialize(self, sample_rate_div = 1, low_pass_filter = 0x01):
MPU_InitRegNum = 17
MPU_Init_Data = [[0, 0]] * MPU_InitRegNum
MPU_Init_Data = [
[0x80, self.__MPUREG_PWR_MGMT_1], # Reset Device
[0x01, self.__MPUREG_PWR_MGMT_1], # Clock Source
[0x00, self.__MPUREG_PWR_MGMT_2], # Enable Acc & Gyro
[low_pass_filter, self.__MPUREG_CONFIG], # Use DLPF set Gyroscope bandwidth 184Hz, temperature bandwidth 188Hz
[0x18, self.__MPUREG_GYRO_CONFIG], # +-2000dps
[0x08, self.__MPUREG_ACCEL_CONFIG], # +-4G
[0x09, self.__MPUREG_ACCEL_CONFIG_2], # Set Acc Data Rates, Enable Acc LPF , Bandwidth 184Hz
[0x30, self.__MPUREG_INT_PIN_CFG],
#[0x40, self.__MPUREG_I2C_MST_CTRL], # I2C Speed 348 kHz
#[0x20, self.__MPUREG_USER_CTRL], # Enable AUX
[0x20, self.__MPUREG_USER_CTRL], # I2C Master mode
[0x0D, self.__MPUREG_I2C_MST_CTRL], # I2C configuration multi-master IIC 400KHz
[self.__AK8963_I2C_ADDR, self.__MPUREG_I2C_SLV0_ADDR], #Set the I2C slave addres of AK8963 and set for write.
#[0x09, self.__MPUREG_I2C_SLV4_CTRL],
#[0x81, self.__MPUREG_I2C_MST_DELAY_CTRL], #Enable I2C delay
[self.__AK8963_CNTL2, self.__MPUREG_I2C_SLV0_REG], #I2C slave 0 register address from where to begin data transfer
[0x01, self.__MPUREG_I2C_SLV0_DO], # Reset AK8963
[0x81, self.__MPUREG_I2C_SLV0_CTRL], #Enable I2C and set 1 byte
[self.__AK8963_CNTL1, self.__MPUREG_I2C_SLV0_REG], #I2C slave 0 register address from where to begin data transfer
[0x12, self.__MPUREG_I2C_SLV0_DO], # Register value to continuous measurement in 16bit
[0x81, self.__MPUREG_I2C_SLV0_CTRL] #Enable I2C and set 1 byte
]
for i in range(0, MPU_InitRegNum):
self.WriteReg(MPU_Init_Data[i][1], MPU_Init_Data[i][0])
time.sleep(0.01) # I2C must slow down the write speed, otherwise it won't work
self.set_acc_scale(self.__BITS_FS_16G)
self.set_gyro_scale(self.__BITS_FS_2000DPS)
self.calib_mag()
# -----------------------------------------------------------------------------------------------
# ACCELEROMETER SCALE
# usage: call this function at startup, after initialization, to set the right range for the
# accelerometers. Suitable ranges are:
# BITS_FS_2G
# BITS_FS_4G
# BITS_FS_8G
# BITS_FS_16G
# returns the range set (2,4,8 or 16)
# -----------------------------------------------------------------------------------------------
def set_acc_scale(self, scale):
self.WriteReg(self.__MPUREG_ACCEL_CONFIG, scale)
if (scale == self.__BITS_FS_2G):
self.acc_divider = 16384.0
elif (scale == self.__BITS_FS_4G):
self.acc_divider = 8192.0
elif (scale == self.__BITS_FS_8G):
self.acc_divider = 4096.0
elif (scale == self.__BITS_FS_16G):
self.acc_divider = 2048.0
temp_scale = self.ReadReg(self.__MPUREG_ACCEL_CONFIG)
if (temp_scale == self.__BITS_FS_2G):
temp_scale = 2
elif (temp_scale == self.__BITS_FS_4G):
temp_scale = 4
elif (temp_scale == self.__BITS_FS_8G):
temp_scale = 8
elif (temp_scale == self.__BITS_FS_16G):
temp_scale = 16
return temp_scale
# -----------------------------------------------------------------------------------------------
# GYROSCOPE SCALE
# usage: call this function at startup, after initialization, to set the right range for the
# gyroscopes. Suitable ranges are:
# BITS_FS_250DPS
# BITS_FS_500DPS
# BITS_FS_1000DPS
# BITS_FS_2000DPS
# returns the range set (250,500,1000 or 2000)
# -----------------------------------------------------------------------------------------------
def set_gyro_scale(self, scale):
self.WriteReg(self.__MPUREG_GYRO_CONFIG, scale)
if (scale == self.__BITS_FS_250DPS):
self.gyro_divider = 131.0
elif (scale == self.__BITS_FS_500DPS):
self.gyro_divider = 65.6
elif (scale == self.__BITS_FS_1000DPS):
self.gyro_divider = 32.8
elif (scale == self.__BITS_FS_2000DPS):
self.gyro_divider = 16.4
temp_scale = self.ReadReg(self.__MPUREG_GYRO_CONFIG)
if (temp_scale == self.__BITS_FS_250DPS):
temp_scale = 250
elif (temp_scale == self.__BITS_FS_500DPS):
temp_scale = 500
elif (temp_scale == self.__BITS_FS_1000DPS):
temp_scale = 1000
elif (temp_scale == self.__BITS_FS_2000DPS):
temp_scale = 2000
return temp_scale
# -----------------------------------------------------------------------------------------------
# WHO AM I?
# usage: call this function to know if SPI is working correctly. It checks the I2C address of the
# mpu9250 which should be 104 when in SPI mode.
# returns the I2C address (104)
# -----------------------------------------------------------------------------------------------
def whoami(self):
return self.ReadReg(self.__MPUREG_WHOAMI)
# -----------------------------------------------------------------------------------------------
# READ ACCELEROMETER
# usage: call this function to read accelerometer data. Axis represents selected axis:
# 0 -> X axis
# 1 -> Y axis
# 2 -> Z axis
# -----------------------------------------------------------------------------------------------
def read_acc(self):
response = self.ReadRegs(self.__MPUREG_ACCEL_XOUT_H, 6)
for i in range(0, 3):
data = self.byte_to_float(response[i*2:i*2+2])
self.accelerometer_data[i] = self.G_SI * data / self.acc_divider
# -----------------------------------------------------------------------------------------------
# READ GYROSCOPE
# usage: call this function to read gyroscope data. Axis represents selected axis:
# 0 -> X axis
# 1 -> Y axis
# 2 -> Z axis
# -----------------------------------------------------------------------------------------------
def read_gyro(self):
response = self.ReadRegs(self.__MPUREG_GYRO_XOUT_H, 6)
for i in range(0, 3):
data = self.byte_to_float(response[i*2:i*2+2])
self.gyroscope_data[i] = (self.PI/180) * data / self.gyro_divider
# -----------------------------------------------------------------------------------------------
# READ TEMPERATURE
# usage: call this function to read temperature data.
# returns the value in Celsius
# -----------------------------------------------------------------------------------------------
def read_temp(self):
response = self.ReadRegs(self.__MPUREG_TEMP_OUT_H, 2)
#temp = response[0]*256.0 + response[1]
temp = self.byte_to_float(response)
self.temperature = (temp/340.0)+36.53
# -----------------------------------------------------------------------------------------------
# READ ACCELEROMETER CALIBRATION
# usage: call this function to read accelerometer data. Axis represents selected axis:
# 0 -> X axis
# 1 -> Y axis
# 2 -> Z axis
# returns Factory Trim value
# -----------------------------------------------------------------------------------------------
def calib_acc(self):
temp_scale = self.ReadReg(self.__MPUREG_ACCEL_CONFIG)
self.set_acc_scale(self.__BITS_FS_8G)
response = self.ReadRegs(self.__MPUREG_SELF_TEST_X, 4)
self.calib_data[0] = ((response[0] & 11100000) >> 3) | ((response[3] & 00110000) >> 4)
self.calib_data[1] = ((response[1] & 11100000) >> 3) | ((response[3] & 00001100) >> 2)
self.calib_data[2] = ((response[2] & 11100000) >> 3) | ((response[3] & 00000011))
self.set_acc_scale(temp_scale)
# -----------------------------------------------------------------------------------------------
def AK8963_whoami(self):
self.WriteReg(self.__MPUREG_I2C_SLV0_ADDR, self.__AK8963_I2C_ADDR | self.__READ_FLAG) #Set the I2C slave addres of AK8963 and set for read.
self.WriteReg(self.__MPUREG_I2C_SLV0_REG, self.__AK8963_WIA) #I2C slave 0 register address from where to begin data transfer
self.WriteReg(self.__MPUREG_I2C_SLV0_CTRL, 0x81) #Read 1 byte from the magnetometer
#self.WriteReg(self.__MPUREG_I2C_SLV0_CTRL, 0x81) # Enable I2C and set bytes
time.sleep(0.01)
return self.ReadReg(self.__MPUREG_EXT_SENS_DATA_00) # Read I2C
# -----------------------------------------------------------------------------------------------
def calib_mag(self):
# Set the I2C slave addres of AK8963 and set for read.
self.WriteReg(self.__MPUREG_I2C_SLV0_ADDR, self.__AK8963_I2C_ADDR | self.__READ_FLAG)
# I2C slave 0 register address from where to begin data transfer
self.WriteReg(self.__MPUREG_I2C_SLV0_REG, self.__AK8963_ASAX)
# Read 3 bytes from the magnetometer
self.WriteReg(self.__MPUREG_I2C_SLV0_CTRL, 0x83)
time.sleep(0.01)
response = self.ReadRegs(self.__MPUREG_EXT_SENS_DATA_00, 3)
for i in range(0, 3):
self.magnetometer_ASA[i] = ((float(response[i]) - 128)/256 + 1) * self.__Magnetometer_Sensitivity_Scale_Factor
# -----------------------------------------------------------------------------------------------
def read_mag(self):
# Set the I2C slave addres of AK8963 and set for read.
self.WriteReg(self.__MPUREG_I2C_SLV0_ADDR, self.__AK8963_I2C_ADDR | self.__READ_FLAG)
# I2C slave 0 register address from where to begin data transfer
self.WriteReg(self.__MPUREG_I2C_SLV0_REG, self.__AK8963_HXL)
# Read 6 bytes from the magnetometer
self.WriteReg(self.__MPUREG_I2C_SLV0_CTRL, 0x87)
time.sleep(0.01)
response = self.ReadRegs(self.__MPUREG_EXT_SENS_DATA_00, 7)
#must start your read from AK8963A register 0x03 and read seven bytes so that upon read of ST2 register 0x09 the AK8963A will unlatch the data registers for the next measurement.
for i in range(0, 3):
data = self.byte_to_float_le(response[i*2:i*2+2])
self.magnetometer_data[i] = data * self.magnetometer_ASA[i]
# -----------------------------------------------------------------------------------------------
def read_all(self):
# Send I2C command at first
# Set the I2C slave addres of AK8963 and set for read.
self.WriteReg(self.__MPUREG_I2C_SLV0_ADDR, self.__AK8963_I2C_ADDR | self.__READ_FLAG)
# I2C slave 0 register address from where ; //Read 7 bytes from the magnetometerto begin data transfer
self.WriteReg(self.__MPUREG_I2C_SLV0_REG, self.__AK8963_HXL)
# Read 7 bytes from the magnetometer
self.WriteReg(self.__MPUREG_I2C_SLV0_CTRL, 0x87)
# must start your read from AK8963A register 0x03 and read seven bytes so that upon read of ST2 register 0x09 the AK8963A will unlatch the data registers for the next measurement.
# time.sleep(0.001)
response = self.ReadRegs(self.__MPUREG_ACCEL_XOUT_H, 21);
# Get Accelerometer values
for i in range(0, 3):
data = self.byte_to_float(response[i*2:i*2+2])
self.accelerometer_data[i] = self.G_SI * data / self.acc_divider
# Get temperature
i = 3
temp = self.byte_to_float(response[i*2:i*2+2])
self.temperature = (temp/340.0)+36.53
# Get gyroscope values
for i in range(4, 7):
data = self.byte_to_float(response[i*2:i*2+2])
self.gyroscope_data[i-4] = (self.PI/180) * data / self.gyro_divider
# Get magnetometer values
for i in range(7, 10):
data = self.byte_to_float_le(response[i*2:i*2+2])
self.magnetometer_data[i-7] = data * self.magnetometer_ASA[i-7]
# -----------------------------------------------------------------------------------------------
# GET VALUES
# usage: call this functions to read and get values
# returns accel, gyro and mag values
# -----------------------------------------------------------------------------------------------
def getMotion9(self):
self.read_all()
m9a = self.accelerometer_data
m9g = self.gyroscope_data
m9m = self.magnetometer_data
return m9a, m9g, m9m
# -----------------------------------------------------------------------------------------------
def getMotion6(self):
self.read_acc()
self.read_gyro()
m6a = self.accelerometer_data
m6g = self.gyroscope_data
return m6a, m6g
# -----------------------------------------------------------------------------------------------
def byte_to_float(self, input_buffer):
byte_array = array.array("B", input_buffer)
signed_16_bit_int, = struct.unpack(">h", byte_array)
return float(signed_16_bit_int)
# -----------------------------------------------------------------------------------------------
def byte_to_float_le(self, input_buffer):
byte_array = array.array("B", input_buffer)
signed_16_bit_int, = struct.unpack("<h", byte_array)
return float(signed_16_bit_int)

294
darkwater_640/smbus.py Normal file
View File

@@ -0,0 +1,294 @@
# Copyright (c) 2016 Adafruit Industries
# Author: Tony DiCola
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
from ctypes import *
from fcntl import ioctl
import struct
# I2C C API constants (from linux kernel headers)
I2C_M_TEN = 0x0010 # this is a ten bit chip address
I2C_M_RD = 0x0001 # read data, from slave to master
I2C_M_STOP = 0x8000 # if I2C_FUNC_PROTOCOL_MANGLING
I2C_M_NOSTART = 0x4000 # if I2C_FUNC_NOSTART
I2C_M_REV_DIR_ADDR = 0x2000 # if I2C_FUNC_PROTOCOL_MANGLING
I2C_M_IGNORE_NAK = 0x1000 # if I2C_FUNC_PROTOCOL_MANGLING
I2C_M_NO_RD_ACK = 0x0800 # if I2C_FUNC_PROTOCOL_MANGLING
I2C_M_RECV_LEN = 0x0400 # length will be first received byte
I2C_SLAVE = 0x0703 # Use this slave address
I2C_SLAVE_FORCE = 0x0706 # Use this slave address, even if
# is already in use by a driver!
I2C_TENBIT = 0x0704 # 0 for 7 bit addrs, != 0 for 10 bit
I2C_FUNCS = 0x0705 # Get the adapter functionality mask
I2C_RDWR = 0x0707 # Combined R/W transfer (one STOP only)
I2C_PEC = 0x0708 # != 0 to use PEC with SMBus
I2C_SMBUS = 0x0720 # SMBus transfer
# ctypes versions of I2C structs defined by kernel.
class i2c_msg(Structure):
_fields_ = [
('addr', c_uint16),
('flags', c_uint16),
('len', c_uint16),
('buf', POINTER(c_uint8))
]
class i2c_rdwr_ioctl_data(Structure):
_fields_ = [
('msgs', POINTER(i2c_msg)),
('nmsgs', c_uint32)
]
def make_i2c_rdwr_data(messages):
"""Utility function to create and return an i2c_rdwr_ioctl_data structure
populated with a list of specified I2C messages. The messages parameter
should be a list of tuples which represent the individual I2C messages to
send in this transaction. Tuples should contain 4 elements: address value,
flags value, buffer length, ctypes c_uint8 pointer to buffer.
"""
# Create message array and populate with provided data.
msg_data_type = i2c_msg*len(messages)
msg_data = msg_data_type()
for i, m in enumerate(messages):
msg_data[i].addr = m[0] & 0x7F
msg_data[i].flags = m[1]
msg_data[i].len = m[2]
msg_data[i].buf = m[3]
# Now build the data structure.
data = i2c_rdwr_ioctl_data()
data.msgs = msg_data
data.nmsgs = len(messages)
return data
# Create an interface that mimics the Python SMBus API.
class SMBus(object):
"""I2C interface that mimics the Python SMBus API but is implemented with
pure Python calls to ioctl and direct /dev/i2c device access.
"""
def __init__(self, bus=None):
"""Create a new smbus instance. Bus is an optional parameter that
specifies the I2C bus number to use, for example 1 would use device
/dev/i2c-1. If bus is not specified then the open function should be
called to open the bus.
"""
self._device = None
if bus is not None:
self.open(bus)
def __del__(self):
"""Clean up any resources used by the SMBus instance."""
self.close()
def __enter__(self):
"""Context manager enter function."""
# Just return this object so it can be used in a with statement, like
# with SMBus(1) as bus:
# # do stuff!
return self
def __exit__(self, exc_type, exc_val, exc_tb):
"""Context manager exit function, ensures resources are cleaned up."""
self.close()
return False # Don't suppress exceptions.
def open(self, bus):
"""Open the smbus interface on the specified bus."""
# Close the device if it's already open.
if self._device is not None:
self.close()
# Try to open the file for the specified bus. Must turn off buffering
# or else Python 3 fails (see: https://bugs.python.org/issue20074)
self._device = open('/dev/i2c-{0}'.format(bus), 'r+b', buffering=0)
# TODO: Catch IOError and throw a better error message that describes
# what's wrong (i.e. I2C may not be enabled or the bus doesn't exist).
def close(self):
"""Close the smbus connection. You cannot make any other function
calls on the bus unless open is called!"""
if self._device is not None:
self._device.close()
self._device = None
def _select_device(self, addr):
"""Set the address of the device to communicate with on the I2C bus."""
ioctl(self._device.fileno(), I2C_SLAVE, addr & 0x7F)
def read_byte(self, addr):
"""Read a single byte from the specified device."""
assert self._device is not None, 'Bus must be opened before operations are made against it!'
self._select_device(addr)
return ord(self._device.read(1))
def read_byte_data(self, addr, cmd):
"""Read a single byte from the specified cmd register of the device."""
assert self._device is not None, 'Bus must be opened before operations are made against it!'
# Build ctypes values to marshall between ioctl and Python.
reg = c_uint8(cmd)
result = c_uint8()
# Build ioctl request.
request = make_i2c_rdwr_data([
(addr, 0, 1, pointer(reg)), # Write cmd register.
(addr, I2C_M_RD, 1, pointer(result)) # Read 1 byte as result.
])
# Make ioctl call and return result data.
ioctl(self._device.fileno(), I2C_RDWR, request)
return result.value
def read_word_data(self, addr, cmd):
"""Read a word (2 bytes) from the specified cmd register of the device.
Note that this will interpret data using the endianness of the processor
running Python (typically little endian)!
"""
assert self._device is not None, 'Bus must be opened before operations are made against it!'
# Build ctypes values to marshall between ioctl and Python.
reg = c_uint8(cmd)
result = c_uint16()
# Build ioctl request.
request = make_i2c_rdwr_data([
(addr, 0, 1, pointer(reg)), # Write cmd register.
(addr, I2C_M_RD, 2, cast(pointer(result), POINTER(c_uint8))) # Read word (2 bytes).
])
# Make ioctl call and return result data.
ioctl(self._device.fileno(), I2C_RDWR, request)
return result.value
def read_block_data(self, addr, cmd):
"""Perform a block read from the specified cmd register of the device.
The amount of data read is determined by the first byte send back by
the device. Data is returned as a bytearray.
"""
# TODO: Unfortunately this will require calling the low level I2C
# access ioctl to trigger a proper read_block_data. The amount of data
# returned isn't known until the device starts responding so an I2C_RDWR
# ioctl won't work.
raise NotImplementedError()
def read_i2c_block_data(self, addr, cmd, length=32):
"""Perform a read from the specified cmd register of device. Length number
of bytes (default of 32) will be read and returned as a bytearray.
"""
assert self._device is not None, 'Bus must be opened before operations are made against it!'
# Build ctypes values to marshall between ioctl and Python.
reg = c_uint8(cmd)
result = create_string_buffer(length)
# Build ioctl request.
request = make_i2c_rdwr_data([
(addr, 0, 1, pointer(reg)), # Write cmd register.
(addr, I2C_M_RD, length, cast(result, POINTER(c_uint8))) # Read data.
])
# Make ioctl call and return result data.
ioctl(self._device.fileno(), I2C_RDWR, request)
return bytearray(result.raw) # Use .raw instead of .value which will stop at a null byte!
def write_quick(self, addr):
"""Write a single byte to the specified device."""
# What a strange function, from the python-smbus source this appears to
# just write a single byte that initiates a write to the specified device
# address (but writes no data!). The functionality is duplicated below
# but the actual use case for this is unknown.
assert self._device is not None, 'Bus must be opened before operations are made against it!'
# Build ioctl request.
request = make_i2c_rdwr_data([
(addr, 0, 0, None), # Write with no data.
])
# Make ioctl call and return result data.
ioctl(self._device.fileno(), I2C_RDWR, request)
def write_byte(self, addr, val):
"""Write a single byte to the specified device."""
assert self._device is not None, 'Bus must be opened before operations are made against it!'
self._select_device(addr)
data = bytearray(1)
data[0] = val & 0xFF
self._device.write(data)
def write_byte_data(self, addr, cmd, val):
"""Write a byte of data to the specified cmd register of the device.
"""
assert self._device is not None, 'Bus must be opened before operations are made against it!'
# Construct a string of data to send with the command register and byte value.
data = bytearray(2)
data[0] = cmd & 0xFF
data[1] = val & 0xFF
# Send the data to the device.
self._select_device(addr)
self._device.write(data)
def write_word_data(self, addr, cmd, val):
"""Write a word (2 bytes) of data to the specified cmd register of the
device. Note that this will write the data in the endianness of the
processor running Python (typically little endian)!
"""
assert self._device is not None, 'Bus must be opened before operations are made against it!'
# Construct a string of data to send with the command register and word value.
data = struct.pack('=BH', cmd & 0xFF, val & 0xFFFF)
# Send the data to the device.
self._select_device(addr)
self._device.write(data)
def write_block_data(self, addr, cmd, vals):
"""Write a block of data to the specified cmd register of the device.
The amount of data to write should be the first byte inside the vals
string/bytearray and that count of bytes of data to write should follow
it.
"""
# Just use the I2C block data write to write the provided values and
# their length as the first byte.
data = bytearray(len(vals)+1)
data[0] = len(vals) & 0xFF
data[1:] = vals[0:]
self.write_i2c_block_data(addr, cmd, data)
def write_i2c_block_data(self, addr, cmd, vals):
"""Write a buffer of data to the specified cmd register of the device.
"""
assert self._device is not None, 'Bus must be opened before operations are made against it!'
# Construct a string of data to send, including room for the command register.
data = bytearray(len(vals)+1)
data[0] = cmd & 0xFF # Command register at the start.
data[1:] = vals[0:] # Copy in the block data (ugly but necessary to ensure
# the entire write happens in one transaction).
# Send the data to the device.
self._select_device(addr)
self._device.write(data)
def process_call(self, addr, cmd, val):
"""Perform a smbus process call by writing a word (2 byte) value to
the specified register of the device, and then reading a word of response
data (which is returned).
"""
assert self._device is not None, 'Bus must be opened before operations are made against it!'
# Build ctypes values to marshall between ioctl and Python.
data = create_string_buffer(struct.pack('=BH', cmd, val))
result = c_uint16()
# Build ioctl request.
request = make_i2c_rdwr_data([
(addr, 0, 3, cast(pointer(data), POINTER(c_uint8))), # Write data.
(addr, I2C_M_RD, 2, cast(pointer(result), POINTER(c_uint8))) # Read word (2 bytes).
])
# Make ioctl call and return result data.
ioctl(self._device.fileno(), I2C_RDWR, request)
# Note the python-smbus code appears to have a rather serious bug and
# does not return the result value! This is fixed below by returning it.
return result.value

View File

@@ -1,161 +0,0 @@
#!/usr/bin/python
import re
import smbus
# ===========================================================================
# Adafruit_I2C Class
# ===========================================================================
class Adafruit_I2C(object):
@staticmethod
def getPiRevision():
"Gets the version number of the Raspberry Pi board"
# Revision list available at: http://elinux.org/RPi_HardwareHistory#Board_Revision_History
try:
with open('/proc/cpuinfo', 'r') as infile:
for line in infile:
# Match a line of the form "Revision : 0002" while ignoring extra
# info in front of the revsion (like 1000 when the Pi was over-volted).
match = re.match('Revision\s+:\s+.*(\w{4})$', line)
if match and match.group(1) in ['0000', '0002', '0003']:
# Return revision 1 if revision ends with 0000, 0002 or 0003.
return 1
elif match:
# Assume revision 2 if revision ends with any other 4 chars.
return 2
# Couldn't find the revision, assume revision 0 like older code for compatibility.
return 0
except:
return 0
@staticmethod
def getPiI2CBusNumber():
# Gets the I2C bus number /dev/i2c#
return 1 if Adafruit_I2C.getPiRevision() > 1 else 0
def __init__(self, address, busnum=-1, debug=False):
self.address = address
# By default, the correct I2C bus is auto-detected using /proc/cpuinfo
# Alternatively, you can hard-code the bus version below:
# self.bus = smbus.SMBus(0); # Force I2C0 (early 256MB Pi's)
# self.bus = smbus.SMBus(1); # Force I2C1 (512MB Pi's)
self.bus = smbus.SMBus(busnum if busnum >= 0 else Adafruit_I2C.getPiI2CBusNumber())
self.debug = debug
def reverseByteOrder(self, data):
"Reverses the byte order of an int (16-bit) or long (32-bit) value"
# Courtesy Vishal Sapre
byteCount = len(hex(data)[2:].replace('L','')[::2])
val = 0
for i in range(byteCount):
val = (val << 8) | (data & 0xff)
data >>= 8
return val
def errMsg(self):
print "Error accessing 0x%02X: Check your I2C address" % self.address
return -1
def write8(self, reg, value):
"Writes an 8-bit value to the specified register/address"
try:
self.bus.write_byte_data(self.address, reg, value)
if self.debug:
print "I2C: Wrote 0x%02X to register 0x%02X" % (value, reg)
except IOError, err:
return self.errMsg()
def write16(self, reg, value):
"Writes a 16-bit value to the specified register/address pair"
try:
self.bus.write_word_data(self.address, reg, value)
if self.debug:
print ("I2C: Wrote 0x%02X to register pair 0x%02X,0x%02X" %
(value, reg, reg+1))
except IOError, err:
return self.errMsg()
def writeRaw8(self, value):
"Writes an 8-bit value on the bus"
try:
self.bus.write_byte(self.address, value)
if self.debug:
print "I2C: Wrote 0x%02X" % value
except IOError, err:
return self.errMsg()
def writeList(self, reg, list):
"Writes an array of bytes using I2C format"
try:
if self.debug:
print "I2C: Writing list to register 0x%02X:" % reg
print list
self.bus.write_i2c_block_data(self.address, reg, list)
except IOError, err:
return self.errMsg()
def readList(self, reg, length):
"Read a list of bytes from the I2C device"
try:
results = self.bus.read_i2c_block_data(self.address, reg, length)
if self.debug:
print ("I2C: Device 0x%02X returned the following from reg 0x%02X" %
(self.address, reg))
print results
return results
except IOError, err:
return self.errMsg()
def readU8(self, reg):
"Read an unsigned byte from the I2C device"
try:
result = self.bus.read_byte_data(self.address, reg)
if self.debug:
print ("I2C: Device 0x%02X returned 0x%02X from reg 0x%02X" %
(self.address, result & 0xFF, reg))
return result
except IOError, err:
return self.errMsg()
def readS8(self, reg):
"Reads a signed byte from the I2C device"
try:
result = self.bus.read_byte_data(self.address, reg)
if result > 127: result -= 256
if self.debug:
print ("I2C: Device 0x%02X returned 0x%02X from reg 0x%02X" %
(self.address, result & 0xFF, reg))
return result
except IOError, err:
return self.errMsg()
def readU16(self, reg, little_endian=True):
"Reads an unsigned 16-bit value from the I2C device"
try:
result = self.bus.read_word_data(self.address,reg)
# Swap bytes if using big endian because read_word_data assumes little
# endian on ARM (little endian) systems.
if not little_endian:
result = ((result << 8) & 0xFF00) + (result >> 8)
if (self.debug):
print "I2C: Device 0x%02X returned 0x%04X from reg 0x%02X" % (self.address, result & 0xFFFF, reg)
return result
except IOError, err:
return self.errMsg()
def readS16(self, reg, little_endian=True):
"Reads a signed 16-bit value from the I2C device"
try:
result = self.readU16(reg,little_endian)
if result > 32767: result -= 65536
return result
except IOError, err:
return self.errMsg()
if __name__ == '__main__':
try:
bus = Adafruit_I2C(address=0)
print "Default I2C bus is accessible"
except:
print "Error accessing default I2C bus"

View File

@@ -1,92 +0,0 @@
#!/usr/bin/python
import time
import math
from Adafruit_I2C import Adafruit_I2C
# ============================================================================
# Adafruit PCA9685 16-Channel PWM Servo Driver
# ============================================================================
class PWM :
# Registers/etc.
__MODE1 = 0x00
__MODE2 = 0x01
__SUBADR1 = 0x02
__SUBADR2 = 0x03
__SUBADR3 = 0x04
__PRESCALE = 0xFE
__LED0_ON_L = 0x06
__LED0_ON_H = 0x07
__LED0_OFF_L = 0x08
__LED0_OFF_H = 0x09
__ALL_LED_ON_L = 0xFA
__ALL_LED_ON_H = 0xFB
__ALL_LED_OFF_L = 0xFC
__ALL_LED_OFF_H = 0xFD
# Bits
__RESTART = 0x80
__SLEEP = 0x10
__ALLCALL = 0x01
__INVRT = 0x10
__OUTDRV = 0x04
general_call_i2c = Adafruit_I2C(0x00)
@classmethod
def softwareReset(cls):
"Sends a software reset (SWRST) command to all the servo drivers on the bus"
cls.general_call_i2c.writeRaw8(0x06) # SWRST
def __init__(self, address=0x40, debug=False):
self.i2c = Adafruit_I2C(address)
self.i2c.debug = debug
self.address = address
self.debug = debug
if (self.debug):
print "Reseting PCA9685 MODE1 (without SLEEP) and MODE2"
self.setAllPWM(0, 0)
self.i2c.write8(self.__MODE2, self.__OUTDRV)
self.i2c.write8(self.__MODE1, self.__ALLCALL)
time.sleep(0.005) # wait for oscillator
mode1 = self.i2c.readU8(self.__MODE1)
mode1 = mode1 & ~self.__SLEEP # wake up (reset sleep)
self.i2c.write8(self.__MODE1, mode1)
time.sleep(0.005) # wait for oscillator
def setPWMFreq(self, freq):
"Sets the PWM frequency"
prescaleval = 25000000.0 # 25MHz
prescaleval /= 4096.0 # 12-bit
prescaleval /= float(freq)
prescaleval -= 1.0
if (self.debug):
print "Setting PWM frequency to %d Hz" % freq
print "Estimated pre-scale: %d" % prescaleval
prescale = math.floor(prescaleval + 0.5)
if (self.debug):
print "Final pre-scale: %d" % prescale
oldmode = self.i2c.readU8(self.__MODE1);
newmode = (oldmode & 0x7F) | 0x10 # sleep
self.i2c.write8(self.__MODE1, newmode) # go to sleep
self.i2c.write8(self.__PRESCALE, int(math.floor(prescale)))
self.i2c.write8(self.__MODE1, oldmode)
time.sleep(0.005)
self.i2c.write8(self.__MODE1, oldmode | 0x80)
def setPWM(self, channel, on, off):
"Sets a single PWM channel"
self.i2c.write8(self.__LED0_ON_L+4*channel, on & 0xFF)
self.i2c.write8(self.__LED0_ON_H+4*channel, on >> 8)
self.i2c.write8(self.__LED0_OFF_L+4*channel, off & 0xFF)
self.i2c.write8(self.__LED0_OFF_H+4*channel, off >> 8)
def setAllPWM(self, on, off):
"Sets a all PWM channels"
self.i2c.write8(self.__ALL_LED_ON_L, on & 0xFF)
self.i2c.write8(self.__ALL_LED_ON_H, on >> 8)
self.i2c.write8(self.__ALL_LED_OFF_L, off & 0xFF)
self.i2c.write8(self.__ALL_LED_OFF_H, off >> 8)

View File

@@ -1 +0,0 @@
from .dw640HAT import dw_DCMotor, dw_MotorCONTROL

View File

@@ -1,282 +0,0 @@
#!/usr/bin/python
import RPi.GPIO as GPIO
from Adafruit_PWM_Servo_Driver import PWM
import time
# class Adafruit_StepperMotor:
# MICROSTEPS = 8
# MICROSTEP_CURVE = [0, 50, 98, 142, 180, 212, 236, 250, 255]
#
# #MICROSTEPS = 16
# # a sinusoidal curve NOT LINEAR!
# #MICROSTEP_CURVE = [0, 25, 50, 74, 98, 120, 141, 162, 180, 197, 212, 225, 236, 244, 250, 253, 255]
#
# def __init__(self, controller, num, steps=200):
# self.MC = controller
# self.revsteps = steps
# self.motornum = num
# self.sec_per_step = 0.1
# self.steppingcounter = 0
# self.currentstep = 0
#
# num -= 1
#
# if (num == 0):
# self.PWMA = 8
# self.AIN2 = 9
# self.AIN1 = 10
# self.PWMB = 13
# self.BIN2 = 12
# self.BIN1 = 11
# elif (num == 1):
# self.PWMA = 2
# self.AIN2 = 3
# self.AIN1 = 4
# self.PWMB = 7
# self.BIN2 = 6
# self.BIN1 = 5
# else:
# raise NameError('MotorHAT Stepper must be between 1 and 2 inclusive')
#
# def setSpeed(self, rpm):
# self.sec_per_step = 60.0 / (self.revsteps * rpm)
# self.steppingcounter = 0
#
# def oneStep(self, dir, style):
# pwm_a = pwm_b = 255
#
# # first determine what sort of stepping procedure we're up to
# if (style == Adafruit_MotorHAT.SINGLE):
# if ((self.currentstep/(self.MICROSTEPS/2)) % 2):
# # we're at an odd step, weird
# if (dir == Adafruit_MotorHAT.FORWARD):
# self.currentstep += self.MICROSTEPS/2
# else:
# self.currentstep -= self.MICROSTEPS/2
# else:
# # go to next even step
# if (dir == Adafruit_MotorHAT.FORWARD):
# self.currentstep += self.MICROSTEPS
# else:
# self.currentstep -= self.MICROSTEPS
# if (style == Adafruit_MotorHAT.DOUBLE):
# if not (self.currentstep/(self.MICROSTEPS/2) % 2):
# # we're at an even step, weird
# if (dir == Adafruit_MotorHAT.FORWARD):
# self.currentstep += self.MICROSTEPS/2
# else:
# self.currentstep -= self.MICROSTEPS/2
# else:
# # go to next odd step
# if (dir == Adafruit_MotorHAT.FORWARD):
# self.currentstep += self.MICROSTEPS
# else:
# self.currentstep -= self.MICROSTEPS
# if (style == Adafruit_MotorHAT.INTERLEAVE):
# if (dir == Adafruit_MotorHAT.FORWARD):
# self.currentstep += self.MICROSTEPS/2
# else:
# self.currentstep -= self.MICROSTEPS/2
#
# if (style == Adafruit_MotorHAT.MICROSTEP):
# if (dir == Adafruit_MotorHAT.FORWARD):
# self.currentstep += 1
# else:
# self.currentstep -= 1
#
# # go to next 'step' and wrap around
# self.currentstep += self.MICROSTEPS * 4
# self.currentstep %= self.MICROSTEPS * 4
#
# pwm_a = pwm_b = 0
# if (self.currentstep >= 0) and (self.currentstep < self.MICROSTEPS):
# pwm_a = self.MICROSTEP_CURVE[self.MICROSTEPS - self.currentstep]
# pwm_b = self.MICROSTEP_CURVE[self.currentstep]
# elif (self.currentstep >= self.MICROSTEPS) and (self.currentstep < self.MICROSTEPS*2):
# pwm_a = self.MICROSTEP_CURVE[self.currentstep - self.MICROSTEPS]
# pwm_b = self.MICROSTEP_CURVE[self.MICROSTEPS*2 - self.currentstep]
# elif (self.currentstep >= self.MICROSTEPS*2) and (self.currentstep < self.MICROSTEPS*3):
# pwm_a = self.MICROSTEP_CURVE[self.MICROSTEPS*3 - self.currentstep]
# pwm_b = self.MICROSTEP_CURVE[self.currentstep - self.MICROSTEPS*2]
# elif (self.currentstep >= self.MICROSTEPS*3) and (self.currentstep < self.MICROSTEPS*4):
# pwm_a = self.MICROSTEP_CURVE[self.currentstep - self.MICROSTEPS*3]
# pwm_b = self.MICROSTEP_CURVE[self.MICROSTEPS*4 - self.currentstep]
#
#
# # go to next 'step' and wrap around
# self.currentstep += self.MICROSTEPS * 4
# self.currentstep %= self.MICROSTEPS * 4
#
# # only really used for microstepping, otherwise always on!
# self.MC._pwm.setPWM(self.PWMA, 0, pwm_a*16)
# self.MC._pwm.setPWM(self.PWMB, 0, pwm_b*16)
#
# # set up coil energizing!
# coils = [0, 0, 0, 0]
#
# if (style == Adafruit_MotorHAT.MICROSTEP):
# if (self.currentstep >= 0) and (self.currentstep < self.MICROSTEPS):
# coils = [1, 1, 0, 0]
# elif (self.currentstep >= self.MICROSTEPS) and (self.currentstep < self.MICROSTEPS*2):
# coils = [0, 1, 1, 0]
# elif (self.currentstep >= self.MICROSTEPS*2) and (self.currentstep < self.MICROSTEPS*3):
# coils = [0, 0, 1, 1]
# elif (self.currentstep >= self.MICROSTEPS*3) and (self.currentstep < self.MICROSTEPS*4):
# coils = [1, 0, 0, 1]
# else:
# step2coils = [ [1, 0, 0, 0],
# [1, 1, 0, 0],
# [0, 1, 0, 0],
# [0, 1, 1, 0],
# [0, 0, 1, 0],
# [0, 0, 1, 1],
# [0, 0, 0, 1],
# [1, 0, 0, 1] ]
# coils = step2coils[self.currentstep/(self.MICROSTEPS/2)]
#
# #print "coils state = " + str(coils)
# self.MC.setPin(self.AIN2, coils[0])
# self.MC.setPin(self.BIN1, coils[1])
# self.MC.setPin(self.AIN1, coils[2])
# self.MC.setPin(self.BIN2, coils[3])
#
# return self.currentstep
#
# def step(self, steps, direction, stepstyle):
# s_per_s = self.sec_per_step
# lateststep = 0
#
# if (stepstyle == Adafruit_MotorHAT.INTERLEAVE):
# s_per_s = s_per_s / 2.0
# if (stepstyle == Adafruit_MotorHAT.MICROSTEP):
# s_per_s /= self.MICROSTEPS
# steps *= self.MICROSTEPS
#
# print s_per_s, " sec per step"
#
# for s in range(steps):
# lateststep = self.oneStep(direction, stepstyle)
# time.sleep(s_per_s)
#
# if (stepstyle == Adafruit_MotorHAT.MICROSTEP):
# # this is an edge case, if we are in between full steps, lets just keep going
# # so we end on a full step
# while (lateststep != 0) and (lateststep != self.MICROSTEPS):
# lateststep = self.oneStep(dir, stepstyle)
# time.sleep(s_per_s)
class dw_DCMotor:
def __init__(self, controller, num):
self.speed = 0
self.MC = controller
self.motornum = num
modepin = in1 = in2 = 0
if (num == 0):
in2 = 8 #phase
in1 = 9 #enable
elif (num == 1):
in2 = 10 #phase
in1 = 11 #enable
elif (num == 2):
in2 = 7 #phase
in1 = 6 #enable
elif (num == 3):
in2 = 5 #phase
in1 = 4 #enable
elif (num == 4):
in2 = 0 #phase
in1 = 1 #enable
elif (num == 5):
in2 = 2 #phase
in1 = 3 #enable
else:
raise NameError('MotorHAT Motor must be between 1 and 6 inclusive')
self.PHpin = in2
self.ENpin = in1
# switch off
self.run(dw_MotorCONTROL.RELEASE, 0)
def setMotorSpeed(self, value):
if(value > 0) and (value <= 255):
self.run(dw_MotorCONTROL.FORWARD, value)
if(value == 0):
self.run(dw_MotorCONTROL.RELEASE, value)
if(value < 0) and (value >= -255):
self.run(dw_MotorCONTROL.BACKWARD, abs(value))
def run(self, command, speed = 0):
if not self.MC:
return
if (command == dw_MotorCONTROL.FORWARD):
self.MC.setPin(self.PHpin, 0)
self.MC._pwm.setPWM(self.ENpin, 0, speed*16)
if (command == dw_MotorCONTROL.BACKWARD):
self.MC.setPin(self.PHpin, 1)
self.MC._pwm.setPWM(self.ENpin, 0, speed*16)
if (command == dw_MotorCONTROL.RELEASE):
self.MC.setPin(self.PHpin, 0)
self.MC.setPin(self.ENpin, 0)
def setSpeed(self, speed):
if (speed < 0):
speed = 0
if (speed > 255):
speed = 255
self.MC._pwm.setPWM(self.ENpin, 0, speed*16)
class dw_MotorCONTROL:
FORWARD = 1
BACKWARD = 2
BRAKE = 3
RELEASE = 4
SINGLE = 1
DOUBLE = 2
INTERLEAVE = 3
MICROSTEP = 4
ININ = 0
PHASE = 1
def __init__(self, addr = 0x60, freq = 1600):
self._i2caddr = addr # default addr on HAT
self._frequency = freq # default @1600Hz PWM freq
# self.steppers = [ Adafruit_StepperMotor(self, 1), Adafruit_StepperMotor(self, 2) ]
self._pwm = PWM(addr, debug=False)
self._pwm.setPWMFreq(self._frequency)
# Just gonna default to high for now
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
GPIO.setup(17, GPIO.OUT)
GPIO.setup(27, GPIO.OUT)
GPIO.setup(22, GPIO.OUT)
GPIO.output(17, GPIO.HIGH)
GPIO.output(27, GPIO.HIGH)
GPIO.output(22, GPIO.HIGH)
self.motors = [ dw_DCMotor(self, m) for m in range(6) ]
def setPin(self, pin, value):
if (pin < 0) or (pin > 15):
raise NameError('PWM pin must be between 0 and 15 inclusive')
if (value != 0) and (value != 1):
raise NameError('Pin value must be 0 or 1!')
if (value == 0):
self._pwm.setPWM(pin, 0, 4096)
if (value == 1):
self._pwm.setPWM(pin, 4096, 0)
def getMotor(self, num):
if (num < 1) or (num > 6):
raise NameError('MotorHAT Motor must be between 1 and 6 inclusive')
return self.motors[num-1]
def allOff(self):
for y in range(5):
self.motors[y].run(dw_MotorCONTROL.RELEASE, 0)

65
examples/640imutest.py Normal file
View File

@@ -0,0 +1,65 @@
"""
MS5611 driver code is placed under the BSD license.
Copyright (c) 2014, Emlid Limited, www.emlid.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Emlid Limited nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL EMLID LIMITED BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
import spidev
import time
import sys
from darkwater_640 import MPU9250
imu = MPU9250()
if imu.testConnection():
print("Connection established: True")
else:
sys.exit("Connection established: False")
imu.initialize()
time.sleep(1)
while True:
# imu.read_all()
# imu.read_gyro()
# imu.read_acc()
# imu.read_temp()
# imu.read_mag()
# print "Accelerometer: ", imu.accelerometer_data
# print "Gyroscope: ", imu.gyroscope_data
# print "Temperature: ", imu.temperature
# print "Magnetometer: ", imu.magnetometer_data
# time.sleep(0.1)
m9a, m9g, m9m = imu.getMotion9()
print("Acc:", "{:+7.3f}".format(m9a[0]), "{:+7.3f}".format(m9a[1]), "{:+7.3f}".format(m9a[2]) )
print("Gyr:", "{:+8.3f}".format(m9g[0]), "{:+8.3f}".format(m9g[1]), "{:+8.3f}".format(m9g[2]) )
print("Mag:", "{:+7.3f}".format(m9m[0]), "{:+7.3f}".format(m9m[1]), "{:+7.3f}".format(m9m[2]) )
time.sleep(0.5)

84
examples/640motortest.py Normal file
View File

@@ -0,0 +1,84 @@
import time
from darkwater_640 import dw_Controller, dw_Motor
dw = dw_Controller( addr=0x60 )
m1 = dw.getMotor(1)
m2 = dw.getMotor(2)
m3 = dw.getMotor(3)
m4 = dw.getMotor(4)
m5 = dw.getMotor(5)
m6 = dw.getMotor(6)
m1.off()
m2.off()
m3.off()
m4.off()
m5.off()
m6.off()
time.sleep(1)
##time.sleep(10)
print("Set forward - ")
print("Motor 1")
m1.setMotorSpeed(255)
time.sleep(1)
print("Motor 2")
m2.setMotorSpeed(255)
time.sleep(1)
print("Motor 3")
m3.setMotorSpeed(255)
time.sleep(1)
print("Motor 4")
m4.setMotorSpeed(255)
time.sleep(1)
print("Motor 5")
m5.setMotorSpeed(255)
time.sleep(1)
print("Motor 6")
m6.setMotorSpeed(255)
time.sleep(1)
print("Stopping - ")
print("Motor 1")
m1.setMotorSpeed(0)
time.sleep(1)
print("Motor 2")
m2.setMotorSpeed(0)
time.sleep(1)
print("Motor 3")
m3.setMotorSpeed(0)
time.sleep(1)
print("Motor 4")
m4.setMotorSpeed(0)
time.sleep(1)
print("Motor 5")
m5.setMotorSpeed(0)
time.sleep(1)
print("Motor 6")
m6.setMotorSpeed(0)
time.sleep(1)
print("Set reverse - ")
print("Motor 1")
m1.setMotorSpeed(-255)
time.sleep(1)
print("Motor 2")
m2.setMotorSpeed(-255)
time.sleep(1)
print("Motor 3")
m3.setMotorSpeed(-255)
time.sleep(1)
print("Motor 4")
m4.setMotorSpeed(-255)
time.sleep(1)
print("Motor 5")
m5.setMotorSpeed(-255)
time.sleep(1)
print("Motor 6")
m6.setMotorSpeed(-255)
time.sleep(1)
print("All off")
m1.off()
m2.off()
m3.off()
m4.off()
m5.off()
m6.off()

35
examples/640servotest.py Normal file
View File

@@ -0,0 +1,35 @@
import time
from darkwater_640 import dw_Controller, dw_Servo
dw = dw_Controller( addr=0x60 )
s1 = dw.getServo(1)
s2 = dw.getServo(2)
s1.off()
s2.off()
time.sleep(1)
print("Set 2000uS - ")
print("Servo 1")
s1.setPWMuS(2000)
time.sleep(1)
print("Servo 2")
s2.setPWMuS(2000)
time.sleep(1)
print("Set 1500uS - ")
print("Servo 1")
s1.setPWMuS(1500)
time.sleep(1)
print("Servo 2")
s2.setPWMuS(1500)
time.sleep(1)
print("Set 1000uS - ")
print("Servo 1")
s1.setPWMuS(1000)
time.sleep(1)
print("Servo 2")
s2.setPWMuS(1000)
time.sleep(1)
print("All off")
s1.off()
s2.off()

View File

@@ -0,0 +1,33 @@
import time
from darkwater_640 import dw_Controller, dw_Stepper
dw = dw_Controller( addr=0x60 )
s1 = dw.getStepper(1)
s2 = dw.getStepper(2)
s3 = dw.getStepper(3)
s1.off()
s2.off()
s3.off()
s1.setMotorSpeed(200)
s2.setMotorSpeed(200)
s3.setMotorSpeed(200)
print("1 Forward")
s1.step( 400, dw_Controller.FORWARD, dw_Controller.DOUBLE )
print("2 Forward")
s2.step( 400, dw_Controller.FORWARD, dw_Controller.DOUBLE )
print("3 Forward")
s3.step( 400, dw_Controller.FORWARD, dw_Controller.DOUBLE )
print("1 Reverse")
s1.step( 400, dw_Controller.REVERSE, dw_Controller.DOUBLE )
print("2 Reverse")
s2.step( 400, dw_Controller.REVERSE, dw_Controller.DOUBLE )
print("3 Reverse")
s3.step( 400, dw_Controller.REVERSE, dw_Controller.DOUBLE )
s1.off()
s2.off()
s3.off()

View File

@@ -1,21 +0,0 @@
import time
from dw640HAT import dw_MotorCONTROL, dw_DCMotor
dw = dw_MotorCONTROL( addr=0x60 )
m = dw.getMotor(1)
m.run(dw_MotorCONTROL.RELEASE)
time.sleep(5)
##time.sleep(10)
print "Set forward"
m.setMotorSpeed(255)
time.sleep(5)
print "stop"
m.setMotorSpeed(0)
time.sleep(5)
print "Set reverse"
m.setMotorSpeed(-255)
time.sleep(5)
print "stop"
m.run(dw_MotorCONTROL.RELEASE)

11
setup.py Normal file
View File

@@ -0,0 +1,11 @@
from distutils.core import setup
setup(name = 'darkwater_640',
version = '1.0.2',
author = 'Team Dark Water',
author_email = 'team@darkwater.io',
description = 'Library for Dark Water 640 board',
license = 'MIT',
url = 'https://github.com/darkwaterfoundation/darkwater_python_640',
packages = ['darkwater_640']
)