Refactor/lora cli #5
Keine Dateien entsprechen Ihrer Suche
@@ -72,12 +72,6 @@ namespace Config {
|
|||||||
constexpr uint8_t LORA_PREAMBLE_LENGTH_MSB = 0x00;
|
constexpr uint8_t LORA_PREAMBLE_LENGTH_MSB = 0x00;
|
||||||
constexpr uint8_t LORA_PREAMBLE_LENGTH_LSB = 0x0C; // Semtech Factor Default is 0x08, MeshCom with 0x0C, i.e. 12 symbols
|
constexpr uint8_t LORA_PREAMBLE_LENGTH_LSB = 0x0C; // Semtech Factor Default is 0x08, MeshCom with 0x0C, i.e. 12 symbols
|
||||||
|
|
||||||
// Modem Config 1: BW 250kHz (0x80), CR 4/6 (0x04), Explicit Header (0x00) -> 0x84
|
|
||||||
// constexpr uint8_t LORA_MODEM_CONFIG_1 = 0x84; // @TODO: will be removed in v1.1
|
|
||||||
|
|
||||||
// Modem Config 2: SF11 (0xB0), TX Continuous off (0x00), CRC On (0x04) -> 0xB4
|
|
||||||
// constexpr uint8_t LORA_MODEM_CONFIG_2 = 0xB4; // @TODO: will be removed in v1.1
|
|
||||||
|
|
||||||
// --- DYNAMIC COMPILE-TIME BIT-SHIFTING EXPRESSIONS ---
|
// --- DYNAMIC COMPILE-TIME BIT-SHIFTING EXPRESSIONS ---
|
||||||
|
|
||||||
// Helper to map bandwidth kHz to Semtech Register Bits (7-4 of RegModemConfig1)
|
// Helper to map bandwidth kHz to Semtech Register Bits (7-4 of RegModemConfig1)
|
||||||
|
|||||||
+191
-40
@@ -141,31 +141,35 @@ esp_err_t LoraController::initializeHardware() {
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Configure interrupt triggering on the rising edge (PosEdge)
|
||||||
err = gpio_set_intr_type(Config::LORA_IRQ_PIN, GPIO_INTR_POSEDGE);
|
err = gpio_set_intr_type(Config::LORA_IRQ_PIN, GPIO_INTR_POSEDGE);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "Failed to set GPIO interrupt type: %s", esp_err_to_name(err));
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
// Bind the static ISR handler to the physical pin context
|
||||||
err = gpio_isr_handler_add(Config::LORA_IRQ_PIN, LoraController::gpioIsrHandler, m_isrSemaphore);
|
err = gpio_isr_handler_add(Config::LORA_IRQ_PIN, LoraController::gpioIsrHandler, m_isrSemaphore);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "Failed to register GPIO ISR handler: %s", esp_err_to_name(err));
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
return err;
|
// MARK STATE MATRIX READY (Thread-Safe Status Update)
|
||||||
|
// We use a safe timeout here. If the mutex is permanently blocked during boot,
|
||||||
|
// we must escalate this as a system fault instead of returning ESP_OK.
|
||||||
|
if (xSemaphoreTake(m_stateMutex, pdMS_TO_TICKS(100)) == pdTRUE) {
|
||||||
|
m_state.isInitialized = true;
|
||||||
|
m_state.frequencyHz = Config::LORA_DEFAULT_FREQ_HZ;
|
||||||
|
xSemaphoreGive(m_stateMutex);
|
||||||
|
} else {
|
||||||
|
ESP_LOGE(TAG, "Fatal: State mutex acquisition timed out during hardware registration.");
|
||||||
|
return ESP_ERR_TIMEOUT; // Escalate the mutex block as a hard boot failure
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "SX1276 LoRa transceiver attached successfully via SPI (Version: 0x%02X).", version);
|
||||||
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
void LoraController::setModeReceiver() {
|
|
||||||
writeRegister(0x01, 0x80U); // Sleep + LoRa Mode
|
|
||||||
vTaskDelay(pdMS_TO_TICKS(10));
|
|
||||||
|
|
||||||
// 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 state changed to RXCONTINUOUS");
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
void LoraController::setModeReceiver() {
|
void LoraController::setModeReceiver() {
|
||||||
// DIO0 auf RxDone mappen (Bit 7-6 auf 00)
|
// DIO0 auf RxDone mappen (Bit 7-6 auf 00)
|
||||||
writeRegister(0x40, 0x00U);
|
writeRegister(0x40, 0x00U);
|
||||||
@@ -185,14 +189,18 @@ void LoraController::applyModemConfig() {
|
|||||||
writeRegister(0x01, 0x80U);
|
writeRegister(0x01, 0x80U);
|
||||||
vTaskDelay(pdMS_TO_TICKS(10));
|
vTaskDelay(pdMS_TO_TICKS(10));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Apply RF carrier center frequency (e.g., MeshCom 433.175 MHz)
|
// Apply RF carrier center frequency (e.g., MeshCom 433.175 MHz)
|
||||||
writeRegister(0x06, Config::LORA_FRF_MSB);
|
writeRegister(0x06, Config::LORA_FRF_MSB);
|
||||||
writeRegister(0x07, Config::LORA_FRF_MID);
|
writeRegister(0x07, Config::LORA_FRF_MID);
|
||||||
writeRegister(0x08, Config::LORA_FRF_LSB);
|
writeRegister(0x08, Config::LORA_FRF_LSB);
|
||||||
|
|
||||||
|
|
||||||
// just write the m_state frequency....
|
// just write the m_state frequency....
|
||||||
m_state.frequencyHz = Config::LORA_DEFAULT_FREQ_HZ; // @TODO: remove this line after implementation of the doubly linked list
|
m_state.frequencyHz = Config::LORA_DEFAULT_FREQ_HZ; // @TODO: remove this line after implementation of the doubly linked list
|
||||||
|
|
||||||
|
|
||||||
// Configure modem bandwidth, spreading factor, and coding rate
|
// Configure modem bandwidth, spreading factor, and coding rate
|
||||||
writeRegister(0x1D, Config::LORA_MODEM_CONFIG_1);
|
writeRegister(0x1D, Config::LORA_MODEM_CONFIG_1);
|
||||||
writeRegister(0x1E, Config::LORA_MODEM_CONFIG_2);
|
writeRegister(0x1E, Config::LORA_MODEM_CONFIG_2);
|
||||||
@@ -211,27 +219,76 @@ void LoraController::applyModemConfig() {
|
|||||||
ESP_LOGI(TAG, "Radio profile applied: 433.175 MHz | SF11 | BW 250kHz | SyncWord 0x34");
|
ESP_LOGI(TAG, "Radio profile applied: 433.175 MHz | SF11 | BW 250kHz | SyncWord 0x34");
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
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
|
#ifndef MATH_COMPARE
|
||||||
writeRegister(0x01, 0x81U);
|
#define MATH_COMPARE 0
|
||||||
vTaskDelay(pdMS_TO_TICKS(5));
|
#endif
|
||||||
|
|
||||||
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::setFrequency(uint32_t freq_hz) {
|
void LoraController::setFrequency(uint32_t freq_hz) {
|
||||||
// @TODO
|
if (m_spiHandle == nullptr) return;
|
||||||
|
|
||||||
|
if (xSemaphoreTake(m_stateMutex, portMAX_DELAY) == pdTRUE) {
|
||||||
|
m_state.frequencyHz = freq_hz;
|
||||||
|
|
||||||
|
// 1. Fetch current mode and shift transceiver into Standby (0x01)
|
||||||
|
// Changing RF carrier registers is strictly prohibited while actively radiating or sampling
|
||||||
|
const uint8_t previousMode = readRegister(0x01); // RegOpMode
|
||||||
|
writeRegister(0x01, static_cast<uint8_t>((previousMode & 0xF8U) | 0x01U));
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(2));
|
||||||
|
|
||||||
|
// 2. High-speed fractional frequency calculation
|
||||||
|
// Formula optimized from: (freq_hz * 2^19) / 32,000,000 b/c 32,000,000 can be seen as 2^11 * 5^6:
|
||||||
|
// we see freq_hz * 2^(19-11)/5^6 which is: freq_hz * 2^8 / 15625, i.e.
|
||||||
|
// reduced to exact fraction: 256 / 15,625
|
||||||
|
// or even more using shift operation, we can switch on a compare of all three methods with #MATH_COMPARE 1
|
||||||
|
|
||||||
|
#if (MATH_COMPARE == 1)
|
||||||
|
// =================================================================
|
||||||
|
// BENCHMARK MODE (Runs all 3 variants and measures CPU cycles)
|
||||||
|
// =================================================================
|
||||||
|
uint32_t start_cycles = esp_cpu_get_cycle_count();
|
||||||
|
[[maybe_unused]] volatile uint32_t frf_long = static_cast<uint32_t>((static_cast<uint64_t>(freq_hz) << 19) / 32000000ULL);
|
||||||
|
uint32_t cycles_long = esp_cpu_get_cycle_count() - start_cycles;
|
||||||
|
|
||||||
|
start_cycles = esp_cpu_get_cycle_count();
|
||||||
|
[[maybe_unused]] volatile uint32_t frf_64bit = static_cast<uint32_t>((static_cast<uint64_t>(freq_hz) * 256ULL) / 15625ULL);
|
||||||
|
uint32_t cycles_64bit = esp_cpu_get_cycle_count() - start_cycles;
|
||||||
|
|
||||||
|
start_cycles = esp_cpu_get_cycle_count();
|
||||||
|
volatile uint32_t frf_shift = static_cast<uint32_t>((static_cast<uint64_t>(freq_hz) * 1125899907ULL) >> 36);
|
||||||
|
uint32_t cycles_shift = esp_cpu_get_cycle_count() - start_cycles;
|
||||||
|
|
||||||
|
printf("[MATH_COMPARE] Freq: %lu Hz -> long 64bit: %lu cycles | 64bit: %lu cycles | Shift: %lu cycles\n",
|
||||||
|
freq_hz, cycles_long, cycles_64bit, cycles_shift);
|
||||||
|
|
||||||
|
const uint32_t frf = frf_shift; // Fallback to the safe bit-shift variant
|
||||||
|
#else
|
||||||
|
// =================================================================
|
||||||
|
// PRODUCTION MODE (Highly optimized, zero benchmark overhead)
|
||||||
|
// =================================================================
|
||||||
|
const uint32_t frf = static_cast<uint32_t>((static_cast<uint64_t>(freq_hz) * 1125899907ULL) >> 36);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
// 3. Shift the 24-bit frequency tracking token across the three consecutive hardware registers
|
||||||
|
writeRegister(0x06, static_cast<uint8_t>((frf >> 16) & 0xFFU)); // RegFrfMsb
|
||||||
|
writeRegister(0x07, static_cast<uint8_t>((frf >> 8) & 0xFFU)); // RegFrfMid
|
||||||
|
writeRegister(0x08, static_cast<uint8_t>(frf & 0xFFU)); // RegFrfLsb
|
||||||
|
|
||||||
|
// 4. Restore previous operational transceiver path configuration
|
||||||
|
writeRegister(0x01, previousMode);
|
||||||
|
xSemaphoreGive(m_stateMutex);
|
||||||
|
|
||||||
|
// 5. Force state synchronization and PLL locking verification
|
||||||
|
setModeReceiver();
|
||||||
|
|
||||||
|
// High-priority diagnosis logging bypasses CLI lock buffers uncensored
|
||||||
|
printf("\r\n[LoRa HW] Frequency successfully shifted to %lu Hz (FRF Word: 0x%06X)\r\n",
|
||||||
|
(unsigned long)freq_hz, (unsigned int)(frf & 0xFFFFFFU));
|
||||||
|
fflush(stdout);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void LoraController::setBandwidth(uint32_t bw_khz) {
|
void LoraController::setBandwidth(uint32_t bw_khz) {
|
||||||
if (m_spiHandle == nullptr) return;
|
if (m_spiHandle == nullptr) return;
|
||||||
@@ -316,6 +373,18 @@ void LoraController::calculateTelemetry(uint8_t rawRssi, uint8_t rawSnr, float&
|
|||||||
}
|
}
|
||||||
|
|
||||||
void LoraController::run() {
|
void LoraController::run() {
|
||||||
|
// Lambda Object:
|
||||||
|
auto utf8_advance = [](const std::string& str, size_t start_byte, size_t num_chars) -> size_t {
|
||||||
|
size_t i = start_byte;
|
||||||
|
size_t chars_counted = 0;
|
||||||
|
while (i < str.length() && chars_counted < num_chars) {
|
||||||
|
if (((unsigned char)str[i] & 0xC0) != 0x80) chars_counted++;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
while (i < str.length() && ((unsigned char)str[i] & 0xC0) == 0x80) i++;
|
||||||
|
return i;
|
||||||
|
};
|
||||||
|
|
||||||
if (initializeHardware() != ESP_OK) {
|
if (initializeHardware() != ESP_OK) {
|
||||||
ESP_LOGE(TAG, "Critical: LoRa init failed. Thread aborted.");
|
ESP_LOGE(TAG, "Critical: LoRa init failed. Thread aborted.");
|
||||||
return;
|
return;
|
||||||
@@ -328,7 +397,9 @@ void LoraController::run() {
|
|||||||
ESP_LOGI(TAG, "LoRa RX was initialized and is running");
|
ESP_LOGI(TAG, "LoRa RX was initialized and is running");
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
if (xSemaphoreTake(m_isrSemaphore, portMAX_DELAY) == pdTRUE) {
|
// CHANGE: wait a maximum of 500 ms instead of infinity long
|
||||||
|
if (xSemaphoreTake(m_isrSemaphore, pdMS_TO_TICKS(500)) == pdTRUE) {
|
||||||
|
// if (xSemaphoreTake(m_isrSemaphore, portMAX_DELAY) == pdTRUE) {
|
||||||
uint32_t irqFlags = readRegister(0x12);
|
uint32_t irqFlags = readRegister(0x12);
|
||||||
writeRegister(0x12, static_cast<uint8_t>(irqFlags));
|
writeRegister(0x12, static_cast<uint8_t>(irqFlags));
|
||||||
|
|
||||||
@@ -368,7 +439,6 @@ void LoraController::run() {
|
|||||||
printf("%c", c);
|
printf("%c", c);
|
||||||
else
|
else
|
||||||
printf("~"); // not printable chars
|
printf("~"); // not printable chars
|
||||||
|
|
||||||
}
|
}
|
||||||
printf("\"\r\n Hex : ");
|
printf("\"\r\n Hex : ");
|
||||||
for (uint8_t i = 0; i < packet.length; i++)
|
for (uint8_t i = 0; i < packet.length; i++)
|
||||||
@@ -380,11 +450,39 @@ void LoraController::run() {
|
|||||||
|
|
||||||
// FIXED: Uses the new parameter order (line first, text second)
|
// FIXED: Uses the new parameter order (line first, text second)
|
||||||
if (display != nullptr) {
|
if (display != nullptr) {
|
||||||
char displayBuffer[16];
|
char displayBuffer[85]; // worst case 21 chars with each 4 bytes + Nullbyte.
|
||||||
snprintf(displayBuffer, sizeof(displayBuffer), "LoRa rx #%03u", (unsigned int)(packetCounter % 1000));
|
|
||||||
|
snprintf(displayBuffer, sizeof(displayBuffer), "#%03u %4.1fdBm %2.1fdB",
|
||||||
|
(unsigned int)(packetCounter % 1000),
|
||||||
|
packet.rssi,
|
||||||
|
packet.snr);
|
||||||
display->printMessage(2, displayBuffer);
|
display->printMessage(2, displayBuffer);
|
||||||
|
// we don't care about UTF-8 for now
|
||||||
|
std::string text(reinterpret_cast<const char*>(packet.payload), static_cast<size_t>(packet.length));
|
||||||
|
|
||||||
|
size_t byte_pos = 0;
|
||||||
|
uint8_t linenr = 3; // start at line 3
|
||||||
|
|
||||||
|
while ( byte_pos < text.length() && linenr < 8) {
|
||||||
|
// The '::' ensures that the global/local function is called.
|
||||||
|
size_t next_byte_pos = utf8_advance(text, byte_pos, 21);
|
||||||
|
std::string line = text.substr(byte_pos, next_byte_pos - byte_pos);
|
||||||
|
|
||||||
|
std::strncpy(displayBuffer, line.c_str(), sizeof(displayBuffer ) - 1);
|
||||||
|
displayBuffer[sizeof(displayBuffer) - 1] ='\0';
|
||||||
|
|
||||||
|
display->printMessage(linenr++, displayBuffer);
|
||||||
|
byte_pos = next_byte_pos;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// New: after work or after 500ms delay, the chip has usually changed its mode, we update the state for the m_state:
|
||||||
|
uint8_t currentHardwareMode = readRegister(0x01);
|
||||||
|
if (xSemaphoreTake(m_stateMutex, pdMS_TO_TICKS(10)) == pdTRUE) {
|
||||||
|
m_state.currentMode = currentHardwareMode;
|
||||||
|
xSemaphoreGive(m_stateMutex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -400,3 +498,56 @@ void LoraController::startTask(uint32_t priority) {
|
|||||||
1
|
1
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Generates an atomic, high-performance runtime snapshot of configuration parameters and metrics.
|
||||||
|
*/
|
||||||
|
LoraStateSnapshot LoraController::getSnapshot() {
|
||||||
|
LoraStateSnapshot snapshot = {};
|
||||||
|
|
||||||
|
// Sichere die thread-übergreifende Konsistenz mittels Mutex ab
|
||||||
|
if (xSemaphoreTake(m_stateMutex, portMAX_DELAY) == pdTRUE) {
|
||||||
|
|
||||||
|
// Kopiere die primitiven Werte aus dem internen Zustandstabellen-Struct (m_state)
|
||||||
|
snapshot.frequencyHz = m_state.frequencyHz;
|
||||||
|
snapshot.bandwidthKhz = m_state.bandwidthKhz;
|
||||||
|
snapshot.totalPacketsReceived = m_state.totalPacketsReceived;
|
||||||
|
snapshot.totalPacketsTransmitted = m_state.totalPacketsTransmitted;
|
||||||
|
snapshot.spreadingFactor = m_state.spreadingFactor;
|
||||||
|
snapshot.codingRate = m_state.codingRate;
|
||||||
|
snapshot.isInitialized = m_state.isInitialized;
|
||||||
|
|
||||||
|
// Zähle die Elemente in der Liste, ohne die schweren Payload-Vektoren zu kopieren
|
||||||
|
// TODO install the package list:
|
||||||
|
// snapshot.currentHistoryCount = m_state.packetList.size();
|
||||||
|
|
||||||
|
// Read Register 0x01 ONLY ONCE to avoid SPI race conditions
|
||||||
|
// uint8_t rohofmode = readRegister(0x01);
|
||||||
|
// uint8_t rawMode = rohofmode & 0x07U;
|
||||||
|
// use the cached mode from RAM instead live register read
|
||||||
|
uint8_t rawMode = m_state.currentMode & 0x07U;
|
||||||
|
|
||||||
|
// Print the real, unmanipulated hex value safely
|
||||||
|
//printf("Readregister actual raw hex: 0x%02X\r\n", rohofmode);
|
||||||
|
//fflush(stdout);
|
||||||
|
|
||||||
|
static const char* const MODE_STRINGS[] = {
|
||||||
|
"SLEEP", // 0x00
|
||||||
|
"STANDBY", // 0x01
|
||||||
|
"FSTX", // 0x02
|
||||||
|
"TX", // 0x03
|
||||||
|
"FSRX", // 0x04
|
||||||
|
"RXCONTINUOUS", // 0x05
|
||||||
|
"RXSINGLE", // 0x06
|
||||||
|
"CAD" // 0x07
|
||||||
|
};
|
||||||
|
|
||||||
|
strncpy(snapshot.currentModeStr, MODE_STRINGS[rawMode], sizeof(snapshot.currentModeStr) - 1);
|
||||||
|
snapshot.currentModeStr[sizeof(snapshot.currentModeStr) - 1] = '\0'; // guarantee NULL termination
|
||||||
|
|
||||||
|
// Release mutex immediately
|
||||||
|
xSemaphoreGive(m_stateMutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return snapshot;
|
||||||
|
}
|
||||||
@@ -16,6 +16,9 @@
|
|||||||
#include "freertos/semphr.h"
|
#include "freertos/semphr.h"
|
||||||
#include "freertos/task.h"
|
#include "freertos/task.h"
|
||||||
#include "lora_types.hpp"
|
#include "lora_types.hpp"
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -76,6 +79,17 @@ public:
|
|||||||
*/
|
*/
|
||||||
void setBandwidth(uint32_t bw_khz);
|
void setBandwidth(uint32_t bw_khz);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Generates an atomic, high-performance runtime snapshot of configuration parameters and metrics.
|
||||||
|
* @details This routine performs a rapid, thread-safe memory copy of the volatile tracking state variables.
|
||||||
|
* Unlike getPacketListCopy(), this method explicitly excludes the heavy heap-allocated packet list data,
|
||||||
|
* making it exceptionally lightweight and highly suitable for high-frequency UI display refreshes,
|
||||||
|
* CLI updates, or periodic remote health diagnostics.
|
||||||
|
* @note Acquires the internal synchronization mutex lock momentarily to guarantee structural telemetry alignment.
|
||||||
|
* @return A self-contained snapshot structure mapping the current system hardware configurations and operational counts.
|
||||||
|
*/
|
||||||
|
LoraStateSnapshot getSnapshot();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static const char* TAG;
|
static const char* TAG;
|
||||||
|
|
||||||
@@ -110,4 +124,6 @@ private:
|
|||||||
*/
|
*/
|
||||||
void calculateTelemetry(uint8_t rawRssi, uint8_t rawSnr, float& outRssi, float& outSnr);
|
void calculateTelemetry(uint8_t rawRssi, uint8_t rawSnr, float& outRssi, float& outSnr);
|
||||||
|
|
||||||
|
size_t utf8_advance(const std::string& str, size_t start_byte, size_t num_chars);
|
||||||
|
|
||||||
};
|
};
|
||||||
@@ -20,9 +20,28 @@ struct LoraStateTable {
|
|||||||
uint32_t totalPacketsTransmitted = 0; ///< Accumulator metric track-counting egress frames transmitted. (4 Bytes)
|
uint32_t totalPacketsTransmitted = 0; ///< Accumulator metric track-counting egress frames transmitted. (4 Bytes)
|
||||||
uint32_t nextDatabaseId = 1; ///< Identity sequencer pointer controlling the next node's databaseId field. (4 Bytes)
|
uint32_t nextDatabaseId = 1; ///< Identity sequencer pointer controlling the next node's databaseId field. (4 Bytes)
|
||||||
uint32_t bandwidthKhz = 250; ///< Modem bandwidth performance target configured in kHz. (4 Bytes)
|
uint32_t bandwidthKhz = 250; ///< Modem bandwidth performance target configured in kHz. (4 Bytes)
|
||||||
|
uint8_t currentMode = 0; ///< Current operationl modem mode (1 Byte)
|
||||||
uint8_t spreadingFactor = 11; ///< LoRa Spreading Factor configuration (typically ranges between 6 and 12). (1 Byte)
|
uint8_t spreadingFactor = 11; ///< LoRa Spreading Factor configuration (typically ranges between 6 and 12). (1 Byte)
|
||||||
uint8_t codingRate = 6; ///< Coding Rate denominator setting (e.g., value 6 yields a 4/6 coding protection overhead). (1 Byte)
|
uint8_t codingRate = 6; ///< Coding Rate denominator setting (e.g., value 6 yields a 4/6 coding protection overhead). (1 Byte)
|
||||||
bool isInitialized = false; ///< True if hardware was successfully validated and attached during initialization. (1 Byte)
|
bool isInitialized = false; ///< True if hardware was successfully validated and attached during initialization. (1 Byte)
|
||||||
|
|
||||||
// The three 1-byte variables at the bottom align perfectly into a 32-bit slot, leaving minimal trailing padding.
|
// The three 1-byte variables at the bottom align perfectly into a 32-bit slot, leaving minimal trailing padding.
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Thread-isolated telemetry snapshot for UI displays and remote diagnostics.
|
||||||
|
* @note Optimized using "largest-to-smallest" data type ordering to minimize memory padding.
|
||||||
|
*/
|
||||||
|
struct LoraStateSnapshot {
|
||||||
|
size_t currentHistoryCount; ///< Soft-limit threshold capping the maximum number of history records stored before auto-purge. (4 or 8 Bytes)
|
||||||
|
uint32_t frequencyHz; ///< RF carrier operating frequency configured in Hertz. (4 Bytes)
|
||||||
|
uint32_t bandwidthKhz; ///< Modem bandwidth performance target configured in kHz. (4 Bytes)
|
||||||
|
uint32_t totalPacketsReceived; ///< Accumulator metric track-counting successful ingress frames. (4 Bytes)
|
||||||
|
uint32_t totalPacketsTransmitted; ///< Accumulator metric track-counting egress frames transmitted. (4 Bytes)
|
||||||
|
char currentModeStr[16]; ///< Human-readable string representation of the current operational modem mode. (16 Bytes)
|
||||||
|
uint8_t spreadingFactor; ///< LoRa Spreading Factor configuration (typically ranges between 6 and 12). (1 Byte)
|
||||||
|
uint8_t codingRate; ///< Coding Rate denominator setting (e.g., value 6 yields a 4/6 coding protection overhead). (1 Byte)
|
||||||
|
bool isInitialized; ///< True if hardware was successfully validated and attached during initialization. (1 Byte)
|
||||||
|
|
||||||
|
// Elements are strictly organized by size alignment requirements to completely prevent intermittent alignment gaps.
|
||||||
|
};
|
||||||
In neuem Issue referenzieren
Einen Benutzer sperren