Hackster is hosting Hackster Holidays, Ep. 6: Livestream & Giveaway Drawing. Watch previous episodes or stream live on Monday!Stream Hackster Holidays, Ep. 6 on Monday!
_larry_
Published

GNSS - Track Your Position with Espruino

Track your position and view your full route on a map!

BeginnerFull instructions provided3 hours3,954
GNSS - Track Your Position with Espruino

Things used in this project

Hardware components

Espruino Pico
Espruino Pico
×1
u-blox GG-1802 Dual GNSS Receiver
Ublox NEO-M8N - UART communication, Galileo enabled !
×1
Mini SD Card Module
×1
Lipo battery - 800 mAH
Battery small enough to fit the case
×1
Elegoo Prototype PCB - 5x7cm
I use the 5x7cm PCB for the case
×1
Plastic case - 8.8x5.7x3cm
Case for the final assembly
×1
Wemos shield accu
Lipo battery charger
×1
Micro JST PH 2.0
JST connector to powered the PCB and compatible with de Wemos Lipo battery charger!
×1
Micro sd card
×1
Solderless Breadboard Half Size
Solderless Breadboard Half Size
×1
USB to UART TTL
Serial UART Interface Converter Adapter Module
×1

Software apps and online services

u-blox U-Center App
App to configure the U-Blox GNSS module
Espruino Web IDE
The Web IDE is the preferred way to program Espruino

Story

Read more

Schematics

Project schema

There is one version in development mode, and another in standalone mode, the circuit being connected to a Lipo battery.

Project schema Fritzing

Wirering our module

Code

index.js

JavaScript
Main entry point for the app
const fs = require("fs");
const modBoardLed = require('mod-boardled');
const modGNSS = require('mod-gnss');
const modSD = require('mod-sd');

function onInit() {
    modSD.init();
    modSD.createNewFile("position-file.json");

    // modGNSS init method params :
    //-------------------------------
    // 1) Set the GNSS to write data or only console.log on the IDE
    //    modGNSS.MODE.DATA       // write data
    //    modGNSS.MODE.DEBUG      // log data
    //-------------------------------
    // 2) Set local time offset base on UTC global time (set to 2 - French summer time)
    //-------------------------------
    // 3 & 4 ) callbacks when satellite data available or not

    modGNSS.init(modGNSS.MODE.DATA, 2,
        //--- On Fix
        function (data) {
            modBoardLed.signalOnFix();
            modSD.writeData(data);
        },
        //--- On Search
        function () {
            modBoardLed.signalOnSearch();
        }
    );
}

mod-sd.js

JavaScript
Write a line of data (position, date time ...) on SD card every time GNSS get some
let o = {};

let fileName;

o.init = function(){
    const spi = new SPI();
    spi.setup({mosi:B5, miso:B4, sck:B3 });
    E.connectSDCard(spi,B10 /* CS on B0 available ! */);
};

o.createNewFile = function(fileName){
    fileName = filename;
    //--- Create file on SD card
    fs.writeFileSync(fileName, "");
};

o.writeData = function(lineData){

    const latitude  = lineData.lat;
    const longitude = lineData.lon;
    const elevation = lineData.altitude;
    const time      = lineData.time;

    const mmappedLineDate = `{"latitude":${latitude},"longitude":${longitude},"elevation":${elevation},"time":"${time}"},`;

    // Add new data line :
    fs.appendFileSync(fileName, "\n" + mmappedLineDate);

};

o.readFile = function(filename){
    console.log(fs.readFileSync(filename));
};

module.exports = o;

mod-boardled.js

JavaScript
Here to blink :)
var o = {};

//--- Blink RED led on Pico
o.signalOnSearch = function (){
    digitalWrite(LED1, true);
    setTimeout(function(){
        digitalWrite(LED1, false);
    },50);
};

//--- Blink GREEN led on Pico
o.signalOnFix = function () {
    digitalWrite(LED2, true);
    setTimeout(function(){
        digitalWrite(LED2, false);
    },50);
};

module.exports = o;

mod-gnss.js

JavaScript
Parse NMEA raw data to something more humanly understandable
var o = {};
o.MODE = {
    DEBUG: "DEBUG",
    DATA: "DATA"
};

o.localOffset = 2; // Default French local Offset (summer time)
let GNGGADataAvailable = false;
let GNGGASequenceList = [];

function parseGNSSData(lineStr) {

    let result = {
        dataAvailable: false,
        lat: null,
        lon: null,
        altitude: null,
        time: null
    };

    //--- Exemple GNGGA sentence :
    // $GNGGA,163251.00,4853.12648,N,00218.80880,E,2,11,2.06,76.7,M,46.2,M,,0000*71

    let lineList = lineStr.split(",");

    switch (lineList[0]) {

        case "$GNGGA": // Position data

            if (lineList[6] != 0) {
                GNGGADataAvailable = true;
                GNGGASequenceList = lineList;
            } else if (lineList[6] == 0) {
                result.dataAvailable = false;
                GNGGADataAvailable = false;
            }
            break;

        case "$GNRMC":  // Date/Time data

            if (GNGGADataAvailable) {

                //--- lat & long
                let latitude = GNGGASequenceList[2].substring(0, 2);
                let restLatitude = GNGGASequenceList[2].substring(2, GNGGASequenceList[2].length);
                let lat = +latitude + (restLatitude / 60);
                lat = lat.toFixed(6);

                let longitude = GNGGASequenceList[4].substring(0, 3);
                let restLongitude = GNGGASequenceList[4].substring(3, GNGGASequenceList[2].length);
                let lon = +longitude + (restLongitude / 60);
                lon = lon.toFixed(6);

                result.lat = lat;
                result.lon = lon;

                //--- altitude
                result.altitude = GNGGASequenceList[9];


                //-------------- Exemple data time : 2016-07-06T12:36:00Z
                //--- time
                let dateGNSS = "20" + lineList[9].substr(4, 2);
                dateGNSS = dateGNSS + "-" + lineList[9].substr(2, 2);
                dateGNSS = dateGNSS + "-" + lineList[9].substr(0, 2);

                let timeGNSS = lineList[1].substr(0, 2);
                timeGNSS = timeGNSS + ":" + lineList[1].substr(2, 2);
                timeGNSS = timeGNSS + ":" + lineList[1].substr(4, 2) + "Z";

                //--- add local offset to UTC (France / summer ):
                let time = new Date(dateGNSS + "T" + timeGNSS);
                time = time.setHours(time.getHours() + o.localOffset);
                time = new Date(time).toISOString();

                result.time = time;
                result.dataAvailable = true;

            } else {
                result.dataAvailable = false;
            }

            break;
    }


    return result;
}

o.init = function (mode, localOffset, onFix, onSearch) {

    o.localOffset = localOffset ;

    Serial1.setup(9600, {rx: B7, tx: B6});


    switch (mode) {

        case o.MODE.DATA:
            let gps = require("GPS").connect(Serial1);

            gps.on('line', function (lineStr) {

                if (parseGNSSData(lineStr).dataAvailable) {
                    onFix(parseGNSSData(lineStr));
                } else {
                    onSearch();
                }

            });
            break;

        case o.MODE.DEBUG:
            require("GPS").connect(Serial1, function (data) {

                if(data.fix == 0 ){
                    modBoardLed.signalOnSearch();
                } else if (data.fix != 0){
                    modBoardLed.signalOnFix();
                }

                console.log(data);
            });
            break;
    }
};

module.exports = o;

gps-to-gpx

JavaScript
Generate .gpx file
const fs = require("fs");
const lineReader = require('line-reader');
const createGpx = require("gps-to-gpx").default;

//--- Create valid Json file from text file

// valid Json Object template (see gps-to-gpx npm module doc)
let validTemplateObj = {
    activityType: "my-cool-activity",   // <--- CHANGE route activity here !
    startTime: "",
    waypoints: []
};

let waypoints = [];

lineReader.eachLine('./path/to/position-file.txt', function (line) { // <--- CHANGE your raw position text file path here !
    console.log(line);
    if(!!line || line != ""){
        waypoints.push(JSON.parse(line))
    }

}, () => {

    // Set start time 5 seconde before fist position time
    let startTime = new Date(waypoints[0].time);
    startTime = startTime.setSeconds(startTime.getSeconds() - 5);
    startTime = new Date(startTime).toISOString();


    //--- Create & write .gpx file
    const gpx = createGpx(waypoints, {
        activityName: validTemplateObj.activityType,
        startTime: startTime,
    });

    fs.writeFileSync("./path/to/position-file.gpx", gpx.toString()); // <--- CHANGE output .gpx path here !
    console.log("GPS to GPX, file write DONE!");

});

gps-to-gpx dependencies

JSON
Node js dependencies file
{
  "name": "scripts",
  "version": "1.0.0",
  "description": "",
  "main": "gps-to-gpx.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "gps-to-gpx": "^1.4.0"
  },
  "devDependencies": {
    "line-reader": "^0.4.0"
  }
}

Credits

_larry_

_larry_

3 projects • 0 followers
Thanks to Espruino and iforce2d.

Comments