请选择 进入手机版 | 继续访问电脑版
查看: 992|回复: 0

[智能家居] 【智能家居挑战赛】基于IMX8嵌入式系统和单片机USB通信的...

[复制链接]
  • TA的每日心情
    开心
    2018-4-20 15:04
  • 签到天数: 8 天

    [LV.3]偶尔看看II

    49

    主题

    188

    帖子

    1

    金牌会员

    Rank: 6Rank: 6

    积分
    2946
    最后登录
    2023-7-24
    发表于 2022-3-17 10:20:42 | 显示全部楼层 |阅读模式
    项目概要:此系统功能有网络摄像头图像传输,温湿度数据采集,光照强度数据采集,大气压数据采集,网络摄像头图像远程监控,手机浏览器网页读取显示等功能,系统分为四个部分,第一部分为基于IMX8嵌入式系统的主服务器,
    负责功能为:-摄像头图像采集与UDP发送
    -基于TCP的HTML网页界面搭建
    -USBHID主机接收

    第二部分为基于单片机MCU核心板的采集模块,负责功能为
    -DHT11温湿度采集
    -光照强度模拟信号AD采集
    -大气压数字信号读取
    -USBHID从机发送,发送目标为第一部分的IMX8嵌入式系统

    第三部分为基于x86电脑的QT上位机,负责功能为
    -接收第一部分IMX8嵌入式系统的网络摄像头数据,协议为UDP
    -接收第一部分IMX8嵌入式系统的温湿度,光照强度,大气压数据并显示,协议同样为UDP

    第四部分为手机或x86电脑,使用浏览器访问第一部分IMX8嵌入式系统搭建的TCP-HTML网页界面并显示

    系统要点如下:
    -网络摄像头数据传输采用协议为UDP而非TCP,提高传输效率,不过可手动切换为TCP模式,使用UDP模式,由于x86PC端IP是固定的,因此UDP发送端报文只需要配置协商好的端口号,然后接收端打开该端口号的接收就可以进行大流量数据传输
    -HTML网页服务器只能采用TCP方式传输,无法使用UDP
    -USB通信协议为Custom HID,简化IMX8嵌入式系统部分编程复杂度,提高项目可实施性
    -IMX8嵌入式系统上有片内M4核,但是普适性不高,采用普适性更高的片外MCU+USB通信

    系统框图如下:



    运行效果,QT上位机界面部分:


    手机浏览器网页:



    实物连接图拍照:


    如图所示,IMX8嵌入式系统开发板既可以连接网线也可以通过无线WIFI进行通信。

    IMX8嵌入式系统代码,基于Unix系统编程,使用发行版系统为Debian10。
    UDP发送图片文件帧到x86PC:
    1. int UDP_Send_Picture(socklen_t socket_send , struct sockaddr_in addr , char* filename)
    2. {
    3.     struct Package
    4.     {
    5.         int length;
    6.         char data[UDP_FRAME_LEN];
    7.         int fin;
    8.     }picture;

    9.     socklen_t addr_len = sizeof(struct sockaddr_in);

    10.     FILE *fp;
    11.     fp = fopen(filename, "rb+");
    12.     printf("fp = %d\\n",fp);
    13.     fseek(fp, 0, SEEK_END);
    14.     int fend = ftell(fp);
    15.     fseek(fp, 0, 0);
    16.     int sendbytes;
    17.     printf("fend = %d\\n",fend);
    18.     while(fend > 0)
    19.     {
    20.         memset(picture.data, 0, sizeof(picture.data));
    21.         fread(picture.data, UDP_FRAME_LEN, 1, fp);
    22.         if(fend >= UDP_FRAME_LEN)
    23.         {
    24.             picture.length = UDP_FRAME_LEN;
    25.             picture.fin = 0;
    26.         }
    27.         else
    28.         {
    29.             picture.length = fend;
    30.             picture.fin = 1;
    31.         }

    32.         //printf("sendbytes = %d \\n",sendbytes);

    33.         sendbytes = sendto(socket_send, (char *)&picture, sizeof(struct Package), 0, (struct sockaddr*)&addr,addr_len);

    34.         if(sendbytes == -1)
    35.         {
    36.             printf("Send Picture Failed!d\\n");
    37.             return -1;
    38.         }
    39.         else
    40.         {
    41.             fend -= UDP_FRAME_LEN;
    42.         }
    43.     }
    44.     fclose(fp);
    45.     return 0;
    46. }
    复制代码

    建立UDP发送套接字:
    1. int UDP_Send_Found(socklen_t* socket_found , struct sockaddr_in *addr , char* ip , int port)
    2. {
    3.     *socket_found = socket(AF_INET, SOCK_DGRAM, 0);

    4.     if(*socket_found == (~0))
    5.     {
    6.         printf("Create udp send socket failed!\\n");
    7.         return -1;
    8.     }

    9.     addr->sin_family = AF_INET;
    10.     addr->sin_addr.s_addr = inet_addr(ip);
    11.     addr->sin_port = htons(port);
    12.     memset(addr->sin_zero, 0, 8);
    13.     return 0;
    14. }
    复制代码

    抓取V4L2 USB摄像头图像帧并保存在缓存中:
    1. typedef struct
    2. {
    3.     void *start;
    4.     unsigned int length;
    5. } buffer;
    复制代码
    1. int V4l2_Grab()
    2. {
    3.     unsigned int n_buffers;

    4.     buffers = (buffer* )malloc(req.count*sizeof(*buffers));
    5.     if (buffers == NULL)
    6.     {
    7.         printf ("Out of memory\\n");
    8.         return 0;
    9.     }

    10.     for (n_buffers = 0; n_buffers < req.count; n_buffers++)
    11.     {
    12.         buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    13.         buf.memory = V4L2_MEMORY_MMAP;
    14.         buf.index = n_buffers;
    15.         //query buffers
    16.         if (ioctl (fd_video,VIDIOC_QUERYBUF, &buf) == -1)
    17.         {
    18.             printf("query buffer error\\n");
    19.             return(0);
    20.         }

    21.         buffers[n_buffers].length = buf.length;
    22.         buffers[n_buffers].start = mmap(NULL,buf.length,PROT_READ |PROT_WRITE, MAP_SHARED,
    23.         fd_video, buf.m.offset);
    24.         if (buffers[n_buffers].start == MAP_FAILED)
    25.         {
    26.             printf("buffer map error\\n");
    27.             return 0;
    28.         }
    29.     }

    30.     for (n_buffers = 0; n_buffers < req.count; n_buffers++)
    31.     {
    32.         buf.index = n_buffers;
    33.         ioctl(fd_video, VIDIOC_QBUF, &buf);
    34.     }

    35.     type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    36.     ioctl (fd_video, VIDIOC_STREAMON, &type);

    37.     ioctl(fd_video, VIDIOC_DQBUF, &buf);

    38. //    printf("grab yuyv OK!\\n");
    39.     return 1;
    40. }
    复制代码
    1. int Yuyv_2_RGB888(buffer* input_buffers,unsigned char *output_buffer)
    2. {
    3.     int i,j,r1,g1,b1,r2,g2,b2;
    4.     unsigned char y1,y2,u,v;
    5.     unsigned char *pointer;

    6.     pointer =(unsigned char*)input_buffers[0].start;

    7.     for(i=0;i<IMAGEHEIGHT;i++)
    8.     {
    9.     for(j=0;j<IMAGEWIDTH/2;j++)
    10.     {
    11.     y1 = *( pointer + (i*IMAGEWIDTH/2+j)*4);
    12.     u  = *( pointer + (i*IMAGEWIDTH/2+j)*4 + 1);
    13.     y2 = *( pointer + (i*IMAGEWIDTH/2+j)*4 + 2);
    14.     v  = *( pointer + (i*IMAGEWIDTH/2+j)*4 + 3);

    15.     r1 = y1 + 1.042*(v-128);
    16.     g1 = y1 - 0.34414*(u-128) - 0.71414*(v-128);
    17.     b1 = y1 + 1.772*(u-128);

    18.     r2 = y2 + 1.042*(v-128);
    19.     g2 = y2 - 0.34414*(u-128) - 0.71414*(v-128);
    20.     b2 = y2 + 1.772*(u-128);

    21.     if(r1>255)
    22.     r1 = 255;
    23.     else if(r1<0)
    24.     r1 = 0;

    25.     if(b1>255)
    26.     b1 = 255;
    27.     else if(b1<0)
    28.     b1 = 0;

    29.     if(g1>255)
    30.     g1 = 255;
    31.     else if(g1<0)
    32.     g1 = 0;

    33.     if(r2>255)
    34.     r2 = 255;
    35.     else if(r2<0)
    36.     r2 = 0;

    37.     if(b2>255)
    38.     b2 = 255;
    39.     else if(b2<0)
    40.     b2 = 0;

    41.     if(g2>255)
    42.     g2 = 255;
    43.     else if(g2<0)
    44.     g2 = 0;

    45.     *(output_buffer + (i*IMAGEWIDTH/2+j)*6    ) = (unsigned char)r1;
    46.     *(output_buffer + (i*IMAGEWIDTH/2+j)*6 + 1) = (unsigned char)g1;
    47.     *(output_buffer + (i*IMAGEWIDTH/2+j)*6 + 2) = (unsigned char)b1;
    48.     *(output_buffer + (i*IMAGEWIDTH/2+j)*6 + 3) = (unsigned char)r2;
    49.     *(output_buffer + (i*IMAGEWIDTH/2+j)*6 + 4) = (unsigned char)g2;
    50.     *(output_buffer + (i*IMAGEWIDTH/2+j)*6 + 5) = (unsigned char)b2;
    51.     }
    52.     }
    53. //        printf("change to RGB OK! \\n");
    54.     free(input_buffers);
    55.     return 0;
    56. }
    复制代码

    主循环处理:
    1. while(1)
    2. {
    3.   V4l2_Grab();
    4.   if(buffers != NULL)
    5.   {
    6.      Yuyv_2_RGB888(buffers,frame_buffer_video);
    7.      Encode_Jpeg(frame_buffer_video , 640 , 480 , (char*)"/home/proj/1.jpg");
    8.      UDP_Send_Picture(socklen_udp_camera_send,sockaddr_udp_camera_send,(char*)"/home/proj/1.jpg");
    9.   }
    10. }
    复制代码

    建立HID枚举主设备并枚举特定VID/PID从设备(HID单片机):
    1. handle = hid_open(2020, 2020, NULL);
    2.     if (!handle)
    3.     {
    4.         printf("unable to open hidraw device\\n");
    5.         return -1;
    6.     }
    7.    
    8.     hid_set_nonblocking(handle, 1);
    9.     printf("open hidraw device\\n");
    复制代码

    USBHID接收并通过UDP发送,使用单独线程进行:
    1. pthread_t tid_udp_send;
    2. pthread_create(&tid_udp_send , NULL , Thread_UDP_Send , (void*)&t2a);
    复制代码
    1. void * Thread_UDP_Send(void *arg)
    2. {
    3.     thread2_args t2a = *(thread2_args*)arg;
    4.     int res , sendbytes;
    5.     socklen_t addr_len = sizeof(struct sockaddr_in);
    6.     uint8_t buf_usb_recv[64];
    7.     uint8_t buf_udp_send[UDP_FRAME_LEN];
    8.     while(1)
    9.     {
    10.         res = hid_read(handle, buf_usb_recv, 64);
    11.         if(res > 0)
    12.         {
    13.             printf("res = %d buf[2] = %d buf[3] = %d buf[4] = %d buf[5] = %d\\n",res ,
    14.             buf_usb_recv[2] , buf_usb_recv[3] , buf_usb_recv[4] , buf_usb_recv[5]);
    15.             buf_udp_send[0] = 0x12;
    16.             buf_udp_send[1] = 0x23;
    17.             buf_udp_send[2] = 0x34;
    18.             buf_udp_send[3] = 0x45;
    19.             buf_udp_send[4] = buf_usb_recv[2];
    20.             dht11_temp = buf_usb_recv[2];
    21.             buf_udp_send[5] = buf_usb_recv[3];
    22.             dht11_humi = buf_usb_recv[3];
    23.             buf_udp_send[6] = buf_usb_recv[4];
    24.             buf_udp_send[7] = buf_usb_recv[5];
    25.             adc_value = (buf_usb_recv[4] << 8) | buf_usb_recv[5];
    26.             sendbytes = sendto(t2a.fd, buf_udp_send, UDP_FRAME_LEN , 0, (struct sockaddr*)&t2a.addr , addr_len);
    27.         }
    28.     }
    29. }
    复制代码

    TCP-HTML网页的接收和发送采用两个单独线程进行:
    1. pthread_t tid_tcp_web_recv;
    2.     pthread_create(&tid_tcp_web_recv , NULL , Thread_TCP_Web_Recv , NULL);
    3.     pthread_create(&id_tcp_send , NULL , Thread_TCP_Web_Send , NULL);
    复制代码

    TCP发送线程:
    1. void * Thread_TCP_Web_Send(void *arg)
    2. {
    3.     int len,j;
    4.     float cpu_temp;
    5.     while(1)
    6.     {
    7.         if(flag_keep_alive && flag_post_once)
    8.         {
    9.             flag_post_once = 0;
    10.             if(flag_get)
    11.                 printf("收到客户端GET请求,服务器可进行一次POST操作\\n");
    12.             else if(flag_post)
    13.                 printf("收到客户端POST回应,服务器可进行一次POST操作\\n");

    14.             File_Read_All_Text((char*)HTML_FILE);
    15.             len_buf_html=File_Read_Length((char*)HTML_FILE);
    16.             printf("\\n\\nHTML文件长度:len_buf_html=%d\\n\\n",len_buf_html);
    17.             //cpu_temp = Get_CPU_Temp();
    18.             cpu_temp = 37.7;
    19.             printf("\\n\\nCPU温度:%f\\n\\n",cpu_temp);
    20.             buf_html[143]=(int)(cpu_temp) / 10 % 10 + \'0\',
    21.             buf_html[144]=(int)(cpu_temp) % 10 + \'0\',
    22.             buf_html[146]=(int)(cpu_temp * 10) % 10 + \'0\';

    23.             buf_html[167]=(int)(dht11_temp) / 10 % 10 + \'0\',
    24.             buf_html[168]=(int)(dht11_temp) % 10 + \'0\';

    25.             buf_html[189]=(int)(dht11_humi) / 10 % 10 + \'0\',
    26.             buf_html[190]=(int)(dht11_humi) % 10 + \'0\';   

    27.             //adc_value
    28.             buf_html[214]=(int)(adc_value) / 1000 % 10 + \'0\',
    29.             buf_html[215]=(int)(adc_value) / 100 %10 + \'0\';         
    30.             buf_html[216]=(int)(adc_value) / 10 % 10 + \'0\',
    31.             buf_html[217]=(int)(adc_value) % 10 + \'0\';   

    32.             a[31]=len_buf_html/10000%10+\'0\';
    33.             a[32]=len_buf_html/1000%10+\'0\';
    34.             a[33]=len_buf_html/100%10+\'0\';
    35.             a[34]=len_buf_html/10%10+\'0\';
    36.             a[35]=len_buf_html%10+\'0\';
    37.             for(len=0;a[len]!=\'\\0\';len++)
    38.                 putchar(a[len]);
    39.             putchar(\'\\n\');
    40.             send(fd_socket_conn,a,len,0);
    41.             for(len=0;buf_html[len]!=\'\\0\';len++);
    42.             send(fd_socket_conn,buf_html,len,0);
    43.             free(buf_html);
    44.             close(fd_socket_conn);
    45.         }
    46.         /*
    47.         bzero(sendbuf,100);
    48.         scanf("%s",sendbuf);

    49.         if(sendbuf[0]==\'1\')
    50.         {
    51.             if(thread_flag==0)
    52.             {
    53.                 thread_flag=1;
    54.                 pthread_create(&id2,NULL,Thread_CPU_Temp,NULL);
    55.                 if (pthread_mutex_init(&mut,NULL))
    56.                 {
    57.                     printf("互斥锁初始化失败\\n");
    58.                 }
    59.                 if (pthread_cond_init(&cond,NULL))
    60.                 {
    61.                     printf("cond初始化失败\\n");
    62.                 }
    63.             }
    64.         }
    65.         else if(sendbuf[0]==\'2\')
    66.         {
    67.             thread_pause();
    68.         }
    69.         else if(sendbuf[0]==\'3\')
    70.         {
    71.             thread_resume();
    72.         }
    73.         */
    74.     }
    75. }
    复制代码


    TCP接收线程:
    1. void * Thread_TCP_Web_Recv(void *arg)
    2. {
    3.     int i,j;
    4.     while(1)
    5.     {
    6.         fd_socket_conn = accept(socket_web_server , (struct sockaddr *)&sockaddr_in_conn , &addrsize);
    7.         if(fd_socket_conn < 0)
    8.         {
    9.             printf("服务器接听失败!\\n");
    10.             flag_keep_alive = 0;
    11.         }
    12.         else if(fd_socket_conn >= 0)
    13.         {
    14.             printf("已有客户端成功连接服务器!\\n");
    15.         }
    16.         bzero(recvbuf,1000);
    17.         recv(fd_socket_conn,recvbuf,1000,0);
    18.         printf("------\\n%s\\n------\\n",recvbuf);
    19.         if(recvbuf[0]==\'G\'&&recvbuf[1]==\'E\'&&recvbuf[2]==\'T\')
    20.         {
    21.             printf("客户端浏览器发出GET请求\\n");
    22.             flag_get = 1;
    23.             flag_post = 0;
    24.             flag_post_once = 1;
    25.         }
    26.         if(recvbuf[0]==\'P\'&&recvbuf[1]==\'O\'&&recvbuf[2]==\'S\'&&recvbuf[3]==\'T\')
    27.         {
    28.             printf("客户端浏览器发出POST回应\\n");
    29.             flag_get = 0;
    30.             flag_post = 1;
    31.             flag_post_once = 1;
    32.         }

    33.         for(i = 0;i <= HTTP_GET_DEFAULT_LEN ; i ++)
    34.         {
    35.             if(recvbuf[i]==\'H\'&&recvbuf[i+1]==\'T\'&&recvbuf[i+2]==\'T\'&&recvbuf[i+3]==\'P\'&&
    36.             recvbuf[i+4]==\'/\')
    37.             {
    38.                     printf("HTTP协议版本/类型:");
    39.                     for(j=i;j<=i+7;j++)
    40.                         putchar(recvbuf[j]);
    41.                             putchar(\'\\n\');
    42.             }

    43.             if(recvbuf[i]==\'H\'&&recvbuf[i+1]==\'o\'&&recvbuf[i+2]==\'s\'&&recvbuf[i+3]==\'t\')
    44.             {
    45.                     printf("主机地址及端口号:");
    46.                     for(j=i+6;j<=i+22;j++)
    47.                         putchar(recvbuf[j]);
    48.                             putchar(\'\\n\');
    49.             }
    50.             if(recvbuf[i]==\'C\'&&recvbuf[i+1]==\'o\'&&recvbuf[i+2]==\'n\'&&recvbuf[i+3]==\'n\'&&
    51.             recvbuf[i+4]==\'e\'&&recvbuf[i+5]==\'c\'&&recvbuf[i+6]==\'t\'&&recvbuf[i+7]==\'i\'&&
    52.             recvbuf[i+8]==\'o\'&&recvbuf[i+9]==\'n\')
    53.             {
    54.                     printf("连接方式:");
    55.                     for(j=i+12;j<=i+21;j++)
    56.                         putchar(recvbuf[j]);
    57.                     putchar(\'\\n\');
    58.                     flag_keep_alive = 1;
    59.             }
    60.             if(recvbuf[i]==\'l\'&&recvbuf[i+1]==\'e\'&&recvbuf[i+2]==\'d\'&&recvbuf[i+3]==\'_\'&&
    61.             recvbuf[i+4]==\'s\'&&recvbuf[i+5]==\'w\'&&recvbuf[i+6]==\'i\'&&recvbuf[i+7]==\'t\'&&
    62.             recvbuf[i+8]==\'c\'&&recvbuf[i+9]==\'h\'&&recvbuf[i+10]==\'=\')
    63.             {
    64.                 printf("检测到LED控制指令:\\n");
    65.                 if(recvbuf[i+11]==\'o\'&&recvbuf[i+12]==\'n\')
    66.                 {
    67.                     printf("LED开\\n");
    68.                     //LED_Control(1);
    69.                 }
    70.                 else if(recvbuf[i+11]==\'o\'&&recvbuf[i+12]==\'f\'&&recvbuf[i+13]==\'f\')
    71.                 {
    72.                     printf("LED关\\n");
    73.                     //LED_Control(0);
    74.                 }
    75.             }
    76.         }
    77.     }
    78. }
    复制代码


    Makefile:
    1. PROG_UDP_IMAGE = udpimage
    2. SRCS_UDP_IMAGE = main.cc camera.cc tcp_udp_app.cc lcd.cc hid.cc

    3. CC = g++

    4. CLEANFILES = $(PROG_MAIN)

    5. INCLUDES ?=
    6. CFLAGS += -ljpeg -lpthread -ludev
    7. LDFLAGS ?=


    8. all: $(PROG_UDP_IMAGE)

    9. $(PROG_UDP_IMAGE): $(SRCS_UDP_IMAGE)
    10.         $(CC) -o $@ $^ $(CFLAGS) $(LDFLAGS) $(INCLUDES)

    11. clean:
    12.         rm -f $(CLEANFILES)
    复制代码


    QT上位机应用程序:


    视频展示:





    来源: 【智能家居挑战赛】基于IMX8嵌入式系统和单片机USB通信的...
    今天心情不错
    回复

    使用道具 举报

    您需要登录后才可以回帖 注册/登录

    本版积分规则

    关闭

    站长推荐上一条 /4 下一条

    Archiver|手机版|小黑屋|恩智浦技术社区

    GMT+8, 2024-4-19 06:37 , Processed in 0.101352 second(s), 17 queries , MemCache On.

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表