27const uint8_t HDR[4] = {0xF4, 0xF3, 0xF2, 0xF1};
28const uint8_t FTR[4] = {0xF8, 0xF7, 0xF6, 0xF5};
29const uint8_t CMD_HDR[4] = {0xFD, 0xFC, 0xFB, 0xFA};
30const uint8_t CMD_FTR[4] = {0x04, 0x03, 0x02, 0x01};
32const uint16_t LEN_BASIC = 13;
33const uint16_t LEN_ENGINEERING = 35;
35uint16_t rd16(
const uint8_t *p)
37 return (uint16_t)p[0] | ((uint16_t)p[1] << 8);
41size_t cmd_frame(uint8_t *buf,
size_t cap, uint16_t word,
const uint8_t *val,
size_t vlen)
43 size_t need = 4 + 2 + 2 + vlen + 4;
44 if (!buf || cap < need)
49 for (
int k = 0; k < 4; k++)
51 buf[i++] = CMD_HDR[k];
53 uint16_t dl = (uint16_t)(2 + vlen);
54 buf[i++] = (uint8_t)(dl & 0xFF);
55 buf[i++] = (uint8_t)(dl >> 8);
56 buf[i++] = (uint8_t)(word & 0xFF);
57 buf[i++] = (uint8_t)(word >> 8);
58 for (
size_t k = 0; k < vlen; k++)
62 for (
int k = 0; k < 4; k++)
64 buf[i++] = CMD_FTR[k];
72 if (!f || !out || len < (
size_t)(6 + LEN_BASIC + 4))
76 if (memcmp(f, HDR, 4) != 0)
80 uint16_t dl = rd16(f + 4);
81 if ((
size_t)(6 + dl + 4) != len)
85 if (memcmp(f + 6 + dl, FTR, 4) != 0)
90 const uint8_t *p = f + 6;
92 memset(&r, 0,
sizeof(r));
101 else if (p[0] == 0x01)
103 if (dl != LEN_ENGINEERING)
144 else if (p[11] != 0x55)
188 uint32_t total = 6u + (uint32_t)rd16(s->
buf + 4) + 4u;
194 s->
total = (uint16_t)total;
234 static const uint8_t v[2] = {0x01, 0x00};
235 return cmd_frame(buf, cap, 0x00FF, v, 2);
239 return cmd_frame(buf, cap, 0x00FE,
nullptr, 0);
243 return cmd_frame(buf, cap, on ? 0x0062 : 0x0063, nullptr, 0);
247 return cmd_frame(buf, cap, 0x00A3,
nullptr, 0);
253 const uint8_t v[2] = {(uint8_t)(on ? 0x01 : 0x00), 0x00};
254 return cmd_frame(buf, cap, 0x00A4, v, 2);
259 static const uint8_t v[2] = {0x01, 0x00};
260 return cmd_frame(buf, cap, 0x00A5, v, 2);
270 const uint8_t v[6] = {(uint8_t)password[0], (uint8_t)password[1], (uint8_t)password[2],
271 (uint8_t)password[3], (uint8_t)password[4], (uint8_t)password[5]};
272 return cmd_frame(buf, cap, 0x00A9, v, 6);
280 if (!f || !out || len < 14)
284 for (
int k = 0; k < 4; k++)
286 if (f[k] != CMD_HDR[k])
291 size_t dl = (size_t)f[4] | ((
size_t)f[5] << 8);
292 if (dl < 4 || len != 4 + 2 + dl + 4)
296 for (
int k = 0; k < 4; k++)
298 if (f[6 + dl + k] != CMD_FTR[k])
303 out->
command = (uint16_t)((uint16_t)f[6] | ((uint16_t)f[7] << 8));
304 out->
status = (uint16_t)((uint16_t)f[8] | ((uint16_t)f[9] << 8));
312 return ack && ack->
status == 0;
321 for (
int k = 0; k < 6; k++)
359 while (Serial2.available())
374 return s_ld.have ? &s_ld.last :
nullptr;
HLK-LD2410 24 GHz mmWave presence / motion radar codec (PC_ENABLE_LD2410).
bool pc_ld2410_ack_ok(const Ld2410Ack *ack)
True if ack reports success (Ld2410Ack::status == 0).
size_t pc_ld2410_cmd_bluetooth(uint8_t *buf, size_t cap, bool on)
LD2410B: turn the Bluetooth radio on (value 0x0001) or off (0x0000). Word 0x00A4.
size_t pc_ld2410_cmd_engineering(uint8_t *buf, size_t cap, bool on)
Enable (0x0062) or disable (0x0063) engineering mode.
size_t pc_ld2410_cmd_get_mac(uint8_t *buf, size_t cap)
LD2410B: query the module's Bluetooth MAC address (word 0x00A5, value 0x0001).
bool pc_ld2410_stream_push(Ld2410Stream *s, uint8_t byte, Ld2410Report *out)
Feed one received byte. When it completes a valid report frame, fills out and returns true; otherwise...
bool pc_ld2410_parse_ack(const uint8_t *frame, size_t len, Ld2410Ack *out)
Decode one command-ACK frame (header, intra-frame length, footer and length agreement all checked).
bool pc_ld2410_poll()
Pump the UART through the stream.
bool pc_ld2410_set_engineering(bool on)
Enable/disable engineering mode (brackets the command with enable/end).
size_t pc_ld2410_cmd_set_bt_password(uint8_t *buf, size_t cap, const char password[6])
LD2410B: set the 6-octet Bluetooth control password (word 0x00A9). Takes effect after a restart and s...
bool pc_ld2410_parse_report(const uint8_t *frame, size_t len, Ld2410Report *out)
Decode one whole LD2410 report frame (header F4 F3 F2 F1 .. footer F8 F7 F6 F5). Pure - no I/O....
bool pc_ld2410_present(const Ld2410Report *r)
true if r shows any target (moving or stationary).
#define LD2410_MAX_GATES
Range gates the LD2410 reports energy for in engineering mode (gate 0..8).
size_t pc_ld2410_cmd_restart(uint8_t *buf, size_t cap)
Restart the module (word 0x00A3).
void pc_ld2410_stream_reset(Ld2410Stream *s)
Reset a stream to the syncing state.
uint16_t pc_ld2410_distance_cm(const Ld2410Report *r)
Best available target distance (cm): the moving distance if moving, else stationary.
const Ld2410Report * pc_ld2410_last()
The most recently decoded report, or nullptr before the first one arrives.
size_t pc_ld2410_cmd_config_end(uint8_t *buf, size_t cap)
"End configuration" (word 0x00FE).
#define LD2410_FRAME_MAX
Largest assembled frame: header(4) + len(2) + payload(<=60) + footer(4), rounded up.
bool pc_ld2410_restart()
Restart the module (brackets the command with enable/end).
bool pc_ld2410_begin(int rx_pin, int tx_pin)
Open UART2 at PC_LD2410_BAUD on rx_pin / tx_pin.
bool pc_ld2410_ack_mac(const Ld2410Ack *ack, uint8_t mac[6])
Extract the 6-octet MAC from a get-MAC ACK (word 0x01A5) into mac, in wire order.
size_t pc_ld2410_cmd_config_enable(uint8_t *buf, size_t cap)
"Enable configuration" (word 0x00FF, value 0x0001).
User-facing configuration for ProtoCore.
#define PC_LD2410_BAUD
LD2410 UART baud rate (the module's fixed factory default is 256000).
A decoded command-ACK frame. payload points into the caller's frame (not copied).
uint16_t status
0 = success, 1 = failure.
size_t payload_len
octets at payload.
const uint8_t * payload
command-specific data after the status word (nullptr if none).
uint16_t command
ACK command word: the request word | 0x0100 (e.g. 0x01A5 for get-MAC).
A decoded LD2410 target report. Engineering fields are 0 unless engineering.
uint16_t detect_cm
overall detection distance (cm)
uint8_t static_gate_energy[LD2410_MAX_GATES]
per-gate stationary energy (0-100)
uint16_t moving_cm
moving target distance (cm)
uint8_t out_pin
OUT pin level (0/1)
uint8_t max_static_gate
highest configured stationary gate
uint8_t state
one of LD2410_STATE_*
uint8_t static_energy
stationary target energy (0-100)
uint8_t engineering
1 if this was an engineering-mode frame (per-gate energies valid)
uint8_t moving_gate_energy[LD2410_MAX_GATES]
per-gate moving energy (0-100)
uint8_t moving_energy
moving target energy (0-100)
uint8_t max_moving_gate
highest configured moving gate
uint8_t light
photosensor level (0-255)
uint16_t static_cm
stationary target distance (cm)
static constexpr uint8_t LD2410_STATE_NONE
no target
static constexpr uint8_t LD2410_STATE_BOTH
both a moving and a stationary target
static constexpr uint8_t LD2410_STATE_STATIC
stationary target only
static constexpr uint8_t LD2410_STATE_MOVING
moving target only
Byte-by-byte report-frame reassembler (fixed buffer, resyncs on noise).
uint16_t pos
bytes collected so far
uint8_t hdr_match
header bytes matched while syncing (phase = pos<4)
uint16_t total
expected full-frame length (known after the length field)
uint8_t buf[LD2410_FRAME_MAX]
frame under construction
uint8_t phase
0 sync, 1 length, 2 body