Browse Source

added all doxygen comments

pull/2/head
Hendrik Schutter 1 year ago
parent
commit
7fe1f18985
  1. 2
      components/mesh_ota/HTTPS_Client.c
  2. 81
      components/mesh_ota/Mesh_Network.c
  3. 96
      components/mesh_ota/Mesh_OTA.c
  4. 7
      components/mesh_ota/Mesh_OTA_Globals.c
  5. 128
      components/mesh_ota/Mesh_OTA_Partition_Access.c
  6. 163
      components/mesh_ota/Mesh_OTA_Util.c
  7. 9
      components/mesh_ota/include/Mesh_Network.h
  8. 7
      components/mesh_ota/include/Mesh_OTA.h
  9. 8
      components/mesh_ota/include/Mesh_OTA_Globals.h
  10. 10
      components/mesh_ota/include/Mesh_OTA_Partition_Access.h
  11. 7
      components/mesh_ota/include/Mesh_OTA_Util.h

2
components/mesh_ota/HTTPS_Client.c

@ -56,7 +56,7 @@ https_client_ret_t errHTTPSClientInitialize(void)
* @brief receive a image segment from server
* @param cpu8Data data buffer
* @param cpcu32DataLenght desired byte amount
* @param pu32BytesRead actual received byte amount
* @param pu32BytesRead actual received byte amount
* @return HTTPS_Client error code
* @author Hendrik Schutter
* @date 20.01.2021

81
components/mesh_ota/Mesh_Network.c

@ -224,6 +224,15 @@ esp_err_t errMeshNetworkSetChangeStateOfServerWorkerHandle(void (*pChangeStateOf
return ESP_OK;
}
/**
* @fn esp_err_t errMeshNetworkSendMeshPacket(const mesh_addr_t* const cpcAddrDest, const MESH_PACKET_t* const cpcPacket)
* @brief send packet to mesh node
* @param cpcAddrDest address from mesh node
* @param cpcPacket packet to send
* @return ESP32 error code
* @author Hendrik Schutter
* @date 20.01.2021
*/
esp_err_t errMeshNetworkSendMeshPacket(const mesh_addr_t* const cpcAddrDest, const MESH_PACKET_t* const cpcPacket)
{
esp_err_t err;
@ -240,11 +249,27 @@ esp_err_t errMeshNetworkSendMeshPacket(const mesh_addr_t* const cpcAddrDest, con
return err;
}
bool bMeshNetworkIsRootNode()
/**
* @fn bool bMeshNetworkIsRootNode()
* @brief return true if this node is the root
* @param void
* @return boolean
* @author Hendrik Schutter
* @date 21.01.2021
*/
bool bMeshNetworkIsRootNode(void)
{
return esp_mesh_is_root();
}
/**
* @fn bool bMeshNetworkIsNodeNeighbour(const mesh_addr_t* const cpcNode)
* @brief return true if node is neighbour if this
* @param cpcNode to check
* @return boolean
* @author Hendrik Schutter
* @date 21.01.2021
*/
bool bMeshNetworkIsNodeNeighbour(const mesh_addr_t* const cpcNode)
{
esp_err_t err = ESP_OK;
@ -280,7 +305,16 @@ bool bMeshNetworkIsNodeNeighbour(const mesh_addr_t* const cpcNode)
return bReturn;
}
//returns true if MAC address is equal
/**
* @fn bool bMeshNetworkCheckMACEquality(const uint8_t* const cpcu8aMAC, const uint8_t* const cpcu8bMAC)
* @brief returns true if MAC address is equal
* @param cpcu8aMAC first MAC
* @param cpcu8bMAC second MAC
* @return boolean
* @author Hendrik Schutter
* @date 21.01.2021
*/
bool bMeshNetworkCheckMACEquality(const uint8_t* const cpcu8aMAC, const uint8_t* const cpcu8bMAC)
{
bool bRet = true;
@ -306,7 +340,15 @@ bool bMeshNetworkCheckMACEquality(const uint8_t* const cpcu8aMAC, const uint8_t*
return bRet;
}
esp_err_t errMeshNetworkStartReceiveTask()
/**
* @fn esp_err_t errMeshNetworkStartReceiveTask()
* @brief start the task to receive the mesh packets
* @param void
* @return ESP32 error code
* @author Hendrik Schutter
* @date 21.01.2021
*/
esp_err_t errMeshNetworkStartReceiveTask(void)
{
esp_err_t err = ESP_OK;
BaseType_t xReturned;
@ -320,11 +362,28 @@ esp_err_t errMeshNetworkStartReceiveTask()
return err;
}
/**
* @fn vMeshNetworkGetOwnAddr(mesh_addr_t* const cpMeshOwnAddr)
* @brief return own MAC addr
* @param cpMeshOwnAddr pointer to own mac
* @return void
* @author Hendrik Schutter
* @date 21.01.2021
*/
void vMeshNetworkGetOwnAddr(mesh_addr_t* const cpMeshOwnAddr)
{
memcpy(cpMeshOwnAddr->addr, u8ownMAC, 6);
}
/**
* @fn esp_err_t errMeshNetworkGetChildren(mesh_addr_t* const cpChildren, uint16_t* const cpu16ChildrenSize)
* @brief get all connected children to node in array
* @param cpChildren pointer to array
* @param cpu16ChildrenSize pointer to size of array
* @return ESP32 error code
* @author Hendrik Schutter
* @date 21.01.2021
*/
esp_err_t errMeshNetworkGetChildren(mesh_addr_t* const cpChildren, uint16_t* const cpu16ChildrenSize)
{
esp_err_t err = ESP_OK;
@ -349,6 +408,14 @@ esp_err_t errMeshNetworkGetChildren(mesh_addr_t* const cpChildren, uint16_t* con
return err;
}
/**
* @fn void vMeshNetworkTaskReceiveMeshData(void *arg)
* @brief Task to receive all mesh packets
* @param arg
* @return void
* @author Hendrik Schutter
* @date 21.01.2021
*/
void vMeshNetworkTaskReceiveMeshData(void *arg)
{
esp_err_t err;
@ -398,6 +465,14 @@ void vMeshNetworkTaskReceiveMeshData(void *arg)
} //end while
}
/**
* @fn esp_err_t errMeshNetworkGetParentNode(mesh_addr_t* const cpMeshParentAddr)
* @brief get parrent node if connected to it
* @param cpMeshParentAddr pointer to parent node addrs
* @return ESP32 error code
* @author Hendrik Schutter
* @date 21.01.2021
*/
esp_err_t errMeshNetworkGetParentNode(mesh_addr_t* const cpMeshParentAddr)
{
esp_err_t err = ESP_OK;

96
components/mesh_ota/Mesh_OTA.c

@ -1,3 +1,10 @@
/**
* @file Mesh_OTA.c
* @brief Start and implement OTA updates via HTTPS from server and other mesh nodes (bidirectional)
* @author Hendrik Schutter
* @date 21.01.2021
*/
#include "Mesh_OTA.h"
#include "Mesh_OTA_Util.h"
#include "Mesh_OTA_Globals.h"
@ -5,7 +12,18 @@
static const char *LOG_TAG = "mesh_ota";
esp_err_t errMeshOTAInitialize()
/**
* @fn esp_err_t errMeshOTAInitialize(void)
* @brief Starts Mesh OTA functionality
* @param void
* @return ESP32 error code
* @author Hendrik Schutter
* @date 21.01.2021
*
* Initialize queues and tasks
* Set callbacks
*/
esp_err_t errMeshOTAInitialize(void)
{
esp_err_t err = ESP_OK;
BaseType_t xReturned;
@ -15,7 +33,7 @@ esp_err_t errMeshOTAInitialize()
queueNodes = xQueueCreate(QUEUE_NODES_SIZE, sizeof(mesh_addr_t));
if (queueNodes == 0) // Queue not created
{
ESP_LOGE(LOG_TAG, "Unable to create Queue for Nodes");
ESP_LOGE(LOG_TAG, "Unable to create queue for nodes");
err = ESP_FAIL;
}
@ -25,7 +43,7 @@ esp_err_t errMeshOTAInitialize()
queueMessageOTA = xQueueCreate(QUEUE_MESSAGE_OTA_SIZE, sizeof(MESH_PACKET_t));
if (queueMessageOTA == 0) // Queue not created
{
ESP_LOGE(LOG_TAG, "Unable to create Queue for OTA Messages");
ESP_LOGE(LOG_TAG, "Unable to create queue for OTA messages");
err = ESP_FAIL;
}
}
@ -35,7 +53,7 @@ esp_err_t errMeshOTAInitialize()
bsStartStopServerWorker = xSemaphoreCreateBinary();
if( bsStartStopServerWorker == NULL )
{
ESP_LOGE(LOG_TAG, "Unable to create Mutex to represent state of Server worker");
ESP_LOGE(LOG_TAG, "Unable to create mutex to represent state of server worker");
err = ESP_FAIL;
}
}
@ -45,7 +63,7 @@ esp_err_t errMeshOTAInitialize()
bsOTAProcess = xSemaphoreCreateBinary();
if( bsOTAProcess == NULL )
{
ESP_LOGE(LOG_TAG, "Unable to create Mutex to grant access to OTA Process");
ESP_LOGE(LOG_TAG, "Unable to create mutex to grant access to OTA process");
err = ESP_FAIL;
}
}
@ -55,11 +73,12 @@ esp_err_t errMeshOTAInitialize()
xSemaphoreGive(bsOTAProcess); //unlock binary semaphore
if( bsOTAProcess == NULL )
{
ESP_LOGE(LOG_TAG, "Unable to unlock Mutex to grant access to OTA Process");
ESP_LOGE(LOG_TAG, "Unable to unlock mutex to grant access to OTA process");
err = ESP_FAIL;
}
}
//register callbacks in network
ERROR_CHECK(errMeshNetworkSetChildConnectedHandle(vMeshOtaUtilAddNodeToPossibleUpdatableQueue));
ERROR_CHECK(errMeshNetworkSetOTAMessageHandleHandle(vMeshOtaUtilAddOtaMessageToQueue));
ERROR_CHECK(errMeshNetworkSetChangeStateOfServerWorkerHandle(vMeshOtaUtilChangeStateOfServerWorker));
@ -99,9 +118,17 @@ esp_err_t errMeshOTAInitialize()
return err;
}
/**
* @fn void vMeshOtaTaskServerWorker(void *arg)
* @brief Task for updating from server via HTTPS
* @param arg
* @return void
* @author Hendrik Schutter
* @date 21.01.2021
*/
void vMeshOtaTaskServerWorker(void *arg)
{
esp_err_t err;
esp_err_t err = ESP_OK;
bool bNewOTAImage; //true if a new ota image was downloaded and validated
bool bFirstRun = true;
@ -118,6 +145,7 @@ void vMeshOtaTaskServerWorker(void *arg)
if(bFirstRun == true)
{
//init on first run
ERROR_CHECK(errHTTPSClientInitialize());
bFirstRun = false;
}
@ -141,6 +169,14 @@ void vMeshOtaTaskServerWorker(void *arg)
}
}
/**
* @fn void vMeshOtaTaskServerWorker(void *arg)
* @brief Task for updating from nodes in mesh network
* @param arg
* @return void
* @author Hendrik Schutter
* @date 21.01.2021
*/
void vMeshOtaTaskOTAWorker(void *arg)
{
esp_err_t err = ESP_OK;
@ -155,8 +191,6 @@ void vMeshOtaTaskOTAWorker(void *arg)
if((uxQueueSpacesAvailable(queueNodes) - QUEUE_NODES_SIZE) == 0)
{
//nodes queue is empty
ESP_LOGI(LOG_TAG, "nodes queue is empty");
if((bWantReboot == true) && (OTA_ALLOW_REBOOT == 1))
{
ESP_LOGE(LOG_TAG, "ESP32 Reboot ...");
@ -169,8 +203,6 @@ void vMeshOtaTaskOTAWorker(void *arg)
else
{
//queue not empty
ESP_LOGI(LOG_TAG, "nodes queue not empty: %i", (QUEUE_NODES_SIZE - uxQueueSpacesAvailable(queueNodes)));
if (xQueueReceive(queueNodes, &meshNodeAddr, ((100) / portTICK_PERIOD_MS)) != pdTRUE)
{
ESP_LOGE(LOG_TAG, "Unable to receive OTA Messages from Queue");
@ -188,7 +220,6 @@ void vMeshOtaTaskOTAWorker(void *arg)
{
vMeshOtaUtilClearNeighboursQueue(&meshNodeAddr); //remove this node from queue
}
}
if(bNewOTAImage == true)
@ -196,18 +227,28 @@ void vMeshOtaTaskOTAWorker(void *arg)
//set want reboot
ESP_LOGI(LOG_TAG, "Updated successfully via Mesh, set pending reboot");
bWantReboot = true;
vMeshOtaUtilAddAllNeighboursToQueue(); //add all existing neighbours to queue
}
vTaskDelay( (1000) / portTICK_PERIOD_MS);
}
}
/**
* @fn esp_err_t errMeshOtaSlaveEndpoint(bool* const cpbNewOTAImage)
* @brief Endpoint for OTA process that is called from remote node
* @param cpbNewOTAImage pointer to boolean to signal if a new image was successfully received
* @return ESP32 error code
* @author Hendrik Schutter
* @date 21.01.2021
*
* Answers the OTA_Version_Request with OTA_Version_Response
* calls errMeshOtaPartitionAccessMeshReceive OR errMeshOtaPartitionAccessMeshTransmit based on version number
*/
esp_err_t errMeshOtaSlaveEndpoint(bool* const cpbNewOTAImage)
{
esp_err_t err = ESP_OK;
MESH_PACKET_t sOTAMessage;
const esp_partition_t* pBootPartition; //pointer to boot partition (that will booted after reset)
const esp_partition_t* cpBootPartition = NULL; //pointer to boot partition (that will booted after reset)
esp_app_desc_t bootPartitionDesc; //Metadate from boot partition
*cpbNewOTAImage = false; //set default false
@ -225,8 +266,8 @@ esp_err_t errMeshOtaSlaveEndpoint(bool* const cpbNewOTAImage)
{
xSemaphoreTake(bsOTAProcess, portMAX_DELAY); //wait for binary semaphore that allows to start the OTA process
pBootPartition = esp_ota_get_boot_partition(); //get boot partition (that will booted after reset), not the running partition
ERROR_CHECK(esp_ota_get_partition_description(pBootPartition, &bootPartitionDesc)); //get metadata of partition
cpBootPartition = esp_ota_get_boot_partition(); //get boot partition (that will booted after reset), not the running partition
ERROR_CHECK(esp_ota_get_partition_description(cpBootPartition, &bootPartitionDesc)); //get metadata of partition
//send OTA_Version_Response to sender of OTA_Version_Request packet wirh version in payload
ERROR_CHECK(errMeshOtaUtilSendOTAVersionResponse(&sOTAMessage.meshSenderAddr));
@ -252,11 +293,23 @@ esp_err_t errMeshOtaSlaveEndpoint(bool* const cpbNewOTAImage)
return err;
}
/**
* @fn esp_err_t errMeshOtaMasterEndpoint(bool* const cpbNewOTAImage, const mesh_addr_t* const cpcMeshNodeAddr)
* @brief Endpoint for OTA process that calls remote node
* @param cpbNewOTAImage pointer to boolean to signal if a new image was successfully received
* @param cpcMeshNodeAddr pointer to remote node addr
* @return ESP32 error code
* @author Hendrik Schutter
* @date 21.01.2021
*
* Sends the OTA_Version_Request to remote node
* calls errMeshOtaPartitionAccessMeshReceive OR errMeshOtaPartitionAccessMeshTransmit based on version number received
*/
esp_err_t errMeshOtaMasterEndpoint(bool* const cpbNewOTAImage, const mesh_addr_t* const cpcMeshNodeAddr)
{
esp_err_t err = ESP_OK;
MESH_PACKET_t sOTAMessage;
const esp_partition_t* pBootPartition; //pointer to boot partition (that will booted after reset)
const esp_partition_t* cpBootPartition = NULL; //pointer to boot partition (that will booted after reset)
esp_app_desc_t bootPartitionDesc; //Metadata from boot partition
bool bNodeIsConnected = false;
bool bNodeIsResponding = false;
@ -268,7 +321,6 @@ esp_err_t errMeshOtaMasterEndpoint(bool* const cpbNewOTAImage, const mesh_addr_t
bNodeIsConnected = true; //node is one of the neighbours
xSemaphoreTake(bsOTAProcess, portMAX_DELAY); //wait for binary semaphore that allows to start the OTA process
ESP_LOGI(LOG_TAG, "Mesh-Master: send Version_Request to 0x%x", cpcMeshNodeAddr->addr[5]);
ERROR_CHECK(errMeshOtaUtilSendOTAVersionRequest(cpcMeshNodeAddr)); //send OTA_VERSION_REQUEST with local version in payload
for (uint32_t u32Index = 0; u32Index < QUEUE_MESSAGE_OTA_SIZE; u32Index++) //loop through all OTA messages
@ -285,8 +337,8 @@ esp_err_t errMeshOtaMasterEndpoint(bool* const cpbNewOTAImage, const mesh_addr_t
if((err == ESP_OK) && (sOTAMessage.type == OTA_Version_Response) && (bMeshNetworkCheckMACEquality(sOTAMessage.meshSenderAddr.addr, cpcMeshNodeAddr->addr))) //if OTA_Version_Request
{
bNodeIsResponding = true;
pBootPartition = esp_ota_get_boot_partition(); //get boot partition (that will booted after reset), not the running partition
ERROR_CHECK(esp_ota_get_partition_description(pBootPartition, &bootPartitionDesc)); //get metadata of partition
cpBootPartition = esp_ota_get_boot_partition(); //get boot partition (that will booted after reset), not the running partition
ERROR_CHECK(esp_ota_get_partition_description(cpBootPartition, &bootPartitionDesc)); //get metadata of partition
if((bMeshOtaUtilNewerVersion((bootPartitionDesc).version, (char*) sOTAMessage.au8Payload)) && (err == ESP_OK)) //compare local and remote version
{
@ -313,7 +365,7 @@ esp_err_t errMeshOtaMasterEndpoint(bool* const cpbNewOTAImage, const mesh_addr_t
else
{
// OTA Message queue is empty --> wait some time
ESP_LOGI(LOG_TAG, "OTA-Master: OTA Message queue is empty --> wait some time");
ESP_LOGD(LOG_TAG, "OTA-Master: OTA Message queue is empty --> wait some time");
vTaskDelay( (1000/QUEUE_MESSAGE_OTA_SIZE) / portTICK_PERIOD_MS);
}
}//end loop
@ -323,7 +375,7 @@ esp_err_t errMeshOtaMasterEndpoint(bool* const cpbNewOTAImage, const mesh_addr_t
if((bNodeIsResponding == false) && (bNodeIsConnected == true))
{
//add node back to queue if connected and NOT responding
ESP_LOGI(LOG_TAG, "OTA-Master: connected and NOT responding --> add node back to queue ");
ESP_LOGD(LOG_TAG, "OTA-Master: connected and NOT responding --> add node back to queue ");
vMeshOtaUtilAddNodeToPossibleUpdatableQueue(cpcMeshNodeAddr->addr);
}
return err;

7
components/mesh_ota/Mesh_OTA_Globals.c

@ -1,3 +1,10 @@
/**
* @file Mesh_OTA_Globals.c
* @brief global variables unsed in Mesh_OTA
* @author Hendrik Schutter
* @date 21.01.2021
*/
#include "Mesh_OTA_Globals.h"
xQueueHandle queueNodes; //nodes that should be checked for ota update (contains children and parent)

128
components/mesh_ota/Mesh_OTA_Partition_Access.c

@ -1,3 +1,13 @@
/**
* @file Mesh_OTA_Partition_Access.c
* @brief Write and read partition if requested from Mesh_OTA
* @author Hendrik Schutter
* @date 21.01.2021
*
* Additional Infos: Write image via HTTPS
* Receive or transmit via Mesh
*/
#include "Mesh_OTA.h"
#include "Mesh_OTA_Util.h"
#include "Mesh_OTA_Globals.h"
@ -5,6 +15,18 @@
static const char *LOG_TAG = "mesh_ota_partition_access";
/**
* @fn esp_err_t errMeshOtaPartitionAccessHttps(bool* const cpbNewOTAImage)
* @brief Downloads and writes the image from the server to partition
* @param cpbNewOTAImage pointer to boolean to signal if a new image was successfully received
* @return ESP32 error code
* @author Hendrik Schutter
* @date 21.01.2021
*
* Checks if the image on server is newer
* Downloads the image in segements
* Handles OTA process
*/
esp_err_t errMeshOtaPartitionAccessHttps(bool* const cpbNewOTAImage)
{
esp_err_t err = ESP_OK;
@ -22,7 +44,8 @@ esp_err_t errMeshOtaPartitionAccessHttps(bool* const cpbNewOTAImage)
ERROR_CHECK(errMeshOtaUtilExtractVersionNumber(u8OTABuffer, &u32BytesRead, pcRemoteVersionNumber)); //extract version numbers
if(err == ESP_OK) //check if version number is found
//check if version number is found
if(err == ESP_OK)
{
xSemaphoreTake(bsOTAProcess, portMAX_DELAY); //wait for binary semaphore that allows to start the OTA process
@ -35,8 +58,8 @@ esp_err_t errMeshOtaPartitionAccessHttps(bool* const cpbNewOTAImage)
ESP_LOGI(LOG_TAG, "Server: image is newer --> OTA update required");
ERROR_CHECK(errMeshOtaUtilFindImageStart(u8OTABuffer, &u32BufferLenght, &u32StartOffset)); //get image start offset
ERROR_CHECK(esp_ota_begin(pOTAPartition, OTA_SIZE_UNKNOWN, &otaHandle)); //start ota update process
if(err == ESP_OK)
{
//image download and ota partition write
@ -54,7 +77,8 @@ esp_err_t errMeshOtaPartitionAccessHttps(bool* const cpbNewOTAImage)
u32OTABytesWritten = u32OTABytesWritten + u32BytesRead; //update counter
}
}
while ((u32BytesRead > 0) && (err == ESP_OK) && (u32OTABytesWritten <= pOTAPartition->size)); //loop until error or complete image downloaded
//loop until error or complete image downloaded
while ((u32BytesRead > 0) && (err == ESP_OK) && (u32OTABytesWritten <= pOTAPartition->size));
}
if(err == ESP_OK)
@ -70,7 +94,7 @@ esp_err_t errMeshOtaPartitionAccessHttps(bool* const cpbNewOTAImage)
else
{
//error occurred --> abort ota update process
ESP_LOGE(LOG_TAG, "abort ota process due to error 0x%x -> %s", err, esp_err_to_name(err));
ESP_LOGE(LOG_TAG, "abort OTA process due to error 0x%x -> %s", err, esp_err_to_name(err));
ERROR_CHECK(esp_ota_abort(otaHandle));
*cpbNewOTAImage = false; //ota update failed
}
@ -84,27 +108,39 @@ esp_err_t errMeshOtaPartitionAccessHttps(bool* const cpbNewOTAImage)
return err;
}
/**
* @fn esp_err_t errMeshOtaPartitionAccessMeshTransmit(const mesh_addr_t* const cpcMeshNodeAddr)
* @brief Reads the local image and sends it to node
* @param cpcMeshNodeAddr pointer to mesh node addr to send the image segments to
* @return ESP32 error code
* @author Hendrik Schutter
* @date 21.01.2021
*
* Reads the newest OTA image in segments
* Sends the image to mesh node in segments
* Handles OTA process
*/
esp_err_t errMeshOtaPartitionAccessMeshTransmit(const mesh_addr_t* const cpcMeshNodeAddr)
{
esp_err_t err = ESP_OK;
const esp_partition_t* pBootPartition; //pointer to boot partition (that will booted after reset)
const esp_partition_t* pBootPartition = NULL; //pointer to boot partition (that will booted after reset)
MESH_PACKET_t sMeshPacket; //packet for sending and receiving
// uint32_t u32Index = 0U; //index for partition read offset
bool bAbort = false; //abort the OTA process
bool bNodeIsResponding = false; //remote node is still active
uint32_t u32OTABytesWritten = 0U;
uint32_t u32SegmentCounter = 0U;
uint32_t u32OTABytesWritten = 0U; //counter of bytes unsed for progress log
uint32_t u32SegmentCounter = 0U; //counter of segments unsed for progress log
pBootPartition = esp_ota_get_boot_partition(); //get boot partition (that will booted after reset), not the running partition
//loop through partition to read in segmensts until end or error or abort called
while( ((OTA_MESH_SEGMENT_SIZE * u32SegmentCounter) < pBootPartition->size) && (err == ESP_OK) && (bAbort == false))
{
bNodeIsResponding = false; //reset to default
bNodeIsResponding = false; //reset to default for this loop
// read partition with offset based in index
ERROR_CHECK(esp_partition_read(pBootPartition, (OTA_MESH_SEGMENT_SIZE * u32SegmentCounter), sMeshPacket.au8Payload, OTA_MESH_SEGMENT_SIZE));
u32OTABytesWritten = ((u32SegmentCounter+1) * OTA_MESH_SEGMENT_SIZE);
u32OTABytesWritten = ((u32SegmentCounter+1) * OTA_MESH_SEGMENT_SIZE); //calc bytes that are written in this ota process
vMeshOtaUtilPrintOTAProgress(&(pBootPartition->size), &u32OTABytesWritten, Transmitter);
if(err == ESP_OK)
@ -115,10 +151,9 @@ esp_err_t errMeshOtaPartitionAccessMeshTransmit(const mesh_addr_t* const cpcMesh
if((OTA_MESH_SEGMENT_SIZE * (u32SegmentCounter+1)) >= pBootPartition->size) //check if last segment
{
//last partition image segment --> send OTA_Complete
ESP_LOGI(LOG_TAG, "OTA-TX: last segment--> send Complete");
ESP_LOGD(LOG_TAG, "OTA-TX: last segment--> send Complete");
sMeshPacket.type = OTA_Complete;
}
//ESP_LOGI(LOG_TAG, "OTA-TX: send packet");
err = errMeshNetworkSendMeshPacket(cpcMeshNodeAddr, &sMeshPacket);
}
else
@ -126,39 +161,35 @@ esp_err_t errMeshOtaPartitionAccessMeshTransmit(const mesh_addr_t* const cpcMesh
// error while read --> send OTA_ABORT and abort this OTA process
sMeshPacket.type = OTA_Abort;
bAbort = true;
ESP_LOGI(LOG_TAG, "OTA-TX: error while read --> send ABORT");
ESP_LOGE(LOG_TAG, "OTA-TX: error while read --> send ABORT");
errMeshNetworkSendMeshPacket(cpcMeshNodeAddr, &sMeshPacket);
}
// loop through all OTA messages or until abort is called or error
for (uint32_t u32Index = 0; ((u32Index < QUEUE_MESSAGE_OTA_SIZE) && (bAbort == false) && (err == ESP_OK)); u32Index++) //loop through all OTA messages
{
// if(uxQueueSpacesAvailable(queueMessageOTA) < QUEUE_MESSAGE_OTA_SIZE)
// {
//queue not empty
//get OTA message from queue
if (xQueueReceive(queueMessageOTA, &sMeshPacket, ((OTA_MESH_TIMEOUT) / portTICK_PERIOD_MS)) != pdTRUE)
{
ESP_LOGE(LOG_TAG, "Unable to receive OTA Messages from queue");
err = ESP_FAIL;
}
if((err == ESP_OK) && (bMeshNetworkCheckMACEquality(sMeshPacket.meshSenderAddr.addr, cpcMeshNodeAddr->addr))) //if OTA_Version_Request
//check if from correct node
if((err == ESP_OK) && (bMeshNetworkCheckMACEquality(sMeshPacket.meshSenderAddr.addr, cpcMeshNodeAddr->addr)))
{
//packet from node received
//packet from node received --> handle it
switch (sMeshPacket.type)
{
case OTA_ACK: //increase index for next round
u32Index++;
case OTA_ACK: //start next loop for segment
bNodeIsResponding = true;
u32Index = QUEUE_MESSAGE_OTA_SIZE;//this will end the loop through all OTA messages
u32Index = QUEUE_MESSAGE_OTA_SIZE; //this will end the loop through all OTA messages
break;
case OTA_Abort: //abort this OTA process
bAbort = true;
bNodeIsResponding = true;
break;
default:
//receives wrong OTA message type from node --> back to queue
//vMeshOtaUtilAddOtaMessageToQueue(&sMeshPacket);
break;
}
}
@ -167,16 +198,6 @@ esp_err_t errMeshOtaPartitionAccessMeshTransmit(const mesh_addr_t* const cpcMesh
//received from wrong node --> back to queue
vMeshOtaUtilAddOtaMessageToQueue(&sMeshPacket);
}
/*
}
else
{
// OTA Message queue is empty --> wait some time
ESP_LOGI(LOG_TAG, "OTA-TX: ota message queue empty --> wait");
vTaskDelay( (OTA_MESH_TIMEOUT) / portTICK_PERIOD_MS);
}
*/
}//end OTA message loop
if(bNodeIsResponding == false)
@ -188,10 +209,23 @@ esp_err_t errMeshOtaPartitionAccessMeshTransmit(const mesh_addr_t* const cpcMesh
}
u32SegmentCounter++;
}//end of partition segment loop
vMeshOtaUtilClearOtaMessageQueue(cpcMeshNodeAddr);
vMeshOtaUtilClearOtaMessageQueue(cpcMeshNodeAddr); //remove all OTA messages from remote node
return err;
}
/**
* @fn esp_err_t errMeshOtaPartitionAccessMeshReceive(bool* const cpbNewOTAImage, const mesh_addr_t* const cpcMeshNodeAddr)
* @brief Downloads and writes the image from the remote node
* @param cpbNewOTAImage pointer to boolean to signal if a new image was successfully received
* @param cpcMeshNodeAddr pointer to mesh node addr to receive the image segments from
* @return ESP32 error code
* @author Hendrik Schutter
* @date 21.01.2021
*
* Receives the images segments from remote node
* Writtes segments to OTA partition
* Handles OTA process
*/
esp_err_t errMeshOtaPartitionAccessMeshReceive(bool* const cpbNewOTAImage, const mesh_addr_t* const cpcMeshNodeAddr)
{
esp_err_t err = ESP_OK;
@ -201,8 +235,8 @@ esp_err_t errMeshOtaPartitionAccessMeshReceive(bool* const cpbNewOTAImage, const
bool bNodeIsResponding = false; //remote node is still active
uint32_t u32OTABytesWritten = 0U; //counter unsed for progress log
static esp_ota_handle_t otaHandle; //OTA process handle
*cpbNewOTAImage = false;
uint32_t u32SegmentCounter = 0U;
*cpbNewOTAImage = false; //set default to false
uint32_t u32SegmentCounter = 0U; //counter of segments unsed for progress log
ERROR_CHECK(esp_ota_begin(pOTAPartition, OTA_SIZE_UNKNOWN, &otaHandle)); //start ota update process
@ -230,24 +264,21 @@ esp_err_t errMeshOtaPartitionAccessMeshReceive(bool* const cpbNewOTAImage, const
{
case OTA_Complete: //signal end of this OTA process, fall through because same behavior as OTA_Data
bComplete = true;
ESP_LOGI(LOG_TAG, "OTA-RX: rec Complete --> last segment");
//fall through
//fall through
case OTA_Data: //data segement received
bNodeIsResponding = true;
ERROR_CHECK(esp_ota_write(otaHandle, sMeshPacket.au8Payload, OTA_MESH_SEGMENT_SIZE));
u32OTABytesWritten = ((u32SegmentCounter+1) * OTA_MESH_SEGMENT_SIZE);
vMeshOtaUtilPrintOTAProgress(&(pOTAPartition->size), &u32OTABytesWritten, Receiver);
u32OTABytesWritten = ((u32SegmentCounter+1) * OTA_MESH_SEGMENT_SIZE); //calc bytes that are written in this ota process
vMeshOtaUtilPrintOTAProgress(&(pOTAPartition->size), &u32OTABytesWritten, Receiver);
u32Index = QUEUE_MESSAGE_OTA_SIZE; //this will end the loop through all OTA messages
break;
case OTA_Abort: //abort this OTA process
bAbort = true;
bNodeIsResponding = true;
ESP_LOGI(LOG_TAG, "OTA-RX: rec Abort");
ESP_LOGE(LOG_TAG, "OTA-RX: receives abort --> abort this OTA process on this node");
//this will end the loop through all OTA messages
break;
default:
//receives wrong OTA message type from node --> back to queue
//vMeshOtaUtilAddOtaMessageToQueue(&sMeshPacket);
break;
}
}
@ -256,16 +287,6 @@ esp_err_t errMeshOtaPartitionAccessMeshReceive(bool* const cpbNewOTAImage, const
//received from wrong node --> back to queue
vMeshOtaUtilAddOtaMessageToQueue(&sMeshPacket);
}
/* }
else
{
ESP_LOGI(LOG_TAG, "OTA-RX: ota message queue empty --> wait");
// OTA Message queue is empty --> wait some time
vTaskDelay( (OTA_MESH_TIMEOUT) / portTICK_PERIOD_MS);
}
*/
}//end of OTA message loop
if(bNodeIsResponding == false)
@ -284,7 +305,6 @@ esp_err_t errMeshOtaPartitionAccessMeshReceive(bool* const cpbNewOTAImage, const
if(bAbort == false)
{
//no error while ota write --> send OTA_ACK packet
//ESP_LOGI(LOG_TAG, "OTA-RX: no error while ota write --> send OTA_ACK packet");
sMeshPacket.type = OTA_ACK;
err = errMeshNetworkSendMeshPacket(cpcMeshNodeAddr, &sMeshPacket);
}
@ -305,7 +325,7 @@ esp_err_t errMeshOtaPartitionAccessMeshReceive(bool* const cpbNewOTAImage, const
{
//all OTA segments received --> validate
ESP_LOGI(LOG_TAG, "OTA-RX: validate image ");
ERROR_CHECK(esp_ota_end(otaHandle));
ERROR_CHECK(esp_ota_end(otaHandle)); //validate image
ERROR_CHECK(esp_ota_set_boot_partition(pOTAPartition));
if(err == ESP_OK)
{

163
components/mesh_ota/Mesh_OTA_Util.c

@ -1,17 +1,32 @@
/**
* @file Mesh_OTA_Util.c
* @brief Utility and helper functions to perfrom mesh OTA updates
* @author Hendrik Schutter
* @date 21.01.2021
*/
#include "Mesh_OTA_Util.h"
#include "Mesh_OTA_Globals.h"
static const char *LOG_TAG = "mesh_ota";
/**
* @fn bool bMeshOtaUtilNewerVersion(const char* cpu8Local, const char* cpu8Remote)
* @brief compares to version strings
* @param cpu8Local local image version string
* @param cpu8Remote remote image version string
* @return bool
* @author Hendrik Schutter
* @date 21.01.2021
*
* Returns true if remote is newer
*/
bool bMeshOtaUtilNewerVersion(const char* cpu8Local, const char* cpu8Remote)
{
/*
* Return true if remote version is newer (higher) than local version
*/
char u8LocalTmp[12]; //local version
char u8RemoteTmp[12]; //remote version
char* pu8saveptrLocal; //context for strok_r
char* pu8saveptrRemote; //context for strok_r
char* pu8saveptrLocal = NULL; //context for strok_r
char* pu8saveptrRemote = NULL; //context for strok_r
bool bReturn = false; //flag to stop loop
uint8_t u8Index = 0; //numbers counter in version string
@ -34,6 +49,18 @@ bool bMeshOtaUtilNewerVersion(const char* cpu8Local, const char* cpu8Remote)
return bReturn;
}
/**
* @fn esp_err_t errMeshOtaUtilExtractVersionNumber(const char* cpu8Data, uint32_t* const cpcu32DataLenght, char* const pc8RemoteVersionNumber)
* @brief extract version number from image data
* @param cpu8Data image data buffer
* @param cpcu32DataLenght pointer to lenght of image data
* @param pc8RemoteVersionNumber pointer version number
* @return ESP32 error code
* @author Hendrik Schutters
* @date 21.01.2021
*
* Search version number in raw image data
*/
esp_err_t errMeshOtaUtilExtractVersionNumber(const char* cpu8Data, uint32_t* const cpcu32DataLenght, char* const pc8RemoteVersionNumber)
{
uint32_t u32StartOffset;
@ -51,14 +78,25 @@ esp_err_t errMeshOtaUtilExtractVersionNumber(const char* cpu8Data, uint32_t* con
return err;
}
/**
* @fn esp_err_t errMeshOtaUtilFindImageStart(const char* const cpu8Data, const uint32_t* const cpcu32DataLenght, uint32_t* const cpu32StartOffset)
* @brief find start offset from image raw data
* @param cpu8Data image data buffer
* @param cpcu32DataLenght pointer to lenght of image data
* @param cpu32StartOffset pointer to determined offset
* @return ESP32 error code
* @author Hendrik Schutter
* @date 21.01.2021
*
* Search offset in raw image data from server (exclude HTTP response)
*/
esp_err_t errMeshOtaUtilFindImageStart(const char* const cpu8Data, const uint32_t* const cpcu32DataLenght, uint32_t* const cpu32StartOffset)
{
/*
Offset value
0 = 0xE9 (first byte in image --> magic byte)
48 = first digit of version number
*/
// Offset value
// 0 = 0xE9 (first byte in image --> magic byte)
// 48 = first digit of version number
esp_err_t errReturn = ESP_OK;
bool bImageStartOffsetFound = false;
uint32_t u32DataIndex = 0;
@ -129,13 +167,21 @@ esp_err_t errMeshOtaUtilFindImageStart(const char* const cpu8Data, const uint32_
return errReturn;
}
/**
* @fn esp_err_t errMeshOtaUtilSendOTAVersionRequest(const mesh_addr_t* const cpcMeshReceiverAddr)
* @brief send OTA_Version_Request to node
* @param cpcMeshReceiverAddr node addr
* @return ESP32 error code
* @author Hendrik Schutter
* @date 21.01.2021
*/
esp_err_t errMeshOtaUtilSendOTAVersionRequest(const mesh_addr_t* const cpcMeshReceiverAddr)
{
esp_err_t err = ESP_OK;
MESH_PACKET_t packet;
packet.type = OTA_Version_Request;
const esp_partition_t* pBootPartition; //pointer to boot partition (that will booted after reset)
const esp_partition_t* pBootPartition = NULL; //pointer to boot partition (that will booted after reset)
esp_app_desc_t bootPartitionDesc; //Metadata from boot partition
pBootPartition = esp_ota_get_boot_partition(); //get boot partition (that will booted after reset), not the running partition
@ -145,25 +191,43 @@ esp_err_t errMeshOtaUtilSendOTAVersionRequest(const mesh_addr_t* const cpcMeshRe
return err;
}
/**
* @fn esp_err_t errMeshOtaUtilSendOTAVersionResponse(const mesh_addr_t* const cpcMeshReceiverAddr)
* @brief send OTA_Version_Response to node
* @param cpcMeshReceiverAddr node addr
* @return ESP32 error code
* @author Hendrik Schutter
* @date 21.01.2021
*/
esp_err_t errMeshOtaUtilSendOTAVersionResponse(const mesh_addr_t* const cpcMeshReceiverAddr)
{
esp_err_t err = ESP_OK;
MESH_PACKET_t packet;
packet.type = OTA_Version_Response;
const esp_partition_t* pBootPartition; //pointer to boot partition (that will booted after reset)
const esp_partition_t* pBootPartition = NULL; //pointer to boot partition (that will booted after reset)
esp_app_desc_t bootPartitionDesc; //Metadata from boot partition
pBootPartition = esp_ota_get_boot_partition(); //get boot partition (that will booted after reset), not the running partition
ERROR_CHECK(esp_ota_get_partition_description(pBootPartition, &bootPartitionDesc)); //get metadate of partition
memcpy(&packet.au8Payload, &bootPartitionDesc.version, 12); //copy local version to OTA_Version_Response packet
ESP_LOGI(LOG_TAG, "Send OTA_Version_Response to 0x%x", cpcMeshReceiverAddr->addr[5]);
ESP_LOGD(LOG_TAG, "Send OTA_Version_Response to 0x%x", cpcMeshReceiverAddr->addr[5]);
err = errMeshNetworkSendMeshPacket(cpcMeshReceiverAddr, &packet);
return err;
}
/**
* @fn void vMeshOtaUtilPrintOTAProgress(const uint32_t* const cpcu32TotalImageSize, const uint32_t* const cpcu32BytesWritten, const OTA_MESH_ROLE_t ceRole)
* @brief print LOG for OTA process progress
* @param cpcu32TotalImageSize size of OTA partition
* @param cpcu32BytesWritten actual bytes written
* @param ceRole role if this OTA process
* @return void
* @author Hendrik Schutter
* @date 21.01.2021
*/
void vMeshOtaUtilPrintOTAProgress(const uint32_t* const cpcu32TotalImageSize, const uint32_t* const cpcu32BytesWritten, const OTA_MESH_ROLE_t ceRole)
{
uint32_t u32Percentage = 0U;
@ -196,6 +260,14 @@ void vMeshOtaUtilPrintOTAProgress(const uint32_t* const cpcu32TotalImageSize, co
}
}
/**
* @fn void vMeshOtaUtilAddAllNeighboursToQueue(void)
* @brief add all neigbhours (children and parent) to queue
* @param void
* @return void
* @author Hendrik Schutter
* @date 21.01.2021
*/
void vMeshOtaUtilAddAllNeighboursToQueue(void)
{
esp_err_t err = ESP_OK;
@ -209,7 +281,6 @@ void vMeshOtaUtilAddAllNeighboursToQueue(void)
if(err == ESP_OK)
{
vMeshOtaUtilAddNodeToPossibleUpdatableQueue(addrParent.addr);
ESP_LOGI(LOG_TAG, "added parent");
}
err = ESP_OK; //reset error code
@ -219,10 +290,17 @@ void vMeshOtaUtilAddAllNeighboursToQueue(void)
for (uint16_t u16Index = 0; ((u16Index < u16ChildrenSize) && (err == ESP_OK)); u16Index++)
{
vMeshOtaUtilAddNodeToPossibleUpdatableQueue(childrenAddr[u16Index].addr);
ESP_LOGI(LOG_TAG, "added child");
}
}
/**
* @fn void vMeshOtaUtilClearOtaMessageQueue(const mesh_addr_t* const cpcMeshNodeAddr)
* @brief remode all OTA messages from this node in queue
* @param cpcMeshNodeAddr node addr
* @return void
* @author Hendrik Schutter
* @date 21.01.2021
*/
void vMeshOtaUtilClearOtaMessageQueue(const mesh_addr_t* const cpcMeshNodeAddr)
{
MESH_PACKET_t sMeshPacket; //packet for sending and receiving
@ -235,14 +313,18 @@ void vMeshOtaUtilClearOtaMessageQueue(const mesh_addr_t* const cpcMeshNodeAddr)
//received OTA message is NOT from cpcMeshNodeAddr --> keep it in queue
vMeshOtaUtilAddOtaMessageToQueue(&sMeshPacket);
}
else
{
ESP_LOGI(LOG_TAG, "Removed type %i from node 0x%x", sMeshPacket.type, cpcMeshNodeAddr->addr[5]);
}
}
}//end OTA message loop
}
/**
* @fn void vMeshOtaUtilClearNeighboursQueue(const mesh_addr_t* const cpcMeshNodeAddr)
* @brief remode all instances of this node in queue
* @param cpcMeshNodeAddr node addr
* @return void
* @author Hendrik Schutter
* @date 21.01.2021
*/
void vMeshOtaUtilClearNeighboursQueue(const mesh_addr_t* const cpcMeshNodeAddr)
{
mesh_addr_t sNode; //packet for sending and receiving
@ -255,15 +337,18 @@ void vMeshOtaUtilClearNeighboursQueue(const mesh_addr_t* const cpcMeshNodeAddr)
//node is NOT cpcMeshNodeAddr --> keep it in queue
vMeshOtaUtilAddNodeToPossibleUpdatableQueue(cpcMeshNodeAddr->addr);
}
else
{
ESP_LOGI(LOG_TAG, "Removed node 0x%x", cpcMeshNodeAddr->addr[5]);
}
}
}//end OTA message loop
}
/**
* @fn void vMeshOtaUtilAddNodeToPossibleUpdatableQueue(const uint8_t* const cpcu8MAC)
* @brief add node instance to queue
* @param cpcu8MAC MAC addr of node
* @return void
* @author Hendrik Schutter
* @date 21.01.2021
*/
void vMeshOtaUtilAddNodeToPossibleUpdatableQueue(const uint8_t* const cpcu8MAC)
{
//send payload to node queues
@ -276,10 +361,18 @@ void vMeshOtaUtilAddNodeToPossibleUpdatableQueue(const uint8_t* const cpcu8MAC)
}
else
{
ESP_LOGI(LOG_TAG, "added node \"%x:%x:%x:%x:%x:%x\" to possible updatable queue", addrNode.addr[0], addrNode.addr[1], addrNode.addr[2], addrNode.addr[3], addrNode.addr[4], addrNode.addr[5]);
ESP_LOGD(LOG_TAG, "added node \"%x:%x:%x:%x:%x:%x\" to possible updatable queue", addrNode.addr[0], addrNode.addr[1], addrNode.addr[2], addrNode.addr[3], addrNode.addr[4], addrNode.addr[5]);
}
}
/**
* @fn void vMeshOtaUtilAddOtaMessageToQueue(const MESH_PACKET_t* const cpcuMeshPacket)
* @brief add OTA message to queue
* @param cpcuMeshPacket OTA message
* @return void
* @author Hendrik Schutter
* @date 21.01.2021
*/
void vMeshOtaUtilAddOtaMessageToQueue(const MESH_PACKET_t* const cpcuMeshPacket)
{
//send ota packet to packet queue
@ -292,14 +385,14 @@ void vMeshOtaUtilAddOtaMessageToQueue(const MESH_PACKET_t* const cpcuMeshPacket)
switch (cpcuMeshPacket->type)
{
case OTA_Abort:
ESP_LOGI(LOG_TAG, "added ota message to queue: OTA_Abort from 0x%x", cpcuMeshPacket->meshSenderAddr.addr[5]);
ESP_LOGD(LOG_TAG, "added ota message to queue: OTA_Abort from 0x%x", cpcuMeshPacket->meshSenderAddr.addr[5]);
break;
case OTA_Version_Request:
ESP_LOGI(LOG_TAG, "added ota message to queue: OTA_Version_Request from 0x%x", cpcuMeshPacket->meshSenderAddr.addr[5]);
ESP_LOGD(LOG_TAG, "added ota message to queue: OTA_Version_Request from 0x%x", cpcuMeshPacket->meshSenderAddr.addr[5]);
break;
case OTA_Version_Response:
ESP_LOGI(LOG_TAG, "added ota message to queue: OTA_Version Response from 0x%x", cpcuMeshPacket->meshSenderAddr.addr[5]);
ESP_LOGD(LOG_TAG, "added ota message to queue: OTA_Version Response from 0x%x", cpcuMeshPacket->meshSenderAddr.addr[5]);
break;
default:
break;
@ -307,14 +400,20 @@ void vMeshOtaUtilAddOtaMessageToQueue(const MESH_PACKET_t* const cpcuMeshPacket)
}
}
void vMeshOtaUtilChangeStateOfServerWorker(const bool cbState) //allow access via function ptn to network_handler
/**
* @fn void vMeshOtaUtilChangeStateOfServerWorker(const bool cbState)
* @brief callback for mesh network if connectivity to server changed
* @param cbState boolean state
* @return void
* @author Hendrik Schutter
* @date 21.01.2021
*/
void vMeshOtaUtilChangeStateOfServerWorker(const bool cbState)
{
static bool bLastState = false;
if(cbState != bLastState) //change only if necessary
{
ESP_LOGI(LOG_TAG, "server worker change handler");
if(cbState == true)
{
if (xSemaphoreGive(bsStartStopServerWorker) != pdTRUE)

9
components/mesh_ota/include/Mesh_Network.h

@ -1,3 +1,12 @@
/**
* @file Mesh_Network.h
* @brief Mesh network layer used by OTA and APP
* @author Hendrik Schutter, init based in ESP32-IDE code
* @date 20.01.2021
*
* Additional Infos: Start network and send and receive data.
*/
#ifndef H_MESH_NETWORK
#define H_MESH_NETWORK

7
components/mesh_ota/include/Mesh_OTA.h

@ -1,3 +1,10 @@
/**
* @file Mesh_OTA.h
* @brief Start and implement OTA updates via HTTPS from server and other mesh nodes (bidirectional)
* @author Hendrik Schutter
* @date 21.01.2021
*/
#ifndef H_MESH_OTA
#define H_MESH_OTA

8
components/mesh_ota/include/Mesh_OTA_Globals.h

@ -1,3 +1,11 @@
/**
* @file Mesh_OTA_Globals.h
* @brief global variables unsed in Mesh_OTA
* @author Hendrik Schutter
* @date 21.01.2021
*
*/
#ifndef H_MESH_OTA_GLOBALS
#define H_MESH_OTA_GLOBALS

10
components/mesh_ota/include/Mesh_OTA_Partition_Access.h

@ -1,3 +1,13 @@
/**
* @file Mesh_OTA_Partition_Access.h
* @brief Write and read partition if requested from Mesh_OTA
* @author Hendrik Schutter
* @date 21.01.2021
*
* Additional Infos: Write image via HTTPS
* Receive or transmit via Mesh
*/
#ifndef H_MESH_OTA_PARTITION_ACCESS
#define H_MESH_OTA_PARTITION_ACCESS

7
components/mesh_ota/include/Mesh_OTA_Util.h

@ -1,3 +1,10 @@
/**
* @file Mesh_OTA_Util.h
* @brief Utility and helper functions to perfrom mesh OTA updates
* @author Hendrik Schutter
* @date 21.01.2021
*/
#ifndef H_MESH_OTA_UTIL
#define H_MESH_OTA_UTIL

Loading…
Cancel
Save

Du besuchst diese Seite mit einem veralteten IPv4-Internetzugang. Möglicherweise treten in Zukunft Probleme mit der Erreichbarkeit und Performance auf. Bitte frage deinen Internetanbieter oder Netzwerkadministrator nach IPv6-Unterstützung.
You are visiting this site with an outdated IPv4 internet access. You may experience problems with accessibility and performance in the future. Please ask your ISP or network administrator for IPv6 support.
Weitere Infos | More Information
Klicke zum schließen | Click to close