Skip to content

v1.19.3 - DAC Raw Protocol Support (2025-12-19)

What Changed?

This release adds raw DAC protocol support via new SET_DAC and GET_DAC commands, enabling direct byte-level control of threshold voltage alongside the existing decoded SET_THRESHOLD command. Users can now interact with the DAC protocol in two complementary ways: high-level (decoded threshold 0-1023) or low-level (raw protocol bytes). The feature maintains full backward compatibility with existing detection and configuration commands.


What's New

Main Feature: Raw DAC Protocol Interface

What it does:

Exposes low-level access to the DAC (Digital-to-Analog Converter) threshold protocol, complementing the high-level SET_THRESHOLD command. Allows users to directly specify raw protocol bytes (byte1, byte2) that get decoded internally to threshold values, or retrieve the current threshold by decoding raw bytes back to decimal.

How to use it:

# High-level approach (existing, unchanged)
SET_THRESHOLD 1 512          # Set channel 1 to decoded value 512

# Low-level approach (new)
SET_DAC 1 0x10 0x20          # Set channel 1 using raw protocol bytes
GET_DAC 1                     # Retrieve decoded threshold value for channel 1

Both approaches modify the same internal threshold value and sync with hardware identically. The difference is input format:

  • SET_THRESHOLD: Accepts decoded 10-bit value (0-1023 range)
  • SET_DAC: Accepts raw protocol bytes (0x00-0xFF for each byte)

Code example:

// From src/command/dac.cpp - handle_set_dac implementation
command_response_t handle_set_dac(const command_t& cmd) {
  // Parse: <channel> <byte1_hex> <byte2_hex>
  uint8_t ch = (uint8_t)strtoul(cmd.args[0], &endptr, 10);
  uint8_t byte1 = (uint8_t)strtoul(cmd.args[1], &endptr, 16);  // hex
  uint8_t byte2 = (uint8_t)strtoul(cmd.args[2], &endptr, 16);  // hex

  // Set via Command class (decodes bytes, validates, syncs hardware)
  if (!Command::getInstance().set_dac(ch, byte1, byte2)) {
    return command_response_error(DEVICE_CODE_OUT_OF_RANGE,
                                  "Invalid DAC bytes");
  }

  // Return payload with sent bytes and confirmed channel
  static JsonDocument payload;
  payload["channel"] = ch;
  payload["byte1"] = (int)byte1;
  payload["byte2"] = (int)byte2;
  // ...
}

Installation

Quick Start

# Get the release
git checkout v1.19.3

# Build
task build

# Upload
task upload

# Check it works
task monitor

# Test the new commands
# In serial monitor, try:
# SET_DAC 1 0x10 0x20
# GET_DAC 1

What's Different from the Last Version?

✅ Added

  • SET_DAC <ch> <byte1> <byte2> command - Set DAC threshold via raw protocol bytes
  • GET_DAC <ch> command - Retrieve threshold by decoding raw protocol bytes
  • Command::set_dac(ch, byte1, byte2) method - Low-level DAC control with automatic decoding and hardware sync
  • Command handler file src/command/dac.cpp - Implements both SET_DAC and GET_DAC handlers following Phase 6 payload pointer pattern
  • Command dispatch table entries in v2_command_manager.cpp - Registers new commands for text protocol routing

🔧 Changed

  • Updated command dispatch table comment count: Detection Configuration now lists 8 commands (was 6)
  • Updated SET_THRESHOLD and GET_THRESHOLD descriptions to clarify they work with decoded values, vs. SET_DAC/GET_DAC for raw bytes

🐛 Fixed

  • None in this release

Is It Safe to Upgrade?

Backward Compatible: Yes

  • All existing commands (SET_THRESHOLD, GET_THRESHOLD, detection, LED, RTC, GNSS, WiFi) work unchanged
  • New commands are additive only - no modifications to existing behavior
  • Both threshold setting methods modify the same internal state identically
  • No changes to hardware protocol or serial interface
  • No changes to configuration storage or initialization

Tests Passed

  • ✅ Build esp32dev-dev: SUCCESS (349KB flash, 28.9KB RAM)
  • ✅ Pre-commit hooks: All passed (formatting, trailing whitespace, JSON/YAML validation)
  • ✅ Compilation: No warnings or errors
  • ✅ Link step: All symbols resolved, no undefined references
  • ✅ Binary size: Within margins (26.6% flash usage)

Release Details

  • Date: 2025-12-19
  • Version: v1.19.3
  • Files Changed: 4
  • include/v2_command.h - Added set_dac() method declaration
  • src/v2_command.cpp - Implemented set_dac() with validation and hardware sync
  • src/command/dac.cpp - New handler file with handle_set_dac() and handle_get_dac()
  • src/v2_command_manager.cpp - Registered handlers in dispatch table and forward declarations
  • Lines Added: ~150 (implementation) + ~30 (registration)
  • Dependencies: No new libraries or dependencies

Technical Notes

DAC Protocol Encoding/Decoding

The DAC protocol uses a 10-bit threshold encoded across two bytes:

  • byte1 = 0x10 + (threshold >> 6) - protocol header + upper 4 bits
  • byte2 = (threshold << 2) & 0xFF - lower 8 bits shifted

The Command::set_dac() method automatically decodes incoming bytes, validates the result (0-1023 range), sends to hardware via dac_send(), and stores the decoded value in the appropriate threshold member (threshold1_, threshold2_, or threshold3_).

Command Dispatch

Both SET_DAC and GET_DAC are registered in the command dispatch table in Detection Configuration section: - SET_DAC: No aliases (low-frequency, not abbreviated) - GET_DAC: No aliases (low-frequency, not abbreviated)

This follows the pattern of other threshold commands while maintaining clear distinction between high-level (SET_THRESHOLD) and low-level (SET_DAC) interfaces.

Architecture

The implementation follows the 3-layer command architecture: - Layer 1 (Handlers): handle_set_dac() and handle_get_dac() in src/command/dac.cpp - Layer 2 (Dispatcher): CommandQueue::dispatch() routes to handlers via dispatch table - Layer 3 (Serialization): DeviceResponse::send() outputs response as JSONL

Handlers use the Phase 6 payload pointer pattern, storing command-specific fields in static JsonDocument structures.


Next Steps

  1. Testing: Manual verification of SET_DAC and GET_DAC via serial monitor
  2. Documentation: Update API docs to describe raw protocol byte format and decoding logic
  3. Examples: Add protocol byte encoding/decoding examples for users working with external controllers
  4. Integration: Consider adding CommandParser class for centralized argument validation (v1.20.0+)