Category Archives: Mobile

Matters relating to mobile services, app development, mapping and data provision

IOT Project – Using an ESP8266 with IOS Push Notifications

Following on from a recent post http://www.geothread.net/iot-project-using-an-esp32-device-to-check-a-web-service/, here at Cranfield University, we wanted to explore the use of ‘Push Notifications‘ to a mobile phone or tablet from an event triggered on a ESP8266 Internet of Things device. This could be useful for a range of applications – for example following a trigger from a sensor to indicate that some threshold has been exceeded (e.g. a set temperature or humidity), or from the utility previously described, testing periodically to see if a web service is running or not.

Contents:
Parts required
Push notification configuration and Prowl
Hardware
Configuring Arduino
Test Sketch
Results
Next Steps
Buttons Sketch
Web Service monitoring with push notifications

To get this all working we need a simple test rig, described here, to bring together all the parts.

Parts required:

top
IOS device – Apple iPhone (Android is OK too – comments below)
– installed with ‘Prowl’ app
ESP8266 device – We used a ‘TOOGOO ESP8266’
Arduino IDE correctly configured
– installed with ESP8266WiFi and EspProwl libraries
– suitable serial driver

Push notification configuration and Prowl:

top
If we want to be informed of Internet events, we can make devices trigger communications in. number of ways – examples being emails, tweets, and push notifications. Of these, ‘Push Notifications’ are the most immediate. They can put a note up on the home screen of a mobile device (phone or tablet) to draw immediate attention. We used a mobile phone for this test, an Apple iPhone running IOS.

The leading IOS push notifier is Prowl – https://www.prowlapp.com. The web service is free to use, so we registered a login account with Prowl. The next step is to generate a unique API keycode in Prowl. In doing this, a short description can be added to remind what it relates to, e.g. ‘IoT notifications’. One can have multiple API key codes for different applications/projects.

Once that is done, the next step is to install the Prowl app on the iPhone. The app is available on the iTunes App Store.

Note that the app is not free, but it is priced modestly, and is fair given the great service Prowl provides. Once the app is installed, the account details set above on the website can be entered.

We are now ready to receive push notifications.

Android

The Prowl app is designed for IOS devices. Android has its own equivalent utilities such as PushBullet, described in a similar tutorial blog online here.

Hardware:

top
The last post used an ESP32 device. These chips are newer than the ESP8266 devices, being slightly more expensive, and with BlueTooth as an additional feature alongside WiFi. To keep costs down, we used the TOOGOO ESP8266 device, available from a range of providers, eg. Amazon. The ESP8266 is described well in this blog article.

Configuring Arduino:

top
The ESP8266 device is designed to operate with the Arduino IDE development environment. As described fully in the earlier post, using a MacBook, one needs to install a serial driver to communicate with the hardware, and the appropriate cables. Next, as the device is an ESP8266, we need to install the appropriate device libraries.

In the Arduino ‘Preferences’ dialog, one can add references to external libraries through adding a line to the source. We added the ESP8266 source thus:

http://arduino.esp8266.com/stable/package_esp8266com_index.json

Now in the Arduino, under ‘Sketch’ -> ‘Include Library’, we can see ‘ESP8266WiFi’, which can be added to our test sketch.

For the particular TOOGOO ESP8266 device, we used the Arduino board definition ‘NodeMCU 1.0 (ESP-12e Module)’.

EPS8266 notifications

We now need a means in the Arduino code sketch to configure and initiate push notifications. Fortunately we can use the excellent EspProwl library described online at https://github.com/marcos69/EspProwl. The code here can be downloaded to the library folder of the Arduino installation folder. Now the library can be added to the Sketch code (e.g. #include <EspProwl.h>), and then used in the code.

Once this is all in place, we can develop a minimal test sketch to open a WiFi connection and initiate a push notification.

Test Sketch code:

top

#include &lt;ESP8266WiFi.h&gt;
#include &lt;EspProwl.h&gt;

// WiFi parameters
char* ssid = "MYSSID";
char* password = "MYWIFIKEY";

void setup() {
       // Start Serial
       Serial.begin(115200);
       WiFi.begin(ssid, password);
       while (WiFi.status() != WL_CONNECTED) {
          delay(500);
          Serial.print(".");
       }
       Serial.println("");
       Serial.println("WiFi now connected at address");
       // Print the IP address
       Serial.println(WiFi.localIP());
       EspProwl.begin();
       EspProwl.setApiKey("MY_PROWL_API_CODE");
       EspProwl.setApplicationName("ESP6266 Prowl test");
       int returnCode = EspProwl.push("Hello World event", "Initial notification", 0);
}

void loop() {
  // not used in this test
}

Results:

top

The result of the test sketch is that once it runs, a WiFi connection is made, and then a push notification initiated. Note that the push here is placed in the ‘Setup’ block – so it only runs once (fine for testing). Note also that one can firstly set an Application Name, and secondly, a Push Title and Message. This permits a lot of flexibility in the messaging options open. Hopefully when run the results will appear on the mobile device as shown.

The Prowl app does permit some filtering and the ability to (temporarily) filter messages and have quiet times etc. which could be useful if there are a lot of messages appearing.

Next Steps:

top
This project has shown how to create push notifications, triggered by the ESP8266 device. Note, we used the Setup block in the code example above – in reality one would use the ‘Loop’ block for event monitoring and message triggering. However, more programmatic control is needed for this to be used in a real project. For example, if one is say monitoring temperature every few minutes, the operator may not wish to receive a push notification at the same frequency, or once messaged, another trigger notification may not be wanted until the threshold is crossed once more. In either case, a secondary ‘loop’ or register could be configured in the code sketch, within the continuously running 5 minute loop, so as to restrict messaging to say an hourly basis or just when significant changes occur – this might prove especially relevant if some other action is being undertaken at the finer timestep – such as database logging of data. It depends on the application – but overall push notifications offer a very useful tool for building up control and monitoring systems.

Buttons Sketch:

top
The hardware device also has a few in-built buttons. So we created a simple sketch that triggers push notifications when they are pressed and released:

#include &lt;avdweb_Switch.h&gt;
// See https://github.com/avandalen/avdweb_Switch
#include &lt;ESP8266WiFi.h&gt;
#include &lt;EspProwl.h&gt;
// Drawing on code at https://github.com/marcos69/EspProwl

// Buttons - FLATH, RSET, D5, D6, D7
const byte buttonUp_Pin = D6;
Switch buttonUp = Switch(buttonUp_Pin);
const byte buttonDown_Pin = D7;
Switch buttonDown = Switch(buttonDown_Pin);
const byte buttonPress_Pin = D5;
Switch buttonPress = Switch(buttonPress_Pin);
int i;

// WiFi parameters
char* ssid = "MYSSID";
char* password = "MYWIFIKEY";

void setup() {
       Serial.begin(115200);
       Serial.println("Starting:");
       WiFi.begin(ssid, password);
       while (WiFi.status() != WL_CONNECTED) {
          delay(500);
          Serial.print(".");
       }
       Serial.println("");
       Serial.println("WiFi now connected at address");
       // Print the IP address
       Serial.println(WiFi.localIP());
       EspProwl.begin();
       EspProwl.setApiKey("MY_PROWL_API_CODE");
       EspProwl.setApplicationName("ESP6266 Prowl test");
       int returnCode = EspProwl.push("Button press test", "Ready", 0);
}

void loop() {
       buttonUp.poll();
       if(buttonUp.pushed()) {
           Serial.print(++i); Serial.print(" "); Serial.print("Up pushed, ");
           int returnCode = EspProwl.push("Up", "Pushed", 0);
       }
       if(buttonUp.released()) {
           Serial.println("Up released");
           int returnCode = EspProwl.push("Up", "Release", 0);
       }
       buttonDown.poll();
       if(buttonDown.pushed()) {
           Serial.print(++i); Serial.print(" "); Serial.print("Down pushed, ");
           int returnCode = EspProwl.push("Down", "Pushed", 0);
       }
       if(buttonDown.released()) {
            Serial.println("Down released");
            int returnCode = EspProwl.push("Down", "Release", 0);
       }
       buttonPress.poll();
       if(buttonPress.pushed()) {
            Serial.print(++i); Serial.print(" "); Serial.print("Press pushed, ");
            int returnCode = EspProwl.push("Press", "Pushed", 0);
       }
       if(buttonPress.released()) {
            Serial.println("Press released");
            int returnCode = EspProwl.push("Press", "Release", 0);
       }
}


Web Service monitoring with push notifications:

top
An earlier blog showed how to use an EPS device to monitor a web service, http://www.geothread.net/iot-project-using-an-esp32-device-to-check-a-web-service/. We can revisit that code now, and add in Prowl push alert code. We can also make the code a little more sophisticated too to avoid false positives. As before you will need to add in the SSID and WiFi passcode, and the website address and REST endpoint, as well as the Prowl API code. Here is the source code, edited to work on an EPS8266 device, rather than EPS32 device (its the slightly cheaper older chip).

// TTGO EPS8266_WebServiceCheck : WiFi & Bluetooth Battery ESP32 Module - webservices checker

// Import required libraries
#include <Wire.h>
#include <OLEDDisplayFonts.h>
#include <OLEDDisplay.h>
#include <OLEDDisplayUi.h>
#include <SSD1306Wire.h>
#include <SSD1306.h>
#include "images.h"
#include "fonts.h"
#include <ESP8266WiFi.h>
#include <EspProwl.h>

// The built-in OLED is a 128*64 mono pixel display
// i2c address = 0x3c
// SDA = 5
// SCL = 4
SSD1306 display(0x3c, 5, 4);

// Web service to check
const int httpPort = 80;
const char* host = "MYWEBSERVICE_HOSTNAME";

// WiFi parameters
const char* ssid = "MYSSID";
const char* password = "MYWIFIKEY";

void setup() {
       // Initialize the display
       display.init();
       //display.flipScreenVertically();
       display.setFont(Roboto_Medium_14);

       // Start Serial
       Serial.begin(115200);
       // Connect to WiFi
       display.drawString(0, 0, "Going online");
       display.drawXbm(34, 14, WiFi_Logo_width, WiFi_Logo_height, WiFi_Logo_bits);
       display.display();
       WiFi.begin(ssid, password);
       while (WiFi.status() != WL_CONNECTED) {
          delay(500);
          Serial.print(".");
       }
       Serial.println("");
       Serial.println("WiFi now connected at address");
       // Print the IP address
       Serial.println(WiFi.localIP());
       display.clear();
       EspProwl.begin();
       EspProwl.setApiKey("MY_PROWL_API_CODE");
       EspProwl.setApplicationName("Web Service Checker");
       int returnCode = EspProwl.push("Checker", "System up", 0);
}

void loop() {
       Serial.print("\r\nConnecting to ");
       Serial.println(host);
       display.clear();
       display.setTextAlignment(TEXT_ALIGN_LEFT);
       display.drawString(0, 0, "Check web service");
       display.display();
       Serial.println("Check web service");
       
       // Setup URI for GET request
       String url = "/PATH/TO/REST/ENDPOINT/";
       // if service is up ok, return will contain: 'Service running'

       WiFiClient client;
       if (!client.connect(host, httpPort)) {
         Serial.println("Connection failed");
         display.clear();
         display.drawString(0, 0, "Connection failed");
         display.display();
         return;
       }

       client.print("GET " + url + " HTTP/1.1\r\n");
       client.print("Host: " + (String)host + "\r\n");
       client.print("Authorization: Basic YWRtaW46NSs2KndralhLcVApOVd2JWokQ2o=\r\n");
       client.print("User-Agent: Arduino/1.0\r\n");
       client.print("Cache-Control: no-cache\r\n\r\n");

       // Read all the lines of the reply from server
       delay(800);
       bool running = false;
       while (client.available()) {
         String line = client.readStringUntil('\r\n');
         Serial.println(line);
         if (line == "Service running") {
           running = true;
         }
       }
       if (running == true) {
           display.drawString(0, 25, "Service up OK");
           display.display();
           delay(3000);
           ProwlAlert(running);       
       } else {
           display.drawString(0, 25, "Service DOWN");
           display.display();
           delay(3000);
           ProwlAlert(running);       
       }

       Serial.println();
       Serial.println("Closing connection");
       Serial.println("=================================================");
       Serial.println("Sleeping");
       display.clear();
       display.drawString(0, 0, "Closing connection");
       display.display();
       delay(1000);
       display.clear();
       client.stop();
       // progress bar
       for (int i=1; i<=28; i++) {
         float progress = (float) i / 28 * 100;
         delay(500); // = all adds up to delay 14000 (14 sec)
         // draw percentage as String
         display.drawProgressBar(0, 32, 120, 10, (uint8_t) progress);
         display.display();
         display.setTextAlignment(TEXT_ALIGN_CENTER);
         display.drawString(64, 15, "Sleeping " + String((int) progress) + "%");
         display.display();
         display.clear();
         Serial.print((int) progress);Serial.print(",");
       }
       delay (1000);
}

int ProwlAlert(bool running) {
        // prevent too many false positives
        static int counter = 0;
        static bool resumed = true;
        int returnCode = -999;
        Serial.print("Status: ");
        Serial.print(running);
        Serial.print(" | Run count: ");
        Serial.println(counter);
        if (running == true) {
           counter = 0;
           if (resumed == false) {
              returnCode = EspProwl.push("Checker", "Service resumed as normal", 0);
              resumed = true;
           }
        } else {
           counter = counter + 1;
           resumed = false;       
        }
        if (counter == 5) {
           returnCode = EspProwl.push("Checker", "Service detected as being down - attention required", 0);
           counter = 0;
        }
        return returnCode;
}

Soilscapes – Mobile soil mapping app

Soilscapes app

The Soilscapes app is currently available for iPhone and iPad

Cranfield University has developed its first app, which is distributed freely to Apple mobile devices through the iTunes store.

The Soilscapes app provides mobile access to the national information on soil from the popular Soilscapes service which currently can be accessed on Cranfield’s LandIS (land information system) website – www.landis.org.uk/soilscapes

LandIS, which holds the English and Welsh national soil maps and property data, is managed by Cranfield with Defra (the Department for Environment, Food & Rural Affairs). The new app forms part of a wider University drive to extend mapping to mobile apps as well as making the unique national capabilities held at Cranfield more widely available.

The app allows users to inspect the soil properties in their neighbourhood and to understand the extent and diversity of soils in England and Wales, providing for public education and awareness, something key to current thinking and needs at Defra.

The app is built around many of the technical concepts and frameworks highlighted here on GeoThread, including those covered in our guide to building a mobile mapping application. We hope that now much of the groundwork and testing has been done with this application, it will pave the way for further mobile mapping apps in the future.

Whilst the app is currently only available for Apple iOS devices, an Android version is now ready for launch and we hope to make it available through the Google Play store very soon.

Building a mobile mapping application

Leaflet mobile mapping application

Leaflet mobile mapping application

Our earlier articles on mobile application development demonstrated the process of getting an app up and running on an Android phone using Adobe PhoneGap and GitHub. For the purposes of this article we’re going to assume that you are now comfortable with that process and will demonstrate how easy it is begin building mobile mapping applications. This is something we are developing a lot of interest in here at Cranfield University.

As with previous examples, the app is going to be based on a package of HTML, CSS and JavaScript code and will therefore use one of the many JavaScript web mapping APIs available. Any of the following APIs would work well:

  • Google Maps API
  • ArcGIS API for Javascript
  • Leaflet
  • OpenLayers
  • OS OpenSpace API

The decision on which one to use will depend on what the functional requirements of your app are, how familiar you are with a particular API and a number of other factors. For this example we are going to build a simple mobile web map using the Leaflet mapping API. Leaflet is an extremely lightweight JavaScript library which makes it ideal for bundling with a mobile app, as we shall see shortly. It is also relatively simple to use and has great flexibility for puling in geospatial resources from a number of different sources, in a variety of formats.

The Leaflet website provides a page of useful examples to get you started with creating basic web maps. Below is a typical web page that would display a basic web map using Leaflet. You can save this code to a HTML file and view it in a browser to test it out for yourself.

<!DOCTYPE html>
<html>
<head>
<title>Basic Leaflet Map</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.7.1/leaflet.css" />

<style type="text/css">
body {
	padding: 0;
	margin: 0;
}
html, body, #map {
	height: 100%;
}
</style>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
</head>
<body>
<div id="map"></div>
<script src="http://cdn.leafletjs.com/leaflet-0.7.1/leaflet.js"></script> 
<script>
	var map = L.map('map').setView([52.04, -0.73], 12);
        L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
		maxZoom: 18			
	}).addTo(map);
</script>
</body>
</html>

The process of turning this into a mobile app is very straightforward, assuming you’ve already got your PhoneGap/Cordova framework in place. Rather than build a new index.htm for our project, we’ll take the default index.htm that has been created for us and drop in the relevant sections of the Leaflet map example above into the correct places. The index.htm file that sits within the www folder of you mobile app framework (taken from the default PhoneGap example on GitHub) should look something like this:

<html>
    <head>
        <meta charset="utf-8" />
        <meta name="format-detection" content="telephone=no" />
        <meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, height=device-height, target-densitydpi=device-dpi" />
        <link rel="stylesheet" type="text/css" href="css/index.css" />
        <title>Hello World</title>
    </head>
    <body>
        <div class="app">
            <h1>PhoneGap</h1>
            <div id="deviceready" class="blink">
                <p class="event listening">Connecting to Device</p>
                <p class="event received">Device is Ready</p>
            </div>
        </div>
        <script type="text/javascript" src="phonegap.js"></script>
        <script type="text/javascript" src="js/index.js"></script>
        <script type="text/javascript">
            app.initialize();
        </script>
    </body>
</html>

The key parts of the Leaflet map example are as follows:

<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.7.1/leaflet.css" />

This ensures the appropriate CSS files are loaded so that the map is is styled correctly.

body {
	padding: 0;
	margin: 0;
}
html, body, #map {
	height: 100%;
}

Here we are defining some layout parameters, including how much space the #map element of the page should use

<div id=”map”></div>

This is the container in the body of the page that will house our map

<script src="http://cdn.leafletjs.com/leaflet-0.7.1/leaflet.js"></script> 

Load in the Leaflet API

<script>
	var map = L.map('map').setView([52.04, -0.73], 12);
	L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
		maxZoom: 18			
	}).addTo(map);
</script>

This is the code that generates and renders the map on the page.

Dropping these elements into our existing index.htm whilst retaining the important components (such as app.initialise(); and the calls to phonegap.js and js/index.js) that are already there, gives a page that looks as follows:

<html>
    <head>
        <meta charset="utf-8" />
        <meta name="format-detection" content="telephone=no" />
        <meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, height=device-height, target-densitydpi=device-dpi" />
        <link rel="stylesheet" type="text/css" href="css/index.css" />
		<meta name="viewport" content="width=device-width, initial-scale=1.0">
		<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.7.1/leaflet.css" />
        <title>Basic Leaflet Mobile Map</title>
    </head>
    <body>
        <div class="app">
            <div id="map"></div>

        </div>
	<script src="http://cdn.leafletjs.com/leaflet-0.7.1/leaflet.js"></script> 
	<script>
		var map = L.map('map').setView([52.04, -0.73], 12);		
	        L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
			maxZoom: 18					
		}).addTo(map);
	</script>
        <script type="text/javascript" src="phonegap.js"></script>
        <script type="text/javascript" src="js/index.js"></script>
        <script type="text/javascript">
            app.initialize();
        </script>
    </body>
</html>

This can now be compiled and deployed to your device using the process detailed earlier to give you a simple mobile mapping application.

Hosting libraries and APIs locally

Before we finish, we shall make one more minor alteration to our app to improve efficiency and performance. Currently the app loads up the Leaflet API from http://cdn.leafletjs.com/leaflet-0.7.1/leaflet.js. We are going to take a copy of the full API, copy it to our app’s folder tree and deploy it to the phone so that our app can reference it locally rather than having to load it over web each time the app is launched. This will reduce the reliance on network connectivity and speed up application launch times as well as eliminate any reliance on the Leaflet website; we want our app to continue to work regardless of whether any remote websites may be experiencing problems of their own.

The full leaflet API can be downloaded here:
http://leafletjs.com/download.html
Unpack the zipfile and place the contents into a subfolder of your www folder named leaflet within your mobile app framework. All that needs to be done now is to modify the calls in your code that load the Leaflet API and CSS so that they reference your local copies. They should be changed from

<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.7.1/leaflet.css" />
<script src="http://cdn.leafletjs.com/leaflet-0.7.1/leaflet.js?2"></script>

to

<link rel="stylesheet" href="leaflet/leaflet.css" />
<script src="leaflet/leaflet.js"></script>

This change will make your app almost fully self sufficient, without any reliance on loading libraries from external websites. Note that the map tiles that make up your map are still being loaded from the following location:
http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png
Your app will therefore still require a working internet connection in order to function correctly. In our next post we’ll discuss bundling a selection of map tiles with your application so it can be used completely offline with no internet connection

Leaflet mobile mapping application

Leaflet mobile mapping application

Mobile App Development with Dojo

Our earlier blog article about Mobile App Development With PhoneGap showed how easy it is to develop a basic mobile app using the Apache Cordova / Adobe PhoneGap framework, compiled with Adobe PhoneGap Build. However, the resultant app is not styled in a way that resembles a ‘native app’.
RockPaperScissors_Game
In developing apps here at Cranfield University we are increasingly adopting the ‘Dojo’ JavaScript API (http://dojotoolkit.org/).
Dojo
Dojo allows a powerful open source API framework for developing industry-strength web applications, and includes a powerful suite of tools for developing mobile apps. To give an example of this in action, following the previous ‘Rock, Paper, Scissors’ app article, the following code can be substituted for the file ‘index.htm’.

<!DOCTYPE html>
<html>
	<head>
		<meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, height=device-height, target-densitydpi=device-dpi" />
		<meta name="apple-mobile-web-app-capable" content="yes"/>
		<meta names="apple-mobile-web-app-status-bar-style" content="black-translucent" />
		<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
		<!--<link rel="stylesheet" type="text/css" href="css/index.css" />-->
		<title>Rock Paper Scissors Game</title>
		<!-- set Dojo configuration, load Dojo -->
		<script>
			dojoConfig= {
				async: true,
				mblAlwaysHideAddressBar:true
			};
		</script>
		<script src="dojox/mobile/deviceTheme.js"></script>
		<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/dojo/1.9.2/dojo/dojo.js"></script> 
		<script>
			require(["dojox/mobile",
			         "dojox/mobile/parser",
			         "dojox/mobile/compat",
			         "dojox/mobile/deviceTheme",
				 "dojox/mobile/Heading",
			         "dojox/mobile/ScrollableView",
			         "dojox/mobile/Button",
			         "dojo/dom-attr",
			         "dojo/_base/array",
			         "dojox/mobile/compat",
			         "dojo/domReady!"],
					function(dm, parser, compat, deviceTheme, heading, scrollableView,
					         button, domAttr, baseArray, compat) {
					parser.parse();
			});
		</script>
	<script type="text/javascript">
		var CHOICE = { // set up enumerations
			ROCK     : {value: 1, name: "Rock", code: "R"}, 
			PAPER    : {value: 2, name: "Paper", code: "P"}, 
			SCISSORS : {value: 3, name: "Scissors", code: "S"}
		};
		var plays = 0;
		var humanWins = 0;
		var compWins  = 0;

		function playGame(play) {
			if(typeof play === 'undefined'){return;}
			var humanChoice = null;
			var compChoice  = null;
			plays++;
			
			if (play==='R') {
				humanChoice = CHOICE.ROCK;
			} else if (play==='P') {
				humanChoice = CHOICE.PAPER;
			} else if (play==='S') {
				humanChoice = CHOICE.SCISSORS;
			}
	
			var computer = (Math.floor( Math.random() * 3 + 0.5 ));
				if ( computer === 1 ) {
					compChoice = CHOICE.ROCK }
				else { if ( computer === 2 ) {
					compChoice = CHOICE.PAPER }
				else { 
					compChoice = CHOICE.SCISSORS }
				}
			document.getElementById('result').innerHTML="<p>You chose '" + humanChoice.name + "' and I chose '" + compChoice.name + "'</p>";
			<!--document.getElementById('log').innerHTML+="H:" + humanChoice.code + " C:" + compChoice.code + "|";-->
	
			var win = humanChoice.value - compChoice.value;
			if ( win === 0 ) { 
				document.getElementById('result').innerHTML += "<p><b>So we've drawn</b></p>" } 
			else {
				if ( win === -2 || win === 1 ) { 
					document.getElementById('result').innerHTML += "<p><b>So you win</b></p>"; humanWins++; }
				else {
					if ( win === -1 || win === 2 ) { 
						document.getElementById('result').innerHTML += "<p><b>So I win</b></p>"; compWins++; }
					else {}
				}
			}
			document.getElementById('tally').innerHTML = "<p><i>Leaderboard</i>&nbsp;&nbsp;Plays:&nbsp;" + plays + "<br />Your wins:&nbsp;" + humanWins + "&nbsp;&nbsp;&nbsp;My wins:&nbsp;" + compWins + "&nbsp;&nbsp;&nbsp;Draws:&nbsp;" + (plays - (humanWins + compWins)) + "</p>";
			if ( compWins < humanWins ) { 
				document.getElementById('tally').innerHTML += "<p>You're in the lead</p>"; } 
			else if ( compWins > humanWins ) {
				document.getElementById('tally').innerHTML += "<p>I'm in the lead</p>"; } 
			else if ( compWins == humanWins ) {
				document.getElementById('tally').innerHTML += "<p>We're running head to head</p>"; }
		}
		
		function restart() {
			plays = 0;
			humanWins = 0;
			compWins  = 0;
			document.getElementById('result').innerHTML = "&nbsp;";
			document.getElementById('tally').innerHTML = "&nbsp;";
			document.getElementById('log').innerHTML = "&nbsp;";
		}
		</script>
		<style type="text/css">
			html, body{
				height: 100%;
				overflow: hidden;}
			p {font-size:25px;}
			button {font-size:45px; width:250px; margin:20px 50px;}
			.mblBlueButton {
				background-image: -webkit-gradient(linear, left top, left bottom, from(#7a9de9), to(#2362dd), color-stop(0.5, #366edf), color-stop(0.5, #215fdc));
				background-image: linear-gradient(to bottom, #7a9de9 0%, #366edf 50%, #215fdc 50%, #2362dd 100%);
				color: white;
			}
			.mblBlueButtonSelected {
				background-image: -webkit-gradient(linear, left top, left bottom, from(#8ea4c1), to(#4a6c9b), color-stop(0.5, #5877a2), color-stop(0.5, #476999));
				background-image: linear-gradient(to bottom, #8ea4c1 0%, #5877a2 50%, #476999 50%, #4a6c9b 100%);
			}
		</style>
	</head>
	<body style="visibility:hidden;">
	<div id="settings" dojoType="dojox/mobile/View">
		<div dojoType="dojox/mobile/Heading" data-dojo-props="fixed: 'top'">
			Rock Paper Scissors Game
		</div>
		<div data-dojo-type="dojox.mobile.RoundRect">
			<h2 dojoType="dojox.mobile.RoundRectCategory">Choose ...</h2>
			<button dojoType="dojox.mobile.Button" class="mblBlueButton" label="Rock" onClick="playGame('R')"></button>
			<button dojoType="dojox.mobile.Button" class="mblBlueButton" label="Paper" onClick="playGame('P')"></button>
			<button dojoType="dojox.mobile.Button" class="mblBlueButton" label="Scissors" onClick="playGame('S')"></button>
			<button dojoType="dojox.mobile.Button" class="mblButton" label="Restart" onClick="restart()"></button>
			<span id="result">&nbsp;</span><br />
			<span id="tally">&nbsp;</span><br />
			<span id="log">&nbsp;</span><br />
		</div>
	</div>
	</body>
</html>

The result of this is a styled app taking on the visual characteristics of a native Android app. Note the use of the Dojo ‘dojox/mobile/deviceTheme’ require. This permits automatic styling for all the different mobile platforms. For more information, see the excellent mobile tutorials on the Dojo website, as well as the mobile showcase. Note also that this code includes a live online call to load the Dojo API. Clearly this will not work if your device is offline. To resolve this, a further refinement would be to create a custom API library build stored locally containing the Dojo code needed.
RockPaperScissors_Game_Dojo

Mobile App Development With PhoneGap

Introduction – Building mobile apps
One area of computing we are developing a keen interest in here at Cranfield University is the development of mobile apps. The future is firmly mobile and meeting the explosive growth in mobile phone and tablet computing throws traditional software development approaches in the air. New approaches are needed to develop app tools for location-based mapping and GIS. With much to learn to achieve this, this article outlines some of the basic steps needed to develop apps for a mobile device. The app developed here will be a simple ‘RockPaperScissors’ game, ported to an Android phone, and using the PhoneGap development platform. You will need an Android device.

Cross-platform development
The first issue to recognise is the sheer diversity of mobile devices and operating systems available – with Apple IOS, Google Android, Windows Phone and Blackberry to choose from by example. Each platform has its own preferred development tools and deployment approaches. Developing the personal skills to develop native code apps for each of these platforms would be a huge task. So ideally a means is needed to allow the development of one set of code that can then be ported across these platforms. A number of tools exist that achieve this, but one that stands out for us is the combined offering of Apache Cordova (http://cordova.apache.org/) and Adobe PhoneGap (https://build.phonegap.com/).

PhoneGap and Cordova
Apache Cordova is a platform for building native mobile applications using HTML, CSS and JavaScript. If you can develop a webpage, you should be able to build a Cordova app. Apache note that “Cordova graduated in October 2012 as a top level project within the Apache Software Foundation (ASF)” and that “it will always remain free and open source”. The software company Adobe have then picked up Cordova and used it to produce their own SDK ‘PhoneGap’. Like Cordova, PhoneGap allows native mobile app development using only HTML, CSS and JavaScript. In addition to PhoneGap itself, Adobe also provide a cloud-based mobile app compiler for PhoneGap apps, capable of producing compiled code for each of the major mobile architectures, PhoneGap Build (https://build.phonegap.com/): this is used in developing the app here.

PhoneGapBuild

Setup – preparing to build a mobile app
PhoneGap Build accepts a package of the HTML, CSS and JavaScript code. PhoneGap Build is designed to retrieve code held in the GitHub repository (https://github.com/). Therefore, if you don’t already have accounts, for this exercise you will need to create personal accounts in both PhoneGap Build, and in GitHub. You must also install and configure a copy of Git on your local development computer. For Windows PCs, the best option is ‘GitHub for Windows’ (http://windows.github.com/).
GitForWindows
Once installed, Git can be linked by association with your github.com account. The local Git acts to hold a set of files linked to a software project (a ‘repository’), these can then be uploaded to the GitHub website (automatically), and from there, loaded straight into the PhoneGap Build tool. Local project files (HTML, CSS and JavaScript code) can be edited by any generic text editor, for example NotePad++ (http://notepad-plus-plus.org/).

GitHub

Summary of setup tasks:
1. Create account at GitHub (https://github.com/).
2. Create account at PhoneGap Build Build (https://build.phonegap.com/).
3. Install a local copy of ‘GitHub for Windows’ (http://windows.github.com/) – link to GitHub.
4. If needed, install a good text editor such as NotePad++ (http://notepad-plus-plus.org/).

Assembling the source code
GitHub already contains a basic PhoneGap example app to get you going. This ‘repo’ (repository) can be copied and then adapted to create your own custom project. To achieve this, first search for, then download as a ‘zip’ file the existing ‘phonegap / phonegap-start’ repo from the GitHub website. After creating a new local Git repo, the ‘www’ folder from the ‘phonegap-start’ app can be copied to the new repo folder, and then adapted by editing.

The basic workflow for developing a basic mobile app is as follows:
1. Log in to GitHub and locate the existing code ‘phonegap / phonegap-start’ (https://github.com/phonegap/phonegap-start).
2. Download this example repo as a zip file to a local file – all required source code is included.
3. Use ‘GitHub for Windows’ to create your own repo, called ‘RockPaperScissors’.
4. Now extract the ‘www’ folder from the downloaded zip file and copy to the new repo folder.
5. Next, you can edit and adapt the code. For this rock paper scissors game tutorial, replace the contents of the file ‘index.htm’ with the following code:

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8" />
		<meta name="format-detection" content="telephone=no" />
		<meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, height=device-height, target-densitydpi=device-dpi" />
		<link rel="stylesheet" type="text/css" href="css/index.css" />
		<title>Rock Paper Scissors Game</title>
		<style type="text/css">
			p, form {font-size:25px;}
			button  {font-size:45px; width:200px; margin:25px 50px;}
		</style>
	</head>
	<body>
	<h1>Rock Paper Scissors Game</h1>
	<br />
	<p><i>What do you choose?</i></p>
	<button type="button" onclick="PlayGame('R')">Rock</button></br>
	<button type="button" onclick="PlayGame('P')">Paper</button></br>
	<button type="button" onclick="PlayGame('S')">Scissors</button></br>
	<span id="result">&nbsp;</span><br />
	<span id="tally">&nbsp;</span><br />
	<span id="log">&nbsp;</span><br />
				
	<script type="text/javascript">
		var CHOICE = { // set up enumerations
			ROCK     : {value: 1, name: "Rock", code: "R"}, 
			PAPER    : {value: 2, name: "Paper", code: "P"}, 
			SCISSORS : {value: 3, name: "Scissors", code: "S"}
		};
		var humanWins = 0;
		var compWins = 0;

		function PlayGame(play) {
			var humanChoice;
			var compChoice;
	
			if (play==='R') {
				humanChoice = CHOICE.ROCK;
			} else if (play==='P') {
				humanChoice = CHOICE.PAPER;
			} else if (play==='S') {
				humanChoice = CHOICE.SCISSORS;
			}
	
			var computer = (Math.floor( Math.random() * 3 + 0.5 ));
				if ( computer === 1 ) {
					compChoice = CHOICE.ROCK }
				else { if ( computer === 2 ) {
					compChoice = CHOICE.PAPER }
				else { 
					compChoice = CHOICE.SCISSORS }
				}
			document.getElementById('result').innerHTML="<p>You chose '" + humanChoice.name + "' and I chose '" + compChoice.name + "'</p>";
			//document.getElementById('log').innerHTML+="H:" + humanChoice.code + " C:" + compChoice.code + "-";
	
			var win = humanChoice.value - compChoice.value;
			if ( win === 0 ) { 
				document.getElementById('result').innerHTML += "<p><b>So we draw</b></p>" } 
			else {
				if ( win === -2 || win === 1 ) { 
					document.getElementById('result').innerHTML += "<p><b>So you win</b></p>"; humanWins++; }
				else {
					if ( win === -1 || win === 2 ) { 
						document.getElementById('result').innerHTML += "<p><b>So I win</b></p>"; compWins++; }
					else {}
				}
			}
			document.getElementById('tally').innerHTML = "<p><i>Leaderboard:</i><br />Your wins: " + humanWins + "&nbsp;&nbsp;&nbsp;My wins: " + compWins + "</p>";
			if ( compWins < humanWins ) { 
				document.getElementById('tally').innerHTML += "<p>You're in the lead</p>"; } 
			else if ( compWins > humanWins ) {
				document.getElementById('tally').innerHTML += "<p>I'm in the lead</p>"; } 
			else if ( compWins == humanWins ) {
				document.getElementById('tally').innerHTML += "<p>We're running head to head</p>"; } 		
		}
		</script>
		<script type="text/javascript" src="js/index.js"></script>
		<script type="text/javascript">
			app.initialize();
		</script>
	</body>
</html>

Maths credit: Liam

6. Once the file is saved, be sure the local Git recognises these files as having been added to the local repo, then commit the new repo up to the GitHub website. If further edits are made to the code, be sure to ‘sync’ the fileset in the local Git with the GitHub, thus ensuring the latest files are copied across.

Note, a quicker alternative to these steps just to get you going, is just to create a new ‘fork’ (copy) of our working app – search for the public repo ‘rendzina / RockPaperScissors’ (https://github.com/rendzina/RockPaperScissors).

Compiling and installing the app
Once the app is completed to your satisfaction in the local repo, and then committed and uploaded to the GitHub, it can be accessed by PhoneGap build. Once the final version of the app is ready in GitHub, open PhoneGap Build in a browser. At this point also, you can connect the Android device physically to the local computer via a USB cable. The default USB connection will allow you to upload the compiled Android Package (apk) file to the device’s ‘downloads’ folder, from where it can be installed.
Installed_app

The basic workflow for compiling and installing the app is as follows:
1. Log in to PhoneGap Build and select ‘new app’.
2. You can now either upload a zipfile of the local app fileset, or direct PhoneGap Build to the Github repo by URL (e.g. https://github.com/<yourusername>/RockPaperScissors).
3. Once loaded, select ‘Ready to build’ to compile the app. Note that if the code was sourced from GitHub, the ‘Hydration’ option can usefully be selected for managing updates once it is installed on a device.
4. Once the app is compiled, select the Android icon to save the compiled ‘apk’ file off locally.
5. Once saved, the ‘apk’ file can be uploaded to the device’s ‘download’ folder. Once uploaded, the file can then be installed directly from file on the device. Note that to do this you may need to set an option allowing apk files uploaded in this manner to be installed.

Hopefully, you can now run your app on the device!
RockPaperScissors_Game
Making further edits to your code
Making further edits to the code involves simply re-editing source code files, uploading it and then recompiling the app. If GitHub was used, then making any further committed edits to the web GitHub code fileset, compiled in PhoneGap Build, will result in your being given an option to update the app directly on the device.

The basic workflow for implementing further code edits is as follows:
1. Make required code changes to the local app code fileset (HTML, CSS and JavaScript files).
2. In the local Git, make a new Commit for these edits, then ensure the newly committed fileset is synchronised with the web GitHub repo.
3. Once that is done, Select ‘Update code’ in PhoneGap Build (assuming the app source was GitHub) – the latest fileset is pulled over from GitHub and a new compile job queued.
4. If ‘Hydration’ was enabled, you can try running the app again and it should reload the updated app (by a wireless connection if available – you don’t need to have a USB cable). If you didn’t use Hydration, you will need to uninstall the app, copy over the latest apk file and reinstall before running.
Hydration
Next steps
Hopefully by the end of this you will have a working mobile app using PhoneGap. However, this is just the start of mobile app development. Firstly note the user interface does not currently look like a native mobile app. This can be solved by developing the software using a further third party JavaScript library, such as Dojo (http://dojotoolkit.org/) or JQuery (http://jquery.com/), both of which have powerful mobile development options. We prefer the Dojo framework and a further blog article shows how to adapt the game here for Dojo. Developing in JavaScript also has advantages in that third party mapping and GIS APIs are also available for use, such as Leaflet (http://leafletjs.com/).

The development environment used can also be enhanced. The example above just uses a good text editor to edit the HTML, CSS and JavaScript code. A better approach is to use an Integrated Development Environment (IDE) such as Eclipse (http://www.eclipse.org/) or NetBeans (https://netbeans.org/). These are generic cross-platform tools. However, it is also perfectly possible to use the native platform-specific development tools to develop apps. For example, for Android this is the Google Android Developer Tools plugin (ADT) for Eclipse, or the new Android Studio (http://developer.android.com/tools/). These latter tools also usefully include device emulators to try out your app on before deployment. Establishing the ‘software stack’ required for this type of development with PhoneGap becomes more complex, as a number of dependent software tools need to be installed – this will form the basis for a later article here on GeoThread.
PhoneGap_Logo

Mobile mapping

TApple_Map_Luton_she competition in the field of mobile mapping is heating up! Today sees the launch of the Google Maps service back as an ‘app’ on the new iPhone 5 operating system. The Apple mapping app has come in for some stick in the press of late, and certainly some of the geolocations do need updating and correcting. We searched for nearby ‘Luton’ and it appears in Devon rather than Bedfordshire – these, and similar blips, are of course teething issues and hopefully will be dealt with swiftly.

Of course, with the opportunity to link services and to gain valuable user-preference information, there is a battle ongoing for providing the best mobile mapping – good news for us!