#include <HTTPClient.h>
#include "esp_camera.h"
#include <WiFi.h>
#include <ArduinoJson.h>
#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<String>(); // 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
}