#include #include "esp_camera.h" #include #include #include "string.h" unsigned long lastCaptureTime = 0; // Armazena o momento da última captura de foto // Configuração global da rede wi-fi const char* ssid = "***********";// MODIFICAR ESTA LINHA...... const char* password = "**********";// MODIFICAR ESTA LINHA...... const int LED_FLASH_PIN = 4; // Defina o pino do LED flash bool enviarFoto = false; // Variável para controlar o envio da foto // Configuração global da api const char* FKhorta = "1"; // MODIFICAR ESTA LINHA...... const char* apikey = "******************************"; // MODIFICAR ESTA LINHA...... int lastStatusChange = -1; // Valor inicial, pode ser 0 ou 1 dependendo do seu caso // Protótipos das funções void initCamera(); int enviar(int inFKsensor, String inValor); void enviarEmPartes(int inFKsensor, String inValor); void verifiqueInternetReconecte(int espera); void conectar(); int getchanges(int FKsensor); void capturarFoto(int envia, int flash); int isJPEG(const uint8_t *buffer, size_t length); String bytesToHexString(const uint8_t *data, size_t length); // Função para inicializar a câmera void initCamera() { camera_config_t config; config.ledc_channel = LEDC_CHANNEL_0; config.ledc_timer = LEDC_TIMER_0; config.pin_d0 = 5; // D0 config.pin_d1 = 18; // D1 config.pin_d2 = 19; // D2 config.pin_d3 = 21; // D3 config.pin_d4 = 36; // D4 config.pin_d5 = 39; // D5 config.pin_d6 = 34; // D6 config.pin_d7 = 35; // D7 config.pin_xclk = 0; // XCLK config.pin_pclk = 22; // PCLK config.pin_vsync = 25; // VSYNC config.pin_href = 23; // HREF config.pin_sscb_sda = 26; // SIOD config.pin_sscb_scl = 27; // SIOC config.pin_pwdn = 32; // PWDN config.pin_reset = -1; // Não usado config.xclk_freq_hz = 20000000; config.pixel_format = PIXFORMAT_JPEG; /* if(psramFound()){ config.frame_size = FRAMESIZE_SVGA;//(800x600) config.jpeg_quality = 10; config.fb_count = 2; } else { config.frame_size = FRAMESIZE_VGA; config.jpeg_quality = 12; config.fb_count = 1; } */ config.frame_size = FRAMESIZE_SVGA; config.jpeg_quality = 12; /*FORÇAR A OBTENÇÃO DA IMAGEM ATUALIZADA Opção 1) fb_count = 2 and CAMERA_GRAB_LATEST Opção 2) fb_count = 1, CAMERA_GRAB_WHEN_EMPTY and esp_camera_fb_return(esp_camera_fb_get()); */ config.fb_count = 2; config.grab_mode=CAMERA_GRAB_LATEST; // Inicialização da câmera esp_err_t err = esp_camera_init(&config); sensor_t *s = esp_camera_sensor_get(); s->set_aec2(s, true); // Ativar a exposição automática //s->set_brightness(s, 0); // Define o brilho para 0 //s->set_contrast(s, 1); // Define o contraste para 1 //s->set_saturation(s, 0); // Define a saturação para 0 if (err != ESP_OK) { Serial.printf("Erro ao inicializar a câmera! (Erro: %d)", err); return; } } /* * converte em uma string hexadecimal. */ String bytesToHexString(const uint8_t *data, size_t length) { // Crie uma string para armazenar os dados em hexadecimal char *hexString = new char[length * 2 + 1]; // Duas caracteres hexadecimais por byte + 1 para o caractere nulo // Converta os dados em hexadecimal e armazene na string for (size_t i = 0; i < length; i++) { sprintf(hexString + i * 2, "%02X", data[i]); // Dois caracteres por byte } // Adicione o caractere nulo para terminar a string hexString[length * 2] = '\0'; // Crie um objeto String a partir da string hexadecimal String hexStringObj = String(hexString); // Libere a memória alocada para a string delete[] hexString; return hexStringObj; } /* * verifica os dois primerios e dois útimos FF D8 ... FF D9 para uma validação parcial de JPG. */ int isJPEG(const uint8_t *buffer, size_t length) { // Verifica se o comprimento do buffer é suficiente para conter um cabeçalho e um rodapé JPEG if (length < 4) { return 0; } // Verifica os primeiros dois bytes para o início do cabeçalho JPEG (FF D8) if (buffer[0] == 0xFF && buffer[1] == 0xD8) { // Verifica os últimos dois bytes para o final do arquivo JPEG (FF D9) if (buffer[length - 2] == 0xFF && buffer[length - 1] == 0xD9) { return 1; } } return 0; } // Conecta-se ao WiFi void conectar(){ WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(1000); Serial.println("Tentando se conectar ao WiFi..."); } Serial.println("Conectado ao WiFi com sucesso!"); } // Se não tiver Internet reestabelecer conexão após 'espera' milisegundos. void verifiqueInternetReconecte(int espera) { if (WiFi.status() != WL_CONNECTED) { Serial.print("Sem internet. Tentar conexão em "); Serial.print(espera); Serial.println(" segundos..."); delay(espera); conectar(); } } void setup() { Serial.begin(115200); pinMode(LED_FLASH_PIN, OUTPUT); // Configura o pino do LED flash como saída conectar(); initCamera(); // Espera 2 segundos para estabilizar a câmera delay(2000); } /* *Enviar por partes de 50000 */ void enviarEmPartes(int inFKsensor, String inValor) { const size_t parteTamanho = 50000; // Tamanho de cada parte (50.000 caracteres) size_t totalLength = inValor.length(); String lastID = ""; // Inicializa o lastID como vazio int pos = 0; for (size_t i = 0; i < totalLength; i += parteTamanho) { size_t parteLength = min(parteTamanho, totalLength - i); String parte = inValor.substring(i, i + parteLength); // Monta o corpo da requisição String data_to_send = "apikey=" + String(apikey) + "&FKhorta=" + String(FKhorta) + "&FKsensor=" + String(inFKsensor) + "&c=" + parte; pos = pos +1; Serial.println("Parte"+ String(pos) +" enviada..."); if (WiFi.status() == WL_CONNECTED) { HTTPClient http; http.begin("https://horta.gvsoftwares.com.br/api/set_leiturapost/"); http.addHeader("Content-type", "application/x-www-form-urlencoded; charset=utf-8"); // Envia o lastID a partir da segunda parte (não na primeira) if (lastID != "" && i > 0) { data_to_send += "&id=" + lastID; } int codigo_resposta = http.POST(data_to_send); if (codigo_resposta > 0) { Serial.println("Código HTTP -> " + String(codigo_resposta)); // Captura o lastID apenas na primeira parte if (codigo_resposta == 200 && i == 0) { // primeira parte String corpo_resposta = http.getString(); Serial.println("O servidor respondeu:"); Serial.println(corpo_resposta); // Capacidade inicial definida como suficiente const size_t initialCapacity = 200; // Capacidade inicial fixa DynamicJsonDocument doc(initialCapacity); DeserializationError error = deserializeJson(doc, corpo_resposta); if (!error) { lastID = doc["lastID"].as(); // Atualiza o lastID com a resposta do servidor Serial.println("lastID atualizado: " + lastID); } else { Serial.println("Erro ao desserializar o JSON: "); Serial.println(error.c_str()); } } } else { Serial.println("Erro ao enviar POST:"); Serial.println(codigo_resposta); } http.end(); } else { Serial.println("Erro na conexão WIFI."); } delay(500); // Pequeno delay entre envios de partes } } /* * Enviar as imagens para a api. */ int enviar(int inFKsensor, String inValor) { if (WiFi.status() == WL_CONNECTED) { HTTPClient http; String data_to_send = "apikey=" + String(apikey) + "&FKhorta=" + String(FKhorta) + "&FKsensor=" + String(inFKsensor) + "&c=" + String(inValor); //Serial.println(data_to_send); http.begin("https://horta.gvsoftwares.com.br/api/set_leiturapost/"); http.addHeader("Content-type", "application/x-www-form-urlencoded; charset=utf-8"); int codigo_resposta = http.POST(data_to_send); if (codigo_resposta > 0) { Serial.println("Código HTTP -> " + String(codigo_resposta)); if (codigo_resposta == 200) { String corpo_resposta = http.getString(); Serial.println("O servidor Respondeu:"); Serial.println(corpo_resposta); return 1; } } else { Serial.println("Erro ao enviar POST:"); Serial.println(codigo_resposta); return 0; } http.end(); } else { Serial.println("Erro na conexão WIFI."); return 0; } } /* * capturar a foto com flash */ void capturarFoto(int envia, int flash) { camera_fb_t * fb = NULL; if (flash == 1){ digitalWrite(LED_FLASH_PIN, HIGH); } delay(500); fb = esp_camera_fb_get(); delay(200); digitalWrite(LED_FLASH_PIN, LOW); if (fb) { Serial.println("Tamanho:"); Serial.println(fb->len); // Exibir o tamanho em decimal Serial.println("É uma imagem jpg (0xFF 0xD8 ... 0xFF 0xD9)?"); Serial.println(isJPEG(fb->buf, fb->len)); String hexData = bytesToHexString(fb->buf, fb->len); //Serial.println("hexData:"); //Serial.println(hexData); if (envia==1) { if (fb->len < 90000){ //enviar em uma única string. enviar(55, hexData);// MODIFICAR ESTA LINHA...... o id 55 }else{ //enviar em partes um string maior ou igual à 90000 characteres. enviarEmPartes(55,hexData);// MODIFICAR ESTA LINHA...... o id 55 } } esp_camera_fb_return(fb); // Libera a memória da imagem após o uso delay(200); } else { Serial.println("Erro ao capturar a foto"); } } void loop() { unsigned long currentTime = millis(); verifiqueInternetReconecte(2000); // Verificar se passou 30 minutos desde a última captura ou se é a primeira captura if (currentTime - lastCaptureTime >= 1800000 || lastCaptureTime == 0) { capturarFoto(1, 1); // Captura e envia a foto lastCaptureTime = currentTime; // Atualiza o tempo da última captura Serial.println("Foto capturada e enviada."); } // Verifica se houve mudança no status do sensor int novoStatusChange = getchanges(55);// MODIFICAR ESTA LINHA...... o id 55 if (novoStatusChange != lastStatusChange && !enviarFoto && novoStatusChange != -1) { capturarFoto(1, 1); // Captura e envia a foto lastStatusChange = novoStatusChange; Serial.println("Mudança de status detectada. Foto capturada e enviada."); } delay(1000); // Aguarda 1 segundo antes de verificar novamente } /* * Mudanças no status do botão on/off */ int getchanges(int FKsensor) { // Faça a requisição HTTP HTTPClient http; // Informe a URL desejada String url = "https://horta.gvsoftwares.com.br/api/get_sensoresstatuschange/?FKsensor=" + String(FKsensor); http.begin(url); // Obtenha e imprima o código de status da resposta int httpCode = http.GET(); Serial.print("Código de Status HTTP: "); Serial.println(httpCode); // Inicialize a variável de retorno int statusChange = -1; // Defina um valor padrão // Se a resposta for bem-sucedida, analise o JSON if (httpCode > 0) { String payload = http.getString(); Serial.println("Resposta do servidor: "); Serial.println(payload); // Tamanho suficiente para armazenar o JSON const size_t capacity = JSON_ARRAY_SIZE(1) + JSON_OBJECT_SIZE(1) + 10; // Aloque o JSONBuffer DynamicJsonDocument doc(capacity); // Deserializar a string JSON DeserializationError error = deserializeJson(doc, payload); // Verificar erros de desserialização if (error) { Serial.print(F("Erro ao desserializar JSON: ")); Serial.println(error.c_str()); } else { // Obter o valor da chave "statuschange" statusChange = doc[0]["statuschange"]; } } http.end(); return statusChange; // Retorna o valor, mesmo que seja o padrão -1 em caso de erro }