Pfeerick ISS Tracker Emergency!

It’s been blinking red for weeks.
I suspect one of the services upon which it depends has broken down.

I can’t find the GitHub, so this is the code:

/**
 * Original code written by pollux labs, 2020
 * URL: https://gist.github.com/polluxlabs/1ba7824175c5e011565bd61af2fd1c6b
 */
// #define USE_OLED
#define SCREEN_WIDTH 128   // OLED display width, in pixels
#define SCREEN_HEIGHT 32   // OLED display height, in pixels
#define OLED_RESET -1      // Reset pin # (-1 if sharing Arduino reset pin or no reset pin)
#define OLED_I2C_ADDR 0x3C // Displays I2C address
#define OLED_ROTATION 0    // 0 = 0, 1 = 90, 2 = 180 or 3 = 270 (degree rotation)

#define NEOPIXEL_PIN D3
#ifndef NUM_OF_NEOPIXELS
#define NUM_OF_NEOPIXELS 12
#endif
#define STATUS_LED LED_BUILTIN
#define STATUS_LED_INVERTED true

#include <Arduino.h>
#include <ESP8266HTTPClient.h>
#include <ESP8266WiFi.h>
#include <ArduinoJson.h>
#include <Adafruit_NeoPixel.h>
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <TimeLib.h>
#include <Timezone.h>

#include "secrets.h"

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

long riseTime = 0;                 // Time the ISS will rise for current position
long currentTime = 0;              // Current time (UTC)
long flyoverDuration = 0;          // Duration of ISS pass for current position
long timeUntilFlyover = 0;         // How long it will be until the next flyover
long timeUntilFlyoverComplete = 0; // How long it will be until the current flyover is complete
time_t utcTime;                    // Current UTC time

enum machineStates
{
  WIFI_INIT,
  START,
  GET_TIME,
  GET_NEXT_PASS,
  WAIT_FOR_PASS,
  PASS_START,
  PASSING,
  PASS_COMPLETE
};

enum machineStates currentState = WIFI_INIT;

Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUM_OF_NEOPIXELS, NEOPIXEL_PIN, NEO_GRB + NEO_KHZ800);
WiFiClient client;

void success();
void fail();
bool getCurrentTime();
bool getNextPass();

void setup()
{
  Serial.begin(9600);
  pinMode(STATUS_LED, OUTPUT); // LED Pin
  digitalWrite(STATUS_LED, (STATUS_LED_INVERTED == true) ? HIGH : LOW);

  pixels.begin();
  pixels.setBrightness(100);
  pixels.show();

  Serial.println("");
  Serial.println(F("ISS Flyover Notifier"));
  Serial.println("");

  if (!display.begin(SSD1306_SWITCHCAPVCC, OLED_I2C_ADDR))
  {
    Serial.println(F("SSD1306 initialisation failed"));
  }
  display.setRotation(OLED_ROTATION);
  display.clearDisplay();
}

void loop()
{
  unsigned long loopStart = millis();
  switch (currentState)
  {
  case WIFI_INIT:
  {
    WiFi.begin(ssid, password);
    Serial.print(F("WiFi Connecting..."));

    display.clearDisplay();
    display.setTextSize(2);
    display.setTextColor(WHITE);
    display.setCursor(30, 0);
    // Display static text
    display.print("Connect");
    display.setCursor(32, 16);
    display.print("to WiFi");
    display.display();


    int waitTime = 0;
    while ((WiFi.status() != WL_CONNECTED) && (waitTime < 300))
    {
      Serial.print(".");
      digitalWrite(STATUS_LED, !digitalRead(STATUS_LED));
      delay(100);
      waitTime++;
    }

    digitalWrite(STATUS_LED, (STATUS_LED_INVERTED == true) ? HIGH : LOW);

    if (WiFi.status() == WL_CONNECTED)
    {
      success();
      Serial.println("CONNECTED!");
      currentState = START;
    }
    else
    {
      Serial.println("FAIL!");
      Serial.print(F("Device will restart in "));
      for (int i = 5; i >= 1; i--)
      {
        Serial.print(i);
        Serial.print("...");
        fail();
        delay(500);
      }
      ESP.restart();
    }
    break;
  }
  case START:
  {
    if (WiFi.status() == WL_CONNECTED)
    {
      currentState = GET_TIME;
    }
    else
    {
      currentState = WIFI_INIT;
    }

    break;
  }
  case GET_TIME:
  {
    Serial.println(F("Getting the current time (UTC)..."));

    display.clearDisplay();
    display.setTextSize(2);
    display.setTextColor(WHITE);
    display.setCursor(30, 0);
    // Display static text
    display.println("Get UTC");
    display.setCursor(32, 16);
    display.println("Time");
    display.display();

    if (!getCurrentTime())
    {
      fail();
      delay(1000);
    }
    else
    {
      success();

      utcTime = currentTime;

      Serial.printf("Current time : %02d:%02d:%02d %02d-%02d-%04d (UTC)\n",
                    hour(utcTime), minute(utcTime), second(utcTime),
                    day(utcTime), month(utcTime), year(utcTime));

      currentState = GET_NEXT_PASS;
    }
    break;
  }
  case GET_NEXT_PASS:
  {
    Serial.println(F("Looking up next ISS flyover time..."));

    display.clearDisplay();
    display.setTextSize(2);
    display.setTextColor(WHITE);
    display.setCursor(30, 0);
    display.println("Get Next");
    display.setCursor(32, 16);
    display.println("ISS Pass");
    display.display();

    if (!getNextPass())
    {
      fail();
      delay(1000);
    }
    else
    {
      success();

      // compute time until rise
      timeUntilFlyover = riseTime - currentTime;

      uint32_t t = timeUntilFlyover;
      int s = t % 60;
      t = (t - s) / 60;
      int m = t % 60;
      t = (t - m) / 60;
      int h = t;
      Serial.printf("That's %02i hours, %02i minutes and %02i seconds from now\n", h, m, s);

      t = flyoverDuration;
      s = t % 60;
      t = (t - s) / 60;
      m = t % 60;
      Serial.printf("Estimated pass duration will be %02i minutes and %02i second(s)\n", m, s);

      timeUntilFlyoverComplete = flyoverDuration;
      currentState = WAIT_FOR_PASS;
    }
    break;
  }
  case WAIT_FOR_PASS:
  {
    if (timeUntilFlyover > 0)
    { // while the ISS isn't overhead

      uint32_t t = timeUntilFlyover;
      int s = t % 60;
      t = (t - s) / 60;
      int m = t % 60;
      t = (t - m) / 60;
      int h = t;
      Serial.printf("Next ISS flyover in %02i:%02i:%02i\n", h, m, s);

      display.clearDisplay();
      display.setTextSize(1);
      display.setTextColor(WHITE);
      display.setCursor(20, 0);
      display.print("Next pass in:");
      display.setTextSize(2);
      display.setCursor(32, 16);
      display.printf("%02i:%02i:%02i", h, m, s);
      display.display();

      timeUntilFlyover--;

      unsigned long loopOverhead = millis() - loopStart;
      delay(1000 - loopOverhead);
    }
    else
    {
      currentState = PASS_START;
    }
    break;
  }
  case PASS_START:
  {
    // when ISS rises  above the horizon
    Serial.println(F("ISS overhead!"));

    uint32_t t = timeUntilFlyoverComplete;
    int s = t % 60;
    t = (t - s) / 60;
    int m = t % 60;
    Serial.printf("Estimated pass duration %02i minutes and %02i second(s)\n", m, s);

    currentState = PASSING;
    break;
  }
  case PASSING:
  {
    if (timeUntilFlyoverComplete > 0)
    {
      // map remaining flyover time on a color gradient
      int colorRed = map(timeUntilFlyoverComplete, 0, flyoverDuration, 200, 0);
      int colorBlue = map(timeUntilFlyoverComplete, 0, flyoverDuration, 0, 200);

      // show the current color on all LEDs
      pixels.fill(pixels.Color(colorRed, 0, colorBlue));
      pixels.show();

      timeUntilFlyoverComplete--;

      uint32_t t = timeUntilFlyoverComplete;
      int s = t % 60;
      t = (t - s) / 60;
      int m = t % 60;
      Serial.printf("Pass time remaining: %02i minutes and %02i second(s)\n", m, s);

      display.clearDisplay();
      display.setTextSize(2);
      display.setTextColor(WHITE);
      display.setCursor(20, 0);
      display.print("Overhead!");
      display.setTextSize(2);
      display.setCursor(32, 16);
      display.printf("%02i:%02i", m, s);
      display.display();

      unsigned long loopOverhead = millis() - loopStart;
      delay(1000 - loopOverhead);
    }
    else
    {
      currentState = PASS_COMPLETE;
    }
    break;
  }
  case PASS_COMPLETE:
  {
    // shut down the NeoPixel until next ISS flyover
    pixels.clear();
    pixels.show();

    currentState = START;
    break;
  }
  }
}

void success()
{
  pixels.fill(pixels.Color(0, 255, 0));
  pixels.show();

  delay(500);

  pixels.clear();
  pixels.show();
}

void fail()
{
  pixels.fill(pixels.Color(255, 0, 0));
  pixels.show();

  delay(500);

  pixels.clear();
  pixels.show();
}

// Current time (UTC)
bool getCurrentTime()
{
  HTTPClient http;
  http.begin(client, "http://worldtimeapi.org/api/timezone/Etc/UTC"); // URL for getting the current time

  int httpCode = http.GET();
  if (httpCode > 0)
  {
    if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY)
    { // Check for the returning code
      String payload = http.getString();

      const size_t capacity = JSON_OBJECT_SIZE(15) + 550;
      DynamicJsonDocument doc(capacity);
      DeserializationError error = deserializeJson(doc, payload);
      http.end();

      if (error)
      {
        Serial.print(F("deserializeJson() failed(current time): "));
        Serial.println(error.c_str());
        return false;
      }

      currentTime = doc["unixtime"]; // save current time
      return true;
    }
    else
    {
      Serial.printf("getCurrentTime(): HTTP request failed, server response: %03i\n", httpCode);
      return false;
    }
  }
  else
  {
    Serial.printf("getCurrentTime(): HTTP request failed, reason: %s\n", http.errorToString(httpCode).c_str());
    return false;
  }
}

bool getNextPass()
{
  HTTPClient http;
  http.begin(client, "http://api.open-notify.org/iss-pass.json?lat=" + String(latitude) + "&lon=" + String(longitude) + "&alt=" + String(altitude) + "&n=5"); // URL for API call

  int httpCode = http.GET();
  if (httpCode > 0)
  {
    if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY)
    {
      String payload = http.getString(); // save response

      const size_t capacity = JSON_ARRAY_SIZE(5) + 5 * JSON_OBJECT_SIZE(2) + JSON_OBJECT_SIZE(3) + JSON_OBJECT_SIZE(5) + 190;
      DynamicJsonDocument doc(capacity);
      DeserializationError error = deserializeJson(doc, payload);
      http.end();

      if (error)
      {
        Serial.print(F("deserializeJson() failed: "));
        Serial.println(error.c_str());
        return false;
      }

      JsonObject request = doc["request"];
      long request_datetime = request["datetime"];
      Serial.printf("ISS API dt   : %02d:%02d:%02d %02d-%02d-%4d (UTC)\n",
                    hour(request_datetime), minute(request_datetime), second(request_datetime),
                    day(request_datetime), month(request_datetime), year(request_datetime));

      JsonArray response = doc["response"];
      flyoverDuration = response[0]["duration"]; // save duration of the next flyover
      riseTime = response[0]["risetime"];        // save start time of the next flyover

      if (riseTime < currentTime)
      { // If ISS has already passed, take the next flyover
        flyoverDuration = response[1]["duration"];
        riseTime = response[1]["risetime"];
      }

      Serial.printf("Next pass at : %02d:%02d:%02d %02d-%02d-%04d (UTC)\n",
                    hour(riseTime), minute(riseTime), second(riseTime), day(riseTime), month(riseTime), year(riseTime));

      TimeChangeRule *tcr;
      time_t t = myTz.toLocal(riseTime, &tcr);

      Serial.printf("             : %02d:%02d:%02d %02d-%02d-%04d (%s)\n",
                    hour(t), minute(t), second(t), day(t), month(t), year(t), tcr->abbrev);

      return true;
    }
    else
    {
      Serial.printf("getNextPass(): HTTP request failed, server response: %03i\n", httpCode);
      return false;
    }
  }
  else
  {
    Serial.printf("getNextPass(): HTTP request failed, reason: %s\n", http.errorToString(httpCode).c_str());
    return false;
  }
}

Well…

http://api.open-notify.org/iss-pass.json?lat=0.0&lon=0.0&alt=0.0&n=5

Not sure what can be done about it, GitHub - open-notify/Open-Notify-API: Source code for api.open-notify.org indicates there have been no changes for 6 years and an issue is open about maintanence…

A related API is however online

http://api.open-notify.org/iss-now.json

in browser I get

{"iss_position": {"latitude": "37.0430", "longitude": "-98.6316"}, "timestamp": 1666632869, "message": "success"}

back.

Try asking the people in the Git repo what’s up there.