From 7a167c4b656bf2fdc1b3244e31dae4bd550a65ee Mon Sep 17 00:00:00 2001 From: Bryce Gruber Date: Sun, 6 Jul 2025 22:49:21 -0500 Subject: [PATCH 1/2] initial race osd features --- lib/MSP/msptypes.h | 6 +- lib/RaceOSD/devRaceOSD.cpp | 510 +++++++++++++++++++++++++++++++++++++ lib/RaceOSD/devRaceOSD.h | 63 +++++ lib/RaceOSD/racetypes.h | 75 ++++++ src/Vrx_main.cpp | 18 ++ targets/hdzero.ini | 2 + 6 files changed, 673 insertions(+), 1 deletion(-) create mode 100644 lib/RaceOSD/devRaceOSD.cpp create mode 100644 lib/RaceOSD/devRaceOSD.h create mode 100644 lib/RaceOSD/racetypes.h diff --git a/lib/MSP/msptypes.h b/lib/MSP/msptypes.h index 2c05fbb5..f8d29109 100644 --- a/lib/MSP/msptypes.h +++ b/lib/MSP/msptypes.h @@ -20,7 +20,6 @@ #define MSP_ELRS_SET_RX_LOAN_MODE 0x0F #define MSP_ELRS_GET_BACKPACK_VERSION 0x10 #define MSP_ELRS_BACKPACK_CRSF_TLM 0x11 -#define MSP_ELRS_SET_SEND_UID 0x00B5 #define MSP_ELRS_SET_OSD 0x00B6 // Config opcodes @@ -56,3 +55,8 @@ #define MSP_ELRS_BACKPACK_GET_VERSION 0x0381 // get the bacpack firmware version #define MSP_ELRS_BACKPACK_GET_STATUS 0x0382 // get the status of the backpack #define MSP_ELRS_BACKPACK_SET_PTR 0x0383 // forwarded back to TX backpack + +// incoming, packets originating from the Timer +#define MSP_ELRS_SET_SEND_UID 0x0401 // Reserve 0x0400 for future use +#define MSP_ELRS_SET_RACE_STATE 0x0402 +#define MSP_ELRS_SET_RACEOSD_DATA 0x0403 \ No newline at end of file diff --git a/lib/RaceOSD/devRaceOSD.cpp b/lib/RaceOSD/devRaceOSD.cpp new file mode 100644 index 00000000..6c2102d6 --- /dev/null +++ b/lib/RaceOSD/devRaceOSD.cpp @@ -0,0 +1,510 @@ +#include +#include +#include "msp.h" +#include "msptypes.h" +#include "devRaceOSD.h" + +#if defined(RACEOSD_HEIGHT) && defined(RACEOSD_WIDTH) + +// FreeRTOS management +#if defined(PLATFORM_ESP32) +static StaticSemaphore_t xMutexBuffer; +static SemaphoreHandle_t xSemaphore = NULL; +static TimerHandle_t xOSDTimer = NULL; +static TaskHandle_t xOSDUpdateTask = NULL; +#endif + +// OSD Data +static mspPacket_t displayPacket; +static raceState_e state = RACE_STATE_READY; +static uint32_t raceClockStartTime; +static uint32_t raceDuration; +static bool rowHasUpdate[RACEOSD_HEIGHT]; +static RowOSDElements *assignedRowElements[RACEOSD_HEIGHT]; + +// Create allocate memory for elements from timer +static TimerOSDElement timerElements[35] = { + {RACE_TOTAL_PILOTS, 1}, + {RACE_LAP_MAX, 2}, + {RACE_CONSECUTIVES_BASE, 1}, + {RACE_WIN_CONDITION, 1}, + {RACE_BEST_LAP, 1}, + {RACE_BEST_LAP_CALLSIGN, 16}, + {RACE_SPLIT_COUNT, 1}, + + {CURRENT_CALLSIGN, 16}, + {CURRENT_SEAT, 1}, + {CURRENT_POSITION, 1}, + {CURRENT_LAP_NUMBER, 2}, + {CURRENT_SPLIT_NUMBER, 1}, + {CURRENT_TIME, 10}, + {CURRENT_TOTAL_TIME, 10}, + {CURRENT_TOTAL_TIME_LAPS, 10}, + {CURRENT_CONSECUTIVES, 10}, + {CURRENT_CONSECUTIVES_BASE, 1}, + {CURRENT_IS_BEST, 1}, + {CURRENT_IS_DONE, 1}, + + {NEXT_CALLSIGN, 16}, + {NEXT_SEAT, 1}, + {NEXT_POSITION, 1}, + {NEXT_DIFF_TIME, 10}, + {NEXT_LAP_NUMBER, 1}, + {NEXT_SPLIT_NUMBER, 1}, + {NEXT_LAST_TIME, 10}, + {NEXT_TOTAL_TIME, 10}, + + {FIRST_CALLSIGN, 16}, + {FIRST_SEAT, 1}, + {FIRST_POSITION, 1}, + {FIRST_DIFF_TIME, 1}, + {FIRST_LAP_NUMBER, 1}, + {FIRST_SPLIT_NUMBER, 1}, + {FIRST_LAST_TIME, 10}, + {FIRST_TOTAL_TIME, 10}}; + +// Setup iterator for system elements +static void getRaceClockData(uint8_t *buffer); +static SystemOSDElement systemElements[1] = { + {SYSTEM_RACE_TIME, getRaceClockData, 10}}; + +TimerOSDElement::TimerOSDElement(timerRaceData_e id, size_t size) +{ + this->id = id; + this->size = size; + data = new uint8_t[size]; + memset(data, EMPTY_VALUE, sizeof(*data)); +} + +TimerOSDElement::~TimerOSDElement() +{ + delete[] data; + data = nullptr; +} + +void +TimerOSDElement::getData(uint8_t *data) +{ + memcpy(data, this->data, sizeof(*this->data)); +} + +SystemOSDElement::SystemOSDElement(systemRaceData_e id, std::function call, size_t size) +{ + this->id = id; + this->call = call; + this->size = size; +} + +RowOSDElements::RowOSDElements(size_t size) +{ + this->size = size; +} + +void +SystemOSDElement::getData(uint8_t *data) +{ + call(data); +} + +/* + * Process incoming race state packet +*/ +void processRaceStatePacket(mspPacket_t *packet) +{ + // TODO: Handle potential conditions from + // previous state + + switch (packet->readByte()) + { + case RACE_STATE_READY: + state = RACE_STATE_READY; + break; + case RACE_STATE_SCHEDULED: + state = RACE_STATE_SCHEDULED; + break; + case RACE_STATE_STAGING: + state = RACE_STATE_STAGING; + break; + case RACE_STATE_RACING: + state = RACE_STATE_RACING; + break; + case RACE_STATE_OVERTIME: + state = RACE_STATE_OVERTIME; + break; + case RACE_STATE_PAUSED: + state = RACE_STATE_PAUSED; + break; + case RACE_STATE_STOPPED: + state = RACE_STATE_STOPPED; + break; + default: + break; + } +} + +/* + * Saves incoming timer race data +*/ +static void saveRaceOSDPacket(mspPacket_t *packet, TimerOSDElement *element) +{ + static u_int16_t packet_size; + + #if defined(PLATFORM_ESP32) + if (element->enabled && xSemaphoreTake(xSemaphore, portMAX_DELAY) == pdTRUE) + #else + if (element.enabled) + #endif + { + // Clear any previously stored data + memset(element->data, EMPTY_VALUE, sizeof(*element->data)); + packet_size = packet->payloadSize - 1; // One byte was already read from payload + + // Write new data + for (int i = 0; i < packet_size && i < element->size; i++) + { + element->data[i] = packet->readByte(); + } + + // Mark flag to render row updates + rowHasUpdate[element->row] = true; + + #if defined(PLATFORM_ESP32) + xSemaphoreGive(xSemaphore); + #endif + } + +} + +/* + * Process incoming timer race data packet +*/ +void processRaceOSDPacket(mspPacket_t *packet) +{ + switch (packet->readByte()) + { + case RACE_TOTAL_PILOTS: + saveRaceOSDPacket(packet, &timerElements[0]); + break; + case RACE_LAP_MAX: + saveRaceOSDPacket(packet, &timerElements[1]); + break; + case RACE_CONSECUTIVES_BASE: + saveRaceOSDPacket(packet, &timerElements[2]); + break; + case RACE_WIN_CONDITION: + saveRaceOSDPacket(packet, &timerElements[3]); + break; + case RACE_BEST_LAP: + saveRaceOSDPacket(packet, &timerElements[4]); + break; + case RACE_BEST_LAP_CALLSIGN: + saveRaceOSDPacket(packet, &timerElements[5]); + break; + case RACE_SPLIT_COUNT: + saveRaceOSDPacket(packet, &timerElements[6]); + break; + case CURRENT_CALLSIGN: + saveRaceOSDPacket(packet, &timerElements[7]); + break; + case CURRENT_SEAT: + saveRaceOSDPacket(packet, &timerElements[8]); + break; + case CURRENT_POSITION: + saveRaceOSDPacket(packet, &timerElements[9]); + break; + case CURRENT_LAP_NUMBER: + saveRaceOSDPacket(packet, &timerElements[10]); + break; + case CURRENT_SPLIT_NUMBER: + saveRaceOSDPacket(packet, &timerElements[11]); + break; + case CURRENT_TIME: + saveRaceOSDPacket(packet, &timerElements[12]); + break; + case CURRENT_TOTAL_TIME: + saveRaceOSDPacket(packet, &timerElements[13]); + break; + case CURRENT_TOTAL_TIME_LAPS: + saveRaceOSDPacket(packet, &timerElements[14]); + break; + case CURRENT_CONSECUTIVES: + saveRaceOSDPacket(packet, &timerElements[15]); + break; + case CURRENT_CONSECUTIVES_BASE: + saveRaceOSDPacket(packet, &timerElements[16]); + break; + case CURRENT_IS_BEST: + saveRaceOSDPacket(packet, &timerElements[17]); + break; + case CURRENT_IS_DONE: + saveRaceOSDPacket(packet, &timerElements[18]); + break; + case NEXT_CALLSIGN: + saveRaceOSDPacket(packet, &timerElements[19]); + break; + case NEXT_SEAT: + saveRaceOSDPacket(packet, &timerElements[20]); + break; + case NEXT_POSITION: + saveRaceOSDPacket(packet, &timerElements[21]); + break; + case NEXT_DIFF_TIME: + saveRaceOSDPacket(packet, &timerElements[22]); + break; + case NEXT_LAP_NUMBER: + saveRaceOSDPacket(packet, &timerElements[23]); + break; + case NEXT_SPLIT_NUMBER: + saveRaceOSDPacket(packet, &timerElements[24]); + break; + case NEXT_LAST_TIME: + saveRaceOSDPacket(packet, &timerElements[25]); + break; + case NEXT_TOTAL_TIME: + saveRaceOSDPacket(packet, &timerElements[26]); + break; + case FIRST_CALLSIGN: + saveRaceOSDPacket(packet, &timerElements[27]); + break; + case FIRST_SEAT: + saveRaceOSDPacket(packet, &timerElements[28]); + break; + case FIRST_POSITION: + saveRaceOSDPacket(packet, &timerElements[29]); + break; + case FIRST_DIFF_TIME: + saveRaceOSDPacket(packet, &timerElements[30]); + break; + case FIRST_LAP_NUMBER: + saveRaceOSDPacket(packet, &timerElements[31]); + break; + case FIRST_SPLIT_NUMBER: + saveRaceOSDPacket(packet, &timerElements[32]); + break; + case FIRST_LAST_TIME: + saveRaceOSDPacket(packet, &timerElements[33]); + break; + case FIRST_TOTAL_TIME: + saveRaceOSDPacket(packet, &timerElements[34]); + break; + default: + break; + } +} + +/* + * Calculate and format race clock data +*/ +static void getRaceClockData(uint8_t* buffer) +{ + static uint32_t clock; + + clock = millis() - raceClockStartTime; + if (raceDuration > 0) + clock = raceDuration - clock; + + // TODO: Write formated clock data into buffer +} + +/* + * Renders the elements in a row +*/ +static void renderOSDRowUpdate(uint8_t* rowData, uint8_t row_num) +{ + static uint8_t* elementData; + static OSDElement *element; + + #if defined(PLATFORM_ESP32) + // Take mutex to prevent updates to row while rendering elements + if (xSemaphoreTake(xSemaphore, portMAX_DELAY) == pdTRUE) + { + #endif + + for (int i = 0; i < assignedRowElements[row_num]->size; i++) + { + element = assignedRowElements[row_num]->elements[i]; + + uint8_t buffer[element->size]; + elementData = buffer; + element->getData(elementData); + + for (int i = 0; i < element->size && i + element->col < RACEOSD_WIDTH; i++) + { + //Break early if two consecutive EMPTY_VALUEs are detected + if (elementData[i] == EMPTY_VALUE && i + 1 < element->size && elementData[i + 1] == EMPTY_VALUE) + break; + + rowData[element->col + i] = elementData[i]; + } + } + + rowHasUpdate[row_num] = false; + #if defined(PLATFORM_ESP32) + xSemaphoreGive(xSemaphore); + } + #endif +} + +/* + * @brief Send rendered elements to host + * + * Render each updated row and send data to + * the host device. +*/ +static void sendOSDUpdates(void *) +{ + static MSP msp; + static bool updated_flag; + static mspPacket_t packet; + static uint8_t rowData[RACEOSD_WIDTH]; + + updated_flag = false; + for (int i = 0; i < RACEOSD_HEIGHT; i++) + { + if (rowHasUpdate[i]) + { + memset(rowData, EMPTY_VALUE, sizeof(rowData)); + renderOSDRowUpdate(rowData, i); + packet.reset(); + packet.makeCommand(); + packet.function = MSP_ELRS_SET_OSD; + packet.addByte(0x03); + packet.addByte(i); + packet.addByte(0); + + for (int j = 0; j < RACEOSD_WIDTH; j++) + { + packet.addByte(rowData[j]); + } + + msp.sendPacket(&packet, &Serial); + updated_flag = true; + } + } + + // Send packet to render updated row(s) + if (updated_flag) + { + msp.sendPacket(&displayPacket, &Serial); + } + + #if defined(PLATFORM_ESP32) + vTaskDelete(NULL); + xOSDUpdateTask = NULL; + #endif +} + +/* + * @brief Trigger the OSD updates + * + * Either spawn in a new task to update the race OSD or perform + * the updates as a function call (platform dependent) +*/ +#if defined(PLATFORM_ESP32) +static void runRaceOSDUpdates(TimerHandle_t timer) +{ + // Schedule a lower priority task instead of running from a ISR + if (xOSDUpdateTask == NULL) + xTaskCreate(sendOSDUpdates, "Update OSD", 4096, NULL, 8, &xOSDUpdateTask); +} +#else +void runRaceOSDUpdates(void) +{ + + static uint32_t lastUpdateTime; + static uint32_t now; + + now = millis(); + if (now - lastUpdateTime > OSD_REFRESH_MILLIS) + { + sendOSDUpdates(); + lastUpdateTime = now; + } +} +#endif + +/* + * @brief Builds element array for a row + * + * Builds an array that contains the element pointers + * for each row to allow for efficiently rendering OSD updates. + * This is expected to only be called at startup. + * + * @param row_num The row to build the array for +*/ +static void buildRowElementsArray(uint8_t row_num) +{ + uint8_t element_count = 0; + + // Count the total number of elements in row + for (const TimerOSDElement &element : timerElements) + { + if (element.enabled && element.row == row_num) + element_count++; + } + + for (const SystemOSDElement &element : systemElements) + { + if (element.enabled && element.row == row_num) + element_count++; + } + + RowOSDElements *assignedElements = new RowOSDElements(element_count); + element_count = 0; + + for (TimerOSDElement element : timerElements) + { + if (element.enabled && element.row == row_num) + assignedElements->elements[element_count++] = &element; + } + + for (SystemOSDElement element : systemElements) + { + if (element.enabled && element.row == row_num) + assignedElements->elements[element_count++] = &element; + } + + assignedRowElements[row_num] = assignedElements; +} + +/* + * @brief RaceOSD startup function + * + * Sets up and configures the systems the + * RaceOSD relies on to function. +*/ +void setupRaceOSD(void) +{ + + // Load element settings from EEPROM + // TODO + + // Build element array for each row + for (int i = 0; i < RACEOSD_HEIGHT; i++) + { + buildRowElementsArray(i); + } + + // Build display packet for repeated use + displayPacket.reset(); + displayPacket.makeCommand(); + displayPacket.function = MSP_ELRS_SET_OSD; + displayPacket.addByte(0x04); + + #if defined(PLATFORM_ESP32) + // Setup osd element mutex + xSemaphore = xSemaphoreCreateMutexStatic(&xMutexBuffer); + + // Setup timer for generating the OSD update task + xOSDTimer = xTimerCreate( + "OSD Update Timer", + OSD_REFRESH_MILLIS / portTICK_PERIOD_MS, + pdTRUE, + (void *)0, + runRaceOSDUpdates); + + xTimerStart(xOSDTimer, portMAX_DELAY); + #endif +} + +#endif diff --git a/lib/RaceOSD/devRaceOSD.h b/lib/RaceOSD/devRaceOSD.h new file mode 100644 index 00000000..7196e111 --- /dev/null +++ b/lib/RaceOSD/devRaceOSD.h @@ -0,0 +1,63 @@ +#include "msp.h" +#include "racetypes.h" + +#if defined(RACEOSD_HEIGHT) && defined(RACEOSD_WIDTH) + +#ifndef RACEOSD_ENABLED +#define RACEOSD_ENABLED 1 +#endif + +#define EMPTY_VALUE 0 +#define OSD_REFRESH_MILLIS 100 + +class OSDElement +{ +public: + bool enabled; + uint8_t col; + uint8_t row; + size_t size; + + virtual void getData(uint8_t *) {}; +}; + +class TimerOSDElement : public OSDElement +{ +public: + timerRaceData_e id; + uint8_t *data; + + TimerOSDElement(timerRaceData_e id, size_t size); + ~TimerOSDElement(); + void getData(uint8_t *); +}; + +class SystemOSDElement : public OSDElement +{ +public: + systemRaceData_e id; + std::function call; + + SystemOSDElement(systemRaceData_e id, std::function call, size_t size); + void getData(uint8_t *); +}; + +typedef struct RowOSDElements +{ + size_t size; + OSDElement **elements; + + RowOSDElements(size_t size); +} RowOSDElements_t; + +// Public functions +void processRaceOSDPacket(mspPacket_t *packet); +void processRaceStatePacket(mspPacket_t *packet); + +#if defined(PLATFORM_ESP8266) +void runRaceOSDUpdates(void); +#endif + +void setupRaceOSD(void); + +#endif diff --git a/lib/RaceOSD/racetypes.h b/lib/RaceOSD/racetypes.h new file mode 100644 index 00000000..0989630f --- /dev/null +++ b/lib/RaceOSD/racetypes.h @@ -0,0 +1,75 @@ +typedef enum +{ + RACE_STATE_READY = 0x00, + RACE_STATE_SCHEDULED = 0x01, + RACE_STATE_STAGING = 0x02, + RACE_STATE_RACING = 0x03, + RACE_STATE_OVERTIME = 0x04, + RACE_STATE_PAUSED = 0x05, + RACE_STATE_STOPPED = 0x06 +} raceState_e; + +// Currently modeled on RotorHazard's Realtime lap information specification +// https://github.com/RotorHazard/RotorHazard/wiki/Specification:-Realtime-lap-information +typedef enum +{ + // Race Data Object + // The race data object contains information about the race that may be + // important to the interpretation of other data. + + RACE_TOTAL_PILOTS = 0x00, + RACE_LAP_MAX = 0x01, + RACE_CONSECUTIVES_BASE = 0x02, + RACE_WIN_CONDITION = 0x03, + RACE_BEST_LAP = 0x04, + RACE_BEST_LAP_CALLSIGN = 0x05, + RACE_SPLIT_COUNT = 0x06, + + // Current Lap Data Object + // The current lap data object contains data of the pilot currently crossing. + + CURRENT_CALLSIGN = 0x10, + CURRENT_SEAT = 0x11, + CURRENT_POSITION = 0x12, + CURRENT_LAP_NUMBER = 0x13, + CURRENT_SPLIT_NUMBER = 0x14, + CURRENT_TIME = 0x15, + CURRENT_TOTAL_TIME = 0x16, + CURRENT_TOTAL_TIME_LAPS = 0x17, + CURRENT_CONSECUTIVES = 0x18, + CURRENT_CONSECUTIVES_BASE = 0x19, + CURRENT_IS_BEST = 0x1A, + CURRENT_IS_DONE = 0x1B, + + // Next Rank Data Object + // The next rank data object contains information about the pilot that occupies + // the next higher rank at the time of crossing. + + NEXT_CALLSIGN = 0x20, + NEXT_SEAT = 0x21, + NEXT_POSITION = 0x22, + NEXT_DIFF_TIME = 0x23, + NEXT_LAP_NUMBER = 0x24, + NEXT_SPLIT_NUMBER = 0x25, + NEXT_LAST_TIME = 0x26, + NEXT_TOTAL_TIME = 0x27, + + // First Rank Data Object + // The first rank data object contains information about the pilot that + // occupies the highest rank (1) at the time of crossing. + + FIRST_CALLSIGN = 0x30, + FIRST_SEAT = 0x31, + FIRST_POSITION = 0x32, + FIRST_DIFF_TIME = 0x33, + FIRST_LAP_NUMBER = 0x34, + FIRST_SPLIT_NUMBER = 0x35, + FIRST_LAST_TIME = 0x36, + FIRST_TOTAL_TIME = 0x37, + +} timerRaceData_e; + +typedef enum +{ + SYSTEM_RACE_TIME = 0x00 +} systemRaceData_e; \ No newline at end of file diff --git a/src/Vrx_main.cpp b/src/Vrx_main.cpp index 466ff183..3d796a47 100644 --- a/src/Vrx_main.cpp +++ b/src/Vrx_main.cpp @@ -24,6 +24,7 @@ #include "devButton.h" #include "devLED.h" #include "devHeadTracker.h" +#include "devRaceOSD.h" #ifdef RAPIDFIRE_BACKPACK #include "rapidfire.h" @@ -245,6 +246,14 @@ void ProcessMSPPacket(mspPacket_t *packet) DBGLN("Processing MSP_ELRS_SET_OSD..."); vrxModule.SetOSD(packet); break; + #if defined(RACEOSD_ENABLED) + case MSP_ELRS_SET_RACE_STATE: + processRaceStatePacket(packet); + break; + case MSP_ELRS_SET_RACEOSD_DATA: + processRaceOSDPacket(packet); + break; + #endif case MSP_ELRS_BACKPACK_SET_HEAD_TRACKING: DBGLN("Processing MSP_ELRS_BACKPACK_SET_HEAD_TRACKING..."); headTrackingEnabled = packet->readByte(); @@ -480,6 +489,11 @@ void setup() #if defined(HDZERO_BACKPACK) Serial.begin(VRX_UART_BAUD); #endif + + #if defined(RACEOSD_ENABLED) + setupRaceOSD(); + #endif + DBGLN("Setup completed"); } @@ -559,6 +573,10 @@ void loop() if (xQueueReceive(rxqueue, &rxPacket, (TickType_t)512) == pdTRUE) ProcessMSPPacket(&rxPacket); } +#elif defined(PLATFORM_ESP8266) + #if defined(RACEOSD_ENABLED) + runRaceOSDUpdates(); + #endif #endif } diff --git a/targets/hdzero.ini b/targets/hdzero.ini index 7797bfa6..d36b0877 100644 --- a/targets/hdzero.ini +++ b/targets/hdzero.ini @@ -48,6 +48,8 @@ build_flags = -D PIN_LED=4 -D NO_AUTOBIND=1 -D SUPPORT_HEADTRACKING + -D RACEOSD_HEIGHT=18 + -D RACEOSD_WIDTH=50 [env:HDZero_Goggle_ESP32_Backpack_via_WIFI] extends = env:HDZero_Goggle_ESP32_Backpack_via_UART From 9e3a8aa530eda813632d84c58d9c242465a1e58f Mon Sep 17 00:00:00 2001 From: Bryce Gruber Date: Mon, 7 Jul 2025 21:04:09 -0500 Subject: [PATCH 2/2] reduce task generation --- lib/RaceOSD/devRaceOSD.cpp | 109 +++++++++++++++++++++++-------------- 1 file changed, 69 insertions(+), 40 deletions(-) diff --git a/lib/RaceOSD/devRaceOSD.cpp b/lib/RaceOSD/devRaceOSD.cpp index 6c2102d6..908c3057 100644 --- a/lib/RaceOSD/devRaceOSD.cpp +++ b/lib/RaceOSD/devRaceOSD.cpp @@ -10,8 +10,14 @@ #if defined(PLATFORM_ESP32) static StaticSemaphore_t xMutexBuffer; static SemaphoreHandle_t xSemaphore = NULL; -static TimerHandle_t xOSDTimer = NULL; + +#define STACK_SIZE 1028 static TaskHandle_t xOSDUpdateTask = NULL; +StaticTask_t xTaskBuffer; +StackType_t xStack[ STACK_SIZE ]; + +static TimerHandle_t xOSDTimer = NULL; +StaticTimer_t pxTimerBuffer; #endif // OSD Data @@ -351,46 +357,55 @@ static void renderOSDRowUpdate(uint8_t* rowData, uint8_t row_num) * Render each updated row and send data to * the host device. */ -static void sendOSDUpdates(void *) +static void runOSDUpdates(void *) { static MSP msp; static bool updated_flag; static mspPacket_t packet; static uint8_t rowData[RACEOSD_WIDTH]; - updated_flag = false; - for (int i = 0; i < RACEOSD_HEIGHT; i++) + #if defined(PLATFORM_ESP32) + while (1) { - if (rowHasUpdate[i]) + // Reset the notification state and wait for new + // notification to proceed + xTaskNotifyStateClear(xOSDUpdateTask); + xTaskNotifyWait(0x00, ULONG_MAX, NULL, portMAX_DELAY); + #endif + + updated_flag = false; + for (int i = 0; i < RACEOSD_HEIGHT; i++) { - memset(rowData, EMPTY_VALUE, sizeof(rowData)); - renderOSDRowUpdate(rowData, i); - packet.reset(); - packet.makeCommand(); - packet.function = MSP_ELRS_SET_OSD; - packet.addByte(0x03); - packet.addByte(i); - packet.addByte(0); - - for (int j = 0; j < RACEOSD_WIDTH; j++) + if (rowHasUpdate[i]) { - packet.addByte(rowData[j]); + memset(rowData, EMPTY_VALUE, sizeof(rowData)); + renderOSDRowUpdate(rowData, i); + packet.reset(); + packet.makeCommand(); + packet.function = MSP_ELRS_SET_OSD; + packet.addByte(0x03); + packet.addByte(i); + packet.addByte(0); + + for (int j = 0; j < RACEOSD_WIDTH; j++) + { + packet.addByte(rowData[j]); + } + + msp.sendPacket(&packet, &Serial); + updated_flag = true; } - - msp.sendPacket(&packet, &Serial); - updated_flag = true; } - } + - // Send packet to render updated row(s) - if (updated_flag) - { - msp.sendPacket(&displayPacket, &Serial); - } + // Send packet to render updated row(s) + if (updated_flag) + { + msp.sendPacket(&displayPacket, &Serial); + } #if defined(PLATFORM_ESP32) - vTaskDelete(NULL); - xOSDUpdateTask = NULL; + } #endif } @@ -401,14 +416,17 @@ static void sendOSDUpdates(void *) * the updates as a function call (platform dependent) */ #if defined(PLATFORM_ESP32) -static void runRaceOSDUpdates(TimerHandle_t timer) +static void triggerRaceOSDUpdates(TimerHandle_t timer) { - // Schedule a lower priority task instead of running from a ISR - if (xOSDUpdateTask == NULL) - xTaskCreate(sendOSDUpdates, "Update OSD", 4096, NULL, 8, &xOSDUpdateTask); + // Notify OSD updater task to proceed if in a active + // race state + if (state == RACE_STATE_STAGING || + state == RACE_STATE_RACING || + state == RACE_STATE_OVERTIME) + xTaskNotify(xOSDUpdateTask, 0, eSetValueWithOverwrite); } #else -void runRaceOSDUpdates(void) +void triggerRaceOSDUpdates(void) { static uint32_t lastUpdateTime; @@ -417,7 +435,7 @@ void runRaceOSDUpdates(void) now = millis(); if (now - lastUpdateTime > OSD_REFRESH_MILLIS) { - sendOSDUpdates(); + runOSDUpdates(); lastUpdateTime = now; } } @@ -495,13 +513,24 @@ void setupRaceOSD(void) // Setup osd element mutex xSemaphore = xSemaphoreCreateMutexStatic(&xMutexBuffer); - // Setup timer for generating the OSD update task - xOSDTimer = xTimerCreate( - "OSD Update Timer", - OSD_REFRESH_MILLIS / portTICK_PERIOD_MS, - pdTRUE, - (void *)0, - runRaceOSDUpdates); + // Create task to run OSD updates + xOSDUpdateTask = xTaskCreateStatic( + runOSDUpdates, + "OSD Updater", + STACK_SIZE, + NULL, + 8, + xStack, + &xTaskBuffer); + + // Setup timer for generating osd update notifications + xOSDTimer = xTimerCreateStatic( + "OSD Trigger Timer", + OSD_REFRESH_MILLIS / portTICK_PERIOD_MS, + pdTRUE, + (void *)0, + triggerRaceOSDUpdates, + &pxTimerBuffer); xTimerStart(xOSDTimer, portMAX_DELAY); #endif