Track the International Space Station across the world using a WiFi enabled Pico RP2040 microcontroller.

As the Raspberry Pi Pico doesn't have WiFi I brought a ESP32spi co-processor to allow the Pico to communicate over wifi. For my first wifi Pico project I decided to build a Space Station Tracker which displays the ISS's current location on a map. I have previously done this with a Raspberry Pi Zero W using a Triple Colour e-ink display, article here , but I though I would see if I can get it to work on a RP2040 Pico with only 264k of memory.

Raspberry Pi Pico Space Station Tracker

The concept for this projects is to get a Json file from api.open-notify.org which contains the current Latitude and Longitude of the ISS. Then compare that to a list of Latitude and Longitude co-ordinates for the Capital Cities of the world. This is then plotted on a map of the world with information on how far the ISS is from the closest Capital City.

It is not only interesting to know where the Space Station is but also helps kids and adults learn the geography of the world while watching the dot move across the map.

I am using Circuitpython to build the ISS Tracker as I have the Adafruit ESP32spi Wifi breakout board. 

To build this project I used:

  • Raspberry Pi RP2040 Pico
  • Adafruit ESP32spi WiFi co-processor
  • ST7789V 1.3 inch 240 x 240 LCD Display by Pimoroni
  • Pimoroni - Pico Proto board and female headers
  • CircuitPython  firmware installed to the Pico

The programs flow is:

  • Retrieve the json file of the ISS's co-ordinates from the internet
  • Compare the co-ordinates to a list of Capital City co-ordinates to find the closes city
  • Calculate the position of the red dot on the world map
  • Calculate the trail of dots to show where the ISS has been
  • Build the display image of a map and location text
  • Display image

Retrieving the ISS's Location

The first stage is to get the ESP32spi setup and able to retrieve the Json file from the internet to read it's current location. Create a file called code.py and start coding.
The ESP32 requires these adafruit CircuitPython libraries:


import board
import busio
from digitalio import DigitalInOut
from adafruit_esp32spi import adafruit_esp32spi
from adafruit_esp32spi import adafruit_esp32spi_wifimanager
import adafruit_esp32spi.adafruit_esp32spi_socket as socket
from time import sleep

Additional libraries that will be needed for this project are:


import displayio
from adafruit_st7789 import ST7789
import terminalio
import adafruit_requests as requests
from time import sleep
import haversine
from adafruit_display_text import label
import adafruit_imageload
from adafruit_display_shapes.circle import Circle
from adafruit_display_shapes.rect import Rect
from adafruit_bitmap_font import bitmap_font as bitf

The ESP32 was connected with Pico SPi pins. Pico GPIO Layout cheatsheet 

#ESP32 Setup on Pico:
esp32_cs = DigitalInOut(board.GP13) 
esp32_ready = DigitalInOut(board.GP8)
esp32_reset = DigitalInOut(board.GP9)
spi = busio.SPI(board.GP10, board.GP11, board.GP12) #sck, tx, rx
esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset)

Create a file called secrets.py and save it to the Pico's CIRCUITPY folder. The secrets file should contain your WiFi networks SSID and password like this;


secrets = {
    'ssid' : 'YOUR-SSID',
    'password' : 'YOUR-PASSWORD'
    }

 The next step in code.py is to set the wifi manger up with the secrets.py file.


try:
    from secrets import secrets
except ImportError:
    print("WiFi secrets are kept in secrets.py, please add them there!")
    raise
wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets)

This next function will be used to connect the ESP32's wifi to your wifi network and also reconnect if the connection is lost while the program is running


def esp32():
    try:
        s = esp.status
        print("ESP32 Status", s)
        if s == 0:
            wifi.connect()
    except RuntimeError as r:
        wifi.reset()

Now the code is in place to give the Pico internet access the next function is used to retrieve the ISS co-ordernates.


def iss_lat_lon():
    JSON_URL = "http://api.open-notify.org/iss-now.json"
    try:
        result = requests.get(JSON_URL).json()["iss_position"]
    except RuntimeError as r:
        wifi.reset()
        #esp.reset
        esp32()
        result = requests.get(JSON_URL).json()["iss_position"]
    lat = result["latitude"]
    lon = result["longitude"]
    return lat,lon

 To reduce load on open-notify.org it is recommended that the json file is not accessed more than once every 5 seconds. For this project I have set it at once every 30 seconds which will be later in the script.

Now enter this code and run the script. This will show the co-ordinates in the output window.

while 1:
    print(iss_lat_lon())
    sleep(10)

Once the ISS position is ready, it can be compared against a csv list of global capital cities. I would have liked to use a list of the majour cities of the world but the Pico does not have enough memory to achieve this. So a list of 196 Capital cities just about fits. The base list came from https://simplemaps.com/data/world-cities which was then limited.
The haversine calculation is used to get the distance between two locations on the earth. I have used the python code by Wayne Dyck and saved it as haversine.py to the CIRCUITPY folder. This can be downloaded from https://gist.github.com/rochacbruno/2883505

The full code for this project is available at the end of this article, so for the next stage a list of city latitude and longitude co-ordinates have already been loaded. This next function takes the ISS co-ordinates and compares it to every city's co-ordinates and records the closest city using the haversine calculation

"data" is the latitude, longitude, country, city from the csv converted to a list.


-41.28646	174.776236	New Zealand	Wellington
-35.282	        149.128684	Australia	Canberra
-34.901113	-56.164531	Uruguay	        Montevideo
-34.603684	-58.381559	Argentina	Buenos Aires

"sz" is the amount of rows in the list

"iss" is the current ISS co-ordernates

Each cities lat, lon co-ordinates are compared to the ISS's lat, lon co-ordinates and the distance, country and city is logged in the variables. If the current distance is closer then the logged distance then the variables are updated. At the end the closest city will be left in the variables.


def CalcLoc(data, sz, iss):
    country = ""
    city = ""
    citydist = 0
    mindist = 99999999
    for i in range(sz):
        d = haversine.distance((float(data[i][0]),float(data[i][1])),(float(iss[0]),float(iss[1])))
        if d < mindist:
            city = data[i][2]
            country = data[i][3]
            citydist = d
            mindist = d
    return (country,city,round(citydist,2))

Position the ISS's location on a Map

To be able to draw the ISS's location on a map the Latitude & Longitude need to be converted to the pixels of a map.

Latitude and Longitude coordinates pinpoint locations on the earth 180 degrees North to South and 360 degrees West to East.
The north pole is +90 and the south pole -90 with the equator at zero.
For left to right, -180 to +180 is at the international date line near Japan, Australia in the Pacific Ocean. Latitude Zero is a vertical line through the UK.
The best way to show this on a map is to use world images that are multiples of  360 x 180.
I am using a 240 pixel screen but an image of 180 X 90 is a bit small.  So I have gone with a image that is 360 x 180. The display will then move left and right as the ISS moves across the world.

Earth Long Lat

The latitude co-ordinates go from +90 at the north pole to -90 at the south pole. The display co ordinates for the screen map is 0 at the north pole and 179 at the south pole.

To get the red dot that shows the ISS's location in the correct place, a calculation neds to be made to convert between the two coordinated systems.
180-(ISS_lat+90) = image Y.
ie if the latitude is -20 then 180-(-20+90) = 110 pixels from the top.

The longitude is a little easier. Longitude goes -180 to +180. The image goes from 0 to 179 pixels left to right.
So just add 180 to the ISS's longitude to get the correct point on the image. -30 + 180 = 150 pixels from the left.

The image of the world should be the equirectangular version, which is a flattened sphere. The top and bottom of the image are stretched and distorted. If a flat world map is used the coordinates won't line up.

world map good  This image will show the ISS's location correctly.

world map badThis image will cause the location to be wrong.

The Earth images for this project were created from the ones here https://visibleearth.nasa.gov/collection/1484/blue-marble

For CircuitPython the world image needs to be in BMP format and as low quality as you can use to save memory. I have used 16 colours, Dithered with RLE compression and 4bit colour. This is 21k.

worldmap360

 Now all the information is available to build the display image. In addition to the Circle showing the ISS's location, a trail of dots are drawn to show the route where the ISS's has been during the last orbit. It takes the Space Station about 90 minutes to orbit the Earth. Due to the Pico's limited memory the trail is 32 dots with 1 dot every 2 minutes.

The Pico Space Station Tracker

Pico Space Station Tracker Display

The project files can be downloaded here Pico-Iss-Tracker-Project-Files.zip

Unzip the files to your CIRCUITPY folder on the Pico.

The following Circuitpython Libraries will be needed in the CIRCUITPY/lib folder. 

  • adafruit_bus_device
  • adafruit_esp32spi
  • adafruit_display_text
  • adafruit_display_shapes
  • adafruit_imageload
  • adafruit_bitmap_font
  • adafruit_st7789.mpy
  • adafruit_request.mpy

 The secrets.py file is also required with the WiFi network connection details as described in the article.

 

 


Add comment