• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

嵌入式Linux项目Linux的全志H616开发板智能家居项目语音控制、人脸识别、安卓APP和PC端QT客户端远程操控有视频功能展示

武飞扬头像
阿龙还在写代码
帮助1

目录

一、功能需求

二、开发环境

1、硬件:

2、软件:

3、引脚分配:

三、关键点

1、设计模式之工厂模式

2、wiringPi库下的相关硬件操作函数调用

3、语音模块的串口通信

4、线程

5、摄像头的实时监控和拍照功能

6、人脸识别

四、编译和运行

五、视频功能展示


一、功能需求

  • 火焰传感器检测到火焰时,蜂鸣器报警,直到无火焰时停止报警
  • 语音控制多个LED灯的开和关(二楼灯、餐厅灯、客厅灯、卫生间灯)
  • 语音开启摄像头,并在对应IP地址的网页上实时监控画面
  • 语音控制摄像头拍摄照片,存在当前文件夹下(通过filezilla将照片传到PC上查看)
  • 语言开启人脸识别功能,将拍摄照片与本人照片对比,识别成功蜂鸣器滴一声,失败滴四声
  • 通过socket网络,实现开发板跑服务端,安卓手机跑客户端APP或PC上位机跑客户端,实现手机或上位机远程发送指令完成以上功能,并实时将温湿度传感器所测数据在安卓APP或PC上位机的QT界面上显示

二、开发环境

1、硬件:

Orangepi Zero2 全志H616开发板,语音模块SU-03T,摄像头模组OV9726,蜂鸣器,火焰传感器,4个LED等,4路继电器组,6v电源,若干杜邦线。

2、软件:

MobaXterm、VS Code、FileZilla

3、引脚分配:

在MobaXterm命令控制终端输入gpio readall可以查看开发板上的所有引脚。语音模块、蜂鸣器、火焰传感器和4路继电器组的引脚接线在下图框出。

学新通

由于加了温湿度传感器之后板子上引脚不够,所以在第二个视频演示时会把语言识别模块替换下来。温湿度传感器对应IO为,VCC--5V, GND--GND, DAT--3wPi。

三、关键点

1、设计模式之工厂模式

工厂模式提供了一种将对象的实例化过程封装在工厂类中的方式。本文通过使用工厂模式,将对象的创建与使用代码分离,提供一种统一的接口来创建不同类型的对象。在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象(链表)。

  1.  
    //1.指令工厂初始化
  2.  
    pCommandHead = addVoiceContrlToInputCommandLink(pCommandHead);//串口
  3.  
    pCommandHead = addSocketContrlToInputCommandLink(pCommandHead);
  4.  
     
  5.  
    //2.设备控制工厂初始化
  6.  
    //四个LED 火灾 蜂鸣器 摄像头
  7.  
    pdeviceHead = addBathroomLightToDeviceLink(pdeviceHead);
  8.  
    pdeviceHead = addUpstairLightToDeviceLink(pdeviceHead);
  9.  
    pdeviceHead = addRestaurantLightToDeviceLink(pdeviceHead);
  10.  
    pdeviceHead = addLivingroomLightToDeviceLink(pdeviceHead);
  11.  
    pdeviceHead = addFireToDeviceLink(pdeviceHead);
  12.  
    pdeviceHead = addBeepToDeviceLink(pdeviceHead);
  13.  
    pdeviceHead = addCameraToDeviceLink(pdeviceHead);

2、线程

主函数中创建了三个线程:语音线程、火焰检测线程、网络线程。

语音线程(voiceThread):完成串口的配置和初始化,在while循环里每隔0.3s检查串口是否有语音命令词到来,有则执行对应操作。

火焰检查线程(socketThread):while循环里每隔0.5s检测是否有火焰,有则蜂鸣器发出警报,知道无火焰。

网络线程(socketThread):接收客户端指令,实现上位机远程发送指令完成 [ 功能要求 ] 所述功能;给客户端发送温湿度传感器所测数据,实时显示在QT界面上。

  1.  
    //1、接收客户端的指令控制灯和摄像头 执行指令功能与语音控制复用
  2.  
    //2、向客户端发送温湿度数据
  3.  
    void *socket_thread(){
  4.  
    struct sockaddr_in c_addr;
  5.  
    memset(&c_addr,0,sizeof(struct sockaddr_in));
  6.  
    int clen = sizeof(struct sockaddr_in);
  7.  
     
  8.  
    socketHandler = findCommandByName("socketServer", pCommandHead);
  9.  
    if(socketHandler == NULL){
  10.  
    printf("find socketHandler error\n");
  11.  
    pthread_exit(NULL);
  12.  
    }
  13.  
    printf("%s init success\n", socketHandler->commandName);
  14.  
    socketHandler->Init(socketHandler, NULL, NULL);
  15.  
     
  16.  
    while(1){
  17.  
    c_fd = accept(socketHandler->sfd, (struct sockaddr *)&c_addr, &clen);
  18.  
    piThreadCreate(read_thread);
  19.  
    pthread_tempAndHumi_create();
  20.  
    }
  21.  
    }
学新通

 在网络线程中,首先对套接字进行初始化配置,包括IPV4因特网协议、TCP协议的配置(socket),绑定IP地址和端口号(bind),通过套接字标识符监听对应端口(listen)并等待客户端接入(accept)。

客户端接入后,即创建读数据线程,在while循环中进程阻塞在read函数,直到客户端发出指令,指令的执行函数与语音模块的语音指令执行函数复用(voiceContrlFunc函数在第4小点展示)。例如:在QT界面按下 [开客厅灯] 按钮,客户端通过网络发出字符串"OLL" ,在服务端read读到指令放入socketHandler->command,调用函数voiceContrlFunc(socketHandler->command)执行指令。

同时客户端在接入之后会调用函数pthread_tempAndHumi_create()创建发数据的线程,即实时发送温湿度的数据,QT中配合信号槽接收数据并在QT界面上显示,代码如下。

  1.  
    void *read_thread(){
  2.  
    while(1){
  3.  
    int n_read = 0;
  4.  
    memset(socketHandler->command, '\0', sizeof(socketHandler->command));
  5.  
    n_read = read(c_fd, socketHandler->command, sizeof(socketHandler->command));//n_read是读到字节数
  6.  
    voiceContrlFunc(socketHandler->command);
  7.  
    if(n_read == -1){
  8.  
    perror("read");
  9.  
    }
  10.  
    else if(n_read>0){
  11.  
    printf("\nget: %d, %s\n",n_read, socketHandler->command);
  12.  
    }
  13.  
    else{
  14.  
    printf("client quit\n");
  15.  
    break;
  16.  
    }
  17.  
    }
  18.  
    }
学新通

 QT程序,控制开关客厅灯的函数:

  1.  
    void Widget::on_livingRoomLight_clicked()
  2.  
    {
  3.  
    if(livingRoomLightFlag == 1){
  4.  
    //客户端向服务端发送消息
  5.  
    if(tcpSocket->state() == QAbstractSocket::ConnectedState){
  6.  
    ui->livingRoomLight->setText("关客厅灯");
  7.  
    tcpSocket->write("OLL");
  8.  
    ui->textBrowser->append("> 客厅灯已打开\n");
  9.  
    livingRoomLightFlag = 0;
  10.  
    }
  11.  
    else{
  12.  
    ui->textBrowser->append("请先与服务端连接!");
  13.  
    }
  14.  
    }
  15.  
    else{
  16.  
    if(tcpSocket->state() == QAbstractSocket::ConnectedState){
  17.  
    ui->livingRoomLight->setText("开客厅灯");
  18.  
    tcpSocket->write("CLL");
  19.  
    ui->textBrowser->append("> 客厅灯已关闭\n");
  20.  
    livingRoomLightFlag = 1;
  21.  
    }
  22.  
    else{
  23.  
    ui->textBrowser->append("请先与服务端连接!");
  24.  
    }
  25.  
    }
  26.  
    }
学新通

 QT程序中的信号槽连接和TCP网络读函数:

  1.  
    connect(tcpSocket, SIGNAL(readyRead()), this, SLOT(receiveMessages()));
  2.  
     
  3.  
    void Widget::receiveMessages()
  4.  
    {
  5.  
    QByteArray tmpByteArray = tcpSocket->readAll();
  6.  
    char* tempHumi;
  7.  
    tempHumi = tmpByteArray.data();
  8.  
    ui->tempLabel->setText(QString::number(tempHumi[0]));
  9.  
    ui->humiLabel->setText(QString::number(tempHumi[2]));
  10.  
    }

 学新通

                                                                   PC端QT界面

学新通

                                                                         APP界面

3、wiringPi库下的相关硬件操作函数调用

包括wiringPi库的初始化,蜂鸣器、火焰传感器、继电器组的输入输出引脚配置和高低电平设置。wiringP库下的串口配置和初始化。

4、语音模块的串口通信

本文用的是全志H616芯片中的串口设备/dev/ttyS5,波特率115200,实现与语音模块的串口通信。语音模块收到我们发出的命令词,将命令词转化为16进制数据通过串口发送到开发板,在程序中完成对串口数据接收、存储、判断是哪种命令、执行对应命令的操作,包括开关二楼灯、餐厅灯、客厅灯、卫生间灯、开启摄像头、拍一张照片和人脸识别。

语音模块SU-03T需要烧入对应命令的SDK,本文配置的SDK是在智能公元/AI产品零代码平台上完成,免费的。

  1.  
    int get_voice_type(char *cmd)
  2.  
    {
  3.  
    if(!strcmp("OLL", cmd)) return OLL;
  4.  
    if(!strcmp("ORL", cmd)) return ORL;
  5.  
    if(!strcmp("OUL", cmd)) return OUL;
  6.  
    if(!strcmp("OBL", cmd)) return OBL;
  7.  
    if(!strcmp("CLL", cmd)) return CLL;
  8.  
    if(!strcmp("CRL", cmd)) return CRL;
  9.  
    if(!strcmp("CUL", cmd)) return CUL;
  10.  
    if(!strcmp("CBL", cmd)) return CBL;
  11.  
    if(!strcmp("OC" , cmd)) return OC ;
  12.  
    if(!strcmp("TAP", cmd)) return TAP;
  13.  
    if(!strcmp("OFR", cmd)) return OFR;
  14.  
    perror("voice recognition failure");
  15.  
    }
  16.  
     
  17.  
    void voiceContrlFunc(char *cmd){
  18.  
    switch(get_voice_type(cmd)){
  19.  
    case OLL://OLL ASCII对应的16进制4f 4c 4c
  20.  
    printf("open livingroom light\n");
  21.  
    struct Devices *tmpOpenLivingroomLight = findDeviceByName("livingroomLight", pdeviceHead);
  22.  
    tmpOpenLivingroomLight->open(tmpOpenLivingroomLight->pinNum);
  23.  
    break;
  24.  
    case ORL://ORL 4f 52 4c
  25.  
    printf("open restaurant light\n");
  26.  
    struct Devices *tmpOpenRestaurantLight = findDeviceByName("restaurantLight", pdeviceHead);
  27.  
    tmpOpenRestaurantLight->open(tmpOpenRestaurantLight->pinNum);
  28.  
    break;
  29.  
    case OUL://OUL 4f 55 4c
  30.  
    printf("open upstair light\n");
  31.  
    struct Devices *tmpOpenUpstairLight = findDeviceByName("upstairLight", pdeviceHead);
  32.  
    tmpOpenUpstairLight->open(tmpOpenUpstairLight->pinNum);
  33.  
    break;
  34.  
    case OBL://OBL 4f 42 4c
  35.  
    printf("open bathroom light\n");
  36.  
    struct Devices *tmpOpenBathroomLight = findDeviceByName("bathroomLight", pdeviceHead);
  37.  
    tmpOpenBathroomLight->open(tmpOpenBathroomLight->pinNum);
  38.  
    break;
  39.  
    case CLL://CLL 43 4c 4c
  40.  
    printf("close livingroom light\n");
  41.  
    struct Devices *tmpCloseLivingroomLight = findDeviceByName("livingroomLight", pdeviceHead);
  42.  
    tmpCloseLivingroomLight->close(tmpCloseLivingroomLight->pinNum);
  43.  
    break;
  44.  
    case CRL://CRL 43 52 4c
  45.  
    printf("close restaurant light\n");
  46.  
    struct Devices *tmpCloseRestaurantLight = findDeviceByName("restaurantLight", pdeviceHead);
  47.  
    tmpCloseRestaurantLight->close(tmpCloseRestaurantLight->pinNum);
  48.  
    break;
  49.  
    case CUL://CUL 43 55 4c
  50.  
    printf("close upstair light\n");
  51.  
    struct Devices *tmpCloseUpstairLight = findDeviceByName("upstairLight", pdeviceHead);
  52.  
    tmpCloseUpstairLight->close(tmpCloseUpstairLight->pinNum);
  53.  
    break;
  54.  
    case CBL://CBL 43 42 4c
  55.  
    printf("close bathroom light\n");
  56.  
    struct Devices *tmpCloseBathroomLight = findDeviceByName("bathroomLight", pdeviceHead);
  57.  
    tmpCloseBathroomLight->close(tmpCloseBathroomLight->pinNum);
  58.  
    break;
  59.  
    case OC://OC 4f 43
  60.  
    printf("open camera\n");
  61.  
    printf(" -------------------------------------------------------------------\n");
  62.  
    printf(" --\033[1;32m 已开启摄像头,请到指定网页观看画面 https//192.168.43.206:8081 \033[0m--\n");//黄色字体
  63.  
    printf(" -------------------------------------------------------------------\n");
  64.  
    printf("\n");
  65.  
    break;
  66.  
    case TAP://TAP 54 41 50
  67.  
    printf("take a picture\n");
  68.  
    struct Devices *tmpTakeAPictureCamera = findDeviceByName("camera", pdeviceHead);
  69.  
    tmpTakeAPictureCamera->takeAPicture();
  70.  
    printf(" --------------------------------------\n");
  71.  
    printf(" --\033[1;32m 已拍照,请在当前文件夹下查看照片 \033[0m--\n");//黄色字体
  72.  
    printf(" --------------------------------------\n");
  73.  
    printf("\n");
  74.  
    break;
  75.  
    case OFR://OFR 4f 46 52
  76.  
    printf("open face recognition\n");
  77.  
    struct Devices *tmpFaceRecCamera = findDeviceByName("camera", pdeviceHead);
  78.  
    tmpFaceRecCamera->faceRecognition();
  79.  
    break;
  80.  
    }
  81.  
    }
学新通

学新通

5、摄像头的实时监控和拍照功能

6、人脸识别

通过命令词【人脸识别】让摄像头拍摄一张照片并与本地的本人照片做对比,成功则蜂鸣器滴一声,失败则滴四声。人脸识别的处理程序调用的是翔云OCR人脸识别 (netocr.com)的人脸识别API,该接口地址https://netocr.com/api/faceliu.do是https协议的地址,https协议是在http和tcp之间多添加了一层,进行身份验证和数据加密。

若要访问https协议的地址,则需要用到Libcurl这个跨平台的网络协议库,配合OpenSSL库,就可以访问https协议的接口。(编译OpenSSL支持Libcurl的https访问,如果直接编译Libcurl,只能访问http不能访问https,需要OpenSSL库才能访问https)

学新通

 camera.c

  1.  
    #include "contrlDevices.h"
  2.  
    #include <stdio.h>
  3.  
    #include <curl/curl.h>
  4.  
    #include <string.h>
  5.  
    #include <stdlib.h>
  6.  
    #include <sys/types.h>
  7.  
    #include <sys/stat.h>
  8.  
    #include <fcntl.h>
  9.  
    #include <unistd.h>
  10.  
     
  11.  
    extern struct Devices *pdeviceHead;//extern表面变量或函数是定义在其他文件中,声明 为全局变量在该源文件使用
  12.  
    struct Devices* findDeviceByName(char *name, struct Devices *phead);
  13.  
     
  14.  
    #define true 1
  15.  
    #define false 0
  16.  
    struct Devices camera;
  17.  
    char buf[1024] = {'\0'};//全局
  18.  
     
  19.  
    void cameraTakeAPicture(){
  20.  
    system("(fswebcam -d /dev/video0 --no-banner -r 1280x720 -S 5 ./image.jpg) > tmpFile");//照片存放在当前目录下
  21.  
    }
  22.  
     
  23.  
    size_t readData(void *ptr, size_t size, size_t nmemb, void *stream){
  24.  
    strncpy(buf, ptr, 1024);
  25.  
    }
  26.  
     
  27.  
     
  28.  
    char *getPicBase64FromFile(char *filePath){
  29.  
    char *bufPic = NULL;
  30.  
    char cmd[128] = {'\0'};
  31.  
    sprintf(cmd, "base64 %s > tmpFile", filePath);
  32.  
    system(cmd);//图片的base64流数据存入tmpFile文件中
  33.  
     
  34.  
    int fd = open("./tmpFile", O_RDWR);
  35.  
    int filelen = lseek(fd, 0, SEEK_END);
  36.  
    lseek(fd, 0, SEEK_SET);//重新让文件的光标回到初始位置
  37.  
    bufPic = (char *)malloc(filelen 2);// 1也可以 多加点没毛病
  38.  
    memset(bufPic, '\0', filelen 2);
  39.  
    read(fd, bufPic, filelen);
  40.  
    close(fd);
  41.  
    system("rm -f tmpFile");
  42.  
    return bufPic;
  43.  
    }
  44.  
     
  45.  
    void cameraFaceRecognition(){
  46.  
    camera.takeAPicture();
  47.  
    CURL *curl;
  48.  
    CURLcode res;
  49.  
    char *postString;
  50.  
    char *img1;
  51.  
    char *img2;
  52.  
    char *key = "DYRrmZz2rTwYGywyWdhKzR";
  53.  
    char *secret = "56bc8e083a9b4d9fbf590413ddcb3a61";
  54.  
    int typeId = 21;
  55.  
    char *format = "xml";
  56.  
    char *bufPic1 = getPicBase64FromFile("./image.jpg");
  57.  
    char *bufPic2 = getPicBase64FromFile("./zyl.jpg");
  58.  
     
  59.  
    int len = strlen(key) strlen(secret) strlen(bufPic1) strlen(bufPic2) 128;
  60.  
    postString = (char *)malloc(len);
  61.  
    memset(postString, '\0', len);//sizeof(postString)替换成len,因为postString是指针
  62.  
    sprintf(postString, "&img1=%s&img2=%s&key=%s&secret=%s&typeId=%d&format=%s", bufPic1, bufPic2, key, secret, 21, format);//拼接字符串
  63.  
     
  64.  
     
  65.  
    curl = curl_easy_init();
  66.  
    if (curl){
  67.  
    curl_easy_setopt(curl, CURLOPT_COOKIEFILE, "/tmp/cookie.txt"); // 指定cookie文件
  68.  
    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postString); // 指定post内容
  69.  
    curl_easy_setopt(curl, CURLOPT_URL,"https://netocr.com/api/faceliu.do"); // 指定url
  70.  
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, readData);//当有数据回来 调用回调函数
  71.  
    res = curl_easy_perform(curl);
  72.  
     
  73.  
    struct Devices *beepHandler = findDeviceByName("beep", pdeviceHead);
  74.  
    if(strstr(buf, "是") != NULL){
  75.  
    beepHandler->open(beepHandler->pinNum); usleep(300000);
  76.  
    beepHandler->close(beepHandler->pinNum);
  77.  
    printf("\n");
  78.  
    printf(" -----------------------------------\n");
  79.  
    printf(" --\033[1;32m 人脸识别成功: the same person \033[0m--\n");//绿色字体
  80.  
    printf(" -----------------------------------\n");
  81.  
    printf("\n");
  82.  
    }
  83.  
    else{
  84.  
    int i = 4;
  85.  
    while(i--){
  86.  
    beepHandler->open(beepHandler->pinNum); usleep(200000);
  87.  
    beepHandler->close(beepHandler->pinNum); usleep(100000);
  88.  
    }
  89.  
    printf("\n");
  90.  
    printf(" ------------------------------------\n");
  91.  
    printf(" --\033[1;31m 人脸识别失败: different person \033[0m--\n");//红色字体
  92.  
    printf(" ------------------------------------\n");
  93.  
    printf("\n");
  94.  
    }
  95.  
    curl_easy_cleanup(curl);
  96.  
    }
  97.  
    }
  98.  
     
  99.  
    //实例化对象
  100.  
    struct Devices camera = {
  101.  
    .deviceName = "camera",
  102.  
     
  103.  
    .takeAPicture = cameraTakeAPicture,
  104.  
    .faceRecognition = cameraFaceRecognition
  105.  
     
  106.  
    };
  107.  
     
  108.  
    struct Devices* addCameraToDeviceLink(struct Devices *phead){
  109.  
    if(phead == NULL){
  110.  
    return &camera;
  111.  
    }
  112.  
    else{//头插
  113.  
    camera.next = phead;
  114.  
    phead = &camera;
  115.  
    return phead;
  116.  
    }
  117.  
    }
学新通

7、qt程序跨平台运行(编译成安卓APP)

搭建环境所需的安装包:

学新通

四、编译和运行

编译时需要用到一些库文件和该库文件里的头文件,用到温湿度传感器时加上tempAndHumi.c

gcc bathroomLight.c livingroomLight.c restaurantLight.c upstairLight.c socketContrl.c voiceContrl.c fireDetection.c beep.c camera.c usartContrl.c main.c -I ../httpHandler/curl-7.71.1/_install/include/ -L ../httpHandler/curl-7.71.1/_install/lib/ -lcurl -lwiringPi -lpthread

运行:

sudo ./a.out

五、视频功能展示

           智能家居功能展示

        PC端QT界面功能展示

            安卓app功能展示

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhgbkgkf
系列文章
更多 icon
同类精品
更多 icon
继续加载