一、简介
本 demo 基于 OpenHarmony3.1Beta 版本开发,该样例能够接入数字管家应用,通过数字管家应用监测体重秤上报数据,获得当前测量到的体重,身高,并在应用端形成一段时间内记录的体重值,以折线图的形式表现出来,根据计算的 BMI 值来提醒当前身体健康状态,推送健康小知识。
1. 交互流程
如上图所示,智能体重称整体方案原理图可以大致分成:智能体重称设备、数字管家应用、云平台三部分。智能体重称通过 MQTT 协议连接华为 IOT 物联网平台,从而实现命令的接收和属性上报。 关于智能设备接入华为云 IoT 平台的详细细节可以参考 连接 IOT 云平台指南;智能设备同数字管家应用之间的设备模型定义可以参考 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 秒后松开按键,可以清除已保存得配网信息
xr806 模块,在设备正常工作后,按 k1 按键,可以初始化当前得重量为 0,高度为 0
二、 快速上手
1. 硬件准备
- 全志 xr806 模组
- hcsr04 超声波模块
- hx711 称重模块带支架托盘
- 预装 HarmonyOS 手机一台
2、环境准备
参照文档:XR806 快速上手指导文档
3、编译前准备
设备侧代码下载
具体仓库地址:https://gitee.com/openharmony-sig/knowledge\_demo\_smart\_home/
下载方式:使用 git 命令下载,指令如下(用户也可以根据需要将该仓库 fork 到自己的目录下后进行下载)
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 代码下载替换
当前官方 soc 代码由于 DHCP 暂未适配,所以暂时不支持 AP 模式,这时需要下载并替换之前 SOC 代码。如果官方 soc 代码已修复该问题,可忽略此步骤。
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
整合并修改完成后的目录结构如下图
修改文件
- 修改编译依赖
打开 device/soc/allwinner/xradio/xr806/BUILD.gn,添加应用依赖 (deps 字段):
module_group(module_name) {
modules = [
"src",
"project",
"include",
]
configs = [
":SdkLdCconfig",
]
deps = [ "//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",
"//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",
"//third_party/mbedtls/include/",
"//third_party/mbedtls/include/",
"//third_party/cJSON",
"//kernel/liteos_m/components/cmsis/2.0",
"//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
测试发现,当 fd 为 0 的时候,在执行 recv 时会立马返回 - 1,因此做下面规避操作。
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;
...
}
系统 setsockopt 函数未适配,因此需要做下面的修改:
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
在文件顶部添加打印函数定义以及添加 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
系统部分 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 () 函数中,也需要注释掉未实现的接口,否则编译报错:
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 中添加对应 timersub 和 timeradd 的实现 (系统中未实现该函数):
// 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. 因为弱引用导致无法链接相关符号,因此需要注释以下几个文件中的弱引用。
文件一 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 框架修改了设备驱动注册的管脚号,导致应用无法根据 HCS 的引脚操作对应的 GPIO,此问题已经提 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;
}
- 将对应的驱动文件复制到 drvier 对应目录:
因为主仓代码中未将对应的驱动文件合并到 driver/adpater/platform 对应的目录下,固需要手动将文件拷贝到对应目录。若主仓已合入,可忽略此步骤。
// 拷贝gpio驱动
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 = [ "." ]
}
为了节省 ram 资源,可以把无用的资源先关闭,如关闭内部 codec,将 device/soc/allwinner/xradio/xr806/project/prj_config.h 中的 PRJCONF_INTERNAL_SOUNDCARD_EN 设置为 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 根目录执行
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 快速上手指导文档
6、设备配网
在设备上电前需准备好安装了数字管家应用的 HarmonyOS 手机,详情见数字管家应用开发 , 并在设置中开启手机的 NFC 功能;
写设备 NFC 标签,详细操作见设备 NFC 标签指导文档 ;
烧录完成后,上电。开发者在观察开发板上状态 LED 灯以8Hz 的频率闪烁时,将手机上半部靠近开发板 NFC 标签处 (无 NFC 标签的可用 NFC 贴纸替代);
碰一碰后手机将自动拉起数字管家应用并进入配网状态;
配网过程中需要 连接设备的 AP 热点,然后填写需要配置的 wifi 的密码;
最后点击配置,手机会将 ssid 以及对应的密码通过 AP 热点发送到设备。
对于想要学好鸿蒙的的朋友们来说,这里分享一份学习指南有需要的可以扫码免费领取!!!
- 鸿蒙学习路线图:为您提供一个清晰的鸿蒙学习规划,助您高效掌握关键知识点。
有了路线图,怎么能没有学习资料呢,小编也准备了一份联合鸿蒙官方发布笔记整理收纳的《鸿蒙开发学习笔记》,内容包含ArkTS、ArkUI、Web开发、应用模型、资源分类…等知识点。
【有需要的朋友,可以扫描下方二维码免费领取!!!】
《鸿蒙(HarmonyOS)开发学习指南》
第一章 快速入门
1、开发准备
2、构建第一个ArkTS应用(Stage模型)
3、构建第一个ArkTS应用(FA模型)
4、构建第一个JS应用(FA模型)
5、…
第二章 开发基础知识
1、应用程序包基础知识
2、应用配置文件(Stage模型)
3、应用配置文件概述(FA模型)
4、…
第三章 资源分类与访问
1、 资源分类与访问
2、 创建资源目录和资源文件
3、 资源访问
4、…
第四章 学习ArkTs语言
1、初识ArkTS语言
2、基本语法
3、状态管理
4、其他状态管理
5、渲染控制
6、…
第五章 UI开发
1.方舟开发框架(ArkUI)概述
2.基于ArkTS声明式开发范式
3.兼容JS的类Web开发范式
4…
第六章 Web开发
1.Web组件概述
2.使用Web组件加载页面
3.设置基本属性和事件
4.在应用中使用前端页面JavaScript
5.ArkTS语言基础类库概述
6.并发
7…
11.网络与连接
12.电话服务
13.数据管理
14.文件管理
15.后台任务管理
16.设备管理
17…
第七章 应用模型
1.应用模型概述
2.Stage模型开发指导
3.FA模型开发指导
4…
扫描下方二维码免费领取,《鸿蒙(HarmonyOS)开发学习指南》