Introduction: GPS Tracking 3D Map

This project is a 3D printed 3D map, with roads, rivers and towns, with LED beacons to show the location of members of the family. It can show whether or not a child is at school, or just the location of both parents. We can also use it to predict which time the parents get home, so that dinner can be made for the right time. It's also just a generally cool project to show off and display to family and friends.

I hope you enjoy making this Instructable, or enjoy finding out about a project I have made

Step 1: Getting a 3D Map

TO get a 3D map of your area, I have written a separate instructable to help guide you through the process of making one. The link to the instructable is here:

https://www.instructables.com/id/Making-a-3D-Print...

Step 2: Preparing the Map for LED Inserts

Now that you have a 3D map, with roads, towns and rivers, we need a way of indicating where a person is on the map. I used bi-colour 3mm RG LEDs, because the main purpose of the map is to show where the two parents are. In certain places I used an RGB LED, to allow me to show where the oldest child was. There is a limit of 28 pins to output on the Raspberry Pi, So choose the locations of the LEDs wisely. I ended up using around 24 of them, so you should be fine.

To drill PLA, I found a normal wood drill bit worked well, and I treated is as I would treat wood.

In places, where the map was too thick, I would drill out the base layer with a large drill bit, and then the visible above layer with the correct 3mm drill bit.

Step 3: Insert the LEDs

Now that we have holes for the LEDs to sit in, We can glue them in. PVA or Superglue works well for this, I found that PVA ran around it sealing it in place, and superglue also worked very well. Make sure that with each LED, they only stick out on the visible side by a few mm, Because having the LEDs stick out all the way looks a bit messy. Don't worry about the legs on the back, we can fold these over once they are soldered.

Step 4: Connect the LEDs to the Raspberry Pi

I directly soldered the LEDs to the Raspberry Pi, however, if you have one with a pre-soldered header, or you want to be able to use the pi for something else, then I would suggest using jumper wires for each LED, meaning that the Pi is removeable. You can see that once I had soldered the LED, I folded the legs down so they did not stick on the back.

Step 5: Test the LEDs

To make sure that all the LEDs are working, I ran a script that goes through each possible pin, and lights them up, one at a time, which ,oves onto the next one when I click enter. This allowed me to note down which pin number did which location, which came in very useful.

import RPi.GPIO as GPIO
import time 

GPIO.setmode(GPIO.BCM)
for i in range(0, 28):
    GPIO.setup(i, GPIO.OUT)

for i in range(0, 28):
        GPIO.output(i, GPIO.HIGH)
        time.sleep(0.3)
        GPIO.output(i, GPIO.LOW)
        print("That Was: " + str(i))
        z = raw_input("Next?")


Whilst this was happening, I would note down on a text file which pin did which location and which color. You need to do this, as it is very useful in the next step.

Step 6: Code to Turn on the LED When Requested

The way that I have done this project involves one Raspberry Pi Zero W, with a basic website which allows you to turn on a pin. This meant that the main Pi 4, which is usually on, and running, can do the processing, and then the little Pi 0 only has to turn on a pin, making things slightly more complicated. I did this because it suits my setup, and also I felt the Pi 0 can be a bit slow for what we will be doing later.

import RPi.GPIO as GPIO
import time 
from flask import Flask, render_template, request, jsonify
import os

app = Flask(__name__)
p = []
GPIO.setmode(GPIO.BCM)
for i in range(0, 28):
    GPIO.setup(i, GPIO.OUT)

@app.route('/')
def index():    
    return request.remote_addr
@app.route("/off/<pin>")
def turn_off(pin):
    GPIO.output(int(pin), GPIO.LOW)
    return "Off"
@app.route("/off/all")
def alloff():
    for i in range(0, 28):
        GPIO.output(i, GPIO.LOW)
    return "off"
@app.route("/on/<pin>")
def turn_on(pin):
    GPIO.output(int(pin), GPIO.HIGH)
    return "On"
    
if __name__ == '__main__':
    app.run(debug=True, host='0.0.0.0')

The way this works is it waits for the url of the pi's IP address and then an on or off and then the pin number.

save this code in the home directory of the Raspberry Pi, and name it "pin_website.py"

You will need to set this to run automatically, so to do this, in the terminal type: sudo nano /etc/profile

At the bottom of this file, add "python3 pin_website.py &"

The "&" is essential, as it makes it run in the background, and therefore allows boot to continue

Step 7: How to Recieve Location

Using IFTTT, you can set up a service so that when the phone enters a certain location, it can email you, or ping a web address, or message you on telegram.

Step 8: How This All Works

The setup I have is a Server Pi, hosting my website, with port forwarding and a static DNS using the service provided by https://freedns.afraid.org/. A lot of this is quite complex, and you need to have an understanding of port forwarding, I might make an instructable on how to do this part another time.

Another way you can do it is to use telegram to get messages to the pi, or possibly the easiest, is to set up an email reader that reads the emails and recieves location updates via that.

I have not tried the Telegram bot or an email reader, but there are plenty of tutorials out there that will show you how to.

Here is my Flask / Python code which is then requested by webhooks using IFTTT:

from flask import Flask, render_template, request, jsonify
import os
from datetime import datetime
from map import *

app = Flask(__name__)
l = 0
setup()

@app.route('/')
def index():    
    return request.remote_addr

@app.route('/mum/enter/<location>')
def mu(location):
    mum.current_loc(location)
    return "Thanks For The Update, Mum!"

@app.route("/dad/enter/<l>")
def da(l):
    dad.current_loc(l)
    return "Thanks For The Update, Dad!"

@app.route("/child/enter/<l>")
def child_enter(l):
    me.current_loc(l)
    return "Hey, Me"

@app.route('/mum/exit/<location>')
def mume(location):
    mum.offline(location)
    return "Thanks For The Update, Mum!"

@app.route("/dad/exit/<l>")
def dade(l):
    dad.offline(l)
    return "Thanks For The Update, Dad!"

@app.route("/child/exit/<l>")
def child_exit(l):
    me.offline(l)
    return "Hey, Me"

@app.route("/reset")
def redo():
    setup()
    return "Reset!"
if __name__ == '__main__':
    app.run(debug=True, host='0.0.0.0')

and map.py:

import http.client, urllib.request, urllib.parse, urllib.error, base64
import ast, json
import time
import threading
import os
params = urllib.parse.urlencode({
})
last_loc = 0
dlast_loc = 0
mlast_loc = 0

def setup():
    conn = http.client.HTTPSConnection('freedns.afraid.org')
    conn.request("GET", str("/dynamic/update.php?ZmFpOWlJQ29QczhiOW1iYWJoNVdVcG9HOjE5MTM2ODU2"))
    response = conn.getresponse()
    conn = http.client.HTTPConnection('192.168.1.251:5000')
    conn.request("GET", str("/off/all"))
    response = conn.getresponse()
    f = open("pin", "w")
    f.write(str(-1))
    f.close()
    f = open("pind", "w")
    f.write(str(-1))
    f.close()
    f = open("pinm", "w")
    f.write(str(-1))
    f.close()
     

class mum:    
    def current_loc(l):
        global last_loc
        locs = {
        "llansantffraid": 4,
        "oswestry": 5,
        "lynclys": 8,
        "home": 9,
        "shrewsbury": 11,
        "llanymynech": 13,
        "four crosses": 18,
        "llandrinio": 25,
        "welshpool": 27
        }
        f = open("pin", "w")
        f.write(str(-1))
        f.close()
        time.sleep(1)
        conn = http.client.HTTPConnection('192.168.1.251:5000')
        conn.request("GET", str("/off/") + str(last_loc))
        response = conn.getresponse()            
        conn = http.client.HTTPConnection('192.168.1.251:5000')
        conn.request("GET", str("/on/") + str(locs[l]))
        response = conn.getresponse()
        last_loc = locs[l]
    
    def offline(l):
        global last_loc
        locs = {
        "llansantffraid": 4,
        "oswestry": 5,
        "lynclys": 8,
        "home": 9,
        "shrewsbury": 11,
        "llanymynech": 13,
        "four crosses": 18,
        "llandrinio": 25,
        "welshpool": 27
        }
        conn = http.client.HTTPConnection('192.168.1.251:5000')
        conn.request("GET", str("/off/") + str(last_loc))
        response = conn.getresponse()            
        f = open("pin", "w")
        f.write(str(locs[l]))
        f.close()
        os.system("python3 flash.py &")
    

   
        
class dad:
    locs = {
        "welshpool": 3,
        "lynclys": 1,
        "home": 23,
        "shrewsbury": 0,
        "llanymynech": 6,
        "four crosses": 15,
        "llandrinio": 10,
        "welshpool": 24
        }
    def current_loc(l):
        global dlast_loc
        locs = {
        "welshpool": 3,
        "lynclys": 1,
        "home": 23,
        "shrewsbury": 0,
        "llanymynech": 6,
        "four crosses": 15
        }
        f = open("pind", "w")
        f.write(str(-1))
        f.close()
        time.sleep(1)
        conn = http.client.HTTPConnection('192.168.1.251:5000')
        conn.request("GET", str("/off/") + str(dlast_loc))
        response = conn.getresponse()            
        conn = http.client.HTTPConnection('192.168.1.251:5000')
        conn.request("GET", str("/on/") + str(locs[l]))
        response = conn.getresponse()
        dlast_loc = locs[l]
    
    def offline(l):
        global dlast_loc
        locs = {
        "welshpool": 3,
        "lynclys": 1,
        "home": 23,
        "shrewsbury": 0,
        "llanymynech": 6,
        "four crosses": 15,
        "llandrinio": 10
        }
        conn = http.client.HTTPConnection('192.168.1.251:5000')
        conn.request("GET", str("/off/") + str(dlast_loc))
        response = conn.getresponse()            
        f = open("pind", "w")
        f.write(str(locs[l]))
        f.close()
        os.system("python3 flashd.py &")
class me:
    def current_loc(l):
        global mlast_loc
        locs = {
        "home": 22,
        "school": 2,
        "oswestry": 14
        }
        f = open("pinm", "w")
        f.write(str(-1))
        f.close()
        time.sleep(1)
        conn = http.client.HTTPConnection('192.168.1.251:5000')
        conn.request("GET", str("/off/") + str(mlast_loc))
        response = conn.getresponse()            
        conn = http.client.HTTPConnection('192.168.1.251:5000')
        conn.request("GET", str("/on/") + str(locs[l]))
        response = conn.getresponse()
        mlast_loc = locs[l]
    def offline(l):
        global dlast_loc
        locs = {
        "home": 22,
        "school": 2,
        "oswestry": 14
        }
        conn = http.client.HTTPConnection('192.168.1.251:5000')
        conn.request("GET", str("/off/") + str(mlast_loc))
        response = conn.getresponse()            
        f = open("pinm", "w")
        f.write(str(locs[l]))
        f.close()
        os.system("python3 flashm.py &")

Step 9: Build Your Own From Inspiration Off My Project

So I know that the previous step will be very hard to understand, so I will leave it as showing you how to make the map, and be able to have a raspberry pi that turns on and off the LEDs. You now need to create a python script that, using IFTTT, emails you. Then you need to find a email reading piece of code, which is quite easy (google it). Then once you read an email and find the location of a parent, use 'if' statements to find which pin to turn on.

On the map, A flashing light means that they have just left the area

The way to turn on the LEDs on another pi from python is as below:

import http.client, urllib.request, urllib.parse, urllib.error, base64

params = urllib.parse.urlencode({
})

conn = http.client.HTTPConnection('192.168.1.251:5000') #change this with the raspberry pi's map IP address
conn.request("GET", str("/off/2")) # this turns off pin number 2
response = conn.getresponse() # this requests the URL, and then the map pi reads this and turns off pin number 2 

Basically, I hope you can use what I have done with my 3D map as inspiration to make your own GPS tracking map.

Maps Challenge

Participated in the
Maps Challenge