Skip to content

Commit

Permalink
ESP32: OTA Requestor and OTA Provider example applications (#11320)
Browse files Browse the repository at this point in the history
* Added OTA requester and provider app for esp32

* Apply suggestions from code review

Co-authored-by: Boris Zbarsky <[email protected]>

* Addressed review comments

* Addressed review comments

* Apply suggestions from code review

Co-authored-by: Carol Yang <[email protected]>

* Restyled by clang-format

* Addressed review comments

* Added few words to spellcheck dictionary

* Added .gitignore files to esp32 ota apps

Co-authored-by: Boris Zbarsky <[email protected]>
Co-authored-by: Carol Yang <[email protected]>
Co-authored-by: Restyled.io <[email protected]>
  • Loading branch information
4 people authored and pull[bot] committed Nov 29, 2021
1 parent 47237a7 commit 1713650
Show file tree
Hide file tree
Showing 36 changed files with 2,946 additions and 2 deletions.
8 changes: 8 additions & 0 deletions .github/.wordlist.txt
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ AppConfig
ApplicationBasic
ApplicationIdentifier
ApplicationLauncher
ApplyUpdateRequest
approver
appspot
aps
Expand Down Expand Up @@ -340,6 +341,7 @@ env
esd
ESPPORT
Espressif
esptool
eth
EthernetNetworkDiagnostics
ethernets
Expand Down Expand Up @@ -690,6 +692,11 @@ optionsMask
optionsOverride
orgs
OTA
OTAProviderIpAddress
OTAProviderNodeId
OTAProviderSerialPort
OTARequesterImpl
OTARequestorSerialPort
OTBR
otcli
PAA
Expand Down Expand Up @@ -848,6 +855,7 @@ SetpointRaiseLower
SetUpPINCode
SetupQRCode
sexualized
shubhamdp
SIGINT
SiLabs
SiliconLabs
Expand Down
5 changes: 5 additions & 0 deletions examples/ota-provider-app/esp32/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
*.vscode

/build/
/sdkconfig
/sdkconfig.old
33 changes: 33 additions & 0 deletions examples/ota-provider-app/esp32/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#
# Copyright (c) 2021 Project CHIP Authors
# All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# The following lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)

include($ENV{IDF_PATH}/tools/cmake/project.cmake)
include(${CMAKE_CURRENT_LIST_DIR}/../../common/cmake/idf_flashing.cmake)

set(EXTRA_COMPONENT_DIRS
"${CMAKE_CURRENT_LIST_DIR}/third_party/connectedhomeip/config/esp32/components"
"${CMAKE_CURRENT_LIST_DIR}/../../common/QRCode"
)

project(chip-ota-provider-app)
idf_build_set_property(CXX_COMPILE_OPTIONS "-std=gnu++14;-Os;-DLWIP_IPV6_SCOPES=0;-DCHIP_HAVE_CONFIG_H" APPEND)
idf_build_set_property(C_COMPILE_OPTIONS "-Os;-DLWIP_IPV6_SCOPES=0" APPEND)

flashing_script()
73 changes: 73 additions & 0 deletions examples/ota-provider-app/esp32/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# CHIP ESP32 OTA Provider Example

A prototype application that demonstrates OTA provider capabilities.

## Supported Devices

- This example supports ESP32 and ESP32C3. For details please check
[here](https://github.com/shubhamdp/connectedhomeip/tree/shubhamdp-patch-1/examples/all-clusters-app/esp32#supported-devices).

## Building the Example Application

- If you are building for the first time please check
[Building the Example Application](https://github.com/shubhamdp/connectedhomeip/tree/shubhamdp-patch-1/examples/all-clusters-app/esp32#building-the-example-application)
guide.
- Otherwise, `idf.py build` works!

## Flashing the Example Application

```
idf.py -p <OTAProviderSerialPort> flash
```

## Flashing the hello-world.bin OTA image

Flash hello-world OTA image on OTA Provider's "ota_data" flash partition. Please
find hello-world.bin
[here](http://shubhamdp.github.io/esp_ota/esp32/hello-world-flash-in-ota-provider-partition.bin).
This OTA image is built for ESP32, it will not work on other devices. This is
the OTA upgrade image and will be sent to OTA requestor.

```
esptool.py -p <OTAProviderSerialPort> write_flash 0x206400 hello-world-flash-in-ota-provider-partition.bin
```

NOTE: This is a modified binary which contains the size of OTA image at first 4
bytes.

Run the idf monitor

```
idf.py -p <OTAProviderSerialPort> monitor
```

## Commissioning over BLE using chip-tool

- Please build the standalone chip-tool as described [here](../../chip-tool)
- Commissioning the OTA Provider

```
./out/debug/chip-tool pairing ble-wifi 12345 <ssid> <passphrase> 0 20202021 3841
```

---

Please note down the IP Address and Node ID of OTA Provider, these are required
for [OTA Requestor Example](../../ota-requestor-app/esp32). Once OTA provider is
commissioned then head over to
[OTA Requestor Example](../../ota-requestor-app/esp32).

---

## Features

- Can complete full BDX transfer
- Provide the full OTA image to Requestor

## Limitations

- Synchronous BDX transfer only
- Does not check VID/PID
- Only one transfer at a time
- Does not check incoming UpdateTokens
- Does not support the header defined in Matter Specification.
193 changes: 193 additions & 0 deletions examples/ota-provider-app/esp32/main/BdxOtaSender.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
/*
*
* Copyright (c) 2021 Project CHIP Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include <BdxOtaSender.h>

#include <lib/core/CHIPError.h>
#include <lib/support/BitFlags.h>
#include <lib/support/CHIPMemString.h>
#include <messaging/ExchangeContext.h>
#include <messaging/Flags.h>
#include <protocols/bdx/BdxTransferSession.h>

using chip::bdx::StatusCode;
using chip::bdx::TransferControlFlags;
using chip::bdx::TransferSession;

void BdxOtaSender::SetCallbacks(BdxOtaSenderCallbacks callbacks)
{
mOnBlockQueryCallback = callbacks.onBlockQuery;
mOnTransferCompleteCallback = callbacks.onTransferComplete;
mOnTransferFailedCallback = callbacks.onTransferFailed;
}

void BdxOtaSender::HandleTransferSessionOutput(TransferSession::OutputEvent & event)
{
CHIP_ERROR err = CHIP_NO_ERROR;

if (event.EventType != TransferSession::OutputEventType::kNone)
{
ChipLogDetail(BDX, "OutputEvent type: %s", event.ToString(event.EventType));
}

switch (event.EventType)
{
case TransferSession::OutputEventType::kNone:
break;
case TransferSession::OutputEventType::kMsgToSend: {
chip::Messaging::SendFlags sendFlags;
if (!event.msgTypeData.HasMessageType(chip::Protocols::SecureChannel::MsgType::StatusReport))
{
// All messages sent from the Sender expect a response, except for a StatusReport which would indicate an error and the
// end of the transfer.
sendFlags.Set(chip::Messaging::SendMessageFlags::kExpectResponse);
}
VerifyOrReturn(mExchangeCtx != nullptr, ChipLogError(BDX, "%s: mExchangeCtx is null", __FUNCTION__));
err = mExchangeCtx->SendMessage(event.msgTypeData.ProtocolId, event.msgTypeData.MessageType, std::move(event.MsgData),
sendFlags);
if (err != CHIP_NO_ERROR)
{
ChipLogError(BDX, "SendMessage failed: %s", chip::ErrorStr(err));
}
break;
}
case TransferSession::OutputEventType::kInitReceived: {
// TransferSession will automatically reject a transfer if there are no
// common supported control modes. It will also default to the smaller
// block size.
TransferSession::TransferAcceptData acceptData;
acceptData.ControlMode = TransferControlFlags::kReceiverDrive; // OTA must use receiver drive
acceptData.MaxBlockSize = mTransfer.GetTransferBlockSize();
acceptData.StartOffset = mTransfer.GetStartOffset();
acceptData.Length = mTransfer.GetTransferLength();
VerifyOrReturn(mTransfer.AcceptTransfer(acceptData) == CHIP_NO_ERROR,
ChipLogError(BDX, "%s: %s", __FUNCTION__, chip::ErrorStr(err)));
break;
}
case TransferSession::OutputEventType::kQueryReceived: {
TransferSession::BlockData blockData;
uint16_t blockSize = mTransfer.GetTransferBlockSize();
uint16_t bytesToRead = blockSize;

chip::System::PacketBufferHandle blockBuf = chip::System::PacketBufferHandle::New(bytesToRead);
if (blockBuf.IsNull())
{
// TODO: AbortTransfer() needs to support GeneralStatusCode failures as well as BDX specific errors.
mTransfer.AbortTransfer(StatusCode::kUnknown);
return;
}

if (mOnBlockQueryCallback != nullptr && mOnBlockQueryCallback->mCall != nullptr)
{
if (CHIP_NO_ERROR !=
mOnBlockQueryCallback->mCall(mOnBlockQueryCallback->mContext, blockBuf, blockData.Length, blockData.IsEof,
mNumBytesSent))
{
ChipLogError(BDX, "onBlockQuery Callback failed");
mTransfer.AbortTransfer(StatusCode::kUnknown);
return;
}
}
else
{
ChipLogError(BDX, "onBlockQuery Callback not set");
mTransfer.AbortTransfer(StatusCode::kUnknown);
return;
}

blockData.Data = blockBuf->Start();
mNumBytesSent = static_cast<uint32_t>(mNumBytesSent + blockData.Length);

VerifyOrReturn(CHIP_NO_ERROR == mTransfer.PrepareBlock(blockData),
ChipLogError(BDX, "%s: PrepareBlock failed: %s", __FUNCTION__, chip::ErrorStr(err)));
break;
}
case TransferSession::OutputEventType::kAckReceived:
break;
case TransferSession::OutputEventType::kAckEOFReceived:
ChipLogDetail(BDX, "Transfer completed, got AckEOF");
if (mOnTransferCompleteCallback != nullptr && mOnTransferCompleteCallback->mCall != nullptr)
{
mOnTransferCompleteCallback->mCall(mOnTransferCompleteCallback->mContext);
}
else
{
ChipLogError(BDX, "onTransferComplete Callback not set");
}
Reset();
break;
case TransferSession::OutputEventType::kStatusReceived:
ChipLogError(BDX, "Got StatusReport %x", static_cast<uint16_t>(event.statusData.statusCode));
if (mOnTransferFailedCallback != nullptr && mOnTransferFailedCallback->mCall != nullptr)
{
mOnTransferFailedCallback->mCall(mOnTransferFailedCallback->mContext, kErrorBdxSenderStatusReceived);
}
else
{
ChipLogError(BDX, "onTransferFailed Callback not set");
}
Reset();
break;
case TransferSession::OutputEventType::kInternalError:
ChipLogError(BDX, "InternalError");
if (mOnTransferFailedCallback != nullptr && mOnTransferFailedCallback->mCall != nullptr)
{
mOnTransferFailedCallback->mCall(mOnTransferFailedCallback->mContext, kErrorBdxSenderInternal);
}
{
ChipLogError(BDX, "onTransferFailed Callback not set");
}
Reset();
break;
case TransferSession::OutputEventType::kTransferTimeout:
ChipLogError(BDX, "Transfer timed out");
if (mOnTransferFailedCallback != nullptr && mOnTransferFailedCallback->mCall != nullptr)
{
mOnTransferFailedCallback->mCall(mOnTransferFailedCallback->mContext, kErrorBdxSenderTimeOut);
}
{
ChipLogError(BDX, "onTransferFailed Callback not set");
}
Reset();
break;
case TransferSession::OutputEventType::kAcceptReceived:
case TransferSession::OutputEventType::kBlockReceived:
default:
// TransferSession should prevent this case from happening.
ChipLogError(BDX, "%s: unsupported event type", __FUNCTION__);
}
}

void BdxOtaSender::Reset()
{
mTransfer.Reset();
if (mExchangeCtx != nullptr)
{
mExchangeCtx->Close();
}
mNumBytesSent = 0;
}

uint16_t BdxOtaSender::GetTransferBlockSize(void)
{
return mTransfer.GetTransferBlockSize();
}

uint64_t BdxOtaSender::GetTransferLength()
{
return mTransfer.GetTransferLength();
}
Loading

0 comments on commit 1713650

Please sign in to comment.