// Rozsirujici deska k SQM pro zpracovani GPS signalu //===================================================== // sqm.astromik.org // // verze 28.12.2023 pro desku SQM-GPS-5(6) // ---------------------------------- // POZOR! // V Arduino IDE nastavit hardware (soubor boards.txt) na 4MHz krystal // Po nahrani prelozeneho programu pak upravit FUSE: // LowFuse : 0xDC // HighFuse : 0xDE // ExtendFuse: 0xFF // Strucny popis programu // // Poloha se zjistuje z GPS modulu (NEO-6M, ATGM336H ...) pres seriovou linku (Tx, Rx). // Zpracovavaji se zpravy (vety) GxRMC a GxGGA. // Druha strana komunikace probiha pres I2C s hlavni SQM deskou. // Pomoci I2C komunikace je mozne stahnout data (zprumerovane souradnice, cas a dalsi informace o GPS modulu). // Take je mozne pres I2C ovladat LED - docasne vsechny zhasnout (funkce 20) a pak obnovit do puvodniho stavu (funkce 21), // nebo provest HOT restart GPS modulu (funkce 22). // // Pri sepnuti servisniho pinu behem zapnuti napajeni se nastavi GPS modul na prijem zprav GxRMC a GxGGA (ostatni zpravy se vypnou). // Nastavi se i rychlost odesilani NMEA zprav na 1 sekundu. // Pri beznem provozu se spojenim servisniho pinu na GND rozblikaji vsechny LED frekvenci 1Hz (strida 50%) // Po 5 sekundach spojeni dochazi k prepinani faze blikani LED1. Nektere GPS moduly odesilaji NMEA zpravy v obracenem poradi a to zpusobuje, // ze LED1 blika s vyrazne delsim rozsvicenym stavem. Po prepnuti faze se pak usetri trochu energie a snizi celkove vyzarovani svetla z SQM. // Prepnuti faze je signalizovano rychlym zablikanim LED1. // 3 signalizacni LEDky informuji o stavu GPS modulu: // LED1 - prepina se podle prave zpracovavane vety (GxRMC nesviti / GxGGA sviti) - blikani je spravny stav, modul NEO-6M vysila obe zpravy // LED2 - stav prijmu datumu a casu (cas neni k dispozici - sviti / cas je k dispozici - zhasnuto) // LED3 - dostupnost souradnic // sviti = souradnice jeste nejsou k dispozici, neni zafixovano ani na jeden satelit // blika = je zafixovano alespon na 1 satelit, ale jeste nejsou souradnice, nebo pocet prijatych souradnic nestaci na prumerovani (mene nez 10) // nesviti = vsechno je v poradku, souradnice jsou k dispozici (pocet OK souradnic je 10 a vice) // Spravny stav je tedy takovy, ze blika LED1 a ostatni LED jsou zhasnute. // V pripade, ze blika LED1 a LED2 je zhasnuta, dojde pri mereni jasu automaticky k serizeni casu v RTC na hlavni desce SQM-BAS. // Data se do SQM odesilaji pomoci 19 8-bitovych registru // priklad exportnich dat: // 0 --- 8 prumer LAT (pro severni polokouli zvetseny o 90 stupnu) // 1 --- 79 // 2 --- 192 // 3 --- 172 // 4 --- 11 prumer LON (pro vychodni polokouli zvetseny o 180 stupnu) // 5 --- 149 // 6 --- 204 // 7 --- 147 // 8 --- 100 OK cnt (pocet bezchybnych zafixovanych dat v rade) // 9 --- 21 YY (datum a cas z GPS - v GMT zone) // 10 --- 1 MM // 11 --- 1 DD // 12 --- 11 HH // 13 --- 49 NN // 14 --- 22 SS // 15 --- 1 Nadmorska vyska zvysena o 500m // 16 --- 241 // 17 --- 4 Pocet 'viditelnych' satelitu // 18 --- 23 10x HDoP (Horizontal Dilution of Precision) ... mensi cislo = vetsi presnost // 19 --- 1 status bajt: // bit 0 = '0' pole klouzaku jeste neni zaplneno // bit 0 = '1' pole klouzaku zaplneno, muzou se stahovat data // bit 1 = '0' cas se do RTC nezapise (v interni verzi GPS tento bit bude vzdycky v '1') // bit 1 = '1' pri stazeni dat se do RTC ulozi cas z GPS (pokud bude k dispozci) // bit 2 = '0' puvodni externi verze GPS // bit 2 = '1' nova interni verze GPS // // Na zvlastni pozadavek odeslany pres I2C sbernici (prikazy 50 az 55 a 60 az 65) je mozne si vyzadat i kompletni // obsah NMEA vet GxRMC a GxGGA. Vety se zpatky do SQM odesilaji po 15 znacich. // Pri pozadavku s kodem 70 se vrati 15-znakovy text s verzi programu (yyyy-mm-dd..INT) //====================================================================================================================== char verzeSW[] = "2023-12-28..INT"; #define F_CPU 4000000UL #include // knihovna pro I2C komunikaci #include // knihovna pro vnitrni EEPROM #define pin_LED1 2 #define pin_LED2 3 #define pin_LED3 4 #define pin_setup 6 // pri prizemneni behem resetu se do GPS modulu odesle prikaz pro povoleni GxGGA a GxRMC vety a toto nastaveni se ulozi do vnitrni EEPROM GPS modulu #define pin_dta_rdy A1 // signalizace pro SQM, ze je modul GPS zapnuty #define I2C_ADDR_GPS 0x17 // I2C adresa pridavneho modulu pro GPS // do retezce 'veta' se budou zapisovat prijata data z GPS char veta[] = " @\0"; // $GPRMC,110109.00,A,4926.67723,N,01421.99214,E,0.521,,311220,,,A*72 char veta_RMC[] = " @\0"; // $GPGGA,103515.00,4926.65079,N,01421.99995,E,1,05,4.92,499.9,M,44.3,M,,*5C char veta_GGA[] = " @\0"; // POZOR! Nektere prijimace, ktere kombinuji prijem vice druzicovych systemu (GPS, GLONASS, BEIDOU), misto "GPRMC" a "GPGGA" vraci retezce "GNRMC" a "GNGGA" // Treti znak ve zprave je tedy nutne pri zpracovani zprav ignorovat char rozlozeny_pole[16][27]; // vety se sem do toho pole rozkladaji na jednotlive polozky (puvodne byly ve vete oddelene carkami) byte ukazatel_klouzaku; unsigned long klouzak_LAT[10]; // pole pro prumerovani zjistenych souradnic unsigned long klouzak_LON[10]; float klouzak_ALT[10]; boolean pripraveno = false; // je zaznamenany alespon 1 blok klouzaku (10 vzorku), tak se da pocitat prumer byte volatile prikaz_I2C; // z SQM (MASTER) se da do modulu (SLAVE) poslat prikaz: // 0 = bez prikazu (predchozi prikaz byl vykonan) // 10 = zadost o zpacovana data // 20 = zhasnout docasne vsechny LED (pri mereni jasu) // 21 = obnovit puvodni svit LED // 50 az 55 = stahnout 15 bajtu dlouhy blok textu ze zpravy GxRMC (dohromady az 90 znaku) // 60 az 65 = stahnout 15 bajtu dlouhy blok textu ze zpravy GxGGA (dohromady az 90 znaku) // 70 = stahnout 15 bajtu dlouhy blok textu s verzi programu boolean stav_LED_1; // kvuli zhasinani a obnovovavni LED pre I2C prikaz se musi zapamatovat aktualni stav LED boolean stav_LED_2; boolean stav_LED_3; boolean pamet_LED_1; // kvuli zhasinani a obnovovavni LED pre I2C prikaz se musi zapamatovat stav LED pred zhasnutim boolean pamet_LED_2; boolean pamet_LED_3; boolean ignoruj_vetu; // kdyz se prijme veta, o kterou nemam zajem byte OK_byte; // pocitadlo nepretrzitych prijatych dat byte CRC_end; // na ktere pozici ve vete se objevila '*' (pouzito pro vypocet CRC) byte ukazatel; byte EXPORT_pole[25]; // sem se ukladaji zpracovana data z GPS a odsud se pak preposilaji pres I2C char rd; byte aktivace_dta_rdy = 0; // "pin_dta_rdy" se prepina do HIGH az kdyz se podari alespon 1x nacist z modulu GPS obe NMEA vety unsigned long starttime; char switchChar; // na stridacku se zpracovavaji vety "GxRMC" (obsahuji datum) a "GxGGA" (obsahuji nadmorskou vysku) char typ_vety; // aktualni typ prijate vety ('R' nebo 'G') byte statusy_LED; // promenna, do ktere se prubezne uklada pozadovany stav 3 LED (bit0=LED1; bit1=LED2; bit2 = LED3; // bit5=blikani LED3 nadrazene nad bit2 ) boolean zhasnuto; // pri docasnem zhasnuti blokuje dalsi rozsveceni LED boolean blik3; // pouzito pro blikani 3. LED boolean faze_LED1; // u nekterych modulu maji jde nejdriv veta GGA a az potom RMC. To zpusobuje, ze LED1 blika s opacnou stridou. // zmenou teto promenne se stav LED prohodi unsigned long cas_prvniho_cyklu; // pro podprogram na zmenu faze LED1 (5-sekundovy odpocet drzeni servisniho pinu v LOW) boolean prvni_cyklus; unsigned long blik500; // casovani 500ms pro servisni blikani vsech LED boolean stav500; // pro prepinani stavu LED pri servisnim blikani // prikazy pro GPS modul (UBX protokol) // sync sync class id delka (LittleEndian) byte zacatek_prikazu[] = { 0xB5, 0x62, 0x06, 0x01, 0x03, 0x00}; // zacatek vypinaciho i zapinaciho prikazu je vzdycky stejny // msgclass id rate CK_A CK_B byte zapni_GxGGA[] = { 0xF0, 0x00, 0x01, 0xFB, 0x10}; byte zapni_GxRMC[] = { 0xF0, 0x04, 0x01, 0xFF, 0x18}; byte vypni_GxGLL[] = { 0xF0, 0x01, 0x00, 0xFB, 0x11}; byte vypni_GxGSA[] = { 0xF0, 0x02, 0x00, 0xFC, 0x13}; byte vypni_GxGSV[] = { 0xF0, 0x03, 0x00, 0xFD, 0x15}; byte vypni_GxVTG[] = { 0xF0, 0x05, 0x00, 0xFF, 0x19}; byte vypni_GxGRS[] = { 0xF0, 0x06, 0x00, 0x00, 0x1B}; byte vypni_GxGST[] = { 0xF0, 0x07, 0x00, 0x01, 0x1D}; byte vypni_GxZDA[] = { 0xF0, 0x08, 0x00, 0x02, 0x1F}; byte vypni_GxGBS[] = { 0xF0, 0x09, 0x00, 0x03, 0x21}; byte vypni_GxDTM[] = { 0xF0, 0x0A, 0x00, 0x04, 0x23}; byte vypni_GxGNS[] = { 0xF0, 0x0D, 0x00, 0x07, 0x29}; byte vypni_GxVLW[] = { 0xF0, 0x0F, 0x00, 0x09, 0x2D}; byte vypni_GPTXT[] = { 0xF0, 0x41, 0x00, 0x3B, 0x91}; byte vypni_PUBX1[] = { 0xF1, 0x00, 0x00, 0xFB, 0x12}; byte vypni_PUBX2[] = { 0xF1, 0x03, 0x00, 0xFE, 0x18}; byte vypni_PUBX3[] = { 0xF1, 0x04, 0x00, 0xFF, 0x1A}; byte vypni_XXXX1[] = { 0xF0, 0x0E, 0x00, 0x08, 0x2B}; byte vypni_XXXX2[] = { 0xF1, 0x01, 0x00, 0xFC, 0x14}; byte vypni_XXXX3[] = { 0xF1, 0x05, 0x00, 0x00, 0x1C}; byte vypni_XXXX4[] = { 0xF1, 0x06, 0x00, 0x01, 0x1E}; byte jeden_Hz[] = {0xB5, 0x62, 0x06, 0x08, 0x06, 0x00, 0xE8, 0x03, 0x01, 0x00, 0x01, 0x00, 0x01, 0x39, 0xB5, 0x62, 0x06, 0x08, 0x00, 0x00, 0x0E, 0x30}; byte SAVE_EEPROM[] = {0xB5, 0x62, 0x06, 0x09, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x31, 0xBF}; byte hot_start[] = {0xB5, 0x62, 0x06, 0x04, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x10, 0x68}; //====================================================================================================================== //====================================================================================================================== void setup(void) { UBRR0H = 0; // doladeni rychlosti seriove linky na 9600bps pro 4MHz krystal (kat. list ATmega328 str. 201) UBRR0L = 25; byte pomprom = EEPROM.read(0); // promenna pro zjisteni predchozi faze blikani LED1 switch (pomprom) { case 0: faze_LED1 = false; break; case 1: faze_LED1 = true; break; default: EEPROM.write(0,0); faze_LED1 = false; } Wire.begin(I2C_ADDR_GPS); // rozsirujici deska bude pres I2C komunikovat na adrese 0x17 Wire.onRequest(requestEvent); Wire.onReceive(receiveEvent); Serial.begin(9600); // rychlost komunikace s GPS modulem pinMode(pin_setup ,INPUT_PULLUP); // jumper pro zakladni nastaveni GPS modulu // @/1 pinMode(pin_LED1 ,OUTPUT); // LEDKY pinMode(pin_LED2 ,OUTPUT); pinMode(pin_LED3 ,OUTPUT); pinMode(pin_dta_rdy, OUTPUT); digitalWrite(pin_dta_rdy, LOW); // po spusteni "pin_dta_rdy" zustane jeste chvili v LOW if (digitalRead(pin_setup) == LOW) // kdyz je pro zapnuti prizemneny pin_setup, dojde k prenastaveni NMEA vet z GPS modulu { for (byte i = 1 ; i < 6 ; i++) // 5x bliknuti frekvenci 1Hz kvuli kontrole spravne rychlosti krystalu (4MHz) { LED1(false); // strida 50% LED2(false); LED3(false); delay(500); LED1(true); LED2(true); LED3(true); delay(500); } if (digitalRead(pin_setup) == LOW) // kdyz je i po 5 sekundach sepnuty kontakt, nastavi se modul NEO-6M { GPS_setup(); // povoleni GxGGA a GxRMC vet. Ostatni zrusit. while (true) // Z teto nekonecne smycky se vypadne jen resetem { LED1(true); // nekonecne rychle blikani (1Hz) vsech LED najednou LED2(true); // strida 10% LED3(true); delay(100); LED1(false); LED2(false); LED3(false); delay(900); } } } bitSet(statusy_LED,0); // po zapnuti napajeni se vsechny LED rozsviti (znameni vsech chyb: GPS neodesila vety, signal neobsahuje cas, signal je spatny) bitSet(statusy_LED,1); bitSet(statusy_LED,2); LED1(true); // rozsviti vsechny LED LED2(true); LED3(true); delay(500); switchChar = 'R'; // nejdriv se bude zjisovat typ vety "GxRMC" EXPORT_pole[19] = 0; // neni zaplneno pole pro klouzaky, neni pozadavek na nastavovani RTC } //====================================================================================================================== //====================================================================================================================== void loop(void) { ignoruj_vetu = false; if(prikaz_I2C == 0) { if (Serial.available()) // kdyz se neco objevi na seriove lince z GPS modulu { CRC_end = 1; while( rd != '$') rd = Serial.read(); // cekani na zacatek vety ukazatel = 0; veta[ukazatel] = rd; // '$' je vzdycky na zacatku starttime = millis(); while (rd != '\r' and (millis() - starttime < 700)) // kdyz uz je znamy zacatek vety, musi se zbytek vety stahnout do 0.7 sekundy { if (Serial.available()) { rd = Serial.read(); if (ukazatel == 2) { typ_vety = rd; // 3.znak od zacatku urcuje typ prijate vety if (typ_vety != switchChar ) // kdyz se typ vety neshoduje s pozadovanou vetou, cetni se prerusi se znackou ignoruj_vetu = true { ignoruj_vetu = true; break; } } if (rd == '*') CRC_end = ukazatel; // znacka, do ktereho znaku se bude pocitat CRC ukazatel++; veta[ukazatel] = rd; // vsechny prijate znaky se zapisuji do znakoveho pole 'veta[]' } } if (ignoruj_vetu == false) // kdyz je nactena spravna veta, tak se zpracuje { byte blok = 0; // rozlozeni vety na jednotlive bloky mezi carkami byte pozice = 0; byte rad = 0; char znak = ' '; while (znak != '\r' ) { znak = veta[pozice]; if (switchChar == 'G') veta_GGA[pozice] = znak; if (switchChar == 'R') veta_RMC[pozice] = znak; if (znak == ',') { rozlozeny_pole[blok][rad] = '\0'; blok ++; rad = 0; } else { rozlozeny_pole[blok][rad] = znak; rad ++; } pozice ++; } while (pozice < 90) // kompletni smazani aktualne zpracovavane vety pred jejim naplnenim novymi daty { if (switchChar == 'G') veta_GGA[pozice] = ' '; if (switchChar == 'R') veta_RMC[pozice] = ' '; pozice ++; } if (switchChar == 'G') zpracuj_vetu_GxGGA(); // Nektere prijimace vraci retezec GNGGA, ale protoze se druhy znak nevyhodnocuje, tak to nevadi if (switchChar == 'R') zpracuj_vetu_GxRMC(); // Nektere prijimace vraci retezec GNRMC, ale protoze se druhy znak nevyhodnocuje, tak to nevadi byte i; for (i = 0 ; i < 90 ; i ++) // po zpracovani prislusne vety se pole veta[] smaze { veta[i] = ' '; } veta[i+1] = '\0'; if (switchChar == 'R') // na zaver se pripravi prijem druhe vety, nez ktera byla zpracovavana doted { switchChar = 'G'; // priste zpracuj GxGGA vetu } else { switchChar = 'R'; // priste zpracuj GxRMC vetu } } } } if (aktivace_dta_rdy == 0b11) // obe vety byly alespon 1x prijaty { digitalWrite(pin_dta_rdy, HIGH); // "pin_dta_rdy" se prepne na HIGH } obsluha_LED(); } //====================================================================================================================== //---------------------------------------------- // v GxGGA vete se zjistuje nadmorska vyska, pocet satelitu a kvalita zamereni (HDoP) void zpracuj_vetu_GxGGA(void) { aktivace_dta_rdy = aktivace_dta_rdy | 0b01; // GxGGA veta byla alespon 1x prijata byte CRC=0; // vypocet CRC z prijatych dat for (byte i = 1 ; i <= CRC_end ; i++) CRC = CRC xor veta[i]; if (crc_check(CRC , rozlozeny_pole[14][1],rozlozeny_pole[14][2])) // kontrolni soucet souhlasi, takze nejake vety se z GPS modulu ziskavaji { if (faze_LED1 == true) bitSet(statusy_LED,0); // LED1 rozsvit - LED1 bude blikat (v GxGGA vete se rozsveci, v GxRMC vete se zhasina) else bitClear(statusy_LED,0); // LED1 zhasni - LED1 bude blikat (v GxGGA vete se zhasina, v GxRMC vete se rozsveci) int poc_sat = atof(rozlozeny_pole[7]); // protoze je v poli HDoP pri ztrate signalu posledni platna hodnota, musi se testovat i pocet fixnutych satelitu int HDoP = (atof(rozlozeny_pole[8]) + 0.05) * 10; // Starsi verze modulu hlasily pred zafixovanim HDoP=99.99, novejsi hlasi HDoP=25.5. if (poc_sat < 4) HDoP = 255; // pri ztrate signalu se HDoP nastavuje na maximum (255 = err), takze LED3 neblika ale rozsviti se if (HDoP > 250) // pro obe varianty modulu plati, ze HDoP musi byt mensi nez 25.5, aby LED zacala blikat { bitClear(statusy_LED,5); // signal neni vubec (pocet satelitu = 0, HDoP = 99.99 nebo 25.5) takze blikani je zrusene (LED trvale sviti) } else { if (OK_byte < 10) // signal zafixovany alespon na 1 satelit, ale jeste neni dostatek zaznamu pro klouzak - blikani aktivni { bitSet(statusy_LED,5); // doplneni blikani o kratky "predblik" } else // uz je dostek zaznamu pro klouzak - blikani vypnute (LED trvale zhasnuta) { bitClear(statusy_LED,5); } } if (rozlozeny_pole[6][0] != '0') // kdyz je zafixovany signal, provede se zpracovani { byte pocet_satelitu; pocet_satelitu = (rozlozeny_pole[7][0] - 48) * 10; // desitky satelitu pocet_satelitu = pocet_satelitu + (rozlozeny_pole[7][1] - 48) ; // jednotky satelitu EXPORT_pole[17] = pocet_satelitu; // maximalne by jich melo byt 12 (to se vejde do bajtu) // Horizontal Dilution of Precision 10x if(HDoP > 255) HDoP = 255; // maximalne se musi vejit do jednoho bajtu EXPORT_pole[18] = HDoP & 0xFF; if (HDoP < 100 and OK_byte > 10) // dobra kvalita, alespon 10 vzorku bez chyb { bitClear(statusy_LED,2); // LED3 zhasnout } else { bitSet(statusy_LED,2); // LED3 rozsvitit } klouzak_ALT[ukazatel_klouzaku] = atof(rozlozeny_pole[9]); // klouzavy prumer souradnic z 10 vzorku if (pripraveno == true) { float suma_ALT = 0; for (byte i = 0 ; i < 10 ; i ++) { suma_ALT = suma_ALT + klouzak_ALT[i]; } unsigned int prumer_ALT = 500 +( (suma_ALT + 5) / 10); // Vyska se prenasi posunuta o 500m (0 mnm = 500; 380mnm = 880 ; 550mnm = 1050, -200mnm = 300) EXPORT_pole[15] = prumer_ALT >> 8 & 0xFF; EXPORT_pole[16] = prumer_ALT & 0xFF; OK_byte ++; if (OK_byte > 100) OK_byte = 100; EXPORT_pole[8] = OK_byte ; // kolik vzorku proslo v rade bez problemu (maximalni hodnota je 100, dal se nepocita) } } else // neni zafixovano { bitSet(statusy_LED,2); // LED3 rozsvitit EXPORT_pole[0] = 255; // LAT nastavit mimo rozsah (Err) EXPORT_pole[1] = 255; EXPORT_pole[2] = 255; EXPORT_pole[3] = 255; EXPORT_pole[4] = 255; // LON nastavit mimo rozsah (Err) EXPORT_pole[5] = 255; EXPORT_pole[6] = 255; EXPORT_pole[7] = 255; EXPORT_pole[8] = 0; // OK cnt EXPORT_pole[15] = 255; // ALT nastavit mimo rozsah (Err) EXPORT_pole[16] = 255; EXPORT_pole[17] = 0; // pocet satelitu EXPORT_pole[18] = 255; // HDoP (Err) EXPORT_pole[19] = EXPORT_pole[19] & 0b11111110; // status maze informaci o zaplnenem poli klouzaku } } else // neco je spatne (nebyly prijate souradnice nebo nesouhlasi CRC, nebo je ve vete vystrazna znacka) { OK_byte = 0; // nulovani poctu byzchybnych prijmu v rade EXPORT_pole[8] = 0; bitSet(statusy_LED,2); // LED3 rozsvitit } } //---------------------------------------------- //---------------------------------------------- // Z vety GxRMC se berou souradnice, datum a cas. Taky se tu sleduji vystrahy, pri kterych se nuluje pocet bezchybnych prijmu void zpracuj_vetu_GxRMC(void) { aktivace_dta_rdy = aktivace_dta_rdy | 0b10; // GxRMC veta byla alespon 1x prijata byte CRC=0; // vypocet CRC z prijatych dat for (byte i = 1 ; i <= CRC_end ; i++) CRC = CRC xor veta[i]; // rozklad casu na hodiny, minuty a sekundy byte GMT_hod = 0; byte GMT_min = 0; byte GMT_sek = 0; GMT_hod = (rozlozeny_pole[1][0] - 48) * 10; // desitky hodin GMT_hod = GMT_hod + (rozlozeny_pole[1][1] - 48) ; // jednotky hodin GMT_min = (rozlozeny_pole[1][2] - 48) * 10; // desitky minut GMT_min = GMT_min + (rozlozeny_pole[1][3] - 48) ; // jednotky minut GMT_sek = (rozlozeny_pole[1][4] - 48) * 10; // desitky sekund GMT_sek = GMT_sek + (rozlozeny_pole[1][5] - 48) ; // jednotky sekund // rozklad datumu na dny, mesice a roky byte GMT_den = 0; byte GMT_mes = 0; byte GMT_rok = 0; GMT_den = (rozlozeny_pole[9][0] - 48) * 10; // desitky dni GMT_den = GMT_den + (rozlozeny_pole[9][1] - 48) ; // jednotky dni GMT_mes = (rozlozeny_pole[9][2] - 48) * 10; // desitky mesicu GMT_mes = GMT_mes + (rozlozeny_pole[9][3] - 48) ; // jednotky mesicu GMT_rok = (rozlozeny_pole[9][4] - 48) * 10; // desitky roku GMT_rok = GMT_rok + (rozlozeny_pole[9][5] - 48) ; // jednotky roku if (GMT_rok > 20 and rozlozeny_pole[9][5] != '\0' ) // kdyz je rok vetsi, nez 2020, da se predpokladat, ze alespon datum a cas je prijaty v poradku { EXPORT_pole[9] = GMT_rok; EXPORT_pole[10] = GMT_mes; EXPORT_pole[11] = GMT_den; EXPORT_pole[12] = GMT_hod; EXPORT_pole[13] = GMT_min; EXPORT_pole[14] = GMT_sek; bitClear(statusy_LED,1); // LED2 zhasnout EXPORT_pole[19] = EXPORT_pole[19] | 0b00000110; // vzdycky zapnout nastaveni RTC podle GPS a zaroven informace, ze se jedna o interni verzi GPS desky // @/1 // // // // // // } else // prijaty rok neni v poradku { bitSet(statusy_LED,1); // LED2 rozsvitit } if (crc_check(CRC,rozlozeny_pole[12][2],rozlozeny_pole[12][3])) // kontrolni soucet souhlasi { if (faze_LED1 == true) bitClear(statusy_LED,0); // LED1 zhasni - LED1 bude blikat (v GxGGA vete se rozsveci, v GxRMC vete se zhasina) else bitSet(statusy_LED,0); // LED1 rozsvit - LED1 bude blikat (v GxGGA vete se zhasina, v GxRMC vete se rozsveci) if (rozlozeny_pole[2][0] == 'A' and rozlozeny_pole[12][0] == 'A') // znacky, ze je signal v poradku { // rozklad casu na hodiny, minuty a sekundy byte GMT_hod = 0; byte GMT_min = 0; byte GMT_sek = 0; GMT_hod = (rozlozeny_pole[1][0] - 48) * 10; // desitky hodin GMT_hod = GMT_hod + (rozlozeny_pole[1][1] - 48) ; // jednotky hodin GMT_min = (rozlozeny_pole[1][2] - 48) * 10; // desitky minut GMT_min = GMT_min + (rozlozeny_pole[1][3] - 48) ; // jednotky minut GMT_sek = (rozlozeny_pole[1][4] - 48) * 10; // desitky sekund GMT_sek = GMT_sek + (rozlozeny_pole[1][5] - 48) ; // jednotky sekund // rozklad datumu na dny, mesice a roky byte GMT_den = 0; byte GMT_mes = 0; byte GMT_rok = 0; GMT_den = (rozlozeny_pole[9][0] - 48) * 10; // desitky dni GMT_den = GMT_den + (rozlozeny_pole[9][1] - 48) ; // jednotky dni GMT_mes = (rozlozeny_pole[9][2] - 48) * 10; // desitky mesicu GMT_mes = GMT_mes + (rozlozeny_pole[9][3] - 48) ; // jednotky mesicu GMT_rok = (rozlozeny_pole[9][4] - 48) * 10; // desitky roku GMT_rok = GMT_rok + (rozlozeny_pole[9][5] - 48) ; // jednotky roku // prepocet zemepisne sirky unsigned long geoLAT = 0; unsigned long pomprom_geomin; geoLAT = (rozlozeny_pole[3][0] - 48) * 10000000UL; // desitky stupnu geoLAT = geoLAT + (rozlozeny_pole[3][1] - 48) * 1000000UL; // jednotky stupnu pomprom_geomin = (rozlozeny_pole[3][2] - 48) * 1000000UL; // desitky minut pomprom_geomin = pomprom_geomin + (rozlozeny_pole[3][3] - 48) * 100000UL; // jednotky minut // rozlozeny_pole[3][4] = desetinna tecka pomprom_geomin = pomprom_geomin + (rozlozeny_pole[3][5] - 48) * 10000UL; // desetiny minut pomprom_geomin = pomprom_geomin + (rozlozeny_pole[3][6] - 48) * 1000UL; // setiny minut pomprom_geomin = pomprom_geomin + (rozlozeny_pole[3][7] - 48) * 100UL; // tisiciny minut pomprom_geomin = pomprom_geomin + (rozlozeny_pole[3][8] - 48) * 10UL; // desettisiciny minut pomprom_geomin = pomprom_geomin + (rozlozeny_pole[3][9] - 48) * 1UL; // stotisiciny minut pomprom_geomin = pomprom_geomin / 6UL; geoLAT = geoLAT + pomprom_geomin; if (rozlozeny_pole[4][0] == 'N') geoLAT =geoLAT + 90000000UL; else if (rozlozeny_pole[4][0] != 'S') geoLAT = 0xFFFFFFFFUL; // prepocet zemepisne delky unsigned long geoLON = 0; geoLON = (rozlozeny_pole[5][0] - 48) * 100000000UL; // stovky stupnu geoLON = geoLON + (rozlozeny_pole[5][1] - 48) * 10000000UL; // desitky stupnu geoLON = geoLON + (rozlozeny_pole[5][2] - 48) * 1000000UL; // jednotky stupnu pomprom_geomin = (rozlozeny_pole[5][3] - 48) * 1000000UL; // desitky minut pomprom_geomin = pomprom_geomin + (rozlozeny_pole[5][4] - 48) * 100000UL; // jednotky minut // rozlozeny_pole[5][5] = desetinna tecka pomprom_geomin = pomprom_geomin + (rozlozeny_pole[5][6] - 48) * 10000UL; // desetiny minut pomprom_geomin = pomprom_geomin + (rozlozeny_pole[5][7] - 48) * 1000UL; // setiny minut pomprom_geomin = pomprom_geomin + (rozlozeny_pole[5][8] - 48) * 100UL; // tisiciny minut pomprom_geomin = pomprom_geomin + (rozlozeny_pole[5][9] - 48) * 10UL; // desettisiciny minut pomprom_geomin = pomprom_geomin + (rozlozeny_pole[5][10] - 48) * 1UL; // stotisiciny minut pomprom_geomin = pomprom_geomin / 6UL; geoLON = geoLON + pomprom_geomin; if (rozlozeny_pole[6][0] == 'E') geoLON =geoLON + 180000000UL; else if (rozlozeny_pole[6][0] != 'W') geoLON = 0xFFFFFFFFUL; // klouzavy prumer souradnic z 10 vzorku klouzak_LAT[ukazatel_klouzaku] = geoLAT; klouzak_LON[ukazatel_klouzaku] = geoLON; ukazatel_klouzaku ++; if (ukazatel_klouzaku == 10) { ukazatel_klouzaku = 0; pripraveno = true; EXPORT_pole[19] = EXPORT_pole[19] | 0b00000001; // pole pro klouzaky bylo zaplneno a muze se stahovat } if (pripraveno == true) { unsigned long suma_LAT = 0; unsigned long suma_LON = 0; for (byte i = 0 ; i < 10 ; i ++) { suma_LAT = suma_LAT + klouzak_LAT[i]; suma_LON = suma_LON + klouzak_LON[i]; } unsigned long prumer_LAT = suma_LAT / 10; unsigned long prumer_LON = suma_LON / 10; EXPORT_pole[0] = (prumer_LAT >> 24) & 0xFF; EXPORT_pole[1] = (prumer_LAT >> 16) & 0xFF; EXPORT_pole[2] = (prumer_LAT >> 8) & 0xFF; EXPORT_pole[3] = prumer_LAT & 0xFF; EXPORT_pole[4] = (prumer_LON >> 24) & 0xFF; EXPORT_pole[5] = (prumer_LON >> 16) & 0xFF; EXPORT_pole[6] = (prumer_LON >> 8) & 0xFF; EXPORT_pole[7] = prumer_LON & 0xFF; OK_byte ++; if (OK_byte > 100) OK_byte = 100; EXPORT_pole[8] = OK_byte ; // kolik vzorku proslo v rade bez problemu (maximalni hodnota je 100, dal se nepocita) } EXPORT_pole[9] = GMT_rok; EXPORT_pole[10] = GMT_mes; EXPORT_pole[11] = GMT_den; EXPORT_pole[12] = GMT_hod; EXPORT_pole[13] = GMT_min; EXPORT_pole[14] = GMT_sek; } else // spatny signal, nebo nejake varovani ve vete { OK_byte = 0; // nulovani poctu bezchybnych prijmu v rade for (byte i = 0; i < 10 ; i++) // vsechny klouzaky se nuluji { klouzak_LAT[ukazatel_klouzaku] = 0xFFFFFFFF; klouzak_LON[ukazatel_klouzaku] = 0xFFFFFFFF; } pripraveno = false; bitSet(statusy_LED,2); // LED3 rozsvitit EXPORT_pole[0] = 255; // LAT nastavit mimo rozsah (Err) EXPORT_pole[1] = 255; EXPORT_pole[2] = 255; EXPORT_pole[3] = 255; EXPORT_pole[4] = 255; // LON nastavit mimo rozsah (Err) EXPORT_pole[5] = 255; EXPORT_pole[6] = 255; EXPORT_pole[7] = 255; EXPORT_pole[8] = 0; EXPORT_pole[15] = 255; // ALT nastavit mimo rozsah (Err) EXPORT_pole[16] = 255; EXPORT_pole[17] = 0; // pocet satelitu EXPORT_pole[18] = 255; // HDoP (Err) EXPORT_pole[19] = EXPORT_pole[19] & 0b11111110; // status maze informaci o zaplnenem poli klouzaku } } else // nesouhlasi CRC { OK_byte = 0; // nulovani poctu bezchybnych prijmu v rade EXPORT_pole[8] = 0; } } //---------------------------------------------- //---------------------------------------------- // porovnani prijateho hexadecimalniho kodu na konci radky s vypoctem kodem boolean crc_check(byte CRC_bajt, char HigHalf, char LoHalf) { byte CRC_pul = CRC_bajt >> 4; // horni pulka bajtu char pomprom; if (CRC_pul > 9) pomprom = 65 + CRC_pul - 10; else pomprom = 48 + CRC_pul; if (pomprom != HigHalf) return false; // porovnava se s prvnim prijatym HEXA znakem CRC_pul = CRC_bajt & 0b00001111; // spodni pulka if (CRC_pul > 9) pomprom = 65 + CRC_pul - 10; else pomprom = 48 + CRC_pul; if (pomprom != LoHalf) return false; // porovnava se s druhym prijatym HEXA znakem else return true; } //---------------------------------------------- //---------------------------------------------- // vsechny nepotrebne NMEA zpravy se vypnou, zustanou jen GxGGA a GxRMC void GPS_setup(void) { Serial.flush(); Serial.write(zacatek_prikazu, 6); Serial.write(vypni_XXXX4, 5); LED3(false); delay(1500); Serial.write(zacatek_prikazu, 6); Serial.write(vypni_GxGLL, 5); LED1(true); delay(1500); Serial.write(zacatek_prikazu, 6); Serial.write(vypni_GxGSA, 5); LED2(true); delay(1500); Serial.write(zacatek_prikazu, 6); Serial.write(vypni_GxGSV, 5); LED3(true); delay(1500); Serial.write(zacatek_prikazu, 6); Serial.write(vypni_GxVTG, 5); LED1(false); delay(1500); Serial.write(zacatek_prikazu, 6); Serial.write(vypni_GxGRS, 5); LED2(false); delay(1500); Serial.write(zacatek_prikazu, 6); Serial.write(vypni_GxGST, 5); LED3(false); delay(1500); Serial.write(zacatek_prikazu, 6); Serial.write(vypni_GxZDA, 5); LED1(true); delay(1500); Serial.write(zacatek_prikazu, 6); Serial.write(vypni_GxGBS, 5); LED2(true); delay(1500); Serial.write(zacatek_prikazu, 6); Serial.write(vypni_GxDTM, 5); LED3(true); delay(1500); Serial.write(zacatek_prikazu, 6); Serial.write(vypni_GxGNS, 5); LED1(false); delay(1500); Serial.write(zacatek_prikazu, 6); Serial.write(vypni_GxVLW, 5); LED2(false); delay(1500); Serial.write(zacatek_prikazu, 6); Serial.write(vypni_PUBX1, 5); LED3(false); delay(1500); Serial.write(zacatek_prikazu, 6); Serial.write(vypni_PUBX2, 5); LED1(true); delay(1500); Serial.write(zacatek_prikazu, 6); Serial.write(vypni_PUBX3, 5); LED2(true); delay(1500); Serial.write(zacatek_prikazu, 6); Serial.write(vypni_XXXX1, 5); LED3(true); delay(1500); Serial.write(zacatek_prikazu, 6); Serial.write(vypni_XXXX2, 5); LED1(false); delay(1500); Serial.write(zacatek_prikazu, 6); Serial.write(vypni_XXXX3, 5); LED2(false); delay(1500); Serial.write(zacatek_prikazu, 6); Serial.write(vypni_XXXX4, 5); LED3(false); delay(1500); Serial.write(zacatek_prikazu, 6); Serial.write(zapni_GxGGA, 5); LED1(true); delay(1500); Serial.write(zacatek_prikazu, 6); Serial.write(zapni_GxRMC, 5); LED2(true); delay(1500); Serial.write(zacatek_prikazu, 6); Serial.write(vypni_GPTXT, 5); LED3(true); delay(1500); Serial.write(jeden_Hz , 22); LED1(false); delay(1500); Serial.write(SAVE_EEPROM , 21); LED2(false); delay(1500); Serial.write(hot_start , 12); } //---------------------------------------------- //---------------------------------------------- void LED1(boolean stav) // rozsvecovani a zhasinani LED { stav_LED_1 = stav; if (stav == true) digitalWrite(pin_LED1, HIGH); else digitalWrite(pin_LED1, LOW); } //---------------------------------------------- //---------------------------------------------- void LED2(boolean stav) { stav_LED_2 = stav; if (stav == true) digitalWrite(pin_LED2, HIGH); else digitalWrite(pin_LED2, LOW); } //---------------------------------------------- //---------------------------------------------- void LED3(boolean stav) { stav_LED_3 = stav; if (stav == true) digitalWrite(pin_LED3, HIGH); else digitalWrite(pin_LED3, LOW); } //---------------------------------------------- //---------------------------------------------- void obsluha_LED(void) { while (digitalRead(pin_setup) == LOW) // servisni funkce, ktera by mela kdykoliv rozblikat vsechny LED frekvenci 1Hz { if ((millis() % 1000) < 500) // pravidelne blikani vsech LED s periodou 1s { digitalWrite(pin_LED1, LOW); digitalWrite(pin_LED2, LOW); digitalWrite(pin_LED3, LOW); } else { digitalWrite(pin_LED1, HIGH); digitalWrite(pin_LED2, HIGH); digitalWrite(pin_LED3, HIGH); } if ((millis() - cas_prvniho_cyklu) > 5000) // po 5 sekundach spojeni servisniho pinu do LOW se memi faze LED1 { if (faze_LED1 == true) // zmena faze LED1 se zapisem noveho stavu do EEPROM { faze_LED1 = false; EEPROM.write(0,0); } else { faze_LED1 = true; EEPROM.write(0,1); } for (byte i = 0; i < 10 ; i++) // rychle zablikani LEDl jako informace o zmene stavu { digitalWrite(pin_LED1, LOW); delay(50); digitalWrite(pin_LED1, HIGH); delay(50); } cas_prvniho_cyklu = millis(); // dalsi prepnuti stavu je mozne az po 5 sekundach } } cas_prvniho_cyklu = millis(); // pri rozepnutem servisnim pinu se zaznamenava cas, ze ktereho se pak urcuje doba trvani sepnuti pinu if (zhasnuto == false) // obsluha LED je povolena { LED1(bitRead(statusy_LED,0)); LED2(bitRead(statusy_LED,1)); if (bitRead(statusy_LED,5) == 0) // bit5 obsahuje informaci o blikani 3. LED { LED3(bitRead(statusy_LED,2)); // kdyz blikat nema, tak je trvale rozsvicena, nebo zhasnuta podle stavu bitu 2 } else // kdyz blikat ma, prepina se s globalni promennou 'blik3' { if (blik3 == true) { digitalWrite(pin_LED3, LOW); blik3 = false; } else { digitalWrite(pin_LED3, HIGH); blik3 = true; } } } } //---------------------------------------------- //---------------------------------------------- // Prisla zadost o data pres I2C, podle kodu prikazu se odeslou pozadovane bloky dat (POZOR! buffer I2C zvlada maximalne 32 Bajtu) void requestEvent(void) { if (prikaz_I2C >= 50 and prikaz_I2C < 60) // pro prikazy 50 az 55 se odesilaji 15 znaku dlouhe casti vety GxRMC { for (byte snd = (prikaz_I2C-50) * 15 ; snd < ((prikaz_I2C-50) * 15)+15 ; snd ++) { Wire.write(veta_RMC[snd]); } prikaz_I2C = 0; } if (prikaz_I2C >= 60 and prikaz_I2C < 70) // pro prikazy 60 az 65 se odesilaji 15 znaku dlouhe casti vety GxGGA { for (byte snd = (prikaz_I2C-60) * 15 ; snd < ((prikaz_I2C-60) * 15)+15 ; snd ++) { Wire.write(veta_GGA[snd]); } prikaz_I2C = 0; } if (prikaz_I2C == 70) // pro prikaz 70 vrati v textovem formatu verzi programu ("yyyy-mm-dd..INT") { for (byte snd = 0 ; snd < 15 ; snd ++) { Wire.write(verzeSW[snd]); } prikaz_I2C = 0; } if (prikaz_I2C == 10) // prikaz 10 odesle 20 bajtu velke pole zpracovanych dat { Wire.write(EXPORT_pole, 20); prikaz_I2C = 0; } if (prikaz_I2C == 20 ) // prikaz 20 se zpracovava v RecieveEventu. { prikaz_I2C = 0; // tady se jen nastavi znacka, ze byl vykonan } if (prikaz_I2C == 21 ) // prikaz 21 se zpracovava v RecieveEventu. { prikaz_I2C = 0; // tady se jen nastavi znacka, ze byl vykonan } } //---------------------------------------------- //---------------------------------------------- // zjisteni kodu prikazu ktery prisel pres I2C void receiveEvent(int prijato) { prikaz_I2C = prijato; // uplne k nicemu, protoze na dalsich radkach se 'prikaz_I2C' stejne hned prepise. // zabrani ale kompilatoru ve varovani pro nepouzity parametr 'prijato' if (Wire.available()) { prikaz_I2C = Wire.read(); } else { prikaz_I2C = 0; } if (prikaz_I2C == 20 ) // zhasnuti vsech LED pred merenim jasu { pamet_LED_1 = stav_LED_1; pamet_LED_2 = stav_LED_2; pamet_LED_3 = stav_LED_3; LED1(false); LED2(false); LED3(false); zhasnuto = true; prikaz_I2C = 0; } if (prikaz_I2C == 21 ) // obnoveni stavu vsech LED po mereni jasu { LED1(pamet_LED_1); LED2(pamet_LED_2); LED3(pamet_LED_3); zhasnuto = false; prikaz_I2C = 0; } if (prikaz_I2C == 22 ) // HOT restart GPS modulu { LED1(false); LED2(false); LED3(false); delay(1500); LED1(true); LED2(true); LED3(true); Serial.write(hot_start , 12); delay(1500); } } //----------------------------------------------