Überwachungskamera: Unterschied zwischen den Versionen

Aus HSHL Mechatronik
Zur Navigation springen Zur Suche springen
 
(33 dazwischenliegende Versionen von 2 Benutzern werden nicht angezeigt)
Zeile 179: Zeile 179:
# Mittelring (Dient zur Fixierung der Bauteile ohne diese Anzukleben / Festzuschrauben),  
# Mittelring (Dient zur Fixierung der Bauteile ohne diese Anzukleben / Festzuschrauben),  
# Rückseite (Bietet erhöhtes Volumen für die Verkabelung und Stromzufuhr)
# Rückseite (Bietet erhöhtes Volumen für die Verkabelung und Stromzufuhr)
<br/><br/>
 
<gallery widths="400" heights="400" >
Datei:CAD_Camera_Front.png|400px|Abb. 4: CAD-Modell - Gehäuse Vorderseite
Datei:CAD_Camera_Back.png|400px|Abb. 5: CAD-Modell - Gehäuse Rückseite
</gallery>
 
<br/>
'''Halterung''' <br/>
'''Halterung''' <br/>
Die Halterung kann in 2 Dimensionen Hoch/Runter und Links/Rechts bewegt werden. Hierbei dienen Schrauben zur Fixierung der Position, indem sie mittels "Quetschscheiben" die Bewegung verhindern.
Die Halterung kann in 2 Dimensionen Hoch/Runter und Links/Rechts bewegt werden. Hierbei dienen Schrauben zur Fixierung der Position, indem sie mittels "Quetschscheiben" die Bewegung verhindern.
An der Rückseite bietet die Halterung eine Platte zur Befestigung mittels Schrauben für einen Gewindedurchmesser von 4mm in allen 4 Ecken. Die U-Förmige verbindung zwischen Kameragehäuse & Halterung sowie Wandmontageplatte und Halterung wurden seperat gedruckt, da die orientierung im 3D Drucker elementar wichtig für die Stabilität und Flexibilität dieses Bauteils ist. Ohne Die seperierung würde das Bauteil beim Zusammenbau brechen.
An der Rückseite bietet die Halterung eine Platte zur Befestigung mittels Schrauben für einen Gewindedurchmesser von 4mm in allen 4 Ecken. Die U-Förmige verbindung zwischen Kameragehäuse & Halterung sowie Wandmontageplatte und Halterung wurden seperat gedruckt, da die orientierung im 3D Drucker elementar wichtig für die Stabilität und Flexibilität dieses Bauteils ist. Ohne Die seperierung würde das Bauteil beim Zusammenbau brechen.
<br/><br/>
 
<gallery widths="400" heights="400" >
Datei:CAD_Halter_Front.png|400px|Abb. 6: CAD-Modell - Halterung
Datei:CAD_Camera_Komplett_Front.png|400px|Abb. 7: CAD-Modell - Kamera Vorderseite
Datei:CAD_Camera_Komplett_Back.png|400px|Abb. 8: CAD-Modell - Kamera Rückseite
</gallery>
 
<br/>
'''Schaltung''' <br/>
'''Schaltung''' <br/>
Zum Einsatz kommt der Mikrokontroller ESP32-CAM, da dieser ab Werk einen Anschluss für eine Kamera sowie Bibliotheken zum auslesen dieser verfügt.
Zum Einsatz kommt der Mikrokontroller ESP32-CAM, da dieser ab Werk einen Anschluss für eine Kamera sowie Bibliotheken zum auslesen dieser verfügt.
Zeile 189: Zeile 202:
Der Mikrokontroller besitzt keine Analogen-Pins für das Mikrofon.  
Der Mikrokontroller besitzt keine Analogen-Pins für das Mikrofon.  
Aus diesem Grund sind das Mikrofon und der PIR an den digitalen Pins des Mikrokontrollers angeschlossen. Mittels Widerstand wird ein Schwellwert auf dem Mikrofon und PIR-Sensor eingestellt.
Aus diesem Grund sind das Mikrofon und der PIR an den digitalen Pins des Mikrokontrollers angeschlossen. Mittels Widerstand wird ein Schwellwert auf dem Mikrofon und PIR-Sensor eingestellt.
Der Wifi-Chip ist ebenfalls auf dem Mikrokontoller vorhanden, sodass kein extra Modul benötigt wird.
Der Wifi-Chip ist ebenfalls auf dem Mikrokontroller vorhanden, sodass kein extra Modul benötigt wird.
<br/><br/>
 
<gallery widths="400" heights="400" >
Datei:Schaltung_Ueberwachungskamera.png|400px|Abb. 9: Schaltung
Datei:Verkabelung_Ueberwachungskamera.png|400px|Abb. 10: Verkabelung
</gallery>
 
 
<br/>
'''Code''' <br/>
'''Code''' <br/>
Die Implementierung von Telegramm und das Verschicken von Bildern ist in Anlehnung an "[https://randomnerdtutorials.com/telegram-esp32-cam-photo-arduino/ Random Nerd Tutorial]" implementiert worden.
Die Implementierung von Telegramm und das Verschicken von Bildern ist in Anlehnung an "[https://randomnerdtutorials.com/telegram-esp32-cam-photo-arduino/ Random Nerd Tutorial]" implementiert worden.
In "BOT_TOKEN" muss der Unique-Token des Erstellten Bots eingefügt werden. In "USER_CHAT_IDS" können eine beliebige Anzahl von Telegramm Accounts eingebunden werden, die ein Bild erhalten wollen.
In "BOT_TOKEN" muss der Unique-Token des Erstellten Bots eingefügt werden. In "USER_CHAT_IDS" können eine beliebige Anzahl von Telegramm Accounts eingebunden werden, die ein Bild erhalten wollen.
Mit dem Befehl "/picture" kann ein manuell erstelltes Bild angefordert werden.
Mit dem Befehl "/picture" kann ein manuell erstelltes Bild angefordert werden.
Mithilfe von den Variablen "motionState" und "micState" kan verhindert werden, dass aufgrund des selben Eingangssignals 2 identische Bilder direkt hintereinander versendet werden.
Mithilfe von den Variablen "motionState" und "micState" kann verhindert werden, dass aufgrund des selben Eingangssignals 2 identische Bilder direkt hintereinander versendet werden.
<br/>
 
<gallery widths="400" heights="400" >
Datei:PAP_Ueberwachungskamera.png|400px|Abb. 11: Programmablaufsplan
</gallery>
 
<div style="width:1100px; height:800px; overflow:scroll; border: hidden">
<div style="width:1100px; height:800px; overflow:scroll; border: hidden">
<syntaxhighlight lang="cpp" style="border: none; background-color: #EFF1C1; font-size:larger">
<syntaxhighlight lang="cpp" style="border: none; background-color: #EFF1C1; font-size:larger">
//WIFI
// Dieses Projekt wurde in Anlehnung an
// "https://randomnerdtutorials.com/telegram-esp32-cam-photo-arduino/"
// implementiert.
 
// WIFI
#include <WiFi.h>
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <WiFiClientSecure.h>


//TELEGRAM
// TELEGRAM
#include <UniversalTelegramBot.h>
#include <UniversalTelegramBot.h>
#include <ArduinoJson.h>
#include <ArduinoJson.h>


//CAMERA
// CAMERA
#include "esp_camera.h"
#include "esp_camera.h"
#include "camera_pins.h"
#include "camera_pins.h"


//SENSOR
// SENSOR
#define MOTION_PIN 2
#define MOTION_PIN 2
bool motionState = false;
#define MIC_PIN 13
#define MIC_PIN 13
bool micState = false;


//WIFI
char* SSID_WIFI = "<SSID>";
char* PASSWORD_WIFI = "<PASSWORT>";


//TELEGRAM
// WIFI
String BOT_TOKEN = "<TOKEN:TOKEN>";
char* SSID_WIFI = "WLAN_NAME";
char* PASSWORD_WIFI = "WLAN_PASSWORT";
 
// TELEGRAM
String BOT_TOKEN = "5690480986:AAEtpi5MD_GvyM1vOKoehP6ygjSJTMAtyBU";
int USER_CHAT_ID_LENGTH = 1;
int USER_CHAT_ID_LENGTH = 1;
String USER_CHAT_IDS[] = {"<USER ID>", "<USER ID2>"};
String USER_CHAT_IDS[] = {"BENUTZER_ID_1", "BENUTZER_ID_2"};


//TELEGRAM
WiFiClientSecure client;
WiFiClientSecure client;
UniversalTelegramBot bot(BOT_TOKEN, client);
UniversalTelegramBot bot(BOT_TOKEN, client);


// SENSOR Variablen
bool motionState = false;
bool micState = false;


//REQUEST TIME
// REQUEST TIME
int requestDelay = 1000;
int requestDelay = 1000;
unsigned long lastTime;
unsigned long lastTime;


// Verschicke eingehende Nachricht über Serial zum Debuggen
void printNewMessage(int index) {
void printNewMessage(int index) {
   Serial.println("------------------------");
   Serial.println("------------------------");
Zeile 247: Zeile 277:
}
}


// Eingehende Nachricht verarbeiten
void processMessage(int messageNum) {
void processMessage(int messageNum) {
  // Iteration über alle Nachrichten
   for (int i=0; i < messageNum; i++) {
   for (int i=0; i < messageNum; i++) {
     printNewMessage(i);
     printNewMessage(i);
     String chat_id = String(bot.messages[i].chat_id);
     String chat_id = String(bot.messages[i].chat_id);
      
 
     // Testen, ob ID des Users in USER_CHAT_IDS enthalten ist
     if (chatIdIsInChatIdList(chat_id) == true) {
     if (chatIdIsInChatIdList(chat_id) == true) {
     
      // ID des Users ist enthalten => Nachricht kann Analysiert werden
       String text = bot.messages[i].text;
       String text = bot.messages[i].text;
       String user = bot.messages[i].from_name;
       String user = bot.messages[i].from_name;


      // Wenn Befehl = "/start" => Sende Befehlsübersicht zu
       if (text == "/start") {
       if (text == "/start") {
         String response = "";
         String response = "";
Zeile 264: Zeile 301:
         bot.sendMessage(chat_id, response, "");
         bot.sendMessage(chat_id, response, "");
       }
       }
     
      // Wenn Befehl = "/picture" => Aktuelles Kamera Bild übermitteln
       else if (text == "/picture") {
       else if (text == "/picture") {
         String response = "";
         String response = "";
Zeile 270: Zeile 309:
         sendPicture(chat_id, false);
         sendPicture(chat_id, false);
       }
       }
     
      // Wenn Befehl nicht vorhanden => Sende "Unbekannter Befehl"
       else {
       else {
         String response = "";
         String response = "";
Zeile 276: Zeile 317:
         bot.sendMessage(chat_id, response, "");
         bot.sendMessage(chat_id, response, "");
       }
       }
     
      // Wenn ID des Users in USER_CHAT_IDS nicht enthalten ist => Übermittel keine Daten
     } else {
     } else {
       bot.sendMessage(chat_id, "Unauthorized User", "");
       bot.sendMessage(chat_id, "Unauthorized User", "");
Zeile 283: Zeile 326:
}
}


// Testen, ob User ID in USER_CHAT_IDS enthalten ist
bool chatIdIsInChatIdList(String chat_id) {
bool chatIdIsInChatIdList(String chat_id) {
   for (int i = 0; i < USER_CHAT_ID_LENGTH; i++) {
   for (int i = 0; i < USER_CHAT_ID_LENGTH; i++) {
Zeile 292: Zeile 336:
}
}


// Photo erstellen, verschicken an chat_id
String sendPicture(String chat_id, bool flash) {
String sendPicture(String chat_id, bool flash) {
   const char* myDomain = "api.telegram.org";
   const char* myDomain = "api.telegram.org";
   String getAll = "";
   String getAll = "";
   String getBody = "";
   String getBody = "";
    
 
   // Photo erstellen
   camera_fb_t* fb = NULL;
   camera_fb_t* fb = NULL;
   fb = esp_camera_fb_get();
   fb = esp_camera_fb_get();
  // Abbruch, wenn Photo nicht erstellt werden konnte
   if (!fb) {
   if (!fb) {
     Serial.println("Camera capture failed");
     Serial.println("Camera capture failed");
Zeile 307: Zeile 355:
   Serial.println("Connect to " + String(myDomain));
   Serial.println("Connect to " + String(myDomain));


  // Verbindung mit Telegram erfolgreich
   if (client.connect(myDomain, 443)) {
   if (client.connect(myDomain, 443)) {
     Serial.println("Connection successful");
     Serial.println("Connection successful");
    // HTTP Request erstellen
     //X ==> Boundary
     //X ==> Boundary
     String head = "--X\r\nContent-Disposition: form-data; name=\"chat_id\"; \r\n\r\n" + chat_id + "\r\n--X\r\nContent-Disposition: form-data; name=\"photo\"; filename=\"esp32-cam.jpg\"\r\nContent-Type: image/jpeg\r\n\r\n";
     String head = "--X\r\nContent-Disposition: form-data; name=\"chat_id\"; \r\n\r\n" + chat_id + "\r\n--X\r\nContent-Disposition: form-data; name=\"photo\"; filename=\"esp32-cam.jpg\"\r\nContent-Type: image/jpeg\r\n\r\n";
     String tail = "\r\n--X--\r\n";
     String tail = "\r\n--X--\r\n";
 
   
    // Variablen für HTTP Request
     uint16_t imageLen = fb->len;
     uint16_t imageLen = fb->len;
     uint16_t extraLen = head.length() + tail.length();
     uint16_t extraLen = head.length() + tail.length();
Zeile 341: Zeile 393:
     esp_camera_fb_return(fb);
     esp_camera_fb_return(fb);


     int waitTime = 10000;  // timeout 10 seconds
     int waitTime = 10000;  // Warte 10 Sekunden
     long startTimer = millis();
     long startTimer = millis();
     boolean state = false;
     boolean state = false;
Zeile 373: Zeile 425:
   return getBody;
   return getBody;
}
}


void setup() {
void setup() {
  // Initialisiere Sensoren, Telegram und Wifi
   startSerial();
   startSerial();
   connectToWifi();
   connectToWifi();
Zeile 381: Zeile 435:
}
}


void setupHardware() {
// Starte Serielle Schnittstelle
  /*
  pinMode(MOTION_PIN, INPUT);
  pinMode(MIC_PIN, INPUT);
  */
}
 
void startSerial() {
void startSerial() {
   Serial.begin(115200);
   Serial.begin(115200);
Zeile 397: Zeile 445:
}
}


// Verbinde mit WLAN
void connectToWifi() {
void connectToWifi() {
   Serial.println("Connect to WIFI");
   Serial.println("Connect to WIFI");
Zeile 409: Zeile 458:
}
}


// Konfiguriere PINs der Kamera
void setupCamera() {
void setupCamera() {
   Serial.println("Setup CAMERA");
   Serial.println("Setup CAMERA");
Zeile 444: Zeile 494:


void loop() {
void loop() {
  // Überprüfe Sensor Werte
   checkSensors();
   checkSensors();
  // Überprüfe Nachrichten der Benutzer
   checkMessagingServer();
   checkMessagingServer();
}
}
Zeile 450: Zeile 503:
void checkSensors() {
void checkSensors() {


   //PIR MOTION SENSOR
   // Wenn Bewegungserkennung wieder unter Schwellwert
  //Serial.println(digitalRead(MOTION_PIN));
   if (motionState == true && digitalRead(MOTION_PIN) == 0) {
   if (motionState == true && digitalRead(MOTION_PIN) == 0) {
     motionState = false;
     motionState = false;
   }
   }
 
  // Wenn PIR Sensor über Schwellwert und
  // derzeit keine Übermittlung läuft
   if (motionState == false && digitalRead(MOTION_PIN) == 1) {
   if (motionState == false && digitalRead(MOTION_PIN) == 1) {
     for (int i = 0 ;i < USER_CHAT_ID_LENGTH; i++) {
     for (int i = 0 ;i < USER_CHAT_ID_LENGTH; i++) {
      //Verschicke Photo und Text "MOTION detected" an alle hinterlegten IDs
       bot.sendMessage(USER_CHAT_IDS[i], "MOTION detected" , "");
       bot.sendMessage(USER_CHAT_IDS[i], "MOTION detected" , "");
       sendPicture(USER_CHAT_IDS[i], false);
       sendPicture(USER_CHAT_IDS[i], false);
Zeile 463: Zeile 520:
   }
   }


   //MICROFON
   // Wenn Mikrofon Pegel wieder unter Schwellwert
  Serial.println(digitalRead(MIC_PIN));
   if (micState == true && digitalRead(MIC_PIN) == 0) {
   if (micState == true && digitalRead(MIC_PIN) == 0) {
     micState = false;
     micState = false;
   }
   }
  // Wenn  Mikrofon Pegel über Schwellwert und
  // derzeit keine Übermittlung läuft
   if (micState == false && digitalRead(MIC_PIN) == 1) {
   if (micState == false && digitalRead(MIC_PIN) == 1) {
    //Verschicke Photo und Text "SOUND detected" an alle hinterlegten IDs
     for (int i = 0 ;i < USER_CHAT_ID_LENGTH; i++) {
     for (int i = 0 ;i < USER_CHAT_ID_LENGTH; i++) {
       bot.sendMessage(USER_CHAT_IDS[i], "SOUND detected" , "");
       bot.sendMessage(USER_CHAT_IDS[i], "SOUND detected" , "");
Zeile 477: Zeile 538:
}
}


// Überprüft per Polling,
// ob noch nicht Abgerufte Nachrichten auf dem Server enthalten sind
void checkMessagingServer() {
void checkMessagingServer() {
  // Jede Sekunde Nachrichten abrufen
   if (millis() > lastTime + requestDelay)  {
   if (millis() > lastTime + requestDelay)  {
     int numOfMessages = bot.getUpdates(bot.last_message_received + 1);
     int numOfMessages = bot.getUpdates(bot.last_message_received + 1);
     if (numOfMessages != 0) {
     if (numOfMessages != 0) {
       bool newMessage = true;
       bool newMessage = true;
      // Solange noch neue Nachrichten vorhanden
       while(newMessage) {
       while(newMessage) {
        // Bearbeite Nachricht
         processMessage(numOfMessages);
         processMessage(numOfMessages);


         // is there another new message
         // Sind in der Zwischenzeit noch neue Nachrichten eingetroffen
         numOfMessages = bot.getUpdates(bot.last_message_received + 1);
         numOfMessages = bot.getUpdates(bot.last_message_received + 1);
         if (numOfMessages == 0) {
         if (numOfMessages == 0) {
Zeile 497: Zeile 566:
</syntaxhighlight>
</syntaxhighlight>
</div>
</div>
<br/><br/>
<br/>
'''Zusammenbau''' <br/>
'''Zusammenbau''' <br/>
Beim Zusammenbau wurde darauf geachtet, dass die Kabel nicht direkt an die Kontakte gelötet werden, um später noch Änderungen am Programmcode vornehmen zu können.
Beim Zusammenbau wurde darauf geachtet, dass die Kabel nicht direkt an die Kontakte gelötet werden, um später noch Änderungen am Programmcode vornehmen zu können.
Zeile 571: Zeile 640:
== Ergebnis ==
== Ergebnis ==


LINK ZU VIDEO EINFÜGEN
Die Überwachungskamera ist vielseitig einsetzbar. Unter anderem auch als Baby-Cam. <br/><br/>
Hier ein Video:
{{#ev:youtube|https://www.youtube.com/watch?v=PvxNElTAMEY| 600 | |}}


== Zusammenfassung ==
== Zusammenfassung ==
Zeile 585: Zeile 656:


<gallery widths="1500" heights="640">
<gallery widths="1500" heights="640">
Datei:Projektplan Ueberwachungskamera.png|1400px|Abb.4: Projektplan als Gantt-Diagramm
Datei:Projektplan Ueberwachungskamera.png|1400px|Abb.12: Projektplan als Gantt-Diagramm
</gallery>
</gallery>


Zeile 659: Zeile 730:
|}
|}


== Weblinks ==
=== CAD-Modelle ===
 
ZIP-File mit allen CAD-Modellen:
== Literatur ==
[[:Datei:CAD-Modelle_Ueberwachungskamera.zip|Überwachungskamera]]
 


=== Programmcode ===
ZIP-File mit dem Programmcode:
[[:Datei:Telegram_picture.zip|Programmcode]]


<!-- Fügen Sie diesen Footer hinzu.  -->
<!-- Fügen Sie diesen Footer hinzu.  -->
----
----
→ zurück zur Übersicht: [[:Kategorie:ProjekteET_MTR_BSE_WS2022|WS 22/23: Angewandte Elektrotechnik (BSE)]]
→ zurück zur Übersicht: [[:Kategorie:ProjekteET_MTR_BSE_WS2022|WS 22/23: Angewandte Elektrotechnik (BSE)]]

Aktuelle Version vom 9. Januar 2023, 10:35 Uhr

Autoren: Kevin Mudzcinski & Henry Fröse
Betreuer: Prof. Göbel & Prof. Schneider


→ zurück zur Übersicht: WS 20/21: Angewandte Elektrotechnik (BSE)

→ zurück zur Übersicht: WS 21/22: Angewandte Elektrotechnik (BSE)

→ zurück zur Übersicht: WS 22/23: Angewandte Elektrotechnik (BSE)

Einleitung

Im Rahmen des GET-Fachpraktikums [[1]] entsteht das Projekt „Überwachungskamera“. Sinn des Projektes ist es, ein mechatronisches System zu entwerfen, zu fertigen und zu testen. Dabei sollen vor allem die Kenntnisse aus dem Modul Mess- und Regelungstechnik zur Hilfe genommen werden. Das System registriert mit Hilfe der Sensoren ungewöhnliche Ereignisse aufgrund von Bewegungen bzw. Geräuschen. Daraufhin macht die Kamera ein Foto und sendet dieses per Telegram-Bot [2] an den Nutzer.

An dem Projekt arbeiten die Mechatronik-Studenten Kevin Mudzcinski und Henry Fröse.

Anforderungen

Tabelle 1: Testbare, atomare Anforderungen
ID Inhalt Ersteller Datum Geprüft von Datum
1 Die Kamera muss mit 5V Spannung versorgt werden. Henry Fröse 01.10.2022 Kevin Mudzinski 11.10.2022
2 Der PIR-Sensor muss Bewegungen registrieren und ein Bild verschicken. Henry Fröse 01.10.2022 Kevin Mudzinski 11.10.2022
3 Der Sound-Sensor muss Geräusche größer Vergleichspegel registrieren und ein Bild verschicken. Henry Fröse 01.10.2022 Kevin Mudzinski 11.10.2022
4 Die ESP32-CAM erstellt ein Foto, wenn der Bewegungs- oder der Geräuschssensor ein HIGH-Signal dem Arduino überträgt. Henry Fröse 01.10.2022 Kevin Mudzinski 11.10.2022
5 Der Kamera muss im Heimnetzwerk mittels SSID und Passwort eingebunden sein. Henry Fröse 01.10.2022 Kevin Mudzinski 11.10.2022
6 Das Bild muss an alle gewünschten Benutzer übertragen werden. Henry Fröse 01.10.2022 Kevin Mudzinski 11.10.2022
7 Es müssen mehrere Benutzer eingebunden werden können, die benachrichtigt werden. Henry Fröse 01.10.2022 Kevin Mudzinski 11.10.2022
8 Die Benutzer muss die Möglichkeit haben, manuell ein Bild anzufordern. Henry Fröse 01.10.2022 Kevin Mudzinski 11.10.2022
9 Die Kamera muss in 2 Dimensionen ausgerichtet werden können. Henry Fröse 01.10.2022 Kevin Mudzinski 11.10.2022
10 Die Kamera muss in ihrer Ausrichtung fixierbar sein. Henry Fröse 01.10.2022 Kevin Mudzinski 11.10.2022
11 Die Kamera muss mittels Schraubverbindung an Oberflächen fixierbar sein. Henry Fröse 01.10.2022 Kevin Mudzinski 11.10.2022

Tabelle 1 zeigt die funktionalen Anforderungen.

Funktionaler Systementwurf/Technischer Systementwurf


Komponentenspezifikation

Tabelle 2: Liste aller Komponenten
ID Komponente Bezeichnung Bild
1 PIR Sensor HC-SR501
mini
mini
2 Mikrofon Soundsensor KY-038
mini
mini
3 Entwicklungsplatine inkl. Kamera ESP32-CAM
mini
mini
4 Gehäuse 3D-Druck PLA in schwarz
mini
mini
5 Datenkabel und Stromkabel Litze 0.5mm^2 verschiedene Farben
mini
mini
6 Stromanschluss 1m USB-3.0-Female auf Micro-USB-3.0-Female Kabel

(Micro-USB Seite wird entfernt und abisoliert)

mini
mini
7 Verbrauchsmaterial interne Verkabelung Schrumpfschlauch und Lötzinn
mini
mini

Umsetzung (HW/SW)


Gehäuse
Das komplette Gehäuse ist mittels 3D-Druck erstellt worden. Die Verbindung der einzelnen Komponenten ist mittels Schrauben gelöst worden, um die Kamera leicht auseinandernehmbar zu gestalten. Um die Verschiedenen Teile stabil Drucken zu können wurden vereinzelt Komponenten, wie z.B. die Kamerarückwand und die Verbindung an die Halterung. Das Gehäuse ist in 3 Schichten unterteilt:

  1. Vorderseite (Bietet Aussparungen für die Kamera, Mikrofon und PIR),
  2. Mittelring (Dient zur Fixierung der Bauteile ohne diese Anzukleben / Festzuschrauben),
  3. Rückseite (Bietet erhöhtes Volumen für die Verkabelung und Stromzufuhr)


Halterung
Die Halterung kann in 2 Dimensionen Hoch/Runter und Links/Rechts bewegt werden. Hierbei dienen Schrauben zur Fixierung der Position, indem sie mittels "Quetschscheiben" die Bewegung verhindern. An der Rückseite bietet die Halterung eine Platte zur Befestigung mittels Schrauben für einen Gewindedurchmesser von 4mm in allen 4 Ecken. Die U-Förmige verbindung zwischen Kameragehäuse & Halterung sowie Wandmontageplatte und Halterung wurden seperat gedruckt, da die orientierung im 3D Drucker elementar wichtig für die Stabilität und Flexibilität dieses Bauteils ist. Ohne Die seperierung würde das Bauteil beim Zusammenbau brechen.


Schaltung
Zum Einsatz kommt der Mikrokontroller ESP32-CAM, da dieser ab Werk einen Anschluss für eine Kamera sowie Bibliotheken zum auslesen dieser verfügt. Dieser benötigt eine 5V-Spannungsversorgung, welche wir mittels USB bereitstellen können. Dadurch kann der Anwender entscheidern, ob er die Kamera mittels Powerbank oder USB-Netzteil betreibt. Der Mikrokontroller besitzt keine Analogen-Pins für das Mikrofon. Aus diesem Grund sind das Mikrofon und der PIR an den digitalen Pins des Mikrokontrollers angeschlossen. Mittels Widerstand wird ein Schwellwert auf dem Mikrofon und PIR-Sensor eingestellt. Der Wifi-Chip ist ebenfalls auf dem Mikrokontroller vorhanden, sodass kein extra Modul benötigt wird.



Code
Die Implementierung von Telegramm und das Verschicken von Bildern ist in Anlehnung an "Random Nerd Tutorial" implementiert worden. In "BOT_TOKEN" muss der Unique-Token des Erstellten Bots eingefügt werden. In "USER_CHAT_IDS" können eine beliebige Anzahl von Telegramm Accounts eingebunden werden, die ein Bild erhalten wollen. Mit dem Befehl "/picture" kann ein manuell erstelltes Bild angefordert werden. Mithilfe von den Variablen "motionState" und "micState" kann verhindert werden, dass aufgrund des selben Eingangssignals 2 identische Bilder direkt hintereinander versendet werden.

// Dieses Projekt wurde in Anlehnung an
// "https://randomnerdtutorials.com/telegram-esp32-cam-photo-arduino/"
// implementiert.

// WIFI
#include <WiFi.h>
#include <WiFiClientSecure.h>

// TELEGRAM
#include <UniversalTelegramBot.h>
#include <ArduinoJson.h>

// CAMERA
#include "esp_camera.h"
#include "camera_pins.h"

// SENSOR
#define MOTION_PIN 2
#define MIC_PIN 13


// WIFI
char* SSID_WIFI = "WLAN_NAME";
char* PASSWORD_WIFI = "WLAN_PASSWORT";

// TELEGRAM
String BOT_TOKEN = "5690480986:AAEtpi5MD_GvyM1vOKoehP6ygjSJTMAtyBU";
int USER_CHAT_ID_LENGTH = 1;
String USER_CHAT_IDS[] = {"BENUTZER_ID_1", "BENUTZER_ID_2"};

WiFiClientSecure client;
UniversalTelegramBot bot(BOT_TOKEN, client);

// SENSOR Variablen
bool motionState = false;
bool micState = false;

// REQUEST TIME
int requestDelay = 1000;
unsigned long lastTime;

// Verschicke eingehende Nachricht über Serial zum Debuggen
void printNewMessage(int index) {
  Serial.println("------------------------");
  Serial.println("NEW MESSAGE:");
  Serial.print("USER:    ");
  Serial.println(bot.messages[index].from_name);
  Serial.print("USER ID: ");
  Serial.println(bot.messages[index].chat_id);
  Serial.print("TEXT:    ");
  Serial.println(bot.messages[index].text);
  Serial.println("------------------------");
}

// Eingehende Nachricht verarbeiten
void processMessage(int messageNum) {

  // Iteration über alle Nachrichten
  for (int i=0; i < messageNum; i++) {
    printNewMessage(i);
    String chat_id = String(bot.messages[i].chat_id);

    // Testen, ob ID des Users in USER_CHAT_IDS enthalten ist
    if (chatIdIsInChatIdList(chat_id) == true) {
      
      // ID des Users ist enthalten => Nachricht kann Analysiert werden
      String text = bot.messages[i].text;
      String user = bot.messages[i].from_name;

      // Wenn Befehl = "/start" => Sende Befehlsübersicht zu
      if (text == "/start") {
        String response = "";
        response += "Welcome, " + user + ".\n";
        response += "Use the following commands to control your outputs.\n\n";
        response += "/picture to take a picture \n";
        response += "/start to get information\n";
        bot.sendMessage(chat_id, response, "");
      }
      
      // Wenn Befehl = "/picture" => Aktuelles Kamera Bild übermitteln
      else if (text == "/picture") {
        String response = "";
        response +=  "Take an Image...";
        bot.sendMessage(chat_id, response, "");
        sendPicture(chat_id, false);
      }
      
      // Wenn Befehl nicht vorhanden => Sende "Unbekannter Befehl"
      else {
        String response = "";
        response += "Unbekannter Befehl!\n";
        response += "Bitte versuche es erneut.";
        bot.sendMessage(chat_id, response, "");
      }
      
      // Wenn ID des Users in USER_CHAT_IDS nicht enthalten ist => Übermittel keine Daten
    } else {
      bot.sendMessage(chat_id, "Unauthorized User", "");
      continue;
    }
  }
}

// Testen, ob User ID in USER_CHAT_IDS enthalten ist
bool chatIdIsInChatIdList(String chat_id) {
  for (int i = 0; i < USER_CHAT_ID_LENGTH; i++) {
    if (chat_id == USER_CHAT_IDS[i]){
      return true;
    }
  }
   return false;
}

// Photo erstellen, verschicken an chat_id
String sendPicture(String chat_id, bool flash) {
  const char* myDomain = "api.telegram.org";
  String getAll = "";
  String getBody = "";

  // Photo erstellen
  camera_fb_t* fb = NULL;
  fb = esp_camera_fb_get();

  // Abbruch, wenn Photo nicht erstellt werden konnte
  if (!fb) {
    Serial.println("Camera capture failed");
    delay(1000);
    ESP.restart();
    return "Camera capture failed";
  }
  Serial.println("Connect to " + String(myDomain));

  // Verbindung mit Telegram erfolgreich
  if (client.connect(myDomain, 443)) {
    Serial.println("Connection successful");

    // HTTP Request erstellen
    //X ==> Boundary
    String head = "--X\r\nContent-Disposition: form-data; name=\"chat_id\"; \r\n\r\n" + chat_id + "\r\n--X\r\nContent-Disposition: form-data; name=\"photo\"; filename=\"esp32-cam.jpg\"\r\nContent-Type: image/jpeg\r\n\r\n";
    String tail = "\r\n--X--\r\n";
    
    // Variablen für HTTP Request
    uint16_t imageLen = fb->len;
    uint16_t extraLen = head.length() + tail.length();
    uint16_t totalLen = imageLen + extraLen;

    client.println("POST /bot" + BOT_TOKEN + "/sendPhoto HTTP/1.1");
    client.println("Host: " + String(myDomain));
    client.println("Content-Length: " + String(totalLen));
    client.println("Content-Type: multipart/form-data; boundary=X");
    client.println();
    client.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) {
        client.write(fbBuf, 1024);
        fbBuf += 1024;
      }
      else if (fbLen % 1024 > 0) {
        size_t remainder = fbLen % 1024;
        client.write(fbBuf, remainder);
      }
    }

    client.print(tail);

    esp_camera_fb_return(fb);

    int waitTime = 10000;   // Warte 10 Sekunden
    long startTimer = millis();
    boolean state = false;

    while ((startTimer + waitTime) > millis())
    {
      Serial.print(".");
      delay(100);
      while (client.available())
      {
        char c = client.read();
        if (c == '\n')
        {
          if (getAll.length() == 0) state = true;
          getAll = "";
        }
        else if (c != '\r')
          getAll += String(c);
        if (state == true) getBody += String(c);
        startTimer = millis();
      }
      if (getBody.length() > 0) break;
    }
    client.stop();
    Serial.println(getBody);
  }
  else {
    getBody = "Connected to api.telegram.org failed.";
    Serial.println("Connected to api.telegram.org failed.");
  }
  return getBody;
}


void setup() {
  // Initialisiere Sensoren, Telegram und Wifi
  startSerial();
  connectToWifi();
  setTelegramCertificate();
  setupCamera();
}

// Starte Serielle Schnittstelle
void startSerial() {
  Serial.begin(115200);
  Serial.println("Start SERIAL");
}

void setTelegramCertificate() {
  client.setCACert(TELEGRAM_CERTIFICATE_ROOT);
}

// Verbinde mit WLAN
void connectToWifi() {
  Serial.println("Connect to WIFI");
  WiFi.begin(SSID_WIFI, PASSWORD_WIFI);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.print("IP: ");
  Serial.println(WiFi.localIP());
}

// Konfiguriere PINs der Kamera
void setupCamera() {
  Serial.println("Setup CAMERA");
  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;
  config.frame_size = FRAMESIZE_SVGA; //SVGA 800x600  / UXGA 1600x1200
  config.jpeg_quality = 10;
  config.fb_count = 2;

  esp_err_t err = esp_camera_init(&config);
  if (err != ESP_OK) {
    Serial.printf("Setup CAMERA failed with ERROR: ", err);
    return;
  }
}

void loop() {
  // Überprüfe Sensor Werte
  checkSensors();

  // Überprüfe Nachrichten der Benutzer
  checkMessagingServer();
}

void checkSensors() {

  // Wenn Bewegungserkennung wieder unter Schwellwert
  if (motionState == true && digitalRead(MOTION_PIN) == 0) {
    motionState = false;
  }
  
  // Wenn PIR Sensor über Schwellwert und 
  // derzeit keine Übermittlung läuft
  if (motionState == false && digitalRead(MOTION_PIN) == 1) {
    for (int i = 0 ;i < USER_CHAT_ID_LENGTH; i++) {

      //Verschicke Photo und Text "MOTION detected" an alle hinterlegten IDs
      bot.sendMessage(USER_CHAT_IDS[i], "MOTION detected" , "");
      sendPicture(USER_CHAT_IDS[i], false);
    }
    motionState = true;
  }

  // Wenn Mikrofon Pegel wieder unter Schwellwert
  if (micState == true && digitalRead(MIC_PIN) == 0) {
    micState = false;
  }

  // Wenn  Mikrofon Pegel über Schwellwert und 
  // derzeit keine Übermittlung läuft
  if (micState == false && digitalRead(MIC_PIN) == 1) {

    //Verschicke Photo und Text "SOUND detected" an alle hinterlegten IDs
    for (int i = 0 ;i < USER_CHAT_ID_LENGTH; i++) {
      bot.sendMessage(USER_CHAT_IDS[i], "SOUND detected" , "");
      sendPicture(USER_CHAT_IDS[i], false);
    }
    micState = true;
  }
}

// Überprüft per Polling,
// ob noch nicht Abgerufte Nachrichten auf dem Server enthalten sind
void checkMessagingServer() {

  // Jede Sekunde Nachrichten abrufen
  if (millis() > lastTime + requestDelay)  {
    int numOfMessages = bot.getUpdates(bot.last_message_received + 1);
    if (numOfMessages != 0) {
      bool newMessage = true;

      // Solange noch neue Nachrichten vorhanden
      while(newMessage) {

        // Bearbeite Nachricht
        processMessage(numOfMessages);

        // Sind in der Zwischenzeit noch neue Nachrichten eingetroffen
        numOfMessages = bot.getUpdates(bot.last_message_received + 1);
        if (numOfMessages == 0) {
          newMessage = false;
        }
      }
    }
    lastTime = millis();
  }
}


Zusammenbau
Beim Zusammenbau wurde darauf geachtet, dass die Kabel nicht direkt an die Kontakte gelötet werden, um später noch Änderungen am Programmcode vornehmen zu können. Aus diesem Grund sind die Sensoren und der Mikrokontroller mittels Steckverbinder verschaltet.

Komponententest

Tabelle 3: Test der Anforderungen
ID Inhalt Getestet von Datum
1 Die Kamera muss mit 5V Spannung versorgt werden. Kevin Mudzinski 11.10.2022
2 Der PIR-Sensor muss Bewegungen registrieren und ein Bild verschicken. Kevin Mudzinski 11.10.2022
3 Der Sound-Sensor muss Geräusche größer Vergleichspegel registrieren und ein Bild verschicken. Henry Fröse 01.10.2022
4 Die ESP32-CAM erstellt ein Foto, wenn der Bewegungs- oder der Geräuschssensor ein HIGH-Signal dem Arduino überträgt. Henry Fröse 01.10.2022
5 Der Kamera muss im Heimnetzwerk mittels SSID und Passwort eingebunden sein. Kevin Mudzinski 11.10.2022
6 Das Bild muss an alle gewünschten Benutzer übertragen werden. Kevin Mudzinski 11.10.2022
7 Es müssen mehrere Benutzer eingebunden werden können, die benachrichtigt werden. Henry Fröse 01.10.2022
8 Die Benutzer muss die Möglichkeit haben, manuell ein Bild anzufordern. Kevin Mudzinski 11.10.2022
9 Die Kamera muss in 2 Dimensionen ausgerichtet werden können. Henry Fröse 01.10.2022
10 Die Kamera muss in ihrer Ausrichtung fixierbar sein. Henry Fröse 01.10.2022
11 Die Kamera muss mittels Schraubverbindung an Oberflächen fixierbar sein. Kevin Mudzinski 11.10.2022

Tabelle 3 zeigt, welche Anforderung von wem getestet wurde

Ergebnis

Die Überwachungskamera ist vielseitig einsetzbar. Unter anderem auch als Baby-Cam.

Hier ein Video:

Zusammenfassung

Lessons Learned

Bei der Umsetzung des Projektes mussten wir feststellen, dass die Ausrichtung von kleinen Bauteilen auf dem 3D-Drucker von größter Relevanz ist, um die Stabilität der Bauteile sicherstellen zu können.

Des Weiteren haben wir gelernt, dass sich Telegramm gut dafür eignet, schnell Daten, wie Text und Bild, verschiedenen Personen zu übermitteln, ohne eine eigene Server- & Benachrichtigungs-Infrastruktur zu erstellen.

Projektunterlagen

Projektplan

Projektdurchführung

Tabelle 4: Bearbeitung der Meilensteine
ID Meilensteine Erledigt am Erledigt von
1 PIR Sensor wird vom Mikrocontroller eingelesen und detektiert Bewegung korrekt 12.10.2022 Henry Fröse
2 Mikrofon wird vom Mikrocontroller eingelesen und detektiert Lautstärke korrekt 28.10.2022 Henry Fröse
3 Inbetriebnahme der Kamera mittels enthaltenem Beispielprogramm 04.11.2022 Henry Fröse
4 Verbinden des Mikrocontrollers mit dem Heimnetzwerk 08.11.2022 Henry Fröse
5 Erstellung eines Telegramm-Bots 09.11.2022 Kevin Mudczinski
6 Verbindung zwischen Telegram-Bot und Mikrocontroller aufgebaut 14.11.2022 Kevin Mudczinski
7 Erstellung einfacher Testbefehle mit Textzurückgabe 15.11.2022 Kevin Mudczinski
8 Verschicken von Bilder mittels Telegram-Bot 17.11.2022 Kevin Mudczinski
9 Automatische Versendung von Bildern bei Bewegungen und Geräuschen 20.11.2022 Henry Fröse
10 Entwicklung eines Gehäuses 05.11.2022 Kevin Mudczinski
11 Entwicklung einer Wandhalterung für die Kamera 27.11.2022 Kevin Mudczinski
12 Löten und Zusammenbau der Kamera mit Sensoren 08.12.2022 Kevin Mudczinski

CAD-Modelle

ZIP-File mit allen CAD-Modellen: Überwachungskamera

Programmcode

ZIP-File mit dem Programmcode: Programmcode


→ zurück zur Übersicht: WS 22/23: Angewandte Elektrotechnik (BSE)