Files
HIL_setup/I2C_emulation_flow.md
2026-02-09 16:36:29 +01:00

4.0 KiB

SCD30 HIL Framework Documentation

1. System Architecture

The system consists of three layers. The PC handles the Logic, the Emulator handles the Signal Bridge, and the Target runs the Production Code.

graph TD
    subgraph "PC (Python)"
        A[Global Test Script] <--> B[HIL Library]
    end
    subgraph "Emulator ESP32 (Slave)"
        C[UART Driver] <--> D[I2C Slave Driver]
    end
    subgraph "Target ESP32 (Master)"
        E[SCD30 Driver Component] <--> F[Application Logic]
    end

    B <-- "921600 Baud Serial" --> C
    D <-- "I2C Bus (Clock Stretching)" --> E


2. The Logic Workflow (Sequence)

This is the most critical part: how we synchronize a slow PC with a fast I2C bus.

sequenceDiagram
    participant PC as Python (Global Script)
    participant Slave as ESP32 Emulator (Slave)
    participant Master as ESP32 Target (Master)

    Note over Master: App calls scd30_read_data()
    Master->>Slave: I2C Write: [0x03, 0x00] (Read Cmd)
    activate Slave
    Slave->>PC: UART Send: "CMD:0300\n"
    Note right of Slave: Slave STRETCHES Clock (SCL LOW)
    
    Note over PC: Lib parses CMD, calculates<br/>Floats & CRCs
    PC->>Slave: UART Send: "DATA:010203...\n"
    
    Slave->>Slave: Parse Hex to Bytes
    Slave->>Master: Load I2C Buffer & Release SCL
    deactivate Slave
    Master->>Slave: I2C Read: 18 Bytes
    
    Note over Master: App parses bytes to Float
    Master->>PC: UART Print: "CO2: 450.00..."
    Note over PC: Global script captures & logs to CSV


3. Component Breakdown

A. Target ESP32 (scd30.c)

  • Role: Acts as the I2C Master. It believes it is talking to a real SCD30.
  • Key Logic: * Initialization: Configures scl_wait_us = 150000. This is the timeout for how long it will wait while the Slave is stretching the clock.
  • Parsing: Takes 18 bytes and reconstructs three 32-bit floats. It skips every 3rd byte because that is the CRC.
  • Polling: It continuously asks "Are you ready?" (0x0202) until the Emulator (PC) replies with "Yes" (0x01).

B. Emulator ESP32 (main.c)

  • Role: The "Transparent Bridge."
  • Key Logic:
  • Event-Driven: Uses a callback (on_recv_done) to notify a task that the Master has sent a command.
  • Clock Stretching: By not loading the TX buffer immediately, the hardware automatically holds the SCL line low. This pauses the Master's CPU.
  • Synchronization: It bridges the I2C domain (microseconds) to the PC domain (milliseconds).

C. Python Library (hil_lib.py)

  • Role: The "Sensor Physics Engine."
  • Key Logic:
  • SCD30 Protocol Emulation: It knows that if the Master sends 0202, it must reply with 000103 (Ready).
  • Data Formatting: Converts human-readable numbers (like 450.0) into the raw hex bytes and CRCs the ESP32 expects.

D. Global Script (run_test.py)

  • Role: The "Test Orchestrator."
  • Key Logic:
  • Iteration: Loops through a list of test values.
  • Verification: It acts as a "Man-in-the-Middle." It knows what value it sent to the sensor and reads the Target's serial to see what value the Target received.
  • Persistence: Saves the input/output pair into a .csv for audit and validation.

4. Hardware Connectivity

Connection Requirement Why?
SDA to SDA 4.7kΩ Pull-up to 3.3V Data signal integrity
SCL to SCL 4.7kΩ Pull-up to 3.3V Clock signal integrity
GND to GND Solid Copper Wire Reference voltage for signals
UART 0 USB Cable to PC Command/Data bridge

5. Troubleshooting the Workflow

  • Symptom: Target says Failed to read data.

  • Cause: Python script took longer than 200ms to respond OR scl_wait_us is too low.

  • Symptom: Target reads 0.00.

  • Cause: The CRC check failed in the C code, or the Python script sent the bytes in the wrong order.

  • Symptom: CSV only has Column 1, not Column 2.

  • Cause: The Target ESP32 is not printing to the correct Serial port, or the baud rate is mismatched.