diff --git a/platformio.ini b/platformio.ini index a5022b6..f63eede 100644 --- a/platformio.ini +++ b/platformio.ini @@ -50,7 +50,7 @@ build_flags = ; C99 for KISS implementation -std=c99 ; Default LoRa radio parameters — override per-board as needed - -DLORA_FREQ_KHZ=869525UL + -DLORA_FREQ_KHZ=868000UL -DLORA_BW_HZ=125000UL -DLORA_SF=7 -DLORA_CR=5 diff --git a/src/config.h b/src/config.h index 1080ba6..01c5768 100644 --- a/src/config.h +++ b/src/config.h @@ -2,7 +2,7 @@ /* Default LoRa parameters — override per-board in hardware/.../platformio.ini */ #ifndef LORA_FREQ_KHZ -# define LORA_FREQ_KHZ 869525UL /* 869.525 MHz */ +# define LORA_FREQ_KHZ 868000UL /* 868 MHz */ #endif #ifndef LORA_BW_HZ # define LORA_BW_HZ 125000UL /* 125 kHz */ diff --git a/src/main.cpp b/src/main.cpp index f3e4f2f..a8c3d79 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -8,6 +8,217 @@ static kiss_decoder_t rx_decoder; /* host → modem (serial in) */ static uint8_t tx_buf[KISS_MAX_FRAME]; static uint8_t radio_buf[KISS_MAX_FRAME]; +/* ── Config command handler (Port 2) ────────────────────────────────────── */ + +#define CMD_RES_OK 0x01 +#define CMD_RES_ERROR 0x02 +#define CMD_GET_RADIO 0x10 +#define CMD_SET_RADIO 0x11 +#define CMD_GET_FREQUENCY 0x12 +#define CMD_SET_FREQUENCY 0x13 +#define CMD_GET_BANDWIDTH 0x14 +#define CMD_SET_BANDWIDTH 0x15 +#define CMD_GET_SF 0x16 +#define CMD_SET_SF 0x17 +#define CMD_GET_CR 0x18 +#define CMD_SET_CR 0x19 +#define CMD_GET_POWER 0x1A +#define CMD_SET_POWER 0x1B +#define CMD_GET_SYNCWORD 0x1C +#define CMD_SET_SYNCWORD 0x1D + +static void encode_u32_be(uint8_t *dst, uint32_t val) { + dst[0] = (uint8_t)(val >> 24); + dst[1] = (uint8_t)(val >> 16); + dst[2] = (uint8_t)(val >> 8); + dst[3] = (uint8_t)val; +} + +static uint32_t decode_u32_be(const uint8_t *src) { + return ((uint32_t)src[0] << 24) | ((uint32_t)src[1] << 16) | + ((uint32_t)src[2] << 8) | (uint32_t)src[3]; +} + +static void send_response(uint8_t cmd, const uint8_t *payload, size_t len) { + uint8_t frame[KISS_MAX_FRAME]; + frame[0] = cmd; + if (len > 0) { + for (size_t i = 0; i < len; i++) { + frame[1 + i] = payload[i]; + } + } + size_t encoded = kiss_encode(KISS_PORT_CONFIG, frame, 1 + len, tx_buf, + sizeof(tx_buf)); + Serial.write(tx_buf, encoded); +} + +static void handle_config_command(const kiss_frame_t *frame) { + if (frame->len == 0) + return; + + uint8_t cmd = frame->data[0]; + radio_config_t cfg; + + switch (cmd) { + case CMD_GET_RADIO: { + radio_get_config(&cfg); + uint8_t payload[13]; + encode_u32_be(&payload[0], cfg.freq_khz); + encode_u32_be(&payload[4], cfg.bw_hz); + payload[8] = cfg.sf; + payload[9] = cfg.cr; + payload[10] = (uint8_t)cfg.power_dbm; + payload[11] = 0; /* reserved */ + payload[12] = 0; /* reserved */ + send_response(CMD_GET_RADIO, payload, 13); + break; + } + + case CMD_SET_RADIO: { + if (frame->len < 11) + break; /* Insufficient payload */ + radio_get_config(&cfg); /* Preserve syncword */ + cfg.freq_khz = decode_u32_be(&frame->data[1]); + cfg.bw_hz = decode_u32_be(&frame->data[5]); + cfg.sf = frame->data[9]; + cfg.cr = frame->data[10]; + cfg.power_dbm = (int8_t)frame->data[11]; + if (radio_set_config(&cfg) == 0) { + send_response(CMD_RES_OK, 0, 0); + } else { + send_response(CMD_RES_ERROR, 0, 0); + } + break; + } + + case CMD_GET_FREQUENCY: { + radio_get_config(&cfg); + uint8_t payload[4]; + encode_u32_be(payload, cfg.freq_khz); + send_response(CMD_GET_FREQUENCY, payload, 4); + break; + } + + case CMD_SET_FREQUENCY: { + if (frame->len < 5) + break; + radio_get_config(&cfg); + cfg.freq_khz = decode_u32_be(&frame->data[1]); + if (radio_set_config(&cfg) == 0) { + send_response(CMD_RES_OK, 0, 0); + } else { + send_response(CMD_RES_ERROR, 0, 0); + } + break; + } + + case CMD_GET_BANDWIDTH: { + radio_get_config(&cfg); + uint8_t payload[4]; + encode_u32_be(payload, cfg.bw_hz); + send_response(CMD_GET_BANDWIDTH, payload, 4); + break; + } + + case CMD_SET_BANDWIDTH: { + if (frame->len < 5) + break; + radio_get_config(&cfg); + cfg.bw_hz = decode_u32_be(&frame->data[1]); + if (radio_set_config(&cfg) == 0) { + send_response(CMD_RES_OK, 0, 0); + } else { + send_response(CMD_RES_ERROR, 0, 0); + } + break; + } + + case CMD_GET_SF: { + radio_get_config(&cfg); + uint8_t payload[1] = {cfg.sf}; + send_response(CMD_GET_SF, payload, 1); + break; + } + + case CMD_SET_SF: { + if (frame->len < 2) + break; + radio_get_config(&cfg); + cfg.sf = frame->data[1]; + if (radio_set_config(&cfg) == 0) { + send_response(CMD_RES_OK, 0, 0); + } else { + send_response(CMD_RES_ERROR, 0, 0); + } + break; + } + + case CMD_GET_CR: { + radio_get_config(&cfg); + uint8_t payload[1] = {cfg.cr}; + send_response(CMD_GET_CR, payload, 1); + break; + } + + case CMD_SET_CR: { + if (frame->len < 2) + break; + radio_get_config(&cfg); + cfg.cr = frame->data[1]; + if (radio_set_config(&cfg) == 0) { + send_response(CMD_RES_OK, 0, 0); + } else { + send_response(CMD_RES_ERROR, 0, 0); + } + break; + } + + case CMD_GET_POWER: { + radio_get_config(&cfg); + uint8_t payload[1] = {(uint8_t)cfg.power_dbm}; + send_response(CMD_GET_POWER, payload, 1); + break; + } + + case CMD_SET_POWER: { + if (frame->len < 2) + break; + radio_get_config(&cfg); + cfg.power_dbm = (int8_t)frame->data[1]; + if (radio_set_config(&cfg) == 0) { + send_response(CMD_RES_OK, 0, 0); + } else { + send_response(CMD_RES_ERROR, 0, 0); + } + break; + } + + case CMD_GET_SYNCWORD: { + radio_get_config(&cfg); + uint8_t payload[1] = {cfg.syncword}; + send_response(CMD_GET_SYNCWORD, payload, 1); + break; + } + + case CMD_SET_SYNCWORD: { + if (frame->len < 2) + break; + radio_get_config(&cfg); + cfg.syncword = frame->data[1]; + if (radio_set_config(&cfg) == 0) { + send_response(CMD_RES_OK, 0, 0); + } else { + send_response(CMD_RES_ERROR, 0, 0); + } + break; + } + + default: + /* Unknown command, no response */ + break; + } +} + void setup() { Serial.begin(KISS_BAUD); kiss_decoder_init(&rx_decoder); @@ -23,8 +234,9 @@ void loop() { if (kiss_decode(&rx_decoder, (uint8_t)Serial.read(), &frame)) { if (frame.port == KISS_PORT_DATA) { radio_tx(frame.data, frame.len); + } else if (frame.port == KISS_PORT_CONFIG) { + handle_config_command(&frame); } - /* port 2 config handling goes here */ } }