add ota algorithm for device
1. effective "Differential Upgrade" patch algorithm with high compression rate 2. effective recovery algorithm support recovery firmware in blocks which has low memory consumption and wear-leveling strategies, especially suitable for embeded devices with low RAM. 3. add sample ota bootloader project, see: board\TencentOS_tiny_EVB_MX_Plus\KEIL\ota\ota_bootloader_recovery 4. add sample application project for download firmware through http, see: board\TencentOS_tiny_EVB_MX_Plus\KEIL\ota\ota_application_download_through_http 5. add sample application project for download firmware through qcloud explorer console, see: board\TencentOS_tiny_EVB_MX_Plus\KEIL\ota\ota_application_download_through_qcloud_iot_explorer 6. an OTA markdown document is pending
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
#include "esp8266.h"
|
||||
#include "mcu_init.h"
|
||||
#include "sal_module_wrapper.h"
|
||||
#include "cmsis_os.h"
|
||||
|
||||
#include "tos_ota_download.h"
|
||||
|
||||
extern ota_flash_drv_t stm32l4_norflash_onchip_drv_ota;
|
||||
extern ota_flash_prop_t stm32l4_norflash_onchip_prop_ota;
|
||||
|
||||
k_sem_t sem;
|
||||
|
||||
#define TASK1_STK_SIZE 512
|
||||
void user_task(void *arg);
|
||||
osThreadDef(user_task, osPriorityNormal, 1, TASK1_STK_SIZE);
|
||||
|
||||
#define TASK2_STK_SIZE 1024
|
||||
void ota_download_task(void *arg);
|
||||
osThreadDef(ota_download_task, osPriorityNormal, 1, TASK2_STK_SIZE);
|
||||
|
||||
void user_task(void *arg)
|
||||
{
|
||||
int iter = 0;
|
||||
|
||||
while (K_TRUE) {
|
||||
tos_task_delay(1000);
|
||||
|
||||
printf("do sth(v1.1)...\n");
|
||||
|
||||
if (++iter == 2) {
|
||||
printf("trigger ota download\n");
|
||||
tos_sem_post(&sem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ota_download_task(void *arg)
|
||||
{
|
||||
tos_sem_pend(&sem, TOS_TIME_FOREVER);
|
||||
|
||||
esp8266_sal_init(HAL_UART_PORT_0);
|
||||
esp8266_join_ap("SheldonDai", "srnr6x9xbhmb0");
|
||||
|
||||
uint32_t partition_addr = 0x08024800;
|
||||
|
||||
if (tos_ota_env_init(OTA_UPDATE_IN_POSITION, partition_addr, &stm32l4_norflash_onchip_drv_ota, &stm32l4_norflash_onchip_prop_ota) < 0) {
|
||||
printf("env init failed!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!tos_ota_download_http("http://39.108.190.129:8000/patch.bin")) {
|
||||
printf("download successfully!\n");
|
||||
} else {
|
||||
printf("download failed!\n");
|
||||
}
|
||||
}
|
||||
|
||||
void application_entry(void *arg)
|
||||
{
|
||||
tos_sem_create(&sem, 0);
|
||||
|
||||
osThreadCreate(osThread(user_task), NULL); // Create task1
|
||||
osThreadCreate(osThread(ota_download_task), NULL); // Create task2
|
||||
}
|
||||
|
@@ -17,16 +17,19 @@
|
||||
#include "esp8266.h"
|
||||
#endif
|
||||
|
||||
#include "ota_download.h"
|
||||
|
||||
extern ota_flash_drv_t stm32l4_norflash_onchip_drv_ota;
|
||||
extern ota_flash_prop_t stm32l4_norflash_onchip_prop_ota;
|
||||
|
||||
void application_entry(void *arg)
|
||||
{
|
||||
extern void mqtt_basic_thread(void);
|
||||
|
||||
#ifdef USE_LWIP
|
||||
dns_init();
|
||||
MX_LWIP_Init();
|
||||
#endif
|
||||
|
||||
#ifdef USE_ESP8266
|
||||
#ifdef USE_ESP8266x
|
||||
extern int esp8266_sal_init(hal_uart_port_t uart_port);
|
||||
extern int esp8266_join_ap(const char *ssid, const char *pwd);
|
||||
esp8266_sal_init(HAL_UART_PORT_0);
|
||||
@@ -38,10 +41,17 @@ void application_entry(void *arg)
|
||||
bc35_28_95_sal_init(HAL_UART_PORT_0);
|
||||
#endif
|
||||
|
||||
mqtt_basic_thread();
|
||||
uint32_t partition_addr = 0x08024800;
|
||||
|
||||
if (ota_env_init(OTA_UPDATE_IN_POSITION, partition_addr, &stm32l4_norflash_onchip_drv_ota, &stm32l4_norflash_onchip_prop_ota) < 0) {
|
||||
printf("ota env init failed!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
ota_download_through_explorer();
|
||||
|
||||
while (1) {
|
||||
printf("This is a mqtt demo!\r\n");
|
||||
printf("This is a qcloud explorer sdk ota demo!\r\n");
|
||||
tos_task_delay(1000);
|
||||
}
|
||||
}
|
@@ -0,0 +1,503 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright (C) 2016 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
|
||||
* Licensed under the MIT License (the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://opensource.org/licenses/MIT
|
||||
|
||||
* 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "qcloud_iot_export.h"
|
||||
#include "qcloud_iot_import.h"
|
||||
#include "lite-utils.h"
|
||||
|
||||
#include "tos_kv.h"
|
||||
#include "ota_download.h"
|
||||
|
||||
#ifdef AUTH_MODE_CERT
|
||||
static char sg_cert_file[PATH_MAX + 1]; // full path of device cert file
|
||||
static char sg_key_file[PATH_MAX + 1]; // full path of device key file
|
||||
#endif
|
||||
|
||||
static DeviceInfo sg_devInfo;
|
||||
|
||||
static int sg_pub_ack = K_FALSE;
|
||||
static int sg_packet_id = 0;
|
||||
|
||||
static void event_handler(void *pclient, void *handle_context, MQTTEventMsg *msg)
|
||||
{
|
||||
uintptr_t packet_id = (uintptr_t)msg->msg;
|
||||
|
||||
switch (msg->event_type) {
|
||||
case MQTT_EVENT_UNDEF:
|
||||
Log_i("undefined event occur.");
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_DISCONNECT:
|
||||
Log_i("MQTT disconnect.");
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_RECONNECT:
|
||||
Log_i("MQTT reconnect.");
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_SUBCRIBE_SUCCESS:
|
||||
Log_i("subscribe success, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_SUBCRIBE_TIMEOUT:
|
||||
Log_i("subscribe wait ack timeout, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_SUBCRIBE_NACK:
|
||||
Log_i("subscribe nack, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_PUBLISH_SUCCESS:
|
||||
Log_i("publish success, packet-id=%u", (unsigned int)packet_id);
|
||||
if (sg_packet_id == packet_id)
|
||||
sg_pub_ack = K_TRUE;
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_PUBLISH_TIMEOUT:
|
||||
Log_i("publish timeout, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_PUBLISH_NACK:
|
||||
Log_i("publish nack, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
default:
|
||||
Log_i("Should NOT arrive here.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* demo of firmware info management in device side for resuming download from break point */
|
||||
#define KEY_CURR_VERSION "c_vs"
|
||||
#define KEY_MD5 "md5"
|
||||
#define KEY_DOWNLOADED_SIZE "dl_sz"
|
||||
#define KEY_PREV_VERSION "r_vs"
|
||||
|
||||
static void local_firmware_info_get(char **local_version, char **local_md5, char **local_fw_size)
|
||||
{
|
||||
char *prev_version, *json_doc = NULL;
|
||||
|
||||
#define LOCAL_FW_INFO_MAX 128
|
||||
size_t info_len;
|
||||
static char local_fw_info[LOCAL_FW_INFO_MAX];
|
||||
|
||||
if (tos_kv_get("local_fw", local_fw_info, sizeof(local_fw_info), &info_len) != KV_ERR_NONE) {
|
||||
*local_version = NULL;
|
||||
*local_md5 = NULL;
|
||||
*local_fw_size = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
json_doc = local_fw_info;
|
||||
|
||||
*local_version = LITE_json_value_of(KEY_CURR_VERSION, json_doc);
|
||||
*local_md5 = LITE_json_value_of(KEY_MD5, json_doc);
|
||||
*local_fw_size = LITE_json_value_of(KEY_DOWNLOADED_SIZE, json_doc);
|
||||
prev_version = LITE_json_value_of(KEY_PREV_VERSION, json_doc);
|
||||
}
|
||||
|
||||
/* update local firmware info for resuming download from break point */
|
||||
static int local_firmware_info_update(const char *curr_version, const char *prev_version, const char *md5, uint32_t downloaded_size)
|
||||
{
|
||||
#define JSON_DOC_LEN 256
|
||||
static char json_doc[JSON_DOC_LEN];
|
||||
|
||||
memset(json_doc, 0, JSON_DOC_LEN);
|
||||
HAL_Snprintf(json_doc, JSON_DOC_LEN, "{\"%s\":\"%s\", \"%s\":\"%s\",\"%s\":%d,\"%s\":\"%s\"}", \
|
||||
KEY_CURR_VERSION, curr_version,
|
||||
KEY_MD5, md5,
|
||||
KEY_DOWNLOADED_SIZE, downloaded_size, \
|
||||
KEY_PREV_VERSION, prev_version);
|
||||
|
||||
if (tos_kv_set("local_fw", json_doc, strlen(json_doc)) != KV_ERR_NONE) {
|
||||
Log_e("save version failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* get remote firmware information */
|
||||
static int remote_firmware_info_get(void *ota_client, char remote_version[], char remote_md5[], uint32_t *remote_fw_size)
|
||||
{
|
||||
if (IOT_OTA_Ioctl(ota_client, IOT_OTAG_VERSION, remote_version, 128) < 0) {
|
||||
return -1;
|
||||
}
|
||||
if (IOT_OTA_Ioctl(ota_client, IOT_OTAG_MD5SUM, remote_md5, 33) < 0) {
|
||||
return -1;
|
||||
}
|
||||
if (IOT_OTA_Ioctl(ota_client, IOT_OTAG_FILE_SIZE, remote_fw_size, sizeof(uint32_t)) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
Log_d("remote firmware: fw_size: %d version: %s md5: %s\n", *remote_fw_size, remote_version, remote_md5);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* calculate left MD5 for resuming download from break point */
|
||||
static int downloaded_firmware_md5(void *ota_client, size_t size)
|
||||
{
|
||||
#define BUF_SIZE 128
|
||||
static char buf[BUF_SIZE];
|
||||
size_t remain_len, read_len;
|
||||
|
||||
uint32_t ota_flash_addr = ota_partition_start(OTA_PARTITION_OTA);
|
||||
|
||||
remain_len = size;
|
||||
|
||||
#define MIN(a, b) ((a) < (b) ? (a) : (b))
|
||||
while (remain_len > 0) {
|
||||
read_len = MIN(sizeof(buf), remain_len);
|
||||
if (ota_flash_read(ota_flash_addr, buf, read_len) < 0) {
|
||||
return -1;
|
||||
}
|
||||
IOT_OTA_UpdateClientMd5(ota_client, buf, read_len);
|
||||
remain_len -= read_len;
|
||||
ota_flash_addr += read_len;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int connect_init_params_setup(MQTTInitParams* initParams)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = HAL_GetDevInfo((void *)&sg_devInfo);
|
||||
if (QCLOUD_RET_SUCCESS != ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
initParams->device_name = sg_devInfo.device_name;
|
||||
initParams->product_id = sg_devInfo.product_id;
|
||||
|
||||
#ifdef AUTH_MODE_CERT
|
||||
char certs_dir[PATH_MAX + 1] = "certs";
|
||||
char current_path[PATH_MAX + 1];
|
||||
char *cwd = getcwd(current_path, sizeof(current_path));
|
||||
if (cwd == NULL) {
|
||||
Log_e("getcwd return NULL");
|
||||
return QCLOUD_ERR_FAILURE;
|
||||
}
|
||||
sprintf(sg_cert_file, "%s/%s/%s", current_path, certs_dir, sg_devInfo.dev_cert_file_name);
|
||||
sprintf(sg_key_file, "%s/%s/%s", current_path, certs_dir, sg_devInfo.dev_key_file_name);
|
||||
|
||||
initParams->cert_file = sg_cert_file;
|
||||
initParams->key_file = sg_key_file;
|
||||
#else
|
||||
initParams->device_secret = sg_devInfo.device_secret;
|
||||
#endif
|
||||
|
||||
|
||||
initParams->command_timeout = QCLOUD_IOT_MQTT_COMMAND_TIMEOUT;
|
||||
initParams->keep_alive_interval_ms = QCLOUD_IOT_MQTT_KEEP_ALIVE_INTERNAL;
|
||||
initParams->auto_connect_enable = 1;
|
||||
initParams->event_handle.h_fp = event_handler;
|
||||
|
||||
return QCLOUD_RET_SUCCESS;
|
||||
}
|
||||
|
||||
int ota_init(void **mqtt_client, void **ota_client)
|
||||
{
|
||||
int rc = 0;
|
||||
MQTTInitParams init_params = DEFAULT_MQTTINIT_PARAMS;
|
||||
|
||||
rc = connect_init_params_setup(&init_params);
|
||||
if (rc != QCLOUD_RET_SUCCESS) {
|
||||
Log_e("init params err,rc=%d", rc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
void *the_mqtt_client = IOT_MQTT_Construct(&init_params);
|
||||
if (!the_mqtt_client) {
|
||||
Log_e("Cloud Device Construct Failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
Log_i("Cloud Device Construct Success");
|
||||
|
||||
void *the_ota_client = IOT_OTA_Init(sg_devInfo.product_id, sg_devInfo.device_name, the_mqtt_client);
|
||||
if (!the_ota_client) {
|
||||
Log_e("initialize OTA failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
*mqtt_client = the_mqtt_client;
|
||||
*ota_client = the_ota_client;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int remote_firmware_download(void *ota_client, void *mqtt_client, char *running_version, uint32_t offset, uint32_t fw_size, uint32_t *downloaded_size)
|
||||
{
|
||||
int rc;
|
||||
uint32_t is_firmware_valid;
|
||||
uint32_t received_len;
|
||||
static char remote_version[128], remote_md5[33];
|
||||
|
||||
#define OTA_DOWNLOAD_BUF_LEN 128
|
||||
static char ota_download_buf[OTA_DOWNLOAD_BUF_LEN];
|
||||
|
||||
uint32_t flash_addr = ota_partition_start(OTA_PARTITION_OTA);
|
||||
|
||||
////////////////////////////////////////////////
|
||||
/************* some sanity check *************/
|
||||
|
||||
/* is the file too large? */
|
||||
if (fw_size > ota_partition_size(OTA_PARTITION_OTA)) {
|
||||
Log_e("OTA partition too small, fw_size: %d, ota size: %d", fw_size, ota_partition_size(OTA_PARTITION_OTA));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* we need the buf_len to be flash write-align aligned */
|
||||
if (!ota_flash_size_is_aligned(sizeof(ota_download_buf))) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* a total new firmware download, not start from a break point
|
||||
we need to erase the flash to make flash ready
|
||||
*/
|
||||
if (offset == 0 &&
|
||||
ota_flash_erase_blocks(flash_addr, fw_size) < 0) {
|
||||
Log_e("flash erase failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////
|
||||
|
||||
/* start http connect */
|
||||
rc = IOT_OTA_StartDownload(ota_client, offset, fw_size);
|
||||
if (rc != QCLOUD_RET_SUCCESS) {
|
||||
Log_e("OTA download start err, rc: %d", rc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (offset > 0) {
|
||||
Log_d("Get offset: %x", offset);
|
||||
if (downloaded_firmware_md5(ota_client, offset) < 0) {
|
||||
Log_e("cal download firmware md5 failed");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
do {
|
||||
received_len = IOT_OTA_FetchYield(ota_client, ota_download_buf, OTA_DOWNLOAD_BUF_LEN, 1);
|
||||
if (received_len <= 0) {
|
||||
Log_e("download fail rc = %d", received_len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (received_len < OTA_DOWNLOAD_BUF_LEN &&
|
||||
!ota_flash_size_is_aligned(received_len)) {
|
||||
Log_e("size not aligned %d", received_len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ota_flash_write(flash_addr, ota_download_buf, ota_flash_write_size_aligned_get(received_len)) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* get OTA information and update local info */
|
||||
IOT_OTA_Ioctl(ota_client, IOT_OTAG_FETCHED_SIZE, downloaded_size, sizeof(uint32_t));
|
||||
#if 0
|
||||
IOT_OTA_Ioctl(ota_client, IOT_OTAG_FILE_SIZE, &size_file, 4);
|
||||
#endif
|
||||
IOT_OTA_Ioctl(ota_client, IOT_OTAG_MD5SUM, remote_md5, 33);
|
||||
IOT_OTA_Ioctl(ota_client, IOT_OTAG_VERSION, remote_version, 128);
|
||||
|
||||
rc = local_firmware_info_update(remote_version, running_version, remote_md5, *downloaded_size);
|
||||
if (rc != QCLOUD_RET_SUCCESS) {
|
||||
Log_e("update local fw info err, rc: %d", rc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
IOT_MQTT_Yield(mqtt_client, 100);
|
||||
} while (!IOT_OTA_IsFetchFinish(ota_client));
|
||||
|
||||
/* must check MD5 match or not */
|
||||
IOT_OTA_Ioctl(ota_client, IOT_OTAG_CHECK_FIRMWARE, &is_firmware_valid, sizeof(uint32_t));
|
||||
if (is_firmware_valid == 0) {
|
||||
Log_e("The firmware is invalid");
|
||||
rc = local_firmware_info_update(NULL, running_version, NULL, 0);
|
||||
if (rc != QCLOUD_RET_SUCCESS) {
|
||||
Log_e("update local fw info err, rc:%d", rc);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ota_download_through_explorer(void)
|
||||
{
|
||||
int rc;
|
||||
void *mqtt_client, *ota_client;
|
||||
|
||||
uint32_t downloadeded_size;
|
||||
|
||||
uint32_t offset;
|
||||
|
||||
uint32_t local_fw_size;
|
||||
char *local_version = NULL, *local_md5 = NULL, *local_fw_size_str = NULL;
|
||||
|
||||
uint32_t remote_fw_size;
|
||||
static char remote_version[128], remote_md5[33];
|
||||
|
||||
ota_img_vs_t curr_version;
|
||||
char running_version[10] = { 0 };
|
||||
|
||||
static char version[128], md5sum[33];
|
||||
|
||||
IOT_Log_Set_Level(eLOG_DEBUG);
|
||||
|
||||
if (ota_init(&mqtt_client, &ota_client) < 0) {
|
||||
Log_e("ota init Failed");
|
||||
return QCLOUD_ERR_FAILURE;
|
||||
}
|
||||
|
||||
/* 1. make sure relative topic subscribe successfully */
|
||||
IOT_MQTT_Yield(mqtt_client, 1000);
|
||||
|
||||
/* 2. get local firmware information */
|
||||
local_firmware_info_get(&local_version, &local_md5, &local_fw_size_str);
|
||||
|
||||
if (local_version && local_md5 && local_fw_size_str) {
|
||||
Log_d("local_version: %s local_md5: %s, local_size: %s", local_version, local_md5, local_fw_size_str);
|
||||
}
|
||||
local_fw_size = local_fw_size_str ? atoi(local_fw_size_str) : 0;
|
||||
|
||||
/* 3. get current running firmware version */
|
||||
curr_version = ota_info_curr_version();
|
||||
snprintf(running_version, sizeof(running_version), "%d.%d", curr_version.major, curr_version.minor);
|
||||
Log_d("running version: %s", running_version);
|
||||
|
||||
/* 4. must report local version first */
|
||||
if (IOT_OTA_ReportVersion(ota_client, running_version) < 0) {
|
||||
Log_e("report OTA version failed");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
do {
|
||||
/* idle here until ota upgrate command received */
|
||||
Log_i("wait for ota upgrade command...");
|
||||
|
||||
IOT_MQTT_Yield(mqtt_client, 200);
|
||||
|
||||
HAL_SleepMs(2000);
|
||||
} while (!IOT_OTA_IsFetching(ota_client));
|
||||
|
||||
/* 5. get remote firmware information */
|
||||
if (remote_firmware_info_get(ota_client, remote_version, remote_md5, &remote_fw_size) < 0) {
|
||||
Log_e("get remote firmware infomation failed\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* 6. compare remote and local firmware, check whether pre-download finished or not */
|
||||
if (!local_version || !local_md5 || !local_fw_size_str) {
|
||||
/* no previous downloaded firmware */
|
||||
offset = 0;
|
||||
} else if (strcmp(local_version, remote_version) != 0 ||
|
||||
strcmp(local_md5, remote_md5) != 0 ||
|
||||
local_fw_size > remote_fw_size) {
|
||||
/* there's a previous downloaded firmware, but not the same as current remote firmweare */
|
||||
offset = 0;
|
||||
} else {
|
||||
offset = local_fw_size;
|
||||
}
|
||||
|
||||
/* if local_firmware offset does not equal to remote_fw_size, means we have not finish firmware download */
|
||||
if (offset != remote_fw_size &&
|
||||
remote_firmware_download(ota_client, mqtt_client, running_version, offset, remote_fw_size, &downloadeded_size) < 0) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
printf("new firmware downloaded!\n");
|
||||
|
||||
#if 0
|
||||
/* begin execute OTA files, should report upgrade begin */
|
||||
sg_packet_id = IOT_OTA_ReportUpgradeBegin(ota_client);
|
||||
if (sg_packet_id < 0) {
|
||||
Log_e("report OTA begin failed error: %d", sg_packet_id);
|
||||
return QCLOUD_ERR_FAILURE;
|
||||
}
|
||||
|
||||
while (!sg_pub_ack) {
|
||||
HAL_SleepMs(1000);
|
||||
IOT_MQTT_Yield(mqtt_client, 200);
|
||||
}
|
||||
sg_pub_ack = K_FALSE;
|
||||
|
||||
/* add your own upgrade logic here*/
|
||||
// fw_upgrade.....
|
||||
|
||||
if (QCLOUD_RET_SUCCESS == rc) {
|
||||
/* if upgrade success */
|
||||
/* after execute OTA files, should report upgrade result */
|
||||
sg_packet_id = IOT_OTA_ReportUpgradeSuccess(ota_client, NULL);
|
||||
if (sg_packet_id < 0) {
|
||||
Log_e("report OTA result failed error:%d", sg_packet_id);
|
||||
return QCLOUD_ERR_FAILURE;
|
||||
}
|
||||
while (!sg_pub_ack) {
|
||||
HAL_SleepMs(1000);
|
||||
IOT_MQTT_Yield(mqtt_client, 200);
|
||||
}
|
||||
rc = local_firmware_info_update(version, version, md5sum, size_downloaded); // just for example, add your own logic
|
||||
sg_pub_ack = K_FALSE;
|
||||
|
||||
} else {
|
||||
/* if upgrade fail */
|
||||
sg_packet_id = IOT_OTA_ReportUpgradeFail(ota_client, NULL);
|
||||
if (0 > sg_packet_id) {
|
||||
Log_e("report OTA result failed error:%d", sg_packet_id);
|
||||
return QCLOUD_ERR_FAILURE;
|
||||
}
|
||||
while (!sg_pub_ack) {
|
||||
HAL_SleepMs(1000);
|
||||
IOT_MQTT_Yield(mqtt_client, 200);
|
||||
}
|
||||
rc = local_firmware_info_update(NULL, running_version, NULL, 0); // just for example, add your own logic
|
||||
sg_pub_ack = K_FALSE;
|
||||
}
|
||||
#endif
|
||||
|
||||
exit:
|
||||
if (local_version) {
|
||||
HAL_Free(local_version);
|
||||
local_version = NULL;
|
||||
}
|
||||
|
||||
if (local_md5) {
|
||||
HAL_Free(local_md5);
|
||||
local_md5 = NULL;
|
||||
}
|
||||
|
||||
if (local_fw_size_str) {
|
||||
HAL_Free(local_fw_size_str);
|
||||
local_fw_size_str = NULL;
|
||||
}
|
||||
|
||||
IOT_OTA_Destroy(ota_client);
|
||||
|
||||
IOT_MQTT_Destroy(&mqtt_client);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@@ -157,7 +157,7 @@ static int update_local_fw_info(const char *version, const char *preVer, const c
|
||||
FILE *fp;
|
||||
int wlen;
|
||||
int ret = QCLOUD_RET_SUCCESS;
|
||||
char dataBuff[INFO_FILE_MAX_LEN];
|
||||
static char dataBuff[INFO_FILE_MAX_LEN];
|
||||
|
||||
memset(dataBuff, 0, INFO_FILE_MAX_LEN);
|
||||
HAL_Snprintf(dataBuff, INFO_FILE_MAX_LEN, "{\"%s\":\"%s\", \"%s\":\"%s\",\"%s\":%d,\"%s\":\"%s\"}", \
|
||||
@@ -190,7 +190,7 @@ exit:
|
||||
/* get local firmware offset for resuming download from break point */
|
||||
static int getFwOffset(void *h_ota, char *local_ver, char *local_md5, char *local_size, uint32_t *offset, uint32_t *size_file)
|
||||
{
|
||||
char version[128], md5sum[33];
|
||||
static char version[128], md5sum[33];
|
||||
uint32_t local_len;
|
||||
int Ret;
|
||||
|
||||
@@ -217,7 +217,7 @@ static int cal_exist_fw_md5(void *h_ota, FILE *fp, size_t size)
|
||||
{
|
||||
#define BUFF_LEN 1024
|
||||
|
||||
char buff[BUFF_LEN];
|
||||
static char buff[BUFF_LEN];
|
||||
size_t rlen;
|
||||
int Ret = QCLOUD_RET_SUCCESS;
|
||||
|
||||
@@ -273,7 +273,7 @@ static int _setup_connect_init_params(MQTTInitParams* initParams)
|
||||
return QCLOUD_RET_SUCCESS;
|
||||
}
|
||||
|
||||
int ota_thread(int argc, char **argv)
|
||||
int ota_thread(void)
|
||||
{
|
||||
IOT_Log_Set_Level(eLOG_DEBUG);
|
||||
int rc;
|
||||
@@ -341,15 +341,15 @@ int ota_thread(int argc, char **argv)
|
||||
if(offset == size_file) {
|
||||
Log_d("download success last time without report!");
|
||||
upgrade_fetch_success = true;
|
||||
|
||||
/* get fw information */
|
||||
|
||||
/* get fw information */
|
||||
IOT_OTA_Ioctl(h_ota, IOT_OTAG_MD5SUM, md5sum, 33);
|
||||
IOT_OTA_Ioctl(h_ota, IOT_OTAG_VERSION, version, 128);
|
||||
size_downloaded = size_file;
|
||||
break;
|
||||
}
|
||||
|
||||
/*start http connect*/
|
||||
/*start http connect*/
|
||||
rc = IOT_OTA_StartDownload(h_ota, offset, size_file);
|
||||
if (QCLOUD_RET_SUCCESS != rc) {
|
||||
Log_e("OTA download start err,rc:%d", rc);
|
||||
@@ -381,7 +381,7 @@ int ota_thread(int argc, char **argv)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
do {
|
||||
len = IOT_OTA_FetchYield(h_ota, buf_ota, OTA_BUF_LEN, 1);
|
||||
|
@@ -1,151 +0,0 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright (C) 2016 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
|
||||
* Licensed under the MIT License (the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://opensource.org/licenses/MIT
|
||||
|
||||
* 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 "tos_k.h"
|
||||
#include "qcloud.h"
|
||||
|
||||
static void response_message_callback(void *msg, void *context)
|
||||
{
|
||||
int ret = -1;
|
||||
char* payload = NULL;
|
||||
int payload_len = 0;
|
||||
coap_event_type_t event_type;
|
||||
coap_message_t *message = NULL;
|
||||
|
||||
message = msg;
|
||||
event_type = qcloud_coap_event_type_get(message);
|
||||
|
||||
switch (event_type) {
|
||||
case COAP_EVENT_TYPE_RECEIVE_ACK:
|
||||
QCLOUD_LOG_I("message received ACK, msgid: %d", qcloud_coap_msg_id_get(message));
|
||||
break;
|
||||
|
||||
case COAP_EVENT_TYPE_RECEIVE_RESPCONTENT:
|
||||
ret = qcloud_coap_msg_payload_get(message, &payload, &payload_len);
|
||||
if (ret == QCLOUD_ERR_SUCCESS) {
|
||||
QCLOUD_LOG_I("message received response, len: %d content: %s", payload_len, payload);
|
||||
} else {
|
||||
QCLOUD_LOG_E("message received response, content error.");
|
||||
}
|
||||
break;
|
||||
|
||||
case COAP_EVENT_TYPE_UNAUTHORIZED:
|
||||
QCLOUD_LOG_I("coap client auth token expired or invalid, msgid: %d", qcloud_coap_msg_id_get(message));
|
||||
break;
|
||||
|
||||
case COAP_EVENT_TYPE_FORBIDDEN:
|
||||
QCLOUD_LOG_I("coap URI is invalid for this device, msgid: %d", qcloud_coap_msg_id_get(message));
|
||||
break;
|
||||
|
||||
case COAP_EVENT_TYPE_INTERNAL_SERVER_ERROR:
|
||||
QCLOUD_LOG_I("coap server internal error, msgid: %d", qcloud_coap_msg_id_get(message));
|
||||
break;
|
||||
|
||||
case COAP_EVENT_TYPE_ACK_TIMEOUT:
|
||||
QCLOUD_LOG_I("message receive ACK timeout, msgid: %d", qcloud_coap_msg_id_get(message));
|
||||
break;
|
||||
|
||||
case COAP_EVENT_TYPE_SEPRESP_TIMEOUT:
|
||||
QCLOUD_LOG_I("message received ACK but receive response timeout, msgid: %d", qcloud_coap_msg_id_get(message));
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void event_handler(void *context, coap_event_t *event)
|
||||
{
|
||||
switch (event->type) {
|
||||
case COAP_EVENT_TYPE_RECEIVE_ACK:
|
||||
QCLOUD_LOG_I("message received ACK, msgid: %d", *(uint16_t *)event->message);
|
||||
break;
|
||||
|
||||
case COAP_EVENT_TYPE_RECEIVE_RESPCONTENT:
|
||||
QCLOUD_LOG_I("message received response, content: %s", qcloud_coap_msg_id_get(event->message));
|
||||
break;
|
||||
|
||||
case COAP_EVENT_TYPE_UNAUTHORIZED:
|
||||
QCLOUD_LOG_I("coap client auth token expired or invalid, msgid: %d", *(uint16_t *)event->message);
|
||||
break;
|
||||
|
||||
case COAP_EVENT_TYPE_FORBIDDEN:
|
||||
QCLOUD_LOG_I("coap URI is invalid for this device, msgid: %d", *(uint16_t *)event->message);
|
||||
break;
|
||||
|
||||
case COAP_EVENT_TYPE_INTERNAL_SERVER_ERROR:
|
||||
QCLOUD_LOG_I("coap server internal error, msgid: %d", *(uint16_t *)event->message);
|
||||
break;
|
||||
|
||||
case COAP_EVENT_TYPE_ACK_TIMEOUT:
|
||||
QCLOUD_LOG_I("message receive ACK timeout, msgid: %d", *(uint16_t *)event->message);
|
||||
break;
|
||||
|
||||
case COAP_EVENT_TYPE_SEPRESP_TIMEOUT:
|
||||
QCLOUD_LOG_I("message received ACK but receive response timeout, msgid: %d", *(uint16_t *)event->message);
|
||||
break;
|
||||
|
||||
default:
|
||||
QCLOUD_LOG_E("unrecogonized event type: %d", event->type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
qcloud_device_t device;
|
||||
qcloud_coap_client_t client;
|
||||
|
||||
int coap_thread(void)
|
||||
{
|
||||
char topic[128];
|
||||
qcloud_err_t rc;
|
||||
coap_send_opt_t send_opt;
|
||||
|
||||
qcloud_device_create(&device, "ABU45A5KT8", "dev001", "RPpqUxL03frSDSufVhjuLw==");
|
||||
|
||||
qcloud_coap_client_create(&client, &device, event_handler);
|
||||
|
||||
qcloud_coap_client_connect(&client);
|
||||
|
||||
do {
|
||||
memset(&send_opt, 0, sizeof(coap_send_opt_t));
|
||||
send_opt.payload = "{\"name\":\"hello world\"}";
|
||||
send_opt.payload_len = strlen("{\"name\":\"hello world\"}");
|
||||
send_opt.resp_cb = response_message_callback;
|
||||
|
||||
sprintf(topic, "%s/%s/data", device.product_id, device.device_name);
|
||||
QCLOUD_LOG_I("topic: %s", topic);
|
||||
|
||||
rc = qcloud_coap_client_msg_send(&client, topic, &send_opt);
|
||||
if (rc != QCLOUD_ERR_SUCCESS) {
|
||||
QCLOUD_LOG_E("client publish topic failed :%d.", rc);
|
||||
} else {
|
||||
QCLOUD_LOG_D("client topic has been sent, msg_id: %d", rc);
|
||||
}
|
||||
|
||||
rc = qcloud_coap_client_yield(&client, 200);
|
||||
|
||||
if (rc != QCLOUD_ERR_SUCCESS){
|
||||
QCLOUD_LOG_E("exit with error: %d", rc);
|
||||
break;
|
||||
}
|
||||
|
||||
osal_sleep_ms(4000);
|
||||
} while (1);
|
||||
|
||||
qcloud_coap_client_destroy(&client);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
@@ -1,46 +0,0 @@
|
||||
#include "tos_k.h"
|
||||
|
||||
/* 用户根据自己的底层通信链路来配置此宏
|
||||
* 如果是基于以太网lwip的链路,这里应该定义 USE_LWIP
|
||||
* 如果是基于模组的通信链路,这里应该定义相应的模组宏,如使用ESP8266则定义 USE_ESP8266
|
||||
* */
|
||||
#define USE_ESP8266
|
||||
|
||||
#ifdef USE_LWIP
|
||||
#include "lwip/api.h"
|
||||
#include "lwip/sockets.h"
|
||||
#include "lwip/err.h"
|
||||
#include "lwip/sys.h"
|
||||
#endif
|
||||
|
||||
#ifdef USE_ESP8266
|
||||
#include "esp8266.h"
|
||||
#endif
|
||||
|
||||
void application_entry(void *arg)
|
||||
{
|
||||
#ifdef USE_LWIP
|
||||
dns_init();
|
||||
MX_LWIP_Init();
|
||||
#endif
|
||||
|
||||
#ifdef USE_ESP8266
|
||||
extern int esp8266_sal_init(hal_uart_port_t uart_port);
|
||||
extern int esp8266_join_ap(const char *ssid, const char *pwd);
|
||||
esp8266_sal_init(HAL_UART_PORT_0);
|
||||
esp8266_join_ap("SheldonDai", "srnr6x9xbhmb0");
|
||||
#endif
|
||||
|
||||
#ifdef USE_NB_BC35
|
||||
extern int bc35_28_95_sal_init(hal_uart_port_t uart_port);
|
||||
bc35_28_95_sal_init(HAL_UART_PORT_0);
|
||||
#endif
|
||||
|
||||
coap_thread();
|
||||
|
||||
while (1) {
|
||||
printf("This is a coap demo!\r\n");
|
||||
tos_task_delay(1000);
|
||||
}
|
||||
}
|
||||
|
@@ -1,671 +0,0 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright (C) 2016 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
|
||||
* Licensed under the MIT License (the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://opensource.org/licenses/MIT
|
||||
|
||||
* 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 "qcloud.h"
|
||||
#include "tos_k.h"
|
||||
|
||||
#if (QCLOUD_CFG_EVENT_EN > 0u)
|
||||
|
||||
#define EVENT_MESSAGE_MAX (64)
|
||||
#define EVENT_NAME_MAX (64)
|
||||
|
||||
typedef enum event_status_en {
|
||||
EVENT_STATUS_LIGHT_OFF,
|
||||
EVENT_STATUS_LIGHT_ON,
|
||||
} event_status_t;
|
||||
|
||||
static template_bool_t event_status;
|
||||
static template_string_t event_message[EVENT_MESSAGE_MAX + 1];
|
||||
static template_float_t event_voltage;
|
||||
static template_string_t event_name[EVENT_NAME_MAX + 1];
|
||||
static template_int_t event_error_code;
|
||||
|
||||
static shadow_dev_property_t event_property_status[] = {
|
||||
{ .key = "status", .data = &event_status, .type = JSON_DATA_TYPE_BOOL },
|
||||
{ .key = "message", .data = event_message, .type = JSON_DATA_TYPE_STRING },
|
||||
};
|
||||
|
||||
static shadow_dev_property_t event_property_low_voltage[] = {
|
||||
{ .key = "voltage", .data = &event_voltage, .type = JSON_DATA_TYPE_FLOAT },
|
||||
};
|
||||
|
||||
static shadow_dev_property_t event_property_hardware_fault[] = {
|
||||
{ .key = "name", .data = event_name, .type = JSON_DATA_TYPE_STRING },
|
||||
{ .key = "error_code", .data = &event_error_code, .type = JSON_DATA_TYPE_INT32},
|
||||
};
|
||||
|
||||
static qcloud_event_t events[] = {
|
||||
{
|
||||
.event_name = "status_report",
|
||||
.type = "info",
|
||||
.timestamp = 0,
|
||||
.event_payload_count = sizeof(event_property_status) / sizeof(event_property_status[0]),
|
||||
.event_payload = event_property_status,
|
||||
},
|
||||
{
|
||||
.event_name = "low_voltage",
|
||||
.type = "alert",
|
||||
.timestamp = 0,
|
||||
.event_payload_count = sizeof(event_property_low_voltage) / sizeof(event_property_low_voltage[0]),
|
||||
.event_payload = event_property_low_voltage,
|
||||
},
|
||||
{
|
||||
.event_name = "hardware_fault",
|
||||
.type = "fault",
|
||||
.timestamp = 0,
|
||||
.event_payload_count = sizeof(event_property_hardware_fault) / sizeof(event_property_hardware_fault[0]),
|
||||
.event_payload = event_property_hardware_fault,
|
||||
},
|
||||
};
|
||||
|
||||
#define EVENT_COUNTS (sizeof(events) / sizeof(events[0]))
|
||||
|
||||
#define EVENT_FLAG_STATUS (1u << 0)
|
||||
#define EVENT_FLAG_LOW_VOLTAGE (1u << 1)
|
||||
#define EVENT_FLAG_HARDWARE_FAULT (1u << 2)
|
||||
|
||||
static uint32_t event_flags = 0;
|
||||
|
||||
void event_flag_set(uint32_t flag)
|
||||
{
|
||||
event_flags |= flag & 0xffffffff;
|
||||
}
|
||||
|
||||
void event_flag_unset(uint32_t flag)
|
||||
{
|
||||
event_flags &= (~flag) & 0xffffffff;
|
||||
}
|
||||
|
||||
uint32_t event_flag_get(void)
|
||||
{
|
||||
return event_flags;
|
||||
}
|
||||
|
||||
void event_flag_clear(void)
|
||||
{
|
||||
event_flags = 0;
|
||||
}
|
||||
|
||||
/*-----------------event config end -------------------*/
|
||||
|
||||
static void update_event_timestamp(qcloud_event_t *event)
|
||||
{
|
||||
#ifdef EVENT_TIMESTAMP_USED
|
||||
event->timestamp = (uint32_t)tos_systick_get(); //should be UTC and accurate
|
||||
#else
|
||||
event->timestamp = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void on_event_post_handler(void *client, mqtt_incoming_msg_t *msg)
|
||||
{
|
||||
QCLOUD_LOG_D("reply:%.*s", msg->payload_len, msg->payload);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
// dealing with product property template
|
||||
|
||||
#define LIGHT_NAME_MAX 64
|
||||
|
||||
typedef enum light_color_en {
|
||||
LIGHT_COLOR_RED = 0,
|
||||
LIGHT_COLOR_GREEN = 1,
|
||||
LIGHT_COLOR_BLUE = 2,
|
||||
} light_color_t; // a enum to describe the color of a light
|
||||
|
||||
typedef enum light_switch_state_en {
|
||||
LIGHT_SWITCH_STATE_OFF = 0,
|
||||
LIGHT_SWITCH_STATE_ON = 1,
|
||||
} light_switch_state_t; // a enum to describe the switch state of a light
|
||||
|
||||
typedef enum property_state_en {
|
||||
PROPERTY_STATE_NOCHANGE,
|
||||
PROPERTY_STATE_CHANGED,
|
||||
} property_state_t; // a enum to describe whether the property state has changed
|
||||
|
||||
typedef struct property_wrapper_st {
|
||||
shadow_dev_property_t property;
|
||||
property_state_t state;
|
||||
} property_wrapper_t; // a wrapper of one property(contains the property and change state)
|
||||
|
||||
/* describe a light property */
|
||||
typedef struct light_property_st {
|
||||
property_wrapper_t switch_state;
|
||||
property_wrapper_t color;
|
||||
property_wrapper_t brightness;
|
||||
property_wrapper_t name;
|
||||
} light_property_t; // all the properties of a light
|
||||
|
||||
// how many properties of a light?
|
||||
#define LIGHT_PROPERTY_COUNT (sizeof(light_property_t) / sizeof(property_wrapper_t))
|
||||
|
||||
typedef union light_property_handler_un {
|
||||
property_wrapper_t property_wrappers[LIGHT_PROPERTY_COUNT];
|
||||
light_property_t property_wrappers_of;
|
||||
} light_property_handler_t; // a handler, user to iterate through all the properties
|
||||
|
||||
typedef struct light_profile_st {
|
||||
template_bool_t switch_state;
|
||||
template_int_t color;
|
||||
template_float_t brightness;
|
||||
template_string_t name[LIGHT_NAME_MAX + 1];
|
||||
} light_profile_t; // just a data container
|
||||
|
||||
static int is_light_property_changed = QCLOUD_FALSE;
|
||||
static int is_new_property_reported = QCLOUD_FALSE;
|
||||
static light_profile_t light_profile;
|
||||
static light_property_handler_t light_property_handler;
|
||||
|
||||
static void data_template_init(qcloud_device_t *device)
|
||||
{
|
||||
memset((void *)&light_profile, 0, sizeof(light_profile_t));
|
||||
|
||||
light_profile.switch_state = LIGHT_SWITCH_STATE_OFF;
|
||||
light_profile.color = LIGHT_COLOR_RED;
|
||||
light_profile.brightness = 0.0;
|
||||
|
||||
strncpy(light_profile.name, device->device_name, LIGHT_NAME_MAX);
|
||||
light_profile.name[LIGHT_NAME_MAX] = '\0';
|
||||
|
||||
|
||||
light_property_handler.property_wrappers_of.switch_state.property.key = "power_switch";
|
||||
light_property_handler.property_wrappers_of.switch_state.property.data = &light_profile.switch_state;
|
||||
light_property_handler.property_wrappers_of.switch_state.property.type = JSON_DATA_TYPE_BOOL;
|
||||
|
||||
light_property_handler.property_wrappers_of.color.property.key = "color";
|
||||
light_property_handler.property_wrappers_of.color.property.data = &light_profile.color;
|
||||
light_property_handler.property_wrappers_of.color.property.type = JSON_DATA_TYPE_INT32;
|
||||
|
||||
light_property_handler.property_wrappers_of.brightness.property.key = "brightness";
|
||||
light_property_handler.property_wrappers_of.brightness.property.data = &light_profile.brightness;
|
||||
light_property_handler.property_wrappers_of.brightness.property.type = JSON_DATA_TYPE_FLOAT;
|
||||
|
||||
light_property_handler.property_wrappers_of.name.property.key = "name";
|
||||
light_property_handler.property_wrappers_of.name.property.data = &light_profile.name;
|
||||
light_property_handler.property_wrappers_of.name.property.type = JSON_DATA_TYPE_STRING;
|
||||
};
|
||||
|
||||
static int is_self_property(json_data_type_t property_type)
|
||||
{
|
||||
return property_type == JSON_DATA_TYPE_BOOL || // power switch
|
||||
property_type == JSON_DATA_TYPE_INT32|| // color
|
||||
property_type == JSON_DATA_TYPE_FLOAT || // brightness
|
||||
property_type == JSON_DATA_TYPE_STRING; // device name
|
||||
}
|
||||
|
||||
static void property_do_update(shadow_dev_property_t *property)
|
||||
{
|
||||
switch (property->type) {
|
||||
case JSON_DATA_TYPE_BOOL: // power switch
|
||||
light_profile.switch_state = *(template_bool_t *)property->data;
|
||||
break;
|
||||
|
||||
case JSON_DATA_TYPE_INT32: // color
|
||||
light_profile.color = *(template_int_t *)property->data;
|
||||
break;
|
||||
|
||||
case JSON_DATA_TYPE_FLOAT: // brightness
|
||||
light_profile.brightness = *(template_float_t *)property->data;
|
||||
break;
|
||||
|
||||
case JSON_DATA_TYPE_STRING: // device name
|
||||
/* 如果多个字符串属性,根据pProperty->key值匹配,处理字符串 */
|
||||
if (strcmp("name", property->key) != 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
memset(light_profile.name, 0, sizeof(light_profile.name));
|
||||
strncpy(light_profile.name, property->data, LIGHT_NAME_MAX);
|
||||
light_profile.name[LIGHT_NAME_MAX] = '\0';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* 如果有自定义的字符串或者json,需要在这里解析 */
|
||||
static qcloud_err_t property_update(const char *json_doc, shadow_dev_property_t *property)
|
||||
{
|
||||
QCLOUD_POINTER_SANITY_CHECK(json_doc, QCLOUD_ERR_INVAL);
|
||||
QCLOUD_POINTER_SANITY_CHECK(property, QCLOUD_ERR_INVAL);
|
||||
|
||||
char *json_doc_mutable = NULL, *property_data = NULL;
|
||||
|
||||
/* convert const char * to char * */
|
||||
json_doc_mutable = osal_malloc(strlen(json_doc));
|
||||
if(!json_doc_mutable)
|
||||
{
|
||||
return QCLOUD_ERR_FAILURE;
|
||||
}
|
||||
QCLOUD_FUNC_EXIT_RC_IF(json_doc_mutable, NULL, QCLOUD_ERR_FAILURE);
|
||||
strcpy(json_doc_mutable, json_doc);
|
||||
|
||||
property_data = LITE_json_value_of((char *)property->key, json_doc_mutable);
|
||||
if (!property_data) {
|
||||
QCLOUD_LOG_D("property:%s no matched", property->key);
|
||||
osal_free(json_doc_mutable);
|
||||
return QCLOUD_ERR_FAILURE;
|
||||
}
|
||||
|
||||
property_do_update(property);
|
||||
|
||||
osal_free(property_data);
|
||||
osal_free(json_doc_mutable);
|
||||
|
||||
return QCLOUD_ERR_SUCCESS;
|
||||
}
|
||||
|
||||
/* 服务端有控制消息下发,会触发这里的delta回调 */
|
||||
static void on_property_delta_handler(void *client, const char *json_doc, uint32_t json_doc_len, shadow_dev_property_t *property)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
for (i = 0; i < LIGHT_PROPERTY_COUNT; ++i) {
|
||||
/* 其他数据类型已经在_handle_delta流程统一处理了,字符串和json串需要在这里处理,因为只有产品自己才知道string/json的自定义解析 */
|
||||
if (strcmp(light_property_handler.property_wrappers[i].property.key, property->key) != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
light_property_handler.property_wrappers[i].state = PROPERTY_STATE_CHANGED;
|
||||
|
||||
if (is_self_property(light_property_handler.property_wrappers[i].property.type)) {
|
||||
property_update(json_doc, &(light_property_handler.property_wrappers[i].property));
|
||||
}
|
||||
|
||||
QCLOUD_LOG_I("property=%s changed", property->key);
|
||||
is_light_property_changed = QCLOUD_TRUE;
|
||||
return;
|
||||
}
|
||||
|
||||
QCLOUD_LOG_E("property=%s changed no match", property->key);
|
||||
}
|
||||
|
||||
/* 注册数据模板属性 */
|
||||
static qcloud_err_t data_template_property_register(qcloud_shadow_client_t *client)
|
||||
{
|
||||
int i = 0;
|
||||
qcloud_err_t rc;
|
||||
|
||||
for (i = 0; i < LIGHT_PROPERTY_COUNT; ++i) {
|
||||
rc = qcloud_shadow_device_property_register(client, &light_property_handler.property_wrappers[i].property, on_property_delta_handler);
|
||||
|
||||
if (rc != QCLOUD_ERR_SUCCESS) {
|
||||
rc = qcloud_shadow_client_destroy(client);
|
||||
QCLOUD_LOG_E("device template property register failed, err: %d", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
QCLOUD_LOG_I("data template property=%s registered.", light_property_handler.property_wrappers[i].property.key);
|
||||
}
|
||||
|
||||
return QCLOUD_ERR_SUCCESS;
|
||||
}
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
__weak void OLED_Clear(void)
|
||||
{
|
||||
printf("OLED Clear\n");
|
||||
}
|
||||
|
||||
__weak void OLED_ShowString(int x, int y, uint8_t *str, int bold)
|
||||
{
|
||||
printf("OLED ShowString: %s\n", (char *)str);
|
||||
}
|
||||
|
||||
// handle the light(simulated)
|
||||
static void light_change_color(const char *color)
|
||||
{
|
||||
// 作为demo,这里用oled屏字符显示来模拟灯颜色的切换
|
||||
// 这里应该由用户实现硬件操作代码,来改变智能灯的颜色
|
||||
// 此处demo,在开发板显示屏上显示具体的颜色
|
||||
OLED_ShowString(0, 0, (uint8_t *)color, 8);
|
||||
}
|
||||
|
||||
static void light_change_brightness(template_float_t brightness)
|
||||
{
|
||||
// 作为demo,这里用oled屏字符显示来模拟灯颜色的切换
|
||||
// 这里应该由用户实现硬件操作代码,来改变智能灯的颜色
|
||||
// 此处demo,在开发板显示屏上显示具体的颜色
|
||||
char brightness_str[12];
|
||||
|
||||
snprintf(brightness_str, sizeof(brightness_str), "%f", brightness);
|
||||
brightness_str[sizeof(brightness_str) - 1] = '\0';
|
||||
OLED_ShowString(0, 2, (uint8_t *)brightness_str, 8);
|
||||
}
|
||||
|
||||
static void light_power_on(void)
|
||||
{
|
||||
// 作为demo,这里用oled屏字符显示来模拟灯颜色的切换
|
||||
OLED_Clear();
|
||||
}
|
||||
|
||||
static void light_power_off(void)
|
||||
{
|
||||
// 作为demo,这里用oled屏字符显示来模拟灯颜色的切换
|
||||
char *info = "light off";
|
||||
OLED_Clear();
|
||||
OLED_ShowString(0, 0, (uint8_t *)info, 16);
|
||||
}
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static void incoming_messsage_handler(void *client, void *context, mqtt_event_t *event)
|
||||
{
|
||||
uint16_t packet_id = *(uint16_t *)event->message;
|
||||
|
||||
switch (event->type) {
|
||||
case MQTT_EVENT_UNDEF:
|
||||
QCLOUD_LOG_I("undefined event occur.");
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_DISCONNECT:
|
||||
QCLOUD_LOG_I("MQTT disconnect.");
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_RECONNECT:
|
||||
QCLOUD_LOG_I("MQTT reconnect.");
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_SUBCRIBE_SUCCESS:
|
||||
QCLOUD_LOG_I("subscribe success, packet-id=%u", (uint32_t)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_SUBCRIBE_TIMEOUT:
|
||||
QCLOUD_LOG_I("subscribe wait ack timeout, packet-id=%u", (uint32_t)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_SUBCRIBE_NACK:
|
||||
QCLOUD_LOG_I("subscribe nack, packet-id=%u", (uint32_t)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_PUBLISH_SUCCESS:
|
||||
QCLOUD_LOG_I("publish success, packet-id=%u", (uint32_t)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_PUBLISH_TIMEOUT:
|
||||
QCLOUD_LOG_I("publish timeout, packet-id=%u", (uint32_t)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_PUBLISH_NACK:
|
||||
QCLOUD_LOG_I("publish nack, packet-id=%u", (uint32_t)packet_id);
|
||||
break;
|
||||
default:
|
||||
QCLOUD_LOG_I("Should NOT arrive here.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* 示例灯光控制处理逻辑 */
|
||||
static void deal_down_stream_user_logic(void)
|
||||
{
|
||||
char *color_name;
|
||||
|
||||
/* 灯光颜色 */
|
||||
switch (light_profile.color) {
|
||||
case LIGHT_COLOR_RED:
|
||||
color_name = " RED ";
|
||||
break;
|
||||
|
||||
case LIGHT_COLOR_GREEN:
|
||||
color_name = "GREEN";
|
||||
break;
|
||||
|
||||
case LIGHT_COLOR_BLUE:
|
||||
color_name = "BLUE";
|
||||
break;
|
||||
}
|
||||
|
||||
if (light_profile.switch_state == LIGHT_SWITCH_STATE_ON) {
|
||||
/* 灯光开启式,按照控制参数展示 */
|
||||
light_power_on();
|
||||
light_change_color(color_name);
|
||||
light_change_brightness(light_profile.brightness);
|
||||
} else {
|
||||
/* 灯光关闭展示 */
|
||||
light_power_off();
|
||||
}
|
||||
|
||||
#if (QCLOUD_CFG_EVENT_EN > 0u)
|
||||
if (light_property_handler.property_wrappers_of.switch_state.state == PROPERTY_STATE_CHANGED) {
|
||||
if (light_profile.switch_state == LIGHT_SWITCH_STATE_ON) {
|
||||
strcpy(event_message, "light on");
|
||||
event_status = EVENT_STATUS_LIGHT_ON;
|
||||
} else {
|
||||
strcpy(event_message, "light off");
|
||||
event_status = EVENT_STATUS_LIGHT_OFF;
|
||||
}
|
||||
|
||||
event_flag_set(EVENT_FLAG_STATUS);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* 用户需要实现的上行数据的业务逻辑,此处仅供示例 */
|
||||
static void deal_up_stream_user_logic(shadow_dev_property_t *properties_report[], int *count)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
*count = 0;
|
||||
|
||||
for (i = 0, j = 0; i < LIGHT_PROPERTY_COUNT; ++i) {
|
||||
if (light_property_handler.property_wrappers[i].state == PROPERTY_STATE_CHANGED) {
|
||||
properties_report[j++] = &(light_property_handler.property_wrappers[i].property);
|
||||
light_property_handler.property_wrappers[i].state = PROPERTY_STATE_NOCHANGE;
|
||||
}
|
||||
}
|
||||
*count = j;
|
||||
}
|
||||
|
||||
static void on_shadow_update_handler(void *client,
|
||||
qcloud_shadow_req_method_t method,
|
||||
qcloud_shadow_req_state_t req_state,
|
||||
const char *json_doc,
|
||||
void *context)
|
||||
{
|
||||
QCLOUD_LOG_I("recv shadow update response, request state: %d", req_state);
|
||||
}
|
||||
|
||||
/* 5s定时上报属性状态,可根据业务裁剪,此处仅供示例 */
|
||||
qcloud_err_t timely_reporting(shadow_dev_property_t *properties_report[], osal_timer_t *report_timer)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (osal_timer_is_expired(report_timer)){
|
||||
for (i = 0; i < LIGHT_PROPERTY_COUNT; ++i) {
|
||||
properties_report[i] = &(light_property_handler.property_wrappers[i].property);
|
||||
osal_timer_countdown_ms(report_timer, 5000);
|
||||
}
|
||||
return QCLOUD_ERR_SUCCESS;
|
||||
}
|
||||
|
||||
return QCLOUD_ERR_INVAL;
|
||||
}
|
||||
|
||||
static qcloud_device_t device;
|
||||
static qcloud_shadow_client_t shadow_client;
|
||||
static char shadow_update_buffer[2048];
|
||||
static shadow_dev_property_t *properties_report[LIGHT_PROPERTY_COUNT];
|
||||
|
||||
#if (QCLOUD_CFG_EVENT_EN > 0u)
|
||||
static qcloud_event_client_t event_client;
|
||||
#endif
|
||||
|
||||
int data_template_light_thread(void)
|
||||
{
|
||||
qcloud_err_t rc;
|
||||
int properties_report_count = 0;
|
||||
osal_timer_t report_timer;
|
||||
|
||||
#if (QCLOUD_CFG_EVENT_EN > 0u)
|
||||
uint32_t event_flag;
|
||||
int event_count;
|
||||
qcloud_event_t *events2report[EVENT_COUNTS];
|
||||
#endif
|
||||
|
||||
QCLOUD_LOG_I("data template sample start");
|
||||
|
||||
qcloud_device_create(&device, "XC31USKYPL", "dev001", "Pz1wK0fVJHxSojqxDuuvmg==");
|
||||
|
||||
// init connection
|
||||
qcloud_shadow_client_create(&shadow_client, &device, incoming_messsage_handler, SHADOW_TYPE_TEMPLATE);
|
||||
|
||||
light_power_off();
|
||||
|
||||
// init data template
|
||||
data_template_init(&device);
|
||||
|
||||
#if (QCLOUD_CFG_EVENT_EN > 0u)
|
||||
rc = qcloud_event_client_create(&event_client, &shadow_client, &device);
|
||||
if (rc != QCLOUD_ERR_SUCCESS) {
|
||||
QCLOUD_LOG_E("event init failed: %d", rc);
|
||||
return rc;
|
||||
}
|
||||
#endif
|
||||
|
||||
// register data template propertys here
|
||||
rc = data_template_property_register(&shadow_client);
|
||||
if (rc == QCLOUD_ERR_SUCCESS) {
|
||||
QCLOUD_LOG_I("data template propertys register success");
|
||||
} else {
|
||||
QCLOUD_LOG_E("data template propertys register failed: %d", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
#define SHADOW_REQUEST_TIMEOUT (10) // in seconds
|
||||
// 离线期间服务端可能有下行命令,此处实现同步。version同步后台非必要
|
||||
rc = qcloud_shadow_client_get_sync(&shadow_client, SHADOW_REQUEST_TIMEOUT);
|
||||
if (rc != QCLOUD_ERR_SUCCESS) {
|
||||
QCLOUD_LOG_E("device shadow get failed, err: %d", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
// 属性定时上报timer,可以根据业务需要裁剪。
|
||||
osal_timer_init(&report_timer);
|
||||
|
||||
while (qcloud_shadow_client_is_connected(&shadow_client) ||
|
||||
rc == QCLOUD_ERR_MQTT_ATTEMPTING_RECONNECT ||
|
||||
rc == QCLOUD_ERR_MQTT_RECONNECTED ||
|
||||
rc == QCLOUD_ERR_SUCCESS) {
|
||||
|
||||
rc = qcloud_shadow_client_yield(&shadow_client, 200);
|
||||
if (rc == QCLOUD_ERR_MQTT_ATTEMPTING_RECONNECT) {
|
||||
osal_sleep_ms(1000);
|
||||
continue;
|
||||
} else if (rc != QCLOUD_ERR_SUCCESS && rc != QCLOUD_ERR_MQTT_RECONNECTED) {
|
||||
QCLOUD_LOG_E("exit with error: %d", rc);
|
||||
break;
|
||||
}
|
||||
|
||||
/* 服务端下行消息,业务处理逻辑1入口 */
|
||||
if (is_light_property_changed) {
|
||||
deal_down_stream_user_logic();
|
||||
|
||||
/* 业务逻辑处理完后需要同步通知服务端:设备数据已更新,删除dseire数据 */
|
||||
rc = qcloud_shadow_client_desire_null_construct(&shadow_client, shadow_update_buffer, sizeof(shadow_update_buffer));
|
||||
if (rc == QCLOUD_ERR_SUCCESS) {
|
||||
rc = qcloud_shadow_client_update_sync(&shadow_client, shadow_update_buffer, sizeof(shadow_update_buffer), 5);
|
||||
if (rc == QCLOUD_ERR_SUCCESS) {
|
||||
is_light_property_changed = QCLOUD_FALSE;
|
||||
|
||||
// 用户需要根据业务情况修改上报flag的赋值位置,此处仅为示例。
|
||||
is_new_property_reported = QCLOUD_TRUE;
|
||||
QCLOUD_LOG_I("shadow update(desired) success");
|
||||
} else {
|
||||
QCLOUD_LOG_E("shadow update(desired) failed, err: %d", rc);
|
||||
}
|
||||
} else {
|
||||
QCLOUD_LOG_E("construct desire failed, err: %d", rc);
|
||||
}
|
||||
}
|
||||
|
||||
/* 设备上行消息,业务逻辑2入口 */
|
||||
if (is_new_property_reported) {
|
||||
/* delta消息是属性的desire和属性的report的差异集,收到deseire消息处理后,要report属性的状态 */
|
||||
deal_up_stream_user_logic(properties_report, &properties_report_count);
|
||||
if (properties_report_count > 0) {
|
||||
rc = qcloud_shadow_client_report_construct_array(&shadow_client, shadow_update_buffer, sizeof(shadow_update_buffer),
|
||||
properties_report_count, properties_report);
|
||||
if (rc == QCLOUD_ERR_SUCCESS) {
|
||||
QCLOUD_LOG_D("report: %s", shadow_update_buffer);
|
||||
rc = qcloud_shadow_client_update_async(&shadow_client, shadow_update_buffer, sizeof(shadow_update_buffer),
|
||||
on_shadow_update_handler, NULL, 5);
|
||||
if (rc == QCLOUD_ERR_SUCCESS) {
|
||||
is_new_property_reported = QCLOUD_FALSE;
|
||||
QCLOUD_LOG_I("shadow update(reported) success");
|
||||
} else {
|
||||
QCLOUD_LOG_E("shadow update(reported) failed, err: %d", rc);
|
||||
}
|
||||
|
||||
} else {
|
||||
QCLOUD_LOG_E("construct reported failed, err: %d", rc);
|
||||
}
|
||||
} else {
|
||||
QCLOUD_LOG_D("no data need to be reported or someting goes wrong");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (QCLOUD_ERR_SUCCESS == timely_reporting(properties_report, &report_timer)){
|
||||
rc = qcloud_shadow_client_report_construct_array(&shadow_client, shadow_update_buffer, sizeof(shadow_update_buffer),
|
||||
LIGHT_PROPERTY_COUNT, properties_report);
|
||||
if (rc == QCLOUD_ERR_SUCCESS) {
|
||||
QCLOUD_LOG_D("cycle report:%s", shadow_update_buffer);
|
||||
rc = qcloud_shadow_client_update_async(&shadow_client, shadow_update_buffer, sizeof(shadow_update_buffer),
|
||||
on_shadow_update_handler, NULL, 5);
|
||||
if (rc == QCLOUD_ERR_SUCCESS) {
|
||||
is_new_property_reported = QCLOUD_FALSE;
|
||||
QCLOUD_LOG_I("shadow update(reported) success");
|
||||
} else {
|
||||
QCLOUD_LOG_E("shadow update(reported) failed, err: %d", rc);
|
||||
}
|
||||
} else {
|
||||
QCLOUD_LOG_E("construct reported failed, err: %d", rc);
|
||||
}
|
||||
}
|
||||
|
||||
#if (QCLOUD_CFG_EVENT_EN > 0u)
|
||||
// 事件上报
|
||||
event_count = 0;
|
||||
event_flag = event_flag_get();
|
||||
if (EVENT_COUNTS > 0 && event_flag > 0) {
|
||||
int i = 0;
|
||||
for (i = 0; i < EVENT_COUNTS; ++i) {
|
||||
if (event_flag & (1 << i)) { // i-th event is set
|
||||
events2report[event_count++] = &events[i];
|
||||
update_event_timestamp(&events[i]);
|
||||
}
|
||||
}
|
||||
|
||||
rc = qcloud_event_client_post(&event_client, shadow_update_buffer, sizeof(shadow_update_buffer), \
|
||||
event_count, events2report, on_event_post_handler);
|
||||
if (rc != QCLOUD_ERR_SUCCESS) {
|
||||
QCLOUD_LOG_E("event post failed: %d", rc);
|
||||
}
|
||||
|
||||
event_flag_clear();
|
||||
}
|
||||
#endif
|
||||
|
||||
osal_sleep_ms(3000);
|
||||
}
|
||||
|
||||
qcloud_shadow_client_destroy(&shadow_client);
|
||||
#if (QCLOUD_CFG_EVENT_EN > 0u)
|
||||
qcloud_event_client_destroy(&event_client);
|
||||
#endif
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
@@ -1,46 +0,0 @@
|
||||
#include "tos_k.h"
|
||||
|
||||
/* 用户根据自己的底层通信链路来配置此宏
|
||||
* 如果是基于以太网lwip的链路,这里应该定义 USE_LWIP
|
||||
* 如果是基于模组的通信链路,这里应该定义相应的模组宏,如使用ESP8266则定义 USE_ESP8266
|
||||
* */
|
||||
#define USE_ESP8266
|
||||
|
||||
#ifdef USE_LWIP
|
||||
#include "lwip/api.h"
|
||||
#include "lwip/sockets.h"
|
||||
#include "lwip/err.h"
|
||||
#include "lwip/sys.h"
|
||||
#endif
|
||||
|
||||
#ifdef USE_ESP8266
|
||||
#include "esp8266.h"
|
||||
#endif
|
||||
|
||||
void application_entry(void *arg)
|
||||
{
|
||||
#ifdef USE_LWIP
|
||||
dns_init();
|
||||
MX_LWIP_Init();
|
||||
#endif
|
||||
|
||||
#ifdef USE_ESP8266
|
||||
extern int esp8266_sal_init(hal_uart_port_t uart_port);
|
||||
extern int esp8266_join_ap(const char *ssid, const char *pwd);
|
||||
esp8266_sal_init(HAL_UART_PORT_0);
|
||||
esp8266_join_ap("SheldonDai", "srnr6x9xbhmb0");
|
||||
#endif
|
||||
|
||||
#ifdef USE_NB_BC35
|
||||
extern int bc35_28_95_sal_init(hal_uart_port_t uart_port);
|
||||
bc35_28_95_sal_init(HAL_UART_PORT_0);
|
||||
#endif
|
||||
|
||||
data_template_light_thread();
|
||||
|
||||
while (1) {
|
||||
printf("This is a tencent cloud sdk data template demo!\r\n");
|
||||
tos_task_delay(1000);
|
||||
}
|
||||
}
|
||||
|
@@ -1,189 +0,0 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright (C) 2016 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
|
||||
* Licensed under the MIT License (the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://opensource.org/licenses/MIT
|
||||
|
||||
* 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <limits.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include "tos_k.h"
|
||||
#include "qcloud.h"
|
||||
|
||||
|
||||
static int sg_count = 0;
|
||||
static int sg_sub_packet_id = -1;
|
||||
|
||||
void event_handler(void *client, void *context, mqtt_event_t *event) {
|
||||
mqtt_incoming_msg_t* mqtt_messge = NULL;
|
||||
uint16_t packet_id = *(uint16_t *)event->message;
|
||||
|
||||
switch(event->type) {
|
||||
case MQTT_EVENT_UNDEF:
|
||||
QCLOUD_LOG_I("undefined event occur.");
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_DISCONNECT:
|
||||
QCLOUD_LOG_I("MQTT disconnect.");
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_RECONNECT:
|
||||
QCLOUD_LOG_I("MQTT reconnect.");
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_PUBLISH_RECVEIVED:
|
||||
mqtt_messge = (mqtt_incoming_msg_t*)event->message;
|
||||
QCLOUD_LOG_I("topic message arrived but without any related handle: topic=%.*s, topic_msg=%.*s",
|
||||
mqtt_messge->topic_len,
|
||||
mqtt_messge->topic,
|
||||
mqtt_messge->payload_len,
|
||||
mqtt_messge->payload);
|
||||
break;
|
||||
case MQTT_EVENT_SUBCRIBE_SUCCESS:
|
||||
QCLOUD_LOG_I("subscribe success, packet-id=%u", (unsigned int)packet_id);
|
||||
sg_sub_packet_id = packet_id;
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_SUBCRIBE_TIMEOUT:
|
||||
QCLOUD_LOG_I("subscribe wait ack timeout, packet-id=%u", (unsigned int)packet_id);
|
||||
sg_sub_packet_id = packet_id;
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_SUBCRIBE_NACK:
|
||||
QCLOUD_LOG_I("subscribe nack, packet-id=%u", (unsigned int)packet_id);
|
||||
sg_sub_packet_id = packet_id;
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_UNSUBCRIBE_SUCCESS:
|
||||
QCLOUD_LOG_I("unsubscribe success, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_UNSUBCRIBE_TIMEOUT:
|
||||
QCLOUD_LOG_I("unsubscribe timeout, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_UNSUBCRIBE_NACK:
|
||||
QCLOUD_LOG_I("unsubscribe nack, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_PUBLISH_SUCCESS:
|
||||
QCLOUD_LOG_I("publish success, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_PUBLISH_TIMEOUT:
|
||||
QCLOUD_LOG_I("publish timeout, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_PUBLISH_NACK:
|
||||
QCLOUD_LOG_I("publish nack, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
default:
|
||||
QCLOUD_LOG_I("Should NOT arrive here.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* MQTT<54><54>Ϣ<EFBFBD><CFA2><EFBFBD>մ<EFBFBD><D5B4><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
*
|
||||
* @param topicName topic<69><63><EFBFBD><EFBFBD>
|
||||
* @param topicNameLen topic<69><63><EFBFBD><EFBFBD>
|
||||
* @param message <20>Ѷ<EFBFBD><D1B6><EFBFBD><EFBFBD><EFBFBD>Ϣ<EFBFBD>Ľṹ
|
||||
* @param userData <20><>Ϣ<EFBFBD><CFA2><EFBFBD><EFBFBD>
|
||||
*/
|
||||
static void on_message_callback(void *client, mqtt_incoming_msg_t *message, void *private_data)
|
||||
{
|
||||
if (!message) {
|
||||
return;
|
||||
}
|
||||
|
||||
QCLOUD_LOG_I("Receive Message With topicName:%.*s, payload:%.*s",
|
||||
(int) message->topic_len, message->topic, (int) message->payload_len, (char *)message->payload);
|
||||
}
|
||||
|
||||
static qcloud_device_t device;
|
||||
static mqtt_connect_opt_t connect_opt;
|
||||
static qcloud_mqtt_client_t client;
|
||||
|
||||
static char topic_filter[128] = {0};
|
||||
|
||||
#define MAX_SIZE_OF_TOPIC_CONTENT 100
|
||||
static char topic_payload[MAX_SIZE_OF_TOPIC_CONTENT + 1] = {0};
|
||||
|
||||
void mqtt_basic_thread(void)
|
||||
{
|
||||
qcloud_err_t rc;
|
||||
mqtt_publish_opt_t publish_opt;
|
||||
mqtt_subscribe_opt_t subscribe_opt;
|
||||
|
||||
QCLOUD_LOG_I("mqtt_sample start");
|
||||
|
||||
qcloud_device_create(&device, "XC31USKYPL", "dev001", "Pz1wK0fVJHxSojqxDuuvmg==");
|
||||
|
||||
qcloud_mqtt_connect_opt_create(&connect_opt, &device, MQTT_VERSION_3_1_1, 240, MQTT_CLEAN_SESSION_STATE_ENABLED);
|
||||
|
||||
qcloud_mqtt_client_create(&client, &device, event_handler, NULL, QCLOUD_AUTO_CONN_STATE_ENABLED);
|
||||
|
||||
qcloud_mqtt_client_connect(&client, &connect_opt);
|
||||
|
||||
osal_snprintf(topic_filter, sizeof(topic_filter), "%s/%s/%s", device.product_id, device.device_name, "data");
|
||||
|
||||
subscribe_opt.message_handler = on_message_callback;
|
||||
subscribe_opt.private_data = NULL;
|
||||
subscribe_opt.qos = MQTT_QOS0;
|
||||
|
||||
rc = qcloud_mqtt_client_subscribe(&client, topic_filter, &subscribe_opt);
|
||||
|
||||
if (rc != QCLOUD_ERR_SUCCESS) {
|
||||
QCLOUD_LOG_E("Subscribe Failed: %d", rc);
|
||||
} else {
|
||||
QCLOUD_LOG_D("Subscribe success");
|
||||
}
|
||||
|
||||
do {
|
||||
rc = qcloud_mqtt_client_yield(&client, &connect_opt, 3000);
|
||||
if (rc == QCLOUD_ERR_MQTT_ATTEMPTING_RECONNECT) {
|
||||
osal_sleep_ms(1000);
|
||||
continue;
|
||||
} else if (rc != QCLOUD_ERR_SUCCESS && rc != QCLOUD_ERR_MQTT_RECONNECTED) {
|
||||
QCLOUD_LOG_E("exit with error: %d", rc);
|
||||
break;
|
||||
}
|
||||
|
||||
// <20>ȴ<EFBFBD><C8B4><EFBFBD><EFBFBD>Ľ<EFBFBD><C4BD><EFBFBD>
|
||||
if (sg_sub_packet_id > 0) {
|
||||
sprintf(topic_filter, "%s/%s/%s", device.product_id, device.device_name, "data");
|
||||
osal_snprintf(topic_payload, sizeof(topic_payload), "{\"action\": \"publish_test\", \"count\": \"%d\"}", sg_count++);
|
||||
|
||||
memset(&publish_opt, 0, sizeof(mqtt_publish_opt_t));
|
||||
publish_opt.qos = MQTT_QOS1;
|
||||
publish_opt.payload = topic_payload;
|
||||
publish_opt.payload_len = strlen(topic_payload);
|
||||
|
||||
rc = qcloud_mqtt_client_publish(&client, topic_filter, &publish_opt);
|
||||
if (rc != QCLOUD_ERR_SUCCESS) {
|
||||
QCLOUD_LOG_E("client publish topic failed :%d.", rc);
|
||||
}
|
||||
}
|
||||
|
||||
osal_sleep_ms(1000);
|
||||
} while (1);
|
||||
|
||||
qcloud_mqtt_client_destroy(&client);
|
||||
|
||||
return;
|
||||
}
|
||||
|
Reference in New Issue
Block a user