|
}
Project Surprises:
- GPIO02 boot-up tests for PSRAM voltage requirement interfered with HSPI Bus usage. Required eFuse setting to permanent PSRAM voltage level for HSPI normal operation
- ESP32 Cam with AI-Thinker hardware setup can be used for the project with a simple pin assignment change and the creators choice of one function loss.
- An overly elaborate method for using a vibrating motor to trigger SFX module, isolating the module electrically from Microcontroller abandon due to unnecessary complexity and stray triggering from handling project.
- SFX module uses High Brightness LEDs that cause short bursts of high current that have to be handled by separate battery pack in order to not require additional power supply circuitry for a common power supply.
- I had a ICR18650 lying around, so battery operation of project utilizing built in charging of the ArduCam. Then again, it could be a self-destruct method.
- Had to pin down ArduCam board in coffin, so I used a piece of a plastic fork.
Programming Elements
- TFT display for the ArduCam
- HSPI usage
- Facial Detection Code
- Simple RAW Image decoding for saved images
- Camera image buffer display to TFT display
Assemblies
Resource
Demonstration Video
Demo Video
Code
The code is a splicing of several programs and samples. See the Resource Section.
Includes
#include <SPI.h>
#include "FS.h"
#include <SD.h>
#include "Adafruit_GFX.h"
#include "Adafruit_GC9A01A.h"
#include "esp_camera.h"
#include "esp_timer.h"
#include "human_face_detect_msr01.hpp"
#include "human_face_detect_mnp01.hpp"
Defines
// Pin definition for CAMERA_MODEL_AI_THINKER
#define PWDN_GPIO_NUM 32
#define RESET_GPIO_NUM -1
#define XCLK_GPIO_NUM 0
#define SIOD_GPIO_NUM 26
#define SIOC_GPIO_NUM 27
#define Y9_GPIO_NUM 35
#define Y8_GPIO_NUM 34
#define Y7_GPIO_NUM 39
#define Y6_GPIO_NUM 36
#define Y5_GPIO_NUM 21
#define Y4_GPIO_NUM 19
#define Y3_GPIO_NUM 18
#define Y2_GPIO_NUM 5
#define VSYNC_GPIO_NUM 25
#define HREF_GPIO_NUM 23
#define PCLK_GPIO_NUM 22
#define SPI_MOSI 13
#define SPI_MISO 12
#define SPI_CLK 14
#define SPI_SS 15
#define TFT_DC 33 // Data Command control pin
#define TFT_CS 15 // Chip select control pin
#define TFT_RST -1
#define TFT_BL 3
#define SD_CS 4
#define SFX_MODULE_PIN 2
#define BUZZER_PIN 1
#define TFT_ROTATION 0 // 0 to 3
Initializations
SPIClass MySPI(HSPI); // Declare an HSPI bus object (to use or BOTH TFT and SD-card)
Adafruit_GC9A01A tft(&MySPI, TFT_DC, TFT_CS);
boolean camera_ok;
boolean InitSD()
{
boolean success=SD.begin(SD_CS,MySPI);
if (!success)
{
post_Error("Fail: check formatted SD-card is in!");
return false;
}
listDir(SD, "/", 0);
return success;
}
boolean camera_init()
{
camera_config_t config;
config.ledc_channel = LEDC_CHANNEL_0;
config.ledc_timer = LEDC_TIMER_0;
config.pin_d0 = Y2_GPIO_NUM;
config.pin_d1 = Y3_GPIO_NUM;
config.pin_d2 = Y4_GPIO_NUM;
config.pin_d3 = Y5_GPIO_NUM;
config.pin_d4 = Y6_GPIO_NUM;
config.pin_d5 = Y7_GPIO_NUM;
config.pin_d6 = Y8_GPIO_NUM;
config.pin_d7 = Y9_GPIO_NUM;
config.pin_xclk = XCLK_GPIO_NUM;
config.pin_pclk = PCLK_GPIO_NUM;
config.pin_vsync = VSYNC_GPIO_NUM;
config.pin_href = HREF_GPIO_NUM;
config.pin_sscb_sda = SIOD_GPIO_NUM;
config.pin_sscb_scl = SIOC_GPIO_NUM;
config.pin_pwdn = PWDN_GPIO_NUM;
config.pin_reset = RESET_GPIO_NUM;
config.xclk_freq_hz = 20000000;
config.pixel_format = PIXFORMAT_RGB565;
if (psramFound())
{ // larger bufer
config.frame_size = FRAMESIZE_QVGA;
config.jpeg_quality = 2;
config.fb_count = 2;
config.grab_mode = CAMERA_GRAB_LATEST;
}
else
{ // smaller buffer
config.frame_size = FRAMESIZE_QVGA;
config.jpeg_quality = 12;
config.fb_count = 1;
}
esp_err_t err = esp_camera_init(&config);
if (err != ESP_OK)
{
post_Error("Camera init failed with error: "+String(err));
return false;;
}
sensor_t * s = esp_camera_sensor_get();
s->set_hmirror(s, 1); // 0 = disable , 1 = enable
s->set_vflip(s, 1); // 0 = disable , 1 = enable
return true;
}
Core
void setup()
{
delay(200);
pinMode(BUZZER_PIN,OUTPUT);
pinMode(TFT_CS,OUTPUT);
pinMode(SD_CS,OUTPUT);
pinMode(SFX_MODULE_PIN,OUTPUT);
analogWrite(TFT_BL,255);
tft.begin();
tft.setRotation(TFT_ROTATION);
tft.setTextColor(GC9A01A_WHITE);
tft.setTextSize(2);
tft_clear();
tft.println("Hello World");
InitSD();
camera_ok=camera_init();
if (!camera_ok) post_Error("but without Camera");
delay(1000);
randomSeed(analogRead(0));
}
void loop()
{
int face_test = 0;
int loop_count = 25;
tft_clear();
TFT_fade(-1,10);
display_randon_sd_raw();
TFT_fade(1,10);
for(int x = 0; x < loop_count; ++x)
{
if (xcapture_handler(&face_test) == 100)
{
buzzer_zap(200);
SFX_Module_Fire();
delay(5000);
x = loop_count;
}
}
}
Display Functions
void tft_clear()
{
tft.fillScreen(GC9A01A_BLACK);
tft.setCursor(0,0); // top
}
void TFT_fade(int fade_type, int fade_delay)
{
switch (fade_type)
{
case 1:
for(int x=0;x<256;++x)
{
analogWrite(TFT_BL,x);
delay(fade_delay);
}
break;
case -1:
for(int x=255;x>-1;--x)
{
analogWrite(TFT_BL,x);
delay(fade_delay);
}
analogWrite(TFT_BL,0);
analogWrite(TFT_BL,0);
analogWrite(TFT_BL,0);
break;
}
delay(fade_delay);
}
Stored Image Functions
String picture_gallery[64];
int picture_gallery_size = 0;
void listDir(fs::FS &fs, const char * dirname, uint8_t levels)
{
File root = fs.open(dirname);
if (!root)
{
post_Error("Failed to open directory");
return;
}
if (!root.isDirectory())
{
post_Error("Not a directory");
return;
}
File file = root.openNextFile();
while(file)
{
if (file.isDirectory())
{
if (levels)
{
listDir(fs, file.name(), levels -1);
}
}
else
{
picture_gallery[picture_gallery_size] = file.name();
++picture_gallery_size;
}
file = root.openNextFile();
}
}
void display_randon_sd_raw()
{
int choice = random(picture_gallery_size);
int success;
success = sd_load_and_display_raw("/"+picture_gallery[choice]);
}
Displaying Images
int generate_image_from_camera(camera_fb_t *fb)
{
uint16_t *data = (uint16_t *)fb->buf;
uint16_t pixel_data;
for(uint16_t y=0;y<240;++y)
{
data = data + 40;
for(uint16_t x=0;x<240;++x)
{
pixel_data = *data;
pixel_data = (((pixel_data) & 0xFF00) >> 8) | (((pixel_data) & 0xFF) << 8);
tft.drawRGBBitmap(x,y, &pixel_data,1,1);
++data;
}
data = data + 40;
}
return(1);
}
int sd_load_and_display_raw(String file_name)
{
char pixel_red;
char pixel_green;
char pixel_blue;
uint16_t pixel_data;
uint16_t pixel_datar;
uint16_t pixel_datag;
uint16_t pixel_datab;
uint8_t test_line[724];
File file_pointer = SD.open(file_name);
if (!file_pointer)
post_Error("File not found: "+file_name);
file_pointer.read(test_line,54);
for(int y=0;y<240;++y)
{
file_pointer.read(test_line,720);
for(int x=0;x<240;++x)
{
pixel_red = test_line[(x*3)];
pixel_green = test_line[(x*3)+1];
pixel_blue = test_line[(x*3)+2];
pixel_datar = pixel_red;
pixel_datar = pixel_datar & 0b0000000011111000;
pixel_datar = pixel_datar << 8;
pixel_datag = ((pixel_green & 0b11100000) >> 2) | pixel_red & 0b00000111;;
pixel_datag = pixel_datag << 5;
pixel_datab = (pixel_blue & 0b11111000) >> 3;
pixel_data = pixel_datar | pixel_datag | pixel_datab;
tft.drawRGBBitmap(x,y, &pixel_data,1,1);
}
}
if (file_pointer)
file_pointer.close();
return(1);
}
Remains
void post_Error(String message)
{
// Place Holder
//
}
#define SFX_TRIGGER_LENGTH 25
void SFX_Module_Fire()
{
digitalWrite(SFX_MODULE_PIN,HIGH);
delay(SFX_TRIGGER_LENGTH);
digitalWrite(SFX_MODULE_PIN,LOW);
}
void buzzer_zap(int buzz_time)
{
digitalWrite(BUZZER_PIN,HIGH);
delay(buzz_time);
digitalWrite(BUZZER_PIN,LOW);
}
static esp_err_t xcapture_handler(int *req)
{
camera_fb_t *fb = NULL, *temp_fb;
esp_err_t res = ESP_OK;
int64_t fr_start = esp_timer_get_time();
res = 0;
fb = esp_camera_fb_get();
if (!fb)
{
return ESP_FAIL;
}
bool detected = false;
int face_id = 0;
// Code Snipet from Webserver ESP32-Cam demonstration example code
// Hard to balance in this application
// Higher Reliability Testing
// HumanFaceDetectMSR01 s1(0.01F, 0.5F, 10, 0.2F);
// HumanFaceDetectMNP01 s2(0.5F, 0.3F, 5);
// std::list<dl::detect::result_t> &candidates = s1.infer((uint16_t *)fb->buf, {(int)fb->height, (int)fb->width, 3});
// std::list<dl::detect::result_t> &results = s2.infer((uint16_t *)fb->buf, {(int)fb->height, (int)fb->width, 3}, candidates);
// Lower Reliability Testing
HumanFaceDetectMSR01 s1(0.1F, 0.5F, 10, 0.4F);
std::list<dl::detect::result_t> &results = s1.infer((uint16_t *)fb->buf, {(int)fb->height, (int)fb->width, 3});
if (results.size() > 0)
{
generate_image_from_camera(fb);
delay(1000) ;
res = 100;
}
esp_camera_fb_return(fb);
int64_t fr_end = esp_timer_get_time();
int64_t fr_delta = fr_end - fr_start;
return res;
}
|