安信可ESP32-CAM摄像头开发demo--局域网拍照、实时视频、人脸识别

目录

一、前言

    ESP32-CAM是安信可发布小尺寸的摄像头模组。该模块可以作为最小系统独立工作,尺寸仅为2740.54.5mm。 ESP32-CAM可广泛应用于各种物联网场合,适用于家庭智能设备、工业无线控制、无线监控、人脸识别以及其它物联网应用,是物联网应用的理想解决方案。 ESP32-CAM采用DIP封装,直接插上底板即可使用,实现产品的快速生产,为客户提供高可靠性的连接方式,方便应用于各种物联网硬件终端场合

准备工作

  1. ESP_CAM模块开发板 链接
  2. 串口模块(USB-TTL)
  3. 杜邦线
  4. PC串口调试助手
  5. 硬件接线
  6. 请保证模块输入电源至少5V 2A,否则图片会有几率出现水纹。

二、环境搭建及编译

2.1 Linux环境安装

本文开发环境基于Linux,环境搭建步骤如下:

  1. 下载安装 VM 虚拟机,点我下载
    和谐码:FC7D0-D1YDL-M8DXZ-CYPZE-P2AY6(仅学习使用)
  2. 下载镜像,这里选择ubuntu桌面版18.04.4版本,点我下载
  3. 安装Ubuntu ,安装教程参考

我们还需要安装几个常用的软件 :

sudo apt-get purge vim-common
sudo apt-get install vim
sudo apt install git

然后安装一堆依赖环境

sudo apt-get install git wget flex bison gperf python python-pip python-setuptools python-serial python-click python-cryptography python-future python-pyparsing python-pyelftools cmake ninja-build ccache libffi-dev libssl-dev

2.2 代码获取

git clone --recurse-submodules https://github.com/Ai-Thinker-Open/Ai-Thinker-Open_ESP32-CAMERA_LAN.git

子模块拉取完后我们可以看见Ai-Thinker-Open_ESP32-CAMERA_LAN里面有个esp-idf,此版本为v4.0。

设置 IDF_PATH 路径
以我的环境为例:

export IDF_PATH=/mnt/hgfs/share/Ai-Thinker-Open_ESP32-CAMERA_LAN/esp-idf

之后按下 i 表示嵌入代码: vim ~/.bashrc
任意一处添加 表示嵌入上面代码!
按下esc 再 :wq 表示写入保存: source ~/.bashrc
IDF_PATH 路径测试是否设置成功: echo $IDF_PATH
除了 ESP-IDF 本身,您还需要安装 ESP-IDF 使用的各种工具,比如编译器、调试器、Python 包等!

cd /mnt/hgfs/share/Ai-Thinker-Open_ESP32-CAMERA_LAN/esp-idf    //以我的路径为例
./install.sh

出现这个 , 表示 安装成功
在这里插入图片描述
此时,您刚刚安装的工具尚未添加至 PATH 环境变量,无法通过“命令窗口”使用这些工具。因此,必须设置一些环境变量,这可以通过 ESP-IDF 提供的另一个脚本完成。
注意下面命令2个小数点中间有一个空格!

. ./export.sh

成功后,便这样提示:
在这里插入图片描述

2.3 编译工程

  1. 进入示例工程 esp_cam\examples\single_chip\camera_web_server
cd esp_cam\examples\single_chip\camera_web_server
  1. 设置menuconfig
make menuconfig

进入Serial flasher config设置如下
在这里插入图片描述

进入Camera Web Server —>Camera Pins —> Select Camera Pinout —> 选择ESP32-CAM by AI-Thinker

在这里插入图片描述
设置WIFI模式(station/ AP模式二选一即可)

  1. station模式
    进入camera_web> Camera Web Server > WiFi Settings,设置WIFI名字和密码
    在这里插入图片描述
    或者使用Smartconfig 配网 ,进入board config > smart config ,输入Y使能

在这里插入图片描述
2. AP模式
进入Camera Web Server > WiFi Settings,输入AP SSID、AP password、AP Channel、AP IP Address等
在这里插入图片描述
如果需要人脸识别功能,进入 Camera Web Server,选择ESP-WHO Face Detection,输入Y ,进入Detection Model,选择MTMN
在这里插入图片描述
如果需要LED灯补光,进入 Camera Web Server > LED Illuminator,选择LED Illuminator Enabled 输入Y使能
在这里插入图片描述

保存退出,编译下载

make    //编译
make flash  //下载
make monitor  //串口打印

三、建立局域网

station模式和AP模式建立局域网的步骤有所不同,根据自己配置的WIFI模式选择

3.1 station 模式

  1. 如果没有使能smartconfig,并且写入了WiFi的名字和密码,那么上电之后能自动连接WiFi
  2. 如果使能了smartconfig,则可以使用EspTouch进行smartconfig配网 ,EspTouch配网APP点我下载
    在这里插入图片描述
  3. 配好网后,可以在串口打印中看到模组分配到的IP地址,如上图,电脑或手机连接到同一个WiFi,在浏览器中输入模组的IP地址

3.2 AP模式

电脑或手机连接模组的热点,在浏览器中输入3.3章节中设置好的IP地址即可
在这里插入图片描述

四、拍照,视频流,人脸识别

4.1 拍照,视频流

电脑与模组建立局域网后,点击Get Still拍照,点击Start Stream拍视频,效果如下图:
在这里插入图片描述
点击右上角save可保存当前照片,点击Resolution设置分辨率,LED Intensity设置LED灯亮度

4.2 人脸检测、人脸识别

人脸检测和人脸识别会占用大量内存,不能使用大分辨率,需把分辨率设置为320x240
在这里插入图片描述
在这里插入图片描述

打开Face Detection即可实现人脸检测,打开Face Recognition实现人脸识别,点击Enroll Face采集人脸,采集成功显示如下图
在这里插入图片描述
当识别出采集过的人脸时,显示如下图
在这里插入图片描述

4.3 smartconfig配网后SSID,password保存

    截至2020/11/23 在sdk里面仅提供smartconfig示例,配网成功后并没有保存WiFi信息。这里提供smartconfig配网成功后保存SSID、Password的示例,配网成功后自动保存ssid和password,上电自动连接上一次连接的WiFi。

  1. 将app_board.c替换为下面的代码
#include "string.h"
#include "app_uart.h"
#include "app_board.h"
#include "app_sd.h"
#include "app_smart_wifi.h"
#include "app_camera.h"
#include "app_wifi.h"
#include "sdkconfig.h"
#include "nvs_flash.h"
#include <stdbool.h>
#include "esp_wifi.h"
#include "esp_log.h"
#include "esp_err.h"
#include "mdns.h"

static const char *TAG = "app_board";

wifi_config_t wifi_config;

void router_wifi_save_info(uint8_t *ssid, uint8_t *password);

void router_wifi_save_info(uint8_t *ssid, uint8_t *password)
{
    nvs_handle out_handle;
    char data[65];
    if (nvs_open("wifi_info", NVS_READWRITE, &out_handle) != ESP_OK)
    {
        return;
    }

    memset(data, 0x0, sizeof(data));
    strncpy(data, (char *)ssid, strlen((char *)ssid));
    if (nvs_set_str(out_handle, "ssid", data) != ESP_OK)
    {
        ESP_LOGI(TAG,"--set ssid fail");
    }

    memset(data, 0x0, sizeof(data));
    strncpy(data, (char *)password, strlen((char *)password));
    if (nvs_set_str(out_handle, "password", data) != ESP_OK)
    {
        ESP_LOGI(TAG,"--set password fail");
    }
    nvs_commit(out_handle);
    nvs_close(out_handle);
}

bool router_wifi_read()
{
    nvs_handle out_handle;
    size_t size = 0;
    nvs_open("wifi_info", NVS_READONLY, &out_handle);
    memset(&wifi_config, 0x0, sizeof(wifi_config));
    size = sizeof(wifi_config.sta.ssid);
    if(nvs_get_str(out_handle, "ssid", (char *)wifi_config.sta.ssid, &size) == ESP_OK)
    {
        if (size > 0)
        {
            size = sizeof(wifi_config.sta.password);
            if (nvs_get_str(out_handle, "password", (char *)wifi_config.sta.password, &size) == ESP_OK)
            {
                ESP_LOGI(TAG,"-- get ssid: %s", wifi_config.sta.ssid);
                ESP_LOGI(TAG,"-- get password: %s", wifi_config.sta.password);
                return 1;
            }
        }
    }
    nvs_close(out_handle);
    return 0;
}

static int s_retry_num = 0;
static esp_err_t wifi_handler(void *ctx, system_event_t *event)
{
    switch(event->event_id) {
    case SYSTEM_EVENT_AP_STACONNECTED:
        ESP_LOGI(TAG, "station:" MACSTR " join, AID=%d",
                 MAC2STR(event->event_info.sta_connected.mac),
                 event->event_info.sta_connected.aid);
        break;
    case SYSTEM_EVENT_AP_STADISCONNECTED:
        ESP_LOGI(TAG, "station:" MACSTR "leave, AID=%d",
                 MAC2STR(event->event_info.sta_disconnected.mac),
                 event->event_info.sta_disconnected.aid);
        break;
    case SYSTEM_EVENT_STA_START:
        esp_wifi_connect();
        break;
    case SYSTEM_EVENT_STA_GOT_IP:
        ESP_LOGI(TAG, "got ip:%s",
                 ip4addr_ntoa(&event->event_info.got_ip.ip_info.ip));
        s_retry_num = 0;
        break;
    case SYSTEM_EVENT_STA_DISCONNECTED:
        {
            if (s_retry_num < CONFIG_ESP_MAXIMUM_RETRY) {
                esp_wifi_connect();
                s_retry_num++;
                ESP_LOGI(TAG,"retry to connect to the AP");
            }
            ESP_LOGI(TAG,"connect to the AP fail");
            break;
        }
    default:
        break;
    }
    mdns_handle_system_event(ctx, event);
    return ESP_OK;
}

void wifi_connet(void )
{
    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    tcpip_adapter_init();
    ESP_ERROR_CHECK(esp_event_loop_init(wifi_handler, NULL));
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));
    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
    ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) );
    ESP_ERROR_CHECK(esp_wifi_start());
}


  void app_board_main(void)
 {
        app_camera_main();
#ifdef CONFIG_ESP_SMAERT_CONFIG
        if(router_wifi_read()==0){
            initialise_wifi();
        }
        else{
            wifi_connet();
        }
        
#else        
        app_wifi_main();
#endif
        uart_init();
        SdCard_init();
        
 }
  1. 将app_smart_wifi.c替换为一下代码
#include <string.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_wifi.h"
#include "esp_wpa2.h"
#include "esp_event.h"
#include "esp_log.h"
#include "esp_system.h"
#include "nvs_flash.h"
#include "tcpip_adapter.h"
#include "esp_smartconfig.h"

#include <time.h>
#include <sys/time.h>
#include "esp_sntp.h"
#include "sdkconfig.h"

extern void router_wifi_save_info(uint8_t *ssid, uint8_t *password);
/* FreeRTOS event group to signal when we are connected & ready to make a request */
static EventGroupHandle_t s_wifi_event_group;

/* The event group allows multiple bits for each event,
   but we only care about one event - are we connected
   to the AP with an IP? */
static const int CONNECTED_BIT = BIT0;
static const int ESPTOUCH_DONE_BIT = BIT1;
static const char *TAG = "smartconfig_example";

static void smartconfig_example_task(void * parm);

uint8_t ssid[33] = { 0 };
uint8_t password[65] = { 0 };

static void event_handler(void* arg, esp_event_base_t event_base, 
                                int32_t event_id, void* event_data)
{
    if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
        xTaskCreate(smartconfig_example_task, "smartconfig_example_task", 4096, NULL, 3, NULL);
    } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
        esp_wifi_connect();
        xEventGroupClearBits(s_wifi_event_group, CONNECTED_BIT);
    } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
        xEventGroupSetBits(s_wifi_event_group, CONNECTED_BIT);
    } else if (event_base == SC_EVENT && event_id == SC_EVENT_SCAN_DONE) {
        ESP_LOGI(TAG, "Scan done");
    } else if (event_base == SC_EVENT && event_id == SC_EVENT_FOUND_CHANNEL) {
        ESP_LOGI(TAG, "Found channel");
    } else if (event_base == SC_EVENT && event_id == SC_EVENT_GOT_SSID_PSWD) {
        ESP_LOGI(TAG, "Got SSID and password");

        smartconfig_event_got_ssid_pswd_t *evt = (smartconfig_event_got_ssid_pswd_t *)event_data;
        wifi_config_t wifi_config;

        bzero(&wifi_config, sizeof(wifi_config_t));
        memcpy(wifi_config.sta.ssid, evt->ssid, sizeof(wifi_config.sta.ssid));
        memcpy(wifi_config.sta.password, evt->password, sizeof(wifi_config.sta.password));
        wifi_config.sta.bssid_set = evt->bssid_set;
        if (wifi_config.sta.bssid_set == true) {
            memcpy(wifi_config.sta.bssid, evt->bssid, sizeof(wifi_config.sta.bssid));
        }

        memcpy(ssid, evt->ssid, sizeof(evt->ssid));
        memcpy(password, evt->password, sizeof(evt->password));
        ESP_LOGI(TAG, "SSID:%s", ssid);
        ESP_LOGI(TAG, "PASSWORD:%s", password);

        ESP_ERROR_CHECK( esp_wifi_disconnect() );
        ESP_ERROR_CHECK( esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) );
        ESP_ERROR_CHECK( esp_wifi_connect() );
    } else if (event_base == SC_EVENT && event_id == SC_EVENT_SEND_ACK_DONE) {
        xEventGroupSetBits(s_wifi_event_group, ESPTOUCH_DONE_BIT);
    }
}
void time_sync_notification_cb(struct timeval *tv)
{
    ESP_LOGI(TAG, "SNTP OK ");
}
static void initialize_sntp(void)
{
    ESP_LOGI(TAG, "Initializing SNTP");
    sntp_setoperatingmode(SNTP_OPMODE_POLL);
    sntp_setservername(0, "pool.ntp.org");
    sntp_set_time_sync_notification_cb(time_sync_notification_cb);
#ifdef CONFIG_SNTP_TIME_SYNC_METHOD_SMOOTH
    sntp_set_sync_mode(SNTP_SYNC_MODE_SMOOTH);
#endif
    sntp_init();
}

static void smartconfig_example_task(void * parm)
{
    EventBits_t uxBits;
    ESP_ERROR_CHECK( esp_smartconfig_set_type(SC_TYPE_ESPTOUCH) );
    smartconfig_start_config_t cfg = SMARTCONFIG_START_CONFIG_DEFAULT();
    ESP_ERROR_CHECK( esp_smartconfig_start(&cfg) );
    while (1) {
        uxBits = xEventGroupWaitBits(s_wifi_event_group, CONNECTED_BIT | ESPTOUCH_DONE_BIT, true, false, portMAX_DELAY); 
        if(uxBits & CONNECTED_BIT) {
            ESP_LOGI(TAG, "WiFi Connected to ap");
        }
        if(uxBits & ESPTOUCH_DONE_BIT) {
            ESP_LOGI(TAG, "smartconfig over");
            router_wifi_save_info(ssid,password);
#if CONFIG_ESP_SNTP_ENABLED
            {
            initialize_sntp();

            // wait for time to be set
            time_t now = 0;
            struct tm timeinfo = { 0 };
            int retry = 0;
            const int retry_count = 10;
            while (sntp_get_sync_status() == SNTP_SYNC_STATUS_RESET && ++retry < retry_count) {
                //ESP_LOGI(TAG, "Waiting for system time to be set... (%d/%d)", retry, retry_count);
                vTaskDelay(1000 / portTICK_PERIOD_MS);
            }
            
            time(&now);
            localtime_r(&now, &timeinfo);
            }
#endif
            esp_smartconfig_stop();
            vTaskDelete(NULL);
        }
    }
}

void initialise_wifi(void)
{
    tcpip_adapter_init();
    s_wifi_event_group = xEventGroupCreate();
    ESP_ERROR_CHECK(esp_event_loop_create_default());

    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK( esp_wifi_init(&cfg) );

    ESP_ERROR_CHECK( esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL) );
    ESP_ERROR_CHECK( esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL) );
    ESP_ERROR_CHECK( esp_event_handler_register(SC_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL) );

    ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) );
    ESP_ERROR_CHECK( esp_wifi_start() );
}

五、固件下载

这里有我编译好的固件,可提供大家测试使用:

  1. Smartconfig配网固件,点我下载
  2. AP模式固件,热点名字为AiThinker_Cam,IP地址为192.168.4.1,点我下载
已标记关键词 清除标记