Files
lorakiss/src/kiss.c
Maze X 8883ee3e94 Scaffold PlatformIO project with 20 board configs and C99/C++ source skeleton
Three-tier configuration hierarchy:
- [env:base] — RadioLib + default LoRa parameters
- [soc_esp32/esp32s3/nrf52] — platform + framework per SoC
- [env:board_name] — board-specific pins + chip selection

20 boards across 4 vendors:
- Heltec: 11 boards (T114, CT62, E213, E290, Mesh Solar, T190, Tracker,
  Tracker V2, V2, V3, V4)
- LilyGo: 4 boards (T-Beam 1W, sx1262, sx1276, supreme)
- Seeed: 1 board (Xiao S3 + Wio SX1262 with verified pins)
- RAK: 4 boards (RAK11310, RAK3112, RAK3401, RAK3x72, RAK4631)

Known/verified pins: Heltec V2/V3/V4, RAK4631, Seeed Xiao S3
FIXME pins: all others (placeholders for future research)

Source skeleton:
- config.h — compile-time defaults + pin validation (#error checks)
- kiss.h/c — KISS protocol implementation (C99)
- radio.h/cpp — RadioLib wrapper with C API (extern "C" boundary)
- main.cpp — Arduino entry point

All files pass pre-commit (prettier, markdownlint, YAML check).
2026-03-27 17:15:30 +01:00

112 lines
3.1 KiB
C

/* KISS protocol implementation — C99 */
#include "kiss.h"
void kiss_decoder_init(kiss_decoder_t *dec) {
dec->state = KISS_STATE_IDLE;
dec->len = 0;
}
bool kiss_decode(kiss_decoder_t *dec, uint8_t byte, kiss_frame_t *frame) {
switch (dec->state) {
case KISS_STATE_IDLE:
if (byte == KISS_FEND) {
dec->len = 0;
dec->state = KISS_STATE_IN_FRAME;
}
return false;
case KISS_STATE_IN_FRAME:
if (byte == KISS_FESC) {
dec->state = KISS_STATE_ESCAPE;
return false;
}
if (byte == KISS_FEND) {
if (dec->len > 0) {
/* Frame complete */
frame->port = dec->buf[0] & 0x0Fu;
frame->len = dec->len - 1;
if (frame->len > 0) {
for (size_t i = 0; i < frame->len; i++) {
frame->data[i] = dec->buf[i + 1];
}
}
dec->state = KISS_STATE_IDLE;
return true;
}
dec->state = KISS_STATE_IDLE;
return false;
}
if (dec->len < KISS_MAX_FRAME + 1) {
dec->buf[dec->len++] = byte;
}
return false;
case KISS_STATE_ESCAPE:
if (byte == KISS_TFEND) {
byte = KISS_FEND;
} else if (byte == KISS_TFESC) {
byte = KISS_FESC;
}
if (dec->len < KISS_MAX_FRAME + 1) {
dec->buf[dec->len++] = byte;
}
dec->state = KISS_STATE_IN_FRAME;
return false;
}
return false;
}
size_t kiss_encode(uint8_t port, const uint8_t *data, size_t len,
uint8_t *dst, size_t dst_cap) {
if (dst_cap < len + 3)
return 0; /* Need at least: FEND type data... FEND */
size_t pos = 0;
dst[pos++] = KISS_FEND;
/* Type byte: port in upper nibble, cmd in lower nibble (0 for data) */
uint8_t type = (port << 4) | 0x00;
if (type == KISS_FEND) {
dst[pos++] = KISS_FESC;
dst[pos++] = KISS_TFEND;
} else if (type == KISS_FESC) {
dst[pos++] = KISS_FESC;
dst[pos++] = KISS_TFESC;
} else {
dst[pos++] = type;
}
/* Payload with escaping */
for (size_t i = 0; i < len; i++) {
if (pos + 2 > dst_cap)
return 0; /* Overflow */
if (data[i] == KISS_FEND) {
dst[pos++] = KISS_FESC;
dst[pos++] = KISS_TFEND;
} else if (data[i] == KISS_FESC) {
dst[pos++] = KISS_FESC;
dst[pos++] = KISS_TFESC;
} else {
dst[pos++] = data[i];
}
}
if (pos + 1 > dst_cap)
return 0;
dst[pos++] = KISS_FEND;
return pos;
}
size_t kiss_encode_quality(int8_t snr, int16_t rssi, uint8_t *dst,
size_t dst_cap) {
if (dst_cap < 5)
return 0; /* Need: FEND type snr rssi_hi rssi_lo FEND */
uint8_t payload[3];
payload[0] = (uint8_t)snr;
payload[1] = (uint8_t)((rssi >> 8) & 0xFF);
payload[2] = (uint8_t)(rssi & 0xFF);
return kiss_encode(KISS_PORT_QUALITY, payload, 3, dst, dst_cap);
}