×

ARDUINO

Control d’accés amb contrasenya. Orgue de llums. Comparativa de editors Arduino. Projecte bon Nadal. Sensor de gas i de fums. Control de la qualitat del aire interior (CO2) amb semàfor LED.

NodeMCU

NodeMCU-ThingSpeak. ThingSpeak. NodeMCU com AccessPoint. Control de la llum de casa per web. Control de un led per el mòbil. Sistema de fitxers en ESP8266. Altura del nivell d'aigua. Avis d’alarma a Telegram. Control de la llum de casa des de Telegram. Control de la intensitat de llum de un LED per Internet. Magatzematge de temperatura i humitat en Thinger. Publicar temperatura i humitat en Google Sheets. ESP8266 i Firebase. ESP8266-BME280 Firebase. La bàscula. ESP8266 NodeMCU amb cèl·lula de càrrega..

ESP32

ESP32-CAM, exemple de prova. Esp32-Cam captura fotos per ser vistes en la WEB. Pujar fotos a un servidor FTP amb ESP32-CAM. Foto amb ESP32-CAM i Telegram. Alarma amb avis a Telegram, utilitzant la placa ESP32-CAM.

PROGRAMACIÓ

Matemàtiques. App Inventor tutorial. MicroPython en ESP8266. Visual Estudio Code per Arduino

Intel·ligència artificial

Classificador d’imatges.

SERVIDORS

Instal·lar i configurar Apache en Ubuntu. Accés a un servidor Linux remotament. Client i Servidor FTP.

MÀQUINES VIRTUALS

Màquina virtual UBUNTU. WindowsXP en Virtualbox.

ELECTRÒNICA

Simulador Multisim. Simulador Proteus.

BASE DE DADES

MySQL. Instal·lació. MySQL. Aplicacions.

JAVA

Netbeans Maven MySQL. Java+Netbeans+Maven+EclipseLink+MySQL Aplicació d’escriptori JavaFX App JavaFX amb SceneBuilder App Java-Swing
favicon
logo

Tresavis

Benvinguts al blog de tresavis de noves tecnologies i més.

Correu : tresavis3@gmail.com

facebook   Instagram   youtube   x   tiktok

☰ Pràctiques   ☰ Articles

Alarma amb avis a Telegram, utilitzant la placa ESP32-CAM.

Descripció.

En aquesta practica amb la placa ESP32-CAM, un sensor de moviment i un BOT de Telegram volem que al detectar moviment envií la foto del intrús al nostre compte de Telegram. També poder saber la humitat i la temperatura que ens dona el sensor GY-BME/P280.

Contingut:

diagrama

Estudis previs.

Per informació de la placa ESP32-CAM cal visitar la pagina:

esp32-cam.php

BOT de Telegram.

Esp32-CAM pot interactuar amb un BOT de Telegram per rebre missatges codificar-los i enviar resposta a la nostre compte de Telegram com fotos i lectura de sensors.

Per informació de com crear un BOT, es pot visitar la pagina:

esp32-cam-telegram.php

Components per desenvolupar la practica i circuit.

Mòdul ESP32-CAM AI-Thinker amb la càmera OV2640.

Programador FTDI per pujar el codi a la placa.

Sensor de moviment mini PIR.

Sensor de temperatura i humitat GY-BME/P280.

Bot de Telegram.

Alimentador a 5 V.

Cable USB-miniUSB.

Arduino IDE.

Protoboard i cables.

components

Construïm el circuit amb la protoboard i les connexions adients i veiem que hi ha masses cables i que tapen la lent de la càmera.

diagrama

Millorem les connexions i el resultat es el següent:

diagrama

Llibreries.

Per interactuar amb Telegram utilitzem la llibreria Universal Telegram Bot Library de Brian Lough.

Pagina per descarregar la llibreria:

https://github.com/witnessmenow/Universal-Arduino-Telegram-Bot/archive/master.zip

En el IDE de Arduino anar a Esbós/Inclou biblioteca/Afegeix una biblioteca .ZIP... i seleccionar la carpeta descarregada.

ArduinoJson Library de Benoit Blanchon.

La instalen des del gestor de biblioteques.

llibraria

També tenim que instal·lar la llibreria BME280 SpackFun, per poder treballar amb els sensor de temperatura i humitat. En altres projectes amb els sensor BME280 utilitzem la llibreria Adafruit_BME280, però en aquest cas entra en conflicte amb les altres llibreries. Per instal·lar BME280 SpackFun ho fem des de el IDE, en eines/gestiona les biblioteques.

Comandos que accepta el Bot de Telegram.

Una vegada creat el Bot podem establir una conversa amb el microcontrolador ESP32.

Els comandos per interactuar son:/start, /foto, /flash, /alarmaON, /alarmaOFF.

/start. Al enviar aquest missatge la resposta es un missatge de benvinguda amb tots el comandos de control de la placa.

/foto. La placa ESP32-CAM dispara una foto i respon enviant-la per Telegram.

/flash.ESP32-CAM al rebre aquest missatge encén el led de la placa. Si es troba encès l’apaga.

/meteo.ESP32-CAM envia les lectures de temperatura i humitat.

/alamaON.La placa activa el sensor de moviment.

/alamaOFF.La placa des-activa el sensor de moviment.

Al detectar moviment envia automàticament una foto per Telegram.

Connexió de ESP32-CAM amb el programador FTDI.

Per pujar el programa a la placa necessitem un programador FTDI.

A l’hora de pujar el codi es necessari fer un pont entre els pins GND i IO0 i fer un reset. El pont es treu una vegada ja sa pujat el codi.

Comprovar el jumper del FTDI que estigui en 5V i no en 3.3V.

Per mes informació visitar la pàgina:

esp32-cam.php#conexio programador0
programador

CODI.

esp32-alarma-telegramV2.ino
/*
  Rui Santos
  Complete project details at https://TresAvis.com/esp32-cam-shield-pcb-telegram/
  CODI MODIFICAT PER TRESAVIS https://tresavis.cat
  Project created using Brian Lough's Universal Telegram Bot Library: https://github.com/witnessmenow/Universal-Arduino-Telegram-Bot

  The above copyright notice and this permission notice shall be included in all
  copies or substantial portions of the Software.
*/

#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"

// Parametres WiFi
const char* ssid = "mirablau";
const char* password = "********";

// 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 = "**********";

// Initialize Telegram BOT
// bot token obtingut del gotFather
String BOTtoken = "**********:******_**************************";

bool sendPhoto = false;

WiFiClientSecure clientTCP;

// Inicialitza el bot amb el token i 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

// led flash de la placa ESP32
#define FLASH_LED_PIN 4
bool flashState = LOW;

// Motion Sensor
bool motionDetected = false;
// variable booleana alarma
bool alarma = false;

// Define I2C Pins for BME280
#define I2C_SDA 14
#define I2C_SCL 15

// Sensor de temperatura i humitat
BME280 bme;
 
int botRequestDelay = 1000;   // mean time between scan messages
long lastTimeBotRan;     // last time messages' scan has been done

// Prototips (enunciats) de les funcions a utilitzar, que s'implementan al final del codi
// gestiona els nou missatges rebuts, com a parametre el numero de missatges
void handleNewMessages(int numNewMessages);
// Envia la foto a Telegram, retorna un String
String sendPhotoTelegram();
// Llegeix la temperatura i la humitat del sensor BME280, retornant la lectura
String getReadings();


// Indicates when motion is detected
static void IRAM_ATTR detectsMovement(void * arg){
  //Serial.println("MOTION DETECTED!!!");
  motionDetected = true;
}

//////////////////////////////  setup() //////////////////////////////////
void setup(){
  // Deshabilita posibles aturades per falta de potencia
  WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); 
  Serial.begin(115200);
  
  pinMode(FLASH_LED_PIN, OUTPUT);
  digitalWrite(FLASH_LED_PIN, flashState);

  // Init BME280 sensor
  Wire.begin(I2C_SDA, I2C_SCL);
  bme.settings.commInterface = I2C_MODE;
  bme.settings.I2CAddress = 0x76;
  bme.settings.runMode = 3;
  bme.settings.tStandby = 0;
  bme.settings.filter = 0;
  bme.settings.tempOverSample = 1;
  bme.settings.pressOverSample = 1;
  bme.settings.humidOverSample = 1;
  bme.begin();
  
  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());

  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_CIF);  // 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);
  }
}

////////////////////////////////  loop()  ////////////////////////////////////////////
void loop(){
  if (sendPhoto){
    Serial.println("Preparing photo");
    sendPhotoTelegram(); 
    sendPhoto = false; 
  }

  if(alarma && motionDetected){
    bot.sendMessage(chatId, "Moviment detectat !!", "");
    Serial.println("Moviment Detectat");
    sendPhotoTelegram();
    motionDetected = false;
  }
  
  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();
  }
}

/////////////////////////////  sendPhotoTelegram()  ////////////////////////////////////////
String sendPhotoTelegram(){
  const char* myDomain = "api.telegram.org";
  String getAll = "";
  String getBody = "";

  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";
  }  
  
  Serial.println("Connect to " + String(myDomain));

  if (clientTCP.connect(myDomain, 443)) {
    Serial.println("Connection successful");
    
    String head = "--TresAvis\r\nContent-Disposition: form-data; name=\"chat_id\"; \r\n\r\n" + chatId + "\r\n--TresAvis\r\nContent-Disposition: form-data; name=\"photo\"; filename=\"esp32-cam.jpg\"\r\nContent-Type: image/jpeg\r\n\r\n";
    String tail = "\r\n--TresAvis--\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=TresAvis");
    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);
    
    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;
}

////////////////////////////  handleNewMessages(int numNewMessages) //////////////////////
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 == "/foto") {
      sendPhoto = true;
      Serial.println("New photo  request");
    }
    if (text == "/alarmaON") {
      alarma = true;
      Serial.println("alarma activada");
       bot.sendMessage(chatId, "Alarma activada", "");
    }
     if (text == "/alarmaOFF") {
      alarma = false;
      Serial.println("alarma des-activada");
      bot.sendMessage(chatId, "Alarma des-activada", "");
    }
    if (text == "/meteo"){
      String readings = getReadings();
      bot.sendMessage(chatId, readings, "");
    }
    if (text == "/start"){
      String welcome = "Benvingut a ESP32-ALARMA-Telegram bot.\n";
      welcome += "/foto : dispara la foto\n";
      welcome += "/flash : canvia estat de flash LED\n";
      welcome += "/meteo : petició de temperatura i humitat\n\n";
      welcome += "/alarmaON : connectar alarma\n\n";
      welcome += "/alarmaOFF : des-connectar alarma\n\n";
      welcome += "Rebem una foto quan es detecta moviment.\n";
      bot.sendMessage(chatId, welcome, "Markdown");
    }
  }
}

//////////////////////////// Funció getReadings()  ///////////////////////////////////////////
// Get BME280 sensor readings and return them as a String variable
String getReadings(){
  float temperature, humidity;
  temperature = bme.readTempC();
  //temperature = bme.readTempF();
  humidity = bme.readFloatHumidity();
  String message = "Temperatura: " + String(temperature) + " ºC \n";
  message += "Humitat: " + String (humidity) + " % \n";
  return message;
}

Explicacions del codi.

Cal visitar la pagina:

esp32-cam-telegram.php#explicacions

Aquí expliquem les part diferents de la practica anterior.

Tenim tres variables Booleanas, que inicialment estan en fals. Son els estats de moviment, de l’alarma i sendPhoto.

// Motion Sensor
bool motionDetected = false;
// variable booleana alarma
bool alarma = false;
bool sendPhoto = false;

La funció detectsMovement() es una funció callback que es cridada quan es detecta moviment, el que fa es que posa la variable motionDetected a true.

// Indicates when motion is detected
static void IRAM_ATTR detectsMovement(void * arg){
  //Serial.println("MOTION DETECTED!!!");
  motionDetected = true;
}

En el loop(), si alarma es true i motionDetected es true, envia la foto.

  if(alarma && motionDetected){
    bot.sendMessage(chatId, "Moviment detectat !!", "");
    Serial.println("Moviment Detectat");
    sendPhotoTelegram();
    motionDetected = false;
  }

Sensor de temperatura, humitat, BME280. Definim els pins SDA i SCL:

// Define I2C Pins for BME280
#define I2C_SDA 14
#define I2C_SCL 15

Es crea una instancia de la classe BME280 anomenat bme:

// Sensor de temperatura i humitat
BME280 bme;

Inicialitzem el sensor BME280:

  // Init BME280 sensor
  Wire.begin(I2C_SDA, I2C_SCL);
  bme.settings.commInterface = I2C_MODE;
  bme.settings.I2CAddress = 0x76;
  bme.settings.runMode = 3;
  bme.settings.tStandby = 0;
  bme.settings.filter = 0;
  bme.settings.tempOverSample = 1;
  bme.settings.pressOverSample = 1;
  bme.settings.humidOverSample = 1;
  bme.begin();

La funció getReadings() retorna un String amb les lectures de temperatura i humitat.

// Get BME280 sensor readings and return them as a String variable
String getReadings(){
  float temperature, humidity;
  temperature = bme.readTempC();
  //temperature = bme.readTempF();
  humidity = bme.readFloatHumidity();
  String message = "Temperatura: " + String(temperature) + " ºC \n";
  message += "Humitat: " + String (humidity) + " % \n";
  return message;
}

Configurem una interrupció en el pin GPIO 13, quan es detecta moviment:

  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);
  }

En el loop():

Si la variable sendPhoto es true, la placa envia la foto i torna a posar la variable a false.

  if (sendPhoto){
    Serial.println("Preparing photo");
    sendPhotoTelegram(); 
    sendPhoto = false; 
  }

Quan alarma i motionDetected son true, s’envia foto i es retorna la variable motionDetected a false.

  if(alarma && motionDetected){
    bot.sendMessage(chatId, "Moviment detectat !!", "");
    Serial.println("Moviment Detectat");
    sendPhotoTelegram();
    motionDetected = false;
  }

La placa mira per nous missatges cada segon:

  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();
  }

Quan arriba un nou missatge, crida a la funció encarregada de manipular-lo handleNewMessages(numNewMessages). Aquesta funció el que fa es obtenir el missatge rebut del BOT (text) i actuar en cada cas:

    // 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 == "/foto") {
      sendPhoto = true;
      Serial.println("New photo  request");
    }
    if (text == "/alarmaON") {
      alarma = true;
      Serial.println("alarma activada");
       bot.sendMessage(chatId, "Alarma activada", "");
    }
     if (text == "/alarmaOFF") {
      alarma = false;
      Serial.println("alarma des-activada");
      bot.sendMessage(chatId, "Alarma des-activada", "");
    }
    if (text == "/meteo"){
      String readings = getReadings();
      bot.sendMessage(chatId, readings, "");
    }
    if (text == "/start"){
      String welcome = "Benvingut a ESP32-ALARMA-Telegram bot.\n";
      welcome += "/foto : dispara la foto\n";
      welcome += "/flash : canvia estat de flash LED\n";
      welcome += "/meteo : petició de temperatura i humitat\n\n";
      welcome += "/alarmaON : connectar alarma\n\n";
      welcome += "/alarmaOFF : des-connectar alarma\n\n";
      welcome += "Rebem una foto quan es detecta moviment.\n";
      bot.sendMessage(chatId, welcome, "Markdown");
    }

Resultat.

resultat0

El resultat des de l’aplicació per Windows de Telegram es el següent:

resultat

Contingut.


Nosaltres

arcStMarti
canyelles1
canyelles2
roques
carabela
casaPiscina
cova
frares
garbi
celRogent
lluna1
lluna2
pell
port

Tags

Noves tecnologies WEB Arduino Domótica MycroPython NodeMCU IoT Java ESP8266 Lora C++ Programació Server-Client