diff --git a/radio/src/gui/common/stdlcd/radio_sdmanager.cpp b/radio/src/gui/common/stdlcd/radio_sdmanager.cpp index d86ebbb95..6587aabc2 100644 --- a/radio/src/gui/common/stdlcd/radio_sdmanager.cpp +++ b/radio/src/gui/common/stdlcd/radio_sdmanager.cpp @@ -88,7 +88,9 @@ void onSdFormatConfirm(const char * result) void onUpdateConfirmation(const char * result) { if (result == STR_OK) { - // TODO OTA Update + OtaUpdateInformation * destination = moduleState[EXTERNAL_MODULE].otaUpdateInformation; + Pxx2OtaUpdate otaUpdate(EXTERNAL_MODULE, destination->candidateReceiversNames[destination->selectedReceiverIndex]); + otaUpdate.flashFirmware(destination->filename); } else { moduleState[EXTERNAL_MODULE].mode = MODULE_MODE_NORMAL; @@ -97,14 +99,14 @@ void onUpdateConfirmation(const char * result) void onUpdateStateChanged() { - if (reusableBuffer.sdManager.otaInformation.step == BIND_INFO_REQUEST) { - POPUP_CONFIRMATION(PXX2receiversModels[reusableBuffer.sdManager.otaInformation.receiverInformation.modelID], onUpdateConfirmation); + if (reusableBuffer.sdManager.otaUpdateInformation.step == BIND_INFO_REQUEST) { + POPUP_CONFIRMATION(PXX2receiversModels[reusableBuffer.sdManager.otaUpdateInformation.receiverInformation.modelID], onUpdateConfirmation); char * tmp = strAppend(reusableBuffer.sdManager.otaReceiverVersion, TR_CURRENT_VERSION); - tmp = strAppendUnsigned(tmp, 1 + reusableBuffer.sdManager.otaInformation.receiverInformation.swVersion.major); + tmp = strAppendUnsigned(tmp, 1 + reusableBuffer.sdManager.otaUpdateInformation.receiverInformation.swVersion.major); *tmp++ = '.'; - tmp = strAppendUnsigned(tmp, reusableBuffer.sdManager.otaInformation.receiverInformation.swVersion.minor); + tmp = strAppendUnsigned(tmp, reusableBuffer.sdManager.otaUpdateInformation.receiverInformation.swVersion.minor); *tmp++ = '.'; - tmp = strAppendUnsigned(tmp, reusableBuffer.sdManager.otaInformation.receiverInformation.swVersion.revision); + tmp = strAppendUnsigned(tmp, reusableBuffer.sdManager.otaUpdateInformation.receiverInformation.swVersion.revision); SET_WARNING_INFO(reusableBuffer.sdManager.otaReceiverVersion, tmp - reusableBuffer.sdManager.otaReceiverVersion, 0); } } @@ -203,9 +205,9 @@ void onSdManagerMenu(const char * result) } #endif else if (result == STR_FLASH_RECEIVER_OTA) { - getSelectionFullPath(lfn); - memclear(&reusableBuffer.sdManager.otaInformation, sizeof(BindInformation)); - moduleState[EXTERNAL_MODULE].startBind(&reusableBuffer.sdManager.otaInformation, onUpdateStateChanged); + memclear(&reusableBuffer.sdManager.otaUpdateInformation, sizeof(OtaUpdateInformation)); + getSelectionFullPath(reusableBuffer.sdManager.otaUpdateInformation.filename); + moduleState[EXTERNAL_MODULE].startBind(&reusableBuffer.sdManager.otaUpdateInformation, onUpdateStateChanged); } #endif #if defined(LUA) @@ -219,10 +221,10 @@ void onSdManagerMenu(const char * result) void onUpdateReceiverSelection(const char * result) { if (result != STR_EXIT) { - reusableBuffer.sdManager.otaInformation.selectedReceiverIndex = (result - reusableBuffer.sdManager.otaInformation.candidateReceiversNames[0]) / sizeof(reusableBuffer.sdManager.otaInformation.candidateReceiversNames[0]); - reusableBuffer.sdManager.otaInformation.step = BIND_INFO_REQUEST; + reusableBuffer.sdManager.otaUpdateInformation.selectedReceiverIndex = (result - reusableBuffer.sdManager.otaUpdateInformation.candidateReceiversNames[0]) / sizeof(reusableBuffer.sdManager.otaUpdateInformation.candidateReceiversNames[0]); + reusableBuffer.sdManager.otaUpdateInformation.step = BIND_INFO_REQUEST; #if defined(SIMU) - reusableBuffer.sdManager.otaInformation.receiverInformation.modelID = 0x01; + reusableBuffer.sdManager.otaUpdateInformation.receiverInformation.modelID = 0x01; onUpdateStateChanged(); #endif } @@ -483,11 +485,11 @@ void menuRadioSdManager(event_t _event) } if (moduleState[EXTERNAL_MODULE].mode == MODULE_MODE_BIND) { - if (reusableBuffer.sdManager.otaInformation.step == BIND_INIT) { - if (reusableBuffer.sdManager.otaInformation.candidateReceiversCount > 0) { - popupMenuItemsCount = min(reusableBuffer.sdManager.otaInformation.candidateReceiversCount, PXX2_MAX_RECEIVERS_PER_MODULE); + if (reusableBuffer.sdManager.otaUpdateInformation.step == BIND_INIT) { + if (reusableBuffer.sdManager.otaUpdateInformation.candidateReceiversCount > 0) { + popupMenuItemsCount = min(reusableBuffer.sdManager.otaUpdateInformation.candidateReceiversCount, PXX2_MAX_RECEIVERS_PER_MODULE); for (uint8_t i=0; i 1 #define IS_RANGECHECK_ENABLE() (moduleState[0].mode == MODULE_MODE_RANGECHECK || moduleState[1].mode == MODULE_MODE_RANGECHECK) @@ -77,7 +78,8 @@ enum ModuleSettingsMode MODULE_MODE_BIND, MODULE_MODE_SHARE, MODULE_MODE_RANGECHECK, - MODULE_MODE_RESET + MODULE_MODE_RESET, + MODULE_MODE_OTA_UPDATE, }; @@ -104,35 +106,44 @@ PACK(struct ModuleInformation { } receivers[PXX2_MAX_RECEIVERS_PER_MODULE]; }); -struct ModuleSettings { - uint8_t state; // 0x00 = READ 0x40 = WRITE - tmr10ms_t retryTime; - uint8_t rfProtocol; - uint8_t externalAntenna; - int8_t txPower; +class ModuleSettings { + public: + uint8_t state; // 0x00 = READ 0x40 = WRITE + tmr10ms_t retryTime; + uint8_t rfProtocol; + uint8_t externalAntenna; + int8_t txPower; }; -struct ReceiverSettings { - uint8_t state; // 0x00 = READ 0x40 = WRITE - tmr10ms_t timeout; - uint8_t receiverId; - uint8_t dirty; - uint8_t telemetryDisabled; - uint8_t pwmRate; - uint8_t outputsCount; - uint8_t outputsMapping[24]; +class ReceiverSettings { + public: + uint8_t state; // 0x00 = READ 0x40 = WRITE + tmr10ms_t timeout; + uint8_t receiverId; + uint8_t dirty; + uint8_t telemetryDisabled; + uint8_t pwmRate; + uint8_t outputsCount; + uint8_t outputsMapping[24]; }; -struct BindInformation { - uint8_t step; - uint32_t timeout; - char candidateReceiversNames[PXX2_MAX_RECEIVERS_PER_MODULE][PXX2_LEN_RX_NAME + 1]; - uint8_t candidateReceiversCount; - uint8_t selectedReceiverIndex; - uint8_t rxUid; - uint8_t lbtMode; - uint8_t flexMode; - PXX2HardwareInformation receiverInformation; +class BindInformation { + public: + uint8_t step; + uint32_t timeout; + char candidateReceiversNames[PXX2_MAX_RECEIVERS_PER_MODULE][PXX2_LEN_RX_NAME + 1]; + uint8_t candidateReceiversCount; + uint8_t selectedReceiverIndex; + uint8_t rxUid; + uint8_t lbtMode; + uint8_t flexMode; + PXX2HardwareInformation receiverInformation; +}; + +class OtaUpdateInformation: public BindInformation { + public: + char filename[_MAX_LFN+1]; + uint32_t address; }; typedef void (* ModuleCallback)(); @@ -156,6 +167,7 @@ PACK(struct ModuleState { ModuleInformation * moduleInformation; ModuleSettings * moduleSettings; BindInformation * bindInformation; + OtaUpdateInformation * otaUpdateInformation; }; ModuleCallback callback; diff --git a/radio/src/pulses/pxx2.cpp b/radio/src/pulses/pxx2.cpp index c07795f28..c2b8cda00 100644 --- a/radio/src/pulses/pxx2.cpp +++ b/radio/src/pulses/pxx2.cpp @@ -287,8 +287,40 @@ void Pxx2Pulses::setupShareMode(uint8_t module) Pxx2Transport::addByte(reusableBuffer.moduleSetup.pxx2.shareReceiverIndex); } +void Pxx2Pulses::sendOtaUpdate(uint8_t module, const char * rxName, uint32_t address, const char * data) +{ + initFrame(); + + addFrameType(PXX2_TYPE_C_OTA, PXX2_TYPE_ID_OTA); + + if (rxName) { + Pxx2Transport::addByte(0x00); + for (uint8_t i=0; istep) { + if (elapsed++ > timeout) { + return false; + } + RTOS_WAIT_MS(1); + telemetryWakeup(); + } + + return true; +} + +const char * Pxx2OtaUpdate::doFlashFirmware(const char * filename) +{ + FIL file; + uint8_t buffer[32]; + UINT count = 0; + OtaUpdateInformation * destination = moduleState[module].otaUpdateInformation; + + destination->step = OTA_UPDATE_START; + extmodulePulsesData.pxx2.sendOtaUpdate(module, rxName, 0, nullptr); + if (!waitStep(OTA_UPDATE_START_ACK, 10)) { + return "OTA start failed"; + } + + if (f_open(&file, filename, FA_READ) != FR_OK) { + return "Opening file failed"; + } + + uint32_t size = f_size(&file); + uint32_t done = 0; + while (1) { + done += count; + drawProgressScreen(getBasename(filename), "OTA update...", done, size); + if (f_read(&file, buffer, sizeof(buffer), &count) != FR_OK) { + f_close(&file); + return "Reading file failed"; + } + destination->step = OTA_UPDATE_TRANSFER; + extmodulePulsesData.pxx2.sendOtaUpdate(module, nullptr, done, (char *)buffer); + if (!waitStep(OTA_UPDATE_TRANSFER_ACK, 10)) { + return "OTA transfer failed"; + } + if (count < sizeof(buffer)) { + f_close(&file); + break; + } + } + + destination->step = OTA_UPDATE_EOF; + extmodulePulsesData.pxx2.sendOtaUpdate(module, nullptr, done, nullptr); + if (!waitStep(OTA_UPDATE_END, 10)) { + return "OTA end failed"; + } + + return nullptr; +} + +void Pxx2OtaUpdate::flashFirmware(const char * filename) +{ + pausePulses(); + + watchdogSuspend(100); + RTOS_WAIT_MS(100); + + moduleState[module].mode = MODULE_MODE_OTA_UPDATE; + const char * result = doFlashFirmware(filename); + moduleState[module].mode = MODULE_MODE_NORMAL; + + AUDIO_PLAY(AU_SPECIAL_SOUND_BEEP1 ); + BACKLIGHT_ENABLE(); + + if (result) { + POPUP_WARNING(STR_FIRMWARE_UPDATE_ERROR); + SET_WARNING_INFO(result, strlen(result), 0); + } + else { + POPUP_INFORMATION(STR_FIRMWARE_UPDATE_SUCCESS); + } + + watchdogSuspend(100); + RTOS_WAIT_MS(100); + + resumePulses(); +} diff --git a/radio/src/pulses/pxx2.h b/radio/src/pulses/pxx2.h index 49227c62a..4a5ba5d55 100644 --- a/radio/src/pulses/pxx2.h +++ b/radio/src/pulses/pxx2.h @@ -41,6 +41,7 @@ #define PXX2_TYPE_ID_SPECTRUM 0x02 #define PXX2_TYPE_C_OTA 0xFE + #define PXX2_TYPE_ID_OTA 0x02 #define PXX2_CHANNELS_FLAG0_FAILSAFE (1 << 6) #define PXX2_CHANNELS_FLAG0_RANGECHECK (1 << 7) @@ -130,6 +131,15 @@ enum PXX2BindSteps { BIND_OK }; +enum PXX2OtaUpdateSteps { + OTA_UPDATE_START = BIND_OK + 1, + OTA_UPDATE_START_ACK, + OTA_UPDATE_TRANSFER, + OTA_UPDATE_TRANSFER_ACK, + OTA_UPDATE_EOF, + OTA_UPDATE_END +}; + enum PXX2ReceiverStatus { PXX2_HARDWARE_INFO, PXX2_SETTINGS_READ, @@ -178,6 +188,8 @@ class Pxx2Transport: public DataBuffer, public Pxx2CrcMixin { }; class Pxx2Pulses: public Pxx2Transport { + friend class Pxx2OtaUpdate; + public: void setupFrame(uint8_t module); @@ -204,6 +216,8 @@ class Pxx2Pulses: public Pxx2Transport { void setupPowerMeter(uint8_t module); + void sendOtaUpdate(uint8_t module, const char * rxName, uint32_t address, const char * data); + void addHead() { // send 7E, do not CRC @@ -265,4 +279,22 @@ class Pxx2Pulses: public Pxx2Transport { } }; +class Pxx2OtaUpdate { + public: + Pxx2OtaUpdate(uint8_t module, const char * rxName): + module(module), + rxName(rxName) + { + } + + void flashFirmware(const char * filename); + + protected: + uint8_t module; + const char * rxName; + + const char * doFlashFirmware(const char * filename); + bool waitStep(uint8_t step, uint8_t timeout); +}; + #endif diff --git a/radio/src/telemetry/frsky_pxx2.cpp b/radio/src/telemetry/frsky_pxx2.cpp index 674a19654..7e05082fb 100644 --- a/radio/src/telemetry/frsky_pxx2.cpp +++ b/radio/src/telemetry/frsky_pxx2.cpp @@ -239,6 +239,32 @@ void processPowerMeterFrame(uint8_t module, uint8_t * frame) } } +void processOtaUpdateFrame(uint8_t module, uint8_t * frame) +{ + if (moduleState[module].mode != MODULE_MODE_OTA_UPDATE) { + return; + } + + OtaUpdateInformation * destination = moduleState[module].otaUpdateInformation; + + if (destination->step == OTA_UPDATE_START) { + if (frame[3] == 0x00 && memcmp(&destination->candidateReceiversNames[destination->selectedReceiverIndex], &frame[4], PXX2_LEN_RX_NAME) == 0) { + destination->step = OTA_UPDATE_START_ACK; + } + } + else if (destination->step == OTA_UPDATE_TRANSFER) { + uint32_t address = *((uint32_t *)&frame[4]); + if (frame[3] == 0x01 && destination->address == address) { + destination->step = OTA_UPDATE_TRANSFER_ACK; + } + } + else if (destination->step == OTA_UPDATE_EOF) { + if (frame[3] == 0x02) { + destination->step = OTA_UPDATE_END; + } + } +} + void processModuleFrame(uint8_t module, uint8_t *frame) { switch (frame[2]) { @@ -296,6 +322,10 @@ void processPXX2Frame(uint8_t module, uint8_t *frame) processToolsFrame(module, frame); break; + case PXX2_TYPE_C_OTA: + processOtaUpdateFrame(module, frame); + break; + default: break; }