Skip to content

Commit 0cc2407

Browse files
committed
enable adafruit_bus_device and add test
1 parent 7043e6f commit 0cc2407

File tree

4 files changed

+241
-4
lines changed

4 files changed

+241
-4
lines changed

ports/zephyr-cp/boards/adafruit/feather_rp2040_zephyr/autogen_board_info.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ _eve = false
88
_pew = false
99
_pixelmap = false
1010
_stage = false
11-
adafruit_bus_device = false
11+
adafruit_bus_device = true
1212
adafruit_pixelbuf = false
1313
aesio = true
1414
alarm = false

ports/zephyr-cp/boards/native/native_sim/autogen_board_info.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ _eve = false
88
_pew = false
99
_pixelmap = false
1010
_stage = false
11-
adafruit_bus_device = false
11+
adafruit_bus_device = true
1212
adafruit_pixelbuf = false
1313
aesio = true
1414
alarm = false

ports/zephyr-cp/cptools/build_circuitpython.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
"math",
6363
"msgpack",
6464
"aesio",
65+
"adafruit_bus_device",
6566
]
6667
# Flags that don't match with with a *bindings module. Some used by adafruit_requests
6768
MPCONFIG_FLAGS = ["array", "errno", "io", "json", "math"]
@@ -549,8 +550,8 @@ async def build_circuitpython():
549550
hal_source.extend(top.glob(f"ports/zephyr-cp/common-hal/{module.name}/*.c"))
550551
# Only include shared-module/*.c if no common-hal/*.c files were found
551552
if len(hal_source) == len_before or module.name in SHARED_MODULE_AND_COMMON_HAL:
552-
hal_source.extend(top.glob(f"shared-module/{module.name}/*.c"))
553-
hal_source.extend(top.glob(f"shared-bindings/{module.name}/*.c"))
553+
hal_source.extend(top.glob(f"shared-module/{module.name}/**/*.c"))
554+
hal_source.extend(top.glob(f"shared-bindings/{module.name}/**/*.c"))
554555
if module.name in LIBRARY_SOURCE:
555556
for library_source in LIBRARY_SOURCE[module.name]:
556557
library_sources.extend(top.glob(library_source))
Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
# SPDX-FileCopyrightText: 2026 Scott Shawcroft for Adafruit Industries
2+
# SPDX-License-Identifier: MIT
3+
4+
"""Test adafruit_bus_device.i2c_device.I2CDevice against the AT24 EEPROM emulator."""
5+
6+
import pytest
7+
8+
9+
PROBE_OK_CODE = """\
10+
import board
11+
from adafruit_bus_device.i2c_device import I2CDevice
12+
13+
i2c = board.I2C()
14+
device = I2CDevice(i2c, 0x50)
15+
print(f"device created: {type(device).__name__}")
16+
i2c.deinit()
17+
print("done")
18+
"""
19+
20+
21+
@pytest.mark.circuitpy_drive({"code.py": PROBE_OK_CODE})
22+
def test_i2cdevice_probe_success(circuitpython):
23+
"""Constructing I2CDevice with probe=True succeeds when AT24 is present at 0x50."""
24+
circuitpython.wait_until_done()
25+
26+
output = circuitpython.serial.all_output
27+
assert "device created: I2CDevice" in output
28+
assert "done" in output
29+
30+
31+
PROBE_FAIL_CODE = """\
32+
import board
33+
from adafruit_bus_device.i2c_device import I2CDevice
34+
35+
i2c = board.I2C()
36+
try:
37+
device = I2CDevice(i2c, 0x51)
38+
print("unexpected_success")
39+
except ValueError as e:
40+
print(f"probe_failed: {e}")
41+
except OSError as e:
42+
print(f"probe_failed: {e}")
43+
i2c.deinit()
44+
print("done")
45+
"""
46+
47+
48+
@pytest.mark.circuitpy_drive({"code.py": PROBE_FAIL_CODE})
49+
def test_i2cdevice_probe_failure(circuitpython):
50+
"""Constructing I2CDevice with probe=True raises when no device responds."""
51+
circuitpython.wait_until_done()
52+
53+
output = circuitpython.serial.all_output
54+
assert "probe_failed" in output
55+
assert "unexpected_success" not in output
56+
assert "done" in output
57+
58+
59+
PROBE_DISABLED_CODE = """\
60+
import board
61+
from adafruit_bus_device.i2c_device import I2CDevice
62+
63+
i2c = board.I2C()
64+
# probe=False should not raise even without a device at this address
65+
device = I2CDevice(i2c, 0x51, probe=False)
66+
print(f"device created: {type(device).__name__}")
67+
i2c.deinit()
68+
print("done")
69+
"""
70+
71+
72+
@pytest.mark.circuitpy_drive({"code.py": PROBE_DISABLED_CODE})
73+
def test_i2cdevice_probe_disabled(circuitpython):
74+
"""Constructing I2CDevice with probe=False succeeds regardless of device presence."""
75+
circuitpython.wait_until_done()
76+
77+
output = circuitpython.serial.all_output
78+
assert "device created: I2CDevice" in output
79+
assert "done" in output
80+
81+
82+
READ_CODE = """\
83+
import board
84+
from adafruit_bus_device.i2c_device import I2CDevice
85+
86+
i2c = board.I2C()
87+
device = I2CDevice(i2c, 0x50)
88+
89+
# Read first byte by writing the memory address, then reading.
90+
buf = bytearray(1)
91+
with device:
92+
device.write(bytes([0x00]))
93+
device.readinto(buf)
94+
print(f"AT24 byte 0: 0x{buf[0]:02X}")
95+
if buf[0] == 0xFF:
96+
print("eeprom_valid")
97+
i2c.deinit()
98+
print("done")
99+
"""
100+
101+
102+
@pytest.mark.circuitpy_drive({"code.py": READ_CODE})
103+
def test_i2cdevice_write_then_readinto_separate(circuitpython):
104+
"""write() followed by readinto() inside a single context manager block reads EEPROM data."""
105+
circuitpython.wait_until_done()
106+
107+
output = circuitpython.serial.all_output
108+
assert "AT24 byte 0: 0xFF" in output
109+
assert "eeprom_valid" in output
110+
assert "done" in output
111+
112+
113+
WRITE_THEN_READINTO_CODE = """\
114+
import board
115+
from adafruit_bus_device.i2c_device import I2CDevice
116+
117+
i2c = board.I2C()
118+
device = I2CDevice(i2c, 0x50)
119+
120+
out_buf = bytes([0x00])
121+
in_buf = bytearray(4)
122+
with device:
123+
device.write_then_readinto(out_buf, in_buf)
124+
print(f"first 4 bytes: {[hex(b) for b in in_buf]}")
125+
if all(b == 0xFF for b in in_buf):
126+
print("eeprom_valid")
127+
i2c.deinit()
128+
print("done")
129+
"""
130+
131+
132+
@pytest.mark.circuitpy_drive({"code.py": WRITE_THEN_READINTO_CODE})
133+
def test_i2cdevice_write_then_readinto_atomic(circuitpython):
134+
"""write_then_readinto() performs an atomic write+read against the EEPROM."""
135+
circuitpython.wait_until_done()
136+
137+
output = circuitpython.serial.all_output
138+
assert "first 4 bytes:" in output
139+
assert "eeprom_valid" in output
140+
assert "done" in output
141+
142+
143+
WRITE_READBACK_CODE = """\
144+
import board
145+
import time
146+
from adafruit_bus_device.i2c_device import I2CDevice
147+
148+
i2c = board.I2C()
149+
device = I2CDevice(i2c, 0x50)
150+
151+
# Write four bytes starting at EEPROM address 0x10.
152+
payload = bytes([0xDE, 0xAD, 0xBE, 0xEF])
153+
with device:
154+
device.write(bytes([0x10]) + payload)
155+
156+
# EEPROM needs a moment to commit the write internally.
157+
time.sleep(0.01)
158+
159+
readback = bytearray(4)
160+
with device:
161+
device.write_then_readinto(bytes([0x10]), readback)
162+
print(f"readback: {[hex(b) for b in readback]}")
163+
if bytes(readback) == payload:
164+
print("readback_ok")
165+
i2c.deinit()
166+
print("done")
167+
"""
168+
169+
170+
@pytest.mark.circuitpy_drive({"code.py": WRITE_READBACK_CODE})
171+
def test_i2cdevice_write_then_read_roundtrip(circuitpython):
172+
"""Writing bytes to the EEPROM and reading them back returns the written values."""
173+
circuitpython.wait_until_done()
174+
175+
output = circuitpython.serial.all_output
176+
assert "readback_ok" in output
177+
assert "done" in output
178+
179+
180+
SLICE_CODE = """\
181+
import board
182+
from adafruit_bus_device.i2c_device import I2CDevice
183+
184+
i2c = board.I2C()
185+
device = I2CDevice(i2c, 0x50)
186+
187+
# Use start/end kwargs to send only a slice of the outgoing buffer.
188+
out = bytearray([0xAA, 0x00, 0xBB])
189+
dest = bytearray(4)
190+
with device:
191+
# Only send the middle byte (the memory address 0x00).
192+
device.write_then_readinto(out, dest, out_start=1, out_end=2, in_start=0, in_end=2)
193+
print(f"partial read: {[hex(b) for b in dest]}")
194+
# Only the first two entries should have been written by the read.
195+
if dest[0] == 0xFF and dest[1] == 0xFF and dest[2] == 0x00 and dest[3] == 0x00:
196+
print("slice_ok")
197+
i2c.deinit()
198+
print("done")
199+
"""
200+
201+
202+
@pytest.mark.circuitpy_drive({"code.py": SLICE_CODE})
203+
def test_i2cdevice_buffer_slices(circuitpython):
204+
"""write_then_readinto honors out_start/out_end and in_start/in_end bounds."""
205+
circuitpython.wait_until_done()
206+
207+
output = circuitpython.serial.all_output
208+
assert "slice_ok" in output
209+
assert "done" in output
210+
211+
212+
DISABLED_CODE = """\
213+
import board
214+
from adafruit_bus_device.i2c_device import I2CDevice
215+
216+
i2c = board.I2C()
217+
try:
218+
device = I2CDevice(i2c, 0x50)
219+
print("unexpected_success")
220+
except (ValueError, OSError) as e:
221+
print(f"probe_failed: {e}")
222+
i2c.deinit()
223+
print("done")
224+
"""
225+
226+
227+
@pytest.mark.circuitpy_drive({"code.py": DISABLED_CODE})
228+
@pytest.mark.disable_i2c_devices("eeprom@50")
229+
def test_i2cdevice_probe_fails_when_device_disabled(circuitpython):
230+
"""Probe fails when the AT24 emulator device is disabled on the bus."""
231+
circuitpython.wait_until_done()
232+
233+
output = circuitpython.serial.all_output
234+
assert "probe_failed" in output
235+
assert "unexpected_success" not in output
236+
assert "done" in output

0 commit comments

Comments
 (0)