Skip to content

I2CDeviceScanning

Rob Dobson edited this page Oct 27, 2026 · 7 revisions

I2C Device Scanning

Overview

The I2C bus scanning system automatically detects devices connected to the I2C bus, including devices connected through bus multiplexers. Scanning is distinct from polling: scanning detects which devices are present and determines their type, while polling retrieves data from known online devices.

The scanning process is sophisticated, using priority-based address ordering rather than simple linear scanning, and handles complex scenarios including bus stuck conditions, and hot-swapping of devices.

Scan vs Poll

It's important to understand the distinction between these two operations:

Operation Purpose Frequency Target
Scanning Detect device presence and identify device type Variable (fast initially, then slow periodic) All potential I2C addresses
Polling Retrieve data from known devices Regular intervals (device-specific) Only identified, online devices

Scanning discovers what devices are present; polling retrieves data from those devices.

Scan Modes

The scanner operates in different modes that progress through the initialization and steady-state operation:

Scan Mode Progression

IDLE
  ↓
MAIN_BUS_MUX_ONLY  (Detect bus multiplexers on main bus)
  ↓
MAIN_BUS          (Scan all addresses on main bus)
  ↓
SCAN_FAST         (Fast scanning of all addresses including slots)
  ↓
SCAN_SLOW         (Periodic slow scanning)

Mode Descriptions

Mode Purpose Duration Slot Scanning
IDLE Initial state before scanning starts Momentary No
MAIN_BUS_MUX_ONLY Detect bus multiplexers only Until multiplexers stable Main bus only
MAIN_BUS Scan main bus for all devices Until sweep complete Main bus only
SCAN_FAST Rapid detection of all devices Until sweep complete Main bus + all slots
SCAN_SLOW Periodic background scanning Ongoing (configurable period) Main bus + all slots

Mode Transitions

  • IDLE → MAIN_BUS_MUX_ONLY: On initialization or when requested
  • MAIN_BUS_MUX_ONLY → MAIN_BUS: After multiplexers detected and stable
  • MAIN_BUS → SCAN_FAST: After main bus scan complete
  • SCAN_FAST → SCAN_SLOW: After fast scan sweep complete
  • Any → MAIN_BUS_MUX_ONLY: When multiplexer state changes (new mux detected or mux online/offline status changes)

Priority-Based Scanning

Unlike simple linear scanning (0x01, 0x02, 0x03...), the I2C scanner uses priority lists to scan addresses in an intelligent order based on:

  1. Common device addresses: Addresses that are frequently used by popular I2C devices
  2. Device type likelihood: Addresses associated with device types registered in the system
  3. Boost addresses: high-priority addresses from the scanBoost configuration, and those an identification hook sets at runtime (setScanBoostAddrs())

Scan Priority Lists

The lists come from the device type records (each record's scanPriority, built at runtime so records from the device type file count too), plus the boost addresses in the first list:

  • List 0: the primary address of each high-priority record, and the boost addresses (scanBoost and setScanBoostAddrs(); a boosted address is scanned in this list only)
  • List 1: the other addresses of high-priority records, and medium-priority records
  • List 2: every other address that something could claim - a device type record identifies or claims it, or it is a multiplexer address
  • Background: every other address, including those only an identification hook handles. They are probed slowly, spread evenly over backgroundSweepMs (default 10 s for every such address on the main bus and every slot). A device found there is identified as anywhere else - records first, then the hook if it handles the address - or appears as an unidentified element

List 0 is scanned a whole slot position at a time. After each run of list 0, list 1 scans a chunk of (its size / 3) addresses and list 2 a chunk of (its size / 9), fractions carried over. Each list gets the same share of probes as if it were scanned whole every 3 or 9 runs of list 0, but a list-0 address comes round again after one chunk of each lower list rather than after a whole lower list.

An identification hook declares the addresses it handles with setNewDeviceIdentAddrRanges() (a hook that declares none handles every address). Handling an address doesn't make it scanned often: the hook boosts the addresses where it expects its devices with setScanBoostAddrs(), and the rest of its addresses are covered by the background sweep. Robotical's Axiom RSAO support, for example, handles 0x0F and 0x10-0x6F minus its excluded ranges, and boosts 0x0F and its first-choice module addresses: a module there is found in about 2 s, and one elsewhere within the 10 s sweep. (RaftI2C 1.23.0 and later; before that, every address the hook handled was in list 2.)

Benefits of Priority Scanning

  • Faster detection of common devices (e.g., IMUs, displays, sensors)
  • Reduced bus overhead by scanning less common addresses less frequently
  • Customizable through scanBoost configuration for application-specific devices

Scan Boost Configuration

High-priority addresses can be specified in configuration:

{
    "DevMan": {
        "scanBoost": ["0x6a", "0x36", "0x70"]
    }
}

These addresses are added to Priority 0 (highest priority) and will be scanned most frequently.

Bus Multiplexer Support

The scanning system supports I2C bus multiplexers (e.g., TCA9548A, PCA9548A) which allow multiple devices with the same address to coexist on the same bus by connecting them to different multiplexer slots.

Slot Numbering

  • Slot 0: Main I2C bus (no multiplexer)
  • Slots 1-N: Multiplexer slots (1-based numbering)

For a system with 2 multiplexers, each with 8 slots:

  • Main bus: Slot 0
  • Mux 0, Slot 0-7: Slots 1-8
  • Mux 1, Slot 0-7: Slots 9-16

Multiplexer Detection

Multiplexers are detected during the MAIN_BUS_MUX_ONLY phase:

  1. Scanner attempts to communicate with addresses in the configured multiplexer range
  2. Devices that respond and exhibit multiplexer behavior are registered as multiplexers
  3. Detection requires multiple consecutive successful responses (threshold-based detection to avoid false positives)
  4. Once detected, the scanner builds a list of available slots for subsequent scanning phases

Multiplexer Slot Scanning Order

Each list is scanned on the main bus and every multiplexer slot in a cycle of slot visits, spread evenly through the cycle. How often a slot is visited depends on its activity:

Slot Visits per cycle (default)
Main bus 2
A slot where something came online or went in the last slotChangedMs (10 s), or whose multiplexer changed slotChangedWeight (3)
A slot with an online device slotKnownWeight (2)
Any other slot 1

Every slot is in every cycle, so a quiet slot is still covered; the cycle is the sum of the visits (e.g. 13 for the main bus and 8 slots of which 3 have devices, against 10 with no weighting). devman/busstatus reports scan.slots, slotsKnown, slotsChanged and slotCycle. A slot stays selected for consecutive probes on it (only one slot is ever selected), so the multiplexer isn't rewritten for every probe.

Multiplexer Supervision and Changes

Every muxCheckMs (100 ms) one multiplexer address is probed, in turn, on the slot it is reached through, so a multiplexer that goes (a hub unplugged) or comes is noticed within a few hundred ms. When one goes, the devices on its slots are reported as gone (including devices that aren't polled). After startup, a multiplexer change doesn't restart scanning: its slots are marked as changed (so they are visited most) and a fast scan runs for up to fastScanMaxMs. During startup the multiplexers are scanned again from the start, as before.

A device that stays off the bus until it gets a general call (its record has a generalCall, e.g. the Excelitas CaliPile) is woken by the scanner: in the fast and slow modes it sends the general calls of the enabled records on one slot at a time in turn, a full round of the slots taking generalCallRoundMs (2 s), and the device is then found by the ordinary scan. busstatus counts them (scan.generalCalls). (RaftI2C versions after 1.24.0)

Avoiding False Detections

The scanner implements several checks to avoid incorrectly detecting the same device multiple times:

  • Main bus devices excluded from slot scans: If a device is detected on the main bus, it won't be reported as present on multiplexer slots (even though it will respond on all slots)
  • New slot devices checked on the main bus: a device that answers on a slot but isn't known there is probed on the main bus (slots disabled) first; if it answers there, it is a main-bus device (one that has just come back) and is recorded on the main bus
  • Multiplexer address slot matching: Multiplexer addresses are only scanned on their correct slot
  • IO Expander exclusion: Defined IO expander addresses are excluded from scanning to avoid conflicts

Scan Timing

Fast vs Slow Scanning

Mode Speed Purpose
Fast Scan As fast as the pass allows (fastScanMaxUnyieldMs per pass) After startup or requestScan(fast); ends after its sweeps or fastScanMaxMs (default 2 s)
Slow Scan A share of the bus Steady state: finding devices that are plugged in or removed

Slow scanning uses a share of bus time that falls as polling needs more: scanShareMaxPct (default 30%) on a quiet bus, reduced in proportion to the poll load down to scanShareMinPct (default 10%), so a busy bus keeps time for polls and a quiet one finds devices quickly. The time is earned continuously and spent by each pass of the bus task (at most slowScanMaxUnyieldMs per pass, and not more often than every busScanPeriodMs). devman/busstatus reports the share in use (scan.sharePct).

Coming online: an address must answer twice before it is online. After the first answer it is probed again on the next pass (a few ms later) rather than when the scan comes round again, so a new device is online within a few ms of being seen, and identified straight after.

Configuration (I2C bus settings)

{
    "busScanPeriodMs": 5,
    "scanShareMinPct": 10,
    "scanShareMaxPct": 30,
    "backgroundSweepMs": 10000,
    "fastScanMaxMs": 2000
}

Measured (FeatherS3, 100 kHz, 2026-10-16)

A device that comes back on the main bus at a high-priority address is identified 45-110 ms later with light polling, and 50-265 ms later with about half the bus used by polling (previously 86-206 ms and 430-475 ms).

Scan Sweep

A scan sweep is one complete pass through all addresses (and slots) that need to be scanned according to the current priority level.

Sweep Completion Criteria

A sweep is complete when:

  • All addresses in the current priority list have been scanned
  • All applicable slots have been checked for the current address set

After sweep completion:

  • The mode may transition to the next mode (e.g., MAIN_BUS → SCAN_FAST)
  • Priority lists are cycled according to their priority counts
  • Sweep timing is recorded for performance monitoring

Scan Process Flow

Single Address Scan

For each address to be scanned:

  1. Get Next Address: Priority-based selection from scan lists
  2. Slot Validation:
    • Skip if address is on main bus and we're scanning a slot
    • Skip if it's a multiplexer on the wrong slot
    • Skip if it's a defined IO expander
    • Skip if address is currently being polled
  3. Enable Multiplexer Slot: If scanning a slot, enable the appropriate multiplexer configuration
  4. Perform Scan: Send I2C transaction to test device presence
  5. Handle Result:
    • Device responds: Mark online, trigger device identification
    • No response: Mark offline or not present
    • Bus stuck: Attempt recovery procedures
  6. Disable Multiplexer Slots: Return multiplexers to disabled state
  7. Update State: Update bus element tracker and status manager

State Updates

When a device responds during scanning:

Device Responds
  ↓
Update Status Manager (online/offline tracking)
  ↓
Bus Element Tracker (record found on main bus or slot)
  ↓
If changing to ONLINE:
  ↓
  Trigger Device Identification
  ↓
  Store Device Type Information

Bus Stuck Handling

The scanner includes sophisticated bus stuck detection and recovery:

Detection

  • Bus stuck conditions detected during scan transactions
  • Can occur on main bus or when enabling multiplexer slots

Recovery Strategies

  1. Bus Clocking: Send clock pulses to release stuck devices
  2. Slot Disabling: Disable all multiplexer slots
  3. Power Cycling: If power control available, cycle power to problematic slot(s)
  4. Multiplexer Reset: Use hardware reset pins to reset multiplexers

Recovery Attempt Order

Before Slot Enable (main bus issue):

  1. Clock the bus
  2. Disable all slots
  3. Power cycle entire bus (if available)

After Slot Enable (single slot issue):

  1. Clock the bus
  2. Disable the problematic slot
  3. Power cycle the single slot (if available)
  4. If still stuck, escalate to full bus recovery

Multiple recovery attempts are made before declaring the bus permanently stuck.

Device Hot-Swapping

The scanner supports device hot-swapping through:

  1. Continuous Monitoring: Slow scan mode periodically checks all addresses
  2. Online/Offline Tracking: Threshold-based detection to avoid transient false positives
  3. Device Re-identification: When a device comes online, identification runs again
  4. Graceful Offline Handling: Devices that go offline are marked as such without disrupting other devices

Detection Thresholds

To avoid false positives from transient communication errors:

  • Two successful probes are needed to mark a device as online (the second is made on the next pass)
  • Three consecutive failed accesses (scans or polls) mark it as offline - a success in between resets the count

Scan Service Function

The scanner is serviced from the I2C task at regular intervals:

bool taskService(uint64_t curTimeUs, 
                 uint64_t maxFastTimeInLoopUs, 
                 uint64_t maxSlowTimeInLoopUs)

Time Budget

  • Fast Scan: Allowed more time per service call for rapid detection
  • Slow Scan: Reduced time budget to minimize impact on polling operations

The function returns true if fast scanning is in progress, allowing the caller to adjust task timing.

Integration with Other Systems

Device Identification

When scanning detects a device coming online, it triggers device identification:

Scan Detects Device Online
  ↓
Device Identification Manager
  ↓
Identify Device Type (query WHO_AM_I, etc.)
  ↓
Store Device Type Information
  ↓
Begin Polling (if supported)

See I2C Device Identification and Polling for details on how device types are determined, devices are initialized, and data is retrieved through polling.

Polling Integration

Scanning avoids addresses that are currently being polled to prevent conflicts:

  • The scanner checks isAddrBeingPolled() before scanning an address
  • If an address is being polled, it is skipped during that sweep
  • This ensures polling operations (which retrieve real data) are not interrupted by scanning operations

Status Manager

The scanner continuously updates the BusStatusMgr with device online/offline status:

  • updateBusElemState(): Called for each scan result
  • informBusStuck(): Called when bus stuck condition detected
  • goingOffline(): Called when power cycling devices offline
  • setBusElemDeviceStatus(): Called with device identification results

Configuration Example

Complete configuration for scanning with multiplexers:

{
    "DevMan": {
        "busScanPeriodMs": 20000,
        "scanBoost": ["0x6a", "0x36"],
        "Mux": {
            "enable": true,
            "minAddr": "0x70",
            "maxAddr": "0x77",
            "rstPin": 5,
            "clearCascadeMux": false
        }
    }
}

Parameters:

  • busScanPeriodMs: Slow scan period in milliseconds
  • scanBoost: Array of high-priority addresses (hex strings)
  • Mux.enable: Enable multiplexer support
  • Mux.minAddr: Minimum multiplexer address
  • Mux.maxAddr: Maximum multiplexer address
  • Mux.rstPin: GPIO pin for hardware reset of multiplexers
  • Mux.clearCascadeMux: Clear cascaded multiplexer slots on detection

Related Documentation

Clone this wiki locally