diff --git a/src/cli.cpp b/src/cli.cpp index bac2cb4..5914944 100644 --- a/src/cli.cpp +++ b/src/cli.cpp @@ -18,11 +18,13 @@ #include "driver/uart.h" #include "esp_log.h" #include "config.hpp" +#include "lora.hpp" static const char* TAG = "CLI"; static i2c* i2c_bus = nullptr; static LedController* led_system = nullptr; static DisplayController* oled_display = nullptr; +static LoraController* lora_system = nullptr; #define CLI_UART_PORT UART_NUM_0 #define BUF_SIZE 1024 @@ -49,6 +51,10 @@ void cli_init(i2c* bus_pointer, LedController* led_pointer, DisplayController* d oled_display = display_pointer; } + +void cli_init_lora(LoraController* lora_pointer) { + lora_system = lora_pointer; +} /** * @brief Software timer callback task executed once terminal interaction has gone stale. * @param xTimer Handle tracking the specific FreeRTOS timing token instance. @@ -82,6 +88,8 @@ void process_command(const char* cmd) { printf(" mute - Set custom logging timeout (Current: %lus)\r\n", (unsigned long)current_mute_time_sec); printf(" blink - Set LED profile in ms (e.g. blink 1000 50)\r\n"); printf(" oled - Print text to OLED line 0-7 (e.g. oled 0 Test)\r\n"); + printf(" freq - Set dynamic radio frequency (433 / 868 MHz bounds)\r\n"); + printf(" bw <62|125|250|500>- Set dynamic LoRa bandwidth in kHz\r\n"); printf(" i2c - Scan the configured I2C bus infrastructure\r\n"); } else if (strcmp(cmd, "version") == 0) { @@ -107,8 +115,7 @@ void process_command(const char* cmd) { xTimerStop(silence_timer, pdMS_TO_TICKS(10)); } printf("Success: Infinite mute activated. Debug logs are permanently suppressed.\r\n"); - } - else { + } else { // DYNAMIC TIMEOUT: Extracted multiplication step to decouple constant values from structural macros uint32_t timeout_ms = current_mute_time_sec * 1000; @@ -147,6 +154,53 @@ void process_command(const char* cmd) { printf("Usage error: Please specify parameters as 'blink '\r\n"); } } + else if (strncmp(cmd, "freq ", 5) == 0) { + uint32_t target_freq_hz = 0; + if (sscanf(cmd + 5, "%lu", &target_freq_hz) == 1) { + // Validate limits to protect the SX1276 front-end (e.g., 410 MHz to 525 MHz) + bool valid_433 = (target_freq_hz >= 430000000 && target_freq_hz <= 440000000); + bool valid_868 = (target_freq_hz >= 863000000 && target_freq_hz <= 870000000); + + if (valid_433 || valid_868) { + if (lora_system != nullptr) { + // SX1276 formula: Frf = Ffreq * 2^19 / 32000000 + // Using double to prevent truncation during division before bit masking + double fraction = (static_cast(target_freq_hz) * 524288.0) / 32000000.0; + uint32_t frf = static_cast(fraction); + + uint8_t msb = static_cast((frf >> 16) & 0xFFU); + uint8_t mid = static_cast((frf >> 8) & 0xFFU); + uint8_t lsb = static_cast(frf & 0xFFU); + + lora_system->setFrequency(msb, mid, lsb); + printf("Success: Frequency shifted to %lu Hz.\r\n", (unsigned long)target_freq_hz); + } else { + printf("Error: LoraController handle is uninitialized.\r\n"); + } + } else { + printf("Error: Frequency out of boundaries 430-440 / 863-870 MHz band limits.\r\n"); + } + } else { + printf("Usage error: Please specify parameters as 'freq '\r\n"); + } + } + else if (strncmp(cmd, "bw ", 3) == 0) { + uint32_t target_bw = 0; + if (sscanf(cmd + 3, "%lu", &target_bw) == 1) { + if (target_bw == 62 || target_bw == 125 || target_bw == 250 || target_bw == 500) { + if (lora_system != nullptr) { + lora_system->setBandwidth(target_bw); + printf("Success: Dispatched bandwidth configuration update to %lu kHz.\r\n", (unsigned long)target_bw); + } else { + printf("Error: LoraController handle is uninitialized.\r\n"); + } + } else { + printf("Error: Invalid choice. Supported bandwidth options are: 62, 125, 250, 500.\r\n"); + } + } else { + printf("Usage error: Please specify parameter as 'bw <62|125|250|500>'\r\n"); + } + } else if (strncmp(cmd, "oled ", 5) == 0) { uint32_t targetLine = 0; char textBuffer[64]; diff --git a/src/config.hpp b/src/config.hpp index 04d9d33..2bf686a 100644 --- a/src/config.hpp +++ b/src/config.hpp @@ -3,7 +3,7 @@ * @file config.hpp * @brief Global compile-time configuration profile and pin mapping for FoxMesh * @author OM Tom (OE3TKT) - * + * * @license SPDX-License-Identifier: MIT * @copyright Copyright (c) 2026 OM Tom (OE3TKT) * For the full license terms, see the LICENSE file in the project root. @@ -31,7 +31,7 @@ namespace Config { // Hardware Configuration constexpr gpio_num_t LED_GPIO_PIN = GPIO_NUM_25; constexpr int BLINK_INTERVAL_MS = 5000; - + // OLED Display I2C Bus Configuration constexpr gpio_num_t OLED_SDA_PIN = GPIO_NUM_21; constexpr gpio_num_t OLED_SCL_PIN = GPIO_NUM_22; @@ -45,4 +45,16 @@ namespace Config { constexpr gpio_num_t LORA_CS_PIN = GPIO_NUM_18; constexpr gpio_num_t LORA_RST_PIN = GPIO_NUM_23; // Adjusted configuration for T3_V1.6 hardware revision constexpr gpio_num_t LORA_IRQ_PIN = GPIO_NUM_26; + + // Semtech SX1276 LoRa Configuration Parameters + // 433.175 MHz is calculated by: 433175000 / (32000000 / 2^19) = 7096525 = 0x06C4CD + constexpr uint8_t LORA_FRF_MSB = 0x06; + constexpr uint8_t LORA_FRF_MID = 0xC4; + constexpr uint8_t LORA_FRF_LSB = 0xCD; + + // Modem Config 1: BW 250kHz (0x80), CR 4/6 (0x04), Explicit Header (0x00) -> 0x84 + constexpr uint8_t LORA_MODEM_CONFIG_1 = 0x84; + + // Modem Config 2: SF11 (0xB0), TX Continuous off (0x00), CRC On (0x04) -> 0xB4 + constexpr uint8_t LORA_MODEM_CONFIG_2 = 0xB4; } diff --git a/src/lora.cpp b/src/lora.cpp index 509224c..2478cc5 100644 --- a/src/lora.cpp +++ b/src/lora.cpp @@ -61,6 +61,9 @@ void LoraController::writeRegister(uint8_t address, uint8_t value) { spi_device_transmit(m_spiHandle, &trans); } +// Suppress the ISO C++ braced-groups macro expansion warning for the FreeRTOS portYIELD macro +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpedantic" void IRAM_ATTR LoraController::gpioIsrHandler(void* arg) { auto* semaphore = static_cast(arg); if (semaphore != nullptr) { @@ -71,6 +74,7 @@ void IRAM_ATTR LoraController::gpioIsrHandler(void* arg) { } } } +#pragma GCC diagnostic pop esp_err_t LoraController::initializeHardware() { esp_err_t err; @@ -121,14 +125,57 @@ void LoraController::setModeReceiver() { writeRegister(0x01, 0x80U); // Sleep + LoRa Mode vTaskDelay(pdMS_TO_TICKS(10)); - // Hardwired for 868.1 MHz fallback channel - writeRegister(0x06, 0xD9U); - writeRegister(0x07, 0x06U); - writeRegister(0x08, 0x66U); + // Apply MeshCom network profile: 433.175 MHz, BW 250kHz, SF11, CR 4/6 + writeRegister(0x06, Config::LORA_FRF_MSB); + writeRegister(0x07, Config::LORA_FRF_MID); + writeRegister(0x08, Config::LORA_FRF_LSB); + + writeRegister(0x1D, Config::LORA_MODEM_CONFIG_1); // RegModemConfig1 + writeRegister(0x1E, Config::LORA_MODEM_CONFIG_2); // RegModemConfig2 writeRegister(0x40, 0x00U); // DIO0 map onto RxDone writeRegister(0x01, 0x85U); // RXCONTINUOUS - ESP_LOGI(TAG, "Radio base configuration applied. Listening on 868.1 MHz."); + ESP_LOGI(TAG, "Radio configured: 433.175 MHz | BW 250kHz | SF11 | CR 4/6"); +} + +void LoraController::setFrequency(uint8_t msb, uint8_t mid, uint8_t lsb) { + if (m_spiHandle == nullptr) return; + + // Safely force transceiver to Standby to update hardware registers safely + writeRegister(0x01, 0x81U); + vTaskDelay(pdMS_TO_TICKS(5)); + + writeRegister(0x06, msb); + writeRegister(0x07, mid); + writeRegister(0x08, lsb); + + // Re-engage Continous Reception Mode + writeRegister(0x01, 0x85U); + ESP_LOGI(TAG, "Frequency dynamically shifted to: 0x%02X%02X%02X", msb, mid, lsb); +} + +void LoraController::setBandwidth(uint32_t bw_khz) { + if (m_spiHandle == nullptr) return; + uint8_t bw_setting = 0x00U; + bool valid = false; + + if (bw_khz == 62) { bw_setting = 0x60U; valid = true; } + if (bw_khz == 125) { bw_setting = 0x70U; valid = true; } + if (bw_khz == 250) { bw_setting = 0x80U; valid = true; } + if (bw_khz == 500) { bw_setting = 0x90U; valid = true; } + + if (!valid) return; + + writeRegister(0x01, 0x81U); + vTaskDelay(pdMS_TO_TICKS(5)); + + uint8_t current_config = readRegister(0x1D); + current_config = static_cast(current_config & 0x0FU); + current_config = static_cast(current_config | bw_setting); + + writeRegister(0x1D, current_config); + writeRegister(0x01, 0x85U); + ESP_LOGI(TAG, "Modem bandwidth dynamically shifted to %lu kHz.", (unsigned long)bw_khz); } esp_err_t LoraController::transmitFrame(const uint8_t* buffer, uint8_t length) { @@ -171,7 +218,7 @@ void LoraController::run() { uint32_t irqFlags = readRegister(0x12); writeRegister(0x12, static_cast(irqFlags)); - if ((irqFlags & 0x40U) != 0U) { // RxDone + if ((irqFlags & 0x40U) != 0U) { // RxDone Flag LoraPacket packet = {}; packet.length = readRegister(0x13); @@ -181,12 +228,33 @@ void LoraController::run() { for (uint8_t i = 0; i < packet.length; i++) { packet.payload[i] = readRegister(0x00); } - + // Calculate the telemetry data packet.rssi = static_cast(static_cast(readRegister(0x1A)) - 157); packet.snr = static_cast(static_cast(readRegister(0x1B)) / 4); - ESP_LOGI(TAG, "Incoming frame packet received! Size: %u bytes, RSSI: %d dBm, SNR: %d dB", - packet.length, packet.rssi, packet.snr); + // LIVE-AUSGABE DIREKT AUF DIE CONSOLE (Garantiert un-gemutet!) + printf("\r\n--------------------------------------------------\r\n"); + printf("[LoRa RX] Paket erfolgreich empfangen!\r\n"); + printf(" Laenge: %u Bytes\r\n", packet.length); + printf(" RSSI : %d dBm (Signalstaerke)\r\n", packet.rssi); + printf(" SNR : %d dB (Rauschabstand)\r\n", packet.snr); + //ESP_LOGI(TAG, "Incoming frame packet received! Size: %u bytes, RSSI: %d dBm, SNR: %d dB", + // packet.length, packet.rssi, packet.snr); + + // ASCII-Klartext-Ausgabe für das Terminal formatiert + printf(" Inhalt: \""); + for (uint8_t i = 0; i < packet.length; i++) { + char c = static_cast(packet.payload[i]); + // Nur druckbare Zeichen ausgeben, um das Terminal nicht zu verwirren + if (c >= 32 && c <= 126) { + printf("%c", c); + } else { + printf("."); // Punkt für nicht-druckbare Binärbytes + } + } + printf("\"\r\n--------------------------------------------------\r\n"); + printf("%s # ", Config::PROJECT_TITLE); // CLI-Prompt wiederherstellen + fflush(stdout); // FIXED: Uses the new parameter order (line first, text second) if (display != nullptr) { diff --git a/src/lora.hpp b/src/lora.hpp index 49b93e1..9437950 100644 --- a/src/lora.hpp +++ b/src/lora.hpp @@ -49,6 +49,20 @@ public: */ esp_err_t transmitFrame(const uint8_t* buffer, uint8_t length); + /** + * @brief Dynamically updates the transceiver operating frequency at runtime. + * @param msb Most Significant Byte of the frequency fraction. + * @param mid Middle Significant Byte of the frequency fraction. + * @param lsb Least Significant Byte of the frequency fraction. + */ + void setFrequency(uint8_t msb, uint8_t mid, uint8_t lsb); + + /** + * @brief Dynamically updates the LoRa signal bandwidth at runtime. + * @param bw_khz Target bandwidth in kHz (e.g., 62, 125, 250, 500). + */ + void setBandwidth(uint32_t bw_khz); + private: spi_device_handle_t m_spiHandle = nullptr; TaskHandle_t m_taskHandle = nullptr;