Alarm domowy z powiadomieniem GSM

Urządzenia alarmowe bardzo często są wyposażane w różnego rodzaju sygnalizacje włamania. Najprostszym jest uruchomienie sygnału dźwiękowego czy optycznego, te bardziej zaawansowane potrafią podawać swój status przez Internet czy telefon. Prezentowane w tym artykule urządzenie to amatorski alarm, który potrafi przesłać informację o załączeniu przez krótką informację tekstową, tak zwany SMS. Dla potrzeb modelu wykorzystałem modem GSM firmy Wavecom (testowałem również Siemens M20), jednak można użyć dowolnego telefonu komórkowego. Być może jego wykorzystanie będzie wymagało rozwiązania problemu zasilania oraz sposobu kodowania SMS, jednak jest możliwe i wykonalne w warunkach warsztatu elektronika – amatora.

Prezentowana centralka alarmowa jest jednym z tego rodzaju urządzeń elektronicznych, których podstawowa wartość skupia się na wykonaniu odpowiedniego oprogramowania. Układ elektryczny jest prosty i nieco przypomina płytkę prototypową przeznaczoną do projektowania układów z mikrokontrolerami. Jednostkę sterującą stanowi układ mikrokontrolera firmy ATMEL AT90S2313 taktowany wewnętrznym generatorem o częstotliwości stabilizowanej kwarcem 7,3728MHz. Mikrokontroler ma 15 linii wejścia – wyjścia. Dwie z nich wykorzystywane są przez UART (RXD – 2, TXD – 3), pozostałe trzynaście steruje pracą układów peryferyjnych lub służy do komunikacji mikrokontrolera z otoczeniem.  Port B ustawiany jest przez program jako wyjściowy z załączonymi rezystorami pull-up (DDRB=0xFF). Tryb ten nie ulega zmianie w czasie pracy programu mimo, iż czasami port ten wyprowadza dane a czasami służy do ich odczytu. Jest to pewna szczególna cecha mikrokontrolera AVR, wrócimy jeszcze do niej w dalszej części artykułu. Podobnie jest w przypadku portu D, chociaż tutaj część linii jest liniami wyjściowymi (TXD, DIR) a część wejściowymi (RXD i bity 2 do 5). Nie mniej jednak raz ustawiony tryb pracy nie jest zmieniany podczas funkcjonowania urządzenia.
Jak wspomniałem wcześniej, mikrokontroler AT90S2313 posiada 15 linii portów wejścia – wyjścia. Potrzeby układu alarmu są jednak znacznie większe. Posiada on 8 wejść przeznaczonych dla czujników a dodatkowo wyświetlacz i klawiatura wymagają użycia odpowiednio co najmniej 6 i 7 wyprowadzeń portów. To tylko podstawowe układy otoczenia. Dodajmy do tego jeszcze co najmniej 1 linię dla sterowania układem sygnalizacji alarmu i otrzymamy liczbę 22 wyprowadzeń portów koniecznych do poprawnego funkcjonowania układu alarmu. Konieczna więc była albo zmiana mikrokontrolera na taki posiadający większą liczbę portów albo też zastosowania dodatkowych układów scalonych. Zdecydowałem się na to drugie rozwiązanie.
Sygnał z czujników alarmowych podawany jest na doprowadzenia diody transoptora. Transoptor zapewnia izolację galwaniczną oraz eliminuje część niskonapięciowych zakłóceń, które mogą pojawić się na linii doprowadzającej sygnał z czujnika. Użyłem 2 elementów firmy SHARP o symbolu PC849 (odpowiednik funkcjonalny produkowany jest przez firmę LITEON pod oznaczeniem LTV849). Każda z obudów zawiera 4 transoptory składające się z fototranzystora i diody. Szeregowo połączone z diodami rezystory R1..R8 należy dobrać do parametrów posiadanych czujników. Te stosowane przez mnie o wartości 1 kOhm dobrane zostały dla napięcia sterującego 9...12V.
Osiem wejść czujników podłączone jest do układu multipleksera, który w zależności od poziomu logicznego sygnału przyłożonego na wejście A/B (wyprowadzenie 1) podaje na wyjścia 1Y..1Y odpowiednio stany logiczne z wejść 1A..4A lub 1B..4B. Umożliwia to 4-bitowemu portowi mikrokontrolera odczyt 8-bitowych danych w dwóch „porcjach” po 4 bity. Wyborem 4 wejść, z których odczytywany jest stan steruje wyprowadzenie 6 portu D mikrokontrolera.
Niektóre z portów mikrokontrolera pełnią podwójną rolę. Oprócz tego, że są wyjściowymi np. dla modułu wyświetlacza LCD, to są również wejściowymi dla wierszy klawiatury. Zacznijmy może opis od wyświetlacza. W swojej konstrukcji użyłem modułu LCD 2 linie po 20 znaków w każdej. Wyświetlacz pracuje w trybie interfejsu 4-bitowego wykorzystując linie portu PB0..PB4 jako linie danych, PB4 jako linię wyboru rejestru RS i PB7 jako ENABLE. Wszystkie linie portów oprócz ENABLE są wykorzystywane w dwójnasób: jako sterujące wyświetlaczem oraz pracą klawiatury. Wykorzystywany jest fakt, że w czasie trwania stanu niskiego na linii ENABLE nie są do rejestrów wyświetlacza zapisywane żadne dane. Umożliwia to zmiany stanów pozostałych linii bez zakłócania pracy modułu.
Typowo, na wyprowadzenie 3 złącza wyświetlacza podane jest napięcie z suwaka potencjometru służące do regulacji kontrastu a pomiędzy 15 i 16 doprowadzone jest napięcie dla podświetlenia tła (około 4,3V). Układ można uprościć zwierając napięcie regulacji kontrastu do masy. Dla większości wyświetlaczy LCD jest to wystarczające.
Klawiaturę zbudowano jako matrycę 3 kolumn i 4 wierszy. Trzy bity portu B (4, 5 i 6) pracują jako wyjściowe, cztery jako wejściowe. Wszystkie linie mają załączone wewnętrznie rezystory pull-up. Wyjątkiem są tutaj bity 0 i 1, które wymagają zewnętrznych rezystorów podciągających. Kierunek pracy portu nie jest zmieniany przez program sterujący. Linie pracują zawsze jako wyjściowe a w razie potrzeby ich stan sprawdzany jest przez odczyt rejestru PIN. W architekturze mikrokontrolera AVR rejestr PIN to adres, przy odczycie którego, wyprowadzenie portu załączane jest przez specjalny bufor trójstanowy do wewnętrznej magistrali danych mikrokontrolera.
Rolę układu dopasowującego poziomy napięć interfejsu UART mikrokontrolera do RS232 pełni układ MAX232. Wykorzystane są tylko 2 układy driverów znajdujące się w strukturze. Sygnał interfejsu doprowadzony jest na złącze męskie, 9-wyprowadzeniowe. Układu MAX można nie wlutowywać lub nie wykorzystywać. Zworki JP1 i JP2 znajdujące się na płytce umożliwiają odłączenie sygnałów RXD i TXD mikrokontrolera od układu MAX, i doprowadzenie go do inwertorów 74HCT14. W zamiarze miał być to układ dopasowujący do telefonu GSM (Siemens C35). Rezystory R20 i R21 to dzielnik napięcia, który jest tak dobrany, aby napięcie wyjściowe nie przekraczało 3,3V w stanie wysokim. W drugą stronę, bramka 74HCT14 pracująca jako odbiornik, posiada w strukturze przerzutnik Schmitta, który powoduje, że napięcie powyżej 2,4V uważane jest za stan wysoki. Zaznaczam jednak, że alarm testowany był wyłącznie z modemami firmy Siemens (M20T) oraz Wavecom (WMOD2), a interfejs dla telefonu GSM nie był używany. Złącze dla „zwykłego” telefonu GSM oznaczone jest jako X14 a dla modemu GSM - X12. Bramki układu inwertora pracują również jako układy wyjściowe sterujące pracą przekaźnika oraz diody LED. Całość układu zasilana jest napięciem 5V z umieszczonego na płytce układu stabilizatora 7805 (U3). Do poprawnej pracy wymaga on podania co najmniej 7V na zaciski złącza X10. Uwaga na polaryzację! Kondensatory C9..C12 pełnią rolę filtrowania napięcia zasilającego. C10 i C11 powinny być umieszczone jak najbliżej wyprowadzeń stabilizatora.  Rezystor R11 i kondensator C4 to prosty układ RC (rezystor ładowany przez kondensator) wytwarzający sygnał „reset” dla mikrokontrolera. Elementem wykonawczym alarmu jest przekaźnik RL1. Na doprowadzeniach jego cewki znajduje się dioda D3 mająca za zadania eliminowanie przepięć powstających w czasie załączania i wyłączania cewki. Jak wspomniałem wcześniej, rolę układu dopasowującego pełnią bramki inwertora połączone równolegle w celu zapewnienia większej wydajności prądowej. Przekaźnik sterowany jest wspólnie z diodą LED. Załączeniu przekaźnika odpowiada stan zaświecenia diody.
W pewnych momentach pracy układu, dioda wykorzystywana jest również do sygnalizacji. Wówczas jednak jej błyski są na tyle krótkie, że nie powodują załączenia przekaźnika. Również w czasie przeglądania wierszy klawiatury starałem się dobrać czasy na tyle krótkie aby nie powodować załączania urządzeń zewnętrznych.

 

Tab. 1. Opis wyprowadzeń mikrokontrolera AT90S2313

Wyprowadzenie mikrokontrolera

Opis funkcji

Nazwa

Numer (obudowa DIL20)

Oznaczenie sygnału na schemacie

PB0/AIN0
PB1/AIN1
PB2
PB3/OC1
PB4
PB5/MOSI
PB6/MISO
PB7/SCK

12
13
14
15
16
17
18
19

DB4
DB5
DB6
DB7
RS
COL
LED
EN

Linia danych DB4 modułu LCD, wiersz 2 klawiatury
Linia danych DB5 modułu LCD, wiersz 3 klawiatury
Linia danych DB6 modułu LCD, wiersz 4 klawiatury
Linia danych DB7 modułu LCD, wiersz 1 klawiatury
Linia RS modułu LCD, kolumna 1 klawiatury
Kolumna 2 klawiatury
Linia załączająca przekaźnik i LED, kolumna 3 klawiatury
Linia ENABLE modułu LCD

PD0/RXD
PD1/TXD
PD2/INT0
PD3/INT1
PD4/T0
PD5/T1
PD6/ICP

2
3
6
7
8
9
11

RXD
TXD
-
-
-
-
DIR

Linia odbiornika interfejsu UART mikrokontrolera
Linia nadajnika interfejsu UART mikrokontrolera
Wejście sygnału z czujki zewnętrznej (X1 i X5)
Wejście sygnału z czujki zewnętrznej (X2 i X6)
Wejście sygnału z czujki zewnętrznej (X3 i X7)
Wejście sygnału z czujki zewnętrznej (X4 i X8)
Sterowanie wyborem wejść multipleksera (U2)

 

Opis programu sterującego

Początkowo zamierzałem wykonać całość programu sterującego pracą alarmu w językach Bascom lub GCC. Okazało się jednak, że mikrokontroler AT90S2313 ze swoimi 2 kB pamięci FLASH jest zbyt skromny jak na wymagania kompilatora języka wysokiego poziomu. Zupełnie zaskoczył mnie kompilator GCC. Przeniosłem do niego bibliotekę funkcji, napisaną co prawda dla 8051, lecz po drobnych modyfikacjach kompilator GCC przetworzył źródło na kod wynikowy bez większego problemu. Jednak rozmiar tego kodu był dla mnie zupełnym zaskoczeniem! Wykonałem wyłącznie procedury obsługi LCD w języku C i te po kompilacji zajęły 3 kB! Dla porównania analogiczny funkcjonalnie program wykonany w Bascom AVR zajął jedynie ok.500 bajtów. Podobnie zresztą jak w RC-51. Być może nie umiałem włączyć jakiejś opcji optymalizacji ale... Tak czy inaczej – 2 kB to za mało pamięci aby była wystarczającą do wykonania tego typu aplikacji w jakimś języku wysokiego poziomu. Chcąc nie chcąc, zgodnie z zasadą „coś za coś”, musiałem wykonać całość programu sterującego w języku AVR Asembler. W tym przypadku był to rozmiar kodu wynikowego kosztem czasu spędzonego przy jego implementacji. Przy uruchamianiu i testowaniu programu sterującego posługiwałem się AVR Studio 4 firmy ATMEL.
Aby omówić sposób funkcjonowania programu, muszę wyjaśnić kilka podstawowych reguł, którymi kieruję się przy pisaniu tego rodzaju aplikacji. Dotyczą one głównie stałych i zmiennych oraz sposobu ich interpretacji przez funkcje. Zacznijmy od liczb. Zgodnie z notacją przyjętą przez firmę Atmel, każdą liczbę rozpoczyna bajt mniej znaczący a kończy bardziej znaczący. I tak – jeśli liczba jest 4-bajtową zmienną umieszczoną w rejestrach R0 - R01 - R02 - R03, to najmniej znaczący bajt zawiera R0 a najbardziej znaczący R03. Każdy łańcuch tekstowy wysyłany na wyświetlacz zawiera tylko kody znaków większych od 0x20 (32 dziesiętnie, kod znaku odstępu) a kończy się znakiem o kodzie 0x00. Podobnie jest z ciągami znaków wysyłanych do modemu ale podlegających przetwarzaniu przed ich wysłaniem. Na przykład polecenie zapisując kod PIN zawiera część stałą „AT+CPIN=” oraz zmienny – zależny od karty SIM – poufny kod PIN. Inaczej jest z poleceniami przesyłanymi wprost do modemu. Te kończy sekwencja kodów CR (powrót kursora do początku linii, 0x0A) oraz LF (nowa linia, 0x0C). Reasumując – każdy tekst wyświetlany lub przetwarzany kończy kod 0x00 a każdy tekst przesyłany jako stały do modemu, kończą kody CR-LF. Ten sposób zapamiętywania danych stosowany jest konsekwentnie w całym programie. Wyjątkiem jest sposób w jaki przechowywane są kody załączenia i wyłączenia alarmu oraz kod PIN.
Program główny jest bardzo krótki. Na początku ustawiane są tryby pracy portów B i D. Do rejestru DDRB wpisywana jest wartość 0xFF, co oznacza, że wszystkie wyjścia mają załączone rezystory pull-up i pracują jako wyjściowe. Do rejestru DDRD wpisywana jest wartość 0xC0, która ustawia bity 6 i 7 portu D jako wyjściowe a pozostałe jako wejściowe. Tu jedna uwaga. Linia TXD portu C (bit 1) pracuje jako wyjściowa, jednak wpisanie do rejestru DDRD „1” na pozycję odpowiednią dla tego bitu powoduje, że pracuje ona jako zwykły port wyjściowy i traci swoją funkcję jako TXD. Aby linia ta pracowała poprawnie jako wyjście interfejsu UART, należy do DDRD na pozycji 1 wpisać „0” lub pozostawić stan taki, jaki jest po załączeniu zasilania.
Następne linie programu ustalają częstotliwość z jaką wywoływane będą przerwania Timer’a 0 i 1. Ten pierwszy to licznik 8-bitowy generujący przerwanie po zliczeniu 255 impulsów. 256 impuls zegarowy ustawia flagę przepełnienia OVF0 i powoduje przejście do obsługi przerwania. Przy częstotliwości rezonatora kwarcowego 7,3728MHz, preskalerze ustawionym na wartość 1024 oraz początkowej wartości licznika 0 (licznik Timer’a 0 liczy w górę), przerwania będą wywoływane z częstotliwością 7372800 : 1024 : 256 = 28,125 Hz. Timer 1 również ma preskaler ustawiony na wartość 1024, jednak do rejestrów wpisywana jest wartość inicjująca, od której Timer zlicza impulsy zegarowe w górę, aż do przepełnienia. Timer 1, w odróżnieniu od Timer’a 0, jest 16-bitowy, toteż liczy do wartości 65535. Impuls numer 65536 powoduje przepełnienie, ustawienie flagi OVF1 i przejście do procedury obsługi przerwania. Tu przerwanie wywoływane jest z częstotliwością 7372800 : 1024 : (65535 – 58335) = 1 Hz. Przerwania są zablokowane po uruchomieniu mikrokontrolera, jednak pozostawiony „na wszelki wypadek” rozkaz CLI wyłącza je i blokuje aż do momentu, gdy konieczne będzie ich użycie. Zostawmy na razie procedury obsługi przerwań – będzie jeszcze okazja do nich wrócić.
Od etykiety „STATUS_CHECK” rozpoczyna się część programu mająca za zadanie rozpoznanie trybu, w którym znajdował się mikrokontroler przed wyłączeniem napięcia zasilania. Wywoływana zostaje funkcja odczytująca bajt spod adresu 0x00 w pamięci EEPROM. Wartość tego bajtu odpowiada statusowi urządzenia. Jeśli komórka ta zawiera wartość różną od 0xF0 lub 0x0F, to program uznaje, że jest to pierwsze załączenie alarmu i zażąda wykonania wszystkich niezbędnych nastaw, to jest: podania kodu załączenia i wyłączenia, podania kodu PIN dla używanej karty SIM, wprowadzenia numeru telefonu oraz opisów dla lokalizacji alarmu i wejść czujników. Wybrana metoda sprawdzenia statusu urządzenia jest o tyle pewna, że każdy zapis programu do pamięci FLASH mikrokontrolera AT90S2313 powoduje usunięcie zawartości EEPROM (cały EEPROM zostaje zapisany wartością 0xFF). Po wprowadzeniu nastaw do komórki „STATUS” zostaje zapisana wartość odpowiednia dla stanu wyłączenia alarmu.
Poświęćmy teraz kilka chwil wysyłanym do modemu rozkazom i ich znaczeniu. Interfejs UART pracuje z szybkością 9600 bitów na sekundę. Identyczną prędkość powinniśmy ustawić w posiadanym telefonie, czy modemie. Aby dokonać niezbędnych nastaw należy przy pomocy dowolnego programu terminala nawiązać połączenie z modemem i wydać polecenie „AT+IPR=9600;&W”. Ustawi ono i zapisze w pamięci konfiguracji modemu parametr prędkości transmisji. W przypadku użycia telefonu GSM może się okazać, że nie zaakceptuje on wyżej wymienionej komendy. Wówczas prawdopodobnie łatwiej będzie nam zmienić prędkość z jaką pracuje UART mikrokontrolera, aniżeli telefonu. Można to zrobić poprawiając funkcję „uart_init” a konkretnie wartość wpisywaną do rejestru UBRR. Należy dobrać ją w zależności od używanego rezonatora i wymaganej prędkości transmisji. Gotowe przykłady można znaleźć w notach aplikacyjnych na stronie producenta http://www.atmel.com lub wyliczyć samodzielnie na podstawie wzorów znajdujących się w kartach katalogowych mikrokontrolerów AVR. Gdy sprawdzimy, że modem (telefon) gotowy jest do pracy i komunikuje się poprawnie używając prędkości 9600 bps, możemy podłączyć go do naszego układu.
Jako pierwszą program sterujący wysyła komendę „ATZ”. Powoduje ona ustawienie domyślnego trybu pracy – przywrócone zostają również wszystkie te parametry, które nie zostały zapamiętane na stałe jako domyślne. Następny rozkaz wprowadza numer PIN właściwy dla karty na przykład: „AT+CPIN=9889”. Kod PIN to hasło, którego rola polega na zabezpieczeniu telefonu GSM przed niepowołanym dostępem. Tutaj szczerze powiedziawszy trochę przeszkadza ale niestety – jest wymagany w celu uruchomienia modemu. Jako kolejne wysyłane są polecenia „ATE0” wyłączające tzw. echo komendy oraz „AT+CPMS=SM” ustawiające miejsce przechowywania wiadomości SMS na kartę SIM. Jednocześnie z przyjmowaniem tych poleceń modem włącza się do sieci. W przypadku modemu WAVECOM gotowość do pracy sygnalizowana jest przez migoczącą z częstotliwością około 1 Hz diodę LED, w przypadku SIEMENS MT20 jest odwrotnie – dioda LED zaświeca się na stałe.
Po każdym poleceniu wysłanym do modemu wywoływana jest funkcja „gets” odbierająca ciąg znaków z UART. Oczekiwane jest, że modem odpowie co najmniej wysyłając kody CR i LF. W przypadku braku odpowiedzi od modemu, program przestanie pracować już w fazie inicjacji. Można w ten sposób łatwo stwierdzić, że brak jest poprawnej łączności z modemem. Niestety, nie wykonałem w programie detekcji odłączenia modemu. W przypadku, gdy modem zostanie odłączony tuż przed wysłaniem SMS z powiadomieniem o załączeniu alarmu, alarm zostanie włączony a następnie program będzie oczekiwał na połączenie z modemem i odpowiedź na przesłane polecenia. Jeśli jej nie otrzyma – zawiesi się sprawdzając w nieskończoność stan flagi gotowości do odbioru znaku (bajtu) z UART.
Od etykiety „main” rozpoczyna się główna pętla programu. Na jej początku ponownie odczytywany jest bajt statusu i w zależności od jego wartości podejmowana jest odpowiednia akcja. Polega ona bądź to na wywołaniu procedury obsługi alarmu w stanie czuwania, bądź to obsługi alarmu w stanie wyłączenia.

Stan wyłączenia
Za jego obsługę odpowiedzialna jest funkcja „alarm_off”. Po jej wywołaniu czyszczony jest ekran wyświetlacza LCD a następnie wyświetlona zostaje informacja z pamięci ROM umieszczona w niej pod nazwami symbolicznymi „whenoff” oraz „set_1”. Ta pierwsza umieszczona zostaje w 1 linii ekranu – ja zadeklarowałem ją jako „ALARM WYŁĄCZONY”. Ta druga umieszczona w 2 linii LCD, to pytanie o kod wyłączenia. Zadeklarowałem ją jako „Kod wyłączenia?”. Później zmiennej „visible” nadana zostaje wartość „UCANTSEE” a zmiennej „sensors” wartość „SENSOROFF”. Po wyżej wymienionych operacji, wywoływana jest funkcja odczytu kodu wprowadzanego przy pomocy klawiatury. Kod jest zawsze 4 cyfrowy. Rezultat pracy funkcji zwracany jest w rejestrach o nazwach symbolicznych „cchar1” do „cchar4”. Uwaga: kod załączenia musi być inny niż „0000”. Jest on zarezerwowany do wprowadzenia urządzenia w tryb serwisowy!
Funkcja „compare_codes” porównuje 4 bajty pobrane z pamięci EEPROM spod adresu podanego w rejestrze „eeaddr” z tymi odczytanymi z klawiatury. Jeśli kod wprowadzony jest identyczny z zapamiętanym, do komórki zawierającej bajt statusu w EEPROM, zapisana zostaje wartość informująca program o tym, że alarm jest załączony i następuje powrót do programu głównego. Teraz na podstawie zawartości komórki „status” pamięci EEPROM, wywołana zostaje funkcja obsługująca stan załączenia alarmu.

Stan załączenia
Całość funkcji związanych ze stanem czuwania alarmu umieszczona jest w podprogramie pod etykietą „alarm_on”. Po wejściu do podprogramu załączane jest przerwanie Timer’a 1. Fragment programu zawierający kod jego obsługi, pokazałem na listingu 1. Procedura ta ma do wykonania dwa podstawowe zadania. Ma zwiększyć wartość zmiennej - licznika „goon” oraz odświeżyć zawartość rejestrów licznika Timer’a 1. Do tych dwóch zadań dodałem jeszcze trzecie – krótki, trwający około 1 milisekundy, błysk diody LED. Sygnalizuje ona w ten sposób, że odmierzany jest czas do załączenia. Pętla rozpoczynająca się od etykiety „al_on_wait” trwa dokładnie tyle czasu odmierzanego w sekundach, ile podano w stałej „DELAYTIME”. Jako typową wartość zadeklarowałem 30 sekund. Jest to czas, przed upływem którego osoba załączająca alarm musi się oddalić z obszaru zadziałania czujników.

List. 1. Procedura obsługi przerwania Timer’a 1
;------------------------------------------------------
;obsługa przerwania timera 1
;odmierzanie czasu opóźnienia do zadziałania alarmu
;------------------------------------------------------
tim1_irq:
    sbi    portb,ALARMOUT  ;załączenie LED (migotanie w czasie "ALARMDELAY")
    push   temp            ;przechowanie zawartości "temp" na stosie
    ldi    temp,TIM1RLDH
    out    TCNT1H,temp
    ldi    temp,TIM1RLDL
    out    TCNT1L,temp     ;odświeżenie zawartości Timer'a 1
    inc    goon            ;zwiększenie licznika "goon"
    ldi    temp,0xFF
tim1_loop:
    rcall  delay1ms        ;opóźnienie czasowe dla LED
    pop    temp
    cbi    portb,ALARMOUT  ;wyłączenie diody LED
    reti

Po upływie tego czasu, rozkaz CLI wyłącza obsługę przerwań a przerwanie pochodzące od Timer’a 1 jest blokowane. Teraz wyświetlony zostaje komunikat o załączeniu alarmu („whenon” i „set_2”) a następnie do rejestru kontrolnego przerwań (TIMSK) wprowadzana jest nastawa załączająca przerwanie na skutek przepełnienia Timer’a 0. Jest ono odpowiedzialne za odpytywanie stanu czujników (sensorów) alarmu. Rozkazy składające się na procedurę obsługi umieściłem na listingu 2. Ze względu na pozorną złożoność poświęćmy jej troszeczkę uwagi.

List. 2. Procedura obsługi przerwania Timer’a 0
;--------------------------------------
;obsługa przerwania timera 0
;odpytywanie wejść alarmu
;--------------------------------------

tim0_irq:
    push   temp            ;zapamiętanie "temp" na stosie
    cbi    portd,HIGHNIBBLE ;odczyt młodszej połówki bajtu wejść czujników
    nop                    ;przerwa na czasy propagacji układów
    nop
    in     temp,pind       ;odczyt młodszej połówki bajtu
    asr    temp            ;"dopasowanie" bitów
    asr    temp
    mov    sensors,temp
    sbi    portd,HIGHNIBBLE
    nop                    ;przerwa na czasy propagacji układów
    nop
    in     temp,pind       ;odczyt starszej połówki bajtu
    asr    temp            ;"dopasowanie" bitów
    asr    temp
    andi   temp,0x0F
    swap   temp
    or     sensors,temp
    pop    temp
    reti

Rozkaz „push temp” przechowuje wartość rejestru „temp” na stosie. Następnie zerowany jest bit 6 portu D oznaczony na schemacie jako DIR. Wartość „0” tego bitu oznacza, że odczytane zostaną czujniki podłączone do wejść 1A, 2A, 3A i 4A mikrokontrolera. Tworzą one młodszą połówkę bajtu stanu czujników. Jest ona odczytywana z rejestru PIND, przesunięta dwukrotnie w prawo (dwa polecenia „asr temp”) i zostaje zapamiętana w zmiennej „sensors”. Następnie, po ustawieniu stanu wysokiego na wyprowadzeniu PORTD.6, pobrany zostaje starsza połowa bajtu stanu czujników, przesunięta dwukrotnie w prawo i zsumowana z młodszą. Wynik sumowania zostaje zapamiętany w zmiennej „sensors”. Po odtworzeniu wartości „temp” rola procedury kończy się. Inne funkcje sprawdzają odczytany stan czujników i podejmują odpowiednią akcję. W przypadku stwierdzenia na jednym z wejść informacyjnych mikrokontrolera (PD2..PD5) stanu niskiego, rola przerwania Timer’a 0 kończy się – zostaje ono zablokowane. Dalsze przeglądanie czujników nie jest potrzebne. Ponownie zerowany jest licznik czasu „goon” i załączone zostaje przerwanie Timer’a 0 tym razem odmierzając czas upływający od momentu zadziałania czujnika do momentu załączenia alarmu. Ponownie jest to czas podany w sekundach o wartości „DELAYTIME”. Opóźnienie zostało wprowadzone po to, aby użytkownik miał możliwość wprowadzenia kodu wyłączającego alarm.
Procedura „alarm_on”, sprawdza kod wyłączenia wprowadzany przez użytkownika podobnie jak poprzedniczka. W ten sam sposób kończy pracę ustawiając bajt statusu urządzenia w EEPROM i powracając do głównej pętli programu w przypadku zgodności wprowadzonego kodu z tym zapisanym w EEPROM. Ponownie jak poprzednio – decyzję o podejmowanej przez urządzenie akcji podejmuje główna pętla programu na podstawie zawartości komórki pamięci zawierającej bajt statusu urządzenia. W związku z tym, że jest to pamięć, która nie traci zawartości po wyłączeniu napięcia zasilania, urządzenie po odłączeniu i ponownym podłączeniu napięcia zasilania zostanie wprowadzone w ten sam stan, w którym znajdowało się przed wyłączeniem.

Odczyt kodu
Ramy tego artykułu są trochę zbyt szczupłe aby omówić blisko 1,5 tysiąca linii programu napisanego w języku asemblera. Niestety w związku z tym pewne aspekty będą pominięte i dociekliwy Czytelnik musi sam przeanalizować kod źródłowy programu tak, aby dotrzeć do pewnych szczegółów. Jedną z takich dwóch „trudnych” procedur jest funkcja odczytu kodu wprowadzanego z klawiatury. Zwłaszcza kilka początkowych linii wymaga dokładniejszego omówienia. Umieściłem je na listingu 3.

Listing 3. Fragment funkcji odczytu kodu „read_code”
read_code_loop:
    brid   read_code_next  ;jeśli przerwania są wyłączone,przejdź do
                           ;odczyt klawiatury i nie sprawdzaj stanu sensorów
    mov    temp,sensors    ;sprawdzenie stanu czujników!!! konieczne tutaj,
                           ;bo funkcja nie przerywa pracy do momentu odczytu
                           ;pełnego, 4-cyfrowego kodu
    cpi    temp,SENSORSOFF
    breq   read_code_next  ;jeśli żaden sensor nie jest aktywny omiń
                           ;poniższe rozkazy
    brts   read_code_act   ;jeśli flaga T jest ustawiona, to nie wykonuj
                           ;nastaw przerwania Timera 1
    cli                    ;wyłączenie przerwań
    clr    goon            ;kasowanie licznika czasu opóźnionego załączenia
                           ;(pracuje w przerw.Timer'a 1)
    ldi    temp,0x80
    out    TIMSK,temp      ;załączenie przerwań Timer'a 1 (co 1 sek.)
    sei                    ;załączenie przerwań
read_code_act:
    set                    ;ustawienie flagi "T"
    push   zl              ;przechowanie wskaźnika do bufora w RAM
    rcall  alarm_active
    pop    zl
read_code_next:
    rcall  kbd_read        ;odczyt klawiatury, znak zwracany w “char”

Od etykiety „read_code_next” rozpoczyna się właściwa procedura rozpoznawania wciśniętych klawiszy i nadawania wartości komórkom „cchar1”... „cchar4”. Pamiętajmy o tym, że funkcja jest uniwersalna i wykorzystywana zarówno w trybie wyłączenia alarmu jak i w trybie załączenia. Tryby te różnią się pomiędzy sobą przede wszystkim tym, że w stanie załączenia aktywne jest przerwanie Timer’a 0 przeglądające stan czujników. W związku z tym rozkaz „brid read_code_next” powoduje natychmiastowe przejście do funkcji interpretacji klawiszy wówczas, gdy nie jest załączona obsługa przerwania. Zrobiłem tak po to, aby wartość zmiennej „sensors” nie wpływała na stan alarmu. Dlaczego? Prześledźmy dalsze linie programu.
Sekwencja rozkazów

mov    temp,sensors
cpi    temp,SENSORSOFF

sprawdza, czy któryś z czujników alarmu jest aktywny. Obojętnie czy w stanie wyłączenia, czy załączenia podejmowana jest akcja na podstawie stanu zmiennej „sensors”! W związku z tym możliwe jest uaktywnienie sygnalizacji dźwiękowej i powiadomienia SMS w stanie wyłączenia alarmu. Omawiane wcześniej polecenie „brid” ma urządzenie przed tym uchronić.
Również w tej funkcji konieczne jest jednokrotne zainicjowanie wartości „goon” i załączenie przerwań Timer’a 1 w celu odmierzania czasu opóźnionego załączenia. Inicjacja przeprowadzana jest w zależności od stanu flagi T. Rozkaz „brts read_code_act” omija polecenia nadające wartości zmiennym w przypadku, gdy flaga jest ustawiona. Polecenia „set” ustawia flagę T po przeprowadzeniu inicjalizacji.

Załączenie alarmu
Na listingu 4 umieściłem fragment kodu źródłowego programu odpowiedzialny za obsługę uaktywnienia alarmu. Rozpoczyna się ona od etykiety „alarm_active” zestawem rozkazów testujących stan zmiennej „goon” odmierzającej czas do załączenia alarmu. Jeśli wartość „goon” jest mniejsza lub równa „DELAYTIME”, to nie jest podejmowana żadna akcja. Jeśli wartość „goon” jest większa od „DELAYTIME”, rozkaz „sbi portb,ALARMOUT” załącza sygnalizację dźwiękową a polecenie „rcall send_SMS” wywołuje podprogram wysyłający SMS pod zaprogramowany i zapamiętany w EEPROM numer telefonu. SMS będzie wysłany, o ile bit 0 zmiennej „sms_sent” nie jest ustawiony. Dodatkowo blokowane są przerwania, co powoduje zaniechanie sprawdzania stanu czujników oraz odmierzania czasu. Alarm pozostaje załączony do momentu wprowadzenia kodu wyłączenia lub odłączenia napięcia zasilania.

List. 4. Procedura obsługi załączenia alarmu
;----------------------------------------------------------------------

;alarm aktywny - sygnalizacja dźwiękowa, powiadomienie za pomocą SMS,
;oczekiwanie na kod wyłączenia
;----------------------------------------------------------------------
alarm_active:
    mov    temp,goon
    subi   temp,DELAYTIME  ;sprawdzenie,czy upłynął już czas opóźnienia
    brsh   active_and_on
    rjmp   active_exit
active_and_on:
    cli                    ;wyłączenie przerwań - już nie są potrzebne
    sbi    portb,ALARMOUT  ;załączenie wyjścia alarmu
    tst    smssent         ;jeśli wysyłano już SMS,to nie wysyłaj powtórnie
    brne   active_exit
    rcall  send_SMS        ;wysłanie sms z powiadomieniem
active_exit:
       ret

Wysyłanie SMS

Wysyłanie wiadomości SMS przez mikrokontroler to pytanie bardzo często pojawiające się w listach kierowanych do Redakcji oraz na grupach dyskusyjnych w Internecie, dlatego też poświęcę mu nieco czasu. Oba z posiadanych przez mnie modemów GSM obsługują dwa tryby przesyłania wiadomości SMS. Tryby te oznaczone są numerami 0 i 1. Odpowiedź Twojego telefonu lub modemu na rozkaz „AT+CMGF?” lub „AT+CMGF=?” będzie zawierać listę obsługiwanych trybów pracy. Ja dla swojej aplikacji wybrałem tryb 1, wygodniejszy i wolę nie wyrażać swojej opinii na temat kodowania wiadomości w trybie 0, zwanym również „PDU mode”.
Podane poniżej informacje opracowane zostały na podstawie dokumentacji firmy SIEMENS dla modemu GSM M20. Zainteresowanym poszerzeniem swojej wiedzy na ten temat odsyłam szczególnie do czterech źródeł:
- Dokumentacji modemu M20 SIEMENS dostępnej na stronie producenta pod nazwą „sms_guid_v1_0.doc”. Autorem jest T.Schuh.
- Noty aplikacyjnej umieszczonej pod adresem „http://www.mcselec.com/an_31.htm” . Autorem jest Vilko Sustić.
- Noty aplikacyjnej umieszczonej pod adresem „http://www.mcselec.com/an_117.htm”. Autorem jest Warren Read.
Informacji zawartych na stronie internetowej http://www.dreamfabric.com/sms/

Mode 0 (PDU)
W tym trybie na twórcę aplikacji spada obowiązek zakodowania całej informacji dotyczącej wysyłanej wiadomości. Spójrzmy na przykład zaczerpnięty z materiałów firmy Siemens przesyłający wiadomość o treści „THE BIG BROWN FOX” pod numer telefonu +991234567 (kliknij na tabelę, aby powiększyć).

Wyjaśnijmy znaczenie poszczególnych pól wiadomości SMS.

Długość pola SCA jest opcjonalną dla niektórych telefonów. Jest to podana szesnastkowo liczba oktetów numeru Centrum Usług. Większość telefonów umożliwia wprowadzenie w tym miejscu wartości 0x00, więc nie trzeba podawać i tym samym znać tego parametru.
Typ numeru Centrum Usług podanie wartości 91 oznacza numerację międzynarodową (np. z prefix +48 dla Polski), 81 to numeracja lokalna, obowiązująca na terenie danego kraju
Numer Centrum Usług to numer podany przez operatora, dzięki któremu możliwe jest przesyłanie wiadomości SMS. Np. dla sieci Plus GSM jest to numer +48601000100, Zwróćmy uwagę na sposób kodowania tego numeru. Na przykład numer „601 000100” zakodowany będzie w sposób następujący:
Numeracja krajowa, bez numeru kierunkowego: 81
Nieparzysta liczba znaków – na końcu dodawana jest litera F, więc numer przyjmie postać „601000100F”
Numer po kodowaniu będzie wyglądał następująco: „06010010F0”.

Numer bitu

7

6

5

4

3

2

1

0

Nazwa bitu

TP-RP

TP-UDHI

TP-SRR

TP-VPF

TP-VPF

TP-RD

TP-MTI

TP-MTI

 




Pierwszy oktet komunikatu SMS
TP-RP – ścieżka (numer) nadawcy; ustawienie bitu oznacza, że ścieżka dla odpowiedzi na wysłany SMS jest ustawiona
TP-UDHI – ustawienie bitu oznacza, że przesyłane dane użytkownika rozpoczynają się od nagłówka identyfikującego
TP-SRR – ustawienie tego bitu oznacza, że nadawca żąda raportu na temat odbioru wiadomości SMS przez odbiorcę
TP-VPF – bity określające format okresu ważności wiadomości SMS:
            - 0 0 : TP-VPP nieobecne
            - 1 0 : TP-VPP obecne, format relacyjny (0..143 = (wartość parametru + 1) x 5 minut, 144..167 =
            12 godzin + (wartość parametru - 143) x 30 minut, 168..196 = (wartość parametru–166)x1 dzień,
            197..255 = (wartość parametru – 192) x 7 dni)
            - 0 1 : format rozszerzony, 7 oktetów
            - 1 1 : format absolutny, 7 oktetów
TP-RD – ustawienie bitu powoduje, że duplikaty wiadomości są odrzucane
TP-MTI – typ komunikatu: wartość bitów 0 1 powoduje, że wiadomość przesyłana jest w trybie
SMS-SUBMIT
Pole statusu określające numer odniesienia dla wysyłanego komunikatu; wpisanie wartości 0x00 powoduje, że numer telefonu, z którego wysyłamy SMS jest równocześnie numerem odniesienia.
Długość numeru odbiorcy SMS, podana w ilości bajtów numeru.
Typ numeru odbiorcy SMS (81 lub 91).
Numer odbiorcy wiadomości SMS (kodowany w ten sam sposób, co numer Centrum Usług).
Identyfikator protokołu komunikacyjnego. Wartością typową, używaną przez większość sieci telefonów komórkowych jest 0x00.
Schemat kodowania danych. Wprowadzona wartość 0x00 oznacza, że znaki kodowane są w postaci 7-bitowych znaków ASCII.
Okres ważności wiadomości SMS. Liczba podana i interpretowana zgodnie z kodem reguły podanym przez bity TP-VPF.
Liczba oktetów bajtów tekstu.
Tekst wiadomości

Wiadomość SMS zgodnie ze specyfikacją organizacji ETSI (http://www.etsi.org) może mieć do 160 znaków długości, gdzie każdy znak zakodowany jest w postaci kodu 7-bitowego. Znaki o kodach długości 8-bitów zazwyczaj nie są przez telefony wyświetlana, natomiast czasami używane są jako sterujące (przesyłanie obrazów, dzwonków itp.). Komunikaty zakodowane w postaci liczb 16-bitowych (maksymalnie 70 znaków) używane są do przesyłania komunikatów zakodowanych według standardu UNICODE (UCS2) i mogą być wyświetlane przez większość aparatów GSM. Niektóre z telefonów wyświetlają te komunikaty jako FLASH SMS. My zajmiemy się kodowaniem standardowym, gdy kody znaków mają długość 7 bitów. Prześledźmy sposób kodowania tekstu na przykładzie: zakodujmy wiadomość „ELEKTRONIKA”.
Tekst „ELEKTRONIKA” zawiera 11 znakÓW nazywane septetami ze względu na swą długość – 7 bitów. My musimy zmienić sposób kodowania z septetów na oktety – znaki o długości 8 bitów.

E

L

E

K

T

R

O

N

I

K

A

0x45

0x4C

0x45

0x4B

0x54

0x52

0x4F

0x4E

0x49

0x4B

0x41

1000101

1001100

1000101

1001011

1010100

1010010

1001111

1001110

1001001

1001011

1000001

1000101

1001100

1000101

1001011

1010100

1010010

1001111

1001110

1001001

1001011

1000001


Pierwszy septet (E) zamieniany jest na oktet poprzez „zabranie“ najmniej znaczącego bitu następnego w kolejności znaku i wprowadzenie go na pozycję bitu 7 (0 + 1000101 = 01000101 à 0x45). Po tej operacji bit jest usuwany, więc następne przekształcenie (L) zabierze dwa bity z kolejnego znaku, kolejne trzy i tak dalej. W konsekwencji znak ósmy zostanie całkowicie usunięty. Dla znaku numer dziewięć proces rozpocznie się od początku.

01000101

01100110

01110001

01001001

10010101

00111110

10011101

 

11001001

01100101

00010000

0x45

0x66

0x71

0x49

0x95

0x3E

0x9D

 

0xC9

0x65

0x10


Jako przykład zakodowaną wiadomość „ELEKTRONIKA” i wyślijmy na numer sieci PLUS. Centrum wiadomości (SCA) ma tutaj numer +48601000310. Prześlijmy wiadomość na numer +48605010203.
Długość pola SCA (w tym przypadku 7 bajtów, z polem typu SCA): 07
Typ SCA - numeracja międzynarodowa: 91
Numer SCA: 8406010013F0
Pierwszy oktet wiadomości SMS: 11
Pole statusu: 00
Długość numeru odbiorcy SMS (liczba bajtów): 07
Typ numeru odbiorcy – numeracja międzynarodowa: 91
Numer odbiorcy: 8496959192F3
Identyfikator protokołu: 00
Schemat kodowania: 00
Okres ważności (1 tydzień): BF
Długość tekstu: 0x0A
Tekst: 45667149953E9DC96510
Zbierzmy to w całość: 07918406010013F0110007918496959192F30000BF0A45667149953E9DC96510
Niestety – oprócz znajomości długości niektórych ze składników wiadomości, aby ją wysłać musimy jeszcze znać jej całkowitą długość. W naszym przykładzie wynosi ona 32 bajty. Zestaw poleceń, które musimy wydać modemowi aby wysłać wiadomość, będzie następujący:
AT+CMGF=0 <CR>
AT+CMGS=32 <CR>
>07918406010013F0110007918496959192F30000BF0A45667149953E9DC96510<CTRL+Z>
Uwaga: wyżej wymienione znaki są reprezentacją bajtów o wartości zakodowanej szesnastkowo. Cyfra „32” jest długością przesyłanej informacji podaną w bajtach. Podany przykład kodowania jest właściwy dla informacji wprowadzanej przez interfejs komunikacyjny. Inaczej wysyła się wiadomość, gdy numer Centrum Usług znajduje się na karcie czy też gdy wysyłana jest wiadomość zapamiętana w pamięci telefonu lub karty SIM.

Mode 1 (TEXT MODE)
W przeciwieństwie do swojego poprzednika , tryb 1 zwany również TEXT MODE jest bardzo łatwy w użyciu. Poniższa sekwencja poleceń wysyła identyczną wiadomość jak wyżej w trybie tekstowym:
AT+CMGF=1 <CR>
AT+CMGS=”+48605010203” <CR>
>ELEKTRONIKA<CTRL+Z><CR>
Trybem tym posłużyłem się tworząc funkcję „send_SMS”. Niestety, nie wszystkie telefony GSM będą ten tryb obsługiwać.

Funkcja wysyłająca wiadomość SMS: „send_sms”.
Na listingu 5 umieściłem kod źródłowy funkcji wysyłającej wiadomość SMS. Część wiadomości docierającej do adresata jest stała, zdefiniowana w programie. Część wiadomości taka, jak lokalizacja obiektu i opis czujnika jest zmienna i może być zdefiniowana przez użytkownika. Definicja zostaje zapamiętana w pamięci EEPROM i jest używana przy kompozycji wiadomości. Przeanalizujmy listing 4.

List. 5. Procedura wysyłania wiadomości tekstowej SMS
send_SMS:       

    ldi    zl,LOW(send_sms_1<<1)  ;Z = adres nastawy-polecenia w ROM
    ldi    zh,HIGH(send_sms_1<<1)
    rcall  putf               ;wysłanie polecenia
    rcall  gets               ;pobranie odpowiedzi modemu
    ldi    zl,LOW(send_sms_2<<1)  ;Z = adres napisu w ROM
    ldi    zh,HIGH(send_sms_2<<1)
    ldi    yl,LOW(buffer)     ;Y = adres bufora w RAM
    clr    yh                 ;tu adresy RAM mogą być tylko 1-bajtowe
    rcall  move_rom2ram       ;przepisanie danych z ROM do RAM,
                               ;adresy ROM w Z,RAM w Y
    ldi    eeaddr,SMSNUMBER_EEADDR  ;pobranie numeru telefonu z eeprom
                              ;do "buffer", na który wskazuje Y
    rcall  ee_read2buf        ;uwaga: zmieniany jest rejestr Y
    ldi    temp,0x22          ;cudzysłów zamykający
    st     y,temp
    rcall  st_EOL             ;zapisanie znaków CR+LF do bufora
    rcall  puts_buffer        ;wysłanie zawartości "buffer" przez UART
;teraz kompozycja SMS do wysłania
    ldi    zl,LOW(send_sms_3<<1)  ;odczyt części "stałej" z ROM i EEPROM
    ldi    zh,HIGH(send_sms_3<<1)
    ldi    yl,LOW(buffer)
    clr    yh
    rcall  move_rom2ram
    ldi    eeaddr,SMSTEXT_EEADDR  ;teraz odczyt opisu obiektu (30 znaków
                              ;wprowadzone przez użytkownika)
    rcall  ee_read2buf
    ldi    temp,','           ;do temp kod ascii przecinka
    st     y,temp             ;zapisanie go w buforze
    clr    temp               ;wyliczenia adresu etykiety i jej odczyt
                              ;(jeśli czujnik jest aktywny,to bit
send_SMS_sen:
    inc    temp               ;przyjmuje wartość "0")
    clc                       ;kasowanie flagi "carry"
    ror    sensors            ;przeniesienie najmłodszego bitu do C
    brcs   send_SMS_sen       ;jeśli C jest "1",to wejście nie jest aktywne
    mov    eeaddr,temp
    rcall  sen_ee_addr        ;wyznaczenie adresu w eeprom
    inc    yl                 ;odczyt etykiety wejścia do bufora w RAM
    rcall  ee_read2buf
    ldi    temp,0x1A          ;zapamiętanie znaku "Ctrl+Z"
    st     y,temp
    rcall  st_EOL             ;zapisanie kodów CR+LF
    rcall  puts_buffer        ;wysłanie zawartości "buffer" przez UART
    inc    smssent            ;ustawienie znacznika wysłania SMS
    ret

Jako pierwsze wysyłane jest do modemu polecenie zapisane w pamięci ROM zdefiniowane pod etykietą „send_sms_1”. Jest to polecenie załączenia trybu tekstowego „AT+CMGF=1”. Uwaga! Zarówno polecenie wyświetlające listę dostępnych trybów jak i załączające konkretny tryb pracy działa dopiero po włączeniu się telefonu do sieci. Każde polecenie musi być zakończone znakiem CR (0x0D). Za przesłanie polecenia z ROM odpowiedzialna jest funkcja o nazwie „putf”. Różni się ona od „puts” tym, że dane wysyłane przez UART pobiera z ROM a nie bufora w pamięci RAM. W następnej kolejności przesyłany jest do modemu numer telefonu, którego stała część zdefiniowana jest pod etykietą „send_sms_2” a zmienna, wprowadzana przez użytkownika, pobierana jest z EEPROM. Polecenie zawierające numer telefonu adresata wiadomości komponowane jest w pamięci RAM i przesyłane do modemu przez funkcję „puts”. Podobnie tworzona jest wiadomość tekstowa: komponowana w RAM z części stałej „send_sms_3”, tekstu – lokalizacji obiektu wprowadzonej przez użytkownika oraz etykiety czujnika wyliczanej na podstawie zawartości zmiennej „sensors” (zmienna ta przesuwana jest w prawo z użyciem flagi przeniesienia C do momentu aż flaga ta zostanie wyzerowana; logiczne „0” oznacza stan aktywny czujnika – liczba przesunięć liczona wskazuje na numer wejścia). Tekst musi być zakończony znakiem CTRL+Z. Jest to dobrze znany nam znak końca zbioru tekstowego o kodzie 26 (0x1A).

Montaż.

Całość zmontowano na dwóch płytkach jednostronnych: po jednej dla klawiatury i układu sterującego. Konstrukcja alarmu jest bardzo prosta a jej montaż nie powinien sprawiać trudności nawet początkującemu elektronikowi. Montaż rozpoczynamy od wlutowania łączówek – ja wykonywałem je przy pomocy drutu „srebrzanki”. Później elementy bierne – rezystory i kondensatory, półprzewodniki a na końcu układ mikrokontrolera. Mikrokontroler nie musi być zaprogramowany. Dolutowując do jego wyprowadzeń złącze można zaprogramować go już w układzie. Tak właśnie uruchamiałem swój układ.
Rezystory R1 do R8 należy dobrać w zależności od posiadanych czujników. Wartość 1k jest właściwą dla napięcia wejściowego od 9 do 12V i transoptorów LTV849. Dla PC849 firmy SHARP można użyć rezystorów o większej wartości. Wartość rezystancji można oszacować na podstawie wzoru: (Napięcie czujnika – 1,5) : 0,01 = Rezystancja w Ohm.
Program napisany został w taki sposób, że za aktywny uważany jest stan, gdy na wejściu pojawi się napięcie. W przypadku odwrotnym wymagane jest zanegowanie wartości zmiennej „sensors” po odczycie czujników. W takiej sytuacji wystarczy w procedurze obsługi przerwania Timer’a 0 na końcu dodać polecenie „neg sensors”.
Zarówno klawiatura jak i wyświetlacz podłączone zostały z użyciem złącz zaciskanych o nazwie IDC. Wyświetlacz wymaga złącza o 16 wyprowadzeniach, klawiatura o 10. Oczywiście złącza można zastąpić przewodami. Stabilizator 7805 dobrze jest umieścić na niewielkim radiatorze. Jego temperatura będzie zależeć bardzo mocno od modelu użytego wyświetlacza oraz doprowadzonego napięcia wejściowego. Ja użyłem zgodnego z LCD wyświetlacza VFD, który charakteryzuje się dosyć dużym poborem prądu i przez to stabilizator bardzo mocno nagrzewał się w czasie pracy (wyświetlacz pobiera około 0,3A co przy napięciu zasilającym 12V daje moc traconą przeszło 2W). Układ powinien być zasilany napięciem stałym o wartości co najmniej 8V i nie więcej niż 15V.
Jeśli chcemy wykorzystać modem GSM (na przykład WAVECOM), na płytce montujemy złącze DSUB-9 męskie (szpilki). Jest ono właściwe dla urządzenia sterującego transmisją. Do złącza tego wprost dołączamy kabel modemu.
Elementem wykonawczym jest przekaźnik z cewką na napięcie 5V. W modelu używałem przekaźnika firmy ITT o oznaczeniu RZ2H-5. Styki przekaźnika, podobnie jak wejścia czujników alarmu, wyprowadzone są przez złącza terminatory o rastrze 3,5mm („pod śrubkę”) umożliwiające wygodne podłączenie i rozłączenie układu.
Karta SIM musi być przygotowana do pracy. To znaczy musi zawierać poprawny numer centrum serwisowego oraz być aktywna. Programowanie parametrów karty można wykonać bądź to z użyciem programu terminala, bądź to umieszczając kartę w zwykłym telefonie GSM.

Użytkowanie alarmu

Po pierwszym załączeniu użytkownik musi wykonać konfigurowanie. Proszę pamiętać o tym, że interfejs użytkownika jest bardzo skromny, ponieważ ilość pamięci programu, którą dysponuje mikrokontroler również jest bardzo mała.
Jako pierwszy należy wprowadzić kod wyłączenia alarmu. Może i powinien on być inny od kodu załączenia. Wprowadzamy 4 cyfry – w tym momencie zmienna „visible” ma wartość „UCANSEE” i można odczytać wprowadzane znaki. Kod zawiera 4 cyfry, po których wprowadzeniu użytkownik zostanie poproszony o akceptację przez naciśnięcie klawisza ENTER (w prawym, dolnym rogu klawiatury) lub możliwe jest powtórne wprowadzenie innego (?) kodu po naciśnięciu klawisza CLR (środek dolnego rzędu klawiatury). Później w identyczny sposób wprowadzamy kod wyłączenia i numer PIN dla karty telefonu GSM. Teraz następuje pytanie o numer telefonu, na który wysyłane będą SMS. Należy wprowadzić go łącznie z numerem kierunkowym kraju, na przykład „48605010203”. Procedura wprowadzania numeru telefonu jest wspólna z tą przeznaczoną do wprowadzania tekstu: dłuższe przytrzymanie klawisza powoduje wprowadzenie litery. Proszę zwrócić uwagę na to, aby wprowadzić wyłącznie cyfry. W przypadku pomyłki, kasowanie jest możliwe przez naciśnięcie klawisza CLR. Numer zostaje zapamiętany po naciśnięciu ENTER.
Następnie użytkownik pytany jest o część opisową dla SMS. Przeznaczona jest ona dla krótkiego opisu lokalizacji, na przykład „JAGIELLOŃSKA 2/3”. Można wprowadzić do 28 znaków. Dodatkowo należy podać opisy dla wejść poszczególnych sensorów, na przykład 1 – „OKNO”, 2 – „DRZWI” itp. Zalecam aby wprowadzić cokolwiek, co pozwoli zidentyfikować wejście czujnika – chociażby numer 1, 2, 3 itp. Na etykietę – opis wejścia czujnika, przeznaczone jest 8 znaków. Jeśli nie zostaną wprowadzone żadne dane tekstowe, program zapisze pod adresem łańcucha znaków w EEPROM znak jego końca. Mogą się wówczas pojawić błędy przy kompozycji i wysyłaniu wiadomości SMS. Wprowadzanie tekstu kończy krótkie przyciśnięcie klawisza ENTER.  Proszę nie przytrzymywać go przez czas dłuższy niż 1 sekunda.
Na rysunku 1 umieściłem opis klawiatury. Tekst wprowadzany jest podobnie jak przy pisaniu wiadomości SMS w telefonie komórkowym. Dłuższe przytrzymanie klawisza, powoduje wyprowadzenie kolejnych znaków. I tak dla przykładu pod klawiszem „1” ukryte są litery A, B i C oraz oczywiście cyfra 1. Zwolnienie klawisza powoduje przesunięcie kursora na następną pozycję po upływie czasu około 1 sekundy.

Rys. 1. Rozmieszczenie klawiszy alarmu (na zielono wpisano kody zwracane przez procedurę kbd_read)

Po wprowadzeniu nastaw alarm przejdzie do pracy w trybie wyłączonym. Podanie kodu załączenia spowoduje, że odmierzony zostanie czas 30 sekund a następnie alarm przejdzie do trybu czuwania. W tym czasie widać migotanie diody LED co 1 sekundę i pojawia się komunikat „czekaj...”, chociaż bardziej na miejscu byłby napis „uciekaj!”. Jest to jednak napis wspólny dla różnych funkcji, nie było miejsca w pamięci ROM na bardziej sugestywne komunikaty.
Pojawienie się napięcia na wejściu czujnika powoduje, że na kolektorze tranzystora pojawi się stan niski. Jest on interpretowany przez mikrokontroler jako uaktywnienie alarmu. Od momentu pojawienia się stanu niskiego CPU zaczyna odmierzać czas 30 sekund i jeśli w tym czasie nie zostanie wprowadzony kod wyłączenia, załączany jest przekaźnik oraz wysyłane powiadomienia SMS. Alarm jest załączony aż do momentu odłączenia napięcia zasilania lub wprowadzenia kodu wyłączenia. Podobnie jak poprzednio, odmierzanie czasu sygnalizowane jest przez migotanie diody LED.
Gdy alarm znajduje się w trybie wyłączenia, możliwe jest ponowne wprowadzenie nastaw. Nastawy wykonane poprzednio i zapisane w EEPROM są zamazywane a na ich miejsce zapisywane są nowe, wprowadzane przez użytkownika. Nie istnieje więc możliwość modyfikacji nastaw – należy podać je wszystkie na nowo. Aby wejść do trybu nastaw, w trybie wyłączenia należy wpisać kod „0000” a następnie kod wyłączenia alarmu. Po prawidłowym jego podaniu, możliwe jest wprowadzenie nastaw.

Podsumowanie

Urządzenie alarmowe jest urządzeniem amatorskim i jako takie nosi jego znamiona. Jest przykładem aplikacji wykorzystującej modem lub telefon GSM do sygnalizacji. Wraz z urządzeniem udostępniam również źródło programu i zachęcam do samodzielnych jego modyfikacji. Można na przykład usunąć z programu możliwość wysyłania w wiadomości SMS etykiet opisów czujników a zaoszczędzone w ten sposób miejsce w pamięci programu wykorzystać na implementację lepszego interfejsu użytkownika lub innego sposobu kodowania wiadomości niż TEXT MODE. Można wreszcie pozbyć się procedur komunikacji z telefonem GSM i wykorzystywać urządzenie jak zwykłą centralkę alarmową.
Całość programu została wykonana i skompilowana przy pomocy AVR STUDIO 4. Jest to kompletne środowisko do pisania i uruchamiania aplikacji dla mikrokontrolerów AVR. Do linii RESET, MISO, MOSI i SCK mikrokontrolera przylutowałem złącze i przy wykorzystaniu programatora ISP programowałem i testowałem układ. Jest to bardzo wygodna, nie tylko dla elektronika amatora, metoda. Pozwala na uruchamianie aplikacji w rzeczywistym środowisku jej pracy.
Program zajmuje 128 bajtów pamięci EEPROM na zmienne definiowane przez użytkownika. Ich wykaz Można znaleźć w tabeli 2. Kod wynikowy po kompilacji zajmuje 1019 słów dwubajtowych, to jest 2038 z 2048 bajtów pamięci programu AT90S2313. Niestety, nie ma już zbyt wiele miejsca na implementację nowych funkcji lub dłuższych komunikatów. Można je wykonać jedynie kosztem usunięcia lub optymalizacji już istniejących.

 

Jacek Bogusz
j.bogusz@easy-soft.net.pl

 

 



Tab. 2. Wykaz adresów zmiennych umieszczonych w pamięci EEPROM


Etykieta adresu

Adres zmiennej w EEPROM

Opis

STATUS_EEADDR

ONCODE_EEADDR
OFFCODE_EEADDR
PINNUMBER_EEADDR
SMSNUMBER_EEADDR
SENSOR1_EEADDR

SENSOR2_EEADDR

SENSOR3_EEADDR
SENSOR4_EEADDR
SENSOR5_EEADDR
SENSOR6_EEADDR
SENSOR7_EEADDR
SENSOR8_EEADDR
SMSTEXT_EEADDR

 

 

0x00

0x01
0x05
0x09
0x0D
0x1B

0x23

0x2C
0x35
0x3E
0x47
0x50
0x59
0x62

 

 

Komórka, w której zapamiętany jest status alarmu (załączony = 0xF0, wyłączony = 0x0F)
Kod załączenia alarmu w postaci 4 kodów znaków ASCII
Kod wyłączenia alarmu w postaci 4 kodów znaków ASCII
Numer PIN telefonu, również 4 znaki ASCII
Numer telefonu, na który przesyłane będą powiadomienia SMS; zmienna ma długość 12 bajtów – 11 na numer telefonu + 0x00
Etykieta wejścia czujnika 1; zmienna ma długość 9 bajtów – 8 na opis + 0x00
Etykieta wejścia czujnika 2, jak wyżej
- / / -      3
- / / -      4
- / / -      5
- / / -      6
- / / -      7
- / / -      8
Tekst komunikatu SMS przeznaczony do zidentyfikowania obiektu, na przykład ulica + numer domu; długość 28 bajtów – 27 na tekst + 0x00

 

Listing 6. Wykaz stałych używanych w programie
;stałe związane z wyświetlaczem
.equ   ENABLE = 7     ;numer bitu portu b dla linii ENABLE wyświetlacza LCD
.equ   REGSEL = 4     ;numer bitu portu b dla linii R/S wyświetlacza LCD
;stałe związane z UART i tablicami danych

.equ   CR = 0x0A      ;kod znaku powrotu kursora
.equ   LF = 0x0D      ;kod znaku nowej linii    
.equ   EOF = 0x1A     ;kod znaku końca zbioru tekstowego
.equ   EOD = 0x00     ;koniec danych (tabeli, tekstu itp)

;stałe związane z klawiaturą
.equ   COLUMN1 = 4    ;numer bitu portu b 1 kolumny klawiatury
.equ   COLUMN2 = 5    ;numer bitu portu b 2 kolumny klawiatury
.equ   COLUMN3 = 6    ;numer bitu portu b 3 kolumny klawiatury
.equ   NOKEY = 0xFF   ;jeśli "char" zwracany przez funkcję odczytu
                      ;klawiatury
przyjmuje wartość NOKEY, oznacza to,
                      ;że nie jest
wciśnięty żaden klawisz
.equ   CLRKEY = 10    ;umowny kod klawisza "CLR"
.equ   ENTERKEY = 11  ;umowny kod klawisza "ENTER"
.equ   UCANSEE = 0xFF ;stała dla "visible" oznacza, że wprowadzane znaki
                      ;można czytać
.equ   UCANTSEE = 0x00 ;stała dla "visible" oznacza, że wprowadzanych znaków
                      ;nie
można czytać (wyświetlany jest znak zastępczy *)
;stałe stanu alarmu
.equ   ALARMON = 0x0F     ;status alarmu - załączony
.equ   ALARMOFF = 0xF0    ;status alarmu - wyłączony
.equ   ALARMACTIVE = 0x55 ;status alarmu – sygnalizacja (nieużywany)
.equ   ALARMOUT = 6       ;bit wyjścia portu b sterujący załączeniem alarmu
.equ   HIGHNIBBLE = 6     ;bit wyjścia portu D sterujący wyborem połówki
                          ;bajtu czujników

.equ   SENSORSOFF = 0xFF  ;wartość zmiennej „sensors” oznaczająca, że żaden
                          ;z czujników nie jest aktywny (wyzwolony przez
                          ;warunek
alarmu)
.equ   DELAYTIME = 30     ;czas opóźnienia do zadziałania alarmu (w sek.)
.equ   TIM1RLDH = 0xE3
.equ   TIM1RLDL = 0xDF    ;stałe do odświeżania zawartości Timer'a 1
                          ;(częstotliwość
wywoływania przerwania to 1 Hz)
;++++++++++++++++++++++++++++
;teksty do wyświetlenia
;++++++++++++++++++++++++++++
;tekst powitania
welcome1:  .db   "ALARM+SMS",EOD
;teksty statusu
whenoff:   .db   "ALARM WYLACZONY",EOD
whenon:    .db   "ALARM AKTYWNY",EOD
;teksty menu nastaw
set_1:     .db   "Kod zalaczenia?",EOD
set_2:     .db   "Kod wylaczenia?",EOD
set_3:     .db   "Kod PIN modemu?",EOD
set_4:     .db   "Telefon dla SMS?",EOD
set_5:     .db   "Tekst SMS (30 zn.)?",EOD
set_6:     .db   "Tekst wej. (8 zn.)?",EOD
set_ok:    .db   "ENTER=OK,C=popraw   ",EOD
set_wait:  .db   "Czekaj...",EOD
;nastawy modemu GSM
init_modem_1:
            .db  "ATZ",CR,LF     ;ten tekst wysyłany będzie przez UART,
                                 ;dlatego
kończy go CR+LF
init_modem_2:
            .db  "AT+CPIN=",EOD  ;ten tekst będzie przetwarzany przed
                                 ;wysłaniem,
dlatego kończy się EOD
init_modem_3:
            .db  "ATE0",CR,LF

init_modem_5:
            .db  "AT+CPMS=",0x22,"SM",0x22,CR,LF

send_sms_1:
            .db  "AT+CMGF=1",CR,LF

send_sms_2:
            .db  "AT+CMGS=",0x22,"+",EOD

send_sms_3:
            .db  "ALARM ZALACZONY W OBIEKCIE ",EOD

;tablica znaków do wprowadzania tekstu, naciśnięciu klawisza odpowiada znak
;z tablicy
kolumna 1 zawiera znaki dla pojedynczego naciśnięcia, 2 dla
;podwójnego itd.

chartable:
            .db  '0',   ' ',   '/',   '-'    ;kody dla klawisza "0"

            .db  '1',   'A',   'B',   'C'    ;kody dla klawisza "1"
            .db  '2',   'D',   'E',   'F'    ;kody dla klawisza "2"
            .db  '3',   'G',   'H',   'I'    ;kody dla klawisza "3"
            .db  '4',   'J',   'K',   'L'    ;kody dla klawisza "4"
            .db  '5',   'M',   'N',   'O'    ;kody dla klawisza "5"
            .db  '6',   'P',   'Q',   'R'    ;kody dla klawisza "6"
            .db  '7',   'S',   'T',   'U'    ;kody dla klawisza "7"
            .db  '8',   'V',   'W',   'Z'    ;kody dla klawisza "8"
            .db  '9',   'Y',   'Z',   '.'    ;kody dla klawisza "9"

 

Listing 7. Wykaz definicji rejestrów używanych w programie (zmiennych)
;definicje zmiennych pomocniczych
.dseg
.def   cchar1= r02   ;kod BCD znaku zwracanego przez funkcję "read_code"/1
.def   cchar2= r03   ; j.w. /2
.def   cchar3= r04   ; j.w. /3
.def   cchar4= r05   ; j.w. /4
.def   visible = r06 ;zmienna ma wartość 1,gdy wprowadzany kod może
                     ;być czytany
.def   length= r07   ;długość wprowadzanego tekstu dla read_text
.def   sensors = r08 ;stan sensorów (czujników) alarmu
.def   goon = r09    ;inkrementowana co 1 s przez funkcja odmierzająca
                     ;czas opóźnienia podczas obsługi przerwania Timer’a 1
.def   copypb= r10   ;przechowuje kopię portu b
.def   copypd= r11   ;przechowuje kopię portu d
.def   smssent = r12 ;wartość różna od 0 oznacza, że SMS został wysłany
.def   temp   = r16  ;zmienna pomocnicza
.def   delay1 = r17  ;dla procedury delay
.def   delay2 = r18  ; - / / -
.def   delay3 = r19  ;w niej podawany jest argument dla funkcji „delaysec”
                     ;(w sekundach)
.def   delay = r20   ;główna zmienna dla delay-w niej jest podawany argument
                     ;dla funkcji „delayms” (w milisekundach)
.def   i = r21       ;zmienna pomocnicza - pętle i iteracje

.def   char = r22    ;bufor znaków dla lcd
.def   eedata = r23  ;bajt do zapisu w eeprom
.def   eeaddr = r24  ;adres zapisu bajtu w eeprom
.def   sbuf = r25    ;bufor dla znaku odbieranego/wysyłanego przez uart
;xp zawiera odciętą, yp rzędną (definicja pokrywa się z "delay")
.def   xp = R17
.def   yp = R18

http://www.tomaszbogusz.blox.pl/

ZałącznikWielkość
Schemat i płytka alarmu (alarm-gsm-sch.zip)90.04 KB
Program źródłowy (alarm-gsm-pliki.zip)16.26 KB
Schemat i płytki jako PDF (alarm-gsm-pcb.pdf)239.33 KB

Dodaj nowy komentarz

Zawartość pola nie będzie udostępniana publicznie.