From aa9cbf7b85308d91ce775d71c36cdd96b5740529 Mon Sep 17 00:00:00 2001 From: Charlie Jaewoong Mun <40027494+happybono@users.noreply.github.com> Date: Sun, 24 Nov 2019 14:03:57 +0900 Subject: [PATCH] Add files via upload --- Ch12-1_aircleaner.ino | 115 ++ .../FinedustMonitorWithGPS.ino | 115 ++ FinedustMonitorWithGPS/RunningMedian.cpp | 123 ++ FinedustMonitorWithGPS/RunningMedian.h | 52 + FinedustMonitorWithGPS/dust.ino | 42 + FinedustMonitorWithGPS/oled.ino | 79 + FinedustMonitorWithGPS/server.ino | 104 ++ FinedustMonitorWithGPS/temperature.ino | 29 + FinedustMonitorWithGPS/wifi.ino | 29 + .../examples/BasicExample/BasicExample.ino | 91 + .../examples/DeviceExample/DeviceExample.ino | 92 + .../examples/FullExample/FullExample.ino | 159 ++ .../examples/KitchenSink/KitchenSink.ino | 191 ++ .../SatElevTracker/SatElevTracker.ino | 150 ++ .../sample_satellite_elevation_log.txt | 1606 +++++++++++++++++ .../SatelliteTracker/SatelliteTracker.ino | 149 ++ .../UsingCustomFields/UsingCustomFields.ino | 69 + TinyGPSPlus-master/keywords.txt | 71 + TinyGPSPlus-master/library.json | 22 + TinyGPSPlus-master/library.properties | 9 + TinyGPSPlus-master/src/TinyGPS++.cpp | 503 ++++++ TinyGPSPlus-master/src/TinyGPS++.h | 278 +++ 22 files changed, 4078 insertions(+) create mode 100644 Ch12-1_aircleaner.ino create mode 100644 FinedustMonitorWithGPS/FinedustMonitorWithGPS.ino create mode 100644 FinedustMonitorWithGPS/RunningMedian.cpp create mode 100644 FinedustMonitorWithGPS/RunningMedian.h create mode 100644 FinedustMonitorWithGPS/dust.ino create mode 100644 FinedustMonitorWithGPS/oled.ino create mode 100644 FinedustMonitorWithGPS/server.ino create mode 100644 FinedustMonitorWithGPS/temperature.ino create mode 100644 FinedustMonitorWithGPS/wifi.ino create mode 100644 TinyGPSPlus-master/examples/BasicExample/BasicExample.ino create mode 100644 TinyGPSPlus-master/examples/DeviceExample/DeviceExample.ino create mode 100644 TinyGPSPlus-master/examples/FullExample/FullExample.ino create mode 100644 TinyGPSPlus-master/examples/KitchenSink/KitchenSink.ino create mode 100644 TinyGPSPlus-master/examples/SatElevTracker/SatElevTracker.ino create mode 100644 TinyGPSPlus-master/examples/SatElevTracker/sample_satellite_elevation_log.txt create mode 100644 TinyGPSPlus-master/examples/SatelliteTracker/SatelliteTracker.ino create mode 100644 TinyGPSPlus-master/examples/UsingCustomFields/UsingCustomFields.ino create mode 100644 TinyGPSPlus-master/keywords.txt create mode 100644 TinyGPSPlus-master/library.json create mode 100644 TinyGPSPlus-master/library.properties create mode 100644 TinyGPSPlus-master/src/TinyGPS++.cpp create mode 100644 TinyGPSPlus-master/src/TinyGPS++.h diff --git a/Ch12-1_aircleaner.ino b/Ch12-1_aircleaner.ino new file mode 100644 index 0000000..2560f7f --- /dev/null +++ b/Ch12-1_aircleaner.ino @@ -0,0 +1,115 @@ +#include +#include + +#define AIR_PIN 14 + +const char* ssid = " "; +const char* password = " "; +const char* mqtt_server = "13.209.250.27"; +const char* topic = "speak_topic"; + +WiFiClient espClient; +PubSubClient client(espClient); + +long lastMsg = 0; +char msg[50]; +int value = 0; + +void setup_wifi() { + delay(10); + Serial.println(); + Serial.print("Connecting to "); + Serial.println(ssid); + + WiFi.begin(ssid, password); + + while (WiFi.status() != WL_CONNECTED) { + delay(500); + Serial.print("."); + } + + randomSeed(micros()); + + Serial.println(""); + Serial.println("WiFi connected"); + Serial.println("IP address: "); + Serial.println(WiFi.localIP()); +} + +void callback(char* topic, byte* payload, unsigned int length) { + String final_string; + + Serial.print("Message arrived ["); + Serial.print(topic); + Serial.print("] "); + for (int i = 0; i < length; i++) { + Serial.print((char)payload[i]); + final_string += (char)payload[i]; + } + + Serial.println(); + Serial.println("-----------------------------------------"); + Serial.println(final_string); + // Switch on the LED if an 1 was received as first character + // fix it - magiceco// + if (final_string == "1") { + //digitalWrite(BUILTIN_LED, LOW); // Turn the LED on (Note that LOW is the voltage level + digitalWrite(AIR_PIN, LOW); + // but actually the LED is on; this is because + // it is acive low on the ESP-01) + } + else if(final_string == "2") { + //digitalWrite(BUILTIN_LED, HIGH);// Turn the LED off by making the voltage HIGH + digitalWrite(AIR_PIN, HIGH); + } + +} + +void reconnect() { + // Loop until we're reconnected + while (!client.connected()) { + Serial.print("Attempting MQTT connection..."); + // Create a random client ID + String clientId = "ESP8266Client-"; + clientId += String(random(0xffff), HEX); + // Attempt to connect + if (client.connect(clientId.c_str())) { + Serial.println("connected"); + // Once connected, publish an announcement... + client.publish("outTopic", "hello world"); + // ... and resubscribe + client.subscribe(topic); + } else { + Serial.print("failed, rc="); + Serial.print(client.state()); + Serial.println(" try again in 5 seconds"); + // Wait 5 seconds before retrying + delay(5000); + } + } +} + +void setup() { + Serial.begin(115200); + pinMode(AIR_PIN, OUTPUT); + digitalWrite(AIR_PIN, LOW); + setup_wifi(); + client.setServer(mqtt_server, 1883); + client.setCallback(callback); +} + +void loop() { + if (!client.connected()) { + reconnect(); + } + client.loop(); + long now = millis(); + if (now - lastMsg > 2000) { + lastMsg = now; + ++value; + snprintf (msg, 75, "hello world #%ld", value); + Serial.print("Publish message: "); + Serial.println(msg); + client.publish("outTopic", msg); + } +} diff --git a/FinedustMonitorWithGPS/FinedustMonitorWithGPS.ino b/FinedustMonitorWithGPS/FinedustMonitorWithGPS.ino new file mode 100644 index 0000000..8199231 --- /dev/null +++ b/FinedustMonitorWithGPS/FinedustMonitorWithGPS.ino @@ -0,0 +1,115 @@ +// +// FILE: FinedustMonitorWithGPS.ino +// AUTHOR: Jaewoong Mun (happybono@outlook.com) +// CREATED: November 19, 2019 +// +// Released to the public domain +// + +#include +#include +#include "RunningMedian.h" + +RunningMedian pm25s = RunningMedian(19); +RunningMedian pm10s = RunningMedian(19); + +char* ssid = "HappyBono-GalaxyNote10+"; +char* password = "06020688"; +String api_key = "*2446388DA8B643EE7AA752DB64A42926444CDE2A"; +#define PLAIVE_SERVER_ENABLE +//#define THINGSPEAK_SERVER_ENABLE + +boolean wifi_ready; +float map_x, map_y; +String s_map_x, s_map_y; + +TinyGPSPlus gps; +SoftwareSerial ss(12, 13); +SoftwareSerial dust(D1, D0, false, 256); + +void got_dust(int pm25, int pm10) { //formula for dust sensor just use!! + pm25 /= 10; + pm10 /= 10; + pm25s.add(pm25); + pm10s.add(pm10); + do_oled(pm25, pm10); //print pm25, pm10 in oled +} + + +//서버에 보내기(send server) +void do_interval() { + if (wifi_ready){ +#ifdef PLAIVE_SERVER_ENABLE + do_server_plaive(api_key,int(pm25s.getMedian()), int(pm10s.getMedian()), get_temperature(), s_map_x, s_map_y); +#else + #ifdef THINGSPEAK_SERVER_ENABLE + do_server_thingspeak(api_key,int(pm25s.getMedian()), int(pm10s.getMedian()),get_temperature()); + #else + do_server_default(api_key,int(pm25s.getMedian()), int(pm10s.getMedian()),get_temperature()); + #endif +#endif + } + //wifi is ok +} + +unsigned long mark = 0; +boolean got_interval = false; + +//초기 세팅 +void setup() { + Serial.begin(115200); + dust.begin(9600); + ss.begin(9600); + setup_oled();//oled setting + wifi_ready = connect_ap(ssid, password); //wifi connection 유무 + + if (!wifi_ready) nowifi_oled();//wifi no connection + delay(5000); + Serial.println("\nDust Sensor Box V1.2, 2019/11/24 HappyBono"); +} + +//아두이노가 반복적으로 작동하는 부분 +void loop() { + if(ss.available()<=0){ + Serial.println("SIGNAL STATUS : WEAK"); + s_map_x = String(map_x,6); + s_map_y = String(map_y,6); + } + else{ + while(ss.available()>0){ + Serial.println("SIGNAL STATUS : GREAT"); + if(gps.encode(ss.read())){ + Serial.println("GPS READ"); + Serial.println(ss.read()); + if (gps.location.isValid()){ + Serial.println("LOCATION : GREAT"); + map_x = gps.location.lat(); + map_y = gps.location.lng(); + Serial.println(String(map_x,6)); + Serial.println(String(map_y,6)); + } + } + s_map_x = String(map_x,6); + s_map_y = String(map_y,6); + yield(); + } + } + while (dust.available() > 0) { + do_dust(dust.read(), got_dust); + yield(); //loop 에서 while 문을 사용하는 경우 yield 를 포함해주어야 합니다. + } + //Serial.println(map_x); + //Serial.print("pm 25 : "); + //Serial.println(int(pm25s.getMedian())); + + if (millis() > mark) {//one minute(60000) interval + mark = millis() + 60000; + got_interval = true; + } + + if (got_interval) { + got_interval = false; + do_interval(); + } + yield(); +} diff --git a/FinedustMonitorWithGPS/RunningMedian.cpp b/FinedustMonitorWithGPS/RunningMedian.cpp new file mode 100644 index 0000000..ace1d69 --- /dev/null +++ b/FinedustMonitorWithGPS/RunningMedian.cpp @@ -0,0 +1,123 @@ + +// FILE: RunningMedian.cpp +// AUTHOR: Rob dot Tillaart at gmail dot com +// VERSION: 0.1.04 +// PURPOSE: RunningMedian library for Arduino + +#include "RunningMedian.h" + +RunningMedian::RunningMedian(uint8_t size) +{ + _size = constrain(size, MEDIAN_MIN_SIZE, MEDIAN_MAX_SIZE); + // array's could be allocated by malloc here, + // but using fixed size is easier. + clear(); +} + +RunningMedian::RunningMedian() +{ + _size = MEDIAN_DEF_SIZE; + clear(); +} +// resets all counters +void RunningMedian::clear() +{ + _cnt = 0; + _idx = 0; + _sorted = false; +} + +// adds a new value to the data-set +// or overwrites the oldest if full. +void RunningMedian::add(float value) +{ + _ar[_idx++] = value; + if (_idx >= _size) _idx = 0; // wrap around + if (_cnt < _size) _cnt++; + _sorted = false; +} + +float RunningMedian::getMedian() +{ + if (_cnt > 0) + { + if (_sorted == false) sort(); + return _as[_cnt/2]; + } + return NAN; +} + +#ifdef RUNNING_MEDIAN_ALL +float RunningMedian::getHighest() +{ + if (_cnt > 0) + { + if (_sorted == false) sort(); + return _as[_cnt-1]; + } + return NAN; +} + +float RunningMedian::getLowest() +{ + if (_cnt > 0) + { + if (_sorted == false) sort(); + return _as[0]; + } + return NAN; +} + +float RunningMedian::getAverage() +{ + if (_cnt > 0) + { + float sum = 0; + for (uint8_t i=0; i< _cnt; i++) sum += _ar[i]; + return sum / _cnt; + } + return NAN; +} + +float RunningMedian::getAverage(uint8_t nMedians) +{ + if ((_cnt > 0) && (nMedians > 0)) + { + if (_cnt < nMedians) nMedians = _cnt; // when filling the array for first time + uint8_t start = ((_cnt - nMedians)/2); + uint8_t stop = start + nMedians; + sort(); + float sum = 0; + for (uint8_t i = start; i < stop; i++) sum += _as[i]; + return sum / nMedians; + } + return NAN; +} + +uint8_t RunningMedian::getSize() { return _size; }; + +uint8_t RunningMedian::getCount() { return _cnt; }; +#endif + +void RunningMedian::sort() +{ + // copy + for (uint8_t i=0; i< _cnt; i++) _as[i] = _ar[i]; + + // sort all + for (uint8_t i=0; i< _cnt-1; i++) + { + uint8_t m = i; + for (uint8_t j=i+1; j< _cnt; j++) + { + if (_as[j] < _as[m]) m = j; + } + if (m != i) + { + float t = _as[m]; // PATCH from 0.1.05 (was long) + _as[m] = _as[i]; + _as[i] = t; + } + } + _sorted = true; +} diff --git a/FinedustMonitorWithGPS/RunningMedian.h b/FinedustMonitorWithGPS/RunningMedian.h new file mode 100644 index 0000000..49d9aef --- /dev/null +++ b/FinedustMonitorWithGPS/RunningMedian.h @@ -0,0 +1,52 @@ +#ifndef RunningMedian_h +#define RunningMedian_h + +#if defined(ARDUINO) && ARDUINO >= 100 +#include "Arduino.h" +#else +#include "WProgram.h" +#endif + +#include + +#define RUNNING_MEDIAN_VERSION "0.1.04" + +// should at least be 5 to be practical +#define MEDIAN_MIN_SIZE 1 +#define MEDIAN_MAX_SIZE 19 +#define MEDIAN_DEF_SIZE 5 + +// conditional compile to minimize lib +#define RUNNING_MEDIAN_ALL + +class RunningMedian +{ +public: + RunningMedian(uint8_t); + RunningMedian(); + + void clear(); + void add(float); + float getMedian(); + +#ifdef RUNNING_MEDIAN_ALL + float getAverage(); + float getAverage(uint8_t); + float getHighest(); + float getLowest(); + + uint8_t getSize(); + uint8_t getCount(); +#endif + +protected: + boolean _sorted; + uint8_t _size; + uint8_t _cnt; + uint8_t _idx; + float _ar[MEDIAN_MAX_SIZE]; + float _as[MEDIAN_MAX_SIZE]; + void sort(); +}; + +#endif diff --git a/FinedustMonitorWithGPS/dust.ino b/FinedustMonitorWithGPS/dust.ino new file mode 100644 index 0000000..58d160c --- /dev/null +++ b/FinedustMonitorWithGPS/dust.ino @@ -0,0 +1,42 @@ +// +// FILE: dust.ino +// AUTHOR: Jaewoong Mun (happybono@outlook.com) +// CREATED: November 19, 2019 +// +// Released to the public domain +// + +int stat = 1; +int cnt = 0; +char buf[10]; + +void do_dust(char c, void (*function)(int, int)) { + //Serial.print("stat="+ String(stat) +", "+ "cnt="+ String(cnt) +" "); + //Serial.print(c, HEX); + //Serial.println(" "); + + if (stat == 1) { + if (c == 0xAA) stat = 2; + } else + if (stat == 2) { + if (c == 0xC0) stat =3; + else stat = 1; + } else + if (stat == 3) { + buf[cnt++] = c; + if (cnt == 7) stat = 4; + } else + if (stat == 4) { + if (c == 0xAB) { + //check checusum + stat = 1; + } + else { + //Serial.println("Eh? wrong tailer"); + } + cnt = 0; + int pm25 = buf[0] + 256*buf[1]; + int pm10 = buf[2] + 256*buf[3]; + function(pm25, pm10); + } +} diff --git a/FinedustMonitorWithGPS/oled.ino b/FinedustMonitorWithGPS/oled.ino new file mode 100644 index 0000000..d50a061 --- /dev/null +++ b/FinedustMonitorWithGPS/oled.ino @@ -0,0 +1,79 @@ +// +// FILE: oled.ino +// AUTHOR: Jaewoong Mun (happybono@outlook.com) +// CREATED: November 19, 2019 +// +// Released to the public domain +// + +#include "SSD1306.h" + +SSD1306 display(0x3c, D3, D2); //Data, Clock + +void setup_oled() { + display.init(); + display.clear(); + display.flipScreenVertically(); + display.setFont(ArialMT_Plain_10); + display.drawString(0,13, "initialize OLED..."); + display.display(); +} + +void wifi_oled(int cnt) { + display.clear(); + display.setFont(ArialMT_Plain_10); + display.drawString(0,13, "waiting wifi..."); + display.drawString(0,24, String("ssid= ")+String(ssid)); + display.drawString(0,35, String("password= ")+ String(password)); + display.drawString(0,53, String(cnt)); + display.display(); +} + +void nowifi_oled() { + display.clear(); + display.setFont(ArialMT_Plain_10); + display.drawString(0,0, "NO WIFI: skip wifi..."); + display.drawString(0,13, "waiting dust value..."); + display.drawString(0,24, String("Check dust sensor,")); + display.drawString(0,35, String("if you see this message.")); + display.display(); +} + +bool m = false; +int v1[128], v2[128]; + +void do_oled(int pm25, int pm10) { + String m1; + display.clear(); + display.setFont(ArialMT_Plain_10); + if (m) m1 = "*"; else m1 = " "; + m = m?false:true; + display.drawString(123,0, m1); + if (!wifi_ready) display.drawString(0,53, "No WiFi"); + display.drawString(0,0, "pm 2.5"); + display.drawString(64,0, "pm 10.0"); + display.setFont(ArialMT_Plain_24); + display.drawString(0,13, String(pm25)); + display.drawString(64,13, String(pm10)); + + int vmax = 0; + int vmin = 999; + for (int i=0; i<128; i++) { + if (i<127) { v1[i] = v1[i+1]; v2[i] = v2[i+1]; } + else { v1[i] = pm25; v2[i] = pm10; } + vmax = vmax>v2[i]?vmax:v2[i]; // assume that v2 > v1 always + vmin = vminYSPAN?float(YSPAN)/range:1.; + int delta = vmax>YSPAN?vmin:0; + + for (int i=0; i< 128; i++) { + if (v1[i] && v2[i]) { + display.drawVerticalLine(i, 63-int(scale*(v2[i]-delta)), !int(scale*(v2[i]-v1[i]))?1:int(scale*(v2[i]-v1[i]))); + } + } + display.display(); +} diff --git a/FinedustMonitorWithGPS/server.ino b/FinedustMonitorWithGPS/server.ino new file mode 100644 index 0000000..2d3318f --- /dev/null +++ b/FinedustMonitorWithGPS/server.ino @@ -0,0 +1,104 @@ +// +// FILE: server.ino +// AUTHOR: Jaewoong Mun (happybono@outlook.com) +// CREATED: November 19, 2019 +// +// Released to the public domain +// + +const char* host_plaive = "data.plaive.10make.com"; +const char* host_thingspeak = "api.thingspeak.com"; +const char* host_default = "finedustapi.10make.com"; + +const int httpPort = 80; + +#include +#include +WiFiClient client; +String data; +String contentType; + +void do_server_plaive(String api_key,int pm25, int pm10,float temperature,String map_x, String map_y) { + + data = "api_key="+ String(api_key) + "&field1=" + String(pm25) + "&field2=" + String(pm10) + "&field3=" + String(temperature) + "&field4=" + String(map_x) + "&field5=" + String(map_y); + //contentType= "application/x-www-form-urlencoded"; + + //서버 통신 공식 client.println을 써야한다. + if(client.connect(host_plaive,httpPort)){ + Serial.println("connected"); + client.print("GET /insert.php?"); + client.print(data); + client.println(" HTTP/1.1"); + client.println("Host: " + String(host_plaive)); // SERVER ADDRESS HERE TOO + client.println("Cache-Control: no-cache"); + //client.println("Content-Type: application/xE-www-form-urlencoded"); + //client.print("Content-Length: "); + //client.println(data.length()); + client.println("Connection: close"); + client.println(); + //client.print(data); + } + + //서버 통신이 되지 않으면 + else{ + Serial.println("connection failed: "); + return; + } +} + +void do_server_thingspeak(String api_key,int pm25, int pm10,float temperature) { + + data = "api_key="+ String(api_key) + "&field1=" + String(pm25) + "&field2=" + String(pm10) + "&field3=" + String(temperature); + //contentType= "application/x-www-form-urlencoded"; + + //서버 통신 공식 client.println을 써야한다. + if(client.connect(host_thingspeak,httpPort)){ + Serial.println("connected"); + client.print("GET /update?"); + client.print(data); + client.println(" HTTP/1.1"); + client.println("Host: " + String(host_thingspeak)); // SERVER ADDRESS HERE TOO + client.println("Cache-Control: no-cache"); + + //client.println("Content-Type: application/xE-www-form-urlencoded"); + //client.print("Content-Length: "); + //client.println(data.length()); + client.println("Connection: close"); + client.println(); + + //client.print(data); + } + + //서버 통신이 되지 않으면 + else{ + Serial.println("connection failed: "); + return; + } +} + +void do_server_default(String api_key,int pm25, int pm10,float temperature) { + + data = "api_key="+ String(api_key) + "&pm25=" + String(pm25) + "&pm10=" + String(pm10) + "&temp=" + String(temperature); + contentType= "application/x-www-form-urlencoded"; + + //서버 통신 공식 client.println을 써야한다. + if(client.connect(host_default,httpPort)){ + Serial.println("connected"); + client.print("GET /insert.php?"); + client.print(data); + client.println(" HTTP/1.1"); + client.println("Host: " + String(host_default)); // SERVER ADDRESS HERE TOO + client.println("Content-Type: application/xE-www-form-urlencoded"); + client.print("Content-Length: "); + client.println(data.length()); + client.println("Connection: close"); + client.println(); + //client.print(data); + } + + //서버 통신이 되지 않으면 + else{ + Serial.println("Connection Failed: "); + return; + } +} diff --git a/FinedustMonitorWithGPS/temperature.ino b/FinedustMonitorWithGPS/temperature.ino new file mode 100644 index 0000000..194a8f5 --- /dev/null +++ b/FinedustMonitorWithGPS/temperature.ino @@ -0,0 +1,29 @@ +// +// FILE: temperature.ino +// AUTHOR: Jaewoong Mun (happybono@outlook.com) +// CREATED: November 19, 2019 +// +// Released to the public domain +// + +#include +#include + +#define ONE_WIRE_BUS D4 + +OneWire oneWire(ONE_WIRE_BUS); +DallasTemperature DS18B20(&oneWire); + +void setup_temperature() { + DS18B20.begin(); +} + +float get_temperature() { + float temp; + do { + DS18B20.requestTemperatures(); + temp = DS18B20.getTempCByIndex(0); + delay(100); + } while (temp == 85.0 || temp == (-127.0)); + return temp; +} diff --git a/FinedustMonitorWithGPS/wifi.ino b/FinedustMonitorWithGPS/wifi.ino new file mode 100644 index 0000000..1599656 --- /dev/null +++ b/FinedustMonitorWithGPS/wifi.ino @@ -0,0 +1,29 @@ +// +// FILE: wifi.ino +// AUTHOR: Jaewoong Mun (happybono@outlook.com) +// CREATED: November 19, 2019 +// +// Released to the public domain +// + +#include + +boolean connect_ap(char* ssid, char* password) { + int count = 60; //60번안에 wifi 연결하면 성공, 아니면 실패 + Serial.println(); + Serial.print("connecting to "); + Serial.println(ssid); + WiFi.begin(ssid, password); + while (WiFi.status() != WL_CONNECTED) { + delay(500); + Serial.print("."); + //wifi_oled(count); + if (!count--) { + Serial.print("\nNO WIFI"); + return(false); + } + } + Serial.print("\n Got WiFi, IP address: "); + Serial.println(WiFi.localIP()); + return(true); +} diff --git a/TinyGPSPlus-master/examples/BasicExample/BasicExample.ino b/TinyGPSPlus-master/examples/BasicExample/BasicExample.ino new file mode 100644 index 0000000..da62825 --- /dev/null +++ b/TinyGPSPlus-master/examples/BasicExample/BasicExample.ino @@ -0,0 +1,91 @@ +#include +/* + This sample sketch should be the first you try out when you are testing a TinyGPS++ + (TinyGPSPlus) installation. In normal use, you feed TinyGPS++ objects characters from + a serial NMEA GPS device, but this example uses static strings for simplicity. +*/ + +// A sample NMEA stream. +const char *gpsStream = + "$GPRMC,045103.000,A,3014.1984,N,09749.2872,W,0.67,161.46,030913,,,A*7C\r\n" + "$GPGGA,045104.000,3014.1985,N,09749.2873,W,1,09,1.2,211.6,M,-22.5,M,,0000*62\r\n" + "$GPRMC,045200.000,A,3014.3820,N,09748.9514,W,36.88,65.02,030913,,,A*77\r\n" + "$GPGGA,045201.000,3014.3864,N,09748.9411,W,1,10,1.2,200.8,M,-22.5,M,,0000*6C\r\n" + "$GPRMC,045251.000,A,3014.4275,N,09749.0626,W,0.51,217.94,030913,,,A*7D\r\n" + "$GPGGA,045252.000,3014.4273,N,09749.0628,W,1,09,1.3,206.9,M,-22.5,M,,0000*6F\r\n"; + +// The TinyGPS++ object +TinyGPSPlus gps; + +void setup() +{ + Serial.begin(115200); + + Serial.println(F("BasicExample.ino")); + Serial.println(F("Basic demonstration of TinyGPS++ (no device needed)")); + Serial.print(F("Testing TinyGPS++ library v. ")); Serial.println(TinyGPSPlus::libraryVersion()); + Serial.println(F("by Mikal Hart")); + Serial.println(); + + while (*gpsStream) + if (gps.encode(*gpsStream++)) + displayInfo(); + + Serial.println(); + Serial.println(F("Done.")); +} + +void loop() +{ +} + +void displayInfo() +{ + Serial.print(F("Location: ")); + if (gps.location.isValid()) + { + Serial.print(gps.location.lat(), 6); + Serial.print(F(",")); + Serial.print(gps.location.lng(), 6); + } + else + { + Serial.print(F("INVALID")); + } + + Serial.print(F(" Date/Time: ")); + if (gps.date.isValid()) + { + Serial.print(gps.date.month()); + Serial.print(F("/")); + Serial.print(gps.date.day()); + Serial.print(F("/")); + Serial.print(gps.date.year()); + } + else + { + Serial.print(F("INVALID")); + } + + Serial.print(F(" ")); + if (gps.time.isValid()) + { + if (gps.time.hour() < 10) Serial.print(F("0")); + Serial.print(gps.time.hour()); + Serial.print(F(":")); + if (gps.time.minute() < 10) Serial.print(F("0")); + Serial.print(gps.time.minute()); + Serial.print(F(":")); + if (gps.time.second() < 10) Serial.print(F("0")); + Serial.print(gps.time.second()); + Serial.print(F(".")); + if (gps.time.centisecond() < 10) Serial.print(F("0")); + Serial.print(gps.time.centisecond()); + } + else + { + Serial.print(F("INVALID")); + } + + Serial.println(); +} diff --git a/TinyGPSPlus-master/examples/DeviceExample/DeviceExample.ino b/TinyGPSPlus-master/examples/DeviceExample/DeviceExample.ino new file mode 100644 index 0000000..489c30b --- /dev/null +++ b/TinyGPSPlus-master/examples/DeviceExample/DeviceExample.ino @@ -0,0 +1,92 @@ +#include +#include +/* + This sample sketch demonstrates the normal use of a TinyGPS++ (TinyGPSPlus) object. + It requires the use of SoftwareSerial, and assumes that you have a + 4800-baud serial GPS device hooked up on pins 4(rx) and 3(tx). +*/ +static const int RXPin = 4, TXPin = 3; +static const uint32_t GPSBaud = 4800; + +// The TinyGPS++ object +TinyGPSPlus gps; + +// The serial connection to the GPS device +SoftwareSerial ss(RXPin, TXPin); + +void setup() +{ + Serial.begin(115200); + ss.begin(GPSBaud); + + Serial.println(F("DeviceExample.ino")); + Serial.println(F("A simple demonstration of TinyGPS++ with an attached GPS module")); + Serial.print(F("Testing TinyGPS++ library v. ")); Serial.println(TinyGPSPlus::libraryVersion()); + Serial.println(F("by Mikal Hart")); + Serial.println(); +} + +void loop() +{ + // This sketch displays information every time a new sentence is correctly encoded. + while (ss.available() > 0) + if (gps.encode(ss.read())) + displayInfo(); + + if (millis() > 5000 && gps.charsProcessed() < 10) + { + Serial.println(F("No GPS detected: check wiring.")); + while(true); + } +} + +void displayInfo() +{ + Serial.print(F("Location: ")); + if (gps.location.isValid()) + { + Serial.print(gps.location.lat(), 6); + Serial.print(F(",")); + Serial.print(gps.location.lng(), 6); + } + else + { + Serial.print(F("INVALID")); + } + + Serial.print(F(" Date/Time: ")); + if (gps.date.isValid()) + { + Serial.print(gps.date.month()); + Serial.print(F("/")); + Serial.print(gps.date.day()); + Serial.print(F("/")); + Serial.print(gps.date.year()); + } + else + { + Serial.print(F("INVALID")); + } + + Serial.print(F(" ")); + if (gps.time.isValid()) + { + if (gps.time.hour() < 10) Serial.print(F("0")); + Serial.print(gps.time.hour()); + Serial.print(F(":")); + if (gps.time.minute() < 10) Serial.print(F("0")); + Serial.print(gps.time.minute()); + Serial.print(F(":")); + if (gps.time.second() < 10) Serial.print(F("0")); + Serial.print(gps.time.second()); + Serial.print(F(".")); + if (gps.time.centisecond() < 10) Serial.print(F("0")); + Serial.print(gps.time.centisecond()); + } + else + { + Serial.print(F("INVALID")); + } + + Serial.println(); +} diff --git a/TinyGPSPlus-master/examples/FullExample/FullExample.ino b/TinyGPSPlus-master/examples/FullExample/FullExample.ino new file mode 100644 index 0000000..52d5c16 --- /dev/null +++ b/TinyGPSPlus-master/examples/FullExample/FullExample.ino @@ -0,0 +1,159 @@ +#include +#include +/* + This sample code demonstrates the normal use of a TinyGPS++ (TinyGPSPlus) object. + It requires the use of SoftwareSerial, and assumes that you have a + 4800-baud serial GPS device hooked up on pins 4(rx) and 3(tx). +*/ +static const int RXPin = 4, TXPin = 3; +static const uint32_t GPSBaud = 4800; + +// The TinyGPS++ object +TinyGPSPlus gps; + +// The serial connection to the GPS device +SoftwareSerial ss(RXPin, TXPin); + +void setup() +{ + Serial.begin(115200); + ss.begin(GPSBaud); + + Serial.println(F("FullExample.ino")); + Serial.println(F("An extensive example of many interesting TinyGPS++ features")); + Serial.print(F("Testing TinyGPS++ library v. ")); Serial.println(TinyGPSPlus::libraryVersion()); + Serial.println(F("by Mikal Hart")); + Serial.println(); + Serial.println(F("Sats HDOP Latitude Longitude Fix Date Time Date Alt Course Speed Card Distance Course Card Chars Sentences Checksum")); + Serial.println(F(" (deg) (deg) Age Age (m) --- from GPS ---- ---- to London ---- RX RX Fail")); + Serial.println(F("----------------------------------------------------------------------------------------------------------------------------------------")); +} + +void loop() +{ + static const double LONDON_LAT = 51.508131, LONDON_LON = -0.128002; + + printInt(gps.satellites.value(), gps.satellites.isValid(), 5); + printFloat(gps.hdop.hdop(), gps.hdop.isValid(), 6, 1); + printFloat(gps.location.lat(), gps.location.isValid(), 11, 6); + printFloat(gps.location.lng(), gps.location.isValid(), 12, 6); + printInt(gps.location.age(), gps.location.isValid(), 5); + printDateTime(gps.date, gps.time); + printFloat(gps.altitude.meters(), gps.altitude.isValid(), 7, 2); + printFloat(gps.course.deg(), gps.course.isValid(), 7, 2); + printFloat(gps.speed.kmph(), gps.speed.isValid(), 6, 2); + printStr(gps.course.isValid() ? TinyGPSPlus::cardinal(gps.course.deg()) : "*** ", 6); + + unsigned long distanceKmToLondon = + (unsigned long)TinyGPSPlus::distanceBetween( + gps.location.lat(), + gps.location.lng(), + LONDON_LAT, + LONDON_LON) / 1000; + printInt(distanceKmToLondon, gps.location.isValid(), 9); + + double courseToLondon = + TinyGPSPlus::courseTo( + gps.location.lat(), + gps.location.lng(), + LONDON_LAT, + LONDON_LON); + + printFloat(courseToLondon, gps.location.isValid(), 7, 2); + + const char *cardinalToLondon = TinyGPSPlus::cardinal(courseToLondon); + + printStr(gps.location.isValid() ? cardinalToLondon : "*** ", 6); + + printInt(gps.charsProcessed(), true, 6); + printInt(gps.sentencesWithFix(), true, 10); + printInt(gps.failedChecksum(), true, 9); + Serial.println(); + + smartDelay(1000); + + if (millis() > 5000 && gps.charsProcessed() < 10) + Serial.println(F("No GPS data received: check wiring")); +} + +// This custom version of delay() ensures that the gps object +// is being "fed". +static void smartDelay(unsigned long ms) +{ + unsigned long start = millis(); + do + { + while (ss.available()) + gps.encode(ss.read()); + } while (millis() - start < ms); +} + +static void printFloat(float val, bool valid, int len, int prec) +{ + if (!valid) + { + while (len-- > 1) + Serial.print('*'); + Serial.print(' '); + } + else + { + Serial.print(val, prec); + int vi = abs((int)val); + int flen = prec + (val < 0.0 ? 2 : 1); // . and - + flen += vi >= 1000 ? 4 : vi >= 100 ? 3 : vi >= 10 ? 2 : 1; + for (int i=flen; i 0) + sz[len-1] = ' '; + Serial.print(sz); + smartDelay(0); +} + +static void printDateTime(TinyGPSDate &d, TinyGPSTime &t) +{ + if (!d.isValid()) + { + Serial.print(F("********** ")); + } + else + { + char sz[32]; + sprintf(sz, "%02d/%02d/%02d ", d.month(), d.day(), d.year()); + Serial.print(sz); + } + + if (!t.isValid()) + { + Serial.print(F("******** ")); + } + else + { + char sz[32]; + sprintf(sz, "%02d:%02d:%02d ", t.hour(), t.minute(), t.second()); + Serial.print(sz); + } + + printInt(d.age(), d.isValid(), 5); + smartDelay(0); +} + +static void printStr(const char *str, int len) +{ + int slen = strlen(str); + for (int i=0; i +#include +/* + This sample code demonstrates just about every built-in operation of TinyGPS++ (TinyGPSPlus). + It requires the use of SoftwareSerial, and assumes that you have a + 4800-baud serial GPS device hooked up on pins 4(rx) and 3(tx). +*/ +static const int RXPin = 4, TXPin = 3; +static const uint32_t GPSBaud = 4800; + +// The TinyGPS++ object +TinyGPSPlus gps; + +// The serial connection to the GPS device +SoftwareSerial ss(RXPin, TXPin); + +// For stats that happen every 5 seconds +unsigned long last = 0UL; + +void setup() +{ + Serial.begin(115200); + ss.begin(GPSBaud); + + Serial.println(F("KitchenSink.ino")); + Serial.println(F("Demonstrating nearly every feature of TinyGPS++")); + Serial.print(F("Testing TinyGPS++ library v. ")); Serial.println(TinyGPSPlus::libraryVersion()); + Serial.println(F("by Mikal Hart")); + Serial.println(); +} + +void loop() +{ + // Dispatch incoming characters + while (ss.available() > 0) + gps.encode(ss.read()); + + if (gps.location.isUpdated()) + { + Serial.print(F("LOCATION Fix Age=")); + Serial.print(gps.location.age()); + Serial.print(F("ms Raw Lat=")); + Serial.print(gps.location.rawLat().negative ? "-" : "+"); + Serial.print(gps.location.rawLat().deg); + Serial.print("[+"); + Serial.print(gps.location.rawLat().billionths); + Serial.print(F(" billionths], Raw Long=")); + Serial.print(gps.location.rawLng().negative ? "-" : "+"); + Serial.print(gps.location.rawLng().deg); + Serial.print("[+"); + Serial.print(gps.location.rawLng().billionths); + Serial.print(F(" billionths], Lat=")); + Serial.print(gps.location.lat(), 6); + Serial.print(F(" Long=")); + Serial.println(gps.location.lng(), 6); + } + + else if (gps.date.isUpdated()) + { + Serial.print(F("DATE Fix Age=")); + Serial.print(gps.date.age()); + Serial.print(F("ms Raw=")); + Serial.print(gps.date.value()); + Serial.print(F(" Year=")); + Serial.print(gps.date.year()); + Serial.print(F(" Month=")); + Serial.print(gps.date.month()); + Serial.print(F(" Day=")); + Serial.println(gps.date.day()); + } + + else if (gps.time.isUpdated()) + { + Serial.print(F("TIME Fix Age=")); + Serial.print(gps.time.age()); + Serial.print(F("ms Raw=")); + Serial.print(gps.time.value()); + Serial.print(F(" Hour=")); + Serial.print(gps.time.hour()); + Serial.print(F(" Minute=")); + Serial.print(gps.time.minute()); + Serial.print(F(" Second=")); + Serial.print(gps.time.second()); + Serial.print(F(" Hundredths=")); + Serial.println(gps.time.centisecond()); + } + + else if (gps.speed.isUpdated()) + { + Serial.print(F("SPEED Fix Age=")); + Serial.print(gps.speed.age()); + Serial.print(F("ms Raw=")); + Serial.print(gps.speed.value()); + Serial.print(F(" Knots=")); + Serial.print(gps.speed.knots()); + Serial.print(F(" MPH=")); + Serial.print(gps.speed.mph()); + Serial.print(F(" m/s=")); + Serial.print(gps.speed.mps()); + Serial.print(F(" km/h=")); + Serial.println(gps.speed.kmph()); + } + + else if (gps.course.isUpdated()) + { + Serial.print(F("COURSE Fix Age=")); + Serial.print(gps.course.age()); + Serial.print(F("ms Raw=")); + Serial.print(gps.course.value()); + Serial.print(F(" Deg=")); + Serial.println(gps.course.deg()); + } + + else if (gps.altitude.isUpdated()) + { + Serial.print(F("ALTITUDE Fix Age=")); + Serial.print(gps.altitude.age()); + Serial.print(F("ms Raw=")); + Serial.print(gps.altitude.value()); + Serial.print(F(" Meters=")); + Serial.print(gps.altitude.meters()); + Serial.print(F(" Miles=")); + Serial.print(gps.altitude.miles()); + Serial.print(F(" KM=")); + Serial.print(gps.altitude.kilometers()); + Serial.print(F(" Feet=")); + Serial.println(gps.altitude.feet()); + } + + else if (gps.satellites.isUpdated()) + { + Serial.print(F("SATELLITES Fix Age=")); + Serial.print(gps.satellites.age()); + Serial.print(F("ms Value=")); + Serial.println(gps.satellites.value()); + } + + else if (gps.hdop.isUpdated()) + { + Serial.print(F("HDOP Fix Age=")); + Serial.print(gps.hdop.age()); + Serial.print(F("ms raw=")); + Serial.print(gps.hdop.value()); + Serial.print(F(" hdop=")); + Serial.println(gps.hdop.hdop()); + } + + else if (millis() - last > 5000) + { + Serial.println(); + if (gps.location.isValid()) + { + static const double LONDON_LAT = 51.508131, LONDON_LON = -0.128002; + double distanceToLondon = + TinyGPSPlus::distanceBetween( + gps.location.lat(), + gps.location.lng(), + LONDON_LAT, + LONDON_LON); + double courseToLondon = + TinyGPSPlus::courseTo( + gps.location.lat(), + gps.location.lng(), + LONDON_LAT, + LONDON_LON); + + Serial.print(F("LONDON Distance=")); + Serial.print(distanceToLondon/1000, 6); + Serial.print(F(" km Course-to=")); + Serial.print(courseToLondon, 6); + Serial.print(F(" degrees [")); + Serial.print(TinyGPSPlus::cardinal(courseToLondon)); + Serial.println(F("]")); + } + + Serial.print(F("DIAGS Chars=")); + Serial.print(gps.charsProcessed()); + Serial.print(F(" Sentences-with-Fix=")); + Serial.print(gps.sentencesWithFix()); + Serial.print(F(" Failed-checksum=")); + Serial.print(gps.failedChecksum()); + Serial.print(F(" Passed-checksum=")); + Serial.println(gps.passedChecksum()); + + if (gps.charsProcessed() < 10) + Serial.println(F("WARNING: No GPS data. Check wiring.")); + + last = millis(); + Serial.println(); + } +} \ No newline at end of file diff --git a/TinyGPSPlus-master/examples/SatElevTracker/SatElevTracker.ino b/TinyGPSPlus-master/examples/SatElevTracker/SatElevTracker.ino new file mode 100644 index 0000000..afc6d8a --- /dev/null +++ b/TinyGPSPlus-master/examples/SatElevTracker/SatElevTracker.ino @@ -0,0 +1,150 @@ +#include +#include +/* + This sample code tracks satellite elevations using TinyGPSCustom objects. + + Satellite numbers and elevations are not normally tracked by TinyGPS++, but + by using TinyGPSCustom we get around this. + + It requires the use of SoftwareSerial and assumes that you have a + 4800-baud serial GPS device hooked up on pins 4(RX) and 3(TX). +*/ +static const int RXPin = 4, TXPin = 3; +static const uint32_t GPSBaud = 4800; +static const int MAX_SATELLITES = 40; +static const int PAGE_LENGTH = 40; + +// The TinyGPS++ object +TinyGPSPlus gps; + +// The serial connection to the GPS device +SoftwareSerial ss(RXPin, TXPin); + +TinyGPSCustom totalGPGSVMessages(gps, "GPGSV", 1); // $GPGSV sentence, first element +TinyGPSCustom messageNumber(gps, "GPGSV", 2); // $GPGSV sentence, second element +TinyGPSCustom satNumber[4]; // to be initialized later +TinyGPSCustom elevation[4]; +bool anyChanges = false; +unsigned long linecount = 0; + +struct +{ + int elevation; + bool active; +} sats[MAX_SATELLITES]; + +void setup() +{ + Serial.begin(115200); + ss.begin(GPSBaud); + + Serial.println(F("SatElevTracker.ino")); + Serial.println(F("Displays GPS satellite elevations as they change")); + Serial.print(F("Testing TinyGPS++ library v. ")); Serial.println(TinyGPSPlus::libraryVersion()); + Serial.println(F("by Mikal Hart")); + Serial.println(); + + // Initialize all the uninitialized TinyGPSCustom objects + for (int i=0; i<4; ++i) + { + satNumber[i].begin(gps, "GPGSV", 4 + 4 * i); // offsets 4, 8, 12, 16 + elevation[i].begin(gps, "GPGSV", 5 + 4 * i); // offsets 5, 9, 13, 17 + } +} + +void loop() +{ + // Dispatch incoming characters + if (ss.available() > 0) + { + gps.encode(ss.read()); + + if (totalGPGSVMessages.isUpdated()) + { + for (int i=0; i<4; ++i) + { + int no = atoi(satNumber[i].value()); + if (no >= 1 && no <= MAX_SATELLITES) + { + int elev = atoi(elevation[i].value()); + sats[no-1].active = true; + if (sats[no-1].elevation != elev) + { + sats[no-1].elevation = elev; + anyChanges = true; + } + } + } + + int totalMessages = atoi(totalGPGSVMessages.value()); + int currentMessage = atoi(messageNumber.value()); + if (totalMessages == currentMessage && anyChanges) + { + if (linecount++ % PAGE_LENGTH == 0) + printHeader(); + TimePrint(); + for (int i=0; i +#include +/* + This sample code demonstrates how to use an array of TinyGPSCustom objects + to monitor all the visible satellites. + + Satellite numbers, elevation, azimuth, and signal-to-noise ratio are not + normally tracked by TinyGPS++, but by using TinyGPSCustom we get around this. + + The simple code also demonstrates how to use arrays of TinyGPSCustom objects, + each monitoring a different field of the $GPGSV sentence. + + It requires the use of SoftwareSerial, and assumes that you have a + 4800-baud serial GPS device hooked up on pins 4(RX) and 3(TX). +*/ +static const int RXPin = 4, TXPin = 3; +static const uint32_t GPSBaud = 4800; + +// The TinyGPS++ object +TinyGPSPlus gps; + +// The serial connection to the GPS device +SoftwareSerial ss(RXPin, TXPin); + +/* + From http://aprs.gids.nl/nmea/: + + $GPGSV + + GPS Satellites in view + + eg. $GPGSV,3,1,11,03,03,111,00,04,15,270,00,06,01,010,00,13,06,292,00*74 + $GPGSV,3,2,11,14,25,170,00,16,57,208,39,18,67,296,40,19,40,246,00*74 + $GPGSV,3,3,11,22,42,067,42,24,14,311,43,27,05,244,00,,,,*4D + + 1 = Total number of messages of this type in this cycle + 2 = Message number + 3 = Total number of SVs in view + 4 = SV PRN number + 5 = Elevation in degrees, 90 maximum + 6 = Azimuth, degrees from true north, 000 to 359 + 7 = SNR, 00-99 dB (null when not tracking) + 8-11 = Information about second SV, same as field 4-7 + 12-15= Information about third SV, same as field 4-7 + 16-19= Information about fourth SV, same as field 4-7 +*/ + +static const int MAX_SATELLITES = 40; + +TinyGPSCustom totalGPGSVMessages(gps, "GPGSV", 1); // $GPGSV sentence, first element +TinyGPSCustom messageNumber(gps, "GPGSV", 2); // $GPGSV sentence, second element +TinyGPSCustom satsInView(gps, "GPGSV", 3); // $GPGSV sentence, third element +TinyGPSCustom satNumber[4]; // to be initialized later +TinyGPSCustom elevation[4]; +TinyGPSCustom azimuth[4]; +TinyGPSCustom snr[4]; + +struct +{ + bool active; + int elevation; + int azimuth; + int snr; +} sats[MAX_SATELLITES]; + +void setup() +{ + Serial.begin(115200); + ss.begin(GPSBaud); + + Serial.println(F("SatelliteTracker.ino")); + Serial.println(F("Monitoring satellite location and signal strength using TinyGPSCustom")); + Serial.print(F("Testing TinyGPS++ library v. ")); Serial.println(TinyGPSPlus::libraryVersion()); + Serial.println(F("by Mikal Hart")); + Serial.println(); + + // Initialize all the uninitialized TinyGPSCustom objects + for (int i=0; i<4; ++i) + { + satNumber[i].begin(gps, "GPGSV", 4 + 4 * i); // offsets 4, 8, 12, 16 + elevation[i].begin(gps, "GPGSV", 5 + 4 * i); // offsets 5, 9, 13, 17 + azimuth[i].begin( gps, "GPGSV", 6 + 4 * i); // offsets 6, 10, 14, 18 + snr[i].begin( gps, "GPGSV", 7 + 4 * i); // offsets 7, 11, 15, 19 + } +} + +void loop() +{ + // Dispatch incoming characters + if (ss.available() > 0) + { + gps.encode(ss.read()); + if (totalGPGSVMessages.isUpdated()) + { + for (int i=0; i<4; ++i) + { + int no = atoi(satNumber[i].value()); + // Serial.print(F("SatNumber is ")); Serial.println(no); + if (no >= 1 && no <= MAX_SATELLITES) + { + sats[no-1].elevation = atoi(elevation[i].value()); + sats[no-1].azimuth = atoi(azimuth[i].value()); + sats[no-1].snr = atoi(snr[i].value()); + sats[no-1].active = true; + } + } + + int totalMessages = atoi(totalGPGSVMessages.value()); + int currentMessage = atoi(messageNumber.value()); + if (totalMessages == currentMessage) + { + Serial.print(F("Sats=")); Serial.print(gps.satellites.value()); + Serial.print(F(" Nums=")); + for (int i=0; i +#include + +/* + This sample demonstrates TinyGPS++'s capacity for extracting custom + fields from any NMEA sentence. TinyGPS++ has built-in facilities for + extracting latitude, longitude, altitude, etc., from the $GPGGA and + $GPRMC sentences. But with the TinyGPSCustom type, you can extract + other NMEA fields, even from non-standard NMEA sentences. + + It requires the use of SoftwareSerial, and assumes that you have a + 4800-baud serial GPS device hooked up on pins 4(RX) and 3(TX). +*/ +static const int RXPin = 4, TXPin = 3; +static const uint32_t GPSBaud = 4800; + +// The TinyGPS++ object +TinyGPSPlus gps; + +// The serial connection to the GPS device +SoftwareSerial ss(RXPin, TXPin); + +/* + By declaring TinyGPSCustom objects like this, we announce that we + are interested in the 15th, 16th, and 17th fields in the $GPGSA + sentence, respectively the PDOP (F("positional dilution of precision")), + HDOP (F("horizontal...")), and VDOP (F("vertical...")). + + (Counting starts with the field immediately following the sentence name, + i.e. $GPGSA. For more information on NMEA sentences, consult your + GPS module's documentation and/or http://aprs.gids.nl/nmea/.) + + If your GPS module doesn't support the $GPGSA sentence, then you + won't get any output from this program. +*/ + +TinyGPSCustom pdop(gps, "GPGSA", 15); // $GPGSA sentence, 15th element +TinyGPSCustom hdop(gps, "GPGSA", 16); // $GPGSA sentence, 16th element +TinyGPSCustom vdop(gps, "GPGSA", 17); // $GPGSA sentence, 17th element + +void setup() +{ + Serial.begin(115200); + ss.begin(GPSBaud); + + Serial.println(F("UsingCustomFields.ino")); + Serial.println(F("Demonstrating how to extract any NMEA field using TinyGPSCustom")); + Serial.print(F("Testing TinyGPS++ library v. ")); Serial.println(TinyGPSPlus::libraryVersion()); + Serial.println(F("by Mikal Hart")); + Serial.println(); +} + +void loop() +{ + // Every time anything is updated, print everything. + if (gps.altitude.isUpdated() || gps.satellites.isUpdated() || + pdop.isUpdated() || hdop.isUpdated() || vdop.isUpdated()) + { + Serial.print(F("ALT=")); Serial.print(gps.altitude.meters()); + Serial.print(F(" PDOP=")); Serial.print(pdop.value()); + Serial.print(F(" HDOP=")); Serial.print(hdop.value()); + Serial.print(F(" VDOP=")); Serial.print(vdop.value()); + Serial.print(F(" SATS=")); Serial.println(gps.satellites.value()); + } + + while (ss.available() > 0) + gps.encode(ss.read()); +} + diff --git a/TinyGPSPlus-master/keywords.txt b/TinyGPSPlus-master/keywords.txt new file mode 100644 index 0000000..91d119b --- /dev/null +++ b/TinyGPSPlus-master/keywords.txt @@ -0,0 +1,71 @@ +####################################### +# Syntax Coloring Map for TinyGPS++ +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +TinyGPSPlus KEYWORD1 +TinyGPSLocation KEYWORD1 +TinyGPSDate KEYWORD1 +TinyGPSTime KEYWORD1 +TinyGPSSpeed KEYWORD1 +TinyGPSCourse KEYWORD1 +TinyGPSAltitude KEYWORD1 +TinyGPSInteger KEYWORD1 +TinyGPSDecimal KEYWORD1 +TinyGPSCustom KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +encode KEYWORD2 +location KEYWORD2 +date KEYWORD2 +time KEYWORD2 +speed KEYWORD2 +course KEYWORD2 +altitude KEYWORD2 +satellites KEYWORD2 +hdop KEYWORD2 +libraryVersion KEYWORD2 +distanceBetween KEYWORD2 +courseTo KEYWORD2 +cardinal KEYWORD2 +charsProcessed KEYWORD2 +sentencesWithFix KEYWORD2 +failedChecksum KEYWORD2 +passedChecksum KEYWORD2 +isValid KEYWORD2 +isUpdated KEYWORD2 +age KEYWORD2 +lat KEYWORD2 +lng KEYWORD2 +isUpdatedDate KEYWORD2 +isUpdatedTime KEYWORD2 +year KEYWORD2 +month KEYWORD2 +day KEYWORD2 +hour KEYWORD2 +minute KEYWORD2 +second KEYWORD2 +centisecond KEYWORD2 +value KEYWORD2 +knots KEYWORD2 +mph KEYWORD2 +mps KEYWORD2 +kmph KEYWORD2 +deg KEYWORD2 +billionths KEYWORD2 +negative KEYWORD2 +meters KEYWORD2 +miles KEYWORD2 +kilometers KEYWORD2 +feet KEYWORD2 + +####################################### +# Constants (LITERAL1) +####################################### + diff --git a/TinyGPSPlus-master/library.json b/TinyGPSPlus-master/library.json new file mode 100644 index 0000000..cdf6262 --- /dev/null +++ b/TinyGPSPlus-master/library.json @@ -0,0 +1,22 @@ +{ + "name": "TinyGPSPlus", + "version": "1.0.0", + "keywords": "gps,NMEA", + "description": "A new, customizable Arduino NMEA parsing library", + "repository": + { + "type": "git", + "url": "https://github.com/mikalhart/TinyGPSPlus.git" + }, + "authors": + [ + { + "name": "Mikal Hart", + "email": "mikal@arduniana.org", + "url": "http://arduiniana.org", + "maintainer": true + } + ], + "frameworks": "arduino", + "platforms": "*" +} diff --git a/TinyGPSPlus-master/library.properties b/TinyGPSPlus-master/library.properties new file mode 100644 index 0000000..b17e8b3 --- /dev/null +++ b/TinyGPSPlus-master/library.properties @@ -0,0 +1,9 @@ +name=TinyGPS++ +version=1.0.0 +author=Mikal Hart +maintainer=Mikal Hart +sentence=TinyGPS++ provides object-oriented parsing of GPS (NMEA) sentences +paragraph=NMEA is the standard format GPS devices use to report location, time, altitude, etc. TinyGPS++ is a compact, resilient library that parses the most common NMEA 'sentences' used: GGA and RMC. It can also be customized to extract data from *any* compliant sentence. +category=Communication +url=https://github.com/mikalhart/TinyGPSPlus +architectures=* diff --git a/TinyGPSPlus-master/src/TinyGPS++.cpp b/TinyGPSPlus-master/src/TinyGPS++.cpp new file mode 100644 index 0000000..e1ec777 --- /dev/null +++ b/TinyGPSPlus-master/src/TinyGPS++.cpp @@ -0,0 +1,503 @@ +/* +TinyGPS++ - a small GPS library for Arduino providing universal NMEA parsing +Based on work by and "distanceBetween" and "courseTo" courtesy of Maarten Lamers. +Suggestion to add satellites, courseTo(), and cardinal() by Matt Monson. +Location precision improvements suggested by Wayne Holder. +Copyright (C) 2008-2013 Mikal Hart +All rights reserved. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "TinyGPS++.h" + +#include +#include +#include + +#define _GPRMCterm "GPRMC" +#define _GPGGAterm "GPGGA" +#define _GNRMCterm "GNRMC" +#define _GNGGAterm "GNGGA" + +TinyGPSPlus::TinyGPSPlus() + : parity(0) + , isChecksumTerm(false) + , curSentenceType(GPS_SENTENCE_OTHER) + , curTermNumber(0) + , curTermOffset(0) + , sentenceHasFix(false) + , customElts(0) + , customCandidates(0) + , encodedCharCount(0) + , sentencesWithFixCount(0) + , failedChecksumCount(0) + , passedChecksumCount(0) +{ + term[0] = '\0'; +} + +// +// public methods +// + +bool TinyGPSPlus::encode(char c) +{ + ++encodedCharCount; + + switch(c) + { + case ',': // term terminators + parity ^= (uint8_t)c; + case '\r': + case '\n': + case '*': + { + bool isValidSentence = false; + if (curTermOffset < sizeof(term)) + { + term[curTermOffset] = 0; + isValidSentence = endOfTermHandler(); + } + ++curTermNumber; + curTermOffset = 0; + isChecksumTerm = c == '*'; + return isValidSentence; + } + break; + + case '$': // sentence begin + curTermNumber = curTermOffset = 0; + parity = 0; + curSentenceType = GPS_SENTENCE_OTHER; + isChecksumTerm = false; + sentenceHasFix = false; + return false; + + default: // ordinary characters + if (curTermOffset < sizeof(term) - 1) + term[curTermOffset++] = c; + if (!isChecksumTerm) + parity ^= c; + return false; + } + + return false; +} + +// +// internal utilities +// +int TinyGPSPlus::fromHex(char a) +{ + if (a >= 'A' && a <= 'F') + return a - 'A' + 10; + else if (a >= 'a' && a <= 'f') + return a - 'a' + 10; + else + return a - '0'; +} + +// static +// Parse a (potentially negative) number with up to 2 decimal digits -xxxx.yy +int32_t TinyGPSPlus::parseDecimal(const char *term) +{ + bool negative = *term == '-'; + if (negative) ++term; + int32_t ret = 100 * (int32_t)atol(term); + while (isdigit(*term)) ++term; + if (*term == '.' && isdigit(term[1])) + { + ret += 10 * (term[1] - '0'); + if (isdigit(term[2])) + ret += term[2] - '0'; + } + return negative ? -ret : ret; +} + +// static +// Parse degrees in that funny NMEA format DDMM.MMMM +void TinyGPSPlus::parseDegrees(const char *term, RawDegrees °) +{ + uint32_t leftOfDecimal = (uint32_t)atol(term); + uint16_t minutes = (uint16_t)(leftOfDecimal % 100); + uint32_t multiplier = 10000000UL; + uint32_t tenMillionthsOfMinutes = minutes * multiplier; + + deg.deg = (int16_t)(leftOfDecimal / 100); + + while (isdigit(*term)) + ++term; + + if (*term == '.') + while (isdigit(*++term)) + { + multiplier /= 10; + tenMillionthsOfMinutes += (*term - '0') * multiplier; + } + + deg.billionths = (5 * tenMillionthsOfMinutes + 1) / 3; + deg.negative = false; +} + +#define COMBINE(sentence_type, term_number) (((unsigned)(sentence_type) << 5) | term_number) + +// Processes a just-completed term +// Returns true if new sentence has just passed checksum test and is validated +bool TinyGPSPlus::endOfTermHandler() +{ + // If it's the checksum term, and the checksum checks out, commit + if (isChecksumTerm) + { + byte checksum = 16 * fromHex(term[0]) + fromHex(term[1]); + if (checksum == parity) + { + passedChecksumCount++; + if (sentenceHasFix) + ++sentencesWithFixCount; + + switch(curSentenceType) + { + case GPS_SENTENCE_GPRMC: + date.commit(); + time.commit(); + if (sentenceHasFix) + { + location.commit(); + speed.commit(); + course.commit(); + } + break; + case GPS_SENTENCE_GPGGA: + time.commit(); + if (sentenceHasFix) + { + location.commit(); + altitude.commit(); + } + satellites.commit(); + hdop.commit(); + break; + } + + // Commit all custom listeners of this sentence type + for (TinyGPSCustom *p = customCandidates; p != NULL && strcmp(p->sentenceName, customCandidates->sentenceName) == 0; p = p->next) + p->commit(); + return true; + } + + else + { + ++failedChecksumCount; + } + + return false; + } + + // the first term determines the sentence type + if (curTermNumber == 0) + { + if (!strcmp(term, _GPRMCterm) || !strcmp(term, _GNRMCterm)) + curSentenceType = GPS_SENTENCE_GPRMC; + else if (!strcmp(term, _GPGGAterm) || !strcmp(term, _GNGGAterm)) + curSentenceType = GPS_SENTENCE_GPGGA; + else + curSentenceType = GPS_SENTENCE_OTHER; + + // Any custom candidates of this sentence type? + for (customCandidates = customElts; customCandidates != NULL && strcmp(customCandidates->sentenceName, term) < 0; customCandidates = customCandidates->next); + if (customCandidates != NULL && strcmp(customCandidates->sentenceName, term) > 0) + customCandidates = NULL; + + return false; + } + + if (curSentenceType != GPS_SENTENCE_OTHER && term[0]) + switch(COMBINE(curSentenceType, curTermNumber)) + { + case COMBINE(GPS_SENTENCE_GPRMC, 1): // Time in both sentences + case COMBINE(GPS_SENTENCE_GPGGA, 1): + time.setTime(term); + break; + case COMBINE(GPS_SENTENCE_GPRMC, 2): // GPRMC validity + sentenceHasFix = term[0] == 'A'; + break; + case COMBINE(GPS_SENTENCE_GPRMC, 3): // Latitude + case COMBINE(GPS_SENTENCE_GPGGA, 2): + location.setLatitude(term); + break; + case COMBINE(GPS_SENTENCE_GPRMC, 4): // N/S + case COMBINE(GPS_SENTENCE_GPGGA, 3): + location.rawNewLatData.negative = term[0] == 'S'; + break; + case COMBINE(GPS_SENTENCE_GPRMC, 5): // Longitude + case COMBINE(GPS_SENTENCE_GPGGA, 4): + location.setLongitude(term); + break; + case COMBINE(GPS_SENTENCE_GPRMC, 6): // E/W + case COMBINE(GPS_SENTENCE_GPGGA, 5): + location.rawNewLngData.negative = term[0] == 'W'; + break; + case COMBINE(GPS_SENTENCE_GPRMC, 7): // Speed (GPRMC) + speed.set(term); + break; + case COMBINE(GPS_SENTENCE_GPRMC, 8): // Course (GPRMC) + course.set(term); + break; + case COMBINE(GPS_SENTENCE_GPRMC, 9): // Date (GPRMC) + date.setDate(term); + break; + case COMBINE(GPS_SENTENCE_GPGGA, 6): // Fix data (GPGGA) + sentenceHasFix = term[0] > '0'; + break; + case COMBINE(GPS_SENTENCE_GPGGA, 7): // Satellites used (GPGGA) + satellites.set(term); + break; + case COMBINE(GPS_SENTENCE_GPGGA, 8): // HDOP + hdop.set(term); + break; + case COMBINE(GPS_SENTENCE_GPGGA, 9): // Altitude (GPGGA) + altitude.set(term); + break; + } + + // Set custom values as needed + for (TinyGPSCustom *p = customCandidates; p != NULL && strcmp(p->sentenceName, customCandidates->sentenceName) == 0 && p->termNumber <= curTermNumber; p = p->next) + if (p->termNumber == curTermNumber) + p->set(term); + + return false; +} + +/* static */ +double TinyGPSPlus::distanceBetween(double lat1, double long1, double lat2, double long2) +{ + // returns distance in meters between two positions, both specified + // as signed decimal-degrees latitude and longitude. Uses great-circle + // distance computation for hypothetical sphere of radius 6372795 meters. + // Because Earth is no exact sphere, rounding errors may be up to 0.5%. + // Courtesy of Maarten Lamers + double delta = radians(long1-long2); + double sdlong = sin(delta); + double cdlong = cos(delta); + lat1 = radians(lat1); + lat2 = radians(lat2); + double slat1 = sin(lat1); + double clat1 = cos(lat1); + double slat2 = sin(lat2); + double clat2 = cos(lat2); + delta = (clat1 * slat2) - (slat1 * clat2 * cdlong); + delta = sq(delta); + delta += sq(clat2 * sdlong); + delta = sqrt(delta); + double denom = (slat1 * slat2) + (clat1 * clat2 * cdlong); + delta = atan2(delta, denom); + return delta * 6372795; +} + +double TinyGPSPlus::courseTo(double lat1, double long1, double lat2, double long2) +{ + // returns course in degrees (North=0, West=270) from position 1 to position 2, + // both specified as signed decimal-degrees latitude and longitude. + // Because Earth is no exact sphere, calculated course may be off by a tiny fraction. + // Courtesy of Maarten Lamers + double dlon = radians(long2-long1); + lat1 = radians(lat1); + lat2 = radians(lat2); + double a1 = sin(dlon) * cos(lat2); + double a2 = sin(lat1) * cos(lat2) * cos(dlon); + a2 = cos(lat1) * sin(lat2) - a2; + a2 = atan2(a1, a2); + if (a2 < 0.0) + { + a2 += TWO_PI; + } + return degrees(a2); +} + +const char *TinyGPSPlus::cardinal(double course) +{ + static const char* directions[] = {"N", "NNE", "NE", "ENE", "E", "ESE", "SE", "SSE", "S", "SSW", "SW", "WSW", "W", "WNW", "NW", "NNW"}; + int direction = (int)((course + 11.25f) / 22.5f); + return directions[direction % 16]; +} + +void TinyGPSLocation::commit() +{ + rawLatData = rawNewLatData; + rawLngData = rawNewLngData; + lastCommitTime = millis(); + valid = updated = true; +} + +void TinyGPSLocation::setLatitude(const char *term) +{ + TinyGPSPlus::parseDegrees(term, rawNewLatData); +} + +void TinyGPSLocation::setLongitude(const char *term) +{ + TinyGPSPlus::parseDegrees(term, rawNewLngData); +} + +double TinyGPSLocation::lat() +{ + updated = false; + double ret = rawLatData.deg + rawLatData.billionths / 1000000000.0; + return rawLatData.negative ? -ret : ret; +} + +double TinyGPSLocation::lng() +{ + updated = false; + double ret = rawLngData.deg + rawLngData.billionths / 1000000000.0; + return rawLngData.negative ? -ret : ret; +} + +void TinyGPSDate::commit() +{ + date = newDate; + lastCommitTime = millis(); + valid = updated = true; +} + +void TinyGPSTime::commit() +{ + time = newTime; + lastCommitTime = millis(); + valid = updated = true; +} + +void TinyGPSTime::setTime(const char *term) +{ + newTime = (uint32_t)TinyGPSPlus::parseDecimal(term); +} + +void TinyGPSDate::setDate(const char *term) +{ + newDate = atol(term); +} + +uint16_t TinyGPSDate::year() +{ + updated = false; + uint16_t year = date % 100; + return year + 2000; +} + +uint8_t TinyGPSDate::month() +{ + updated = false; + return (date / 100) % 100; +} + +uint8_t TinyGPSDate::day() +{ + updated = false; + return date / 10000; +} + +uint8_t TinyGPSTime::hour() +{ + updated = false; + return time / 1000000; +} + +uint8_t TinyGPSTime::minute() +{ + updated = false; + return (time / 10000) % 100; +} + +uint8_t TinyGPSTime::second() +{ + updated = false; + return (time / 100) % 100; +} + +uint8_t TinyGPSTime::centisecond() +{ + updated = false; + return time % 100; +} + +void TinyGPSDecimal::commit() +{ + val = newval; + lastCommitTime = millis(); + valid = updated = true; +} + +void TinyGPSDecimal::set(const char *term) +{ + newval = TinyGPSPlus::parseDecimal(term); +} + +void TinyGPSInteger::commit() +{ + val = newval; + lastCommitTime = millis(); + valid = updated = true; +} + +void TinyGPSInteger::set(const char *term) +{ + newval = atol(term); +} + +TinyGPSCustom::TinyGPSCustom(TinyGPSPlus &gps, const char *_sentenceName, int _termNumber) +{ + begin(gps, _sentenceName, _termNumber); +} + +void TinyGPSCustom::begin(TinyGPSPlus &gps, const char *_sentenceName, int _termNumber) +{ + lastCommitTime = 0; + updated = valid = false; + sentenceName = _sentenceName; + termNumber = _termNumber; + memset(stagingBuffer, '\0', sizeof(stagingBuffer)); + memset(buffer, '\0', sizeof(buffer)); + + // Insert this item into the GPS tree + gps.insertCustom(this, _sentenceName, _termNumber); +} + +void TinyGPSCustom::commit() +{ + strcpy(this->buffer, this->stagingBuffer); + lastCommitTime = millis(); + valid = updated = true; +} + +void TinyGPSCustom::set(const char *term) +{ + strncpy(this->stagingBuffer, term, sizeof(this->stagingBuffer)); +} + +void TinyGPSPlus::insertCustom(TinyGPSCustom *pElt, const char *sentenceName, int termNumber) +{ + TinyGPSCustom **ppelt; + + for (ppelt = &this->customElts; *ppelt != NULL; ppelt = &(*ppelt)->next) + { + int cmp = strcmp(sentenceName, (*ppelt)->sentenceName); + if (cmp < 0 || (cmp == 0 && termNumber < (*ppelt)->termNumber)) + break; + } + + pElt->next = *ppelt; + *ppelt = pElt; +} diff --git a/TinyGPSPlus-master/src/TinyGPS++.h b/TinyGPSPlus-master/src/TinyGPS++.h new file mode 100644 index 0000000..60485cb --- /dev/null +++ b/TinyGPSPlus-master/src/TinyGPS++.h @@ -0,0 +1,278 @@ +/* +TinyGPS++ - a small GPS library for Arduino providing universal NMEA parsing +Based on work by and "distanceBetween" and "courseTo" courtesy of Maarten Lamers. +Suggestion to add satellites, courseTo(), and cardinal() by Matt Monson. +Location precision improvements suggested by Wayne Holder. +Copyright (C) 2008-2013 Mikal Hart +All rights reserved. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef __TinyGPSPlus_h +#define __TinyGPSPlus_h + +#if defined(ARDUINO) && ARDUINO >= 100 +#include "Arduino.h" +#else +#include "WProgram.h" +#endif +#include + +#define _GPS_VERSION "1.0.2" // software version of this library +#define _GPS_MPH_PER_KNOT 1.15077945 +#define _GPS_MPS_PER_KNOT 0.51444444 +#define _GPS_KMPH_PER_KNOT 1.852 +#define _GPS_MILES_PER_METER 0.00062137112 +#define _GPS_KM_PER_METER 0.001 +#define _GPS_FEET_PER_METER 3.2808399 +#define _GPS_MAX_FIELD_SIZE 15 + +struct RawDegrees +{ + uint16_t deg; + uint32_t billionths; + bool negative; +public: + RawDegrees() : deg(0), billionths(0), negative(false) + {} +}; + +struct TinyGPSLocation +{ + friend class TinyGPSPlus; +public: + bool isValid() const { return valid; } + bool isUpdated() const { return updated; } + uint32_t age() const { return valid ? millis() - lastCommitTime : (uint32_t)ULONG_MAX; } + const RawDegrees &rawLat() { updated = false; return rawLatData; } + const RawDegrees &rawLng() { updated = false; return rawLngData; } + double lat(); + double lng(); + + TinyGPSLocation() : valid(false), updated(false) + {} + +private: + bool valid, updated; + RawDegrees rawLatData, rawLngData, rawNewLatData, rawNewLngData; + uint32_t lastCommitTime; + void commit(); + void setLatitude(const char *term); + void setLongitude(const char *term); +}; + +struct TinyGPSDate +{ + friend class TinyGPSPlus; +public: + bool isValid() const { return valid; } + bool isUpdated() const { return updated; } + uint32_t age() const { return valid ? millis() - lastCommitTime : (uint32_t)ULONG_MAX; } + + uint32_t value() { updated = false; return date; } + uint16_t year(); + uint8_t month(); + uint8_t day(); + + TinyGPSDate() : valid(false), updated(false), date(0) + {} + +private: + bool valid, updated; + uint32_t date, newDate; + uint32_t lastCommitTime; + void commit(); + void setDate(const char *term); +}; + +struct TinyGPSTime +{ + friend class TinyGPSPlus; +public: + bool isValid() const { return valid; } + bool isUpdated() const { return updated; } + uint32_t age() const { return valid ? millis() - lastCommitTime : (uint32_t)ULONG_MAX; } + + uint32_t value() { updated = false; return time; } + uint8_t hour(); + uint8_t minute(); + uint8_t second(); + uint8_t centisecond(); + + TinyGPSTime() : valid(false), updated(false), time(0) + {} + +private: + bool valid, updated; + uint32_t time, newTime; + uint32_t lastCommitTime; + void commit(); + void setTime(const char *term); +}; + +struct TinyGPSDecimal +{ + friend class TinyGPSPlus; +public: + bool isValid() const { return valid; } + bool isUpdated() const { return updated; } + uint32_t age() const { return valid ? millis() - lastCommitTime : (uint32_t)ULONG_MAX; } + int32_t value() { updated = false; return val; } + + TinyGPSDecimal() : valid(false), updated(false), val(0) + {} + +private: + bool valid, updated; + uint32_t lastCommitTime; + int32_t val, newval; + void commit(); + void set(const char *term); +}; + +struct TinyGPSInteger +{ + friend class TinyGPSPlus; +public: + bool isValid() const { return valid; } + bool isUpdated() const { return updated; } + uint32_t age() const { return valid ? millis() - lastCommitTime : (uint32_t)ULONG_MAX; } + uint32_t value() { updated = false; return val; } + + TinyGPSInteger() : valid(false), updated(false), val(0) + {} + +private: + bool valid, updated; + uint32_t lastCommitTime; + uint32_t val, newval; + void commit(); + void set(const char *term); +}; + +struct TinyGPSSpeed : TinyGPSDecimal +{ + double knots() { return value() / 100.0; } + double mph() { return _GPS_MPH_PER_KNOT * value() / 100.0; } + double mps() { return _GPS_MPS_PER_KNOT * value() / 100.0; } + double kmph() { return _GPS_KMPH_PER_KNOT * value() / 100.0; } +}; + +struct TinyGPSCourse : public TinyGPSDecimal +{ + double deg() { return value() / 100.0; } +}; + +struct TinyGPSAltitude : TinyGPSDecimal +{ + double meters() { return value() / 100.0; } + double miles() { return _GPS_MILES_PER_METER * value() / 100.0; } + double kilometers() { return _GPS_KM_PER_METER * value() / 100.0; } + double feet() { return _GPS_FEET_PER_METER * value() / 100.0; } +}; + +struct TinyGPSHDOP : TinyGPSDecimal +{ + double hdop() { return value() / 100.0; } +}; + +class TinyGPSPlus; +class TinyGPSCustom +{ +public: + TinyGPSCustom() {}; + TinyGPSCustom(TinyGPSPlus &gps, const char *sentenceName, int termNumber); + void begin(TinyGPSPlus &gps, const char *_sentenceName, int _termNumber); + + bool isUpdated() const { return updated; } + bool isValid() const { return valid; } + uint32_t age() const { return valid ? millis() - lastCommitTime : (uint32_t)ULONG_MAX; } + const char *value() { updated = false; return buffer; } + +private: + void commit(); + void set(const char *term); + + char stagingBuffer[_GPS_MAX_FIELD_SIZE + 1]; + char buffer[_GPS_MAX_FIELD_SIZE + 1]; + unsigned long lastCommitTime; + bool valid, updated; + const char *sentenceName; + int termNumber; + friend class TinyGPSPlus; + TinyGPSCustom *next; +}; + +class TinyGPSPlus +{ +public: + TinyGPSPlus(); + bool encode(char c); // process one character received from GPS + TinyGPSPlus &operator << (char c) {encode(c); return *this;} + + TinyGPSLocation location; + TinyGPSDate date; + TinyGPSTime time; + TinyGPSSpeed speed; + TinyGPSCourse course; + TinyGPSAltitude altitude; + TinyGPSInteger satellites; + TinyGPSHDOP hdop; + + static const char *libraryVersion() { return _GPS_VERSION; } + + static double distanceBetween(double lat1, double long1, double lat2, double long2); + static double courseTo(double lat1, double long1, double lat2, double long2); + static const char *cardinal(double course); + + static int32_t parseDecimal(const char *term); + static void parseDegrees(const char *term, RawDegrees °); + + uint32_t charsProcessed() const { return encodedCharCount; } + uint32_t sentencesWithFix() const { return sentencesWithFixCount; } + uint32_t failedChecksum() const { return failedChecksumCount; } + uint32_t passedChecksum() const { return passedChecksumCount; } + +private: + enum {GPS_SENTENCE_GPGGA, GPS_SENTENCE_GPRMC, GPS_SENTENCE_OTHER}; + + // parsing state variables + uint8_t parity; + bool isChecksumTerm; + char term[_GPS_MAX_FIELD_SIZE]; + uint8_t curSentenceType; + uint8_t curTermNumber; + uint8_t curTermOffset; + bool sentenceHasFix; + + // custom element support + friend class TinyGPSCustom; + TinyGPSCustom *customElts; + TinyGPSCustom *customCandidates; + void insertCustom(TinyGPSCustom *pElt, const char *sentenceName, int index); + + // statistics + uint32_t encodedCharCount; + uint32_t sentencesWithFixCount; + uint32_t failedChecksumCount; + uint32_t passedChecksumCount; + + // internal utilities + int fromHex(char a); + bool endOfTermHandler(); +}; + +#endif // def(__TinyGPSPlus_h)