378 lines
8.8 KiB
C++
378 lines
8.8 KiB
C++
#include "esp_camera.h"
|
|
#include <WiFi.h>
|
|
|
|
#include <stdbool.h>
|
|
#include "esp_system.h"
|
|
#include "driver/i2c_master.h"
|
|
|
|
#include "src/lipton/cci.h"
|
|
#include "src/lipton/vospi.h"
|
|
#include "src/lipton/lepton_system.h"
|
|
#include "src/lipton/lepton_utilities.h"
|
|
|
|
#include "esp_log.h"
|
|
|
|
#include "driver/uart.h"
|
|
|
|
#include "mbedtls/base64.h"
|
|
|
|
//
|
|
// WARNING!!! PSRAM IC required for UXGA resolution and high JPEG quality
|
|
// Ensure ESP32 Wrover Module or other board with PSRAM is selected
|
|
// Partial images will be transmitted if image exceeds buffer size
|
|
//
|
|
// You must select partition scheme from the board menu that has at least 3MB APP space.
|
|
// Face Recognition is DISABLED for ESP32 and ESP32-S2, because it takes up from 15
|
|
// seconds to process single frame. Face Detection is ENABLED if PSRAM is enabled as well
|
|
|
|
// ===================
|
|
// Select camera model
|
|
// ===================
|
|
// #define CAMERA_MODEL_WROVER_KIT // Has PSRAM
|
|
// #define CAMERA_MODEL_ESP_EYE // Has PSRAM
|
|
#define CAMERA_MODEL_ESP32S3_EYE // Has PSRAM
|
|
// #define CAMERA_MODEL_M5STACK_PSRAM // Has PSRAM
|
|
// #define CAMERA_MODEL_M5STACK_V2_PSRAM // M5Camera version B Has PSRAM
|
|
// #define CAMERA_MODEL_M5STACK_WIDE // Has PSRAM
|
|
// #define CAMERA_MODEL_M5STACK_ESP32CAM // No PSRAM
|
|
// #define CAMERA_MODEL_M5STACK_UNITCAM // No PSRAM
|
|
// #define CAMERA_MODEL_M5STACK_CAMS3_UNIT // Has PSRAM
|
|
// #define CAMERA_MODEL_AI_THINKER // Has PSRAM
|
|
// #define CAMERA_MODEL_TTGO_T_JOURNAL // No PSRAM
|
|
// #define CAMERA_MODEL_XIAO_ESP32S3 // Has PSRAM
|
|
// ** Espressif Internal Boards **
|
|
// #define CAMERA_MODEL_ESP32_CAM_BOARD
|
|
// #define CAMERA_MODEL_ESP32S2_CAM_BOARD
|
|
// #define CAMERA_MODEL_ESP32S3_CAM_LCD
|
|
// #define CAMERA_MODEL_DFRobot_FireBeetle2_ESP32S3 // Has PSRAM
|
|
// #define CAMERA_MODEL_DFRobot_Romeo_ESP32S3 // Has PSRAM
|
|
#include "camera_pins.h"
|
|
|
|
//
|
|
// LEP Task constants
|
|
//
|
|
|
|
// Number of consecutive VoSPI resynchronization attempts before attempting to reset
|
|
#define LEP_SYNC_FAIL_FAULT_LIMIT 10
|
|
|
|
// Reset fail delay before attempting a re-init (seconds)
|
|
#define LEP_RESET_FAIL_RETRY_SECS 5
|
|
|
|
//
|
|
// Code start
|
|
//
|
|
int vsync_count = 0;
|
|
int sync_fail_count = 0;
|
|
int reset_fail_count = 0;
|
|
int64_t vsyncDetectedUsec;
|
|
|
|
WiFiServer lipSrv(8078);
|
|
|
|
// ===========================
|
|
// Enter your WiFi credentials
|
|
// ===========================
|
|
const char *ssid = "CansatDev";
|
|
const char *password = "plechovka";
|
|
|
|
void startCameraServer();
|
|
void setupLedFlash(int pin);
|
|
|
|
void setup()
|
|
{
|
|
gpio_config_t usb_phy_conf = {
|
|
.pin_bit_mask = (1ULL << 19) | (1ULL << 20),
|
|
.mode = GPIO_MODE_OUTPUT,
|
|
.pull_up_en = GPIO_PULLUP_ENABLE,
|
|
.pull_down_en = GPIO_PULLDOWN_DISABLE,
|
|
.intr_type = GPIO_INTR_DISABLE,
|
|
};
|
|
gpio_config(&usb_phy_conf);
|
|
|
|
printf("[MAIN] Start task\n");
|
|
|
|
LEP_CONFIG = {
|
|
.agc_set_enabled = true,
|
|
.emissivity = 100,
|
|
.gain_mode = LEP_GAIN_AUTO};
|
|
|
|
// initialize spi, i2c and gpio for lepton
|
|
if (!lepton_io_init())
|
|
{
|
|
printf("[MAIN] Error: I/O init failed");
|
|
while (1)
|
|
{
|
|
delay(100);
|
|
}
|
|
}
|
|
|
|
// allocate lepton buffers
|
|
if (!lepton_buffer_init())
|
|
{
|
|
printf("[MAIN] Error: Memory init failed");
|
|
while (1)
|
|
{
|
|
delay(100);
|
|
}
|
|
}
|
|
|
|
// wait for lepton to initialize
|
|
delay(1000);
|
|
|
|
// Attempt to initialize the VoSPI interface
|
|
if (vospi_init() != ESP_OK)
|
|
{
|
|
printf("[MAIN] Error: Lepton VoSPI initialization failed\n");
|
|
while (1)
|
|
{
|
|
delay(100);
|
|
}
|
|
}
|
|
|
|
// initialize lepton
|
|
if (!lepton_init())
|
|
{
|
|
printf("[MAIN] Error: Lepton CCI initialization failed\n");
|
|
while (1)
|
|
{
|
|
delay(100);
|
|
}
|
|
}
|
|
|
|
// disable data from lepton
|
|
digitalWrite(LEP_CSN_PIN, 1);
|
|
|
|
printf("LEPTON INIT DONE");
|
|
|
|
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_sccb_sda = -1;
|
|
config.pin_sccb_scl = -1;
|
|
config.sccb_i2c_port = I2C_MASTER_NUM;
|
|
config.pin_pwdn = PWDN_GPIO_NUM;
|
|
config.pin_reset = RESET_GPIO_NUM;
|
|
config.xclk_freq_hz = 20000000;
|
|
config.frame_size = FRAMESIZE_UXGA;
|
|
config.pixel_format = PIXFORMAT_JPEG; // for streaming
|
|
// config.pixel_format = PIXFORMAT_RGB565; // for face detection/recognition
|
|
config.grab_mode = CAMERA_GRAB_WHEN_EMPTY;
|
|
config.fb_location = CAMERA_FB_IN_PSRAM;
|
|
config.jpeg_quality = 12;
|
|
config.fb_count = 1;
|
|
|
|
// if PSRAM IC present, init with UXGA resolution and higher JPEG quality
|
|
// for larger pre-allocated frame buffer.
|
|
if (config.pixel_format == PIXFORMAT_JPEG)
|
|
{
|
|
if (psramFound())
|
|
{
|
|
config.jpeg_quality = 10;
|
|
config.fb_count = 2;
|
|
config.grab_mode = CAMERA_GRAB_LATEST;
|
|
}
|
|
else
|
|
{
|
|
// Limit the frame size when PSRAM is not available
|
|
config.frame_size = FRAMESIZE_SVGA;
|
|
config.fb_location = CAMERA_FB_IN_DRAM;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Best option for face detection/recognition
|
|
config.frame_size = FRAMESIZE_240X240;
|
|
#if CONFIG_IDF_TARGET_ESP32S3
|
|
config.fb_count = 2;
|
|
#endif
|
|
}
|
|
|
|
#if defined(CAMERA_MODEL_ESP_EYE)
|
|
pinMode(13, INPUT_PULLUP);
|
|
pinMode(14, INPUT_PULLUP);
|
|
#endif
|
|
|
|
// camera init
|
|
esp_err_t err = esp_camera_init(&config);
|
|
if (err != ESP_OK)
|
|
{
|
|
printf("Camera init failed with error 0x%x", err);
|
|
return;
|
|
}
|
|
|
|
sensor_t *s = esp_camera_sensor_get();
|
|
// initial sensors are flipped vertically and colors are a bit saturated
|
|
if (s->id.PID == OV3660_PID)
|
|
{
|
|
s->set_vflip(s, 1); // flip it back
|
|
s->set_brightness(s, 1); // up the brightness just a bit
|
|
s->set_saturation(s, -2); // lower the saturation
|
|
}
|
|
// drop down frame size for higher initial frame rate
|
|
if (config.pixel_format == PIXFORMAT_JPEG)
|
|
{
|
|
s->set_framesize(s, FRAMESIZE_QVGA);
|
|
}
|
|
|
|
#if defined(CAMERA_MODEL_M5STACK_WIDE) || defined(CAMERA_MODEL_M5STACK_ESP32CAM)
|
|
s->set_vflip(s, 1);
|
|
s->set_hmirror(s, 1);
|
|
#endif
|
|
|
|
#if defined(CAMERA_MODEL_ESP32S3_EYE)
|
|
s->set_vflip(s, 1);
|
|
#endif
|
|
|
|
// Setup LED FLash if LED pin is defined in camera_pins.h
|
|
#if defined(LED_GPIO_NUM)
|
|
setupLedFlash(LED_GPIO_NUM);
|
|
#endif
|
|
|
|
WiFi.begin(ssid, password);
|
|
WiFi.setSleep(false);
|
|
|
|
printf("WiFi connecting\n");
|
|
while (WiFi.status() != WL_CONNECTED)
|
|
{
|
|
delay(500);
|
|
printf(".");
|
|
}
|
|
printf("\n");
|
|
printf("WiFi connected\n");
|
|
|
|
startCameraServer();
|
|
|
|
printf("Camera Ready! Use 'http://");
|
|
printf(WiFi.localIP().toString().c_str());
|
|
lipSrv.begin();
|
|
printf("' to connect\n");
|
|
}
|
|
|
|
uint8_t *take_picture()
|
|
{
|
|
digitalWrite(LEP_CSN_PIN, 0);
|
|
delay(10);
|
|
|
|
while (1)
|
|
{
|
|
// Spin waiting for vsync to be asserted
|
|
int t = 0;
|
|
while (digitalRead(LEP_VSYNC_PIN) == 0 && t++ < 11000)
|
|
{
|
|
}
|
|
|
|
vsyncDetectedUsec = esp_timer_get_time();
|
|
|
|
// Attempt to process a segment
|
|
if (t >= 11000 || vospi_transfer_segment(vsyncDetectedUsec))
|
|
{
|
|
// Got image
|
|
vsync_count = 0;
|
|
if (t >= 11000)
|
|
printf("LEPOVF%d\n", t);
|
|
else
|
|
{
|
|
printf("LEPNOVF%d\n", t);
|
|
}
|
|
|
|
// Copy the frame to the current half of the shared buffer and let rsp_task know
|
|
vospi_get_frame(&rsp_lep_buffer);
|
|
|
|
// Hold fault counters reset while operating
|
|
sync_fail_count = 0;
|
|
|
|
// disable lepton again
|
|
digitalWrite(LEP_CSN_PIN, 1);
|
|
return rsp_lep_buffer.lep_bufferP;
|
|
}
|
|
else
|
|
{
|
|
if (++vsync_count >= 36)
|
|
{
|
|
vsync_count = 0;
|
|
printf("[MAIN] Could not get lepton image\n");
|
|
|
|
delay(185);
|
|
|
|
if (sync_fail_count++ == LEP_SYNC_FAIL_FAULT_LIMIT)
|
|
{
|
|
return nullptr;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
void lepton_restart()
|
|
{
|
|
while (1)
|
|
{
|
|
delay(LEP_RESET_FAIL_RETRY_SECS * 1000);
|
|
printf("LEPRESET\n\a");
|
|
|
|
#ifdef LEP_RESET_PIN
|
|
digitalWrite(LEP_RESET_PIN, LEP_RESET_ON);
|
|
delay(10);
|
|
digitalWrite(LEP_RESET_PIN, LEP_RESET_OFF);
|
|
#endif
|
|
|
|
// Delay for Lepton internal initialization (max 950 mSec)
|
|
delay(1000);
|
|
|
|
// if successfully initialized, break out
|
|
if (lepton_init())
|
|
break;
|
|
}
|
|
}
|
|
|
|
void loop()
|
|
{
|
|
|
|
WiFiClient client = lipSrv.available();
|
|
|
|
if (client)
|
|
{
|
|
|
|
printf("Got client %s\n", client.remoteIP().toString().c_str());
|
|
while (client.connected())
|
|
{
|
|
|
|
if (client.available() && client.read() == 'F')
|
|
{
|
|
uint8_t *picture = take_picture();
|
|
if (picture)
|
|
{
|
|
client.write(picture, LEP_NUM_PIXELS);
|
|
}
|
|
else
|
|
{
|
|
printf("Failed to take picture\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
// close the connection:
|
|
|
|
client.stop();
|
|
}
|
|
|
|
// if (picture) {
|
|
// pic_to_uart(picture);
|
|
// } else {
|
|
// lepton_restart();
|
|
// }
|
|
}
|