Alarmanlage mit RFID und automatischem Anruf / SMS versand und Steuerung per SMS

Guten Abend allerseits!

ich bin momentan dran eine Alarmanlage für eine Gartenhütte zu bauen, da bei Nachbarsgrundstück vor kurzem eingebrochen wurde. Bisher hat alles funktioniert, bis auf das sich der Arduino ab und zu mal aufhängt, das heißt er macht gar nichts mehr.

Habe ein Problem schon gefunden indem ich die einzelnen Schritte an den seriellen Monitor geschickt und geschaut hab, wo er stehen bleibt. Und zwar beim RTC.. warum weiß ich nicht, evtl braucht das RTC bisschen Zeit um Informationen rauszugeben!? Daher hab ich ein Delay von 50ms eingefügt.

Zudem habe ich 75% des Arbeitsspeichers belegt, kann es auch daran liegen?

btw. Die SMS abfrage ist sehr unsauber geschrieben, aber sie funtioniert :slight_smile: möchte ich noch ändern

###########
Setup // Loop
###########

#include <SPI.h>
#include <MFRC522.h>
#include <SoftwareSerial.h>
#include <Wire.h>
#include "RTClib.h"
 
RTC_DS1307 rtc;


//Create software serial object to communicate with SIM800L
SoftwareSerial mySerial(9, 10);

bool alarmspeicher;
int i;
int state = 0;
char telefonnummer[] = "+4917632xxxxxxx"; //Telefonnummer für Anruf u. SMS
char tel_befehl[50];
char incoming_msg [155];

//Variablen aktuelle Zeit
unsigned int time_min;
unsigned int time_sek;
unsigned int time_hr;

//Pufferspeicher für SMS-Nachricht
char buffer_1[250];
 
#define RST_PIN   5     // SPI Reset Pin
#define SS_PIN    6    // SPI Slave Select Pin
 
MFRC522 mfrc522(SS_PIN, RST_PIN);   // Instanz des MFRC522 erzeugen

unsigned long previousMillis = 0; // speichert den Zeitpunkt an dem zuletzt geschalten wurde
const long interval = 3000; // Länge der Pause in ms

byte rfid_id[10];
// rfid_safed. Dies ist der registrierte RFID-Chip
byte rfid_safed[10] = {105, 03, 61, 163};
bool speicher; 

  
void setup() {
  Serial.begin(9600);  // Serielle Kommunikation mit dem PC initialisieren
  SPI.begin();         // Initialisiere SPI Kommunikation
  mfrc522.PCD_Init();  // Initialisiere MFRC522 Lesemodul
  //Starte Verbindung zu RTC
  rtc.begin();
  //Starte Verbindung zu Sim800L
  mySerial.begin(9600); 
  
  //LEDs
  pinMode(3,OUTPUT);
  pinMode(4,OUTPUT);

  //Bewegungsmelder
  pinMode(6,OUTPUT);
  pinMode(7,INPUT);


  //Türschalter
  // freie Pins 0,1,3,8
}

void loop() {
 Serial.println("im Loop");
 
  // Nur wenn eine Karte gefunden wird und gelesen werden konnte, wird der Inhalt von IF ausgeführt
  // PICC = proximity integrated circuit card = kontaktlose Chipkarte


  if (mfrc522.PICC_IsNewCardPresent() && mfrc522.PICC_ReadCardSerial() ) {
    Serial.print("Gelesene UID:");
    for (byte i = 0; i < mfrc522.uid.size; i++) {
            // Abstand zwischen HEX-Zahlen und führende Null bei Byte < 16
            Serial.print(mfrc522.uid.uidByte[i] < 0x10 ? " 0" : " ");
            Serial.print(mfrc522.uid.uidByte[i], DEC);
            rfid_id[i] = mfrc522.uid.uidByte[i];
            }
            Serial.println("");
        //Überprüfung ob RFID-Chip autorisiert ist
        if (speicher == LOW && rfid_id[0] == rfid_safed[0] && rfid_id[1] == rfid_safed[1] && rfid_id[2] == rfid_safed[2] && rfid_id[3] == rfid_safed[3])
            {
              Serial.println("Chip autorisiert");
              if(speicher == LOW) alarm_ein();
              return;
              }
         else if (speicher == LOW) 
               {
                Serial.println("Chip nicht autorisiert");
                
                analogWrite (3,120);
                analogWrite (4,255);
                delay(1000);
                analogWrite (3,0);
                analogWrite (4,0);
               }
         if (rfid_id[0] == rfid_safed[0] && rfid_id[1] == rfid_safed[1] && rfid_id[2] == rfid_safed[2] && rfid_id[3] == rfid_safed[3])
            {
              Serial.println("Chip autorisiert");
              if(speicher == HIGH) alarm_aus();
              
              }
         else if (speicher == HIGH) 
                  Serial.println("Chip nicht autorisiert");
                analogWrite (3,120);
                analogWrite (4,255);
                delay(1000);
                analogWrite (3,0);
                analogWrite (4,0);
    } 

    
// Versetzt die gelesene Karte in einen Ruhemodus, um nach anderen Karten suchen zu können.
    mfrc522.PICC_HaltA();


//Dies wird ausgeführt, wenn die Alarmanlage scharf gestellt ist
if (speicher == HIGH)
{
  scharfstellung();
  }

if (speicher == LOW) digitalWrite(4,HIGH);

delay(1000);
}

###########
zeit() RTC
###########

void zeit()
{

DateTime now = rtc.now();
 delay(50);
 time_min = (now.minute()); // schreibe aktuelle uhrzeit in Variable
 time_sek = (now.second());
 time_hr = (now.hour());
}

###########
alarm_aus / Alarm ausschalten
###########

void alarm_aus()
{
  Serial.println("alarm_aus");
  speicher = LOW;
  Serial.println("Alarmanlage wurde ausgeschaltet");
  digitalWrite(3,LOW);
  digitalWrite(4,HIGH);
  





//Gelesene Karte wieder auf 0 Setzen
memset(rfid_id, 0, sizeof(rfid_id)); // evtl. auch 'sizeof(rfid_id)'

  //Prüfung ob gelöscht
/*int  i=0;
  while(i<=sizeof(rfid_id)){
     Serial.print(rfid_id[i]);
     Serial.print(" ");
     i++;
     }
 i=0; 
*/
  
  delay(1000);
  // Versetzt die gelesene Karte in einen Ruhemodus, um nach anderen Karten suchen zu können.
  mfrc522.PICC_HaltA();
  delay(1000);
return;
  }

###########
alarm_ein() / Alarm einschalten
###########

void alarm_ein()

{
   Serial.println("Alarm_ein");
  //Speichervariable Alarmanlagezustand
  speicher = HIGH;
  Serial.println("Alarmanlage wurde scharfgestellt");
  digitalWrite(4,LOW);
  delay(50);
  digitalWrite(3,HIGH);
  delay(2000);
  digitalWrite(3,LOW);
  delay(1000);

  //LED indikator Alarm ein
  int i=0;
  while(i<=5) {
        digitalWrite(3,HIGH);
        delay(100);
        digitalWrite(3,LOW);
        delay(100);
        i++;
        }
  digitalWrite(3,HIGH);

  


//Gelesene ID aus RFID-Chip wieder auf 0 Setzen
memset(rfid_id, 0,sizeof(rfid_id)); // evtl. auch 'sizeof(rfid_id)'

  //Prüfung ob gelöscht 
  /*
  i=0;
  while(i<=sizeof(rfid_id)){
     Serial.print(rfid_id[i]);
     Serial.print(" ");
     i++;
     }
   */



  i=0;  
  delay(1000);
  // Versetzt die gelesene Karte in einen Ruhemodus, um nach anderen Karten suchen zu können.
  mfrc522.PICC_HaltA();
return;
  }

###########
buffer_del() // SMS Pufferspeicher löschen
###########

void buffer_del()
{  
  //NachrichtenBuffer löschen
  i=0;
  while(i<=sizeof(buffer_1))
    {
    buffer_1[i] = 0;
    i++;
    }
  Serial.println("SMS Buffer gelöscht");  

}

###########
scharfstellung() // Alarmanlage ist eingeschaltet
###########

void scharfstellung()
{
Serial.println("in scharfstellung");
//Lässt die rote LED blinken
unsigned long currentMillis = millis(); // Aktuelle Zeit wird in currentMillis gespeichert
Serial.println("Step1");
if (currentMillis - previousMillis >= interval) { // Falls mehr als 1000 ms vergangen sind
    previousMillis = currentMillis; // Zeitpunkt der letzten Schaltung wird festgehalten 
    digitalWrite(3, !digitalRead(3)); // LED wird ein- bzw. ausgeschaltet
    }
Serial.println("Step2");


 if (alarmspeicher==HIGH && digitalRead(7) == LOW)   //Alarmspeicher zurücksetzen, Wenn alarm nicht mehr ausgelöst
      {
      alarmspeicher = LOW;
      Serial.println("Alarmspeicher wurde zurückgesetzt");
      }
  else    
      {
      

      //aktuelle Zeit vom RTC abrufen
      Serial.println("Step3");
      zeit();

      //Jede Minute SMS abrufen
      Serial.println("Step4");
      if (time_sek <= 1) readSMS();

      Serial.println("Step5");

  // Anruf starten wenn HIGH geschlossen ######Alarmauslösung 
  // Später noch ändern! Ausführen bei LOW-Signal (Drahtbruchsicherheit)
      if(digitalRead(7)==HIGH && alarmspeicher==LOW) 
          {
          alarmspeicher = HIGH;
          Serial.print("Alarmspeicher: ");
          Serial.println(alarmspeicher);
          delay(1000);
          Serial.println("Anruf wird gestartet");
          //startcall(); //wieder aktivieren wenn Programm läuft!

          //sendSMS(); //optional kann eine SMS gesendet werden

          }

      }
Serial.println("Step6");
}

###########
sendSMS() // SMS schicken
###########

void sendSMS()
{
   Serial.println("in sendsms");
  if (state = 20)
  {
  mySerial.println("AT"); //Handshake mit SIM-Modul
  updateSerial();

  mySerial.println("AT+CMGF=1"); // Textmodus konfig.
  updateSerial();
  mySerial.println("AT+CMGS=\"+4917632XXXXXX\"");
  updateSerial();
  mySerial.print("ALARM ausgeloest"); //text 
  updateSerial();
  mySerial.write(26);

  }
}

edit: rest vom code kommt in 5 minuten, da Uploadtimer 5min.
edit2: Handynr. gelöscht, Hinzufügen von Funktion

Funktionen sind folgende:
-Alarmanlage kann über RFID-Tag eingeschaltet werden -> LED blinkt Rot und zeigt: Alarmanlage ist scharfgestellt
-Soll später auch mit dem RFID Tag vom Handy funktionieren
-Mit RFID-Tag kann die Alarmanlage wieder ausgeschaltet werden, LED ist dauerhaft grün
-Alarmanlage löst bei Alarm (Türschalter, Bewegungsmelder) einen Anruf aus und sendet optional eine SMS
-Durch senden von einer SMS mit Inhalt 't' zum Arduino kann ein Testanruf ausgeführt werden, um zu schauen ob er noch "wach" ist :stuck_out_tongue:
-Durch senden von einer SMS mit Inhalt 's' kann die Alarmanlage ausgeschaltet werden

Habt ihr Erfahrung mit einem Akku? Habe mir eine 9V "weidezaunbatterie" überlegt, die sollte 30 Tage halten.

###########
readSMS() // SMS lesen und verarbeiten
###########

void readSMS()
{
   Serial.println("in smslesen");
  char msg[120];
  mySerial.println("AT"); //Handshake mit simmodul
  updateSerial();
  
  mySerial.println("AT+CMGF=1"); // Konfigurierung Text Modus
  updateSerial();
  mySerial.println("AT+CNMI=1,2,0,0,0"); 
  updateSerial();
 



  char arrayread = 0;



  // Hole die an char array (puffer) übergebenen Werte und 
  // schreibe sie in den arrayread
  // Textnachricht beginnt bei buffer_1[52] (muss eventuell geprüft werden im Seriellen monitor)
  i=51;
  
  while(i<=60){
      Serial.println("Schleife i=");
      Serial.println(i);  
      Serial.println(buffer_1[i]);
      i++;
      delay(100);
      }
  
  arrayread=(buffer_1[52]); //52 Dort fängt der Text an
  
  //Überprüfung ob korrekte Zahl im Array gewählt wurde
  Serial.println("##In Variable geschriebener Char##");
  delay(100);
  Serial.println(arrayread);
  Serial.println("################");
  
  delay(100);


  // Entscheidung was ausgeführt werden soll



  // Empfangene SMS mit t, Probeanruf wird durchgeführt
  
  if (arrayread == 't')
  { 
  Serial.println("Testanruf wird ausgefuehrt... bitte warten");
  startcall();
  }

  if (arrayread == 'r' && state != 50)
  { 
  Serial.println("Reset wurede durch SMS ausgelöst");
  buffer_del();
  setup();
  }

  //Wenn Status 50 (Stoppstellung) && SMS mit r kommt
  if (state == 50 && arrayread == 'r')
  {
  Serial.println("Controller wird zurueck in LOOP versetzt");
  buffer_del();
  delay(1500);
  loop();
  }
  
  if (arrayread == 's')
  { 
  Serial.println("Alarmanlage wird ausgeschaltet");
  buffer_del();
  speicher = LOW;
  alarm_aus();
  return;
  }
  
  delay(1000);

  //Alle gespeicherten Nachrichten 1x am Tag löschen
  if(time_hr == 20 && time_min <= 1 && time_sek <= 30)
   {
    mySerial.println("AT+CMGD=1,4"); 
    Serial.println("Nachrichtenspeicher wurde geloescht");
    Serial.print("Timestamp:"); Serial.print(time_hr); Serial.print(":"); Serial.print(time_min); Serial.print(":");Serial.print(time_sek);
    
   }
   
buffer_del();
//if (state == 50) stopstellung();    
}

###########
startcall() // Anruf starten
###########

void startcall()
{
 Serial.println("in startcall");
  state=30;


mySerial.println("AT"); //Handshake mit SIM-Modul, gibt OK zurück
  updateSerial();


//Schreib Telnr. aus Variable in neuen Char
sprintf(tel_befehl, "ATD+ %s\;", telefonnummer);


  mySerial.println(tel_befehl); //Anruf starten Befehl
  updateSerial();
  Serial.println("Anruf wurde gestartet");
  //Anrufdauer 90 sek.
  delay(90000); 
  //Auflegen
  mySerial.println("ATH");
  updateSerial();



}

###########
updateSerial() // Update und Übergabe Serielle schnittstelle, Füllen von SMS Puffer
###########

char msg;

void updateSerial()
{
  delay(500);
  i=0;
  while (Serial.available()) 
  {
       
        mySerial.write(Serial.read());
  }
 
  while(mySerial.available()) 
  { 
    msg = (mySerial.read());
    Serial.write(msg);
    buffer_1[i] = msg;
    i++;

   
  }
}

Weitere Frage aufgetaucht:

Sollte man Serial.print Befehle zum seriellen monitor auskommentieren, wenn der Arduino nicht über USB verbunden ist?

Er hängt sich meistens in der funktion scharfstellung() auf. Laut seriellem monitor müsste das relativ weit oben im Code passieren.

Habe mal noch den Schaltplan angehängt

Alarmanlage_schaltplan.pdf (110 KB)

Na dann..

marci9425:
Bisher hat alles funktioniert, bis auf das sich der Arduino ab und zu mal aufhängt, das heißt er macht gar nichts mehr.

Es wäre eine schnelle - aber NICHT zielführende - Möglichkeit den Watchdog auslösen zu lassen.
Dann würde es zumindest zu einem Neustart kommen.

Zudem habe ich 75% des Arbeitsspeichers belegt, kann es auch daran liegen?

Naja 75% geht schon.

btw. Die SMS abfrage ist sehr unsauber geschrieben, aber sie funtioniert :slight_smile: möchte ich noch ändern

Gut zu wissen...

###########
Setup // Loop
###########

Nein das geht garnicht.

Hier hatte ich - und ganz bestimmt auch viele Leser aufgehört.

Da Du keine Antwort bisher bekamst...:
Nimm Deinen Sketch, ersetze Rufnummern, NFC-ID, Passwörter etc. Speichere ihn bei Dir und im Dateinamen zusätzlich "fuerforum"gefolgt vom Datum/Zeit z.B. 20200407_1940
.Damit hast Du lokal Deine Kopie und den selben (Er-)Kenntnisstand wie diejenigen, die sich dann daran abarbeiten.

Stell den Sketch komplett rein, damit jeder der es möchte den auch kompilieren kann.
Wenn Libs verwendet werden, bitte die Quellen mit angeben.

Es wird schon....

Serial.println("Controller wird zurueck in LOOP versetzt");

buffer_del();
  delay(1500);
  loop();

Das ist gar nicht gut.

Im übrigen kannst du mit dem F-Makro viel RAM sparen.

Guten Abend,

Danke erstmal für die Antworten!
Das ist der komplette Sketch, jedoch habe ich die Funktionen in Tabs ausgelagert, um es übersichtlicher zu halten.

Gerne kann ich den Sketch in einem archiv bereitstellen.

Was ist gar nicht gut?
delay? Loop()? Lieber return?
die Funktion serial.print(f(„text“)); habe ich erst vorhin entdeckt

Was ist gar nicht gut?
delay? Loop()? Lieber return?

In loop wieder loop() aufrufen. Rekursion ist eine theoretische Spielerei für Informatiker. Die bei knappem RAM in die Hose geht. Wenn du loop beendest (return;) wird loop neu gestartet. Aber wofür soll das gut sein....
Ebenso ist setup() nochmal aufrufen kein Reset!

die Funktion serial.print(F(„text")); habe ich erst vorhin entdeckt

Schön. Wird -konsequent angewendet- den RAM Bedarf reduzieren.

michael_x:
In loop wieder loop() aufrufen. Rekursion ist eine theoretische Spielerei für Informatiker. Die bei knappem RAM in die Hose geht. Wenn du loop beendest (return;) wird loop neu gestartet. Aber wofür soll das gut sein....
Ebenso ist setup() nochmal aufrufen kein Reset!
Schön. Wird -konsequent angewendet- den RAM Bedarf reduzieren.

loop() habe ich geschrieben um aus der Funktion readSMS vorzeitig rauszukommen, d.h. er soll danach in der Funktion nichts mehr abarbeiten und wieder in den loop springen.

Habe das Abrufen der Zeitfunktion (RTC) mal auskommentiert, bisher läuft es seit mehreren Stunden ohne Probleme

Habe die Dateien mal angehängt, hoffe das ist so besser?

alarmanlage_RFID - Kopie.zip (6.93 KB)

ein erster Blick,

  Serial.println("Anruf wurde gestartet");
  //Anrufdauer 90 sek.
  delay(90000);
  //Auflegen
  mySerial.println("ATH");

In der Zeit kannst Du nicht einmal den Alarm löschen.

      //aktuelle Zeit vom RTC abrufen
/*
      zeit();
      delay(50);
      //Jede Minute SMS abrufen
      Serial.println(time_sek);
      delay(50);
      if (time_sek <= 1) readSMS();
      delay(50);
*/

Mach das anders:
Bau Dir eine Variable in der Du Dir die minute merkst, während der Du das letzte mal sms gelesen hast.

prüfe ob gemerkte minute != aktuelle minute, dann lese SMS und stelle gemerkte minute auf aktuelle minute.

 //Alle gespeicherten Nachrichten 1x am Tag löschen
  if(time_hr == 20 && time_min <= 1 && time_sek <= 30)

Das ist die einzige Stelle, an der Du ggfls. nochmal die Uhrzeit benutzt.
Die gesamten selbst angelegten Variablen aus dem RTC-tab machen daher keinen Sinn.
Wenn Du nur 1x da durch wilst, musst Du das auch so programmieren.

Das geht alles ohne time_xx Variablen.
RTC-Tab löschen; DateTime now = rtc.now(); an den Anfang von loop() und fertig.

Und schmeiss die ganzen delays so weit als möglich aus Deinem Code.

Moin!

marci9425:
... um aus der Funktion readSMS vorzeitig rauszukommen

Darum gab es schon Glaubenskriege.
Fraktion 1 besteht darauf, dass der einzige Ausgang einer Funktion das return am Ende sein darf.
Die andere Fraktion erlaubt auch mitten in einer Funktion return.

Ich neige zur zweiten Variante, die erste führt m.E. häufig zu sehr verschachtelten Konstruktionen oder zu einem Haufen sehr vieler sehr kleiner Funktionen.

In jedem Fall geht es im rufenden Program ja an der Stelle nach der Funktion weiter. Wenn das nicht der Fall sein soll, musst Du da ansetzen - Funktion gibt einen Wert zurück und abhängig davon geht es direkt weiter oder eben nicht.

Gruß Walter