├── .github ├── ISSUE_TEMPLATE.md └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── Adafruit_GPIO ├── FT232H.py ├── GPIO.py ├── I2C.py ├── MCP230xx.py ├── PCA95xx.py ├── PCF8574.py ├── PWM.py ├── Platform.py ├── SPI.py └── __init__.py ├── LICENSE ├── README.md ├── ez_setup.py ├── setup.py └── tests ├── MockGPIO.py ├── __init__.py ├── test_GPIO.py ├── test_I2C.py ├── test_PWM.py ├── test_Platform.py └── test_SPI.py /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Thank you for opening an issue on an Adafruit Python library repository. To 2 | improve the speed of resolution please review the following guidelines and 3 | common troubleshooting steps below before creating the issue: 4 | 5 | - **Do not use GitHub issues for troubleshooting projects and issues.** Instead use 6 | the forums at http://forums.adafruit.com to ask questions and troubleshoot why 7 | something isn't working as expected. In many cases the problem is a common issue 8 | that you will more quickly receive help from the forum community. GitHub issues 9 | are meant for known defects in the code. If you don't know if there is a defect 10 | in the code then start with troubleshooting on the forum first. 11 | 12 | - **If following a tutorial or guide be sure you didn't miss a step.** Carefully 13 | check all of the steps and commands to run have been followed. Consult the 14 | forum if you're unsure or have questions about steps in a guide/tutorial. 15 | 16 | - **For Python/Raspberry Pi projects check these very common issues to ensure they don't apply**: 17 | 18 | - If you are receiving an **ImportError: No module named...** error then a 19 | library the code depends on is not installed. Check the tutorial/guide or 20 | README to ensure you have installed the necessary libraries. Usually the 21 | missing library can be installed with the `pip` tool, but check the tutorial/guide 22 | for the exact command. 23 | 24 | - **Be sure you are supplying adequate power to the board.** Check the specs of 25 | your board and power in an external power supply. In many cases just 26 | plugging a board into your computer is not enough to power it and other 27 | peripherals. 28 | 29 | - **Double check all soldering joints and connections.** Flakey connections 30 | cause many mysterious problems. See the [guide to excellent soldering](https://learn.adafruit.com/adafruit-guide-excellent-soldering/tools) for examples of good solder joints. 31 | 32 | If you're sure this issue is a defect in the code and checked the steps above 33 | please fill in the following fields to provide enough troubleshooting information. 34 | You may delete the guideline and text above to just leave the following details: 35 | 36 | - Platform/operating system (i.e. Raspberry Pi with Raspbian operating system, 37 | Windows 32-bit, Windows 64-bit, Mac OSX 64-bit, etc.): **INSERT PLATFORM/OPERATING 38 | SYSTEM HERE** 39 | 40 | - Python version (run `python -version` or `python3 -version`): **INSERT PYTHON 41 | VERSION HERE** 42 | 43 | - Error message you are receiving, including any Python exception traces: **INSERT 44 | ERROR MESAGE/EXCEPTION TRACES HERE*** 45 | 46 | - List the steps to reproduce the problem below (if possible attach code or commands 47 | to run): **LIST REPRO STEPS BELOW** 48 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Thank you for creating a pull request to contribute to Adafruit's GitHub code! 2 | Before you open the request please review the following guidelines and tips to 3 | help it be more easily integrated: 4 | 5 | - **Describe the scope of your change--i.e. what the change does and what parts 6 | of the code were modified.** This will help us understand any risks of integrating 7 | the code. 8 | 9 | - **Describe any known limitations with your change.** For example if the change 10 | doesn't apply to a supported platform of the library please mention it. 11 | 12 | - **Please run any tests or examples that can exercise your modified code.** We 13 | strive to not break users of the code and running tests/examples helps with this 14 | process. 15 | 16 | Thank you again for contributing! We will try to test and integrate the change 17 | as soon as we can, but be aware we have many GitHub repositories to manage and 18 | can't immediately respond to every request. There is no need to bump or check in 19 | on a pull request (it will clutter the discussion of the request). 20 | 21 | Also don't be worried if the request is closed or not integrated--sometimes the 22 | priorities of Adafruit's GitHub code (education, ease of use) might not match the 23 | priorities of the pull request. Don't fret, the open source community thrives on 24 | forks and GitHub makes it easy to keep your changes in a forked repo. 25 | 26 | After reviewing the guidelines above you can delete this text from the pull request. 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | dist/ 3 | *.egg-info 4 | *.pyc 5 | setuptools* 6 | 7 | .idea -------------------------------------------------------------------------------- /Adafruit_GPIO/FT232H.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2014 Adafruit Industries 2 | # Author: Tony DiCola 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy 5 | # of this software and associated documentation files (the "Software"), to deal 6 | # in the Software without restriction, including without limitation the rights 7 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | # copies of the Software, and to permit persons to whom the Software is 9 | # furnished to do so, subject to the following conditions: 10 | # 11 | # The above copyright notice and this permission notice shall be included in 12 | # all copies or substantial portions of the Software. 13 | # 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | # THE SOFTWARE. 21 | 22 | import atexit 23 | import logging 24 | import math 25 | import os 26 | import subprocess 27 | import sys 28 | import time 29 | 30 | import ftdi1 as ftdi 31 | 32 | import Adafruit_GPIO.GPIO as GPIO 33 | 34 | 35 | logger = logging.getLogger(__name__) 36 | 37 | FT232H_VID = 0x0403 # Default FTDI FT232H vendor ID 38 | FT232H_PID = 0x6014 # Default FTDI FT232H product ID 39 | 40 | MSBFIRST = 0 41 | LSBFIRST = 1 42 | 43 | _REPEAT_DELAY = 4 44 | 45 | 46 | def _check_running_as_root(): 47 | # NOTE: Checking for root with user ID 0 isn't very portable, perhaps 48 | # there's a better alternative? 49 | if os.geteuid() != 0: 50 | raise RuntimeError('Expected to be run by root user! Try running with sudo.') 51 | 52 | def disable_FTDI_driver(): 53 | """Disable the FTDI drivers for the current platform. This is necessary 54 | because they will conflict with libftdi and accessing the FT232H. Note you 55 | can enable the FTDI drivers again by calling enable_FTDI_driver. 56 | """ 57 | logger.debug('Disabling FTDI driver.') 58 | if sys.platform == 'darwin': 59 | logger.debug('Detected Mac OSX') 60 | # Mac OS commands to disable FTDI driver. 61 | _check_running_as_root() 62 | subprocess.call('kextunload -b com.apple.driver.AppleUSBFTDI', shell=True) 63 | subprocess.call('kextunload /System/Library/Extensions/FTDIUSBSerialDriver.kext', shell=True) 64 | elif sys.platform.startswith('linux'): 65 | logger.debug('Detected Linux') 66 | # Linux commands to disable FTDI driver. 67 | _check_running_as_root() 68 | subprocess.call('modprobe -r -q ftdi_sio', shell=True) 69 | subprocess.call('modprobe -r -q usbserial', shell=True) 70 | # Note there is no need to disable FTDI drivers on Windows! 71 | 72 | def enable_FTDI_driver(): 73 | """Re-enable the FTDI drivers for the current platform.""" 74 | logger.debug('Enabling FTDI driver.') 75 | if sys.platform == 'darwin': 76 | logger.debug('Detected Mac OSX') 77 | # Mac OS commands to enable FTDI driver. 78 | _check_running_as_root() 79 | subprocess.check_call('kextload -b com.apple.driver.AppleUSBFTDI', shell=True) 80 | subprocess.check_call('kextload /System/Library/Extensions/FTDIUSBSerialDriver.kext', shell=True) 81 | elif sys.platform.startswith('linux'): 82 | logger.debug('Detected Linux') 83 | # Linux commands to enable FTDI driver. 84 | _check_running_as_root() 85 | subprocess.check_call('modprobe -q ftdi_sio', shell=True) 86 | subprocess.check_call('modprobe -q usbserial', shell=True) 87 | 88 | def use_FT232H(): 89 | """Disable any built in FTDI drivers which will conflict and cause problems 90 | with libftdi (which is used to communicate with the FT232H). Will register 91 | an exit function so the drivers are re-enabled on program exit. 92 | """ 93 | disable_FTDI_driver() 94 | atexit.register(enable_FTDI_driver) 95 | 96 | def enumerate_device_serials(vid=FT232H_VID, pid=FT232H_PID): 97 | """Return a list of all FT232H device serial numbers connected to the 98 | machine. You can use these serial numbers to open a specific FT232H device 99 | by passing it to the FT232H initializer's serial parameter. 100 | """ 101 | try: 102 | # Create a libftdi context. 103 | ctx = None 104 | ctx = ftdi.new() 105 | # Enumerate FTDI devices. 106 | device_list = None 107 | count, device_list = ftdi.usb_find_all(ctx, vid, pid) 108 | if count < 0: 109 | raise RuntimeError('ftdi_usb_find_all returned error {0}: {1}'.format(count, ftdi.get_error_string(self._ctx))) 110 | # Walk through list of devices and assemble list of serial numbers. 111 | devices = [] 112 | while device_list is not None: 113 | # Get USB device strings and add serial to list of devices. 114 | ret, manufacturer, description, serial = ftdi.usb_get_strings(ctx, device_list.dev, 256, 256, 256) 115 | if serial is not None: 116 | devices.append(serial) 117 | device_list = device_list.next 118 | return devices 119 | finally: 120 | # Make sure to clean up list and context when done. 121 | if device_list is not None: 122 | ftdi.list_free(device_list) 123 | if ctx is not None: 124 | ftdi.free(ctx) 125 | 126 | 127 | class FT232H(GPIO.BaseGPIO): 128 | # Make GPIO constants that match main GPIO class for compatibility. 129 | HIGH = GPIO.HIGH 130 | LOW = GPIO.LOW 131 | IN = GPIO.IN 132 | OUT = GPIO.OUT 133 | 134 | def __init__(self, vid=FT232H_VID, pid=FT232H_PID, serial=None): 135 | """Create a FT232H object. Will search for the first available FT232H 136 | device with the specified USB vendor ID and product ID (defaults to 137 | FT232H default VID & PID). Can also specify an optional serial number 138 | string to open an explicit FT232H device given its serial number. See 139 | the FT232H.enumerate_device_serials() function to see how to list all 140 | connected device serial numbers. 141 | """ 142 | # Initialize FTDI device connection. 143 | self._ctx = ftdi.new() 144 | if self._ctx == 0: 145 | raise RuntimeError('ftdi_new failed! Is libftdi1 installed?') 146 | # Register handler to close and cleanup FTDI context on program exit. 147 | atexit.register(self.close) 148 | if serial is None: 149 | # Open USB connection for specified VID and PID if no serial is specified. 150 | self._check(ftdi.usb_open, vid, pid) 151 | else: 152 | # Open USB connection for VID, PID, serial. 153 | self._check(ftdi.usb_open_string, 's:{0}:{1}:{2}'.format(vid, pid, serial)) 154 | # Reset device. 155 | self._check(ftdi.usb_reset) 156 | # Disable flow control. Commented out because it is unclear if this is necessary. 157 | #self._check(ftdi.setflowctrl, ftdi.SIO_DISABLE_FLOW_CTRL) 158 | # Change read & write buffers to maximum size, 65535 bytes. 159 | self._check(ftdi.read_data_set_chunksize, 65535) 160 | self._check(ftdi.write_data_set_chunksize, 65535) 161 | # Clear pending read data & write buffers. 162 | self._check(ftdi.usb_purge_buffers) 163 | # Enable MPSSE and syncronize communication with device. 164 | self._mpsse_enable() 165 | self._mpsse_sync() 166 | # Initialize all GPIO as inputs. 167 | self._write(b'\x80\x00\x00\x82\x00\x00') 168 | self._direction = 0x0000 169 | self._level = 0x0000 170 | 171 | def close(self): 172 | """Close the FTDI device. Will be automatically called when the program ends.""" 173 | if self._ctx is not None: 174 | ftdi.free(self._ctx) 175 | self._ctx = None 176 | 177 | def _write(self, string): 178 | """Helper function to call write_data on the provided FTDI device and 179 | verify it succeeds. 180 | """ 181 | # Get modem status. Useful to enable for debugging. 182 | #ret, status = ftdi.poll_modem_status(self._ctx) 183 | #if ret == 0: 184 | # logger.debug('Modem status {0:02X}'.format(status)) 185 | #else: 186 | # logger.debug('Modem status error {0}'.format(ret)) 187 | length = len(string) 188 | try: 189 | ret = ftdi.write_data(self._ctx, string, length) 190 | except TypeError: 191 | ret = ftdi.write_data(self._ctx, string); #compatible with libFtdi 1.3 192 | # Log the string that was written in a python hex string format using a very 193 | # ugly one-liner list comprehension for brevity. 194 | #logger.debug('Wrote {0}'.format(''.join(['\\x{0:02X}'.format(x) for x in bytearray(string)]))) 195 | if ret < 0: 196 | raise RuntimeError('ftdi_write_data failed with error {0}: {1}'.format(ret, ftdi.get_error_string(self._ctx))) 197 | if ret != length: 198 | raise RuntimeError('ftdi_write_data expected to write {0} bytes but actually wrote {1}!'.format(length, ret)) 199 | 200 | def _check(self, command, *args): 201 | """Helper function to call the provided command on the FTDI device and 202 | verify the response matches the expected value. 203 | """ 204 | ret = command(self._ctx, *args) 205 | logger.debug('Called ftdi_{0} and got response {1}.'.format(command.__name__, ret)) 206 | if ret != 0: 207 | raise RuntimeError('ftdi_{0} failed with error {1}: {2}'.format(command.__name__, ret, ftdi.get_error_string(self._ctx))) 208 | 209 | def _poll_read(self, expected, timeout_s=5.0): 210 | """Helper function to continuously poll reads on the FTDI device until an 211 | expected number of bytes are returned. Will throw a timeout error if no 212 | data is received within the specified number of timeout seconds. Returns 213 | the read data as a string if successful, otherwise raises an execption. 214 | """ 215 | start = time.time() 216 | # Start with an empty response buffer. 217 | response = bytearray(expected) 218 | index = 0 219 | # Loop calling read until the response buffer is full or a timeout occurs. 220 | while time.time() - start <= timeout_s: 221 | ret, data = ftdi.read_data(self._ctx, expected - index) 222 | # Fail if there was an error reading data. 223 | if ret < 0: 224 | raise RuntimeError('ftdi_read_data failed with error code {0}.'.format(ret)) 225 | # Add returned data to the buffer. 226 | response[index:index+ret] = data[:ret] 227 | index += ret 228 | # Buffer is full, return the result data. 229 | if index >= expected: 230 | return bytes(response) 231 | time.sleep(0.01) 232 | raise RuntimeError('Timeout while polling ftdi_read_data for {0} bytes!'.format(expected)) 233 | 234 | def _mpsse_enable(self): 235 | """Enable MPSSE mode on the FTDI device.""" 236 | # Reset MPSSE by sending mask = 0 and mode = 0 237 | self._check(ftdi.set_bitmode, 0, 0) 238 | # Enable MPSSE by sending mask = 0 and mode = 2 239 | self._check(ftdi.set_bitmode, 0, 2) 240 | 241 | def _mpsse_sync(self, max_retries=10): 242 | """Synchronize buffers with MPSSE by sending bad opcode and reading expected 243 | error response. Should be called once after enabling MPSSE.""" 244 | # Send a bad/unknown command (0xAB), then read buffer until bad command 245 | # response is found. 246 | self._write(b'\xAB') 247 | # Keep reading until bad command response (0xFA 0xAB) is returned. 248 | # Fail if too many read attempts are made to prevent sticking in a loop. 249 | tries = 0 250 | sync = False 251 | while not sync: 252 | data = self._poll_read(2) 253 | if data == b'\xFA\xAB': 254 | sync = True 255 | tries += 1 256 | if tries >= max_retries: 257 | raise RuntimeError('Could not synchronize with FT232H!') 258 | 259 | def mpsse_set_clock(self, clock_hz, adaptive=False, three_phase=False): 260 | """Set the clock speed of the MPSSE engine. Can be any value from 450hz 261 | to 30mhz and will pick that speed or the closest speed below it. 262 | """ 263 | # Disable clock divisor by 5 to enable faster speeds on FT232H. 264 | self._write(b'\x8A') 265 | # Turn on/off adaptive clocking. 266 | if adaptive: 267 | self._write(b'\x96') 268 | else: 269 | self._write(b'\x97') 270 | # Turn on/off three phase clock (needed for I2C). 271 | # Also adjust the frequency for three-phase clocking as specified in section 2.2.4 272 | # of this document: 273 | # http://www.ftdichip.com/Support/Documents/AppNotes/AN_255_USB%20to%20I2C%20Example%20using%20the%20FT232H%20and%20FT201X%20devices.pdf 274 | if three_phase: 275 | self._write(b'\x8C') 276 | else: 277 | self._write(b'\x8D') 278 | # Compute divisor for requested clock. 279 | # Use equation from section 3.8.1 of: 280 | # http://www.ftdichip.com/Support/Documents/AppNotes/AN_108_Command_Processor_for_MPSSE_and_MCU_Host_Bus_Emulation_Modes.pdf 281 | # Note equation is using 60mhz master clock instead of 12mhz. 282 | divisor = int(math.ceil((30000000.0-float(clock_hz))/float(clock_hz))) & 0xFFFF 283 | if three_phase: 284 | divisor = int(divisor*(2.0/3.0)) 285 | logger.debug('Setting clockspeed with divisor value {0}'.format(divisor)) 286 | # Send command to set divisor from low and high byte values. 287 | self._write(bytes(bytearray((0x86, divisor & 0xFF, (divisor >> 8) & 0xFF)))) 288 | 289 | def mpsse_read_gpio(self): 290 | """Read both GPIO bus states and return a 16 bit value with their state. 291 | D0-D7 are the lower 8 bits and C0-C7 are the upper 8 bits. 292 | """ 293 | # Send command to read low byte and high byte. 294 | self._write(b'\x81\x83') 295 | # Wait for 2 byte response. 296 | data = self._poll_read(2) 297 | # Assemble response into 16 bit value. 298 | low_byte = ord(data[0]) 299 | high_byte = ord(data[1]) 300 | logger.debug('Read MPSSE GPIO low byte = {0:02X} and high byte = {1:02X}'.format(low_byte, high_byte)) 301 | return (high_byte << 8) | low_byte 302 | 303 | def mpsse_gpio(self): 304 | """Return command to update the MPSSE GPIO state to the current direction 305 | and level. 306 | """ 307 | level_low = (self._level & 0xFF) 308 | level_high = ((self._level >> 8) & 0xFF) 309 | dir_low = (self._direction & 0xFF) 310 | dir_high = ((self._direction >> 8) & 0xFF) 311 | return bytes(bytearray((0x80, level_low, dir_low, 0x82, level_high, dir_high))) 312 | 313 | def mpsse_write_gpio(self): 314 | """Write the current MPSSE GPIO state to the FT232H chip.""" 315 | self._write(self.mpsse_gpio()) 316 | 317 | def get_i2c_device(self, address, **kwargs): 318 | """Return an I2CDevice instance using this FT232H object and the provided 319 | I2C address. Meant to be passed as the i2c_provider parameter to objects 320 | which use the Adafruit_Python_GPIO library for I2C. 321 | """ 322 | return I2CDevice(self, address, **kwargs) 323 | 324 | # GPIO functions below: 325 | 326 | def _setup_pin(self, pin, mode): 327 | if pin < 0 or pin > 15: 328 | raise ValueError('Pin must be between 0 and 15 (inclusive).') 329 | if mode not in (GPIO.IN, GPIO.OUT): 330 | raise ValueError('Mode must be GPIO.IN or GPIO.OUT.') 331 | if mode == GPIO.IN: 332 | # Set the direction and level of the pin to 0. 333 | self._direction &= ~(1 << pin) & 0xFFFF 334 | self._level &= ~(1 << pin) & 0xFFFF 335 | else: 336 | # Set the direction of the pin to 1. 337 | self._direction |= (1 << pin) & 0xFFFF 338 | 339 | def setup(self, pin, mode): 340 | """Set the input or output mode for a specified pin. Mode should be 341 | either OUT or IN.""" 342 | self._setup_pin(pin, mode) 343 | self.mpsse_write_gpio() 344 | 345 | def setup_pins(self, pins, values={}, write=True): 346 | """Setup multiple pins as inputs or outputs at once. Pins should be a 347 | dict of pin name to pin mode (IN or OUT). Optional starting values of 348 | pins can be provided in the values dict (with pin name to pin value). 349 | """ 350 | # General implementation that can be improved by subclasses. 351 | for pin, mode in iter(pins.items()): 352 | self._setup_pin(pin, mode) 353 | for pin, value in iter(values.items()): 354 | self._output_pin(pin, value) 355 | if write: 356 | self.mpsse_write_gpio() 357 | 358 | def _output_pin(self, pin, value): 359 | if value: 360 | self._level |= (1 << pin) & 0xFFFF 361 | else: 362 | self._level &= ~(1 << pin) & 0xFFFF 363 | 364 | def output(self, pin, value): 365 | """Set the specified pin the provided high/low value. Value should be 366 | either HIGH/LOW or a boolean (true = high).""" 367 | if pin < 0 or pin > 15: 368 | raise ValueError('Pin must be between 0 and 15 (inclusive).') 369 | self._output_pin(pin, value) 370 | self.mpsse_write_gpio() 371 | 372 | def output_pins(self, pins, write=True): 373 | """Set multiple pins high or low at once. Pins should be a dict of pin 374 | name to pin value (HIGH/True for 1, LOW/False for 0). All provided pins 375 | will be set to the given values. 376 | """ 377 | for pin, value in iter(pins.items()): 378 | self._output_pin(pin, value) 379 | if write: 380 | self.mpsse_write_gpio() 381 | 382 | def input(self, pin): 383 | """Read the specified pin and return HIGH/true if the pin is pulled high, 384 | or LOW/false if pulled low.""" 385 | return self.input_pins([pin])[0] 386 | 387 | def input_pins(self, pins): 388 | """Read multiple pins specified in the given list and return list of pin values 389 | GPIO.HIGH/True if the pin is pulled high, or GPIO.LOW/False if pulled low.""" 390 | if [pin for pin in pins if pin < 0 or pin > 15]: 391 | raise ValueError('Pin must be between 0 and 15 (inclusive).') 392 | _pins = self.mpsse_read_gpio() 393 | return [((_pins >> pin) & 0x0001) == 1 for pin in pins] 394 | 395 | 396 | class SPI(object): 397 | def __init__(self, ft232h, cs=None, max_speed_hz=1000000, mode=0, bitorder=MSBFIRST): 398 | self._ft232h = ft232h 399 | # Initialize chip select pin if provided to output high. 400 | if cs is not None: 401 | ft232h.set_high(cs) 402 | ft232h.setup(cs, GPIO.OUT) 403 | self._cs = cs 404 | # Initialize clock, mode, and bit order. 405 | self.set_clock_hz(max_speed_hz) 406 | self.set_mode(mode) 407 | self.set_bit_order(bitorder) 408 | 409 | def _assert_cs(self): 410 | if self._cs is not None: 411 | self._ft232h.set_low(self._cs) 412 | 413 | def _deassert_cs(self): 414 | if self._cs is not None: 415 | self._ft232h.set_high(self._cs) 416 | 417 | def set_clock_hz(self, hz): 418 | """Set the speed of the SPI clock in hertz. Note that not all speeds 419 | are supported and a lower speed might be chosen by the hardware. 420 | """ 421 | self._ft232h.mpsse_set_clock(hz) 422 | 423 | def set_mode(self, mode): 424 | """Set SPI mode which controls clock polarity and phase. Should be a 425 | numeric value 0, 1, 2, or 3. See wikipedia page for details on meaning: 426 | http://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus 427 | """ 428 | if mode < 0 or mode > 3: 429 | raise ValueError('Mode must be a value 0, 1, 2, or 3.') 430 | if mode == 0: 431 | # Mode 0 captures on rising clock, propagates on falling clock 432 | self.write_clock_ve = 1 433 | self.read_clock_ve = 0 434 | # Clock base is low. 435 | clock_base = GPIO.LOW 436 | elif mode == 1: 437 | # Mode 1 capture of falling edge, propagate on rising clock 438 | self.write_clock_ve = 0 439 | self.read_clock_ve = 1 440 | # Clock base is low. 441 | clock_base = GPIO.LOW 442 | elif mode == 2: 443 | # Mode 2 capture on rising clock, propagate on falling clock 444 | self.write_clock_ve = 1 445 | self.read_clock_ve = 0 446 | # Clock base is high. 447 | clock_base = GPIO.HIGH 448 | elif mode == 3: 449 | # Mode 3 capture on falling edge, propagage on rising clock 450 | self.write_clock_ve = 0 451 | self.read_clock_ve = 1 452 | # Clock base is high. 453 | clock_base = GPIO.HIGH 454 | # Set clock and DO as output, DI as input. Also start clock at its base value. 455 | self._ft232h.setup_pins({0: GPIO.OUT, 1: GPIO.OUT, 2: GPIO.IN}, {0: clock_base}) 456 | 457 | def set_bit_order(self, order): 458 | """Set order of bits to be read/written over serial lines. Should be 459 | either MSBFIRST for most-significant first, or LSBFIRST for 460 | least-signifcant first. 461 | """ 462 | if order == MSBFIRST: 463 | self.lsbfirst = 0 464 | elif order == LSBFIRST: 465 | self.lsbfirst = 1 466 | else: 467 | raise ValueError('Order must be MSBFIRST or LSBFIRST.') 468 | 469 | def write(self, data): 470 | """Half-duplex SPI write. The specified array of bytes will be clocked 471 | out the MOSI line. 472 | """ 473 | #check for hardware limit of FT232H and similar MPSSE chips 474 | if (len(data) > 65536): 475 | print('the FTDI chip is limited to 65536 bytes (64 KB) of input/output per command!') 476 | print('use for loops for larger reads') 477 | exit(1) 478 | # Build command to write SPI data. 479 | command = 0x10 | (self.lsbfirst << 3) | self.write_clock_ve 480 | logger.debug('SPI write with command {0:2X}.'.format(command)) 481 | # Compute length low and high bytes. 482 | # NOTE: Must actually send length minus one because the MPSSE engine 483 | # considers 0 a length of 1 and FFFF a length of 65536 484 | # splitting into two lists for two commands to prevent buffer errors 485 | data1 = data[:len(data)//2] 486 | data2 = data[len(data)//2:] 487 | len_low1 = (len(data1) - 1) & 0xFF 488 | len_high1 = ((len(data1) - 1) >> 8) & 0xFF 489 | len_low2 = (len(data2) - 1) & 0xFF 490 | len_high2 = ((len(data2) - 1) >> 8) & 0xFF 491 | self._assert_cs() 492 | # Send command and length, then data, split into two commands, handle for length 1 493 | if len(data1) > 0: 494 | self._ft232h._write(bytes(bytearray((command, len_low1, len_high1)))) 495 | self._ft232h._write(bytes(bytearray(data1))) 496 | if len(data2) > 0: 497 | self._ft232h._write(bytes(bytearray((command, len_low2, len_high2)))) 498 | self._ft232h._write(bytes(bytearray(data2))) 499 | self._deassert_cs() 500 | 501 | def read(self, length): 502 | """Half-duplex SPI read. The specified length of bytes will be clocked 503 | in the MISO line and returned as a bytearray object. 504 | """ 505 | #check for hardware limit of FT232H and similar MPSSE chips 506 | if (1 > length > 65536): 507 | print('the FTDI chip is limited to 65536 bytes (64 KB) of input/output per command!') 508 | print('use for loops for larger reads') 509 | exit(1) 510 | # Build command to read SPI data. 511 | command = 0x20 | (self.lsbfirst << 3) | (self.read_clock_ve << 2) 512 | logger.debug('SPI read with command {0:2X}.'.format(command)) 513 | # Compute length low and high bytes. 514 | # NOTE: Must actually send length minus one because the MPSSE engine 515 | # considers 0 a length of 1 and FFFF a length of 65536 516 | #force odd numbers to round up instead of down 517 | lengthR = length 518 | if length % 2 == 1: 519 | lengthR += 1 520 | lengthR = lengthR//2 521 | #when odd length requested, get the remainder instead of the same number 522 | lenremain = length - lengthR 523 | len_low = (lengthR - 1) & 0xFF 524 | len_high = ((lengthR - 1) >> 8) & 0xFF 525 | self._assert_cs() 526 | # Send command and length. 527 | # Perform twice to prevent error from hardware defect/limits 528 | self._ft232h._write(bytes(bytearray((command, len_low, len_high)))) 529 | payload1 = self._ft232h._poll_read(lengthR) 530 | self._ft232h._write(bytes(bytearray((command, len_low, len_high)))) 531 | payload2 = self._ft232h._poll_read(lenremain) 532 | self._deassert_cs() 533 | # Read response bytes 534 | return bytearray(payload1 + payload2) 535 | 536 | def bulkread(self, data = [], lengthR = 'None', readmode = 1): 537 | """Half-duplex SPI write then read. Send command and payload to slave as bytearray 538 | then consequently read out response from the slave for length in bytes. 539 | Designed for use with NOR or NAND flash chips, and possibly SD cards...etc... 540 | Read command is cut in half and performed twice in series to prevent single byte errors. 541 | Hardware limits per command are enforced before doing anything. 542 | Read length is an optional argument, so that it can function similar to transfer 543 | but still half-duplex. 544 | For reading without writing, one can send a blank array or skip that argument. 545 | """ 546 | #check for hardware limit of FT232H and similar MPSSE chips 547 | if (1 > lengthR > 65536)|(len(data) > 65536): 548 | print('the FTDI chip is limited to 65536 bytes (64 KB) of input/output per command!') 549 | print('use for loops for larger reads') 550 | exit(1) 551 | #default mode is to act like `transfer` but half-duplex 552 | if (lengthR == 'None')&(readmode == 1): 553 | lengthR = len(data) 554 | #command parameters definition and math 555 | #MPSSE engine sees length 0 as 1 byte, so - 1 lengths 556 | commandW = 0x10 | (self.lsbfirst << 3) | self.write_clock_ve 557 | lengthW = len(data) - 1 558 | len_lowW = (lengthW) & 0xFF 559 | len_highW = ((lengthW) >> 8) & 0xFF 560 | commandR = 0x20 | (self.lsbfirst << 3) | (self.read_clock_ve << 2) 561 | #force odd numbers to round up instead of down 562 | length = lengthR 563 | if lengthR % 2 == 1: 564 | length += 1 565 | length = length//2 566 | #when odd length requested, get the remainder instead of the same number 567 | lenremain = lengthR - length 568 | len_lowR = (length - 1) & 0xFF 569 | len_highR = ((length - 1) >> 8) & 0xFF 570 | #logger debug info 571 | logger.debug('SPI bulkread with write command {0:2X}.'.format(commandW)) 572 | logger.debug('and read command {0:2X}.'.format(commandR)) 573 | #begin command set 574 | self._assert_cs() 575 | #write command, these have to be separated due to TypeError 576 | self._ft232h._write(bytes(bytearray((commandW, len_lowW, len_highW)))) 577 | self._ft232h._write(bytes(bytearray(data))) 578 | #read command, which is divided into two commands 579 | self._ft232h._write(bytes(bytearray((commandR, len_lowR, len_highR)))) 580 | payload1 = self._ft232h._poll_read(length) 581 | self._ft232h._write(bytes(bytearray((commandR, len_lowR, len_highR)))) 582 | payload2 = self._ft232h._poll_read(lenremain) 583 | self._deassert_cs() 584 | #end command set 585 | # Read response bytes 586 | return bytearray(payload1 + payload2) 587 | 588 | def transfer(self, data): 589 | """Full-duplex SPI read and write. The specified array of bytes will be 590 | clocked out the MOSI line, while simultaneously bytes will be read from 591 | the MISO line. Read bytes will be returned as a bytearray object. 592 | """ 593 | #check for hardware limit of FT232H and similar MPSSE chips 594 | if (len(data) > 65536): 595 | print('the FTDI chip is limited to 65536 bytes (64 KB) of input/output per command!') 596 | print('use for loops for larger reads') 597 | exit(1) 598 | # Build command to read and write SPI data. 599 | command = 0x30 | (self.lsbfirst << 3) | (self.read_clock_ve << 2) | self.write_clock_ve 600 | logger.debug('SPI transfer with command {0:2X}.'.format(command)) 601 | # Compute length low and high bytes. 602 | # NOTE: Must actually send length minus one because the MPSSE engine 603 | # considers 0 a length of 1 and FFFF a length of 65536 604 | data1 = data[:len(data)//2] 605 | data2 = data[len(data)//2:] 606 | len_low1 = (len(data1) - 1) & 0xFF 607 | len_high1 = ((len(data1) - 1) >> 8) & 0xFF 608 | len_low2 = (len(data2) - 1) & 0xFF 609 | len_high2 = ((len(data2) - 1) >> 8) & 0xFF 610 | payload1 = '' 611 | payload2 = '' 612 | #start command set 613 | self._assert_cs() 614 | # Perform twice to prevent error from hardware defect/limits 615 | # Send command and length, then data, split into two commands, handle for length 1 616 | if len(data1) > 0: 617 | self._ft232h._write(bytes(bytearray((command, len_low1, len_high1)))) 618 | self._ft232h._write(bytes(bytearray(data1))) 619 | payload1 = self._ft232h._poll_read(len(data1)) 620 | if len(data2) > 0: 621 | self._ft232h._write(bytes(bytearray((command, len_low2, len_high2)))) 622 | self._ft232h._write(bytes(bytearray(data2))) 623 | payload2 = self._ft232h._poll_read(len(data2)) 624 | #self._ft232h._write('\x87') 625 | self._deassert_cs() 626 | # Read response bytes. 627 | return bytearray(payload1 + payload2) 628 | 629 | class I2CDevice(object): 630 | """Class for communicating with an I2C device using the smbus library. 631 | Allows reading and writing 8-bit, 16-bit, and byte array values to registers 632 | on the device.""" 633 | # Note that most of the functions in this code are adapted from this app note: 634 | # http://www.ftdichip.com/Support/Documents/AppNotes/AN_255_USB%20to%20I2C%20Example%20using%20the%20FT232H%20and%20FT201X%20devices.pdf 635 | def __init__(self, ft232h, address, clock_hz=100000): 636 | """Create an instance of the I2C device at the specified address on the 637 | specified I2C bus number.""" 638 | self._address = address 639 | self._ft232h = ft232h 640 | # Enable clock with three phases for I2C. 641 | self._ft232h.mpsse_set_clock(clock_hz, three_phase=True) 642 | # Enable drive-zero mode to drive outputs low on 0 and tri-state on 1. 643 | # This matches the protocol for I2C communication so multiple devices can 644 | # share the I2C bus. 645 | self._ft232h._write(b'\x9E\x07\x00') 646 | self._idle() 647 | 648 | def _idle(self): 649 | """Put I2C lines into idle state.""" 650 | # Put the I2C lines into an idle state with SCL and SDA high. 651 | self._ft232h.setup_pins({0: GPIO.OUT, 1: GPIO.OUT, 2: GPIO.IN}, 652 | {0: GPIO.HIGH, 1: GPIO.HIGH}) 653 | 654 | def _transaction_start(self): 655 | """Start I2C transaction.""" 656 | # Clear command buffer and expected response bytes. 657 | self._command = [] 658 | self._expected = 0 659 | 660 | def _transaction_end(self): 661 | """End I2C transaction and get response bytes, including ACKs.""" 662 | # Ask to return response bytes immediately. 663 | self._command.append(b'\x87') 664 | # Send the entire command to the MPSSE. 665 | self._ft232h._write(b''.join(self._command)) 666 | # Read response bytes and return them. 667 | return bytearray(self._ft232h._poll_read(self._expected)) 668 | 669 | def _i2c_start(self): 670 | """Send I2C start signal. Must be called within a transaction start/end. 671 | """ 672 | # Set SCL high and SDA low, repeat 4 times to stay in this state for a 673 | # short period of time. 674 | self._ft232h.output_pins({0: GPIO.HIGH, 1: GPIO.LOW}, write=False) 675 | self._command.append(self._ft232h.mpsse_gpio() * _REPEAT_DELAY) 676 | # Now drop SCL to low (again repeat 4 times for short delay). 677 | self._ft232h.output_pins({0: GPIO.LOW, 1: GPIO.LOW}, write=False) 678 | self._command.append(self._ft232h.mpsse_gpio() * _REPEAT_DELAY) 679 | 680 | def _i2c_idle(self): 681 | """Set I2C signals to idle state with SCL and SDA at a high value. Must 682 | be called within a transaction start/end. 683 | """ 684 | self._ft232h.output_pins({0: GPIO.HIGH, 1: GPIO.HIGH}, write=False) 685 | self._command.append(self._ft232h.mpsse_gpio() * _REPEAT_DELAY) 686 | 687 | def _i2c_stop(self): 688 | """Send I2C stop signal. Must be called within a transaction start/end. 689 | """ 690 | # Set SCL low and SDA low for a short period. 691 | self._ft232h.output_pins({0: GPIO.LOW, 1: GPIO.LOW}, write=False) 692 | self._command.append(self._ft232h.mpsse_gpio() * _REPEAT_DELAY) 693 | # Set SCL high and SDA low for a short period. 694 | self._ft232h.output_pins({0: GPIO.HIGH, 1: GPIO.LOW}, write=False) 695 | self._command.append(self._ft232h.mpsse_gpio() * _REPEAT_DELAY) 696 | # Finally set SCL high and SDA high for a short period. 697 | self._ft232h.output_pins({0: GPIO.HIGH, 1: GPIO.HIGH}, write=False) 698 | self._command.append(self._ft232h.mpsse_gpio() * _REPEAT_DELAY) 699 | 700 | def _i2c_read_bytes(self, length=1): 701 | """Read the specified number of bytes from the I2C bus. Length is the 702 | number of bytes to read (must be 1 or more). 703 | """ 704 | for i in range(length-1): 705 | # Read a byte and send ACK. 706 | self._command.append(b'\x20\x00\x00\x13\x00\x00') 707 | # Make sure pins are back in idle state with clock low and data high. 708 | self._ft232h.output_pins({0: GPIO.LOW, 1: GPIO.HIGH}, write=False) 709 | self._command.append(self._ft232h.mpsse_gpio()) 710 | # Read last byte and send NAK. 711 | self._command.append(b'\x20\x00\x00\x13\x00\xFF') 712 | # Make sure pins are back in idle state with clock low and data high. 713 | self._ft232h.output_pins({0: GPIO.LOW, 1: GPIO.HIGH}, write=False) 714 | self._command.append(self._ft232h.mpsse_gpio()) 715 | # Increase expected number of bytes. 716 | self._expected += length 717 | 718 | def _i2c_write_bytes(self, data): 719 | """Write the specified number of bytes to the chip.""" 720 | for byte in data: 721 | # Write byte. 722 | self._command.append(bytes(bytearray((0x11, 0x00, 0x00, byte)))) 723 | # Make sure pins are back in idle state with clock low and data high. 724 | self._ft232h.output_pins({0: GPIO.LOW, 1: GPIO.HIGH}, write=False) 725 | self._command.append(self._ft232h.mpsse_gpio() * _REPEAT_DELAY) 726 | # Read bit for ACK/NAK. 727 | self._command.append(b'\x22\x00') 728 | # Increase expected response bytes. 729 | self._expected += len(data) 730 | 731 | def _address_byte(self, read=True): 732 | """Return the address byte with the specified R/W bit set. If read is 733 | True the R/W bit will be 1, otherwise the R/W bit will be 0. 734 | """ 735 | if read: 736 | return (self._address << 1) | 0x01 737 | else: 738 | return self._address << 1 739 | 740 | def _verify_acks(self, response): 741 | """Check all the specified bytes have the ACK bit set. Throws a 742 | RuntimeError exception if not all the ACKs are set. 743 | """ 744 | for byte in response: 745 | if byte & 0x01 != 0x00: 746 | raise RuntimeError('Failed to find expected I2C ACK!') 747 | 748 | def ping(self): 749 | """Attempt to detect if a device at this address is present on the I2C 750 | bus. Will send out the device's address for writing and verify an ACK 751 | is received. Returns true if the ACK is received, and false if not. 752 | """ 753 | self._idle() 754 | self._transaction_start() 755 | self._i2c_start() 756 | self._i2c_write_bytes([self._address_byte(False)]) 757 | self._i2c_stop() 758 | response = self._transaction_end() 759 | if len(response) != 1: 760 | raise RuntimeError('Expected 1 response byte but received {0} byte(s).'.format(len(response))) 761 | return ((response[0] & 0x01) == 0x00) 762 | 763 | def writeRaw8(self, value): 764 | """Write an 8-bit value on the bus (without register).""" 765 | value = value & 0xFF 766 | self._idle() 767 | self._transaction_start() 768 | self._i2c_start() 769 | self._i2c_write_bytes([self._address_byte(False), value]) 770 | self._i2c_stop() 771 | response = self._transaction_end() 772 | self._verify_acks(response) 773 | 774 | def write8(self, register, value): 775 | """Write an 8-bit value to the specified register.""" 776 | value = value & 0xFF 777 | self._idle() 778 | self._transaction_start() 779 | self._i2c_start() 780 | self._i2c_write_bytes([self._address_byte(False), register, value]) 781 | self._i2c_stop() 782 | response = self._transaction_end() 783 | self._verify_acks(response) 784 | 785 | def write16(self, register, value, little_endian=True): 786 | """Write a 16-bit value to the specified register.""" 787 | value = value & 0xFFFF 788 | value_low = value & 0xFF 789 | value_high = (value >> 8) & 0xFF 790 | if not little_endian: 791 | value_low, value_high = value_high, value_low 792 | self._idle() 793 | self._transaction_start() 794 | self._i2c_start() 795 | self._i2c_write_bytes([self._address_byte(False), register, value_low, 796 | value_high]) 797 | self._i2c_stop() 798 | response = self._transaction_end() 799 | self._verify_acks(response) 800 | 801 | def writeList(self, register, data): 802 | """Write bytes to the specified register.""" 803 | self._idle() 804 | self._transaction_start() 805 | self._i2c_start() 806 | self._i2c_write_bytes([self._address_byte(False), register] + data) 807 | self._i2c_stop() 808 | response = self._transaction_end() 809 | self._verify_acks(response) 810 | 811 | def readList(self, register, length): 812 | """Read a length number of bytes from the specified register. Results 813 | will be returned as a bytearray.""" 814 | if length <= 0: 815 | raise ValueError("Length must be at least 1 byte.") 816 | self._idle() 817 | self._transaction_start() 818 | self._i2c_start() 819 | self._i2c_write_bytes([self._address_byte(True), register]) 820 | self._i2c_stop() 821 | self._i2c_idle() 822 | self._i2c_start() 823 | self._i2c_read_bytes(length) 824 | self._i2c_stop() 825 | response = self._transaction_end() 826 | self._verify_acks(response[:-length]) 827 | return response[-length:] 828 | 829 | def readRaw8(self): 830 | """Read an 8-bit value on the bus (without register).""" 831 | self._idle() 832 | self._transaction_start() 833 | self._i2c_start() 834 | self._i2c_write_bytes([self._address_byte(False)]) 835 | self._i2c_stop() 836 | self._i2c_idle() 837 | self._i2c_start() 838 | self._i2c_write_bytes([self._address_byte(True)]) 839 | self._i2c_read_bytes(1) 840 | self._i2c_stop() 841 | response = self._transaction_end() 842 | self._verify_acks(response[:-1]) 843 | return response[-1] 844 | 845 | def readU8(self, register): 846 | """Read an unsigned byte from the specified register.""" 847 | self._idle() 848 | self._transaction_start() 849 | self._i2c_start() 850 | self._i2c_write_bytes([self._address_byte(False), register]) 851 | self._i2c_stop() 852 | self._i2c_idle() 853 | self._i2c_start() 854 | self._i2c_write_bytes([self._address_byte(True)]) 855 | self._i2c_read_bytes(1) 856 | self._i2c_stop() 857 | response = self._transaction_end() 858 | self._verify_acks(response[:-1]) 859 | return response[-1] 860 | 861 | def readS8(self, register): 862 | """Read a signed byte from the specified register.""" 863 | result = self.readU8(register) 864 | if result > 127: 865 | result -= 256 866 | return result 867 | 868 | def readU16(self, register, little_endian=True): 869 | """Read an unsigned 16-bit value from the specified register, with the 870 | specified endianness (default little endian, or least significant byte 871 | first).""" 872 | self._idle() 873 | self._transaction_start() 874 | self._i2c_start() 875 | self._i2c_write_bytes([self._address_byte(False), register]) 876 | self._i2c_stop() 877 | self._i2c_idle() 878 | self._i2c_start() 879 | self._i2c_write_bytes([self._address_byte(True)]) 880 | self._i2c_read_bytes(2) 881 | self._i2c_stop() 882 | response = self._transaction_end() 883 | self._verify_acks(response[:-2]) 884 | if little_endian: 885 | return (response[-1] << 8) | response[-2] 886 | else: 887 | return (response[-2] << 8) | response[-1] 888 | 889 | def readS16(self, register, little_endian=True): 890 | """Read a signed 16-bit value from the specified register, with the 891 | specified endianness (default little endian, or least significant byte 892 | first).""" 893 | result = self.readU16(register, little_endian) 894 | if result > 32767: 895 | result -= 65536 896 | return result 897 | 898 | def readU16LE(self, register): 899 | """Read an unsigned 16-bit value from the specified register, in little 900 | endian byte order.""" 901 | return self.readU16(register, little_endian=True) 902 | 903 | def readU16BE(self, register): 904 | """Read an unsigned 16-bit value from the specified register, in big 905 | endian byte order.""" 906 | return self.readU16(register, little_endian=False) 907 | 908 | def readS16LE(self, register): 909 | """Read a signed 16-bit value from the specified register, in little 910 | endian byte order.""" 911 | return self.readS16(register, little_endian=True) 912 | 913 | def readS16BE(self, register): 914 | """Read a signed 16-bit value from the specified register, in big 915 | endian byte order.""" 916 | return self.readS16(register, little_endian=False) 917 | -------------------------------------------------------------------------------- /Adafruit_GPIO/GPIO.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2014 Adafruit Industries 2 | # Author: Tony DiCola 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy 5 | # of this software and associated documentation files (the "Software"), to deal 6 | # in the Software without restriction, including without limitation the rights 7 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | # copies of the Software, and to permit persons to whom the Software is 9 | # furnished to do so, subject to the following conditions: 10 | # 11 | # The above copyright notice and this permission notice shall be included in 12 | # all copies or substantial portions of the Software. 13 | # 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | # THE SOFTWARE. 21 | 22 | import Adafruit_GPIO.Platform as Platform 23 | 24 | 25 | OUT = 0 26 | IN = 1 27 | HIGH = True 28 | LOW = False 29 | 30 | RISING = 1 31 | FALLING = 2 32 | BOTH = 3 33 | 34 | PUD_OFF = 0 35 | PUD_DOWN = 1 36 | PUD_UP = 2 37 | 38 | class BaseGPIO(object): 39 | """Base class for implementing simple digital IO for a platform. 40 | Implementors are expected to subclass from this and provide an implementation 41 | of the setup, output, and input functions.""" 42 | 43 | def setup(self, pin, mode, pull_up_down=PUD_OFF): 44 | """Set the input or output mode for a specified pin. Mode should be 45 | either OUT or IN.""" 46 | raise NotImplementedError 47 | 48 | def output(self, pin, value): 49 | """Set the specified pin the provided high/low value. Value should be 50 | either HIGH/LOW or a boolean (true = high).""" 51 | raise NotImplementedError 52 | 53 | def input(self, pin): 54 | """Read the specified pin and return HIGH/true if the pin is pulled high, 55 | or LOW/false if pulled low.""" 56 | raise NotImplementedError 57 | 58 | def set_high(self, pin): 59 | """Set the specified pin HIGH.""" 60 | self.output(pin, HIGH) 61 | 62 | def set_low(self, pin): 63 | """Set the specified pin LOW.""" 64 | self.output(pin, LOW) 65 | 66 | def is_high(self, pin): 67 | """Return true if the specified pin is pulled high.""" 68 | return self.input(pin) == HIGH 69 | 70 | def is_low(self, pin): 71 | """Return true if the specified pin is pulled low.""" 72 | return self.input(pin) == LOW 73 | 74 | 75 | # Basic implementation of multiple pin methods just loops through pins and 76 | # processes each one individually. This is not optimal, but derived classes can 77 | # provide a more optimal implementation that deals with groups of pins 78 | # simultaneously. 79 | # See MCP230xx or PCF8574 classes for examples of optimized implementations. 80 | 81 | def output_pins(self, pins): 82 | """Set multiple pins high or low at once. Pins should be a dict of pin 83 | name to pin value (HIGH/True for 1, LOW/False for 0). All provided pins 84 | will be set to the given values. 85 | """ 86 | # General implementation just loops through pins and writes them out 87 | # manually. This is not optimized, but subclasses can choose to implement 88 | # a more optimal batch output implementation. See the MCP230xx class for 89 | # example of optimized implementation. 90 | for pin, value in iter(pins.items()): 91 | self.output(pin, value) 92 | 93 | def setup_pins(self, pins): 94 | """Setup multiple pins as inputs or outputs at once. Pins should be a 95 | dict of pin name to pin type (IN or OUT). 96 | """ 97 | # General implementation that can be optimized by derived classes. 98 | for pin, value in iter(pins.items()): 99 | self.setup(pin, value) 100 | 101 | def input_pins(self, pins): 102 | """Read multiple pins specified in the given list and return list of pin values 103 | GPIO.HIGH/True if the pin is pulled high, or GPIO.LOW/False if pulled low. 104 | """ 105 | # General implementation that can be optimized by derived classes. 106 | return [self.input(pin) for pin in pins] 107 | 108 | 109 | def add_event_detect(self, pin, edge): 110 | """Enable edge detection events for a particular GPIO channel. Pin 111 | should be type IN. Edge must be RISING, FALLING or BOTH. 112 | """ 113 | raise NotImplementedError 114 | 115 | def remove_event_detect(self, pin): 116 | """Remove edge detection for a particular GPIO channel. Pin should be 117 | type IN. 118 | """ 119 | raise NotImplementedError 120 | 121 | def add_event_callback(self, pin, callback): 122 | """Add a callback for an event already defined using add_event_detect(). 123 | Pin should be type IN. 124 | """ 125 | raise NotImplementedError 126 | 127 | def event_detected(self, pin): 128 | """Returns True if an edge has occured on a given GPIO. You need to 129 | enable edge detection using add_event_detect() first. Pin should be 130 | type IN. 131 | """ 132 | raise NotImplementedError 133 | 134 | def wait_for_edge(self, pin, edge): 135 | """Wait for an edge. Pin should be type IN. Edge must be RISING, 136 | FALLING or BOTH.""" 137 | raise NotImplementedError 138 | 139 | def cleanup(self, pin=None): 140 | """Clean up GPIO event detection for specific pin, or all pins if none 141 | is specified. 142 | """ 143 | raise NotImplementedError 144 | 145 | 146 | # helper functions useful to derived classes 147 | 148 | def _validate_pin(self, pin): 149 | # Raise an exception if pin is outside the range of allowed values. 150 | if pin < 0 or pin >= self.NUM_GPIO: 151 | raise ValueError('Invalid GPIO value, must be between 0 and {0}.'.format(self.NUM_GPIO)) 152 | 153 | def _bit2(self, src, bit, val): 154 | bit = 1 << bit 155 | return (src | bit) if val else (src & ~bit) 156 | 157 | 158 | class RPiGPIOAdapter(BaseGPIO): 159 | """GPIO implementation for the Raspberry Pi using the RPi.GPIO library.""" 160 | 161 | def __init__(self, rpi_gpio, mode=None): 162 | self.rpi_gpio = rpi_gpio 163 | # Suppress warnings about GPIO in use. 164 | rpi_gpio.setwarnings(False) 165 | # Setup board pin mode. 166 | if mode == rpi_gpio.BOARD or mode == rpi_gpio.BCM: 167 | rpi_gpio.setmode(mode) 168 | elif mode is not None: 169 | raise ValueError('Unexpected value for mode. Must be BOARD or BCM.') 170 | else: 171 | # Default to BCM numbering if not told otherwise. 172 | rpi_gpio.setmode(rpi_gpio.BCM) 173 | # Define mapping of Adafruit GPIO library constants to RPi.GPIO constants. 174 | self._dir_mapping = { OUT: rpi_gpio.OUT, 175 | IN: rpi_gpio.IN } 176 | self._pud_mapping = { PUD_OFF: rpi_gpio.PUD_OFF, 177 | PUD_DOWN: rpi_gpio.PUD_DOWN, 178 | PUD_UP: rpi_gpio.PUD_UP } 179 | self._edge_mapping = { RISING: rpi_gpio.RISING, 180 | FALLING: rpi_gpio.FALLING, 181 | BOTH: rpi_gpio.BOTH } 182 | 183 | def setup(self, pin, mode, pull_up_down=PUD_OFF): 184 | """Set the input or output mode for a specified pin. Mode should be 185 | either OUTPUT or INPUT. 186 | """ 187 | self.rpi_gpio.setup(pin, self._dir_mapping[mode], 188 | pull_up_down=self._pud_mapping[pull_up_down]) 189 | 190 | def output(self, pin, value): 191 | """Set the specified pin the provided high/low value. Value should be 192 | either HIGH/LOW or a boolean (true = high). 193 | """ 194 | self.rpi_gpio.output(pin, value) 195 | 196 | def input(self, pin): 197 | """Read the specified pin and return HIGH/true if the pin is pulled high, 198 | or LOW/false if pulled low. 199 | """ 200 | return self.rpi_gpio.input(pin) 201 | 202 | def input_pins(self, pins): 203 | """Read multiple pins specified in the given list and return list of pin values 204 | GPIO.HIGH/True if the pin is pulled high, or GPIO.LOW/False if pulled low. 205 | """ 206 | # maybe rpi has a mass read... it would be more efficient to use it if it exists 207 | return [self.rpi_gpio.input(pin) for pin in pins] 208 | 209 | def add_event_detect(self, pin, edge, callback=None, bouncetime=-1): 210 | """Enable edge detection events for a particular GPIO channel. Pin 211 | should be type IN. Edge must be RISING, FALLING or BOTH. Callback is a 212 | function for the event. Bouncetime is switch bounce timeout in ms for 213 | callback 214 | """ 215 | kwargs = {} 216 | if callback: 217 | kwargs['callback']=callback 218 | if bouncetime > 0: 219 | kwargs['bouncetime']=bouncetime 220 | self.rpi_gpio.add_event_detect(pin, self._edge_mapping[edge], **kwargs) 221 | 222 | def remove_event_detect(self, pin): 223 | """Remove edge detection for a particular GPIO channel. Pin should be 224 | type IN. 225 | """ 226 | self.rpi_gpio.remove_event_detect(pin) 227 | 228 | def add_event_callback(self, pin, callback): 229 | """Add a callback for an event already defined using add_event_detect(). 230 | Pin should be type IN. 231 | """ 232 | self.rpi_gpio.add_event_callback(pin, callback) 233 | 234 | def event_detected(self, pin): 235 | """Returns True if an edge has occured on a given GPIO. You need to 236 | enable edge detection using add_event_detect() first. Pin should be 237 | type IN. 238 | """ 239 | return self.rpi_gpio.event_detected(pin) 240 | 241 | def wait_for_edge(self, pin, edge): 242 | """Wait for an edge. Pin should be type IN. Edge must be RISING, 243 | FALLING or BOTH. 244 | """ 245 | self.rpi_gpio.wait_for_edge(pin, self._edge_mapping[edge]) 246 | 247 | def cleanup(self, pin=None): 248 | """Clean up GPIO event detection for specific pin, or all pins if none 249 | is specified. 250 | """ 251 | if pin is None: 252 | self.rpi_gpio.cleanup() 253 | else: 254 | self.rpi_gpio.cleanup(pin) 255 | 256 | class AdafruitBBIOAdapter(BaseGPIO): 257 | """GPIO implementation for the Beaglebone Black using the Adafruit_BBIO 258 | library. 259 | """ 260 | 261 | def __init__(self, bbio_gpio): 262 | self.bbio_gpio = bbio_gpio 263 | # Define mapping of Adafruit GPIO library constants to RPi.GPIO constants. 264 | self._dir_mapping = { OUT: bbio_gpio.OUT, 265 | IN: bbio_gpio.IN } 266 | self._pud_mapping = { PUD_OFF: bbio_gpio.PUD_OFF, 267 | PUD_DOWN: bbio_gpio.PUD_DOWN, 268 | PUD_UP: bbio_gpio.PUD_UP } 269 | self._edge_mapping = { RISING: bbio_gpio.RISING, 270 | FALLING: bbio_gpio.FALLING, 271 | BOTH: bbio_gpio.BOTH } 272 | 273 | def setup(self, pin, mode, pull_up_down=PUD_OFF): 274 | """Set the input or output mode for a specified pin. Mode should be 275 | either OUTPUT or INPUT. 276 | """ 277 | self.bbio_gpio.setup(pin, self._dir_mapping[mode], 278 | pull_up_down=self._pud_mapping[pull_up_down]) 279 | 280 | def output(self, pin, value): 281 | """Set the specified pin the provided high/low value. Value should be 282 | either HIGH/LOW or a boolean (true = high). 283 | """ 284 | self.bbio_gpio.output(pin, value) 285 | 286 | def input(self, pin): 287 | """Read the specified pin and return HIGH/true if the pin is pulled high, 288 | or LOW/false if pulled low. 289 | """ 290 | return self.bbio_gpio.input(pin) 291 | 292 | def input_pins(self, pins): 293 | """Read multiple pins specified in the given list and return list of pin values 294 | GPIO.HIGH/True if the pin is pulled high, or GPIO.LOW/False if pulled low. 295 | """ 296 | # maybe bbb has a mass read... it would be more efficient to use it if it exists 297 | return [self.bbio_gpio.input(pin) for pin in pins] 298 | 299 | def add_event_detect(self, pin, edge, callback=None, bouncetime=-1): 300 | """Enable edge detection events for a particular GPIO channel. Pin 301 | should be type IN. Edge must be RISING, FALLING or BOTH. Callback is a 302 | function for the event. Bouncetime is switch bounce timeout in ms for 303 | callback 304 | """ 305 | kwargs = {} 306 | if callback: 307 | kwargs['callback']=callback 308 | if bouncetime > 0: 309 | kwargs['bouncetime']=bouncetime 310 | self.bbio_gpio.add_event_detect(pin, self._edge_mapping[edge], **kwargs) 311 | 312 | def remove_event_detect(self, pin): 313 | """Remove edge detection for a particular GPIO channel. Pin should be 314 | type IN. 315 | """ 316 | self.bbio_gpio.remove_event_detect(pin) 317 | 318 | def add_event_callback(self, pin, callback, bouncetime=-1): 319 | """Add a callback for an event already defined using add_event_detect(). 320 | Pin should be type IN. Bouncetime is switch bounce timeout in ms for 321 | callback 322 | """ 323 | kwargs = {} 324 | if bouncetime > 0: 325 | kwargs['bouncetime']=bouncetime 326 | self.bbio_gpio.add_event_callback(pin, callback, **kwargs) 327 | 328 | def event_detected(self, pin): 329 | """Returns True if an edge has occured on a given GPIO. You need to 330 | enable edge detection using add_event_detect() first. Pin should be 331 | type IN. 332 | """ 333 | return self.bbio_gpio.event_detected(pin) 334 | 335 | def wait_for_edge(self, pin, edge): 336 | """Wait for an edge. Pin should be type IN. Edge must be RISING, 337 | FALLING or BOTH. 338 | """ 339 | self.bbio_gpio.wait_for_edge(pin, self._edge_mapping[edge]) 340 | 341 | def cleanup(self, pin=None): 342 | """Clean up GPIO event detection for specific pin, or all pins if none 343 | is specified. 344 | """ 345 | if pin is None: 346 | self.bbio_gpio.cleanup() 347 | else: 348 | self.bbio_gpio.cleanup(pin) 349 | 350 | class AdafruitMinnowAdapter(BaseGPIO): 351 | """GPIO implementation for the Minnowboard + MAX using the mraa library""" 352 | 353 | def __init__(self,mraa_gpio): 354 | self.mraa_gpio = mraa_gpio 355 | # Define mapping of Adafruit GPIO library constants to mraa constants 356 | self._dir_mapping = { OUT: self.mraa_gpio.DIR_OUT, 357 | IN: self.mraa_gpio.DIR_IN } 358 | self._pud_mapping = { PUD_OFF: self.mraa_gpio.MODE_STRONG, 359 | PUD_UP: self.mraa_gpio.MODE_HIZ, 360 | PUD_DOWN: self.mraa_gpio.MODE_PULLDOWN } 361 | self._edge_mapping = { RISING: self.mraa_gpio.EDGE_RISING, 362 | FALLING: self.mraa_gpio.EDGE_FALLING, 363 | BOTH: self.mraa_gpio.EDGE_BOTH } 364 | 365 | def setup(self,pin,mode): 366 | """Set the input or output mode for a specified pin. Mode should be 367 | either DIR_IN or DIR_OUT. 368 | """ 369 | self.mraa_gpio.Gpio.dir(self.mraa_gpio.Gpio(pin),self._dir_mapping[mode]) 370 | 371 | def output(self,pin,value): 372 | """Set the specified pin the provided high/low value. Value should be 373 | either 1 (ON or HIGH), or 0 (OFF or LOW) or a boolean. 374 | """ 375 | self.mraa_gpio.Gpio.write(self.mraa_gpio.Gpio(pin), value) 376 | 377 | def input(self,pin): 378 | """Read the specified pin and return HIGH/true if the pin is pulled high, 379 | or LOW/false if pulled low. 380 | """ 381 | return self.mraa_gpio.Gpio.read(self.mraa_gpio.Gpio(pin)) 382 | 383 | def add_event_detect(self, pin, edge, callback=None, bouncetime=-1): 384 | """Enable edge detection events for a particular GPIO channel. Pin 385 | should be type IN. Edge must be RISING, FALLING or BOTH. Callback is a 386 | function for the event. Bouncetime is switch bounce timeout in ms for 387 | callback 388 | """ 389 | kwargs = {} 390 | if callback: 391 | kwargs['callback']=callback 392 | if bouncetime > 0: 393 | kwargs['bouncetime']=bouncetime 394 | self.mraa_gpio.Gpio.isr(self.mraa_gpio.Gpio(pin), self._edge_mapping[edge], **kwargs) 395 | 396 | def remove_event_detect(self, pin): 397 | """Remove edge detection for a particular GPIO channel. Pin should be 398 | type IN. 399 | """ 400 | self.mraa_gpio.Gpio.isrExit(self.mraa_gpio.Gpio(pin)) 401 | 402 | def wait_for_edge(self, pin, edge): 403 | """Wait for an edge. Pin should be type IN. Edge must be RISING, 404 | FALLING or BOTH. 405 | """ 406 | self.bbio_gpio.wait_for_edge(self.mraa_gpio.Gpio(pin), self._edge_mapping[edge]) 407 | 408 | def get_platform_gpio(**keywords): 409 | """Attempt to return a GPIO instance for the platform which the code is being 410 | executed on. Currently supports only the Raspberry Pi using the RPi.GPIO 411 | library and Beaglebone Black using the Adafruit_BBIO library. Will throw an 412 | exception if a GPIO instance can't be created for the current platform. The 413 | returned GPIO object is an instance of BaseGPIO. 414 | """ 415 | plat = Platform.platform_detect() 416 | if plat == Platform.RASPBERRY_PI: 417 | import RPi.GPIO 418 | return RPiGPIOAdapter(RPi.GPIO, **keywords) 419 | elif plat == Platform.BEAGLEBONE_BLACK: 420 | import Adafruit_BBIO.GPIO 421 | return AdafruitBBIOAdapter(Adafruit_BBIO.GPIO, **keywords) 422 | elif plat == Platform.MINNOWBOARD: 423 | import mraa 424 | return AdafruitMinnowAdapter(mraa, **keywords) 425 | elif plat == Platform.JETSON_NANO: 426 | import Jetson.GPIO 427 | return RPiGPIOAdapter(Jetson.GPIO, **keywords) 428 | elif plat == Platform.UNKNOWN: 429 | raise RuntimeError('Could not determine platform.') 430 | -------------------------------------------------------------------------------- /Adafruit_GPIO/I2C.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2014 Adafruit Industries 2 | # Author: Tony DiCola 3 | # Based on Adafruit_I2C.py created by Kevin Townsend. 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in 13 | # all copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | # THE SOFTWARE. 22 | import logging 23 | import os 24 | import subprocess 25 | 26 | import Adafruit_GPIO.Platform as Platform 27 | 28 | 29 | def reverseByteOrder(data): 30 | """DEPRECATED: See https://github.com/adafruit/Adafruit_Python_GPIO/issues/48""" 31 | # # Courtesy Vishal Sapre 32 | # byteCount = len(hex(data)[2:].replace('L','')[::2]) 33 | # val = 0 34 | # for i in range(byteCount): 35 | # val = (val << 8) | (data & 0xff) 36 | # data >>= 8 37 | # return val 38 | raise RuntimeError('reverseByteOrder is deprecated! See: https://github.com/adafruit/Adafruit_Python_GPIO/issues/48') 39 | 40 | def get_default_bus(): 41 | """Return the default bus number based on the device platform. For a 42 | Raspberry Pi either bus 0 or 1 (based on the Pi revision) will be returned. 43 | For a Beaglebone Black the first user accessible bus, 1, will be returned. 44 | """ 45 | plat = Platform.platform_detect() 46 | if plat == Platform.RASPBERRY_PI: 47 | if Platform.pi_revision() == 1: 48 | # Revision 1 Pi uses I2C bus 0. 49 | return 0 50 | else: 51 | # Revision 2 Pi uses I2C bus 1. 52 | return 1 53 | elif plat == Platform.BEAGLEBONE_BLACK: 54 | # Beaglebone Black has multiple I2C buses, default to 1 (P9_19 and P9_20). 55 | return 1 56 | else: 57 | raise RuntimeError('Could not determine default I2C bus for platform.') 58 | 59 | def get_i2c_device(address, busnum=None, i2c_interface=None, **kwargs): 60 | """Return an I2C device for the specified address and on the specified bus. 61 | If busnum isn't specified, the default I2C bus for the platform will attempt 62 | to be detected. 63 | """ 64 | if busnum is None: 65 | busnum = get_default_bus() 66 | return Device(address, busnum, i2c_interface, **kwargs) 67 | 68 | def require_repeated_start(): 69 | """Enable repeated start conditions for I2C register reads. This is the 70 | normal behavior for I2C, however on some platforms like the Raspberry Pi 71 | there are bugs which disable repeated starts unless explicitly enabled with 72 | this function. See this thread for more details: 73 | http://www.raspberrypi.org/forums/viewtopic.php?f=44&t=15840 74 | """ 75 | plat = Platform.platform_detect() 76 | if plat == Platform.RASPBERRY_PI and os.path.exists('/sys/module/i2c_bcm2708/parameters/combined'): 77 | # On the Raspberry Pi there is a bug where register reads don't send a 78 | # repeated start condition like the kernel smbus I2C driver functions 79 | # define. As a workaround this bit in the BCM2708 driver sysfs tree can 80 | # be changed to enable I2C repeated starts. 81 | subprocess.check_call('chmod 666 /sys/module/i2c_bcm2708/parameters/combined', shell=True) 82 | subprocess.check_call('echo -n 1 > /sys/module/i2c_bcm2708/parameters/combined', shell=True) 83 | # Other platforms are a no-op because they (presumably) have the correct 84 | # behavior and send repeated starts. 85 | 86 | 87 | class Device(object): 88 | """Class for communicating with an I2C device using the adafruit-pureio pure 89 | python smbus library, or other smbus compatible I2C interface. Allows reading 90 | and writing 8-bit, 16-bit, and byte array values to registers 91 | on the device.""" 92 | def __init__(self, address, busnum, i2c_interface=None): 93 | """Create an instance of the I2C device at the specified address on the 94 | specified I2C bus number.""" 95 | self._address = address 96 | if i2c_interface is None: 97 | # Use pure python I2C interface if none is specified. 98 | import Adafruit_PureIO.smbus 99 | self._bus = Adafruit_PureIO.smbus.SMBus(busnum) 100 | else: 101 | # Otherwise use the provided class to create an smbus interface. 102 | self._bus = i2c_interface(busnum) 103 | self._logger = logging.getLogger('Adafruit_I2C.Device.Bus.{0}.Address.{1:#0X}' \ 104 | .format(busnum, address)) 105 | 106 | def writeRaw8(self, value): 107 | """Write an 8-bit value on the bus (without register).""" 108 | value = value & 0xFF 109 | self._bus.write_byte(self._address, value) 110 | self._logger.debug("Wrote 0x%02X", 111 | value) 112 | 113 | def write8(self, register, value): 114 | """Write an 8-bit value to the specified register.""" 115 | value = value & 0xFF 116 | self._bus.write_byte_data(self._address, register, value) 117 | self._logger.debug("Wrote 0x%02X to register 0x%02X", 118 | value, register) 119 | 120 | def write16(self, register, value): 121 | """Write a 16-bit value to the specified register.""" 122 | value = value & 0xFFFF 123 | self._bus.write_word_data(self._address, register, value) 124 | self._logger.debug("Wrote 0x%04X to register pair 0x%02X, 0x%02X", 125 | value, register, register+1) 126 | 127 | def writeList(self, register, data): 128 | """Write bytes to the specified register.""" 129 | self._bus.write_i2c_block_data(self._address, register, data) 130 | self._logger.debug("Wrote to register 0x%02X: %s", 131 | register, data) 132 | 133 | def readList(self, register, length): 134 | """Read a length number of bytes from the specified register. Results 135 | will be returned as a bytearray.""" 136 | results = self._bus.read_i2c_block_data(self._address, register, length) 137 | self._logger.debug("Read the following from register 0x%02X: %s", 138 | register, results) 139 | return results 140 | 141 | def readRaw8(self): 142 | """Read an 8-bit value on the bus (without register).""" 143 | result = self._bus.read_byte(self._address) & 0xFF 144 | self._logger.debug("Read 0x%02X", 145 | result) 146 | return result 147 | 148 | def readU8(self, register): 149 | """Read an unsigned byte from the specified register.""" 150 | result = self._bus.read_byte_data(self._address, register) & 0xFF 151 | self._logger.debug("Read 0x%02X from register 0x%02X", 152 | result, register) 153 | return result 154 | 155 | def readS8(self, register): 156 | """Read a signed byte from the specified register.""" 157 | result = self.readU8(register) 158 | if result > 127: 159 | result -= 256 160 | return result 161 | 162 | def readU16(self, register, little_endian=True): 163 | """Read an unsigned 16-bit value from the specified register, with the 164 | specified endianness (default little endian, or least significant byte 165 | first).""" 166 | result = self._bus.read_word_data(self._address,register) & 0xFFFF 167 | self._logger.debug("Read 0x%04X from register pair 0x%02X, 0x%02X", 168 | result, register, register+1) 169 | # Swap bytes if using big endian because read_word_data assumes little 170 | # endian on ARM (little endian) systems. 171 | if not little_endian: 172 | result = ((result << 8) & 0xFF00) + (result >> 8) 173 | return result 174 | 175 | def readS16(self, register, little_endian=True): 176 | """Read a signed 16-bit value from the specified register, with the 177 | specified endianness (default little endian, or least significant byte 178 | first).""" 179 | result = self.readU16(register, little_endian) 180 | if result > 32767: 181 | result -= 65536 182 | return result 183 | 184 | def readU16LE(self, register): 185 | """Read an unsigned 16-bit value from the specified register, in little 186 | endian byte order.""" 187 | return self.readU16(register, little_endian=True) 188 | 189 | def readU16BE(self, register): 190 | """Read an unsigned 16-bit value from the specified register, in big 191 | endian byte order.""" 192 | return self.readU16(register, little_endian=False) 193 | 194 | def readS16LE(self, register): 195 | """Read a signed 16-bit value from the specified register, in little 196 | endian byte order.""" 197 | return self.readS16(register, little_endian=True) 198 | 199 | def readS16BE(self, register): 200 | """Read a signed 16-bit value from the specified register, in big 201 | endian byte order.""" 202 | return self.readS16(register, little_endian=False) 203 | -------------------------------------------------------------------------------- /Adafruit_GPIO/MCP230xx.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2014 Adafruit Industries 2 | # Author: Tony DiCola 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy 5 | # of this software and associated documentation files (the "Software"), to deal 6 | # in the Software without restriction, including without limitation the rights 7 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | # copies of the Software, and to permit persons to whom the Software is 9 | # furnished to do so, subject to the following conditions: 10 | # 11 | # The above copyright notice and this permission notice shall be included in 12 | # all copies or substantial portions of the Software. 13 | # 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | # THE SOFTWARE. 21 | import math 22 | 23 | import Adafruit_GPIO as GPIO 24 | import Adafruit_GPIO.I2C as I2C 25 | 26 | 27 | class MCP230xxBase(GPIO.BaseGPIO): 28 | """Base class to represent an MCP230xx series GPIO extender. Is compatible 29 | with the Adafruit_GPIO BaseGPIO class so it can be used as a custom GPIO 30 | class for interacting with device. 31 | """ 32 | 33 | def __init__(self, address, i2c=None, **kwargs): 34 | """Initialize MCP230xx at specified I2C address and bus number. If bus 35 | is not specified it will default to the appropriate platform detected bus. 36 | """ 37 | # Create I2C device. 38 | if i2c is None: 39 | import Adafruit_GPIO.I2C as I2C 40 | i2c = I2C 41 | self._device = i2c.get_i2c_device(address, **kwargs) 42 | # Assume starting in ICON.BANK = 0 mode (sequential access). 43 | # Compute how many bytes are needed to store count of GPIO. 44 | self.gpio_bytes = int(math.ceil(self.NUM_GPIO/8.0)) 45 | # Buffer register values so they can be changed without reading. 46 | self.iodir = [0xFF]*self.gpio_bytes # Default direction to all inputs. 47 | self.gppu = [0x00]*self.gpio_bytes # Default to pullups disabled. 48 | self.gpio = [0x00]*self.gpio_bytes 49 | # Write current direction and pullup buffer state. 50 | self.write_iodir() 51 | self.write_gppu() 52 | 53 | 54 | def setup(self, pin, value): 55 | """Set the input or output mode for a specified pin. Mode should be 56 | either GPIO.OUT or GPIO.IN. 57 | """ 58 | self._validate_pin(pin) 59 | # Set bit to 1 for input or 0 for output. 60 | if value == GPIO.IN: 61 | self.iodir[int(pin/8)] |= 1 << (int(pin%8)) 62 | elif value == GPIO.OUT: 63 | self.iodir[int(pin/8)] &= ~(1 << (int(pin%8))) 64 | else: 65 | raise ValueError('Unexpected value. Must be GPIO.IN or GPIO.OUT.') 66 | self.write_iodir() 67 | 68 | 69 | def output(self, pin, value): 70 | """Set the specified pin the provided high/low value. Value should be 71 | either GPIO.HIGH/GPIO.LOW or a boolean (True = HIGH). 72 | """ 73 | self.output_pins({pin: value}) 74 | 75 | def output_pins(self, pins): 76 | """Set multiple pins high or low at once. Pins should be a dict of pin 77 | name to pin value (HIGH/True for 1, LOW/False for 0). All provided pins 78 | will be set to the given values. 79 | """ 80 | [self._validate_pin(pin) for pin in pins.keys()] 81 | # Set each changed pin's bit. 82 | for pin, value in iter(pins.items()): 83 | if value: 84 | self.gpio[int(pin/8)] |= 1 << (int(pin%8)) 85 | else: 86 | self.gpio[int(pin/8)] &= ~(1 << (int(pin%8))) 87 | # Write GPIO state. 88 | self.write_gpio() 89 | 90 | 91 | def input(self, pin): 92 | """Read the specified pin and return GPIO.HIGH/True if the pin is pulled 93 | high, or GPIO.LOW/False if pulled low. 94 | """ 95 | return self.input_pins([pin])[0] 96 | 97 | def input_pins(self, pins): 98 | """Read multiple pins specified in the given list and return list of pin values 99 | GPIO.HIGH/True if the pin is pulled high, or GPIO.LOW/False if pulled low. 100 | """ 101 | [self._validate_pin(pin) for pin in pins] 102 | # Get GPIO state. 103 | self.gpio = self._device.readList(self.GPIO, self.gpio_bytes) 104 | # Return True if pin's bit is set. 105 | return [(self.gpio[int(pin/8)] & 1 << (int(pin%8))) > 0 for pin in pins] 106 | 107 | 108 | def pullup(self, pin, enabled): 109 | """Turn on the pull-up resistor for the specified pin if enabled is True, 110 | otherwise turn off the pull-up resistor. 111 | """ 112 | self._validate_pin(pin) 113 | if enabled: 114 | self.gppu[int(pin/8)] |= 1 << (int(pin%8)) 115 | else: 116 | self.gppu[int(pin/8)] &= ~(1 << (int(pin%8))) 117 | self.write_gppu() 118 | 119 | def write_gpio(self, gpio=None): 120 | """Write the specified byte value to the GPIO registor. If no value 121 | specified the current buffered value will be written. 122 | """ 123 | if gpio is not None: 124 | self.gpio = gpio 125 | self._device.writeList(self.GPIO, self.gpio) 126 | 127 | def write_iodir(self, iodir=None): 128 | """Write the specified byte value to the IODIR registor. If no value 129 | specified the current buffered value will be written. 130 | """ 131 | if iodir is not None: 132 | self.iodir = iodir 133 | self._device.writeList(self.IODIR, self.iodir) 134 | 135 | def write_gppu(self, gppu=None): 136 | """Write the specified byte value to the GPPU registor. If no value 137 | specified the current buffered value will be written. 138 | """ 139 | if gppu is not None: 140 | self.gppu = gppu 141 | self._device.writeList(self.GPPU, self.gppu) 142 | 143 | 144 | class MCP23017(MCP230xxBase): 145 | """MCP23017-based GPIO class with 16 GPIO pins.""" 146 | # Define number of pins and registor addresses. 147 | NUM_GPIO = 16 148 | IODIR = 0x00 149 | GPIO = 0x12 150 | GPPU = 0x0C 151 | 152 | def __init__(self, address=0x20, **kwargs): 153 | super(MCP23017, self).__init__(address, **kwargs) 154 | 155 | 156 | class MCP23008(MCP230xxBase): 157 | """MCP23008-based GPIO class with 8 GPIO pins.""" 158 | # Define number of pins and registor addresses. 159 | NUM_GPIO = 8 160 | IODIR = 0x00 161 | GPIO = 0x09 162 | GPPU = 0x06 163 | 164 | def __init__(self, address=0x20, **kwargs): 165 | super(MCP23008, self).__init__(address, **kwargs) 166 | -------------------------------------------------------------------------------- /Adafruit_GPIO/PCA95xx.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Adafruit compatible using BaseGPIO class to represent a PCA9555 IO expander 3 | Copyright (C) 2016 Matias Vidal 4 | Ported from: https://github.com/dberlin/PCA95XX 5 | 6 | # Copyright 2012 Daniel Berlin 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy of 9 | this software and associated documentation files (the "Software"), to deal in 10 | the Software without restriction, including without limitation the rights to 11 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 12 | of the Software, and to permit persons to whom the Software is furnished to do 13 | so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE.''' 25 | 26 | import Adafruit_GPIO as GPIO 27 | import Adafruit_GPIO.I2C as I2C 28 | 29 | # For the PCA 953X and 955X series, the chips with 8 GPIO's have these port numbers 30 | # The chips with 16 GPIO's have the first port for each type at double these numbers 31 | # IE The first config port is 6 32 | 33 | INPUT_PORT = 0 34 | OUTPUT_PORT = 1 35 | POLARITY_PORT = 2 36 | CONFIG_PORT = 3 37 | 38 | IN = GPIO.IN 39 | OUT = GPIO.OUT 40 | HIGH = GPIO.HIGH 41 | LOW = GPIO.LOW 42 | 43 | 44 | class PCA9555(GPIO.BaseGPIO): 45 | """Class to represent a PCA9555 GPIO extender. Compatible 46 | with the Adafruit_GPIO BaseGPIO class so it can be used as a custom GPIO 47 | class for interacting with device. 48 | """ 49 | NUM_GPIO = 16 50 | 51 | def __init__(self, address=0x20, busnum=None, i2c=None, num_gpios=16, **kwargs): 52 | address = int(address) 53 | self.__name__ = "PCA955" 54 | # Create I2C device. 55 | i2c = i2c or I2C 56 | busnum = busnum or i2c.get_default_bus() 57 | self._device = i2c.get_i2c_device(address, busnum, **kwargs) 58 | self.num_gpios = num_gpios 59 | 60 | if self.num_gpios <= 8: 61 | self.iodir = self._device.readU8(CONFIG_PORT) 62 | self.outputvalue = self._device.readU8(OUTPUT_PORT) 63 | 64 | elif self.num_gpios > 8 and self.num_gpios <= 16: 65 | self.iodir = self._device.readU16(CONFIG_PORT<< 1) 66 | self.outputvalue = self._device.readU16(OUTPUT_PORT << 1) 67 | 68 | def _changebit(self, bitmap, bit, value): 69 | assert value == 1 or value == 0, "Value is %s must be 1 or 0" % value 70 | if value == 0: 71 | return bitmap & ~(1 << bit) 72 | elif value == 1: 73 | return bitmap | (1 << bit) 74 | 75 | # Change the value of bit PIN on port PORT to VALUE. If the 76 | # current pin state for the port is passed in as PORTSTATE, we 77 | # will avoid doing a read to get it. The port pin state must be 78 | # complete if passed in (IE it should not just be the value of the 79 | # single pin we are trying to change) 80 | def _readandchangepin(self, port, pin, value, portstate = None): 81 | assert pin >= 0 and pin < self.num_gpios, "Pin number %s is invalid, only 0-%s are valid" % (pin, self.num_gpios) 82 | if not portstate: 83 | if self.num_gpios <= 8: 84 | portstate = self._device.readU8(port) 85 | elif self.num_gpios > 8 and self.num_gpios <= 16: 86 | portstate = self._device.readU16(port << 1) 87 | newstate = self._changebit(portstate, pin, value) 88 | if self.num_gpios <= 8: 89 | self._device.write8(port, newstate) 90 | else: 91 | self._device.write16(port << 1, newstate) 92 | return newstate 93 | 94 | # Polarity inversion 95 | def polarity(self, pin, value): 96 | return self._readandchangepin(POLARITY_PORT, pin, value) 97 | 98 | # Pin direction 99 | def config(self, pin, mode): 100 | self.iodir = self._readandchangepin(CONFIG_PORT, pin, mode, self.iodir) 101 | return self.iodir 102 | 103 | def output(self, pin, value): 104 | assert self.iodir & (1 << pin) == 0, "Pin %s not set to output" % pin 105 | self.outputvalue = self._readandchangepin(OUTPUT_PORT, pin, value, self.outputvalue) 106 | return self.outputvalue 107 | 108 | def input(self, pin): 109 | assert self.iodir & (1 << pin) != 0, "Pin %s not set to input" % pin 110 | if self.num_gpios <= 8: 111 | value = self._device.readU8(INPUT_PORT) 112 | elif self.num_gpios > 8 and self.num_gpios <= 16: 113 | value = self._device.readU16(INPUT_PORT << 1) 114 | return value & (1 << pin) 115 | 116 | def setup(self, pin, mode): 117 | self.config(pin, mode) 118 | 119 | def cleanup(self, pin=None): 120 | # nothing to cleanup 121 | pass 122 | -------------------------------------------------------------------------------- /Adafruit_GPIO/PCF8574.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Adafruit compatible using BaseGPIO class to represent a PCF8574/A IO expander 3 | Copyright (C) 2015 Sylvan Butler 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | of the Software, and to permit persons to whom the Software is furnished to do 10 | so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE.''' 22 | 23 | import Adafruit_GPIO as GPIO 24 | import Adafruit_GPIO.I2C as I2C 25 | 26 | 27 | 28 | IN = GPIO.IN 29 | OUT = GPIO.OUT 30 | HIGH = GPIO.HIGH 31 | LOW = GPIO.LOW 32 | 33 | 34 | class PCF8574(GPIO.BaseGPIO): 35 | """Class to represent a PCF8574 or PCF8574A GPIO extender. Compatible 36 | with the Adafruit_GPIO BaseGPIO class so it can be used as a custom GPIO 37 | class for interacting with device. 38 | """ 39 | 40 | NUM_GPIO = 8 41 | 42 | def __init__(self, address=0x27, busnum=None, i2c=None, **kwargs): 43 | address = int(address) 44 | self.__name__ = \ 45 | "PCF8574" if address in range(0x20, 0x28) else \ 46 | "PCF8574A" if address in range(0x38, 0x40) else \ 47 | "Bad address for PCF8574(A): 0x%02X not in range [0x20..0x27, 0x38..0x3F]" % address 48 | if self.__name__[0] != 'P': 49 | raise ValueError(self.__name__) 50 | # Create I2C device. 51 | i2c = i2c or I2C 52 | busnum = busnum or i2c.get_default_bus() 53 | self._device = i2c.get_i2c_device(address, busnum, **kwargs) 54 | # Buffer register values so they can be changed without reading. 55 | self.iodir = 0xFF # Default direction to all inputs is in 56 | self.gpio = 0x00 57 | self._write_pins() 58 | 59 | 60 | def _write_pins(self): 61 | self._device.writeRaw8(self.gpio | self.iodir) 62 | 63 | def _read_pins(self): 64 | return self._device.readRaw8() & self.iodir 65 | 66 | 67 | def setup(self, pin, mode): 68 | self.setup_pins({pin: mode}) 69 | 70 | def setup_pins(self, pins): 71 | if False in [y for x,y in [(self._validate_pin(pin),mode in (IN,OUT)) for pin,mode in pins.items()]]: 72 | raise ValueError('Invalid MODE, IN or OUT') 73 | for pin,mode in pins.items(): 74 | self.iodir = self._bit2(self.iodir, pin, mode) 75 | self._write_pins() 76 | 77 | 78 | def output(self, pin, value): 79 | self.output_pins({pin: value}) 80 | 81 | def output_pins(self, pins): 82 | [self._validate_pin(pin) for pin in pins.keys()] 83 | for pin,value in pins.items(): 84 | self.gpio = self._bit2(self.gpio, pin, bool(value)) 85 | self._write_pins() 86 | 87 | 88 | def input(self, pin): 89 | return self.input_pins([pin])[0] 90 | 91 | def input_pins(self, pins): 92 | [self._validate_pin(pin) for pin in pins] 93 | inp = self._read_pins() 94 | return [bool(inp & (1< 100.0: 47 | raise ValueError('Invalid duty cycle value, must be between 0.0 to 100.0 (inclusive).') 48 | # Make pin an output. 49 | self.rpi_gpio.setup(pin, self.rpi_gpio.OUT) 50 | # Create PWM instance and save a reference for later access. 51 | self.pwm[pin] = self.rpi_gpio.PWM(pin, frequency_hz) 52 | # Start the PWM at the specified duty cycle. 53 | self.pwm[pin].start(dutycycle) 54 | 55 | def set_duty_cycle(self, pin, dutycycle): 56 | """Set percent duty cycle of PWM output on specified pin. Duty cycle must 57 | be a value 0.0 to 100.0 (inclusive). 58 | """ 59 | if dutycycle < 0.0 or dutycycle > 100.0: 60 | raise ValueError('Invalid duty cycle value, must be between 0.0 to 100.0 (inclusive).') 61 | if pin not in self.pwm: 62 | raise ValueError('Pin {0} is not configured as a PWM. Make sure to first call start for the pin.'.format(pin)) 63 | self.pwm[pin].ChangeDutyCycle(dutycycle) 64 | 65 | def set_frequency(self, pin, frequency_hz): 66 | """Set frequency (in Hz) of PWM output on specified pin.""" 67 | if pin not in self.pwm: 68 | raise ValueError('Pin {0} is not configured as a PWM. Make sure to first call start for the pin.'.format(pin)) 69 | self.pwm[pin].ChangeFrequency(frequency_hz) 70 | 71 | def stop(self, pin): 72 | """Stop PWM output on specified pin.""" 73 | if pin not in self.pwm: 74 | raise ValueError('Pin {0} is not configured as a PWM. Make sure to first call start for the pin.'.format(pin)) 75 | self.pwm[pin].stop() 76 | del self.pwm[pin] 77 | 78 | 79 | class BBIO_PWM_Adapter(object): 80 | """PWM implementation for the BeagleBone Black using the Adafruit_BBIO.PWM 81 | library. 82 | """ 83 | 84 | def __init__(self, bbio_pwm): 85 | self.bbio_pwm = bbio_pwm 86 | 87 | def start(self, pin, dutycycle, frequency_hz=2000): 88 | """Enable PWM output on specified pin. Set to intiial percent duty cycle 89 | value (0.0 to 100.0) and frequency (in Hz). 90 | """ 91 | if dutycycle < 0.0 or dutycycle > 100.0: 92 | raise ValueError('Invalid duty cycle value, must be between 0.0 to 100.0 (inclusive).') 93 | self.bbio_pwm.start(pin, dutycycle, frequency_hz) 94 | 95 | def set_duty_cycle(self, pin, dutycycle): 96 | """Set percent duty cycle of PWM output on specified pin. Duty cycle must 97 | be a value 0.0 to 100.0 (inclusive). 98 | """ 99 | if dutycycle < 0.0 or dutycycle > 100.0: 100 | raise ValueError('Invalid duty cycle value, must be between 0.0 to 100.0 (inclusive).') 101 | self.bbio_pwm.set_duty_cycle(pin, dutycycle) 102 | 103 | def set_frequency(self, pin, frequency_hz): 104 | """Set frequency (in Hz) of PWM output on specified pin.""" 105 | self.bbio_pwm.set_frequency(pin, frequency_hz) 106 | 107 | def stop(self, pin): 108 | """Stop PWM output on specified pin.""" 109 | self.bbio_pwm.stop(pin) 110 | 111 | 112 | def get_platform_pwm(**keywords): 113 | """Attempt to return a PWM instance for the platform which the code is being 114 | executed on. Currently supports only the Raspberry Pi using the RPi.GPIO 115 | library and Beaglebone Black using the Adafruit_BBIO library. Will throw an 116 | exception if a PWM instance can't be created for the current platform. The 117 | returned PWM object has the same interface as the RPi_PWM_Adapter and 118 | BBIO_PWM_Adapter classes. 119 | """ 120 | plat = Platform.platform_detect() 121 | if plat == Platform.RASPBERRY_PI: 122 | import RPi.GPIO 123 | return RPi_PWM_Adapter(RPi.GPIO, **keywords) 124 | elif plat == Platform.BEAGLEBONE_BLACK: 125 | import Adafruit_BBIO.PWM 126 | return BBIO_PWM_Adapter(Adafruit_BBIO.PWM, **keywords) 127 | elif plat == Platform.UNKNOWN: 128 | raise RuntimeError('Could not determine platform.') 129 | -------------------------------------------------------------------------------- /Adafruit_GPIO/Platform.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2014 Adafruit Industries 2 | # Author: Tony DiCola 3 | 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy 5 | # of this software and associated documentation files (the "Software"), to deal 6 | # in the Software without restriction, including without limitation the rights 7 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | # copies of the Software, and to permit persons to whom the Software is 9 | # furnished to do so, subject to the following conditions: 10 | 11 | # The above copyright notice and this permission notice shall be included in all 12 | # copies or substantial portions of the Software. 13 | 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | # SOFTWARE. 21 | import platform 22 | import re 23 | 24 | # Platform identification constants. 25 | UNKNOWN = 0 26 | RASPBERRY_PI = 1 27 | BEAGLEBONE_BLACK = 2 28 | MINNOWBOARD = 3 29 | JETSON_NANO = 4 30 | 31 | def platform_detect(): 32 | """Detect if running on the Raspberry Pi or Beaglebone Black and return the 33 | platform type. Will return RASPBERRY_PI, BEAGLEBONE_BLACK, or UNKNOWN.""" 34 | # Handle Raspberry Pi 35 | pi = pi_version() 36 | if pi is not None: 37 | return RASPBERRY_PI 38 | 39 | # Handle Beaglebone Black 40 | # TODO: Check the Beaglebone Black /proc/cpuinfo value instead of reading 41 | # the platform. 42 | plat = platform.platform() 43 | if plat.lower().find('armv7l-with-debian') > -1: 44 | return BEAGLEBONE_BLACK 45 | elif plat.lower().find('armv7l-with-ubuntu') > -1: 46 | return BEAGLEBONE_BLACK 47 | elif plat.lower().find('armv7l-with-glibc2.4') > -1: 48 | return BEAGLEBONE_BLACK 49 | elif plat.lower().find('tegra-aarch64-with-ubuntu') > -1: 50 | return JETSON_NANO 51 | 52 | # Handle Minnowboard 53 | # Assumption is that mraa is installed 54 | try: 55 | import mraa 56 | if mraa.getPlatformName()=='MinnowBoard MAX': 57 | return MINNOWBOARD 58 | except ImportError: 59 | pass 60 | 61 | # Couldn't figure out the platform, just return unknown. 62 | return UNKNOWN 63 | 64 | 65 | def pi_revision(): 66 | """Detect the revision number of a Raspberry Pi, useful for changing 67 | functionality like default I2C bus based on revision.""" 68 | # Revision list available at: http://elinux.org/RPi_HardwareHistory#Board_Revision_History 69 | with open('/proc/cpuinfo', 'r') as infile: 70 | for line in infile: 71 | # Match a line of the form "Revision : 0002" while ignoring extra 72 | # info in front of the revsion (like 1000 when the Pi was over-volted). 73 | match = re.match('Revision\s+:\s+.*(\w{4})$', line, flags=re.IGNORECASE) 74 | if match and match.group(1) in ['0000', '0002', '0003']: 75 | # Return revision 1 if revision ends with 0000, 0002 or 0003. 76 | return 1 77 | elif match: 78 | # Assume revision 2 if revision ends with any other 4 chars. 79 | return 2 80 | # Couldn't find the revision, throw an exception. 81 | raise RuntimeError('Could not determine Raspberry Pi revision.') 82 | 83 | 84 | def pi_version(): 85 | """Detect the version of the Raspberry Pi. Returns either 1, 2 or 86 | None depending on if it's a Raspberry Pi 1 (model A, B, A+, B+), 87 | Raspberry Pi 2 (model B+), or not a Raspberry Pi. 88 | """ 89 | # Check /proc/cpuinfo for the Hardware field value. 90 | # 2708 is pi 1 91 | # 2709 is pi 2 92 | # 2835 is pi 3 on 4.9.x kernel 93 | # Anything else is not a pi. 94 | with open('/proc/cpuinfo', 'r') as infile: 95 | cpuinfo = infile.read() 96 | # Match a line like 'Hardware : BCM2709' 97 | match = re.search('^Hardware\s+:\s+(\w+)$', cpuinfo, 98 | flags=re.MULTILINE | re.IGNORECASE) 99 | if not match: 100 | # Couldn't find the hardware, assume it isn't a pi. 101 | return None 102 | if match.group(1) == 'BCM2708': 103 | # Pi 1 104 | return 1 105 | elif match.group(1) == 'BCM2709': 106 | # Pi 2 107 | return 2 108 | elif match.group(1) == 'BCM2835': 109 | # Pi 3 / Pi on 4.9.x kernel 110 | return 3 111 | else: 112 | # Something else, not a pi. 113 | return None 114 | -------------------------------------------------------------------------------- /Adafruit_GPIO/SPI.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2014 Adafruit Industries 2 | # Author: Tony DiCola 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy 5 | # of this software and associated documentation files (the "Software"), to deal 6 | # in the Software without restriction, including without limitation the rights 7 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | # copies of the Software, and to permit persons to whom the Software is 9 | # furnished to do so, subject to the following conditions: 10 | # 11 | # The above copyright notice and this permission notice shall be included in 12 | # all copies or substantial portions of the Software. 13 | # 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | # THE SOFTWARE. 21 | 22 | import operator 23 | import time 24 | 25 | import Adafruit_GPIO as GPIO 26 | 27 | 28 | MSBFIRST = 0 29 | LSBFIRST = 1 30 | 31 | 32 | class SpiDev(object): 33 | """Hardware-based SPI implementation using the spidev interface.""" 34 | 35 | def __init__(self, port, device, max_speed_hz=500000): 36 | """Initialize an SPI device using the SPIdev interface. Port and device 37 | identify the device, for example the device /dev/spidev1.0 would be port 38 | 1 and device 0. 39 | """ 40 | import spidev 41 | self._device = spidev.SpiDev() 42 | self._device.open(port, device) 43 | self._device.max_speed_hz=max_speed_hz 44 | # Default to mode 0, and make sure CS is active low. 45 | self._device.mode = 0 46 | self._device.cshigh = False 47 | 48 | def set_clock_hz(self, hz): 49 | """Set the speed of the SPI clock in hertz. Note that not all speeds 50 | are supported and a lower speed might be chosen by the hardware. 51 | """ 52 | self._device.max_speed_hz=hz 53 | 54 | def set_mode(self, mode): 55 | """Set SPI mode which controls clock polarity and phase. Should be a 56 | numeric value 0, 1, 2, or 3. See wikipedia page for details on meaning: 57 | http://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus 58 | """ 59 | if mode < 0 or mode > 3: 60 | raise ValueError('Mode must be a value 0, 1, 2, or 3.') 61 | self._device.mode = mode 62 | 63 | def set_bit_order(self, order): 64 | """Set order of bits to be read/written over serial lines. Should be 65 | either MSBFIRST for most-significant first, or LSBFIRST for 66 | least-signifcant first. 67 | """ 68 | if order == MSBFIRST: 69 | self._device.lsbfirst = False 70 | elif order == LSBFIRST: 71 | self._device.lsbfirst = True 72 | else: 73 | raise ValueError('Order must be MSBFIRST or LSBFIRST.') 74 | 75 | def close(self): 76 | """Close communication with the SPI device.""" 77 | self._device.close() 78 | 79 | def write(self, data): 80 | """Half-duplex SPI write. The specified array of bytes will be clocked 81 | out the MOSI line. 82 | """ 83 | self._device.writebytes(data) 84 | 85 | def read(self, length): 86 | """Half-duplex SPI read. The specified length of bytes will be clocked 87 | in the MISO line and returned as a bytearray object. 88 | """ 89 | return bytearray(self._device.readbytes(length)) 90 | 91 | def transfer(self, data): 92 | """Full-duplex SPI read and write. The specified array of bytes will be 93 | clocked out the MOSI line, while simultaneously bytes will be read from 94 | the MISO line. Read bytes will be returned as a bytearray object. 95 | """ 96 | return bytearray(self._device.xfer2(data)) 97 | 98 | class SpiDevMraa(object): 99 | """Hardware SPI implementation with the mraa library on Minnowboard""" 100 | def __init__(self, port, device, max_speed_hz=500000): 101 | import mraa 102 | self._device = mraa.Spi(0) 103 | self._device.mode(0) 104 | 105 | def set_clock_hz(self, hz): 106 | """Set the speed of the SPI clock in hertz. Note that not all speeds 107 | are supported and a lower speed might be chosen by the hardware. 108 | """ 109 | self._device.frequency(hz) 110 | 111 | def set_mode(self,mode): 112 | """Set SPI mode which controls clock polarity and phase. Should be a 113 | numeric value 0, 1, 2, or 3. See wikipedia page for details on meaning: 114 | http://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus 115 | """ 116 | if mode < 0 or mode > 3: 117 | raise ValueError('Mode must be a value 0, 1, 2, or 3.') 118 | self._device.mode(mode) 119 | 120 | def set_bit_order(self, order): 121 | """Set order of bits to be read/written over serial lines. Should be 122 | either MSBFIRST for most-significant first, or LSBFIRST for 123 | least-signifcant first. 124 | """ 125 | if order == MSBFIRST: 126 | self._device.lsbmode(False) 127 | elif order == LSBFIRST: 128 | self._device.lsbmode(True) 129 | else: 130 | raise ValueError('Order must be MSBFIRST or LSBFIRST.') 131 | 132 | def close(self): 133 | """Close communication with the SPI device.""" 134 | self._device.Spi() 135 | 136 | def write(self, data): 137 | """Half-duplex SPI write. The specified array of bytes will be clocked 138 | out the MOSI line. 139 | """ 140 | self._device.write(bytearray(data)) 141 | 142 | class BitBang(object): 143 | """Software-based implementation of the SPI protocol over GPIO pins.""" 144 | 145 | def __init__(self, gpio, sclk, mosi=None, miso=None, ss=None): 146 | """Initialize bit bang (or software) based SPI. Must provide a BaseGPIO 147 | class, the SPI clock, and optionally MOSI, MISO, and SS (slave select) 148 | pin numbers. If MOSI is set to None then writes will be disabled and fail 149 | with an error, likewise for MISO reads will be disabled. If SS is set to 150 | None then SS will not be asserted high/low by the library when 151 | transfering data. 152 | """ 153 | self._gpio = gpio 154 | self._sclk = sclk 155 | self._mosi = mosi 156 | self._miso = miso 157 | self._ss = ss 158 | # Set pins as outputs/inputs. 159 | gpio.setup(sclk, GPIO.OUT) 160 | if mosi is not None: 161 | gpio.setup(mosi, GPIO.OUT) 162 | if miso is not None: 163 | gpio.setup(miso, GPIO.IN) 164 | if ss is not None: 165 | gpio.setup(ss, GPIO.OUT) 166 | # Assert SS high to start with device communication off. 167 | gpio.set_high(ss) 168 | # Assume mode 0. 169 | self.set_mode(0) 170 | # Assume most significant bit first order. 171 | self.set_bit_order(MSBFIRST) 172 | 173 | def set_clock_hz(self, hz): 174 | """Set the speed of the SPI clock. This is unsupported with the bit 175 | bang SPI class and will be ignored. 176 | """ 177 | pass 178 | 179 | def set_mode(self, mode): 180 | """Set SPI mode which controls clock polarity and phase. Should be a 181 | numeric value 0, 1, 2, or 3. See wikipedia page for details on meaning: 182 | http://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus 183 | """ 184 | if mode < 0 or mode > 3: 185 | raise ValueError('Mode must be a value 0, 1, 2, or 3.') 186 | if mode & 0x02: 187 | # Clock is normally high in mode 2 and 3. 188 | self._clock_base = GPIO.HIGH 189 | else: 190 | # Clock is normally low in mode 0 and 1. 191 | self._clock_base = GPIO.LOW 192 | if mode & 0x01: 193 | # Read on trailing edge in mode 1 and 3. 194 | self._read_leading = False 195 | else: 196 | # Read on leading edge in mode 0 and 2. 197 | self._read_leading = True 198 | # Put clock into its base state. 199 | self._gpio.output(self._sclk, self._clock_base) 200 | 201 | def set_bit_order(self, order): 202 | """Set order of bits to be read/written over serial lines. Should be 203 | either MSBFIRST for most-significant first, or LSBFIRST for 204 | least-signifcant first. 205 | """ 206 | # Set self._mask to the bitmask which points at the appropriate bit to 207 | # read or write, and appropriate left/right shift operator function for 208 | # reading/writing. 209 | if order == MSBFIRST: 210 | self._mask = 0x80 211 | self._write_shift = operator.lshift 212 | self._read_shift = operator.rshift 213 | elif order == LSBFIRST: 214 | self._mask = 0x01 215 | self._write_shift = operator.rshift 216 | self._read_shift = operator.lshift 217 | else: 218 | raise ValueError('Order must be MSBFIRST or LSBFIRST.') 219 | 220 | def close(self): 221 | """Close the SPI connection. Unused in the bit bang implementation.""" 222 | pass 223 | 224 | def write(self, data, assert_ss=True, deassert_ss=True): 225 | """Half-duplex SPI write. If assert_ss is True, the SS line will be 226 | asserted low, the specified bytes will be clocked out the MOSI line, and 227 | if deassert_ss is True the SS line be put back high. 228 | """ 229 | # Fail MOSI is not specified. 230 | if self._mosi is None: 231 | raise RuntimeError('Write attempted with no MOSI pin specified.') 232 | if assert_ss and self._ss is not None: 233 | self._gpio.set_low(self._ss) 234 | for byte in data: 235 | for i in range(8): 236 | # Write bit to MOSI. 237 | if self._write_shift(byte, i) & self._mask: 238 | self._gpio.set_high(self._mosi) 239 | else: 240 | self._gpio.set_low(self._mosi) 241 | # Flip clock off base. 242 | self._gpio.output(self._sclk, not self._clock_base) 243 | # Return clock to base. 244 | self._gpio.output(self._sclk, self._clock_base) 245 | if deassert_ss and self._ss is not None: 246 | self._gpio.set_high(self._ss) 247 | 248 | def read(self, length, assert_ss=True, deassert_ss=True): 249 | """Half-duplex SPI read. If assert_ss is true, the SS line will be 250 | asserted low, the specified length of bytes will be clocked in the MISO 251 | line, and if deassert_ss is true the SS line will be put back high. 252 | Bytes which are read will be returned as a bytearray object. 253 | """ 254 | if self._miso is None: 255 | raise RuntimeError('Read attempted with no MISO pin specified.') 256 | if assert_ss and self._ss is not None: 257 | self._gpio.set_low(self._ss) 258 | result = bytearray(length) 259 | for i in range(length): 260 | for j in range(8): 261 | # Flip clock off base. 262 | self._gpio.output(self._sclk, not self._clock_base) 263 | # Handle read on leading edge of clock. 264 | if self._read_leading: 265 | if self._gpio.is_high(self._miso): 266 | # Set bit to 1 at appropriate location. 267 | result[i] |= self._read_shift(self._mask, j) 268 | else: 269 | # Set bit to 0 at appropriate location. 270 | result[i] &= ~self._read_shift(self._mask, j) 271 | # Return clock to base. 272 | self._gpio.output(self._sclk, self._clock_base) 273 | # Handle read on trailing edge of clock. 274 | if not self._read_leading: 275 | if self._gpio.is_high(self._miso): 276 | # Set bit to 1 at appropriate location. 277 | result[i] |= self._read_shift(self._mask, j) 278 | else: 279 | # Set bit to 0 at appropriate location. 280 | result[i] &= ~self._read_shift(self._mask, j) 281 | if deassert_ss and self._ss is not None: 282 | self._gpio.set_high(self._ss) 283 | return result 284 | 285 | def transfer(self, data, assert_ss=True, deassert_ss=True): 286 | """Full-duplex SPI read and write. If assert_ss is true, the SS line 287 | will be asserted low, the specified bytes will be clocked out the MOSI 288 | line while bytes will also be read from the MISO line, and if 289 | deassert_ss is true the SS line will be put back high. Bytes which are 290 | read will be returned as a bytearray object. 291 | """ 292 | if self._mosi is None: 293 | raise RuntimeError('Write attempted with no MOSI pin specified.') 294 | if self._miso is None: 295 | raise RuntimeError('Read attempted with no MISO pin specified.') 296 | if assert_ss and self._ss is not None: 297 | self._gpio.set_low(self._ss) 298 | result = bytearray(len(data)) 299 | for i in range(len(data)): 300 | for j in range(8): 301 | # Write bit to MOSI. 302 | if self._write_shift(data[i], j) & self._mask: 303 | self._gpio.set_high(self._mosi) 304 | else: 305 | self._gpio.set_low(self._mosi) 306 | # Flip clock off base. 307 | self._gpio.output(self._sclk, not self._clock_base) 308 | # Handle read on leading edge of clock. 309 | if self._read_leading: 310 | if self._gpio.is_high(self._miso): 311 | # Set bit to 1 at appropriate location. 312 | result[i] |= self._read_shift(self._mask, j) 313 | else: 314 | # Set bit to 0 at appropriate location. 315 | result[i] &= ~self._read_shift(self._mask, j) 316 | # Return clock to base. 317 | self._gpio.output(self._sclk, self._clock_base) 318 | # Handle read on trailing edge of clock. 319 | if not self._read_leading: 320 | if self._gpio.is_high(self._miso): 321 | # Set bit to 1 at appropriate location. 322 | result[i] |= self._read_shift(self._mask, j) 323 | else: 324 | # Set bit to 0 at appropriate location. 325 | result[i] &= ~self._read_shift(self._mask, j) 326 | if deassert_ss and self._ss is not None: 327 | self._gpio.set_high(self._ss) 328 | return result 329 | -------------------------------------------------------------------------------- /Adafruit_GPIO/__init__.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | 3 | from Adafruit_GPIO.GPIO import * 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Adafruit Industries 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | !!!Deprecation Warning!!! 2 | =================== 3 | This library has been deprecated in favor of [our python3 Blinka library](https://github.com/adafruit/Adafruit_Blinka). We have replaced all of the libraries that use this repo with CircuitPython libraries that are Python3 compatible, and support a [wide variety of single board/linux computers](https://circuitpython.org/blinka)! 4 | 5 | Visit https://circuitpython.org/blinka for more information 6 | 7 | CircuitPython has [support for almost 200 different drivers](https://circuitpython.readthedocs.io/projects/bundle/en/latest/drivers.html), and a as well as [FT232H support for Mac/Win/Linux](https://learn.adafruit.com/circuitpython-on-any-computer-with-ft232h)! 8 | 9 | !!!Deprecation Warning!!! 10 | =================== 11 | 12 | Adafruit Python GPIO Library 13 | ============================ 14 | 15 | Library to provide a cross-platform GPIO interface on the Raspberry Pi and Beaglebone Black using the [RPi.GPIO](https://pypi.python.org/pypi/RPi.GPIO) and [Adafruit_BBIO](https://pypi.python.org/pypi/Adafruit_BBIO) libraries. 16 | 17 | The library is currently in an early stage, but you can see how its used in the [Adafruit Nokia LCD library](https://github.com/adafruit/Adafruit_Nokia_LCD) to write Python code that is easily portable between the Raspberry Pi and Beaglebone Black. 18 | 19 | Note that you typically don't need to install this library directly as other libraries will depend on it in their setup and automatically install it. However if you do need to manually install do so by running these commands: 20 | 21 | - On a Debian-based Linux like Raspbian, Ubuntu, etc. in a terminal execute: 22 | 23 | ``` 24 | sudo apt-get update 25 | sudo apt-get install build-essential python-pip python-dev python-smbus git 26 | git clone https://github.com/adafruit/Adafruit_Python_GPIO.git 27 | cd Adafruit_Python_GPIO 28 | sudo python setup.py install 29 | ``` 30 | 31 | - On Mac OSX, first install PIP by [downloading the python script here](https://bootstrap.pypa.io/get-pip.py) and execute it with `python get-pip.py` in a terminal, then install the [git source control system](http://git-scm.com/downloads). Then in a terminal execute: 32 | 33 | ``` 34 | git clone https://github.com/adafruit/Adafruit_Python_GPIO.git 35 | cd Adafruit_Python_GPIO 36 | sudo python setup.py install 37 | ``` 38 | 39 | - On Windows, first install the [latest Python 2.7 version](https://www.python.org/downloads/windows/), then install PIP by [downloading the python script here](https://bootstrap.pypa.io/get-pip.py) and execute it with `python get-pip.py` in a terminal, and finally install the [git source control system](http://git-scm.com/downloads). Then in a git bash prompt execute: 40 | 41 | ``` 42 | git clone https://github.com/adafruit/Adafruit_Python_GPIO.git 43 | cd Adafruit_Python_GPIO 44 | python setup.py install 45 | ``` 46 | 47 | Contributing 48 | ------------ 49 | 50 | For information on contributing, such as how to run tests, etc. please see the [project wiki](https://github.com/adafruit/Adafruit_Python_GPIO/wiki/Running-Tests) on GitHub. 51 | -------------------------------------------------------------------------------- /ez_setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Bootstrap setuptools installation 3 | 4 | To use setuptools in your package's setup.py, include this 5 | file in the same directory and add this to the top of your setup.py:: 6 | 7 | from ez_setup import use_setuptools 8 | use_setuptools() 9 | 10 | To require a specific version of setuptools, set a download 11 | mirror, or use an alternate download directory, simply supply 12 | the appropriate options to ``use_setuptools()``. 13 | 14 | This file can also be run as a script to install or upgrade setuptools. 15 | """ 16 | import os 17 | import shutil 18 | import sys 19 | import tempfile 20 | import zipfile 21 | import optparse 22 | import subprocess 23 | import platform 24 | import textwrap 25 | import contextlib 26 | 27 | from distutils import log 28 | 29 | try: 30 | from site import USER_SITE 31 | except ImportError: 32 | USER_SITE = None 33 | 34 | DEFAULT_VERSION = "3.5.1" 35 | DEFAULT_URL = "https://pypi.python.org/packages/source/s/setuptools/" 36 | 37 | def _python_cmd(*args): 38 | """ 39 | Return True if the command succeeded. 40 | """ 41 | args = (sys.executable,) + args 42 | return subprocess.call(args) == 0 43 | 44 | 45 | def _install(archive_filename, install_args=()): 46 | with archive_context(archive_filename): 47 | # installing 48 | log.warn('Installing Setuptools') 49 | if not _python_cmd('setup.py', 'install', *install_args): 50 | log.warn('Something went wrong during the installation.') 51 | log.warn('See the error message above.') 52 | # exitcode will be 2 53 | return 2 54 | 55 | 56 | def _build_egg(egg, archive_filename, to_dir): 57 | with archive_context(archive_filename): 58 | # building an egg 59 | log.warn('Building a Setuptools egg in %s', to_dir) 60 | _python_cmd('setup.py', '-q', 'bdist_egg', '--dist-dir', to_dir) 61 | # returning the result 62 | log.warn(egg) 63 | if not os.path.exists(egg): 64 | raise IOError('Could not build the egg.') 65 | 66 | 67 | def get_zip_class(): 68 | """ 69 | Supplement ZipFile class to support context manager for Python 2.6 70 | """ 71 | class ContextualZipFile(zipfile.ZipFile): 72 | def __enter__(self): 73 | return self 74 | def __exit__(self, type, value, traceback): 75 | self.close 76 | return zipfile.ZipFile if hasattr(zipfile.ZipFile, '__exit__') else \ 77 | ContextualZipFile 78 | 79 | 80 | @contextlib.contextmanager 81 | def archive_context(filename): 82 | # extracting the archive 83 | tmpdir = tempfile.mkdtemp() 84 | log.warn('Extracting in %s', tmpdir) 85 | old_wd = os.getcwd() 86 | try: 87 | os.chdir(tmpdir) 88 | with get_zip_class()(filename) as archive: 89 | archive.extractall() 90 | 91 | # going in the directory 92 | subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0]) 93 | os.chdir(subdir) 94 | log.warn('Now working in %s', subdir) 95 | yield 96 | 97 | finally: 98 | os.chdir(old_wd) 99 | shutil.rmtree(tmpdir) 100 | 101 | 102 | def _do_download(version, download_base, to_dir, download_delay): 103 | egg = os.path.join(to_dir, 'setuptools-%s-py%d.%d.egg' 104 | % (version, sys.version_info[0], sys.version_info[1])) 105 | if not os.path.exists(egg): 106 | archive = download_setuptools(version, download_base, 107 | to_dir, download_delay) 108 | _build_egg(egg, archive, to_dir) 109 | sys.path.insert(0, egg) 110 | 111 | # Remove previously-imported pkg_resources if present (see 112 | # https://bitbucket.org/pypa/setuptools/pull-request/7/ for details). 113 | if 'pkg_resources' in sys.modules: 114 | del sys.modules['pkg_resources'] 115 | 116 | import setuptools 117 | setuptools.bootstrap_install_from = egg 118 | 119 | 120 | def use_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL, 121 | to_dir=os.curdir, download_delay=15): 122 | to_dir = os.path.abspath(to_dir) 123 | rep_modules = 'pkg_resources', 'setuptools' 124 | imported = set(sys.modules).intersection(rep_modules) 125 | try: 126 | import pkg_resources 127 | except ImportError: 128 | return _do_download(version, download_base, to_dir, download_delay) 129 | try: 130 | pkg_resources.require("setuptools>=" + version) 131 | return 132 | except pkg_resources.DistributionNotFound: 133 | return _do_download(version, download_base, to_dir, download_delay) 134 | except pkg_resources.VersionConflict as VC_err: 135 | if imported: 136 | msg = textwrap.dedent(""" 137 | The required version of setuptools (>={version}) is not available, 138 | and can't be installed while this script is running. Please 139 | install a more recent version first, using 140 | 'easy_install -U setuptools'. 141 | 142 | (Currently using {VC_err.args[0]!r}) 143 | """).format(VC_err=VC_err, version=version) 144 | sys.stderr.write(msg) 145 | sys.exit(2) 146 | 147 | # otherwise, reload ok 148 | del pkg_resources, sys.modules['pkg_resources'] 149 | return _do_download(version, download_base, to_dir, download_delay) 150 | 151 | def _clean_check(cmd, target): 152 | """ 153 | Run the command to download target. If the command fails, clean up before 154 | re-raising the error. 155 | """ 156 | try: 157 | subprocess.check_call(cmd) 158 | except subprocess.CalledProcessError: 159 | if os.access(target, os.F_OK): 160 | os.unlink(target) 161 | raise 162 | 163 | def download_file_powershell(url, target): 164 | """ 165 | Download the file at url to target using Powershell (which will validate 166 | trust). Raise an exception if the command cannot complete. 167 | """ 168 | target = os.path.abspath(target) 169 | cmd = [ 170 | 'powershell', 171 | '-Command', 172 | "(new-object System.Net.WebClient).DownloadFile(%(url)r, %(target)r)" % vars(), 173 | ] 174 | _clean_check(cmd, target) 175 | 176 | def has_powershell(): 177 | if platform.system() != 'Windows': 178 | return False 179 | cmd = ['powershell', '-Command', 'echo test'] 180 | devnull = open(os.path.devnull, 'wb') 181 | try: 182 | try: 183 | subprocess.check_call(cmd, stdout=devnull, stderr=devnull) 184 | except Exception: 185 | return False 186 | finally: 187 | devnull.close() 188 | return True 189 | 190 | download_file_powershell.viable = has_powershell 191 | 192 | def download_file_curl(url, target): 193 | cmd = ['curl', url, '--silent', '--output', target] 194 | _clean_check(cmd, target) 195 | 196 | def has_curl(): 197 | cmd = ['curl', '--version'] 198 | devnull = open(os.path.devnull, 'wb') 199 | try: 200 | try: 201 | subprocess.check_call(cmd, stdout=devnull, stderr=devnull) 202 | except Exception: 203 | return False 204 | finally: 205 | devnull.close() 206 | return True 207 | 208 | download_file_curl.viable = has_curl 209 | 210 | def download_file_wget(url, target): 211 | cmd = ['wget', url, '--quiet', '--output-document', target] 212 | _clean_check(cmd, target) 213 | 214 | def has_wget(): 215 | cmd = ['wget', '--version'] 216 | devnull = open(os.path.devnull, 'wb') 217 | try: 218 | try: 219 | subprocess.check_call(cmd, stdout=devnull, stderr=devnull) 220 | except Exception: 221 | return False 222 | finally: 223 | devnull.close() 224 | return True 225 | 226 | download_file_wget.viable = has_wget 227 | 228 | def download_file_insecure(url, target): 229 | """ 230 | Use Python to download the file, even though it cannot authenticate the 231 | connection. 232 | """ 233 | try: 234 | from urllib.request import urlopen 235 | except ImportError: 236 | from urllib2 import urlopen 237 | src = dst = None 238 | try: 239 | src = urlopen(url) 240 | # Read/write all in one block, so we don't create a corrupt file 241 | # if the download is interrupted. 242 | data = src.read() 243 | dst = open(target, "wb") 244 | dst.write(data) 245 | finally: 246 | if src: 247 | src.close() 248 | if dst: 249 | dst.close() 250 | 251 | download_file_insecure.viable = lambda: True 252 | 253 | def get_best_downloader(): 254 | downloaders = [ 255 | download_file_powershell, 256 | download_file_curl, 257 | download_file_wget, 258 | download_file_insecure, 259 | ] 260 | 261 | for dl in downloaders: 262 | if dl.viable(): 263 | return dl 264 | 265 | def download_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL, 266 | to_dir=os.curdir, delay=15, downloader_factory=get_best_downloader): 267 | """ 268 | Download setuptools from a specified location and return its filename 269 | 270 | `version` should be a valid setuptools version number that is available 271 | as an egg for download under the `download_base` URL (which should end 272 | with a '/'). `to_dir` is the directory where the egg will be downloaded. 273 | `delay` is the number of seconds to pause before an actual download 274 | attempt. 275 | 276 | ``downloader_factory`` should be a function taking no arguments and 277 | returning a function for downloading a URL to a target. 278 | """ 279 | # making sure we use the absolute path 280 | to_dir = os.path.abspath(to_dir) 281 | zip_name = "setuptools-%s.zip" % version 282 | url = download_base + zip_name 283 | saveto = os.path.join(to_dir, zip_name) 284 | if not os.path.exists(saveto): # Avoid repeated downloads 285 | log.warn("Downloading %s", url) 286 | downloader = downloader_factory() 287 | downloader(url, saveto) 288 | return os.path.realpath(saveto) 289 | 290 | def _build_install_args(options): 291 | """ 292 | Build the arguments to 'python setup.py install' on the setuptools package 293 | """ 294 | return ['--user'] if options.user_install else [] 295 | 296 | def _parse_args(): 297 | """ 298 | Parse the command line for options 299 | """ 300 | parser = optparse.OptionParser() 301 | parser.add_option( 302 | '--user', dest='user_install', action='store_true', default=False, 303 | help='install in user site package (requires Python 2.6 or later)') 304 | parser.add_option( 305 | '--download-base', dest='download_base', metavar="URL", 306 | default=DEFAULT_URL, 307 | help='alternative URL from where to download the setuptools package') 308 | parser.add_option( 309 | '--insecure', dest='downloader_factory', action='store_const', 310 | const=lambda: download_file_insecure, default=get_best_downloader, 311 | help='Use internal, non-validating downloader' 312 | ) 313 | parser.add_option( 314 | '--version', help="Specify which version to download", 315 | default=DEFAULT_VERSION, 316 | ) 317 | options, args = parser.parse_args() 318 | # positional arguments are ignored 319 | return options 320 | 321 | def main(): 322 | """Install or upgrade setuptools and EasyInstall""" 323 | options = _parse_args() 324 | archive = download_setuptools( 325 | version=options.version, 326 | download_base=options.download_base, 327 | downloader_factory=options.downloader_factory, 328 | ) 329 | return _install(archive, _build_install_args(options)) 330 | 331 | if __name__ == '__main__': 332 | sys.exit(main()) 333 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | print('Adafruit GPIO Library') 2 | print('Works best with Python 2.7') 3 | print('THIS INSTALL SCRIPT MAY REQUIRE ROOT/ADMIN PERMISSIONS') 4 | print('Especially if you installed python for "all users" on Windows') 5 | print('\ntry the following in your systems terminal if ensurepip is not sufficient:') 6 | print('$ python -m ensurepip --upgrade') 7 | print('$ python -m pip install --upgrade pip setuptools') 8 | 9 | import sys 10 | try: 11 | import pip 12 | from setuptools import setup, find_packages 13 | except ImportError: 14 | import ensurepip 15 | ensurepip.version() 16 | ensurepip.bootstrap() 17 | from setuptools import setup, find_packages 18 | 19 | # Define required packages. 20 | requires = ['adafruit-pureio'] 21 | 22 | # Assume spidev is required on non-windows & non-mac platforms (i.e. linux). 23 | if sys.platform != 'win32' and sys.platform != 'darwin': 24 | requires.append('spidev') 25 | 26 | setup(name = 'Adafruit_GPIO', 27 | version = '1.0.4', 28 | author = 'Tony DiCola', 29 | author_email = 'tdicola@adafruit.com', 30 | description = 'Library to provide a cross-platform GPIO interface on the Raspberry Pi and Beaglebone Black using the RPi.GPIO and Adafruit_BBIO libraries.', 31 | license = 'MIT', 32 | url = 'https://github.com/adafruit/Adafruit_Python_GPIO/', 33 | install_requires = requires, 34 | test_suite = 'tests', 35 | packages = find_packages()) 36 | -------------------------------------------------------------------------------- /tests/MockGPIO.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2014 Adafruit Industries 2 | # Author: Tony DiCola 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy 5 | # of this software and associated documentation files (the "Software"), to deal 6 | # in the Software without restriction, including without limitation the rights 7 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | # copies of the Software, and to permit persons to whom the Software is 9 | # furnished to do so, subject to the following conditions: 10 | # 11 | # The above copyright notice and this permission notice shall be included in 12 | # all copies or substantial portions of the Software. 13 | # 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | # THE SOFTWARE. 21 | 22 | import Adafruit_GPIO as GPIO 23 | 24 | 25 | class MockGPIO(GPIO.BaseGPIO): 26 | def __init__(self): 27 | self.pin_mode = {} 28 | self.pin_written = {} 29 | self.pin_read = {} 30 | 31 | def setup(self, pin, mode): 32 | self.pin_mode[pin] = mode 33 | 34 | def output(self, pin, bit): 35 | self.pin_written.setdefault(pin, []).append(1 if bit else 0) 36 | 37 | def input(self, pin): 38 | if pin not in self.pin_read: 39 | raise RuntimeError('No mock GPIO data to read for pin {0}'.format(pin)) 40 | return self.pin_read[pin].pop(0) == 1 41 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adafruit/Adafruit_Python_GPIO/a12fee39839665966bd124fd22588b2c87ced9d2/tests/__init__.py -------------------------------------------------------------------------------- /tests/test_GPIO.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2014 Adafruit Industries 2 | # Author: Tony DiCola 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy 5 | # of this software and associated documentation files (the "Software"), to deal 6 | # in the Software without restriction, including without limitation the rights 7 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | # copies of the Software, and to permit persons to whom the Software is 9 | # furnished to do so, subject to the following conditions: 10 | # 11 | # The above copyright notice and this permission notice shall be included in 12 | # all copies or substantial portions of the Software. 13 | # 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | # THE SOFTWARE. 21 | 22 | import unittest 23 | 24 | from mock import Mock, patch 25 | 26 | import Adafruit_GPIO as GPIO 27 | import Adafruit_GPIO.SPI as SPI 28 | import Adafruit_GPIO.Platform as Platform 29 | 30 | from MockGPIO import MockGPIO 31 | 32 | 33 | class TestBaseGPIO(unittest.TestCase): 34 | def test_set_high_and_set_low(self): 35 | gpio = MockGPIO() 36 | gpio.set_high(1) 37 | gpio.set_low(1) 38 | self.assertDictEqual(gpio.pin_written, {1: [1, 0]}) 39 | 40 | def test_is_high_and_is_low(self): 41 | gpio = MockGPIO() 42 | gpio.pin_read[1] = [0, 0, 1, 1] 43 | self.assertTrue(gpio.is_low(1)) 44 | self.assertFalse(gpio.is_high(1)) 45 | self.assertFalse(gpio.is_low(1)) 46 | self.assertTrue(gpio.is_high(1)) 47 | 48 | def test_output_pins(self): 49 | gpio = MockGPIO() 50 | gpio.output_pins({0: True, 1: False, 7: True}) 51 | self.assertDictEqual(gpio.pin_written, {0: [1], 1: [0], 7: [1]}) 52 | 53 | 54 | class TestRPiGPIOAdapter(unittest.TestCase): 55 | def test_setup(self): 56 | rpi_gpio = Mock() 57 | adapter = GPIO.RPiGPIOAdapter(rpi_gpio) 58 | adapter.setup(1, GPIO.OUT) 59 | rpi_gpio.setup.assert_called_with(1, rpi_gpio.OUT, pull_up_down=rpi_gpio.PUD_OFF) 60 | adapter.setup(1, GPIO.IN) 61 | rpi_gpio.setup.assert_called_with(1, rpi_gpio.IN, pull_up_down=rpi_gpio.PUD_OFF) 62 | adapter.setup(1, GPIO.IN, GPIO.PUD_DOWN) 63 | rpi_gpio.setup.assert_called_with(1, rpi_gpio.IN, pull_up_down=rpi_gpio.PUD_DOWN) 64 | adapter.setup(1, GPIO.IN, GPIO.PUD_UP) 65 | rpi_gpio.setup.assert_called_with(1, rpi_gpio.IN, pull_up_down=rpi_gpio.PUD_UP) 66 | 67 | def test_output(self): 68 | rpi_gpio = Mock() 69 | adapter = GPIO.RPiGPIOAdapter(rpi_gpio) 70 | adapter.output(1, True) 71 | rpi_gpio.output.assert_called_with(1, True) 72 | adapter.output(1, False) 73 | rpi_gpio.output.assert_called_with(1, False) 74 | 75 | def test_input(self): 76 | rpi_gpio = Mock() 77 | adapter = GPIO.RPiGPIOAdapter(rpi_gpio) 78 | rpi_gpio.input = Mock(return_value=True) 79 | val = adapter.input(1) 80 | self.assertTrue(val) 81 | rpi_gpio.input.assert_called_with(1) 82 | 83 | def test_setmode(self): 84 | rpi_gpio = Mock() 85 | adapter = GPIO.RPiGPIOAdapter(rpi_gpio, mode=rpi_gpio.BCM) 86 | rpi_gpio.setmode.assert_called_with(rpi_gpio.BCM) 87 | adapter = GPIO.RPiGPIOAdapter(rpi_gpio, mode=rpi_gpio.BOARD) 88 | rpi_gpio.setmode.assert_called_with(rpi_gpio.BOARD) 89 | adapter = GPIO.RPiGPIOAdapter(rpi_gpio) 90 | rpi_gpio.setmode.assert_called_with(rpi_gpio.BCM) 91 | 92 | def test_add_event_detect(self): 93 | rpi_gpio = Mock() 94 | adapter = GPIO.RPiGPIOAdapter(rpi_gpio) 95 | adapter.add_event_detect(1, GPIO.RISING) 96 | rpi_gpio.add_event_detect.assert_called_with(1, rpi_gpio.RISING) 97 | 98 | def test_remove_event_detect(self): 99 | rpi_gpio = Mock() 100 | adapter = GPIO.RPiGPIOAdapter(rpi_gpio) 101 | adapter.remove_event_detect(1) 102 | rpi_gpio.remove_event_detect.assert_called_with(1) 103 | 104 | def test_add_event_callback(self): 105 | rpi_gpio = Mock() 106 | adapter = GPIO.RPiGPIOAdapter(rpi_gpio) 107 | adapter.add_event_callback(1, callback=self.test_add_event_callback) 108 | rpi_gpio.add_event_callback.assert_called_with(1, self.test_add_event_callback) 109 | 110 | def test_event_detected(self): 111 | rpi_gpio = Mock() 112 | adapter = GPIO.RPiGPIOAdapter(rpi_gpio) 113 | adapter.event_detected(1) 114 | rpi_gpio.event_detected.assert_called_with(1) 115 | 116 | def test_wait_for_edge(self): 117 | rpi_gpio = Mock() 118 | adapter = GPIO.RPiGPIOAdapter(rpi_gpio) 119 | adapter.wait_for_edge(1, GPIO.FALLING) 120 | rpi_gpio.wait_for_edge.assert_called_with(1, rpi_gpio.FALLING) 121 | 122 | def test_cleanup(self): 123 | rpi_gpio = Mock() 124 | adapter = GPIO.AdafruitBBIOAdapter(rpi_gpio) 125 | adapter.cleanup() 126 | rpi_gpio.cleanup.assert_called() 127 | 128 | def test_cleanup_pin(self): 129 | rpi_gpio = Mock() 130 | adapter = GPIO.AdafruitBBIOAdapter(rpi_gpio) 131 | adapter.cleanup(1) 132 | rpi_gpio.cleanup.assert_called_with(1) 133 | 134 | 135 | class TestAdafruitBBIOAdapter(unittest.TestCase): 136 | def test_setup(self): 137 | bbio_gpio = Mock() 138 | adapter = GPIO.AdafruitBBIOAdapter(bbio_gpio) 139 | adapter.setup(1, GPIO.OUT) 140 | bbio_gpio.setup.assert_called_with(1, bbio_gpio.OUT, pull_up_down=bbio_gpio.PUD_OFF) 141 | adapter.setup(1, GPIO.IN) 142 | bbio_gpio.setup.assert_called_with(1, bbio_gpio.IN, pull_up_down=bbio_gpio.PUD_OFF) 143 | adapter.setup(1, GPIO.IN, GPIO.PUD_DOWN) 144 | bbio_gpio.setup.assert_called_with(1, bbio_gpio.IN, pull_up_down=bbio_gpio.PUD_DOWN) 145 | adapter.setup(1, GPIO.IN, GPIO.PUD_UP) 146 | bbio_gpio.setup.assert_called_with(1, bbio_gpio.IN, pull_up_down=bbio_gpio.PUD_UP) 147 | 148 | def test_output(self): 149 | bbio_gpio = Mock() 150 | adapter = GPIO.AdafruitBBIOAdapter(bbio_gpio) 151 | adapter.output(1, True) 152 | bbio_gpio.output.assert_called_with(1, True) 153 | adapter.output(1, False) 154 | bbio_gpio.output.assert_called_with(1, False) 155 | 156 | def test_input(self): 157 | bbio_gpio = Mock() 158 | adapter = GPIO.AdafruitBBIOAdapter(bbio_gpio) 159 | bbio_gpio.input = Mock(return_value=True) 160 | val = adapter.input(1) 161 | self.assertTrue(val) 162 | bbio_gpio.input.assert_called_with(1) 163 | 164 | def test_add_event_detect(self): 165 | bbio_gpio = Mock() 166 | adapter = GPIO.AdafruitBBIOAdapter(bbio_gpio) 167 | adapter.add_event_detect(1, GPIO.RISING) 168 | bbio_gpio.add_event_detect.assert_called_with(1, bbio_gpio.RISING) 169 | 170 | def test_add_event_detect(self): 171 | bbio_gpio = Mock() 172 | adapter = GPIO.AdafruitBBIOAdapter(bbio_gpio) 173 | adapter.add_event_detect(1, GPIO.RISING) 174 | bbio_gpio.add_event_detect.assert_called_with(1, bbio_gpio.RISING) 175 | 176 | def test_remove_event_detect(self): 177 | bbio_gpio = Mock() 178 | adapter = GPIO.AdafruitBBIOAdapter(bbio_gpio) 179 | adapter.remove_event_detect(1) 180 | bbio_gpio.remove_event_detect.assert_called_with(1) 181 | 182 | def test_add_event_callback(self): 183 | bbio_gpio = Mock() 184 | adapter = GPIO.AdafruitBBIOAdapter(bbio_gpio) 185 | adapter.add_event_callback(1, callback=self.test_add_event_callback) 186 | bbio_gpio.add_event_callback.assert_called_with(1, self.test_add_event_callback) 187 | 188 | def test_event_detected(self): 189 | bbio_gpio = Mock() 190 | adapter = GPIO.AdafruitBBIOAdapter(bbio_gpio) 191 | adapter.event_detected(1) 192 | bbio_gpio.event_detected.assert_called_with(1) 193 | 194 | def test_wait_for_edge(self): 195 | bbio_gpio = Mock() 196 | adapter = GPIO.AdafruitBBIOAdapter(bbio_gpio) 197 | adapter.wait_for_edge(1, GPIO.FALLING) 198 | bbio_gpio.wait_for_edge.assert_called_with(1, bbio_gpio.FALLING) 199 | 200 | def test_cleanup(self): 201 | bbio_gpio = Mock() 202 | adapter = GPIO.AdafruitBBIOAdapter(bbio_gpio) 203 | adapter.cleanup() 204 | bbio_gpio.cleanup.assert_called() 205 | 206 | def test_cleanup_pin(self): 207 | bbio_gpio = Mock() 208 | adapter = GPIO.AdafruitBBIOAdapter(bbio_gpio) 209 | adapter.cleanup(1) 210 | bbio_gpio.cleanup.assert_called_with(1) 211 | 212 | 213 | class TestGetPlatformGPIO(unittest.TestCase): 214 | @patch.dict('sys.modules', {'RPi': Mock(), 'RPi.GPIO': Mock()}) 215 | @patch('Adafruit_GPIO.Platform.platform_detect', Mock(return_value=Platform.RASPBERRY_PI)) 216 | def test_raspberrypi(self): 217 | gpio = GPIO.get_platform_gpio() 218 | self.assertIsInstance(gpio, GPIO.RPiGPIOAdapter) 219 | 220 | @patch.dict('sys.modules', {'Adafruit_BBIO': Mock(), 'Adafruit_BBIO.GPIO': Mock()}) 221 | @patch('Adafruit_GPIO.Platform.platform_detect', Mock(return_value=Platform.BEAGLEBONE_BLACK)) 222 | def test_beagleboneblack(self): 223 | gpio = GPIO.get_platform_gpio() 224 | self.assertIsInstance(gpio, GPIO.AdafruitBBIOAdapter) 225 | 226 | @patch('Adafruit_GPIO.Platform.platform_detect', Mock(return_value=Platform.UNKNOWN)) 227 | def test_unknown(self): 228 | self.assertRaises(RuntimeError, GPIO.get_platform_gpio) 229 | -------------------------------------------------------------------------------- /tests/test_I2C.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2014 Adafruit Industries 2 | # Author: Tony DiCola 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy 5 | # of this software and associated documentation files (the "Software"), to deal 6 | # in the Software without restriction, including without limitation the rights 7 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | # copies of the Software, and to permit persons to whom the Software is 9 | # furnished to do so, subject to the following conditions: 10 | # 11 | # The above copyright notice and this permission notice shall be included in 12 | # all copies or substantial portions of the Software. 13 | # 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | # THE SOFTWARE. 21 | 22 | import logging 23 | import unittest 24 | 25 | from mock import Mock, patch 26 | 27 | import Adafruit_GPIO.Platform as Platform 28 | 29 | 30 | # Enable debug logging to stdout during tests. 31 | logging.basicConfig() 32 | logging.getLogger().setLevel(logging.DEBUG) 33 | 34 | 35 | class MockSMBus(object): 36 | # Mock the smbus.SMBus class to record all data written to specific 37 | # addresses and registers in the _written member. 38 | def __init__(self): 39 | # _written will store a dictionary of address to register dictionary. 40 | # Each register dictionary will store a mapping of register value to 41 | # an array of all written values (in sequential write order). 42 | self._written = {} 43 | self._read = {} 44 | 45 | def _write_register(self, address, register, value): 46 | self._written.setdefault(address, {}).setdefault(register, []).append(value) 47 | 48 | def _read_register(self, address, register): 49 | return self._read.get(address).get(register).pop(0) 50 | 51 | def write_byte_data(self, address, register, value): 52 | self._write_register(address, register, value) 53 | 54 | def write_word_data(self, address, register, value): 55 | self._write_register(address, register, value >> 8 & 0xFF) 56 | self._write_register(address, register+1, value & 0xFF) 57 | 58 | def write_i2c_block_data(self, address, register, values): 59 | for i, value in enumerate(values): 60 | self._write_register(address, register+i, value & 0xFF) 61 | 62 | def read_byte_data(self, address, register): 63 | return self._read_register(address, register) 64 | 65 | def read_word_data(self, address, register): 66 | high = self._read_register(address, register) 67 | low = self._read_register(address, register+1) 68 | return (high << 8) | low 69 | 70 | def read_i2c_block_data(self, address, length): 71 | return [self._read_register(address+i) for i in range(length)] 72 | 73 | 74 | def create_device(address, busnum): 75 | # Mock the smbus module and inject it into the global namespace so the 76 | # Adafruit_GPIO.I2C module can be imported. Also inject a mock SMBus 77 | # instance to be returned by smbus.SMBus function calls. 78 | smbus = Mock() 79 | mockbus = MockSMBus() 80 | smbus.SMBus.return_value = mockbus 81 | with patch.dict('sys.modules', {'smbus': smbus}): 82 | import Adafruit_GPIO.I2C as I2C 83 | return (I2C.Device(address, busnum), smbus, mockbus) 84 | 85 | def safe_import_i2c(): 86 | # Mock the smbus module and inject it into the global namespace so the 87 | # Adafruit_GPIO.I2C module can be imported. The imported I2C module is 88 | # returned so global functions can be called on it. 89 | with patch.dict('sys.modules', {'smbus': Mock() }): 90 | import Adafruit_GPIO.I2C as I2C 91 | return I2C 92 | 93 | 94 | class TestI2CDevice(unittest.TestCase): 95 | 96 | def test_address_and_bus_set_correctly(self): 97 | device, smbus, mockbus = create_device(0x1F, 1) 98 | self.assertEqual(device._bus, mockbus) 99 | smbus.SMBus.assert_called_with(1) 100 | self.assertEqual(device._address, 0x1F) 101 | 102 | def test_write8(self): 103 | device, smbus, mockbus = create_device(0x1F, 1) 104 | device.write8(0xFE, 0xED) 105 | self.assertDictEqual(mockbus._written, { 0x1F: { 0xFE: [0xED] }}) 106 | 107 | def test_write8_truncates_to_8bits(self): 108 | device, smbus, mockbus = create_device(0x1F, 1) 109 | device.write8(0xFE, 0xBEEFED) 110 | self.assertDictEqual(mockbus._written, { 0x1F: { 0xFE: [0xED] }}) 111 | 112 | def test_write16(self): 113 | device, smbus, mockbus = create_device(0x1F, 1) 114 | device.write16(0xFE, 0xBEEF) 115 | self.assertDictEqual(mockbus._written, { 0x1F: { 0xFE: [0xBE], 116 | 0xFF: [0xEF] }}) 117 | 118 | def test_write16_truncates_to_8bits(self): 119 | device, smbus, mockbus = create_device(0x1F, 1) 120 | device.write16(0xFE, 0xFEEDBEEF) 121 | self.assertDictEqual(mockbus._written, { 0x1F: { 0xFE: [0xBE], 122 | 0xFF: [0xEF] }}) 123 | 124 | def test_writeList(self): 125 | device, smbus, mockbus = create_device(0x1F, 1) 126 | device.writeList(0x00, [0xFE, 0xED, 0xBE, 0xEF]) 127 | self.assertDictEqual(mockbus._written, { 0x1F: { 0x00: [0xFE], 128 | 0x01: [0xED], 129 | 0x02: [0xBE], 130 | 0x03: [0xEF] }}) 131 | 132 | def test_readU8(self): 133 | device, smbus, mockbus = create_device(0x1F, 1) 134 | mockbus._read[0x1F] = { 0xFE: [0xED] } 135 | value = device.readU8(0xFE) 136 | self.assertEqual(value, 0xED) 137 | 138 | def test_readS8(self): 139 | device, smbus, mockbus = create_device(0x1F, 1) 140 | mockbus._read[0x1F] = { 0xFE: [0xED] } 141 | value = device.readS8(0xFE) 142 | self.assertEqual(value, -19) 143 | 144 | def test_readU16(self): 145 | device, smbus, mockbus = create_device(0x1F, 1) 146 | mockbus._read[0x1F] = { 0xFE: [0xED], 0xFF: [0x01] } 147 | value = device.readU16(0xFE) 148 | self.assertEqual(value, 0xED01) 149 | 150 | def test_readS16(self): 151 | device, smbus, mockbus = create_device(0x1F, 1) 152 | mockbus._read[0x1F] = { 0xFE: [0xED], 0xFF: [0x01] } 153 | value = device.readS16(0xFE) 154 | self.assertEqual(value, -4863) 155 | 156 | 157 | class TestGetDefaultBus(unittest.TestCase): 158 | @patch('Adafruit_GPIO.Platform.pi_revision', Mock(return_value=1)) 159 | @patch('Adafruit_GPIO.Platform.platform_detect', Mock(return_value=Platform.RASPBERRY_PI)) 160 | def test_raspberry_pi_rev1(self): 161 | I2C = safe_import_i2c() 162 | bus = I2C.get_default_bus() 163 | self.assertEqual(bus, 0) 164 | 165 | @patch('Adafruit_GPIO.Platform.pi_revision', Mock(return_value=2)) 166 | @patch('Adafruit_GPIO.Platform.platform_detect', Mock(return_value=Platform.RASPBERRY_PI)) 167 | def test_raspberry_pi_rev2(self): 168 | I2C = safe_import_i2c() 169 | bus = I2C.get_default_bus() 170 | self.assertEqual(bus, 1) 171 | 172 | @patch('Adafruit_GPIO.Platform.platform_detect', Mock(return_value=Platform.BEAGLEBONE_BLACK)) 173 | def test_beaglebone_black(self): 174 | I2C = safe_import_i2c() 175 | bus = I2C.get_default_bus() 176 | self.assertEqual(bus, 1) 177 | 178 | @patch('Adafruit_GPIO.Platform.platform_detect', Mock(return_value=Platform.UNKNOWN)) 179 | def test_unknown(self): 180 | I2C = safe_import_i2c() 181 | self.assertRaises(RuntimeError, I2C.get_default_bus) 182 | -------------------------------------------------------------------------------- /tests/test_PWM.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2014 Adafruit Industries 2 | # Author: Tony DiCola 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy 5 | # of this software and associated documentation files (the "Software"), to deal 6 | # in the Software without restriction, including without limitation the rights 7 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | # copies of the Software, and to permit persons to whom the Software is 9 | # furnished to do so, subject to the following conditions: 10 | # 11 | # The above copyright notice and this permission notice shall be included in 12 | # all copies or substantial portions of the Software. 13 | # 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | # THE SOFTWARE. 21 | 22 | import unittest 23 | 24 | from mock import Mock, patch 25 | 26 | import Adafruit_GPIO.PWM as PWM 27 | import Adafruit_GPIO.Platform as Platform 28 | 29 | 30 | class TestRPi_PWM_Adapter(unittest.TestCase): 31 | def test_setup(self): 32 | rpi_gpio = Mock() 33 | pwm = PWM.RPi_PWM_Adapter(rpi_gpio) 34 | pwm.start(1, 50) 35 | rpi_gpio.PWM.assert_called_with(1, 2000) 36 | 37 | def test_set_duty_cycle_valid(self): 38 | rpi_gpio = Mock() 39 | pwm = PWM.RPi_PWM_Adapter(rpi_gpio) 40 | pwm.start(1, 50) 41 | pwm.set_duty_cycle(1, 75) 42 | # Implicit verification that no assertion or other error thrown. 43 | 44 | def test_set_duty_cycle_invalid(self): 45 | rpi_gpio = Mock() 46 | pwm = PWM.RPi_PWM_Adapter(rpi_gpio) 47 | pwm.start(1, 50) 48 | self.assertRaises(ValueError, pwm.set_duty_cycle, 1, 150) 49 | self.assertRaises(ValueError, pwm.set_duty_cycle, 1, -10) 50 | 51 | def test_set_frequency(self): 52 | rpi_gpio = Mock() 53 | pwm = PWM.RPi_PWM_Adapter(rpi_gpio) 54 | pwm.start(1, 50) 55 | pwm.set_frequency(1, 1000) 56 | # Implicit verification that no assertion or other error thrown. 57 | 58 | 59 | class TestBBIO_PWM_Adapter(unittest.TestCase): 60 | def test_setup(self): 61 | bbio_pwm = Mock() 62 | pwm = PWM.BBIO_PWM_Adapter(bbio_pwm) 63 | pwm.start('P9_16', 50) 64 | bbio_pwm.start.assert_called_with('P9_16', 50, 2000) 65 | 66 | def test_set_duty_cycle_valid(self): 67 | bbio_pwm = Mock() 68 | pwm = PWM.BBIO_PWM_Adapter(bbio_pwm) 69 | pwm.start('P9_16', 50) 70 | pwm.set_duty_cycle('P9_16', 75) 71 | bbio_pwm.set_duty_cycle.assert_called_with('P9_16', 75) 72 | 73 | def test_set_duty_cycle_invalid(self): 74 | bbio_pwm = Mock() 75 | pwm = PWM.BBIO_PWM_Adapter(bbio_pwm) 76 | pwm.start('P9_16', 50) 77 | self.assertRaises(ValueError, pwm.set_duty_cycle, 'P9_16', 150) 78 | self.assertRaises(ValueError, pwm.set_duty_cycle, 'P9_16', -10) 79 | 80 | def test_set_frequency(self): 81 | bbio_pwm = Mock() 82 | pwm = PWM.BBIO_PWM_Adapter(bbio_pwm) 83 | pwm.start('P9_16', 50) 84 | pwm.set_frequency('P9_16', 1000) 85 | bbio_pwm.set_frequency.assert_called_with('P9_16', 1000) 86 | 87 | 88 | class TestGetPlatformPWM(unittest.TestCase): 89 | @patch.dict('sys.modules', {'RPi': Mock(), 'RPi.GPIO': Mock()}) 90 | @patch('Adafruit_GPIO.Platform.platform_detect', Mock(return_value=Platform.RASPBERRY_PI)) 91 | def test_raspberrypi(self): 92 | pwm = PWM.get_platform_pwm() 93 | self.assertIsInstance(pwm, PWM.RPi_PWM_Adapter) 94 | 95 | @patch.dict('sys.modules', {'Adafruit_BBIO': Mock(), 'Adafruit_BBIO.PWM': Mock()}) 96 | @patch('Adafruit_GPIO.Platform.platform_detect', Mock(return_value=Platform.BEAGLEBONE_BLACK)) 97 | def test_beagleboneblack(self): 98 | pwm = PWM.get_platform_pwm() 99 | self.assertIsInstance(pwm, PWM.BBIO_PWM_Adapter) 100 | 101 | @patch('Adafruit_GPIO.Platform.platform_detect', Mock(return_value=Platform.UNKNOWN)) 102 | def test_otherplatform(self): 103 | self.assertRaises(RuntimeError, PWM.get_platform_pwm) 104 | -------------------------------------------------------------------------------- /tests/test_Platform.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2014 Adafruit Industries 2 | # Author: Tony DiCola 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy 5 | # of this software and associated documentation files (the "Software"), to deal 6 | # in the Software without restriction, including without limitation the rights 7 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | # copies of the Software, and to permit persons to whom the Software is 9 | # furnished to do so, subject to the following conditions: 10 | # 11 | # The above copyright notice and this permission notice shall be included in 12 | # all copies or substantial portions of the Software. 13 | # 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | # THE SOFTWARE. 21 | import unittest 22 | 23 | from mock import Mock, patch 24 | 25 | import Adafruit_GPIO.Platform as Platform 26 | 27 | 28 | class TestPlatformDetect(unittest.TestCase): 29 | @patch('platform.platform', Mock(return_value='Linux-3.8.13-bone47-armv7l-with-debian-7.4')) 30 | def test_beaglebone_black(self): 31 | result = Platform.platform_detect() 32 | self.assertEquals(result, Platform.BEAGLEBONE_BLACK) 33 | 34 | @patch('platform.platform', Mock(return_value='Darwin-13.2.0-x86_64-i386-64bit')) 35 | def test_unknown(self): 36 | result = Platform.platform_detect() 37 | self.assertEquals(result, Platform.UNKNOWN) 38 | 39 | 40 | class TestPiRevision(unittest.TestCase): 41 | def test_revision_1(self): 42 | with patch('__builtin__.open') as mock_open: 43 | handle = mock_open.return_value.__enter__.return_value 44 | handle.__iter__.return_value = iter(['Revision : 0000']) 45 | rev = Platform.pi_revision() 46 | self.assertEquals(rev, 1) 47 | with patch('__builtin__.open') as mock_open: 48 | handle = mock_open.return_value.__enter__.return_value 49 | handle.__iter__.return_value = iter(['Revision : 0002']) 50 | rev = Platform.pi_revision() 51 | self.assertEquals(rev, 1) 52 | with patch('__builtin__.open') as mock_open: 53 | handle = mock_open.return_value.__enter__.return_value 54 | handle.__iter__.return_value = iter(['Revision : 0003']) 55 | rev = Platform.pi_revision() 56 | self.assertEquals(rev, 1) 57 | 58 | def test_revision_2(self): 59 | with patch('__builtin__.open') as mock_open: 60 | handle = mock_open.return_value.__enter__.return_value 61 | handle.__iter__.return_value = iter(['Revision : 000e']) 62 | rev = Platform.pi_revision() 63 | self.assertEquals(rev, 2) 64 | 65 | def test_unknown_revision(self): 66 | with patch('__builtin__.open') as mock_open: 67 | handle = mock_open.return_value.__enter__.return_value 68 | handle.__iter__.return_value = iter(['foobar']) 69 | self.assertRaises(RuntimeError, Platform.pi_revision) 70 | 71 | -------------------------------------------------------------------------------- /tests/test_SPI.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2014 Adafruit Industries 2 | # Author: Tony DiCola 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy 5 | # of this software and associated documentation files (the "Software"), to deal 6 | # in the Software without restriction, including without limitation the rights 7 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | # copies of the Software, and to permit persons to whom the Software is 9 | # furnished to do so, subject to the following conditions: 10 | # 11 | # The above copyright notice and this permission notice shall be included in 12 | # all copies or substantial portions of the Software. 13 | # 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | # THE SOFTWARE. 21 | 22 | import unittest 23 | 24 | import Adafruit_GPIO as GPIO 25 | import Adafruit_GPIO.SPI as SPI 26 | 27 | from MockGPIO import MockGPIO 28 | 29 | 30 | class TestBitBangSPI(unittest.TestCase): 31 | def test_pin_modes_set_correctly(self): 32 | gpio = MockGPIO() 33 | device = SPI.BitBang(gpio, 1, 2, 3, 4) 34 | self.assertDictEqual(gpio.pin_mode, { 1: GPIO.OUT, 35 | 2: GPIO.OUT, 36 | 3: GPIO.IN, 37 | 4: GPIO.OUT }) 38 | 39 | def test_ss_set_high_after_initialization(self): 40 | gpio = MockGPIO() 41 | device = SPI.BitBang(gpio, 1, 2, 3, 4) 42 | self.assertListEqual(gpio.pin_written[4], [1]) 43 | 44 | def test_mode_0_write(self): 45 | gpio = MockGPIO() 46 | device = SPI.BitBang(gpio, 1, 2, 3, 4) 47 | device.write([0x1F]) 48 | # Verify clock 49 | self.assertListEqual(gpio.pin_written[1], [0, 1, 0, 1, 0, 1, 0, 1, 50 | 0, 1, 0, 1, 0, 1, 0, 1, 0]) 51 | # Verify MOSI 52 | self.assertListEqual(gpio.pin_written[2], [0, 0, 0, 1, 1, 1, 1, 1]) 53 | # Verify MISO 54 | self.assertNotIn(3, gpio.pin_written) 55 | # Verify SS 56 | self.assertListEqual(gpio.pin_written[4], [1, 0, 1]) 57 | 58 | def test_write_assert_deassert_ss_false(self): 59 | gpio = MockGPIO() 60 | device = SPI.BitBang(gpio, 1, 2, 3, 4) 61 | device.write([0x1F], assert_ss=False, deassert_ss=False) 62 | self.assertListEqual(gpio.pin_written[4], [1]) 63 | 64 | def test_write_lsbfirst(self): 65 | gpio = MockGPIO() 66 | device = SPI.BitBang(gpio, 1, 2, 3, 4) 67 | device.set_bit_order(SPI.LSBFIRST) 68 | device.write([0x1F]) 69 | self.assertListEqual(gpio.pin_written[2], [1, 1, 1, 1, 1, 0, 0, 0]) 70 | 71 | def test_invalid_mode_fails(self): 72 | gpio = MockGPIO() 73 | device = SPI.BitBang(gpio, 1, 2, 3, 4) 74 | self.assertRaises(ValueError, device.set_mode, -1) 75 | self.assertRaises(ValueError, device.set_mode, 4) 76 | 77 | def test_invalid_bit_order_fails(self): 78 | gpio = MockGPIO() 79 | device = SPI.BitBang(gpio, 1, 2, 3, 4) 80 | self.assertRaises(ValueError, device.set_bit_order, -1) 81 | self.assertRaises(ValueError, device.set_bit_order, 2) 82 | 83 | def test_mode_0_read(self): 84 | gpio = MockGPIO() 85 | device = SPI.BitBang(gpio, 1, 2, 3, 4) 86 | gpio.pin_read[3] = [0, 0, 0, 1, 1, 1, 1, 1] 87 | result = device.read(1) 88 | # Verify clock 89 | self.assertListEqual(gpio.pin_written[1], [0, 1, 0, 1, 0, 1, 0, 1, 90 | 0, 1, 0, 1, 0, 1, 0, 1, 0]) 91 | # Verify MOSI 92 | self.assertNotIn(2, gpio.pin_written) 93 | # Verify MISO 94 | self.assertNotIn(3, gpio.pin_written) 95 | # Verify SS 96 | self.assertListEqual(gpio.pin_written[4], [1, 0, 1]) 97 | # Verify result 98 | self.assertEqual(result, bytearray([0x1F])) 99 | 100 | def test_read_assert_deassert_ss_false(self): 101 | gpio = MockGPIO() 102 | device = SPI.BitBang(gpio, 1, 2, 3, 4) 103 | gpio.pin_read[3] = [0, 0, 0, 1, 1, 1, 1, 1] 104 | result = device.read(1, assert_ss=False, deassert_ss=False) 105 | self.assertListEqual(gpio.pin_written[4], [1]) 106 | 107 | def test_read_multiple_bytes(self): 108 | gpio = MockGPIO() 109 | device = SPI.BitBang(gpio, 1, 2, 3, 4) 110 | gpio.pin_read[3] = [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 111 | 0, 0, 0, 1, 1, 1, 1, 1] 112 | result = device.read(3) 113 | # Verify clock 114 | self.assertListEqual(gpio.pin_written[1], [0, 1, 0, 1, 0, 1, 0, 1, 115 | 0, 1, 0, 1, 0, 1, 0, 1, 116 | 0, 1, 0, 1, 0, 1, 0, 1, 117 | 0, 1, 0, 1, 0, 1, 0, 1, 118 | 0, 1, 0, 1, 0, 1, 0, 1, 119 | 0, 1, 0, 1, 0, 1, 0, 1, 0]) 120 | # Verify MOSI 121 | self.assertNotIn(2, gpio.pin_written) 122 | # Verify MISO 123 | self.assertNotIn(3, gpio.pin_written) 124 | # Verify SS 125 | self.assertListEqual(gpio.pin_written[4], [1, 0, 1]) 126 | # Verify result 127 | self.assertEqual(result, bytearray([0x1F, 0xF8, 0x1F])) 128 | 129 | def test_write_multiple_bytes(self): 130 | gpio = MockGPIO() 131 | device = SPI.BitBang(gpio, 1, 2, 3, 4) 132 | device.write([0x1F, 0xF8, 0x1F]) 133 | # Verify clock 134 | self.assertListEqual(gpio.pin_written[1], [0, 1, 0, 1, 0, 1, 0, 1, 135 | 0, 1, 0, 1, 0, 1, 0, 1, 136 | 0, 1, 0, 1, 0, 1, 0, 1, 137 | 0, 1, 0, 1, 0, 1, 0, 1, 138 | 0, 1, 0, 1, 0, 1, 0, 1, 139 | 0, 1, 0, 1, 0, 1, 0, 1, 0]) 140 | # Verify MOSI 141 | self.assertListEqual(gpio.pin_written[2], [0, 0, 0, 1, 1, 1, 1, 1, 142 | 1, 1, 1, 1, 1, 0, 0, 0, 143 | 0, 0, 0, 1, 1, 1, 1, 1]) 144 | # Verify MISO 145 | self.assertNotIn(3, gpio.pin_written) 146 | # Verify SS 147 | self.assertListEqual(gpio.pin_written[4], [1, 0, 1]) 148 | 149 | def test_mode_0_transfer(self): 150 | gpio = MockGPIO() 151 | device = SPI.BitBang(gpio, 1, 2, 3, 4) 152 | gpio.pin_read[3] = [0, 0, 0, 1, 1, 1, 1, 1] 153 | result = device.transfer([0xF8]) 154 | # Verify clock 155 | self.assertListEqual(gpio.pin_written[1], [0, 1, 0, 1, 0, 1, 0, 1, 156 | 0, 1, 0, 1, 0, 1, 0, 1, 0]) 157 | # Verify MOSI 158 | self.assertListEqual(gpio.pin_written[2], [1, 1, 1, 1, 1, 0, 0, 0]) 159 | # Verify MISO 160 | self.assertNotIn(3, gpio.pin_written) 161 | # Verify SS 162 | self.assertListEqual(gpio.pin_written[4], [1, 0, 1]) 163 | # Verify result 164 | self.assertEqual(result, bytearray([0x1F])) 165 | 166 | def test_transfer_multiple_bytes(self): 167 | gpio = MockGPIO() 168 | device = SPI.BitBang(gpio, 1, 2, 3, 4) 169 | gpio.pin_read[3] = [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 170 | 0, 0, 0, 1, 1, 1, 1, 1] 171 | result = device.transfer([0xF8, 0x1F, 0xF8]) 172 | # Verify clock 173 | self.assertListEqual(gpio.pin_written[1], [0, 1, 0, 1, 0, 1, 0, 1, 174 | 0, 1, 0, 1, 0, 1, 0, 1, 175 | 0, 1, 0, 1, 0, 1, 0, 1, 176 | 0, 1, 0, 1, 0, 1, 0, 1, 177 | 0, 1, 0, 1, 0, 1, 0, 1, 178 | 0, 1, 0, 1, 0, 1, 0, 1, 0]) 179 | # Verify MOSI 180 | self.assertListEqual(gpio.pin_written[2], [1, 1, 1, 1, 1, 0, 0, 0, 181 | 0, 0, 0, 1, 1, 1, 1, 1, 182 | 1, 1, 1, 1, 1, 0, 0, 0]) 183 | # Verify MISO 184 | self.assertNotIn(3, gpio.pin_written) 185 | # Verify SS 186 | self.assertListEqual(gpio.pin_written[4], [1, 0, 1]) 187 | # Verify result 188 | self.assertEqual(result, bytearray([0x1F, 0xF8, 0x1F])) 189 | 190 | #TODO: Test mode 1, 2, 3 191 | 192 | #TODO: Test null MOSI, MISO, SS 193 | --------------------------------------------------------------------------------