ISS Position Sample

Last updated on: January 20, 2023


The "ISS Position Sample" uses HTTP(S) to get the current latitude and longitude of the ISS. The sample then attempts to match the latitude and longitude to a location on earth. This sample is compatible with the following boards:

  • Icarus IoT board
  • Icarus Bee
  • Icarus SoM
  • Icarus SoM DK
[NCS v2.1.x and v2.2.x] Download sample from Icarus - ISS Position Sample (NCS v2.1.x and v2.2.x)

Project configuration

The initialization of the application is done in the prj.conf file. By enabling certain options in this file, Zephyr will include the required libraries for the application. For this sample the following options are set:


The option CONFIG_NETWORKING=y enables generic link layer and networking support.

The option CONFIG_NET_NATIVE=n disables the Zephyr native IP stack.

The option CONFIG_NET_SOCKETS=y enables the sockets API.

The option CONFIG_NET_SOCKETS_POSIX_NAMES=y allows the sockets API to be available under the standard POSIX names.

The option CONFIG_REST_CLIENT=y enables Nordic's REST client library, which handles the HTTP(S) requests.

The option CONFIG_NRF_MODEM_LIB=y enables Nordic's modem library.

The option CONFIG_LTE_LINK_CONTROL=y enables the nRF91 LTE link control library.

The option CONFIG_LTE_NETWORK_TIMEOUT=120 sets the timeout for attempting to connect to an LTE network to 120 seconds.

The option CONFIG_LTE_AUTO_INIT_AND_CONNECT=n disables automatic initialization and connection to an LTE network before the application starts. This allows for more control over when the initialization and connection happens.

The option CONFIG_MODEM_KEY_MGMT=y enables the nRF9160 modem key management library.

The option CONFIG_JSON_LIBRARY=y enables Zephyr's JSON library.

Code explanation


Since both of the HTTP(S) responses in this sample are JSON objects, the sample first needs to set up a way to handle this data. Zephyr's JSON library can parse a JSON string and store the values contained within it in pre-defined structs. Looking at the iss_position struct as an example:

struct iss_position {
char *latitude;
char *longitude;

Each member of this struct needs to match a field in the JSON string. The data type of the member needs to match the data type in the JSON string as well. For this struct then the corresponding JSON object would be:

"latitude": "...",
"longitude": "...",

in this case the response from the ISS position API returns the latitude and longitude as strings, hence why the data type in the struct is a char *.

Furthermore, the JSON library expects a descriptor for the struct. This is a second struct which lets the JSON library know how to parse the JSON string. The descriptor struct for the above struct is as follows:

static const struct json_obj_descr iss_position_descriptor[] = {
JSON_OBJ_DESCR_PRIM(struct iss_position, latitude, JSON_TOK_STRING),
JSON_OBJ_DESCR_PRIM(struct iss_position, longitude, JSON_TOK_STRING),

Once this is done for both of the responses, the JSON library can now parse the HTTP(S) responses into the structs. The data can then be accessed from those structs.

ISS position request

This is the first request made by the sample is a HTTP GET request to get the ISS latitude and longitude. The functionality for this is contained in the get_iss_position function.

This is where Nordic's REST client library is used. The use of this library effectively boils down to setting the appropriate values to the rest_client_req_context struct.

This struct contains all the information about the HTTP request to be made. Detailed information about the various members of this struct can be found in Nordic's own documentation.

Importantly, since this is a HTTP request, TLS is not used. This is shown by the following members of the rest_client_req_context struct:

.tls_peer_verify = TLS_PEER_VERIFY_NONE,

Along with the rest_client_req_context struct, a second struct is needed in order to make the request. The rest_client_resp_context contains information about the individual parts of the response, such as the status code, and the response body.

With both structs set up, the request is sent as follows:

rest_client_request(&request_context, &response_context);

From here the sample checks the status code of the response. If the request was successful, the body of the response is parsed using the JSON library, and the results are stored in the corresponding struct.

Reverse geolocation request

Using the latitude and longitude obtained from the ISS location HTTP request, the sample now attempts to determine where that location is in the world. This is handled by the get_reverse_geolocation function.

Inside this function the same rest_client_req_context struct needs to be set up. Additionally, this time the request is made using TLS. This means that first of all a certificate needs to be provisioned.

The certificate in question is a Let's encrypt R3 certificate. It is provisioned before connecting to the LTE network inside the cert_provision function. More detailed information about using TLS on the nRF9160 can be found in Nordic's documentation.

The sample arbitrarily provisions the certificate to the security tag defined by:

NOTE: While the chosen security tag is arbitrary, values above 2,147,483,647 are reserved for the modem and so should not be used to provision certificates to.

With the provisioning done, the chosen security tag can be passed to the rest_client_req_context along with the option to use TLS:

.tls_peer_verify = TLS_PEER_VERIFY_REQUIRED,

with TLS enabled, the sample sends the HTTPS GET request to the reverse geolocation API, and parses the JSON response as before. The results are printed in a loop every 30 seconds.

NOTE: The reverse geolocation API can only return locations which are above land. If the ISS is currently above an ocean, the "Unable to geocode" error will be printed.