ISS Position Sample
Introduction
The ISS Position sample fetches the current position of the International Space Station via an HTTP API and performs reverse geocoding over HTTPS to display the location name. It demonstrates REST client usage with both HTTP and HTTPS endpoints. This sample is compatible with the following boards:
Download
ISS PositionNCS v3.2.1Project configuration
The application is configured in the prj.conf file:
CONFIG_MAIN_STACK_SIZE=4096
CONFIG_NEWLIB_LIBC=y
CONFIG_NETWORKING=y
CONFIG_NET_NATIVE=n
CONFIG_NET_SOCKETS=y
CONFIG_REST_CLIENT=y
CONFIG_NRF_MODEM_LIB=y
CONFIG_LTE_LINK_CONTROL=y
CONFIG_LTE_NETWORK_TIMEOUT=120
CONFIG_MODEM_KEY_MGMT=y
CONFIG_JSON_LIBRARY=y
CONFIG_MAIN_STACK_SIZE=4096 increases the main thread stack size to accommodate the REST client and JSON parsing buffers.
CONFIG_NETWORKING and CONFIG_NET_NATIVE=n enable networking with the IP stack offloaded to the modem.
CONFIG_NET_SOCKETS enables the sockets API.
CONFIG_REST_CLIENT enables Nordic's REST client library, which handles the HTTP(S) requests.
CONFIG_NRF_MODEM_LIB enables Nordic's modem library. CONFIG_LTE_LINK_CONTROL enables the LTE link control library with a connection timeout of 120 seconds.
CONFIG_MODEM_KEY_MGMT enables the modem key management library, needed for TLS certificate provisioning.
CONFIG_JSON_LIBRARY enables Zephyr's JSON library for parsing API responses.
The sysbuild.conf file enables MCUBoot so the sample can be flashed over USB:
SB_CONFIG_BOOTLOADER_MCUBOOT=y
Code explanation
JSON parsing
Both HTTP responses are JSON objects. Zephyr's JSON library parses JSON strings into pre-defined structs. For example, the ISS position struct and its descriptor:
struct iss_position {
char *latitude;
char *longitude;
};
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),
};
Each struct member matches a field in the JSON response. The descriptor tells the JSON library how to parse each field.
TLS certificate provisioning
The HTTPS request requires a TLS certificate. An ISRG Root X1 certificate (included in the cert/ directory) is provisioned to the modem before connecting to the LTE network:
#define TLS_SEC_TAG 301222
static int cert_provision(void)
{
int err;
bool exists;
err = modem_key_mgmt_exists(TLS_SEC_TAG, MODEM_KEY_MGMT_CRED_TYPE_CA_CHAIN, &exists);
if (err) {
printk("Failed to check for certificates, err %d\n", err);
return err;
}
if (exists) {
int mismatch = modem_key_mgmt_cmp(TLS_SEC_TAG,
MODEM_KEY_MGMT_CRED_TYPE_CA_CHAIN,
cert, strlen(cert));
if (!mismatch) {
return 0;
}
err = modem_key_mgmt_delete(TLS_SEC_TAG, MODEM_KEY_MGMT_CRED_TYPE_CA_CHAIN);
if (err) {
printk("Failed to delete existing certificate, err %d\n", err);
}
}
err = modem_key_mgmt_write(TLS_SEC_TAG, MODEM_KEY_MGMT_CRED_TYPE_CA_CHAIN,
cert, sizeof(cert) - 1);
if (err) {
printk("Failed to provision certificate, err %d\n", err);
return err;
}
return 0;
}
The function checks if the certificate already exists and only re-provisions it if it has changed.
Security tag values above 2,147,483,647 are reserved for the modem and should not be used.
ISS position request
The get_iss_position function makes an HTTP GET request to retrieve the ISS latitude and longitude. Since this is a plain HTTP request, TLS is disabled:
struct rest_client_req_context req = {
.http_method = HTTP_GET,
.host = iss_position_api_host,
.url = iss_position_api_endpoint,
.port = 80,
.sec_tag = SEC_TAG_TLS_INVALID,
.tls_peer_verify = TLS_PEER_VERIFY_NONE,
.resp_buff = resp_buf,
.resp_buff_len = sizeof(resp_buf),
.timeout_ms = 30 * MSEC_PER_SEC,
...
};
struct rest_client_resp_context resp;
rest_client_request(&req, &resp);
If the request succeeds, the response body is parsed using the JSON library.
Reverse geolocation request
The get_reverse_geolocation function uses the ISS coordinates to look up the location name via an HTTPS request. The previously provisioned TLS certificate is used:
.sec_tag = TLS_SEC_TAG,
.tls_peer_verify = TLS_PEER_VERIFY_REQUIRED,
Main loop
The sample initializes the modem, provisions the certificate, connects to the LTE network, and then fetches the ISS position and reverse geolocation in a loop every 5 seconds:
err = nrf_modem_lib_init();
err = cert_provision();
printk("Waiting for network... ");
err = lte_lc_connect();
printk("OK\n");
while (true) {
if (get_iss_position(&iss_result)) {
printk("ISS Position: %s, %s (timestamp: %d)\n",
lat, lon, iss_result.timestamp);
if (get_reverse_geolocation(&geo_result, lat, lon)) {
printk("That is over: %s\n", geo_result.display_name);
}
}
k_sleep(K_SECONDS(5));
}
The reverse geolocation API can only return locations above land. If the ISS is currently above an ocean, an error will be printed.