Bangle.js Data Storage

In Bangle.js you may often want to store data over time, and then recall it later on your PC. In this tutorial we'll talk about how to do that.

There's a more general tutorial about storing data on Espruino as well.

We'll assume you've been through creating an app already so you know what's involved in creating one.

For this example, we're just going to have a menu that lets you write your current GPS location and time to a file along with a name (for example you could mark the location of every tree you find by standing by it and choosing Add Tree).

Note: We've already added a gpspoilog to the App Loader for you, so while you can upload this code yourself you'll find it is already all available for you.

Watch App

This is the code for the app - the majority of it is just creating the menu:

var menuItems = {
  "":{title:"GPS POI Log"},
  " ":{value:"No Fix"},
  "Add Tree" : ()=>addItem("Tree"),
  "Add Gate" : ()=>addItem("Gate"),
  "Add Flower" : ()=>addItem("Flower"),
  "Add Plant" : ()=>addItem("Plant")
};

var menu = E.showMenu(menuItems);
var gps = { fix : 0};
var gpsCount = 0;
// Create the file in append mode
var file = require("Storage").open("gpspoilog.csv","a");

function setStatus(msg) {
  menuItems[" "].value = msg;
  menu.draw();
}

Bangle.on('GPS',function(g) {
  gps = g;
  gpsCount++;
  var msg;
  if (g.fix) {
    msg = g.satellites + " Satellites";
  } else {
    msg = "No Fix";
  }
  setStatus(" "+"-\\|/"[gpsCount&3]);
});


function addItem(name) {
  if (!gps.fix) {
    setStatus("Ignored - no fix");
    return; // don't do anything as no fix
  }
  // The fields we want to put in out CSV file
  var csv = [
    0|getTime(), // Time to the nearest second
    gps.lat,
    gps.lon,
    gps.alt,
    name
  ];
  // Write data here
  file.write(csv.join(",")+"\n");
  setStatus("Written");
}


Bangle.loadWidgets();
Bangle.drawWidgets();
Bangle.setGPSPower(1);

First we need to open a file, which is done with this line:

var file = require("Storage").open("gpspoilog.csv","a");

This opens a file in append (a) mode. If it doesn't exist, it is created empty, but if it does then subsequent writes will go on the end.

And now, we just need to write data into the file in addItem using file.write:

file.write(csv.join(",")+"\n");

A few things are going on here:

Ok, now this is sorted, you can upload the app, run it, and get some data:

require("Storage").write("gpspoilog.info",{
  "id":"gpspoilog",
  "name":"GPS POI Log",
  "src":"gpspoilog.app.js"
});

Now, you can go for a walk - when you have a GPS signal you can start logging some data by choosing Add Plant, etc from the menu.

app

Reading the data

With the IDE

The easiest way to read the data is to open the file in the Web IDE. This may be enough for many of you:

In the app

You can access the data programmatically - either reading in chunks or line by line.

For example this code will write one line at a time:

var f = require("Storage").open("gpspoilog.csv","r");
var l = f.readLine();
while (l!==undefined) {
  console.log(l);
  l = f.readLine();
}

Reading with the App Loader

However, you can also add the loading functionality to the App Loader itself.

First, we'll need to add the app. This is basically as documented in Adding an app to the Bangle.js App Loader with one extra addition - a "interface" element in the JSON.

  { "id": "gpspoilog",
    "name": "GPS POI Logger",
    "shortName":"GPS POI Log",
    "icon": "app.png",
    "version":"0.01",
    "description": "A way to log points of interest with their GPS coordinates",
    "tags": "outdoors",
    "interface": "interface.html",
    "storage": [
      {"name":"gpspoilog.app.js","url":"app.js"},
      {"name":"gpspoilog.img","url":"app-icon.js","evaluate":true}
    ]
  }

Now you need to add the 'interface' file - copy the following to apps/gpspoilog/interface.html.

<html>
  <head>
    <link rel="stylesheet" href="../../css/spectre.min.css">
  </head>
  <body>
    <div id="data"></div>
    <button class="btn btn-default" id="btnSave">Save</button>
    <button class="btn btn-default" id="btnDelete">Delete</button>

    <script src="../../core/lib/interface.js"></script>
    <script>
var dataElement = document.getElementById("data");
var csvData = "";

function getData() {
  // show loading window
  Util.showModal("Loading...");
  // get the data
  dataElement.innerHTML = "";
  Util.readStorageFile(`gpspoilog.csv`,data=>{
    csvData = data.trim();
    // remove window
    Util.hideModal();
    // If no data, report it and exit
    if (data.length==0) {
      dataElement.innerHTML = "<b>No data found</b>";
      return;
    }
    // Otherwise parse the data and output it as a table
    dataElement.innerHTML = `<table>
    <tr>
      <th>Time</th>
      <th>Lat</th>
      <th>Lon</th>
      <th>Alt</th>
      <th>Type</th>
    </tr>`+data.trim().split("\n").map(l=>{
      l = l.split(",");
      return `<tr>
      <td>${(new Date(l[0]*1000)).toLocaleString()}</td>
      <td>${l[1]}</td>
      <td>${l[2]}</td>
      <td>${l[3]}</td>
      <td>${l[4]}</td>
      </tr>`
    }).join("\n")+"</table>";
  });
}

// You can call a utility function to save the data
document.getElementById("btnSave").addEventListener("click", function() {
  Util.saveCSV("gpsdata", csvData);
});
// Or you can also delete the file
document.getElementById("btnDelete").addEventListener("click", function() {
  Util.showModal("Deleting...");
  Util.eraseStorageFile("gpspoilog.csv", function() {
    Util.hideModal();
    getData();
  });
});
// Called when app starts
function onInit() {
  getData();
}

    </script>
  </body>
</html>

This does a few basic things:

For more information about the available functions you can check out the interface.js file.

To use your new interface HTML file:

And that's it! When you have it working as you want you can even submit it to the official App Loader site

This page is auto-generated from GitHub. If you see any mistakes or have suggestions, please let us know.