preface
Recently, I was studying the COAP protocol and found a COAP simple library that can be used on ESP32 when trying to use the COAP protocol. Although the library is not perfect, the part about loop processing should not be completed, but it is easier for friends who contact COAP for the first time to understand and learn. Friends who need it can download it below:
https://github.com/hirotakast...
I used to use IOT PI COAP to communicate with PC node coap, but because the COAP simple library is imperfect, I can't communicate with node coap normally. I can only communicate with the same library devices. This time, I'll try M2M communication between ESP32.
Get Library
This library can be downloaded using the arduino IDE:
If you don't see this library, you can go to the preferences and add the website of the additional development board Manager:
https://github.com/espressif/...
I can refer to for specific use arduino ultra detailed introduction to development Or download it directly through the GitHub website I sent above.
Code parsing
The following code may have been changed or cropped for ease of explanation.
This demo is an integration of client and server. You only need to register the corresponding callback function.
Initialization part
This part includes device initialization, protocol initialization and other parts, focusing on the callback function of the server / client. Similar to the SDDC official demo, after registering the callback function, find the corresponding callback function through the corresponding endpoint.
#include <WiFi.h> #include <WiFiUdp.h> #include <coap-simple.h> void setup() { Serial.begin(115200); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println(""); Serial.println("WiFi connected"); Serial.println("IP address: "); Serial.println(WiFi.localIP()); // LED State pinMode(9, OUTPUT); digitalWrite(9, HIGH); LEDSTATE = true; // Add server url endpoint // You can add multiple endpoint URLs // coap.server(callback_switch, "switch"); // coap.server(callback_env, "env/temp"); // coap.server(callback_env, "env/humidity"); Serial.println("Setup Callback Light"); // In fact, the registration server handles the callback function // Add the handler pointer and url to uri.add coap.server(callback_light, "light"); // Registers the callback function for the client response. // this endpoint is single callback. Serial.println("Setup Response Callback"); // It is the same as above. In fact, it is to register the callback function pointer in resp coap.response(callback_response); // Start the soap server / client using the default port 5683 coap.start(); } void loop() { // As a client, send a GET or put soap request to the soap server // Can be sent to another ESP32 // msgid = coap.put(IPAddress(192, 168, 128, 101), 5683, "light", "0"); // msgid = coap.get(IPAddress(192, 168, 128, 101), 5683, "light"); delay(1000); coap.loop(); }
Callback function
// The CoAP server endpoint URL processes and responds to commands sent by the client void callback_light(CoapPacket &packet, IPAddress ip, int port) { // This is a callback function that simulates the control lamp by receiving the command Serial.println("[Light] ON/OFF"); Serial.println(packet.messageid); // Send response char p[packet.payloadlen + 1]; memcpy(p, packet.payload, packet.payloadlen); p[packet.payloadlen] = NULL; String message(p); if (message.equals("0")) LEDSTATE = false; else if(message.equals("1")) LEDSTATE = true; if (LEDSTATE) { digitalWrite(9, HIGH) ; Serial.println("[Light] ON"); coap.sendResponse(ip, port, packet.messageid, "1"); } else { digitalWrite(9, LOW) ; Serial.println("[Light] OFF"); coap.sendResponse(ip, port, packet.messageid, "0"); } } // CoAP client response callback void callback_response(CoapPacket &packet, IPAddress ip, int port) { Serial.println("[Coap Response got]"); char p[packet.payloadlen + 1]; memcpy(p, packet.payload, packet.payloadlen); p[packet.payloadlen] = NULL; Serial.println(p); }
Library code
Message structure definition:
// Determine the message type in the SOAP message layer typedef enum { COAP_CON = 0, // Reliable transmission COAP_NONCON = 1, // Unreliable transmission COAP_ACK = 2, // reply COAP_RESET = 3 // Passive retransmission request after message abnormality } COAP_TYPE; // The actions executed by the command are at the request / response layer typedef enum { COAP_GET = 1, COAP_POST = 2, // Active retransmission command COAP_PUT = 3, COAP_DELETE = 4 } COAP_METHOD; // Response code, equivalent to function return value or err code, is in the request / response layer typedef enum { COAP_CREATED = RESPONSE_CODE(2, 1), COAP_DELETED = RESPONSE_CODE(2, 2), COAP_VALID = RESPONSE_CODE(2, 3), COAP_CHANGED = RESPONSE_CODE(2, 4), COAP_CONTENT = RESPONSE_CODE(2, 5), COAP_BAD_REQUEST = RESPONSE_CODE(4, 0), COAP_UNAUTHORIZED = RESPONSE_CODE(4, 1), COAP_BAD_OPTION = RESPONSE_CODE(4, 2), COAP_FORBIDDEN = RESPONSE_CODE(4, 3), COAP_NOT_FOUNT = RESPONSE_CODE(4, 4), COAP_METHOD_NOT_ALLOWD = RESPONSE_CODE(4, 5), COAP_NOT_ACCEPTABLE = RESPONSE_CODE(4, 6), COAP_PRECONDITION_FAILED = RESPONSE_CODE(4, 12), COAP_REQUEST_ENTITY_TOO_LARGE = RESPONSE_CODE(4, 13), COAP_UNSUPPORTED_CONTENT_FORMAT = RESPONSE_CODE(4, 15), COAP_INTERNAL_SERVER_ERROR = RESPONSE_CODE(5, 0), COAP_NOT_IMPLEMENTED = RESPONSE_CODE(5, 1), COAP_BAD_GATEWAY = RESPONSE_CODE(5, 2), COAP_SERVICE_UNAVALIABLE = RESPONSE_CODE(5, 3), COAP_GATEWAY_TIMEOUT = RESPONSE_CODE(5, 4), COAP_PROXYING_NOT_SUPPORTED = RESPONSE_CODE(5, 5) } COAP_RESPONSE_CODE; // Option number, in the SOAP message layer typedef enum { COAP_IF_MATCH = 1, COAP_URI_HOST = 3, COAP_E_TAG = 4, COAP_IF_NONE_MATCH = 5, COAP_URI_PORT = 7, COAP_LOCATION_PATH = 8, COAP_URI_PATH = 11, COAP_CONTENT_FORMAT = 12, COAP_MAX_AGE = 14, COAP_URI_QUERY = 15, COAP_ACCEPT = 17, COAP_LOCATION_QUERY = 20, COAP_PROXY_URI = 35, COAP_PROXY_SCHEME = 39 } COAP_OPTION_NUMBER; // The content type and Accept are used to represent the media format of the CoAP payload typedef enum { COAP_NONE = -1, COAP_TEXT_PLAIN = 0, COAP_APPLICATION_LINK_FORMAT = 40, COAP_APPLICATION_XML = 41, COAP_APPLICATION_OCTET_STREAM = 42, COAP_APPLICATION_EXI = 47, COAP_APPLICATION_JSON = 50, COAP_APPLICATION_CBOR = 60 } COAP_CONTENT_TYPE; class CoapOption { public: uint8_t number; uint8_t length; uint8_t *buffer; }; class CoapPacket { public: uint8_t type = 0; uint8_t code = 0; const uint8_t *token = NULL; uint8_t tokenlen = 0; const uint8_t *payload = NULL; size_t payloadlen = 0; uint16_t messageid = 0; uint8_t optionnum = 0; CoapOption options[COAP_MAX_OPTION_NUM]; void addOption(uint8_t number, uint8_t length, uint8_t *opt_payload); };
Packet sending:
Fill in the UDP address, port, endpoint and other path related information of the package here, as well as the information of the COAP request / response layer
uint16_t Coap::send(IPAddress ip, int port, const char *url, COAP_TYPE type, COAP_METHOD method, const uint8_t *token, uint8_t tokenlen, const uint8_t *payload, size_t payloadlen, COAP_CONTENT_TYPE content_type) { // make packet CoapPacket packet; packet.type = type; packet.code = method; packet.token = token; packet.tokenlen = tokenlen; packet.payload = payload; packet.payloadlen = payloadlen; packet.optionnum = 0; packet.messageid = rand(); // use URI_HOST UIR_PATH char ipaddress[16] = ""; sprintf(ipaddress, "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]); packet.addOption(COAP_URI_HOST, strlen(ipaddress), (uint8_t *)ipaddress); // parse url int idx = 0; for (int i = 0; i < strlen(url); i++) { if (url[i] == '/') { packet.addOption(COAP_URI_PATH, i-idx, (uint8_t *)(url + idx)); idx = i + 1; } } if (idx <= strlen(url)) { packet.addOption(COAP_URI_PATH, strlen(url)-idx, (uint8_t *)(url + idx)); } // if Content-Format option uint8_t optionBuffer[2] {0}; if (content_type != COAP_NONE) { optionBuffer[0] = ((uint16_t)content_type & 0xFF00) >> 8; optionBuffer[1] = ((uint16_t)content_type & 0x00FF) ; packet.addOption(COAP_CONTENT_FORMAT, 2, optionBuffer); } // send packet return this->sendPacket(packet, ip, port); }
Here's the data for assembling the message layer of the coap package
uint16_t Coap::sendPacket(CoapPacket &packet, IPAddress ip, int port) { uint8_t buffer[COAP_BUF_MAX_SIZE]; uint8_t *p = buffer; uint16_t running_delta = 0; uint16_t packetSize = 0; // Making the base head of the coap package *p = 0x01 << 6; *p |= (packet.type & 0x03) << 4; *p++ |= (packet.tokenlen & 0x0F); *p++ = packet.code; *p++ = (packet.messageid >> 8); *p++ = (packet.messageid & 0xFF); p = buffer + COAP_HEADER_SIZE; packetSize += 4; // make token if (packet.token != NULL && packet.tokenlen <= 0x0F) { memcpy(p, packet.token, packet.tokenlen); p += packet.tokenlen; packetSize += packet.tokenlen; } // make option header for (int i = 0; i < packet.optionnum; i++) { uint32_t optdelta; uint8_t len, delta; if (packetSize + 5 + packet.options[i].length >= COAP_BUF_MAX_SIZE) { return 0; } optdelta = packet.options[i].number - running_delta; COAP_OPTION_DELTA(optdelta, &delta); COAP_OPTION_DELTA((uint32_t)packet.options[i].length, &len); *p++ = (0xFF & (delta << 4 | len)); if (delta == 13) { *p++ = (optdelta - 13); packetSize++; } else if (delta == 14) { *p++ = ((optdelta - 269) >> 8); *p++ = (0xFF & (optdelta - 269)); packetSize+=2; } if (len == 13) { *p++ = (packet.options[i].length - 13); packetSize++; } else if (len == 14) { *p++ = (packet.options[i].length >> 8); *p++ = (0xFF & (packet.options[i].length - 269)); packetSize+=2; } memcpy(p, packet.options[i].buffer, packet.options[i].length); p += packet.options[i].length; packetSize += packet.options[i].length + 1; running_delta = packet.options[i].number; } // make payload if (packet.payloadlen > 0) { if ((packetSize + 1 + packet.payloadlen) >= COAP_BUF_MAX_SIZE) { return 0; } *p++ = 0xFF; memcpy(p, packet.payload, packet.payloadlen); packetSize += 1 + packet.payloadlen; } _udp->beginPacket(ip, port); _udp->write(buffer, packetSize); _udp->endPacket(); return packet.messageid; }
Because the unpacking loop part of this library has not been completed, I won't talk about it here
Result display
The COAP client sent three messages with ID 201252415712868, and then the server returned the three messages with data, and the client got the required data.
summary
It feels strange? That's right. The demo is not perfect, but the library is relatively simple and easy to understand. At the same time, it has a basic framework. It's easier to understand COAP when you understand the code.