Ciao a tutti,
Premesso che non sono uno sviluppatore ma solo un appassionato, ho costruito una Birdcam con ESP32-Cam partendo dal progetto (e dal codice) su ESP32-CAM Shield PCB with Telegram: Photos, Outputs, Sensor, Motion | Random Nerd Tutorials
Ecco i link di alcune foto della realizzazione (Case 3D disegnato da me)
Ho modificato sia il progetto HQ che il codice, rimuovendo il sensore BME280 e aggiungendo alcune funzionalità sw/abilitando altri comandi (start/stop detection; flash quando si scatta foto; mostrare il Boot time ; comando di reboot) che non erano presenti nel progetto originale ma mi sono rimasti 2 problemi abbastanza bloccanti che non riesco a risolvere .. nonostante ci abbia provato per diverso tempo senza successo..
Per questo chiedo il vostro aiuto..
I 2 problemi che ho sono i seguenti:
-
quando viene scattata una foto (sia con rilevamento automatico del movimento che con comando manuale "/photo" tramite Telegram) l'immagine inviata a Telegram NON è quella scattata in quel momento ma sembra essere quella scattata nel loop precedente o comunque in un momento diverso (antecedente allo scatto)
Spero di essermi spiegato chiaramente.. -
Il flash (che credevo di aver attivato prima del momento dello scatto e disattivato dopo 1000 msec) è invece attivato nel momento sbagliato... quindi le immagini sono completamente nere se le foto sono scattate al buio/di notte (ho bisogno del flash proprio perché vorrei usarlo anche di notte)..
Entrambi i problemi sembrano essere legati a loop/timing errati... ma non sono riuscito a identificare l'errore nel codice, che riporto qui sotto
C'è qualcuno che vuole aiutarmi?
Grazie in anticipo
/*
by Stefano
*/
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include "soc/soc.h"
#include "soc/rtc_cntl_reg.h"
#include "esp_camera.h"
#include <UniversalTelegramBot.h>
#include <ArduinoJson.h>
#include <Wire.h>
#include "SparkFunBME280.h"
// Replace with your network credentials
const char* ssid = "myssid";
const char* password = "mypassword";
// Use @myidbot to find out the chat ID of an individual or a group
// Also note that you need to click "start" on a bot before it can
// message you
String chatId = "mychatid";
// Initialize Telegram BOT
String BOTtoken = "mytoken";
//aggiunti da Stefano
bool sendPhoto = false;
unsigned long bootTime = 0; // Variable to store the boot time
WiFiClientSecure clientTCP;
UniversalTelegramBot bot(BOTtoken, clientTCP);
//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 FLASH_LED_PIN 4
bool flashState = LOW;
// Motion Sensor
bool motionDetected = false;
//aggiunti da Stefano
bool capturestop = false;
bool rebootloop = false;
int botRequestDelay = 1000; // mean time between scan messages
long lastTimeBotRan; // last time messages' scan has been done
void handleNewMessages(int numNewMessages);
String sendPhotoTelegram();
// Indicates when motion is detected
static void IRAM_ATTR detectsMovement(void * arg) {
//Serial.println("MOTION DETECTED!!!");
motionDetected = true;
}
void setup() {
WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0);
Serial.begin(115200);
pinMode(FLASH_LED_PIN, OUTPUT);
digitalWrite(FLASH_LED_PIN, flashState);
WiFi.mode(WIFI_STA);
Serial.println();
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
clientTCP.setCACert(TELEGRAM_CERTIFICATE_ROOT); // Add root certificate for api.telegram.org
while (WiFi.status() != WL_CONNECTED) {
Serial.print(".");
delay(500);
}
Serial.println();
Serial.print("ESP32-CAM IP Address: ");
Serial.println(WiFi.localIP());
// Save the boot time
bootTime = millis();
// Give some time for the PIR sensor to warm up
Serial.println("Waiting 20 sec for the sensor to warm up on first boot");
delay(20000); // 20 seconds
// Blink LED 3 times to indicate PIR sensor warmed up
digitalWrite(4, HIGH);
delay(200);
digitalWrite(4, LOW);
delay(100);
digitalWrite(4, HIGH);
delay(200);
digitalWrite(4, LOW);
delay(100);
digitalWrite(4, HIGH);
delay(200);
digitalWrite(4, LOW);
Serial.println("PIR sensor is ACTIVE");
// Send welcome message to Telegram
// appare solo al boot
String welcomeMessage = "Hello! I'm now online.";
bot.sendMessage(chatId, welcomeMessage);
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_JPEG;
//init with high specs to pre-allocate larger buffers
if (psramFound()) {
config.frame_size = FRAMESIZE_UXGA;
config.jpeg_quality = 10; //0-63 lower number means higher quality
config.fb_count = 2;
} else {
config.frame_size = FRAMESIZE_SVGA;
config.jpeg_quality = 12; //0-63 lower number means higher quality
config.fb_count = 1;
}
// camera init
esp_err_t err = esp_camera_init(&config);
if (err != ESP_OK) {
Serial.printf("Camera init failed with error 0x%x", err);
delay(1000);
ESP.restart();
}
// Drop down frame size for higher initial frame rate
sensor_t * s = esp_camera_sensor_get();
s->set_framesize(s, FRAMESIZE_XGA); // UXGA|SXGA|XGA|SVGA|VGA|CIF|QVGA|HQVGA|QQVGA
// PIR Motion Sensor mode INPUT_PULLUP
//err = gpio_install_isr_service(0);
err = gpio_isr_handler_add(GPIO_NUM_13, &detectsMovement, (void *) 13);
if (err != ESP_OK) {
Serial.printf("handler add failed with error 0x%x \r\n", err);
}
err = gpio_set_intr_type(GPIO_NUM_13, GPIO_INTR_POSEDGE);
if (err != ESP_OK) {
Serial.printf("set intr type failed with error 0x%x \r\n", err);
}
}
// Inizio LOOP
void loop() {
if (sendPhoto) {
Serial.println("Preparing photo");
sendPhotoTelegram();
sendPhoto = false;
}
if (capturestop) {
if (motionDetected) {
bot.sendMessage(chatId, "Motion detected!!", "");
Serial.println("Motion Detected");
// Accende il flash
pinMode(4, OUTPUT);
digitalWrite(4, HIGH);
sendPhotoTelegram();
motionDetected = false;
// Spegni il flash dopo aver scattato la foto
delay(1000);
digitalWrite(4, LOW);
delay (20000);
}
}
if (millis() > lastTimeBotRan + botRequestDelay) {
int numNewMessages = bot.getUpdates(bot.last_message_received + 1);
while (numNewMessages) {
Serial.println("got response");
handleNewMessages(numNewMessages);
numNewMessages = bot.getUpdates(bot.last_message_received + 1);
}
lastTimeBotRan = millis();
}
// Function to handle the reboot command
if (rebootloop) {
handleRebootCommand();
rebootloop = false;
}
}
// Fine LOOP
// Function to handle the reboot command
void handleRebootCommand() {
// Perform a complete reboot of the ESP32
delay (1000);
ESP.restart();
delay (1000);
}
String sendPhotoTelegram() {
const char* myDomain = "api.telegram.org";
String getAll = "";
String getBody = "";
// Accende il flash
pinMode(4, OUTPUT);
digitalWrite(4, HIGH);
// Scattare una foto
camera_fb_t * fb = NULL;
fb = esp_camera_fb_get();
if (!fb) {
Serial.println("Camera capture failed");
delay(1000);
ESP.restart();
return "Camera capture failed";
}
// Spegni il flash dopo aver scattato la foto
delay(1000);
digitalWrite(4, LOW);
Serial.println("Connect to " + String(myDomain));
if (clientTCP.connect(myDomain, 443)) {
Serial.println("Connection successful");
String head = "--Stefano\r\nContent-Disposition: form-data; name=\"chat_id\"; \r\n\r\n" + chatId + "\r\n--Stefano\r\nContent-Disposition: form-data; name=\"photo\"; filename=\"esp32-cam.jpg\"\r\nContent-Type: image/jpeg\r\n\r\n";
String tail = "\r\n--Stefano--\r\n";
uint16_t imageLen = fb->len;
uint16_t extraLen = head.length() + tail.length();
uint16_t totalLen = imageLen + extraLen;
clientTCP.println("POST /bot" + BOTtoken + "/sendPhoto HTTP/1.1");
clientTCP.println("Host: " + String(myDomain));
clientTCP.println("Content-Length: " + String(totalLen));
clientTCP.println("Content-Type: multipart/form-data; boundary=Stefano");
clientTCP.println();
clientTCP.print(head);
uint8_t *fbBuf = fb->buf;
size_t fbLen = fb->len;
for (size_t n = 0; n < fbLen; n = n + 1024) {
if (n + 1024 < fbLen) {
clientTCP.write(fbBuf, 1024);
fbBuf += 1024;
}
else if (fbLen % 1024 > 0) {
size_t remainder = fbLen % 1024;
clientTCP.write(fbBuf, remainder);
}
}
clientTCP.print(tail);
esp_camera_fb_return(fb);
// Da capire a cosa serve?
int waitTime = 10000; // timeout 10 seconds
long startTimer = millis();
boolean state = false;
while ((startTimer + waitTime) > millis()) {
Serial.print(".");
delay(100);
while (clientTCP.available()) {
char c = clientTCP.read();
if (state == true) getBody += String(c);
if (c == '\n') {
if (getAll.length() == 0) state = true;
getAll = "";
}
else if (c != '\r')
getAll += String(c);
startTimer = millis();
}
if (getBody.length() > 0) break;
}
clientTCP.stop();
Serial.println(getBody);
}
else {
getBody = "Connected to api.telegram.org failed.";
Serial.println("Connected to api.telegram.org failed.");
}
return getBody;
}
// Fine Sendphoto to Telegram
// Inizio Handle New messages
void handleNewMessages(int numNewMessages) {
Serial.print("Handle New Messages: ");
Serial.println(numNewMessages);
for (int i = 0; i < numNewMessages; i++) {
// Chat id of the requester
String chat_id = String(bot.messages[i].chat_id);
if (chat_id != chatId) {
bot.sendMessage(chat_id, "Unauthorized user", "");
continue;
}
// Print the received message
String text = bot.messages[i].text;
Serial.println(text);
String fromName = bot.messages[i].from_name;
if (text == "/flash") {
flashState = !flashState;
digitalWrite(FLASH_LED_PIN, flashState);
}
if (text == "/photo") {
sendPhoto = true;
Serial.println("New photo request");
}
if (text == "/reboot") {
bot.sendMessage(chatId, "OK, Rebooting ESP32 .... ");
delay(500);
rebootloop = true;
}
if (text == "/stop") {
capturestop = false;
bot.sendMessage(chatId, "Capture stopped");
}
if (text == "/start") {
capturestop = true;
bot.sendMessage(chatId, "Capture started");
}
if (text == "/boot") {
unsigned long uptime = millis() - bootTime;
unsigned long seconds = uptime / 1000;
unsigned long hours = seconds / 3600;
unsigned long minutes = (seconds % 3600) / 60;
seconds = seconds % 60;
String uptimeStr = String(hours) + "h " + String(minutes) + "m " + String(seconds) + "s";
String message = "ESP32-CAM è acceso da: " + uptimeStr;
bot.sendMessage(chatId, message, "");
}
if (text == "/help") {
String welcome = "Welcome to the ESP32-CAM BirdCam Telegram bot.\n";
welcome += "/start: Start detection\n";
welcome += "/stop: Stop detection\n";
welcome += "/reboot : Reboot Birdcam\n";
welcome += "/photo: Takes a new photo\n";
welcome += "/flash: Toggle flash LED\n";
welcome += "/boot: Boot time ESP32-CAM\n";
welcome += "To start capture pls run the /start command\n";
welcome += "You'll receive a photo whenever motion is detected.\n";
bot.sendMessage(chatId, welcome, "Markdown");
}
}
}