uLisp


Here's an example to demonstrate a simple GPS application written in uLisp. It uses a GPS module with an I2C interface, making it easy to read the GPS parameters without needing to worry about parsing the NMEA sentences. For details of the I2C GPS Module see I2C GPS Module on Technoblogy. There's also a PCB version of the module; see I2C GPS Module PCB.

The project plots the GPS position to a monochrome 128x64 OLED display, so as you walk around it shows a track of your route. I ran it on an Adafruit ItsyBitsy M4 [1], which is compact enough to fit on two mini breadboards along with the OLED display and GPS module:

GPSPlotter.jpg

The circuit

Here's how everything's interconnected:

GPSPlotter.gif

The display needs to be a monochrome 128x64 OLED display based on the SH1106 driver chip; for example, the Geekcreit 1.3" I2C OLED display, available from Banggood [2]. The SH1106 driver chip supports reading back the display memory, which is essential for plotting a single point without affecting the neighbouring points.

The ItsyBitsy M4 needs the two 4.7kΩ pullups to VCC on the I2C signals SDA and SCL. I/O pin 2 is used to detect the INT signal from the GPS module, which goes low when new GPS data is ready to be read.

Converting latitude and longitude to x and y

The main part of the program reads the latitude and longitude from the I2C GPS module, converts them to x,y coordinates, and plots them as a point on the display.

For small distances we can assume that the surface of the earth is flat and use the following formulae:

x = R ⋅ λ ⋅ cos (φ)

y = R ⋅ φ

where φ is the latitude in radians, λ is the longitude in radians, and R is the earth's mean radius, 6371km.

Here's the routine position that reads the latitude and longitude bytes from the I2C GPS module, converts them to x and y coordinates, and returns them as a list:

(defun position () (let (lat long) (with-i2c (str 58) (write-byte 4 str) (restart-i2c str 8) (setq lat (/ (read4 str) 6e5)) (setq long (/ (read4 str) 6e5))) (list (* *r* (radians long) (cos (radians lat))) (* *r* (radians lat)))))

This uses the following routine, radians, to convert from degrees to radians:

(defun radians (deg) (* deg (/ (asin 1) 90)))

The variable *r* is the radius of the earth, in metres:

(defvar *r* 6.371e6)

The routine read4 reads four bytes from the I2C stream and returns a 32-bit integer:

(defun read4 (str) (+ (read-byte str) (ash (read-byte str) 8) (ash (read-byte str) 16) (ash (read-byte str) 24)))

Plotting the path

The program also uses modified versions of some of the plotting routines from the project Graphics display interface in Lisp.

The main program nav first initialises and clears the display, and waits until the GPS module has obtained a fix. It then plots points relative to the starting position, which is centred on the centre of the display:

(defun nav () (init-display) (clear) (loop (unless (digitalread 2) (return))) (cross) (let ((home (position))) (loop (let* ((here (position)) (dx (round (- (first here) (first home)) 10)) (dy (round (- (second here) (second home)) 10))) (point (+ dx 64) (+ dy 32))) (loop (unless (digitalread 2) (return))))))

With these figures each pixel represents 10m, so the whole display represents an area 1280 x 640m.

To make the program run automatically first compile and upload uLisp with the #define:

#define resetautorun

Then enter the program and save it as an image with the command:

(save-image 'nav)

When you reset the ItsyBitsy M4 the Lisp image will be loaded and the function (nav) run automatically. You can power the whole project from a 3.7V LiPo battery connected to BAT and G.

Here's the whole GPS mapping application in a single file: GPS mapping application program.

Further suggestions

With a bit more work this program could form the basis for a GPS treasure-hunt project, or a hiking companion. It could also be combined with the Route finder application to make a navigator application.