3 个文件变更+710-0
Asle/hi3863/BUILD.gn+64-0
@@ -0,0 +1,64 @@
1+# Copyright (c) 2024 Shenzhen Kaihong Digital Industry Development Co.,Ltd.
2+# Licensed under the Apache License, Version 2.0 (the "License");
3+# you may not use this file except in compliance with the License.
4+# You may obtain a copy of the License at
5+#
6+# http://www.apache.org/licenses/LICENSE-2.0
7+#
8+# Unless required by applicable law or agreed to in writing, software
9+# distributed under the License is distributed on an "AS IS" BASIS,
10+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+# See the License for the specific language governing permissions and
12+# limitations under the License.
13+ 
14+import("//build/lite/config/component/lite_component.gni")
15+import("//build/ohos.gni")
16+ 
17+ohos_static_library("sle_demo") {
18+ subsystem_name = "communication"
19+ part_name = "iot_connect"
20+ 
21+ sources=["sle_demo.c",]
22+
23+ include_dirs = [
24+ "//commonlibrary/utils_lite/include",
25+ 
26+ #3863
27+ "//device/soc/hisilicon/hi3863v100/sdk_liteos/include",
28+ "//device/soc/hisilicon/hi3863v100/sdk_liteos/include/middleware/services/bts/common",
29+ "//device/soc/hisilicon/hi3863v100/sdk_liteos/include/middleware/services/bts/ble",
30+ "//device/soc/hisilicon/hi3863v100/sdk_liteos/kernel/osal/include",
31+ "//device/soc/hisilicon/hi3863v100/sdk_liteos/kernel/osal/include/memory",
32+ "//base/iothardware/peripheral/interfaces/inner_api",
33+ 
34+ "//device/soc/hisilicon/hi3863v100/sdk_liteos/middleware/utils/app_init",
35+ 
36+ #sle
37+ "//device/soc/hisilicon/hi3863v100/sdk_liteos/include/middleware/services/bts/sle",
38+
39+ "//foundation/iot_connect/iot_connect/include",
40+ "//foundation/iot_connect/iot_connect/interface",
41+ "//foundation/iot_connect/iot_connect/utils/include",
42+ "//foundation/communication/bluetooth/interfaces/inner_api/include/c_header",
43+ "//foundation/communication/bluetooth/frameworks/inner/ipc/common",
44+
45+ "//third_party/cJSON"
46+ ]
47+ deps = []
48+ 
49+ include_dirs +=[
50+ "//foundation/communication/iot_connect/interfaces/kits/common",
51+ "//foundation/communication/iot_connect/interfaces/kits/oh_connect",
52+ 
53+ "//device/soc/hisilicon/hi3863v100/sdk_liteos/include/driver",
54+ "//device/soc/hisilicon/hi3863v100/sdk_liteos/drivers/drivers/hal/gpio",
55+ "//device/soc/hisilicon/hi3863v100/sdk_liteos/drivers/chips/ws63/porting/gpio",
56+ "//device/soc/hisilicon/hi3863v100/sdk_liteos/drivers/drivers/hal/gpio/v150",
57+ "//device/soc/hisilicon/hi3863v100/sdk_liteos/drivers/chips/ws63/porting/pinctrl",
58+ "//device/soc/hisilicon/hi3863v100/sdk_liteos/middleware/chips/ws63/littlefs"
59+ ]
60+
61+ deps += [
62+ "//foundation/communication/iot_connect:iotc_static",
63+ ]
64+}
Asle/hi3863/README.md+55-0
@@ -0,0 +1,55 @@
1+# Hi3863 SLE Only 编译构建
2+ 
3+## 1、获取小型系统底座
4+ 
5+```bash
6+repo init -u git@gitcode.com:ohos-oneconnect/manifest.git -b OpenHarmony-5.1.0-Release -m nearlink.xml -g ohos:mini --no-repo-verify
7+repo sync -c
8+repo forall -c 'git lfs pull'
9+bash build/prebuilts_download.sh (如执行不成功,可加上sudo权限)
10+```
11+ 
12+## 2、编译环境搭建
13+ 
14+```bash
15+编译前步骤:
16+ 
17+1. 通过cd切换到foundation/communication/nearlink/目录,
18+ 执行bash copy.sh //使得nearlink仓目录下的openharmony目录覆盖到代码根目录
19+ 
20+2. 通过cd切回到代码根目录:cd ../../../
21+ 
22+ 执行hb set . //选择hi3863产品,如nearlink_dk3863e
23+ hb build -f //当前为共建模式,已经调整patch方式为copy方式,无需patch
24+```
25+ 
26+## 3、添加demo
27+ 
28+将hi3863文件夹放置在`vendor/hihope/nearlink_dk3863e/app`目录下,并将重命名为sle_demo,并修改`vendor/hihope/nearlink_dk3863e/app/BUILD.gn`
29+ 
30+```gn
31+group("app") {
32+ deps = [
33+ # "gas_leak_detector",
34+ # "gas_control_valves",
35+ # "sn_test",
36+ "sle_demo"
37+ ]
38+}
39+```
40+ 
41+## 4、Iotc修改
42+ 
43+- 进入`foundation/communication/iot_connect/iotc.gni`关闭ble和wifi支持
44+ 
45+```gni
46+declare_args() {
47+ iot_connect_ble_support = false
48+ iot_connect_sle_support = false
49+ iot_connect_wifi_support = false
50+ iot_connect_ailife_support = false
51+ iot_connect_mbedtls_v2_support = false
52+ iot_connect_mbedtls_psk_support = false
53+ iot_connect_mbedtls_ccm_support = false
54+}
55+```
Asle/hi3863/sle_demo.c+591-0
@@ -0,0 +1,591 @@
1+#include <stdlib.h>
2+#include <string.h>
3+#include <stdbool.h>
4+#include "soc_osal.h"
5+#include "app_init.h"
6+#include "iotc_errcode.h"
7+#include "iotc_oh_sle.h"
8+#include "iotc_oh_sdk.h"
9+#include "iotc_oh_device.h"
10+#include "securec.h"
11+#include "cJSON.h"
12+#include "iotc_conf.h"
13+ 
14+#define BLINKY_TASK_PRIO 24
15+#define BLINKY_TASK_STACK_SIZE 0x1000
16+ 
17+#define MAINTASK_SIZE (5*1024)
18+#define MONITOR_SIZE (1*1024)
19+ 
20+#define DEMO_LOG(...) do { \
21+ printf("DEMO[%s:%u]", __func__, __LINE__); \
22+ printf(__VA_ARGS__); \
23+ printf("\r\n"); \
24+ } while (0)
25+ 
26+/**
27+ * [MUST] MODIFY ME
28+ * DEV_INFO为产品信息,应与云/APP侧配置一致
29+ */
30+static const IotcDeviceInfo DEV_INFO = {
31+ .sn = "hi3863test001",
32+ .prodId = "0001H",
33+ .subProdId = "63",
34+ .model = "Hi3863",
35+ .devTypeId = "1005",
36+ .devTypeName = "SmartSwitch",
37+ .manuId = "111",
38+ .manuName = "HiSilicon",
39+ .devName = "SmartSwitch",
40+ .fwv = "1.0.0",
41+ .hwv = "1.0.0",
42+ .swv = "1.0.0",
43+ .protType = IOTC_PROT_TYPE_SLE,
44+};
45+ 
46+/**
47+ * [MUST] MODIFY ME
48+ * SVC_INFO为产品服务信息,应与云/APP侧配置一致
49+ */
50+static const IotcServiceInfo SVC_INFO[] = {
51+ {"switch", "switch"},
52+};
53+ 
54+/**
55+ * [MUST] MODIFY ME
56+ * PIN_CODE为产品使用的配网PIN码,应与APP侧配置一致
57+ */
58+static const char *PIN_CODE = "01234567";
59+ 
60+/**
61+ * [MUST] MODIFY ME
62+ * AC_KEY为产品厂商的AC KEY,应与云侧配置一致
63+ */
64+const uint8_t AC_KEY[IOTC_AC_KEY_LEN] = {
65+ 0x49, 0x3F, 0x45, 0x4A, 0x3A, 0x72, 0x38, 0x7B, 0x36, 0x32, 0x50, 0x3C, 0x49, 0x39, 0x62, 0x38,
66+ 0x72, 0xCB, 0x6D, 0xC5, 0xAE, 0xE5, 0x4A, 0x82, 0xD3, 0xE5, 0x6D, 0xF5, 0x36, 0x82, 0x62, 0xEB,
67+ 0x89, 0x30, 0x6C, 0x88, 0x32, 0x56, 0x23, 0xFD, 0xB8, 0x67, 0x90, 0xA7, 0x7B, 0x61, 0x1E, 0xAE
68+};
69+ 
70+/**
71+ * [MUST] MODIFY ME
72+ * 下面代码为开关服务的实现样例
73+ * SwitchPutCharState函数为控制指令的处理,控制指令报文为{"on":10}
74+ * SwitchGetCharState函数为查询指令的处理
75+ * g_switch保存了开关的状态
76+ * 产品可以参考该DEMO并实现自己的服务函数添加到SVC_MAP中
77+ */
78+static bool g_switch = false;
79+ 
80+static int32_t SwitchPutCharState(const IotcServiceInfo *svc, const char *data, uint32_t len)
81+{
82+ if (data == NULL || len == 0) { DEMO_LOG("param invalid"); return -1; }
83+ 
84+ cJSON *json = cJSON_Parse(data);
85+ if (json == NULL) { DEMO_LOG("parse error"); return -1; }
86+ cJSON *item = cJSON_GetObjectItem(json, "on");
87+ if (item == NULL || !cJSON_IsNumber(item)) { cJSON_Delete(json); DEMO_LOG("get on error"); return -1; }
88+ int on = cJSON_GetNumberValue(item);
89+ DEMO_LOG("switch on put %d=>%d", g_switch, on);
90+ g_switch = (on == 0) ? false : true;
91+ cJSON_Delete(json);
92+ return 0;
93+}
94+ 
95+static int32_t SwitchGetCharState(const IotcServiceInfo *svc, char **data, uint32_t *len)
96+{
97+ if (data == NULL || *data != NULL) { DEMO_LOG("param invalid"); return -1; }
98+ 
99+ cJSON *json = cJSON_CreateObject();
100+ if (json == NULL) { DEMO_LOG("create obj error"); return -1; }
101+ if (cJSON_AddNumberToObject(json, "on", g_switch) == NULL) { cJSON_Delete(json); DEMO_LOG("add num error"); return -1; }
102+ *data = cJSON_PrintUnformatted(json);
103+ cJSON_Delete(json);
104+ if (*data == NULL) { DEMO_LOG("json print error"); return -1; }
105+ DEMO_LOG("switch on get %d", g_switch);
106+ 
107+ *len = strlen(*data);
108+ return 0;
109+}
110+ 
111+/**
112+ * [MUST] MODIFY ME
113+ * SVC_MAP为服务处理函数表
114+ * 其中svc为SVC_INFO中的服务指针,用于标识是哪个服务
115+ * putCharState为控制指令处理函数
116+ * getCharState为查询指令处理函数
117+ * 开发者应实现自己的服务处理函数并添加到该表中
118+ */
119+const struct SvcMap {
120+ const IotcServiceInfo *svc;
121+ int32_t (*putCharState)(const IotcServiceInfo *svc, const char *data, uint32_t len);
122+ int32_t (*getCharState)(const IotcServiceInfo *svc, char **data, uint32_t *len);
123+} SVC_MAP[] = {
124+ {&SVC_INFO[0], SwitchPutCharState, SwitchGetCharState},
125+};
126+ 
127+/**
128+ * [SHOULD] MODIFY ME
129+ * PutCharState为注册到iot connect中的控制指令处理业务回调
130+ * 当收到控制指令时,该函数会通过SVC_MAP进行SID维度的控制分发
131+ */
132+static int32_t PutCharState(const IotcCharState state[], uint32_t num)
133+{
134+ if (state == NULL || num == 0) {
135+ DEMO_LOG("param invalid");
136+ return -1;
137+ }
138+ 
139+ int32_t ret = 0;
140+ for (uint32_t i = 0; i < num; ++i) {
141+ for (uint32_t j = 0; j < (sizeof(SVC_MAP) / sizeof(SVC_MAP[0])); ++j) {
142+ DEMO_LOG("put char sid:%s data:%s", state[i].svcId, state[i].data);
143+ if (strcmp(state[i].svcId, SVC_MAP[j].svc->svcId) != 0 || SVC_MAP[j].putCharState == NULL) {
144+ continue;
145+ }
146+ int32_t curRet = SVC_MAP[j].putCharState(SVC_MAP[j].svc, state[i].data, state[i].len);
147+ if (curRet != 0) {
148+ ret = curRet;
149+ DEMO_LOG("put char sid:%s error %d", state[i].svcId, ret);
150+ }
151+ }
152+ }
153+ return ret;
154+}
155+ 
156+/**
157+ * [SHOULD] MODIFY ME
158+ * GetCharState为注册到iot connect中的查询指令处理业务回调
159+ * 当收到查询指令时,该函数会通过SVC_MAP进行SID维度的查询分发
160+ */
161+static int32_t GetCharState(const IotcCharState state[], char *out[], uint32_t len[], uint32_t num)
162+{
163+ if (state == NULL || num == 0 || out == NULL || len == NULL) {
164+ DEMO_LOG("param invalid");
165+ return -1;
166+ }
167+ 
168+ int32_t ret = 0;
169+ for (uint32_t i = 0; i < num; ++i) {
170+ for (uint32_t j = 0; j < (sizeof(SVC_MAP) / sizeof(SVC_MAP[0])); ++j) {
171+ DEMO_LOG("get char sid:%s", state[i].svcId);
172+ if (strcmp(state[i].svcId, SVC_MAP[j].svc->svcId) != 0 || SVC_MAP[j].getCharState == NULL) {
173+ continue;
174+ }
175+ int32_t curRet = SVC_MAP[j].getCharState(SVC_MAP[j].svc, &out[i], &len[i]);
176+ if (curRet != 0) {
177+ ret = curRet;
178+ DEMO_LOG("get char sid:%s error %d", state[i].svcId, ret);
179+ }
180+ }
181+ }
182+ 
183+ return ret;
184+}
185+ 
186+/**
187+ * [SHOULD] MODIFY ME
188+ * ReportAll为注册到iot connect中的全量服务上报业务回调
189+ * 当设备上线时会通过该接口上报所有可上报服务,同步端侧和APP/云侧服务状态
190+ */
191+static int32_t ReportAll(void)
192+{
193+ IotcCharState reportInfo[sizeof(SVC_MAP) / sizeof(SVC_MAP[0])] = {0};
194+ int32_t ret;
195+ for (uint32_t i = 0; i < (sizeof(SVC_MAP) / sizeof(SVC_MAP[0])); ++i) {
196+ reportInfo[i].svcId = SVC_MAP[i].svc->svcId;
197+ ret = SVC_MAP[i].getCharState(SVC_MAP[i].svc, (char **)&reportInfo[i].data, &reportInfo[i].len);
198+ if (ret != 0) {
199+ DEMO_LOG("get char sid:%s error %d", reportInfo[i].svcId, ret);
200+ break;
201+ }
202+ }
203+ if (ret == 0) {
204+ ret = IotcOhDevReportCharState(reportInfo, sizeof(reportInfo) / sizeof(reportInfo[0]));
205+ }
206+ 
207+ for (uint32_t i = 0; i < (sizeof(SVC_MAP) / sizeof(SVC_MAP[0])); ++i) {
208+ if (reportInfo[i].data != NULL) {
209+ cJSON_free((char *)reportInfo[i].data);
210+ reportInfo[i].data = NULL;
211+ }
212+ }
213+ return ret;
214+}
215+ 
216+/**
217+ * [SHOULD] MODIFY ME
218+ * GetPincode为注册到iot connect中的获取PIN码回调
219+ * 设备配网或执行进场控制时,会通过该接口获取PIN码用来和APP协商会话秘钥
220+ */
221+static int32_t GetPincode(uint8_t *buf, uint32_t bufLen)
222+{
223+ if (buf == NULL || bufLen > IOTC_PINCODE_LEN) {
224+ DEMO_LOG("param invalid");
225+ return -1;
226+ }
227+ 
228+ int32_t ret = memcpy_s(buf, bufLen, PIN_CODE, strlen(PIN_CODE));
229+ if (ret != EOK) {
230+ return -1;
231+ }
232+ return 0;
233+}
234+ 
235+/**
236+ * [SHOULD] MODIFY ME
237+ * GetAcKey为注册到iot connect中的获取AC KEY回调
238+ */
239+static int32_t GetAcKey(uint8_t *buf, uint32_t bufLen)
240+{
241+ if (buf == NULL || bufLen > IOTC_AC_KEY_LEN) {
242+ DEMO_LOG("param invalid");
243+ return -1;
244+ }
245+ int32_t ret = memcpy_s(buf, bufLen, AC_KEY, sizeof(AC_KEY));
246+ if (ret != EOK) {
247+ return -1;
248+ }
249+ return 0;
250+}
251+ 
252+/**
253+ * [MUST] MODIFY ME
254+ * NoticeReboot为注册到iot connect中的重启回调,开发者应在通知重启时重启设备或进程
255+ */
256+ 
257+static const uint8_t PRESET_PSK[IOTC_OH_AUTH_PSK_LEN] = {
258+ 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
259+ 0x39, 0x30, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66,
260+ 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
261+ 0x39, 0x30, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66,
262+};
263+ 
264+/**
265+ * [MUST] MODIFY ME
266+ * NoticeReboot为注册到iot connect中的重启回调,开发者应在通知重启时重启设备或进程
267+ */
268+static int32_t NoticeReboot(IotcRebootReason res)
269+{
270+ DEMO_LOG("notice reboot res %d", res);
271+ return 0;
272+}
273+ 
274+/**
275+ * [MUST] MODIFY ME
276+ * PresetProvision为设置预置设备认证数据(PSK方式)
277+ */
278+static int32_t PresetProvision(IotcProvisionData *provision)
279+{
280+ /* 产品认证 by SN */
281+ provision->prod.authType = IOTC_PROD_AUTH_TYPE_SN;
282+ provision->prod.auth.pccSerialNumber = DEV_INFO.sn;
283+ /* 设备认证 by PSK */
284+ provision->dev.authType = IOTC_DEV_AUTH_TYPE_PSK;
285+ provision->dev.auth.psk.vid = DEV_INFO.manuId;
286+ provision->dev.auth.psk.pid = DEV_INFO.prodId;
287+ provision->dev.auth.psk.pskId = DEV_INFO.sn;
288+ provision->dev.auth.psk.psk = PRESET_PSK;
289+ provision->dev.auth.psk.pskLen = sizeof(PRESET_PSK);
290+ return 0;
291+}
292+ 
293+static void ClearProvisionData(IotcProvisionData *provision)
294+{
295+ (void)provision;
296+}
297+ 
298+/* ================================================================
299+ * 自定义数据接收回调
300+ * vendor 数据格式: [{"sid":"switch","data":{"on":1}}]
301+ * 根据 sid 匹配 SVC_MAP,调用对应的 putCharState 处理控制指令
302+ * ================================================================ */
303+static int32_t SleRecvCustomData(const uint8_t *data, uint32_t len)
304+{
305+ DEMO_LOG("SleRecvCustomData len:%u data:%.*s", len, len, (const char *)data);
306+ if (data == NULL || len == 0) {
307+ DEMO_LOG("param invalid");
308+ return -1;
309+ }
310+ 
311+ cJSON *json = cJSON_Parse((const char *)data);
312+ if (json == NULL) {
313+ DEMO_LOG("json parse error");
314+ return -1;
315+ }
316+ 
317+ if (!cJSON_IsArray(json)) {
318+ DEMO_LOG("vendor is not array");
319+ cJSON_Delete(json);
320+ return -1;
321+ }
322+ 
323+ int32_t ret = 0;
324+ for (int i = 0; i < cJSON_GetArraySize(json); ++i) {
325+ cJSON *serviceItem = cJSON_GetArrayItem(json, i);
326+ if (serviceItem == NULL) {
327+ continue;
328+ }
329+ 
330+ cJSON *sidItem = cJSON_GetObjectItem(serviceItem, "sid");
331+ cJSON *dataItem = cJSON_GetObjectItem(serviceItem, "data");
332+ if (sidItem == NULL || !cJSON_IsString(sidItem) || dataItem == NULL) {
333+ DEMO_LOG("invalid service item");
334+ continue;
335+ }
336+ 
337+ const char *sid = cJSON_GetStringValue(sidItem);
338+ char *dataStr = cJSON_PrintUnformatted(dataItem);
339+ if (dataStr == NULL) {
340+ continue;
341+ }
342+ 
343+ DEMO_LOG("recv custom data sid:%s data:%s", sid, dataStr);
344+ 
345+ /* 遍历 SVC_MAP,匹配 sid 并调用 putCharState */
346+ for (uint32_t j = 0; j < (sizeof(SVC_MAP) / sizeof(SVC_MAP[0])); ++j) {
347+ if (SVC_MAP[j].svc->svcId == NULL || SVC_MAP[j].putCharState == NULL ||
348+ strcmp(sid, SVC_MAP[j].svc->svcId) != 0) {
349+ continue;
350+ }
351+ int32_t curRet = SVC_MAP[j].putCharState(SVC_MAP[j].svc, dataStr, strlen(dataStr));
352+ if (curRet != 0) {
353+ DEMO_LOG("put char sid:%s error %d", sid, curRet);
354+ ret = curRet;
355+ }
356+ break;
357+ }
358+ 
359+ cJSON_free(dataStr);
360+ 
361+ /* 控制指令处理后,通过 sendCustomData 上报当前状态 */
362+ sendCustomData();
363+ }
364+ 
365+ cJSON_Delete(json);
366+ return ret;
367+}
368+ 
369+#define SET_OH_SDK_OPTION(ret, option, ...) \
370+ do { \
371+ (ret) = IotcOhSetOption((option), __VA_ARGS__); \
372+ if ((ret) != 0) { \
373+ DEMO_LOG("set option %d error %d", (option), (ret)); \
374+ return ret; \
375+ } \
376+ } while (0);
377+ 
378+/**
379+ * [MUST] USE ME
380+ * IotcOhDemoEntry为iot connect业务入口,开发者应在业务进程启动时调用
381+ */
382+int32_t IotcOhDemoEntry(void)
383+{
384+ /* 初始化设备信息模块 */
385+ int32_t ret = IotcOhDevInit();
386+ if (ret != 0) {
387+ DEMO_LOG("init device error %d", ret);
388+ return ret;
389+ }
390+ 
391+ /* 配置 iotc 主线程栈大小,避免 provision/SPEKE 栈溢出 */
392+ SET_OH_SDK_OPTION(ret, IOTC_OH_OPTION_SDK_MAIN_TASK_SIZE, MAINTASK_SIZE);
393+ IotcOhSetOption(IOTC_OH_OPTION_SDK_MONITOR_TASK_SIZE, MONITOR_SIZE);
394+ /* 初始化SLE发现控制模块 */
395+ ret = IotcOhSleEnable();
396+ if (ret != 0) {
397+ DEMO_LOG("enable sle connect error %d", ret);
398+ return ret;
399+ }
400+ 
401+ /* 配置iot connect必要的回调 */
402+ SET_OH_SDK_OPTION(ret, IOTC_OH_OPTION_DEVICE_PUT_CHAR_STATE_CALLBACK, PutCharState);
403+ SET_OH_SDK_OPTION(ret, IOTC_OH_OPTION_DEVICE_GET_CHAR_STATE_CALLBACK, GetCharState);
404+ SET_OH_SDK_OPTION(ret, IOTC_OH_OPTION_DEVICE_REPORT_ALL_CALLBACK, ReportAll);
405+ SET_OH_SDK_OPTION(ret, IOTC_OH_OPTION_DEVICE_GET_PINCODE_CALLBACK, GetPincode);
406+ SET_OH_SDK_OPTION(ret, IOTC_OH_OPTION_DEVICE_GET_AC_KEY_CALLBACK, GetAcKey);
407+ SET_OH_SDK_OPTION(ret, IOTC_OH_OPTION_DEVICE_DATA_FREE_CALLBACK, cJSON_free);
408+ SET_OH_SDK_OPTION(ret, IOTC_OH_OPTION_DEVICE_REBOOT_CALLBACK, NoticeReboot);
409+ /* 注册自定义数据接收回调(业务层解析 vendor → 匹配 SVC_MAP → 调用 putCharState) */
410+ SET_OH_SDK_OPTION(ret, IOTC_OH_OPTION_SLE_RECV_CUSTOM_DATA_CALLBACK, SleRecvCustomData);
411+ /* 配置设备信息与服务信息 */
412+ DEMO_LOG("IOTC_OH_OPTION_DEVICE_DEV_INFO sn:%s manuName:%s fwv:%s", DEV_INFO.sn, DEV_INFO.manuName, DEV_INFO.fwv);
413+ SET_OH_SDK_OPTION(ret, IOTC_OH_OPTION_DEVICE_DEV_INFO, &DEV_INFO);
414+ SET_OH_SDK_OPTION(ret, IOTC_OH_OPTION_DEVICE_SVC_INFO, SVC_INFO, sizeof(SVC_INFO) / sizeof(SVC_INFO[0]));
415+ /* 启动后的广播超时时间 */
416+ SET_OH_SDK_OPTION(ret, IOTC_OH_OPTION_SLE_START_UP_ADV_TIMEOUT, (10 * 60 * 1000));
417+ 
418+ /* 配置设备认证数据(预置PSK provision) */
419+ IotcPresetProvisionCallbacks provisionCallbacks = { PresetProvision, ClearProvisionData };
420+ SET_OH_SDK_OPTION(ret, IOTC_OH_OPTION_DEVICE_PRESET_PROVISION_CALLBACKS, &provisionCallbacks);
421+ 
422+ /* 拉起iot connect业务线程 */
423+ ret = IotcOhMain();
424+ if (ret != 0) {
425+ DEMO_LOG("iotc oh main error %d", ret);
426+ return ret;
427+ }
428+ 
429+ DEMO_LOG("iotc oh main success");
430+ return ret;
431+}
432+ 
433+/**
434+ * [SHOULD] USE ME
435+ * IotcOhDemoExit为iot connect业务退出函数,开发者应在不使用相关业务并需要释放iot connect资源时调用
436+ */
437+void IotcOhDemoExit(void)
438+{
439+ int32_t ret = IotcOhStop();
440+ if (ret != 0) {
441+ DEMO_LOG("iotc stop error %d", ret);
442+ }
443+ 
444+ ret = IotcOhSleDisable();
445+ if (ret != 0) {
446+ DEMO_LOG("iotc sle disable error %d", ret);
447+ }
448+ 
449+ ret = IotcOhDevDeinit();
450+ if (ret != 0) {
451+ DEMO_LOG("iotc dev info deinit error %d", ret);
452+ }
453+}
454+ 
455+/**
456+ * [MUST] USE ME
457+ * IotcOhDemoRestore为iot connect恢复出厂函数,开发者应在设备被重置时调用
458+ */
459+void IotcOhDemoRestore(void)
460+{
461+ DEMO_LOG("restore");
462+ IotcOhRestore();
463+}
464+ 
465+/**
466+ * [MUST] USE ME
467+ * IotcOhDemoReport为设备数据变化上报的DEMO函数,开发者应在设备服务发生主动变化时调用该接口上报
468+ */
469+void IotcOhDemoReport(const char *sid)
470+{
471+ DEMO_LOG("report");
472+ if (sid == NULL) {
473+ DEMO_LOG("report sid null");
474+ return;
475+ }
476+ IotcCharState reportInfo = {0};
477+ int32_t ret = -1;
478+ for (uint32_t i = 0; i < (sizeof(SVC_MAP) / sizeof(SVC_MAP[0])); ++i) {
479+ if (SVC_MAP[i].svc->svcId == NULL || SVC_MAP[i].getCharState == NULL ||
480+ strcmp(sid, SVC_MAP[i].svc->svcId) != 0) {
481+ continue;
482+ }
483+ reportInfo.svcId = SVC_MAP[i].svc->svcId;
484+ ret = SVC_MAP[i].getCharState(SVC_MAP[i].svc, (char **)&reportInfo.data, &reportInfo.len);
485+ if (ret != 0) {
486+ DEMO_LOG("get char sid:%s error %d", sid, ret);
487+ }
488+ break;
489+ }
490+ if (ret == 0) {
491+ ret = IotcOhDevReportCharState(&reportInfo, 1);
492+ if (ret != 0) {
493+ DEMO_LOG("report char sid:%s error %d", sid, ret);
494+ } else {
495+ DEMO_LOG("report char sid:%s success", sid);
496+ }
497+ }
498+ 
499+ if (reportInfo.data != NULL) {
500+ cJSON_free((char *)reportInfo.data);
501+ }
502+ return;
503+}
504+ 
505+void sendCustomData(void)
506+{
507+ /* 构建 vendor 格式: {"vendor":[{"sid":"switch","data":{"on":1}}],"seq":N} */
508+ cJSON *dataJson = cJSON_CreateObject();
509+ if (dataJson == NULL) {
510+ DEMO_LOG("create obj error");
511+ goto cleanup;
512+ }
513+ if (cJSON_AddNumberToObject(dataJson, "on", g_switch) == NULL) {
514+ DEMO_LOG("add num error");
515+ goto cleanup;
516+ }
517+ 
518+ cJSON *vendorItem = cJSON_CreateObject();
519+ if (vendorItem == NULL) {
520+ DEMO_LOG("create vendorItem error");
521+ goto cleanup;
522+ }
523+ if (cJSON_AddStringToObject(vendorItem, "sid", "switch") == NULL) {
524+ DEMO_LOG("add sid error");
525+ goto cleanup;
526+ }
527+ if (cJSON_AddItemToObject(vendorItem, "data", dataJson) == NULL) {
528+ DEMO_LOG("add data error");
529+ goto cleanup;
530+ }
531+ 
532+ cJSON *vendor = cJSON_CreateArray();
533+ if (vendor == NULL) {
534+ DEMO_LOG("create vendor error");
535+ goto cleanup;
536+ }
537+ cJSON_AddItemToArray(vendor, vendorItem);
538+ 
539+ cJSON *jsonData = cJSON_CreateObject();
540+ if (cJSON_AddItemToObject(jsonData, "vendor", vendor) == NULL) {
541+ DEMO_LOG("add vendor error");
542+ goto cleanup;
543+ }
544+ 
545+ static uint8_t g_seq = 0;
546+ if (cJSON_AddNumberToObject(jsonData, "seq", g_seq) == NULL) {
547+ DEMO_LOG("add seq error");
548+ goto cleanup;
549+ }
550+ g_seq++;
551+ 
552+ char *data = cJSON_PrintUnformatted(jsonData);
553+ if (*data == NULL) {
554+ DEMO_LOG("json print error");
555+ goto cleanup;
556+ }
557+ uint32_t len = strlen(data);
558+ DEMO_LOG("sendCustomData len:%d data:%s", len, data);
559+ 
560+ IotcOhSleSendCustomSecData("dev_id", data, len);
561+ 
562+cleanup:
563+ if (data) cJSON_free(data);
564+ if (jsonData) cJSON_Delete(jsonData);
565+}
566+ 
567+ 
568+int sle_demo_main(void)
569+{
570+ sleep(1);
571+ IotcOhDemoEntry();
572+ while (true)
573+ {
574+ sleep(1);
575+ }
576+ return 0;
577+}
578+ 
579+static void sle_only_demo_task(void)
580+{
581+ osal_task *task_handle = NULL;
582+ osal_kthread_lock();
583+ task_handle = osal_kthread_create((osal_kthread_handler)sle_demo_main, 0, "SleDemoTask", BLINKY_TASK_STACK_SIZE);
584+ if (task_handle != NULL) {
585+ osal_kthread_set_priority(task_handle, BLINKY_TASK_PRIO);
586+ osal_kfree(task_handle);
587+ }
588+ osal_kthread_unlock();
589+}
590+ 
591+app_run(sle_only_demo_task);