Разное о выращивании табака на Алтае
Вы не вошли.
Страницы 1
Не в сети
Не в сети
Тестовый код.
#include <Arduino.h>
#include <Wire.h>
#include <Adafruit_GFX.h> // Core graphics library
#include <Adafruit_ST7735.h> // Hardware-specific library for ST7735
#include <SPI.h>
#define TFT_MOSI 13
#define TFT_SCLK 14
#define TFT_CS 15 // Chip select control pin
#define TFT_DC 2 // Data Command control pin
#define TFT_RST 12
Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_MOSI, TFT_SCLK, TFT_RST);
float p = 3.1415926;
void setup(void) {
Serial.begin(9600);
Serial.print(F("Hello! ST77xx TFT Test"));
// Use this initializer if using a 1.8" TFT screen:
tft.initR(INITR_BLACKTAB); // Init ST7735S chip, black tab
Serial.println(F("Initialized"));
uint16_t time = millis();
tft.fillScreen(ST77XX_BLACK);
time = millis() - time;
Serial.println(time, DEC);
delay(500);
// large block of text
tft.fillScreen(ST77XX_BLACK);
testdrawtext("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur adipiscing ante sed nibh tincidunt feugiat. Maecenas enim massa, fringilla sed malesuada et, malesuada sit amet turpis. Sed porttitor neque ut ante pretium vitae malesuada nunc bibendum. Nullam aliquet ultrices massa eu hendrerit. Ut sed nisi lorem. In vestibulum purus a tortor imperdiet posuere. ", ST77XX_WHITE);
delay(1000);
// tft print function!
tftPrintTest();
delay(4000);
// a single pixel
tft.drawPixel(tft.width()/2, tft.height()/2, ST77XX_GREEN);
delay(500);
// line draw test
testlines(ST77XX_YELLOW);
delay(500);
// optimized lines
testfastlines(ST77XX_RED, ST77XX_BLUE);
delay(500);
testdrawrects(ST77XX_GREEN);
delay(500);
testfillrects(ST77XX_YELLOW, ST77XX_MAGENTA);
delay(500);
tft.fillScreen(ST77XX_BLACK);
testfillcircles(10, ST77XX_BLUE);
testdrawcircles(10, ST77XX_WHITE);
delay(500);
testroundrects();
delay(500);
testtriangles();
delay(500);
mediabuttons();
delay(500);
Serial.println("done");
delay(1000);
}
void loop() {
tft.invertDisplay(true);
delay(500);
tft.invertDisplay(false);
delay(500);
}
void testlines(uint16_t color) {
tft.fillScreen(ST77XX_BLACK);
for (int16_t x=0; x < tft.width(); x+=6) {
tft.drawLine(0, 0, x, tft.height()-1, color);
delay(0);
}
for (int16_t y=0; y < tft.height(); y+=6) {
tft.drawLine(0, 0, tft.width()-1, y, color);
delay(0);
}
tft.fillScreen(ST77XX_BLACK);
for (int16_t x=0; x < tft.width(); x+=6) {
tft.drawLine(tft.width()-1, 0, x, tft.height()-1, color);
delay(0);
}
for (int16_t y=0; y < tft.height(); y+=6) {
tft.drawLine(tft.width()-1, 0, 0, y, color);
delay(0);
}
tft.fillScreen(ST77XX_BLACK);
for (int16_t x=0; x < tft.width(); x+=6) {
tft.drawLine(0, tft.height()-1, x, 0, color);
delay(0);
}
for (int16_t y=0; y < tft.height(); y+=6) {
tft.drawLine(0, tft.height()-1, tft.width()-1, y, color);
delay(0);
}
tft.fillScreen(ST77XX_BLACK);
for (int16_t x=0; x < tft.width(); x+=6) {
tft.drawLine(tft.width()-1, tft.height()-1, x, 0, color);
delay(0);
}
for (int16_t y=0; y < tft.height(); y+=6) {
tft.drawLine(tft.width()-1, tft.height()-1, 0, y, color);
delay(0);
}
}
void testdrawtext(char *text, uint16_t color) {
tft.setCursor(0, 0);
tft.setTextColor(color);
tft.setTextWrap(true);
tft.print(text);
}
void testfastlines(uint16_t color1, uint16_t color2) {
tft.fillScreen(ST77XX_BLACK);
for (int16_t y=0; y < tft.height(); y+=5) {
tft.drawFastHLine(0, y, tft.width(), color1);
}
for (int16_t x=0; x < tft.width(); x+=5) {
tft.drawFastVLine(x, 0, tft.height(), color2);
}
}
void testdrawrects(uint16_t color) {
tft.fillScreen(ST77XX_BLACK);
for (int16_t x=0; x < tft.width(); x+=6) {
tft.drawRect(tft.width()/2 -x/2, tft.height()/2 -x/2 , x, x, color);
}
}
void testfillrects(uint16_t color1, uint16_t color2) {
tft.fillScreen(ST77XX_BLACK);
for (int16_t x=tft.width()-1; x > 6; x-=6) {
tft.fillRect(tft.width()/2 -x/2, tft.height()/2 -x/2 , x, x, color1);
tft.drawRect(tft.width()/2 -x/2, tft.height()/2 -x/2 , x, x, color2);
}
}
void testfillcircles(uint8_t radius, uint16_t color) {
for (int16_t x=radius; x < tft.width(); x+=radius*2) {
for (int16_t y=radius; y < tft.height(); y+=radius*2) {
tft.fillCircle(x, y, radius, color);
}
}
}
void testdrawcircles(uint8_t radius, uint16_t color) {
for (int16_t x=0; x < tft.width()+radius; x+=radius*2) {
for (int16_t y=0; y < tft.height()+radius; y+=radius*2) {
tft.drawCircle(x, y, radius, color);
}
}
}
void testtriangles() {
tft.fillScreen(ST77XX_BLACK);
uint16_t color = 0xF800;
int t;
int w = tft.width()/2;
int x = tft.height()-1;
int y = 0;
int z = tft.width();
for(t = 0 ; t <= 15; t++) {
tft.drawTriangle(w, y, y, x, z, x, color);
x-=4;
y+=4;
z-=4;
color+=100;
}
}
void testroundrects() {
tft.fillScreen(ST77XX_BLACK);
uint16_t color = 100;
int i;
int t;
for(t = 0 ; t <= 4; t+=1) {
int x = 0;
int y = 0;
int w = tft.width()-2;
int h = tft.height()-2;
for(i = 0 ; i <= 16; i+=1) {
tft.drawRoundRect(x, y, w, h, 5, color);
x+=2;
y+=3;
w-=4;
h-=6;
color+=1100;
}
color+=100;
}
}
void tftPrintTest() {
tft.setTextWrap(false);
tft.fillScreen(ST77XX_BLACK);
tft.setCursor(0, 30);
tft.setTextColor(ST77XX_RED);
tft.setTextSize(1);
tft.println("Hello World!");
tft.setTextColor(ST77XX_YELLOW);
tft.setTextSize(2);
tft.println("Hello World!");
tft.setTextColor(ST77XX_GREEN);
tft.setTextSize(3);
tft.println("Hello World!");
tft.setTextColor(ST77XX_BLUE);
tft.setTextSize(4);
tft.print(1234.567);
delay(1500);
tft.setCursor(0, 0);
tft.fillScreen(ST77XX_BLACK);
tft.setTextColor(ST77XX_WHITE);
tft.setTextSize(0);
tft.println("Hello World!");
tft.setTextSize(1);
tft.setTextColor(ST77XX_GREEN);
tft.print(p, 6);
tft.println(" Want pi?");
tft.println(" ");
tft.print(8675309, HEX); // print 8,675,309 out in HEX!
tft.println(" Print HEX!");
tft.println(" ");
tft.setTextColor(ST77XX_WHITE);
tft.println("Sketch has been");
tft.println("running for: ");
tft.setTextColor(ST77XX_MAGENTA);
tft.print(millis() / 1000);
tft.setTextColor(ST77XX_WHITE);
tft.print(" seconds.");
}
void mediabuttons() {
// play
tft.fillScreen(ST77XX_BLACK);
tft.fillRoundRect(25, 10, 78, 60, 8, ST77XX_WHITE);
tft.fillTriangle(42, 20, 42, 60, 90, 40, ST77XX_RED);
delay(500);
// pause
tft.fillRoundRect(25, 90, 78, 60, 8, ST77XX_WHITE);
tft.fillRoundRect(39, 98, 20, 45, 5, ST77XX_GREEN);
tft.fillRoundRect(69, 98, 20, 45, 5, ST77XX_GREEN);
delay(500);
// play color
tft.fillTriangle(42, 20, 42, 60, 90, 40, ST77XX_BLUE);
delay(50);
// pause color
tft.fillRoundRect(39, 98, 20, 45, 5, ST77XX_RED);
tft.fillRoundRect(69, 98, 20, 45, 5, ST77XX_RED);
// play color
tft.fillTriangle(42, 20, 42, 60, 90, 40, ST77XX_GREEN);
}
Не в сети
Ноги дисплея VDD и BLK соединяем вместе и они идут на питание +3.3В
Ещё кусок кода подпишу, чтоб понятней было подключение дисплея.
#define TFT_MOSI 13//SDA
#define TFT_SCLK 14//SCL
#define TFT_CS 15//CS Chip select control pin
#define TFT_DC 2//DC Data Command control pin
#define TFT_RST 12//RST
Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_MOSI, TFT_SCLK, TFT_RST);
Не в сети
Не в сети
Дальше, код огромный и в основном не мой. Я туда привинтил только обработку дисплея ST8266S. Исходник тут https://github.com/alanesq/esp32cam-demo
Не в сети
Камера делает снимок каждые 20 секунд и выводит изображение на экран.
#include <Arduino.h>
#include <Adafruit_GFX.h> // Core graphics library
#include <Adafruit_ST7735.h> // Hardware-specific library for ST7735
#define TFT_MOSI 13//SDA
#define TFT_SCLK 14//SCL
#define TFT_CS 15//CS Chip select control pin
#define TFT_DC 2//DC Data Command control pin
#define TFT_RST 12//RST
Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_MOSI, TFT_SCLK, TFT_RST);
#include "esp_camera.h" // https://github.com/espressif/esp32-camera
// ---------------------------------------------------------------------------------------------------------
#define SSID_NAME "*******"
#define SSID_PASWORD "******"
// ---------------------------------------------------------------------------------------------------------
#include <Arduino.h>
// forward declarations
bool initialiseCamera();
bool cameraImageSettings();
String localTime();
void flashLED(int reps);
byte storeImage();
void handleRoot();
void handlePhoto();
bool handleImg();
void handleNotFound();
void readRGBImage();
bool getNTPtime(int sec);
bool handleJPG();
void handleStream();
int requestWebPage(String*, String*, int);
void handleTest();
void brightLed(byte ledBrightness);
void setupFlashPWM();
void changeResolution(framesize_t);
void handleData();
// ---------------------------------------------------------------
// -SETTINGS
// ---------------------------------------------------------------
const char* stitle = "ESP32Cam-demo"; // title of this sketch
const char* sversion = "25Jan22"; // Sketch version
bool sendRGBfile = 0; // if set '/rgb' will just return raw rgb data which can be saved as a file rather than display a HTML pag
uint16_t datarefresh = 2200; // how often to refresh data on root web page (ms)
uint16_t imagerefresh = 5000; // how often to refresh the image on root web page (ms)
const bool serialDebug = 1; // show debug info. on serial port (1=enabled, disable if using pins 1 and 3 as gpio)
#define useMCP23017 0 // if MCP23017 IO expander chip is being used (on pins 12 and 13)
// Camera related
bool flashRequired = 1; // If flash to be used when capturing image (1 = yes)
framesize_t FRAME_SIZE_IMAGE = FRAMESIZE_QQVGA; // Image resolution:
// default = "const framesize_t FRAME_SIZE_IMAGE = FRAMESIZE_VGA"
// 160x120 (QQVGA), 128x160 (QQVGA2), 176x144 (QCIF), 240x176 (HQVGA),
// 320x240 (QVGA), 400x296 (CIF), 640x480 (VGA, default), 800x600 (SVGA),
// 1024x768 (XGA), 1280x1024 (SXGA), 1600x1200 (UXGA)
#define PIXFORMAT PIXFORMAT_JPEG; // image format, Options = YUV422, GRAYSCALE, RGB565, JPEG, RGB888
int cameraImageExposure = 0; // Camera exposure (0 - 1200) If gain and exposure both set to zero then auto adjust is enabled
int cameraImageGain = 0; // Image gain (0 - 30)
int cameraImageBrightness = 0; // Image brightness (-2 to +2)
const int TimeBetweenStatus = 600; // speed of flashing system running ok status light (milliseconds)
const int indicatorLED = 33; // onboard small LED pin (33)
// Bright LED (Flash)
const int brightLED = 4; // onboard Illumination/flash LED pin (4)
int brightLEDbrightness = 0; // initial brightness (0 - 255)
const int ledFreq = 5000; // PWM settings
const int ledChannel = 15; // camera uses timer1
const int ledRresolution = 8; // resolution (8 = from 0 to 255)
const int iopinA = 13; // general io pin 13
const int iopinB = 12; // general io pin 12 (must not be high at boot)
const int serialSpeed = 115200; // Serial data speed to use
// NTP - Internet time
const char* ntpServer = "pool.ntp.org";
const char* TZ_INFO = "GMT+0BST-1,M3.5.0/01:00:00,M10.5.0/02:00:00"; // enter your time zone (https://remotemonitoringsystems.ca/time-zone-abbreviations.php)
long unsigned lastNTPtime;
tm timeinfo;
time_t now;
// camera settings (for the standard - OV2640 - CAMERA_MODEL_AI_THINKER)
// see: https://randomnerdtutorials.com/esp32-cam-camera-pin-gpios/
// set camera resolution etc. in 'initialiseCamera()' and 'cameraImageSettings()'
#define CAMERA_MODEL_AI_THINKER
#define PWDN_GPIO_NUM 32 // power to camera (on/off)
#define RESET_GPIO_NUM -1 // -1 = not used
#define XCLK_GPIO_NUM 0
#define SIOD_GPIO_NUM 26 // i2c sda
#define SIOC_GPIO_NUM 27 // i2c scl
#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 // vsync_pin
#define HREF_GPIO_NUM 23 // href_pin
#define PCLK_GPIO_NUM 22 // pixel_clock_pin
// ******************************************************************************************************************
//#include "esp_camera.h" // https://github.com/espressif/esp32-camera
// #include "camera_pins.h"
#include <base64.h> // for encoding buffer to display image on page
#include <WiFi.h>
#include <WebServer.h>
#include <HTTPClient.h>
#include "driver/ledc.h" // used to configure pwm on illumination led
// spiffs used to store images if no sd card present
#include <SPIFFS.h>
#include <FS.h> // gives file access on spiffs
WebServer server(80); // serve web pages on port 80
// Used to disable brownout detection
#include "soc/soc.h"
#include "soc/rtc_cntl_reg.h"
// sd-card
#include "SD_MMC.h" // sd card - see https://randomnerdtutorials.com/esp32-cam-take-photo-save-microsd-card/
#include <SPI.h>
#include <FS.h> // gives file access
#define SD_CS 5 // sd chip select pin = 5
// MCP23017 IO expander on pins 12 and 13 (optional)
#if useMCP23017 == 1
#include <Wire.h>
#include "Adafruit_MCP23017.h"
Adafruit_MCP23017 mcp;
// Wire.setClock(1700000); // set frequency to 1.7mhz
#endif
// Define some global variables:
uint32_t lastStatus = millis(); // last time status light changed status (to flash all ok led)
bool sdcardPresent; // flag if an sd card is detected
int imageCounter; // image file name on sd card counter
String spiffsFilename = "/image.jpg"; // image name to use when storing in spiffs
String ImageResDetails = "Unknown"; // image resolution info
// ******************************************************************************************************************
// ---------------------------------------------------------------
// -SETUP SETUP SETUP SETUP SETUP SETUP
// ---------------------------------------------------------------
void setup() {
if (serialDebug) {
Serial.begin(serialSpeed); // Start serial communication
// Serial.setDebugOutput(true);
Serial.println("\n\n\n"); // line feeds
Serial.println("-----------------------------------");
Serial.printf("Starting - %s - %s \n", stitle, sversion);
Serial.println("-----------------------------------");
// Serial.print("Reset reason: " + ESP.getResetReason());
}
WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); // Turn-off the 'brownout detector'
// small indicator led on rear of esp32cam board
pinMode(indicatorLED, OUTPUT);
digitalWrite(indicatorLED,HIGH);
// Connect to wifi
digitalWrite(indicatorLED,LOW); // small indicator led on
if (serialDebug) {
Serial.print("\nConnecting to ");
Serial.print(SSID_NAME);
Serial.print("\n ");
}
WiFi.begin(SSID_NAME, SSID_PASWORD);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
if (serialDebug) Serial.print(".");
}
if (serialDebug) {
Serial.print("\nWiFi connected, ");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
}
server.begin(); // start web server
digitalWrite(indicatorLED,HIGH); // small indicator led off
// define the web pages (i.e. call these procedures when url is requested)
server.on("/", handleRoot); // root page
server.on("/data", handleData); // suplies data to periodically update root (AJAX)
server.on("/jpg", handleJPG); // capture image and send as jpg
server.on("/stream", handleStream); // stream live video
server.on("/photo", handlePhoto); // save image to sd card
server.on("/img", handleImg); // show image from sd card
server.on("/rgb", readRGBImage); // demo converting image to RGB
server.on("/test", handleTest); // Testing procedure
server.onNotFound(handleNotFound); // invalid url requested
// NTP - internet time
if (serialDebug) Serial.println("\nGetting real time (NTP)");
configTime(0, 0, ntpServer);
setenv("TZ", TZ_INFO, 1);
if (getNTPtime(10)) { // wait up to 10 sec to sync
} else {
if (serialDebug) Serial.println("Time not set");
}
lastNTPtime = time(&now);
// set up camera
if (serialDebug) Serial.print(("\nInitialising camera: "));
if (initialiseCamera()) {
if (serialDebug) Serial.println("OK");
}
else {
if (serialDebug) Serial.println("failed");
}
// Spiffs - for storing images without an sd card
// see: https://circuits4you.com/2018/01/31/example-of-esp8266-flash-file-system-spiffs/
if (!SPIFFS.begin(true)) {
if (serialDebug) Serial.println(("An Error has occurred while mounting SPIFFS - restarting"));
delay(5000);
ESP.restart(); // restart and try again
delay(5000);
} else {
// SPIFFS.format(); // wipe spiffs
if (serialDebug) {
Serial.print(("SPIFFS mounted successfully: "));
Serial.printf("total bytes: %d , used: %d \n", SPIFFS.totalBytes(), SPIFFS.usedBytes());
}
}
// SD Card - if one is detected set 'sdcardPresent' High
if (!SD_MMC.begin("/sdcard", true)) { // if loading sd card fails
// note: ('/sdcard", true)' = 1bit mode - see: https://www.reddit.com/r/esp32/comments/d71es9/a_breakdown_of_my_experience_trying_to_talk_to_an/
if (serialDebug) Serial.println("No SD Card detected");
sdcardPresent = 0; // flag no sd card available
} else {
uint8_t cardType = SD_MMC.cardType();
if (cardType == CARD_NONE) { // if invalid card found
if (serialDebug) Serial.println("SD Card type detect failed");
sdcardPresent = 0; // flag no sd card available
} else {
// valid sd card detected
uint16_t SDfreeSpace = (uint64_t)(SD_MMC.totalBytes() - SD_MMC.usedBytes()) / (1024 * 1024);
if (serialDebug) Serial.printf("SD Card found, free space = %dMB \n", SDfreeSpace);
sdcardPresent = 1; // flag sd card available
}
}
fs::FS &fs = SD_MMC; // sd card file system
// discover the number of image files already stored in '/img' folder of the sd card and set image file counter accordingly
imageCounter = 0;
if (sdcardPresent) {
int tq=fs.mkdir("/img"); // create the '/img' folder on sd card (in case it is not already there)
if (!tq) {
if (serialDebug) Serial.println("Unable to create IMG folder on sd card");
}
// open the image folder and step through all files in it
File root = fs.open("/img");
while (true)
{
File entry = root.openNextFile(); // open next file in the folder
if (!entry) break; // if no more files in the folder
imageCounter ++; // increment image counter
entry.close();
}
root.close();
if (serialDebug) Serial.printf("Image file count = %d \n",imageCounter);
}
// define i/o pins
pinMode(indicatorLED, OUTPUT); // defined again as sd card config can reset it
digitalWrite(indicatorLED,HIGH); // led off = High
pinMode(iopinA, INPUT); // pin 13 - free io pin, can be used for input or output
pinMode(iopinB, OUTPUT); // pin 12 - free io pin, can be used for input or output (must not be high at boot)
// MCP23017 io expander (requires adafruit MCP23017 library)
#if useMCP23017 == 1
Wire.begin(12,13); // use pins 12 and 13 for i2c
mcp.begin(&Wire); // use default address 0
mcp.pinMode(0, OUTPUT); // Define GPA0 (physical pin 21) as output pin
mcp.pinMode(8, INPUT); // Define GPB0 (physical pin 1) as input pin
mcp.pullUp(8, HIGH); // turn on a 100K pullup internally
// change pin state with mcp.digitalWrite(0, HIGH);
// read pin state with mcp.digitalRead(8)
#endif
setupFlashPWM(); // configure PWM for the illumination LED
// startup complete
if (serialDebug) Serial.println("\nStarted...");
flashLED(2); // flash the onboard indicator led
brightLed(64); // change bright LED
delay(200);
brightLed(0); // change bright LED
/////////////////TFT////////////////
tft.initR(INITR_BLACKTAB);
tft.setRotation(1);
tft.fillScreen(ST77XX_BLACK);
tft.drawPixel(tft.width()/2, tft.height()/2,tft.color565(0,255,0));
} // setup
// ******************************************************************************************************************
// ----------------------------------------------------------------
// -LOOP LOOP LOOP LOOP LOOP LOOP LOOP
// ----------------------------------------------------------------
void loop() {
server.handleClient(); // handle any incoming web page requests
// <<< YOUR CODE HERE >>>
// // Capture an image and save to sd card every 20 seconds (i.e. time lapse)
static uint32_t lastCamera = millis();
if ( ((unsigned long)(millis() - lastCamera) >= 20000) && sdcardPresent ) {
lastCamera = millis(); // reset timer
storeImage(); // save an image to sd card
readRGBImage();
}
} // loop
// ******************************************************************************************************************
// ----------------------------------------------------------------
// Initialise the camera
// ----------------------------------------------------------------
// returns TRUE if successful
bool initialiseCamera() {
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; // XCLK 20MHz or 10MHz for OV2640 double FPS (Experimental)
config.pixel_format = PIXFORMAT; // Options = YUV422, GRAYSCALE, RGB565, JPEG, RGB888
config.frame_size = FRAME_SIZE_IMAGE; // Image sizes: 160x120 (QQVGA), 128x160 (QQVGA2), 176x144 (QCIF), 240x176 (HQVGA), 320x240 (QVGA),
// 400x296 (CIF), 640x480 (VGA, default), 800x600 (SVGA), 1024x768 (XGA), 1280x1024 (SXGA),
// 1600x1200 (UXGA)
config.jpeg_quality = 10; // 0-63 lower number means higher quality
config.fb_count = 1; // if more than one, i2s runs in continuous mode. Use only with JPEG
// check the esp32cam board has a psram chip installed (extra memory used for storing captured images)
// Note: if not using "AI thinker esp32 cam" in the Arduino IDE, SPIFFS must be enabled
if (!psramFound()) {
if (serialDebug) Serial.println("Warning: No PSRam found so defaulting to image size 'CIF'");
config.frame_size = FRAMESIZE_CIF;
}
//#if defined(CAMERA_MODEL_ESP_EYE)
// pinMode(13, INPUT_PULLUP);
// pinMode(14, INPUT_PULLUP);
//#endif
esp_err_t camerr = esp_camera_init(&config); // initialise the camera
if (camerr != ESP_OK) {
if (serialDebug) Serial.printf("ERROR: Camera init failed with error 0x%x", camerr);
}
cameraImageSettings(); // apply custom camera settings
return (camerr == ESP_OK); // return boolean result of camera initialisation
}
// ******************************************************************************************************************
// ----------------------------------------------------------------
// -Change camera image settings
// ----------------------------------------------------------------
// Adjust image properties (brightness etc.)
// Defaults to auto adjustments if exposure and gain are both set to zero
// - Returns TRUE if successful
// BTW - some interesting info on exposure times here: https://github.com/raduprv/esp32-cam_ov2640-timelapse
bool cameraImageSettings() {
if (serialDebug) Serial.println("Applying camera settings");
sensor_t *s = esp_camera_sensor_get();
// something to try?: if (s->id.PID == OV3660_PID)
if (s == NULL) {
if (serialDebug) Serial.println("Error: problem reading camera sensor settings");
return 0;
}
// if both set to zero enable auto adjust
if (cameraImageExposure == 0 && cameraImageGain == 0) {
// enable auto adjust
s->set_gain_ctrl(s, 1); // auto gain on
s->set_exposure_ctrl(s, 1); // auto exposure on
s->set_awb_gain(s, 1); // Auto White Balance enable (0 or 1)
s->set_brightness(s, cameraImageBrightness); // (-2 to 2) - set brightness
} else {
// Apply manual settings
s->set_gain_ctrl(s, 0); // auto gain off
s->set_awb_gain(s, 1); // Auto White Balance enable (0 or 1)
s->set_exposure_ctrl(s, 0); // auto exposure off
s->set_brightness(s, cameraImageBrightness); // (-2 to 2) - set brightness
s->set_agc_gain(s, cameraImageGain); // set gain manually (0 - 30)
s->set_aec_value(s, cameraImageExposure); // set exposure manually (0-1200)
}
return 1;
} // cameraImageSettings
// // More camera settings available:
// // If you enable gain_ctrl or exposure_ctrl it will prevent a lot of the other settings having any effect
// // more info on settings here: https://randomnerdtutorials.com/esp32-cam-ov2640-camera-settings/
// s->set_gain_ctrl(s, 0); // auto gain off (1 or 0)
// s->set_exposure_ctrl(s, 0); // auto exposure off (1 or 0)
// s->set_agc_gain(s, cameraImageGain); // set gain manually (0 - 30)
// s->set_aec_value(s, cameraImageExposure); // set exposure manually (0-1200)
// s->set_vflip(s, cameraImageInvert); // Invert image (0 or 1)
// s->set_quality(s, 10); // (0 - 63)
// s->set_gainceiling(s, GAINCEILING_32X); // Image gain (GAINCEILING_x2, x4, x8, x16, x32, x64 or x128)
// s->set_brightness(s, cameraImageBrightness); // (-2 to 2) - set brightness
// s->set_lenc(s, 1); // lens correction? (1 or 0)
// s->set_saturation(s, 0); // (-2 to 2)
// s->set_contrast(s, cameraImageContrast); // (-2 to 2)
// s->set_sharpness(s, 0); // (-2 to 2)
// s->set_hmirror(s, 0); // (0 or 1) flip horizontally
// s->set_colorbar(s, 0); // (0 or 1) - show a testcard
// s->set_special_effect(s, 0); // (0 to 6?) apply special effect
// s->set_whitebal(s, 0); // white balance enable (0 or 1)
// s->set_awb_gain(s, 1); // Auto White Balance enable (0 or 1)
// s->set_wb_mode(s, 0); // 0 to 4 - if awb_gain enabled (0 - Auto, 1 - Sunny, 2 - Cloudy, 3 - Office, 4 - Home)
// s->set_dcw(s, 0); // downsize enable? (1 or 0)?
// s->set_raw_gma(s, 1); // (1 or 0)
// s->set_aec2(s, 0); // automatic exposure sensor? (0 or 1)
// s->set_ae_level(s, 0); // auto exposure levels (-2 to 2)
// s->set_bpc(s, 0); // black pixel correction
// s->set_wpc(s, 0); // white pixel correction
// ******************************************************************************************************************
// Misc small procedures
// ----------------------------------------------------------------
// set up PWM for the illumination LED (flash)
// ----------------------------------------------------------------
// note: I am not sure PWM is very reliable on the esp32cam - requires more testing
void setupFlashPWM() {
ledcSetup(ledChannel, ledFreq, ledRresolution);
ledcAttachPin(brightLED, ledChannel);
brightLed(brightLEDbrightness);
}
// change illumination LED brightness
void brightLed(byte ledBrightness){
brightLEDbrightness = ledBrightness; // store setting
ledcWrite(ledChannel, ledBrightness); // change LED brightness (0 - 255)
if (serialDebug) Serial.println("Brightness changed to " + String(ledBrightness) );
}
// ----------------------------------------------------------------
// returns the current real time as a String
// ----------------------------------------------------------------
// see: https://randomnerdtutorials.com/esp32-date-time-ntp-client-server-arduino/
String localTime() {
struct tm timeinfo;
char ttime[40];
if(!getLocalTime(&timeinfo)) return"Failed to obtain time";
strftime(ttime,40, "%A %B %d %Y %H:%M:%S", &timeinfo);
return ttime;
}
// ----------------------------------------------------------------
// flash the indicator led 'reps' number of times
// ----------------------------------------------------------------
void flashLED(int reps) {
for(int x=0; x < reps; x++) {
digitalWrite(indicatorLED,LOW);
delay(1000);
digitalWrite(indicatorLED,HIGH);
delay(500);
}
}
// ----------------------------------------------------------------
// send standard html header (i.e. start of web page)
// ----------------------------------------------------------------
void sendHeader(WiFiClient &client, char* hTitle) {
// Start page
client.write("HTTP/1.1 200 OK\r\n");
client.write("Content-Type: text/html\r\n");
client.write("Connection: close\r\n");
client.write("\r\n");
client.write("<!DOCTYPE HTML><html lang='en'>\n");
// HTML / CSS
client.printf(R"=====(
<head>
<meta name='viewport' content='width=device-width, initial-scale=1.0'>
<title>%s</title>
<style>
body {
color: black;
background-color: #FFFF00;
text-align: center;
}
input {
background-color: #FF9900;
border: 2px #FF9900;
color: blue;
padding: 3px 6px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
cursor: pointer;
border-radius: 7px;
}
input:hover {
background-color: #FF4400;
}
</style>
</head>
<body>
<h1 style='color:red;'>%s</H1>
)=====", hTitle, hTitle);
}
// ----------------------------------------------------------------
// send a standard html footer (i.e. end of web page)
// ----------------------------------------------------------------
void sendFooter(WiFiClient &client) {
client.write("</body></html>\n");
delay(3);
client.stop();
}
// ----------------------------------------------------------------
// send line of text to both serial port and web page - used by readRGBImage
// ----------------------------------------------------------------
void sendText(WiFiClient &client, String theText) {
if (!sendRGBfile) client.print(theText + "<br>\n");
if (serialDebug || theText.indexOf("error") > 0) Serial.println(theText); // if text contains "error"
}
// ----------------------------------------------------------------
// reset the camera
// ----------------------------------------------------------------
// either hardware(1) or software(0)
void resetCamera(bool type = 0) {
if (type == 1) {
// power cycle the camera module (handy if camera stops responding)
digitalWrite(PWDN_GPIO_NUM, HIGH); // turn power off to camera module
delay(300);
digitalWrite(PWDN_GPIO_NUM, LOW);
delay(300);
initialiseCamera();
} else {
// reset via software (handy if you wish to change resolution or image type etc. - see test procedure)
esp_camera_deinit();
delay(50);
initialiseCamera();
}
}
// ----------------------------------------------------------------
// -change image resolution
// ----------------------------------------------------------------
// if required resolution not supplied it cycles through several
// note: this stops PWM on the flash working for some reason
void changeResolution(framesize_t tRes = FRAMESIZE_96X96) {
esp_camera_deinit(); // disable camera
delay(50);
if (tRes == FRAMESIZE_96X96) { // taken as none supplied so cycle through several
if (FRAME_SIZE_IMAGE == FRAMESIZE_QVGA) tRes = FRAMESIZE_VGA;
else if (FRAME_SIZE_IMAGE == FRAMESIZE_VGA) tRes = FRAMESIZE_XGA;
else if (FRAME_SIZE_IMAGE == FRAMESIZE_XGA) tRes = FRAMESIZE_UXGA;
else tRes = FRAMESIZE_QVGA;
}
FRAME_SIZE_IMAGE = tRes;
initialiseCamera();
if (serialDebug) Serial.println("Camera resolution changed to " + String(tRes));
ImageResDetails = "Unknown"; // set next time image captured
}
// ******************************************************************************************************************
// ----------------------------------------------------------------
// Capture image from camera and save to spiffs or sd card
// ----------------------------------------------------------------
// returns 0 if failed, 1 if stored in spiffs, 2 if stored on sd card
byte storeImage() {
byte sRes = 0; // result flag
fs::FS &fs = SD_MMC; // sd card file system
// capture the image from camera
int currentBrightness = brightLEDbrightness;
if (flashRequired) brightLed(255); // change LED brightness (0 - 255)
camera_fb_t *fb = esp_camera_fb_get(); // capture image frame from camera
if (flashRequired) brightLed(currentBrightness); // change LED brightness back to previous state
if (!fb) {
if (serialDebug) Serial.println("Error: Camera capture failed");
return 0;
}
// save image to Spiffs
if (!sdcardPresent) {
if (serialDebug) Serial.println("Storing image to spiffs only");
SPIFFS.remove(spiffsFilename); // if file name already exists delete it
File file = SPIFFS.open(spiffsFilename, FILE_WRITE); // create new file
if (!file) {
if (serialDebug) Serial.println("Failed to create file in Spiffs - will format and try again");
if (!SPIFFS.format()) { // format spiffs
if (serialDebug) Serial.println("Spiffs format failed");
} else {
file = SPIFFS.open(spiffsFilename, FILE_WRITE); // try again to create new file
if (!file) {
if (serialDebug) Serial.println("Still unable to create file in spiffs");
}
}
}
if (file) { // if file has been created ok write image data to it
if (file.write(fb->buf, fb->len)) {
sRes = 1; // flag as saved ok
} else {
if (serialDebug) Serial.println("Error: failed to write image data to spiffs file");
}
}
if (sRes == 1 && serialDebug) {
Serial.print("The picture has been saved to Spiffs as " + spiffsFilename);
Serial.print(" - Size: ");
Serial.print(file.size());
Serial.println(" bytes");
}
file.close();
}
// save the image to sd card
if (sdcardPresent) {
if (serialDebug) Serial.printf("Storing image #%d to sd card \n", imageCounter);
String SDfilename = "/img/" + String(imageCounter + 1) + ".jpg"; // build the image file name
File file = fs.open(SDfilename, FILE_WRITE); // create file on sd card
if (!file) {
if (serialDebug) Serial.println("Error: Failed to create file on sd-card: " + SDfilename);
} else {
if (file.write(fb->buf, fb->len)) { // File created ok so save image to it
if (serialDebug) Serial.println("Image saved to sd card");
imageCounter ++; // increment image counter
sRes = 2; // flag as saved ok
} else {
if (serialDebug) Serial.println("Error: failed to save image data file on sd card");
}
file.close(); // close image file on sd card
}
}
esp_camera_fb_return(fb); // return frame so memory can be released
return sRes;
} // storeImage
// ******************************************************************************************************************
// ----------------------------------------------------------------
// -Action any user input on root web page
// ----------------------------------------------------------------
void rootUserInput(WiFiClient &client) {
// if button1 was pressed (toggle io pin A)
// Note: if using an input box etc. you would read the value with the command: String Bvalue = server.arg("demobutton1");
// if button1 was pressed (toggle io pin B)
if (server.hasArg("button1")) {
if (serialDebug) Serial.println("Button 1 pressed");
digitalWrite(iopinB,!digitalRead(iopinB)); // toggle output pin on/off
}
// if button2 was pressed (Cycle illumination LED)
if (server.hasArg("button2")) {
if (serialDebug) Serial.println("Button 2 pressed");
if (brightLEDbrightness == 0) brightLed(10); // turn led on dim
else if (brightLEDbrightness == 10) brightLed(40); // turn led on medium
else if (brightLEDbrightness == 40) brightLed(255); // turn led on full
else brightLed(0); // turn led off
}
// if button3 was pressed (toggle flash)
if (server.hasArg("button3")) {
if (serialDebug) Serial.println("Button 3 pressed");
flashRequired = !flashRequired;
}
// if button3 was pressed (format SPIFFS)
if (server.hasArg("button4")) {
if (serialDebug) Serial.println("Button 4 pressed");
if (!SPIFFS.format()) {
if (serialDebug) Serial.println("Error: Unable to format Spiffs");
} else {
if (serialDebug) Serial.println("Spiffs memory has been formatted");
}
}
// if button4 was pressed (change resolution)
if (server.hasArg("button5")) {
if (serialDebug) Serial.println("Button 5 pressed");
changeResolution(); // cycle through some options
}
// if brightness was adjusted - cameraImageBrightness
if (server.hasArg("bright")) {
String Tvalue = server.arg("bright"); // read value
if (Tvalue != NULL) {
int val = Tvalue.toInt();
if (val >= -2 && val <= 2 && val != cameraImageBrightness) {
if (serialDebug) Serial.printf("Brightness changed to %d\n", val);
cameraImageBrightness = val;
cameraImageSettings(); // Apply camera image settings
}
}
}
// if exposure was adjusted - cameraImageExposure
if (server.hasArg("exp")) {
if (serialDebug) Serial.println("Exposure has been changed");
String Tvalue = server.arg("exp"); // read value
if (Tvalue != NULL) {
int val = Tvalue.toInt();
if (val >= 0 && val <= 1200 && val != cameraImageExposure) {
if (serialDebug) Serial.printf("Exposure changed to %d\n", val);
cameraImageExposure = val;
cameraImageSettings(); // Apply camera image settings
}
}
}
// if image gain was adjusted - cameraImageGain
if (server.hasArg("gain")) {
if (serialDebug) Serial.println("Gain has been changed");
String Tvalue = server.arg("gain"); // read value
if (Tvalue != NULL) {
int val = Tvalue.toInt();
if (val >= 0 && val <= 31 && val != cameraImageGain) {
if (serialDebug) Serial.printf("Gain changed to %d\n", val);
cameraImageGain = val;
cameraImageSettings(); // Apply camera image settings
}
}
}
}
// ----------------------------------------------------------------
// -root web page requested i.e. http://x.x.x.x/
// ----------------------------------------------------------------
// web page with control buttons, links etc.
void handleRoot() {
getNTPtime(2); // refresh current time from NTP server
WiFiClient client = server.client(); // open link with client
rootUserInput(client); // Action any user input from this web page
// html header
sendHeader(client, "ESP32Cam demo sketch");
client.write("<FORM action='/' method='post'>\n"); // used by the buttons in the html (action = the web page to send it to
// --------------------------------------------------------------------
// html main body
// Info on the arduino ethernet library: https://www.arduino.cc/en/Reference/Ethernet
// Info in HTML: https://www.w3schools.com/html/
// Info on Javascript (can be inserted in to the HTML): https://www.w3schools.com/js/default.asp
// Verify your HTML is valid: https://validator.w3.org/
client.write("Sketch from: github.com/alanesq/esp32cam-demo<br>");
// ---------------------------------------------------------------------------------------------
// info which is periodically updated usin AJAX - https://www.w3schools.com/xml/ajax_intro.asp
// sd card
if (!sdcardPresent) {
client.println("<p style='color:blue;'>NO SD CARD DETECTED<span id=uImages></span><span id=uUsed></span><span id=uRemain></span></p>"); // spans will be left empty
} else {
client.println("<p>SD Card: <span id=uImages> - </span> images stored, <span id=uUsed> - </span>MB used , <span id=uRemain> - </span>MB remaining</p>\n");
}
// illumination/flash led
client.println("Illumination led brightness=<span id=uBrightness> - </span>, Flash is <span id=uFlash> - </span>");
// Current real time
client.println("<br>Current time: <span id=uTime> - </span>");
// gpio pin status
client.print("<br>GPIO output pin 12 is: <span id=uGPIO12> - </span>, GPIO input pin 13 is: <span id=uGPIO13> - </span>");
// image resolution
client.println("<br>Image size: <span id=uRes> - </span>");
// Javascript - to periodically update above getting info from http://x.x.x.x/data
client.printf(R"=====(
<script>
function getData() {
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
var receivedArr = this.responseText.split(',');
document.getElementById('uImages').innerHTML = receivedArr[0];
document.getElementById('uUsed').innerHTML = receivedArr[1];
document.getElementById('uRemain').innerHTML = receivedArr[2];
document.getElementById('uBrightness').innerHTML = receivedArr[3];
document.getElementById('uTime').innerHTML = receivedArr[4];
document.getElementById('uGPIO12').innerHTML = receivedArr[5];
document.getElementById('uGPIO13').innerHTML = receivedArr[6];
document.getElementById('uFlash').innerHTML = receivedArr[7];
document.getElementById('uRes').innerHTML = receivedArr[8];
}
};
xhttp.open('GET', 'data', true);
xhttp.send();}
getData();
setInterval(function() { getData(); }, %d);
</script>
)=====", datarefresh);
// ---------------------------------------------------------------------------------------------
// // touch input on the two gpio pins
// client.printf("<p>Touch on pin 12: %d </p>\n", touchRead(T5) );
// client.printf("<p>Touch on pin 13: %d </p>\n", touchRead(T4) );
// Control buttons
client.write("<br><br>");
client.write("<input style='height: 35px;' name='button1' value='Toggle pin 12' type='submit'> \n");
client.write("<input style='height: 35px;' name='button2' value='Cycle illumination LED' type='submit'> \n");
client.write("<input style='height: 35px;' name='button3' value='Toggle Flash' type='submit'> \n");
client.write("<input style='height: 35px;' name='button4' value='Wipe SPIFFS memory' type='submit'> \n");
client.write("<input style='height: 35px;' name='button5' value='Change Resolution' type='submit'><br> \n");
// Image setting controls
client.println("<br>CAMERA SETTINGS: ");
client.printf("Brightness: <input type='number' style='width: 50px' name='bright' title='from -2 to +2' min='-2' max='2' value='%d'> \n", cameraImageBrightness);
client.printf("Exposure: <input type='number' style='width: 50px' name='exp' title='from 0 to 1200' min='0' max='1200' value='%d'> \n", cameraImageExposure);
client.printf("Gain: <input type='number' style='width: 50px' name='gain' title='from 0 to 30' min='0' max='30' value='%d'>\n", cameraImageGain);
client.println(" <input type='submit' name='submit' value='Submit change / Refresh Image'>");
client.println("<br>Set exposure and gain to zero for auto adjust");
// links to the other pages available
client.write("<br><br>LINKS: \n");
client.write("<a href='/photo'>Capture an image</a> - \n");
client.write("<a href='/img'>View stored image</a> - \n");
client.write("<a href='/rgb'>Capture Image as raw RGB data</a> - \n");
client.write("<a href='/stream'>Live stream</a> - \n");
client.write("<a href='/test'>Test procedure</a><br>\n");
// capture and show a jpg image
client.write("<br><a href='/jpg'>"); // make it a link
client.write("<img src='/jpg' /> </a>"); // show image from http://x.x.x.x/jpg
// javascript to refresh the image periodically
client.printf(R"=====(
<script>
function refreshImage(){
var timestamp = new Date().getTime();
var el = document.getElementById('image1');
var queryString = '?t=' + timestamp;
el.src = '/jpg' + queryString;
}
setInterval(function() { refreshImage(); }, %d);
</script>
)=====", imagerefresh);
// --------------------------------------------------------------------
sendFooter(client); // close web page
} // handleRoot
// ----------------------------------------------------------------
// -data web page requested i.e. http://x.x.x.x/data
// ----------------------------------------------------------------
// suplies changing info to update root web page as comma seperated String
void handleData(){
// sd sdcard
uint32_t SDusedSpace = 0;
uint32_t SDtotalSpace = 0;
uint32_t SDfreeSpace = 0;
if (sdcardPresent) {
SDusedSpace = SD_MMC.usedBytes() / (1024 * 1024);
SDtotalSpace = SD_MMC.totalBytes() / (1024 * 1024);
SDfreeSpace = SDtotalSpace - SDusedSpace;
}
String reply = "";
if (sdcardPresent) reply += String(imageCounter); // images stored
reply += ",";
if (sdcardPresent) reply += String(SDusedSpace); // space used on sd card
reply += ",";
if (sdcardPresent) reply += String(SDfreeSpace); // space remaining on sd card
reply += ",";
reply += String(brightLEDbrightness); // illumination led brightness
reply += ",";
reply += localTime(); // current date/time
reply += ",";
reply += (digitalRead(iopinB)==1) ? "ON" : "OFF"; // output gpio pin status
reply += ",";
reply += (digitalRead(iopinA)==1) ? "ON" : "OFF"; // output gpio pin status
reply += ",";
reply += (flashRequired==1) ? "ON" : "OFF"; // if flash is enabled
reply += ",";
reply += ImageResDetails; // if flash is enabled
//reply += ",";
server.send(200, "text/plane", reply); //Send millis value only to client ajax request
}
// ******************************************************************************************************************
// ----------------------------------------------------------------
// -photo save to sd card/spiffs i.e. http://x.x.x.x/photo
// ----------------------------------------------------------------
// web page to capture an image from camera and save to spiffs or sd card
void handlePhoto() {
WiFiClient client = server.client(); // open link with client
// log page request including clients IP
IPAddress cIP = client.remoteIP();
if (serialDebug) Serial.println("Save photo requested by " + cIP.toString());
byte sRes = storeImage(); // capture and save an image to sd card or spiffs (store sucess or failed flag - 0=fail, 1=spiffs only, 2=spiffs and sd card)
// html header
sendHeader(client, "Capture and save image");
// html body
if (sRes == 2) {
client.printf("<p>Image saved to sd card as image number %d </p>\n", imageCounter);
} else if (sRes == 1) {
client.write("<p>Image saved in Spiffs</p>\n");
} else {
client.write("<p>Error: Failed to save image</p>\n");
}
client.write("<a href='/'>Return</a>\n"); // link back
// close web page
sendFooter(client);
} // handlePhoto
// ----------------------------------------------------------------
// -display image stored on sd card or SPIFFS i.e. http://x.x.x.x/img?img=x
// ----------------------------------------------------------------
// Display a previously stored image, default image = most recent
// returns 1 if image displayed ok
bool handleImg() {
WiFiClient client = server.client(); // open link with client
bool pRes = 0;
// log page request including clients IP
IPAddress cIP = client.remoteIP();
if (serialDebug) Serial.println("Display stored image requested by " + cIP.toString());
int imgToShow = imageCounter; // default to showing most recent file
// get image number from url parameter
if (server.hasArg("img") && sdcardPresent) {
String Tvalue = server.arg("img"); // read value
imgToShow = Tvalue.toInt(); // convert string to int
if (imgToShow < 1 || imgToShow > imageCounter) imgToShow = imageCounter; // validate image number
}
// if stored on sd card
if (sdcardPresent) {
if (serialDebug) Serial.printf("Displaying image #%d from sd card", imgToShow);
String tFileName = "/img/" + String(imgToShow) + ".jpg";
fs::FS &fs = SD_MMC; // sd card file system
File timg = fs.open(tFileName, "r");
if (timg) {
size_t sent = server.streamFile(timg, "image/jpeg"); // send the image
timg.close();
pRes = 1; // flag sucess
} else {
if (serialDebug) Serial.println("Error: image file not found");
sendHeader(client, "Display stored image");
client.write("<p>Error: Image not found</p></html>\n");
client.write("<br><a href='/'>Return</a>\n"); // link back
sendFooter(client); // close web page
}
}
// if stored in SPIFFS
if (!sdcardPresent) {
if (serialDebug) Serial.println("Displaying image from spiffs");
// check file exists
if (!SPIFFS.exists(spiffsFilename)) {
sendHeader(client, "Display stored image");
client.write("Error: No image found to display\n");
client.write("<br><a href='/'>Return</a>\n"); // link back
sendFooter(client); // close web page
return 0;
}
File f = SPIFFS.open(spiffsFilename, "r"); // read file from spiffs
if (!f) {
if (serialDebug) Serial.println("Error reading " + spiffsFilename);
sendHeader(client, "Display stored image");
client.write("Error reading file from Spiffs\n");
client.write("<br><a href='/'>Return</a>\n"); // link back
sendFooter(client); // close web page
}
else {
size_t sent = server.streamFile(f, "image/jpeg"); // send file to web page
if (!sent) {
if (serialDebug) Serial.println("Error sending " + spiffsFilename);
} else {
pRes = 1; // flag sucess
}
f.close();
}
}
return pRes;
} // handleImg
// ******************************************************************************************************************
// ----------------------------------------------------------------
// -invalid web page requested
// ----------------------------------------------------------------
// Note: shows a different way to send the HTML reply
void handleNotFound() {
String tReply;
if (serialDebug) Serial.print("Invalid page requested");
tReply = "File Not Found\n\n";
tReply += "URI: ";
tReply += server.uri();
tReply += "\nMethod: ";
tReply += ( server.method() == HTTP_GET ) ? "GET" : "POST";
tReply += "\nArguments: ";
tReply += server.args();
tReply += "\n";
for ( uint8_t i = 0; i < server.args(); i++ ) {
tReply += " " + server.argName ( i ) + ": " + server.arg ( i ) + "\n";
}
server.send ( 404, "text/plain", tReply );
tReply = ""; // clear variable
} // handleNotFound
// ******************************************************************************************************************
// ----------------------------------------------------------------
// -access image data as RGB - i.e. http://x.x.x.x/rgb
// ----------------------------------------------------------------
//Demonstration on how to access raw RGB data from the camera
// Notes:
// Set sendRGBfile to 1 in the settings at top of sketch to just send the raw rgb data as a file which can then be used with
// the Processing sketch: https://github.com/alanesq/esp32cam-demo/blob/master/Misc/displayRGB.pde
// otherwise a web page is displayed showing some sample rgb data usage.
// You may want to disable auto white balance when experimenting with RGB otherwise the camera is always trying to adjust the
// image colours to mainly white. (disable in the 'cameraImageSettings' procedure).
// It will fail on the higher resolutions as it requires more than the 4mb of available psram to store the data (1600x1200x3 bytes)
// I learned how to read the RGB data from: https://github.com/Makerfabs/Project_Touch-Screen-Camera/blob/master/Camera_v2/Camera_v2.ino
void readRGBImage() {
// used for timing operations
WiFiClient client = server.client();
uint32_t tTimer; // used to time tasks // open link with client
if (!sendRGBfile) {
// html header
sendHeader(client, "Show RGB data");
// page title including clients IP
IPAddress cIP = client.remoteIP();
sendText(client, "Live image as rgb data, requested by " + cIP.toString()); // 'sendText' sends the String to both serial port and web page
}
// make sure psram is available
if (!psramFound()) {
sendText(client,"error: no psram available to store the RGB data");
client.write("<br><a href='/'>Return</a>\n"); // link back
if (!sendRGBfile) sendFooter(client); // close web page
return;
}
// ****** the main code for converting an image to RGB data *****
// capture a live image from camera (as a jpg)
camera_fb_t * fb = NULL;
tTimer = millis(); // store time that image capture started
fb = esp_camera_fb_get();
if (!fb) {
sendText(client,"error: failed to capture image from camera");
client.write("<br><a href='/'>Return</a>\n"); // link back
if (!sendRGBfile) sendFooter(client); // close web page
return;
} else {
sendText(client, "-JPG image capture took " + String(millis() - tTimer) + " milliseconds"); // report time it took to capture an image
sendText(client, "-Image resolution=" + String(fb->width) + "x" + String(fb->height));
sendText(client, "-Image size=" + String(fb->len) + " bytes");
sendText(client, "-Image format=" + String(fb->format));
sendText(client, "-Free memory=" + String(ESP.getFreeHeap()) + " bytes");
}
/*
// display captured image using base64 - seems a bit unreliable especially with larger images?
if (!sendRGBfile) {
client.print("<br>Displaying image direct from frame buffer");
String base64data = base64::encode(fb->buf, fb->len); // convert buffer to base64
client.print(" - Base64 data length = " + String(base64data.length()) + " bytes\n" );
client.print("<br><img src='data:image/jpg;base64," + base64data + "'></img><br>\n");
}
*/
// allocate memory to store the rgb data (in psram, 3 bytes per pixel)
sendText(client,"<br>Free psram before rgb data allocated = " + String(heap_caps_get_free_size(MALLOC_CAP_SPIRAM) / 1024) + "K");
void *ptrVal = NULL; // create a pointer for memory location to store the data
uint32_t ARRAY_LENGTH = fb->width * fb->height * 3; // calculate memory required to store the RGB data (i.e. number of pixels in the jpg image x 3)
if (heap_caps_get_free_size( MALLOC_CAP_SPIRAM) < ARRAY_LENGTH) {
sendText(client,"error: not enough free psram to store the rgb data");
if (!sendRGBfile) {
client.write("<br><a href='/'>Return</a>\n"); // link back
sendFooter(client); // close web page
}
return;
}
ptrVal = heap_caps_malloc(ARRAY_LENGTH, MALLOC_CAP_SPIRAM); // allocate memory space for the rgb data
uint8_t *rgb = (uint8_t *)ptrVal; // create the 'rgb' array pointer to the allocated memory space
sendText(client,"Free psram after rgb data allocated = " + String(heap_caps_get_free_size(MALLOC_CAP_SPIRAM) / 1024) + "K");
// convert the captured jpg image (fb) to rgb data (store in 'rgb' array)
tTimer = millis(); // store time that image conversion process started
bool jpeg_converted = fmt2rgb888(fb->buf, fb->len, PIXFORMAT_JPEG, rgb);
if (!jpeg_converted) {
sendText(client,"error: failed to convert image to RGB data");
if (!sendRGBfile) {
client.write("<br><a href='/'>Return</a>\n"); // link back
sendFooter(client); // close web page
}
return;
}
sendText(client, "Conversion from jpg to RGB took " + String(millis() - tTimer) + " milliseconds");// report how long the conversion took
// if sendRGBfile is set then just send raw RGB data and close
if (sendRGBfile) {
client.write(rgb, ARRAY_LENGTH); // send the raw rgb data
esp_camera_fb_return(fb); // camera frame buffer
delay(3);
client.stop();
return;
}
// ****** examples of using the resulting RGB data *****
tft.initR(INITR_BLACKTAB);
tft.setRotation(1);
//tft.fillScreen(ST77XX_BLACK);
tft.drawPixel(tft.width()/2, tft.height()/2,tft.color565(0,255,0));
// display some of the resulting data
uint32_t resultsToShow = 57600; // how much data to display
//for (int xx=0;xx<161;++xx){
// }
sendText(client,"<br>R,G,B data for first " + String(resultsToShow / 3) + " pixels of image");
for (uint32_t i = 0; i < resultsToShow-2; i+=3) {
// sendText(client,String(rgb[i+2]) + "," + String(rgb[i+1]) + "," + String(rgb[i+0])); // Red , Green , Blue
// calculate the x and y coordinate of the current pixel
uint16_t x = (i / 3) % fb->width;
uint16_t y = floor( (i / 3) / fb->width);
tft.drawPixel(x,y,tft.color565(rgb[i+2],rgb[i+1],rgb[i+0]));
//tft.drawPixel(x,y,tft.color565(x,y,0));
}
// find the average values for each colour over entire image
uint32_t aRed = 0;
uint32_t aGreen = 0;
uint32_t aBlue = 0;
for (uint32_t i = 0; i < (ARRAY_LENGTH - 2); i+=3) { // go through all data and add up totals
aBlue+=rgb[i];
aGreen+=rgb[i+1];
aRed+=rgb[i+2];
}
aRed = aRed / (fb->width * fb->height); // divide total by number of pixels to give the average value
aGreen = aGreen / (fb->width * fb->height);
aBlue = aBlue / (fb->width * fb->height);
sendText(client,"Average Blue = " + String(aBlue));
sendText(client,"Average Green = " + String(aGreen));
sendText(client,"Average Red = " + String(aRed));
// *******************************************************
client.write("<br><a href='/'>Return</a>\n"); // link back
sendFooter(client); // close web page
// finished with the data so free up the memory space used in psram
esp_camera_fb_return(fb); // camera frame buffer
heap_caps_free(ptrVal); // rgb data
} // readRGBImage
// ******************************************************************************************************************
// ----------------------------------------------------------------
// -get time from ntp server
// ----------------------------------------------------------------
bool getNTPtime(int sec) {
uint32_t start = millis(); // timeout timer
do {
time(&now);
localtime_r(&now, &timeinfo);
if (serialDebug) Serial.print(".");
delay(100);
} while (((millis() - start) <= (1000 * sec)) && (timeinfo.tm_year < (2016 - 1900)));
if (timeinfo.tm_year <= (2016 - 1900)) return false; // the NTP call was not successful
if (serialDebug) {
Serial.print("now ");
Serial.println(now);
}
// Display time
if (serialDebug) {
char time_output[30];
strftime(time_output, 30, "%a %d-%m-%y %T", localtime(&now));
Serial.println(time_output);
Serial.println();
}
return true;
}
// ----------------------------------------------------------------
// -capture jpg image and send i.e. http://x.x.x.x/jpg
// ----------------------------------------------------------------
bool handleJPG() {
WiFiClient client = server.client(); // open link with client
char buf[32];
// capture the jpg image from camera
camera_fb_t * fb = esp_camera_fb_get();
if (!fb) {
if (serialDebug) Serial.println("Error: failed to capture image");
return 0;
}
// store image resolution info.
ImageResDetails = String(fb->width) + "x" + String(fb->height);
// html to send a jpg
const char HEADER[] = "HTTP/1.1 200 OK\r\nAccess-Control-Allow-Origin: *\r\n";
const char CTNTTYPE[] = "Content-Type: image/jpeg\r\nContent-Length: ";
const int hdrLen = strlen(HEADER);
const int cntLen = strlen(CTNTTYPE);
client.write(HEADER, hdrLen);
client.write(CTNTTYPE, cntLen);
sprintf( buf, "%d\r\n\r\n", fb->len); // put text size in to 'buf' char array and send
client.write(buf, strlen(buf));
// send the captured jpg data
client.write((char *)fb->buf, fb->len);
// close client connection
delay(3);
client.stop();
// return image frame so memory can be released
esp_camera_fb_return(fb);
return 1;
} // handleJPG
// ----------------------------------------------------------------
// -stream requested i.e. http://x.x.x.x/stream
// ----------------------------------------------------------------
// Sends video stream - thanks to Uwe Gerlach for the code showing me how to do this
void handleStream(){
WiFiClient client = server.client(); // open link with client
char buf[32];
camera_fb_t * fb = NULL;
// log page request including clients IP
IPAddress cIP = client.remoteIP();
if (serialDebug) Serial.println("Live stream requested by " + cIP.toString());
// html
const char HEADER[] = "HTTP/1.1 200 OK\r\n" \
"Access-Control-Allow-Origin: *\r\n" \
"Content-Type: multipart/x-mixed-replace; boundary=123456789000000000000987654321\r\n";
const char BOUNDARY[] = "\r\n--123456789000000000000987654321\r\n"; // marks end of each image frame
const char CTNTTYPE[] = "Content-Type: image/jpeg\r\nContent-Length: "; // marks start of image data
const int hdrLen = strlen(HEADER); // length of the stored text, used when sending to web page
const int bdrLen = strlen(BOUNDARY);
const int cntLen = strlen(CTNTTYPE);
client.write(HEADER, hdrLen);
client.write(BOUNDARY, bdrLen);
// send live jpg images until client disconnects
while (true)
{
if (!client.connected()) break;
fb = esp_camera_fb_get(); // capture live image as jpg
if (!fb) {
if (serialDebug) Serial.println("Error: failed to capture jpg image");
} else {
// send image
client.write(CTNTTYPE, cntLen); // send content type html (i.e. jpg image)
sprintf( buf, "%d\r\n\r\n", fb->len); // format the image's size as html and put in to 'buf'
client.write(buf, strlen(buf)); // send result (image size)
client.write((char *)fb->buf, fb->len); // send the image data
client.write(BOUNDARY, bdrLen); // send html boundary see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type
esp_camera_fb_return(fb); // return image buffer so memory can be released
}
}
if (serialDebug) Serial.println("Video stream stopped");
delay(3);
client.stop();
} // handleStream
// ******************************************************************************************************************
// ----------------------------------------------------------------
// request a web page
// ----------------------------------------------------------------
// @param page web page to request
// @param received String to store response in
// @param maxWaitTime maximum time to wait for reply (ms)
// @returns http code
// see: https://randomnerdtutorials.com/esp32-http-get-post-arduino/#http-get-1
// to do: limit size of reply
int requestWebPage(String* page, String* received, int maxWaitTime=5000){
if (serialDebug) Serial.println("requesting web page: " + *page);
WiFiClient client;
HTTPClient http; // see: https://github.com/esp8266/Arduino/blob/master/libraries/ESP8266HTTPClient
http.setTimeout(maxWaitTime);
http.begin(client, *page); // for https requires (client, *page, thumbprint) e.g.String thumbprint="08:3B:71:72:02:43:6E:CA:ED:42:86:93:BA:7E:DF:81:C4:BC:62:30";
int httpCode = http.GET(); // http codes: https://en.wikipedia.org/wiki/List_of_HTTP_status_codes
if (serialDebug) Serial.println("http code: " + String(httpCode));
if (httpCode > 0) {
*received = http.getString();
} else {
*received = "error:" + String(httpCode);
}
if (serialDebug) Serial.println(*received);
http.end(); //Close connection
if (serialDebug) Serial.println("Web connection closed");
return httpCode;
} // requestWebPage
// ******************************************************************************************************************
// ----------------------------------------------------------------
// -test procedure i.e. http://x.x.x.x/test
// ----------------------------------------------------------------
void handleTest() {
WiFiClient client = server.client(); // open link with client
// log page request including clients IP
IPAddress cIP = client.remoteIP();
if (serialDebug) Serial.println("Test page requested by " + cIP.toString());
// html header
sendHeader(client, "Testing");
// html body
// -------------------------------------------------------------------
// test code goes here
// demo of drawing on the camera image using javascript / html canvas
// could be of use to show area of interest on the image etc. - see https://www.w3schools.com/html/html5_canvas.asp
// creat a DIV and put image in it with a html canvas on top of it
int imageWidth = 640; // image dimensions on web page
int imageHeight = 480;
client.println("<div style='display:inline-block;position:relative;'>");
client.println("<img style='position:absolute;z-index:10;' src='/jpg' width='" + String(imageWidth) + "' height='" + String(imageHeight) + "' />");
client.println("<canvas style='position:relative;z-index:20;' id='myCanvas' width='" + String(imageWidth) + "' height='" + String(imageHeight) + "'></canvas>");
client.println("</div>");
// javascript to draw on the canvas
client.println("<script>");
client.println("var imageWidth = " + String(imageWidth) + ";");
client.println("var imageHeight = " + String(imageHeight) + ";");
client.print (R"=====(
// connect to the canvas
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
ctx.strokeStyle = "red";
// draw on image
ctx.rect(imageWidth / 2, imageHeight / 2, 60, 40); // box
ctx.moveTo(20, 20); ctx.lineTo(200, 100); // line
ctx.font = "30px Arial"; ctx.fillText("Hello World", 50, imageHeight - 50); // text
ctx.stroke();
</script>\n)=====");
/*
// demo of how to request a web page
String page = "http://urlhere.com"; // url to request
String response; // reply will be stored here
int httpCode = requestWebPage(&page, &response);
// show results
client.println("Web page requested: '" + page + "' - http code: " + String(httpCode));
client.print("<xmp>'"); // enables the html code to be displayed
client.print(response);
client.println("'</xmp><br>");
*/
/*
// // demo useage of the mcp23017 io chipnote: this stops PWM on the flash working for some reason
#if useMCP23017 == 1
while(1) {
mcp.digitalWrite(0, HIGH);
int q = mcp.digitalRead(8);
client.print("<p>HIGH, input =" + String(q) + "</p>");
delay(1000);
mcp.digitalWrite(0, LOW);
client.print("<p>LOW</p>");
delay(1000);
}
#endif
*/
// -------------------------------------------------------------------
client.println("<br><br><a href='/'>Return</a>"); // link back
sendFooter(client); // close web page
} // handleTest
// ******************************************************************************************************************
// end
Не в сети
Полезные ссылки на тему ESP32-CAM.
https://github.com/dproldan/Esp32AutoCamera
http://www.hpcba.com/en/latest/source/D … 2-CAM.html
Не в сети
Страницы 1