From 0430020f3f5fa6e32a736597ae9d2567b2627dd5 Mon Sep 17 00:00:00 2001 From: 3djc <3djc@gh.com> Date: Wed, 26 Feb 2025 11:53:54 +0100 Subject: [PATCH 1/8] workaround: async call handling --- radio/src/targets/taranis/gx12/bsp_io.cpp | 25 +++++++++++++---------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/radio/src/targets/taranis/gx12/bsp_io.cpp b/radio/src/targets/taranis/gx12/bsp_io.cpp index 217eb71385d..f503c3cc371 100644 --- a/radio/src/targets/taranis/gx12/bsp_io.cpp +++ b/radio/src/targets/taranis/gx12/bsp_io.cpp @@ -57,30 +57,33 @@ static uint32_t _read_io_expander(bsp_io_expander* io) return io->state; } -static void _poll_switches(void *pvParameter1, uint32_t ulParameter2) +static void _poll_switches(void *pvParameter1 = nullptr, uint32_t ulParameter2 = 0) { (void)ulParameter2; - bsp_io_expander* io = (bsp_io_expander*)pvParameter1; - _read_io_expander(io); + bsp_io_read_switches(); + bsp_io_read_fs_switches(); } -static void _io_int_handler(bsp_io_expander* io) +static void _io_int_handler() { BaseType_t xHigherPriorityTaskWoken = pdFALSE; + BaseType_t xReturn = pdFALSE; + if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) { - xTimerPendFunctionCallFromISR(_poll_switches, (void*)io, 0, + xReturn = xTimerPendFunctionCallFromISR(_poll_switches, nullptr, 0, &xHigherPriorityTaskWoken); portYIELD_FROM_ISR(xHigherPriorityTaskWoken); + + // TODO: REMOVE THIS WORKAROUND WHEN ASYNC CALL IS FIXED + if(xReturn != pdPASS) { + TRACE("Async call issue"); + _poll_switches(); + } } else { - _read_io_expander(io); + _poll_switches(); } } -static void _io_int_handler() { - _io_int_handler(&_io_switches); - _io_int_handler(&_io_fs_switches); -} - int bsp_io_init() { timersInit(); From dd595f640c19ceb78024f15ac62c2772a318e19b Mon Sep 17 00:00:00 2001 From: 3djc <3djc@gh.com> Date: Sun, 2 Mar 2025 10:59:59 +0100 Subject: [PATCH 2/8] fix: improve stacking protection --- radio/src/edgetx.cpp | 13 +++++++++++-- radio/src/targets/taranis/gx12/bsp_io.cpp | 16 ++++++++-------- radio/src/telemetry/telemetry.cpp | 15 +++++++++++++-- 3 files changed, 32 insertions(+), 12 deletions(-) diff --git a/radio/src/edgetx.cpp b/radio/src/edgetx.cpp index bd86328447c..2cbef927928 100644 --- a/radio/src/edgetx.cpp +++ b/radio/src/edgetx.cpp @@ -259,10 +259,19 @@ static void _timer_10ms_cb(void *pvParameter1, uint32_t ulParameter2) void per10ms() { - if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) { + static bool _timer_10ms_cb_in_queue = false; + + if (!_timer_10ms_cb_in_queue && xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) { BaseType_t xHigherPriorityTaskWoken = pdFALSE; - xTimerPendFunctionCallFromISR(_timer_10ms_cb, nullptr, 0, &xHigherPriorityTaskWoken); + BaseType_t xReturn = pdFALSE; + + xReturn = xTimerPendFunctionCallFromISR(_timer_10ms_cb, nullptr, 0, &xHigherPriorityTaskWoken); portYIELD_FROM_ISR(xHigherPriorityTaskWoken); + if (xReturn == pdPASS) { + _timer_10ms_cb_in_queue = true; + } else { + TRACE("xTimerPendFunctionCallFromISR() queue full"); + } } } diff --git a/radio/src/targets/taranis/gx12/bsp_io.cpp b/radio/src/targets/taranis/gx12/bsp_io.cpp index f503c3cc371..4c140e559d3 100644 --- a/radio/src/targets/taranis/gx12/bsp_io.cpp +++ b/radio/src/targets/taranis/gx12/bsp_io.cpp @@ -38,6 +38,7 @@ struct bsp_io_expander { uint32_t state; }; +static bool _poll_switches_in_queue = false; static bsp_io_expander _io_switches; static bsp_io_expander _io_fs_switches; @@ -60,6 +61,7 @@ static uint32_t _read_io_expander(bsp_io_expander* io) static void _poll_switches(void *pvParameter1 = nullptr, uint32_t ulParameter2 = 0) { (void)ulParameter2; + _poll_switches_in_queue = false; bsp_io_read_switches(); bsp_io_read_fs_switches(); } @@ -69,18 +71,16 @@ static void _io_int_handler() BaseType_t xHigherPriorityTaskWoken = pdFALSE; BaseType_t xReturn = pdFALSE; - if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) { + if (!_poll_switches_in_queue && xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) { xReturn = xTimerPendFunctionCallFromISR(_poll_switches, nullptr, 0, &xHigherPriorityTaskWoken); portYIELD_FROM_ISR(xHigherPriorityTaskWoken); - // TODO: REMOVE THIS WORKAROUND WHEN ASYNC CALL IS FIXED - if(xReturn != pdPASS) { - TRACE("Async call issue"); - _poll_switches(); - } - } else { - _poll_switches(); + if (xReturn == pdPASS) { + _poll_switches_in_queue = true; + } else { + TRACE("xTimerPendFunctionCallFromISR() queue full"); + } } } diff --git a/radio/src/telemetry/telemetry.cpp b/radio/src/telemetry/telemetry.cpp index 033c2222283..b5759d7af16 100644 --- a/radio/src/telemetry/telemetry.cpp +++ b/radio/src/telemetry/telemetry.cpp @@ -245,9 +245,20 @@ static void _poll_frame(void *pvParameter1, uint32_t ulParameter2) void telemetryFrameTrigger_ISR(uint8_t module, const etx_proto_driver_t* drv) { + static bool _poll_frame_in_queue = false; BaseType_t xHigherPriorityTaskWoken = pdFALSE; - xTimerPendFunctionCallFromISR(_poll_frame, (void*)drv, module, &xHigherPriorityTaskWoken); - portYIELD_FROM_ISR( xHigherPriorityTaskWoken ); + BaseType_t xReturn = pdFALSE; + + if (!_poll_frame_in_queue && xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) { + xReturn = xTimerPendFunctionCallFromISR(_poll_frame, (void*)drv, module, &xHigherPriorityTaskWoken); + portYIELD_FROM_ISR( xHigherPriorityTaskWoken ); + + if (xReturn == pdPASS) { + _poll_frame_in_queue = true; + } else { + TRACE("xTimerPendFunctionCallFromISR() queue full"); + } + } } #endif From afe46c8fe828dc91c04de728930b3d0b1255957f Mon Sep 17 00:00:00 2001 From: 3djc <3djc@gh.com> Date: Sun, 2 Mar 2025 11:06:37 +0100 Subject: [PATCH 3/8] fix: tab issue --- radio/src/targets/taranis/gx12/bsp_io.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/targets/taranis/gx12/bsp_io.cpp b/radio/src/targets/taranis/gx12/bsp_io.cpp index 4c140e559d3..de7952640fd 100644 --- a/radio/src/targets/taranis/gx12/bsp_io.cpp +++ b/radio/src/targets/taranis/gx12/bsp_io.cpp @@ -76,7 +76,7 @@ static void _io_int_handler() &xHigherPriorityTaskWoken); portYIELD_FROM_ISR(xHigherPriorityTaskWoken); - if (xReturn == pdPASS) { + if (xReturn == pdPASS) { _poll_switches_in_queue = true; } else { TRACE("xTimerPendFunctionCallFromISR() queue full"); From db2e9741bdf4f66c47543c6d75db61958b2657bc Mon Sep 17 00:00:00 2001 From: 3djc <3djc@gh.com> Date: Tue, 4 Mar 2025 09:54:14 +0100 Subject: [PATCH 4/8] fix: per module timer handling --- radio/src/edgetx.cpp | 4 +++- radio/src/telemetry/telemetry.cpp | 8 +++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/radio/src/edgetx.cpp b/radio/src/edgetx.cpp index 2cbef927928..0b1218dcc84 100644 --- a/radio/src/edgetx.cpp +++ b/radio/src/edgetx.cpp @@ -250,16 +250,18 @@ void timer_10ms() #include #include +static bool _timer_10ms_cb_in_queue = false; + static void _timer_10ms_cb(void *pvParameter1, uint32_t ulParameter2) { (void)pvParameter1; (void)ulParameter2; + _timer_10ms_cb_in_queue = false; timer_10ms(); } void per10ms() { - static bool _timer_10ms_cb_in_queue = false; if (!_timer_10ms_cb_in_queue && xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) { BaseType_t xHigherPriorityTaskWoken = pdFALSE; diff --git a/radio/src/telemetry/telemetry.cpp b/radio/src/telemetry/telemetry.cpp index b5759d7af16..f9348b2edee 100644 --- a/radio/src/telemetry/telemetry.cpp +++ b/radio/src/telemetry/telemetry.cpp @@ -205,12 +205,15 @@ void telemetryStop() } } +static bool _poll_frame_queued[NUM_MODULES] = {false}; + static void _poll_frame(void *pvParameter1, uint32_t ulParameter2) { _telemetryIsPolling = true; auto drv = (const etx_proto_driver_t*)pvParameter1; auto module = (uint8_t)ulParameter2; + _poll_frame_queued[module] = false; auto mod = pulsesGetModuleDriver(module); if (!mod || !mod->drv || !mod->ctx || (drv != mod->drv)) @@ -245,16 +248,15 @@ static void _poll_frame(void *pvParameter1, uint32_t ulParameter2) void telemetryFrameTrigger_ISR(uint8_t module, const etx_proto_driver_t* drv) { - static bool _poll_frame_in_queue = false; BaseType_t xHigherPriorityTaskWoken = pdFALSE; BaseType_t xReturn = pdFALSE; - if (!_poll_frame_in_queue && xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) { + if (!_poll_frame_queued[module] && xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) { xReturn = xTimerPendFunctionCallFromISR(_poll_frame, (void*)drv, module, &xHigherPriorityTaskWoken); portYIELD_FROM_ISR( xHigherPriorityTaskWoken ); if (xReturn == pdPASS) { - _poll_frame_in_queue = true; + _poll_frame_queued[module] = true; } else { TRACE("xTimerPendFunctionCallFromISR() queue full"); } From 23530216a17f9fb272309cc141cae1aa8f260ea3 Mon Sep 17 00:00:00 2001 From: 3djc <3djc@gh.com> Date: Tue, 4 Mar 2025 10:55:30 +0100 Subject: [PATCH 5/8] fix: ensure optimiser won't do us issues --- radio/src/edgetx.cpp | 2 +- radio/src/targets/taranis/gx12/bsp_io.cpp | 2 +- radio/src/telemetry/telemetry.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/radio/src/edgetx.cpp b/radio/src/edgetx.cpp index 0b1218dcc84..3e30a6191e3 100644 --- a/radio/src/edgetx.cpp +++ b/radio/src/edgetx.cpp @@ -250,7 +250,7 @@ void timer_10ms() #include #include -static bool _timer_10ms_cb_in_queue = false; +static volatile bool _timer_10ms_cb_in_queue = false; static void _timer_10ms_cb(void *pvParameter1, uint32_t ulParameter2) { diff --git a/radio/src/targets/taranis/gx12/bsp_io.cpp b/radio/src/targets/taranis/gx12/bsp_io.cpp index de7952640fd..641330434ef 100644 --- a/radio/src/targets/taranis/gx12/bsp_io.cpp +++ b/radio/src/targets/taranis/gx12/bsp_io.cpp @@ -38,7 +38,7 @@ struct bsp_io_expander { uint32_t state; }; -static bool _poll_switches_in_queue = false; +static volatile bool _poll_switches_in_queue = false; static bsp_io_expander _io_switches; static bsp_io_expander _io_fs_switches; diff --git a/radio/src/telemetry/telemetry.cpp b/radio/src/telemetry/telemetry.cpp index f9348b2edee..c9eb2ccc6cc 100644 --- a/radio/src/telemetry/telemetry.cpp +++ b/radio/src/telemetry/telemetry.cpp @@ -205,7 +205,7 @@ void telemetryStop() } } -static bool _poll_frame_queued[NUM_MODULES] = {false}; +static volatile bool _poll_frame_queued[NUM_MODULES] = {false}; static void _poll_frame(void *pvParameter1, uint32_t ulParameter2) { From 36c4a703ef4705067a50729dc7cd63adb243915a Mon Sep 17 00:00:00 2001 From: 3djc <3djc@gh.com> Date: Tue, 4 Mar 2025 11:00:57 +0100 Subject: [PATCH 6/8] chore: cleanup --- radio/src/targets/taranis/gx12/bsp_io.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/targets/taranis/gx12/bsp_io.cpp b/radio/src/targets/taranis/gx12/bsp_io.cpp index 641330434ef..43032065aee 100644 --- a/radio/src/targets/taranis/gx12/bsp_io.cpp +++ b/radio/src/targets/taranis/gx12/bsp_io.cpp @@ -58,7 +58,7 @@ static uint32_t _read_io_expander(bsp_io_expander* io) return io->state; } -static void _poll_switches(void *pvParameter1 = nullptr, uint32_t ulParameter2 = 0) +static void _poll_switches(void *pvParameter1, uint32_t ulParameter2) { (void)ulParameter2; _poll_switches_in_queue = false; From 6f896f5724a90d599e266491f1a0dcd1ea479d48 Mon Sep 17 00:00:00 2001 From: 3djc <3djc@gh.com> Date: Tue, 4 Mar 2025 11:10:32 +0100 Subject: [PATCH 7/8] chore: cleanup --- radio/src/edgetx.cpp | 3 ++- radio/src/targets/taranis/gx12/bsp_io.cpp | 2 +- radio/src/telemetry/telemetry.cpp | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/radio/src/edgetx.cpp b/radio/src/edgetx.cpp index 3e30a6191e3..05421d6ae0b 100644 --- a/radio/src/edgetx.cpp +++ b/radio/src/edgetx.cpp @@ -268,12 +268,13 @@ void per10ms() BaseType_t xReturn = pdFALSE; xReturn = xTimerPendFunctionCallFromISR(_timer_10ms_cb, nullptr, 0, &xHigherPriorityTaskWoken); - portYIELD_FROM_ISR(xHigherPriorityTaskWoken); + if (xReturn == pdPASS) { _timer_10ms_cb_in_queue = true; } else { TRACE("xTimerPendFunctionCallFromISR() queue full"); } + portYIELD_FROM_ISR( xHigherPriorityTaskWoken ); } } diff --git a/radio/src/targets/taranis/gx12/bsp_io.cpp b/radio/src/targets/taranis/gx12/bsp_io.cpp index 43032065aee..d5fa2beee72 100644 --- a/radio/src/targets/taranis/gx12/bsp_io.cpp +++ b/radio/src/targets/taranis/gx12/bsp_io.cpp @@ -74,13 +74,13 @@ static void _io_int_handler() if (!_poll_switches_in_queue && xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) { xReturn = xTimerPendFunctionCallFromISR(_poll_switches, nullptr, 0, &xHigherPriorityTaskWoken); - portYIELD_FROM_ISR(xHigherPriorityTaskWoken); if (xReturn == pdPASS) { _poll_switches_in_queue = true; } else { TRACE("xTimerPendFunctionCallFromISR() queue full"); } + portYIELD_FROM_ISR(xHigherPriorityTaskWoken); } } diff --git a/radio/src/telemetry/telemetry.cpp b/radio/src/telemetry/telemetry.cpp index c9eb2ccc6cc..8ffbb6421a7 100644 --- a/radio/src/telemetry/telemetry.cpp +++ b/radio/src/telemetry/telemetry.cpp @@ -253,13 +253,13 @@ void telemetryFrameTrigger_ISR(uint8_t module, const etx_proto_driver_t* drv) if (!_poll_frame_queued[module] && xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) { xReturn = xTimerPendFunctionCallFromISR(_poll_frame, (void*)drv, module, &xHigherPriorityTaskWoken); - portYIELD_FROM_ISR( xHigherPriorityTaskWoken ); if (xReturn == pdPASS) { _poll_frame_queued[module] = true; } else { TRACE("xTimerPendFunctionCallFromISR() queue full"); } + portYIELD_FROM_ISR( xHigherPriorityTaskWoken ); } } #endif From 92a0ca28730173460311185ed1b9a86fc82affe6 Mon Sep 17 00:00:00 2001 From: raphaelcoeffic <1050031+raphaelcoeffic@users.noreply.github.com> Date: Tue, 4 Mar 2025 11:23:28 +0100 Subject: [PATCH 8/8] refactor(radio): open log files from UI task This helps reducing processing time in the timer task. --- radio/src/logs.cpp | 481 ++++++++++++++++++++++++--------------------- radio/src/main.cpp | 11 +- radio/src/sdcard.h | 4 +- 3 files changed, 255 insertions(+), 241 deletions(-) diff --git a/radio/src/logs.cpp b/radio/src/logs.cpp index 3fd9e9ca978..6e8d87ae501 100644 --- a/radio/src/logs.cpp +++ b/radio/src/logs.cpp @@ -26,15 +26,32 @@ #include "switches.h" #include "hal/adc_driver.h" #include "hal/switch_driver.h" -#include "hal/usb_driver.h" #if defined(LIBOPENUI) #include "libopenui.h" #endif -FIL g_oLogFile __DMA; uint8_t logDelay100ms; -static tmr10ms_t lastLogTime = 0; + +static FIL _log_file __DMA; +static const char* _error_msg = nullptr; + +static const char* openLog(); +static void writeLog(); +static void writeHeader(); + +// static bool logFileIsValid() { return _logFile.obj.fs; } +static void logFileReset() { memset(&_log_file, 0, sizeof(_log_file)); } + +static uint32_t logDelayMs() { return (uint32_t)logDelay100ms * 100; } + +static void displayErrorMsg(const char* err) +{ + if (_error_msg != err) { + _error_msg = err; + POPUP_WARNING_ON_UI_TASK(err, nullptr, false); + } +} #if !defined(SIMU) #include @@ -48,133 +65,93 @@ static StaticTimer_t loggingTimerBuffer; static void loggingTimerCb(TimerHandle_t xTimer) { (void)xTimer; - if (mixerTaskRunning()) { - DEBUG_TIMER_START(debugTimerLoggingWakeup); - logsWrite(); - DEBUG_TIMER_STOP(debugTimerLoggingWakeup); - } + writeLog(); } -void loggingTimerStart() +static int loggingTimerStart(uint32_t period) { if (!loggingTimer) { - loggingTimer = - xTimerCreateStatic("Logging", logDelay100ms*100 / RTOS_MS_PER_TICK, pdTRUE, (void*)0, - loggingTimerCb, &loggingTimerBuffer); + loggingTimer = xTimerCreateStatic( + "Logging", period / RTOS_MS_PER_TICK, pdTRUE, (void*)0, + loggingTimerCb, &loggingTimerBuffer); } - if (loggingTimer) { - if( xTimerStart( loggingTimer, 0 ) != pdPASS ) { - /* The timer could not be set into the Active state. */ - } + if (!loggingTimer || (xTimerStart(loggingTimer, 0) != pdPASS)) { + return -1; } + + return 0; } void loggingTimerStop() { if (loggingTimer) { - if( xTimerStop( loggingTimer, 120 / RTOS_MS_PER_TICK ) != pdPASS ) { + if (xTimerStop(loggingTimer, 120 / RTOS_MS_PER_TICK) != pdPASS) { /* The timer could not be stopped. */ } - loggingTimer = nullptr; } } -void initLoggingTimer() { // called cyclically by main.cpp:perMain() - static uint8_t logDelay100msOld = 0; - - if(loggingTimer == nullptr) { // log Timer not running - if(isFunctionActive(FUNCTION_LOGS) && logDelay100ms > 0) { // if SF Logging is active and log rate is valid - loggingTimerStart(); // start log timer - } - } else { // log timer is already running - if(logDelay100msOld != logDelay100ms) { // if log rate was changed - logDelay100msOld = logDelay100ms; // memorize new log rate +static bool loggingTimerRunning() +{ + return loggingTimer && xTimerIsTimerActive(loggingTimer); +} - if(logDelay100ms > 0) { - if(xTimerChangePeriod( loggingTimer, logDelay100ms*100, 0 ) != pdPASS ) { // and restart timer with new log rate - /* The timer period could not be changed */ - } - } - } +static void loggingTimerSetPeriod(uint32_t period) +{ + if (xTimerChangePeriod(loggingTimer, period / RTOS_MS_PER_TICK, 0) != + pdPASS) { + /* The timer period could not be changed */ } } -#endif -void writeHeader(); +static bool loggingTimerExpired() { return true; } -int getSwitchState(uint8_t swtch) { - int value = getValue(MIXSRC_FIRST_SWITCH + swtch); - return (value == 0) ? 0 : (value < 0) ? -1 : +1; -} - -void logsInit() -{ - memset(&g_oLogFile, 0, sizeof(g_oLogFile)); -} +#else // !SIMU -const char * logsOpen() -{ - if (!sdMounted()) - return STR_NO_SDCARD; +static bool logging_running = false; - // Determine and set log file filename - FRESULT result; +static int loggingTimerStart(uint32_t period) { logging_running = true; } +static int loggingTimerStop() { logging_running = false; } +static bool loggingTimerRunning() { return logging_running; } - // /LOGS/modelnamexxxxxx_YYYY-MM-DD-HHMMSS.log - char filename[sizeof(LOGS_PATH) + LEN_MODEL_NAME + 18 + 4 + 1]; +static void loggingTimerSetPeriod(uint32_t period) {} - // check and create folder here - char* tmp = strAppend(filename, STR_LOGS_PATH); - const char * error = sdCheckAndCreateDirectory(filename); - if (error) { - return error; - } +static bool loggingTimerExpired() +{ + static uint32_t last_log = 0; + uint32_t now_ms = RTOS_GET_MS(); - tmp = strAppend(tmp, "/"); - if (g_model.header.name[0]) { - tmp = strAppend(tmp, sanitizeForFilename(g_model.header.name, LEN_MODEL_NAME)); - } else { - // TODO - uint8_t num = 1; - tmp = strAppend(tmp, STR_MODEL); - tmp = strAppendUnsigned(tmp, num, 2); + if (now_ms - last_log < logDelayMs()) { + return false; } -#if defined(RTCLOCK) - tmp = strAppendDate(tmp, true); -#endif - - strAppend(tmp, STR_LOGS_EXT); + last_log = now_ms; + return true; +} - result = f_open(&g_oLogFile, filename, FA_OPEN_ALWAYS | FA_WRITE | FA_OPEN_APPEND); - if (result != FR_OK) { - return SDCARD_ERROR(result); - } +#endif // !SIMU - if (f_size(&g_oLogFile) == 0) { - writeHeader(); - } - return nullptr; +static int getSwitchState(uint8_t swtch) +{ + int value = getValue(MIXSRC_FIRST_SWITCH + swtch); + return (value == 0) ? 0 : (value < 0) ? -1 : +1; } -void logsClose() +static uint32_t getLogicalSwitchesStates(uint8_t first) { - if (g_oLogFile.obj.fs && sdMounted()) { - if (f_close(&g_oLogFile) != FR_OK) { - // close failed, forget file - g_oLogFile.obj.fs = nullptr; - } - lastLogTime = 0; + uint32_t result = 0; + for (uint8_t i = 0; i < 32; i++) { + result |= (getSwitch(SWSRC_FIRST_LOGICAL_SWITCH + first + i) << i); } - + return result; } -void writeHeader() +static void writeHeader() { #if defined(RTCLOCK) - f_puts("Date,Time,", &g_oLogFile); + f_puts("Date,Time,", &_log_file); #else f_puts("Time,", &g_oLogFile); #endif @@ -194,7 +171,7 @@ void writeHeader() strcat(label, ")"); } strcat(label, ","); - f_puts(label, &g_oLogFile); + f_puts(label, &_log_file); } } } @@ -202,16 +179,16 @@ void writeHeader() auto n_inputs = adcGetMaxInputs(ADC_INPUT_MAIN); for (uint8_t i = 0; i < n_inputs; i++) { const char* p = analogGetCanonicalName(ADC_INPUT_MAIN, i); - f_puts(p, &g_oLogFile); - f_puts(",", &g_oLogFile); + f_puts(p, &_log_file); + f_puts(",", &_log_file); } n_inputs = adcGetMaxInputs(ADC_INPUT_FLEX); for (uint8_t i = 0; i < n_inputs; i++) { if (!IS_POT_AVAILABLE(i)) continue; const char* p = analogGetCanonicalName(ADC_INPUT_FLEX, i); - f_puts(p, &g_oLogFile); - f_puts(",", &g_oLogFile); + f_puts(p, &_log_file); + f_puts(",", &_log_file); } for (uint8_t i = 0; i < switchGetMaxSwitches(); i++) { @@ -221,170 +198,218 @@ void writeHeader() temp = getSwitchName(s, i); *temp++ = ','; *temp = '\0'; - f_puts(s, &g_oLogFile); + f_puts(s, &_log_file); } } - f_puts("LSW,", &g_oLogFile); + f_puts("LSW,", &_log_file); for (uint8_t channel = 0; channel < MAX_OUTPUT_CHANNELS; channel++) { - f_printf(&g_oLogFile, "CH%d(us),", channel+1); + f_printf(&_log_file, "CH%d(us),", channel+1); } - f_puts("TxBat(V)\n", &g_oLogFile); + f_puts("TxBat(V)\n", &_log_file); } -uint32_t getLogicalSwitchesStates(uint8_t first) +static int writeLogLine() { - uint32_t result = 0; - for (uint8_t i=0; i<32; i++) { - result |= (getSwitch(SWSRC_FIRST_LOGICAL_SWITCH+first+i) << i); +#if defined(RTCLOCK) + { + static struct gtm utm; + static gtime_t lastRtcTime = 0; + if (g_rtcTime != lastRtcTime) { + lastRtcTime = g_rtcTime; + gettime(&utm); + } + f_printf(&_log_file, "%4d-%02d-%02d,%02d:%02d:%02d.%02d0,", + utm.tm_year + TM_YEAR_BASE, utm.tm_mon + 1, utm.tm_mday, + utm.tm_hour, utm.tm_min, utm.tm_sec, g_ms100); } - return result; +#else + f_printf(&g_oLogFile, "%d,", tmr10ms); +#endif + + for (int i = 0; i < MAX_TELEMETRY_SENSORS; i++) { + if (isTelemetryFieldAvailable(i)) { + TelemetrySensor& sensor = g_model.telemetrySensors[i]; + TelemetryItem telemetryItem; + + if (sensor.logs) { + if (TELEMETRY_STREAMING() && !telemetryItems[i].isOld()) + telemetryItem = telemetryItems[i]; + + if (sensor.unit == UNIT_GPS) { + if (telemetryItem.gps.longitude && telemetryItem.gps.latitude) { + div_t qr = div((int)telemetryItem.gps.latitude, 1000000); + if (telemetryItem.gps.latitude < 0) f_printf(&_log_file, "-"); + f_printf(&_log_file, "%d.%06d ", abs(qr.quot), abs(qr.rem)); + qr = div((int)telemetryItem.gps.longitude, 1000000); + if (telemetryItem.gps.longitude < 0) f_printf(&_log_file, "-"); + f_printf(&_log_file, "%d.%06d,", abs(qr.quot), abs(qr.rem)); + } else { + f_printf(&_log_file, ","); + } + } else if (sensor.unit == UNIT_DATETIME) { + f_printf(&_log_file, "%4d-%02d-%02d %02d:%02d:%02d,", + telemetryItem.datetime.year, telemetryItem.datetime.month, + telemetryItem.datetime.day, telemetryItem.datetime.hour, + telemetryItem.datetime.min, telemetryItem.datetime.sec); + } else if (sensor.unit == UNIT_TEXT) { + f_printf(&_log_file, "\"%s\",", telemetryItem.text); + } else if (sensor.prec == 2) { + div_t qr = div((int)telemetryItem.value, 100); + if (telemetryItem.value < 0) f_printf(&_log_file, "-"); + f_printf(&_log_file, "%d.%02d,", abs(qr.quot), abs(qr.rem)); + } else if (sensor.prec == 1) { + div_t qr = div((int)telemetryItem.value, 10); + if (telemetryItem.value < 0) f_printf(&_log_file, "-"); + f_printf(&_log_file, "%d.%d,", abs(qr.quot), abs(qr.rem)); + } else { + f_printf(&_log_file, "%d,", telemetryItem.value); + } + } + } + } + + auto n_inputs = adcGetMaxInputs(ADC_INPUT_MAIN); + auto offset = adcGetInputOffset(ADC_INPUT_MAIN); + + for (uint8_t i = 0; i < n_inputs; i++) { + f_printf(&_log_file, "%d,", + calibratedAnalogs[inputMappingConvertMode(offset + i)]); + } + + n_inputs = adcGetMaxInputs(ADC_INPUT_FLEX); + offset = adcGetInputOffset(ADC_INPUT_FLEX); + + for (uint8_t i = 0; i < n_inputs; i++) { + if (IS_POT_AVAILABLE(i)) + f_printf(&_log_file, "%d,", calibratedAnalogs[offset + i]); + } + + for (uint8_t i = 0; i < switchGetMaxSwitches(); i++) { + if (SWITCH_EXISTS(i)) { + f_printf(&_log_file, "%d,", getSwitchState(i)); + } + } + f_printf(&_log_file, "0x%08X%08X,", getLogicalSwitchesStates(32), + getLogicalSwitchesStates(0)); + + for (uint8_t channel = 0; channel < MAX_OUTPUT_CHANNELS; channel++) { + f_printf(&_log_file, "%d,", + PPM_CENTER + channelOutputs[channel] / 2); // in us + } + + div_t qr = div(g_vbat100mV, 10); + return f_printf(&_log_file, "%d.%d\n", abs(qr.quot), abs(qr.rem)); } -void logsWrite() +static void writeLog() { - static const char * error_displayed = nullptr; + const char* err = nullptr; - if (!sdMounted()) { + if (_error_msg || !sdMounted() || !mixerTaskRunning() || + !loggingTimerExpired()) return; + + // TODO: check how long this really takes + if (sdIsFull()) { + err = STR_SDCARD_FULL_EXT; + } else if (writeLogLine() != 0) { + err = STR_SDCARD_ERROR; } - if (isFunctionActive(FUNCTION_LOGS) && logDelay100ms > 0 && !usbPlugged()) { - #if defined(SIMU) || !defined(RTCLOCK) - tmr10ms_t tmr10ms = get_tmr10ms(); // tmr10ms works in 10ms increments - if (lastLogTime == 0 || (tmr10ms_t)(tmr10ms - lastLogTime) >= (tmr10ms_t)(logDelay100ms*10)-1) { - lastLogTime = tmr10ms; - #else - { - #endif - - bool sdCardFull = sdIsFull(); - - // check if file needs to be opened - if (!g_oLogFile.obj.fs) { - const char *result = sdCardFull ? STR_SDCARD_FULL_EXT : logsOpen(); - - // SD card is full or file open failed - if (result) { - if (result != error_displayed) { - error_displayed = result; - POPUP_WARNING_ON_UI_TASK(result, nullptr, false); - } - return; - } - } + // timer is left running even if openLog() + // returned an error as we use the "running" + // state to detect intentional start/stop + if (err) displayErrorMsg(err); +} - // check at every write cycle - if (sdCardFull) { - logsClose(); // timer is still running and code above will try to - // open the file again but will fail with error - // which will trigger the warning popup - return; - } +static const char* openLog() +{ + if (!sdMounted()) return STR_NO_SDCARD; + + // Determine and set log file filename + FRESULT result; + + // /LOGS/modelnamexxxxxx_YYYY-MM-DD-HHMMSS.log + char filename[sizeof(LOGS_PATH) + LEN_MODEL_NAME + + sizeof("_YYYY-MM-DD-HHMMSS.log") + 1]; + + // check and create folder here + char* tmp = strAppend(filename, STR_LOGS_PATH); + const char * error = sdCheckAndCreateDirectory(filename); + if (error) return error; + + tmp = strAppend(tmp, "/"); + if (g_model.header.name[0]) { + tmp = strAppend(tmp, sanitizeForFilename(g_model.header.name, LEN_MODEL_NAME)); + } else { + // TODO + uint8_t num = 1; + tmp = strAppend(tmp, STR_MODEL); + tmp = strAppendUnsigned(tmp, num, 2); + } #if defined(RTCLOCK) - { - static struct gtm utm; - static gtime_t lastRtcTime = 0; - if (g_rtcTime != lastRtcTime) { - lastRtcTime = g_rtcTime; - gettime(&utm); - } - f_printf(&g_oLogFile, "%4d-%02d-%02d,%02d:%02d:%02d.%02d0,", utm.tm_year+TM_YEAR_BASE, utm.tm_mon+1, utm.tm_mday, utm.tm_hour, utm.tm_min, utm.tm_sec, g_ms100); - } -#else - f_printf(&g_oLogFile, "%d,", tmr10ms); + tmp = strAppendDate(tmp, true); #endif - for (int i=0; i 0)) { + if (!loggingTimerRunning()) { + current_delay = logDelay100ms; + loggingStart(logDelayMs()); + } else if (current_delay != logDelay100ms) { + current_delay = logDelay100ms; + loggingTimerSetPeriod(logDelayMs()); } - } - else { - error_displayed = nullptr; + } else { logsClose(); - - #if !defined(SIMU) - loggingTimerStop(); - #endif } } diff --git a/radio/src/main.cpp b/radio/src/main.cpp index cbcc6fb2ece..7d146a3c089 100644 --- a/radio/src/main.cpp +++ b/radio/src/main.cpp @@ -465,10 +465,6 @@ void guiMain(event_t evt) } #endif -#if !defined(SIMU) -void initLoggingTimer(); -#endif - void perMain() { DEBUG_TIMER_START(debugTimerPerMain1); @@ -477,12 +473,7 @@ void perMain() if (!usbPlugged() || (getSelectedUsbMode() == USB_UNSELECTED_MODE)) { checkStorageUpdate(); - -#if !defined(SIMU) // use FreeRTOS software timer if radio firmware - initLoggingTimer(); // initialize software timer for logging -#else - logsWrite(); // call logsWrite the old way for simu -#endif + logsHandle(); } handleUsbConnection(); diff --git a/radio/src/sdcard.h b/radio/src/sdcard.h index a6c380abcb9..a3fb1763c81 100644 --- a/radio/src/sdcard.h +++ b/radio/src/sdcard.h @@ -23,8 +23,6 @@ #include "ff.h" -extern FIL g_oLogFile; - #include "translations.h" #define FILE_COPY_PREFIX "cp_" @@ -120,7 +118,7 @@ const char YAMLFILE_CHECKSUM_TAG_NAME[] = "checksum"; extern uint8_t logDelay100ms; void logsInit(); void logsClose(); -void logsWrite(); +void logsHandle(); void sdInit(); void sdMount();