Skip to content

Commit 34be317

Browse files
authored
Merge pull request #350 from pennam/add-esp32-ota
Add esp32 OTA support
2 parents 6a2339a + e824e4c commit 34be317

File tree

13 files changed

+253
-82
lines changed

13 files changed

+253
-82
lines changed

.github/workflows/compile-examples.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,8 @@ jobs:
179179
# Install ESP32 platform via Boards Manager
180180
- name: esp32:esp32
181181
source-url: https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json
182-
libraries:
182+
libraries: |
183+
- name: Arduino_ESP32_OTA
183184
sketch-paths:
184185

185186
steps:

extras/tools/bin2ota.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@
3030
magic_number = 0x2341025F.to_bytes(4,byteorder='little')
3131
elif board == "OPTA":
3232
magic_number = 0x23410064.to_bytes(4,byteorder='little')
33+
# Magic number for all ESP32 boards not related to (VID/PID)
34+
elif board == "ESP32":
35+
magic_number = 0x45535033.to_bytes(4,byteorder='little')
3336
else:
3437
print ("Error,", board, "is not a supported board type")
3538
sys.exit()

src/AIoTC_Config.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,11 @@
108108
#define OTA_STORAGE_PORTENTA_QSPI (0)
109109
#endif
110110

111-
#if (OTA_STORAGE_SFU || OTA_STORAGE_SSU || OTA_STORAGE_SNU || OTA_STORAGE_PORTENTA_QSPI) && !defined(ARDUINO_AVR_UNO_WIFI_REV2)
111+
#if defined(ARDUINO_ARCH_ESP32)
112+
#define OTA_STORAGE_ESP (1)
113+
#endif
114+
115+
#if (OTA_STORAGE_SFU || OTA_STORAGE_SSU || OTA_STORAGE_SNU || OTA_STORAGE_PORTENTA_QSPI || OTA_STORAGE_ESP) && !defined(ARDUINO_AVR_UNO_WIFI_REV2)
112116
#define OTA_ENABLED (1)
113117
#else
114118
#define OTA_ENABLED (0)

src/ArduinoIoTCloudTCP.cpp

Lines changed: 31 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323

2424
#ifdef HAS_TCP
2525
#include <ArduinoIoTCloudTCP.h>
26+
2627
#ifdef BOARD_HAS_ECCX08
2728
#include "tls/BearSSLTrustAnchors.h"
2829
#include "tls/utility/CryptoUtil.h"
@@ -34,32 +35,18 @@
3435
#endif
3536

3637
#ifdef BOARD_HAS_OFFLOADED_ECCX08
37-
#include <ArduinoECCX08.h>
38-
#include "tls/utility/CryptoUtil.h"
38+
#include <ArduinoECCX08.h>
39+
#include "tls/utility/CryptoUtil.h"
3940
#endif
4041

41-
#ifdef BOARD_STM32H7
42-
# include "tls/utility/SHA256.h"
43-
# include <stm32h7xx_hal_rtc_ex.h>
44-
# include <WiFi.h>
42+
#if OTA_ENABLED
43+
#include "utility/ota/OTA.h"
4544
#endif
4645

47-
#include "utility/ota/OTA.h"
48-
#include "utility/ota/FlashSHA256.h"
4946
#include <algorithm>
5047
#include "cbor/CBOREncoder.h"
51-
5248
#include "utility/watchdog/Watchdog.h"
5349

54-
55-
/******************************************************************************
56-
* EXTERN
57-
******************************************************************************/
58-
59-
#ifdef BOARD_STM32H7
60-
extern RTC_HandleTypeDef RTCHandle;
61-
#endif
62-
6350
/******************************************************************************
6451
LOCAL MODULE FUNCTIONS
6552
******************************************************************************/
@@ -151,61 +138,8 @@ int ArduinoIoTCloudTCP::begin(bool const enable_watchdog, String brokerAddress,
151138
#endif /* AVR */
152139

153140
#if OTA_ENABLED && !defined(__AVR__)
154-
#if defined(BOARD_STM32H7)
155-
/* The length of the application can be retrieved the same way it was
156-
* communicated to the bootloader, that is by writing to the non-volatile
157-
* storage registers of the RTC.
158-
*/
159-
SHA256 sha256;
160-
uint32_t const app_start = 0x8040000;
161-
uint32_t const app_size = HAL_RTCEx_BKUPRead(&RTCHandle, RTC_BKP_DR3);
162-
163-
sha256.begin();
164-
uint32_t b = 0;
165-
uint32_t bytes_read = 0; for(uint32_t a = app_start;
166-
bytes_read < app_size;
167-
bytes_read += sizeof(b), a += sizeof(b))
168-
{
169-
/* Read the next chunk of memory. */
170-
memcpy(&b, reinterpret_cast<const void *>(a), sizeof(b));
171-
/* Feed it to SHA256. */
172-
sha256.update(reinterpret_cast<uint8_t *>(&b), sizeof(b));
173-
}
174-
/* Retrieve the final hash string. */
175-
uint8_t sha256_hash[SHA256::HASH_SIZE] = {0};
176-
sha256.finalize(sha256_hash);
177-
String sha256_str;
178-
std::for_each(sha256_hash,
179-
sha256_hash + SHA256::HASH_SIZE,
180-
[&sha256_str](uint8_t const elem)
181-
{
182-
char buf[4];
183-
snprintf(buf, 4, "%02X", elem);
184-
sha256_str += buf;
185-
});
186-
DEBUG_VERBOSE("SHA256: %d bytes (of %d) read", bytes_read, app_size);
187-
#elif defined(ARDUINO_ARCH_SAMD)
188-
/* Calculate the SHA256 checksum over the firmware stored in the flash of the
189-
* MCU. Note: As we don't know the length per-se we read chunks of the flash
190-
* until we detect one containing only 0xFF (= flash erased). This only works
191-
* for firmware updated via OTA and second stage bootloaders (SxU family)
192-
* because only those erase the complete flash before performing an update.
193-
* Since the SHA256 firmware image is only required for the cloud servers to
194-
* perform a version check after the OTA update this is a acceptable trade off.
195-
* The bootloader is excluded from the calculation and occupies flash address
196-
* range 0 to 0x2000, total flash size of 0x40000 bytes (256 kByte).
197-
*/
198-
String const sha256_str = FlashSHA256::calc(0x2000, 0x40000 - 0x2000);
199-
#elif defined(ARDUINO_NANO_RP2040_CONNECT)
200-
/* The maximum size of a RP2040 OTA update image is 1 MByte (that is 1024 *
201-
* 1024 bytes or 0x100'000 bytes).
202-
*/
203-
String const sha256_str = FlashSHA256::calc(XIP_BASE, 0x100000);
204-
#else
205-
# error "No method for SHA256 checksum calculation over application image defined for this architecture."
206-
#endif
207-
DEBUG_VERBOSE("SHA256: HASH(%d) = %s", strlen(sha256_str.c_str()), sha256_str.c_str());
208-
_ota_img_sha256 = sha256_str;
141+
_ota_img_sha256 = getOTAImageSHA256();
142+
DEBUG_VERBOSE("SHA256: HASH(%d) = %s", strlen(_ota_img_sha256.c_str()), _ota_img_sha256.c_str());
209143
#endif /* OTA_ENABLED */
210144

211145
#if defined(BOARD_HAS_ECCX08) || defined(BOARD_HAS_OFFLOADED_ECCX08) || defined(BOARD_HAS_SE050)
@@ -300,6 +234,11 @@ int ArduinoIoTCloudTCP::begin(bool const enable_watchdog, String brokerAddress,
300234
_ota_cap = true;
301235
#endif
302236

237+
#if defined(ARDUINO_ARCH_ESP32) && OTA_ENABLED
238+
/* NOTE: here is possible to check if current partition scheme is OTA compatible */
239+
_ota_cap = true;
240+
#endif
241+
303242
#ifdef BOARD_HAS_OFFLOADED_ECCX08
304243
if (String(WiFi.firmwareVersion()) < String("1.4.4")) {
305244
DEBUG_ERROR("ArduinoIoTCloudTCP::%s In order to connect to Arduino IoT Cloud, NINA firmware needs to be >= 1.4.4, current %s", __FUNCTION__, WiFi.firmwareVersion());
@@ -840,6 +779,25 @@ void ArduinoIoTCloudTCP::onOTARequest()
840779
bool const use_ethernet = _connection->getInterface() == NetworkAdapter::ETHERNET ? true : false;
841780
_ota_error = portenta_h7_onOTARequest(_ota_url.c_str(), use_ethernet);
842781
#endif
782+
783+
#ifdef ARDUINO_ARCH_ESP32
784+
_ota_error = esp32_onOTARequest(_ota_url.c_str());
785+
#endif
786+
}
787+
788+
String ArduinoIoTCloudTCP::getOTAImageSHA256()
789+
{
790+
#if defined (ARDUINO_ARCH_SAMD)
791+
return samd_getOTAImageSHA256();
792+
#elif defined (ARDUINO_NANO_RP2040_CONNECT)
793+
return rp2040_connect_getOTAImageSHA256();
794+
#elif defined (BOARD_STM32H7)
795+
return portenta_h7_getOTAImageSHA256();
796+
#elif defined (ARDUINO_ARCH_ESP32)
797+
return esp32_getOTAImageSHA256();
798+
#else
799+
# error "No method for SHA256 checksum calculation over application image defined for this architecture."
800+
#endif
843801
}
844802
#endif
845803

src/ArduinoIoTCloudTCP.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,7 @@ class ArduinoIoTCloudTCP: public ArduinoIoTCloudClass
202202

203203
#if OTA_ENABLED
204204
void onOTARequest();
205+
String getOTAImageSHA256();
205206
void sendDevicePropertyToCloud(String const name);
206207
#endif
207208

src/tls/bearssl/dec32be.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
*/
2424

2525
#include <AIoTC_Config.h>
26-
#if defined(BOARD_HAS_ECCX08) || defined(BOARD_HAS_OFFLOADED_ECCX08) || defined(BOARD_HAS_SE050)
26+
#if defined(BOARD_HAS_ECCX08) || defined(BOARD_HAS_OFFLOADED_ECCX08) || defined(BOARD_HAS_SE050) || defined(ARDUINO_ARCH_ESP32)
2727

2828
#include "inner.h"
2929

src/tls/bearssl/enc32be.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
*/
2424

2525
#include <AIoTC_Config.h>
26-
#if defined(BOARD_HAS_ECCX08) || defined(BOARD_HAS_OFFLOADED_ECCX08) || defined(BOARD_HAS_SE050)
26+
#if defined(BOARD_HAS_ECCX08) || defined(BOARD_HAS_OFFLOADED_ECCX08) || defined(BOARD_HAS_SE050) || defined(ARDUINO_ARCH_ESP32)
2727

2828
#include "inner.h"
2929

src/tls/bearssl/sha2small.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
*/
2424

2525
#include <AIoTC_Config.h>
26-
#if defined(BOARD_HAS_ECCX08) || defined(BOARD_HAS_OFFLOADED_ECCX08) || defined(BOARD_HAS_SE050)
26+
#if defined(BOARD_HAS_ECCX08) || defined(BOARD_HAS_OFFLOADED_ECCX08) || defined(BOARD_HAS_SE050) || defined(ARDUINO_ARCH_ESP32)
2727

2828
#include "inner.h"
2929

src/utility/ota/OTA-esp32.cpp

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
/*
2+
This file is part of ArduinoIoTCloud.
3+
4+
Copyright 2020 ARDUINO SA (http://www.arduino.cc/)
5+
6+
This software is released under the GNU General Public License version 3,
7+
which covers the main part of arduino-cli.
8+
The terms of this license can be found at:
9+
https://www.gnu.org/licenses/gpl-3.0.en.html
10+
11+
You can be released from the requirements of the above licenses by purchasing
12+
a commercial license. Buying such a license is mandatory if you want to modify or
13+
otherwise use the software for commercial activities involving the Arduino
14+
software without disclosing the source code of your own applications. To purchase
15+
a commercial license, send an email to license@arduino.cc.
16+
*/
17+
18+
/******************************************************************************
19+
* INCLUDE
20+
******************************************************************************/
21+
22+
#include <AIoTC_Config.h>
23+
24+
#if defined ARDUINO_ARCH_ESP32 && OTA_ENABLED
25+
26+
#include "OTA.h"
27+
#include <Arduino_DebugUtils.h>
28+
#include <Arduino_ESP32_OTA.h>
29+
#include "tls/utility/SHA256.h"
30+
31+
#include <esp_ota_ops.h>
32+
33+
/******************************************************************************
34+
* FUNCTION DEFINITION
35+
******************************************************************************/
36+
37+
int esp32_onOTARequest(char const * ota_url)
38+
{
39+
Arduino_ESP32_OTA::Error ota_err = Arduino_ESP32_OTA::Error::None;
40+
Arduino_ESP32_OTA ota;
41+
42+
/* Initialize the board for OTA handling. */
43+
if ((ota_err = ota.begin()) != Arduino_ESP32_OTA::Error::None)
44+
{
45+
DEBUG_ERROR("Arduino_ESP32_OTA::begin() failed with %d", static_cast<int>(ota_err));
46+
return static_cast<int>(ota_err);
47+
}
48+
49+
/* Download the OTA file from the web storage location. */
50+
int const ota_download = ota.download(ota_url);
51+
if (ota_download <= 0)
52+
{
53+
DEBUG_ERROR("Arduino_ESP_OTA::download() failed with %d", ota_download);
54+
return ota_download;
55+
}
56+
DEBUG_VERBOSE("Arduino_ESP_OTA::download() %d bytes downloaded", static_cast<int>(ota_download));
57+
58+
/* Verify update integrity and apply */
59+
if ((ota_err = ota.update()) != Arduino_ESP32_OTA::Error::None)
60+
{
61+
DEBUG_ERROR("Arduino_ESP_OTA::update() failed with %d", static_cast<int>(ota_err));
62+
return static_cast<int>(ota_err);
63+
}
64+
65+
/* Perform the reset to reboot */
66+
ota.reset();
67+
68+
return static_cast<int>(OTAError::None);
69+
}
70+
71+
String esp32_getOTAImageSHA256()
72+
{
73+
const esp_partition_t *running = esp_ota_get_running_partition();
74+
if (!running) {
75+
DEBUG_ERROR("ESP32::SHA256 Running partition could not be found");
76+
return String();
77+
}
78+
79+
uint8_t *b = (uint8_t*)malloc(SPI_FLASH_SEC_SIZE);
80+
if(b == nullptr) {
81+
DEBUG_ERROR("ESP32::SHA256 Not enough memory to allocate buffer");
82+
return String();
83+
}
84+
85+
SHA256 sha256;
86+
uint32_t const app_start = running->address;
87+
uint32_t const app_size = ESP.getSketchSize();
88+
uint32_t read_bytes = 0;
89+
90+
sha256.begin();
91+
for(uint32_t a = app_start; read_bytes < app_size; )
92+
{
93+
/* Check if we are reading last sector and compute used size */
94+
uint32_t const read_size = read_bytes + SPI_FLASH_SEC_SIZE < app_size ? SPI_FLASH_SEC_SIZE : app_size - read_bytes;
95+
96+
/* Use always 4 bytes aligned reads */
97+
if (!ESP.flashRead(a, reinterpret_cast<uint32_t*>(b), (read_size + 3) & ~3)) {
98+
DEBUG_ERROR("ESP32::SHA256 Could not read data from flash");
99+
return String();
100+
}
101+
sha256.update(b, read_size);
102+
a += read_size;
103+
read_bytes += read_size;
104+
}
105+
free(b);
106+
107+
/* Retrieve the final hash string. */
108+
uint8_t sha256_hash[SHA256::HASH_SIZE] = {0};
109+
sha256.finalize(sha256_hash);
110+
String sha256_str;
111+
std::for_each(sha256_hash,
112+
sha256_hash + SHA256::HASH_SIZE,
113+
[&sha256_str](uint8_t const elem)
114+
{
115+
char buf[4];
116+
snprintf(buf, 4, "%02X", elem);
117+
sha256_str += buf;
118+
});
119+
DEBUG_VERBOSE("SHA256: %d bytes (of %d) read", read_bytes, app_size);
120+
return sha256_str;
121+
}
122+
123+
#endif /* ARDUINO_ARCH_ESP32 */

src/utility/ota/OTA-nano-rp2040.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#include "mbed.h"
3333
#include "FATFileSystem.h"
3434
#include "FlashIAPBlockDevice.h"
35+
#include "utility/ota/FlashSHA256.h"
3536

3637
/******************************************************************************
3738
* FUNCTION DEFINITION
@@ -249,4 +250,12 @@ int rp2040_connect_onOTARequest(char const * ota_url)
249250
return static_cast<int>(OTAError::None);
250251
}
251252

253+
String rp2040_connect_getOTAImageSHA256()
254+
{
255+
/* The maximum size of a RP2040 OTA update image is 1 MByte (that is 1024 *
256+
* 1024 bytes or 0x100'000 bytes).
257+
*/
258+
return FlashSHA256::calc(XIP_BASE, 0x100000);
259+
}
260+
252261
#endif /* ARDUINO_NANO_RP2040_CONNECT */

0 commit comments

Comments
 (0)