scanner_i2c.py
Before writing an I2C sensor driver, the bus must answer: this script sweeps SCL/SDA from 0x08 to 0x77 and lists present addresses. The wiring reflex of every CircuitPython project with an external sensor.
Hardware
An Adafruit Feather nRF52840 Express (nRF52840 Cortex-M4F SoC, BLE 5 / 802.15.4) with on-board NeoPixel, red LED, 2 MB QSPI flash for the CircuitPython filesystem and a LiPo battery connector. An I2C sensor or module (BME680, MPU6050, OLED…) wired to the SCL/SDA pins marked on the Feather.
Wiring
SCL→SCL, SDA→SDA, VCC→3V3, GND→GND. Pull-up resistors already sit on most modules; otherwise add 4.7 kΩ to 3.3 V.
How it works
busio.I2C opens the hardware bus; try_lock takes the bus then scan() probes each address by ACK. Answers print in hex, ready to paste into the driver code.
Running it
CircuitPython (feather_nrf52840_express build) installed via the UF2 bootloader (double reset); edit code.py on the CIRCUITPY drive, console in Mu/Thonny.
Variants
Switch pins for a second software bus, loop the scan to catch dropouts, or chain straight into reading the found sensor.
scanner_i2c.py
# Scanner le bus I2C du board (SCL/SDA) et lister les adresses vives.
# Utile avant d'ajouter un capteur : confirme câblage et adresse.
import time
import board
import busio
scl = board.SCL
sda = board.SDA
i2c = busio.I2C(scl, sda)
print("Balayage I2C de 0x08 à 0x77...")
trouves = []
for adresse in range(0x08, 0x78):
# try_lock/unlock = la méthode portable pour sonder un esclave
if i2c.try_lock():
try:
if adresse in i2c.scan():
pass
finally:
i2c.unlock()
# scan() renvoie la liste complète : on la prend une fois
if i2c.try_lock():
try:
tr = i2c.scan()
finally:
i2c.unlock()
for a in tr:
if a not in trouves:
trouves.append(a)
print("Esclave trouvé : 0x%02X" % a)
print("Terminé :", len(trouves), "périphérique(s).")