Learn to write a simple client application with unicoap
Learn to write a simple client application with unicoap
Sample code. Find the sample code in examples/networking/coap/unicoap_client.
In this tutorial, you will learn how you can create a simple CoAP client with unicoap. We will support CoAP over UDP and DTLS and try out the application using RIOT's native board. Hence, you will need a Linux host and support for tuntap (tap interfaces).
To start, we will create a Makefile and add unicoap as a dependency. Because we want to support UDP and DTLS, we need to add the respective drivers in the Makefile. We will build a small shell application that accepts a URI and a request payload.
Because RIOT allows you to switch the network backend, we need to specify one. In this tutorial, we choose Generic (GNRC) network stack.
In main.c, we include unicoap.
Because we're using the shell to accept a URI, we need to import the shell:
At the bottom of main(), let's initialize the shell. shell_run will run in a loop, accepting commands.
We will define a custom command called unicoap (You can import support for more, predefined shell commands, e.g., to see a list of network interfaces, specify USEMODULE += shell_cmds_default in your Makefile, and type ifconfig in the shell). Place this static definition for the command at the top-level (outside any functions) in your main.c.
The unicoap shell command and its arguments will be handled by a function called _cli, but you can choose any name you would like. _cli is very similar to the way main() handles command-line arguments: it accepts an argc function parameter (number of whitespace-separated command-line arguments) and an argv function parameter (pointer to an array of strings). For brevity, we will omit all the sanity checks on arguments, but you can look up the full implementation in examples/networking/coap/unicoap_client.
Let's make our unicoap shell command accept three positional arguments. Followed by the request method and URI, we want to optionally accept a payload string for the request. Trivially, this only makes sense for, e.g., the PUT and POST methods, as GET request do not have a payload.
First, we need to translate the method string to unicoap's unicoap_method_t type. Note that argv[0] will be the name of the command itself, i.e., the first overall command-line argument. Hence, the method argument will be at argv[1].
Next, we will extract the URI and payload.
Let's prepare a request.
We will specify to which server to send the request by passing a Uniform Resource Identifier (URI). To make that work, we need to import the URI Client Support module in our Makefile.
unicoap supports multiple ways of addressing a server resource. You can create a unicoap_endpoint_t, manually set the IP address, or even resolve a Fully Qualified Domain Name (FQDN) using the Domain Name System (DNS), but we want users of our shell command to be able to type a string like they would in a web browser starting with the URI scheme coap://. The URI way of addressing a resource also saves us the trouble of having to add the URI path manually as CoAP options to the message, e.g., using unicoap_options_t::unicoap_options_add_uri_path.
As we will only be sending requests through a tap interface to the Python server script running on your linux host, we will use an IP address. If you want to use a domain name instead in the URI, please refer to the instructions in URI Client Support.
Now we can send the request. There are multiple versions unicoap provides — we choose the asynchronous version that calls a handler function on the internal processing thread when the response has arrived or the request fails.
Finally, we specify a function that is going to handle response. We're going to implement _on_response next. _on_response will take three arguments: the response, auxiliary information like the server address, and an error argument. Always first check if it is nonzero and bail out if so.
Inside _on_response we're going to log the request initially. We use unicoap_print_code to get a string representation of the CoAP status, i.e., the CoAP message code, and unicoap_message_t::unicoap_message_payload_get_size for the number of payload bytes. This will log a string like response 2.05 Content (42 bytes).
Next, we check if the response format is text/plain and log the response as text. Otherwise, we dump the response payload as hex. To do that, include the object dump header and import the od module.
Top of C file:
We also need to check if the payload in the response message is contiguous, i.e., is not chunked into individual vector elements. Normally, you can expect responses to always be contiguous, but you must check this.
To enable Datagram Transport Layer Security, we already imported the CoAP over DTLS Driver. However, we need to include additional headers to add a DTLS credential to unicoap.
In the main function, we then need to add this credential to the DTLS socket which we can retrieve using unicoap_transport_dtls_get_socket.
Time to test it!
The sample code includes a server.py script that uses aiocoap. To use it we need to compile the client application, run it and then send a CoAP request. We're going to use RIOT's native board for this, i.e., both the client and server will run on your Linux host and there will be no wireless network involved — no antenna needed. To compile and run the app, run this shell command:
This should show something similar to this:
unicoap will emit debug logs depending on CONFIG_UNICOAP_DEBUG_LOGGING and CONFIG_UNICOAP_ASSIST. These settings are enabled in the sample code in app.config.In a second terminal session, run
You should see output like this:
And then type in the RIOT shell (the running example application)
You should see the following response:
Congrats, the client shell is working!
If you want to capture the actual CoAP messages being sent, open a third terminal session and run tcpdump -i tap0 -w coap-welcome.pcap, then execute the client script as described above, then terminate tcpdump with CTRL+C. You can now open coap-greeting.pcap with Wireshark to inspect CoAP messages. You should see a NON request followed by a CON response which elicits another ACK message from the client.