Projekt przedstawia urządzenie, które można wykorzystać w domu do kontroli antywłamaniowej pomieszczeń (Fig. 1). Założenia projektowe:
Sercem układu jest platforma Arduino Leonardo (Fig. 2). Do mikrokontrolera podłączono czujnik PIR DHT11. Dane z czujnika przekazywane są do pinu D12 Arduino. Kontrolę stanu urządzenia (alarm uzbrojony/nieuzbrojony) umożliwia mikroprzycisk, który jest podłączony do pinu D7. Oba piny (D12 i D7) wraz z sygnałami modułu zegara czasu rzeczywistego (I2C), stanową komplet sygnałów wejściowych urządzenia. Sygnały wyjściowe obsługują: buzzer odpowiedzialny za generowanie sygnałów akustycznych (D4), czerwoną diodę LED odpowiedzialną za sygnalizowanie wykrytej aktywności (D11), zieloną diodę LED odpowiedzialną za sygnalizowanie stanu alarmu (nieuzbrojony/uzbrajanie/uzbrojony) (D8), dwukanałowy moduł przekaźników umożliwiający podłączenie dwóch niezależnych urządzeń zewnętrznych (np. oświetlenia, D5 i D9) oraz moduł czytnika kart SD (złącze ICSP oraz D10). Dodatkowo, za pomocą magistrali I2C podłączono moduł wyświetlacza LCD.
Po włączeniu zasilania (5V), układ automatycznie przechodzi w tryb czuwania, w trakcie którego za pomocą pasywnego czujnika podczerwieni (PIR) monitorowana jest aktywność. Działanie czujnika oparte jest o precyzyjny pomiar temperatury otoczenia. Zastosowany czujnik HC-SR501 umożliwia skuteczną detekcję ruchu w promieniu do kilku metrów. W przypadku wykrycia aktywności, w zależności od ustawień czujnika (potencjometry czułości i opóźnienia), włączana jest dioda czerwona LED. Jednocześnie, na wyświetlaczu pojawia się komunikat o wykryciu aktywności. Właściwe funkcje alarmu uruchamiane są po uzbrojeniu urządzenia za pomocą przycisku.
Naciśnięcie przycisku powoduje uzbrojenie urządzenia. Następuje ono bezpośrednio po upłynięciu czasu potrzebnego na opuszczenie zabezpieczanej strefy. Czas opóźnienia ustawiono sztywno na 10 sekund. Podczas uzbrajania urządzenia, zielona dioda LED miga, a na wyświetlaczu LCD naprzemiennie pojawia się i znika komunikat "uzbrajanie". Po upłynięciu czasu uzbrajania, zielona dioda LED zostaje zaświecona na stałe, co sygnalizuje uzbrojenie alarmu i przejście urządzenia w tryb czuwania. Po uzbrojeniu alarmu, na wyświetlaczu LCD pojawia się komunikatu "uzbrojony". W tym samym czasie, na port szeregowy i na kartę SD wysyłany jest raport o treści: "data; godzina; Alarm rozbrojony". Po uzbrojeniu alarmu, stale wykonywana jest detekcja ruchu. Każde wykrycie aktywności jest sygnalizowane zaświeceniem czerwonej diody LED, odpowiednim komunikatem na wyświetlaczu LCD i dodatkowo sygnałem akustycznym oraz zwarciem obu przekaźników obsługujących urządzenia zewnętrzne. Zdarzenie jest także rejestrowane odpowienim zapisem na karcie SD oraz komunikatem wysyłanym na port szeregowy. Alarm jest anulowany po ustaniu aktywności, bądź po okresie zwłoki czujnika PIR wyznaczonej za pomocą odpowiedniego potencjometru. Ustanie aktywności jest rejestrowane na wyświetlaczu LCD, porcie szeregowym i karcie SD. Rozbrojenie alarmu następuje po kolejnym naciśnięciu przycisku. Zdarzenie rozbrojenia alarmu jest rejestrowane wyłączeniem zielonej diody LED, na wyświetlaczu LCD i porcie szeregowym oraz odpowiednim raportem zapisanym na karcie SD.
#include "Wire.h" #include <LiquidCrystal_I2C.h> #include <SD.h> boolean uzbrajanie = false; // Zmienna pomoc. do migania diodami podczasu uzbajania boolean uzbrojony = false; // Zmienna uzbrojenia alarmu int intruz = LOW; // Zmienna wykrycia ruchu int val = 0; int PRZYCISK_stan = LOW; // Zmienna pomoc. potrzebna do jednokrotnej rejestracji uzbrojenia/rozbrojenia alarmu int INTRUZ_stan = LOW; // Zmienna pomoc. potrzebna do jednokrotnej rejestracji aktywności/ustania aktywności int licznik; // Zmienna pomoc. dla obsługi przycisku File logfile; #define PRZYCISK 7 #define PRZEKAZNIK_1 5 #define PRZEKAZNIK_2 9 #define BUZZER 4 #define PIR 12 #define LED_UZBROJENIE 8 #define LED_ALARM 11 #define DS3231_I2C_ADDRESS 0x68 #define CZAS_WYJSCIA 10 // W sekundach LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); // Ustawienie adresu ukladu na 0x27 //=========================================== // Funkcje pomocnicze // Sygnał alarmu void alarm() { tone(BUZZER, 4300); // Wygeneruj sygnał o częstotliwości 4300Hz delay(150); tone(BUZZER, 3500); // Wygeneruj sygnał o częstotliwości 3500Hz delay(150); } // Konwersja liczby dziesiętnej do postaci binarnej byte decToBcd(byte val) { return ( (val / 10 * 16) + (val % 10) ); } // Konwersja liczby binarnej do postaci dziesiętnej byte bcdToDec(byte val) { return ( (val / 16 * 10) + (val % 16) ); } //================================================= void dateTimeWrite() { Wire.beginTransmission(DS3231_I2C_ADDRESS); Wire.write(0x00); Wire.endTransmission(); delay(10); Wire.requestFrom(DS3231_I2C_ADDRESS, 7); delay(10); int sc = bcdToDec(Wire.read()); // Sekunda int mt = bcdToDec(Wire.read()); // Minuta int hr = bcdToDec(Wire.read()); // Godzina int dy = bcdToDec(Wire.read()); // Dzień int dt = bcdToDec(Wire.read()); // Data int mh = bcdToDec(Wire.read()); // Miesiąc int yr = bcdToDec(Wire.read()); // Rok logfile = SD.open("logfile.txt", FILE_WRITE); logfile.print(dt); logfile.print("-"); logfile.print(mh); logfile.print("-"); logfile.print(yr); logfile.print(" "); logfile.print(hr); logfile.print(":"); logfile.print(mt); logfile.print(":"); logfile.print(sc); logfile.close(); Serial.print(dt); Serial.print("-"); Serial.print(mh); Serial.print("-20"); Serial.print(yr); Serial.print("; "); Serial.print(hr); Serial.print(":"); Serial.print(mt); Serial.print(":"); Serial.print(sc); } //=========================================== void setup() { Wire.begin(); Serial.begin(9600); pinMode(PRZYCISK, INPUT_PULLUP); pinMode(PRZEKAZNIK_1, OUTPUT); pinMode(PRZEKAZNIK_2, OUTPUT); pinMode(BUZZER, OUTPUT); pinMode(PIR, INPUT); pinMode(LED_UZBROJENIE, OUTPUT); pinMode(LED_ALARM, OUTPUT); digitalWrite(LED_UZBROJENIE, LOW); digitalWrite(LED_ALARM, LOW); lcd.begin(20, 4); lcd.backlight(); lcd.setCursor(0, 0); lcd.print("Alarm: "); lcd.setCursor(0, 1); lcd.print("Stan: "); delay(500); // while (!Serial) { //Wait for serial port to connect. Needed for Leonardo only // } Serial.print("Inicjalizacja karty SD...\n"); if (!SD.begin(10)) { Serial.println("Blad inicjalizacji karty SD!\n"); return; } Serial.println("Karta jest gotowa do zapisu...\n"); } //void setDS3231time(byte second, byte minute, byte hour, byte dayOfWeek, byte // dayOfMonth, byte month, byte year) //{ // // ustawienie czasu i daty w DS3231 // Wire.beginTransmission(DS3231_I2C_ADDRESS); // Wire.write(0); // określa, że następna wartość ma trafić do rejestru sekund // Wire.write(decToBcd(second)); // ustawienie sekund // Wire.write(decToBcd(minute)); // ustawienie minuty // Wire.write(decToBcd(hour)); // ustawienie godziny // Wire.write(decToBcd(dayOfWeek)); // ustawienie dnia tygodnia (1=niedziela, 7=sobota) // Wire.write(decToBcd(dayOfMonth)); // ustawienie dnia (1-31) // Wire.write(decToBcd(month)); // ustawienie miesiąca // Wire.write(decToBcd(year)); // ustawienie roku (0-99) // Wire.endTransmission(); //} void readDS3231time(byte *second, byte *minute, byte *hour, byte *dayOfWeek, byte *dayOfMonth, byte *month, byte *year) { Wire.beginTransmission(DS3231_I2C_ADDRESS); Wire.write(0); // ustawia rejestr DS3231 na 00h Wire.endTransmission(); Wire.requestFrom(DS3231_I2C_ADDRESS, 7); // żąda 7 bajtów danych od modułu DS3231 począwszy od rejestru 00h *second = bcdToDec(Wire.read() & 0x7f); *minute = bcdToDec(Wire.read()); *hour = bcdToDec(Wire.read() & 0x3f); *dayOfWeek = bcdToDec(Wire.read()); *dayOfMonth = bcdToDec(Wire.read()); *month = bcdToDec(Wire.read()); *year = bcdToDec(Wire.read()); } // Przekazanie danych do wyświetlenie w serial monitorze i do LCD void displayTime() { byte second, minute, hour, dayOfWeek, dayOfMonth, month, year; // retrieve data from DS3231 readDS3231time(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month, &year); // CZAS if (hour < 10) { // Godziny lcd.setCursor(0, 3); lcd.print("0"); lcd.print(hour, DEC); } else { lcd.setCursor(0, 3); lcd.print(hour, DEC); } lcd.print(":"); if (minute < 10) { // Minuty lcd.setCursor(3, 3); lcd.print("0"); lcd.print(minute, DEC); } else { lcd.setCursor(3, 3); lcd.print(minute, DEC); } lcd.print(":"); if (second < 10) { //Sekundy lcd.setCursor(6, 3); lcd.print("0"); lcd.print(second, DEC); } else { lcd.setCursor(6, 3); lcd.print(second, DEC); } lcd.setCursor(0, 2); // DATA lcd.print("0"); if (dayOfMonth < 10) { // Dzień lcd.setCursor(0, 2); lcd.print("0"); lcd.print(dayOfMonth, DEC); } else { lcd.setCursor(0, 2); lcd.print(dayOfMonth, DEC); } lcd.print("."); if (month < 10) { // Miesiąc lcd.setCursor(3, 2); lcd.print("0"); lcd.print(month, DEC); } else { lcd.setCursor(3, 2); lcd.print(month, DEC); } lcd.print("."); lcd.print("20"); // Rok if (year < 10) { lcd.setCursor(8, 2); lcd.print("0"); lcd.print(year, DEC); } else { lcd.setCursor(8, 2); lcd.print(year, DEC); } lcd.setCursor(9, 3); // Dzień tygodnia switch (dayOfWeek) { case 1: lcd.print("Niedziela"); break; case 2: lcd.print("Poniedz."); break; case 3: lcd.print("Wtorek"); break; case 4: lcd.print("Sroda"); break; case 5: lcd.print("Czwartek"); break; case 6: lcd.print("Piatek"); break; case 7: lcd.print("Sobota"); break; } } void loop() { displayTime(); // wyświetlanie czasu rzeczywistego delay(1000); // odśwież co sekundę //====================================== // Obsługa przycisku if (digitalRead(PRZYCISK) == LOW) { // gdy wcisniemy przycisk delay(20); while (digitalRead(PRZYCISK) == LOW); { //sprawia, że pomimo trzymania przycisku wcisnietego zmiana nastapi tylko raz delay(20); } licznik++; } if (licznik % 2 == 0) { //gdy reszta z dzielenia przez 2 == 0 uzbrojony = false; } else { uzbrojony = true; } //====================================== // Permanentne sprawdzanie stanu PIR val = digitalRead(PIR); // Reading the sensor value if (val == HIGH) { // if sensor is high digitalWrite(LED_ALARM, HIGH); delay(100); if (intruz == LOW) { //Serial.println("Wykryto ruch"); intruz = HIGH; // Update the variable state in to HIGH lcd.setCursor(7, 1); lcd.print("intruz "); } } else { digitalWrite(LED_ALARM, LOW); delay(200); if (intruz == HIGH) { lcd.setCursor(7, 1); lcd.print("czuwanie"); intruz = LOW; // update the variable state into LOW } } //========================================= // Test uzbrojenia alarmu "UZBROJONY" if (uzbrojony == true) { // ALARM UZBROJONY //----------------------------------------- // OPÓŹNIENIE w UZBRAJANIU alarmu potrzebne do opuszczenia obszaru rejestracji if (uzbrajanie == false) { for (int i = 0; i <= CZAS_WYJSCIA; i++) { // LED_UZBROJENIE miga przez okres CZAS_WYJSCIA sekund lcd.setCursor(7, 0); lcd.print("uzbrajanie "); // napis "uzbrajanie" na wyświetlaczu LCD miga przez okres CZAS_WYJSCIA sekund digitalWrite(LED_UZBROJENIE, HIGH); delay(500); lcd.setCursor(7, 0); lcd.print(" "); digitalWrite(LED_UZBROJENIE, LOW); delay(500); } uzbrajanie = true; // zmiana stanu zmiennej uzbrajanie na "true" (znaczy: został uzbrojony) } //----------------------------------------- // Po okresie opóźnienia uzbrajania, uzbrój alarm digitalWrite(LED_UZBROJENIE, HIGH); // zaświeć LED_UZBROJENIE //Komunikat na Serial port if(PRZYCISK_stan == LOW){ dateTimeWrite(); // zapisz na karcie SD i porcie szeregowym datę i godzinę Serial.println("; Alarm uzbrojony"); // wyślij na port szeregowy komunikat "Alarm uzbrojony" logfile = SD.open("logfile.txt", FILE_WRITE); logfile.println("; Alarm uzbrojony"); // Zapisz na karcie SD komunikat "Alarm uzbrojony" logfile.close(); PRZYCISK_stan = HIGH; // ustaw zmienną sterującą na poziom HIGH } lcd.setCursor(7, 0); lcd.print("uzbrojony "); // Sprawdzenie stanu PIR if (intruz == HIGH) { // WYKRYTO AKTYWNOŚĆ digitalWrite(PRZEKAZNIK_1, LOW); // włącz PRZEKAZNIK_1 digitalWrite(PRZEKAZNIK_2, LOW); // włącz PRZEKAZNIK_2 alarm(); // włącz BUZZER if(INTRUZ_stan == LOW){ // Test potrzebny do jednokrotnego wysłania komunikatu o wykryciu ruchu dateTimeWrite(); // zapisz na karcie SD i porcie szeregowym datę i godzinę Serial.println("; Wykryto ruch"); // wyślij na port szeregowy komunikat "Wykryto ruch" logfile = SD.open("logfile.txt", FILE_WRITE); logfile.println("; Wykryto ruch"); // Zapisz na karcie SD komunikat "Wykryto ruch" logfile.close(); INTRUZ_stan = HIGH; // ustaw zmienną sterującą na poziom HIGH } } else { // AKTYWNOŚĆ USTAŁA digitalWrite(PRZEKAZNIK_1, HIGH); // wyłącz PRZEKAZNIK_1 digitalWrite(PRZEKAZNIK_2, HIGH); // wyłącz PRZEKAZNIK_2 noTone(BUZZER); // wyłącz BUZZER if(INTRUZ_stan == HIGH){ // Test potrzebny do jednokrotnego wysłania komunikatu o ustaniu ruchu dateTimeWrite(); // zapisz na karcie SD i porcie szeregowym datę i godzinę Serial.println("; Ruch ustal"); // wyślij na port szeregowy komunikat "Wykryto ruch" logfile = SD.open("logfile.txt", FILE_WRITE); logfile.println("; Ruch ustal"); // Zapisz na karcie SD komunikat "Ruch ustal" logfile.close(); INTRUZ_stan = LOW; // ustaw zmienną sterującą na poziom HIGH } } // Test uzbrojenia alarmu "NIEUZBROJONY" } else { noTone(BUZZER); // włącz BUZZER digitalWrite(LED_UZBROJENIE, LOW); //zaświeć LED_UZBROJENIE //Komunikat na Serial port if(PRZYCISK_stan == HIGH){ dateTimeWrite(); // zapisz na karcie SD i porcie szeregowym datę i godzinę Serial.println("; Alarm rozbrojony"); // wyślij na port szeregowy komunikat "Alarm uzbrojony" logfile = SD.open("logfile.txt", FILE_WRITE); logfile.println("; Alarm rozbrojony"); // Zapisz na karcie SD komunikat "Alarm rozbrojony" logfile.close(); PRZYCISK_stan = LOW; // ustaw zmienną sterującą na poziom HIGH } digitalWrite(PRZEKAZNIK_1, HIGH); // wyłącz PRZEKAZNIK_1 digitalWrite(PRZEKAZNIK_2, HIGH); // wyłącz PRZEKAZNIK_2 lcd.setCursor(7, 0); lcd.print("nieuzbrojony"); uzbrajanie = false; //zmienna pom. wykożystyw. podczas uzbrajania alarmu (miganie LED_UZBROJENIE). Aby uzbroić alarm zm. uzbrajanie musi być "false" } }