Skip to content

Commit 524094f

Browse files
committed
feat: Notecard OTA Support
1 parent 2903300 commit 524094f

15 files changed

+702
-53
lines changed

src/AIoTC_Config.h

+34-34
Original file line numberDiff line numberDiff line change
@@ -60,40 +60,6 @@
6060

6161
#if !defined(HAS_NOTECARD)
6262

63-
#if defined(ARDUINO_SAMD_MKRWIFI1010) || defined(ARDUINO_SAMD_NANO_33_IOT)
64-
#define OTA_STORAGE_SNU (1)
65-
#else
66-
#define OTA_STORAGE_SNU (0)
67-
#endif
68-
69-
#if defined(ARDUINO_NANO_RP2040_CONNECT)
70-
#define OTA_STORAGE_SFU (1)
71-
#else
72-
#define OTA_STORAGE_SFU (0)
73-
#endif
74-
75-
#ifdef ARDUINO_SAMD_MKRGSM1400
76-
#define OTA_STORAGE_SSU (1) // OTA_STORAGE_SSU is not implemented yet in OTASamd
77-
#else
78-
#define OTA_STORAGE_SSU (0)
79-
#endif
80-
81-
#if defined(ARDUINO_PORTENTA_H7_M7) || defined(ARDUINO_NICLA_VISION) || defined(ARDUINO_OPTA) || defined(ARDUINO_GIGA)
82-
#define OTA_STORAGE_PORTENTA_QSPI (1)
83-
#else
84-
#define OTA_STORAGE_PORTENTA_QSPI (0)
85-
#endif
86-
87-
#if defined(ARDUINO_ARCH_ESP32) || defined(ARDUINO_UNOR4_WIFI)
88-
#define OTA_STORAGE_ESP (1)
89-
#endif
90-
91-
#if (OTA_STORAGE_SFU || OTA_STORAGE_SNU || OTA_STORAGE_PORTENTA_QSPI || OTA_STORAGE_ESP)
92-
#define OTA_ENABLED (1)
93-
#else
94-
#define OTA_ENABLED (0)
95-
#endif
96-
9763
#if defined(ARDUINO_SAMD_MKRGSM1400) || defined(ARDUINO_SAMD_MKR1000) || \
9864
defined(ARDUINO_SAMD_MKRNB1500) || defined(ARDUINO_PORTENTA_H7_M7) || \
9965
defined (ARDUINO_NANO_RP2040_CONNECT) || defined(ARDUINO_OPTA) || \
@@ -143,6 +109,40 @@
143109

144110
#endif // HAS_NOTECARD
145111

112+
#if defined(ARDUINO_SAMD_MKRWIFI1010) || defined(ARDUINO_SAMD_NANO_33_IOT)
113+
#define OTA_STORAGE_SNU (1)
114+
#else
115+
#define OTA_STORAGE_SNU (0)
116+
#endif
117+
118+
#if defined(ARDUINO_NANO_RP2040_CONNECT)
119+
#define OTA_STORAGE_SFU (1)
120+
#else
121+
#define OTA_STORAGE_SFU (0)
122+
#endif
123+
124+
#ifdef ARDUINO_SAMD_MKRGSM1400
125+
#define OTA_STORAGE_SSU (1) // OTA_STORAGE_SSU is not implemented yet in OTASamd
126+
#else
127+
#define OTA_STORAGE_SSU (0)
128+
#endif
129+
130+
#if defined(ARDUINO_PORTENTA_H7_M7) || defined(ARDUINO_NICLA_VISION) || defined(ARDUINO_OPTA) || defined(ARDUINO_GIGA)
131+
#define OTA_STORAGE_PORTENTA_QSPI (1)
132+
#else
133+
#define OTA_STORAGE_PORTENTA_QSPI (0)
134+
#endif
135+
136+
#if defined(ARDUINO_ARCH_ESP32) || defined(ARDUINO_UNOR4_WIFI)
137+
#define OTA_STORAGE_ESP (1)
138+
#endif
139+
140+
#if (OTA_STORAGE_SFU || OTA_STORAGE_SNU || OTA_STORAGE_PORTENTA_QSPI || OTA_STORAGE_ESP)
141+
#define OTA_ENABLED (1)
142+
#else
143+
#define OTA_ENABLED (0)
144+
#endif
145+
146146
#if defined(ARDUINO_PORTENTA_H7_M7) || defined(ARDUINO_NICLA_VISION) || defined(ARDUINO_OPTA) || defined(ARDUINO_GIGA)
147147
#define BEAR_SSL_CLIENT_IBUF_SIZE (16384 + 325) // Allows download from storage API
148148
#define BOARD_STM32H7

src/ArduinoIoTCloudNotecard.cpp

+9
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,10 @@ ArduinoIoTCloudNotecard::ArduinoIoTCloudNotecard()
6666
,_notecard_polling_interval_ms{DEFAULT_READ_INTERVAL_MS}
6767
,_interrupt_pin{-1}
6868
,_data_available{false}
69+
#if OTA_ENABLED
70+
,_ota(&_message_stream)
71+
,_get_ota_confirmation{nullptr}
72+
#endif
6973
{
7074

7175
}
@@ -99,6 +103,11 @@ int ArduinoIoTCloudNotecard::begin(ConnectionHandler &connection_, int interrupt
99103
// Begin the Notecard time service
100104
_time_service.begin(&connection_);
101105

106+
#if OTA_ENABLED
107+
// Configure the OTA interface
108+
_ota.setConnection(&connection_);
109+
#endif
110+
102111
// Setup retry timers
103112
_connection_attempt.begin(AIOT_CONFIG_RECONNECTION_RETRY_DELAY_ms, AIOT_CONFIG_MAX_RECONNECTION_RETRY_DELAY_ms);
104113

src/ArduinoIoTCloudNotecard.h

+29
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@
1919
#include "ArduinoIoTCloudThing.h"
2020
#include "ArduinoIoTCloudDevice.h"
2121

22+
#if OTA_ENABLED
23+
#include "ota/OTA.h"
24+
#endif /* OTA_ENABLED */
25+
2226
/******************************************************************************
2327
* DEFINES
2428
******************************************************************************/
@@ -33,6 +37,10 @@
3337
* TYPEDEF
3438
******************************************************************************/
3539

40+
#if OTA_ENABLED
41+
typedef bool (*onOTARequestCallbackFunc)(void);
42+
#endif /* OTA_ENABLED */
43+
3644
/******************************************************************************
3745
* CLASS DECLARATION
3846
******************************************************************************/
@@ -82,6 +90,22 @@ class ArduinoIoTCloudNotecard : public ArduinoIoTCloudClass
8290
*/
8391
inline void setNotecardPollingInterval(uint32_t interval_ms) { _notecard_polling_interval_ms = ((interval_ms < 250) ? 250 : interval_ms); }
8492

93+
#if OTA_ENABLED
94+
/* The callback is triggered when the OTA is initiated and it gets executed until _ota_req flag is cleared.
95+
* It should return true when the OTA can be applied or false otherwise.
96+
* See example ArduinoIoTCloud-DeferredOTA.ino
97+
*/
98+
inline void onOTARequestCb(onOTARequestCallbackFunc cb) {
99+
_get_ota_confirmation = cb;
100+
101+
if(_get_ota_confirmation) {
102+
_ota.setOtaPolicies(OTACloudProcessInterface::ApprovalRequired);
103+
} else {
104+
_ota.setOtaPolicies(OTACloudProcessInterface::None);
105+
}
106+
}
107+
#endif /* OTA_ENABLED */
108+
85109
private:
86110

87111
enum class State
@@ -104,6 +128,11 @@ class ArduinoIoTCloudNotecard : public ArduinoIoTCloudClass
104128
int _interrupt_pin;
105129
volatile bool _data_available;
106130

131+
#if OTA_ENABLED
132+
ArduinoCloudOTA _ota;
133+
onOTARequestCallbackFunc _get_ota_confirmation;
134+
#endif /* OTA_ENABLED */
135+
107136
inline virtual PropertyContainer &getThingPropertyContainer() override { return _thing.getPropertyContainer(); }
108137

109138
State handle_ConnectPhy();

src/ota/implementation/OTAEsp32.h

+4
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,11 @@
1010

1111
#pragma once
1212

13+
#ifndef HAS_NOTECARD
1314
#include "ota/interface/OTAInterfaceDefault.h"
15+
#else
16+
#include "ota/interface/OTAInterfaceNotecard.h"
17+
#endif
1418

1519
class ESP32OTACloudProcess: public OTADefaultCloudProcessInterface {
1620
public:

src/ota/implementation/OTANanoRP2040.h

+4
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,11 @@
1010

1111
#pragma once
1212

13+
#ifndef HAS_NOTECARD
1314
#include "ota/interface/OTAInterfaceDefault.h"
15+
#else
16+
#include "ota/interface/OTAInterfaceNotecard.h"
17+
#endif
1418

1519
#include "FATFileSystem.h"
1620
#include "FlashIAPBlockDevice.h"

src/ota/implementation/OTASTM32H7.h

+6-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,12 @@
99
*/
1010

1111
#pragma once
12+
13+
#ifndef HAS_NOTECARD
1214
#include "ota/interface/OTAInterfaceDefault.h"
15+
#else
16+
#include "ota/interface/OTAInterfaceNotecard.h"
17+
#endif
1318

1419
#include <QSPIFBlockDevice.h>
1520

@@ -47,7 +52,7 @@ class STM32H7OTACloudProcess: public OTADefaultCloudProcessInterface {
4752
// we are overriding the method of startOTA in order to open the destination file for the ota download
4853
virtual OTACloudProcessInterface::State startOTA() override;
4954

50-
// whene the download is correctly finished we set the mcu to use the newly downloaded binary
55+
// when the download is correctly finished we set the mcu to use the newly downloaded binary
5156
virtual OTACloudProcessInterface::State flashOTA() override;
5257

5358
// we reboot the device

src/ota/implementation/OTASamd.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,10 @@
1010

1111
#pragma once
1212

13-
#include "ota/interface/OTAInterface.h"
1413
#include <Arduino_DebugUtils.h>
1514

15+
#include "ota/interface/OTAInterface.h"
16+
1617
class SAMDOTACloudProcess: public OTACloudProcessInterface {
1718
public:
1819
SAMDOTACloudProcess(MessageStream *ms);

src/ota/interface/OTAInterface.h

+2
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
/******************************************************************************
2727
* CLASS DECLARATION
2828
******************************************************************************/
29+
class ConnectionHandler; // Forward declaration
2930

3031
class OTACloudProcessInterface: public CloudProcess {
3132
public:
@@ -86,6 +87,7 @@ class OTACloudProcessInterface: public CloudProcess {
8687
virtual void handleMessage(Message*);
8788
// virtual CloudProcess::State getState();
8889
// virtual void hook(State s, void* action);
90+
inline virtual void setConnection(ConnectionHandler * connection) { (void)connection; }
8991
virtual void update() { handleMessage(nullptr); }
9092

9193
inline void approveOta() { policies |= Approved; }

src/ota/interface/OTAInterfaceDefault.cpp

+15-13
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
*/
1010
#include <AIoTC_Config.h>
1111

12-
#if OTA_ENABLED && ! defined(OFFLOADED_DOWNLOAD)
12+
#if OTA_ENABLED && ! defined(OFFLOADED_DOWNLOAD) && ! defined(HAS_NOTECARD)
1313
#include "OTAInterfaceDefault.h"
1414
#include "../OTA.h"
1515

@@ -32,6 +32,8 @@ OTACloudProcessInterface::State OTADefaultCloudProcessInterface::startOTA() {
3232
assert(OTACloudProcessInterface::context != nullptr);
3333
assert(context == nullptr);
3434

35+
DEBUG_DEBUG("OTADefaultCloudProcessInterface::%s initializing download from \"%s\"", __FUNCTION__, OTACloudProcessInterface::context->url);
36+
3537
context = new Context(
3638
OTACloudProcessInterface::context->url,
3739
[this](uint8_t c) {
@@ -58,27 +60,27 @@ OTACloudProcessInterface::State OTADefaultCloudProcessInterface::startOTA() {
5860
http_client->endRequest();
5961

6062
if(res == HTTP_ERROR_CONNECTION_FAILED) {
61-
DEBUG_VERBOSE("OTA ERROR: http client error connecting to server \"%s:%d\"",
63+
DEBUG_ERROR("OTA ERROR: HTTP client error connecting to server \"%s:%d\"",
6264
context->parsed_url.host(), context->parsed_url.port());
6365
return ServerConnectErrorFail;
6466
} else if(res == HTTP_ERROR_TIMED_OUT) {
65-
DEBUG_VERBOSE("OTA ERROR: http client timeout \"%s\"", OTACloudProcessInterface::context->url);
67+
DEBUG_ERROR("OTA ERROR: HTTP client timeout \"%s\"", OTACloudProcessInterface::context->url);
6668
return OtaHeaderTimeoutFail;
6769
} else if(res != HTTP_SUCCESS) {
68-
DEBUG_VERBOSE("OTA ERROR: http client returned %d on get \"%s\"", res, OTACloudProcessInterface::context->url);
70+
DEBUG_ERROR("OTA ERROR: HTTP client returned %d on GET \"%s\"", res, OTACloudProcessInterface::context->url);
6971
return OtaDownloadFail;
7072
}
7173

7274
int statusCode = http_client->responseStatusCode();
7375

7476
if(statusCode != 200) {
75-
DEBUG_VERBOSE("OTA ERROR: get response on \"%s\" returned status %d", OTACloudProcessInterface::context->url, statusCode);
77+
DEBUG_ERROR("OTA ERROR: GET response on \"%s\" returned status %d", OTACloudProcessInterface::context->url, statusCode);
7678
return HttpResponseFail;
7779
}
7880

79-
// The following call is required to save the header value , keep it
81+
// The following call is required to save the header value (keep it)
8082
if(http_client->contentLength() == HttpClient::kNoContentLengthHeader) {
81-
DEBUG_VERBOSE("OTA ERROR: the response header doesn't contain \"ContentLength\" field");
83+
DEBUG_ERROR("OTA ERROR: The response header doesn't contain \"ContentLength\" field");
8284
return HttpHeaderErrorFail;
8385
}
8486

@@ -107,15 +109,15 @@ OTACloudProcessInterface::State OTADefaultCloudProcessInterface::fetch() {
107109
http_res = http_client->read(context->buffer, context->buf_len);
108110

109111
if(http_res < 0) {
110-
DEBUG_VERBOSE("OTA ERROR: Download read error %d", http_res);
112+
DEBUG_ERROR("OTA ERROR: Download read error %d", http_res);
111113
res = OtaDownloadFail;
112114
goto exit;
113115
}
114116

115117
parseOta(context->buffer, http_res);
116118

117119
if(context->writeError) {
118-
DEBUG_VERBOSE("OTA ERROR: File write error");
120+
DEBUG_ERROR("OTA ERROR: File write error");
119121
res = ErrorWriteUpdateFileFail;
120122
goto exit;
121123
}
@@ -130,17 +132,17 @@ OTACloudProcessInterface::State OTADefaultCloudProcessInterface::fetch() {
130132
// validate CRC
131133
context->calculatedCrc32 ^= 0xFFFFFFFF; // finalize CRC
132134
if(context->header.header.crc32 == context->calculatedCrc32) {
133-
DEBUG_VERBOSE("Ota download completed successfully");
135+
DEBUG_DEBUG("OTADefaultCloudProcessInterface::%s OTA download completed successfully", __FUNCTION__);
134136
res = FlashOTA;
135137
} else {
136138
res = OtaHeaderCrcFail;
137139
}
138140
} else if(context->downloadState == OtaDownloadError) {
139-
DEBUG_VERBOSE("OTA ERROR: OtaDownloadError");
141+
DEBUG_ERROR("OTA ERROR: OtaDownloadError");
140142

141143
res = OtaDownloadFail;
142144
} else if(context->downloadState == OtaDownloadMagicNumberMismatch) {
143-
DEBUG_VERBOSE("OTA ERROR: Magic number mismatch");
145+
DEBUG_ERROR("OTA ERROR: Magic number mismatch");
144146
res = OtaHeaderMagicNumberFail;
145147
}
146148

@@ -196,7 +198,7 @@ void OTADefaultCloudProcessInterface::parseOta(uint8_t* buffer, size_t buf_len)
196198
context->downloadedSize += (cursor-buffer);
197199

198200
if((millis() - context->lastReportTime) > 10000) { // Report the download progress each X millisecond
199-
DEBUG_VERBOSE("OTA Download Progress %d/%d", context->downloadedSize, contentLength);
201+
DEBUG_VERBOSE("OTADefaultCloudProcessInterface::%s [%d] OTA Download Progress %d/%d", __FUNCTION__, ::millis(), context->downloadedSize, contentLength);
200202

201203
reportStatus(context->downloadedSize);
202204
context->lastReportTime = millis();

src/ota/interface/OTAInterfaceDefault.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
#include <AIoTC_Config.h>
1313

14-
#if OTA_ENABLED && ! defined(OFFLOADED_DOWNLOAD)
14+
#if OTA_ENABLED && ! defined(OFFLOADED_DOWNLOAD) && ! defined(HAS_NOTECARD)
1515
#include <Arduino.h>
1616

1717
#include <ArduinoHttpClient.h>

0 commit comments

Comments
 (0)