# Micro controller LoRa KISS modem This project is a LoRa KISS modem for common LoRa capable development boards, such as created by vendors like Heltec, RAK, Seeed, etc. It is using the USB-serial interface (optionally hardware serial interface) to allow the user to send and receive LoRa packets using the KISS modem protocol. ## KISS Modem ### KISS Framing KISS (Keep It Simple, Stupid) is a standard TNC protocol for packet radio. Frames use special bytes for delimiting: | Symbol | Value | Meaning | | ------ | ------ | ------------------------------ | | FEND | `0xC0` | Frame end/begin delimiter | | FESC | `0xDB` | Frame escape | | TFEND | `0xDC` | Transposed FEND (follows FESC) | | TFESC | `0xDD` | Transposed FESC (follows FESC) | A frame has the structure: ```text FEND FEND ``` The type byte encodes the port number (upper nibble) and command (lower nibble): ```text type = (port << 4) | cmd ``` For host-to-modem data frames, `cmd = 0x00`. Any `FEND` or `FESC` bytes appearing in the data payload must be escaped as `FESC TFEND` or `FESC TFESC` respectively. ### Ports The KISS modem uses three ports for communication: | Port | Direction | Purpose | | ---- | ------------- | ---------------------------- | | 0 | bidirectional | Raw LoRa packet data | | 1 | TNC → host | Signal quality (SNR + RSSI) | | 2 | bidirectional | Radio configuration commands | ### Port 1 — Signal Quality Before each received LoRa packet delivered on port 0, the modem emits a signal quality frame on port 1. The payload is 3 bytes, big-endian: | Offset | Size | Type | Field | Description | | ------ | ---- | ----- | ----- | ------------------------------------- | | 0 | 1 | int8 | SNR | Signal-to-noise ratio in dB (rounded) | | 1 | 2 | int16 | RSSI | Received signal strength in dBm | Example: `FEND 0x11 [-7] [0xFF 0x99] FEND` = SNR = -7 dB, RSSI = -103 dBm ### Configuration Commands (Port 2) Configuration commands allow the host to query and control the modem's LoRa parameters. Each configuration frame's data payload (after the KISS type byte) begins with a 1-byte command byte. The modem responds to every SET\_\* command with either RES_OK (`0x01`) or RES_ERROR (`0x02`) on port 2. **Commands:** | Cmd | Name | Dir | Payload | | ---- | ------------- | --- | ------------------------------ | | 0x00 | Reserved | — | — | | 0x01 | RES_OK | ← | (none) | | 0x02 | RES_ERROR | ← | (none) | | 0x10 | GET_RADIO | → | (none) | | 0x10 | _resp_ | ← | freq_kHz, bw_hz, sf, cr, power | | 0x11 | SET_RADIO | → | freq_kHz, bw_hz, sf, cr, power | | 0x12 | GET_FREQUENCY | → | (none) | | 0x12 | _resp_ | ← | freq_kHz | | 0x13 | SET_FREQUENCY | → | freq_kHz | | 0x14 | GET_BANDWIDTH | → | (none) | | 0x14 | _resp_ | ← | bw_hz | | 0x15 | SET_BANDWIDTH | → | bw_hz | | 0x16 | GET_SF | → | (none) | | 0x16 | _resp_ | ← | sf | | 0x17 | SET_SF | → | sf | | 0x18 | GET_CR | → | (none) | | 0x18 | _resp_ | ← | cr | | 0x19 | SET_CR | → | cr | | 0x1A | GET_POWER | → | (none) | | 0x1A | _resp_ | ← | power_dBm | | 0x1B | SET_POWER | → | power_dBm | | 0x1C | GET_SYNCWORD | → | (none) | | 0x1C | _resp_ | ← | syncword | | 0x1D | SET_SYNCWORD | → | syncword | Legend: `→` = host → TNC, `←` = TNC → host ### Units All integer fields are transmitted in big-endian byte order. | Parameter | Type | Unit | Encoding | Example | | --------- | ------ | ---- | ------------- | -------------------- | | Frequency | uint32 | kHz | 4 bytes | 869.618 MHz → 869618 | | Bandwidth | uint32 | Hz | 4 bytes | 62.5 kHz → 62500 | | SF | uint8 | — | 1 byte (5–12) | SF 7 → 0x07 | | CR | uint8 | — | 1 byte (5–8) | 4/5 → 0x05 | | Power | int8 | dBm | 1 byte | 22 dBm → 0x16 | | Sync word | uint8 | — | 1 byte | 0x34 → public | ## LoRa ### Hardware Common LoRa hardware is supported via the RadioLib library: | Module | Frequency band | Notes | | --------------- | -------------------- | ------------------------------------ | | SX1262 | 868 / 915 MHz | Used on Heltec, RAK, Seeed boards | | SX1268 | 433 / 470 MHz | 433 MHz ISM band variants | | LR1110 | 150–960 MHz | Semtech LR11xx family | | LR1120 | 150–960 MHz, 2.4 GHz | Semtech LR11xx family | | SX1276 / SX1278 | 137–1020 MHz | Older boards; supported via RadioLib | ### Bandwidths All LoRa bandwidths supported by RadioLib are accepted. SX126x-based boards support: | Bandwidth | Wire value (uint32 Hz) | | --------- | ---------------------- | | 7.8 kHz | 7800 | | 10.4 kHz | 10400 | | 15.6 kHz | 15600 | | 20.8 kHz | 20800 | | 31.25 kHz | 31250 | | 41.7 kHz | 41700 | | 62.5 kHz | 62500 | | 125 kHz | 125000 | | 250 kHz | 250000 | | 500 kHz | 500000 | Note: SX1272/SX1273-based boards support only 125, 250, and 500 kHz. Not all bandwidths are legal for all frequency bands — consult local regulations. ## Project Structure Three-tier configuration hierarchy: base + SoC + board. Pin definitions are compile-time macros in board configs (no separate pins.h). ```text loramodem/ ├── platformio.ini # [platformio] + [env:base] ├── soc/ # SoC shared configs │ ├── esp32/platformio.ini │ ├── esp32s3/platformio.ini │ └── nrf52/platformio.ini ├── hardware/ # 20 board configs │ ├── heltec/ (11 boards) │ ├── lilygo/ (4 boards) │ ├── seeed/ (1 board) │ └── rak/ (4 boards) └── src/ ├── main.cpp # Arduino setup()/loop() — calls C APIs ├── kiss.h / kiss.c # KISS protocol — C99 ├── radio.h / radio.cpp # RadioLib wrapper — C++ (extern "C" API) └── config.h # Compile-time defaults + pin validation ``` ## Building Prerequisites: [PlatformIO](https://platformio.org/) CLI or IDE extension. **Build for a specific board:** ```sh pio run -e heltec_v3 pio run -e rak_rak4631 pio run -e lilygo_t_beam_1w ``` **Upload to a connected board:** ```sh pio run -e heltec_wifi_lora_32_v3 -t upload ``` **Monitor serial output:** ```sh pio device monitor -b 115200 ``` Note: KISS frames are binary. Use a KISS-capable TNC client (e.g., Direwolf, socat) to interact with the modem over serial. Available board environments are defined in `platformio.ini`. ## Configuration Defaults The modem initializes with these LoRa parameters at power-on: | Parameter | Default value | | --------- | -------------------- | | Frequency | TBD MHz | | Bandwidth | 125 kHz | | SF | TBD | | CR | 4/5 | | Power | TBD dBm | | Sync word | `0x34` (LoRa public) | These defaults may be overridden at compile time via `config.h`. Parameters changed via KISS port 2 commands take effect immediately but are **not persisted** across power cycles unless persistent storage (EEPROM/NVS) is implemented. ## Framework This project uses [PlatformIO](https://platformio.org/) as the build system with [RadioLib](https://github.com/jgromes/RadioLib) as the LoRa driver library. Board-specific pin assignments and hardware configurations are isolated in the `variants/` directory, allowing the core KISS modem logic to remain board-agnostic. The USB-CDC serial interface is used by default for KISS communication. On supported boards, a hardware UART may be selected instead via compile-time configuration in `config.h`.