claudeDroneteam-docs
documentation · reference
Docs reference

Structured knowledge from collected_doc_media/claudedrone_docs/. Browse the tree on the left; the source of truth is markdown in the repo.

TF-Luna driver — ESP32-S3 implementation

Firmware-agent implementation notes: frame sync via scan-for-magic 0x59 0x59, UART buffer 512, health flag (amp<100 / 0xFFFF), 7 host unit tests.

stablefirmwareupdated 2026-05-11T00:00:00.000ZClaudeDroneFirmwareTFLunaDriverESP32UART

TF-Luna A03 driver implementation on ESP32-S3 (Arduino framework). Code-only session — hardware bring-up deferred until the ESP32 is wired to D2.

Architecture — pure C++ + Arduino wrapper

The driver is split into two parts:

  • Pure functions (parse_frame, checksum) — plain C++, no Arduino, tested on the host.
  • Class TFLuna — stateful wrapper that uses HardwareSerial, hidden behind #if defined(ARDUINO).

The full pattern is described in Host-testable Arduino pattern.

Files:

src/drivers/tf_luna/
├── tf_luna.h
├── tf_luna.cpp
└── README.md

Plus a main.cpp skeleton — once flashed, immediately dumps frames in pio device monitor.

Three subtle points in the implementation

Frame sync

TF-Luna continuously emits 9-byte packets over UART with the 0x59 0x59 header. There’s no separator between packets. If the receiver starts mid-packet — it’s desynced and distance looks like garbage (thousands of cm).

Fix: scan for 0x59 0x59 in the buffer each time.

Subtlety: if the first byte is 0x59 but the second isn’t (partial sync), drop only one byte, not all 9. Those 9 might contain another 0x59 — the start of the next normal frame.

// pseudo-code
while (len >= 9) {
  if (buf[0] == 0x59 && buf[1] == 0x59) {
    // try parse
    if (parse_ok) { shift_out 9; }
    else          { shift_out 1; }  // partial — byte by byte
  } else {
    shift_out 1;
  }
}

Buffer size

By default, the ESP32 gives a 256-byte UART RX buffer. At 100 Hz × 9 bytes = 900 bytes/sec; 256 fills in ~285 ms. If the scheduler stalls — data loss.

Fix: port_.setRxBufferSize(512) — double margin.

Health flag

Datasheet (Benewake Appendix I): if signal amplitude amp < 100 (weak reflected beam) or amp == 0xFFFF (overexposure on a glossy object) — the distance is unreliable.

Fix: don’t discard these frames; return valid = false. That way the UDP encoder can log “30 frames blind in a row” instead of “30 missed” — these are different diagnoses for the operator.

out.valid = (amp >= 100) && (amp != 0xFFFF);

Tests — 7 cases, all pass

Run: pio test -e native.

# Case Expectation
1 Real packet fields correct, valid=true
2 Corrupted checksum rejected
3 Corrupted magic (not 0x59 0x59) rejected
4 amp=50 parsed, valid=false
5 amp=0xFFFF (overexposure) parsed, valid=false
6 Direct checksum-formula check (zeros, all-FF, real packet) correct values
7 raw temperature → Celsius correct conversion

What didn’t work first time — 2 self-inflicted bugs (caught by tests)

Bug 1 — pio test -e native failed at linking

Config test_build_src = no disables compiling src/. The linker couldn’t find the function implementations.

Fix: #include "../../src/drivers/tf_luna/tf_luna.cpp" directly in the test file. Simpler, more obvious, and requires no build_src_filter gymnastics.

Bug 2 — expected 0x8E instead of 0xEE

In the checksum-formula test I wrote expected 0x8E. Arithmetic error on paper: the sum was 0x18E (correct), & 0xFF gave 0x8E (wrong). The test caught it — exactly the right behavior. Fixed → green.

What’s left — hardware bring-up

When the ESP32 connects to D2:

pio run -e esp32s3_devkit           # compile (toolchain ~5-10 min first time)
pio run -e esp32s3_devkit --target upload
pio device monitor                  # see frames with your eyes

Bring-up checklist (6 items):

  1. Measurement against a static target at 50 cm → distance_cm ≈ 50 ± 6.
  2. Measurement at 200 cm → distance_cm ≈ 200 ± 6.
  3. Against a glossy surface (mirror) → amp ~ 0xFFFF, valid=false.
  4. 60 s stress — no lost frames, no resync.
  5. Yank the connector → recovery after re-connect.
  6. Latency — ~10 ms between frames (100 Hz).
© 2026 claudeDrone Team · auto-pipeline · Nuxt 3 SSR