feat(coap): Add coap multicast client and server example

This commit is contained in:
yuanjm
2020-11-19 19:26:38 +08:00
parent 877695eeb8
commit 549a1e77fa
7 changed files with 204 additions and 40 deletions

View File

@ -68,11 +68,33 @@ published under EPL+EDL: http://www.eclipse.org/californium/
...
```
You can use example coap_server together with coap_client example.
First, make coap_server and coap_client in the same local network by connecting to the same Wifi.
Then, configure the coap_client uri to `coap://232.10.11.12/Espressif ` or `coap://[ff02::fc]/Espressif`.
The coap_server will listen to ipv4 multicast ip address 232.10.11.12 and ipv6 multicast address ff02::fc.
You will see the following log:
```
...
I (468) example_connect: Connecting to HUAWEI_888...
I (1658) wifi:state: 0 -> 2 (b0)
I (1668) wifi:state: 2 -> 3 (0)
I (1675) wifi:state: 3 -> 5 (10)
I (1701) wifi:connected with HUAWEI_888, aid = 2, channel 1, HT20, bssid = 34:29:12:43:c5:40
I (3654) tcpip_adapter: sta ip: 192.168.3.3, mask: 255.255.255.0, gw: 192.168.3.1
I (3658) example_connect: Connected to HUAWEI_888
I (3662) example_connect: IPv4 address: 192.168.3.3
I (3671) example_connect: IPv6 address: fe80:0000:0000:0000:860d:8eff:fe9d:cd90
I (3684) CoAP_client: Resolve the IP address is IPV6, FF02::FC
Received: no data
...
```
## libcoap Documentation
This can be found at https://libcoap.net/doc/reference/4.2.0/
## Troubleshooting
* Please make sure Target Url includes valid `host`, optional `port`, optional `path`, and begins
* Please make sure Target Url includes valid `host` or `ip`, optional `port`, optional `path`, and begins
with `coap://` or `coap+tcp://` for a coap server that supports TCP
(not all do including coap+tcp://californium.eclipse.org).

View File

@ -28,6 +28,7 @@
#define COAP_DEFAULT_TIME_SEC 5
/* Set this to 9 to get verbose logging from within libcoap */
/* If want to change log level num to open log, don't forget to enlarge coap_example_task size*/
#define COAP_LOGGING_LEVEL 0
/* The examples use uri "coap://californium.eclipse.org" that
@ -39,7 +40,7 @@
#define COAP_DEFAULT_DEMO_URI CONFIG_TARGET_DOMAIN_URI
const static char *TAG = "CoAP_client";
static char addr_str[64] = {0};
static int resp_wait = 1;
static coap_optlist_t *optlist = NULL;
static int wait_ms;
@ -126,9 +127,8 @@ clean_up:
static void coap_example_task(void *p)
{
struct hostent *hp;
struct ip4_addr *ip4_addr;
struct addrinfo *ainfo;
struct addrinfo hints;
coap_address_t dst_addr, src_addr;
static coap_uri_t uri;
const char* server_uri = COAP_DEFAULT_DEMO_URI;
@ -165,26 +165,43 @@ static void coap_example_task(void *p)
}
memcpy(phostname, uri.host.s, uri.host.length);
hp = gethostbyname(phostname);
free(phostname);
if (hp == NULL) {
ESP_LOGE(TAG, "DNS lookup failed");
vTaskDelay(1000 / portTICK_PERIOD_MS);
memset(&hints, 0, sizeof(hints));
hints.ai_socktype = SOCK_STREAM;
hints.ai_family = AF_UNSPEC;
if (getaddrinfo(phostname, NULL, &hints, &ainfo) != 0) {
ESP_LOGE(TAG, "getaddrinfo failed");
free(phostname);
vTaskDelay(1000 / portTICK_PERIOD_MS);
continue;
}
/* Code to print the resolved IP.
Note: inet_ntoa is non-reentrant, look at ipaddr_ntoa_r for "real" code */
ip4_addr = (struct ip4_addr *)hp->h_addr;
ESP_LOGI(TAG, "DNS lookup succeeded. IP=%s", inet_ntoa(*ip4_addr));
free(phostname);
coap_address_init(&src_addr);
src_addr.addr.sin.sin_family = AF_INET;
src_addr.addr.sin.sin_port = htons(0);
src_addr.addr.sin.sin_addr.s_addr = INADDR_ANY;
if (ainfo->ai_family == AF_INET) {
struct sockaddr_in *p = (struct sockaddr_in *)ainfo->ai_addr;
inet_ntop(AF_INET, &p->sin_addr, addr_str, sizeof(addr_str));
ESP_LOGI(TAG, "Resolve the IP address is IPV4, %s",addr_str);
src_addr.addr.sin.sin_family = AF_INET;
src_addr.addr.sin.sin_port = htons(0);
src_addr.addr.sin.sin_addr.s_addr = INADDR_ANY;
} else if (ainfo->ai_family == AF_INET6) {
struct sockaddr_in6 *p = (struct sockaddr_in6 *)ainfo->ai_addr;
inet_ntop(AF_INET6, &p->sin6_addr, addr_str, sizeof(addr_str));
ESP_LOGI(TAG, "Resolve the IP address is IPV6, %s",addr_str);
struct in6_addr if_inaddr = { 0 };
struct ip6_addr if_ipaddr = { 0 };
src_addr.addr.sin6.sin6_port = htons(0);
src_addr.addr.sin6.sin6_family = AF_INET6;
src_addr.size = sizeof(struct sockaddr_in6);
bzero(&src_addr.addr.sin6.sin6_addr.un, sizeof(src_addr.addr.sin6.sin6_addr.un));
tcpip_adapter_get_ip6_linklocal(TCPIP_ADAPTER_IF_STA, &if_ipaddr);
inet6_addr_from_ip6addr(&if_inaddr, &if_ipaddr);
src_addr.addr.sin6.sin6_addr = if_inaddr;
} else {
ESP_LOGI(TAG, "ai_family is error %d", ainfo->ai_family);
goto clean_up;
}
if (uri.path.length) {
buflen = BUFSIZE;
@ -223,9 +240,15 @@ static void coap_example_task(void *p)
}
coap_address_init(&dst_addr);
dst_addr.addr.sin.sin_family = AF_INET;
dst_addr.addr.sin.sin_port = htons(uri.port);
dst_addr.addr.sin.sin_addr.s_addr = ip4_addr->addr;
dst_addr.size = ainfo->ai_addrlen;
memcpy(&dst_addr.addr, ainfo->ai_addr, ainfo->ai_addrlen);
if (ainfo->ai_family == AF_INET6) {
dst_addr.addr.sin6.sin6_family = AF_INET6;
dst_addr.addr.sin6.sin6_port = htons(uri.port);
} else {
dst_addr.addr.sin.sin_family = AF_INET;
dst_addr.addr.sin.sin_port = htons(uri.port);
}
session = coap_new_client_session(ctx, &src_addr, &dst_addr,
uri.scheme==COAP_URI_SCHEME_COAP_TCP ? COAP_PROTO_TCP :
@ -272,6 +295,7 @@ clean_up:
if (session) coap_session_release(session);
if (ctx) coap_free_context(ctx);
coap_cleanup();
freeaddrinfo(ainfo);
/* Only send the request off once */
break;
}

View File

@ -1 +1,2 @@
CONFIG_ENABLE_COAP=y
CONFIG_EXAMPLE_CONNECT_IPV6=y

View File

@ -22,6 +22,8 @@ idf.py menuconfig
* Set default serial port under Serial Flasher config
* Set WiFi SSID under Example Configuration
* Set WiFi Password under Example Configuration
* Set IPv4 multicast ip address for server to listen to
* Set IPv6 multicast ip address for server to listen to
### Build and Flash
@ -41,16 +43,16 @@ and the log is such as the following:
```
...
I (332) wifi: mode : sta (30:ae:a4:04:1b:7c)
I (1672) wifi: n:11 0, o:1 0, ap:255 255, sta:11 0, prof:1
I (1672) wifi: state: init -> auth (b0)
I (1682) wifi: state: auth -> assoc (0)
I (1692) wifi: state: assoc -> run (10)
I (1692) wifi: connected with huawei_cw, channel 11
I (1692) wifi: pm start, type: 1
I (2622) event: sta ip: 192.168.3.84, mask: 255.255.255.0, gw: 192.168.3.1
I (2622) CoAP_server: Connected to AP
I (1828) wifi:state: 0 -> 2 (b0)
I (1838) wifi:state: 2 -> 3 (0)
I (1849) wifi:state: 3 -> 5 (10)
I (1868) wifi:connected with HUAWEI_888, aid = 1, channel 1, HT20, bssid = 34:29:12:43:c5:40
I (3829) tcpip_adapter: sta ip: 192.168.3.2, mask: 255.255.255.0, gw: 192.168.3.1
I (3834) example_connect: Connected to HUAWEI_888
I (3837) example_connect: IPv4 address: 192.168.3.2
I (3846) example_connect: IPv6 address: fe80:0000:0000:0000:a6cf:12ff:fee8:733b
I (3864) CoAP_server: Configured IPV4 Multicast address 232.10.11.12
I (3875) CoAP_server: Configured IPV6 Multicast address ff02::fc
...
```

View File

@ -0,0 +1,13 @@
menu "Example Configuration"
config TARGET_MULTICAST_IPV4
string "IPv4 multicast address"
default "232.10.11.12"
help
Target IP for the example to listen.
config TARGET_MULTICAST_IPV6
string "IPv6 multicast address"
default "ff02::fc"
help
Target IP for the example to listen.
endmenu

View File

@ -21,12 +21,17 @@
#include "nvs_flash.h"
#include "protocol_examples_common.h"
#include "lwip/err.h"
#include "lwip/sockets.h"
#include "lwip/sys.h"
#include <lwip/netdb.h>
#include "coap.h"
/* Set this to 9 to get verbose logging from within libcoap */
/* If want to change log level num to open log, don't forget to enlarge coap_example_task size*/
#define COAP_LOGGING_LEVEL 0
const static char *TAG = "CoAP_server";
static char espressif_data[100];
static int espressif_data_len = 0;
@ -93,6 +98,85 @@ hnd_espressif_delete(coap_context_t *ctx,
response->code = COAP_RESPONSE_CODE(202);
}
static int coap_example_join_ipv6(int sock, char *group_name)
{
struct ipv6_mreq mreq = {0};
int netif_index = 0, err = 0;
netif_index = tcpip_adapter_get_netif_index(TCPIP_ADAPTER_IF_STA);
if (netif_index < 0) {
ESP_LOGE(TAG, "Failed to get netif index");
return ESP_FAIL;
}
err = setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_IF, &netif_index, sizeof(uint8_t));
if (err < 0) {
ESP_LOGE(TAG, "Failed to set IPV6_MULTICAST_IF. Error %d", errno);
return ESP_FAIL;
}
err = inet6_aton(group_name, &mreq.ipv6mr_multiaddr);
if (err != 1) {
ESP_LOGE(TAG, "Configured IPV6 multicast address '%s' is invalid.", group_name);
return ESP_FAIL;
}
if (!IN6_IS_ADDR_MULTICAST(&mreq.ipv6mr_multiaddr)) {
ESP_LOGW(TAG, "Configured IPV6 multicast address '%s' is not a valid multicast address. This will probably not work.", group_name);
}
mreq.ipv6mr_interface = (unsigned int)netif_index;
err = setsockopt(sock, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq, sizeof(struct ipv6_mreq));
if (err < 0) {
ESP_LOGE(TAG, "Failed to set IPV6_ADD_MEMBERSHIP. Error %d", errno);
return ESP_FAIL;
}
ESP_LOGI(TAG, "Configured IPV6 Multicast address %s", group_name);
return ESP_OK;
}
static int coap_example_join_ipv4(int sock, char *group_name)
{
struct ip_mreq imreq = { 0 };
struct in_addr iaddr = { 0 };
int err = 0;
// Configure source interface
tcpip_adapter_ip_info_t ip_info = { 0 };
err = tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_STA, &ip_info);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to get IP address info. Error 0x%x", err);
return ESP_FAIL;
}
inet_addr_from_ip4addr(&iaddr, &ip_info.ip);
// Configure multicast address to listen to
err = inet_aton(group_name, &imreq.imr_multiaddr.s_addr);
if (err != 1) {
ESP_LOGE(TAG, "Configured IPV4 multicast address '%s' is invalid.", group_name);
return ESP_FAIL;
}
if (!IP_MULTICAST(ntohl(imreq.imr_multiaddr.s_addr))) {
ESP_LOGW(TAG, "Configured IPV4 multicast address '%s' is not a valid multicast address. This will probably not work.", group_name);
}
// Assign the IPv4 multicast source interface, via its IP
// (only necessary if this socket is IPV4 only)
err = setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, &iaddr, sizeof(struct in_addr));
if (err < 0) {
ESP_LOGE(TAG, "Failed to set IP_MULTICAST_IF. Error %d", errno);
return ESP_FAIL;
}
err = setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imreq, sizeof(struct ip_mreq));
if (err < 0) {
ESP_LOGE(TAG, "Failed to set IP_ADD_MEMBERSHIP. Error %d", errno);
return ESP_FAIL;
}
ESP_LOGI(TAG, "Configured IPV4 Multicast address %s", inet_ntoa(imreq.imr_multiaddr.s_addr));
return ESP_OK;
}
static void coap_example_thread(void *p)
{
coap_context_t *ctx = NULL;
@ -107,24 +191,41 @@ static void coap_example_thread(void *p)
coap_endpoint_t *ep_tcp = NULL;
unsigned wait_ms;
ctx = coap_new_context(NULL);
if (!ctx) {
continue;
}
/* Prepare the CoAP server socket */
coap_address_init(&serv_addr);
serv_addr.addr.sin.sin_family = AF_INET;
serv_addr.addr.sin.sin_addr.s_addr = INADDR_ANY;
serv_addr.addr.sin.sin_port = htons(COAP_DEFAULT_PORT);
ctx = coap_new_context(NULL);
if (!ctx) {
continue;
}
ep_udp = coap_new_endpoint(ctx, &serv_addr, COAP_PROTO_UDP);
ep_udp = coap_new_endpoint(ctx, &serv_addr, COAP_PROTO_UDP); //Add IPv4 endpoint
if (!ep_udp) {
goto clean_up;
}
coap_example_join_ipv4(ep_udp->sock.fd, CONFIG_TARGET_MULTICAST_IPV4);
ep_tcp = coap_new_endpoint(ctx, &serv_addr, COAP_PROTO_TCP);
if (!ep_tcp) {
goto clean_up;
}
/* Prepare the CoAP server socket */
coap_address_init(&serv_addr);
serv_addr.addr.sin6.sin6_port = htons(COAP_DEFAULT_PORT);
serv_addr.addr.sin6.sin6_family = AF_INET6;
serv_addr.size = sizeof(struct sockaddr_in6);
bzero(&serv_addr.addr.sin6.sin6_addr.un, sizeof(serv_addr.addr.sin6.sin6_addr.un));
ep_udp = coap_new_endpoint(ctx, &serv_addr, COAP_PROTO_UDP); //Add IPv6 endpoint
if (!ep_udp) {
goto clean_up;
}
coap_example_join_ipv6(ep_udp->sock.fd, CONFIG_TARGET_MULTICAST_IPV6);
ep_tcp = coap_new_endpoint(ctx, &serv_addr, COAP_PROTO_TCP);
if (!ep_tcp) {
goto clean_up;
}
resource = coap_resource_init(coap_make_str_const("Espressif"), 0);
if (!resource) {
goto clean_up;

View File

@ -1 +1,2 @@
CONFIG_ENABLE_COAP=y
CONFIG_EXAMPLE_CONNECT_IPV6=y