一、簡介
本 demo 基于 OpenHarmony3.1Beta 版本開發(fā),該樣例能夠接入數(shù)字管家應(yīng)用,通過數(shù)字管家應(yīng)用監(jiān)測體重秤上報數(shù)據(jù),獲得當(dāng)前測量到的體重,身高,并在應(yīng)用端形成一段時間內(nèi)記錄的體重值,以折線圖的形式表現(xiàn)出來,根據(jù)計算的 BMI 值來提醒當(dāng)前身體健康狀態(tài),推送健康小知識。
1. 交互流程

如上圖所示,智能體重稱整體方案原理圖可以大致分成:智能體重稱設(shè)備、數(shù)字管家應(yīng)用、云平臺三部分。智能體重稱通過 MQTT 協(xié)議連接華為 IOT 物聯(lián)網(wǎng)平臺,從而實現(xiàn)命令的接收和屬性上報。 關(guān)于智能設(shè)備接入華為云 IoT 平臺的詳細(xì)細(xì)節(jié)可以參考 連接 IOT 云平臺指南;智能設(shè)備同數(shù)字管家應(yīng)用之間的設(shè)備模型定義可以參考 profile .
2. 實物簡介

如上圖示,左邊為全志 xr806 模組,右邊為超聲波測距模塊,echo 腳連接 PA19,Triq 腳連接 PA20,Vcc 腳連接 5V 電源,Gnd 腳接地,

如上圖示,右邊為稱重模塊,clk 腳接 PB15,dt 腳接 PB14,vcc 腳接 5V,gnd 腳接地,稱重傳感器紅色線接 E+,黑色線接 E-,白色線接 A-,綠色線接 A+
左邊 xr806 模塊左下角 k1 按鍵,長按 k1 按鍵不放,同時上電,4-5 秒后松開按鍵,可以清除已保存得配網(wǎng)信息
xr806 模塊,在設(shè)備正常工作后,按 k1 按鍵,可以初始化當(dāng)前得重量為 0,高度為 0
二、 快速上手
1. 硬件準(zhǔn)備
- 全志 xr806 模組
- hcsr04 超聲波模塊
- hx711 稱重模塊帶支架托盤
- 預(yù)裝 HarmonyOS 手機一臺
2、環(huán)境準(zhǔn)備
參照文檔:XR806 快速上手指導(dǎo)文檔
3、編譯前準(zhǔn)備
設(shè)備側(cè)代碼下載
具體倉庫地址:https://gitee.com/openharmony-sig/knowledge\_demo\_smart\_home/
下載方式:使用 git 命令下載,指令如下(用戶也可以根據(jù)需要將該倉庫 fork 到自己的目錄下后進(jìn)行下載)
cd ~
git clone git@gitee.com:openharmony-sig/knowledge_demo_smart_home.git
代碼拷貝
cp -rfa ~/knowledge_demo_smart_home/dev/team_x ~/openharmony/vendor/
cp -rfa ~/knowledge_demo_smart_home/dev/third_party/iot_link ~/openharmony/third_party/
SOC 代碼下載替換
當(dāng)前官方 soc 代碼由于 DHCP 暫未適配,所以暫時不支持 AP 模式,這時需要下載并替換之前 SOC 代碼。如果官方 soc 代碼已修復(fù)該問題,可忽略此步驟。
git clone https://gitee.com/moldy-potato-chips/xr806_-ap_mode.git
mv ~/openharmony/device/soc/allwinner ~/allwinner.org // 不建議直接刪除,
cp -raf xr806_-ap_mode ~/openharmony/device/soc/allwinner
整合并修改完成后的目錄結(jié)構(gòu)如下圖

修改文件
- 修改編譯依賴
打開 device/soc/allwinner/xradio/xr806/BUILD.gn,添加應(yīng)用依賴 (deps 字段):
module_group(module_name) {
modules = [
"src",
"project",
"include",
]
configs = [
":SdkLdCconfig",
]
deps = [ "http://vendor/team_x/smart_weight_scale/demo_smart_weight_scale:smart_weight_scale" ]
}
- 修改編譯方式
將 demo 依賴的庫編譯方式 (static_library) 修改為 (source_set):
具體依賴查看 demo_smart_weight_scale 目錄下的 BUILD.gn:
deps = [
"../../common/iot_wifi_xradio:iot_wifi",
"../../common/iot_cloud:iot_cloud",
"http://third_party/cJSON:cjson",
"../../common/iot_boardbutton_xradio:iot_boardbutton",
"../../common/iot_boardled_xradio:iot_boardled_xradio",
]
其中 //third_party/cJSON 目錄下的 BUILD.gn 建議參照下面的修改:
source_set("cJSON") {
sources = [
"cJSON.c",
"cJSON_Utils.c",
]
ldflags = [ "-lm" ]
}
third_party/iot_link 目錄下的各級使用到的 BUILD.gn 也需要將編譯方式修改為 source_set,或者將所有需要編譯的文件放在 iot_link 目錄的 BUILD.gn 中,如下:
source_set("iot_link") {
sources = [
"link_log/link_log.c",
"link_misc/link_random.c",
"link_misc/link_ring_buffer.c",
"link_misc/link_string.c",
"network/dtls/dtls_al/dtls_al.c",
"network/dtls/mbedtls/mbedtls_port/dtls_interface.c",
"network/dtls/mbedtls/mbedtls_port/mbed_port.c",
"network/dtls/mbedtls/mbedtls_port/timing_alt.c",
"network/mqtt/mqtt_al/mqtt_al.c",
"network/mqtt/paho_mqtt/port/paho_mqtt_port.c",
"network/mqtt/paho_mqtt/port/paho_osdepends.c",
"network/mqtt/paho_mqtt/paho/MQTTClient-C/src/MQTTClient.c",
"network/mqtt/paho_mqtt/paho/MQTTPacket/src/MQTTConnectClient.c",
"network/mqtt/paho_mqtt/paho/MQTTPacket/src/MQTTConnectServer.c",
"network/mqtt/paho_mqtt/paho/MQTTPacket/src/MQTTDeserializePublish.c",
"network/mqtt/paho_mqtt/paho/MQTTPacket/src/MQTTFormat.c",
"network/mqtt/paho_mqtt/paho/MQTTPacket/src/MQTTPacket.c",
"network/mqtt/paho_mqtt/paho/MQTTPacket/src/MQTTSerializePublish.c",
"network/mqtt/paho_mqtt/paho/MQTTPacket/src/MQTTSubscribeClient.c",
"network/mqtt/paho_mqtt/paho/MQTTPacket/src/MQTTSubscribeServer.c",
"network/mqtt/paho_mqtt/paho/MQTTPacket/src/MQTTUnsubscribeClient.c",
"network/mqtt/paho_mqtt/paho/MQTTPacket/src/MQTTUnsubscribeServer.c",
"oc_mqtt/oc_mqtt_al/oc_mqtt_al.c",
"oc_mqtt/oc_mqtt_profile_v5/oc_mqtt_profile.c",
"oc_mqtt/oc_mqtt_profile_v5/oc_mqtt_profile_package.c",
"oc_mqtt/oc_mqtt_profile_v5/oc_mqtt_event.c",
"oc_mqtt/oc_mqtt_tiny_v5/oc_mqtt_tiny.c",
"oc_mqtt/oc_mqtt_tiny_v5/hmac.c",
"queue/queue.c",
]
cflags = [ "-Wno-unused-variable" ]
cflags += [ "-Wno-unused-but-set-variable" ]
cflags += [ "-Wno-sign-compare" ]
cflags += [ "-Wno-unused-parameter" ]
cflags += [ "-Wno-unused-function" ]
ldflags = [ "-Wl,-rpath-link=//device/xradio/xr806/xr_skylark/lib" ]
ldflags += [ "-lmbedtls" ]
include_dirs = [
"inc",
"link_log",
"link_misc",
"queue",
"oc_mqtt/oc_mqtt_tiny_v5",
"oc_mqtt/oc_mqtt_profile_v5",
"oc_mqtt/oc_mqtt_al",
"network/dtls/mbedtls/mbedtls_port",
"network/mqtt/paho_mqtt/port",
"network/mqtt/paho_mqtt/paho/MQTTClient-C/src",
"network/mqtt/paho_mqtt/paho/MQTTPacket/src",
"http://third_party/mbedtls/include/",
"http://third_party/mbedtls/include/",
"http://third_party/cJSON",
"http://kernel/liteos_m/components/cmsis/2.0",
"http://device/xradio/xr806/xr_skylark/include/net/mbedtls-2.2.0/",
]
defines = [
"MQTTCLIENT_PLATFORM_HEADER=paho_osdepends.h",
"WITH_DTLS",
"MBEDTLS_AES_ROM_TABLES",
"MBEDTLS_CONFIG_FILE=\"los_mbedtls_config_dtls.h\"",
"CONFIG_DTLS_MBEDTLS_CERT",
"CONFIG_DTLS_MBEDTLS_PSK",
"CFG_MBEDTLS_MODE=PSK_CERT",
"CONFIG_OC_MQTT_TINY_ENABLE=1"
]
}
- 修改 iot_link 中的部分文件
1.third_party/iot_link/network/mqtt/paho_mqtt/port/paho_mqtt_port.c
測試發(fā)現(xiàn),當(dāng) fd 為 0 的時候,在執(zhí)行 recv 時會立馬返回 - 1,因此做下面規(guī)避操作。
static int __socket_connect(Network *n, const char *host, int port)
{
...
int tmpfd = socket(AF_INET,SOCK_STREAM,0); // to skip fd = 0;
fd = socket(AF_INET,SOCK_STREAM,0);
if(fd == -1) {
return ret;
}
close(tmpfd); // to skip fd = 0;
...
}
系統(tǒng) setsockopt 函數(shù)未適配,因此需要做下面的修改:
static int __socket_read(void *ctx, unsigned char *buf, int len, int timeout)
{
int fd;
int ret = 0;
#if 0
struct timeval timedelay = {timeout / 1000, (timeout % 1000) * 1000};
if(NULL== uf)
{
return ret;
}
fd = (int)(intptr_t)ctx; ///< socket could be zero
if (timedelay.tv_sec < 0 || (timedelay.tv_sec == 0 && timedelay.tv_usec <= 0))
{
timedelay.tv_sec = 0;
timedelay.tv_usec = 100;
}
if(0 != setsockopt(fd,SOL_SOCKET,SO_RCVTIMEO,&timedelay,sizeof(struct timeval)))
{
return ret; //could not support the rcv timeout
}
int bytes = 0;
while (bytes < len) {
int rc = recv(fd, &buf[bytes], (size_t)(len - bytes), 0);
printf("[%s|%s|%d]fd = %d, rc = %d\n", __FILE__,__func__,__LINE__, fd, rc);
if (rc == -1) {
if (errno != EAGAIN && errno != EWOULDBLOCK) {
bytes = -1;
}
break;
} else if (rc == 0) {
bytes = 0;
break;
} else {
bytes += rc;
}
}
return bytes;
#else
int bytes = 0;
fd_set fdset;
struct timeval timedelay = {timeout / 1000, (timeout % 1000) * 1000};
if(NULL== buf)
{
return ret;
}
fd = (int)(intptr_t)ctx; ///< socket could be zero
if (timedelay.tv_sec < 0 || (timedelay.tv_sec == 0 && timedelay.tv_usec <= 0))
{
timedelay.tv_sec = 0;
timedelay.tv_usec = 100;
}
timedelay.tv_sec = 2;
FD_ZERO(&fdset);
FD_SET(fd, &fdset);
ret = select(fd + 1, &fdset, NULL, NULL, &timedelay);
if (ret > 0) {
while (bytes < len) {
int rc = recv(fd, &buf[bytes], (size_t)(len - bytes), 0);
// printf("[%s|%s|%d]fd = %d, rc = %d, errno=%d(%s)\n", __FILE__,__func__,__LINE__, fd, rc,errno, strerror(errno));
if (rc == -1) {
if (errno != EAGAIN && errno != EWOULDBLOCK) {
bytes = -1;
}
break;
} else if (rc == 0) {
bytes = 0;
break;
} else {
bytes += rc;
}
}
}
return bytes;
#endif
}
2.third_party/iot_link/network/dtls/mbedtls/mbedtls_port/dtls_interface.c
在文件頂部添加打印函數(shù)定義以及添加 mbedtls_calloc 以及 mbedtls_free 的定義,否則編譯會提示錯誤:
#define MBEDTLS_LOG LINK_LOG_DEBUG
#ifndef mbedtls_calloc
#define mbedtls_calloc calloc
#endif
#ifndef mbedtls_free
#define mbedtls_free free
#endif
系統(tǒng)部分 mbedtls 接口不一致,固需要注釋部分接口代碼:
mbedtls_ssl_context dtls_ssl_new(dtls_establish_info_s *info, char plat_type)
{
...
if (info->psk_or_cert == VERIFY_WITH_PSK)
{
/*
if ((ret = mbedtls_ssl_conf_psk(conf,
info->v.p.psk,
info->v.p.psk_len,
info->v.p.psk_identity,
strlen((const char *)info->v.p.psk_identity))) != 0)
{
MBEDTLS_LOG("mbedtls_ssl_conf_psk failed: -0x%x", -ret);
goto exit_fail;
}
*/
}
...
}
int dtls_shakehand(mbedtls_ssl_context *ssl, const dtls_shakehand_info_s *info)
{
...
if (MBEDTLS_SSL_IS_CLIENT == info->client_or_server)
{
ret = mbedtls_net_connect(server_fd, info->u.c.host, info->u.c.port, info->udp_or_tcp);
if( 0 != ret)
{
ret = MBEDTLS_ERR_NET_CONNECT_FAILED;
goto exit_fail;
}
}
else
{
//server_fd = (mbedtls_net_context*)atiny_net_bind(NULL, info->u.s.local_port, MBEDTLS_NET_PROTO_UDP);
///< --TODO ,not implement yet
}
...
}
void dtls_init(void)
{
(void)mbedtls_platform_set_calloc_free(calloc, free);
(void)mbedtls_platform_set_snprintf(snprintf);
// (void)mbedtls_platform_set_printf(printf);
}
在 iot_link/network/dtls/mbedtls/mbedtls_port/mbed_port.c 文件中的 dtls_imp_init () 函數(shù)中,也需要注釋掉未實現(xiàn)的接口,否則編譯報錯:
int dtls_imp_init(void)
{
int ret =-1;
// (void)mbedtls_platform_set_calloc_free(calloc, free);
// (void)mbedtls_platform_set_snprintf(snprintf);
// (void)mbedtls_platform_set_printf(printf);
ret = dtls_al_install(&s_mbedtls_io);
return ret;
}
3. 在文件 iot_link/network/mqtt/paho_mqtt/port/paho_osdepends.c 中添加對應(yīng) timersub 和 timeradd 的實現(xiàn) (系統(tǒng)中未實現(xiàn)該函數(shù)):
// add this for "timersub" && "timeradd"
#ifndef timersub
#define timersub(s,t,a) (void) ( (a)->tv_sec = (s)->tv_sec - (t)->tv_sec, \
((a)->tv_usec = (s)->tv_usec - (t)->tv_usec) < 0 && \
((a)->tv_usec += 1000000, (a)->tv_sec--) )
#endif
#ifndef timeradd
#define timeradd(s,t,a) (void) ( (a)->tv_sec = (s)->tv_sec + (t)->tv_sec, \
((a)->tv_usec = (s)->tv_usec + (t)->tv_usec) >= 1000000 && \
((a)->tv_usec -= 1000000, (a)->tv_sec++) )
#endif
4. 編譯中會有部分頭文件提示找不到,這個時候直接將其注釋即可
(iot_link/network/mqtt/paho_mqtt/port/paho_osdepends.h):
#define INVALID_SOCKET SOCKET_ERROR
// #include <sys/socket.h>
#include <sys/param.h>
#include <sys/time.h>
// #include <netinet/in.h>
// #include <netinet/tcp.h>
// #include <arpa/inet.h>
// #include <netdb.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#endif
#if defined(WIN32)
#include <Iphlpapi.h>
#else
// #include <sys/ioctl.h>
// #include <net/if.h>
#endif
5. 因為弱引用導(dǎo)致無法鏈接相關(guān)符號,因此需要注釋以下幾個文件中的弱引用。
文件一 third_party/iot_link/network/dtls/dtls_al/dtls_al.c
#if 0
__attribute__((weak)) int dtls_imp_init(void)
{
LINK_LOG_DEBUG("%s:###please implement dtls by yourself####",__FUNCTION__);
return -1;
}
#endif
extern int dtls_imp_init(void);
文件二 third_party/iot_link/network/mqtt/mqtt_al/mqtt_al.c
#if 0
__attribute__((weak)) int mqtt_imp_init(void)
{
LINK_LOG_DEBUG("%s:###please implement mqtt by yourself####",__FUNCTION__);
return -1;
}
#endif
extern int mqtt_imp_init(void);
文件三 third_party/iot_link/oc_mqtt/oc_mqtt_al/oc_mqtt_al.c
#if 0
__attribute__ ((weak)) int oc_mqtt_imp_init(void)
{
LINK_LOG_DEBUG("%s:###please implement oc mqtt by yourself####",__FUNCTION__);
return 0;
}
__attribute__ ((weak)) int oc_mqtt_demo_main(void)
{
LINK_LOG_WARN("Please implement the oc mqtt v5 demo yourself");
return -1;
}
#endif
extern int oc_mqtt_demo_main(void);
- 修改 GPIO 查找方式
因為 GPIO 框架修改了設(shè)備驅(qū)動注冊的管腳號,導(dǎo)致應(yīng)用無法根據(jù) HCS 的引腳操作對應(yīng)的 GPIO,此問題已經(jīng)提 issue,如果該問題已解決,可以忽略此步驟。
打開 drivers/framework/support/platform/src/gpio/gpio_manager.c,將 cntlr->start = start;注釋即可。
static int32_t GpioManagerAdd(struct PlatformManager *manager, struct PlatformDevice *device)
{
uint16_t start;
struct GpioCntlr *cntlr = CONTAINER_OF(device, struct GpioCntlr, device);
if ((start = GpioCntlrQueryStart(cntlr, &manager->devices)) >= GPIO_NUM_MAX) {
PLAT_LOGE("GpioCntlrAdd: query range for start:%d fail:%d", cntlr->start, start);
return HDF_ERR_INVALID_PARAM;
}
// cntlr->start = start;
DListInsertTail(&device->node, &manager->devices);
PLAT_LOGI("%s: start:%u count:%u", __func__, cntlr->start, cntlr->count);
return HDF_SUCCESS;
}
- 將對應(yīng)的驅(qū)動文件復(fù)制到 drvier 對應(yīng)目錄:
因為主倉代碼中未將對應(yīng)的驅(qū)動文件合并到 driver/adpater/platform 對應(yīng)的目錄下,固需要手動將文件拷貝到對應(yīng)目錄。若主倉已合入,可忽略此步驟。
// 拷貝gpio驅(qū)動
cp -af device/soc/allwinner/xradio/drivers/gpio/gpio_xradio.* driver/adpater/platform/gpio
// 修改driver/adpater/platform/gpio/BUILD.gn文件,加上gpio_xradio的編譯
hdf_driver(module_name) {
sources = []
if (defined(LOSCFG_SOC_COMPANY_BESTECHNIC)) {
sources += [ "gpio_bes.c" ]
}
if (defined(LOSCFG_SOC_COMPANY_ALLWINNER)) {
sources += [ "gpio_xradio.c" ]
}
include_dirs = [ "." ]
}
為了節(jié)省 ram 資源,可以把無用的資源先關(guān)閉,如關(guān)閉內(nèi)部 codec,將 device/soc/allwinner/xradio/xr806/project/prj_config.h 中的 PRJCONF_INTERNAL_SOUNDCARD_EN 設(shè)置為 0,如下:
/* Xradio internal codec sound card enable/disable */
#define PRJCONF_INTERNAL_SOUNDCARD_EN 0
4、代碼編譯
首先可以查看一下hb的版本,如果hb版本為0.4.4版本就不需要更新。
查看 hb 版本
hb --version
更新 hb, 以下指令需要在 openharmony SDK 根目錄執(zhí)行
pip3 uninstall ohos_build
pip3 install build/li
編譯命令:
hb set // 如果是第一次編譯,Input code path 命令行中鍵入"./" 指定OpenHarmony工程編譯根目錄后 回車,
如下圖所示,使用鍵盤上下鍵選中 wifi_skylark

hb build // 如果需要全量編譯,可以添加-f 選項
生成的固件保存在 out/xradio/smart_weight_scale 目錄下
5、固件燒錄
參照文檔:XR806 快速上手指導(dǎo)文檔
6、設(shè)備配網(wǎng)
在設(shè)備上電前需準(zhǔn)備好安裝了數(shù)字管家應(yīng)用的 HarmonyOS 手機,詳情見數(shù)字管家應(yīng)用開發(fā) , 并在設(shè)置中開啟手機的 NFC 功能;
寫設(shè)備 NFC 標(biāo)簽,詳細(xì)操作見設(shè)備 NFC 標(biāo)簽指導(dǎo)文檔 ;
燒錄完成后,上電。開發(fā)者在觀察開發(fā)板上狀態(tài) LED 燈以8Hz 的頻率閃爍時,將手機上半部靠近開發(fā)板 NFC 標(biāo)簽處 (無 NFC 標(biāo)簽的可用 NFC 貼紙?zhí)娲?;
碰一碰后手機將自動拉起數(shù)字管家應(yīng)用并進(jìn)入配網(wǎng)狀態(tài);
配網(wǎng)過程中需要 連接設(shè)備的 AP 熱點,然后填寫需要配置的 wifi 的密碼;
最后點擊配置,手機會將 ssid 以及對應(yīng)的密碼通過 AP 熱點發(fā)送到設(shè)備。
