Tag Archives: GPS

New project : Find nearest booze shop.

Or anything else. ( idea from a reddit post )

UPDATE: 20250523

Combining a GPS module, compass, a LED ring and some code, I want to make a little device which shows you the way to the nearest … something.

To make it completely standalone, I have to use a SIM module. (Same as I have used before) This POC will use my phone as hotspot.

The LED ring will show the direction to go.

Edit: Maybe not a LED ring but a little display.

As previously posted, I was playing with Overpass turbo.
Using an API, I can use code to query this.

  • Arduino sends latitude, longitude to my webserver
  • Webserver queries API for neastest POIs and calculates distance.
  • Send data from webserver to arduino
  • Arduino uses heading data to light up direction LED
    (also on secondary display with distance info?)
    edit: and shop info

Test code for my web server to query the data

import overpy
import math

api = overpy.Overpass()

# This location will be filled with data from GPS module on Arduino.
latitude = 52.2270745        # Center latitude (e.g. Berlin)
longitude = 5.177519      # Center longitude
box_size = 0.05         # Box size in degrees (about ~5 km)

south = latitude - box_size
north = latitude + box_size
west = longitude - box_size
east = longitude + box_size

def haversine(lat1, lon1, lat2, lon2):
    R = 6371  # Earth radius in km
    phi1 = math.radians(lat1)
    phi2 = math.radians(lat2)
    d_phi = math.radians(lat2 - lat1)
    d_lambda = math.radians(lon2 - lon1)

    a = math.sin(d_phi / 2)**2 + math.cos(phi1) * math.cos(phi2) * math.sin(d_lambda / 2)**2
    c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a))

    return R * c  # Distance in kilometers

# Calculate bearing in degrees (0-360)
def bearing(lat1, lon1, lat2, lon2):
    phi1 = math.radians(lat1)
    phi2 = math.radians(lat2)
    delta_lon = math.radians(lon2 - lon1)

    x = math.sin(delta_lon) * math.cos(phi2)
    y = math.cos(phi1) * math.sin(phi2) - math.sin(phi1) * math.cos(phi2) * math.cos(delta_lon)

    initial_bearing = math.atan2(x, y)
    compass_bearing = (math.degrees(initial_bearing) + 360) % 360  # Normalize to 0–360

    return compass_bearing

# Overpass QL query
query = f"""
[out:json];
node
  ["shop"="alcohol"]
  ({south}, {west}, {north}, {east});
out body;
>;
out skel qt;
"""

try:
    result = api.query(query)

    # Collect and sort places by distance
    places = []
    for node in result.nodes:
        node_lat = float(node.lat)
        node_lon = float(node.lon)
        distance = haversine(latitude, longitude, node_lat, node_lon)
        direction = bearing(latitude, longitude, node_lat, node_lon)
        name = node.tags.get("name", "Unnamed")
        places.append((distance, direction, name, node_lat, node_lon))

    places.sort()

    print(f"Found {len(places)} alcohol-related places sorted by distance:")
    for dist, dir_deg, name, lat, lon in places:
        print(f"- {name} at ({lat:.5f}, {lon:.5f}) — {dist:.2f} km, {dir_deg:.0f}°")

except Exception as e:
    print(f"Error: {e}")

Output:

Found 10 alcohol-related places sorted by distance:
- The Skiff at (52.22583, 5.17860) — 0.16 km, 152°
- Onzewijnen at (52.22612, 5.17045) — 0.49 km, 258°
- Gall & Gall at (52.23244, 5.19204) — 1.15 km, 59°
- Gall & Gall at (52.21536, 5.16735) — 1.48 km, 208°
- Eric's Beer Craft at (52.21549, 5.16632) — 1.50 km, 211°
- Slijterij at (52.21082, 5.15692) — 2.29 km, 218°
- Gall & Gall at (52.21590, 5.14074) — 2.80 km, 244°
- Gall & Gall at (52.25422, 5.22705) — 4.53 km, 48°
- Gall & Gall at (52.26808, 5.18348) — 4.58 km, 5°
- Il DiVino at (52.27507, 5.16414) — 5.41 km, 350°

Example using Overpass Turbo to find breweries

Other ideas

  • Geocaching (Thanks Vincent)
  • Find each other at festivals?

UPDATE

Building the hardware : First design

Screen programming (First setup)

Some test code

#include "SPI.h"
#include "Adafruit_GFX.h"
#include "Adafruit_GC9A01A.h"

// Overrule stuff
#define TFT_CS 18   // Chip select
#define TFT_DC 5    // Data/command mode
#define TFT_BL 4    // Backlight control
#define TFT_MOSI 12 // SPI Out AKA SDA
#define TFT_SCLK 13 // Clock out AKA SCL 
#define TFT_MISO -1 // pin not used
#define TFT_RST 23  // Reset ################# IMPORTANT, won't work without!! Took me a hour!

// Need this changed from example also
Adafruit_GC9A01A tft(TFT_CS, TFT_DC,TFT_MOSI,TFT_SCLK,TFT_RST,TFT_MISO);

float angle = 0;

void setup() {
  tft.begin();
  tft.setRotation(0);
  tft.fillScreen(GC9A01A_BLACK);
  drawCompassFace();
}

void loop() {
  drawNeedle(angle, GC9A01A_RED);
  delay(1000);
  drawNeedle(angle, GC9A01A_BLACK);  // Erase previous needle
  angle += 15;
  if (angle >= 360) angle = 0;

  tft.setCursor(60, 100);
  tft.setTextColor(GC9A01A_WHITE);  tft.setTextSize(2);
  tft.println("230 Meters");

}

// Draw static compass face
void drawCompassFace() {
  int cx = tft.width() / 2;
  int cy = tft.height() / 2;
  int radius = 100;

  tft.drawCircle(cx, cy, radius, GC9A01A_WHITE);
  tft.setTextColor(GC9A01A_WHITE);
  tft.setTextSize(1);
  tft.setCursor(cx - 3, cy - radius + 5); tft.print("N");
  tft.setCursor(cx - 3, cy + radius - 10); tft.print("S");
  tft.setCursor(cx - radius + 5, cy - 3); tft.print("W");
  tft.setCursor(cx + radius - 10, cy - 3); tft.print("E");
}

// Draw compass needle
void drawNeedle(float angleDeg, uint16_t color) {
  int cx = tft.width() / 2;
  int cy = tft.height() / 2;
  float angleRad = angleDeg * DEG_TO_RAD;
  int x = cx + cos(angleRad) * 90;
  int y = cy + sin(angleRad) * 90;
  tft.drawLine(cx, cy, x, y, color);
}