├── .gitignore ├── .gitlab-ci.yml ├── README.md ├── setup.py ├── src ├── __init__.py ├── __pycache__ │ ├── __init__.cpython-38.pyc │ ├── i2c.cpython-38.pyc │ └── mcp23017.cpython-38.pyc ├── i2c.py └── mcp23017.py └── test ├── __init__.py ├── __pycache__ ├── __init__.cpython-38.pyc ├── context.cpython-38.pyc ├── smbusmock.cpython-38.pyc ├── test_i2c.cpython-38.pyc └── test_mcp23017.cpython-38.pyc ├── context.py ├── smbusmock.py ├── test_i2c.py └── test_mcp23017.py /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | image: docker-registry.sensorberg.io/devops/dockerimages/python-test:1.0.1 2 | variables: 3 | GIT_SUBMODULE_STRATEGY: recursive 4 | 5 | stages: 6 | - test 7 | - build 8 | 9 | test: 10 | stage: test 11 | script: 12 | - python -m unittest discover 13 | 14 | build: 15 | stage: build 16 | script: 17 | - python setup.py sdist 18 | - pip install twine 19 | - twine upload --repository-url https://python.infrastructure.dev.sensorberg-cloud.com/ dist/* -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MCP23017-python 2 | MCP23017 GPIO-Expander Python Library for RaspberryPi 3 | 4 | [MCP23017 Datasheet](http://ww1.microchip.com/downloads/en/devicedoc/20001952c.pdf) 5 | 6 | ## Features 7 | 8 | currently implemented features: 9 | * set pin mode INPUT or OUTPUT 10 | * set pin mode of all pins 11 | * digital write pin HIGH or LOW 12 | * digital read pin state 13 | * digital read pin state of all pins 14 | * enable interrupt on a pin 15 | * enable interrupt on all pins 16 | * enable interrupt mirroring of BANK_A and BANK_B 17 | * read interrupt flags to see which pin triggered the interrupt 18 | * read the interrupt capture value of the pin when the interrupt happened 19 | 20 | ## Getting started 21 | 22 | Setup a MCP23017 object 23 | ``` 24 | from mcp23017 import * 25 | from i2c import I2C 26 | import smbus 27 | 28 | i2c = I2C(smbus.SMBus(1)) # creates a I2C Object as a wrapper for the SMBus 29 | mcp = MCP23017(0x20, i2c) # creates an MCP object with the given address 30 | ``` 31 | 32 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Learn more: https://github.com/kennethreitz/setup.py 4 | 5 | from setuptools import setup, find_packages 6 | 7 | 8 | with open('README.md') as f: 9 | readme = f.read() 10 | 11 | setup( 12 | name='mcp23017', 13 | version='0.1.0', 14 | description='MCP23017 Library', 15 | long_description=readme, 16 | author='Mirko Haeberlin', 17 | author_email='mirko.haeberlin@sensorberg.com', 18 | url='https://github.com/sensorberg-dev/MCP23017-python', 19 | packages=find_packages(exclude=('tests', 'docs')) 20 | ) -------------------------------------------------------------------------------- /src/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sensorberg/MCP23017-python/37c27f2a2d411bab149f299a82916af68991f137/src/__init__.py -------------------------------------------------------------------------------- /src/__pycache__/__init__.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sensorberg/MCP23017-python/37c27f2a2d411bab149f299a82916af68991f137/src/__pycache__/__init__.cpython-38.pyc -------------------------------------------------------------------------------- /src/__pycache__/i2c.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sensorberg/MCP23017-python/37c27f2a2d411bab149f299a82916af68991f137/src/__pycache__/i2c.cpython-38.pyc -------------------------------------------------------------------------------- /src/__pycache__/mcp23017.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sensorberg/MCP23017-python/37c27f2a2d411bab149f299a82916af68991f137/src/__pycache__/mcp23017.cpython-38.pyc -------------------------------------------------------------------------------- /src/i2c.py: -------------------------------------------------------------------------------- 1 | class I2C(): 2 | def __init__(self, smbus): 3 | """ 4 | Wrapper class for the smbus 5 | :param smbus: the smbus to send and receive data from smbus.SMBus(1) 6 | """ 7 | self.bus = smbus 8 | 9 | def write_to(self, address, offset, value): 10 | self.bus.write_byte_data(address, offset, value) 11 | 12 | def read_from(self, address, offset): 13 | value = self.bus.read_byte_data(address, offset) 14 | return value 15 | 16 | def read(self, address): 17 | return self.bus.read_byte(address) 18 | 19 | def scan(self): 20 | devices = list() 21 | for address in range(255): 22 | try: 23 | self.bus.read_byte(address) #try to read byte 24 | devices.append(address) 25 | except: # exception if read_byte fails 26 | pass 27 | return devices -------------------------------------------------------------------------------- /src/mcp23017.py: -------------------------------------------------------------------------------- 1 | IODIRA = 0x00 # Pin direction register 2 | IODIRB = 0x01 # Pin direction register 3 | IPOLA = 0x02 4 | IPOLB = 0x03 5 | GPINTENA = 0x04 6 | GPINTENB = 0x05 7 | DEFVALA = 0x06 8 | DEFVALB = 0x07 9 | INTCONA = 0x08 10 | INTCONB = 0x09 11 | IOCONA = 0x0A 12 | IOCONB = 0x0B 13 | GPPUA = 0x0C 14 | GPPUB = 0x0D 15 | 16 | INTFA = 0x0E 17 | INTFB = 0x0F 18 | INTCAPA = 0x10 19 | INTCAPB = 0x11 20 | GPIOA = 0x12 21 | GPIOB = 0x13 22 | OLATA = 0x14 23 | OLATB = 0x15 24 | ALL_OFFSET = [IODIRA, IODIRB, IPOLA, IPOLB, GPINTENA, GPINTENB, DEFVALA, DEFVALB, INTCONA, INTCONB, IOCONA, IOCONB, GPPUA, GPPUB, GPIOA, GPIOB, OLATA, OLATB] 25 | 26 | BANK_BIT = 7 27 | MIRROR_BIT = 6 28 | SEQOP_BIT = 5 29 | DISSLW_BIT = 4 30 | HAEN_BIT = 3 31 | ODR_BIT = 2 32 | INTPOL_BIT = 1 33 | 34 | GPA0 = 0 35 | GPA1 = 1 36 | GPA2 = 2 37 | GPA3 = 3 38 | GPA4 = 4 39 | GPA5 = 5 40 | GPA6 = 6 41 | GPA7 = 7 42 | GPB0 = 8 43 | GPB1 = 9 44 | GPB2 = 10 45 | GPB3 = 11 46 | GPB4 = 12 47 | GPB5 = 13 48 | GPB6 = 14 49 | GPB7 = 15 50 | ALL_GPIO = [GPA0, GPA1, GPA2, GPA3, GPA4, GPA5, GPA6, GPA7, GPB0, GPB1, GPB2, GPB3, GPB4, GPB5, GPB6, GPB7] 51 | 52 | HIGH = 0xFF 53 | LOW = 0x00 54 | 55 | INPUT = 0xFF 56 | OUTPUT = 0x00 57 | 58 | class MCP23017: 59 | """ 60 | MCP23017 class to handle ICs register setup 61 | 62 | RegName |ADR | bit7 | bit6 | bit5 | bit4 | bit3 | bit2 | bit1 | bit0 | POR/RST 63 | -------------------------------------------------------------------------------------------------- 64 | IODIRA | 00 | IO7 | IO6 | IO5 | IO4 | IO3 | IO2 | IO1 | IO0 | 1111 1111 65 | IODIRB | 01 | IO7 | IO6 | IO5 | IO4 | IO3 | IO2 | IO1 | IO0 | 1111 1111 66 | IPOLA | 02 | IP7 | IP6 | IP5 | IP4 | IP3 | IP2 | IP1 | IP0 | 0000 0000 67 | IPOLB | 03 | IP7 | IP6 | IP5 | IP4 | IP3 | IP2 | IP1 | IP0 | 0000 0000 68 | GPINTENA | 04 | GPINT7 | GPINT6 | GPINT5 | GPINT4 | GPINT3 | GPINT2 | GPINT1 | GPINT0 | 0000 0000 69 | GPINTENB | 05 | GPINT7 | GPINT6 | GPINT5 | GPINT4 | GPINT3 | GPINT2 | GPINT1 | GPINT0 | 0000 0000 70 | DEFVALA | 06 | DEF7 | DEF6 | DEF5 | DEF4 | DEF3 | DEF2 | DEF1 | DEF0 | 0000 0000 71 | DEFVALB | 07 | DEF7 | DEF6 | DEF5 | DEF4 | DEF3 | DEF2 | DEF1 | DEF0 | 0000 0000 72 | INTCONA | 08 | IOC7 | IOC6 | IOC5 | IOC4 | IOC3 | IOC2 | IOC1 | IOC0 | 0000 0000 73 | INTCONB | 09 | IOC7 | IOC6 | IOC5 | IOC4 | IOC3 | IOC2 | IOC1 | IOC0 | 0000 0000 74 | IOCON | 0A | BANK | MIRROR | SEQOP | DISSLW | HAEN | ODR | INTPOL | - | 0000 0000 75 | IOCON | 0B | BANK | MIRROR | SEQOP | DISSLW | HAEN | ODR | INTPOL | - | 0000 0000 76 | GPPUA | 0C | PU7 | PU6 | PU5 | PU4 | PU3 | PU2 | PU1 | PU0 | 0000 0000 77 | GPPUB | 0D | PU7 | PU6 | PU5 | PU4 | PU3 | PU2 | PU1 | PU0 | 0000 0000 78 | 79 | 80 | """ 81 | 82 | def __init__(self, address, i2c): 83 | self.i2c = i2c 84 | self.address = address 85 | 86 | def set_all_output(self): 87 | """ sets all GPIOs as OUTPUT""" 88 | self.i2c.write_to(self.address, IODIRA, 0x00) 89 | self.i2c.write_to(self.address, IODIRB, 0x00) 90 | 91 | def set_all_input(self): 92 | """ sets all GPIOs as INPUT""" 93 | self.i2c.write_to(self.address, IODIRA, 0xFF) 94 | self.i2c.write_to(self.address, IODIRB, 0xFF) 95 | 96 | def pin_mode(self, gpio, mode): 97 | """ 98 | Sets the given GPIO to the given mode INPUT or OUTPUT 99 | :param gpio: the GPIO to set the mode to 100 | :param mode: one of INPUT or OUTPUT 101 | """ 102 | pair = self.get_offset_gpio_tuple([IODIRA, IODIRB], gpio) 103 | self.set_bit_enabled(pair[0], pair[1], True if mode is INPUT else False) 104 | 105 | def digital_write(self, gpio, direction): 106 | """ 107 | Sets the given GPIO to the given direction HIGH or LOW 108 | :param gpio: the GPIO to set the direction to 109 | :param direction: one of HIGH or LOW 110 | """ 111 | pair = self.get_offset_gpio_tuple([OLATA, OLATB], gpio) 112 | self.set_bit_enabled(pair[0], pair[1], True if direction is HIGH else False) 113 | 114 | def digital_read(self, gpio): 115 | """ 116 | Reads the current direction of the given GPIO 117 | :param gpio: the GPIO to read from 118 | :return: 119 | """ 120 | pair = self.get_offset_gpio_tuple([GPIOA, GPIOB], gpio) 121 | bits = self.i2c.read_from(self.address, pair[0]) 122 | return HIGH if (bits & (1 << pair[1])) > 0 else LOW 123 | 124 | def digital_read_all(self): 125 | """ 126 | Reads the current direction of the given GPIO 127 | :param gpio: the GPIO to read from 128 | :return: 129 | """ 130 | return [self.i2c.read_from(self.address, GPIOA), 131 | self.i2c.read_from(self.address, GPIOB)] 132 | 133 | def set_interrupt(self, gpio, enabled): 134 | """ 135 | Enables or disables the interrupt of a given GPIO 136 | :param gpio: the GPIO where the interrupt needs to be set, this needs to be one of GPAn or GPBn constants 137 | :param enabled: enable or disable the interrupt 138 | """ 139 | pair = self.get_offset_gpio_tuple([GPINTENA, GPINTENB], gpio) 140 | self.set_bit_enabled(pair[0], pair[1], enabled) 141 | 142 | def set_all_interrupt(self, enabled): 143 | """ 144 | Enables or disables the interrupt of a all GPIOs 145 | :param enabled: enable or disable the interrupt 146 | """ 147 | self.i2c.write_to(self.address, GPINTENA, 0xFF if enabled else 0x00) 148 | self.i2c.write_to(self.address, GPINTENB, 0xFF if enabled else 0x00) 149 | 150 | def set_interrupt_mirror(self, enable): 151 | """ 152 | Enables or disables the interrupt mirroring 153 | :param enable: enable or disable the interrupt mirroring 154 | """ 155 | self.set_bit_enabled(IOCONA, MIRROR_BIT, enable) 156 | self.set_bit_enabled(IOCONB, MIRROR_BIT, enable) 157 | 158 | def read_interrupt_captures(self): 159 | """ 160 | Reads the interrupt captured register. It captures the GPIO port value at the time the interrupt occurred. 161 | :return: a tuple of the INTCAPA and INTCAPB interrupt capture as a list of bit string 162 | """ 163 | return (self._get_list_of_interrupted_values_from(INTCAPA), 164 | self._get_list_of_interrupted_values_from(INTCAPB)) 165 | 166 | def _get_list_of_interrupted_values_from(self, offset): 167 | list = [] 168 | interrupted = self.i2c.read_from(self.address, offset) 169 | bits = '{0:08b}'.format(interrupted) 170 | for i in reversed(range(8)): 171 | list.append(bits[i]) 172 | 173 | return list 174 | 175 | def read_interrupt_flags(self): 176 | """ 177 | Reads the interrupt flag which reflects the interrupt condition. A set bit indicates that the associated pin caused the interrupt. 178 | :return: a tuple of the INTFA and INTFB interrupt flags as list of bit string 179 | """ 180 | return (self._read_interrupt_flags_from(INTFA), 181 | self._read_interrupt_flags_from(INTFB)) 182 | 183 | def _read_interrupt_flags_from(self, offset): 184 | list = [] 185 | interrupted = self.i2c.read_from(self.address, offset) 186 | bits = '{0:08b}'.format(interrupted) 187 | for i in reversed(range(8)): 188 | list.append(bits[i]) 189 | 190 | return list 191 | 192 | def read(self, offset): 193 | return self.i2c.read_from(self.address, offset) 194 | 195 | def write(self, offset, value): 196 | return self.i2c.write_to(self.address, offset, value) 197 | 198 | def get_offset_gpio_tuple(self, offsets, gpio): 199 | if offsets[0] not in ALL_OFFSET or offsets[1] not in ALL_OFFSET: 200 | raise TypeError("offsets must contain a valid offset address. See description for help") 201 | if gpio not in ALL_GPIO: 202 | raise TypeError("pin must be one of GPAn or GPBn. See description for help") 203 | 204 | offset = offsets[0] if gpio < 8 else offsets[1] 205 | _gpio = gpio % 8 206 | return (offset, _gpio) 207 | 208 | def set_bit_enabled(self, offset, gpio, enable): 209 | stateBefore = self.i2c.read_from(self.address, offset) 210 | value = (stateBefore | self.bitmask(gpio)) if enable else (stateBefore & ~self.bitmask(gpio)) 211 | self.i2c.write_to(self.address, offset, value) 212 | 213 | def bitmask(self, gpio): 214 | return 1 << (gpio % 8) 215 | -------------------------------------------------------------------------------- /test/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sensorberg/MCP23017-python/37c27f2a2d411bab149f299a82916af68991f137/test/__init__.py -------------------------------------------------------------------------------- /test/__pycache__/__init__.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sensorberg/MCP23017-python/37c27f2a2d411bab149f299a82916af68991f137/test/__pycache__/__init__.cpython-38.pyc -------------------------------------------------------------------------------- /test/__pycache__/context.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sensorberg/MCP23017-python/37c27f2a2d411bab149f299a82916af68991f137/test/__pycache__/context.cpython-38.pyc -------------------------------------------------------------------------------- /test/__pycache__/smbusmock.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sensorberg/MCP23017-python/37c27f2a2d411bab149f299a82916af68991f137/test/__pycache__/smbusmock.cpython-38.pyc -------------------------------------------------------------------------------- /test/__pycache__/test_i2c.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sensorberg/MCP23017-python/37c27f2a2d411bab149f299a82916af68991f137/test/__pycache__/test_i2c.cpython-38.pyc -------------------------------------------------------------------------------- /test/__pycache__/test_mcp23017.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sensorberg/MCP23017-python/37c27f2a2d411bab149f299a82916af68991f137/test/__pycache__/test_mcp23017.cpython-38.pyc -------------------------------------------------------------------------------- /test/context.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import sys 4 | import os 5 | sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) 6 | 7 | from src import * 8 | from src.mcp23017 import * 9 | from src.i2c import * -------------------------------------------------------------------------------- /test/smbusmock.py: -------------------------------------------------------------------------------- 1 | 2 | class MBusMock(): 3 | 4 | def __init__(self): 5 | self.data = {} 6 | for address in range(0x20, 0x28): 7 | self.data[address] = {} 8 | for offset in range(0x00, 0x16): 9 | self.data[address][offset] = 0x00 10 | if offset is 0x00 or offset is 0x01: 11 | self.data[address][offset] = 0xFF 12 | print(self.data) 13 | 14 | def write_byte_data(self, address, offset, value): 15 | self.data[address][offset] = value 16 | 17 | def read_byte_data(self, address, offset): 18 | return self.data[address][offset] 19 | 20 | def read_byte(self, address): 21 | return next(iter(self.data[address].values())) -------------------------------------------------------------------------------- /test/test_i2c.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from test import smbusmock 3 | from .context import * 4 | 5 | class TestI2c(unittest.TestCase): 6 | 7 | def test_i2c_init(self): 8 | smbus = smbusmock.MBusMock() 9 | i2c = I2C(smbus) 10 | self.assertEqual(i2c.bus, smbus) 11 | 12 | 13 | def test_write_read_data(self): 14 | i2c = I2C(smbusmock.MBusMock()) 15 | self.assertEqual(i2c.read_from(0x20, 0x01), 0xff) 16 | i2c.write_to(0x20, 0x01, 0x00) 17 | self.assertEqual(i2c.read_from(0x20, 0x01), 0x00) 18 | 19 | def test_read(self): 20 | i2c = I2C(smbusmock.MBusMock()) 21 | self.assertEqual(i2c.read(0x20), 0xff) 22 | 23 | def test_scan(self): 24 | i2c = I2C(smbusmock.MBusMock()) 25 | devices = i2c.scan() 26 | self.assertEqual(len(devices), 8) 27 | -------------------------------------------------------------------------------- /test/test_mcp23017.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from .context import * 3 | from test import smbusmock 4 | 5 | 6 | class TestMCP23017(unittest.TestCase): 7 | 8 | i2c = I2C(smbusmock.MBusMock()) 9 | mockAddress = 0x20 10 | 11 | def test_mcp23017_init(self): 12 | mcp = MCP23017(self.mockAddress, self.i2c) 13 | self.assertEqual(mcp.address, 0x20) 14 | 15 | def test_get_offset_pin_pair(self): 16 | mcp = MCP23017(self.mockAddress, self.i2c) 17 | pair = mcp.get_offset_gpio_tuple([GPINTENA, GPINTENB], GPA2) 18 | self.assertEqual(pair[0], GPINTENA) 19 | self.assertEqual(pair[1], GPA2) 20 | 21 | pair = mcp.get_offset_gpio_tuple([INTCONA, INTCONB], GPB0) 22 | self.assertEqual(pair[0], INTCONB) 23 | self.assertEqual(pair[1], GPB0 % 8) 24 | 25 | def test_get_offset_pin_pair_type_error(self): 26 | mcp = MCP23017(self.mockAddress, self.i2c) 27 | with self.assertRaises(TypeError): 28 | mcp.get_offset_gpio_tuple([INTCONA, INTCONB], 18) 29 | 30 | with self.assertRaises(TypeError): 31 | mcp.get_offset_gpio_tuple([INTCONA, INTCONB], -1) 32 | 33 | with self.assertRaises(TypeError): 34 | mcp.get_offset_gpio_tuple([0xff, INTCONB], 1) 35 | 36 | with self.assertRaises(TypeError): 37 | mcp.get_offset_gpio_tuple([INTCONB, 0xff], 1) 38 | 39 | def test_set_all_output(self): 40 | mcp = MCP23017(self.mockAddress, self.i2c) 41 | mcp.set_all_output() 42 | self.assertEqual(mcp.read(IODIRA), 0b00000000) 43 | self.assertEqual(mcp.read(IODIRB), 0b00000000) 44 | 45 | def test_set_all_input(self): 46 | mcp = MCP23017(self.mockAddress, self.i2c) 47 | mcp.set_all_input() 48 | self.assertEqual(mcp.read(IODIRA), 0b11111111) 49 | self.assertEqual(mcp.read(IODIRB), 0b11111111) 50 | 51 | def test_pin_mode(self): 52 | mcp = MCP23017(self.mockAddress, self.i2c) 53 | mcp.set_all_output() 54 | mcp.pin_mode(GPA0, INPUT) 55 | self.assertEqual(mcp.read(IODIRA), 0b00000001) 56 | mcp.pin_mode(GPA7, INPUT) 57 | self.assertEqual(mcp.read(IODIRA), 0b10000001) 58 | mcp.pin_mode(GPB7, INPUT) 59 | self.assertEqual(mcp.read(IODIRA), 0b10000001) 60 | self.assertEqual(mcp.read(IODIRB), 0b10000000) 61 | mcp.pin_mode(GPA0, OUTPUT) 62 | mcp.pin_mode(GPA7, OUTPUT) 63 | self.assertEqual(mcp.read(IODIRA), 0b00000000) 64 | 65 | def test_digital_write(self): 66 | mcp = MCP23017(self.mockAddress, self.i2c) 67 | mcp.set_all_output() 68 | self.assertEqual(mcp.read(OLATA), 0b00000000) 69 | self.assertEqual(mcp.read(OLATB), 0b00000000) 70 | 71 | mcp.digital_write(GPA0, HIGH) 72 | self.assertEqual(mcp.read(OLATA), 0b00000001) 73 | mcp.digital_write(GPA0, LOW) 74 | self.assertEqual(mcp.read(OLATA), 0b00000000) 75 | mcp.digital_write(GPA7, HIGH) 76 | self.assertEqual(mcp.read(OLATA), 0b10000000) 77 | 78 | mcp.digital_write(GPB0, HIGH) 79 | self.assertEqual(mcp.read(OLATA), 0b10000000) 80 | self.assertEqual(mcp.read(OLATB), 0b00000001) 81 | mcp.digital_write(GPB0, LOW) 82 | self.assertEqual(mcp.read(OLATA), 0b10000000) 83 | self.assertEqual(mcp.read(OLATB), 0b00000000) 84 | 85 | def test_set_interrupt(self): 86 | mcp = MCP23017(self.mockAddress, self.i2c) 87 | mcp.set_all_input() 88 | mcp.set_interrupt(GPA0, True) 89 | self.assertEqual(mcp.read(GPINTENA), 0b00000001) 90 | mcp.set_interrupt(GPA6, True) 91 | self.assertEqual(mcp.read(GPINTENA), 0b01000001) 92 | mcp.set_interrupt(GPA0, False) 93 | self.assertEqual(mcp.read(GPINTENA), 0b01000000) 94 | mcp.set_interrupt(GPB0, True) 95 | self.assertEqual(mcp.read(GPINTENB), 0b00000001) 96 | 97 | def test_set_all_interrupt(self): 98 | mcp = MCP23017(self.mockAddress, self.i2c) 99 | mcp.set_all_input() 100 | mcp.set_all_interrupt(True) 101 | self.assertEqual(mcp.read(GPINTENA), 0b11111111) 102 | self.assertEqual(mcp.read(GPINTENB), 0b11111111) 103 | mcp.set_all_interrupt(False) 104 | self.assertEqual(mcp.read(GPINTENA), 0b00000000) 105 | self.assertEqual(mcp.read(GPINTENB), 0b00000000) 106 | 107 | def test_interrupt_mirror(self): 108 | mcp = MCP23017(self.mockAddress, self.i2c) 109 | mcp.set_all_input() 110 | mcp.set_interrupt_mirror(True) 111 | self.assertEqual(mcp.read(IOCONA), 0b01000000) 112 | self.assertEqual(mcp.read(IOCONB), 0b01000000) 113 | mcp.set_interrupt_mirror(False) 114 | self.assertEqual(mcp.read(IOCONA), 0b00000000) 115 | self.assertEqual(mcp.read(IOCONB), 0b00000000) 116 | 117 | def test_read_interrupt_captures(self): 118 | mcp = MCP23017(self.mockAddress, self.i2c) 119 | mcp.set_all_output() 120 | self.assertEqual(mcp.read(INTCAPA), 0b00000000) 121 | self.assertEqual(mcp.read(INTCAPB), 0b00000000) 122 | mcp.write(INTCAPA, 0x01) 123 | interruptCaptures = mcp.read_interrupt_captures() 124 | self.assertEqual(interruptCaptures[0][0], "1") --------------------------------------------------------------------------------