Odkaz na první část návodu zde.
Jak jsem slíbil, pokračuji ve svém předchozím článku o GPS loggeru. Tentokrát je rozšířen o GSM modul. Toto šikovné zapojení se může stát základem nějaké pohyblivé meteosondě. Arduino opět sbírá data z teploměru, tlakoměru, vlhkoměru, GPS polohu, nadmořskou výšku, rychlost, počet satelitů… a vypisuje je po sériové lince do USB portu. Tyto údaje opět ukládá na SD kartu, do textového souboru MERENI.txt. Případnou špatnou inicializaci modulů, odpojené čidlo či chybu na SD kartě obdržíme jako chybovou hlášku na sériové lince.
Připojený GSM modul mi umožňuje bezdrátovou, dálkovou komunikaci pomocí mobilních sítí GSM 900MHz, do nadm. Výšky cca 1-2km od povrchu, kam mají teoreticky dané sítě dosah. Pomocí připojeného modulu, mi Arduino odesílá zpět, po prozvonění ze známého čísla, SMS zprávy s GPS polohou a Meteo hodnotami a tato činnost je opět vypsána po sériové lince. SMS je psaná co nejúsporněji a neodesílá mi veškeré údaje, musí se vejít do max. velikosti 160 znaků. Dále mohu naopak ze zvoleného čísla odeslat ve 2 tvarech SMS zprávy do modulu a ten zareaguje 0,5s impulsem na pinech 2 nebo 3. A jako zpětnou vazbu dostanu SMS odpověď o vykonané činnosti. To lze využít pro nějaké ovládání 2 relé. Tak je i nutné sepsat tvar SMS zpráv. Tedy pro sepnutí relé 1 napíši text SMS zprávy: RELE1 a Arduino pošle na pin 2 krátký impuls, společně s tím i odešle zpět zprávu ve tvaru: Odpojeno RELE1. Opět je vypsána hláška po sériové lince. To samé platí i pro rele 2 na pinu 3. Na kontaktním poli mi relé nahrazují dvě LED diody s rezistory 9k9, které mi přišly pod ruku. Pro zapojení relátek je nutné tyto impulzy zesílit pomocí dvou NPN tranzistorů (viz. Schéma pro tranzistory pod textem).
Na obrázku vidíte SMS zprávy: první dvě jsou odeslány Arduinem po zavolání ze zvoleného čísla. Ty ve žlutém rámečku jsem odeslal ze stejného tel. čísla já a jako odpověď mi dorazily SMSky Odpojeno RELE1 a 2. Pro napájení Arduina stačí napětí z USB, ale GSM modul potřebuje kolem 4V a až 2A, to USB nedá. Tak jsem si postavil z několika vyřazených baterek z mobilu a staré nabíječky jednoduchý zdroj na 12AH 4,1V. Na pin Vin na desce Arduina by mělo stačit přivést + pól baterie do desky a dostaneme tak nezávislý zdroj. Prakticky jsem tohle zapojení zatím ale neověřil. Tak nemohu garantovat 100% činnost na 5V.
Teď k samotnému programu, nebudu jej dopodrobna popisovat, protože je již okomentován. Jen pro přesnost, na začátku jsou implementovány potřebné knihovny. Každý modul/čidlo má definovány proměnné/piny a inicializovány knihovny. Ve smyčce void setup je zahájena komunikace a případně vypsány chybové hlášky. Smyčka void loop pak načte hodnoty z GPS modulu a čidla, vytvoří z nich zprávu a tu pošle po sériové lince i uloží na SD kartu ve formě textového dokumentu MERENI.txt. Dále je vytvořena SMS a odeslána na vámi zvolené tel. Číslo. V programu je vykřížkováno jako:
char mojeCislo[16] = "+420xxxxxxxxx";
Zde dosadíte SVOJE číslo, na které chcete posílat SMS. Pro činnost GSM modulu si nezapomeňte pořídit i druhou, nabitou simkartu a v mobilním telefonu jej pak uložte do kontaktů, třeba pod jménem Arduino nebo jinak, to je na vás. Jen podotýkám, dodržte přesný tvar SMS zpráv pro ovládání relé. Program porovná text zprávy, s tím co mu přišlo a jestli se texty shodují (rozdíl znaků = 0), tak se na okamžik sepne jedno z relé.
Pro sepnutí relé 1, je nutné poslat SMS ve tvaru:
RELE1
A jako odpověď dostanete:
Odpojeno RELE1
Pro sepnutí relé 2, je nutné poslat SMS ve tvaru:
RELE2
A jako odpověď dostanete:
Odpojeno RELE2
Pro změnu textu SMS je nutno tuto změnu učinit i v programu. Tedy zde:
char mojeZprava1[delkaZpravy] = "RELE1";
char mojeZprava2[delkaZpravy] = "RELE2";
Nahradíte text RELE1, nebo RELE2 za požadovaný tvar. A samozřejmě tento text je nutné pak dodržet i při odeslání zprávy z vašeho telefonu. Dále by bylo vhodné upravit i text v řádcích Serial.print(…RELE1/2) , aby se po sériové lince opět odesílal správný tvar o vykonané činnosti.
SMS s odpovědí o odpojení RELE1 a 2 se dá změnit v řádku:
String dataString2(" Odpojeno RELE1 ");
String dataString3(" Odpojeno RELE2 ");
Případnou změnu formátu odeslané SMS s Meteo údaji, které se odesílají zpět na váš mobil po prozvonění, učiňte v řádcích dataString4. Zde si můžete nakombinovat odesílané údaje dle libosti. Tedy některé hodnoty vyměnit za jiné, případně odmazat. Jen je nutné dodržet délku 160 znaků.
Seznam potřebných knihoven:
- SoftwareSerial.h
- SPI.h
- Wire.h
- SD.h
- TinyGPS.h
- GPRS_Shield_Arduino.h
- sim900.h
- Adafruit_BME280.h
- Adafruit_Sensor.h
//GPS GSM SD data logger s teplomerem, tlakomerem a vlhkomerem //Knihovny #include <SoftwareSerial.h> #include <SPI.h> #include <Wire.h> #include <SD.h> #include <TinyGPS.h> #include <GPRS_Shield_Arduino.h> #include <sim900.h> #include <Adafruit_BME280.h> #include <Adafruit_Sensor.h> //SD // nastavení pinu CS pro SD kartu - pevně dané shieldem const int sd_CS = 53; //GPS NEO-6M // nastavení propojovacích pinů #define TX1 18 #define RX1 19 // inicializace GPS TinyGPS gps; // vytvoření dočasných proměnných pro načtení dat z GPS modulu float zSirka, zDelka; unsigned long stariDat; int rok; byte mesic, den, hodina, minuta, sekunda, setinaSekundy; // vytvoření proměnné pro vytištění data a času char datumCas[32]; // vytvoření dočasných proměnných pro načtení informací o komunikaci s GPS modulem bool novaData = false; unsigned long znaky; unsigned short slova, chyby; // SIM 900 GSM Shield // nastavení pinu ovladacich rele 1 a 2 #define pinRele1 2 #define pinRele2 3 // nastaveni komunikace se SIM modulem, #define pinTX 11 #define pinRX 10 #define pinPower 9 #define rychlostSim 9600 // vytvoření proměnné s detekovaným číslem char mojeCislo[16] = "+420*********"; // vytvoření proměnných pro práci s SMS #define delkaZpravy 160 char smsZprava[delkaZpravy]; int smsIndex = 0; char telCislo[16]; char casDatum[24]; // inicializace SIM900 modulu z knihovny GPRS sim900(pinTX, pinRX, rychlostSim); //BME280 // nastavení adresy senzoru #define BME280_ADRESA (0x76) // inicializace senzoru BME z knihovny Adafruit_BME280 bme; void setup() { // zahajeni komunikace s PC Serial.begin(9600); // zahajeni komunikace s GPS Serial1.begin(9600); // kontrola napájení modulu SIM900, // v případě vypnutého modulu ho zapneme if(!sim900.checkPowerUp()) { sim900.powerUpDown(pinPower); } // kontrola připojené SD karty if(!SD.begin(sd_CS)) { Serial.println("SD karta neni pripojena nebo je vadna!"); } // chybova hlaska pokud není GSM modul správně nainicializován a pockani // SIM modul if(!sim900.init()) { Serial.println("Chyba inicializace GSM, cekam 5 vterin!"); delay(5000); } else { Serial.println("Inicializace GSM uspesna, cekam 2 vteriny kvuli GPS modulu!"); delay(2000); } } void loop() { // po dobu jedné vteřiny budeme kontrolovat příjem dat z GPS for (unsigned long start = millis(); millis() - start < 1000;) { // kontrola aktivity softwarové komunikace while (Serial1.available()) { // vytvoření proměnné pro uložení načtených dat z GPS char c = Serial1.read(); //Serial.write(c); // pro výpis přijatých dat odkomentujte tento řádek // dekódování přijaté zprávy s kontrolou platných dat if (gps.encode(c)) { // pokud jsou přijatá data platná, nastavíme proměnnou pro tištění dat novaData = true; } } } // pokud proběhl příjem nových dat, vytiskneme všechny dostupné informace if (novaData) { // načtení GPS pozice do proměnných gps.f_get_position(&zSirka, &zDelka, &stariDat); // vytištění informací po sériové lince Serial.println(" ::Dostupne GPS udaje:: "); // vytištění informací po sériové lince Serial.print(" Zemepisna sirka: "); // nejprve zkontrolujeme, jestli máme platné údaje // (zSirka == TinyGPS::GPS_INVALID_F_ANGLE), // pokud nejsou validní (platné), vytiskneme nulu, // v opačném případě vytiskneme obsah proměnné s přesností 6 desetinných míst, // podobným způsobem se pracuje i s ostatními údaji Serial.println(zSirka == TinyGPS::GPS_INVALID_F_ANGLE ? 0.0 : zSirka, 6); Serial.print(" Zemepisna delka: "); Serial.println(zDelka == TinyGPS::GPS_INVALID_F_ANGLE ? 0.0 : zDelka, 6); Serial.print(" Pocet satelitu: "); Serial.print(gps.satellites() == TinyGPS::GPS_INVALID_SATELLITES ? 0 : gps.satellites()); Serial.print(" Presnost: "); Serial.print(gps.hdop() == TinyGPS::GPS_INVALID_HDOP ? 0 : gps.hdop()); Serial.print(" Stari dat: "); Serial.println(stariDat == TinyGPS::GPS_INVALID_AGE ? 0 : stariDat); Serial.print(" Nadmorska vyska m.n.m: "); Serial.println(gps.f_altitude() == TinyGPS::GPS_INVALID_F_ALTITUDE ? 0 : gps.f_altitude()); Serial.print(" Rychlost v km/h: "); Serial.println(gps.f_speed_kmph() == TinyGPS::GPS_INVALID_F_SPEED ? 0 : gps.f_speed_kmph()); // načtení data a času z GPS modulu do proměnných gps.crack_datetime(&rok, &mesic, &den, &hodina, &minuta, &sekunda, &setinaSekundy, &stariDat); // kontrola platnosti dat if (stariDat == TinyGPS::GPS_INVALID_AGE) { Serial.println("Nelze nacist datum a cas."); Serial.println(); } else { Serial.print(" Datum a cas: "); // poskládání celé zprávy do proměnné datumCas a poté její vytištění, // %02d znamená desetinné číslo uvedené za uvozovkami s přesností na 2 číslice sprintf(datumCas, "%02d/%02d/%02d %02d:%02d:%02d", mesic, den, rok, hodina, minuta, sekunda); Serial.println(datumCas); Serial.println(); } } // načtení a vytištění informací o komunikaci s GPS modulem gps.stats(&znaky, &slova, &chyby); Serial.print(" Detekovane znaky: "); Serial.print(znaky); Serial.print(", slova: "); Serial.print(slova); Serial.print(", chyby pri kontrole dat: "); Serial.print(chyby); Serial.println(); // kontrola chyb při komunikaci skrze detekci přijatých znaků if (znaky == 0) { Serial.println(); // vytištění prázdného řádku Serial.println(" Chyba pri prijmu dat z GPS, zkontrolujte zapojeni! "); Serial.println(); } // v případě chyby senzoru je vypsána hláška po sériové lince if (!bme.begin(BME280_ADRESA)) { Serial.println(" BME280 senzor nenalezen, zkontrolujte zapojeni! "); Serial.println(); // vytištění prázdného řádku } else { Serial.println(); // výpis všech dostupných informací ze senzoru BMP // výpis teploty Serial.println(" ::Dostupne meteorologicke udaje:: "); // výpis teploty Serial.print(" Teplota °C: "); Serial.println(bme.readTemperature()); // výpis relativní vlhkosti Serial.print(" Relativni vlhkost %: "); Serial.println(bme.readHumidity()); // výpis tlaku s přepočtem na hektoPascaly Serial.print(" Tlak hPa.: "); Serial.println(bme.readPressure() / 100.0F); // vytištění prázdného řádku Serial.println(); } // otevření souboru na SD kartě s názvem mereni.txt File zapisDat = SD.open("mereni.txt", FILE_WRITE); // vytvoreni textu mereni.txt String dataString1("GPS UDAJE "); dataString1 += (" Zem.sirka: "); dataString1 += String(zSirka == TinyGPS::GPS_INVALID_F_ANGLE ? 0.0 : zSirka, 6); dataString1 += (" Zem.delka: "); dataString1 += String(zDelka == TinyGPS::GPS_INVALID_F_ANGLE ? 0.0 : zDelka, 6); dataString1 += (" Satelity: "); dataString1 += String(gps.satellites() == TinyGPS::GPS_INVALID_SATELLITES ? 0 : gps.satellites()); dataString1 += (" Nadm.vyska: "); dataString1 += String(gps.f_altitude() == TinyGPS::GPS_INVALID_F_ALTITUDE ? 0 : gps.f_altitude()); dataString1 += (" Rychlost km/h: "); dataString1 += String(gps.f_speed_kmph() == TinyGPS::GPS_INVALID_F_SPEED ? 0 : gps.f_speed_kmph()); dataString1 += (" Datum cas: "); dataString1 += String(datumCas); dataString1 += String(" METEO UDAJE "); dataString1 += (" Teplota C: "); dataString1 += String(bme.readTemperature()); dataString1 += (" Rel.vlhkost %: "); dataString1 += String(bme.readHumidity()); dataString1 += (" Tlak hPa.: "); dataString1 += String(bme.readPressure() / 100.0F); // v případě, že je soubor bez problémů vytvořen (pokud neexistuje), // nebo otevřen (pokud existuje), zapiš do něj dataString a ukonči zápis if (zapisDat) { zapisDat.println(dataString1); zapisDat.close(); Serial.println(" Zapis na kartu uspesny. "); Serial.println(); } // v případě chyby při otevírání souboru vypiš chybu else { Serial.println(" Chyba pri otevreni souboru mereni.txt ! "); Serial.println(); } // kontrola SMS zpráv - načtení počtu nepřečtených zpráv smsIndex = sim900.isSMSunread(); // pokud existuje alespoň jedna nepřečtená zpráva, // provedeme její přečtení a detekci textu if (smsIndex > 0) { // načtení SMS zprávy do proměnných ve funkci sim900.readSMS(smsIndex, smsZprava, delkaZpravy); // vymazání aktuálně přečtené zprávy sim900.deleteSMS(smsIndex); // vytištění informací o přijaté SMS zprávě Serial.print("Od cisla: "); Serial.println(telCislo); Serial.print("Zprava: "); Serial.println(smsZprava); // kontrola přijetí SMS zprávy od našeho nastaveného čísla // strcmp porovná 2 stringy a nula znamená počet odlišných znaků if (strcmp(telCislo, mojeCislo)==0) { // Ovladani 2 rele pomoci SMS // proměnná s detekovaným textem v SMS zprávě char mojeZprava1[delkaZpravy] = "RELE1"; char mojeZprava2[delkaZpravy] = "RELE2"; // kontrola přijetí detekovaného textu if (strcmp(smsZprava, mojeZprava1)==0) { Serial.println("Prijata zprava RELE1"); digitalWrite(pinRele1, HIGH); delay(500); digitalWrite(pinRele1, LOW); Serial.println(" Odpojeno RELE1 "); // vytvoreni SMS zpravy String dataString2(" Odpojeno RELE1 "); // převedení textu na proměnnou kompatibilní se SIM modulem dataString2.toCharArray(smsZprava, 160); // poslání SMS zprávy na nastavené číslo sim900.sendSMS(telCislo, smsZprava); Serial.println(" Odeslana SMS: Odpojeno RELE1. "); Serial.println(); } else if (strcmp(smsZprava, mojeZprava2)==0) { Serial.println(" Prijata zprava RELE2 "); digitalWrite(pinRele2, HIGH); delay(500); digitalWrite(pinRele2, LOW); Serial.println(" Odpojeno RELE2 "); // vytvoreni SMS zpravy String dataString3(" Odpojeno RELE2 "); // převedení textu na proměnnou kompatibilní se SIM modulem dataString3.toCharArray(smsZprava, 160); // poslání SMS zprávy na nastavené číslo sim900.sendSMS(telCislo, smsZprava); Serial.println(" Odeslana SMS: Odpojeno RELE2. "); Serial.println(); } else { Serial.println(" Prijata neznama zprava. "); } } else { Serial.println(" Prijata neznama zprava od neznameho cisla. "); } } // kontrola příchozího hovoru s uložením čísla do proměnné if(sim900.isCallActive(telCislo)) { // vytištění informací o příchozím hovoru Serial.print(" Vola cislo: "); Serial.println(telCislo); // pauza po dobu 1 sekundy a následné zavěšení hovoru delay(1000); sim900.hangup(); // kontrola hovoru od našeho nastaveného čísla if (strcmp(telCislo, mojeCislo)==0) { // vytištění informace o poslání zprávy Serial.println(" Zname cislo, odeslana SMS. "); // vytvoreni SMS zpravy String dataString4("Sirka:"); dataString4 += String(zSirka == TinyGPS::GPS_INVALID_F_ANGLE ? 0.0 : zSirka, 6); dataString4 += (" Delka:"); dataString4 += String(zDelka == TinyGPS::GPS_INVALID_F_ANGLE ? 0.0 : zDelka, 6); dataString4 += (" Vyska:"); dataString4 += String(gps.f_altitude() == TinyGPS::GPS_INVALID_F_ALTITUDE ? 0 : gps.f_altitude()); dataString4 += (" Rychlost:"); dataString4 += String(gps.f_speed_kmph() == TinyGPS::GPS_INVALID_F_SPEED ? 0 : gps.f_speed_kmph()); dataString4 += (" Datum Cas:"); dataString4 += String(datumCas); dataString4 += (" Teplota C:"); dataString4 += String(bme.readTemperature()); dataString4 += (" Vlhkost %:"); dataString4 += String(bme.readHumidity()); dataString4 += (" Tlak hPa.:"); dataString4 += String(bme.readPressure() / 100.0F); // převedení textu na proměnnou kompatibilní se SIM modulem dataString4.toCharArray(smsZprava, 160); // poslání SMS zprávy na nastavené číslo sim900.sendSMS(telCislo, smsZprava); } else { // vytištění informací o volání neznámého čísla Serial.println(" Nezname cislo - bez odezvy. "); Serial.print(" Ulozene : "); Serial.println(mojeCislo); Serial.print(" Volajici: "); Serial.println(telCislo); } } delay(2000); }
Schéma pro tranzistory:
Schéma celkové:
Zapojte dle schématu, nezapomeňte propájet propojku JP na desce SIM 900. Bez ní by nefungovalo zapínání pinem 9.