Testing

RX Firmware Testing Guide (MessageRouter Architecture)

Build & Upload

1. Build the RX Firmware

pio run -e rx_board

Expected output:

  • All modules compile without errors

  • Linking succeeds with all symbols resolved

  • Binary size within ESP32 flash limits

2. Upload to RX Board

pio run -e rx_board -t upload

Upload port: as configured in platformio.ini (example: COM8)

3. Monitor Serial Output

pio device monitor -p COM8 -b 115200

Expected boot sequence log:

System booting...
Initializing display...
Waiting for CAN data...

Functional Testing

Test 1: Boot Sequence Validation

Prerequisites: RX board powered, TX board OFF

Expected Behavior:

  1. Serial output shows state transitions:

    • “System booting…” (Boot state)

    • “Initializing display…” (DisplayInit state)

    • “Waiting for CAN data…” (WaitingForData state)

  2. TFT display shows UI with:

    • Speed arc at 0

    • Turn indicators off (transparent)

    • No warning overlays

Pass Criteria: System reaches WaitingForData state without faults


Test 2: Active State Entry

Prerequisites: RX board in WaitingForData state, TX board ready

Test Steps:

  1. Power on TX board (or start transmitting)

  2. Observe serial output

  3. Observe TFT display

Expected Behavior:

  1. Serial: “System active”

  2. Display updates with received cluster data:

    • Speed arc reflects transmitted speed value

    • Turn signals toggle based on TX input

  3. No “STALE DATA” warning

Pass Criteria:

  • State transition WaitingForData → Active occurs

  • UI widgets update in real-time

  • HealthMonitor timestamp resets on each frame


Test 3: Degraded State (Timeout Detection)

Prerequisites: RX board in Active state, TX board transmitting

Test Steps:

  1. Disconnect TX board or stop transmission

  2. Wait for timeout interval (default 1500 ms; configurable)

  3. Observe serial and display

Expected Behavior:

  1. Serial: “WARNING: Stale data detected” (within ~1.5s by default)

  2. Display shows yellow “STALE DATA” overlay at top center

  3. UI widgets freeze at last received values

Pass Criteria:

  • FrameTimeout event generated by HealthMonitor

  • State transition Active → Degraded occurs

  • Warning overlay appears


Test 4: Auto-Recovery from Degraded

Prerequisites: RX board in Degraded state

Test Steps:

  1. Reconnect TX board or resume transmission

  2. Observe serial and display

Expected Behavior:

  1. Serial: “System active” (on first new frame)

  2. “STALE DATA” warning disappears

  3. UI resumes updating with new cluster data

Pass Criteria:

  • State transition Degraded → Active occurs

  • Warning overlay hidden

  • Widget updates resume


Test 5: Fault State Display

Prerequisites: RX board powered OFF

Test Steps:

  1. Disconnect I2C touchscreen (simulate init failure)

  2. Power on RX board

  3. Observe serial and display

Expected Behavior:

  1. Serial shows:

    • “System booting…”

    • “Initializing display…”

    • “FAULT: System halted”

  2. Display shows red “SYSTEM FAULT” overlay centered

Pass Criteria:

  • InitFail event generated during boot

  • State transition to Fault occurs

  • Fault overlay visible

  • System halts updates (no further state changes)


Regression Testing

Test 6: CAN Message Validation

Objective: Verify ISR callback filters invalid frames

Test Cases:

Frame ID

DLC

IDE

Expected Result

0x65

3

0

Accepted, ClusterFrame

0x66

3

0

Rejected (wrong ID)

0x65

2

0

Rejected (DLC too short)

0x65

3

1

Rejected (wrong IDE)

Pass Criteria: Only valid frames (ID=0x65, DLC≥3, IDE=0) trigger UI updates


Test 7: UI Widget Mapping

Objective: Verify DBC signal → widget mapping

Test Data (inject via TX board):

Speed Raw

Expected Arc

Left Signal

Left Opacity

Right Signal

Right Opacity

0

0

0

0 (off)

0

0 (off)

2048

120

1

255 (on)

0

0 (off)

4095

240 (max)

0

0 (off)

1

255 (on)

Pass Criteria: Arc value = (speed_raw * 240) / 4095, opacity toggles correctly


Test 8: Event Queue Saturation

Objective: Verify queue handles high CAN traffic without loss

Test Steps:

  1. Configure TX board to send at max rate (e.g., 100 Hz)

  2. Run for 60 seconds

  3. Monitor serial for queue overflow warnings

Pass Criteria:

  • No lost events (EventQueue uses FreeRTOS buffer)

  • UI remains responsive

  • No watchdog resets


Performance Benchmarks

Test 9: Loop Timing

Measurement: Time between loop() iterations

Method: Add timing code:

static uint32_t lastLoop = 0;
void loop() {
  uint32_t now = millis();
  uint32_t delta = now - lastLoop;
  if (delta > 10) {
    Serial.printf("Loop delay: %u ms\n", delta);
  }
  lastLoop = now;
  // ... rest of loop
}

Target: <10ms typical, <50ms worst-case


Test 10: Timeout Accuracy

Measurement: Time from last frame to FrameTimeout event

Method:

  1. Inject timestamp logging in HealthMonitor::NotifyFrame()

  2. Stop TX transmission

  3. Measure time to “WARNING: Stale data detected”

Target: 1500ms ±100ms (default; adjust if kTimeoutMs changed)


Troubleshooting Guide

Symptom: Display shows garbage/flicker

Diagnosis: SPI conflict or buffer corruption
Fix: Check TFT_eSPI pin config, verify lvgl buffer allocation

Symptom: Touch input not working

Diagnosis: I2C (Wire) not initialized or wrong address
Fix: Verify Wire.begin() called before UiController::Init()

Symptom: “STALE DATA” never appears

Diagnosis: HealthMonitor not being called in loop
Fix: Verify SystemController::Update() called every iteration

Symptom: Stuck in WaitingForData despite TX transmitting

Diagnosis: CAN ISR not firing or event queue full
Fix:

  • Check CAN bus termination resistors

  • Verify frame ID matches Cluster_CANID (0x65)

  • Increase EventQueue size in EventQueue::Init()

Symptom: Random resets/crashes

Diagnosis: Stack overflow or ISR corruption
Fix:

  • Reduce lvgl buffer size (lvglBufferSizePixels)

  • Verify no heavy processing in ISR callbacks

  • Check FreeRTOS stack sizes in sdkconfig


Automated Testing (Future Enhancement)

Unit Test Framework Setup

  1. Add test/ directory with PlatformIO test framework

  2. Mock hardware dependencies (CAN, TFT, Touch)

  3. Test each module in isolation

Example test cases:

  • test_event_queue.cpp: Push/pop operations

  • test_state_machine.cpp: All state transitions

  • test_health_monitor.cpp: Timeout edge cases

  • test_ui_scaling.cpp: Speed → arc value conversion

CI/CD Integration

Add to .github/workflows/build.yml:

- name: Build RX Firmware
  run: pio run -e rx_board
- name: Run Unit Tests
  run: pio test -e native

Sign-Off Checklist

Before release, verify:

  • [ ] All 10 tests above pass

  • [ ] No memory leaks (run for 24hrs without reset)

  • [ ] Build warnings = 0

  • [ ] Serial logging disabled or DEBUG-only in production

  • [ ] Code reviewed against README.md and ARCHITECTURE.md

  • [ ] All doc references updated (README.md, ARCHITECTURE.md, .github/copilot-instructions.md)

Tested By: _______________
Date: _______________
Hardware Rev: _______________
Firmware SHA: _______________