OpenHarmony开源社区
直播中

小伍

9年用户 4834经验值
擅长:电源/新能源 嵌入式技术
私信 关注
[经验]

【OpenHarmony样例】基于XR806开发板开发的智能体重称

一、简介
本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、编译前准备设备侧代码下载
下载方式:使用git 命令下载,指令如下(用户也可以根据需要将该仓库fork到自己的目录下后进行下载)
  1. cd ~
  2. git clone git@gitee.com:openharmony-sig/knowledge_demo_smart_home.git
代码拷贝
  1. cp -rfa  ~/knowledge_demo_smart_home/dev/team_x  ~/openharmony/vendor/
  2. cp -rfa  ~/knowledge_demo_smart_home/dev/third_party/iot_link  ~/openharmony/third_party/
SOC代码下载替换
当前官方soc代码由于DHCP暂未适配,所以暂时不支持AP模式,这时需要下载并替换之前SOC代码。如果官方soc代码已修复该问题,可忽略此步骤。
  1. git clone https://gitee.com/moldy-potato-chips/xr806_-ap_mode.git
  2. mv ~/openharmony/device/soc/allwinner ~/allwinner.org                        // 不建议直接删除,
  3. cp -raf xr806_-ap_mode ~/openharmony/device/soc/allwinner
整合并修改完成后的目录结构如下图
修改文件
  • 修改编译依赖
打开 device/soc/allwinner/xradio/xr806/BUILD.gn,添加应用依赖(deps字段):
  1. module_group(module_name) {
  2.   modules = [
  3.       "src",
  4.       "project",
  5.       "include",
  6.   ]
  7.   configs = [
  8.     ":SdkLdCconfig",
  9.   ]
  10.   deps = [ "//vendor/team_x/smart_weight_scale/demo_smart_weight_scale:smart_weight_scale" ]
  11. }
  • 修改编译方式
将demo依赖的库编译方式(static_library)修改为(source_set):
具体依赖查看demo_smart_weight_scale目录下的BUILD.gn:
  1.     deps = [
  2.        "../../common/iot_wifi_xradio:iot_wifi",
  3.        "../../common/iot_cloud:iot_cloud",
  4.        "//third_party/cJSON:cjson",
  5.        "../../common/iot_boardbutton_xradio:iot_boardbutton",
  6.        "../../common/iot_boardled_xradio:iot_boardled_xradio",
  7.     ]
其中//third_party/cJSON目录下的BUILD.gn建议参照下面的修改:
  1. source_set("cJSON") {
  2.   sources = [
  3.     "cJSON.c",
  4.     "cJSON_Utils.c",
  5.   ]
  6.   ldflags = [ "-lm" ]
  7. }
third_party/iot_link目录下的各级使用到的BUILD.gn也需要将编译方式修改为source_set,或者将所有需要编译的文件放在iot_link目录的BUILD.gn中,如下:
  1. source_set("iot_link") {
  2.     sources = [
  3.         "link_log/link_log.c",
  4.         "link_misc/link_random.c",
  5.         "link_misc/link_ring_buffer.c",
  6.         "link_misc/link_string.c",
  7.         "network/dtls/dtls_al/dtls_al.c",
  8.         "network/dtls/mbedtls/mbedtls_port/dtls_interface.c",
  9.         "network/dtls/mbedtls/mbedtls_port/mbed_port.c",
  10.         "network/dtls/mbedtls/mbedtls_port/timing_alt.c",
  11.         "network/mqtt/mqtt_al/mqtt_al.c",
  12.         "network/mqtt/paho_mqtt/port/paho_mqtt_port.c",
  13.         "network/mqtt/paho_mqtt/port/paho_osdepends.c",
  14.         "network/mqtt/paho_mqtt/paho/MQTTClient-C/src/MQTTClient.c",
  15.         "network/mqtt/paho_mqtt/paho/MQTTPacket/src/MQTTConnectClient.c",
  16.         "network/mqtt/paho_mqtt/paho/MQTTPacket/src/MQTTConnectServer.c",
  17.         "network/mqtt/paho_mqtt/paho/MQTTPacket/src/MQTTDeserializePublish.c",
  18.         "network/mqtt/paho_mqtt/paho/MQTTPacket/src/MQTTFormat.c",
  19.         "network/mqtt/paho_mqtt/paho/MQTTPacket/src/MQTTPacket.c",
  20.         "network/mqtt/paho_mqtt/paho/MQTTPacket/src/MQTTSerializePublish.c",
  21.         "network/mqtt/paho_mqtt/paho/MQTTPacket/src/MQTTSubscribeClient.c",
  22.         "network/mqtt/paho_mqtt/paho/MQTTPacket/src/MQTTSubscribeServer.c",
  23.         "network/mqtt/paho_mqtt/paho/MQTTPacket/src/MQTTUnsubscribeClient.c",
  24.         "network/mqtt/paho_mqtt/paho/MQTTPacket/src/MQTTUnsubscribeServer.c",
  25.         "oc_mqtt/oc_mqtt_al/oc_mqtt_al.c",
  26.         "oc_mqtt/oc_mqtt_profile_v5/oc_mqtt_profile.c",
  27.         "oc_mqtt/oc_mqtt_profile_v5/oc_mqtt_profile_package.c",
  28.         "oc_mqtt/oc_mqtt_profile_v5/oc_mqtt_event.c",
  29.         "oc_mqtt/oc_mqtt_tiny_v5/oc_mqtt_tiny.c",
  30.         "oc_mqtt/oc_mqtt_tiny_v5/hmac.c",
  31.         "queue/queue.c",
  32.     ]
  33.    
  34.     cflags = [ "-Wno-unused-variable" ]
  35.     cflags += [ "-Wno-unused-but-set-variable" ]
  36.     cflags += [  "-Wno-sign-compare" ]
  37.     cflags += [  "-Wno-unused-parameter" ]
  38.     cflags += [  "-Wno-unused-function" ]

  39.     ldflags = [ "-Wl,-rpath-link=//device/xradio/xr806/xr_skylark/lib" ]
  40.     ldflags += [ "-lmbedtls" ]

  41.     include_dirs = [
  42.         "inc",
  43.         "link_log",
  44.         "link_misc",
  45.         "queue",
  46.         "oc_mqtt/oc_mqtt_tiny_v5",
  47.         "oc_mqtt/oc_mqtt_profile_v5",
  48.         "oc_mqtt/oc_mqtt_al",
  49.         "network/dtls/mbedtls/mbedtls_port",
  50.         "network/mqtt/paho_mqtt/port",
  51.         "network/mqtt/paho_mqtt/paho/MQTTClient-C/src",
  52.         "network/mqtt/paho_mqtt/paho/MQTTPacket/src",
  53.         "//third_party/mbedtls/include/",
  54.         "//third_party/mbedtls/include/",
  55.         "//third_party/cJSON",
  56.         "//kernel/liteos_m/components/cmsis/2.0",
  57.         "//device/xradio/xr806/xr_skylark/include/net/mbedtls-2.2.0/",
  58.     ]

  59.     defines = [
  60.         "MQTTCLIENT_PLATFORM_HEADER=paho_osdepends.h",
  61.         "WITH_DTLS",
  62.         "MBEDTLS_AES_ROM_TABLES",
  63.         "MBEDTLS_CONFIG_FILE="los_mbedtls_config_dtls.h"",
  64.         "CONFIG_DTLS_MBEDTLS_CERT",
  65.         "CONFIG_DTLS_MBEDTLS_PSK",
  66.         "CFG_MBEDTLS_MODE=PSK_CERT",
  67.         "CONFIG_OC_MQTT_TINY_ENABLE=1"
  68.     ]
  69. }
  • 修改iot_link中的部分文件
  • third_party/iot_link/network/mqtt/paho_mqtt/port/paho_mqtt_port.c
    测试发现,当fd为0的时候,在执行recv时会立马返回-1,因此做下面规避操作。
    1. static int __socket_connect(Network *n, const char *host, int port)
    2. {
    3.         ...
    4.         int tmpfd = socket(AF_INET,SOCK_STREAM,0); // to skip fd = 0;
    5.         fd = socket(AF_INET,SOCK_STREAM,0);
    6.         if(fd == -1) {
    7.                 return ret;
    8.         }
    9.         close(tmpfd);       // to skip fd = 0;
    10.         ...
    11. }
    12. 系统setsockopt函数未适配,因此需要做下面的修改:

    13. static int __socket_read(void *ctx, unsigned char *buf, int len, int timeout)
    14. {
    15.         int fd;
    16.     int ret = 0;
    17. #if 0
    18.         struct timeval timedelay = {timeout / 1000, (timeout % 1000) * 1000};
    19.     if(NULL== uf)
    20.     {
    21.         return ret;
    22.     }
    23.    
    24.     fd = (int)(intptr_t)ctx;  ///< socket could be zero

    25.     if (timedelay.tv_sec < 0 || (timedelay.tv_sec == 0 && timedelay.tv_usec <= 0))
    26.     {
    27.         timedelay.tv_sec = 0;
    28.         timedelay.tv_usec = 100;
    29.     }
    30.    
    31.     if(0 != setsockopt(fd,SOL_SOCKET,SO_RCVTIMEO,&timedelay,sizeof(struct timeval)))
    32.     {
    33.         return ret;  //could not support the rcv timeout
    34.     }
    35.     int bytes = 0;
    36.     while (bytes < len) {
    37.         int rc = recv(fd, &buf[bytes], (size_t)(len - bytes), 0);
    38.         printf("[%s|%s|%d]fd = %d, rc = %d
    39. ", __FILE__,__func__,__LINE__, fd, rc);
    40.         if (rc == -1) {
    41.             if (errno != EAGAIN && errno != EWOULDBLOCK) {
    42.                 bytes = -1;
    43.             }
    44.             break;
    45.         } else if (rc == 0) {
    46.             bytes = 0;
    47.             break;
    48.         } else {
    49.             bytes += rc;
    50.         }
    51.     }
    52.     return bytes;
    53. #else
    54.         int bytes = 0;
    55.     fd_set fdset;

    56.     struct timeval timedelay = {timeout / 1000, (timeout % 1000) * 1000};
    57.     if(NULL== buf)
    58.     {
    59.         return ret;
    60.     }
    61.    
    62.     fd = (int)(intptr_t)ctx;  ///< socket could be zero

    63.     if (timedelay.tv_sec < 0 || (timedelay.tv_sec == 0 && timedelay.tv_usec <= 0))
    64.     {
    65.         timedelay.tv_sec = 0;
    66.         timedelay.tv_usec = 100;
    67.     }
    68.     timedelay.tv_sec = 2;
    69.     FD_ZERO(&fdset);
    70.     FD_SET(fd, &fdset);

    71.     ret = select(fd + 1, &fdset, NULL, NULL, &timedelay);
    72.     if (ret > 0) {
    73.         while (bytes < len) {
    74.             int rc = recv(fd, &buf[bytes], (size_t)(len - bytes), 0);
    75. //         printf("[%s|%s|%d]fd = %d, rc = %d, errno=%d(%s)
    76. ", __FILE__,__func__,__LINE__, fd, rc,errno, strerror(errno));
    77.             if (rc == -1) {
    78.                 if (errno != EAGAIN && errno != EWOULDBLOCK) {
    79.                     bytes = -1;
    80.                 }
    81.                 break;
    82.             } else if (rc == 0) {
    83.                 bytes = 0;
    84.                 break;
    85.             } else {
    86.                 bytes += rc;
    87.             }
    88.         }
    89.     }

    90.     return bytes;
    91. #endif
    92. }

  • third_party/iot_link/network/dtls/mbedtls/mbedtls_port/dtls_interface.c
    在文件顶部添加打印函数定义以及添加mbedtls_calloc以及mbedtls_free的定义,否则编译会提示错误:
    1. #define MBEDTLS_LOG LINK_LOG_DEBUG
    2. #ifndef mbedtls_calloc
    3. #define mbedtls_calloc  calloc
    4. #endif
    5. #ifndef mbedtls_free
    6. #define mbedtls_free  free
    7. #endif
    系统部分mbedtls接口不一致,固需要注释部分接口代码:
    1. mbedtls_ssl_context dtls_ssl_new(dtls_establish_info_s *info, char plat_type)
    2. {
    3.         ...
    4.         if (info->psk_or_cert == VERIFY_WITH_PSK)
    5.     {
    6. /*
    7.         if ((ret = mbedtls_ssl_conf_psk(conf,
    8.                                         info->v.p.psk,
    9.                                         info->v.p.psk_len,
    10.                                         info->v.p.psk_identity,
    11.                                         strlen((const char *)info->v.p.psk_identity))) != 0)
    12.         {
    13.             MBEDTLS_LOG("mbedtls_ssl_conf_psk failed: -0x%x", -ret);
    14.             goto exit_fail;
    15.         }
    16. */
    17.     }
    18.     ...
    19. }

    20. int dtls_shakehand(mbedtls_ssl_context *ssl, const dtls_shakehand_info_s *info)
    21. {
    22.         ...
    23.         if (MBEDTLS_SSL_IS_CLIENT == info->client_or_server)
    24.     {
    25.         ret = mbedtls_net_connect(server_fd, info->u.c.host, info->u.c.port, info->udp_or_tcp);
    26.         if( 0 != ret)
    27.         {
    28.             ret = MBEDTLS_ERR_NET_CONNECT_FAILED;
    29.             goto exit_fail;
    30.         }
    31.     }
    32.     else
    33.     {
    34.         //server_fd = (mbedtls_net_context*)atiny_net_bind(NULL, info->u.s.local_port, MBEDTLS_NET_PROTO_UDP);
    35.         ///< --TODO ,not implement yet
    36.     }
    37.         ...
    38. }

    39. void dtls_init(void)
    40. {
    41.     (void)mbedtls_platform_set_calloc_free(calloc, free);
    42.     (void)mbedtls_platform_set_snprintf(snprintf);
    43. //    (void)mbedtls_platform_set_printf(printf);
    44. }
    在iot_link/network/dtls/mbedtls/mbedtls_port/mbed_port.c文件中的dtls_imp_init()函数中,也需要注释掉未实现的接口,否则编译报错:
    1. int dtls_imp_init(void)
    2. {
    3.     int ret =-1;

    4.     // (void)mbedtls_platform_set_calloc_free(calloc, free);
    5.     // (void)mbedtls_platform_set_snprintf(snprintf);
    6.     // (void)mbedtls_platform_set_printf(printf);
    7.     ret = dtls_al_install(&s_mbedtls_io);

    8.     return ret;
    9. }

  • 在文件iot_link/network/mqtt/paho_mqtt/port/paho_osdepends.c中添加对应timersub和timeradd的实现(系统中未实现该函数):
    1. // add this for "timersub" && "timeradd"
    2. #ifndef        timersub
    3. #define timersub(s,t,a) (void) ( (a)->tv_sec = (s)->tv_sec - (t)->tv_sec,
    4.         ((a)->tv_usec = (s)->tv_usec - (t)->tv_usec) < 0 &&
    5.         ((a)->tv_usec += 1000000, (a)->tv_sec--) )
    6. #endif
    7. #ifndef        timeradd
    8. #define timeradd(s,t,a) (void) ( (a)->tv_sec = (s)->tv_sec + (t)->tv_sec,
    9.         ((a)->tv_usec = (s)->tv_usec + (t)->tv_usec) >= 1000000 &&
    10.         ((a)->tv_usec -= 1000000, (a)->tv_sec++) )
    11. #endif

  • 编译中会有部分头文件提示找不到,这个时候直接将其注释即可(iot_link/network/mqtt/paho_mqtt/port/paho_osdepends.h):
    1. #define INVALID_SOCKET SOCKET_ERROR
    2. // #include
    3. #include
    4. #include
    5. // #include
    6. // #include
    7. // #include
    8. // #include
    9. #include
    10. #include
    11. #include
    12. #include
    13. #include
    14. #include
    15. #endif

    16. #if defined(WIN32)
    17. #include
    18. #else
    19. // #include
    20. // #include
    21. #endif

  • 因为弱引用导致无法链接相关符号,因此需要注释以下几个文件中的弱引用。
    1. 文件一 third_party/iot_link/network/dtls/dtls_al/dtls_al.c
    2. #if 0
    3. __attribute__((weak))  int dtls_imp_init(void)
    4. {
    5.     LINK_LOG_DEBUG("%s:###please implement dtls by yourself####",__FUNCTION__);
    6.     return -1;
    7. }
    8. #endif
    9. extern int dtls_imp_init(void);

    10. 文件二 third_party/iot_link/network/mqtt/mqtt_al/mqtt_al.c
    11. #if 0
    12. __attribute__((weak))  int mqtt_imp_init(void)
    13. {
    14.     LINK_LOG_DEBUG("%s:###please implement mqtt by yourself####",__FUNCTION__);
    15.     return -1;
    16. }
    17. #endif
    18. extern int mqtt_imp_init(void);

    19. 文件三 third_party/iot_link/oc_mqtt/oc_mqtt_al/oc_mqtt_al.c
    20. #if 0
    21. __attribute__ ((weak)) int oc_mqtt_imp_init(void)
    22. {
    23.     LINK_LOG_DEBUG("%s:###please implement oc mqtt by yourself####",__FUNCTION__);
    24.     return 0;
    25. }

    26. __attribute__ ((weak)) int oc_mqtt_demo_main(void)
    27. {
    28.     LINK_LOG_WARN("Please implement the oc mqtt v5 demo yourself");
    29.     return -1;
    30. }
    31. #endif
    32. extern int oc_mqtt_demo_main(void);

  • 修改GPIO查找方式
因为GPIO框架修改了设备驱动注册的管脚号,导致应用无法根据HCS的引脚操作对应的GPIO,此问题已经提issue,如果该问题已解决,可以忽略此步骤。
打开drivers/framework/support/platform/src/gpio/gpio_manager.c,将cntlr->start = start;注释即可。
  1. static int32_t GpioManagerAdd(struct PlatformManager *manager, struct PlatformDevice *device)
  2. {
  3.     uint16_t start;
  4.     struct GpioCntlr *cntlr = CONTAINER_OF(device, struct GpioCntlr, device);

  5.     if ((start = GpioCntlrQueryStart(cntlr, &manager->devices)) >= GPIO_NUM_MAX) {
  6.         PLAT_LOGE("GpioCntlrAdd: query range for start:%d fail:%d", cntlr->start, start);
  7.         return HDF_ERR_INVALID_PARAM;
  8.     }

  9. //    cntlr->start = start;
  10.     DListInsertTail(&device->node, &manager->devices);
  11.     PLAT_LOGI("%s: start:%u count:%u", __func__, cntlr->start, cntlr->count);
  12.     return HDF_SUCCESS;
  13. }
  • 将对应的驱动文件复制到drvier对应目录:
    因为主仓代码中未将对应的驱动文件合并到driver/adpater/platform对应的目录下,固需要手动将文件拷贝到对应目录。若主仓已合入,可忽略此步骤。
    1. // 拷贝gpio驱动
    2. cp -af device/soc/allwinner/xradio/drivers/gpio/gpio_xradio.* driver/adpater/platform/gpio

    3. // 修改driver/adpater/platform/gpio/BUILD.gn文件,加上gpio_xradio的编译

    4. hdf_driver(module_name) {
    5.   sources = []
    6.   if (defined(LOSCFG_SOC_COMPANY_BESTECHNIC)) {
    7.     sources += [ "gpio_bes.c" ]
    8.   }

    9.   if (defined(LOSCFG_SOC_COMPANY_ALLWINNER)) {
    10.     sources += [ "gpio_xradio.c" ]
    11.   }

    12.   include_dirs = [ "." ]
    13. }
    为了节省ram资源,可以把无用的资源先关闭,如关闭内部codec,将 device/soc/allwinner/xradio/xr806/project/prj_config.h中的PRJCONF_INTERNAL_SOUNDCARD_EN设置为0,如下:
    1. /* Xradio internal codec sound card enable/disable */
    2. #define PRJCONF_INTERNAL_SOUNDCARD_EN   0

4、代码编译
  1. #首先可以查看一下hb的版本,如果hb版本为0.4.4版本就不需要更新。

  2. ```
  3. ## 查看hb版本
  4. hb --version

  5. ## 更新hb, 以下指令需要在openharmony SDK根目录执行
  6. pip3 uninstall ohos_build
  7. pip3 install build/lite
  8. ```

  9. 编译命令:
  10. hb set  // 如果是第一次编译,Input code path 命令行中键入"./" 指定OpenHarmony工程编译根目录后 回车,
  11. 如下图所示,使用键盘上下键选中wifi_skylark
  1. hb build // 如果需要全量编译,可以添加-f 选项
生成的固件保存在out/xradio/smart_weight_scale目录下
5、固件烧录
参照文档: XR806快速上手指导文档

6、设备配网
  • 在设备上电前需准备好安装了数字管家应用的HarmonyOS手机,详情见数字管家应用开发, 并在设置中开启手机的NFC功能;
  • 写设备NFC标签,详细操作见设备NFC标签指导文档;
  • 烧录完成后,上电。开发者在观察开发板上状态LED灯以8Hz的频率闪烁时,将手机上半部靠近开发板NFC标签处(无NFC标签的可用NFC贴纸替代);
  • 碰一碰后手机将自动拉起数字管家应用并进入配网状态;
  • 配网过程中需要 连接设备的AP热点,然后填写需要配置的wifi的密码;
  • 最后点击配置,手机会将ssid以及对应的密码通过AP热点发送到设备。

更多回帖

发帖
×
20
完善资料,
赚取积分