GPS device with text LCD display and track logging to GPX file on SD Card
Dependencies: MODGPS SDFileSystem TextLCD mbed
- Haicom HI-203E Serial GPS Receiver
- Text LCD Display 2x40 (1x HD44780, 4x HD44100)
- RS-232 Level Shifter (MAX232)
- Adafruit Micro-SD breakout board+
mbed Pin # | mbed Pin Function | Peripheral Pin |
---|---|---|
p5 | SPI MOSI / SDFileSystem | SD Card Data In |
p6 | SPI MISO / SDFileSystem | SD Card Data Out |
p7 | SPI SCK / SDFileSystem | SD Card Clk |
p8 | SPI CS / SDFileSystem | SD Card Data CS |
p12 | GPIO / TextLCD | LCD RS |
p14 | GPIO / TextLCD | LCD EN |
p21 | GPIO / InterruptIn | Button switch to GND (start/stop logging) |
p22 | GPIO / TextLCD | LCD D4 |
p23 | GPIO / TextLCD | LCD D5 |
p24 | GPIO / TextLCD | LCD D6 |
p25 | GPIO / TextLCD | LCD D7 |
p27 | UART RX / GPS | GPS TX |
Diff: main.cpp
- Revision:
- 0:e8ad47e9a9b4
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main.cpp Sun Feb 17 17:37:39 2013 +0000 @@ -0,0 +1,248 @@ +/* mbed GPS Logger using SD Card and Text LCD display + * + * Copyright (c) 2013 m.prinke, MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software + * and associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING + * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * @file main.cpp + * @purpose GPS Logger using SD Card and Text LCD display + * @version 0.1 + * @date Feb 2013 + * @author M. Prinke + */ +/** + * Time and position data from GPS receiver is displayed on a text LCD. + * GPS tracks can be written to a logfile (GPX format) on SD Card. + * Logging is started/stopped using a toggle button. + * Everytime logging is started, a new file with the filename pattern /sd/gpslog<nnnn>.gpx + * is created, where <nnnn> is decimal number in the range 0000 to 9999 which is incremented. + * Searching for a new unique filename is started from zero. + * + * LED1: GPS status (blinking: fix invalid; on: fix valid) + * LED2: Logging status (off: stopped; on: logging) + * + * To Do: + * - Adjustable logging cycle time (read from config file on SD Card?) + * - Start new track segment after loss of GPS fix + * - Add support for GSA message (2D/3D fix, dilution of precision) + */ + +#include "mbed.h" +#include "GPS.h" +#include "TextLCD.h" +#include "SDFileSystem.h" + +#define GPS_BAUDRATE 4800 +#define LOGGING_CYCLE_SEC 5 + +DigitalOut led_fix(LED1); // GPS fix (blinking - no data/no fix; on: fix) +DigitalOut led_log(LED2); // logging active +InterruptIn button(p21); // button connects pin to GND +Serial pc(USBTX, USBRX); // tx, rx +GPS gps(NC, p27); // tx, rx +TextLCD lcd(p12, p14, p22, p23, p24, p25, TextLCD::LCD40x2); // rs, e, d4-d7 +SDFileSystem sd(p5, p6, p7, p8, "sd"); // mosi, miso, sclk, cs + +bool g_logging = false; +bool g_changed = false; +uint32_t g_trkpt_no = 0; + +/** + * Function to create a unique filename + * + * @param fn pointer to a filename buffer + * @param number part of filename (returned by reference) + * @return 0 on success, -1 if no unused filename could be created. + */ +int get_filename(char *fn, uint16_t *number) +{ + FILE *fp; + + for (int n=0; n < 9999; n++) { + sprintf(fn, "/sd/gpslog%04d.gpx", n); + if ((fp = fopen(fn, "r")) == NULL) { + *number = n; + // file does not exist yet + return 0; + } else { + fclose(fp); + } + } + + // filename pattern exhausted + return -1; +} + +/** + * Callback function for button interrupt + * + * Debounce button and set change flag. + */ +void change_logging(void) +{ + wait_ms(50); // button debounce + while (!button); // wait until button released + g_changed = true; +} + +/** + * Write GPX file header (including start of track segment) + * + * @param fp file pointer + * @param t time structure + * @param fileno file number + * @return result of last file write access + */ +int write_gpx_header(FILE *fp, GPS_Time t, uint16_t fileno) +{ + g_trkpt_no = 0; //< reset trackpoint number + + fprintf(fp, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"); + fprintf(fp, "<gpx\n"); + fprintf(fp, " version=\"1.0\"\n"); + fprintf(fp, " creator=\"mbed GPS Logger\"\n"); + fprintf(fp, " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"); + fprintf(fp, " xmlns=\"http://www.topografix.com/GPX/1/0\"\n"); + fprintf(fp, " xsi:schemaLocation=\"http://www.topografix.com/GPX/1/0 http://www.topografix.com/GPX/1/0/gpx.xsd\">\n"); + fprintf(fp, "<time>%04d-%02d-%02dT%02d:%02d:%02dZ</time>\n", t.year, t.month, t.day, t.hour, t.minute, t.second); + fprintf(fp, "<trk>\n"); + fprintf(fp, " <name>track%04d</name>\n", fileno); + return fprintf(fp, "<trkseg>\n"); +} + +/** + * Write GPX trackpoint - add your geo-referenced data here + * + * @param fp file pointer + * @param t time structure + * @return result of last file write access + */ +int write_gpx_trkpt(FILE *fp, GPS_Time t) +{ + fprintf(fp, " <trkpt lat=\"%.4f\" lon=\"%.4f\">\n", gps.latitude(), gps.longitude()); + fprintf(fp, " <ele>%.1f</ele>\n", gps.altitude() * 1000.0); + fprintf(fp, " <time>%04d-%02d-%02dT%02d:%02d:%02dZ</time>\n", t.year, t.month, t.day, t.hour, t.minute, t.second); + fprintf(fp, " <name>TP%06d</name>\n", g_trkpt_no++); + return fprintf(fp, " </trkpt>\n"); +} + +/** + * Write GPX file footer (including end of track segment) + * + * @param fp file pointer + * @return result of last file write access + */ +int write_gpx_footer(FILE *fp) +{ + fprintf(fp, "</trkseg>\n"); + fprintf(fp, "</trk>\n"); + return fprintf(fp, "</gpx>\n"); +} + + +int main() { + GPS_Time t; + char filename[30]; + uint16_t fileno; + FILE *fp = NULL; + uint16_t log_wait = 0; + + button.mode(PullUp); + button.fall(&change_logging); + gps.baud(GPS_BAUDRATE); + lcd.cls(); + + while (1) { + // Wait for the GPS NMEA data to become valid. + // (Status flag in RMC message) + while (!gps.isTimeValid()) { + led_fix = !led_fix; + lcd.locate(21, 0); + lcd.printf("[no fix]"); + wait(1); + } + + gps.timeNow(&t); +#if defined(DEBUG) + pc.printf("The time/date is %02d:%02d:%02d %02d/%02d/%04d\r\n", + t.hour, t.minute, t.second, t.day, t.month, t.year); +#endif + lcd.locate(0, 0); + lcd.printf("%02d:%02d:%02d %02d/%02d/%04d %9s %10s", + t.hour, t.minute, t.second, t.day, t.month, t.year, + (gps.getGPSquality() > 0 ? "[fix] " : "[no fix]"), + (g_logging ? "[logging]" : "[stopped]")); + + // Check if at least four satellites produce a position fix and a valid quality. + if (gps.numOfSats() < 4 && gps.getGPSquality() != 0) { + // No fix or poor quality + led_fix = !led_fix; + } else { + // Fix valid and good quality + led_fix = 1; +#if defined(DEBUG) + pc.printf("Lat = %.4f Lon = %.4f Alt = %.1fkm\r\n", + gps.latitude(), gps.longitude(), gps.altitude()); +#endif + lcd.locate(0, 1); + lcd.printf("Lat: %07.4f Lon: %08.4f Alt: %.0fm ", + gps.latitude(), gps.longitude(), gps.altitude() * 1000.0); + + if (g_logging) { + if (log_wait-- == 0) { + log_wait = LOGGING_CYCLE_SEC - 1; + if (write_gpx_trkpt(fp, t) < 0) { + // write error + fclose(fp); + g_logging = false; + led_log = 0; + } + } + } + } + wait(1); + + // Logging state changed + if (g_changed) { + if (!g_logging) { + // Start logging + if (get_filename(filename, &fileno) != 0) { + error("Could not create file with unique name\n"); + } else { + // open file for writing + fp = fopen(filename, "w"); + if (fp == NULL) { + error("Could not open file for write\n"); + } else { + if (write_gpx_header(fp, t, fileno)) { + g_logging = true; + led_log = 1; + } + } + } + } else { + // Stop logging + write_gpx_footer(fp); + // close file + fclose(fp); + g_logging = false; + led_log = 0; + } + g_changed = false; + } // if (changed) + + } // while (1) +} \ No newline at end of file