Skip to content

Instantly share code, notes, and snippets.

@gomako
Created May 15, 2019 15:07
Show Gist options
  • Save gomako/4bcd3731af11896e7985f0d88a0af0a5 to your computer and use it in GitHub Desktop.
Save gomako/4bcd3731af11896e7985f0d88a0af0a5 to your computer and use it in GitHub Desktop.
// Wifi Credentials
const WIFI_NAME = "YOURSSID";
const WIFI_PASS = "YOURPASS";
// Your location latitude and longitude
const lat = 'YOURLAT';
const lon = 'YOURLON';
// Required libs
const http = require("http");
const np = require("neopixel");
// Variable to store the time
let now;
// Variable to store ISS pass info
let iss;
// Variable to set the interval so we can clear it
let intervalId;
// Setup Wifi Shim according to instructions found here:
// https://www.espruino.com/ESP8266
digitalWrite(B9, 1); // enable on Pico Shim V2
Serial2.setup(115200, { rx: A3, tx: A2 });
// Turn LED off
np.write(B5, [0,0,0]);
/**
* Connect to WIFI
*/
var wifi = require("ESP8266WiFi_0v25").connect(Serial2, function (err) {
if (err) throw err;
console.log("Connecting to WiFi");
wifi.connect(WIFI_NAME, WIFI_PASS, err => {
if (err) throw err;
console.log("Connected");
// Once we have connected, set the UTC time
setTime();
});
});
/**
* Set the UTC time to sync with the ISS API
*/
function setTime() {
console.log("Setting Time");
http.get('http://worldclockapi.com/api/json/utc/now', res => {
let contents = '';
res.on("data", data => contents += data);
res.on("close", () => {
let timeJson = JSON.parse(contents);
now = new Date(timeJson.currentDateTime);
// Now we have set the time, let's get the ISS pass data
fetchData();
});
}).on("error", e => console.log("Error getting time: ", e));
}
/**
* Fetch the ISS Pass Data
*/
function fetchData() {
console.log("Fetching Data");
let url = `http://api.open-notify.org/iss-pass.json?lat=${lat}&lon=${lon}`;
http.get(url, res => {
let contents = '';
res.on("data", data => contents += data);
res.on("close", () => {
try {
iss = JSON.parse(contents);
// If we didn't have an error, all is good
if (iss.hasOwnProperty('message') && iss.message == 'success') {
// Calculate the interval between request time and next rise
let interval = (iss.response[0].risetime * 1000) - now.getTime();
let duration = iss.response[0].duration;
showDebug(iss);
// Set the timeout function (multiply the duration by 1k to convert to ms)
setTimeout(flashLED, interval, duration * 1000);
} else {
// Something went wrong, we'll try again in 30 seconds
setTimeout(fetchData, 30 * 1000);
}
} catch (e) {
// There was an error parsing the data, let's try again in 30 seconds
console.log("ERROR parsing ISS json", e);
setTimeout(fetchData, 30 * 1000);
}
});
}).on("error", (e) => {
// There was an error fetching the data, let's try again in 30 seconds
console.log("ERROR fetching data: ", e);
setTimeout(fetchData, 30 * 1000);
});
}
/**
* Flash the LED to notify us of the
*/
function flashLED(duration) {
console.log('ISS Overhead!');
let state = true;
intervalId = setInterval(() => {
if(state) np.write(B5, [100, 10, 10])
else np.write(B5, [10, 30, 100])
state = !state
}, 1000);
setTimeout(() => {
// Clear the interval
clearInterval(intervalId);
// Turn off LED
np.write(B5, [0,0,0]);
// Fetch more data!
setTime();
}, duration);
}
/**
* Show debug information in terminal
*/
function showDebug() {
console.log("Current time: ", now);
console.log("Next rise: ", new Date(iss.response[0].risetime * 1000));
console.log("Pass duration: ", iss.response[0].duration + " seconds. Or " + iss.response[0].duration / 60 + " minutes. ");
}
@wooghee
Copy link

wooghee commented Jun 3, 2019

hello great project!
I have some experience with arduino and tried to use this code for a wemos d1 miniPro board. I thought I could use the inbuilt led instead of an external ws2812b, but somehow I cannot get the code to compile in the arduino IDE.

is this not in the same language? it looks very similar to the C variant arduino uses.

any help is appreciated :)

@gomako
Copy link
Author

gomako commented Jun 3, 2019

@wooghee Thanks!
This is javascript running on an Espruino so won't work in the Arduino IDE. I think it's possible to load the Espruino firmware onto different chips as outlined here so you could try that or port the code yourself to work on Arduino. It's pretty basic so I'm sure you can do it, the steps are:

Good luck!

@wooghee
Copy link

wooghee commented Jun 6, 2019

:) Thank you for you fast and elaborate reply!
I looked into loading Espruino onto my board but did get it to work...

So I tried translating it into arduino code.
This is my first try at API and json stuff...
I got it to read out UTC time/date and store that in a char, but got stuck at json arrays for the passes form the open-notify server.
I think the array is two dimensional, I tried narrowing it down into a single dimension array by asking only for the next pass (n=1).
can you point me in the right direction?

this is what i got so far:

''
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <ArduinoJson.h>

// Wifi Credentials
const char* ssid = "networkname";
const char* password = "password";

// Your location latitude, longitude, altitude and number of passes
const int lat = 47.0000;
const int lon = 8.0000;
const int alt = 450;
const int n = 1;

void setup()
{
Serial.begin(115200);
WiFi.begin(ssid, password);

while (WiFi.status() != WL_CONNECTED)
{
delay(1000);
Serial.println("Connecting...");
}
}

void loop()
{
if (WiFi.status() == WL_CONNECTED)
{
HTTPClient http; //Object of class HTTPClient
http.begin("http://worldclockapi.com/api/json/utc/now");
int httpCode = http.GET();

if (httpCode > 0) 
{
  const size_t bufferSize = JSON_OBJECT_SIZE(2) + JSON_OBJECT_SIZE(3) + JSON_OBJECT_SIZE(5) + JSON_OBJECT_SIZE(8) + 370;
  DynamicJsonBuffer jsonBuffer(bufferSize);
  JsonObject& root = jsonBuffer.parseObject(http.getString());

  const char* currentDateTime = root["currentDateTime"]; 
  const char* dayOfTheWeek = root["dayOfTheWeek"];
  const char* currentFileTime = root["currentFileTime"];

  Serial.print("currentDateTime: ");
  Serial.println(currentDateTime);
  Serial.print("dayOfTheWeek: ");
  Serial.println(dayOfTheWeek);
  Serial.print("currentFileTime: ");
  Serial.println(currentFileTime);
}
http.end(); //Close connection

http.begin("http://api.open-notify.org/iss-pass.json?lat=${lat}&lon=${lon}&alt=${alt}&n=${n}");

if (httpCode > 0) 
{
  const size_t bufferSize = JSON_OBJECT_SIZE(2) + JSON_OBJECT_SIZE(3) + JSON_OBJECT_SIZE(5) + JSON_OBJECT_SIZE(8) + 370;
  DynamicJsonBuffer jsonBuffer(bufferSize);
  JsonObject& root = jsonBuffer.parseObject(http.getString());

  const char* message = root["message"]; 
  const int passes = root["passes"];
  const char* response[n] = {root["response[${n}]"]}; 
  const char* dayOfTheWeek = root["dayOfTheWeek"];

  Serial.print("status: ");
  Serial.println(message);
  Serial.print("passes: ");
  Serial.println(passes);
  Serial.print("first pass: ");
  Serial.println(response[0]);
  
}
http.end(); //Close connection

}
delay(1000);

//run setup again if connection is lost
if (WiFi.status() != WL_CONNECTED) {
setup();
}
}
''

@gomako
Copy link
Author

gomako commented Jun 9, 2019

Hmm, I'm not sure. This part http://api.open-notify.org/iss-pass.json?lat=${lat}&lon=${lon}&alt=${alt}&n=${n} Doesn't look like it should work with Arduino? I may be wrong. Have you tried putting the vars directly into the url?

@wooghee
Copy link

wooghee commented Jun 10, 2019

thank you, it did not occur to me that the issue my be in the url itself,
I tried inserting the variables directly into the url but this did not work.
what seems to work is the following:

//initate the variables

const String iss = "http://api.open-notify.org/iss-pass.json?lat=";
const String lati = "47.03453&lon=";
const String longi = "8.756765&alt=";
const String alti = "230&n=";
const int p = 1;

//call api

http.begin(iss + lati + longi + alti + p);

writing this I think you meant i could just write the url as one single string, since it needs no changing during the program... i feel like an idiot now haha.

I will do this now:
const String iss = "http://api.open-notify.org/iss-pass.json?lat=47.03453&lon=8.756765&alt=230&n=1"

but i still cannot use the data coming from de server in my program.
I try to print everything to serial but it comes back empty:

status:
passes: 0

@gomako
Copy link
Author

gomako commented Jun 10, 2019

writing this I think you meant i could just write the url as one single string, since it needs no changing during the program... i feel like an idiot now haha.

This is what I feel like 99% of the time! Sometimes I explain the things I'm trying to do aloud to inanimate objects and that helps!

I had issues when setting the number of passes to 1 as it would sometimes return zero, or the pass that had already happened, so I just left that off, got the next 5 passes (the default I think) and took the first element in the pass array.

Unfortunately, I don't have a Wemos/ESP8x to test out the code with so I can't really help in terms of the JSON returned etc.

@wooghee
Copy link

wooghee commented Jun 10, 2019

yeah I will try this for my next project :)

do you know if the JSON passes returned is a array with two dimensions? maybe if i try to run a for loop that choses an element and then saves it in its own array?

thank you for your help! really appreciate it :)

@gomako
Copy link
Author

gomako commented Jun 10, 2019

If you visit your api url you should be able to see that the json consists of the following:

{
    message: "success",
    request: {
        altitude: 230,
        datetime: 1560180377,
        latitude: 47.03453,
        longitude: 8.756765,
        passes: 5
    },
    response: [
        {
            duration: 649,
            risetime: 1560183385
        },
        {
            duration: 596,
            risetime: 1560189194
        },
        ...
    ]
}

So there are three top level properties in the json object, message, request and response

Really the only one you are interested in is response and I would think you access it like this response[0].risetime to just get the first one.

Looking at your code, you set your number of passes to const int n = 1; then you declare an array with one element, but then try and assign the second index of the response array to it. I've commented your code below:

    JsonObject& root = jsonBuffer.parseObject(http.getString());
    const char* message = root["message"]; 
    const int passes = root["passes"];

    const char* response[n] = {root["response[${n}]"]}; 
    /* 
    Here you are saying that you want to create an array with a single index (char* response[1]) and assign
    the index of the response that corresponds to n, which is this case is 1, or the second index, as the first index number is 0. 
    So as you have only asked for one pass, response[1] does not exist!
    You should say:
        const char* response[n-1] = {root["response[${n-1}]"]};
    Or to be explicit
        const char* response[0] = {root["response[0]"]};

    // What is this? There is no dayOfTheWeek in the JSON, I would get rid of it
    const char* dayOfTheWeek = root["dayOfTheWeek"];

Keep trying! It's very rewarding once you figure it all out. I would also try out one of the ArduinoJSON examples to make sure that everything is working correctly and that there isn't a bug with your Wemos. It'll help you figure out the syntax, and if in doubt, println it out!

@wooghee
Copy link

wooghee commented Jun 13, 2019

thank you so much for helping me!
with some help of the ArduinoJSON examples i got it to work (edit: no it does not, it seems i just programmed a converter, to deserialize a char, not actually pulling it from the API). even better I figured out that i do not need to connect to the worldclock API since the ISS one does already provide an accurate enough time (edit: thats still something I got right I think)

working code: see next comment

@gomako
Copy link
Author

gomako commented Jun 13, 2019

Excellent, glad to hear it's working!

@wooghee
Copy link

wooghee commented Jun 13, 2019

fixed some stuff; now it really works:


#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <ArduinoJson.h>


// Wifi Credentials
const char* ssid = "ssid";
const char* password = "password";

//API server                                                 lat           lon      
const String iss = "http://api.open-notify.org/iss-pass.json?lat=47.0532452&lon=8.356456&alt=";

//number of passes requested
int p = 2;
int i = 2;
int alt = 100;

//long nowtime;
long interval;
int duration;

void setup() 
{
  Serial.begin(115200);
  while (!Serial) continue;
  WiFi.begin(ssid, password);
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, HIGH);    // turn the LED off by making the voltage LOW
  
  while (WiFi.status() != WL_CONNECTED) 
  {
    delay(1000);
    Serial.println("Connecting...");
    digitalWrite(LED_BUILTIN, LOW);    // turn the LED on
    delay(1000);                        
    digitalWrite(LED_BUILTIN, HIGH);   // turn the LED off by making the voltage LOW                       // 
  }
  
}

void loop() 
{
  if (WiFi.status() == WL_CONNECTED)
  {
    HTTPClient http;                                //Object of class HTTPClient
    http.begin(iss+alt);
    int httpCode = http.GET();
    Serial.print("\n\n\n\n\n\n\n================================================================================");
    Serial.print("\nconnected to:      ");
    Serial.println(iss+alt);
    if (httpCode > 0) 
    {
      const size_t capacity = JSON_ARRAY_SIZE(2) + 2*JSON_OBJECT_SIZE(2) + JSON_OBJECT_SIZE(3) + JSON_OBJECT_SIZE(5) + 200;
      DynamicJsonBuffer jsonBuffer(capacity);

      String json = http.getString();
      
      JsonObject& root = jsonBuffer.parseObject(json);

      // test if parsing succeeded
      if (!root.success()) 
      {
        Serial.println("parseObject() failed");
        return;
      }

      String message = root["message"];                                           // "success"

      JsonObject& request = root["request"];
      int request_altitude = request["altitude"];                                 // 437
      long request_datetime = request["datetime"];                         // 1560370729
      float request_latitude = request["latitude"];                             // 47.0243543
      float request_longitude = request["longitude"];                      // 8.3034321
      int request_passes = request["passes"];                                   // 5
      
      int response_0_duration = root["response"][0]["duration"];           // 491
      long response_0_risetime = root["response"][0]["risetime"];          // 1560416345

      //save to global variables
      duration = response_0_duration;
      interval = ((response_0_risetime)-request_datetime);

      int response_1_duration = root["response"][1]["duration"];           // 639
      long response_1_risetime = root["response"][1]["risetime"];          // 1560422032
      
      Serial.print("\nstatus:            ");
      Serial.println(message);

      Serial.print("\nrequest time:      ");
      Serial.print(request_datetime);
      Serial.println(" unix timestamp");

      Serial.print("\nrequested passes:  ");
      Serial.println(request_passes);
      
       
      Serial.print("\nnext risetime:     ");
      Serial.print(response_0_risetime);
      Serial.println(" unix timestamp");

      Serial.print("\nnext flyover in:   ");
      Serial.print(interval);
      Serial.println(" seconds");

      Serial.print("\nduration:          ");
      Serial.print(duration);
      Serial.println(" seconds");

      json = "";                       // delete stored data
    }
    http.end(); //Close connection
  }

  while(interval<=duration) // check if ISS will come up within a the duration of next pass or is already up at this very moment
  {
    digitalWrite(LED_BUILTIN, LOW);    // turn the LED on
    delay(250);                        
    digitalWrite(LED_BUILTIN, HIGH);   // turn the LED off by making the voltage LOW
    delay(250);                        
    digitalWrite(LED_BUILTIN, LOW);    // turn the LED on
    delay(250);                        
    digitalWrite(LED_BUILTIN, HIGH);   // turn the LED off by making the voltage LOW
    delay(250);                              // that makes total 1 second delay
    interval--;
    if(interval==0)
    {
      interval=10000;
    }
  }
  if(interval>=duration)                    // if not, generate new request
  {
   alt++;
   if(alt==10000){  // increase altitude up to 10000
    alt=100;        // reset altitude
   }
  }
  //run setup again if connection is lost
  if (WiFi.status() != WL_CONNECTED) 
  {
    setup();
  }
  delay(60000);                        // wait 1 minute until next request.
}


Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment