Getting Started with IPWorks IoT CoAP
Requirements: IPWorks IoT
IPWorks IoT is a suite of components aimed at providing lightweight yet fully-featured implementations of protocols used by the Internet of Things. This article focuses specifically on the CoAP component.
CoAP, which stands for "Constrained Application Protocol", is a client/server protocol that allows devices to interact through requests and responses, similar to HTTP. It operates over UDP and includes DTLS support. The CoAP component provides CoAP client and server implementations based on RFC 7252 and RFC 7641.
This article will discuss how to use the CoAP component in both client mode and server mode.
Contents
Client Mode
To operate the CoAP component in client mode, ensure that the Listening property is disabled (this is the default state). While in client mode, the component can be used to send requests to a CoAP server. It can also be used to observe server-side resources, causing the server to send notifications anytime said resources change.
Client-Side DTLS
By default, the component operates in NoSec mode as defined by RFC 7252. Certificate mode DTLS can be enabled by setting the SSLEnabled property to True. The UseRawPublicKey config can be used to enable Raw Public Key mode.
When DTLS is enabled, relevant handshake details will be reported by the SSLStatus event. Initially, the server will present its certificate to the client. The certificate is validated by the system and the status is provided within the SSLServerAuthentication event. If the Accept parameter of the SSLServerAuthentication event is False, the Status parameter will tell you why. If it is decided to continue, you can override and accept the certificate by setting the Accept parameter to True.
In some cases, the server requires the client to present a certificate as well. In this case, a valid certificate will need to be specified via the SSLCert property. his process may look like the following:
coap.OnSSLServerAuthentication += (o, e) => {
if (e.Accept) return;
Console.Write("Server provided the following certificate:\nIssuer: " + e.CertIssuer + "\nSubject: " + e.CertSubject + "\n");
Console.Write("The following problems have been determined for this certificate: " + e.Status + "\n");
Console.Write("Would you like to accept anyways? [y/n] ");
if (Console.Read() == 'y') e.Accept = true;
};
coap.RemoteHost = "remote_ip";
coap.RemotePort = 1234;
coap.Timeout = 30;
// if client authentication is applicable
coap.SSLCert = new Certificate("/path/to/cert.pfx", CertStoreTypes.cstPFXFile, "cert_password", "cert_subject");
Sending Requests
To send a request, populate the RequestData, RequestContentFormat, RequestETag, and RequestOptions properties (if necessary, and as applicable), and then call one of the following methods:
- Get
- Post
- Put
- Delete
- SendCustomRequest
Once a response is received, the ResponseCode, ResponseData, ResponseContentFormat, ResponseETag, and ResponseOptions properties will be populated, and the RequestComplete event will fire. (If the request times out, the properties are not populated, but the event still fires.)
coap.OnRequestComplete += (s, e) => {
Console.WriteLine("Request complete!");
Console.WriteLine(coap.ResponseCode);
};
// Make a GET request to download a picture.
coap.Get("coap://mycoap/pictures/animals/cats4.dat?format=png");
// Imaginary function which accepts PNG image data and displays the picture to the user.
showPicture(coap.ResponseDataB);
Observing Resources
To observe a resource, call the StartObserving method. Assuming the server accepts the observer registration request, it will begin sending notifications for the resource anytime it changes. Each change notification will cause the ResponseCode, ResponseData, ResponseContentFormat, ResponseETag, and ResponseOptions properties to be populated, and the Notification event to fire.
To stop observing a resource, either call StopObserving with the same URI value used to call StartObserving, or set the Notification event's StopObserving parameter to True.
coap.OnNotification += (s, e) => {
// Notifications can arrive out of order; only print to the log if this is the latest one we've received.
if (e.IsLatest) {
Console.WriteLine("Received notification for the resource at: " + e.URI);
Console.WriteLine("New Value: " + coap.ResponseData);
}
}
// Start observing a temperature sensor's data. Assume temperature values are sent back in text format.
coap.StartObserving("coap://mycoap/home/living_room/sensors/temperature?unit=fahrenheit");
// Assume the server accepts the request and starts sending notifications every so often.
// ...
// Later, stop observing the resource.
coap.StopObserving("coap://mycoap/home/living_room/sensors/temperature?unit=fahrenheit");
Server Mode
To operate in server mode, set the LocalPort to the port the component should listen on (typically 5683, the standard CoAP port), then enable the Listening property. Each time a request arrives, the RequestData, RequestContentFormat, RequestETag, and RequestOptions properties will be populated, and the Request event will fire.
Server-Side DTLS
By default, the component operates in NoSec mode as defined by RFC 7252. Certificate mode DTLS can be enabled by setting the SSLEnabled property to True. The UseRawPublicKey config can be used to enable Raw Public Key mode.
When DTLS is enabled, a valid certificate must be selected before the server can start listening for incoming connections. The certificate can be specified via the SSLCert property. Note the certificate must contain a private key.
After doing so, calling StartListening will cause the component to start listening for incoming connections. The component will listen on the interface defined by LocalHost and LocalPort, if specified. Otherwise, these values will be set by the component. If applicable, these values must be set before calling StartListening. For example:
//coap.LocalHost = "some_ip_address";
//coap.LocalPort = 1234;
coap.SSLCert = new Certificate("/path/to/cert.pfx", CertStoreTypes.cstPFXFile, "cert_password", "cert_subject");
coap.StartListening();
Console.WriteLine("Listening on: " + coap.LocalHost + ":" + coap.LocalPort);
while (coap.Listening) {
coap.DoEvents();
}
Responding to Requests
A response can be sent by populating the ResponseCode, ResponseData, ResponseContentFormat, ResponseETag, and ResponseOptions properties as desired before the event finishes.
Alternatively, the Request event's SendResponse parameter can be set to False in order to send a response back later. In this case, the RequestId value from the event should be used to call the SendResponse method later.
coap.OnRequest += (s, e) => {
// For the purpose of this snippet, assume we only service GET requests, which have a method code of 1.
if (e.Method == 1) {
Console.WriteLine("GET request received for URI path: " + e.URIPath + " and URI query params: " + e.URIQuery);
coap.ResponseCode = "2.05"; // "Content".
// Imaginary methods that look up the data and content format of the resource based on the URI path and URI query parameters.
coap.ResponseData = lookupResourceData(e.URIPath, e.URIQuery);
coap.ResponseContentFormat = lookupResourceContentFormat(e.URIPath, e.URIQuery);
} else {
coap.ResponseCode = "4.05"; // "Method Not Allowed".
coap.ResponseData = "Only GET requests are allowed."; // Include a diagnostic payload.
coap.ResponseContentFormat = "";
}
// Alternatively, this event could simply save the e.RequestId value somewhere, then some other code could fill in the
// Response* properties and call the SendResponse() method later.
};
coap.OnResponseComplete += (s, e) => {
Console.WriteLine("Response sent for request with Id " + e.RequestId);
};
Handling Observers
In server mode, the component can also support resource observation. When a client attempts to register itself as an observer of a resource, the Register event will fire; setting this event's Accept parameter to True will cause the component to accept the registration.
To notify clients that a resource has changed, populate the ResponseCode, ResponseData, ResponseContentFormat, ResponseETag, and ResponseOptions properties as desired, and then call the SendNotification method, passing it the URI of a resource that has registered observers.
When a client has unregistered from further change notifications, the Unregistered event will fire.
coap.OnRegister += (s, e) => {
Console.WriteLine("Client " + e.RemoteHost + ":" + e.RemotePort + " has registered for notifications for the URI " + e.URI);
// Imaginary method that helps ensure the application keeps track of observed URIs. The component itself maintains the
// list of observers for each URI, so the application just needs to know that there are observers in the first place.
observerRegisteredForURI(e.URI);
e.Accept = true;
};
coap.OnUnregistered += (s, e) => {
Console.WriteLine("Client " + e.RemoteHost + ":" + e.RemotePort + " has unregistered from notifications for the URI " + e.URI);
// As above, imaginary method that helps ensure the application keeps track of observed URIs.
observerUnregisteredForURI(e.URI);
};
// Somewhere else in the application, this sort of code might get called after a resource changes to inform any observers of
// the change. We use another imaginary method here to check if the changed resource is observed, and to get its information.
if (isResourceObserved()) {
coap.ResponseCode = "2.05"; // "Content".
coap.ResponseDataB = getResourceContent();
coap.ResponseContentFormat = getResourceContentFormat();
coap.SendNotification(getResourceURI());
}
We appreciate your feedback. If you have any questions, comments, or suggestions about this article please contact our support team at kb@nsoftware.com.