COAP messages on W5500 built for Nucleo-L476RG
This project is a project to send and receive COAP messages using W5500 over Nucleo-L476RG.
This project is to send and receive COAP messages using W5500 over Nucleo-L476RG.
What is COAP
COAP is a lightweight protocol designed primarily to support efficient communication between IoT devices and devices with limited resources, such as various sensors.
COAP vs MQTT
COAP | MQTT | |
Purpose | It is primarily designed for communication between constrained devices. | It is primarily designed for communication between many devices. |
Protocol format | Designed based on the RESTful architecture, it has a request and response form similar to HTTP. | It is a message-driven protocol that exchanges messages between brokers and clients. |
Communication | It uses a request-response model, in which the client sends the request and the server responds. | It is based on the Publish-Subscribe model, where devices can subscribe to specific topics to receive messages or publish messages to specific topics. |
Reliability and Connection Management | Primarily uses UDP, offering optional extensions for reliability. It is an unconnected protocol because it is UDP based. | Primarily uses TCP, providing reliable connections and message delivery. |
Project Description
OS : mbed-OS
mbed-coap library : https://github.com/ARMmbed/mbed-os/tree/master/connectivity/libraries/mbed-coap
W5500 interface : https://os.mbed.com/users/sgnezdov/code/W5500Interface/#41393623ead4
#main.cpp
......
int main() {
printf("Easy connect...\n");
NetworkInterface *network = easy_connect(true);
if (!network) {
printf("Cannot connect to the network, see serial output");
return 1;
}
printf("Connected to the network. Opening a socket...\n");
// Open a socket on the network interface
socket.open(network);
// Initialize the CoAP protocol handle, pointing to local implementations on malloc/free/tx/rx functions
coapHandle = sn_coap_protocol_init(&coap_malloc, &coap_free, &coap_tx_cb, &coap_rx_cb);
// UDPSocket::recvfrom is blocking, so run it in a separate RTOS thread
recvfromThread.start(&recvfromMain);
// Path to the resource we want to retrieve
const char* coap_uri_path = "/test";
// See ns_coap_header.h
sn_coap_hdr_s *coap_res_ptr = (sn_coap_hdr_s*)calloc(sizeof(sn_coap_hdr_s), 1);
coap_res_ptr->uri_path_ptr = (uint8_t*)coap_uri_path; // Path
coap_res_ptr->uri_path_len = strlen(coap_uri_path);
coap_res_ptr->msg_code = COAP_MSG_CODE_REQUEST_GET; // CoAP method
coap_res_ptr->content_format = COAP_CT_TEXT_PLAIN; // CoAP content type
coap_res_ptr->payload_len = 0; // Body length
coap_res_ptr->payload_ptr = 0; // Body pointer
coap_res_ptr->options_list_ptr = 0; // Optional: options list
// Message ID is used to track request->response patterns, because we're using UDP (so everything is unconfirmed).
// See the receive code to verify that we get the same message ID back
coap_res_ptr->msg_id = 7;
// Calculate the CoAP message size, allocate the memory and build the message
uint16_t message_len = sn_coap_builder_calc_needed_packet_data_size(coap_res_ptr);
printf("Calculated message length: %d bytes\n", message_len);
uint8_t* message_ptr = (uint8_t*)malloc(message_len);
sn_coap_builder(message_ptr, coap_res_ptr);
// Uncomment to see the raw buffer that will be sent...
// printf("Message is: ");
// for (size_t ix = 0; ix < message_len; ix++) {
// printf("%02x ", message_ptr[ix]);
// }
// printf("\n");
int scount = socket.sendto("i-ce.itron.cloud", 5683, message_ptr, message_len);
printf("Sent %d bytes to coap://i-ce.itron.cloud:5683\n", scount);
free(coap_res_ptr);
free(message_ptr);
Thread::wait(osWaitForever);
}
- Network initialization
- open socket
- COAP protocol init
- COAP message recv thread start
- COAP message builder run
- send COAP message to i-ce.itron.cloud server's 5685 port
#easy_connect.h
......
#define ETHERNET 1
#define WIFI_ESP8266 0
#define MESH_LOWPAN_ND 0
#define MESH_THREAD 0
#define WIFI_ODIN 0
#define WIFI_REALTEK 0
......
NetworkInterface* easy_connect(bool log_messages = false) {
NetworkInterface* network_interface = 0;
int connect_success = -1;
/// This should be removed once mbedOS supports proper dual-stack
#if defined (MESH) || (MBED_CONF_LWIP_IPV6_ENABLED==true)
printf("[EasyConnect] IPv6 mode\n");
#else
printf("[EasyConnect] IPv4 mode\n");
#endif
#if MBED_CONF_APP_NETWORK_INTERFACE == WIFI_ESP8266
...
#elif MBED_CONF_APP_NETWORK_INTERFACE == WIFI_ODIN
...
#elif MBED_CONF_APP_NETWORK_INTERFACE == WIFI_REALTEK
...
#elif MBED_CONF_APP_NETWORK_INTERFACE == ETHERNET
if (log_messages) {
printf("[EasyConnect] Ethernet...\n");
}
spi.format(32, 0);
spi.frequency(100000);
printf("Wait for SPI...\r\n");
wait(1);
printf("Network Setting DHCP\r\n");
uint8_t mac_addr[6] = {0x00, 0x08, 0xdc, 0x45, 0x56, 0x67};
printf("Init with MAC address\n");
eth.init(mac_addr);
int timeout_ms = 15*1000;
int err = dhcp.setup(ð, mac_addr, timeout_ms);
if (err == (-1)) {
printf("Timeout.\n");
return NULL;
}
printf("DHCP completed\n");
printf("Connected, IP: %d.%d.%d.%d\r\n", dhcp.yiaddr[0], dhcp.yiaddr[1], dhcp.yiaddr[2], dhcp.yiaddr[3]);
char ip[24], gateway[24], netmask[24], dnsaddr[24];
sprintf(ip, "%d.%d.%d.%d", dhcp.yiaddr[0], dhcp.yiaddr[1], dhcp.yiaddr[2], dhcp.yiaddr[3]);
sprintf(gateway, "%d.%d.%d.%d", dhcp.gateway[0], dhcp.gateway[1], dhcp.gateway[2], dhcp.gateway[3]);
sprintf(netmask, "%d.%d.%d.%d", dhcp.netmask[0], dhcp.netmask[1], dhcp.netmask[2], dhcp.netmask[3]);
sprintf(dnsaddr, "%d.%d.%d.%d", dhcp.dnsaddr[0], dhcp.dnsaddr[1], dhcp.dnsaddr[2], dhcp.dnsaddr[3]);
eth.init(ip, netmask, gateway);
dns.setup(ð);
dns.set_server(dnsaddr);
bool isOK = dns.lookup("coap.me");
if (isOK) {
printf("is ok\n");
uint32_t hIP = dns.get_ip();
printf("coap.me IP: %u\n", hIP);
} else {
printf("coap.me IP is not ok\n");
}
const SocketAddress dns_sa = SocketAddress((const void *)&dhcp.dnsaddr[0], NSAPI_IPv4, 53);
nsapi_error_t nsErr = nsapi_dns_add_server(dns_sa);
if (nsErr) {
printf("[EasyConnect] error adding DNS entry: %d\n", nsErr);
}
printf("[EasyConnect] DNS IP: %s\n", dns_sa.get_ip_address());
SocketAddress coap_sa;
nsErr = nsapi_dns_query(ð, "coap.me", &coap_sa, NSAPI_IPv4);
if (nsErr) {
printf("[EasyConnect] error resolving coap.me: %d\n", nsErr);
}
if (log_messages) {
printf("[EasyConnect] Using Ethernet\n");
}
network_interface = ð
connect_success = eth.connect();
#endif
#ifdef MESH
...
#endif
if(connect_success == 0) {
if (log_messages) {
printf("[EasyConnect] Connected to Network successfully\n");
print_MAC(network_interface, log_messages);
}
} else {
if (log_messages) {
print_MAC(network_interface, log_messages);
printf("[EasyConnect] Connection to Network Failed %d!\n", connect_success);
}
return NULL;
}
const char *ip_addr = network_interface->get_ip_address();
if (ip_addr == NULL) {
if (log_messages) {
printf("[EasyConnect] ERROR - No IP address\n");
}
return NULL;
}
if (log_messages) {
printf("[EasyConnect] IP address %s\n", ip_addr);
}
return network_interface;
}
Setting up SPI interface and network connectivity to use W5500.