基於OpenHarmony和華為雲平臺打造的智能家居設備,分別為智能門鎖,儲物精靈 NFC版,儲物精靈Pro版三個設備。 ...
本文分享自華為雲社區《華為雲IoT+OpenHarmony的智能家居開發》,作者:袁睿。
一、選題說明
1. 選題為基於OpenHarmony的智能家居,應用場景為戶用,受益人群為住戶。
2. 開發的軟體設備為智能門鎖,儲物精靈,軟硬體開發都有的是光伏逆變器。
3. 解決的問題:
傳統的智能家居:智能單品,需要手動加入場景,無網不能智能控制
創新的智能家居:空間智能,自發現後融入場景,分散式軟匯流排控制
4. 關鍵功能點:
智能門鎖:密碼解鎖,NFC解鎖,數字管家控制,服務卡片控制
儲物精靈:密碼解鎖,NFC解鎖,防火簾控制,分散式軟匯流排控制
逆變器:單相逆變,隔離拓撲,組件小型化,高轉換率與低總諧波失真
二、競賽開發平臺
1. 操作系統:OpenHarmony 3.0
2. 開發軟體:VS code(Deveco studio tool),DevEco Studio
3. 開發板:深開鴻KHDVK-3861B,潤和DAYU200,潤和hispark AI Camera
3. 關於環境:
操作系統:Ubuntu 編譯構建:Python
包管理工具:HPM NPM 環境:Node.js
燒錄軟體:Hiburn USB串口驅動:CH341SER.exe
本地ssh:finalshell ssh文件對比:Beyond Conpare
4. 虛擬機
(1) 虛擬機環境
Ubuntu(華為的硬體開發一般都在此linux環境下進行)
虛擬機Vmware:下載之後使用上述提到的華為雲中國鏡像。
下載VS code的Linux版與OpenHarmony3.0源碼。
(2)虛擬機環境:
將Ubuntu Shell環境修改為bash:ls -l /bin/sh
在下載VS code後下載華為硬體編製插件(Device tool)
(3) HB編譯插件:
安裝:python3 -m pip install --user ohos-build
變數:vim ~/.bashrc
複製粘貼到.bashrc的最後一行:export PATH=~/.local/bin:$PATH
更新變數:source ~/.bashrc
檢查:pip install ohos-build
5. 逆變器的主要硬體選材:
(1)選材方案(選型依據)
Diode(二極體):高頻檢波電路,小型化,配對二極體不混組
Inductor(感應器):標稱電感值,自共振頻率(與互感值成正比)直流電阻(儘可能小),額定電流,直流重疊允許電流,溫度上升允許電流。
Resistor(電阻器):貼片電阻,根據穩定性需求選擇薄膜或厚膜
SPICE NMOS:封裝尺寸,基本上封裝越大越好,能承受的電流就越大;導通電壓Vgs,儘量選擇比實際電路中可提供的控制電壓小的;導通電阻Rds,越小越好相應的導通電阻越小、分壓越小、發熱也越小。寄生電容Cgs,會影響mos的打開速度。寄生電容太大,方波會失真Rds越小,Cgs越大。
(2)主要選材
半導體選材:國產半導體RX65T125PS2A
電源IC選材:國產IC晶元ID7s625
DSP處理器:洞察到STM32系列軟合了DSP處理器的功能。
三、方案詳述
(一)儲物精靈
(1)用戶角度:先從用戶角度考慮需求與如何使用,再從技術層面解析
(具體用戶使用方法這裡不多贅述,詳細內容直接看下午開發內容)
(2)實現原理:(下文的步驟會詳細介紹,在這裡先介紹初期設想)
① 關於Mqtt協議在華為雲的打通(設備線上激活):使用mqttX或mqttfx。
② 華為雲:根據提示創建並獲取密鑰等信息,獲取ClientID等身份識別信息,然後在雲端的Topic(事件主題)中自定義訂閱與發佈,對產品進行定義。
③ AppGallery Connect網站:創建並註冊HarmonyOS產品,根據提示流程。
④ 設備開發具體解析:每個設備都是一個子節點,實現了基於OpenHarmony設備的L0、L1、L2設備之間的互聯互通。主控程式基於 OpenHarmony JS應用程式框架設計實現,並使用MQTT物聯網通信協議接入華為雲IOT平臺,同時可將控制指令發送至華為雲IOT平臺,供雲端處理。DAYU開發板(軟體+硬體)具體實現為中控MQTT通信過程處於內核態驅動程式,JS應用通過發起介面調用後,進入用戶態調用內核態介面的流程,並且JS應用會將所需要向雲端發送的MQTT協議主題內容直接傳入內核態,內核態不作數據處理和解析,直接將數據發佈至雲端,這樣設計的目的是為了在添加設備的時候,僅需改變JS應用的數據結構,並不需要修改設備的代碼,完成瞭解耦合。
NFC錄入與記錄:使用NFC擴展板錄入,詳細請見下方軟匯流排設備通訊鏈接。
⑤ 智能識別對比:識別對象的資料庫,這裡的識別作為單一的照片識別。vuforia 的伺服器製作該 target 的識別資料庫,該資料庫要下載並綁定工程到target。圖片由攝像頭獲取視頻逐幀比對。
(3)設備側
第一步:網路連接 使設備接電後自動聯網
我們會在代碼中預置熱點名稱與密碼
在創建包後新建mqtt_entry.c用於存放熱點自連代碼的地址:
/home/open/Downloads/code-v3.0-LTS/OpenHarmony/applications/sample/wifi-iot/app/mqtt_demo
{ int ret; errno_t rc; hi_wifi_assoc_request assoc_req = {0}; /* 拷貝SSID到assoc的req */ /* copy SSID to assoc_req */ rc = memcpy_s(assoc_req.ssid, HI_WIFI_MAX_SSID_LEN + 1, "rui666", 8); //熱點名 /* WPA-PSK. CNcomment: 認證類型:WPA2-PSK.CNend */ if (rc != EOK) { return -1; } //熱點加密方式 assoc_req.auth = HI_WIFI_SECURITY_WPA2PSK; memcpy(assoc_req.key, "88888888", 11); //熱點的密碼 ret = hi_wifi_sta_connect(&assoc_req); if (ret != HISI_OK) { return -1; } return 0; } //預置熱點名和密碼 在設備通電後會自連
這裡把原有的ability等量代換成了自發現熱點。
*OpenHarmony_ability的碰一碰自發現與自配網見下述。
第二步:上報訂閱與下發,在此包內創建main函數
/home/open/Downloads/code-v3.0-LTS/OpenHarmony/applications/sample/wifi-iot/app/mqtt_demo
void mqtt_callback(MessageData *msg_data) { size_t res_len = 0; uint8_t *response_buf = NULL; char topicname[45] = { "$crsp/" }; LOS_ASSERT(msg_data); printf("topic %.*s receive a message\r\n", msg_data->topicName->lenstring.len, msg_data->topicName->lenstring.data); printf("message is %.*s\r\n", msg_data->message->payloadlen, msg_data->message->payload); } int mqtt_connect(void) { int rc = 0; NetworkInit(&n); NetworkConnect(&n, "a161fa3144.iot-mqtts.cn-north-4.myhuaweicloud.com", 1883); buf_size = 4096+1024; onenet_mqtt_buf = (unsigned char *) malloc(buf_size); onenet_mqtt_readbuf = (unsigned char *) malloc(buf_size); if (!(onenet_mqtt_buf && onenet_mqtt_readbuf)) { printf("No memory for MQTT client buffer!"); return -2; } MQTTClientInit(&mq_client, &n, 1000, onenet_mqtt_buf, buf_size, onenet_mqtt_readbuf, buf_size); MQTTStartTask(&mq_client); data.keepAliveInterval = 30; data.cleansession = 1; data.clientID.cstring = "61f6e729de9933029be57672_88888888_0_0_2022020905"; data.username.cstring = "61f6e729de9933029be57672_88888888"; data.password.cstring = "43872acc0b1e6aa7bf9e6a69f12aa9b1ebc07daffb67e18cf905c847a594f813"; data.cleansession = 1; mq_client.defaultMessageHandler = mqtt_callback; //連接伺服器 rc = MQTTConnect(&mq_client, &data); //訂閱消息,設置回調函數 MQTTSubscribe(&mq_client, "porsche", 0, mqtt_callback); while(1) { MQTTMessage message; message.qos = QOS1; message.retained = 0; message.payload = (void *)"openharmony"; message.payloadlen = strlen("openharmony"); //上報 if (MQTTPublish(&mq_client, "hi3861", &message) < 0) { printf("MQTTPublish faild !\r\n"); } IoTGpioSetOutputVal(9, 0); //9gpio 0 light on usleep(1000000); } return 0; }
第三步:儲物精靈保險模式&舵機開門
舵機開鎖:
int servoID =0; void My_servo(uint8_t servoID,int angle) { int j=0; int k=2000/200; angle = k*angle; for (j=0;j<5;j++){ IoTGpioSetOutputVal(servoID, 1); hi_udelay(angle); IoTGpioSetOutputVal(servoID, 1); hi_udelay(20000-angle); } }
保險模式:
static int DealSetPassword(cJSON *objCmd) { int ret = -1; char *pstr = NULL; cJSON *objParas = NULL; cJSON *objPara = NULL; CommandParamSetPsk setLockPskParam; memset(&setLockPskParam, 0x00, sizeof(CommandParamSetPsk)); if ((objParas = cJSON_GetObjectItem(objCmd, "paras")) == NULL) { RaiseLog(LOG_LEVEL_ERR, "Paras not exist"); return ret; } if ((objPara = cJSON_GetObjectItem(objParas, "PskId")) != NULL) { char *id = cJSON_GetStringValue(objPara); //密碼標識(string型) if (id == NULL || strlen(id) > LOCK_ID_LENGTH) { RaiseLog(LOG_LEVEL_ERR, "check lock id failed!"); return -1; } strncpy(setLockPskParam.id, id, strlen(id)); } else { return ret; } if ((objPara = cJSON_GetObjectItem(objParas, "Option")) != NULL) { char *option = cJSON_GetStringValue(objPara); printf("option = %c \n", *option); //三個命令(string型) if (*option == 'A') { setLockPskParam.option = OPTION_ADD; //新增密碼 } else if (*option == 'U') { setLockPskParam.option = OPTION_UPDATE; //更新密碼 } else if (*option == 'D') { setLockPskParam.option = OPTION_DELETE; //刪除密碼 } else { RaiseLog(LOG_LEVEL_ERR, "no such option(%c)!", *option); return -1; } } else { return ret; } if ((objPara = cJSON_GetObjectItem(objParas, "LockPsk")) != NULL) { char *psk = cJSON_GetStringValue(objPara); if (psk == NULL || strlen(psk) > LOCK_PSK_LENGTH) { RaiseLog(LOG_LEVEL_ERR, "check psk failed!"); return -1; } strncpy(setLockPskParam.password, psk, strlen(psk)); } else { return ret; } ret = IotProfile_CommandCallback(CLOUD_COMMAND_SETPSK, &setLockPskParam); return ret; }
第四步:標註GPIO口
識別GPIO口與接入(這裡要註意一個接的是正極一個是接地還有一個為信號傳輸口)
void mqtt_test(void) { IoTGpioInit(9); IoTGpioSetDir(9, IOT_GPIO_DIR_OUT); mqtt_connect(); }
第五步:吊起mqtt協議(build.gn版)
與主函數平行的Build.gn,吊起函數與第三方庫的內容:
sources = [ "mqtt_test.c", "mqtt_entry.c" ] include_dirs = [ "//utils/native/lite/include", "//kernel/liteos_m/components/cmsis/2.0", "//base/iot_hardware/interfaces/kits/wifiiot_lite", "//vendor/hisi/hi3861/hi3861/third_party/lwip_sack/include", "//foundation/communication/interfaces/kits/wifi_lite/wifiservice", "//third_party/pahomqtt/MQTTPacket/src", "//third_party/pahomqtt/MQTTClient-C/src", "//third_party/pahomqtt/MQTTClient-C/src/liteOS", "//kernel/liteos_m/kal/cmsis", "//base/iot_hardware/peripheral/interfaces/kits", ] deps = [ "//third_party/pahomqtt:pahomqtt_static", //吊起MQTT協議 ] }
Build.gn:與APP併列的build.gn用於指出要編譯的主函數,可以使用startup後面跟要編譯的主包也可以直接features進行選中,在這裡可以把不需要參與編譯的項目通過#給註釋掉。
在start_up里的builld.gn:
import("//build/lite/config/component/lite_component.gni") lite_component("app") { features = [ "mqtt_demo:mqtt_test", //標註主函數,指定位置編譯 ]
儲物精靈Pro版(識別功能版):(使用第三方平臺:Vuforia)
我們的原理就是上傳畫面到雲端,然後逐幀分解比對(此功能目前還在完善)
(4)軟體側(偏向軟體,但是還是嵌入式開發)
步驟一:接收伺服器的存儲代碼
exports.i***ta2=function(req,res){ console.log("iot_data:",req) const url = new URL("Get the URL provided by HUAWEI CLOUD"+req.url) //The address configured inside the forwarding destination let properties = JSON.stringify(req.body.notify_data.body.services) console.log("Store data:",properties) let addArr = [properties] var addSql = 'INSERT INTO sesnor_Record(properties) VALUES (?)' var callBack = function(err,data){ console.log("error:"+err) console.log("Property insertion result:"+JSON.stringify(data)) res.send(data) } sqlUtil.sqlContent(addSql,addArr,callBack) }
步驟二:射頻貼紙&復旦卡拉取本地方案
寫入復旦卡請用第三方的軟體,NFC射頻貼紙使用應用調試助手(華為應用市場可下載)。
void RC522_Config ( void ) { uint8_t ucStatusReturn; //Returns the status uint8_t flag_station = 1; //Leave the flag bit of the function while ( flag_station ) { /* Seek cards (method: all in the range), the first search fails again, and when the search is successful, the card sequence is passed into the array ucArray_ID*/ if ( ( ucStatusReturn = PcdRequest ( PICC_REQALL, ucArray_ID ) ) != MI_OK ) ucStatusReturn = PcdRequest ( PICC_REQALL, ucArray_ID ); if ( ucStatusReturn == MI_OK ) { /* An anti-collision operation in which the selected sequence of cards is passed into an array ucArray_ID */ if ( PcdAnticoll ( ucArray_ID ) == MI_OK ) { if ( PcdSelect ( ucArray_ID ) == MI_OK ) { printf ("\nRC522 is Ready!\n"); flag_station = 0; } } } } }
步驟三:智能窗帘軌解決方案
因為馬達可以無限前後方向的對窗帘軌進行拉動所以選擇馬達來進行拉動。另外要註意馬達的功率來判斷是否加入繼電器,詳情請移步源碼倉(購買馬達時要向供應商索要相關數據,同時向開發板供應商索要已公開的腳位置圖)
static void RtcTimeUpdate(void) { extern int SntpGetRtcTime(int localTimeZone, struct tm *rtcTime); struct tm rtcTime; SntpGetRtcTime(CONFIG_LOCAL_TIMEZONE,&rtcTime); RaiseLog(LOG_LEVEL_INFO, "Year:%d Month:%d Mday:%d Wday:%d Hour:%d Min:%d Sec:%d", \ rtcTime.tm_year + BASE_YEAR_OF_TIME_CALC, rtcTime.tm_mon + 1, rtcTime.tm_mday,\ rtcTime.tm_wday, rtcTime.tm_hour, rtcTime.tm_min, rtcTime.tm_sec); if (rtcTime.tm_wday > 0) { g_appController.curDay = rtcTime.tm_wday - 1; } else { g_appController.curDay = EN_SUNDAY; } g_appController.curSecondsInDay = rtcTime.tm_hour * CN_SECONDS_IN_HOUR + \ rtcTime.tm_min * CN_SECONDS_IN_MINUTE + rtcTime.tm_sec + 8; // add 8 ms offset } static uint32_t Time2Tick(uint32_t ms) { uint64_t ret; ret = ((uint64_t)ms * osKernelGetTickFreq()) / CN_MINISECONDS_IN_SECOND; return (uint32_t)ret; } #define CN_REACTION_TIME_SECONDS 1 static void BoardLedButtonCallbackF1(char *arg) { static uint32_t lastSec = 0; uint32_t curSec = 0; RaiseLog(LOG_LEVEL_INFO, "BUTTON PRESSED"); curSec = g_appController.curSecondsInDay; if((curSec) < (lastSec + CN_REACTION_TIME_SECONDS)) { RaiseLog(LOG_LEVEL_WARN, "Frequecy Pressed Button"); return; } lastSec = curSec; g_appController.curtainStatus = CN_BOARD_SWITCH_ON; g_appController.pwmLedDutyCycle = \ g_appController.pwmLedDutyCycle > 0 ? g_appController.pwmLedDutyCycle:CONFIG_LED_DUTYCYCLEDEFAULT; osEventFlagsSet(g_appController.curtainEvent, CN_LAMP_EVENT_SETSTATUS); return; } static void BoardLedButtonCallbackF2(char *arg) { uint32_t lastSec = 0; uint32_t curSec = 0; RaiseLog(LOG_LEVEL_INFO, "BUTTON PRESSED"); curSec = g_appController.curSecondsInDay; if ((curSec) < (lastSec + CN_REACTION_TIME_SECONDS)) { RaiseLog(LOG_LEVEL_WARN, "Frequecy Pressed Button"); return; } lastSec = curSec; g_appController.curtainStatus = CN_BOARD_SWITCH_OFF; osEventFlagsSet(g_appController.curtainEvent, CN_LAMP_EVENT_SETSTATUS); return; } #define CURTAIN_MOTOR_GPIO_IDX 8 #define WIFI_IOT_IO_FUNC_GPIO_8_GPIO 0 #define WIFI_IOT_IO_FUNC_GPIO_14_GPIO 4 #define MOTOR_WORK_SECOND 6 static void E53SC1_MotorInit(void) { IoTGpioInit(CURTAIN_MOTOR_GPIO_IDX); IoTGpioSetFunc(CURTAIN_MOTOR_GPIO_IDX, WIFI_IOT_IO_FUNC_GPIO_8_GPIO); IoTGpioSetDir(CURTAIN_MOTOR_GPIO_IDX, IOT_GPIO_DIR_OUT); //設置GPIO_8為輸出模式 return; } static void E53SC1_SetCurtainStatus(int curtainStatus) { if ((curtainStatus == CN_BOARD_CURTAIN_OPEN) || (curtainStatus == CN_BOARD_CURTAIN_CLOSE)) { IoTGpioSetOutputVal(CURTAIN_MOTOR_GPIO_IDX, 1); //設置GPIO_8輸出高電平打開電機 sleep(MOTOR_WORK_SECOND); IoTGpioSetOutputVal(CURTAIN_MOTOR_GPIO_IDX, 0); //設置GPIO_8輸出低電平關閉電機 } return; } static void DataCollectAndReport(const void *arg) { (void)arg; uint32_t curtainEvent; uint32_t waitTicks; waitTicks = Time2Tick(CONFIG_SENSOR_SAMPLE_CYCLE); while (1) { curtainEvent = osEventFlagsWait(g_appController.curtainEvent, CN_CURTAIN_EVENT_SETSTATUS, \ osFlagsWaitAny, waitTicks); if (curtainEvent & CN_CURTAIN_EVENT_SETSTATUS) { RaiseLog(LOG_LEVEL_INFO, "GetEvent:%08x", curtainEvent); E53SC1_SetCurtainStatus(g_appController.curtainStatus); } (void) IotProfile_Report(g_appController.curtainStatus); } return; } static int UpdateShedule(CommandParamSetShedule *shedule) { if (shedule->num == 1 && shedule->day[0] == 0) { // set the one time schedule to current weekday shedule->day[0] = (g_appController.curDay + 1); } switch (shedule->option) { case 'A': IOT_ScheduleAdd(shedule->scheduleID, shedule->day, shedule->num, shedule->startHour * CN_SECONDS_IN_HOUR +\ shedule->startMinute * CN_SECONDS_IN_MINUTE, shedule->duration, shedule->shedulecmd.cmd, 0); break; case 'U': IOT_ScheduleUpdate(shedule->scheduleID, shedule->day, shedule->num, shedule->startHour * CN_SECONDS_IN_HOUR +\ shedule->startMinute * CN_SECONDS_IN_MINUTE, shedule->duration, shedule->shedulecmd.cmd, 0); break; case 'D': IOT_ScheduleDelete(shedule->scheduleID, shedule->day, shedule->num, shedule->startHour * CN_SECONDS_IN_HOUR +\ shedule->startMinute * CN_SECONDS_IN_MINUTE, shedule->duration, shedule->shedulecmd.cmd, 0); break; default: RaiseLog(LOG_LEVEL_ERR, "the schedule has no such option!\n"); break; } return 0; } void CurrentTimeCalcTimerHander(){ g_appController.curSecondsInDay ++; } #define TimeCalcTicks_NUMBER 100 #define CN_MINISECONDS_IN_1000MS 1000 static void CurtainShedule(void) { int startSecondInDay = 0; int endSecondInDay = 0; int settingCmd = 0; int executeTaskTime = 0; // indicate the do something busy osTimerId_t CurrentTimeCalc_id; CurrentTimeCalc_id = osTimerNew(CurrentTimeCalcTimerHander, osTimerPeriodic, NULL, NULL); osTimerStart(CurrentTimeCalc_id, TimeCalcTicks_NUMBER); while (1) { osDelay(Time2Tick(CN_MINISECONDS_IN_1000MS)); if (g_appController.curSecondsInDay >= CN_SECONS_IN_DAY) { g_appController.curSecondsInDay = 0; g_appController.curDay++; if (g_appController.curDay >= EN_DAYALL) { g_appController.curDay = EN_MONDAY; } IOT_ScheduleSetUpdate(1); } // check if we need do some task here if (IOT_ScheduleIsUpdate(g_appController.curDay, g_appController.curSecondsInDay)) { if (executeTaskTime > 0) { executeTaskTime = 0; if (g_appController.curtainStatus == CN_BOARD_CURTAIN_OPEN) { g_appController.curtainStatus = CN_BOARD_CURTAIN_CLOSE; osEventFlagsSet(g_appController.curtainEvent, CN_CURTAIN_EVENT_SETSTATUS); } } startSecondInDay = IOT_ScheduleGetStartTime(); endSecondInDay = startSecondInDay + IOT_ScheduleGetDurationTime(); IOT_ScheduleGetCommand(&settingCmd, NULL); } RaiseLog(LOG_LEVEL_INFO, "start:%d end:%d cur:%d",startSecondInDay, endSecondInDay, g_appController.curSecondsInDay); if ((endSecondInDay == startSecondInDay) && (g_appController.curSecondsInDay == endSecondInDay)) { if (g_appController.curtainStatus != settingCmd) { RaiseLog(LOG_LEVEL_INFO, "Triggering"); g_appController.curtainStatus = settingCmd; osEventFlagsSet(g_appController.curtainEvent, CN_CURTAIN_EVENT_SETSTATUS); } IOT_ScheduleSetUpdate(1); } } return; } int IotProfile_CommandCallback(int command, void *buf) { CommandParamSetShedule setSheduleParam; CommandParamSetCurtain setCurtainParam; //CommandParamSetDutyCycle setDutyCycleParam; CLOUD_CommandType cmd = (CLOUD_CommandType)command; if (cmd == CLOUD_COMMAND_SETCURTAIN_STATUS) { setCurtainParam = *(CommandParamSetCurtain *)buf; g_appController.curtainStatus = setCurtainParam.status; RaiseLog(LOG_LEVEL_INFO, "setCurtainParam.status:%d\r\n", setCurtainParam.status); osEventFlagsSet(g_appController.curtainEvent, CN_LAMP_EVENT_SETSTATUS); return 0; } else if (cmd == CLOUD_COMMAND_SETSHEDULE) { setSheduleParam = *(CommandParamSetShedule *)buf; RaiseLog(LOG_LEVEL_INFO, "setshedule:day:%d hour:%d minute:%d duration:%d \r\n", \ setSheduleParam.day,setSheduleParam.startHour,setSheduleParam.startMinute, setSheduleParam.duration); return UpdateShedule(&setSheduleParam); } return -1; } static int IotWifiInfo_get(char *ssid, int id_size, char *pwd, int pd_size) { int retval = UtilsGetValue(SID_KEY, ssid, id_size); if (retval <= 0) { RaiseLog(LOG_LEVEL_ERR, "no such ssid stored! \n"); return 0; } if ( UtilsGetValue(PWD_KEY, pwd, pd_size) < 0) { RaiseLog(LOG_LEVEL_INFO, "ssid(%s) no password stored! \n", ssid); } else { RaiseLog(LOG_LEVEL_INFO, "ssid : %s, pwd : %s! \n", ssid, pwd); } return 1; } static void IotWifiInfo_set(char *ssid, char *pwd) { if (UtilsSetValue(SID_KEY, ssid) != 0) { RaiseLog(LOG_LEVEL_ERR, "store ssid failed! \n"); return; } if (UtilsSetValue(PWD_KEY, pwd) != 0) { RaiseLog(LOG_LEVEL_ERR, "store password failed! \n"); UtilsDeleteValue(SID_KEY); return; } RaiseLog(LOG_LEVEL_INFO, "store password success! \n"); } static void IotMainTaskEntry(const void *arg) { osThreadAttr_t attr; NfcInfo nfcInfo; (void)arg; char ssid[BUFF_SIZE] = {0}; char pwd[BUFF_SIZE] = {0}; int ret = 0; g_appController.pwmLedDutyCycle = CONFIG_LED_DUTYCYCLEDEFAULT; BOARD_InitPwmLed(); BOARD_InitWifi(); E53SC1_MotorInit(); IOT_ScheduleInit(); ret = Board_IsButtonPressedF2(); osDelay(MAIN_TASK_DELAY_TICKS); LedFlashFrequencySet(CONFIG_FLASHLED_FRENETCONFIG); nfcInfo.deviceID = "6136ceba0ad1ed02866fa3b2_Curtain01"; nfcInfo.devicePWD = "12345678"; if (ret) { RaiseLog(LOG_LEVEL_INFO, "Netconfig Button has pressed! \n"); if (BOARD_NAN_NetCfgStartConfig(SOFTAP_NAME, ssid, sizeof(ssid), pwd,