Feedback
Feedback

If you are having issues with the exercises, please create a ticket on DevZone: devzone.nordicsemi.com
Click or drag files to this area to upload. You can upload up to 2 files.

Exercise 1

Connecting to an HTTP server

In this exercise, we will establish communication between your board and an HTTP server to post information, and then remotely access that information using another HTTP client, running on a PC, tablet, or a smartphone.

Sending a POST request to the HTTP server’s /new will return a unique 36-character client ID. We will then use this client ID as the URL when sending and retrieving the information, using PUT and GET requests to ensure it is not altered.

Pressing button 1 on the board will increment a counter and then send a PUT message with that number to our unique client ID. Pressing button 2 will send a GET request to the unique client ID to retrieve the current counter number and print it to the console. We can then use any other HTTP client and the client ID to retrieve the counter number remotely.

We will practice using the HTTP client library in nRF Connect SDK to:

  • Create a socket and connect to the HTTP server
  • Send a POST request and retrieve the client ID
  • Send a PUT message with a counter number to the HTTP server
  • Read the status of the counter on the board from another HTTP client running on a PC, table or smartphone.

Exercise steps

In the GitHub repository for this course, go to the base code for this exercise, found in lesson5/wififund_less5_exer1.

1. Include the HTTP client library in the application.

Enable the Kconfig for the HTTP client library.

Add the following line to the prj.conf file

1.2 Include the header file of the HTTP Client library.

Add the following line in main.c

#include <zephyr/net/http/client.h>

2. Define the macros for the HTTP server hostname and port.

Add the following lines to the top of main.c

#define HTTP_HOSTNAME "echo.thingy.rocks"
#define HTTP_PORT 80

3. Declare the necessary buffers for receiving messages.

Declare recv_buf, which will be passed to the library and used for receiving HTTP messages. Then declare client_id_buf which will be used to store the client ID from the HTTP server. The client ID is a random string of 36 characters, so we make client_id_buf two characters longer to add a null terminator at the end and a front slash at the beginning, making it easier to pass as a URL to the HTTP client library.

Add the following lines to main.c

#define RECV_BUF_SIZE 2048
#define CLIENT_ID_SIZE 36

static char recv_buf[RECV_BUF_SIZE];
static char client_id_buf[CLIENT_ID_SIZE+2];

4. Define the variable for the counter as 0.

Add the following line in main.c

static int counter = 0;

5. Define the function to retrieve the client ID.

Define the function client_get_new_id() to send a POST request to <hostname>/new and retrieve a unique client ID.

5.1 Define the structure http_request and fill the block of memory

Add the following lines to the function

struct http_request req;
memset(&req, 0, sizeof(req));

5.2 Populate the http_request structure

Add the following code snippet

const char *headers[] = {"Connection: close\r\n", NULL};
req.header_fields = headers;
req.method = HTTP_POST;
req.url = "/new";
req.host = HTTP_HOSTNAME;
req.protocol = "HTTP/1.1";
req.response = client_id_cb;
req.recv_buf = recv_buf;
req.recv_buf_len = sizeof(recv_buf);

5.3 Send the request to the HTTP server and close the socket.

To send the request, we will use the API call http_client_req().

Add the following line to the function

LOG_INF("HTTP POST request");
err = http_client_req(sock, &req, 5000, NULL);
if (err < 0) {
	LOG_ERR("Failed to send HTTP POST request, err: %d", err);
}

6. Define the callback function to handle the HTTP response for the client ID request.

Now let’s define the callback function client_id_cb that will handle the HTTP response from the POST message sent in client_get_new_id().

The callback function has the following signature

http_response_cb_t signature

6.1 Log the HTTP response status.

The HTTP response structure struct http_response has a member http_status, which returns the status-code from the HTTP response message. This is useful to log for debugging purposes.

Add the following line

LOG_INF("Response status: %s", rsp->http_status);

6.2 Retrieve and format the client ID.

The client ID is found in the body of the HTTP response. The response contains the member body_frag_start, which is the start address of the body fragment contained in recv_buf. Use strncpy() to copy the client ID into our temporary buffer client_id_buf_tmp, and add a null terminator at the end.

Then we add the front slash “/” to the string, to easily be able to use it as a URL in our HTTP requests.

Add the following lines to main.c

char client_id_buf_tmp[CLIENT_ID_SIZE+1];
strncpy(client_id_buf_tmp, rsp->body_frag_start, CLIENT_ID_SIZE);
client_id_buf_tmp[CLIENT_ID_SIZE]='\0';
client_id_buf[0]='/';
strcat(client_id_buf,client_id_buf_tmp);

LOG_INF("Successfully acquired client ID: %s", client_id_buf);

6.3 Close the socket.

After receiving a response back from the HTTP server, we want to close the socket using close().

LOG_INF("Closing socket: %d", sock);
close(sock);

7. Define the function to send a PUT request to the HTTP server.

Define the function client_http_put() to send PUT requests. This will be similar to what we did in client_get_new_id(), apart from a few changes. For the method, we specify HTTP_PUT. For the url, we provide the client ID, and now we want the response callback to be response_cb.

We also need to set payload and payload_len to what we want to send with the request, i.e the counter variable, counter. Since payload expects const char *, we use snprintf() to copy the counter into a buffer.

Add the following code snippet

int err = 0;
int bytes_written;
const char *headers[] = {"Connection: close\r\n", NULL};

struct http_request req;
memset(&req, 0, sizeof(req));

char buffer[12] = {0};
bytes_written = snprintf(buffer, 12, "%d", counter);
if (bytes_written < 0){
	LOG_ERR("Unable to write to buffer, err: %d", bytes_written);
	return bytes_written;
}
req.header_fields = headers;
req.method = HTTP_PUT;
req.url = client_id_buf;
req.host = HTTP_HOSTNAME;
req.protocol = "HTTP/1.1";
req.payload = buffer;
req.payload_len = bytes_written;
req.response = response_cb;
req.recv_buf = recv_buf;
req.recv_buf_len = sizeof(recv_buf);

LOG_INF("HTTP PUT request: %s", buffer);
err = http_client_req(sock, &req, 5000, NULL);
if (err < 0) {
	LOG_ERR("Failed to send HTTP PUT request %s, err: %d", buffer, err);
}

return err;

8. Define the function to send a GET request to the HTTP server.

Define the function client_http_get() to send a GET request to the same URL to retrieve the message.

Add the following code snippet

int err = 0;
const char *headers[] = {"Connection: close\r\n", NULL};

struct http_request req;
memset(&req, 0, sizeof(req));

req.header_fields = headers;
req.method = HTTP_GET;
req.url = client_id_buf;
req.host = HTTP_HOSTNAME;
req.protocol = "HTTP/1.1";
req.response = response_cb;
req.recv_buf = recv_buf;
req.recv_buf_len = sizeof(recv_buf);

LOG_INF("HTTP GET request");
err = http_client_req(sock, &req, 5000, NULL);
if (err < 0) {
	LOG_ERR("Failed to send HTTP GET request, err: %d", err);
}

return err;

9. Define the callback function response_cb to print the body.

Define the callback function to handle the HTTP response from the PUT and GET messages.

The callback function once again prints the status of the response, found in http_status. Then if the response has a body, it will print it as well. Once we have received the response, we want to close the socket.

Add the following code snippet to your code

LOG_INF("Response status: %s", rsp->http_status);

if (rsp->body_frag_len > 0) {
	char body_buf[rsp->body_frag_len];
	strncpy(body_buf, rsp->body_frag_start, rsp->body_frag_len);
	body_buf[rsp->body_frag_len]='\0';
	LOG_INF("Received: %s", body_buf);
} 

LOG_INF("Closing socket: %d", sock);
close(sock);

10. Define the button handler to send requests upon button triggers.

Upon pressing button 1, we want the button handler to send the counter value to the HTTP server via a PUT request and then increment the counter. On the other hand, we want it to send a GET request to read that value upon pressing button 2.

Add the following code snippet to main.c

if (has_changed & DK_BTN1_MSK && button_state & DK_BTN1_MSK) {
	if (server_connect() >= 0) {
		client_http_put();
		counter++;
	}
} else if (has_changed & DK_BTN2_MSK && button_state & DK_BTN2_MSK) {
	if (server_connect() >= 0) {
		client_http_get();
	}
}

11. Retrieve the client ID upon connection with the server.

Call client_get_new_id() from main(), and print the client ID to the console.

Add the following lines to main()

if (client_get_new_id() < 0) {
	LOG_INF("Failed to get client ID");
	return 0;
}

12. Build and flash the application to your board.

This exercise uses the PSA backend for storing the Wi-Fi credentials. Therefore, you must build with TF-M.

BoardBuild with TF-M
nRF7002 DKnrf7002dk_nrf5340_cpuapp_ns
nRF5340 DK + nRF7002 EKnrf5340dk_nrf5340_cpuapp_ns

If necessary, input the commands to connect to Wi-Fi, as we have done in previous exercises.

When a Wi-Fi connection is established, observe the following log output from the application.

Testing

13. Set up an HTTP client.

To test the application, we need to setup an HTTP client to remotely read the counter value that was sent to the HTTP server. You will need an HTTP client running on your PC, smartphone, or tablet. In this exercise, we will use HTTPie’s web app, an in-browser HTTP client that does not require any installation or setup.

14. Copy the client ID from your device’s log output.

Copy the unique client ID that your device retrieves when first connecting to the HTTP server, it is marked in the log above as <your_client_ID>. You will need your device’s specific client ID to be able to retrieve the value that your device has posted to the server.

15. Open a web browser and go to httpie.io/app.

  1. Select the request method, in this case GET.
  2. Input the request-target, which is the hostname echo.thingy.rocks followed by your specific client ID.
  3. Press button 1 on your nRF70 series board a couple of times, to increment the counter.
  4. Click send to send the HTTP request to the server.

16. Observe the response from the server, number “4” for example, at the bottom of this window.

Register an account
Already have an account? Log in
(All fields are required unless specified optional)

  • 8 or more characters
  • Upper and lower case letters
  • At least one number or special character

Forgot your password?
Enter the email associated with your account, and we will send you a link to reset your password.