Autor: Piotr Rzeszut (Piotrva)
Redakcja: Dondu
Artykuł jest częścią cyklu: Kurs Arduino
Niektórzy z Was pewnie znają różne systemy sterowane za pomocą SMS'ów lub wysyłające tą drogą informacje o działaniu jakiegoś systemu.
Przykładem mogą być systemy automatyki przemysłowej (widziałem tego typu rozwiązania np. w małych elektrowniach wodnych, gdzie informują o awariach) czy centrale alarmowe, które mogą wysłać nam wiadomość o włamaniu, wykryciu dymu itp.
Dlaczego więc sami nie mielibyśmy zbudować jakiegoś urządzenia wyposażonego w taką funkcjonalność?
Moduł GSM, czyli telefon bez klawiatury i wyświetlacza...
Na początek rzecz jasna musimy mieć odpowiedni moduł. Tu do wyboru mamy na prawdę wiele układów, na przykład SIM900, czy SIEMENS TC35, który otrzymałem do testów z:Dokumentacja SIEMENS TC35: SIEMENS-datasheet-gsm-tc35.pdf
Moduły te komunikują się z innymi układami za pomocą interfejsu UART i komend AT. Zasadniczo moduły GSM to telefony komórkowe, które nie posiadają wyświetlacza i klawiatury a zamiast tego komunikują się z użytkownikiem za pomocą wspomnianego interfejsu.
Jedną przypadłością tych modułów jest spory pobór prądu podczas łączenia się z siecią (pik do 2A) - dlatego mimo dostępności na naszym Arduino napięcia 3,3V dla bezproblemowej pracy musimy zasilić moduł z zewnątrz. Płytka z modułem TC35 posiada złącze do podłączenia zasilania o napięciu 7,5-12V. Ja osobiście testowałem moduł z zasilaczem 9V. Koniecznie musimy pamiętać o połączeniu mas modułu i Arduino (łączymy kabelkiem pin GND z pinem GND w Arduino).
Jeśli już mówimy o połączeniach to do komunikacji z modułem wykorzystamy kolejny raz programowy UART (z biblioteką AltSoftSerial załączoną wraz z kodami), dlatego pin TX modułu podpinamy do pinu 9, a RX do pinu 8. Domyślnie moduł komunikuje się z prędkością 9600bps.
Najpierw wgraj nowy program, a potem podłącz moduł - poprzednio wgrany do Arduino program mógł wykorzystywać te piny do innych czynności co może prowadzić do uszkodzenia sprzętu.
Moduł GSM Siemens TC35 |
Komendy AT w telegraficznym skrócie.
Teraz pora poznać kilka najpotrzebniejszych komend AT aby sterować naszym modułem GSM. Każdą komendę powinniśmy zakończyć znakiem powrotu karetki ('\r'). Umożliwia to nam np. program Br@y terminal: https://sites.google.com/site/terminalbpp/Jego ustawienia przedstawia poniższy obrazek. Zwróćmy uwagę na zaznaczony kwadrat +CR, który powoduje wysłanie po każdej komendzie znaku powrotu karetki.
Polecenia wpisujemy w białym polu obok tego kwadratu, o ile nie zaznaczono inaczej i zatwierdzamy klawiszem enter.
Aby ręcznie wysyłać komendy do modułu możemy wgrać program Test z przykładów dla biblioteki AltSoftSerial. Biblioteka w wersji 1.2: AltSoftSerial-v1.2.zip (kopia)
Ustawienia terminala do testów modułu. |
Oczywiście każdy moduł (jak i niektóre telefony - tak, tak, niektóre telefony też można po zastosowaniu odpowiedniego kabla lub jeśli są dostępne odpowiednie sterowniki USB wykorzystać jako moduły GSM obsługiwane komendami AT, ale to bardzo rozległy temat, gdyż co telefon to inna sytuacja) ma swoją listę komend AT i czasem się one różnią. Przykładem może być to że np. moduły G24 od Motoroli wymagają przy nadawaniu ujęcia numeru w cudzysłowy a nasz TC35 musi mieć podany numer bez tych znaków.
Poniżej przedstawiam listę przydatnych przy odbieraniu i wysyłaniu SMS'ów komend według standardów modułu TC35. Bardziej zainteresowanych odsyłam do podręcznika komend AT tego modułu, który jest zaledwie ponad 200-tu stronicową książeczką :-)
Zestaw komend AT: at_commands_tc35.pdf
1. AT
Komenda ta pozwala na sprawdzenie połączenia z modułem - powinniśmy w odpowiedzi otrzymać:OK
2. AT+CPIN?
Komenda sprawdza czy została przeprowadzona weryfikacja PIN. Powinniśmy otrzymać odpowiedź:+CPIN: READY
OK
3. AT+CPIN=xxxx
Komenda służy do wprowadzania kodu pin. Polecam jednak wyłączyć ochronę kodem PIN wkładając kartę do telefonu i wybierając odpowiednie opcje. Wiele kart SIM (w szczególności startery na kartę) ma domyślnie wyłączoną ochronę PIN.4. ATE0 i ATE1
Komenda ATE0 wyłącza echo, czyli wysyłanie przez moduł z powrotem każdej wysłanej komendy.Po poprawnym wykonaniu otrzymamy odpowiedź:
OK
ATE1 włącza echo
5. AT+CMGF=1
Komenda włącza tryb tekstowy wysyłania i odbierania wiadomości. Drugi dostępny tryb to PDU ale jest on bardziej skomplikowany w obsłudze.Zwraca odpowiedź:
OK
6. AT+CMGS=+48xxxxxxxxx
Komenda wysyła SMS pod podany numer telefonu.Po wysłaniu tej komendy pojawia się znak "zachęty": >
Teraz musimy wpisać wiadomość do wysłania (160 znaków max )
A następnie wysyłamy znak CTRL+Z (kod 26 w zapisie dziesiętnym w ASCII) aby wysłać wiadomość.
Korzystając z Br@y terminal po wysłaniu AT+CMGS=(numer) klikamy w szarym polu (czerwona strzałka), tam piszemy tekst wiadomości i wciskamy CTRL+Z - tekst zniknie z szarego okienka i możemy czekać na odpowiedź +CMGS opisaną poniżej.
UWAGA! Nie możemy wysłać żadnego znaku po CTRL+Z do momentu otrzymania odpowiedzi - inaczej wiadomość nie zostanie wysłana (xx - numer identyfikacyjny wiadomości):
+CMGS: xx
OK
7. AT+CMGL i AT+CMGL=ALL
Wyświetla listę wszystkich (lub tylko nieodczytanych przy zastosowaniu wersji bez =ALL) wiadomości z pamięci urządzenia.Odpowiedź to ramki postaci:
+CMGL: 1,"REC UNREAD","+480000000005",,"14/07/26,21:43:15+08"
Test
1 - numer wiadomości w pmięci
REC UNREAD lub REC READ - oznacza czy wiadomość była już wyświetlana czy nie
+480000000005 - numer telefonu nadawcy
14/07/26,21:43:15+08 - data i czas
Test - treść wiadomości - może mieć wiele linijek
Po wyświetlaniu wszystkich wiadomości z pamięci (może być ich wiele) otrzymujemy odpowiedź:
OK
8. AT+CMGR=xx
Odczytuje z pamięci wiadomość o numerze xx. Otrzymujemy w odpowiedzi:+CMGR: "REC READ","+480000000005",,"14/07/26,21:43:15+08"
Test
OK
Znaczenie pól jak poprzednio
9. AT+CMGD=xx
Usuwa z pamięci wiadomość o numerze xxOdpowiedź:
OK
10. AT+CNMI=1,1,0,0,1
Ustawia powiadamianie o nadchodzącym SMS'ie - nie wnikajmy w szczegóły...Odpowiedź:
OK
Każdy nadchodzący SMS zostanie zakomunikowany ramką:
+CMTI: ”SM”,xx
SM - typ pamięci w której przechowywany jest SMS; SM oznacza kartę SIM
xx - numer wiadomości
11. AT+CSQ
Sprawdza jakość sygnałuOdpowiedź:
+CSQ: 13,99
OK
13 - moc sygnału - liczba z zakresu 0-31 (im wyższa tym lepszy zasięg) lub 99 gdy nie można określić
99 - bitowa stopa błędów - nas ta wartość nie interesuje (liczba 0-7 lub 99 gdy nie można określić)
Skrócone "procedury" obsługi telefonu przez komendy AT
Teraz gdy znamy potrzebne nam komendy możemy powiedzieć jaką sekwencję czynności należy wykonać, by odebrać i nadać wiadomość.Aktywacja PIN
Oczywiście na początek musimy przeprowadzić aktywację kodu PIN. Dokonujemy tego komendą AT+CPIN=..., jednak osobiście polecam wyłączenie ochrony PIN i jedynie sprawdzenie aktywacji karty komendą AT+CPIN?Nadajemy wiadomość
- Nadajemy komendę AT+CMGF=1
- Wysyłamy SMS komendą AT+CMGS=...
- Czekamy na odpowiedź +CMGS... OK
Odbieramy wiadomość - metoda z odpytywaniem modułu
- Jeśli nie nadajemy (między krokiem 2 i 3 sekwencji nadawania) wiadomości to co jakiś czas wysyłamy komendę AT+CMGF=1 i potem AT+CMGL lub AT+CMGL=ALL
- Dekodujemy odpowiedzi typu +CMGL...
- Aż do otrzymania OK
- Usuwamy wiadomości z pamięci komendą AT+CMGD...
Odbieramy wiadomość - metoda z powiadamianiem o wiadomości przez moduł
- Nadajemy AT+CMGF=1 i potem AT+CNMI=1,1,0,0,1
- Oczekujemy na ramkę +CMTI:...
- Odczytujemy wiadomość komendą AT+CMGR...
- Usuwamy wiadomość z pamięci komendą AT+CMGD...
Na samym początku możemy przećwiczyć te komendy ręcznie korzystając ze wspomnianego zestawu oprogramowania dla Arduino i na PC.
Automatyzacja, czyli niech Arduino wysyła SMS'y
Poniżej przedstawiam dwa programy, realizujące z różnymi metodami odbierania SMS'ów (z odpytywaniem i sygnalizacją) bardzo proste zadanie:Po wysłaniu z określonego numeru komendy ON lub OFF SMS'em ma zostać odpowiednio zapalona lub zgaszona diodka podpięta do pinu 13 płytki. Po wysłaniu SMS'a o treści ? moduł ma odpowiedzieć na numer podany w kodzie (w innym miejscu niż na początku - druga definicja!!!) informacją o stanie diody.
Dodatkowo na ekranie w terminalu szeregowym Arduino możemy obserwować jak pracuje program i w jakich etapach dekoduje wiadomości.
Wymiana wiadomości SMS z urządzeniem |
Myślę, że nie potrzeba dodatkowych komentarzy poza powyższym tekstem i komentarzami w kodzie - mam nadzieję, ze zainspiruje to Was do stworzenia własnych systemów z modułami GSM, czemu by nie stworzyć centralki alarmowej czy zdalnego sterowania urządzeniami w domu?
Z powodu różnych błędów oprogramowania Arduino moduł może wysyłać duże ilości wiadomości SMS, co może narazić nas na koszty ze strony Operatora.
W związku z tym Autor programów i tego poradnika nie ponosi żadnej odpowiedzialności za wszelkie straty wynikłe z nieprawidłowego działania, zarówno w formie opłat naliczonych przez Operatora jak i innych strat spowodowanych przez nieprawidłowe funkcjonowanie urządzeń budowanych z wykorzystaniem tego poradnika.
W związku z tym Autor programów i tego poradnika nie ponosi żadnej odpowiedzialności za wszelkie straty wynikłe z nieprawidłowego działania, zarówno w formie opłat naliczonych przez Operatora jak i innych strat spowodowanych przez nieprawidłowe funkcjonowanie urządzeń budowanych z wykorzystaniem tego poradnika.
gsm_sterowanie_online.c
/* * GSM TC35 SIMPLE DEMO * Autor: Piotr Rzeszut (http://piotr94.net21.pl) * Data: 26.07.2014 * Artykuł pobrany z blogu: http://mikrokontrolery.blogspot.com */ #include <AltSoftSerial.h> // AltGSM always uses these pins: // // Board Transmit Receive PWM Unusable // ----- -------- ------- ------------ // Arduino Uno 9 8 10 // Arduino Leonardo 5 13 (none) // Arduino Mega 46 48 44, 45 AltSoftSerial GSM; void setup() { Serial.begin(9600); while (!Serial) ; // wait for Arduino Serial Monitor to open Serial.println("GSM testing"); GSM.begin(9600); //GSM.print("ATE1\r");//ATE1 powoduje, że wszystko co wyślemy DO modułu będzie przez niego wysyłane do nas (echo) GSM.print("ATE0\r");//ATE0 wyłącza powyższą funkcjonalność delay(550);//czekamy nieco pinMode(13,OUTPUT);//ustawiamy pin dla diody led jako wyjście } //definiujemy zmienne pomocne przy odbiorze danych char buffer[256]; byte field_begin[14]; int num=0; //oraz zmienne kontrolujące odbiór danych, stałe do porównywania const char SMS_REC[]="+CMGL: "; const char OK[]="OK"; const char ERROR[]="ERROR"; const char accepted_nr[]="\"+48000000005\""; byte nr_correct=0; enum states{IDLE,NEXT_TXT}; states stan=IDLE; byte msg_id=0; byte ok; int j; byte send_resp,wait_for_sending; unsigned long old_millis=millis(); //pętla główna programu void loop() { char c;//zmienna pomocznicza //ten blok pozwala nam przesyłać komendy do telefonu z terminala komputera /*if(Serial.available()){ c=Serial.read(); GSM.print(c); }*/ //ten blok co 5 sekund ustawia format wiadomości na tekstowy a następnie sprawdza listę wiadomości odebranych przez moduł //i romimy to tylko wtedy gdy nie czekamy na nadanie wiadomości SMS if(millis()-old_millis>5000&&wait_for_sending==0){ GSM.print("AT+CMGF=1\r");//ustaw format wiadomości na tesktowy delay(550); GSM.print("AT+CMGL=ALL\r");//wyślij zapytanie o wyświetlenie wszystkich wiadomości old_millis=millis(); } if (GSM.available()) {//jeśli czeka na odbiór jakiś znak c = GSM.read();//to odczytujemy go //Serial.print(c);//a tu możemy do celów debugowania wyświetlić na ekranie to co wysyła nam moduł - nie tylko nasze komunikaty if(c=='\n'){//jeśli to znak zakończenia linii (koniec ramki) to przystępujemy do jej dekodowania switch(stan){ case IDLE://normalny stan - sprawdzamy ramki pod kątem tego czego dotyczą //----------------------------------------------------------------------------------------------------------------------------------- //SMS przychodzący po poleceniu AT+CMGL... - dekodujemy początek wpisu //----------------------------------------------------------------------------------------------------------------------------------- ok=1;//zakładamy, ze to interesująca nas ramka for(int i=0;i<6;i++){//w pętli porównujemy pierwsze znaki ramki ze wzorcem informacji o sms'ie if(buffer[i]!=SMS_REC[i])ok=0;//jeśli jakiś znak się różni to to nie jest interesująca nas ramka } j=0; if(ok){//jeśli mamy interesującą nas ramkę to możemy ją dekodować for(int i=0;i<num;i++){//przeglądamy ją znak po znaku if(buffer[i]==','){//jeśli trafiliśmy na przecinek buffer[i]=0;//to zastępujemy go znakiem 0 - kończy on ciąg znaków field_begin[j++]=i+1;//kolejny znak po przecinku to początek nowego pola - zapisujemy jego pozycję if(j>5){//maksymalnie w tej ramce moze być 5 pól j=0; ok=0; } }else if(buffer[i]=='\r'||buffer[i]=='\n')buffer[i]=0;//znaki kończące ramkę też zastąpimy znakami zerowymi } } if(ok){ //tu odczytuję z tekstu ID wiadomości msg_id=atoi(&buffer[7]); Serial.print("Wiadomosc nr: "); Serial.println(msg_id); Serial.print("Od numeru: "); //następnie z pól odczytujemy numer telefonu wraz z "" w których jest zawarty Serial.println(&buffer[field_begin[1]]); //tu porównujemy ten ciąg z zapisanym w stałej na początku programu - nie chcemy by reklamy czy inne osoby coś mogły zrobić if(strcmp(&buffer[field_begin[1]],accepted_nr)==0){ nr_correct=1; Serial.println("NUMER ZGODNY"); }else{ nr_correct=0; Serial.println("NUMER NIEOBSLUGIWANY"); } stan=NEXT_TXT;//po tej ramce na pewno otrzymamy tekst sms'a, więc informujemy program, że kolejna linijka musi być dekodowana jako tekst a nie komenda } //----------------------------------------------------------------------------------------------------------------------------------- //OK - komunikat poprawnego zakońvczenia wykonywania komendy //----------------------------------------------------------------------------------------------------------------------------------- ok=1;//zakładamy, ze to interesująca nas ramka for(int i=0;i<2;i++){//w pętli porównujemy pierwsze znaki ramki ze wzorcem OK if(buffer[i]!=OK[i])ok=0;//jeśli jakiś znak się różni to to nie jest interesująca nas ramka } if(ok){ wait_for_sending=0;//kasujemy blokadę wysyłania na czas nadawania sms'a //jeśli odebrano jakąś wiadomość if(msg_id!=0){ //to ją usuniemy GSM.print("AT+CMGD="); GSM.print(msg_id); GSM.print("\r"); msg_id=0; }else if(send_resp){//jeśli wiadomość zostałą usunięta i mamy nadać odpowiedź send_resp=0; GSM.print("AT+CMGS=+48000000005\r");//to wysyłamy ją na ten numer delay(550); GSM.print("Led is ");//tu wpisujemy treść wiadomości (do 160 znaków) if(digitalRead(13)){ GSM.print("ENABLED"); }else{ GSM.print("DISABLED"); } GSM.print((char)26);//a następnie zatwierdzamy wysłanie Serial.println("Info wyslane"); wait_for_sending=1;//musimy zadbać, by zanim telefon potwierdzi nadanie wiadomości nie nadać nic do niego } } //----------------------------------------------------------------------------------------------------------------------------------- //ERROR //----------------------------------------------------------------------------------------------------------------------------------- ok=1;//zakładamy, ze to interesująca nas ramka for(int i=0;i<5;i++){//w pętli porównujemy pierwsze znaki ramki ze wzorcem OK if(buffer[i]!=ERROR[i])ok=0;//jeśli jakiś znak się różni to to nie jest interesująca nas ramka } if(ok){ wait_for_sending=0;//kasujemy blokadę wysyłania na czas nadawania sms'a Serial.println("Huston, mamy problem..."); } break; //----------------------------------------------------------------------------------------------------------------------------------- //Tekst wiadomości SMS //----------------------------------------------------------------------------------------------------------------------------------- case NEXT_TXT: for(int i=0;i<num;i++){//przeglądamy ją znak po znaku if(buffer[i]=='\r'||buffer[i]=='\n')buffer[i]=0;//znaki kończące ramkę też zastąpimy znakami zerowymi } Serial.print("Wiadomosc: "); Serial.println(buffer); //sprawdzamy czy to któreś z interesujących nas poleceń - możemy tu dodać własne polecenie, przesyłanie np. nastawy dla PWM itp. if(nr_correct){//ale najpierw sprawdzamy czy numer był naszym numerem - nie chcemy, zeby irytująca reklama włączyła nam oświetlenie w domu :D if(strcmp(buffer,"ON")==0){ digitalWrite(13,HIGH); } if(strcmp(buffer,"OFF")==0){ digitalWrite(13,LOW); } if(strcmp(buffer,"?")==0){ send_resp=1;//poinformujemy, aby po zakończeniu nadawania wysłać informację do użytkownika } } Serial.println(); stan=IDLE; break; } num=0; }else{//jeśli to inny znak niż zakończenie ramki to dopisujemy go do bufora buffer[num++]=c; buffer[num]=0;//ponadto zawsze kończymy zawartośc bufora znakiem 0 - koniec ciągu znaków if(num>255)num=0;//i dodatkowo dbamy o to, żeby nie przepełnić bufora } } }Do pobrania: gsm_sterowanie_online.c (kopia)
gsm_wo_polling_online.c
/* * GSM TC35 DEMO * Autor: Piotr Rzeszut (http://piotr94.net21.pl) * Data: 26.07.2014 * Artykuł pobrany z blogu: http://mikrokontrolery.blogspot.com */ #include <AltSoftSerial.h> // AltGSM always uses these pins: // // Board Transmit Receive PWM Unusable // ----- -------- ------- ------------ // Arduino Uno 9 8 10 // Arduino Leonardo 5 13 (none) // Arduino Mega 46 48 44, 45 AltSoftSerial GSM; void setup() { Serial.begin(9600); while (!Serial) ; // wait for Arduino Serial Monitor to open Serial.println("GSM testing"); GSM.begin(9600); //GSM.print("ATE1\r");//ATE1 powoduje, że wszystko co wyślemy DO modułu będzie przez niego wysyłane do nas (echo) GSM.print("ATE0\r");//ATE0 wyłącza powyższą funkcjonalność delay(550);//czekamy nieco GSM.print("AT+CMGF=1\r");//pformat wiadomości tekstowy delay(550);//czekamy nieco GSM.print("AT+CNMI=1,1,0,0,1\r");//konfigurujemy automatycznie powiadamianie o nadchodzących SMS'ach delay(550);//czekamy nieco pinMode(13,OUTPUT);//ustawiamy pin dla diody led jako wyjście } //definiujemy zmienne pomocne przy odbiorze danych char buffer[256]; byte field_begin[14]; int num=0; //oraz zmienne kontrolujące odbiór danych, stałe do porównywania const char SMS_REC[]="+CMGR:"; const char SMS_CMTI[]="+CMTI:"; const char OK[]="OK"; const char ERROR[]="ERROR"; const char accepted_nr[]="\"+48000000005\""; byte nr_correct=0; enum states{IDLE,READ_TXT,NEXT_TXT,OK_WAIT_DELETE}; states stan=IDLE; byte msg_id=0; byte ok; int j; byte send_resp; unsigned long old_millis=millis(); //pętla główna programu void loop() { char c;//zmienna pomocznicza //ten blok pozwala nam przesyłać komendy do telefonu z terminala komputera if(Serial.available()){ c=Serial.read(); GSM.print(c); } if (GSM.available()) {//jeśli czeka na odbiór jakiś znak c = GSM.read();//to odczytujemy go Serial.print(c);//a tu możemy do celów debugowania wyświetlić na ekranie to co wysyła nam moduł - nie tylko nasze komunikaty if(c=='\n'){//jeśli to znak zakończenia linii (koniec ramki) to przystępujemy do jej dekodowania switch(stan){ case IDLE://normalny stan - sprawdzamy ramki pod kątem tego czego dotycz //----------------------------------------------------------------------------------------------------------------------------------- //SMS przychodzący sygnalizowany automatycznie ramką +CMTI... - dekodujemy początek wpisu //----------------------------------------------------------------------------------------------------------------------------------- ok=1;//zakładamy, ze to interesująca nas ramka for(int i=0;i<5;i++){//w pętli porównujemy pierwsze znaki ramki ze wzorcem informacji o sms'ie if(buffer[i]!=SMS_CMTI[i])ok=0;//jeśli jakiś znak się różni to to nie jest interesująca nas ramka } j=0; if(ok){//jeśli mamy interesującą nas ramkę to możemy ją dekodować for(int i=0;i<num;i++){//przeglądamy ją znak po znaku if(buffer[i]==','){//jeśli trafiliśmy na przecinek buffer[i]=0;//to zastępujemy go znakiem 0 - kończy on ciąg znaków field_begin[j++]=i+1;//kolejny znak po przecinku to początek nowego pola - zapisujemy jego pozycję if(j>5){//maksymalnie w tej ramce moze być 5 pól j=0; ok=0; } }else if(buffer[i]=='\r'||buffer[i]=='\n')buffer[i]=0;//znaki kończące ramkę też zastąpimy znakami zerowymi } } if(ok){ //tu odczytuję z tekstu ID wiadomości msg_id=atoi(&buffer[field_begin[0]]); Serial.print("Wiadomosc nr: "); Serial.println(msg_id); GSM.print("AT+CMGR=");//wysyłam polecenie do odczytania szczegółów i tekstu wiadomości GSM.print(msg_id); GSM.print("\r"); stan=READ_TXT;//po tej ramce na pewno otrzymamy tekst sms'a, więc informujemy program, że kolejna linijka musi być dekodowana jako tekst a nie komenda } break; case READ_TXT: //----------------------------------------------------------------------------------------------------------------------------------- //SMS przychodzący po poleceniu AT+CMGR... - dekodujemy początek wpisu //----------------------------------------------------------------------------------------------------------------------------------- ok=1;//zakładamy, ze to interesująca nas ramka for(int i=0;i<5;i++){//w pętli porównujemy pierwsze znaki ramki ze wzorcem informacji o sms'ie if(buffer[i]!=SMS_REC[i])ok=0;//jeśli jakiś znak się różni to to nie jest interesująca nas ramka } j=0; if(ok){//jeśli mamy interesującą nas ramkę to możemy ją dekodować for(int i=0;i<num;i++){//przeglądamy ją znak po znaku if(buffer[i]==','){//jeśli trafiliśmy na przecinek buffer[i]=0;//to zastępujemy go znakiem 0 - kończy on ciąg znaków field_begin[j++]=i+1;//kolejny znak po przecinku to początek nowego pola - zapisujemy jego pozycję if(j>5){//maksymalnie w tej ramce moze być 5 pól j=0; ok=0; } }else if(buffer[i]=='\r'||buffer[i]=='\n')buffer[i]=0;//znaki kończące ramkę też zastąpimy znakami zerowymi } } if(ok){ Serial.print("Wiadomosc od numeru: "); //następnie z pól odczytujemy numer telefonu wraz z "" w których jest zawarty Serial.println(&buffer[field_begin[0]]); //tu porównujemy ten ciąg z zapisanym w stałej na początku programu - nie chcemy by reklamy czy inne osoby coś mogły zrobić if(strcmp(&buffer[field_begin[0]],accepted_nr)==0){ nr_correct=1; Serial.println("NUMER ZGODNY"); }else{ nr_correct=0; Serial.println("NUMER NIEOBSLUGIWANY"); } stan=NEXT_TXT;//po tej ramce na pewno otrzymamy tekst sms'a, więc informujemy program, że kolejna linijka musi być dekodowana jako tekst a nie komenda } break; //----------------------------------------------------------------------------------------------------------------------------------- //Tekst wiadomości SMS //----------------------------------------------------------------------------------------------------------------------------------- case NEXT_TXT: for(int i=0;i<num;i++){//przeglądamy ją znak po znaku if(buffer[i]=='\r'||buffer[i]=='\n')buffer[i]=0;//znaki kończące ramkę też zastąpimy znakami zerowymi } Serial.print("Wiadomosc: "); Serial.println(buffer); //sprawdzamy czy to któreś z interesujących nas poleceń - możemy tu dodać własne polecenie, przesyłanie np. nastawy dla PWM itp. if(nr_correct){//ale najpierw sprawdzamy czy numer był naszym numerem - nie chcemy, zeby irytująca reklama włączyła nam oświetlenie w domu :D if(strcmp(buffer,"ON")==0){ digitalWrite(13,HIGH); } if(strcmp(buffer,"OFF")==0){ digitalWrite(13,LOW); } if(strcmp(buffer,"?")==0){ send_resp=1;//poinformujemy, aby po zakończeniu nadawania wysłać informację do użytkownika } } Serial.println(); stan=OK_WAIT_DELETE;//następnie po komuniiacie OK usuniemy wiadomosć i ewentualnie wyślemy odpowiedź break; case OK_WAIT_DELETE: //----------------------------------------------------------------------------------------------------------------------------------- //OK - komunikat poprawnego zakończenia wykonywania komendy //----------------------------------------------------------------------------------------------------------------------------------- ok=1;//zakładamy, ze to interesująca nas ramka for(int i=0;i<2;i++){//w pętli porównujemy pierwsze znaki ramki ze wzorcem OK if(buffer[i]!=OK[i])ok=0;//jeśli jakiś znak się różni to to nie jest interesująca nas ramka } if(ok){ //jeśli odebrano jakąś wiadomość if(msg_id!=0){ //to ją usuniemy GSM.print("AT+CMGD="); GSM.print(msg_id); GSM.print("\r"); msg_id=0; }else if(send_resp){//jeśli wiadomość została usunięta i mamy nadać odpowiedź send_resp=0;//to już to zaraz zrobimy GSM.print("AT+CMGS=+48000000005\r");//wysyłamy ją na ten numer delay(550); GSM.print("Led is ");//tu wpisujemy treść wiadomości (do 160 znaków) if(digitalRead(13)){ GSM.print("ENABLED"); }else{ GSM.print("DISABLED"); } GSM.print((char)26);//a następnie zatwierdzamy wysłanie Serial.println("Info wyslane"); } stan=IDLE; if(send_resp)stan=OK_WAIT_DELETE;//jeśli mamy nadać a zrealizowaliśmy tylko usunięcie wiadomości to musimy powtórtzyć ten etap i wysłać odpowiedź } //----------------------------------------------------------------------------------------------------------------------------------- //ERROR //----------------------------------------------------------------------------------------------------------------------------------- ok=1;//zakładamy, ze to interesująca nas ramka for(int i=0;i<5;i++){//w pętli porównujemy pierwsze znaki ramki ze wzorcem OK if(buffer[i]!=ERROR[i])ok=0;//jeśli jakiś znak się różni to to nie jest interesująca nas ramka } if(ok){ Serial.println("Huston, mamy problem..."); stan=IDLE; } break; } num=0; }else{//jeśli to inny znak niż zakończenie ramki to dopisujemy go do bufora buffer[num++]=c; buffer[num]=0;//ponadto zawsze kończymy zawartośc bufora znakiem 0 - koniec ciągu znaków if(num>255)num=0;//i dodatkowo dbamy o to, żeby nie przepełnić bufora } } }Do pobrania: gsm_wo_polling_online.c (kopia)
Cały projekt można pobrać tutaj: gsm_arduino.zip (kopia)
Życzę udanej zabawy!!! :-)
Arduino.
OdpowiedzUsuńCzyli nie ma jakichś przeciwskazań, by uruchomić takie sterowanie na AVR z UARTEM, np. jakaś ATmega8 czy 16?
Absolutnie nie ma, robiłem na ATmega88 z sim900. Zabawa z Uartem jest tak prosta jak obliczenia w Excelu. Tak naprawdę arduino to taka " prostsza biblioteka"dla uC.
OdpowiedzUsuńWitam, zakupiłem modem TC35 prosto z chin. Modem podpięty do komputera przez przejściówkę USB-UART oraz zasilony z 9,5V. Niestety w terminalu cisza. Istnieje jakaś metoda na sprawdzenie w inny sposób sprawności modemu ?
OdpowiedzUsuńMożesz próbować zadzwonić na numer karty włożonej do modemu, ale cisza w terminalu źle wróży - moze jakiś bład w połączeniach?
OdpowiedzUsuńWitam. Super program ale nie rozumiem dwóch rzeczy.(Zaznaczę że nie jestem jakimś zaawansowanym programistą. Raczej hobbysta wiec proszę o wyjaśnienie)
OdpowiedzUsuń1. if(buffer[i]!=SMS_CMTI[i]) - tz. nie widzę w programie żeby do buffer[] cokolwiek było przekazywane wiec jak on porównuje jego zawartość? Co się tam znajduje i jak tam dotarło.
2. for(int i=0;i<num;i++) na początku programu. jak wykonuje pętle sokoro num =0;
Mógłby ktoś wytłumaczyć to w łatwy i zrozumiały sposób?
1. Do zmiennej bufor dane wpisywane są na samym końcu programu (linijki 191. i 216 w pierwszym i drugim programie odpowiednio) - jest tam też komentarz kiedy te dane są dopisywane.
OdpowiedzUsuń2. Zmienna num jest inkrementowana (zwiększana o 1) także w okolicach tychże linijek, czyli zapisu do bufora (to nic innego jak zmienna pamiętająca ilość znaków zapisanych w buforze)
Ten komentarz został usunięty przez autora.
OdpowiedzUsuńWitam
OdpowiedzUsuńPosiadam płytkę Leonardo. Jaki byście polecali moduł GSM do tego (w rozsądnej cenie) Potrzebuję zrobić powiadomienie (zwarcie pinu do masy wysyła SMS o jakieś treści, 3 lub 4 wyjścia) oraz za pomocą wysłania komendy (SMS) aktywować jkieś wyjście (podanie napięcia). Kupiłem chińczyka z SIM00A (taka biała płytka z pinami), ale za żadne skarby nie mogę tego zflashować do ustawień europejskich. Proszę o jakieś info na adres markp11@wp.pl Z gry dzięki. PS jakiś gotowy program mile widziany (pod arduino oczywiście) ;-)
Poprawka SIM900A
OdpowiedzUsuńNajlepiej sflashować używając sprzętowego portu COM.
Usuńzmiany w gsm_sterowanie_online.c dla Arduino i SIM800L:
OdpowiedzUsuńlinia 68: z GSM.print("AT+CMGL=ALL\r"); na GSM.print("AT+CMGL=\"ALL\"\r");
linia 136: x GSM.print("AT+CMGS=+48000000005\r"); na GSM.print("AT+CMGS=\"+48000000005\"\r");
OdpowiedzUsuńW tej chwili jest możliwość kupienia na aliexpress air200. Jest to ciekawy moduł gsm bo posiada możliwość wgrania do niego własnego programu. Nie potrzebujemy wówczas dodatkowego mikrokontrolera. Air200 ma wyprowadzone piny gipo, które można oprogramować. Air200t można obsługiwać za pomocą poleceń AT. Natomiast wersję air200 za pomocą oprogramowania luat.
[img]https://i.imgur.com/dvmBJdo.png[/img]
Jak już wspomniano o dostępnych modułach GSM, to są:
OdpowiedzUsuńAir200 cena około $4
SIM800L $3,4
M590 $1,24
Ceny różne i dostępne funkcje różne. Do sterowania przez SMS wszystkie się nadają.