/* * Dummy LoRa Radio Hardware Simulator * * Simulates a LoRa radio on the native (host) platform using stdio for communication. * Allows testing the KISS protocol and modem logic without hardware. * * Compiled as PlatformIO native environment for integration testing. */ #include #include #include #include #include #include /* Include the KISS implementation directly */ #include "../src/kiss.cpp" /* ─────────────────────────────────────────────────────────────────────────── */ /* Simulated Radio State */ /* ─────────────────────────────────────────────────────────────────────────── */ struct { uint32_t freq_khz; uint32_t bw_hz; uint8_t sf; uint8_t cr; int8_t power_dbm; uint8_t syncword; bool rx_active; uint8_t rx_buf[256]; int rx_len; int8_t rx_snr; int16_t rx_rssi; } radio_state = { .freq_khz = 868000, .bw_hz = 125000, .sf = 7, .cr = 5, .power_dbm = 14, .syncword = 0x12, .rx_active = true, .rx_len = 0, }; /* ─────────────────────────────────────────────────────────────────────────── */ /* Mock C API (must match radio.h) */ /* ─────────────────────────────────────────────────────────────────────────── */ typedef struct { uint32_t freq_khz; uint32_t bw_hz; uint8_t sf; uint8_t cr; int8_t power_dbm; uint8_t syncword; } radio_config_t; typedef struct { int8_t snr; int16_t rssi; } radio_rx_info_t; int radio_init(void) { fprintf(stderr, "[radio] Initialized: %lu kHz, %lu Hz BW, SF %d, CR %d, %d dBm\n", radio_state.freq_khz, radio_state.bw_hz, radio_state.sf, radio_state.cr, radio_state.power_dbm); return 0; } int radio_tx(const uint8_t *data, size_t len) { fprintf(stderr, "[radio] TX %zu bytes at %lu kHz\n", len, radio_state.freq_khz); return 0; } int radio_rx_start(void) { radio_state.rx_active = true; fprintf(stderr, "[radio] RX started\n"); return 0; } bool radio_rx_available(void) { return radio_state.rx_len > 0; } int radio_rx_read(uint8_t *buf, size_t buf_cap, radio_rx_info_t *info) { if (radio_state.rx_len == 0) return 0; size_t n = (radio_state.rx_len < (int)buf_cap) ? radio_state.rx_len : buf_cap; memcpy(buf, radio_state.rx_buf, n); if (info) { info->snr = radio_state.rx_snr; info->rssi = radio_state.rx_rssi; } radio_state.rx_len = 0; radio_rx_start(); return (int)n; } int radio_set_config(const radio_config_t *cfg) { radio_state.freq_khz = cfg->freq_khz; radio_state.bw_hz = cfg->bw_hz; radio_state.sf = cfg->sf; radio_state.cr = cfg->cr; radio_state.power_dbm = cfg->power_dbm; radio_state.syncword = cfg->syncword; fprintf(stderr, "[radio] Config updated: %lu kHz, %lu Hz BW, SF %d, CR %d, %d dBm\n", cfg->freq_khz, cfg->bw_hz, cfg->sf, cfg->cr, cfg->power_dbm); return 0; } void radio_get_config(radio_config_t *cfg) { cfg->freq_khz = radio_state.freq_khz; cfg->bw_hz = radio_state.bw_hz; cfg->sf = radio_state.sf; cfg->cr = radio_state.cr; cfg->power_dbm = radio_state.power_dbm; cfg->syncword = radio_state.syncword; } /* ─────────────────────────────────────────────────────────────────────────── */ /* Serial I/O Wrapper (stdio) */ /* ─────────────────────────────────────────────────────────────────────────── */ static void set_stdin_nonblocking(void) { int flags = fcntl(STDIN_FILENO, F_GETFL, 0); fcntl(STDIN_FILENO, F_SETFL, flags | O_NONBLOCK); } static int stdin_available(void) { fd_set readfds; FD_ZERO(&readfds); FD_SET(STDIN_FILENO, &readfds); struct timeval tv = {.tv_sec = 0, .tv_usec = 0}; return select(STDIN_FILENO + 1, &readfds, NULL, NULL, &tv) > 0; } /* ─────────────────────────────────────────────────────────────────────────── */ /* Dummy Serial replacement (for main.cpp) */ /* ─────────────────────────────────────────────────────────────────────────── */ namespace DummySerial { bool initialized = false; } /* Mock Serial object for Arduino compatibility */ class DummySerialClass { public: void begin(unsigned long baud) { fprintf(stderr, "[serial] begin(%lu)\n", baud); DummySerial::initialized = true; set_stdin_nonblocking(); } int available(void) { return stdin_available(); } int read(void) { unsigned char c; if (::read(STDIN_FILENO, &c, 1) == 1) return c; return -1; } size_t write(const uint8_t *buf, size_t size) { return fwrite(buf, 1, size, stdout); } size_t write(uint8_t c) { return putchar(c) == EOF ? 0 : 1; } int peek(void) { return -1; } void flush(void) { fflush(stdout); } }; DummySerialClass Serial; /* ─────────────────────────────────────────────────────────────────────────── */ /* Include main modem logic */ /* ─────────────────────────────────────────────────────────────────────────── */ #include "../src/main.cpp" /* ─────────────────────────────────────────────────────────────────────────── */ /* Main Entry Point */ /* ─────────────────────────────────────────────────────────────────────────── */ int main(int argc, char *argv[]) { fprintf(stderr, "[simulator] LoRa KISS Modem Simulator\n"); fprintf(stderr, "[simulator] Reading KISS frames from stdin, writing to stdout\n"); fprintf(stderr, "[simulator] Ctrl+D to exit\n\n"); setup(); int iterations = 0; while (true) { loop(); iterations++; usleep(1000); /* 1ms sleep to avoid busy waiting */ } return 0; }