【DIY】BW21-CBV-Kit: Face Recognition Based Access Control System

Introduction

This access control system based on face recognition displays the open and close status of the access on an OLED screen.

 

  1. Send R: Enter registration mode.
  2. In registration mode, send REG = Alice: to register facial information; send DEL = Alice: to delete registered facial information.
  3. Send Y: Save the registered facial information and switch back to recognition mode.
  4. After rebooting the device, it will directly enter recognition mode and load the pre - registered facial information.

Code

Open Arduino IDE, create a new project, and add the following code:

/*

 Access control system based on face recognition (the system starts in recognition mode)

To use this system:

1.Upload the code and open serial monitor

2.Starts directly in recognition mode

3.Serial input 'R' switches to registration mode

4.Register faces using "REG=Name" commands

5.When done, send "Y" to save and switch back to recognition mode

6.In recognition mode, the OLED will show OPEN/CLOSE status

*/

#include "WiFi.h"#include "StreamIO.h"#include "VideoStream.h"#include "RTSP.h"#include "NNFaceDetectionRecognition.h"#include "VideoStreamOverlay.h"#include "AmebaFatFS.h"#include <U8g2lib.h>  // Include U8g2 library for OLED

#define CHANNELVID  0    // Channel for RTSP streaming#define CHANNELJPEG 1    // Channel for taking snapshots#define CHANNELNN   3    // RGB format video for NN only available on channel 3

// Customised resolution for NN#define NNWIDTH  576#define NNHEIGHT 320

// U8g2 OLED setup - adjust according to your OLED modelU8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);

VideoSetting configVID(VIDEO_FHD, 30, VIDEO_H264, 0);VideoSetting configJPEG(VIDEO_FHD, CAM_FPS, VIDEO_JPEG, 1);VideoSetting configNN(NNWIDTH, NNHEIGHT, 10, VIDEO_RGB, 0);

NNFaceDetectionRecognition facerecog;

RTSP rtsp;StreamIO videoStreamer(1, 1);StreamIO videoStreamerFDFR(1, 1);StreamIO videoStreamerRGBFD(1, 1);

char ssid[] = "xxx";    // your network SSID (name)char pass[] = "xxx";        // your network passwordint status = WL_IDLE_STATUS;

bool regMode = false;       // Start in recognition modebool accessGranted = false; // Track access statusuint32_t img_addr = 0;uint32_t img_len = 0;

String fileName;long counter = 0;

// File Initialization

AmebaFatFS fs;

void updateOLEDStatus() {

    u8g2.clearBuffer();

    if (regMode) {

        u8g2.drawStr(0, 20, "REGISTER MODE");

        u8g2.drawStr(0, 40, "REG=<NAME>");

        u8g2.drawStr(0, 60, "Enter 'Y' to save");

    } else {

        u8g2.drawStr(0, 20, "RECOGNITION MODE");

        u8g2.drawStr(0, 40, accessGranted ? "Status: OPEN" : "Status: CLOSE");

    }

    u8g2.sendBuffer();

}

void setup(){

    // Initialize OLED display

    u8g2.begin();

    u8g2.clearBuffer();

    u8g2.setFont(u8g2_font_ncenB10_tr);

    u8g2.drawStr(0, 20, "Initializing...");

    u8g2.sendBuffer();

 

    Serial.begin(115200);

 

    // Attempt to connect to Wifi network:

    while (status != WL_CONNECTED) {

        Serial.print("Attempting to connect to WPA SSID: ");

        Serial.println(ssid);

        status = WiFi.begin(ssid, pass);

 

        // Update OLED

        u8g2.clearBuffer();

        u8g2.drawStr(0, 20, "Connecting WiFi");

        u8g2.sendBuffer();

  

        delay(2000);

    }

 

    // Configure camera video channels with video format information

    Camera.configVideoChannel(CHANNELVID, configVID);

    Camera.configVideoChannel(CHANNELJPEG, configJPEG);

    Camera.configVideoChannel(CHANNELNN, configNN);

    Camera.videoInit();

 

    // Configure RTSP with corresponding video format information

    rtsp.configVideo(configVID);

    rtsp.begin();

 

    // Configure Face Recognition model

    facerecog.configVideo(configNN);

    facerecog.modelSelect(FACE_RECOGNITION, NA_MODEL, DEFAULT_SCRFD, DEFAULT_MOBILEFACENET);

    facerecog.begin();

    facerecog.setResultCallback(FRPostProcess);

 

    // Configure StreamIO object to stream data from video channel to RTSP

    videoStreamer.registerInput(Camera.getStream(CHANNELVID));

    videoStreamer.registerOutput(rtsp);

    if (videoStreamer.begin() != 0) {

        Serial.println("StreamIO link start failed");

    }

 

    // Start data stream from video channel

    Camera.channelBegin(CHANNELVID);

    Camera.channelBegin(CHANNELJPEG);

 

    // Configure StreamIO object to stream data from RGB video channel to face detection

    videoStreamerRGBFD.registerInput(Camera.getStream(CHANNELNN));

    videoStreamerRGBFD.setStackSize();

    videoStreamerRGBFD.setTaskPriority();

    videoStreamerRGBFD.registerOutput(facerecog);

    if (videoStreamerRGBFD.begin() != 0) {

        Serial.println("StreamIO link start failed");

    }

 

    // Start video channel for NN

    Camera.channelBegin(CHANNELNN);

 

    // Start OSD drawing on RTSP video channel

    OSD.configVideo(CHANNELVID, configVID);

    OSD.begin();

 

    // Restore any registered faces saved in flash

    facerecog.restoreRegisteredFace();

 

    // Update OLED

    updateOLEDStatus();

 

    Serial.println("System started in RECOGNITION mode");

    Serial.println("Available commands:");

    Serial.println("R - Switch to registration mode");

    Serial.println("In registration mode:");

    Serial.println("REG={Name} - Register a new face");

    Serial.println("DEL={Name} - Delete a registered face");

    Serial.println("RESET - Reset all registered faces");

    Serial.println("Y - Save faces and switch to recognition mode");

}

void loop(){

    // Handle serial input

    if (Serial.available() > 0) {

        String input = Serial.readString();

        input.trim();

 

        if (input.equalsIgnoreCase("R") && !regMode) {

            // Switch to registration mode

            regMode = true;

            Serial.println("Switched to REGISTRATION mode");

            updateOLEDStatus();

        }

        else if (input.equalsIgnoreCase("Y") && regMode) {

            // Save configurations and switch to recognition mode

            facerecog.backupRegisteredFace();

            regMode = false;

            Serial.println("Faces saved. Switched to RECOGNITION mode");

            updateOLEDStatus();

        }

        else if (regMode) {

            // Registration mode commands

            if (input.startsWith(String("REG="))) {

                String name = input.substring(4);

                facerecog.registerFace(name);

                Serial.print("Registered face: ");

                Serial.println(name);

      

                // Update OLED temporarily

                u8g2.clearBuffer();

                u8g2.drawStr(0, 20, "REGISTERED:");

                u8g2.drawStr(0, 40, name.c_str());

                u8g2.sendBuffer();

                delay(2000);

                updateOLEDStatus();

            }

            else if (input.startsWith(String("DEL="))) {

                String name = input.substring(4);

                facerecog.removeFace(name);

                Serial.print("Removed face: ");

                Serial.println(name);

            }

            else if (input.startsWith(String("RESET"))) {

                facerecog.resetRegisteredFace();

                Serial.println("All registered faces have been reset");

            }

        }

    }

 

    delay(100);

    OSD.createBitmap(CHANNELVID);

    OSD.update(CHANNELVID);

}

// User callback function for post processing of face recognition resultsvoid FRPostProcess(std::vector<FaceRecognitionResult> results){

    uint16_t im_h = configVID.height();

    uint16_t im_w = configVID.width();

 

    printf("Total number of faces detected = %d\r\n", facerecog.getResultCount());

    OSD.createBitmap(CHANNELVID);

 

    if (facerecog.getResultCount() > 0 && !regMode) {

        FaceRecognitionResult face = results[0];

        if (String(face.name()) != String("unknown")) {

            if (!accessGranted) {  // Only trigger once per recognition

                Serial.print("Access GRANTED for: ");

                Serial.println(face.name());

                accessGranted = true;

      

                // Update OLED

                updateOLEDStatus();

      

                // Take snapshot

                fs.begin();

                File file = fs.open(String(fs.getRootPath()) + face.name() + String(++counter) + ".jpg");

                delay(1000);

                Camera.getImage(CHANNELJPEG, &img_addr, &img_len);

                file.write((uint8_t *)img_addr, img_len);

                file.close();

                fs.end();

      

                // After 10 seconds, return to CLOSE status

                delay(5000);

                accessGranted = false;

                updateOLEDStatus();

            }

        } else {

            if (accessGranted) {  // Only update if status changed

                Serial.println("Access DENIED - Unknown face");

                accessGranted = false;

                updateOLEDStatus();

            }

        }

    }

 

    for (int i = 0; i < facerecog.getResultCount(); i++) {

        FaceRecognitionResult item = results[i];

        int xmin = (int)(item.xMin() * im_w);

        int xmax = (int)(item.xMax() * im_w);

        int ymin = (int)(item.yMin() * im_h);

        int ymax = (int)(item.yMax() * im_h);

 

        uint32_t osd_color = (String(item.name()) == String("unknown")) ? OSD_COLOR_RED : OSD_COLOR_GREEN;

  

        printf("Face %d name %s:\t%d %d %d %d\n\r", i, item.name(), xmin, xmax, ymin, ymax);

        OSD.drawRect(CHANNELVID, xmin, ymin, xmax, ymax, 3, osd_color);

 

        char text_str[40];

        snprintf(text_str, sizeof(text_str), "Face:%s", item.name());

        OSD.drawText(CHANNELVID, xmin, ymin - OSD.getTextHeight(CHANNELVID), text_str, osd_color);

    }

    OSD.update(CHANNELVID);

}

Operation Effects:

 

Testing:

 

Results:

 

Portable Access Control System: 

Using the expansion board's onboard battery connector, a lithium - battery can power the system, creating a portable handheld version.