Big update
Needs testing
This commit is contained in:
@@ -1,2 +0,0 @@
|
|||||||
# escape
|
|
||||||
Library for the escape board
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
# escape
|
|
||||||
Library for the escape board
|
|
||||||
@@ -1,144 +0,0 @@
|
|||||||
#!/usr/bin/python
|
|
||||||
|
|
||||||
import RPi.GPIO as GPIO
|
|
||||||
from Adafruit_PWM_Servo_Driver import PWM
|
|
||||||
import time
|
|
||||||
import math
|
|
||||||
|
|
||||||
class dw_PWM:
|
|
||||||
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 = 9
|
|
||||||
elif (num == 1):
|
|
||||||
self.pin = 8
|
|
||||||
elif (num == 2):
|
|
||||||
self.pin = 10
|
|
||||||
elif (num == 3):
|
|
||||||
self.pin = 11
|
|
||||||
elif (num == 4):
|
|
||||||
self.pin = 12
|
|
||||||
elif (num == 5):
|
|
||||||
self.pin = 13
|
|
||||||
elif (num == 6):
|
|
||||||
self.pin = 0
|
|
||||||
elif (num == 7):
|
|
||||||
self.pin = 1
|
|
||||||
elif (num == 8):
|
|
||||||
self.pin = 2
|
|
||||||
elif (num == 9):
|
|
||||||
self.pin = 3
|
|
||||||
elif (num == 10):
|
|
||||||
self.pin = 5
|
|
||||||
elif (num == 11):
|
|
||||||
self.pin = 4
|
|
||||||
else:
|
|
||||||
raise NameError('Port must be between 1 and 12 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.setPWM(self.pin, 0, int(value) )
|
|
||||||
if(value == 0):
|
|
||||||
self.off()
|
|
||||||
|
|
||||||
def setPWMmS(self, length_ms):
|
|
||||||
self.setPWM( round( length_ms * 4096 ) / ( 1000 / self.freq ) )
|
|
||||||
|
|
||||||
def setPWMuS(self, length_us):
|
|
||||||
self.setPWM( round( length_us * 4096 ) / ( 1000000 / self.freq ) )
|
|
||||||
|
|
||||||
def run(self, command, speed = 0):
|
|
||||||
if not self.MC:
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
class dw_PWMCONTROL:
|
|
||||||
|
|
||||||
def __init__(self, addr = 0x61, freq = 100, correctionFactor = 1.0):
|
|
||||||
self._i2caddr = addr # default addr on HAT
|
|
||||||
self._frequency = freq # default @60Hz PWM freq
|
|
||||||
# self.steppers = [ Adafruit_StepperMotor(self, 1), Adafruit_StepperMotor(self, 2) ]
|
|
||||||
self._pwm = PWM(addr, debug=False)
|
|
||||||
self._pwm.setPWMFreq(self._frequency, correctionFactor)
|
|
||||||
# Just gonna default to high for now
|
|
||||||
|
|
||||||
self.servo = [ dw_PWM(self, m, freq) for m in range(6) ]
|
|
||||||
self.esc = [ dw_PWM(self, m, freq) for m in range(6, 12) ]
|
|
||||||
|
|
||||||
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 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.setAllPWM(0, 4096)
|
|
||||||
if (value == 1):
|
|
||||||
self._pwm.setAllPWM(4096, 0)
|
|
||||||
|
|
||||||
def getESC(self, num):
|
|
||||||
if (num < 1) or (num > 6):
|
|
||||||
raise NameError('ESC must be between 1 and 6 inclusive')
|
|
||||||
return self.esc[num-1]
|
|
||||||
|
|
||||||
def getSERVO(self, num):
|
|
||||||
if (num < 1) or (num > 6):
|
|
||||||
raise NameError('Servo must be between 1 and 6 inclusive')
|
|
||||||
return self.servo[num-1]
|
|
||||||
|
|
||||||
def setAllPWM(self, value):
|
|
||||||
if(value > 0):
|
|
||||||
self._pwm.setAllPWM(0, value)
|
|
||||||
if(value == 0):
|
|
||||||
self.allOff()
|
|
||||||
|
|
||||||
def setAllPWMmS(self, value):
|
|
||||||
if(value > 0):
|
|
||||||
self._pwm.setAllPWM(0, value)
|
|
||||||
if(value == 0):
|
|
||||||
self.allOff()
|
|
||||||
|
|
||||||
def setAllPWMuS(self, value):
|
|
||||||
if(value > 0):
|
|
||||||
self._pwm.setAllPWM(0, value)
|
|
||||||
if(value == 0):
|
|
||||||
self.allOff()
|
|
||||||
|
|
||||||
def allOff(self):
|
|
||||||
this.setAllPin( 0 );
|
|
||||||
11
README.md
11
README.md
@@ -1,9 +1,2 @@
|
|||||||
# ESCAPE ESC powered motor driver
|
# escape
|
||||||
Files for the escape board
|
Library for the escape board
|
||||||
|
|
||||||
#License
|
|
||||||
|
|
||||||
Designed by Dark Water. Distributed under a Creative Commons Attribution, Share-Alike license.
|
|
||||||
Development kits based on this product should be distributed under a similar license.
|
|
||||||
Commercial products using the Escape boards as a reference design need not comply with this license; further questions can be sent to team@darkwater.io.
|
|
||||||
Check license.txt for more information.
|
|
||||||
|
|||||||
426
darkwater_escape/GPIO.py
Normal file
426
darkwater_escape/GPIO.py
Normal 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_escape/I2C.py
Normal file
200
darkwater_escape/I2C.py
Normal 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 Adafruit_PureIO.smbus
|
||||||
|
self._bus = Adafruit_PureIO.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_escape/PCA9685.py
Normal file
167
darkwater_escape/PCA9685.py
Normal 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):
|
||||||
|
"""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 = int(math.floor(prescaleval + 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, 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_escape/Platform.py
Normal file
106
darkwater_escape/Platform.py
Normal 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_escape/SPI.py
Normal file
336
darkwater_escape/SPI.py
Normal 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
|
||||||
249
darkwater_escape/darkwater_escape.py
Normal file
249
darkwater_escape/darkwater_escape.py
Normal file
@@ -0,0 +1,249 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
|
||||||
|
import RPi.GPIO as GPIO
|
||||||
|
from PCA9685 import PCA9685
|
||||||
|
import time
|
||||||
|
import math
|
||||||
|
|
||||||
|
class dw_Motor:
|
||||||
|
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 = 9
|
||||||
|
elif (num == 1):
|
||||||
|
self.pin = 8
|
||||||
|
elif (num == 2):
|
||||||
|
self.pin = 10
|
||||||
|
elif (num == 3):
|
||||||
|
self.pin = 11
|
||||||
|
elif (num == 4):
|
||||||
|
self.pin = 12
|
||||||
|
elif (num == 5):
|
||||||
|
self.pin = 13
|
||||||
|
elif (num == 6):
|
||||||
|
self.pin = 0
|
||||||
|
elif (num == 7):
|
||||||
|
self.pin = 1
|
||||||
|
elif (num == 8):
|
||||||
|
self.pin = 2
|
||||||
|
elif (num == 9):
|
||||||
|
self.pin = 3
|
||||||
|
elif (num == 10):
|
||||||
|
self.pin = 5
|
||||||
|
elif (num == 11):
|
||||||
|
self.pin = 4
|
||||||
|
else:
|
||||||
|
raise NameError('Motors must be between 1 and 12 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.setPWM(self.pin, 0, int(value) )
|
||||||
|
if(value == 0):
|
||||||
|
self.off()
|
||||||
|
|
||||||
|
def setPWMmS(self, length_ms):
|
||||||
|
self.setPWM( round( length_ms * 4096 ) / ( 1000 / self.freq ) )
|
||||||
|
|
||||||
|
def setPWMuS(self, length_us):
|
||||||
|
self.setPWM( round( length_us * 4096 ) / ( 1000000 / self.freq ) )
|
||||||
|
|
||||||
|
def setMotorSpeed(self, value):
|
||||||
|
# Check for PWM values
|
||||||
|
if(value > 1000) and (value < 2000):
|
||||||
|
self.setPWMmS(value)
|
||||||
|
# Translate for motor values
|
||||||
|
if(value > 0) and (value <= 255):
|
||||||
|
self.setPWMmS( round(translate(value, 0, 255, 1500, 2000)))
|
||||||
|
if(value == 0):
|
||||||
|
self.setPWMmS(1500)
|
||||||
|
if(value < 0) and (value >= -255):
|
||||||
|
self.setPWMmS(round(translate(abs(value), 0, 255, 1500, 1000)))
|
||||||
|
|
||||||
|
def run(self, command, speed = 0):
|
||||||
|
if not self.MC:
|
||||||
|
return
|
||||||
|
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.BACKWARD):
|
||||||
|
self.MC.setPin(self.PHpin, 1)
|
||||||
|
self.MC._pwm.set_pwm(self.ENpin, 0, speed*16)
|
||||||
|
if (command == dw_Controller.RELEASE):
|
||||||
|
self.MC.setPin(self.PHpin, 0)
|
||||||
|
self.MC.setPin(self.ENpin, 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 = 9
|
||||||
|
elif (num == 1):
|
||||||
|
self.pin = 8
|
||||||
|
elif (num == 2):
|
||||||
|
self.pin = 10
|
||||||
|
elif (num == 3):
|
||||||
|
self.pin = 11
|
||||||
|
elif (num == 4):
|
||||||
|
self.pin = 12
|
||||||
|
elif (num == 5):
|
||||||
|
self.pin = 13
|
||||||
|
elif (num == 6):
|
||||||
|
self.pin = 0
|
||||||
|
elif (num == 7):
|
||||||
|
self.pin = 1
|
||||||
|
elif (num == 8):
|
||||||
|
self.pin = 2
|
||||||
|
elif (num == 9):
|
||||||
|
self.pin = 3
|
||||||
|
elif (num == 10):
|
||||||
|
self.pin = 5
|
||||||
|
elif (num == 11):
|
||||||
|
self.pin = 4
|
||||||
|
else:
|
||||||
|
raise NameError('Port must be between 1 and 12 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.setPWM(self.pin, 0, int(value) )
|
||||||
|
if(value == 0):
|
||||||
|
self.off()
|
||||||
|
|
||||||
|
def setPWMmS(self, length_ms):
|
||||||
|
self.setPWM( round( length_ms * 4096 ) / ( 1000 / self.freq ) )
|
||||||
|
|
||||||
|
def setPWMuS(self, length_us):
|
||||||
|
self.setPWM( round( length_us * 4096 ) / ( 1000000 / self.freq ) )
|
||||||
|
|
||||||
|
def run(self, command, speed = 0):
|
||||||
|
if not self.MC:
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
class dw_Controller:
|
||||||
|
|
||||||
|
def __init__(self, addr = 0x61, freq = 100, correctionFactor = 1.0):
|
||||||
|
self._i2caddr = addr # default addr on HAT
|
||||||
|
self._frequency = freq # default @60Hz 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
|
||||||
|
|
||||||
|
self.servo = [ dw_PWM(self, m, freq) for m in range(6) ]
|
||||||
|
self.esc = [ dw_PWM(self, m, freq) for m in range(6, 12) ]
|
||||||
|
|
||||||
|
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.esc[num-1]
|
||||||
|
|
||||||
|
def getServo(self, num):
|
||||||
|
if (num < 1) or (num > 6):
|
||||||
|
raise NameError('Servos must be between 1 and 6 inclusive')
|
||||||
|
return self.servo[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)
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -1 +0,0 @@
|
|||||||
Placeholder for the Escape board hardware files
|
|
||||||
425
license.txt
425
license.txt
@@ -1,425 +0,0 @@
|
|||||||
Attribution-ShareAlike 4.0 International
|
|
||||||
|
|
||||||
=======================================================================
|
|
||||||
|
|
||||||
Creative Commons Corporation ("Creative Commons") is not a law firm and
|
|
||||||
does not provide legal services or legal advice. Distribution of
|
|
||||||
Creative Commons public licenses does not create a lawyer-client or
|
|
||||||
other relationship. Creative Commons makes its licenses and related
|
|
||||||
information available on an "as-is" basis. Creative Commons gives no
|
|
||||||
warranties regarding its licenses, any material licensed under their
|
|
||||||
terms and conditions, or any related information. Creative Commons
|
|
||||||
disclaims all liability for damages resulting from their use to the
|
|
||||||
fullest extent possible.
|
|
||||||
|
|
||||||
Using Creative Commons Public Licenses
|
|
||||||
|
|
||||||
Creative Commons public licenses provide a standard set of terms and
|
|
||||||
conditions that creators and other rights holders may use to share
|
|
||||||
original works of authorship and other material subject to copyright
|
|
||||||
and certain other rights specified in the public license below. The
|
|
||||||
following considerations are for informational purposes only, are not
|
|
||||||
exhaustive, and do not form part of our licenses.
|
|
||||||
|
|
||||||
Considerations for licensors: Our public licenses are
|
|
||||||
intended for use by those authorized to give the public
|
|
||||||
permission to use material in ways otherwise restricted by
|
|
||||||
copyright and certain other rights. Our licenses are
|
|
||||||
irrevocable. Licensors should read and understand the terms
|
|
||||||
and conditions of the license they choose before applying it.
|
|
||||||
Licensors should also secure all rights necessary before
|
|
||||||
applying our licenses so that the public can reuse the
|
|
||||||
material as expected. Licensors should clearly mark any
|
|
||||||
material not subject to the license. This includes other CC-
|
|
||||||
licensed material, or material used under an exception or
|
|
||||||
limitation to copyright. More considerations for licensors:
|
|
||||||
wiki.creativecommons.org/Considerations_for_licensors
|
|
||||||
|
|
||||||
Considerations for the public: By using one of our public
|
|
||||||
licenses, a licensor grants the public permission to use the
|
|
||||||
licensed material under specified terms and conditions. If
|
|
||||||
the licensor's permission is not necessary for any reason--for
|
|
||||||
example, because of any applicable exception or limitation to
|
|
||||||
copyright--then that use is not regulated by the license. Our
|
|
||||||
licenses grant only permissions under copyright and certain
|
|
||||||
other rights that a licensor has authority to grant. Use of
|
|
||||||
the licensed material may still be restricted for other
|
|
||||||
reasons, including because others have copyright or other
|
|
||||||
rights in the material. A licensor may make special requests,
|
|
||||||
such as asking that all changes be marked or described.
|
|
||||||
Although not required by our licenses, you are encouraged to
|
|
||||||
respect those requests where reasonable. More_considerations
|
|
||||||
for the public:
|
|
||||||
wiki.creativecommons.org/Considerations_for_licensees
|
|
||||||
|
|
||||||
=======================================================================
|
|
||||||
|
|
||||||
Creative Commons Attribution-ShareAlike 4.0 International Public
|
|
||||||
License
|
|
||||||
|
|
||||||
By exercising the Licensed Rights (defined below), You accept and agree
|
|
||||||
to be bound by the terms and conditions of this Creative Commons
|
|
||||||
Attribution-ShareAlike 4.0 International Public License ("Public
|
|
||||||
License"). To the extent this Public License may be interpreted as a
|
|
||||||
contract, You are granted the Licensed Rights in consideration of Your
|
|
||||||
acceptance of these terms and conditions, and the Licensor grants You
|
|
||||||
such rights in consideration of benefits the Licensor receives from
|
|
||||||
making the Licensed Material available under these terms and
|
|
||||||
conditions.
|
|
||||||
|
|
||||||
|
|
||||||
Section 1 -- Definitions.
|
|
||||||
|
|
||||||
a. Adapted Material means material subject to Copyright and Similar
|
|
||||||
Rights that is derived from or based upon the Licensed Material
|
|
||||||
and in which the Licensed Material is translated, altered,
|
|
||||||
arranged, transformed, or otherwise modified in a manner requiring
|
|
||||||
permission under the Copyright and Similar Rights held by the
|
|
||||||
Licensor. For purposes of this Public License, where the Licensed
|
|
||||||
Material is a musical work, performance, or sound recording,
|
|
||||||
Adapted Material is always produced where the Licensed Material is
|
|
||||||
synched in timed relation with a moving image.
|
|
||||||
|
|
||||||
b. Adapter's License means the license You apply to Your Copyright
|
|
||||||
and Similar Rights in Your contributions to Adapted Material in
|
|
||||||
accordance with the terms and conditions of this Public License.
|
|
||||||
|
|
||||||
c. BY-SA Compatible License means a license listed at
|
|
||||||
creativecommons.org/compatiblelicenses, approved by Creative
|
|
||||||
Commons as essentially the equivalent of this Public License.
|
|
||||||
|
|
||||||
d. Copyright and Similar Rights means copyright and/or similar rights
|
|
||||||
closely related to copyright including, without limitation,
|
|
||||||
performance, broadcast, sound recording, and Sui Generis Database
|
|
||||||
Rights, without regard to how the rights are labeled or
|
|
||||||
categorized. For purposes of this Public License, the rights
|
|
||||||
specified in Section 2(b)(1)-(2) are not Copyright and Similar
|
|
||||||
Rights.
|
|
||||||
|
|
||||||
e. Effective Technological Measures means those measures that, in the
|
|
||||||
absence of proper authority, may not be circumvented under laws
|
|
||||||
fulfilling obligations under Article 11 of the WIPO Copyright
|
|
||||||
Treaty adopted on December 20, 1996, and/or similar international
|
|
||||||
agreements.
|
|
||||||
|
|
||||||
f. Exceptions and Limitations means fair use, fair dealing, and/or
|
|
||||||
any other exception or limitation to Copyright and Similar Rights
|
|
||||||
that applies to Your use of the Licensed Material.
|
|
||||||
|
|
||||||
g. License Elements means the license attributes listed in the name
|
|
||||||
of a Creative Commons Public License. The License Elements of this
|
|
||||||
Public License are Attribution and ShareAlike.
|
|
||||||
|
|
||||||
h. Licensed Material means the artistic or literary work, database,
|
|
||||||
or other material to which the Licensor applied this Public
|
|
||||||
License.
|
|
||||||
|
|
||||||
i. Licensed Rights means the rights granted to You subject to the
|
|
||||||
terms and conditions of this Public License, which are limited to
|
|
||||||
all Copyright and Similar Rights that apply to Your use of the
|
|
||||||
Licensed Material and that the Licensor has authority to license.
|
|
||||||
|
|
||||||
j. Licensor means the individual(s) or entity(ies) granting rights
|
|
||||||
under this Public License.
|
|
||||||
|
|
||||||
k. Share means to provide material to the public by any means or
|
|
||||||
process that requires permission under the Licensed Rights, such
|
|
||||||
as reproduction, public display, public performance, distribution,
|
|
||||||
dissemination, communication, or importation, and to make material
|
|
||||||
available to the public including in ways that members of the
|
|
||||||
public may access the material from a place and at a time
|
|
||||||
individually chosen by them.
|
|
||||||
|
|
||||||
l. Sui Generis Database Rights means rights other than copyright
|
|
||||||
resulting from Directive 96/9/EC of the European Parliament and of
|
|
||||||
the Council of 11 March 1996 on the legal protection of databases,
|
|
||||||
as amended and/or succeeded, as well as other essentially
|
|
||||||
equivalent rights anywhere in the world.
|
|
||||||
|
|
||||||
m. You means the individual or entity exercising the Licensed Rights
|
|
||||||
under this Public License. Your has a corresponding meaning.
|
|
||||||
|
|
||||||
|
|
||||||
Section 2 -- Scope.
|
|
||||||
|
|
||||||
a. License grant.
|
|
||||||
|
|
||||||
1. Subject to the terms and conditions of this Public License,
|
|
||||||
the Licensor hereby grants You a worldwide, royalty-free,
|
|
||||||
non-sublicensable, non-exclusive, irrevocable license to
|
|
||||||
exercise the Licensed Rights in the Licensed Material to:
|
|
||||||
|
|
||||||
a. reproduce and Share the Licensed Material, in whole or
|
|
||||||
in part; and
|
|
||||||
|
|
||||||
b. produce, reproduce, and Share Adapted Material.
|
|
||||||
|
|
||||||
2. Exceptions and Limitations. For the avoidance of doubt, where
|
|
||||||
Exceptions and Limitations apply to Your use, this Public
|
|
||||||
License does not apply, and You do not need to comply with
|
|
||||||
its terms and conditions.
|
|
||||||
|
|
||||||
3. Term. The term of this Public License is specified in Section
|
|
||||||
6(a).
|
|
||||||
|
|
||||||
4. Media and formats; technical modifications allowed. The
|
|
||||||
Licensor authorizes You to exercise the Licensed Rights in
|
|
||||||
all media and formats whether now known or hereafter created,
|
|
||||||
and to make technical modifications necessary to do so. The
|
|
||||||
Licensor waives and/or agrees not to assert any right or
|
|
||||||
authority to forbid You from making technical modifications
|
|
||||||
necessary to exercise the Licensed Rights, including
|
|
||||||
technical modifications necessary to circumvent Effective
|
|
||||||
Technological Measures. For purposes of this Public License,
|
|
||||||
simply making modifications authorized by this Section 2(a)
|
|
||||||
(4) never produces Adapted Material.
|
|
||||||
|
|
||||||
5. Downstream recipients.
|
|
||||||
|
|
||||||
a. Offer from the Licensor -- Licensed Material. Every
|
|
||||||
recipient of the Licensed Material automatically
|
|
||||||
receives an offer from the Licensor to exercise the
|
|
||||||
Licensed Rights under the terms and conditions of this
|
|
||||||
Public License.
|
|
||||||
|
|
||||||
b. Additional offer from the Licensor -- Adapted Material.
|
|
||||||
Every recipient of Adapted Material from You
|
|
||||||
automatically receives an offer from the Licensor to
|
|
||||||
exercise the Licensed Rights in the Adapted Material
|
|
||||||
under the conditions of the Adapter's License You apply.
|
|
||||||
|
|
||||||
c. No downstream restrictions. You may not offer or impose
|
|
||||||
any additional or different terms or conditions on, or
|
|
||||||
apply any Effective Technological Measures to, the
|
|
||||||
Licensed Material if doing so restricts exercise of the
|
|
||||||
Licensed Rights by any recipient of the Licensed
|
|
||||||
Material.
|
|
||||||
|
|
||||||
6. No endorsement. Nothing in this Public License constitutes or
|
|
||||||
may be construed as permission to assert or imply that You
|
|
||||||
are, or that Your use of the Licensed Material is, connected
|
|
||||||
with, or sponsored, endorsed, or granted official status by,
|
|
||||||
the Licensor or others designated to receive attribution as
|
|
||||||
provided in Section 3(a)(1)(A)(i).
|
|
||||||
|
|
||||||
b. Other rights.
|
|
||||||
|
|
||||||
1. Moral rights, such as the right of integrity, are not
|
|
||||||
licensed under this Public License, nor are publicity,
|
|
||||||
privacy, and/or other similar personality rights; however, to
|
|
||||||
the extent possible, the Licensor waives and/or agrees not to
|
|
||||||
assert any such rights held by the Licensor to the limited
|
|
||||||
extent necessary to allow You to exercise the Licensed
|
|
||||||
Rights, but not otherwise.
|
|
||||||
|
|
||||||
2. Patent and trademark rights are not licensed under this
|
|
||||||
Public License.
|
|
||||||
|
|
||||||
3. To the extent possible, the Licensor waives any right to
|
|
||||||
collect royalties from You for the exercise of the Licensed
|
|
||||||
Rights, whether directly or through a collecting society
|
|
||||||
under any voluntary or waivable statutory or compulsory
|
|
||||||
licensing scheme. In all other cases the Licensor expressly
|
|
||||||
reserves any right to collect such royalties.
|
|
||||||
|
|
||||||
|
|
||||||
Section 3 -- License Conditions.
|
|
||||||
|
|
||||||
Your exercise of the Licensed Rights is expressly made subject to the
|
|
||||||
following conditions.
|
|
||||||
|
|
||||||
a. Attribution.
|
|
||||||
|
|
||||||
1. If You Share the Licensed Material (including in modified
|
|
||||||
form), You must:
|
|
||||||
|
|
||||||
a. retain the following if it is supplied by the Licensor
|
|
||||||
with the Licensed Material:
|
|
||||||
|
|
||||||
i. identification of the creator(s) of the Licensed
|
|
||||||
Material and any others designated to receive
|
|
||||||
attribution, in any reasonable manner requested by
|
|
||||||
the Licensor (including by pseudonym if
|
|
||||||
designated);
|
|
||||||
|
|
||||||
ii. a copyright notice;
|
|
||||||
|
|
||||||
iii. a notice that refers to this Public License;
|
|
||||||
|
|
||||||
iv. a notice that refers to the disclaimer of
|
|
||||||
warranties;
|
|
||||||
|
|
||||||
v. a URI or hyperlink to the Licensed Material to the
|
|
||||||
extent reasonably practicable;
|
|
||||||
|
|
||||||
b. indicate if You modified the Licensed Material and
|
|
||||||
retain an indication of any previous modifications; and
|
|
||||||
|
|
||||||
c. indicate the Licensed Material is licensed under this
|
|
||||||
Public License, and include the text of, or the URI or
|
|
||||||
hyperlink to, this Public License.
|
|
||||||
|
|
||||||
2. You may satisfy the conditions in Section 3(a)(1) in any
|
|
||||||
reasonable manner based on the medium, means, and context in
|
|
||||||
which You Share the Licensed Material. For example, it may be
|
|
||||||
reasonable to satisfy the conditions by providing a URI or
|
|
||||||
hyperlink to a resource that includes the required
|
|
||||||
information.
|
|
||||||
|
|
||||||
3. If requested by the Licensor, You must remove any of the
|
|
||||||
information required by Section 3(a)(1)(A) to the extent
|
|
||||||
reasonably practicable.
|
|
||||||
|
|
||||||
b. ShareAlike.
|
|
||||||
|
|
||||||
In addition to the conditions in Section 3(a), if You Share
|
|
||||||
Adapted Material You produce, the following conditions also apply.
|
|
||||||
|
|
||||||
1. The Adapter's License You apply must be a Creative Commons
|
|
||||||
license with the same License Elements, this version or
|
|
||||||
later, or a BY-SA Compatible License.
|
|
||||||
|
|
||||||
2. You must include the text of, or the URI or hyperlink to, the
|
|
||||||
Adapter's License You apply. You may satisfy this condition
|
|
||||||
in any reasonable manner based on the medium, means, and
|
|
||||||
context in which You Share Adapted Material.
|
|
||||||
|
|
||||||
3. You may not offer or impose any additional or different terms
|
|
||||||
or conditions on, or apply any Effective Technological
|
|
||||||
Measures to, Adapted Material that restrict exercise of the
|
|
||||||
rights granted under the Adapter's License You apply.
|
|
||||||
|
|
||||||
|
|
||||||
Section 4 -- Sui Generis Database Rights.
|
|
||||||
|
|
||||||
Where the Licensed Rights include Sui Generis Database Rights that
|
|
||||||
apply to Your use of the Licensed Material:
|
|
||||||
|
|
||||||
a. for the avoidance of doubt, Section 2(a)(1) grants You the right
|
|
||||||
to extract, reuse, reproduce, and Share all or a substantial
|
|
||||||
portion of the contents of the database;
|
|
||||||
|
|
||||||
b. if You include all or a substantial portion of the database
|
|
||||||
contents in a database in which You have Sui Generis Database
|
|
||||||
Rights, then the database in which You have Sui Generis Database
|
|
||||||
Rights (but not its individual contents) is Adapted Material,
|
|
||||||
|
|
||||||
including for purposes of Section 3(b); and
|
|
||||||
c. You must comply with the conditions in Section 3(a) if You Share
|
|
||||||
all or a substantial portion of the contents of the database.
|
|
||||||
|
|
||||||
For the avoidance of doubt, this Section 4 supplements and does not
|
|
||||||
replace Your obligations under this Public License where the Licensed
|
|
||||||
Rights include other Copyright and Similar Rights.
|
|
||||||
|
|
||||||
|
|
||||||
Section 5 -- Disclaimer of Warranties and Limitation of Liability.
|
|
||||||
|
|
||||||
a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE
|
|
||||||
EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS
|
|
||||||
AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF
|
|
||||||
ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS,
|
|
||||||
IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION,
|
|
||||||
WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
|
||||||
PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS,
|
|
||||||
ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT
|
|
||||||
KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT
|
|
||||||
ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU.
|
|
||||||
|
|
||||||
b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE
|
|
||||||
TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
|
|
||||||
NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT,
|
|
||||||
INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES,
|
|
||||||
COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR
|
|
||||||
USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN
|
|
||||||
ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR
|
|
||||||
DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR
|
|
||||||
IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.
|
|
||||||
|
|
||||||
c. The disclaimer of warranties and limitation of liability provided
|
|
||||||
above shall be interpreted in a manner that, to the extent
|
|
||||||
possible, most closely approximates an absolute disclaimer and
|
|
||||||
waiver of all liability.
|
|
||||||
|
|
||||||
|
|
||||||
Section 6 -- Term and Termination.
|
|
||||||
|
|
||||||
a. This Public License applies for the term of the Copyright and
|
|
||||||
Similar Rights licensed here. However, if You fail to comply with
|
|
||||||
this Public License, then Your rights under this Public License
|
|
||||||
terminate automatically.
|
|
||||||
|
|
||||||
b. Where Your right to use the Licensed Material has terminated under
|
|
||||||
Section 6(a), it reinstates:
|
|
||||||
|
|
||||||
1. automatically as of the date the violation is cured, provided
|
|
||||||
it is cured within 30 days of Your discovery of the
|
|
||||||
violation; or
|
|
||||||
|
|
||||||
2. upon express reinstatement by the Licensor.
|
|
||||||
|
|
||||||
For the avoidance of doubt, this Section 6(b) does not affect any
|
|
||||||
right the Licensor may have to seek remedies for Your violations
|
|
||||||
of this Public License.
|
|
||||||
|
|
||||||
c. For the avoidance of doubt, the Licensor may also offer the
|
|
||||||
Licensed Material under separate terms or conditions or stop
|
|
||||||
distributing the Licensed Material at any time; however, doing so
|
|
||||||
will not terminate this Public License.
|
|
||||||
|
|
||||||
d. Sections 1, 5, 6, 7, and 8 survive termination of this Public
|
|
||||||
License.
|
|
||||||
|
|
||||||
|
|
||||||
Section 7 -- Other Terms and Conditions.
|
|
||||||
|
|
||||||
a. The Licensor shall not be bound by any additional or different
|
|
||||||
terms or conditions communicated by You unless expressly agreed.
|
|
||||||
|
|
||||||
b. Any arrangements, understandings, or agreements regarding the
|
|
||||||
Licensed Material not stated herein are separate from and
|
|
||||||
independent of the terms and conditions of this Public License.
|
|
||||||
|
|
||||||
|
|
||||||
Section 8 -- Interpretation.
|
|
||||||
|
|
||||||
a. For the avoidance of doubt, this Public License does not, and
|
|
||||||
shall not be interpreted to, reduce, limit, restrict, or impose
|
|
||||||
conditions on any use of the Licensed Material that could lawfully
|
|
||||||
be made without permission under this Public License.
|
|
||||||
|
|
||||||
b. To the extent possible, if any provision of this Public License is
|
|
||||||
deemed unenforceable, it shall be automatically reformed to the
|
|
||||||
minimum extent necessary to make it enforceable. If the provision
|
|
||||||
cannot be reformed, it shall be severed from this Public License
|
|
||||||
without affecting the enforceability of the remaining terms and
|
|
||||||
conditions.
|
|
||||||
|
|
||||||
c. No term or condition of this Public License will be waived and no
|
|
||||||
failure to comply consented to unless expressly agreed to by the
|
|
||||||
Licensor.
|
|
||||||
|
|
||||||
d. Nothing in this Public License constitutes or may be interpreted
|
|
||||||
as a limitation upon, or waiver of, any privileges and immunities
|
|
||||||
that apply to the Licensor or You, including from the legal
|
|
||||||
processes of any jurisdiction or authority.
|
|
||||||
|
|
||||||
|
|
||||||
=======================================================================
|
|
||||||
|
|
||||||
Creative Commons is not a party to its public licenses.
|
|
||||||
Notwithstanding, Creative Commons may elect to apply one of its public
|
|
||||||
licenses to material it publishes and in those instances will be
|
|
||||||
considered the "Licensor." Except for the limited purpose of indicating
|
|
||||||
that material is shared under a Creative Commons public license or as
|
|
||||||
otherwise permitted by the Creative Commons policies published at
|
|
||||||
creativecommons.org/policies, Creative Commons does not authorize the
|
|
||||||
use of the trademark "Creative Commons" or any other trademark or logo
|
|
||||||
of Creative Commons without its prior written consent including,
|
|
||||||
without limitation, in connection with any unauthorized modifications
|
|
||||||
to any of its public licenses or any other arrangements,
|
|
||||||
understandings, or agreements concerning use of licensed material. For
|
|
||||||
the avoidance of doubt, this paragraph does not form part of the public
|
|
||||||
licenses.
|
|
||||||
|
|
||||||
Creative Commons may be contacted at creativecommons.org.
|
|
||||||
Reference in New Issue
Block a user