code.py
  1  # SPDX-FileCopyrightText: 2019 Carter Nelson for Adafruit Industries
  2  #
  3  # SPDX-License-Identifier: MIT
  4  
  5  import time
  6  import math
  7  import board
  8  import displayio
  9  from terminalio import FONT
 10  from adafruit_pyportal import PyPortal
 11  from adafruit_display_shapes.circle import Circle
 12  from adafruit_display_text.label import Label
 13  
 14  #--| USER CONFIG |--------------------------
 15  MARK_SIZE = 10           # marker radius
 16  MARK_COLOR = 0xFF3030    # marker color
 17  MARK_THICKNESS = 5       # marker thickness
 18  TRAIL_LENGTH = 200       # trail length
 19  TRAIL_COLOR = 0xFFFF00   # trail color
 20  DATE_COLOR = 0x111111    # date color
 21  TIME_COLOR = 0x111111    # time color
 22  LAT_MAX = 80             # latitude (deg) of map top/bottom edge
 23  UPDATE_RATE = 10         # update rate in seconds
 24  #-------------------------------------------
 25  
 26  DATA_SOURCE = "http://api.open-notify.org/iss-now.json"
 27  DATA_LOCATION = ["iss_position"]
 28  
 29  WIDTH = board.DISPLAY.width
 30  HEIGHT = board.DISPLAY.height
 31  
 32  # determine the current working directory needed so we know where to find files
 33  cwd = ("/"+__file__).rsplit('/', 1)[0]
 34  pyportal = PyPortal(url=DATA_SOURCE,
 35                      json_path=DATA_LOCATION,
 36                      status_neopixel=board.NEOPIXEL,
 37                      text_font=None,
 38                      default_bg=cwd+"/map.bmp")
 39  
 40  # Connect to the internet and get local time
 41  pyportal.get_local_time()
 42  
 43  # Date and time label
 44  date_label = Label(FONT, text="0000-00-00", color=DATE_COLOR, x=165, y=223)
 45  time_label = Label(FONT, text="00:00:00", color=TIME_COLOR, x=240, y=223)
 46  pyportal.splash.append(date_label)
 47  pyportal.splash.append(time_label)
 48  
 49  # ISS trail
 50  trail_bitmap = displayio.Bitmap(3, 3, 1)
 51  trail_palette = displayio.Palette(1)
 52  trail_palette[0] = TRAIL_COLOR
 53  trail = displayio.Group()
 54  pyportal.splash.append(trail)
 55  
 56  # ISS location marker
 57  marker = displayio.Group()
 58  for r in range(MARK_SIZE - MARK_THICKNESS, MARK_SIZE):
 59      marker.append(Circle(0, 0, r, outline=MARK_COLOR))
 60  pyportal.splash.append(marker)
 61  
 62  def get_location(width=WIDTH, height=HEIGHT):
 63      """Fetch current lat/lon, convert to (x, y) tuple scaled to width/height."""
 64  
 65      # Get location
 66      try:
 67          location = pyportal.fetch()
 68      except RuntimeError:
 69          return None, None
 70  
 71      # Compute (x, y) coordinates
 72      lat = float(location["latitude"])   # degrees, -90 to 90
 73      lon = float(location["longitude"])  # degrees, -180 to 180
 74  
 75      # Scale latitude for cropped map
 76      lat *= 90 / LAT_MAX
 77  
 78      # Mercator projection math
 79      # https://stackoverflow.com/a/14457180
 80      # https://en.wikipedia.org/wiki/Mercator_projection#Alternative_expressions
 81      x = lon + 180
 82      x = width * x / 360
 83  
 84      y = math.radians(lat)
 85      y = math.tan(math.pi / 4 + y / 2)
 86      y = math.log(y)
 87      y = (width * y) / (2 * math.pi)
 88      y = height / 2 - y
 89  
 90      return int(x), int(y)
 91  
 92  def update_display(current_time, update_iss=False):
 93      """Update the display with current info."""
 94  
 95      # ISS location
 96      if update_iss:
 97          x, y = get_location()
 98          if x and y:
 99              marker.x = x
100              marker.y = y
101              if len(trail) >= TRAIL_LENGTH:
102                  trail.pop(0)
103              trail.append(displayio.TileGrid(trail_bitmap,
104                                              pixel_shader=trail_palette,
105                                              x = x - 1,
106                                              y = y - 1) )
107  
108  
109      # Date and time
110      date_label.text = "{:04}-{:02}-{:02}".format(current_time.tm_year,
111                                                   current_time.tm_mon,
112                                                   current_time.tm_mday)
113      time_label.text = "{:02}:{:02}:{:02}".format(current_time.tm_hour,
114                                                   current_time.tm_min,
115                                                   current_time.tm_sec)
116  
117      try:
118          board.DISPLAY.refresh(target_frames_per_second=60)
119      except AttributeError:
120          board.DISPLAY.refresh_soon()
121  
122  
123  # Initial refresh
124  update_display(time.localtime(), True)
125  last_update = time.monotonic()
126  
127  # Run forever
128  while True:
129      now = time.monotonic()
130      new_position = False
131      if now - last_update > UPDATE_RATE:
132          new_position = True
133          last_update = now
134      update_display(time.localtime(), new_position)
135      time.sleep(0.5)