-
Notifications
You must be signed in to change notification settings - Fork 3
I2CDeviceScanning
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.
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.
The scanner operates in different modes that progress through the initialization and steady-state operation:
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 | 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 |
- 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)
Unlike simple linear scanning (0x01, 0x02, 0x03...), the I2C scanner uses priority lists to scan addresses in an intelligent order based on:
- Common device addresses: Addresses that are frequently used by popular I2C devices
- Device type likelihood: Addresses associated with device types registered in the system
-
User boost addresses: User-specified high-priority addresses via
scanBoostconfiguration
Device addresses are organized into priority lists (typically 3-5 lists). The scanner cycles through these lists with different frequencies:
- Priority 0 (Highest): Scanned most frequently
- Priority 1: Scanned less frequently
- Priority 2: Scanned even less frequently
- Priority N: Scanned least frequently
The scan frequency for each priority level follows a quadratic progression: priority N is scanned every SCAN_PRIORITY_COUNTS[N] sweeps through higher priorities.
Example Priority Counts:
// Priority 0: Every 1 sweep
// Priority 1: Every 2 sweeps
// Priority 2: Every 4 sweeps
// Priority 3: Every 9 sweeps- Faster detection of common devices (e.g., IMUs, displays, sensors)
- Reduced bus overhead by scanning less common addresses less frequently
-
Customizable through
scanBoostconfiguration for application-specific devices
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.
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 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
Multiplexers are detected during the MAIN_BUS_MUX_ONLY phase:
- Scanner attempts to communicate with addresses in the configured multiplexer range
- Devices that respond and exhibit multiplexer behavior are registered as multiplexers
- Detection requires multiple consecutive successful responses (threshold-based detection to avoid false positives)
- Once detected, the scanner builds a list of available slots for subsequent scanning phases
To optimize detection of main-bus devices while still checking all slots:
- Main bus scanned twice per cycle (indices 0 and 1 in slot sequence)
- Multiplexer slots scanned once per cycle (indices 2 onwards)
This ensures main bus devices are detected quickly while still covering all slots.
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)
- 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
| Mode | Speed | Purpose |
|---|---|---|
| Fast Scan | Maximum (time-limited per loop) | Initial device detection, hot-swap detection |
| Slow Scan | Periodic (configurable interval) | Background monitoring for device changes |
Fast Scanning:
- Runs continuously during
SCAN_FASTmode - Time-limited per service loop iteration (
maxFastTimeInLoopUs) - Used for rapid initial detection and when changes are expected
Slow Scanning:
- Runs periodically during
SCAN_SLOWmode - Configurable period via
busScanPeriodMs(default: typically 10-30 seconds) - Monitors for device hot-swapping in steady state
{
"DevMan": {
"busScanPeriodMs": 20000
}
}A scan sweep is one complete pass through all addresses (and slots) that need to be scanned according to the current priority level.
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
For each address to be scanned:
- Get Next Address: Priority-based selection from scan lists
-
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
- Enable Multiplexer Slot: If scanning a slot, enable the appropriate multiplexer configuration
- Perform Scan: Send I2C transaction to test device presence
-
Handle Result:
- Device responds: Mark online, trigger device identification
- No response: Mark offline or not present
- Bus stuck: Attempt recovery procedures
- Disable Multiplexer Slots: Return multiplexers to disabled state
- Update State: Update bus element tracker and status manager
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
The scanner includes sophisticated bus stuck detection and recovery:
- Bus stuck conditions detected during scan transactions
- Can occur on main bus or when enabling multiplexer slots
- Bus Clocking: Send clock pulses to release stuck devices
- Slot Disabling: Disable all multiplexer slots
- Power Cycling: If power control available, cycle power to problematic slot(s)
- Multiplexer Reset: Use hardware reset pins to reset multiplexers
Before Slot Enable (main bus issue):
- Clock the bus
- Disable all slots
- Power cycle entire bus (if available)
After Slot Enable (single slot issue):
- Clock the bus
- Disable the problematic slot
- Power cycle the single slot (if available)
- If still stuck, escalate to full bus recovery
Multiple recovery attempts are made before declaring the bus permanently stuck.
The scanner supports device hot-swapping through:
- Continuous Monitoring: Slow scan mode periodically checks all addresses
- Online/Offline Tracking: Threshold-based detection to avoid transient false positives
- Device Re-identification: When a device comes online, identification runs again
- Graceful Offline Handling: Devices that go offline are marked as such without disrupting other devices
To avoid false positives from transient communication errors:
- Multiple consecutive successful scans required to mark a device as online
- Multiple consecutive failed scans required to mark a device as offline
The scanner is serviced from the I2C task at regular intervals:
bool taskService(uint64_t curTimeUs,
uint64_t maxFastTimeInLoopUs,
uint64_t maxSlowTimeInLoopUs)- 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.
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.
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
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
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
- I2C Bus - I2C bus configuration and bus worker task overview
- I2C Device Identification and Polling - How device types are identified and data is retrieved
- Device Data Publishing - How device data is published
- DeviceManager SysMod - DeviceManager features and API
- DeviceManager Settings - Configuration settings for DeviceManager