daily update, add lora files

Dieser Commit ist enthalten in:
tom committed 2026-10-17 23:43:07 +02:00
1 Ursprung 0ab736b338
Commit 19345c3b4b
8 Dateien geändert
+412 -80

Keine Dateien entsprechen Ihrer Suche

+6 -4
Datei anzeigen
@@ -155,7 +155,7 @@ void process_command(const char* cmd) {
if (sscanf(cmd + 5, "%lu %63[^\r\n]", &targetLine, textBuffer) == 2) {
if (targetLine <= 7) {
if (oled_display != nullptr) {
oled_display->printMessage(textBuffer, static_cast<uint8_t>(targetLine));
oled_display->printMessage(static_cast<uint8_t>(targetLine), textBuffer);
printf("Success: Dispatched text '%s' to OLED line %lu.\r\n", textBuffer, (unsigned long)targetLine);
} else {
printf("Error: DisplayController handle is uninitialized.\r\n");
@@ -237,11 +237,13 @@ static void cli_worker_task(void *pvParameters) {
printf("\b \b");
fflush(stdout);
}
// Sanitize and append plain terminal symbols into processing context
else if (data >= 32 && data <= 126 && buffer_index < (BUF_SIZE - 1)) {
// Sanitize and append terminal symbols (including UTF-8 multi-byte characters like German Umlaute)
else if (((data >= 32 && data <= 126) || data >= 128) && buffer_index < (BUF_SIZE - 1)) {
line_buffer[buffer_index++] = static_cast<char>(data);
printf("%c", data);
fflush(stdout);
//fflush(stdout);
// direct write into uart buffer, used for utf-8
//uart_write_bytes(CLI_UART_PORT, &data, 1);
}
}
// Structural task yields preventing internal stack watchdog violations
+31 -18
Datei anzeigen
@@ -112,18 +112,30 @@ static const uint8_t font_5x7[][5] = {
{0x44, 0x28, 0x10, 0x28, 0x44}, // 120 x
{0x0c, 0x50, 0x50, 0x50, 0x3c}, // 121 y
{0x44, 0x64, 0x54, 0x4c, 0x44}, // 122 z
{0x00, 0x08, 0x36, 0x41, 0x00}, // 123 \{
{0x00, 0x08, 0x36, 0x41, 0x00}, // 123 {
{0x00, 0x00, 0x77, 0x00, 0x00}, // 124 |
{0x00, 0x41, 0x36, 0x08, 0x00}, // 125 \}
{0x00, 0x41, 0x36, 0x08, 0x00}, // 125 }
{0x08, 0x08, 0x2a, 0x1c, 0x08}, // 126 ~
// German Umlaute mappings
{0x7d, 0x11, 0x11, 0x11, 0x7d}, // 127 Ä
{0x3c, 0x41, 0x41, 0x41, 0x3c}, // 128 Ö
{0x3d, 0x40, 0x40, 0x40, 0x3d}, // 129 Ü
{0x20, 0x55, 0x54, 0x54, 0x78}, // 130 ä
{0x38, 0x54, 0x54, 0x54, 0x38}, // 131 ö
{0x3c, 0x40, 0x40, 0x20, 0x7c}, // 132 ü
{0x7f, 0x49, 0x49, 0x36, 0x20} // 133 ß
// {0x7d, 0x11, 0x11, 0x11, 0x7d}, // 127 Ä
// {0x3c, 0x41, 0x41, 0x41, 0x3c}, // 128 Ö
// {0x3d, 0x40, 0x40, 0x40, 0x3d}, // 129 Ü
// {0x20, 0x55, 0x54, 0x54, 0x78}, // 130 ä
// {0x38, 0x54, 0x54, 0x54, 0x38}, // 131 ö
// {0x3c, 0x40, 0x40, 0x20, 0x7c}, // 132 ü
// {0x7f, 0x49, 0x49, 0x36, 0x20} // 133 ß
// new German Umlaute mapping (5x7/5x8 Font Matrix)
{0,0,0,0,0}, //127 not allowed
{0x7d, 0x13, 0x13, 0x13, 0x7d}, // 128 Ä
{0x3c, 0x43, 0x43, 0x43, 0x3c}, // 129 Ö
{0x3d, 0x42, 0x42, 0x42, 0x3d}, // 130 Ü
{0x20, 0x55, 0x54, 0x54, 0x78}, // 131 ä
{0x38, 0x55, 0x55, 0x55, 0x38}, // 132 ö
{0x3c, 0x42, 0x42, 0x22, 0x7c}, // 133 ü
{0x7f, 0x49, 0x49, 0x36, 0x00}, // 134 ß (Letzte Spalte auf 0x00 bereinigt)
// {0x3c, 0x42, 0x51, 0x4a, 0x34} // 134 © Copyright
// {0x3c, 0x42, 0x4d, 0x4d, 0x32} // 135
{0x3c, 0x42, 0x95, 0x95, 0x52}
};
/**
@@ -255,7 +267,7 @@ void DisplayController::startTask(uint32_t priority) {
* @param text Pointer to a standard null-terminated sequence of tracking information.
* @param line Selected hardware page index mapping from 0 to 7.
*/
void DisplayController::printMessage(const char* text, uint8_t line) {
void DisplayController::printMessage(uint8_t line, const char* text) {
if (m_msgQueue == nullptr || text == nullptr) return;
DisplayMessage msg = {};
@@ -321,19 +333,20 @@ void DisplayController::run() {
uint8_t c_secondary = static_cast<uint8_t>(incoming.content[i + 1]);
i++; // Advance the pointer to consume the compound UTF-8 token pair
if (c_secondary == 0x84) { fontIndex = 95; validCharacter = true; } // Ä
if (c_secondary == 0x96) { fontIndex = 96; validCharacter = true; } // Ö
if (c_secondary == 0x9C) { fontIndex = 97; validCharacter = true; } // Ü
if (c_secondary == 0xA4) { fontIndex = 98; validCharacter = true; } // ä
if (c_secondary == 0xB6) { fontIndex = 99; validCharacter = true; } // ö
if (c_secondary == 0xBC) { fontIndex = 100; validCharacter = true; } // ü
if (c_secondary == 0x84) { fontIndex = 96; validCharacter = true; } // Ä
if (c_secondary == 0x96) { fontIndex = 97; validCharacter = true; } // Ö
if (c_secondary == 0x9C) { fontIndex = 98; validCharacter = true; } // Ü
if (c_secondary == 0xA4) { fontIndex = 99; validCharacter = true; } // ä
if (c_secondary == 0xB6) { fontIndex = 100; validCharacter = true; } // ö
if (c_secondary == 0xBC) { fontIndex = 101; validCharacter = true; } // ü
}
else if (c_primary == 0xC2 && (i + 1) < textLength) {
uint8_t c_secondary = static_cast<uint8_t>(incoming.content[i + 1]);
i++;
if (c_secondary == 0x9F) { fontIndex = 101; validCharacter = true; } // ß
if (c_secondary == 0x9F) { fontIndex = 102; validCharacter = true; } // ß
if (c_secondary == 0xA9) { fontIndex = 103; validCharacter = true; } // ©
}
else if (c_primary >= 32 && c_primary <= 126) {
else if ((c_primary >= 32 && c_primary <= 126) || (c_primary > 127 && c_primary <= 135)) {
fontIndex = static_cast<size_t>(c_primary - 32);
validCharacter = true;
}
+3 -3
Datei anzeigen
@@ -31,16 +31,16 @@ public:
/**
* @brief Thread-safe non-blocking method to print a message to the OLED display.
* @param text Null-terminated string message (maximum 20 characters for simple lines).
* @param line Destination line identifier index (0 to 7 for page addressing mode).
* @param text Null-terminated string message (maximum 20 characters for simple lines).
*/
void printMessage(const char* text, uint8_t line);
void printMessage(uint8_t line, const char* text);
private:
// Internal structure mapping the safe asynchronous data pipeline
struct DisplayMessage {
char content[24];
uint8_t targetLine;
char content[24];
};
i2c* m_bus;
+210
Datei anzeigen
@@ -0,0 +1,210 @@
/**
* @file lora.cpp
* @brief Thread-safe asynchronous Semtech SX1276 LoRa transceiver driver implementation
* @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.
*/
#include "lora.hpp"
#include "esp_log.h"
#include "config.hpp"
#include "display.hpp"
#include <cstring>
const char* LoraController::TAG = "LORA";
LoraController::LoraController() {
m_isrSemaphore = xSemaphoreCreateBinary();
if (m_isrSemaphore == nullptr) {
ESP_LOGE(TAG, "Fatal: Failed to allocate LoRa ISR synchronization semaphore.");
}
}
LoraController::~LoraController() {
if (m_spiHandle != nullptr) {
spi_bus_remove_device(m_spiHandle);
}
if (m_isrSemaphore != nullptr) {
vSemaphoreDelete(m_isrSemaphore);
}
}
uint8_t LoraController::readRegister(uint8_t address) {
if (m_spiHandle == nullptr) return 0;
uint8_t txData[2] = { static_cast<uint8_t>(address & 0x7FU), 0x00U };
uint8_t rxData[2] = { 0x00U, 0x00U };
spi_transaction_t trans = {};
trans.length = 16;
trans.tx_buffer = txData;
trans.rx_buffer = rxData;
if (spi_device_transmit(m_spiHandle, &trans) != ESP_OK) {
return 0;
}
return rxData[1];
}
void LoraController::writeRegister(uint8_t address, uint8_t value) {
if (m_spiHandle == nullptr) return;
uint8_t txData[2] = { static_cast<uint8_t>(address | 0x80U), value };
spi_transaction_t trans = {};
trans.length = 16;
trans.tx_buffer = txData;
spi_device_transmit(m_spiHandle, &trans);
}
void IRAM_ATTR LoraController::gpioIsrHandler(void* arg) {
auto* semaphore = static_cast<SemaphoreHandle_t>(arg);
if (semaphore != nullptr) {
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
xSemaphoreGiveFromISR(semaphore, &xHigherPriorityTaskWoken);
if (xHigherPriorityTaskWoken == pdTRUE) {
portYIELD_FROM_ISR();
}
}
}
esp_err_t LoraController::initializeHardware() {
esp_err_t err;
spi_device_interface_config_t devcfg = {};
devcfg.clock_speed_hz = 9000000;
devcfg.mode = 0;
devcfg.spics_io_num = Config::LORA_CS_PIN;
devcfg.queue_size = 7;
err = spi_bus_add_device(SPI3_HOST, &devcfg, &m_spiHandle);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to attach device handle to SPI3 host: %s", esp_err_to_name(err));
return err;
}
gpio_reset_pin(Config::LORA_RST_PIN);
gpio_set_direction(Config::LORA_RST_PIN, GPIO_MODE_OUTPUT);
gpio_reset_pin(Config::LORA_IRQ_PIN);
gpio_set_direction(Config::LORA_IRQ_PIN, GPIO_MODE_INPUT);
gpio_set_pull_mode(Config::LORA_IRQ_PIN, GPIO_PULLDOWN_ONLY);
gpio_set_level(Config::LORA_RST_PIN, 0);
vTaskDelay(pdMS_TO_TICKS(20));
gpio_set_level(Config::LORA_RST_PIN, 1);
vTaskDelay(pdMS_TO_TICKS(50));
uint8_t version = readRegister(0x42);
if (version != 0x12) {
ESP_LOGE(TAG, "Transceiver handshake failed! Invalid signature: 0x%02X", version);
return ESP_ERR_INVALID_VERSION;
}
ESP_LOGI(TAG, "SX1276 Handshake success. Silicon version confirmed: 0x12");
err = gpio_install_isr_service(0);
if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) {
return err;
}
err = gpio_set_intr_type(Config::LORA_IRQ_PIN, GPIO_INTR_POSEDGE);
err = gpio_isr_handler_add(Config::LORA_IRQ_PIN, LoraController::gpioIsrHandler, m_isrSemaphore);
return err;
}
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);
writeRegister(0x40, 0x00U); // DIO0 map onto RxDone
writeRegister(0x01, 0x85U); // RXCONTINUOUS
ESP_LOGI(TAG, "Radio base configuration applied. Listening on 868.1 MHz.");
}
esp_err_t LoraController::transmitFrame(const uint8_t* buffer, uint8_t length) {
if (m_spiHandle == nullptr || buffer == nullptr || length == 0) return ESP_ERR_INVALID_STATE;
ESP_LOGI(TAG, "Initiating transmission sequence: Sending %u bytes.", length);
writeRegister(0x01, 0x81U); // STDBY Mode
writeRegister(0x0D, 0x00U); // RegFifoTxBaseAddr
writeRegister(0x09, 0x00U); // RegFifoAddrPtr
for (uint8_t i = 0; i < length; i++) {
writeRegister(0x00, buffer[i]);
}
writeRegister(0x22, length); // RegPayloadLength
writeRegister(0x01, 0x83U); // RegOpMode: TX Mode Trigger
return ESP_OK;
}
void LoraController::taskTrampoline(void* pvParameters) {
auto* instance = static_cast<LoraController*>(pvParameters);
if (instance != nullptr) {
instance->run();
}
vTaskDelete(nullptr);
}
void LoraController::run() {
if (initializeHardware() != ESP_OK) {
ESP_LOGE(TAG, "Critical: LoRa init failed. Thread aborted.");
return;
}
setModeReceiver();
extern DisplayController* display; // Reference the global display singleton
while (true) {
if (xSemaphoreTake(m_isrSemaphore, portMAX_DELAY) == pdTRUE) {
uint32_t irqFlags = readRegister(0x12);
writeRegister(0x12, static_cast<uint8_t>(irqFlags));
if ((irqFlags & 0x40U) != 0U) { // RxDone
LoraPacket packet = {};
packet.length = readRegister(0x13);
uint32_t currentFifoAddress = readRegister(0x10);
writeRegister(0x09, static_cast<uint8_t>(currentFifoAddress));
for (uint8_t i = 0; i < packet.length; i++) {
packet.payload[i] = readRegister(0x00);
}
packet.rssi = static_cast<int8_t>(static_cast<int>(readRegister(0x1A)) - 157);
packet.snr = static_cast<int8_t>(static_cast<int>(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);
// FIXED: Uses the new parameter order (line first, text second)
if (display != nullptr) {
display->printMessage(1, "LoRa Msg Received");
}
}
}
}
}
void LoraController::startTask(uint32_t priority) {
xTaskCreatePinnedToCore(
LoraController::taskTrampoline,
"LORA_EngineTask",
4096,
this,
priority,
&m_taskHandle,
1
);
}
+68
Datei anzeigen
@@ -0,0 +1,68 @@
/**
* @file lora.hpp
* @brief Thread-safe asynchronous Semtech SX1276 LoRa transceiver driver 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.
*/
#pragma once
#include "driver/gpio.h"
#include "driver/spi_master.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "freertos/task.h"
class LoraController {
public:
/**
* @brief Structure defining the localized packet buffer space.
*/
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
};
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.
*/
void startTask(uint32_t priority = 6);
/**
* @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.
*/
esp_err_t transmitFrame(const uint8_t* buffer, uint8_t length);
private:
spi_device_handle_t m_spiHandle = nullptr;
TaskHandle_t m_taskHandle = nullptr;
SemaphoreHandle_t m_isrSemaphore = nullptr;
static const char* TAG;
static void taskTrampoline(void* pvParameters);
static void IRAM_ATTR gpioIsrHandler(void* arg);
void run();
uint8_t readRegister(uint8_t address);
void writeRegister(uint8_t address, uint8_t value);
esp_err_t initializeHardware();
void setModeReceiver();
};
+34 -9
Datei anzeigen
@@ -2,7 +2,7 @@
* @file main.cpp
* @brief Main application entry point for the FoxMesh tracking node
* @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.
@@ -20,19 +20,31 @@
#include "i2c.hpp"
#include "display.hpp" // FIXED: Added missing header for DisplayController
/**
* @brief Global log categorization tag context for the main application loader.
*/
static const char *TAG = "MAIN";
// Global pointer to the thread-safe hardware abstraction instances
/**
* @brief Global pointer tracking the synchronized thread-safe I2C master bus instance.
*/
i2c* i2c_bus = nullptr;
/**
* @brief Global pointer tracking the asynchronous isolated OLED render controller instance.
*/
DisplayController* display = nullptr;
// Global singleton instances for hardware abstraction layers
/**
* @brief Static singleton instance governing hardware status LED blink patterns.
*/
static LedController led(Config::LED_GPIO_PIN);
/**
* @brief Initializes the physical SPI peripheral for the Semtech SX1276 transceiver.
*
* Configures VSPI (SPI3_HOST) using strict compile-time configurations from config.hpp.
*
* 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;
@@ -56,6 +68,12 @@ void init_lora_spi(void) {
// Global application anchor marked as extern "C" for the native ESP-IDF bootloader
extern "C" {
/**
* @brief System core entry point executing synchronous initialization loops.
*
* Spawns the decoupled system worker threads, establishes thread-safe hardware
* bus routing layers, and boots up the interactive terminal environment on Core 0.
*/
void app_main(void) {
// Output system banner configuration parameters on boot
ESP_LOGI(TAG, "=================================================");
@@ -73,7 +91,9 @@ extern "C" {
// Instantiate and activate the isolated background render worker on Core 0
display = new DisplayController(i2c_bus);
display->startTask();
if (display != nullptr) {
display->startTask();
}
// Pass the bus instance dependency to the interactive terminal layer
cli_init(i2c_bus, &led, display);
@@ -85,15 +105,20 @@ extern "C" {
start_cli_task();
// Trigger synchronous welcome splash screen frame
display->printMessage("FoxMesh Ready", 0);
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",
Config::PROJECT_TITLE,
Config::PROJECT_VERSION,
(unsigned long)esp_get_free_heap_size());
vTaskDelay(pdMS_TO_TICKS(5000));
}
}