Commits vergleichen
21
Commits
| Autor | SHA1 | Datum | |
|---|---|---|---|
|
|
bb4fc91703 | ||
|
|
e847e6c750 | ||
|
|
ff975ac87a | ||
|
|
6a8f5db055 | ||
|
|
36c303464f | ||
|
|
04e95484a6 | ||
|
|
8eb3a7ff18 | ||
|
|
35a2aae086 | ||
|
|
f6932c8c91 | ||
|
|
fec37babcf | ||
|
|
7680928dad | ||
|
|
a14c540e1d | ||
|
|
d95a9a4620 | ||
|
|
032b9be518 | ||
|
|
e7fbdb0f8d | ||
|
|
1d1020716e | ||
|
|
9dd939b335 | ||
|
|
a6ad249526 | ||
|
|
e0767d3322 | ||
|
|
418042bb3d | ||
|
|
fb542f9661 |
Keine Dateien entsprechen Ihrer Suche
@@ -1,4 +1,4 @@
|
||||
name: Automated CI Build of FoxMesh
|
||||
name: Build of FoxMesh
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -22,6 +22,7 @@ jobs:
|
||||
- run: echo "🎉 The job was automatically triggered by a ${{ gitea.event_name }} event."
|
||||
- run: echo "🐧 This job is now running on a ${{ runner.os }} server hosted by Gitea!"
|
||||
- run: echo "🔎 The name of your branch is ${{ gitea.ref }} and your repository is ${{ gitea.repository }}."
|
||||
|
||||
# 1. Checkout the target branch
|
||||
- name: Checkout Target branch
|
||||
uses: actions/checkout@v7
|
||||
@@ -43,31 +44,38 @@ jobs:
|
||||
CLEAN_BRANCH=$(echo "$RAW_BRANCH" | sed 's/[^a-zA-Z0-9.\-]/_§/g' | sed 's|§|-|g' | sed 's|_||g')
|
||||
echo "GIT_BRANCH=$CLEAN_BRANCH" >> $GITHUB_ENV
|
||||
|
||||
# 3. Set up Python environment
|
||||
- name: Set up Python 3.x
|
||||
uses: actions/setup-python@v7
|
||||
with:
|
||||
python-version: '3.x'
|
||||
#3 Python alread installed
|
||||
- name: Show Python Version
|
||||
run: python --version
|
||||
|
||||
# 4. Cache global PlatformIO directories, uses a dynamic key based on platformio.ini
|
||||
- name: Cache PlatformIO
|
||||
uses: actions/cache@v6
|
||||
with:
|
||||
path: |
|
||||
~/.cache/pio
|
||||
~/.platformio
|
||||
.platformio
|
||||
.pio
|
||||
key: ${{ runner.os }}-pio-${{ hashFiles('**/platformio.ini') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pio-
|
||||
|
||||
- name: PlatformIO in venv installieren
|
||||
run: |
|
||||
python3 -m venv .venv
|
||||
source .venv/bin/activate
|
||||
python3 -m pip install --quiet --upgrade pip platformio intelhex
|
||||
# Ab hier steht platformio in diesem Schritt zur Verfügung
|
||||
pio --version
|
||||
# Fügt den Pfad absolut dem GitHub/Gitea-Pfad hinzu:
|
||||
echo "$PWD/.venv/bin" >> $GITHUB_PATH
|
||||
|
||||
# 5. Install PlatformIO Core and the required intelhex extension in a single, quiet command
|
||||
- name: Install PlatformIO Core
|
||||
run: python -m pip install --quiet --upgrade pip platformio intelhex
|
||||
# - name: Install PlatformIO Core
|
||||
# run: python -m pip install --quiet --upgrade pip platformio intelhex
|
||||
|
||||
# 6. Install ESP32 Platform SDK
|
||||
- name: Install ESP32 platform
|
||||
run: pio pkg install --silent --global --platform espressif32
|
||||
# - name: Install ESP32 platform
|
||||
# run: pio pkg install --silent --global --platform espressif32
|
||||
|
||||
# 7. Compile the Project
|
||||
- name: Compile the Project
|
||||
|
||||
Binäre Datei nicht angezeigt.
@@ -58,3 +58,87 @@ The frequency block from **433.050 to 434.790 MHz** is shared globally as an **I
|
||||
The 433 MHz ISM band in Europe is heavily congested by consumer devices like car key fobs, weather stations, and garage door openers. If you want to escape this interference completely, the European standard is to switch to the **868 MHz band**.
|
||||
|
||||
* **Note:** MeshCom itself is historically tied to 433 MHz in Austria. Shifting to 868 MHz effectively means migrating away from MeshCom infrastructure into the license-free **Meshtastic** ecosystem, which features a massive, highly dense node network across Austria on 868 MHz.
|
||||
|
||||
---
|
||||
|
||||
## 🔒 Part 3: LoRa Synchronization Words & Hardware Filtering
|
||||
|
||||
The **LoRa Synchronization Word (Sync Word)** is a critical 8-bit hardware filter configured inside register **`0x39` (`RegSyncWord`)** of the Semtech SX1276 transceiver. It functions as a physical RF barrier inside the radio's baseband modem. If an incoming packet's preamble synchronization pattern does not perfectly match the receiver's configured value, the hardware discards the payload instantly on chip-level, preventing the **`RxDone`** flag from firing and keeping the physical interrupt line completely silent.
|
||||
|
||||
### Technical Reference Matrix
|
||||
While register `0x39` accepts any 8-bit value from `0x00` to `0xFF`, specific combinations have established themselves as international operational standards:
|
||||
|
||||
| Sync Word | Network Profile | Operational Target / Description |
|
||||
| :--- | :--- | :--- |
|
||||
| **`0x34`** | **Public LoRaWAN** | **MeshCom Standard** (ÖVSV standard for amateur radio mesh infrastructures operating at **433.175 MHz**). Triggers advanced hardware preamble correlation rules in the silicon for maximum noise immunity near the thermal noise floor. |
|
||||
| **`0x24`** | **Tactical Mesh** | **Meshtastic Default** (Civilian off-grid digital communications). Used globally on the license-free 433 MHz ISM and 868 MHz bands. Requires modern low-data-rate optimization configurations to eliminate sensitivity degradation on legacy SX127x silicon steps. |
|
||||
| **`0x12`** | **Private Network** | **Semtech Factory Default**. Standard baseline initialization value utilized for isolated point-to-point (P2P) bridging outside global networks. |
|
||||
| **`0x55`** | **Isolated Array A** | **Custom / Experimental**. Alternating bit pattern (`01010101`) providing optimal transition density for the internal clock-data recovery (CDR) PLL loop. |
|
||||
| **`0xAA`** | **Isolated Array B** | **Custom / Experimental**. Alternating bit pattern (`10101010`) offering optimal signal integrity transitions for proprietary cryptography deployments. |
|
||||
|
||||
> ⚠️ **The Preamble Correlation Trap:**
|
||||
> Custom deployments should strictly avoid sync words with sparse bit transitions (such as `0x00` or `0xFF`). The internal baseband correlation engine relies on distinct signal transitions to sync its timing grid. Continuous sequences of zeros or ones drastically degrade receiver sensitivity.
|
||||
|
||||
### MeshCom Co-Existence Layout
|
||||
To successfully capture live telemetry frames from official ÖVSV MeshCom gateways, the firmware forces a multi-register synchronization sequence during the unified thread-safe constructor pass:
|
||||
|
||||
```cpp
|
||||
// Predefined LoRa Sync Word options for hardware correlation filtering
|
||||
constexpr uint8_t LORA_SYNC_WORD_PUBLIC_NETWORK_MODE = 0x34; // MeshCom (ÖVSV standard for amateur radio mesh)
|
||||
constexpr uint8_t LORA_SYNC_WORD_TACTICAL_NETWORK_MODE = 0x24; // Meshtastic (Civilian off-grid tactical mesh network)
|
||||
constexpr uint8_t LORA_SYNC_WORD_PRIVATE_NETWORK_MODE = 0x12; // Semtech Default (Factory default / Private point-to-point)
|
||||
constexpr uint8_t LORA_SYNC_WORD_CUSTOM_NETWORK_MODE = 0x55; // Custom / Experimental individual networks (highly isolated)
|
||||
constexpr uint8_t LORA_SYNC_WORD_EXPERIMENTAL_NETWORK_MODE = 0xAA; // Custom / Experimental individual networks (highly isolated)
|
||||
|
||||
// Active hardware synchronization configuration selector
|
||||
constexpr uint8_t LORA_SYNC_WORD = LORA_SYNC_WORD_PUBLIC_NETWORK_MODE;
|
||||
```
|
||||
|
||||
This variable updates the Semtech baseband modem alongside an expanded preamble length of **12 symbols** to prevent packet dropping during long correlation windows:
|
||||
|
||||
```cpp
|
||||
// Write the dynamic sync word byte to register 0x39
|
||||
writeRegister(0x39, Config::LORA_SYNC_WORD);
|
||||
|
||||
// Expand hardware preamble length to 12 symbols for robust network correlation
|
||||
writeRegister(0x20, 0x00U); // RegPreambleMsb
|
||||
writeRegister(0x21, 0x0CU); // RegPreambleLsb (12 = 0x0C)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🛠️ Part 4: Troubleshooting Packet Drops & Telemetry Failures
|
||||
|
||||
When listening to live MeshCom grids or experimental point-to-point networks, your terminal might remain silent despite active RF activity in your area. This section details how to diagnose, interpret, and resolve low-level hardware anomalies using the automated register metrics layer.
|
||||
|
||||
### 1. The Low-Level Interrupt Diagnostics (Bypassing FreeRTOS Flags)
|
||||
If your application task does not wake up, you must verify if the Semtech silicon is tracking any signals at all. By reading register **`0x12` (`RegIrqFlags`)**, you can isolate the failure source instantly:
|
||||
|
||||
```text
|
||||
Reg 0x12 Value Hardware Status Root Cause & Resolution
|
||||
-------------- ----------------------- -------------------------------------------------------------
|
||||
0x00 No Signal Detected - Transceiver is out of range or frequency calibration mismatch.
|
||||
- Check if your device is actively in RXCONTINUOUS mode [0x85].
|
||||
0x20 PayloadCrcError Active - The packet was received but failed the hardware checksum.
|
||||
- Caused by bad SNR (noise floor interference) or SF mismatch.
|
||||
0x80 RxTimeout Active - The preamble was detected, but the complete frame timed out.
|
||||
- Increase your preamble length to 12 symbols (Reg 0x20/0x21).
|
||||
0x40 RxDone Success - The hardware captured, matched, and validated the packet!
|
||||
- If terminal stays silent, check your FreeRTOS semaphore handles.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2. Common Integration Failure Modes
|
||||
|
||||
#### Symptom A: Zero Output from `readRegister(0x42)` during Boot
|
||||
* **Root Cause:** The SPI peripheral host is misconfigured, or the hardware reset pin failed to pull low during the unified constructor pass.
|
||||
* **Resolution:** Verify that `spi_bus_initialize()` is bound to the correct Host controller (`SPI2_HOST` for standard LilyGO TTGO boards). Ensure your initialization logic executes safely on **Core 0** inside the main task context to guarantee uniform driver registration.
|
||||
|
||||
#### Symptom B: The First Packet Arrives, then Total Silence
|
||||
* **Root Cause:** The hardware interrupt flags within register `0x12` were not acknowledged, or the internal read pointer (`RegFifoAddrPtr`) was not explicitly reset to the packet's current base address.
|
||||
* **Resolution:** You must overwrite register `0x12` with its active bit mask at the **absolute beginning** of your processing chain. This forces the physical `DIO0` line to fall back to logic low, enabling the ESP32 to detect subsequent rising edges (`GPIO_INTR_POSEDGE`). Always call `setModeReceiver()` at the end of the extraction loop to clear baseband modulations.
|
||||
|
||||
#### Symptom C: Constant `PayloadCrcError` [0x20] on Local MeshCom Traffic
|
||||
* **Root Cause:** Inverted IQ configurations or local industrial noise (shared 433 MHz ISM congestion) is corrupting incoming headers.
|
||||
* **Resolution:** Verify that your local modem target bandwidth exactly mirrors the MeshCom standard of **250 kHz** and **SF11**. For extreme near-field interference, ensure your `calculateTelemetry()` float calculation is active to track if sub-noise SNR regressions are pulling the signal strength below `-120 dBm`.
|
||||
+3
-1
@@ -23,7 +23,7 @@ build_flags =
|
||||
-D CCACHE_ENABLE=1
|
||||
; Treats ESP-IDF libc extensions headers as system headers to suppress external warnings
|
||||
; -isystem /home/tom/.platformio/packages/framework-espidf/components/esp_libc/platform_include
|
||||
|
||||
-Wno-include-next
|
||||
; --- STRICT MODE FOR PROJECT APPLICATION CODE ONLY ---
|
||||
; These flags are strictly applied ONLY to C++ files inside the src/ directory.
|
||||
build_src_flags =
|
||||
@@ -39,5 +39,7 @@ build_src_flags =
|
||||
-Wconversion
|
||||
; Warns if a local variable shadows another variable in scope
|
||||
-Wshadow
|
||||
|
||||
-Wno-include-next
|
||||
; Promotes all warnings to hard compilation errors to guarantee clean code
|
||||
;-Werror
|
||||
+18
-10
@@ -101,6 +101,23 @@ void process_command(const char* cmd) {
|
||||
printf("Mute Timer Period : %lu seconds\r\n", (unsigned long)current_mute_time_sec);
|
||||
printf("Target CPU Board : ESP32\r\n");
|
||||
printf("ESP-IDF Version : %s\r\n", esp_get_idf_version());
|
||||
|
||||
if (lora_system != nullptr) {
|
||||
// Holt die atomare, thread-sichere Kopie der Telemetriedaten (Stack-Allokation)
|
||||
LoraStateSnapshot snapshot = lora_system->getSnapshot();
|
||||
|
||||
printf("LoRa Hardware : %s\r\n", snapshot.isInitialized ? "INITIALIZED" : "FAILED / UNINITIALIZED");
|
||||
printf("LoRa Transceiver : Mode [%s]\r\n", snapshot.currentModeStr);
|
||||
printf("LoRa Frequency : %lu Hz\r\n", (unsigned long)snapshot.frequencyHz);
|
||||
printf("LoRa Bandwidth : %lu kHz\r\n", (unsigned long)snapshot.bandwidthKhz);
|
||||
printf("LoRa SpreadingFact: SF%u\r\n", snapshot.spreadingFactor);
|
||||
printf("LoRa Coding Rate : 4/%u\r\n", snapshot.codingRate);
|
||||
printf("Packets Received : %lu\r\n", (unsigned long)snapshot.totalPacketsReceived);
|
||||
printf("Packets Transmit : %lu\r\n", (unsigned long)snapshot.totalPacketsTransmitted);
|
||||
printf("History In Memory : %zu Packets\r\n", snapshot.currentHistoryCount);
|
||||
} else {
|
||||
printf("LoRa Snapshot : Error (LoraController Handle uninitialized)\r\n");
|
||||
}
|
||||
}
|
||||
else if (strncmp(cmd, "mute ", 5) == 0) {
|
||||
int parsed_time = atoi(cmd + 5);
|
||||
@@ -163,16 +180,7 @@ void process_command(const char* cmd) {
|
||||
|
||||
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<double>(target_freq_hz) * 524288.0) / 32000000.0;
|
||||
uint32_t frf = static_cast<uint32_t>(fraction);
|
||||
|
||||
uint8_t msb = static_cast<uint8_t>((frf >> 16) & 0xFFU);
|
||||
uint8_t mid = static_cast<uint8_t>((frf >> 8) & 0xFFU);
|
||||
uint8_t lsb = static_cast<uint8_t>(frf & 0xFFU);
|
||||
|
||||
lora_system->setFrequency(msb, mid, lsb);
|
||||
lora_system->setFrequency(target_freq_hz);
|
||||
printf("Success: Frequency shifted to %lu Hz.\r\n", (unsigned long)target_freq_hz);
|
||||
} else {
|
||||
printf("Error: LoraController handle is uninitialized.\r\n");
|
||||
|
||||
+40
-10
@@ -22,7 +22,7 @@
|
||||
namespace Config {
|
||||
// Compile-time constants with strict type enforcement
|
||||
constexpr const char* PROJECT_TITLE = "FoxMesh";
|
||||
constexpr const char* PROJECT_VERSION = "1.0.3";
|
||||
constexpr const char* PROJECT_VERSION = "1.0.5";
|
||||
constexpr const char* PROJECT_AUTHORS = "Tom OE3TKT";
|
||||
|
||||
// CLI Mute Configuration
|
||||
@@ -44,17 +44,47 @@ namespace Config {
|
||||
constexpr gpio_num_t LORA_SCK_PIN = GPIO_NUM_5;
|
||||
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;
|
||||
constexpr gpio_num_t LORA_IRQ_PIN = GPIO_NUM_26; // DIO0_PIN
|
||||
constexpr gpio_num_t LORA_DIO1_PIN = GPIO_NUM_33; // Wichtig für PLL-Lock & Timeout im RX-Modus
|
||||
|
||||
|
||||
// Semtech SX1276 LoRa Configuration Parameters
|
||||
// 433.175 MHz is calculated by: 433175000 / (32000000 / 2^19) = 7097139.2 = 0x6c4b33 // wrong: 0x06C4CD
|
||||
constexpr uint8_t LORA_FRF_MSB = 0x6C;
|
||||
constexpr uint8_t LORA_FRF_MID = 0x4B;
|
||||
constexpr uint8_t LORA_FRF_LSB = 0x33;
|
||||
constexpr uint32_t LORA_DEFAULT_FREQ_HZ = 433175000;
|
||||
constexpr uint32_t LORA_DEFAULT_BW_KHZ = 250;
|
||||
constexpr uint8_t LORA_DEFAULT_SF = 11;
|
||||
constexpr uint8_t LORA_DEFAULT_CR = 6; // CR 4/6 -> Denominator is 6
|
||||
|
||||
// Modem Config 1: BW 250kHz (0x80), CR 4/6 (0x04), Explicit Header (0x00) -> 0x84
|
||||
constexpr uint8_t LORA_MODEM_CONFIG_1 = 0x84;
|
||||
// Predefined LoRa Sync Word options for hardware correlation filtering
|
||||
constexpr uint8_t LORA_SYNC_WORD_PUBLIC_NETWORK_MODE = 0x34; // MeshCom (ÖVSV standard for amateur radio mesh),
|
||||
// The Things Network, Helium
|
||||
constexpr uint8_t LORA_SYNC_WORD_TACTICAL_NETWORK_MODE = 0x24; // Meshtastic (Civilian off-grid tactical mesh network)
|
||||
constexpr uint8_t LORA_SYNC_WORD_PRIVATE_NETWORK_MODE = 0x12; // Semtech Default (Factory default / Private point-to-point)
|
||||
constexpr uint8_t LORA_SYNC_WORD_CUSTOM_NETWORK_MODE = 0x55; // Custom / Experimental individual networks (highly isolated)
|
||||
constexpr uint8_t LORA_SYNC_WORD_EXPERIMENTAL_NETWORK_MODE = 0xAA; // Custom / Experimental individual networks (highly isolated)
|
||||
|
||||
// Modem Config 2: SF11 (0xB0), TX Continuous off (0x00), CRC On (0x04) -> 0xB4
|
||||
constexpr uint8_t LORA_MODEM_CONFIG_2 = 0xB4;
|
||||
// Active hardware synchronization configuration selector
|
||||
constexpr uint8_t LORA_SYNC_WORD = LORA_SYNC_WORD_PUBLIC_NETWORK_MODE; // Selected default synchronization profile
|
||||
|
||||
// Expand hardware preamble length to 12 symbols for robust network correlation (MeshCom value)
|
||||
constexpr uint8_t LORA_PREAMBLE_LENGTH_LSB = 0x0C; // Semtech Factor Default is 0x08
|
||||
|
||||
// The compiler automatically pre-calculates the 24-bit frequency word at compile-time!
|
||||
constexpr uint64_t LORA_FRF_CALC = ((uint64_t)LORA_DEFAULT_FREQ_HZ << 19) / 32000000ULL;
|
||||
constexpr uint8_t LORA_FRF_MSB = static_cast<uint8_t>((LORA_FRF_CALC >> 16) & 0xFFU);
|
||||
constexpr uint8_t LORA_FRF_MID = static_cast<uint8_t>((LORA_FRF_CALC >> 8) & 0xFFU);
|
||||
constexpr uint8_t LORA_FRF_LSB = static_cast<uint8_t>(LORA_FRF_CALC & 0xFFU);
|
||||
|
||||
// --- DYNAMIC COMPILE-TIME BIT-SHIFTING EXPRESSIONS ---
|
||||
|
||||
// Helper to map bandwidth kHz to Semtech Register Bits (7-4 of RegModemConfig1)
|
||||
constexpr uint8_t LORA_BW_REG_VAL = (LORA_DEFAULT_BW_KHZ == 62) ? 0x06 :
|
||||
(LORA_DEFAULT_BW_KHZ == 125) ? 0x07 :
|
||||
(LORA_DEFAULT_BW_KHZ == 250) ? 0x08 :
|
||||
(LORA_DEFAULT_BW_KHZ == 500) ? 0x09 : 0x08;
|
||||
|
||||
// RegModemConfig1 (0x1D): Bandwidth (Bits 7-4), Coding Rate (Bits 3-1), ImplicitHeader (Bit 0 = 0)
|
||||
constexpr uint8_t LORA_MODEM_CONFIG_1 = static_cast<uint8_t>((LORA_BW_REG_VAL << 4) | ((LORA_DEFAULT_CR - 4) << 1));
|
||||
|
||||
// RegModemConfig2 (0x1E): Spreading Factor (Bits 7-4), TxContinuous (Bit 3 = 0), RxPayloadCrcOn (Bit 2 = 1)
|
||||
constexpr uint8_t LORA_MODEM_CONFIG_2 = static_cast<uint8_t>((LORA_DEFAULT_SF << 4) | 0x04U);
|
||||
}
|
||||
+776
-138
Datei-Diff unterdrückt, da er zu groß ist.
Diff laden
+163
-44
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* @file lora.hpp
|
||||
* @brief Thread-safe asynchronous Semtech SX1276 LoRa transceiver driver for FoxMesh
|
||||
* @brief Thread-safe asynchronous Semtech SX1276 LoRa transceiver driver interface.
|
||||
* @author OM Tom (OE3TKT)
|
||||
*
|
||||
* @license SPDX-License-Identifier: MIT
|
||||
@@ -10,73 +10,192 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/spi_master.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "freertos/task.h"
|
||||
#include "lora_types.hpp"
|
||||
#include "esp_err.h"
|
||||
|
||||
class LoraController {
|
||||
public:
|
||||
/**
|
||||
* @brief Structure defining the localized packet buffer space.
|
||||
* @brief Simple internal abstraction layer mirroring the raw incoming packet payload.
|
||||
*/
|
||||
struct LoraPacket {
|
||||
uint8_t payload[256]; ///< Raw data payload frame matching max SX1276 FIFO allocation
|
||||
uint8_t length; ///< Total number of active bytes captured inside the frame
|
||||
int8_t rssi; ///< Received Signal Strength Indicator metric in dBm
|
||||
int8_t snr; ///< Signal-to-Noise Ratio metric in dB
|
||||
float rssi; /**< High-precision calibrated received signal power in dBm. */
|
||||
float snr; /**< High-precision calibrated signal-to-noise ratio in dB. */
|
||||
uint8_t length; /**< Payload size explicitly specified in bytes. */
|
||||
uint8_t payload[256]; /**< Fixed internal memory buffer capturing raw ingress bytes. Max SX1276 FIFO is 256. */
|
||||
};
|
||||
|
||||
explicit LoraController();
|
||||
virtual ~LoraController();
|
||||
|
||||
// Disallow copying to prevent duplicate hardware resource assignment
|
||||
LoraController(const LoraController&) = delete;
|
||||
LoraController& operator=(const LoraController&) = delete;
|
||||
|
||||
/**
|
||||
* @brief Configures peripheral resources and registers the background worker thread on Core 1.
|
||||
* @param priority Scheduler task priority level execution metric.
|
||||
* @brief Controller class orchestrating hardware configurations, data transfers, and metrics collection.
|
||||
*/
|
||||
void startTask(uint32_t priority = 6);
|
||||
class LoraController {
|
||||
private:
|
||||
static const char* TAG; ///< Logging identifier tag for ESP_LOG statements.
|
||||
void* m_spiHandle = nullptr; ///< SPI device interface reference token pointer.
|
||||
SemaphoreHandle_t m_isrSemaphore = nullptr; ///< Binary semaphore facilitating hardware-to-task sync notifications.
|
||||
|
||||
LoraStateTable m_state; ///< Internal volatile tracking record data context.
|
||||
SemaphoreHandle_t m_stateMutex = nullptr; ///< Mutex primitive enforcing mutual exclusion over m_state modifications.
|
||||
|
||||
/**
|
||||
* @brief Transmits a raw data frame onto the physical radio frequency layer.
|
||||
* @param buffer Pointer to the source byte array holding the packet context.
|
||||
* @param length Total number of bytes to dispatch over the air interface.
|
||||
* @return esp_err_t status indicating transaction success.
|
||||
* @brief Reads a value from an individual internal register address of the transceiver.
|
||||
* @param address Target transceiver memory sub-space location address byte.
|
||||
* @return The read byte value configuration currently active.
|
||||
*/
|
||||
uint8_t readRegister(uint8_t address);
|
||||
|
||||
/**
|
||||
* @brief Writes a specific option parameter directly into a single transceiver register address.
|
||||
* @param address Target transceiver memory sub-space location address byte.
|
||||
* @param value Setting parameter layout specification code byte.
|
||||
*/
|
||||
void writeRegister(uint8_t address, uint8_t value);
|
||||
|
||||
/**
|
||||
* @brief Interrupt Service Routine wrapper triggered directly from GPIO state changes.
|
||||
* @param arg Opaque payload pointer referencing the target thread synchronization primitive.
|
||||
*/
|
||||
static void IRAM_ATTR gpioIsrHandler(void* arg);
|
||||
|
||||
/**
|
||||
* @brief Helper context processing background calculations to prune list overflows.
|
||||
* @note This internal call assumes that the m_stateMutex lock has already been successfully acquired.
|
||||
*/
|
||||
void enforceHistoryLimit();
|
||||
|
||||
/**
|
||||
* @brief Main processing task thread loop loopback entry execution hub.
|
||||
*/
|
||||
void run();
|
||||
|
||||
/**
|
||||
* @brief Static FreeRTOS bridge method mapping execution control context flows.
|
||||
* @param pvParameters Context reference address pointing back into the instantiating class layer.
|
||||
*/
|
||||
static void taskTrampoline(void* pvParameters);
|
||||
|
||||
/**
|
||||
* @brief Evaluates raw register readings into calibrated RSSI and SNR telemetry metrics.
|
||||
* @details Automatically compensates the RSSI offset base based on the active carrier frequency
|
||||
* band (Low-Band vs High-Band boundary at 525 MHz) as well as sub-noise SNR conditions.
|
||||
* @param rawRssi The raw digital output read from RegRssiValue (0x1A).
|
||||
* @param rawSnr The raw digital twos-complement output read from RegPktSnrValue (0x1B).
|
||||
* @param[out] outRssi Reference destination pointer receiving the calibrated RSSI value in dBm.
|
||||
* @param[out] outSnr Reference destination pointer receiving the calibrated SNR value in dB.
|
||||
*/
|
||||
void calculateTelemetry(uint8_t rawRssi, uint8_t rawSnr, float& outRssi, float& outSnr);
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Construct a new Lora Controller instance and allocate threading primitives.
|
||||
*/
|
||||
LoraController();
|
||||
|
||||
/**
|
||||
* @brief Destroy the Lora Controller object, releasing active allocations and hardware hooks.
|
||||
*/
|
||||
~LoraController();
|
||||
|
||||
/**
|
||||
* @brief Initializes active interface bindings, reset signals, and evaluates valid silicon signatures.
|
||||
* @return ESP_OK on operational handshake match, alternative error codes upon physical failures.
|
||||
*/
|
||||
esp_err_t initializeHardware();
|
||||
|
||||
/**
|
||||
* @brief Programs basic standard profiling configurations and flags continuous reception monitoring modes.
|
||||
*/
|
||||
void setModeReceiver();
|
||||
|
||||
/**
|
||||
* @brief Directly transmits a raw binary frame layout out onto the RF medium link.
|
||||
* @param buffer Reference data memory segment holding the outbound bytes block.
|
||||
* @param length The complete amount of data characters structured within the payload.
|
||||
* @return ESP_OK upon transmission launch clearance, error conditions otherwise.
|
||||
*/
|
||||
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.
|
||||
* @brief Spawns a dedicated processing task context onto a specifically designated execution core.
|
||||
* @param priority FreeRTOS scheduling execution assignment depth factor.
|
||||
*/
|
||||
void setFrequency(uint8_t msb, uint8_t mid, uint8_t lsb);
|
||||
void startTask(uint32_t priority);
|
||||
|
||||
/**
|
||||
* @brief Dynamically updates the LoRa signal bandwidth at runtime.
|
||||
* @param bw_khz Target bandwidth in kHz (e.g., 62, 125, 250, 500).
|
||||
* @brief Thread-safely updates the operational state tracking variables and overrides transceiver registers.
|
||||
* @param freq_hz Core RF targeting frequency structured in absolute Hertz scale units.
|
||||
*/
|
||||
void setFrequency(uint32_t freq_hz);
|
||||
|
||||
/**
|
||||
* @brief Thread-safely reads out the active target operation frequency metrics context.
|
||||
* @return Current system baseline operational tuning value expressed in Hertz units.
|
||||
*/
|
||||
uint32_t getFrequency();
|
||||
|
||||
/**
|
||||
* @brief Thread-safely updates the operational modem modulation parameter targets.
|
||||
* @param bw_khz Operating modulation channel grid width metric parameters.
|
||||
*/
|
||||
void setBandwidth(uint32_t bw_khz);
|
||||
|
||||
private:
|
||||
spi_device_handle_t m_spiHandle = nullptr;
|
||||
TaskHandle_t m_taskHandle = nullptr;
|
||||
SemaphoreHandle_t m_isrSemaphore = nullptr;
|
||||
/**
|
||||
* @brief Thread-safely updates the operational spreading factor configuration.
|
||||
* @param sf Spreading factor scale (6-12).
|
||||
*/
|
||||
void setSpreadingFactor(uint8_t sf);
|
||||
|
||||
static const char* TAG;
|
||||
/**
|
||||
* @brief Thread-safely updates the operational coding rate protection overhead.
|
||||
* @param cr_denominator Coding rate denominator index (5-8).
|
||||
*/
|
||||
void setCodingRate(uint8_t cr_denominator);
|
||||
|
||||
static void taskTrampoline(void* pvParameters);
|
||||
static void IRAM_ATTR gpioIsrHandler(void* arg);
|
||||
/**
|
||||
* @brief Thread-safely logs an incoming transaction capture directly into the historical context nodes collection.
|
||||
* @param packet Structural dataset mapping telemetry markers alongside the payload block.
|
||||
* @param sequence Absolute hardware sequence reference step index.
|
||||
* @return The unique database ID assigned to the tracking instance container wrapper node.
|
||||
*/
|
||||
uint32_t addPacketToList(const LoraPacket& packet, uint32_t sequence);
|
||||
|
||||
void run();
|
||||
uint8_t readRegister(uint8_t address);
|
||||
void writeRegister(uint8_t address, uint8_t value);
|
||||
/**
|
||||
* @brief Thread-safely extracts and purges an explicit single tracking record node entry via database criteria.
|
||||
* @param databaseId Unique identifier associated with the specific targeted row block element to discard.
|
||||
* @return True if a matching criteria target was located and eliminated, false if no ID matched.
|
||||
*/
|
||||
bool removePacketFromList(uint32_t databaseId);
|
||||
|
||||
esp_err_t initializeHardware();
|
||||
void setModeReceiver();
|
||||
/**
|
||||
* @brief Thread-safely resets the tracked tracking records data tables list back to empty states.
|
||||
*/
|
||||
void clearPacketList();
|
||||
|
||||
/**
|
||||
* @brief Thread-safely sorts the data list according to RSSI signal strength levels.
|
||||
* @param ascending If true, lowest signal strengths come first; if false, strongest signals sort to front.
|
||||
*/
|
||||
void sortListByRssi(bool ascending = false);
|
||||
|
||||
/**
|
||||
* @brief Thread-safely recovers structural chronological data ordering schemes according to database IDs.
|
||||
*/
|
||||
void sortListChronologically();
|
||||
|
||||
/**
|
||||
* @brief Performs a deep copy operation over the linked list under mutex locks to return a safe detached copy.
|
||||
* @return A standalone, thread-isolated linked list duplicate snapshot copy containing current history rows.
|
||||
*/
|
||||
std::list<LoraPacketNode> getPacketListCopy();
|
||||
|
||||
/**
|
||||
* @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();
|
||||
};
|
||||
@@ -0,0 +1,113 @@
|
||||
/**
|
||||
* @file lora_types.hpp
|
||||
* @brief Definition of data structures, nodes, and state tables for the LoRa driver.
|
||||
* @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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wpedantic"
|
||||
#include <list>
|
||||
#include <vector>
|
||||
#pragma GCC diagnostic pop
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* @brief Structure representing a single packet node within the doubly-linked tracking list.
|
||||
*/
|
||||
struct LoraPacketNode {
|
||||
/** @brief Dynamic raw binary storage buffer containing the received message payload. */
|
||||
std::vector<uint8_t> payload;
|
||||
|
||||
/** @brief Unique auto-incremented database identifier for targeting explicit items. */
|
||||
uint32_t databaseId;
|
||||
|
||||
/** @brief The absolute sequence index counting the packets received since device boot. */
|
||||
uint32_t packetSequence;
|
||||
|
||||
/** @brief Received Signal Strength Indicator (RSSI) measured in dBm. */
|
||||
float rssi;
|
||||
|
||||
/** @brief Signal-to-Noise Ratio (SNR) measured in dB. */
|
||||
float snr;
|
||||
|
||||
/** @brief Payload size explicitly specified in bytes. */
|
||||
uint8_t length;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Runtime operational state table monitoring configurations and historical packet data.
|
||||
* @note Optimized using "largest-to-smallest" data type ordering to completely eliminate memory padding.
|
||||
*/
|
||||
struct LoraStateTable {
|
||||
/** @brief Central database component built on a doubly-linked list for versatile node operations. (Contains pointers) */
|
||||
std::list<LoraPacketNode> packetList;
|
||||
|
||||
/** @brief Soft-limit threshold capping the maximum number of history records stored before auto-purge. (4 or 8 Bytes) */
|
||||
size_t maxHistorySize = 10;
|
||||
|
||||
/** @brief RF carrier operating frequency configured in Hertz. (4 Bytes) */
|
||||
uint32_t frequencyHz = 433175000;
|
||||
|
||||
/** @brief Accumulator metric track-counting successful ingress frames. (4 Bytes) */
|
||||
uint32_t totalPacketsReceived = 0;
|
||||
|
||||
/** @brief Accumulator metric track-counting egress frames transmitted. (4 Bytes) */
|
||||
uint32_t totalPacketsTransmitted = 0;
|
||||
|
||||
/** @brief Identity sequencer pointer controlling the next node's databaseId field. (4 Bytes) */
|
||||
uint32_t nextDatabaseId = 1;
|
||||
|
||||
/** @brief Modem bandwidth performance target configured in kHz. (4 Bytes) */
|
||||
uint32_t bandwidthKhz = 250;
|
||||
|
||||
/** @brief LoRa Spreading Factor configuration (typically ranges between 6 and 12). (1 Byte) */
|
||||
uint8_t spreadingFactor = 11;
|
||||
|
||||
/** @brief Coding Rate denominator setting (e.g., value 6 yields a 4/6 coding protection overhead). (1 Byte) */
|
||||
uint8_t codingRate = 6;
|
||||
|
||||
/** @brief True if hardware was successfully validated and attached during initialization. (1 Byte) */
|
||||
bool isInitialized = false;
|
||||
|
||||
// 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 {
|
||||
/** @brief Soft-limit threshold capping the maximum number of history records stored before auto-purge. (4 or 8 Bytes) */
|
||||
size_t currentHistoryCount;
|
||||
|
||||
/** @brief RF carrier operating frequency configured in Hertz. (4 Bytes) */
|
||||
uint32_t frequencyHz;
|
||||
|
||||
/** @brief Modem bandwidth performance target configured in kHz. (4 Bytes) */
|
||||
uint32_t bandwidthKhz;
|
||||
|
||||
/** @brief Accumulator metric track-counting successful ingress frames. (4 Bytes) */
|
||||
uint32_t totalPacketsReceived;
|
||||
|
||||
/** @brief Accumulator metric track-counting egress frames transmitted. (4 Bytes) */
|
||||
uint32_t totalPacketsTransmitted;
|
||||
|
||||
/** @brief LoRa Spreading Factor configuration (typically ranges between 6 and 12). (1 Byte) */
|
||||
uint8_t spreadingFactor;
|
||||
|
||||
/** @brief Coding Rate denominator setting (e.g., value 6 yields a 4/6 coding protection overhead). (1 Byte) */
|
||||
uint8_t codingRate;
|
||||
|
||||
/** @brief True if hardware was successfully validated and attached during initialization. (1 Byte) */
|
||||
bool isInitialized;
|
||||
|
||||
/** @brief Current Mode */
|
||||
char currentModeStr[16];
|
||||
// The three 1-byte variables at the bottom align perfectly into a 32-bit slot, leaving only 1 byte of trailing padding.
|
||||
};
|
||||
+3
-33
@@ -12,7 +12,7 @@
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/spi_master.h"
|
||||
//#include "driver/spi_master.h"
|
||||
#include "esp_log.h"
|
||||
#include "config.hpp"
|
||||
#include "led.hpp"
|
||||
@@ -46,32 +46,6 @@ LoraController* lora_module = nullptr;
|
||||
*/
|
||||
static LedController led(Config::LED_GPIO_PIN);
|
||||
|
||||
/**
|
||||
* @brief Initializes the physical SPI peripheral for the Semtech SX1276 transceiver.
|
||||
*
|
||||
* Configures the VSPI (SPI3_HOST) back-end utilizing strict compile-time layouts
|
||||
* mapped inside config.hpp and configures automatic DMA channel routing.
|
||||
*/
|
||||
void init_lora_spi(void) {
|
||||
esp_err_t ret;
|
||||
|
||||
spi_bus_config_t buscfg = {};
|
||||
buscfg.mosi_io_num = Config::LORA_MOSI_PIN;
|
||||
buscfg.miso_io_num = Config::LORA_MISO_PIN;
|
||||
buscfg.sclk_io_num = Config::LORA_SCK_PIN;
|
||||
buscfg.quadwp_io_num = -1;
|
||||
buscfg.quadhd_io_num = -1;
|
||||
buscfg.max_transfer_sz = 0;
|
||||
|
||||
// Initialize the physical peripheral bus with automatic DMA channel assignment
|
||||
ret = spi_bus_initialize(SPI3_HOST, &buscfg, SPI_DMA_CH_AUTO);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to initialize SPI bus: %s", esp_err_to_name(ret));
|
||||
return;
|
||||
}
|
||||
ESP_LOGI(TAG, "SPI bus initialized successfully on VSPI (SPI3)");
|
||||
}
|
||||
|
||||
// Global application anchor marked as extern "C" for the native ESP-IDF bootloader
|
||||
extern "C" {
|
||||
/**
|
||||
@@ -104,9 +78,6 @@ extern "C" {
|
||||
// Pass the bus instance dependency to the interactive terminal layer
|
||||
cli_init(i2c_bus, &led, display);
|
||||
|
||||
// Initialize the Semtech LoRa SPI connection layer
|
||||
init_lora_spi();
|
||||
|
||||
// Spin up the background console interface thread on Core 0
|
||||
start_cli_task();
|
||||
|
||||
@@ -117,7 +88,7 @@ extern "C" {
|
||||
//connects the lora instance with the CLI control directly (commands freq and bw)
|
||||
cli_init_lora(lora_module);
|
||||
|
||||
// start the radio task strict to the Core 1 (Priority 6)
|
||||
// start the radio task straight to the Core 1 (Priority 6)
|
||||
lora_module->startTask(6);
|
||||
ESP_LOGI(TAG, "LoRa subsystem successfully activated on Core 1.");
|
||||
} else {
|
||||
@@ -127,11 +98,10 @@ extern "C" {
|
||||
// Trigger synchronous welcome splash screen frame
|
||||
if (display != nullptr) {
|
||||
char msg[32];
|
||||
//display->printMessage(0, "FoxMesh " Config::PROJECT_VERSION " \x86" "OE3TKT");
|
||||
snprintf(msg, sizeof(msg), "FoxMesh %s OE3TKT", Config::PROJECT_VERSION);
|
||||
display->printMessage(1, msg);
|
||||
//display->printMessage(2, "Bärbel frühstückt ©");
|
||||
}
|
||||
|
||||
// Main system watchdog and status monitor loop
|
||||
while (true) {
|
||||
ESP_LOGI(TAG, "[%s v%s] Operational. Free Heap: %lu bytes",
|
||||
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren