# 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**. ```mermaid 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. ```mermaid 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
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.