Project 6: I2C Mapmaker - Multi-Sensor Bus Explorer
Scan and profile an I2C bus, detect devices, measure timing, and recover from bus faults.
Quick Reference
| Attribute | Value |
|---|---|
| Difficulty | Level 2: Intermediate |
| Time Estimate | 10-15 hours |
| Main Programming Language | C++ |
| Alternative Programming Languages | C |
| Coolness Level | Level 3: Genuinely Clever |
| Business Potential | 2. The “Component” Business |
| Prerequisites | C/C++ basics, Teensyduino setup, basic electronics, ability to use a multimeter/logic analyzer |
| Key Topics | I2C bus, addressing, pull-ups, fault recovery |
1. Learning Objectives
By completing this project, you will:
- Explain the core question for this project in your own words.
- Implement the main workflow and validate it with measurements.
- Handle at least two failure modes and document recovery.
- Produce a deterministic report that matches hardware behavior.
2. All Theory Needed (Per-Concept Breakdown)
I2C Bus Physics, Addressing, and Robust Scans
Fundamentals I2C is a two-wire open-drain bus where devices share SDA and SCL. Pull-up resistors are required because devices only drive the bus low. Each transaction includes an address and acknowledgments. Reliability depends on pull-ups, wiring length, and device behavior like clock stretching.
Deep Dive into the concept Open-drain signaling means rise time depends on pull-up value and bus capacitance. If the rise time is too slow, the bus violates timing specs and errors appear. Many sensors have configurable addresses, so address conflicts are common. A robust scan attempts all addresses, checks ACKs, and records timing. Clock stretching allows a slow device to hold SCL low; firmware must either allow it or time out and recover. A stuck bus can be recovered by toggling SCL as GPIO until the slave releases SDA. This project turns I2C into an observable system with error logs, timing measurements, and recovery routines rather than a best-effort library call.
How this fit on projects This concept directly powers the implementation choices and validation steps in this project.
Definitions & key terms
- Open-drain: Signaling method where devices only pull lines low.
- ACK/NACK: Acknowledgment bits indicating success or failure.
- Clock stretching: Slave holds SCL low to delay the master.
- Repeated start: I2C sequence that keeps control of the bus between ops.
Mental model diagram (ASCII)
Master -> SDA/SCL bus -> Multiple sensors
How it works (step-by-step)
- Calculate pull-up value for bus capacitance.
- Scan all 7-bit addresses and log ACKs.
- Read device IDs and verify timing.
- Handle NACKs and recover from bus lockups.
Minimal concrete example
Wire.beginTransmission(addr);
Wire.write(reg);
Wire.endTransmission(false);
Wire.requestFrom(addr, 2);
Common misconceptions
- I2C works at any wire length.
- All devices handle clock stretching the same way.
- A scan is enough to guarantee a healthy bus.
Check-your-understanding questions
- Why do pull-ups affect maximum I2C speed?
- What does a NACK tell you about a device?
- How do you recover a stuck bus?
Check-your-understanding answers
- Slow rise times violate timing and reduce reliable speed.
- The device did not respond or was not ready.
- Toggle SCL manually and re-init the bus.
Real-world applications
- Sensor networks
- EEPROM access
- Power management ICs
Where you’ll apply it
- See §3.2 Functional Requirements and §5.10 Implementation Phases in this file.
- Also used in: P07-spi-throughput-bench-high-speed-sensor-logger.md, P16-teensy-data-logger-sd-card-sensor-archive.md
References
- I2C specification (NXP)
- The Book of I2C (Randall Hyde)
Key insights
I2C reliability depends on electrical details as much as software.
Summary
A good I2C master measures, logs, and recovers from bus faults.
Homework/Exercises to practice the concept
- Calculate pull-up for a 200 pF bus at 400 kHz.
- Write a bus recovery routine and test a stuck bus.
Solutions to the homework/exercises
- Use tr = 0.8473RC to estimate R.
- Switch SCL to GPIO and pulse until SDA releases.
3. Project Specification
3.1 What You Will Build
Scan and profile an I2C bus, detect devices, measure timing, and recover from bus faults.
3.2 Functional Requirements
- Scan all 7-bit addresses and detect devices.
- Measure bus rise time and adjust pull-ups.
- Recover from a stuck SDA/SCL line.
- Export a bus map report.
3.3 Non-Functional Requirements
- Performance: Meet the target timing/throughput for the project.
- Reliability: Detect errors and recover without undefined behavior.
- Usability: Provide clear logs and a repeatable workflow.
3.4 Example Usage / Output
./P06-i2c-mapmaker-multi-sensor-bus-explorer --run
3.5 Data Formats / Schemas / Protocols
CSV with columns: address, ack, rise_time_ns, notes
3.6 Edge Cases
- Two devices with same address
- Clock stretching by slow device
- Bus stuck low
3.7 Real World Outcome
You will run the project and see deterministic logs and measurements that match physical hardware behavior.
3.7.1 How to Run (Copy/Paste)
cd project-root
make
./P06-i2c-mapmaker-multi-sensor-bus-explorer --run
3.7.2 Golden Path Demo (Deterministic)
Use a fixed input configuration and a known test signal. Capture output for 60 seconds and verify it matches expected values.
3.7.3 If CLI: exact terminal transcript
$ ./P06-i2c-mapmaker-multi-sensor-bus-explorer --run --seed 42
[INFO] I2C Mapmaker - Multi-Sensor Bus Explorer starting
[INFO] Report saved to data/report.csv
[INFO] Status: OK
$ echo $?
0
Failure Demo (Deterministic)
$ ./P06-i2c-mapmaker-multi-sensor-bus-explorer --run --missing-device
[ERROR] Device not detected
$ echo $?
2
4. Solution Architecture
4.1 High-Level Design
Inputs -> Acquisition -> Processing -> Output/Log
4.2 Key Components
| Component | Responsibility | Key Decisions |
|---|---|---|
| Acquisition | Configure peripherals and capture data | Use stable clock settings |
| Processing | Convert raw data to meaningful values | Apply calibration/filters |
| Output/Log | Emit reports and logs | CSV for reproducibility |
4.3 Data Structures (No Full Code)
struct Sample {
uint32_t timestamp_us;
uint32_t value;
uint32_t flags;
};
4.4 Algorithm Overview
Key Algorithm: Measurement + Report
- Initialize hardware and verify configuration.
- Capture data and record timestamps.
- Compute metrics and write report.
Complexity Analysis:
- Time: O(n) in samples
- Space: O(n) for log storage
5. Implementation Guide
5.1 Development Environment Setup
# Arduino IDE + Teensyduino must be installed
# Optional CLI workflow
arduino-cli core update-index
arduino-cli core install teensy:avr
5.2 Project Structure
project-root/
├── src/
│ ├── main.ino
│ ├── hw_config.h
│ └── measurements.cpp
├── tools/
│ └── analyze.py
├── data/
│ └── samples.csv
└── README.md
5.3 The Core Question You’re Answering
“How do I make an I2C bus reliable and observable?”
5.4 Concepts You Must Understand First
Stop and research these before coding:
- I2C bus, addressing, pull-ups, fault recovery
- Data logging and measurement techniques
- Basic timing math and error analysis
5.5 Questions to Guide Your Design
- What pull-up value matches your bus capacitance?
- How will you detect clock stretching?
- How will you recover from a stuck bus?
5.6 Thinking Exercise
Compute expected rise time for two pull-up values and compare to measurement.
5.7 The Interview Questions They’ll Ask
- Why is I2C open-drain?
- What is a repeated start?
- How do you handle NACKs?
5.8 Hints in Layers
- Start with a single sensor and add devices one by one.
- Use a logic analyzer to measure rise time.
- Implement a GPIO-based bus reset routine.
5.9 Books That Will Help
| Topic | Book | Chapter | |——-|——|———| | I2C basics | The Book of I2C | Ch. 1-3 | | Embedded buses | Making Embedded Systems | Ch. 6 | | Digital design | Digital Design and Computer Architecture | Ch. 4 |
5.10 Implementation Phases
Phase 1: Foundation (4 hours)
Goals:
- Implement address scan
- Measure rise time
Tasks:
- Implement address scan
- Measure rise time
Checkpoint: Bus map started
Phase 2: Core Functionality (6 hours)
Goals:
- Add fault recovery
- Log timing
Tasks:
- Add fault recovery
- Log timing
Checkpoint: Robust scan tool
Phase 3: Polish (3 hours)
Goals:
- Document bus map
- Compare pull-ups
Tasks:
- Document bus map
- Compare pull-ups
Checkpoint: Final report
5.11 Key Implementation Decisions
| Decision | Options | Recommendation | Rationale | |———-|———|—————-|———–| | Buffering | Single buffer, double buffer | Double buffer | Avoids data loss during processing | | Logging format | CSV, binary | CSV | Human-readable while still scriptable | | Clock speed | Default, overclock | Default | Keeps peripherals in spec |
6. Testing Strategy
6.1 Test Categories
| Category | Purpose | Examples | |———-|———|———-| | Unit Tests | Validate math, parsing, and conversions | Timer math, CRC checks | | Integration Tests | Verify peripherals and pipelines | DMA -> buffer -> log | | Edge Case Tests | Handle boundary conditions | Brownout, missing sensor |
6.2 Critical Test Cases
{test_cases}
6.3 Test Data
Use a fixed test input pattern and record outputs to data/report.csv
7. Common Pitfalls & Debugging
7.1 Frequent Mistakes
| Pitfall | Symptom | Solution | |———|———|———-| {pitfalls}
7.2 Debugging Strategies
{debug_strats}
7.3 Performance Traps
Large buffers improve stability but increase latency. Measure both throughput and jitter to choose the right size.
8. Extensions & Challenges
8.1 Beginner Extensions
{ex_begin}
8.2 Intermediate Extensions
{ex_inter}
8.3 Advanced Extensions
{ex_adv}
9. Real-World Connections
9.1 Industry Applications
{industry_apps}
9.2 Related Open Source Projects
{open_source}
9.3 Interview Relevance
{interview_rel}
10. Resources
10.1 Essential Reading
{resources}
10.2 Video Resources
- Embedded systems timing walkthrough (YouTube)
- Teensy hardware deep dive (Conference talk)
10.3 Tools & Documentation
- Teensyduino: Toolchain for Teensy boards
- Logic Analyzer: Timing verification
- Multimeter: Voltage and current measurement
10.4 Related Projects in This Series
{related_projects}
11. Self-Assessment Checklist
11.1 Understanding
- I can explain the main concept without notes.
- I can explain why the measurements match (or do not match) expectations.
- I understand at least one tradeoff made in this project.
11.2 Implementation
- All functional requirements are met.
- All critical test cases pass.
- Logs and reports are reproducible.
- Edge cases are handled.
11.3 Growth
- I documented lessons learned.
- I can explain this project in a job interview.
- I identified one improvement for next iteration.
12. Submission / Completion Criteria
Minimum Viable Completion: {comp_min}
Full Completion: {comp_full}
Excellence (Going Above & Beyond): {comp_ex}