Unified Device Response Protocol - Implementation Checklist¶
Project: kurikintons (OSECHI Firmware) Target Versions: v1.11.3 - v1.12.0 Owner: qumasan Status: Ready for Phase 1 Implementation Last Updated: 2025-12-08
See Also:
- Executive Summary: 2025-12-08-device-response-1-executive-summary.md - 1-page overview
- Architecture Overview: 2025-12-08-device-response-2-architecture-overview.md - Design principles, diagrams, rationale
- Schema Spec: 2025-12-08-device-response-3-schema-specification.md - JSON schema, field definitions, examples
Phase 1: Protocol Layer Definition (v1.12.0) - Estimated: 1-2 days¶
1.1 Create src/device_response.h¶
Estimated Lines: 80-100
Tasks:
- File creation with license header
- Include guards and dependency includes
-
device_response_tstruct definitiontypedef struct { JsonDocument payload; // Contains: type, status, sent_at, data fields } device_response_t; - Inline builder functions (at least these)
-
device_response_ok(const char* key, int32_t value) -
device_response_ok(const char* key, const char* value) -
device_response_error(int error_code, const char* message) - Overloaded versions for other types (float, bool, etc.)
-
device_get_timestamp()helper function with RTC conditional logic - Doxygen comments for all public APIs
- Verify no compilation warnings
Validation:
-
#include "device_response.h"compiles without errors - Function overloading resolves correctly
-
std::move()semantics verified
1.2 Create src/device_response.cpp¶
Estimated Lines: 40-60
Tasks:
- File creation with license header
- Include
device_response.hand<ArduinoJson.h> - Implement
send_device_response(const device_response_t& resp)void send_device_response(const device_response_t& resp) { serializeJson(resp.payload, Serial); Serial.println(); } - Verify JSONL output format with examples
- Handle edge cases (empty payload, large JSON, etc.)
Validation:
- Compilation succeeds
- No linker errors
- Output format matches unified schema
1.3 Modify include/config.h¶
Tasks:
- Add
ENABLE_DEVICE_RESPONSEflag (default: 1)#ifndef ENABLE_DEVICE_RESPONSE #define ENABLE_DEVICE_RESPONSE 1 // Phase 1+: opt-in enabled #endif - Add configuration comment explaining the flag
- Verify no conflicts with existing flags
Validation:
- Build succeeds with
ENABLE_DEVICE_RESPONSE=0RAM: [= ] 8.8% (used 28764 bytes from 327680 bytes) Flash: [=== ] 26.6% (used 348301 bytes from 1310720 bytes) - Build succeeds with
ENABLE_DEVICE_RESPONSE=1RAM: [= ] 8.8% (used 28764 bytes from 327680 bytes) Flash: [=== ] 26.6% (used 348389 bytes from 1310720 bytes) - Binary size difference logged
1.4 Integration Testing¶
Tasks:
- Create simple test in
text_command_manager.cpp#if ENABLE_DEVICE_RESPONSE // Test with GET_VERSION auto resp = device_response_ok("version", config_get_version()); send_device_response(resp); #endif - Capture serial output to file
- Verify JSON structure matches unified schema
-
typefield present and correct -
statusfield present and correct -
sent_atfield present and reasonable value - Response data fields present
Sample Output Verification:
{"type":"response","status":"ok","sent_at":123456,"version":"1.10.0"}
-
Test with error response
{"type":"response","status":"error","sent_at":123456,"error_code":1,"error_message":"Invalid argument"} -
Test with nested object
{"type":"response","status":"ok","sent_at":123456,"gnss":{"latitude":37.3874,"longitude":121.9724}}
Validation:
- All 3 output formats match unified schema
- JSON is valid (use jq or online validator)
- No truncation or malformed output
1.5 Documentation Updates¶
Tasks:
- Add section to
CLAUDE.mdexplaining device_response_t - API documentation (device_response_ok, device_response_error)
- Usage examples (simple, nested, error cases)
- ENABLE_DEVICE_RESPONSE flag explanation
- Create
docs/architecture/unified-device-response-schema.md(already prepared) - Update README.md with note about unified schema initiative
Validation:
- CLAUDE.md builds without errors (if markdown validation available)
- All code examples in docs are valid C++
- Links to schema spec are correct
1.6 Commit and Version¶
Tasks:
- Create feature branch:
feature/device-response-protocol - Git commit:
feat(device_response): add unified protocol layer - Message should reference unified schema doc
- Include scope:
(protocol)or(core) - Push to repository
- Verify CI/CD pipeline passes (if available)
Phase 2: Command Handler Unification (v1.11.3 - v1.11.4) - Estimated: 2-3 weeks¶
2.1 Group A: Simple Commands (Day 1-2)¶
Priority Order (simplest first):
- GET_VERSION (1 minute)
- Before:
send_response(response_string("version", config_get_version())); - After:
send_device_response(device_response_ok("version", config_get_version())); -
Test: Verify output format
-
GET_UPTIME (1 minute)
- Before:
send_response(response_int("uptime_ms", millis())); - After:
send_device_response(device_response_ok("uptime_ms", (int32_t)millis())); -
Test: Verify output format
-
GET_MAC_ADDRESS (1 minute)
- Modify response call
-
Test
-
SET_POLL_COUNT (2 minutes)
- Modify success response
- Keep error response (error path unchanged for now)
-
Test
-
SET_DEADTIME (2 minutes)
-
Similar to SET_POLL_COUNT
-
Repeat pattern for: GET_STREAM, SET_STREAM, GET_QUEUE_STATS, TEST_LED, etc.
Validation for Each Command: - [ ] Command still receives arguments correctly - [ ] Success response matches schema - [ ] Error response matches schema - [ ] No compiler warnings/errors - [ ] Serial output validated manually or with automated JSON parser
Estimated Duration: 15-20 commands × 2-3 minutes = 30-60 minutes
2.2 Group B: Medium Commands with Nested Objects (Day 3-5)¶
Priority Order:
- GET_THRESHOLD (5 minutes)
// Before: send_response(response_pairs("channel", ch, "threshold", val)); // After: JsonDocument payload; payload["type"] = "response"; payload["status"] = "ok"; payload["sent_at"] = device_get_timestamp(); JsonObject threshold = payload["threshold"].to<JsonObject>(); threshold["channel"] = ch; threshold["value"] = val; send_device_response({std::move(payload)}); - Create helper:
device_response_threshold(ch, val)(optional) -
Test: Verify nested structure
-
SET_THRESHOLD (5 minutes)
- Success response with nested object
-
Error response path
-
GET_RTC_TIME / SET_RTC_TIME (5 minutes each)
-
GET_GNSS_STATUS (10 minutes)
{"type":"response","status":"ok","sent_at":...,"gnss":{"satellites":8,"hdop":1.04,...}} -
GET_GNSS_POSITION (10 minutes)
-
GET_GNSS_TIME (5 minutes)
-
GET_BME280 (5 minutes)
Validation for Each Command: - [ ] Nested JSON structure correct - [ ] All fields present - [ ] No JSON serialization errors - [ ] Output can be parsed by kazunoko DeviceResponse model
Estimated Duration: 7 commands × 8-10 minutes avg = 60-90 minutes
2.3 Group C: Complex Commands (Day 6-10)¶
Priority Order:
- GET_STATUS (30 minutes) - Most complex
{ "type":"response", "status":"ok", "sent_at":..., "system":{"version":"...","uptime_ms":...}, "detection":{"poll_count":...,"threshold1":...}, "features":{...} } - Multiple nested objects
- Conditional fields (if ENABLE_*)
-
Test: Full feature flag coverage
-
GET_HELP (20 minutes)
- Array of commands (if applicable)
-
Test: Large JSON output handling
-
Error Response Standardization (10 minutes)
- All error paths use device_response_error()
- Consistent error_code and error_message format
- Test: Verify all error codes
Validation for Complex Commands: - [ ] Large JSON output doesn't truncate - [ ] Nested objects serialize correctly - [ ] Conditional compilation respected (#if ENABLE_*) - [ ] Memory usage acceptable (ArduinoJson dynamic allocation)
Estimated Duration: 10 handlers × 15-20 minutes avg = 150-200 minutes
2.4 Comprehensive Testing¶
Tasks:
- [ ] Create test harness: send each command via serial, capture output
- [ ] Validate each output against unified schema (JSON Schema validator)
- [ ] Verify response_t references are removed from handlers
- [ ] Grep for remaining send_response() calls (should be < 5, error paths)
- [ ] Memory profiling: ensure no stack overflow with nested JsonDocument
- [ ] Regression testing: all commands produce output within 100ms
Tools to Use:
# Validate JSON against schema
python3 -m jsonschema unified-device-response-schema.json < output.jsonl
# Verify no remaining response_t calls
grep -r "response_t" src/text_command* --exclude="*.orig"
# Check response_string, response_int removals
grep -r "send_response(" src/text_command* | grep -v "send_device_response"
2.5 Documentation & Migration Guide¶
Tasks:
- [ ] Update CLAUDE.md with migration examples
- [ ] Before/After code snippets for each group
- [ ] Common patterns (simple, nested, error)
- [ ] Document builder helper functions created during migration
- [ ] If multiple handlers share same structure, create helper
- [ ] Example: device_response_threshold(ch, val)
- [ ] Add section to REFACTORING_ROADMAP summarizing completed work
2.6 Commits (Incremental)¶
Tasks:
- [ ] Group A commit: refactor(text_commands): migrate simple commands to device_response_t
- [ ] Group B commit: refactor(text_commands): migrate nested response commands
- [ ] Group C commit: refactor(text_commands): migrate complex commands (GET_STATUS)
- [ ] Error path commit: refactor(text_commands): standardize error responses
Phase 3: Event Output Unification (v1.12.0) - Estimated: 1-2 days¶
3.1 Modify stream_formatter.cpp¶
Focus: send_jsonl() function (only when STREAM_FORMAT=3)
Tasks: - [ ] Replace manual JsonDocument creation with device_response_t builder pattern
// Before:
void send_jsonl(const event_t *data) {
JsonDocument doc;
doc["hit1"] = data->hit1;
// ...
serializeJson(doc, Serial);
Serial.println();
}
// After:
void send_jsonl(const event_t *data) {
JsonDocument payload;
payload["type"] = "event";
payload["status"] = "ok";
payload["sent_at"] = device_get_timestamp();
payload["hit1"] = data->hit1;
// ...
send_device_response({std::move(payload)});
}
- Verify all ENABLE_* conditional fields included
- Test with different ENABLE flag combinations:
- ENABLE_BME280=0, ENABLE_GNSS=0
- ENABLE_BME280=1, ENABLE_GNSS=0
- ENABLE_BME280=0, ENABLE_GNSS=1
- ENABLE_BME280=1, ENABLE_GNSS=1 (all features)
Validation: - [ ] Event output matches unified schema - [ ] Sample event output:
{"type":"event","status":"ok","sent_at":123456,"hit1":95,"hit2":87,"hit3":91,"adc":2048}
{"type":"event","status":"ok","sent_at":123456,"hit1":95,"hit2":87,"hit3":91,"adc":2048,"tmp_c":25.35,"atm_pa":101325}
3.2 Verify Format Consistency¶
Tasks:
- [ ] Compare response format with event format
- Both use "type", "status", "sent_at"
- Verify consistency across all output
- Test with stream enabled/disabled:
- SET_STREAM on: events should appear in log
- SET_STREAM off: no event output
- Response still appears regardless
3.3 Memory & Performance Testing¶
Tasks: - [ ] Event generation rate test: 100 events generated, measure serial output time - [ ] Baseline (before): X seconds - [ ] After unification: Y seconds - [ ] Acceptable if Y ≤ 1.1 × X (≤10% overhead)
- Stack usage test: Measure free heap during 1000 events
- Verify no memory leak (heap should stabilize)
3.4 Backward Compatibility Check¶
Tasks:
- [ ] Verify event_t struct unchanged (still used by detection loop)
- [ ] stream_formatter.cpp only modifies output, not input pipeline
- [ ] Existing SSV/TSV/CSV paths unchanged (only JSONL modified)
3.5 Commit¶
Tasks:
- [ ] Git commit: refactor(stream_formatter): unify event output with device_response_t
Phase 4: Legacy Code Cleanup (v1.12.0+) - Estimated: 1 day¶
4.1 Remove response.h¶
Tasks:
- [ ] Verify no remaining #include "response.h" (except archived/docs)
grep -r "include.*response\.h" src/ --exclude="*.orig"
include/response.h
- [ ] Confirm no build errors
4.2 Remove response.cpp¶
Tasks:
- [ ] Verify no references to send_response() function
grep -r "send_response" src/ | grep -v "send_device_response"
src/response.cpp
- [ ] Confirm no linker errors
4.3 Remove response_t Type¶
Tasks:
- [ ] Verify all response_t variables replaced
- [ ] Check no response_status_t enum usage remaining
- [ ] Confirm build succeeds
4.4 Disable Conditional Flag¶
Tasks:
- [ ] Remove ENABLE_DEVICE_RESPONSE flag (or set to always 1)
- [ ] Remove conditional compilation wrapper in handlers
4.5 Final Testing¶
Tasks: - [ ] Full integration test: all commands + events produce output - [ ] Binary size measurement: compare to v1.11.x - [ ] Measure reduction from response.h/cpp removal - [ ] Expected: 5-10KB smaller binary
- Serial output validation: random sampling of commands
- 20 commands: GET_VERSION, SET_POLL_COUNT, GET_STATUS, etc.
- Verify each output matches unified schema
4.6 Documentation Finalization¶
Tasks:
- [ ] Update CLAUDE.md: remove legacy response_t section
- [ ] Finalize unified schema spec: docs/architecture/unified-device-response-schema.md
- [ ] Create migration guide: docs/migration/response-unification-v1.12.md
- [ ] Add JSON Schema file: docs/schemas/device-response.json
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"required": ["type", "status", "sent_at"],
"properties": {
"type": {"enum": ["response", "event"]},
"status": {"enum": ["ok", "error"]},
"sent_at": {"type": "integer"}
},
"additionalProperties": true
}
4.7 Commit¶
Tasks:
- [ ] Git commit: chore(response): remove legacy response_t
- [ ] Tag: v1.12.0 or appropriate version
Cross-Phase Validation¶
Code Quality¶
- No compiler warnings (use
PLATFORMIO_BUILD_FLAGS="-Werror") - No unused variables or imports
- Consistent indentation and style (run formatter)
- All Doxygen comments present and correct
Testing¶
- Unit tests for device_response builders (if test framework available)
- Integration tests: all commands produce valid JSON
- Regression tests: compare output to baseline (v1.11.x)
Documentation¶
- All APIs documented in CLAUDE.md
- Unified schema spec complete and accurate
- Examples match actual output
- No broken links
Performance¶
- Command response latency unchanged
- Event generation not impacted
- No memory leaks over 24-hour run
Sign-off¶
Implementation Owner: [To be assigned] Code Review: [To be assigned] QA: [To be assigned] Documentation: [To be assigned]
Target Completion: - Phase 1: Week of 2025-12-09 (v1.11.3) - Phase 2: Week of 2025-12-15 (v1.11.4) - Phase 3-4: Week of 2025-12-29 (v1.12.0)
Status: Ready for development ✅