Skip to content

Uno R4 WiFi OTA update #376

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Sep 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 3 additions & 1 deletion extras/tools/bin2ota.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

if len(sys.argv) != 4:
print ("Usage: bin2ota.py BOARD sketch.bin sketch.ota")
print (" BOARD = [ MKR_WIFI_1010 | NANO_33_IOT | PORTENTA_H7_M7 | NANO_RP2040_CONNECT | NICLA_VISION | OPTA | GIGA | NANO_ESP32 ]")
print (" BOARD = [ MKR_WIFI_1010 | NANO_33_IOT | PORTENTA_H7_M7 | NANO_RP2040_CONNECT | NICLA_VISION | OPTA | GIGA | NANO_ESP32 | ESP32 | UNOR4WIFI]")
sys.exit()

board = sys.argv[1]
Expand Down Expand Up @@ -37,6 +37,8 @@
# Magic number for all ESP32 boards not related to (VID/PID)
elif board == "ESP32":
magic_number = 0x45535033.to_bytes(4,byteorder='little')
elif board == "UNOR4WIFI":
magic_number = 0x23411002.to_bytes(4,byteorder='little')
else:
print ("Error,", board, "is not a supported board type")
sys.exit()
Expand Down
2 changes: 1 addition & 1 deletion src/AIoTC_Config.h
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@
#define OTA_STORAGE_PORTENTA_QSPI (0)
#endif

#if defined(ARDUINO_ARCH_ESP32)
#if defined(ARDUINO_ARCH_ESP32) || defined(ARDUINO_UNOR4_WIFI)
#define OTA_STORAGE_ESP (1)
#endif

Expand Down
4 changes: 3 additions & 1 deletion src/tls/bearssl/dec32be.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@
*/

#include <AIoTC_Config.h>
#if defined(BOARD_HAS_ECCX08) || defined(BOARD_HAS_OFFLOADED_ECCX08) || defined(BOARD_HAS_SE050) || defined(ARDUINO_ARCH_ESP32)
#if defined(BOARD_HAS_ECCX08) || defined(BOARD_HAS_OFFLOADED_ECCX08) || \
defined(BOARD_HAS_SE050) || defined(ARDUINO_ARCH_ESP32) || \
defined(ARDUINO_UNOR4_WIFI)

#include "inner.h"

Expand Down
4 changes: 3 additions & 1 deletion src/tls/bearssl/enc32be.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@
*/

#include <AIoTC_Config.h>
#if defined(BOARD_HAS_ECCX08) || defined(BOARD_HAS_OFFLOADED_ECCX08) || defined(BOARD_HAS_SE050) || defined(ARDUINO_ARCH_ESP32)
#if defined(BOARD_HAS_ECCX08) || defined(BOARD_HAS_OFFLOADED_ECCX08) || \
defined(BOARD_HAS_SE050) || defined(ARDUINO_ARCH_ESP32) || \
defined(ARDUINO_UNOR4_WIFI)

#include "inner.h"

Expand Down
4 changes: 3 additions & 1 deletion src/tls/bearssl/sha2small.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@
*/

#include <AIoTC_Config.h>
#if defined(BOARD_HAS_ECCX08) || defined(BOARD_HAS_OFFLOADED_ECCX08) || defined(BOARD_HAS_SE050) || defined(ARDUINO_ARCH_ESP32)
#if defined(BOARD_HAS_ECCX08) || defined(BOARD_HAS_OFFLOADED_ECCX08) || \
defined(BOARD_HAS_SE050) || defined(ARDUINO_ARCH_ESP32) || \
defined(ARDUINO_UNOR4_WIFI)

#include "inner.h"

Expand Down
187 changes: 187 additions & 0 deletions src/utility/ota/OTA-unor4.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
/*
This file is part of ArduinoIoTCloud.

Copyright 2020 ARDUINO SA (http://www.arduino.cc/)

This software is released under the GNU General Public License version 3,
which covers the main part of arduino-cli.
The terms of this license can be found at:
https://www.gnu.org/licenses/gpl-3.0.en.html

You can be released from the requirements of the above licenses by purchasing
a commercial license. Buying such a license is mandatory if you want to modify or
otherwise use the software for commercial activities involving the Arduino
software without disclosing the source code of your own applications. To purchase
a commercial license, send an email to [email protected].
*/

/******************************************************************************
* INCLUDE
******************************************************************************/

#include <AIoTC_Config.h>

#if defined ARDUINO_UNOR4_WIFI && OTA_ENABLED

#include "OTAUpdate.h"
#include <Arduino_DebugUtils.h>
#include "tls/utility/SHA256.h"
#include "fsp_common_api.h"
#include "r_flash_lp.h"
#include "WiFiS3.h"

/******************************************************************************
* DEFINES
******************************************************************************/

/* Key code for writing PRCR register. */
#define BSP_PRV_PRCR_KEY (0xA500U)
#define BSP_PRV_PRCR_PRC1_UNLOCK ((BSP_PRV_PRCR_KEY) | 0x2U)
#define BSP_PRV_PRCR_LOCK ((BSP_PRV_PRCR_KEY) | 0x0U)

#define OTA_MAGIC (*((volatile uint16_t *) &R_SYSTEM->VBTBKR[4]))
#define OTA_SIZE (*((volatile uint32_t *) &R_SYSTEM->VBTBKR[6]))

/******************************************************************************
* FUNCTION DEFINITION
******************************************************************************/

static void unor4_setOTASize(uint32_t size)
{
R_SYSTEM->PRCR = (uint16_t) BSP_PRV_PRCR_PRC1_UNLOCK;
OTA_MAGIC = 0x07AA;
OTA_SIZE = size;
R_SYSTEM->PRCR = (uint16_t) BSP_PRV_PRCR_LOCK;
}

static uint32_t unor4_getOTASize()
{
if (OTA_MAGIC == 0x07AA)
{
return OTA_SIZE;
}
return 0;
}

static int unor4_codeFlashOpen(flash_lp_instance_ctrl_t * ctrl)
{
flash_cfg_t cfg;

cfg.data_flash_bgo = false;
cfg.p_callback = nullptr;
cfg.p_context = nullptr;
cfg.p_extend = nullptr;
cfg.ipl = (BSP_IRQ_DISABLED);
cfg.irq = FSP_INVALID_VECTOR;
cfg.err_ipl = (BSP_IRQ_DISABLED);
cfg.err_irq = FSP_INVALID_VECTOR;

fsp_err_t rv = FSP_ERR_UNSUPPORTED;

rv = R_FLASH_LP_Open(ctrl,&cfg);
return rv;
}

static int unor4_codeFlashClose(flash_lp_instance_ctrl_t * ctrl)
{
fsp_err_t rv = FSP_ERR_UNSUPPORTED;

rv = R_FLASH_LP_Close(ctrl);
return rv;
}

int unor4_onOTARequest(char const * ota_url)
{
OTAUpdate::Error ota_err = OTAUpdate::Error::None;
OTAUpdate ota;

/* Initialize the board for OTA handling. */
if ((ota_err = ota.begin("/update.bin")) != OTAUpdate::Error::None)
{
DEBUG_ERROR("OTAUpdate::begin() failed with %d", static_cast<int>(ota_err));
return static_cast<int>(ota_err);
}

/* Download the OTA file from the web storage location. */
int const ota_download = ota.download(ota_url,"/update.bin");
if (ota_download <= 0)
{
DEBUG_ERROR("OTAUpdate::download() failed with %d", ota_download);
return ota_download;
}
DEBUG_VERBOSE("OTAUpdate::download() %d bytes downloaded", static_cast<int>(ota_download));

/* Verify update integrity */
if ((ota_err = ota.verify()) != OTAUpdate::Error::None)
{
DEBUG_ERROR("OTAUpdate::verify() failed with %d", static_cast<int>(ota_err));
return static_cast<int>(ota_err);
}

/* Store update size and write OTA magin number */
unor4_setOTASize(ota_download);

/* Flash new firmware */
if ((ota_err = ota.update("/update.bin")) != OTAUpdate::Error::None)
{
DEBUG_ERROR("OTAUpdate::update() failed with %d", static_cast<int>(ota_err));
return static_cast<int>(ota_err);
}

return static_cast<int>(OTAUpdate::Error::None);
}

String unor4_getOTAImageSHA256()
{
/* The length of the application can be retrieved the same way it was
* communicated to the bootloader, that is by writing to the non-volatile
* storage registers of the RTC.
*/
SHA256 sha256;
uint32_t const app_start = 0x4000;
uint32_t const app_size = unor4_getOTASize();

flash_lp_instance_ctrl_t ctrl;
unor4_codeFlashOpen(&ctrl);

sha256.begin();
uint32_t b = 0;
uint32_t bytes_read = 0; for(uint32_t a = app_start;
bytes_read < app_size;
bytes_read += sizeof(b), a += sizeof(b))
{
/* Read the next chunk of memory. */
memcpy(&b, reinterpret_cast<const void *>(a), sizeof(b));
/* Feed it to SHA256. */
sha256.update(reinterpret_cast<uint8_t *>(&b), sizeof(b));
}

unor4_codeFlashClose(&ctrl);

/* Retrieve the final hash string. */
uint8_t sha256_hash[SHA256::HASH_SIZE] = {0};
sha256.finalize(sha256_hash);
String sha256_str;
std::for_each(sha256_hash,
sha256_hash + SHA256::HASH_SIZE,
[&sha256_str](uint8_t const elem)
{
char buf[4];
snprintf(buf, 4, "%02X", elem);
sha256_str += buf;
});
DEBUG_ERROR("SHA256: %d bytes (of %d) read", bytes_read, app_size);
return sha256_str;
}

bool unor4_isOTACapable()
{
/* check firmware version */
String const fv = WiFi.firmwareVersion();
if (fv < String("0.3.0")) {
return false;
}
return true;
}

#endif /* ARDUINO_UNOR4_WIFI */
13 changes: 13 additions & 0 deletions src/utility/ota/OTA.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,12 @@ String esp32_getOTAImageSHA256();
bool esp32_isOTACapable();
#endif

#ifdef ARDUINO_UNOR4_WIFI
int unor4_onOTARequest(char const * url);
String unor4_getOTAImageSHA256();
bool unor4_isOTACapable();
#endif

/******************************************************************************
* PUBLIC MEMBER FUNCTIONS
******************************************************************************/
Expand All @@ -74,6 +80,9 @@ int OTA::onRequest(String url, NetworkAdapter iface)
#elif defined (ARDUINO_ARCH_ESP32)
(void)iface;
return esp32_onOTARequest(url.c_str());
#elif defined (ARDUINO_UNOR4_WIFI)
(void)iface;
return unor4_onOTARequest(url.c_str());
#else
#error "OTA not supported for this architecture"
#endif
Expand All @@ -89,6 +98,8 @@ String OTA::getImageSHA256()
return portenta_h7_getOTAImageSHA256();
#elif defined (ARDUINO_ARCH_ESP32)
return esp32_getOTAImageSHA256();
#elif defined (ARDUINO_UNOR4_WIFI)
return unor4_getOTAImageSHA256();
#else
#error "No method for SHA256 checksum calculation over application image defined for this architecture."
#endif
Expand All @@ -104,6 +115,8 @@ bool OTA::isCapable()
return portenta_h7_isOTACapable();
#elif defined (ARDUINO_ARCH_ESP32)
return esp32_isOTACapable();
#elif defined (ARDUINO_UNOR4_WIFI)
return unor4_isOTACapable();
#else
#error "OTA not supported for this architecture"
#endif
Expand Down