网络摄像头(Webcam)是一种可以通过网络实时传输视频和音频信号的摄像设备。它与传统摄像头的核心区别在于​​内置了数字化和网络化的能力​​,可以直接接入网络(通常通过Wi-Fi或网线),将采集到的画面传输到互联网上的其他设备或平台,无需连接电脑主机。而远端用户可在 PC 上使用标准的网络浏览器,根据网络摄像机的 IP 地址,对网络摄像机进行访问,实时监控目标现场的情况,并可对图像资料实时编辑和存储,同时还可以控制摄像机的云台和镜头,进行全方位地监控。本章的实验是以网络调试助手作为客户端,开发板作为服务器。服务器把摄像头处理的数据使用网卡发送至服务器当中,并且在服务器实时更新图像。
        本章分为如下几个部分:
56.1 ATK-MC5640&MC2640 简介
56.2 硬件设计
56.3 软件设计
56.4 下载验证

56.1 ATK-MC5640&MC2640 简介

        本实验支持原子 5640 和 2640 模块,这两个模块的相关资料可在原子提供的《ATK-MC2640 模块用户手册_V1.1》和《ATK-MC5640 模块用户手册 V1.0》用户手册查看。

56.2 硬件设计

1. 例程功能
        本章实验功能简介:开发板主控芯片通过 SCCB 协议对ATK-MC2640 模块中的摄像头传感器进行配置等通讯,并通过 CAMERA 接口获取ATK-MC2640 模块输出的 JPEG 图像数据,然后将获取到的图像实时的发往至UartDisplay这个软件(使用原子的 ATK-XCAM 2.4 软件,收到一帧数据后会出现闪退,系统是Win11 )。

2. 硬件资源
        1) LED 灯
                LED-IO1
        2) XL9555
                IIC_INT-IO0(需在 P5 连接 IO0)
                IIC_SDA-IO41
                IIC_SCL-IO42
        3) SPILCD
                CS-IO21
                SCK-IO12
                SDA-IO11
                DC-IO40(在 P5 端口,使用跳线帽将 IO_SET 和 LCD_DC 相连)
                PWR- IO1_3(XL9555)
                RST- IO1_2(XL9555)
        4) CAMERA
                OV_SCL-IO38
                OV_SDA- IO39
                VSYNC- IO47
                HREF- IO48
                PCLK- IO45
                D0- IO4
                D1- IO5
                D2- IO6
                D3- IO7
                D4- IO15
                D5- IO16
                D6- IO17
                D7- IO18
                RESET-IO0_5(XL9555)
                PWDN-IO0_4(XL9555)
3. 原理图
        CAMERA 接口与 ESP32-S3 的连接关系,如下图所示:

图 56.2.1 CAMERA 接口与 ESP32-S3 的连接电路图

56.3 软件设计
56.3.1 程序流程图

        本实验的程序流程图:

图 56.3.1.1 程序流程图

56.3.2 程序解析

        在本章节中,主要关注两个文件: lwip_demo.c 和 lwip_demo.h。 lwip_demo.h 文件主要声明了 lwip_demo 函数。主要关注点是 lwip_demo.c文件中的函数。在 lwip_demo 函数中,配置了相关的 TCPClient 参数,并创建了一个名为lwip_send_thread 的发送数据线程。这个线程通过调用 scokec 函数来发送数据到服务器。接下来,将分别详细解释 lwip_demo 函数和lwip_send_thread 任务。

/* 需要自己设置远程IP地址,电脑IP地址 */
#define IP_ADDR   "192.168.1.2"

#define LWIP_DEMO_RX_BUFSIZE         128                        /* 最大接收数据长度 */
#define LWIP_DEMO_PORT               8080                       /* 连接的本地端口号 */
#define LWIP_SEND_THREAD_PRIO        10                         /* 发送数据线程优先级 */
/* 接收数据缓冲区 */
char g_lwip_demo_recvbuf[LWIP_DEMO_RX_BUFSIZE]; 

/* 数据发送标志位 */
uint8_t g_lwip_send_flag;
int g_sock = -1;
int g_lwip_connect_state = 0;
static void lwip_send_thread(void *arg);


/**
 * @brief       发送数据线程
 * @param       无
 * @retval      无
 */
void lwip_data_send(void)
{
    xTaskCreate(lwip_send_thread, "lwip_send_thread", 2*1024, NULL, LWIP_SEND_THREAD_PRIO, NULL);
}

/**
 * @brief       lwip_demo实验入口
 * @param       无
 * @retval      无
 */
void lwip_demo(void)
{
    int err;
    struct sockaddr_in atk_client_addr;
    int recv_data_len;
    char *tbuf;
    char host_ip[] = IP_ADDR;
    lwip_data_send();                                           /* 创建发送数据线程 */
    
    while (1)
    {
sock_start:
        g_lwip_connect_state = 0;
        inet_pton(AF_INET, host_ip, &atk_client_addr.sin_addr);
        atk_client_addr.sin_family = AF_INET;                   /* 表示IPv4网络协议 */
        atk_client_addr.sin_port = htons(LWIP_DEMO_PORT);       /* 端口号 */
        g_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);               /* 可靠数据流交付服务既是TCP协议 */
        memset(&(atk_client_addr.sin_zero), 0, sizeof(atk_client_addr.sin_zero));
        
        tbuf = malloc(200);                                     /* 申请内存 */
        sprintf((char *)tbuf, "Port:%d", LWIP_DEMO_PORT);       /* 客户端端口号 */
        spilcd_show_string(5, 170, 200, 16, 16, tbuf, MAGENTA);
        
        /* 连接远程IP地址 */
        err = connect(g_sock, (struct sockaddr *)&atk_client_addr, sizeof(atk_client_addr));

        if (err == -1)
        {
            spilcd_show_string(5, 190, 200, 16, 16, "State:Disconnect", MAGENTA);
            free(tbuf);
            goto sock_start;
        }

        spilcd_show_string(5, 190, 200, 16, 16, "State:Connection", MAGENTA);
        g_lwip_connect_state = 1;
        
        while (1)
        {
            recv_data_len = recv(g_sock,g_lwip_demo_recvbuf,
                                sizeof(g_lwip_demo_recvbuf) - 1,0);
            if (recv_data_len <= 0)
            {
                g_lwip_connect_state = 0;
				close(g_sock);
                g_sock = -1;
                ESP_LOGE("TAG", "recv failed: errno %d", errno);
                break;
            }
            else
            {
               g_lwip_demo_recvbuf[recv_data_len] = 0;
               ESP_LOGI("TAG", "Received %d bytes from %s:", recv_data_len, host_ip);
               ESP_LOGI("TAG", "%s", g_lwip_demo_recvbuf);
           }
        }
    }
}

/**
 * @brief       发送数据线程函数
 * @param       pvParameters : 传入参数(未用到)
 * @retval      无
 */
void lwip_send_thread(void *pvParameters)
{
    pvParameters = pvParameters;
    camera_fb_t *camera_frame = NULL;
    
    while (1)
    {
        if (g_lwip_connect_state == 1 && g_sock >= 0) /* 有数据要发送 */
        {
            camera_frame = esp_camera_fb_get();
			if (camera_frame)
			{
				int sent = send(g_sock, camera_frame->buf, camera_frame->len,0);
				if (sent <= 0) 
				{
                    g_lwip_connect_state = 0;  // 发送失败时重置连接状态
                }
			}
            esp_camera_fb_return(camera_frame);
        }
        vTaskDelay(pdMS_TO_TICKS(1));
    }
}

        首先创建了一个用于发送 ESP32-S3 设备数据的任务。然后,对 TCPClient进行网络参数配置,并调用 connect 函数来建立与远程服务器的连接。当连接成功时,系统将进入接收轮询任务。如果出现断开连接的情况,系统将尝试重新连接服务器。在发送线程中,发送数据前会检查连接标志位。如果标志位有效,则通过esp_camera_fb_get获取图像信息,然后使用Send函数发送摄像头图像数据给电脑。

56.4 下载验证

        在程序中,首先需要设置好能够连接的WIFI账号(DEFAULT_SSID宏)、密码(DEFAULT_PWD宏)及远程IP(IP_ADDR)信息。然后,使用笔记本电脑作为终端,确保它与 ESP32-S3 设备处于同一网络段内。当 ESP32-S3 设备成功连接到网络时,它的 LCD 显示屏上会显示相应的内容:

图 56.4.1 设备连接到网络时, LCD 显示的信息

        打开视频传输上位机,然后配置网络参数,如 TCPServer 协议、端口号等,最后点击连接,如下图所示。

图 56.4.2 视频传输上位机显示内容

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐