Several examples can be found to configure the ESP8266 as a CoAP server. Last year I too had developed one. Here is my working example.
But what about using the device as a CoAP client? Using the ESP8266 to send requests to a CoAP server? With a two-ESP8266 module setup, this would provide the capability to set up a CoAP client/server architecture.
While I was unable to find a working example, much to my surprise, it was not that difficult to develop a client from my working server. Here is how it was done…
Turning the tables
After reviewing my ESP8266 CoAP server code, the seeds for a client were obvious. The server listens for CoAP request, acts on it, and sends a reply. To make a client, all that is necessary is to reverse the order.
First, use the server’s reply code to send a request to a server. Then simply listen for a reply from the CoAP server using the “listen” code.
Communication Structure
This project uses a standard http server to initiate requests to a CoAP server. With this structure, a standard web browser can be used to test the CoAP client functionality.
Separate Requests and Replies
Using http request to initiate a CoAP client request presents a slight complication. That is, I found that checking for a CoAP reply (UDP) from a request while the http (TCP) connection was open caused the ESP8266 to crash. The problem was that the TCP connection callback function blocks the CoAP UDP reply packet. It was stuck waiting until the ESP8266 watchdog timed out, and then… Crash!
One solution is to use an Asynchronous TCP library such as the one found here. But that is beyond the scope of this simple ESP8266 CoAP client project. However, I do intend to explore that option in a future post on this subject.
But for now, it was easier to resolve this problem by breaking down an http GET initiated CoAP communication exchange into two parts. The first http GET sends a CoAP request to the target CoAP server. After the TCP connection is closed. the CoAP UDP reply is received and stored. A second http GET is then needed to retrieve the CoAP reply.
Sending a CoAP Request
A CoAP request is comprised of a UDP packet per RFC7252. This example uses a simple CoAP server installed on a second ESP8266 that supports just 3 actions.
- Turn Led On/Off
- Blink Led
- Get Sensor Values
The CoAP UDP request packets are pre-defined in unique byte arrays:
- uint8_t packetbuf_LED_ON[] = {0x40, 0x03, 0x00, 0x00, 0xB5, 0x6C, 0x69, 0x67, 0x68, 0x74, 0xFF, 0x31};
- uint8_t packetbuf_LED_OFF[] = {0x40, 0x03, 0x00, 0x00, 0xB5, 0x6C, 0x69, 0x67, 0x68, 0x74, 0xFF, 0x30};
- uint8_t packetbuf_LED_BLINK[] = {0x40, 0x03, 0x00, 0x00, 0xBB, 0x6C, 0x69, 0x67, 0x68, 0x74, 0x5F, 0x62,
- 0x6C, 0x69, 0x6E, 0x6B, 0xFF, 0x35};
- uint8_t packetbuf_GETSENSORS[] = {0x40, 0x03, 0x00, 0x00, 0xB7, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
- 0xFF, 0x2F, 0x3F, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x3D, 0x47,
- 0x65, 0x74, 0x53, 0x65, 0x6E, 0x73, 0x6F, 0x72, 0x73};
The first byte (0x40) identifies the CoAP version as 1.0. The ‘0’ in the lower 4 bits indicate no tokens will follow the message header. The second byte (0x03) identifies the CoAP code as 0.03, indicating the PUT method used in the UDP request packet. Bytes 3 and 4 contain the messageID. This value starts at 0 upon ESP8266 startup and is incremented every time a message is sent.
The lower 4 bits identify the packet option length and is followed by the option value. Then, the byte following the option is the payload deliminator, and is set to oxFF.
Size (bytes) | Value (Hex) | Value(char) | Parameter | |
---|---|---|---|---|
LED ON | 5 | 6C 69 67 68 74 | light | 1 |
LED OFF | 5 | 6C 69 67 68 74 | light | 0 |
LED BLINK | 11 | 6C 69 67 68 74 5F 62 6C 69 6E 6B | light_blink | 0-9 |
GET SENSORS | 72 65 71 75 65 73 74 | request | /?request=GetSensors |
The packet payload, which follows the 0xFF deliminator, contains the request message. As shown in the table above, the parameter is set to 1 to turn the CoAP server ESP8266 circuit LED on and 0 to turn the LED off.
And just as with other web server request, the CoAP requests are sent (udp_send) after an http GET is received.
- // Send CoAP Request to ESP8266 (192.168.0.141)
- else if(os_strcmp(pURL_Param->pParVal[0], "CoAPLedOn")==0) {
- coaplen = sizeof(packetbuf_LED_ON);
- incrMessageID(packetbuf_LED_ON); //Increment Mesaage ID
- udp_send(packetbuf_LED_ON, coaplen);
- payld = "http GET 'CoAPGetReply' required to get CoAP reply";
- }
- // Send CoAP Request to ESP8266 (192.168.0.141)
- else if(os_strcmp(pURL_Param->pParVal[0], "CoAPLedOff")==0) {
- coaplen = sizeof(packetbuf_LED_OFF);
- incrMessageID(packetbuf_LED_OFF); //Increment Mesaage ID
- udp_send(packetbuf_LED_OFF, coaplen);
- payld = "http GET 'CoAPGetReply' required to get CoAP reply";
- }
- // Send CoAP Request to ESP8266 (192.168.0.141)
- else if( (os_strcmp(pURL_Param->pParVal[0], "CoAPLedBlink")==0)&&(os_strcmp(pURL_Param->pParam[1], "cnt")==0) ) {
- coaplen = sizeof(packetbuf_LED_BLINK);
- //Insert Blink Count (1-9)
- *pURL_Param->pParVal[1] = (*pURL_Param->pParVal[1] < 0x31) ? 0x31 : *pURL_Param->pParVal[1];
- *pURL_Param->pParVal[1] = (*pURL_Param->pParVal[1] > 0x39) ? 0x39 : *pURL_Param->pParVal[1];
- packetbuf_LED_BLINK[coaplen-1] = *pURL_Param->pParVal[1];
- incrMessageID(packetbuf_LED_BLINK); //Increment Mesaage ID
- udp_send(packetbuf_LED_BLINK, coaplen); //Send Packet
- payld = "http GET 'CoAPGetReply' required to get CoAP reply";
- }
- // Send CoAP Request to ESP8266 (192.168.0.141)
- else if(os_strcmp(pURL_Param->pParVal[0], "CoAPGetSensors")==0) {
- coaplen = sizeof(packetbuf_GETSENSORS);
- incrMessageID(packetbuf_GETSENSORS); //Increment Mesaage ID
- udp_send(packetbuf_GETSENSORS, coaplen);
- payld = "http GET 'CoAPGetReply' required to get CoAP reply";
- }
- // Send CoAP Request to ESP8266 (192.168.0.141)
- else if(os_strcmp(pURL_Param->pParVal[0], "CoAPGetReply")==0) {
- payld = String(coap_reply_c);
- }
Receiving the CoAP Reply
Note in the above code that the CoAP reply is set (payld) after the UDP packet it sent to the CoAP server. But the reply simply states that you must send a ‘CoAPGetReply’ request in order to get the CoAP reply. As noted previously, that is due to the blocking nature of the TCP callback.
The actual reply is stored in the global character string ‘coap_reply_c’.To get the reply, simply send the second GET request:
<ESP_IP>:<ESP_PORT>/?request=CoAPGetReply
Example Transactions
So here is the browser based requests you can use the test this CoAP server. This example assumes the ESP IP is 192.168.0.141 and the TCP port is set to 9706.
URL (192.168.0.141:9706) Suffix | http Reply | |
---|---|---|
Set CoAP Server LED Off | /?request=CoAPLedOff | http GET 'CoAPGetReply' required to get CoAP reply |
Set CoAP Server LED Off | /?request=CoAPGetReply | 0 |
Set CoAP Server LED On | /?request=CoAPLedOn | http GET 'CoAPGetReply' required to get CoAP reply |
Set CoAP Server LED On | /?request=CoAPGetReply | 1 |
Set CoAP Server LED Blinking 3 times | /?request=CoAPLedBlink&cnt=3 | http GET 'CoAPGetReply' required to get CoAP reply |
Get CoAP Server Sensor Values | /?request=CoAPGetSensors | http GET 'CoAPGetReply' required to get CoAP reply |
Get CoAP Server Sensor Values | /?request=CoAPGetReply | { "Ain0":"142.00", "Ain1":"143.00", "Ain2":"143.00", "Ain3":"145.00", "Ain4":"144.00", "Ain5":"145.00", "Ain6":"144.00", "Ain7":"140.00", "SYS_Heap":"32896", "SYS_Time":"37" } |
This project’s GitHub Repository
In Closing
There you have it. A simple ESP8266 CoAP client structure to use in your custom application. But this is just a starting framework. There are obvious enhancements that can be made to this working example. Here are some of the things I would do next…
- Add web configurable options – Example
- Revise to support Async TCP
- Implementation of additional RFC7252 options
Hope you find this information useful…