【行业应用】基于 Arm 虚拟硬件平台实现微信支付二维码识别的智能闸机系统

基于 Arm 虚拟硬件平台实现微信支付二维码识别的智能闸机系统

 

本实验过程中所显示的优惠价格及费用报销等相关信息仅在【Arm AI 开发体验创造营】体验活动过程中有效,逾期无效,请根据实时价格自行购买和体验。同时,感谢本次体验活动 Arm 导师 Liliya 对于本博客的指导。 详见活动地址:https://marketing.csdn.net/p/a11ba7c4ee98e52253c8608a085424be

文章目录

  • 1 写在前面
  • 2 Arm虚拟硬件简介
  • 3 RT-Thread 简介
  • 4 案例项目简介
    • 4.1 项目简介
    • 4.2 工作原理简介
    • 4.3 核心流程图
  • 5 案例项目实现过程
    • 5.1 RT-Thread 的移植
    • 5.2 Zbar 的移植
    • 5.3 认证授权的逻辑实现
    • 5.4 遇到的难题
  • 6 核心代码展示
    • 6.1 ZBar的移植代码
    • 6.2 授权认证的逻辑代码
    • 6.3 远程联机的核心代码
  • 7 功能测试
    • 7.1 工程的编译构建
    • 7.2 验证基于 ZBar 的二维码识别
    • 7.3 运行示例图展示
    • 7.4 相关验证的输入图片
  • 8 更多思考
    • 8.1 关于 Arm 虚拟硬件的几点优势及本项目的前景
    • 8.2 移植过程的心得体会分享
    • 8.3 项目的后续迭代与展望
  • 9 参考资料

1 写在前面

在常规的嵌入式软件开发中,通常需要在用于开发的电脑主机上提前把应用程序编译好,生成可在嵌入式芯片上运行的文件代码,再通过相应的烧录调试工具,把该代码烧录至开发板中,才能查看验证所编写应用程序的正确性。

传统的嵌入式开发流程中,往往需要用到物理开发板才能进行相应的软件开发。但是,没有拿到物理开发板或对于一些新推出的处理器产品(例如:Arm® Cortex®-M55,Cortex-M85, Ethos™-U 系列 NPU 等)市场上硬件的资源较为稀缺且需要较长的时间才能获取到物理开发板的情况下,是否有办法在相应的平台上进行软件开发呢?

答案自然是有的,这就是我们本期实验手册要给大家介绍的一个非常强大的开发工具:**Arm 虚拟硬件(Arm Virtual Hardware)。**借助 Arm 虚拟硬件平台,我们可以做一些非常实用有趣的工具和案例,达到辅助我们日常开发的目的。

另一方面,在当下越来越复杂的物联网应用,比如一些对实时性要求比较高,且应用逻辑复杂的智能家居应用场景下,往往都需要一个优秀的端侧实时操作系统来做底层支撑。而选择一款操作系统,一是看它的稳定性,二是看的软件生态;恰好 RT-Thread 作为一款国内拥有自主知识产权的优秀国产物联网操作系统,因其高效、代码风格良好、开源生态很强,越来越多的物联网人开始使用它开发一些商业级别的产品。它正是我们的不二选择。

本期文章给大家介绍的 基于 Arm 虚拟硬件平台实现微信支付二维码识别的智能闸机系统,下文会该项目做详细介绍。

2 Arm虚拟硬件简介

Arm 虚拟硬件(Arm Virtual Hardware)提供了一个 Ubuntu Linux 镜像,包括用于 物联网、机器学习和嵌入式应用程序的 Arm 开发工具:例如,Arm 编译器、 FVP 模型和其他针对 Cortex-M 系列处理器的开发工具帮助开发者快速入门。Arm 虚拟硬件限时免费提供用于评估用途,例如,评估 CI/CD、MLOps 和 DevOps 工作流中的自动化测试工作流等。订阅访问和使用此版本的 Arm 虚拟硬件,您需同意产品最终用户许可协议中与免费测试版许可相关的条款和协议。

Arm 虚拟硬件产品的技术概览示意图如下所示。开发者也可访问 Arm 虚拟硬件产品介绍页和产品技术文档了解更多关于 Arm 虚拟硬件产品知识。

在这里插入图片描述

Arm 虚拟硬件产品概览

3 RT-Thread 简介

RT-Thread 诞生于2006年,是国内以开源中立、 社区化发展起来的一款高可靠实时操作系统 ,由睿赛德科技负责开发维护和运营 。因其十五年的沉淀积累, 专业化的运营推广,其高可靠性、安全、高可伸缩性和中间组件丰富易用等特性极大地满足了市场需求。目前已经成为市面上装机量最大(超20亿台)、开发者数量最多(超20万)软硬件生态最好的操作系统之一,被广泛应用于航空、电力、轨道交通、车载、工业自动化、消费电子等众多行业领域。

作为一款优秀的国产操作系统,它的核心软件架构图设计得还是非常精妙,参考如下:

在这里插入图片描述

它具体包括以下部分:

  • 内核层:RT-Thread 内核,是 RT-Thread 的核心部分,包括了内核系统中对象的实现,例如多线程及其调度、信号量、邮箱、消息队列、内存管理、定时器等;libcpu/BSP(芯片移植相关文件 / 板级支持包)与硬件密切相关,由外设驱动和 CPU 移植构成。
  • 组件与服务层:组件是基于 RT-Thread 内核之上的上层软件,例如虚拟文件系统、FinSH 命令行界面、网络框架、设备框架等。采用模块化设计,做到组件内部高内聚,组件之间低耦合。
  • RT-Thread 软件包:运行于 RT-Thread 物联网操作系统平台上,面向不同应用领域的通用软件组件,由描述信息、源代码或库文件组成。RT-Thread 提供了开放的软件包平台,这里存放了官方提供或开发者提供的软件包,该平台为开发者提供了众多可重用软件包的选择,这也是 RT-Thread 生态的重要组成部分。软件包生态对于一个操作系统的选择至关重要,因为这些软件包具有很强的可重用性,模块化程度很高,极大的方便应用开发者在最短时间内,打造出自己想要的系统。RT-Thread 已经支持的软件包数量已经达到 400+,如下举例:
  • 物联网相关的软件包:Paho MQTT、WebClient、mongoose、WebTerminal 等等。
  • 脚本语言相关的软件包:目前支持 Lua、JerryScript、MicroPython、PikaScript。
  • 多媒体相关的软件包:Openmv、mupdf。
  • 工具类软件包:CmBacktrace、EasyFlash、EasyLogger、SystemView。
  • 系统相关的软件包:RTGUI、Persimmon UI、lwext4、partition、SQLite 等等。
  • 外设库与驱动类软件包:RealTek RTL8710BN SDK。
  • 其他。

有更多需要了解 RT-Thread 的可以到它的文档中心做进一步深入学习。

4 案例项目简介

4.1 项目简介

本次实验案例是需要基于 Arm 虚拟硬件平台实现微信支付二维码识别的智能闸机系统,主要适用于一些智能楼宇的秩序管控领域。

它的核心功能有以下几点:

  • 可以实现对微信支付二维码的快速识别;
  • 支持本地脱机模式(端侧认证)运行;
  • 支持远程联机模式(云侧认证)运行;
  • 支持自主登记可被授权的二维码信息。

4.2 工作原理简介

本案例应用的最核心技术是基于图像输入的二维码识别,在一个微信支付展示支付码界面,其本质就是一个一维码和二维码展示,如下图所示:

我们可以看到下方的二维码就是一串可做身份识别的ID数字,我们只要基于一定的管控逻辑对这串数字做认证授权即可。

4.3 核心流程图

录入微信支付二维码授权信息的流程图:

在这里插入图片描述

端侧脱机认证的流程图:
在这里插入图片描述

云测联机认证的流程图:
在这里插入图片描述

注意,本次实验因远程调试,仅仅核心功能实现,在实现过程中,动态图片均以提前预制好的图片为主,无法从摄像图或其他外设驱动获得真实的二维码图片,但核心逻辑是一致的

5 案例项目实现过程

5.1 RT-Thread 的移植

整个 RT-Thread 的移植可以参考我之前的博文,也可以尝试跟随 RT-Thread 的官方文档中心,自行完成,本文不再赘述。

5.2 Zbar 的移植

移植 ZBar 将本案例项目最核心的一部分内容,它将完成对图像(图片)的识别,从中提取二维码信息。

这是 ZBar 的 Github开源网址,https://github.com/ZBar/ZBar/ 但是比较遗憾的是,ZBar项目源码在 2012 年就停止维护运营了,不过其核心功能还是很出色的,值得信赖。

在这里插入图片描述

ZBar 条形码阅读器是一个开源软件,适用于读取不同来源的条形码,如视频流、图像文件和原始强度传感器。它支持 EAN-13/UPC-A, UPC-E, EAN-8, Code 128,Code 39, Codabar, Interleaved 2/5 和二维码。ZBar库支持8位灰度图片/图像数据做为输入进行对条形码和二维码的解码,本身它是不带图片/图像采集功能的,所以在本项目作用时可以考虑使用视频设备(如网络摄像头)作为条形码/二维码做为采集端,本文将不再详细介绍。对于开发者,支持语言绑定包括 C、C++、Python 和 Perl,以及用于 Qt、GTK 和 PyGTK 的 GUI 小部件。该项目旨在创建一个高性能、稳定、健壮的库组件,并提供支持基础设施,使其易于在各种应用程序中使用。

移植 ZBar 主要分为以下几个重要步骤:

  • 识别出编译配置项,形成 zbar_config.h,比如至少要开启 QRCODE 的支持;
  • 识别出参与编译的工程文件,将他们列入到 cbuild 的 proj 工程文件中;
  • 添加对应的宏定义及头文件检索目录;
  • 执行编译,修复一些编译问题,例如函数接口的转换、内存操作等相关接口的调整;
  • 增加若干测试用例,测试 ZBar 识别二维码的基础能力;
  • 存在一个内存释放的错误,修复相关代码 bug。

5.3 认证授权的逻辑实现

因制作逻辑展示,这部分的逻辑相对做得比较简单,将已授权的二维码信息存放在 RAM 内存中,收到 ZBar 识别的二维码信息,与授权库的二维码信息做匹配即可完成认证过程。

具体的可以结合上文的流程图及下文的示例代码展示。

5.4 遇到的难题

这期间遇到的难度有3个:

  1. ZBar 对识别图片的输入,要求是 8 位深度的灰度图片数据,而不能是原生的,比如 24 位 BMP 图片数据;
  2. 工程实例中,对 PNG 图片尚不支持,只能处理 BMP 图片;
  3. ZBar 对某些情况下的图片识别不够,有时会出错而识别不到;或者有时候识别出错误的信息;这可能跟输入图片的质量有关系。

6 核心代码展示

6.1 ZBar的移植代码

两部分内容:配置文件和工程文件列表:

#ifndef __CONFIG_H__
#define __CONFIG_H__#include <stdint.h>#if (CFG_RTT_ZBAR)
//#include "rtthread.h"
#endif/* include/config.h.  Generated from config.h.in by configure.  */
/* include/config.h.in.  Generated from configure.ac by autoheader.  *//* whether to build support for Code 128 symbology */
#define ENABLE_CODE128 1/* whether to build support for Code 39 symbology */
#define ENABLE_CODE39 1/* whether to build support for EAN symbologies */
#define ENABLE_EAN 1/* whether to build support for Interleaved 2 of 5 symbology */
#define ENABLE_I25 1/* whether to build support for PDF417 symbology */
#define ENABLE_PDF417/* whether to build support for QR Code */
#define ENABLE_QRCODE 1/* Program major version (before the '.') as a number */
#define ZBAR_VERSION_MAJOR 0/* Program minor version (after '.') as a number */
#define ZBAR_VERSION_MINOR 10#define         PRIx32   "lx"
#if 0
/* Define to 1 if you have the `atexit' function. */
#define HAVE_ATEXIT 1/* Define to 1 if you have the <dlfcn.h> header file. */
#define HAVE_DLFCN_H 1/* Define to 1 if you have the <fcntl.h> header file. */
#define HAVE_FCNTL_H 1/* Define to 1 if you have the <features.h> header file. */
#define HAVE_FEATURES_H 1/* Define to 1 if you have the `getpagesize' function. */
#define HAVE_GETPAGESIZE 1/* Define if you have the iconv() function and it works. */
#define HAVE_ICONV 1/* Define to 1 if you have the <inttypes.h> header file. */
#define HAVE_INTTYPES_H 1/* Define to 1 if you have the <jpeglib.h> header file. */
/* #undef HAVE_JPEGLIB_H *//* Define to 1 if you have the `jpeg' library (-ljpeg). */
/* #undef HAVE_LIBJPEG *//* Define to 1 if you have the `pthread' library (-lpthread). */
#define HAVE_LIBPTHREAD 1/* Define to 1 if you have the <linux/videodev2.h> header file. */
/* #undef HAVE_LINUX_VIDEODEV2_H *//* Define to 1 if you have the <linux/videodev.h> header file. */
/* #undef HAVE_LINUX_VIDEODEV_H *//* Define to 1 if you have the <memory.h> header file. */
#define HAVE_MEMORY_H 1/* Define to 1 if you have the `memset' function. */
#define HAVE_MEMSET 1/* Define to 1 if you have a working `mmap' system call. */
#define HAVE_MMAP 1/* Define to 1 if you have the <poll.h> header file. */
#define HAVE_POLL_H 1/* Define to 1 if you have the <pthread.h> header file. */
#define HAVE_PTHREAD_H 1/* Define to 1 if you have the `setenv' function. */
#define HAVE_SETENV 1/* Define to 1 if you have the <stdint.h> header file. */
#define HAVE_STDINT_H 1/* Define to 1 if you have the <stdlib.h> header file. */
#define HAVE_STDLIB_H 1/* Define to 1 if you have the <strings.h> header file. */
#define HAVE_STRINGS_H 1/* Define to 1 if you have the <string.h> header file. */
#define HAVE_STRING_H 1/* Define to 1 if you have the <sys/ioctl.h> header file. */
#define HAVE_SYS_IOCTL_H 1/* Define to 1 if you have the <sys/ipc.h> header file. */
#define HAVE_SYS_IPC_H 1/* Define to 1 if you have the <sys/mman.h> header file. */
#define HAVE_SYS_MMAN_H 1/* Define to 1 if you have the <sys/shm.h> header file. */
#define HAVE_SYS_SHM_H 1/* Define to 1 if you have the <sys/stat.h> header file. */
#define HAVE_SYS_STAT_H 1/* Define to 1 if you have the <sys/times.h> header file. */
#define HAVE_SYS_TIMES_H 1/* Define to 1 if you have the <sys/time.h> header file. */
#define HAVE_SYS_TIME_H 1/* Define to 1 if you have the <sys/types.h> header file. */
#define HAVE_SYS_TYPES_H 1/* Define to 1 if the system has the type `uintptr_t'. */
#define HAVE_UINTPTR_T 1/* Define to 1 if you have the <unistd.h> header file. */
#define HAVE_UNISTD_H 1/* Define to 1 if you have the <vfw.h> header file. */
/* #undef HAVE_VFW_H *//* Define to 1 if you have the <X11/extensions/XShm.h> header file. */
/* #undef HAVE_X11_EXTENSIONS_XSHM_H *//* Define to 1 if you have the <X11/extensions/Xvlib.h> header file. */
/* #undef HAVE_X11_EXTENSIONS_XVLIB_H *//* Define as const if the declaration of iconv() needs const. */
#define ICONV_CONST/* Library major version */
#define LIB_VERSION_MAJOR 0/* Library minor version */
#define LIB_VERSION_MINOR 2/* Library revision */
#define LIB_VERSION_REVISION 0/* Define to the sub-directory in which libtool stores uninstalled libraries.*/
#define LT_OBJDIR ".libs/"/* Define to 1 if assertions should be disabled. */
/* #undef NDEBUG *//* Define to 1 if your C compiler doesn't accept -c and -o together. */
/* #undef NO_MINUS_C_MINUS_O *//* Name of package */
#define PACKAGE "zbar"/* Define to the address where bug reports for this package should be sent. */
#define PACKAGE_BUGREPORT "spadix@users.sourceforge.net"/* Define to the full name of this package. */
#define PACKAGE_NAME "zbar"/* Define to the full name and version of this package. */
#define PACKAGE_STRING "zbar 0.10"/* Define to the one symbol short name of this package. */
#define PACKAGE_TARNAME "zbar"/* Define to the version of this package. */
#define PACKAGE_VERSION "0.10"/* Define to 1 if you have the ANSI C header files. */
#define STDC_HEADERS 1/* Version number of package */
#define VERSION "0.10"/* Define to 1 if the X Window System is missing or not being used. */
#define X_DISPLAY_MISSING 1/* Program major version (before the '.') as a number */
#define ZBAR_VERSION_MAJOR 0/* Program minor version (after '.') as a number */
#define ZBAR_VERSION_MINOR 10/* Define for Solaris 2.5.1 so the uint32_t typedef from <sys/synch.h>,<pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the#define below would cause a syntax error. */
/* #undef _UINT32_T *//* Define for Solaris 2.5.1 so the uint8_t typedef from <sys/synch.h>,<pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the#define below would cause a syntax error. */
/* #undef _UINT8_T *//* Minimum Windows API version */
/* #undef _WIN32_WINNT *//* used only for pthread debug attributes */
#define __USE_UNIX98 1/* Define to empty if `const' does not conform to ANSI C. */
/* #undef const *//* Define to `__inline__' or `__inline' if that's what the C compilercalls it, or to nothing if 'inline' is not supported under any name.  */
#ifndef __cplusplus
/* #undef inline */
#endif/* Define to the type of a signed integer type of width exactly 32 bits ifsuch a type exists and the standard includes do not define it. */
/* #undef int32_t *//* Define to the type of an unsigned integer type of width exactly 32 bits ifsuch a type exists and the standard includes do not define it. */
/* #undef uint32_t *//* Define to the type of an unsigned integer type of width exactly 8 bits ifsuch a type exists and the standard includes do not define it. */
/* #undef uint8_t *//* Define to the type of an unsigned integer type wide enough to hold apointer, if such a type exists, and if the system does not define it. */
/* #undef uintptr_t */#ifndef X_DISPLAY_MISSING
# define HAVE_X
#endif
#endif
#endif

工程文件列表展示,proj 文件的增加:

    <group name="zbar"><cflags add="-I ./zbar/decoder -I ./zbar/qrcode -I ./zbar -I ./ -I ./zbar_app" compiler="AC6"/><file category="sourceC" name="zbar/decoder/code39.c"/><file category="sourceC" name="zbar/decoder/code128.c"/><file category="sourceC" name="zbar/decoder/ean.c"/><file category="sourceC" name="zbar/decoder/i25.c"/><file category="sourceC" name="zbar/decoder/pdf417.c"/><file category="sourceC" name="zbar/decoder/qr_finder.c"/><file category="sourceC" name="zbar/qrcode/bch15_5.c"/><file category="sourceC" name="zbar/qrcode/binarize.c"/><file category="sourceC" name="zbar/qrcode/isaac.c"/><file category="sourceC" name="zbar/qrcode/qrdec.c"/><file category="sourceC" name="zbar/qrcode/qrdectxt.c"/><file category="sourceC" name="zbar/qrcode/rs.c"/><file category="sourceC" name="zbar/qrcode/util.c"/><file category="sourceC" name="zbar/config.c"/><file category="sourceC" name="zbar/decoder.c"/><file category="sourceC" name="zbar/error.c"/><file category="sourceC" name="zbar/image.c"/><file category="sourceC" name="zbar/img_scanner.c"/><file category="sourceC" name="zbar/refcnt.c"/><file category="sourceC" name="zbar/scanner.c"/><file category="sourceC" name="zbar/symbol.c"/><file category="sourceC" name="zbar_app/zbar_main.c"/><file category="sourceC" name="zbar_app/zbar_auth.c"/></group>

工程示例图:

在这里插入图片描述

6.2 授权认证的逻辑代码

端侧本地脱机认证的逻辑代码:

// register auth ok qrcode
static char *g_auth_ok_qrcoe_list[] = 
{"130608135571250081",
};int zbar_check_auth_offline(char *qrcode)
{int i = 0;for (i = 0; i < ARRAY_SIZE(g_auth_ok_qrcoe_list); i++) {if (!strcmp(g_auth_ok_qrcoe_list[i], qrcode)) {rt_kprintf("qrcode: %s auth offline ok\n", qrcode);return 0;}}rt_kprintf("qrcode: %s auth offline fail\n", qrcode);return 0;
}

云端远程联机认证的逻辑代码:

端侧构建一个格式为: {“qrcode” : “1234567890” } 的JSON字符串发往云端,云端进行授权认证,将结果返回给端侧,参考如下,更为详细的代码见下一小节。

   ret = iotSocketRecv (sock, buffer, sizeof(buffer));if (ret < 0) {rt_kprintf("recv fail\n");goto exit_entry;}rt_kprintf("<<< %s\n", buffer);cJSON * rsp_root = NULL;cJSON * result = NULL;rsp_root = cJSON_Parse(buffer);result = cJSON_GetObjectItem(rsp_root, "result");if (!strcmp(result->valuestring, "ok")) {rt_kprintf("qrcode: %s auth online ok\n", qrcode);ret = 0;} else {rt_kprintf("qrcode: %s auth online fail\n", qrcode);}

6.3 远程联机的核心代码

本部分利用的 Arm 虚拟硬件独有的 VSocket 特性,感兴趣可以从 Arm 虚拟硬件的帮助文档 https://arm-software.github.io/AVH/main/simulation/html/group__arm__vsocket.html 了解 。

具体的代码体现如下,整体采用的经典的 CS 模型,云端做服务器,端侧做客户端。

服务器云端的Python脚本的代码:

import socket
import sys
import json
import socket
import os
import shutil
import binasciidebug_mode = Falsetcp_mode = True# 定义服务器的IP地址和端口号
SERVER_IP_ADDRESS = "127.0.0.1"
if tcp_mode:TCP_PORT_NO0 = 18888TCP_PORT_NO1 = 18889
else:UDP_PORT_NO = 55556'''
// register auth ok qrcode
static char *g_auth_ok_qrcoe_list[] = 
{"130608135571250081",
};
'''
g_auth_ok_qrcoe_list = ["130608135571250081"]# 创建一个UDP socket
if tcp_mode:serverSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
else:serverSock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)if tcp_mode:try: # Server address and portserver_address = (SERVER_IP_ADDRESS, TCP_PORT_NO0)# Bind the socket to the portif debug_mode:print(f"Starting up on {server_address[0]} port {server_address[1]}")serverSock.bind(server_address)except Exception as e:# Server address and portserver_address = (SERVER_IP_ADDRESS, TCP_PORT_NO1)# Bind the socket to the portif debug_mode:print(f"Starting up on {server_address[0]} port {server_address[1]}")serverSock.bind(server_address)
else:# 绑定socket到指定的IP地址和端口号serverSock.bind((SERVER_IP_ADDRESS, UDP_PORT_NO))file_fd = Noneif tcp_mode:# Listen for incoming connectionsserverSock.listen(1)while True:if tcp_mode:if debug_mode:print("Waiting for a new connection...")connection, client_address = serverSock.accept()#print(connection)#print(client_address)#print('-------------1111')if tcp_mode:data = connection.recv(10240)else:data, addr = serverSock.recvfrom(10240)'''{"qrcode": "1234567","result": "ok",}'''try:qrcode = ""data_str = data.decode('utf-8').strip()if debug_mode:print(data_str)parsed_data = json.loads(data_str)qrcode = parsed_data.get('qrcode')#print(qrcode)except Exception as e:rsp_content = str(e)try:if qrcode in g_auth_ok_qrcoe_list:rsp_content = "{\"qrcode\":\"" + qrcode + "\", \"result\": \"ok\"}"else:rsp_content = "{\"qrcode\":\"" + qrcode + "\", \"result\": \"fail\"}"except Exception as e:rsp_content = str(e)if tcp_mode:connection.sendall(rsp_content.encode('utf-8'))connection.close()else:serverSock.sendto(rsp_content.encode('utf-8'), addr)serverSock.close()if debug_mode:print(rsp_content)print("")

Arm 虚拟硬件(充当客户端)的代码:

int zbar_check_auth_online(char *qrcode)
{int sock;int ret = -1;        char buffer[1024];//rt_kprintf("qrcode: %s\n", qrcode);if ((sock = iotSocketCreate(IOT_SOCKET_AF_INET, IOT_SOCKET_SOCK_STREAM, IOT_SOCKET_IPPROTO_TCP)) < 0) {rt_kprintf("socket fail\n");goto exit_entry;}uint8_t ip_array[] = SERVER_IP_BYTES;uint32_t ip_len = sizeof(ip_array);ret = iotSocketConnect (sock, (const uint8_t *)ip_array, ip_len, SERVER_PORT0);if (ret < 0) {//rt_kprintf("connect fail\n");ret = iotSocketConnect (sock, (const uint8_t *)ip_array, ip_len, SERVER_PORT1);if (ret < 0) {rt_kprintf("connect fail\n");goto exit_entry;}}//snprintf(buffer, sizeof(buffer), "{\"qrcode\" : \"%s\"}", qrcode);cJSON *root = cJSON_CreateObject();cJSON_AddItemToObject(root, "qrcode", cJSON_CreateString(qrcode));char *json_string = cJSON_PrintUnformatted(root);ret = iotSocketSend(sock, json_string, strlen(json_string));if (ret < 0) {rt_kprintf("send fail\n");goto exit_entry;}rt_kprintf(">>> %s\n", json_string);free(json_string);cJSON_Delete(root);ret = iotSocketRecv (sock, buffer, sizeof(buffer));if (ret < 0) {rt_kprintf("recv fail\n");goto exit_entry;}rt_kprintf("<<< %s\n", buffer);cJSON * rsp_root = NULL;cJSON * result = NULL;rsp_root = cJSON_Parse(buffer);result = cJSON_GetObjectItem(rsp_root, "result");if (!strcmp(result->valuestring, "ok")) {rt_kprintf("qrcode: %s auth online ok\n", qrcode);ret = 0;} else {rt_kprintf("qrcode: %s auth online fail\n", qrcode);}cJSON_Delete(rsp_root);exit_entry:if (sock >= 0) {iotSocketClose(sock);}return ret;
}

对源码感兴趣的,可以进一步参考文末给出的仓库地址。

7 功能测试

7.1 工程的编译构建

本工程项目采用 Makefile + cbuild 来进行构建。最外层,我们使用的事Makefile进行操作的封装。

输入make help可以获取帮助信息,一眼可以看出如何进行输入命令:

$ make help
make help       -> Show this help msg..
make build      -> Build thie demo.
make clean      -> Clean object files.
make run        -> Run this demo.
make run_finsh  -> Run this demo, which enable RTT finsh.
make debug      -> Build & run this demo.
make all        -> Source & clean & build & run all together.
make stop       -> Stop to run this demo.

输入make clean 清除编译生成的中间文件,make build 可以进行代码的编译构建,少部分编译警告信息可以忽略:

ubuntu@arm-43ecd4886e664dffa086d3a69133a5e5:~/new/AVH-RTTHREAD-DEMO-RECAN/rt-thread/bsp/arm/AVH-FVP_MPS2_Cortex-M85-ZBar$ make clean
Clean ...
rm -rf ./Objects/
rm -rf out
ubuntu@arm-43ecd4886e664dffa086d3a69133a5e5:~/new/AVH-RTTHREAD-DEMO-RECAN/rt-thread/bsp/arm/AVH-FVP_MPS2_Cortex-M85-ZBar$ 
ubuntu@arm-43ecd4886e664dffa086d3a69133a5e5:~/new/AVH-RTTHREAD-DEMO-RECAN/rt-thread/bsp/arm/AVH-FVP_MPS2_Cortex-M85-ZBar$ make build
Building ...
cbuild --packs ./AVH-FVP_MPS2_Cortex-M85.cprj
info cbuild: Build Invocation 1.2.0 (C) 2022 ARM
(cbuildgen): Build Process Manager 1.3.0 (C) 2022 Arm Ltd. and Contributors
M650: Command completed successfully.
(cbuildgen): Build Process Manager 1.3.0 (C) 2022 Arm Ltd. and Contributors
M652: Generated file for project build: '/home/ubuntu/new/AVH-RTTHREAD-DEMO-RECAN/rt-thread/bsp/arm/AVH-FVP_MPS2_Cortex-M85-ZBar/Objects/CMakeLists.txt'
-- The ASM compiler identification is ARMClang
-- Found assembler: /opt/armcompiler/bin/armclang
-- Configuring done
-- Generating done
-- Build files have been written to: /home/ubuntu/new/AVH-RTTHREAD-DEMO-RECAN/rt-thread/bsp/arm/AVH-FVP_MPS2_Cortex-M85-ZBar/Objects
[8/80] Building C object CMakeFiles/image.dir/home/ubuntu/new/AVH-RTTHREAD-DEMO-RECAN/rt-thread/bsp/arm/AVH-FVP_MPS2_Cortex-M85-ZBar/zbar/decoder.o
In file included from /home/ubuntu/new/AVH-RTTHREAD-DEMO-RECAN/rt-thread/bsp/arm/AVH-FVP_MPS2_Cortex-M85-ZBar/zbar/decoder.c:31:
/home/ubuntu/new/AVH-RTTHREAD-DEMO-RECAN/rt-thread/bsp/arm/AVH-FVP_MPS2_Cortex-M85-ZBar/zbar/decoder.h:194:26: warning: implicit declaration of function 'rt_realloc' is invalid in C99 [-Wimplicit-function-declaration]unsigned char *buf = rt_realloc(dcode->buf, len);^
/home/ubuntu/new/AVH-RTTHREAD-DEMO-RECAN/rt-thread/bsp/arm/AVH-FVP_MPS2_Cortex-M85-ZBar/zbar/decoder.h:194:20: warning: incompatible integer to pointer conversion initializing 'unsigned char *' with an expression of type 'int' [-Wint-conversion]unsigned char *buf = rt_realloc(dcode->buf, len);省略部分log/home/ubuntu/new/AVH-RTTHREAD-DEMO-RECAN/rt-thread/bsp/arm/AVH-FVP_MPS2_Cortex-M85-ZBar/zbar/symbol.h:70:18: warning: incompatible integer to pointer conversion assigning to 'point_t *' (aka 'struct point_s *') from 'int' [-Wint-conversion]sym->pts = rt_realloc(sym->pts, ++sym->pts_alloc * sizeof(point_t));^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/home/ubuntu/new/AVH-RTTHREAD-DEMO-RECAN/rt-thread/bsp/arm/AVH-FVP_MPS2_Cortex-M85-ZBar/zbar_app/zbar_main.c:137:5: warning: implicit declaration of function 'rt_free' is invalid in C99 [-Wimplicit-function-declaration]rt_free(pic_data);^
6 warnings generated.
[80/80] Linking C executable image.axf
"/home/ubuntu/new/AVH-RTTHREAD-DEMO-RECAN/rt-thread/bsp/arm/AVH-FVP_MPS2_Cortex-M85-ZBar/RTE/Device/ARMCM85/ARMCM85_ac6.sct", line 31 (column 7): Warning: L6314W: No section matches pattern *(.bss.noinit).
Program Size: Code=102280 RO-data=123052 RW-data=476 ZI-data=233152  
Finished: 0 information, 1 warning and 0 error messages.
info cbuild: build finished successfully!

至此,我们就完成了面向 Arm 平台的 axf 文件的编译生成。

7.2 验证基于 ZBar 的二维码识别

输入make run_finsh即可把编译生成好的axf文件跑起来:

ubuntu@arm-43ecd4886e664dffa086d3a69133a5e5:~/new/AVH-RTTHREAD-DEMO-RECAN/rt-thread/bsp/arm/AVH-FVP_MPS2_Cortex-M85-ZBar$ make run_finsh
Running ...
make[1]: Entering directory '/home/ubuntu/new/AVH-RTTHREAD-DEMO-RECAN/rt-thread/bsp/arm/AVH-FVP_MPS2_Cortex-M85-ZBar'
make[1]: Leaving directory '/home/ubuntu/new/AVH-RTTHREAD-DEMO-RECAN/rt-thread/bsp/arm/AVH-FVP_MPS2_Cortex-M85-ZBar'
/opt/VHT/bin/FVP_MPS2_Cortex-M85 --stat --simlimit 80000000 -f ./vht_config.txt out/image.axf &
telnetterminal2: Listening for serial connection on port 5000
telnetterminal1: Listening for serial connection on port 5001
telnetterminal0: Listening for serial connection on port 5002\ | /
- RT -     Thread Operating System/ | \     5.2.0 build Jun 15 2024 07:30:022006 - 2024 Copyright by RT-Thread team
Hello ZBAR ...
Debug server create success, accepting clients @ port 12345 ...
Conncting debug server ... Conncted debug server ok ...
msh > 
msh > help
RT-Thread shell commands:
zbar_qrcode_auth - auth qrcode which decoded by zbar
pin              - pin [option]
clear            - clear the terminal screen
version          - show RT-Thread version information
list             - list objects
help             - RT-Thread shell help
ps               - List threads in the system
free             - Show the memory usage in the system

核心的命令就是 /opt/VHT/bin/FVP_MPS2_Cortex-M85 --stat --simlimit 80000000 -f ./vht_config.txt out/image.axf

为便于开发者理解,此处简单的展开说明,该命令调用了 Arm 虚拟硬件镜像中的 Cortex-M85 处理器的 FVP 模型 FVP_MPS2_Cortex-M85 模拟一个硬件开发板,我们直接在云服务器上就把本来应该跑在 Arm 开发板上的可执行文件给跑起来了。其中,该命令部分参数解读如下:

  • FVP_MPS2_Cortex-M85即为所调用的 Cortex-M85 的 FVP 模型的名称。
  • --stat 表示停止模拟时,打印相关的运行状态信息。
  • --simlimit 80000000 表示模拟运行的时间上限为 80000000s,即若用户未手动退出,则80000000s 后程序会自动退出运行。
  • out/image.axf 即为具备 ZBar 解析二维码功能的可执行应用文件。
  • -f vht_config.txt 即指定了 FVP 模型运行时的所依据的配置文件。可以通过 FVP_MPS2_Cortex-M85 -l 命令获取 Cortex-M85 的 FVP 模型所有可配置的参数及其默认值(初始值)信息。用户可根据自身需求进行参数调整,获得不同的应用执行效果。

7.3 运行示例图展示

因仅做核心功能展示,我们这里用到了 RT-Thread 的自定义 finsh 命令,构建了一个 zbar_qrcode_auth 命令,我们通过这个命令可以完成授权认证测试,参考如下。

在实验代码中,已提取将 “130608135571250081” 录入到授权库中,而 “130608135571250082”没有录入进去,所以预期是一个会授权认证成功,一个授权认证失败。

在这里插入图片描述

本地脱机模式认证成功:
在这里插入图片描述

本地脱机模式认证失败:

在这里插入图片描述

云端联机模式认证成功:
在这里插入图片描述

云端联机模式认证失败:

在这里插入图片描述

7.4 相关验证的输入图片

本次案例使用的图片案例位于下面的目录,使用的是红色标记出的两个图片输入:

在这里插入图片描述

在做图片转换的过程,需要用到 Image2Lcd 软件工具,它可以从公开的一些工具网站下载到,使用界面参考如下,它可以将对应的图片生成工程代码中需要的 16 位 RGB565 图片。

在这里插入图片描述

它还可以将图片直接转成 C 语言的数组,方便编程中嵌入使用,非常方便,感兴趣可以自行了解下这个工具的使用。

8 更多思考

8.1 关于 Arm 虚拟硬件的几点优势及本项目的前景

我想补充几点:

  • Arm 虚拟硬件平台给了非常便利的开发、编译、调试、运行验证的操作体验,无论是在开发阶段还是在生产阶段,都能给开发者及企业带来很大的便利。
  • 相对于其他孤立的芯片开发平台,Arm 虚拟硬件平台在成套的软件包上还是比较完毕的,比如 RTOS 相关的、网络相关的、安全相关的等等软件包,都可以通过快速配置得到比较好的复用,这一点在开发流程上,也得到了很大的改善。
  • 借助 Arm 虚拟硬件的网络通讯平台,其具备公网通讯的能力,这一点可以在适当的功能扩展上做成很多基于网络通讯的应用工程,是一个值得期待的开发亮点。
  • 有了 RT-Thread 和 ZBar 的加持,对于一维码和二维码识别的场景,我们就能够支持了,欢迎广大读者想象出更多的应用场景。

8.2 移植过程的心得体会分享

  • 核心逻辑先跑 PC 环境,这一点非常重要;
  • 移植一个开源库,学习它的官方文档是必不可少的;
  • 遇到问题,需要冷静分析,一步步细心排查,不放松一丁点的蛛丝马迹。

8.3 项目的后续迭代与展望

当然,就当前的实现案例来说,也还存在一些不同需要后续补充改进,比如:

  • ZBar 识别成功率的问题;
  • 图片格式的识别问题;
  • 内存占用过大的优化问题;
  • 接入摄像头外设等问题。

这些问题,都是一些值得深挖的核心问题,需要读者朋友进一步去探索和实践,也非常欢迎有有问题与我讨论。

9 参考资料

  1. 本案例的主代码仓库地址:https://github.com/recan-li/AVH-RTTHREAD-DEMO-RECAN 在对应的rt-thread/bsp/arm/ 目录下即可找到对应的FVP适配工程及对应的代码。
  2. Arm 虚拟硬件产品简介
  3. Arm 虚拟硬件帮助文档
  4. Arm 虚拟硬件开发者资源
  5. 【中文技术指南】Arm 虚拟硬件实践专题一:产品订阅指南(百度智能云版)
  6. 【中文技术指南】Arm 虚拟硬件实践专题二:Arm 虚拟硬件 FVP 模型入门指南
  7. 【中文视频直播课】加速AI开发,1小时快速入门Arm虚拟硬件
  8. Arm 社区微信公众号

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/29289.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

解读自然语言处理:技术、应用与未来展望

引言 自然语言处理&#xff08;Natural Language Processing&#xff0c;简称NLP&#xff09;是计算机科学、人工智能和语言学的一个跨学科领域&#xff0c;致力于实现人与计算机之间通过自然语言进行有效沟通的能力。NLP 的核心任务是理解、解释和生成人类语言&#xff0c;使计…

入门二.HTB--Fawn

大佬 [Hackthebox] Fawn (FTP)_根据您的扫描,目标上运行的 ftp 版本是什么?-CSDN博客 大佬 https://www.cnblogs.com/Hekeats-L/p/16535561.html 任务一 FTP缩写 File Transfer Protocol 任务二 FTP的TCP端口 21 FTP监听的TCP端口号为21,监听的数据端口为20。 任务三 FTP…

Spring技术——介绍、初识

从这篇博客正式开始学习 Spring 。 一、整个Spring技术学习的介绍 首先先对 Spring 做一个简单的认识&#xff0c;并从以下3个方面了解学习Spring 技术的意义&#xff1a; &#xff08;1&#xff09;为什么要学习 Spring 技术&#xff1f; 1、它的市场的占有率很高&#xff…

css之浏览器兼容性

css之浏览器兼容性 内核 内核代表作品 兼容的概念和原因 Hack的副作用 常见的CSS Bug 多个图片在一起&#xff0c;图片套个盒子 图片在IE浏览器上有蓝色边框 解决办法&#xff1a;给img加上 border:0 表单元素中距离顶部元素不一样 左边input 右边button 解决办法&#x…

umijs脚手架

node 16.9.1 注意node版本的问题 node 18.20.0 这个问题其实是node与中端连接出错&#xff0c;无法初始化TTY&#xff08;终端设备&#xff09;&#xff0c;可以用cmd命令行来创建umi项目 nvm管理node https://github.com/coreybutler/nvm-windows/releases 这是nvm-window…

Idea Git中 unversioned files的处理

项目中&#xff0c;使用git commit命令可以查看当前所在的分支&#xff0c;以及当前改动的文件&#xff0c;可以使用快捷键Alt 0打开/关闭&#xff1b;如下图所示&#xff0c; 可以看到分成了两个不同的区域&#xff0c; Changes 表示有改动的文件&#xff0c;包括修改、新增…

Python基础语法学习(工程向)-Stage1

输出的方式&#xff1a; print(fabscwdasd {num}) print(asbduwiu %d, a) print(asnidoian %d %d %d,a,b,c)不换行 print(asbdiuabw,end )输入 a input(输入) 只能输入字符串形式&#xff0c;如果相当做数字用则将其转化为数字 只有合法的数字才能转化成功 a int(input()…

厂里资讯之异步通知文章上下架

kafka及异步通知文章上下架 1)自媒体文章上下架 需求分析 2)kafka概述 消息中间件对比 特性ActiveMQRabbitMQRocketMQKafka开发语言javaerlangjavascala单机吞吐量万级万级10万级100万级时效性msusmsms级以内可用性高&#xff08;主从&#xff09;高&#xff08;主从&#…

代码随想录算法训练营第41天|● 01背包问题 二维 ● 01背包问题 一维● 416. 分割等和子集

背包问题 二维01 背包 有n件物品和一个最多能背重量为w 的背包。第i件物品的重量是weight[i]&#xff0c;得到的价值是value[i] 。每件物品只能用一次&#xff0c;求解将哪些物品装入背包里物品价值总和最大。 对于背包问题&#xff0c;有一种写法&#xff0c; 是使用二维数…

Android网络性能监控方案 android线上性能监测

1 Handler消息机制 这里我不会完整的从Handler源码来分析Android的消息体系&#xff0c;而是从Handler自身的特性引申出线上卡顿监控的策略方案。 1.1 方案确认 首先当我们启动一个App的时候&#xff0c;是由AMS通知zygote进程fork出主进程&#xff0c;其中主进程的入口就是Ac…

Mysqld数据库管理

一.Mysqld数据库类型 常用的数据类型 int 整型 无符号[0-4294967296&#xff08;2的32次方&#xff09;-1]&#xff0c;有符号[-2147483648&#xff08;2的31次方&#xff09;-2147483647]float单精度浮点 4字节32位double双精度浮点 8字节64位char固定长度的字符类型…

Matlab基础篇:数据输入输出

前言 数据输入和输出是 Matlab 数据分析和处理的核心部分。良好的数据输入输出能够提高工作效率&#xff0c;并确保数据处理的准确性。本文将详细介绍 Matlab 数据输入输出的各种方法&#xff0c;包括导入和导出数据、数据处理和数据可视化。 一、导入数据 Matlab 提供了多种方…

计算机网络:1概述、2物理层

目录 概述因特网网络、互连网&#xff08;互联网&#xff09;与因特网的区别与关系因特网发展的三个阶段因特网服务提供者&#xff08;Internet Service Provider&#xff0c;ISP&#xff09;因特网的标准化工作因特网的管理结构 三种交换电路交换分组交换报文交换 计算机网络性…

Threejs-09、贴图的加载与环境遮蔽强度设置

1、创建文理加载器 let textureLoader new THREE.TextureLoader();2、加载贴图 // 加载文理 let texture textureLoader.load("./img/image.png") // 加载ao贴图 let aoMap textureLoader.load("./img/image.png");3、创建一个平面 let planeGeomet…

常见日志库NLog、log4net、Serilog和Microsoft.Extensions.Logging介绍和区别

在C#中&#xff0c;日志库的选择主要取决于项目的具体需求&#xff0c;包括性能、易用性、可扩展性等因素。以下是关于NLog、log4net、Serilog和Microsoft.Extensions.Logging的基本介绍和使用示例。 包含如何配置输出日志到当前目录下的log.txt文件及控制台的示例&#xff0c;…

springboot整合sentinel接口熔断

背景 请求第三方接口或者慢接口需要增加熔断处理&#xff0c;避免因为慢接口qps过大导致应用大量工作线程陷入阻塞以至于其他正常接口都不可用&#xff0c;最近项目测试环境就因为一个查询的慢接口调用次数过多&#xff0c;导致前端整个首页都无法加载。 依赖下载 springboo…

网络安全(完整)

WAPI鉴别及密钥管理的方式有两种&#xff0c;既基于证书和基于预共享密钥PSK。若采用基于证书的方式&#xff0c;整个国产包括证书鉴别、单播密钥协商与组播密钥通告&#xff1b;若采用预共享密钥方式&#xff0c;整个国产则为单播密钥协商与组播密钥通告蠕虫利用信息系统缺陷&…

React+TS前台项目实战(十)-- 全局常用组件CopyText封装

文章目录 前言CopyText组件1. 功能分析2. 代码详细注释3. 使用方式4. 效果展示 总结 前言 今天这篇主要讲项目常用复制文本组件封装&#xff0c;这个组件是一个用于拷贝文本的 React 组件&#xff0c;它提供了拷贝&#xff0c;国际化和消息提示的功能 CopyText组件 1. 功能分…

每日一题——Python实现PAT甲级1144 The Missing Number(举一反三+思想解读+逐步优化)四千字好文

一个认为一切根源都是“自己不够强”的INTJ 个人主页&#xff1a;用哲学编程-CSDN博客专栏&#xff1a;每日一题——举一反三Python编程学习Python内置函数 Python-3.12.0文档解读 目录 我的写法 时间复杂度分析 空间复杂度分析 总体空间复杂度&#xff1a;O(N) 总结 我…

端口映射工具下载?

天联是一款强大的端口映射工具&#xff0c;它能够帮助用户实现远程数据采集管理、异地统一管理、随时随地协同办公等多种场景的应用。无论您是医药、餐饮、商超等零售行业的企业&#xff0c;还是需要使用OA、CRM、ERP、财务进销存等系统的企业&#xff0c;甚至是使用视频监控设…