Raspberry Pi Learning Resources

People in Space Indicator

Use the Python requests module to access the People in Space API, and create an indicator to show the number of people currently in space.

On 15th December 2015, British ESA Astronaut Tim Peake set off for the International Space Station, where he will be living and working for six months. As well as the scientific research Tim will be performing while in space, he's also going to be running experiments using Python code written by school students on two Raspberry Pis, as part of the Astro Pi project. By working through this resource, you can keep count of Tim and his fellow astronauts on the ISS.

Browse the People in Space API

API stands for Application Programming Interface. One form of API is a web API, which can be used to extract data from a service provided online. Some APIs are open to the public and provide data free of charge; other APIs require a login to access them, which can be obtained with an application, or can be provided as a service to customers for a fee.

The purpose of providing data via an API is to allow people to access it for their own use or to build programs with it. Some APIs provide business-critical data sets or otherwise important data about things like the weather, financial activity, or even providing access to things like a Twitter stream or Flickr photographs from around the world. Other APIs are just for fun, like the Pokemon API, the Star Wars API or the Adventure Time API. While these APIs were made for fun, they can still be used in interesting and useful ways. You could build a Pokemon game in Minecraft, or build a quiz game about the characters in Adventure Time!

For this project, you're going to use the People in Space API, maintained and provided for free by Nathan Bergey.

  1. Start by opening a web browser and navigating to the following URL:

    http://api.open-notify.org/astros.json
  2. You should see a white page with some text like so:

    People in Space API screenshot

    You are viewing the data provided by the API. This is the manual method of accessing the data.

  3. Observe the data provided. It should look something like this:

    {
      "message": "success",
      "number": 6,
      "people": [
      {
        "craft": "ISS",
        "name": "Mikhail Kornienko"
      },
      ...
    }

    The format of this data is called JSON (pronounced "Jason"), which stands for JavaScript Object Notation. While the name refers to the programming language JavaScript, it's a standard data interchange format which is made to be portable between languages and applications. If you're familiar with Python's dictionary data structure, you'll see the similarities. In other languages, a nested array could be used in the same way.

  4. Note that the API provides three pieces of data: message, number, and people.

    • message is a single string containing the word success
    • number is the integer value 6
    • people is an array of astronauts
  5. Within the people array, there are six objects. Each object contains two pieces of data:

    • craft is the spacecraft on which the astronaut is present
    • name is the astronaut's full name

Using Python's requests module

Now you'll use the requests module in Python to access the API.

  1. Open Python 3 from the main menu:

    Open Python 3

  2. In the Python shell, type the following line and press Enter:

    >>> import requests

    (note the >>> is not typed, but denotes shell input)

    You're using the Python shell's REPL (Read-Eval-Print Loop), which means each line is executed immediately, rather than writing a file, saving it and running all commands in one go

  3. Now create a variable containing the URL of the API as a string:

    url = "http://api.open-notify.org/astros.json"
  4. Make a request to the API by entering:

    r = requests.get(url)
  5. Now simply type r and press Enter; this is the same as typing print(r) in a file. The REPL allows you to quickly inspect objects this way. You should now see the following:

    <Response [200]>

    This states that r contains a Response type object, and shows 200. This is the status code of the request, which means "Success". Other status codes include 404, which means "File not found", and 500 - "Server error".

  6. Now enter help(r). This shows the docstring (documentation string) for the Response object. You should see:

    Help on Response in module requests.models object:
    
    class Response(builtins.object)
     |  The :class:`Response <Response>` object, which contains a
     |  server's response to an HTTP request.
     ...
  7. Observe the section of the docstring headed Data descriptors defined here. You'll see a list of available properties and their descriptions, including:

    • apparent_encoding
    • content
    • is_permanent_redirect
    • is_redirect
    • links
    • ok
    • text

    These properties are essentially variables accessible within an object. For example, r.ok is a variable containing the success status of the request (True or False).

  8. Try accessing some of these properties by entering them in the REPL:

    >>> r.ok
    True
    >>> r.is_redirect
    False
    >>> r.text
    '{\n  "message": "success", \n  "number": 6...

    You'll see that r.text seems to contain the data you need. However, this is provided as a string which makes it difficult to use.

  9. Observe the Methods defined here part of the docstring, which includes:

    json(self, **kwargs)
        Returns the json-encoded content of a response, if any.

    This allows you to access the content of the URL request as JSON, which will make it more useful.

    A method is a function which belongs to an object. It's accessed in the same way as a property, but called like a function with (), for example r.json()

  10. Enter r.json(). You should see:

    {'number': 6, 'message': 'success', 'people': [{'craft': 'ISS', 'name': 'Mikhail Kornienko'}, {'craft': 'ISS', 'name': 'Scott Kelly'}, {'craft': 'ISS', 'name': 'Oleg Kononenko'}, {'craft': 'ISS', 'name': 'Kimiya Yui'}, {'craft': 'ISS', 'name': 'Kjell Lindgren'}, {'craft': 'ISS', 'name': 'Sergey Volkov'}]}
  11. Save this as a variable:

    j = r.json()
  12. Check the data type of the new variable with type(j)

    >>> type(j)
    <class 'dict'>

    You'll see that j is a dictionary, which is a very useful data structure in Python, essentially identical to the JSON format.

  13. To access data inside a dictionary, index it by name with []:

    >>> j['number']
    6

    That's it!

  14. Looking back at the commands used and putting it all together, the following code was required to retrieve the number of people in space:

    import requests
    
    url = "http://api.open-notify.org/astros.json"
    
    r = requests.get(url)
    j = r.json()
    n = j['number']
    print(n)

Adding LEDs

Next, you'll connect some LEDs to the Pi's GPIO pins and use each of them to represent a person in space.

  1. Start by using a male-to-female jumper wire to connect one of the Pi's ground pins to the breadboard, and add a resistor to connect it to the ground rail:

    Breadboard resistor ground rail

  2. Now connect a single LED to your breadboard with the shorter leg (cathode) to the ground rail and place the longer leg (anode) in the middle of the breadboard. Then use a male-to-female jumper wire to connect the LED's row to GPIO pin 2 on the Pi:

    LED on GPIO pin 2

  3. Start by importing the LED class from the GPIO Zero library:

    >>> from gpiozero import LED
  4. Create an instance of an LED object on pin 2:

    >>> led = LED(2)
  5. Try lighting the LED:

    >>> led.on()

    The LED should now be lit!

  6. Connect a second LED to pin 3:

    LEDs on GPIO pins 2 and 3

  7. Connect the rest of your LEDs (10 in total) onto the successive pins (4, 14, 15, 17, 18, 22, 23, 27) in the same way:

    10 LEDs on GPIO pins

    10 LEDs on GPIO pins

  8. Ensure your previously used LED on pin 2 is closed:

    >>> led.close()
  9. Create a list of the pin numbers you're using:

    >>> pins = [2, 3, 4, 14, 15, 17, 18, 27, 22, 23]
  10. Create a list of LEDs using these pin numbers:

    >>> leds = [LED(pin) for pin in pins]

    This is called list comprehension: generating a list in one line instead of using a traditional loop.

  11. Inspect the leds list:

    >>> leds
    [<gpiozero.LED object on pin=2, is_active=False>,
    <gpiozero.LED object on pin=3, is_active=False>,
    <gpiozero.LED object on pin=4, is_active=False>,
    <gpiozero.LED object on pin=14, is_active=False>,
    ...]

    This prints out a representation of each object in the list, which shows some useful information.

  12. Test the leds list by turning them all on:

    >>> [led.on() for led in leds]

    This is another use of a list comprehension: running a command on every item in a list

  13. Turn them off:

    >>> [led.off() for led in leds]

    Tip: use Alt + P (previous) to go through your command history and edit the last command entered.

Make the indicator

Now you'll use the LEDs to display the number of people currently in space.

  1. It's time to bring it all together in a file. Click File > New File.

  2. Save your file as astronauts.py

  3. Start by importing the libraries you've used:

    from gpiozero import LED
    import requests
  4. Add the LED setup:

    pins = [2, 3, 4, 14, 15, 17, 18, 27, 22, 23]
    leds = [LED(p) for p in pins]
  5. Add the requests code:

    url = "http://api.open-notify.org/astros.json"
    
    r = requests.get(url)
    j = r.json()
    n = j['number']
  6. Now, rather than just printing n, you can use it to determine how many LEDs should be lit. Consider the following loop:

    for led in leds:
        led.on()

    This allows you to access each LED in turn. However, we need to know at what point we should stop turning them on.

  7. You'll need to be able to compare the number in n to each LED number (the order in the sequence, not the GPIO pin number). You'll need to use the enumerate function to assign a number to each LED in sequence. See how enumerate works by trying it out in the shell (not the file):

    >>> list(enumerate(leds))
    [(0, <gpiozero.LED object on pin=2, is_active=False>),
     (1, <gpiozero.LED object on pin=3, is_active=False>),
     (2, <gpiozero.LED object on pin=4, is_active=False>),
    ...]

    As you can see, this provides a list of the LEDs associated with index numbers, starting from 0.

  8. Since enumerate returns two values, the index number and the LED object, you can loop over it and access both values by using for i, led in enumerate(leds). Add the following loop to the code in your file:

    for i, led in enumerate(leds):
        if n > i:
            led.on()
        else:
            led.off()
  9. Save and run your code. Assuming the API returns 6, you should have 6 LEDs light up!

  10. You'll want to check the API for changes periodically. It's important to make sure you're not making too many requests to the API. Start by importing sleep by adding the import at the top of the file with the others:

    from time import sleep
  11. Now wrap the requests and LED code in a while loop to make it run continuously and keep updating the LEDs:

    while True:
        r = requests.get(url)
        j = r.json()
        n = j['number']
        for i, led in enumerate(leds):
            if n > i:
                led.on()
            else:
                led.off()
        sleep(60)  # update every minute

    Ensure the sleep is added at the end, so it waits 60 seconds between API calls to check the result every minute.

  12. Run the code and it should always show the current number of people in space. Leave it running and it should update in the future, as astronauts are delivered to and from the International Space Station.

Try making your own indicator unit out of wood or cardboard:

People in Space Indicator

Astronaut names

Finally, learn to get the astronaut names from the API call.

  1. When you originally inspected the JSON object, it showed the names of all the people in space as well as just the number. Return to the Python shell and inspect j['people'] from the dictionary:

    >>> type(j['people'])
    <class 'list'>
    >>> j['people']
    [{'craft': 'ISS', 'name': 'Mikhail Kornienko'}, {'craft': 'ISS', 'name': 'Scott Kelly'}, {'craft': 'ISS', 'name': 'Oleg Kononenko'}, {'craft': 'ISS', 'name': 'Kimiya Yui'}, {'craft': 'ISS', 'name': 'Kjell Lindgren'}, {'craft': 'ISS', 'name': 'Sergey Volkov'}]
  2. You can see that j['people'] is a list. Now inspect the list further:

    >>> len(j['people'])
    6
    >>> type(j['people'][0])
    <class 'dict'>
    >>> j['people'][0]
    {'craft': 'ISS', 'name': 'Mikhail Kornienko'}
  3. You can see that the list contains 6 elements, and that each element is another dictionary. All that's left is to access the individual elements within the dictionary:

    >>> type(j['people'][0]['craft'])
    <class 'str'>
    >>> j['people'][0]['craft']
    'ISS',
    >>> type(j['people'][0]['name'])
    <class 'str'>
    >>> j['people'][0]['name']
    'Mikhail Kornienko'
    >>> j['people'][1]['name']
    'Scott Kelly'
  4. Try printing out all the astronaut names:

    >>> for astronaut in j['people']:
            print(astronaut['name'])
    
    Mikhail Kornienko
    Scott Kelly
    Oleg Kononenko
    Kimiya Yui
    Kjell Lindgren
    Sergey Volkov
  5. Now return to your code, and add a line to save the list of astronauts as a variable after saving n:

    n = j['number']
    astronauts = j['people']
  6. Then add a line to turn all the LEDs off before the loop starts:

    [led.off() for led in leds]
  7. Inside the if n > i statement, after led.on(), add two lines to print the astronaut name and then sleep for 1 second in order to pause between each light coming on:

    if n > i:
        led.on()
        print(astronauts[i]['name'])
        sleep(1)
  8. Run the code again and the LEDs should come on one at a time, with the astronaut's name printed out at the same time.

What next?

Now you've created a People in Space Indicator with LEDs, try expanding your project:

  • Use an add-on board with at least 9 LEDs, instead of individual LEDs
  • Use an LED or LCD display to scroll the names of the astronauts (e.g Sense HAT or Display-O-Tron HAT)
  • Make an on-screen display of the number or names of the astronauts, using PyGame Zero
  • Use requests with another API to look up information about each astronaut

Display-O-Tron HAT indicator

Important dates

Note the following dates of planned delivery and departure of astronauts on the International Space Station:

  • 11 December 2015 - Oleg Kononenko, Kimiya Yui and Kjell Lindgren depart
  • 15 December 2015 - Tim Peake, Yuri Malenchenko and Timothy Kopra arrive
  • 18 March 2016 - Aleksey Ovchinin, Oleg Skripochka and Jeffrey N. Williams arrive
  • 30 May 2016 - Anatoli Ivanishin, Takuya Onishi and Kathleen Rubins arrive
  • 30 September 2016 - Sergey N. Ryzhikov, Andrei Borisenko and Robert S. Kimbrough arrive
  • 30 November 2016 - Oleg Novitskiy, Thomas Pesquet and Peggy Whitson arrive