feat: implement cel_crsf_param_parse

Parse PARAM_ENTRY payload into cel_crsf_param struct. Handles
TEXT_SELECT with options string and UINT8/INT8 with min/max/default/
value fields. Respects hidden flag (bit 7 of type byte). Truncates
name and options to buffer limits.
This commit is contained in:
2026-06-14 21:50:12 +02:00
parent eaaaf710a2
commit 5d18258330
3 changed files with 245 additions and 18 deletions
+61 -18
View File
@@ -59,22 +59,65 @@ int cel_crsf_param_set_power(cel_serial_port* port, int mw,
int cel_crsf_param_parse(cel_crsf_param* out, uint8_t const* payload,
size_t len) {
/* TODO: parse PARAM_ENTRY payload into cel_crsf_param.
* Layout:
* [0] dest (uint8)
* [1] src (uint8)
* [2] index (uint8)
* [3] chunks_remaining (uint8)
* [4] parent (uint8)
* [5] type (uint8, bit 7 = hidden flag)
* [6..] name (null-terminated ASCII string)
* Then type-specific data after name:
* TEXT_SELECT: options (null-terminated, semicolon-separated),
* then [value][min][max][default] (1 byte each)
* UINT8/INT8: [min][max][default][value] (1 byte each)
* Minimum payload length: 6 bytes. */
(void)out;
(void)payload;
(void)len;
return -1;
if (out == NULL || payload == NULL) return -1;
if (len < 6) return -1;
memset(out, 0, sizeof(*out));
size_t offset = 0;
(void)payload[offset++]; /* dest */
(void)payload[offset++]; /* src */
out->index = payload[offset++];
(void)payload[offset++]; /* chunks_remaining */
(void)payload[offset++]; /* parent */
uint8_t raw_type = payload[offset++];
out->hidden = (raw_type & 0x80) ? 1 : 0;
out->type = raw_type & 0x7F;
/* Parse name (null-terminated) */
size_t name_start = offset;
while (offset < len && payload[offset] != '\0') offset++;
size_t name_len = offset - name_start;
if (name_len >= sizeof(out->name)) name_len = sizeof(out->name) - 1;
memcpy(out->name, payload + name_start, name_len);
out->name[name_len] = '\0';
if (offset < len) offset++; /* skip null terminator */
/* Parse type-specific data */
switch (out->type) {
case CEL_PARAM_TEXT_SELECT: {
/* Options: null-terminated, semicolon-separated string */
size_t opts_start = offset;
while (offset < len && payload[offset] != '\0') offset++;
size_t opts_len = offset - opts_start;
if (opts_len >= sizeof(out->options)) opts_len = sizeof(out->options) - 1;
memcpy(out->options, payload + opts_start, opts_len);
out->options[opts_len] = '\0';
if (offset < len) offset++; /* skip null terminator */
/* [value][min][max][default] */
if (offset + 4 <= len) {
out->value = payload[offset++];
out->min_val = payload[offset++];
out->max_val = payload[offset++];
out->default_val = payload[offset++];
}
break;
}
case CEL_PARAM_UINT8:
case CEL_PARAM_INT8:
/* [min][max][default][value] */
if (offset + 4 <= len) {
out->min_val = payload[offset++];
out->max_val = payload[offset++];
out->default_val = payload[offset++];
out->value = payload[offset++];
}
break;
default:
/* Other types have no simple value representation */
break;
}
return 0;
}