Skip to content

Commit

Permalink
feat: Notecard OTA Support
Browse files Browse the repository at this point in the history
  • Loading branch information
zfields committed Sep 10, 2024
1 parent 2903300 commit 524094f
Show file tree
Hide file tree
Showing 15 changed files with 702 additions and 53 deletions.
68 changes: 34 additions & 34 deletions src/AIoTC_Config.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,40 +60,6 @@

#if !defined(HAS_NOTECARD)

#if defined(ARDUINO_SAMD_MKRWIFI1010) || defined(ARDUINO_SAMD_NANO_33_IOT)
#define OTA_STORAGE_SNU (1)
#else
#define OTA_STORAGE_SNU (0)
#endif

#if defined(ARDUINO_NANO_RP2040_CONNECT)
#define OTA_STORAGE_SFU (1)
#else
#define OTA_STORAGE_SFU (0)
#endif

#ifdef ARDUINO_SAMD_MKRGSM1400
#define OTA_STORAGE_SSU (1) // OTA_STORAGE_SSU is not implemented yet in OTASamd
#else
#define OTA_STORAGE_SSU (0)
#endif

#if defined(ARDUINO_PORTENTA_H7_M7) || defined(ARDUINO_NICLA_VISION) || defined(ARDUINO_OPTA) || defined(ARDUINO_GIGA)
#define OTA_STORAGE_PORTENTA_QSPI (1)
#else
#define OTA_STORAGE_PORTENTA_QSPI (0)
#endif

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

#if (OTA_STORAGE_SFU || OTA_STORAGE_SNU || OTA_STORAGE_PORTENTA_QSPI || OTA_STORAGE_ESP)
#define OTA_ENABLED (1)
#else
#define OTA_ENABLED (0)
#endif

#if defined(ARDUINO_SAMD_MKRGSM1400) || defined(ARDUINO_SAMD_MKR1000) || \
defined(ARDUINO_SAMD_MKRNB1500) || defined(ARDUINO_PORTENTA_H7_M7) || \
defined (ARDUINO_NANO_RP2040_CONNECT) || defined(ARDUINO_OPTA) || \
Expand Down Expand Up @@ -143,6 +109,40 @@

#endif // HAS_NOTECARD

#if defined(ARDUINO_SAMD_MKRWIFI1010) || defined(ARDUINO_SAMD_NANO_33_IOT)
#define OTA_STORAGE_SNU (1)
#else
#define OTA_STORAGE_SNU (0)
#endif

#if defined(ARDUINO_NANO_RP2040_CONNECT)
#define OTA_STORAGE_SFU (1)
#else
#define OTA_STORAGE_SFU (0)
#endif

#ifdef ARDUINO_SAMD_MKRGSM1400
#define OTA_STORAGE_SSU (1) // OTA_STORAGE_SSU is not implemented yet in OTASamd
#else
#define OTA_STORAGE_SSU (0)
#endif

#if defined(ARDUINO_PORTENTA_H7_M7) || defined(ARDUINO_NICLA_VISION) || defined(ARDUINO_OPTA) || defined(ARDUINO_GIGA)
#define OTA_STORAGE_PORTENTA_QSPI (1)
#else
#define OTA_STORAGE_PORTENTA_QSPI (0)
#endif

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

#if (OTA_STORAGE_SFU || OTA_STORAGE_SNU || OTA_STORAGE_PORTENTA_QSPI || OTA_STORAGE_ESP)
#define OTA_ENABLED (1)
#else
#define OTA_ENABLED (0)
#endif

#if defined(ARDUINO_PORTENTA_H7_M7) || defined(ARDUINO_NICLA_VISION) || defined(ARDUINO_OPTA) || defined(ARDUINO_GIGA)
#define BEAR_SSL_CLIENT_IBUF_SIZE (16384 + 325) // Allows download from storage API
#define BOARD_STM32H7
Expand Down
9 changes: 9 additions & 0 deletions src/ArduinoIoTCloudNotecard.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@ ArduinoIoTCloudNotecard::ArduinoIoTCloudNotecard()
,_notecard_polling_interval_ms{DEFAULT_READ_INTERVAL_MS}
,_interrupt_pin{-1}
,_data_available{false}
#if OTA_ENABLED
,_ota(&_message_stream)
,_get_ota_confirmation{nullptr}
#endif
{

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

#if OTA_ENABLED
// Configure the OTA interface
_ota.setConnection(&connection_);
#endif

// Setup retry timers
_connection_attempt.begin(AIOT_CONFIG_RECONNECTION_RETRY_DELAY_ms, AIOT_CONFIG_MAX_RECONNECTION_RETRY_DELAY_ms);

Expand Down
29 changes: 29 additions & 0 deletions src/ArduinoIoTCloudNotecard.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@
#include "ArduinoIoTCloudThing.h"
#include "ArduinoIoTCloudDevice.h"

#if OTA_ENABLED
#include "ota/OTA.h"
#endif /* OTA_ENABLED */

/******************************************************************************
* DEFINES
******************************************************************************/
Expand All @@ -33,6 +37,10 @@
* TYPEDEF
******************************************************************************/

#if OTA_ENABLED
typedef bool (*onOTARequestCallbackFunc)(void);
#endif /* OTA_ENABLED */

/******************************************************************************
* CLASS DECLARATION
******************************************************************************/
Expand Down Expand Up @@ -82,6 +90,22 @@ class ArduinoIoTCloudNotecard : public ArduinoIoTCloudClass
*/
inline void setNotecardPollingInterval(uint32_t interval_ms) { _notecard_polling_interval_ms = ((interval_ms < 250) ? 250 : interval_ms); }

#if OTA_ENABLED
/* The callback is triggered when the OTA is initiated and it gets executed until _ota_req flag is cleared.
* It should return true when the OTA can be applied or false otherwise.
* See example ArduinoIoTCloud-DeferredOTA.ino
*/
inline void onOTARequestCb(onOTARequestCallbackFunc cb) {
_get_ota_confirmation = cb;

if(_get_ota_confirmation) {
_ota.setOtaPolicies(OTACloudProcessInterface::ApprovalRequired);
} else {
_ota.setOtaPolicies(OTACloudProcessInterface::None);
}
}
#endif /* OTA_ENABLED */

private:

enum class State
Expand All @@ -104,6 +128,11 @@ class ArduinoIoTCloudNotecard : public ArduinoIoTCloudClass
int _interrupt_pin;
volatile bool _data_available;

#if OTA_ENABLED
ArduinoCloudOTA _ota;
onOTARequestCallbackFunc _get_ota_confirmation;
#endif /* OTA_ENABLED */

inline virtual PropertyContainer &getThingPropertyContainer() override { return _thing.getPropertyContainer(); }

State handle_ConnectPhy();
Expand Down
4 changes: 4 additions & 0 deletions src/ota/implementation/OTAEsp32.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@

#pragma once

#ifndef HAS_NOTECARD
#include "ota/interface/OTAInterfaceDefault.h"
#else
#include "ota/interface/OTAInterfaceNotecard.h"
#endif

class ESP32OTACloudProcess: public OTADefaultCloudProcessInterface {
public:
Expand Down
4 changes: 4 additions & 0 deletions src/ota/implementation/OTANanoRP2040.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@

#pragma once

#ifndef HAS_NOTECARD
#include "ota/interface/OTAInterfaceDefault.h"
#else
#include "ota/interface/OTAInterfaceNotecard.h"
#endif

#include "FATFileSystem.h"
#include "FlashIAPBlockDevice.h"
Expand Down
7 changes: 6 additions & 1 deletion src/ota/implementation/OTASTM32H7.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,12 @@
*/

#pragma once

#ifndef HAS_NOTECARD
#include "ota/interface/OTAInterfaceDefault.h"
#else
#include "ota/interface/OTAInterfaceNotecard.h"
#endif

#include <QSPIFBlockDevice.h>

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

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

// we reboot the device
Expand Down
3 changes: 2 additions & 1 deletion src/ota/implementation/OTASamd.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@

#pragma once

#include "ota/interface/OTAInterface.h"
#include <Arduino_DebugUtils.h>

#include "ota/interface/OTAInterface.h"

class SAMDOTACloudProcess: public OTACloudProcessInterface {
public:
SAMDOTACloudProcess(MessageStream *ms);
Expand Down
2 changes: 2 additions & 0 deletions src/ota/interface/OTAInterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
/******************************************************************************
* CLASS DECLARATION
******************************************************************************/
class ConnectionHandler; // Forward declaration

class OTACloudProcessInterface: public CloudProcess {
public:
Expand Down Expand Up @@ -86,6 +87,7 @@ class OTACloudProcessInterface: public CloudProcess {
virtual void handleMessage(Message*);
// virtual CloudProcess::State getState();
// virtual void hook(State s, void* action);
inline virtual void setConnection(ConnectionHandler * connection) { (void)connection; }
virtual void update() { handleMessage(nullptr); }

inline void approveOta() { policies |= Approved; }
Expand Down
28 changes: 15 additions & 13 deletions src/ota/interface/OTAInterfaceDefault.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
*/
#include <AIoTC_Config.h>

#if OTA_ENABLED && ! defined(OFFLOADED_DOWNLOAD)
#if OTA_ENABLED && ! defined(OFFLOADED_DOWNLOAD) && ! defined(HAS_NOTECARD)
#include "OTAInterfaceDefault.h"
#include "../OTA.h"

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

DEBUG_DEBUG("OTADefaultCloudProcessInterface::%s initializing download from \"%s\"", __FUNCTION__, OTACloudProcessInterface::context->url);

context = new Context(
OTACloudProcessInterface::context->url,
[this](uint8_t c) {
Expand All @@ -58,27 +60,27 @@ OTACloudProcessInterface::State OTADefaultCloudProcessInterface::startOTA() {
http_client->endRequest();

if(res == HTTP_ERROR_CONNECTION_FAILED) {
DEBUG_VERBOSE("OTA ERROR: http client error connecting to server \"%s:%d\"",
DEBUG_ERROR("OTA ERROR: HTTP client error connecting to server \"%s:%d\"",
context->parsed_url.host(), context->parsed_url.port());
return ServerConnectErrorFail;
} else if(res == HTTP_ERROR_TIMED_OUT) {
DEBUG_VERBOSE("OTA ERROR: http client timeout \"%s\"", OTACloudProcessInterface::context->url);
DEBUG_ERROR("OTA ERROR: HTTP client timeout \"%s\"", OTACloudProcessInterface::context->url);
return OtaHeaderTimeoutFail;
} else if(res != HTTP_SUCCESS) {
DEBUG_VERBOSE("OTA ERROR: http client returned %d on get \"%s\"", res, OTACloudProcessInterface::context->url);
DEBUG_ERROR("OTA ERROR: HTTP client returned %d on GET \"%s\"", res, OTACloudProcessInterface::context->url);
return OtaDownloadFail;
}

int statusCode = http_client->responseStatusCode();

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

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

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

if(http_res < 0) {
DEBUG_VERBOSE("OTA ERROR: Download read error %d", http_res);
DEBUG_ERROR("OTA ERROR: Download read error %d", http_res);
res = OtaDownloadFail;
goto exit;
}

parseOta(context->buffer, http_res);

if(context->writeError) {
DEBUG_VERBOSE("OTA ERROR: File write error");
DEBUG_ERROR("OTA ERROR: File write error");
res = ErrorWriteUpdateFileFail;
goto exit;
}
Expand All @@ -130,17 +132,17 @@ OTACloudProcessInterface::State OTADefaultCloudProcessInterface::fetch() {
// validate CRC
context->calculatedCrc32 ^= 0xFFFFFFFF; // finalize CRC
if(context->header.header.crc32 == context->calculatedCrc32) {
DEBUG_VERBOSE("Ota download completed successfully");
DEBUG_DEBUG("OTADefaultCloudProcessInterface::%s OTA download completed successfully", __FUNCTION__);
res = FlashOTA;
} else {
res = OtaHeaderCrcFail;
}
} else if(context->downloadState == OtaDownloadError) {
DEBUG_VERBOSE("OTA ERROR: OtaDownloadError");
DEBUG_ERROR("OTA ERROR: OtaDownloadError");

res = OtaDownloadFail;
} else if(context->downloadState == OtaDownloadMagicNumberMismatch) {
DEBUG_VERBOSE("OTA ERROR: Magic number mismatch");
DEBUG_ERROR("OTA ERROR: Magic number mismatch");
res = OtaHeaderMagicNumberFail;
}

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

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

reportStatus(context->downloadedSize);
context->lastReportTime = millis();
Expand Down
2 changes: 1 addition & 1 deletion src/ota/interface/OTAInterfaceDefault.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

#include <AIoTC_Config.h>

#if OTA_ENABLED && ! defined(OFFLOADED_DOWNLOAD)
#if OTA_ENABLED && ! defined(OFFLOADED_DOWNLOAD) && ! defined(HAS_NOTECARD)
#include <Arduino.h>

#include <ArduinoHttpClient.h>
Expand Down
Loading

0 comments on commit 524094f

Please sign in to comment.