可视化门禁---Linux/Qt+SqLite篇

day1 环境搭建|底层驱动

今日任务

Ubuntu 环境配置、SSH 连接开发板、Qt 交叉编译搭建

V4L2/ALSA/UART/GPIO 硬件调试、4 个底层 Demo、TCP/UDP 基础、多线程模板编写

今日必背 3 题 + 通用技能知识

  1. 交叉编译概念、嵌入式为何必须使用交叉编译?
  2. /dev、/sys、/proc 三个文件系统作用区别?
  3. 多线程互斥锁与条件变量解决的核心问题?
  4. Linux 基础:常用文件 / 目录操作命令、权限 chmod/chown、SSH 远程连接原理
  5. C++ 基础:变量、数据类型、指针基础概念

当日交付

交叉编译环境、四大硬件 Demo、多线程通用模板

小时拆分

09:00--10:30 Ubuntu 配置、SSH 连 T113-S3、环境变量

10:30--12:00 Qt 交叉编译工具链部署、开发板设备节点熟悉

14:00--15:30 V4L2/ALSA/UART/GPIO 硬件调试

15:30--17:00 写 4 个底层 Demo、TCP/UDP 基础代码

17:00--18:00 Linux 多线程、互斥锁、条件变量模板编写

19:00-20:30 常用文件 / 目录操作命令、权限 chmod/chown、SSH 远程连接原理、变量、数据类型、指针基础概念

21:00--21:30 记踩坑笔记

一、Ubuntu 配置、SSH 连 T113-S3、环境变量的踩坑点

1.1Ubuntu的ens33未启动

解决方法

  1. 配置/etc/netplan/01-network-manager-all.yaml
  2. 保存退出后执行sudo netplan apply---预期结果无反应重新刷新终端
  3. ip route show default---预期结果default via [自己的网关] dev ens33 proto static--这里以小米路由器的网关为例

其中 nano /etc/netplan/01-network-manager-all.yaml时可能会出现版本不兼容语法空格有问题等错误配置的时候一定要手敲空格*2不要按tab键

通用版yaml但是这里为了便于观看对其使用的是tab键

network:

version: 2

renderer: networkd

ethernets:

ens33:

addresses:

  • 192.168.31.100/24[你自己配置的静态ip]

gateway4: 192.168.31.1[自己的网关]

nameservers:

addresses:

  • 192.168.31.1[自己的网关]

  • 8.8.8.8

1.2SSH配置

问题描述:

---串口连接使用失败

---查看板子发现存在sshd

---尝试启动sshd失败

解决方法

覆盖原SSH 服务端的加密密钥文件后重新输入执行命令。

生成 RSA 密钥

ssh-keygen -t rsa -f /etc/ssh/ssh_host_rsa_key -N ""

生成 ECDSA 密钥

ssh-keygen -t ecdsa -f /etc/ssh/ssh_host_ecdsa_key -N ""

生成 ED25519 密钥

ssh-keygen -t ed25519 -f /etc/ssh/ssh_host_ed25519_key -N ""

1.3 No route to host的原因

排查步骤:

在虚拟中ping开发板 --- 显示ping不通

在Windows的cmd中ping开发板 --显示ping不通

----确认原因----同网段但 ping 不通,说明开发板和路由器的连接有问题,或者路由器开启了 AP 隔离。

解决步骤:

1.在开发板上ping路由官网

通过---开发板能上网,但路由器限制了设备间互访--this

不通过--开发板的 WiFi 连接异常,需要重新连接 WiFi

尝试解决步骤:

添加中间服务器Ngrok

1.2板子启动速度慢---tf卡抢时间因为内存大

解决方法

在env.txt内写入如下代码--适配新手

bootargs=console=ttyS0,115200 root=/dev/mmcblk0p1 rootwait rw bootcmd=run bootfromsd; run bootfromemmc; bootfromsd=setenv root /dev/mmcblk0p1; load mmc 0 0x41000000 zImage; load mmc 0 0x42000000 board.dtb; bootz 0x41000000 - 0x42000000; bootfromemmc=setenv root /dev/mmcblk1p1; load mmc 1 0x41000000 zImage; load mmc 1 0x42000000 board.dtb; bootz 0x41000000 - 0x42000000; bootdelay=3

1.3挂载上tf卡后的开机启动文件

#!/bin/sh

echo "------run rc.modules file-----"

cp /opt/ip /sbin

#export boot_partition=$(fw_printenv -n boot_partition 2>/dev/null)

if [ x${boot_partition} == xboot ];then

/sbin/insmod /lib/modules/5.4.61/usb-storage.ko

/sbin/insmod /lib/modules/5.4.61/sunxi_gpadc.ko

/sbin/insmod /lib/modules/5.4.61/gt9xxnew_ts.ko

echo 0x07090160 0x083F10F7 > /sys/class/sunxi_dump/write

/sbin/insmod /lib/modules/5.4.61/8723ds.ko

/sbin/insmod /lib/modules/5.4.61/8189fs.ko

/sbin/insmod /lib/modules/5.4.61/r8188eu.ko

ifconfig wlan0 up

sleep 1

wpa_supplicant -B -i wlan0 -c /etc/wpa_supplicant.conf -Dwext &

udhcpc -i wlan0

wpa_supplicant -Dnl80211 -iwlan0 -c /etc/wifi/wpa_supplicant.conf -B

amixer -D hw:audiocodec cset name='Headphone Switch' 1 && \

amixer -D hw:audiocodec cset name='Headphone volume' 25

ifconfig eth0 up

ip link set can0 up type can bitrate 500000

led flash

cd /opt

./led_flash &

cd /

run lvgl demo

lv_examples 0 &

export LD_LIBRARY_PATH='/usr/lib/arm-qt/lib:/usr/lib/arm-qt/plugins/platforms:/usr/lib/eyesee-mpp::/usr/lib/arm-qt/lib'

export OLDPWD='/root'

export PATH='/usr/sbin:/usr/bin:/sbin:/bin'

export PS1='\u@\h:\w\$ '

export PWD='/etc/init.d'

export QTDIR='/usr/lib/arm-qt'

export QT_BOOT='/usr/lib/arm-qt'

export QT_PLUGIN_PATH='/usr/lib/arm-qt/plugins'

export QT_QPA_FONTDIR='/usr/lib/arm-qt/lib/fonts'

export QT_QPA_GENERIC_PLUGINS='tslib:/dev/input/event2'

export QT_QPA_PLATFORM='linuxfb:tty=/dev/fb0'

export QT_QPA_PLATFORM_PLUGIN_PATH='/usr/lib/arm-qt/plugins'

export SHELL='/bin/sh'

export SHLVL='2'

export TERM='vt102'

export TSLIB_CALIBFILE='/etc/pointercal'

export TSLIB_CONFFILE='/etc/ts.conf'

export TSLIB_CONSOLEDEVICE='none'

export TSLIB_FBDEVICE='/dev/fb0'

export TSLIB_PLUGINDIR='/usr/lib/ts'

export TSLIB_TSDEVICE='/dev/input/event2'

export USER='root'

export boot_type='5'

export bt_mac=''

export disp_reserve='2457600,0x43f19000'

export mac_addr=''

export mbr_offset='1032192'

export partitions='mbr@ubi0_0:boot-resource@ubi0_1:env@ubi0_2:env-redund@ubi0_3:boot@ubi0_4:rootfs@ubi0_5:private@ubi0_6:rootfs_data@ubi0_7:UDISK@ubi0_8:'

export snum=''

export specialstr=''

export uboot_message='2018.05-g2a1965a-dirty(07/10/2022-02:50:57)'

export wifi_mac=''

echo 5555 > /sys/class/android_usb/android0/enable

/usr/bin/adbd &

fi

三、V4L2/ALSA/UART/GPIO 硬件调试

外设 引脚 功能 Linux 设备 / 内核号
指纹模块 UART2 TX: PE2RX: PE3 串口收发 /dev/ttyS2
三色 LED 补光 红: PE0绿: PE1蓝: PE4 GPIO 输出 内核号:红: 128绿: 129蓝: 132
继电器 PE5 GPIO 输出 内核号:133
USB 摄像头 USB 口 1 视频采集 /dev/video0
USB 音响 USB 口 2 音频输出 plughw:1,0
ADB 调试 Type-C1 OTG/ADB adb shell
串口调试 Type-C2 UART 日志 /dev/ttyS3

说明:全志 T113-S3 的 GPIO 内核号计算方式:组号*32 + 引脚号,PE 组为第 4 组,因此:

  • PE0 = 4*32 + 0 = 128
  • PE1 = 4*32 + 1 = 129
  • PE2 = 4*32 + 2 = 130
  • PE3 = 4*32 + 3 = 131
  • PE4 = 4*32 + 4 = 132
  • PE5 = 4*32 + 5 = 133

4 个硬件底层 Demo(V4L2/ALSA/UART/GPIO)

1. GPIO 底层 Demo(LED + 继电器)

gpio_demo.c

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>

#define LED_R   "/sys/class/gpio/gpio128/value"
#define LED_G   "/sys/class/gpio/gpio129/value"
#define LED_B   "/sys/class/gpio/gpio132/value"
#define RELAY   "/sys/class/gpio/gpio133/value"

// 写 GPIO
void gpio_write(const char *path, int val) {
    int fd = open(path, O_WRONLY);
    dprintf(fd, "%d", val);
    close(fd);
}

int main() {
    printf("GPIO 底层 Demo\n");

    // 红灯亮
    gpio_write(LED_R, 1);
    sleep(1);
    gpio_write(LED_R, 0);

    // 绿灯亮
    gpio_write(LED_G, 1);
    sleep(1);
    gpio_write(LED_G, 0);

    // 继电器开门 1 秒
    gpio_write(RELAY, 1);
    sleep(1);
    gpio_write(RELAY, 0);

    return 0;
}

UART 底层 Demo(指纹模块 UART2)

uart_demo.c

cpp 复制代码
#include <stdio.h>
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
#include <string.h>

int uart_open(const char *dev) {
    int fd = open(dev, O_RDWR | O_NOCTTY);
    struct termios opt;
    tcgetattr(fd, &opt);

    // 9600 8N1
    cfsetispeed(&opt, B9600);
    cfsetospeed(&opt, B9600);
    opt.c_cflag |= CS8;
    opt.c_cflag &= ~PARENB;
    opt.c_cflag &= ~CSTOPB;

    tcsetattr(fd, TCSANOW, &opt);
    return fd;
}

int main() {
    int fd = uart_open("/dev/ttyS2");
    char buf[32];

    // 发指令
    write(fd, "CHECK_FP\n", 8);
    printf("发送指纹查询指令\n");

    // 读数据
    read(fd, buf, sizeof(buf));
    printf("收到:%s\n", buf);

    close(fd);
    return 0;
}

V4L2 底层 Demo(摄像头采集一帧)

v4l2_demo.c

cpp 复制代码
#include <stdio.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/videodev2.h>

int main() {
    int fd = open("/dev/video0", O_RDWR);
    struct v4l2_capability cap;
    ioctl(fd, VIDIOC_QUERYCAP, &cap);

    printf("摄像头:%s\n", cap.card);
    close(fd);
    return 0;
}

ALSA 底层 Demo(播放音频)

alsa_demo.c

cpp 复制代码
#include <stdio.h>
#include <alsa/asoundlib.h>

int main() {
    snd_pcm_t *handle;
    int ret = snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK, 0);
    printf("ALSA 打开状态:%d\n", ret);
    snd_pcm_close(handle);
    return 0;
}

TCP / UDP 网络 Demo(可远程控制门禁)

1. TCP 服务端 Demo

tcp_server.c

cpp 复制代码
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>

int main() {
    int server_fd = socket(AF_INET, SOCK_STREAM, 0);
    struct sockaddr_in addr = {
        AF_INET, htons(8080), INADDR_ANY
    };
    bind(server_fd, (struct sockaddr*)&addr, sizeof(addr));
    listen(server_fd, 5);

    printf("TCP 监听 8080\n");
    int client_fd = accept(server_fd, NULL, NULL);

    char buf[16];
    read(client_fd, buf, sizeof(buf));
    printf("收到:%s\n", buf);

    close(client_fd);
    close(server_fd);
    return 0;
}

UDP Demo

udp_demo.c

#include <stdio.h>

#include <sys/socket.h>

#include <netinet/in.h>

int main() {

int fd = socket(AF_INET, SOCK_DGRAM, 0);

struct sockaddr_in addr = {AF_INET, htons(8080), INADDR_ANY};

bind(fd, (struct sockaddr*)&addr, sizeof(addr));

char buf[16];

recvfrom(fd, buf, sizeof(buf), 0, NULL, NULL);

printf("UDP 收到:%s\n", buf);

close(fd);

return 0; }

多线程模板(门禁必备:视频 + 指纹 + 网络并行)

thread_demo.c

#include <stdio.h>

#include <pthread.h>

#include <unistd.h>

// 线程1:摄像头预览

void *cam_thread(void *arg) {

while (1) { printf("摄像头运行...\n"); sleep(1); } }

// 线程2:指纹识别

void *fp_thread(void *arg) {

while (1) { printf("指纹监听...\n"); sleep(1); }

}

// 线程3:TCP网络控制

void *tcp_thread(void *arg) {

while (1) { printf("网络监听...\n"); sleep(2); }

}

int main() {

pthread_t t1, t2, t3;

pthread_create(&t1, NULL, cam_thread, NULL);

pthread_create(&t2, NULL, fp_thread, NULL);

pthread_create(&t3, NULL, tcp_thread, NULL);

pthread_join(t1, NULL);

return 0;

}

编译命令(Tina ADB)

编译 GPIO

gcc gpio_demo.c -o gpio

编译 UART

gcc uart_demo.c -o uart

编译 V4L2

gcc v4l2_demo.c -o v4l2

编译 ALSA

gcc alsa_demo.c -o alsa -lasound

编译 TCP/UDP

gcc tcp_server.c -o tcp

gcc udp_demo.c -o udp

编译多线程

gcc thread_demo.c -o thread -pthread

第 2 天 人脸识别|指纹解析|QtUI

今日任务

OpenCV 嵌入式移植、V4L2 视频采集、Haar 人脸检测实现

指纹 UART 协议解析、Qt 门禁主界面开发、多线程解耦防 UI 卡顿

门禁数据库建模:设计管理员表、用户表字段结构,定义主键、账号唯一约束

今日必背 3 题+必备通用技能

  1. Qt 信号槽原理,跨线程操作 UI 的禁忌?
  2. V4L2 摄像头完整采集流程?
  3. 串口通信如何处理丢包、数据异常问题?
  4. Linux:进程查看 ps、kill、后台运行 &、nohup
  5. C++:引用、指针与引用区别

当日交付

人脸检测 Demo、指纹识别 Demo、Qt 门禁基础 UI、门禁管理员 + 用户表建表语句

一、 OpenCV 嵌入式移植

二、V4L2 视频采集

三、Haar 人脸检测实现

四、指纹 UART 协议解析

五、Qt 门禁主界面开发

六、多线程解耦防 UI 卡顿

七、交付物

人脸检测 Demo

指纹识别 Demo

Qt 门禁基础 UI

门禁管理员 + 用户表建表语句

门禁数据库建模:设计管理员表、用户表字段结构,定义主键、账号唯一约束

数据库选型:SQLite3 轻量嵌入式数据库 原因:无服务、单文件、占用资源极小、适配 Linux 嵌入式、掉电不损坏、无需部署,完美适配 T113-S3 硬件资源。核心实体:用户表、指纹表、人脸信息表、开门记录表、系统日志表、管理员表关系:管理员 --- 管理 --- 用户;用户 --- 绑定 --- 指纹 / 人脸;用户 --- 生成 --- 开门记录;系统自动生成 --- 日志

数据库描述

  • 数据库:SQLite3
  • 选型理由:轻量无服务、单文件存储、占用内存极小、适配嵌入式 Linux、无需安装部署、断电安全、支持事务,非常适合 T113-S3 资源受限的嵌入式设备。
  • 核心实体表:
    1. 管理员表 admin
    2. 用户表 user
    3. 人脸信息表 face_info
    4. 指纹信息表 fingerprint
    5. 开门记录表 open_record
    6. 系统日志表 system_log

门禁 SQLite3 实体关系

  1. 管理员 (1) → 管理 → 多用户 (多) 一对多
  2. 用户 (1) → 绑定 → 单条人脸信息 (1) 一对一
  3. 用户 (1) → 绑定 → 单条指纹信息 (1) 一对一
  4. 用户 (1) → 产生 → 多条开门记录 (多) 一对多
  5. 系统自动生成 → 多条系统日志

建表

sql 复制代码
-- 管理员表
CREATE TABLE admin (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    username TEXT NOT NULL UNIQUE,
    password TEXT NOT NULL,
    create_time DATETIME DEFAULT CURRENT_TIMESTAMP
);

-- 用户表
CREATE TABLE user (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    user_name TEXT NOT NULL,
    phone TEXT,
    password TEXT NOT NULL,
    status INTEGER DEFAULT 1,
    create_time DATETIME DEFAULT CURRENT_TIMESTAMP
);

-- 人脸信息表
CREATE TABLE face_info (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    user_id INTEGER UNIQUE NOT NULL,
    face_feature BLOB,
    create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY(user_id) REFERENCES user(id)
);

-- 指纹信息表
CREATE TABLE fingerprint (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    user_id INTEGER UNIQUE NOT NULL,
    finger_code TEXT NOT NULL,
    create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY(user_id) REFERENCES user(id)
);

-- 开门记录表
CREATE TABLE open_record (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    user_id INTEGER NOT NULL,
    open_type INTEGER NOT NULL, -- 1人脸 2指纹 3密码
    open_time DATETIME DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY(user_id) REFERENCES user(id)
);

-- 系统日志表
CREATE TABLE system_log (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    log_type INTEGER,
    log_content TEXT NOT NULL,
    log_time DATETIME DEFAULT CURRENT_TIMESTAMP
);

第 3 天 软键盘|权限开锁|状态机|MFC 抽奖 UI

今日任务

Qt 嵌入式软键盘开发、用户人脸 / 指纹 / 账号绑定、本地数据持久化

人脸 / 指纹 / 密码三种开锁逻辑、GPIO 继电器控制、超时容错、业务状态机完善

今日必背 3 题

  1. 嵌入式状态机设计的优势?
  2. 嵌入式设备本地数据持久化实现方式?
  3. MFC 消息映射机制原理?
  4. Linux:管道 |、重定向 > >>日志查看 tail
  5. C++:函数重载、默认参数

当日交付

三模式完整开锁功能、用户注册业务闭环

一、Qt 嵌入式软键盘开发

二、用户人脸 / 指纹 / 账号绑定

三、本地数据持久化

四、人脸 / 指纹 / 密码三种开锁逻辑

五、GPIO 继电器控制

六、超时容错

七、业务状态机完善

八、交付物

三模式完整开锁功能

用户注册业务闭环

----------------------------------晚上补齐,因为断电写的都没了啊啊啊啊啊啊--------------

相关推荐
初願致夕霞1 小时前
基于系统调用的Linux网络编程——UDP与TCP
linux·网络·c++·tcp/ip·udp
charlie1145141914 小时前
嵌入式Linux驱动开发——新 API 字符设备驱动完整教程 - 从设备结构体到应用测试
linux·运维·驱动开发
飞Link4 小时前
2000 亿砸向算力:字节跳动 AI 基建跨越,后端与运维的“万亿 Token”生死战
运维·人工智能
消失的旧时光-19434 小时前
C语言对象模型系列(四)《Linux 内核里的 container_of 到底是什么黑魔法?》—— 一篇讲透 Linux 内核的“对象模型”核心技巧
linux·c语言·算法
SWAGGY..5 小时前
Linux系统编程:(二)基础指令详解
linux·运维·服务器
kdxiaojie5 小时前
U-Boot分析【学习笔记】(3)
linux·笔记·学习
螺丝钉的扭矩一瞬间产生高能蛋白5 小时前
QT的C++接口基础用法
c++·qt·嵌入式软件·嵌入式linux·linux应用
烛衔溟5 小时前
TypeScript 接口继承与混合类型
linux·ubuntu·typescript
张文君6 小时前
上古世纪服务端编译安装AAEmu docker编译安装
运维·docker·容器