已开启
添加sle only demo #87
qq_45910196创建于 9 天前
添加sle only demo #87
已开启
共 3 个文件变更+710-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 | +} | ||
| @@ -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 | +``` | ||
| @@ -0,0 +1,591 @@ | |||
| 1 | + | ||
| 2 | + | ||
| 3 | + | ||
| 4 | + | ||
| 5 | + | ||
| 6 | + | ||
| 7 | + | ||
| 8 | + | ||
| 9 | + | ||
| 10 | + | ||
| 11 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + | ||
| 20 | + | ||
| 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":1或0} | ||
| 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 | + | ||
| 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); | ||