Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
333a85d
fix(color): manage models buttons incorrect after renaming or removin…
philmoz Aug 25, 2026
1684aee
fix(color): number edit keyboard is not sized to suit display, cursor…
philmoz Aug 25, 2026
8b4f781
fix(color): in manage models, long press on selected model button cle…
philmoz Aug 25, 2026
5501690
fix(color): UI issues adding and removing labels (#7715)
philmoz Aug 27, 2026
d76e7c7
fix(lua): compare float with int strictly in equality (#7611)
bultodepapas Aug 30, 2026
76f0b77
fix(taranis): restore volume cli for I2C audio targets (#6543)
fpenard Aug 30, 2026
898d6e3
chore(elrs): allow future use of unused bits in the "Arming status by…
mha1 Aug 30, 2026
6ea50f5
fix(fw): trim not applied when mix source is inverted (#7779)
philmoz Sep 13, 2026
17cb22f
fix(bw): potential Lua stack overflow in lcd.drawCombobox (#7775)
philmoz Sep 13, 2026
6dde229
fix(firmware): audio logic does not use all of the available buffers …
philmoz Sep 16, 2026
5388301
chore(color): fix compile error if LUA_ALLOCATOR_TRACER is defined (#…
philmoz Sep 18, 2026
bdee8b0
fix(color): labels for a model may get truncated in 'labels.yml' file…
philmoz Sep 23, 2026
3d8fbc5
chore(fw): replace unsafe calls to strAppend with copyToUnTerminated …
philmoz Sep 24, 2026
287b90a
feat(xlite): add compile time option for alternative button layout (#…
netmute Oct 31, 2022
a67fd58
chore(color): remove unused LV_OBJ_FLAG_ENCODER_ACCEL define. (#7809)
philmoz Sep 20, 2026
02845d1
chore: bump version
pfeerick Sep 24, 2026
6eaa48c
fix(stm32): hangs during flashing firmware (#7415)
richardclli Oct 3, 2026
7b0bc59
fix(boot): use lcd-> in 2.11 colorlcd boot menu for #7415's flash err…
pfeerick Oct 3, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ project(EdgeTX)

set(VERSION_MAJOR "2")
set(VERSION_MINOR "11")
set(VERSION_REVISION "7")
set(VERSION_REVISION "8")
set(CODENAME "Jolly Mon")
string(TIMESTAMP BUILD_YEAR "%Y")

Expand Down
74 changes: 46 additions & 28 deletions radio/src/audio.h
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,11 @@ constexpr uint8_t AUDIO_FILENAME_MAXLEN = (AUDIO_LUA_FILENAME_MAXLEN > AUDIO_MOD
#define AUDIO_BUFFER_COUNT (3)
#endif

static_assert((AUDIO_QUEUE_LENGTH & (AUDIO_QUEUE_LENGTH - 1)) == 0,
"AUDIO_QUEUE_LENGTH must be a power of 2");
static_assert(2 * AUDIO_BUFFER_COUNT <= 255,
"AUDIO_BUFFER_COUNT free-running counter must fit in uint8_t");

#define BEEP_MIN_FREQ (150)
#define BEEP_MAX_FREQ (15000)
#define BEEP_DEFAULT_FREQ (2250)
Expand Down Expand Up @@ -300,23 +305,31 @@ class AudioBufferFifo {
#endif

private:
// free-running counters in [0, 2*AUDIO_BUFFER_COUNT); each written by only one side
volatile uint8_t readIdx;
volatile uint8_t writeIdx;
volatile bool bufferFull;

inline uint8_t nextBufferIdx(uint8_t idx) const
inline uint8_t nextIndex(uint8_t idx) const
{
return (idx >= AUDIO_BUFFER_COUNT - 1 ? 0 : idx + 1);
return (idx >= 2 * AUDIO_BUFFER_COUNT - 1 ? 0 : idx + 1);
}

bool full() const { return bufferFull; }
bool empty() const { return readIdx == writeIdx && !bufferFull; }
uint8_t used() const { return bufferFull ? AUDIO_BUFFER_COUNT
: (writeIdx < readIdx) ? writeIdx + AUDIO_BUFFER_COUNT - readIdx
: writeIdx - readIdx; }
inline uint8_t slot(uint8_t idx) const
{
return idx >= AUDIO_BUFFER_COUNT ? idx - AUDIO_BUFFER_COUNT : idx;
}

uint8_t used() const
{
uint8_t w = writeIdx, r = readIdx;
return w >= r ? w - r : w + 2 * AUDIO_BUFFER_COUNT - r;
}

bool full() const { return used() == AUDIO_BUFFER_COUNT; }
bool empty() const { return readIdx == writeIdx; }

public:
AudioBufferFifo() : readIdx(0), writeIdx(0), bufferFull(false)
AudioBufferFifo() : readIdx(0), writeIdx(0)
{
memset(audioBuffers, 0, sizeof(audioBuffers));
}
Expand All @@ -325,26 +338,19 @@ class AudioBufferFifo {
// with audioPushBuffer()
AudioBuffer *getEmptyBuffer() const
{
return full() ? nullptr : &audioBuffers[writeIdx];
return full() ? nullptr : &audioBuffers[slot(writeIdx)];
}

// puts filled buffer into FIFO
void audioPushBuffer()
{
writeIdx = nextBufferIdx(writeIdx);
if (writeIdx == readIdx) bufferFull = true;
}
void audioPushBuffer() { writeIdx = nextIndex(writeIdx); }

void freeNextFilledBuffer()
{
readIdx = nextBufferIdx(readIdx);
bufferFull = false;
}
// frees the last played buffer
void freeNextFilledBuffer() { readIdx = nextIndex(readIdx); }

// returns a pointer to the audio buffer to be played
const AudioBuffer *getNextFilledBuffer()
{
return empty() ? nullptr : &audioBuffers[readIdx];
return empty() ? nullptr : &audioBuffers[slot(readIdx)];
}

bool filledAtleast(int noBuffers) const { return used() >= noBuffers; }
Expand All @@ -356,13 +362,25 @@ class AudioFragmentFifo
friend void printAudioVars();
#endif
private:
// free-running counters in [0, 2*AUDIO_QUEUE_LENGTH); each written by only one side
volatile uint8_t ridx;
volatile uint8_t widx;
AudioFragment fragments[AUDIO_QUEUE_LENGTH];

uint8_t nextIdx(uint8_t idx) const
{
return (idx + 1) & (AUDIO_QUEUE_LENGTH - 1);
return (idx + 1) & (2 * AUDIO_QUEUE_LENGTH - 1);
}

uint8_t slot(uint8_t idx) const
{
return idx & (AUDIO_QUEUE_LENGTH - 1);
}

uint8_t used() const
{
uint8_t w = widx, r = ridx;
return w >= r ? w - r : w + 2 * AUDIO_QUEUE_LENGTH - r;
}

public:
Expand All @@ -372,7 +390,7 @@ class AudioFragmentFifo
{
uint8_t i = ridx;
while (i != widx) {
AudioFragment & fragment = fragments[i];
AudioFragment & fragment = fragments[slot(i)];
if (fragment.id == id) return true;
i = nextIdx(i);
}
Expand All @@ -383,7 +401,7 @@ class AudioFragmentFifo
{
uint8_t i = ridx;
while (i != widx) {
AudioFragment & fragment = fragments[i];
AudioFragment & fragment = fragments[slot(i)];
if (fragment.id == id) fragment.clear();
i = nextIdx(i);
}
Expand All @@ -397,7 +415,7 @@ class AudioFragmentFifo

bool full() const
{
return ridx == nextIdx(widx);
return used() == AUDIO_QUEUE_LENGTH;
}

void clear()
Expand All @@ -408,8 +426,8 @@ class AudioFragmentFifo
const AudioFragment * get()
{
if (!empty()) {
const AudioFragment * result = &fragments[ridx];
if (!fragments[ridx].repeat--) {
const AudioFragment * result = &fragments[slot(ridx)];
if (!fragments[slot(ridx)].repeat--) {
// repeat is done, move to the next fragment
ridx = nextIdx(ridx);
}
Expand All @@ -422,7 +440,7 @@ class AudioFragmentFifo
{
if (!full()) {
// TRACE("fragment %d at %d", fragment.type, widx);
fragments[widx] = fragment;
fragments[slot(widx)] = fragment;
widx = nextIdx(widx);
}
}
Expand Down
23 changes: 16 additions & 7 deletions radio/src/bootloader/bin_fw_files.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,14 @@ static FWFileInfo fwFiles[MAX_FW_FILES];
static uint8_t Block_buffer[BLOCK_LEN];
static UINT BlockCount;

static void flashWriteBlock()
static bool flashWriteBlock()
{
// TODO: use some board provided driver instead
uint32_t blockOffset = 0;
#if !defined(SIMU)
while (BlockCount) {
flashWrite((uint32_t *)firmwareAddress, (uint32_t *)&Block_buffer[blockOffset]);
if (!flashWrite((uint32_t *)firmwareAddress, (uint32_t *)&Block_buffer[blockOffset]))
return false;
blockOffset += FLASH_PAGESIZE;
firmwareAddress += FLASH_PAGESIZE;
if (BlockCount > FLASH_PAGESIZE) {
Expand All @@ -70,6 +71,7 @@ static void flashWriteBlock()
}
}
#endif // SIMU
return true;
}

void sdInit(void)
Expand Down Expand Up @@ -267,17 +269,24 @@ void firmwareInitWrite(uint32_t index)
firmwareWritten = 0;
}

bool firmwareWriteBlock(uint32_t* progress)
FlashWriteRes firmwareWriteBlock(uint32_t* progress)
{
flashWriteBlock();
if (!flashWriteBlock()) return FW_ERROR;

firmwareWritten += sizeof(Block_buffer);
*progress = (100 * firmwareWritten) / firmwareSize;

readFirmwareFile();
// a read error or a short file must not be reported as success
if (readFirmwareFile() != FR_OK) {
return FW_ERROR;
}
if (BlockCount == 0 && firmwareWritten < firmwareSize) {
return FW_ERROR;
}
if (BlockCount == 0 || firmwareWritten >= FLASHSIZE - BOOTLOADER_SIZE) {
return true;
return FW_DONE;
}

return false;
return FW_IN_PROGRESS;
}

1 change: 1 addition & 0 deletions radio/src/bootloader/boot.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ enum BootloaderState {
ST_FLASH_CHECK,
ST_FLASHING,
ST_FLASH_DONE,
ST_FLASH_ERROR,
ST_RESTORE_MENU,
ST_USB,
#if defined(SPI_FLASH)
Expand Down
18 changes: 12 additions & 6 deletions radio/src/bootloader/boot_menu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ void bootloaderMenu()
uint8_t index = 0;
FRESULT fr;
uint32_t nameCount = 0;
uint32_t progress = 0;

sdInit();

Expand All @@ -107,7 +108,8 @@ void bootloaderMenu()
next_frame += FRAME_INTERVAL_MS;

if (state != ST_USB && state != ST_FLASHING
&& state != ST_FLASH_DONE && state != ST_RADIO_MENU) {
&& state != ST_FLASH_DONE && state != ST_FLASH_ERROR
&& state != ST_RADIO_MENU) {
if (usbPlugged()) {
state = ST_USB;
#if !defined(SIMU)
Expand Down Expand Up @@ -244,14 +246,16 @@ void bootloaderMenu()
} else if (result == 1) {
// confirmed
firmwareInitWrite(vpos);
progress = 0;
state = ST_FLASHING;
}
} else if (state == ST_FLASHING) {
uint32_t progress = 0;
bool done = firmwareWriteBlock(&progress);
FlashWriteRes res = firmwareWriteBlock(&progress);
bootloaderDrawScreen(state, progress);
if(done) {
if (res == FW_DONE) {
state = ST_FLASH_DONE;
} else if (res == FW_ERROR) {
state = ST_FLASH_ERROR;
}
#if defined(SPI_FLASH)
} else if (state == ST_CLEAR_FLASH_CHECK) {
Expand Down Expand Up @@ -282,13 +286,15 @@ void bootloaderMenu()
}
}

if (state == ST_FLASH_DONE) {
if (state == ST_FLASH_DONE || state == ST_FLASH_ERROR) {
BootloaderState drawn = state;

if (event == EVT_KEY_BREAK(KEY_EXIT) || event == EVT_KEY_BREAK(KEY_ENTER)) {
state = ST_START;
vpos = 0;
}

bootloaderDrawScreen(state, 100);
bootloaderDrawScreen(drawn, drawn == ST_FLASH_DONE ? 100 : progress);
}

if (event == EVT_KEY_LONG(KEY_EXIT)) {
Expand Down
10 changes: 9 additions & 1 deletion radio/src/bootloader/firmware_files.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,15 @@ enum FlashCheckRes {
};
FlashCheckRes checkFirmwareFile(unsigned int index, FlashCheckRes res);

enum FlashWriteRes {
FW_IN_PROGRESS=0,
FW_DONE,
FW_ERROR
};

void firmwareInitWrite(uint32_t index);
bool firmwareEraseBlock(uint32_t* progress);
bool firmwareWriteBlock(uint32_t* progress);

// Write the block currently in the buffer, then fetch the next one
FlashWriteRes firmwareWriteBlock(uint32_t* progress);

21 changes: 16 additions & 5 deletions radio/src/cli.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1400,11 +1400,17 @@ void printAudioVars()
}
}

cliSerialPrint("FragmentFifo: ridx: %d, widx: %d",
audioQueue.fragmentsFifo.ridx, audioQueue.fragmentsFifo.widx);
cliSerialPrint("audioQueue: readIdx: %d, writeIdx: %d, full: %d",
audioQueue.buffersFifo.readIdx, audioQueue.buffersFifo.writeIdx,
audioQueue.buffersFifo.bufferFull);
cliSerialPrint("FragmentFifo: ridx: %d (slot %d), widx: %d (slot %d)",
audioQueue.fragmentsFifo.ridx,
audioQueue.fragmentsFifo.slot(audioQueue.fragmentsFifo.ridx),
audioQueue.fragmentsFifo.widx,
audioQueue.fragmentsFifo.slot(audioQueue.fragmentsFifo.widx));
cliSerialPrint("audioQueue: readIdx: %d (slot %d), writeIdx: %d (slot %d), full: %d",
audioQueue.buffersFifo.readIdx,
audioQueue.buffersFifo.slot(audioQueue.buffersFifo.readIdx),
audioQueue.buffersFifo.writeIdx,
audioQueue.buffersFifo.slot(audioQueue.buffersFifo.writeIdx),
audioQueue.buffersFifo.full());

cliSerialPrint("normalContext: %u",
(uint32_t)audioQueue.normalContext.fragment.type);
Expand Down Expand Up @@ -1465,6 +1471,11 @@ int cliDisplay(const char ** argv)
gettime(&utm);
cliSerialPrint("rtc = %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);
}
#if defined(VOLUME_I2C_ADDRESS)
else if (!strcmp(argv[1], "volume")) {
cliSerialPrint("volume = %d", getVolume());
}
#endif
else if (!strcmp(argv[1], "uid")) {
char str[LEN_CPU_UID+1];
getCPUUniqueID(str);
Expand Down
11 changes: 9 additions & 2 deletions radio/src/gui/colorlcd/boot_menu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -344,16 +344,20 @@ void bootloaderDrawScreen(BootloaderState st, int opt, const char* str)
lcd->drawText(USB_TXT_X, y + USB_PLG_TXT_YO, TR_BL_USB_CONNECTED, USB_TXT_ALIGN | BL_FOREGROUND);
}
else if (st == ST_FILE_LIST || st == ST_DIR_CHECK || st == ST_FLASH_CHECK ||
st == ST_FLASHING || st == ST_FLASH_DONE) {
st == ST_FLASHING || st == ST_FLASH_DONE || st == ST_FLASH_ERROR) {

bootloaderDrawTitle(LV_SYMBOL_SD_CARD " /FIRMWARE");

if (st == ST_FLASHING || st == ST_FLASH_DONE) {
if (st == ST_FLASHING || st == ST_FLASH_DONE || st == ST_FLASH_ERROR) {
LcdFlags color = BL_RED;

if (st == ST_FLASH_DONE) {
color = BL_GREEN;
opt = 100; // Completed > 100%
} else if (st == ST_FLASH_ERROR) {
// the bar stays where the write gave up
lcd->drawText(LCD_W / 2, (LCD_H - PROGRESS_H) / 2 - EdgeTxStyles::STD_FONT_HEIGHT,
LV_SYMBOL_CLOSE " " TR_BL_WRITING_FAILED, CENTERED | BL_FOREGROUND);
}

lcd->drawRect(PROGRESS_X, (LCD_H - PROGRESS_H) / 2, PROGRESS_W, PROGRESS_H, LINE_H, SOLID, BL_SELECTED);
Expand Down Expand Up @@ -408,6 +412,9 @@ void bootloaderDrawScreen(BootloaderState st, int opt, const char* str)
else if (st == ST_FLASH_DONE) {
pos = lcd->drawText(FOOTER_X1, LCD_H - FOOTER_Y1, LV_SYMBOL_CHARGE " " TR_BL_WRITING_COMPL, FOOTER_ALIGN1 | BL_FOREGROUND);
}
else if (st == ST_FLASH_ERROR) {
pos = lcd->drawText(FOOTER_X1, LCD_H - FOOTER_Y1, LV_SYMBOL_CHARGE " " TR_BL_RETRY_OR_DFU, FOOTER_ALIGN1 | BL_FOREGROUND);
}
}
#if LANDSCAPE
pos = 0;
Expand Down
2 changes: 0 additions & 2 deletions radio/src/gui/colorlcd/controls/color_editor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,6 @@ class ColorBar : public FormField
uint32_t maxValue = 0) :
FormField(parent, r)
{
lv_obj_add_flag(lvobj, LV_OBJ_FLAG_ENCODER_ACCEL);

lv_group_add_obj((lv_group_t*)lv_group_get_default(), lvobj);

lv_obj_add_event_cb(lvobj, ColorBar::pressing, LV_EVENT_PRESSING, nullptr);
Expand Down
Loading
Loading