Skip to content

Commit 48ac2fa

Browse files
committed
feat: add xl9535 support, issue #400
1 parent ad8514c commit 48ac2fa

File tree

1 file changed

+79
-0
lines changed

1 file changed

+79
-0
lines changed

mqtt_io/modules/gpio/xl9535.py

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
"""
2+
XL9535/PCA9535/TCA9535 IO expander
3+
"""
4+
5+
from typing import Optional, cast
6+
7+
from ...exceptions import RuntimeConfigError
8+
from ...types import ConfigType, PinType
9+
from . import GenericGPIO, PinDirection, PinPUD
10+
11+
REQUIREMENTS = ("smbus2",)
12+
CONFIG_SCHEMA = {
13+
"i2c_bus_num": {"type": "integer", "required": True, "empty": False},
14+
"chip_addr": {"type": "integer", "required": True, "empty": False},
15+
}
16+
17+
#XL9535_INPUT_PORT_0 = 0x00,
18+
#XL9535_INPUT_PORT_1 = 0x01
19+
XL9535_OUTPUT_PORT_0 = 0x02
20+
#XL9535_OUTPUT_PORT_1 = 0x03
21+
#XL9535_INVERSION_PORT_0 = 0x04
22+
#XL9535_INVERSION_PORT_1 = 0x05
23+
XL9535_CONFIG_PORT_0 = 0x06
24+
#XL9535_CONFIG_PORT_1 = 0x07
25+
26+
class GPIO(GenericGPIO):
27+
"""
28+
Implementation of GPIO class for the XL9535/PCA9535/TCA9535 IO expander chip.
29+
30+
Pin numbers 0 - 15.
31+
"""
32+
33+
PIN_SCHEMA = {
34+
"pin": {"type": 'integer', "required": True, "min": 0, "max": 15},
35+
}
36+
37+
def setup_module(self) -> None:
38+
# pylint: disable=import-outside-toplevel,import-error
39+
# pylint: disable=no-name-in-module
40+
from smbus2 import SMBus # type: ignore
41+
42+
self.bus = SMBus(self.config["i2c_bus_num"])
43+
self.address = self.config["chip_addr"]
44+
45+
# configure all pins as outputs
46+
self.bus.write_word_data(self.address, XL9535_CONFIG_PORT_0, 0x0000)
47+
# off all outputs be default
48+
self.bus.write_word_data(self.address, XL9535_OUTPUT_PORT_0, 0x0000)
49+
50+
def setup_pin(
51+
self,
52+
pin: PinType,
53+
direction: PinDirection,
54+
pullup: PinPUD,
55+
pin_config: ConfigType,
56+
initial: Optional[str] = None,
57+
) -> None:
58+
# I han't found any ready-made expansion boards on sale where the chip works as an input.
59+
# For simplicity, the implementation only supports the chip working in output mode.
60+
if direction == PinDirection.INPUT:
61+
raise RuntimeConfigError("Unsupported PinDirection: INPUT")
62+
initial = pin_config.get("initial")
63+
if initial is not None:
64+
if initial == "high":
65+
self.set_pin(pin, True)
66+
elif initial == "low":
67+
self.set_pin(pin, False)
68+
69+
def set_pin(self, pin: PinType, value: bool) -> None:
70+
assert pin in range(16), "Pin number must be an integer between 0 and 15"
71+
current_state = self.bus.read_word_data(self.address, XL9535_OUTPUT_PORT_0)
72+
bit = 1 << cast(int, pin)
73+
new_state = current_state | bit if value else current_state & (~bit & 0xffff)
74+
self.bus.write_word_data(self.address, XL9535_OUTPUT_PORT_0, new_state)
75+
76+
def get_pin(self, pin: PinType) -> bool:
77+
assert pin in range(16), "Pin number must be an integer between 0 and 15"
78+
state = self.bus.read_word_data(self.address, XL9535_OUTPUT_PORT_0)
79+
return bool(state & 1 << cast(int, pin))

0 commit comments

Comments
 (0)