]> granicus.if.org Git - esp-idf/commitdiff
asio: initial idf port of asio library without ssl
authorDavid Cermak <david@Davids-MacBook-Pro.local>
Tue, 29 May 2018 09:25:24 +0000 (11:25 +0200)
committerbot <bot@espressif.com>
Mon, 30 Jul 2018 06:28:43 +0000 (06:28 +0000)
62 files changed:
.gitmodules
components/asio/asio [new submodule]
components/asio/component.mk [new file with mode: 0644]
components/asio/port/include/esp_asio_config.h [new file with mode: 0644]
components/asio/port/include/esp_exception.h [new file with mode: 0644]
components/lwip/api/sockets.c
components/lwip/include/lwip/lwip/netdb.h
components/lwip/include/lwip/lwip/sockets.h
components/lwip/include/lwip/posix/netdb.h
components/newlib/platform_include/errno.h [new file with mode: 0644]
components/newlib/platform_include/net/if.h [new file with mode: 0644]
components/newlib/platform_include/pthread.h [new file with mode: 0644]
components/newlib/platform_include/sys/poll.h [new file with mode: 0644]
components/newlib/platform_include/sys/uio.h [new file with mode: 0644]
components/newlib/platform_include/sys/un.h [new file with mode: 0644]
components/newlib/platform_include/sys/unistd.h
components/newlib/pthread.c [new file with mode: 0644]
components/vfs/include/sys/ioctl.h
docs/en/COPYRIGHT.rst
docs/en/api-reference/protocols/asio.rst [new file with mode: 0644]
docs/en/api-reference/protocols/index.rst
docs/zh_CN/api-reference/protocols/asio.rst [new file with mode: 0644]
examples/protocols/asio/chat_client/Makefile [new file with mode: 0644]
examples/protocols/asio/chat_client/README.md [new file with mode: 0644]
examples/protocols/asio/chat_client/asio_chat_client_test.py [new file with mode: 0644]
examples/protocols/asio/chat_client/components/component.mk [new file with mode: 0644]
examples/protocols/asio/chat_client/components/wifi_asio.cpp [new file with mode: 0644]
examples/protocols/asio/chat_client/main/Kconfig.projbuild [new file with mode: 0644]
examples/protocols/asio/chat_client/main/chat_client.cpp [new file with mode: 0644]
examples/protocols/asio/chat_client/main/chat_message.hpp [new file with mode: 0644]
examples/protocols/asio/chat_client/main/component.mk [new file with mode: 0644]
examples/protocols/asio/chat_client/sdkconfig.defaults [new file with mode: 0644]
examples/protocols/asio/chat_server/Makefile [new file with mode: 0644]
examples/protocols/asio/chat_server/README.md [new file with mode: 0644]
examples/protocols/asio/chat_server/asio_chat_server_test.py [new file with mode: 0644]
examples/protocols/asio/chat_server/components/component.mk [new file with mode: 0644]
examples/protocols/asio/chat_server/components/wifi_asio.cpp [new file with mode: 0644]
examples/protocols/asio/chat_server/main/Kconfig.projbuild [new file with mode: 0644]
examples/protocols/asio/chat_server/main/chat_message.hpp [new file with mode: 0644]
examples/protocols/asio/chat_server/main/chat_server.cpp [new file with mode: 0644]
examples/protocols/asio/chat_server/main/component.mk [new file with mode: 0644]
examples/protocols/asio/chat_server/sdkconfig.defaults [new file with mode: 0644]
examples/protocols/asio/tcp_echo_server/Makefile [new file with mode: 0644]
examples/protocols/asio/tcp_echo_server/README.md [new file with mode: 0644]
examples/protocols/asio/tcp_echo_server/asio_tcp_server_test.py [new file with mode: 0644]
examples/protocols/asio/tcp_echo_server/components/component.mk [new file with mode: 0644]
examples/protocols/asio/tcp_echo_server/components/wifi_asio.cpp [new file with mode: 0644]
examples/protocols/asio/tcp_echo_server/main/Kconfig.projbuild [new file with mode: 0644]
examples/protocols/asio/tcp_echo_server/main/component.mk [new file with mode: 0644]
examples/protocols/asio/tcp_echo_server/main/echo_server.cpp [new file with mode: 0644]
examples/protocols/asio/tcp_echo_server/sdkconfig.defaults [new file with mode: 0644]
examples/protocols/asio/udp_echo_server/Makefile [new file with mode: 0644]
examples/protocols/asio/udp_echo_server/README.md [new file with mode: 0644]
examples/protocols/asio/udp_echo_server/asio_udp_server_test.py [new file with mode: 0644]
examples/protocols/asio/udp_echo_server/components/component.mk [new file with mode: 0644]
examples/protocols/asio/udp_echo_server/components/wifi_asio.cpp [new file with mode: 0644]
examples/protocols/asio/udp_echo_server/main/Kconfig.projbuild [new file with mode: 0644]
examples/protocols/asio/udp_echo_server/main/component.mk [new file with mode: 0644]
examples/protocols/asio/udp_echo_server/main/udp_echo_server.cpp [new file with mode: 0644]
examples/protocols/asio/udp_echo_server/sdkconfig.defaults [new file with mode: 0644]
examples/protocols/asio/wifi_init/wifi_asio.cpp [new file with mode: 0644]
tools/ci/mirror-list.txt

index 849ef99043140fce62866b64e17132979fed7406..5c800c562bdc19eeed520f47b4d49838be03d00c 100644 (file)
@@ -41,3 +41,7 @@
 [submodule "components/mbedtls/mbedtls"]
        path = components/mbedtls/mbedtls
        url = https://github.com/espressif/mbedtls.git
+
+[submodule "components/asio/asio"]
+       path = components/asio/asio
+       url = https://github.com/espressif/asio.git
diff --git a/components/asio/asio b/components/asio/asio
new file mode 160000 (submodule)
index 0000000..55efc17
--- /dev/null
@@ -0,0 +1 @@
+Subproject commit 55efc179b76139c8f9b44bf22a4aba4803f7a7bd
diff --git a/components/asio/component.mk b/components/asio/component.mk
new file mode 100644 (file)
index 0000000..e024df3
--- /dev/null
@@ -0,0 +1,6 @@
+COMPONENT_ADD_INCLUDEDIRS := asio/asio/include port/include
+COMPONENT_PRIV_INCLUDEDIRS := private_include
+COMPONENT_SRCDIRS := asio/asio/src
+COMPONENT_OBJEXCLUDE := asio/asio/src/asio_ssl.o
+
+COMPONENT_SUBMODULES += asio
diff --git a/components/asio/port/include/esp_asio_config.h b/components/asio/port/include/esp_asio_config.h
new file mode 100644 (file)
index 0000000..accccad
--- /dev/null
@@ -0,0 +1,45 @@
+// Copyright 2018 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+#ifndef _ESP_ASIO_CONFIG_H_
+#define _ESP_ASIO_CONFIG_H_
+
+//
+// Enabling exceptions only when they are enabled in menuconfig
+//
+# include <sdkconfig.h>
+# ifndef CONFIG_CXX_EXCEPTIONS
+#  define ASIO_NO_EXCEPTIONS
+# endif   // CONFIG_CXX_EXCEPTIONS
+
+//
+// LWIP compatifility inet and address macros/functions
+//
+# define LWIP_COMPAT_SOCKET_INET 1
+# define LWIP_COMPAT_SOCKET_ADDR 1
+
+//
+// Specific ASIO feature flags
+//
+# define ASIO_DISABLE_SERIAL_PORT
+# define ASIO_SEPARATE_COMPILATION
+# define ASIO_STANDALONE
+# define ASIO_NO_TYPEID
+# define ASIO_DISABLE_SIGNAL
+# define ASIO_HAS_PTHREADS
+# define ASIO_DISABLE_EPOLL
+# define ASIO_DISABLE_EVENTFD
+# define ASIO_DISABLE_SIGNAL
+# define ASIO_DISABLE_SIGACTION
+
+#endif // _ESP_ASIO_CONFIG_H_
diff --git a/components/asio/port/include/esp_exception.h b/components/asio/port/include/esp_exception.h
new file mode 100644 (file)
index 0000000..3c5c043
--- /dev/null
@@ -0,0 +1,39 @@
+
+// Copyright 2018 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+#ifndef _ESP_EXCEPTION_H_
+#define _ESP_EXCEPTION_H_
+
+//
+// This exception stub is enabled only if exceptions are disabled in menuconfig
+//
+#if !defined(CONFIG_CXX_EXCEPTIONS) && defined (ASIO_NO_EXCEPTIONS)
+
+#include "esp_log.h"
+
+//
+// asio exception stub
+//
+namespace asio {
+namespace detail {
+template <typename Exception>
+void throw_exception(const Exception& e)
+{
+  ESP_LOGE("esp32_asio_exception", "Caught exception: %s!", e.what());   
+  abort();
+}
+}}
+#endif // CONFIG_CXX_EXCEPTIONS==1 && defined (ASIO_NO_EXCEPTIONS)
+
+#endif // _ESP_EXCEPTION_H_
index 181740813ee5fb0d4ac860029b331b1938187906..9d2d30391c707006498fbc7724347b5dad472465 100644 (file)
@@ -186,6 +186,12 @@ static void sockaddr_to_ipaddr_port(const struct sockaddr* sockaddr, ip_addr_t*
 
 #define NUM_SOCKETS MEMP_NUM_NETCONN
 
+#if !defined IOV_MAX
+#define IOV_MAX 0xFFFF
+#elif IOV_MAX > 0xFFFF
+#error "IOV_MAX larger than supported by LwIP"
+#endif /* IOV_MAX */
+
 /** This is overridable for the rare case where more than 255 threads
  * select on the same socket...
  */
@@ -1197,6 +1203,71 @@ lwip_send(int s, const void *data, size_t size, int flags)
   return (err == ERR_OK ? (int)written : -1);
 }
 
+ssize_t
+lwip_recvmsg(int s, struct msghdr *message, int flags)
+{
+  struct lwip_sock *sock;
+  int i;
+  ssize_t buflen;
+
+  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvmsg(%d, message=%p, flags=0x%x)\n", s, (void *)message, flags));
+  LWIP_ERROR("lwip_recvmsg: invalid message pointer", message != NULL, return ERR_ARG;);
+  LWIP_ERROR("lwip_recvmsg: unsupported flags", (flags & ~(MSG_PEEK|MSG_DONTWAIT)) == 0,
+             set_errno(EOPNOTSUPP); return -1;);
+
+  if ((message->msg_iovlen <= 0) || (message->msg_iovlen > IOV_MAX)) {
+    set_errno(EMSGSIZE);
+    return -1;
+  }
+
+  sock = get_socket(s);
+  if (!sock) {
+    return -1;
+  }
+
+  /* check for valid vectors */
+  buflen = 0;
+  for (i = 0; i < message->msg_iovlen; i++) {
+    if ((message->msg_iov[i].iov_base == NULL) || ((ssize_t)message->msg_iov[i].iov_len <= 0) ||
+        ((size_t)(ssize_t)message->msg_iov[i].iov_len != message->msg_iov[i].iov_len) ||
+        ((ssize_t)(buflen + (ssize_t)message->msg_iov[i].iov_len) <= 0)) {
+      sock_set_errno(sock, ERR_VAL);
+      return -1;
+    }
+    buflen = (ssize_t)(buflen + (ssize_t)message->msg_iov[i].iov_len);
+  }
+
+  int recv_flags = flags;
+  message->msg_flags = 0;
+  /* recv the data */
+  buflen = 0;
+  for (i = 0; i < message->msg_iovlen; i++) {
+    /* try to receive into this vector's buffer */
+    ssize_t recvd_local = lwip_recvfrom(s, message->msg_iov[i].iov_base, message->msg_iov[i].iov_len, recv_flags, NULL, NULL);
+    if (recvd_local > 0) {
+      /* sum up received bytes */
+      buflen += recvd_local;
+    }
+    if ((recvd_local < 0) || (recvd_local < (int)message->msg_iov[i].iov_len) ||
+        (flags & MSG_PEEK)) {
+      /* returned prematurely (or peeking, which might actually be limitated to the first iov) */
+      if (buflen <= 0) {
+        /* nothing received at all, propagate the error */
+        buflen = recvd_local;
+      }
+      break;
+    }
+    /* pass MSG_DONTWAIT to lwip_recv_tcp() to prevent waiting for more data */
+    recv_flags |= MSG_DONTWAIT;
+  }
+  if (buflen > 0) {
+    /* reset socket error since we have received something */
+    sock_set_errno(sock, 0);
+  }
+
+  return buflen;
+}
+
 int
 lwip_sendmsg(int s, const struct msghdr *msg, int flags)
 {
@@ -3237,6 +3308,14 @@ lwip_connect_r(int s, const struct sockaddr *name, socklen_t namelen)
   LWIP_API_UNLOCK();
 }
 
+int
+lwip_recvmsg_r(int s, struct msghdr *msg, int flags)
+{
+  LWIP_API_LOCK();
+  __ret = lwip_recvmsg(s, msg, flags);
+  LWIP_API_UNLOCK();
+}
+
 int
 lwip_listen_r(int s, int backlog)
 {
index 144a6e0bd49680345ee30a17787fb5a2803f8fb2..45a0362e331a196c3b10be273d685692c6794f4e 100644 (file)
@@ -125,12 +125,27 @@ int lwip_getaddrinfo(const char *nodename,
        struct addrinfo **res);
 
 #if LWIP_COMPAT_SOCKETS
+#if LWIP_COMPAT_SOCKET_ADDR == 1
+/* Some libraries have problems with inet_... being macros, so please use this define 
+    to declare normal functions */
+static inline int gethostbyname_r(const char *name, struct hostent *ret, char *buf, size_t buflen, struct hostent **result, int *h_errnop)
+{ return lwip_gethostbyname_r(name, ret, buf, buflen, result, h_errnop); }
+static inline struct hostent *gethostbyname(const char *name)
+{ return lwip_gethostbyname(name); }
+static inline void freeaddrinfo(struct addrinfo *ai)
+{ lwip_freeaddrinfo(ai); }
+static inline int getaddrinfo(const char *nodename, const char *servname, const struct addrinfo *hints, struct addrinfo **res)
+{ return lwip_getaddrinfo(nodename, servname, hints, res); }
+#else
+/* By default fall back to original inet_... macros */
+
 #define gethostbyname(name) lwip_gethostbyname(name)
 #define gethostbyname_r(name, ret, buf, buflen, result, h_errnop) \
        lwip_gethostbyname_r(name, ret, buf, buflen, result, h_errnop)
 #define freeaddrinfo(addrinfo) lwip_freeaddrinfo(addrinfo)
 #define getaddrinfo(nodname, servname, hints, res) \
        lwip_getaddrinfo(nodname, servname, hints, res)
+#endif /* LWIP_COMPAT_SOCKET_ADDR == 1 */
 #endif /* LWIP_COMPAT_SOCKETS */
 
 #ifdef __cplusplus
index e9ebff65606a547f10bcf8b28796ec120a58977c..ecc219b4391b56ba7ff79b95f82d8817d2d5763b 100644 (file)
@@ -530,7 +530,6 @@ int lwip_fcntl(int s, int cmd, int val);
 #if LWIP_COMPAT_SOCKETS != 2
 
 #if ESP_THREAD_SAFE
-
 int lwip_accept_r(int s, struct sockaddr *addr, socklen_t *addrlen);
 int lwip_bind_r(int s, const struct sockaddr *name, socklen_t namelen);
 int lwip_shutdown_r(int s, int how);
@@ -541,6 +540,7 @@ int lwip_setsockopt_r (int s, int level, int optname, const void *optval, sockle
 int lwip_close_r(int s);
 int lwip_connect_r(int s, const struct sockaddr *name, socklen_t namelen);
 int lwip_listen_r(int s, int backlog);
+int lwip_recvmsg_r(int s, struct msghdr *message, int flags);
 int lwip_recv_r(int s, void *mem, size_t len, int flags);
 int lwip_read_r(int s, void *mem, size_t len); 
 int lwip_recvfrom_r(int s, void *mem, size_t len, int flags,
@@ -577,6 +577,8 @@ static inline int connect(int s,const struct sockaddr *name,socklen_t namelen)
 { return lwip_connect_r(s,name,namelen); }
 static inline int listen(int s,int backlog)
 { return lwip_listen_r(s,backlog); }
+static inline int recvmsg(int sockfd, struct msghdr *msg, int flags)
+{ return lwip_recvmsg_r(sockfd, msg, flags); }
 static inline int recv(int s,void *mem,size_t len,int flags)
 { return lwip_recv_r(s,mem,len,flags); }
 static inline int recvfrom(int s,void *mem,size_t len,int flags,struct sockaddr *from,socklen_t *fromlen)
@@ -633,6 +635,8 @@ static inline int connect(int s,const struct sockaddr *name,socklen_t namelen)
 { return lwip_connect(s,name,namelen); }
 static inline int listen(int s,int backlog)
 { return lwip_listen(s,backlog); }
+static inline int recvmsg(int sockfd, struct msghdr *msg, int flags)
+{ return lwip_recvmsg(sockfd, msg, flags); }
 static inline int recv(int s,void *mem,size_t len,int flags)
 { return lwip_recv(s,mem,len,flags); }
 static inline int recvfrom(int s,void *mem,size_t len,int flags,struct sockaddr *from,socklen_t *fromlen)
@@ -671,24 +675,37 @@ static inline int ioctl(int s,int cmd,int argp)
 #endif /* LWIP_COMPAT_SOCKETS != 2 */
 
 #if LWIP_IPV4 && LWIP_IPV6
-#define inet_ntop(af,src,dst,size) \
+#define lwip_inet_ntop(af,src,dst,size) \
     (((af) == AF_INET6) ? ip6addr_ntoa_r((const ip6_addr_t*)(src),(dst),(size)) \
      : (((af) == AF_INET) ? ip4addr_ntoa_r((const ip4_addr_t*)(src),(dst),(size)) : NULL))
-#define inet_pton(af,src,dst) \
+#define lwip_inet_pton(af,src,dst) \
     (((af) == AF_INET6) ? ip6addr_aton((src),(ip6_addr_t*)(dst)) \
      : (((af) == AF_INET) ? ip4addr_aton((src),(ip4_addr_t*)(dst)) : 0))
 #elif LWIP_IPV4 /* LWIP_IPV4 && LWIP_IPV6 */
-#define inet_ntop(af,src,dst,size) \
+#define lwip_inet_ntop(af,src,dst,size) \
     (((af) == AF_INET) ? ip4addr_ntoa_r((const ip4_addr_t*)(src),(dst),(size)) : NULL)
-#define inet_pton(af,src,dst) \
+#define lwip_inet_pton(af,src,dst) \
     (((af) == AF_INET) ? ip4addr_aton((src),(ip4_addr_t*)(dst)) : 0)
 #else /* LWIP_IPV4 && LWIP_IPV6 */
-#define inet_ntop(af,src,dst,size) \
+#define lwip_inet_ntop(af,src,dst,size) \
     (((af) == AF_INET6) ? ip6addr_ntoa_r((const ip6_addr_t*)(src),(dst),(size)) : NULL)
-#define inet_pton(af,src,dst) \
+#define lwip_inet_pton(af,src,dst) \
     (((af) == AF_INET6) ? ip6addr_aton((src),(ip6_addr_t*)(dst)) : 0)
 #endif /* LWIP_IPV4 && LWIP_IPV6 */
 
+#if LWIP_COMPAT_SOCKET_INET == 1
+/* Some libraries have problems with inet_... being macros, so please use this define 
+    to declare normal functions */
+static inline const char *inet_ntop(int af, const void *src, char *dst, socklen_t size)
+{ lwip_inet_ntop(af, src, dst, size);    return dst; }
+static inline int inet_pton(int af, const char *src, void *dst)
+{ lwip_inet_pton(af, src, dst); return 1; }   
+#else
+/* By default fall back to original inet_... macros */
+# define inet_ntop(a,b,c,d) lwip_inet_ntop(a,b,c,d) 
+# define inet_pton(a,b,c)   lwip_inet_pton(a,b,c)   
+#endif /* LWIP_COMPAT_SOCKET_INET */
+
 #endif /* LWIP_COMPAT_SOCKETS */
 
 #ifdef __cplusplus
index 12d4c7f566c77fc4ded1aaf30a67072cd3f7d81c..363154f68893b3c96bc4d610152f87776d105c39 100644 (file)
  */
 
 #include "lwip/netdb.h"
+
+#ifdef ESP_PLATFORM
+int getnameinfo(const struct sockaddr *addr, socklen_t addrlen,
+                char *host, socklen_t hostlen,
+                char *serv, socklen_t servlen, int flags);
+
+#endif
diff --git a/components/newlib/platform_include/errno.h b/components/newlib/platform_include/errno.h
new file mode 100644 (file)
index 0000000..85fb2e1
--- /dev/null
@@ -0,0 +1,39 @@
+
+// Copyright 2018 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+#ifndef _ESP_PLATFORM_ERRNO_H_
+#define _ESP_PLATFORM_ERRNO_H_
+
+#include_next "errno.h"
+
+//
+// Possibly define some missing errors
+//
+#ifndef ESHUTDOWN
+#define ESHUTDOWN      108  /* Cannot send after transport endpoint shutdown */
+#endif
+
+#ifndef EAI_SOCKTYPE
+#define        EAI_SOCKTYPE    10      /* ai_socktype not supported */
+#endif
+
+#ifndef EAI_AGAIN
+#define        EAI_AGAIN            2   /* temporary failure in name resolution */
+#endif
+
+#ifndef EAI_BADFLAGS
+#define        EAI_BADFLAGS     3       /* invalid value for ai_flags */
+#endif
+
+#endif // _ESP_PLATFORM_ERRNO_H_
diff --git a/components/newlib/platform_include/net/if.h b/components/newlib/platform_include/net/if.h
new file mode 100644 (file)
index 0000000..8760bb1
--- /dev/null
@@ -0,0 +1,45 @@
+// Copyright 2018 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+#ifndef _ESP_PLATFORM_NET_IF_H_
+#define _ESP_PLATFORM_NET_IF_H_
+
+#define        MSG_DONTROUTE   0x4     /* send without using routing tables */
+#define        SOCK_SEQPACKET  5       /* sequenced packet stream */
+#define        MSG_EOR         0x8     /* data completes record */
+#define        SOCK_SEQPACKET  5       /* sequenced packet stream */
+#define        SOMAXCONN       128
+
+#define        IF_NAMESIZE     16
+
+#define        IPV6_UNICAST_HOPS   4  /* int; IP6 hops */
+
+#define        NI_MAXHOST  1025
+#define        NI_MAXSERV  32
+#define        NI_NUMERICSERV  0x00000008
+#define        NI_DGRAM        0x00000010
+
+
+struct ipv6_mreq {
+    struct in6_addr ipv6mr_multiaddr;
+    unsigned int    ipv6mr_interface;
+};
+
+typedef u32_t socklen_t;
+
+
+unsigned int if_nametoindex(const char *ifname);
+
+char *if_indextoname(unsigned int ifindex, char *ifname);
+
+#endif // _ESP_PLATFORM_NET_IF_H_
diff --git a/components/newlib/platform_include/pthread.h b/components/newlib/platform_include/pthread.h
new file mode 100644 (file)
index 0000000..4515fb0
--- /dev/null
@@ -0,0 +1,33 @@
+// Copyright 2018 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+#ifndef __ESP_PLATFORM_PTHREAD_H__
+#define __ESP_PLATFORM_PTHREAD_H__
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include_next <pthread.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int pthread_condattr_getclock(const pthread_condattr_t * attr, clockid_t * clock_id);
+
+int pthread_condattr_setclock(pthread_condattr_t *attr, clockid_t clock_id);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // __ESP_PLATFORM_PTHREAD_H__
diff --git a/components/newlib/platform_include/sys/poll.h b/components/newlib/platform_include/sys/poll.h
new file mode 100644 (file)
index 0000000..6e00673
--- /dev/null
@@ -0,0 +1,32 @@
+// Copyright 2018 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+#ifndef _ESP_PLATFORM_SYS_POLL_H_
+#define _ESP_PLATFORM_SYS_POLL_H_
+
+#define        POLLIN          0x0001          /* any readable data available */
+#define        POLLOUT         0x0004          /* file descriptor is writeable */
+#define        POLLPRI         0x0002          /* OOB/Urgent readable data */
+#define        POLLERR         0x0008          /* some poll error occurred */
+#define        POLLHUP         0x0010          /* file descriptor was "hung up" */
+
+struct pollfd {
+   int fd;        /* The descriptor. */
+   short events;  /* The event(s) is/are specified here. */
+   short revents; /* Events found are returned here. */
+};
+
+typedef unsigned int nfds_t;
+int poll(struct pollfd *fds, nfds_t nfds, int timeout);
+
+#endif // _ESP_PLATFORM_SYS_POLL_H_
diff --git a/components/newlib/platform_include/sys/uio.h b/components/newlib/platform_include/sys/uio.h
new file mode 100644 (file)
index 0000000..ede27b2
--- /dev/null
@@ -0,0 +1,21 @@
+// Copyright 2018 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+#ifndef _ESP_PLATFORM_SYS_UIO_H_
+#define _ESP_PLATFORM_SYS_UIO_H_
+
+int writev(int s, const struct iovec *iov, int iovcnt);
+
+ssize_t readv(int fd, const struct iovec *iov, int iovcnt);
+
+#endif // _ESP_PLATFORM_SYS_UIO_H_
diff --git a/components/newlib/platform_include/sys/un.h b/components/newlib/platform_include/sys/un.h
new file mode 100644 (file)
index 0000000..a99b183
--- /dev/null
@@ -0,0 +1,24 @@
+// Copyright 2018 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+#ifndef _ESP_PLATFORM_SYS_UN_H_
+#define _ESP_PLATFORM_SYS_UN_H_
+
+#define        AF_UNIX         1               /* local to host (pipes) */
+
+struct sockaddr_un {
+            short                    sun_family;                /*AF_UNIX*/
+            char                     sun_path[108];             /*path name */
+};
+
+#endif // _ESP_PLATFORM_SYS_UN_H_
index e76c84b90bcaed0baefa33587e9f12259ea47b0d..e09b68be254dfb05afe32597e1de30f825506738 100644 (file)
@@ -23,6 +23,7 @@ extern "C" {
 #include_next <sys/unistd.h>
 
 int     _EXFUN(truncate, (const char *, off_t __length));
+int     _EXFUN(gethostname, (char *__name, size_t __len));
 
 #ifdef __cplusplus
 }
diff --git a/components/newlib/pthread.c b/components/newlib/pthread.c
new file mode 100644 (file)
index 0000000..71e50d9
--- /dev/null
@@ -0,0 +1,10 @@
+#include <pthread.h>
+#include "esp_log.h"
+
+const static char *TAG = "esp32_asio_pthread";
+
+int pthread_condattr_setclock(pthread_condattr_t *attr, clockid_t clock_id) 
+{
+    ESP_LOGW(TAG, "%s: not yet supported!", __FUNCTION__);   
+    return 0;
+}
index 95bad9818bb04b82232fac140a11387c19786f41..90cbb47d663f52d7b89c1af8956fde0b3bdc2653 100644 (file)
 
 #pragma once
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 int ioctl(int fd, int request, ...);
 
+#ifdef __cplusplus
+}
+#endif
index 8068d135d62c5cbe7e15604d2a9832ca0601210b..5c3a71047254fece1f1b4eac46ad580ced14146a 100644 (file)
@@ -53,6 +53,8 @@ These third party libraries can be included into the application (firmware) prod
 
 * :component_file:`SD/MMC driver <sdmmc/sdmmc_cmd.c>` is derived from `OpenBSD SD/MMC driver`_, Copyright (c) 2006 Uwe Stuehler, and is licensed under BSD license.
 
+* :component:`Asio <asio>`, Copyright (c) 2003-2018 Christopher M. Kohlhoff is licensed under the Boost Software License.
+
 Build Tools
 -----------
 
@@ -152,4 +154,4 @@ Copyright (C) 2011, ChaN, all right reserved.
 .. _OpenBSD SD/MMC driver: https://github.com/openbsd/src/blob/f303646/sys/dev/sdmmc/sdmmc.c
 .. _Mbed TLS: https://github.com/ARMmbed/mbedtls
 .. _spiffs: https://github.com/pellepl/spiffs
-
+.. _asio: https://github.com/chriskohlhoff/asio
diff --git a/docs/en/api-reference/protocols/asio.rst b/docs/en/api-reference/protocols/asio.rst
new file mode 100644 (file)
index 0000000..635ef05
--- /dev/null
@@ -0,0 +1,32 @@
+ASIO port
+=========
+
+Overview
+--------
+Asio is a cross-platform C++ library, see https://think-async.com. It provides a consistent asynchronous model using a modern C++ approach.
+
+
+ASIO documentation
+^^^^^^^^^^^^^^^^^^
+Please refer to the original asio documentation at https://think-async.com/Asio/Documentation.
+Asio also comes with a number of examples which could be find under Documentation/Examples on that web site.
+
+Supported features
+^^^^^^^^^^^^^^^^^^
+ESP platform port currently supports only network asynchronous socket operations; does not support serial port and ssl.
+Internal asio settings for ESP include
+- EXCEPTIONS: Supported, choice in menuconfig
+- SIGNAL, SIGACTION: Not supported
+- EPOLL, EVENTFD: Not supported
+- TYPEID: Disabled by default, but supported in toolchain and asio (provided stdlib recompiled with -frtti)
+
+Application Example
+-------------------
+ESP examples are based on standard asio examples `examples/protocols/asio`:
+- udp_echo_server
+- tcp_echo_server
+- chat_client
+- chat_server
+Please refer to the specific example README.md for details
+
+
index 7bcd671152bfebb0ece40c5e068441c1124cccb3..55dcf4325a5083ff01a9460c705b3a40d4073502 100644 (file)
@@ -8,5 +8,6 @@ Protocols API
    ESP-TLS <esp_tls>
    HTTP Client <esp_http_client>
    HTTP Server <http_server>
+   ASIO <asio>
 
 Example code for this API section is provided in :example:`protocols` directory of ESP-IDF examples.
diff --git a/docs/zh_CN/api-reference/protocols/asio.rst b/docs/zh_CN/api-reference/protocols/asio.rst
new file mode 100644 (file)
index 0000000..d1afa23
--- /dev/null
@@ -0,0 +1 @@
+.. include:: ../../../en/api-reference/protocols/asio.rst
\ No newline at end of file
diff --git a/examples/protocols/asio/chat_client/Makefile b/examples/protocols/asio/chat_client/Makefile
new file mode 100644 (file)
index 0000000..8f651a3
--- /dev/null
@@ -0,0 +1,7 @@
+#
+# This is a project Makefile. It is assumed the directory this Makefile resides in is a
+# project subdirectory.
+#
+PROJECT_NAME := asio_chatclient
+
+include $(IDF_PATH)/make/project.mk
diff --git a/examples/protocols/asio/chat_client/README.md b/examples/protocols/asio/chat_client/README.md
new file mode 100644 (file)
index 0000000..0ab16f5
--- /dev/null
@@ -0,0 +1,19 @@
+# ASIO chat server example
+
+Simple asio chat client using WiFi STA
+
+## Example workflow
+
+- WiFi STA is started and trying to connect to the access point defined through `make menuconfig`
+- Once connected and acquired IP address ASIO chat client connects to a corresponding server whose port number and ip are defined through `make menuconfig`
+- Chat client receives all messages from other chat clients, also it sends message received from stdin using `make monitor`
+
+## Running the example
+
+- Run `make menuconfig` to configure the access point's SSID and Password and server ip address and port number
+- Start chat server either on host machine or as another ESP device running chat_server example
+- Run `make flash monitor` to build and upload the example to your board and connect to it's serial terminal
+- Wait for WiFi to connect to your access point
+- Receive and send messages to/from other clients on stdin/stdout via serial terminal.
+
+See the README.md file in the upper level 'examples' directory for more information about examples.
diff --git a/examples/protocols/asio/chat_client/asio_chat_client_test.py b/examples/protocols/asio/chat_client/asio_chat_client_test.py
new file mode 100644 (file)
index 0000000..a1cce97
--- /dev/null
@@ -0,0 +1,98 @@
+import re
+import os
+import sys
+from socket import *
+from threading import Thread
+import time
+
+# this is a test case write with tiny-test-fw.
+# to run test cases outside tiny-test-fw,
+# we need to set environment variable `TEST_FW_PATH`,
+# then get and insert `TEST_FW_PATH` to sys path before import FW module
+test_fw_path = os.getenv("TEST_FW_PATH")
+if test_fw_path and test_fw_path not in sys.path:
+    sys.path.insert(0, test_fw_path)
+
+import TinyFW
+import IDF
+
+global g_client_response;
+global g_msg_to_client;
+
+g_client_response = ""
+g_msg_to_client = "   3XYZ"
+
+def get_my_ip():
+    s1 = socket(AF_INET, SOCK_DGRAM)
+    s1.connect(("8.8.8.8", 80))
+    my_ip = s1.getsockname()[0]
+    s1.close()
+    return my_ip
+
+def chat_server_sketch(my_ip):
+    global g_client_response
+    print("Starting the server on {}".format(my_ip))
+    port=2222
+    s=socket(AF_INET, SOCK_STREAM)
+    s.bind((my_ip, port))
+    s.listen(1)
+    q,addr=s.accept()
+    print("connection accepted")
+    q.send(g_msg_to_client)
+    data = q.recv(1024)
+    # check if received initial empty message
+    if (len(data)>4):
+        g_client_response = data
+    else:
+        g_client_response = q.recv(1024)
+        print("received from client {}".format(g_client_response))
+    s.close()
+    print("server closed")
+
+@IDF.idf_example_test(env_tag="Example_WIFI")
+def test_examples_protocol_asio_chat_client(env, extra_data):
+    """
+    steps: |
+      1. Test to start simple tcp server
+      2. `dut1` joins AP
+      3. Test injects server IP to `dut1`via stdin
+      4. Test evaluates `dut1` receives a message server placed
+      5. Test injects a message to `dut1` to be sent as chat_client message
+      6. Test evaluates received test message in host server
+    """
+    global g_client_response
+    global g_msg_to_client
+    test_msg="ABC"
+    dut1 = env.get_dut("chat_client", "examples/protocols/asio/chat_client")
+    # check and log bin size
+    binary_file = os.path.join(dut1.app.binary_path, "asio_chatclient.bin")
+    bin_size = os.path.getsize(binary_file)
+    IDF.log_performance("asio_chatclient_size", "{}KB".format(bin_size//1024))
+    IDF.check_performance("asio_chatclient_size", bin_size//1024)
+    # 1. start a tcp server on the host
+    host_ip = get_my_ip()
+    thread1 = Thread(target = chat_server_sketch, args = (host_ip,))
+    thread1.start()
+    # 2. start the dut test and wait till client gets IP address
+    dut1.start_app()
+    data = dut1.expect(re.compile(r" sta ip: ([^,]+),"))
+    # 3. send host's IP to the client i.e. the `dut1`
+    dut1.write(host_ip)
+    # 4. client `dut1` should receive a message
+    dut1.expect(g_msg_to_client[4:]) # Strip out the front 4 bytes of message len (see chat_message protocol)
+    # 5. write test message from `dut1` chat_client to the server
+    dut1.write(test_msg)
+    while g_client_response == "":
+        time.sleep(1)
+    print(g_client_response)
+    # 6. evaluate host_server received this message
+    if (g_client_response[4:] == test_msg):
+        print("PASS: Received correct message")
+        pass
+    else:
+        print("Failure!")
+        raise ValueError('Wrong data received from asi tcp server: {} (expected:{})'.format(g_client_response, test_msg))
+    thread1.join()
+
+if __name__ == '__main__':
+    test_examples_protocol_asio_chat_client()
diff --git a/examples/protocols/asio/chat_client/components/component.mk b/examples/protocols/asio/chat_client/components/component.mk
new file mode 100644 (file)
index 0000000..b23b0cb
--- /dev/null
@@ -0,0 +1,10 @@
+#
+# Component Makefile
+#
+# This Makefile should, at the very least, just include $(SDK_PATH)/Makefile. By default, 
+# this will take the sources in the src/ directory, compile them and link them into 
+# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable,
+# please read the SDK documents if you need to do this.
+#
+
+COMPONENT_SRCDIRS = .
diff --git a/examples/protocols/asio/chat_client/components/wifi_asio.cpp b/examples/protocols/asio/chat_client/components/wifi_asio.cpp
new file mode 100644 (file)
index 0000000..b90ce26
--- /dev/null
@@ -0,0 +1,136 @@
+/* Common WiFi Init as STA for ASIO examples
+
+   This example code is in the Public Domain (or CC0 licensed, at your option.)
+
+   Unless required by applicable law or agreed to in writing, this
+   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+   CONDITIONS OF ANY KIND, either express or implied.
+*/
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "freertos/event_groups.h"
+#include "esp_system.h"
+#include "esp_wifi.h"
+#include "esp_event_loop.h"
+#include "esp_log.h"
+#include "nvs_flash.h"
+#include "driver/uart.h"
+#include "esp_console.h"
+#include "esp_vfs_dev.h"
+
+#include "lwip/err.h"
+#include "lwip/sys.h"
+
+#include <cstring>
+
+/* The examples use simple WiFi configuration that you can set via
+   'make menuconfig'.
+
+   If you'd rather not, just change the below entries to strings with
+   the config you want - ie #define EXAMPLE_WIFI_SSID "mywifissid"
+*/
+#define EXAMPLE_ESP_WIFI_SSID      CONFIG_ESP_WIFI_SSID
+#define EXAMPLE_ESP_WIFI_PASS      CONFIG_ESP_WIFI_PASSWORD
+
+/* FreeRTOS event group to signal when we are connected*/
+static EventGroupHandle_t wifi_event_group;
+
+/* The event group allows multiple bits for each event,
+   but we only care about one event - are we connected
+   to the AP with an IP? */
+const int WIFI_CONNECTED_BIT = BIT0;
+
+static const char *TAG = "asio example wifi init";
+
+/**
+ *  Definition of ASIO main method, which is called after network initialized
+ */
+void asio_main();
+
+static esp_err_t event_handler(void *ctx, system_event_t *event)
+{
+    switch(event->event_id) {
+    case SYSTEM_EVENT_STA_START:
+        esp_wifi_connect();
+        break;
+    case SYSTEM_EVENT_STA_GOT_IP:
+        ESP_LOGI(TAG, "got ip:%s",
+                 ip4addr_ntoa(&event->event_info.got_ip.ip_info.ip));
+        xEventGroupSetBits(wifi_event_group, WIFI_CONNECTED_BIT);
+        break;
+    case SYSTEM_EVENT_AP_STACONNECTED:
+        ESP_LOGI(TAG, "station:" MACSTR " join, AID=%d",
+                 MAC2STR(event->event_info.sta_connected.mac),
+                 event->event_info.sta_connected.aid);
+        break;
+    case SYSTEM_EVENT_AP_STADISCONNECTED:
+        ESP_LOGI(TAG, "station:" MACSTR "leave, AID=%d",
+                 MAC2STR(event->event_info.sta_disconnected.mac),
+                 event->event_info.sta_disconnected.aid);
+        break;
+    case SYSTEM_EVENT_STA_DISCONNECTED:
+        esp_wifi_connect();
+        xEventGroupClearBits(wifi_event_group, WIFI_CONNECTED_BIT);
+        break;
+    default:
+        break;
+    }
+    return ESP_OK;
+}
+
+void wifi_init_sta()
+{
+    wifi_event_group = xEventGroupCreate();
+
+    tcpip_adapter_init();
+    ESP_ERROR_CHECK(esp_event_loop_init(event_handler, NULL) );
+
+    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
+    ESP_ERROR_CHECK(esp_wifi_init(&cfg));
+    wifi_config_t wifi_config;
+    // zero out the config struct to ensure defaults are setup
+    memset(&wifi_config, 0, sizeof(wifi_sta_config_t));
+    // only copy ssid&password from example config
+    strcpy((char*)(wifi_config.sta.ssid), EXAMPLE_ESP_WIFI_SSID);
+    strcpy((char*)(wifi_config.sta.password), EXAMPLE_ESP_WIFI_PASS);
+
+    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) );
+    ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) );
+    ESP_ERROR_CHECK(esp_wifi_start() );
+
+    ESP_LOGI(TAG, "wifi_init_sta finished.");
+    ESP_LOGI(TAG, "connect to ap SSID:%s password:%s",
+             EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS);
+}
+
+extern "C" void app_main()
+{
+    //Initialize NVS
+    esp_err_t ret = nvs_flash_init();
+    if (ret == ESP_ERR_NVS_NO_FREE_PAGES) {
+      ESP_ERROR_CHECK(nvs_flash_erase());
+      ret = nvs_flash_init();
+    }
+    ESP_ERROR_CHECK(ret);
+    
+    ESP_LOGI(TAG, "ESP_WIFI_MODE_AP");
+    wifi_init_sta();
+
+    // Initialize VFS & UART so we can use std::cout/cin
+    setvbuf(stdin, NULL, _IONBF, 0);
+    setvbuf(stdout, NULL, _IONBF, 0);
+    /* Install UART driver for interrupt-driven reads and writes */
+    ESP_ERROR_CHECK( uart_driver_install( (uart_port_t)CONFIG_CONSOLE_UART_NUM,
+            256, 0, 0, NULL, 0) );
+    /* Tell VFS to use UART driver */
+    esp_vfs_dev_uart_use_driver(CONFIG_CONSOLE_UART_NUM);
+    esp_vfs_dev_uart_set_rx_line_endings(ESP_LINE_ENDINGS_CR);
+    /* Move the caret to the beginning of the next line on '\n' */
+    esp_vfs_dev_uart_set_tx_line_endings(ESP_LINE_ENDINGS_CRLF);
+
+    // wait till we receive IP, so asio realated code can be started
+    xEventGroupWaitBits(wifi_event_group, WIFI_CONNECTED_BIT, 1, 1, portMAX_DELAY);
+
+    // network is ready, let's proceed with ASIO example
+    asio_main();
+}
diff --git a/examples/protocols/asio/chat_client/main/Kconfig.projbuild b/examples/protocols/asio/chat_client/main/Kconfig.projbuild
new file mode 100644 (file)
index 0000000..32e5f3f
--- /dev/null
@@ -0,0 +1,27 @@
+menu "Example Configuration"
+
+config ESP_WIFI_SSID
+    string "WiFi SSID"
+    default "myssid"
+    help
+       SSID (network name) for the example to connect to.
+
+config ESP_WIFI_PASSWORD
+    string "WiFi Password"
+    default "mypassword"
+    help
+       WiFi password (WPA or WPA2) for the example to use.
+
+config EXAMPLE_PORT
+    string "asio example port number"
+    default "2222"
+    help
+    Port number used by ASIO example
+
+config EXAMPLE_SERVER_IP
+    string "asio example server ip for this client to connect to (leave defalut=FROM_STDIN to enter the server address via serial terminal)"
+    default "FROM_STDIN"
+    help
+    Please set the host name or ip address of corespondant server running
+
+endmenu
diff --git a/examples/protocols/asio/chat_client/main/chat_client.cpp b/examples/protocols/asio/chat_client/main/chat_client.cpp
new file mode 100644 (file)
index 0000000..e18d8d4
--- /dev/null
@@ -0,0 +1,165 @@
+//
+// chat_client.cpp
+// ~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#include <cstdlib>
+#include <deque>
+#include <iostream>
+#include <thread>
+#include "asio.hpp"
+#include "chat_message.hpp"
+
+using asio::ip::tcp;
+
+typedef std::deque<chat_message> chat_message_queue;
+
+class chat_client
+{
+public:
+  chat_client(asio::io_context& io_context,
+      const tcp::resolver::results_type& endpoints)
+    : io_context_(io_context),
+      socket_(io_context)
+  {
+    do_connect(endpoints);
+  }
+
+  void write(const chat_message& msg)
+  {
+    asio::post(io_context_,
+        [this, msg]()
+        {
+          bool write_in_progress = !write_msgs_.empty();
+          write_msgs_.push_back(msg);
+          if (!write_in_progress)
+          {
+            do_write();
+          }
+        });
+  }
+
+  void close()
+  {
+    asio::post(io_context_, [this]() { socket_.close(); });
+  }
+
+private:
+  void do_connect(const tcp::resolver::results_type& endpoints)
+  {
+    asio::async_connect(socket_, endpoints,
+        [this](std::error_code ec, tcp::endpoint)
+        {
+          if (!ec)
+          {
+            do_read_header();
+          }
+        });
+  }
+
+  void do_read_header()
+  {
+    asio::async_read(socket_,
+        asio::buffer(read_msg_.data(), chat_message::header_length),
+        [this](std::error_code ec, std::size_t /*length*/)
+        {
+          if (!ec && read_msg_.decode_header())
+          {
+            do_read_body();
+          }
+          else
+          {
+            socket_.close();
+          }
+        });
+  }
+
+  void do_read_body()
+  {
+    asio::async_read(socket_,
+        asio::buffer(read_msg_.body(), read_msg_.body_length()),
+        [this](std::error_code ec, std::size_t /*length*/)
+        {
+          if (!ec)
+          {
+            std::cout.write(read_msg_.body(), read_msg_.body_length());
+            std::cout << "\n";
+            do_read_header();
+          }
+          else
+          {
+            socket_.close();
+          }
+        });
+  }
+
+  void do_write()
+  {
+    asio::async_write(socket_,
+        asio::buffer(write_msgs_.front().data(),
+          write_msgs_.front().length()),
+        [this](std::error_code ec, std::size_t /*length*/)
+        {
+          if (!ec)
+          {
+            write_msgs_.pop_front();
+            if (!write_msgs_.empty())
+            {
+              do_write();
+            }
+          }
+          else
+          {
+            socket_.close();
+          }
+        });
+  }
+
+private:
+  asio::io_context& io_context_;
+  tcp::socket socket_;
+  chat_message read_msg_;
+  chat_message_queue write_msgs_;
+};
+
+void read_line(char * line, int max_chars);
+
+
+void asio_main()
+{
+    std::string name(CONFIG_EXAMPLE_SERVER_IP);
+    std::string port(CONFIG_EXAMPLE_PORT);
+    char line[chat_message::max_body_length + 1] = { 0 };
+
+    if (name == "FROM_STDIN") {
+      std::cout << "Please enter ip address of chat server" << std::endl;
+      if (std::cin.getline(line, chat_message::max_body_length + 1)) {
+        name = line;
+        std::cout << "Chat server IP:" << name << std::endl;
+      }
+    }
+
+    asio::io_context io_context;
+    tcp::resolver resolver(io_context);
+    auto endpoints = resolver.resolve(name, port);
+
+    chat_client c(io_context, endpoints);
+
+    std::thread t([&io_context](){ io_context.run(); });
+
+    while (std::cin.getline(line, chat_message::max_body_length + 1) && std::string(line) != "exit\n") {
+      chat_message msg;
+      msg.body_length(std::strlen(line));
+      std::memcpy(msg.body(), line, msg.body_length());
+      msg.encode_header();
+      c.write(msg);
+    }
+
+    c.close();
+    t.join();
+}
diff --git a/examples/protocols/asio/chat_client/main/chat_message.hpp b/examples/protocols/asio/chat_client/main/chat_message.hpp
new file mode 100644 (file)
index 0000000..629105b
--- /dev/null
@@ -0,0 +1,91 @@
+//
+// chat_message.hpp
+// ~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef CHAT_MESSAGE_HPP
+#define CHAT_MESSAGE_HPP
+
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+
+class chat_message
+{
+public:
+  enum { header_length = 4 };
+  enum { max_body_length = 512 };
+
+  chat_message()
+    : body_length_(0)
+  {
+  }
+
+  const char* data() const
+  {
+    return data_;
+  }
+
+  char* data()
+  {
+    return data_;
+  }
+
+  std::size_t length() const
+  {
+    return header_length + body_length_;
+  }
+
+  const char* body() const
+  {
+    return data_ + header_length;
+  }
+
+  char* body()
+  {
+    return data_ + header_length;
+  }
+
+  std::size_t body_length() const
+  {
+    return body_length_;
+  }
+
+  void body_length(std::size_t new_length)
+  {
+    body_length_ = new_length;
+    if (body_length_ > max_body_length)
+      body_length_ = max_body_length;
+  }
+
+  bool decode_header()
+  {
+    char header[header_length + 1] = "";
+    std::strncat(header, data_, header_length);
+    body_length_ = std::atoi(header);
+    if (body_length_ > max_body_length)
+    {
+      body_length_ = 0;
+      return false;
+    }
+    return true;
+  }
+
+  void encode_header()
+  {
+    char header[header_length + 1] = "";
+    std::sprintf(header, "%4d", static_cast<int>(body_length_));
+    std::memcpy(data_, header, header_length);
+  }
+
+private:
+  char data_[header_length + max_body_length];
+  std::size_t body_length_;
+};
+
+#endif // CHAT_MESSAGE_HPP
diff --git a/examples/protocols/asio/chat_client/main/component.mk b/examples/protocols/asio/chat_client/main/component.mk
new file mode 100644 (file)
index 0000000..61f8990
--- /dev/null
@@ -0,0 +1,8 @@
+#
+# Main component makefile.
+#
+# This Makefile can be left empty. By default, it will take the sources in the 
+# src/ directory, compile them and link them into lib(subdirectory_name).a 
+# in the build directory. This behaviour is entirely configurable,
+# please read the ESP-IDF documents if you need to do this.
+#
diff --git a/examples/protocols/asio/chat_client/sdkconfig.defaults b/examples/protocols/asio/chat_client/sdkconfig.defaults
new file mode 100644 (file)
index 0000000..d99552e
--- /dev/null
@@ -0,0 +1 @@
+CONFIG_MAIN_TASK_STACK_SIZE=8192
diff --git a/examples/protocols/asio/chat_server/Makefile b/examples/protocols/asio/chat_server/Makefile
new file mode 100644 (file)
index 0000000..c69f109
--- /dev/null
@@ -0,0 +1,7 @@
+#
+# This is a project Makefile. It is assumed the directory this Makefile resides in is a
+# project subdirectory.
+#
+PROJECT_NAME := asio_chatserver
+
+include $(IDF_PATH)/make/project.mk
diff --git a/examples/protocols/asio/chat_server/README.md b/examples/protocols/asio/chat_server/README.md
new file mode 100644 (file)
index 0000000..591efd9
--- /dev/null
@@ -0,0 +1,22 @@
+# ASIO chat server example
+
+Simple asio chat server using WiFi STA
+
+## Example workflow
+
+- WiFi STA is started and trying to connect to the access point defined through `make menuconfig`
+- Once connected and acquired IP address, ASIO chat server is started on port number defined through `make menuconfig`
+- Chat server echoes a message (received from any client) to all connected clients
+
+## Running the example
+
+- Run `make menuconfig` to configure the access point's SSID and Password and port number
+- Run `make flash monitor` to build and upload the example to your board and connect to it's serial terminal
+- Wait for WiFi to connect to your access point (note the IP address)
+- Connect to the server using multiple clients, for example using any option below
+    - build and run asi chat client on your host machine
+    - run chat_client asio example on ESP platform
+    - since chat message consist of ascii size and message, it is possible to 
+        netcat `nc IP PORT` and type for example `   4ABC<CR>` to transmit 'ABC\n'
+
+See the README.md file in the upper level 'examples' directory for more information about examples.
diff --git a/examples/protocols/asio/chat_server/asio_chat_server_test.py b/examples/protocols/asio/chat_server/asio_chat_server_test.py
new file mode 100644 (file)
index 0000000..55746f1
--- /dev/null
@@ -0,0 +1,56 @@
+import re
+import os
+import sys
+from socket import *
+
+
+# this is a test case write with tiny-test-fw.
+# to run test cases outside tiny-test-fw,
+# we need to set environment variable `TEST_FW_PATH`,
+# then get and insert `TEST_FW_PATH` to sys path before import FW module
+test_fw_path = os.getenv("TEST_FW_PATH")
+if test_fw_path and test_fw_path not in sys.path:
+    sys.path.insert(0, test_fw_path)
+
+import TinyFW
+import IDF
+
+
+
+
+@IDF.idf_example_test(env_tag="Example_WIFI")
+def test_examples_protocol_asio_chat_server(env, extra_data):
+    """
+    steps: |
+      1. join AP
+      2. Start server
+      3. Test connects to server and sends a test message
+      4. Test evaluates received test message from server
+    """
+    test_msg="   4ABC\n"
+    dut1 = env.get_dut("chat_server", "examples/protocols/asio/chat_server")
+    # check and log bin size
+    binary_file = os.path.join(dut1.app.binary_path, "asio_chatserver.bin")
+    bin_size = os.path.getsize(binary_file)
+    IDF.log_performance("asio_chatserver_bin_size", "{}KB".format(bin_size//1024))
+    IDF.check_performance("asio_chatserver_size", bin_size//1024)
+    # 1. start test
+    dut1.start_app()
+    # 2. get the server IP address
+    data = dut1.expect(re.compile(r" sta ip: ([^,]+),"))
+    # 3. create tcp client and connect to server
+    cli = socket(AF_INET,SOCK_STREAM)
+    cli.connect((data[0],80))
+    cli.send(test_msg)
+    data = cli.recv(1024)
+    # 4. check the message received back from the server
+    if (data == test_msg):
+        print("PASS: Received correct message {}".format(data))
+        pass
+    else:
+        print("Failure!")
+        raise ValueError('Wrong data received from asi tcp server: {} (expoected:{})'.format(data, test_msg))
+
+
+if __name__ == '__main__':
+    test_examples_protocol_asio_chat_server()
diff --git a/examples/protocols/asio/chat_server/components/component.mk b/examples/protocols/asio/chat_server/components/component.mk
new file mode 100644 (file)
index 0000000..b23b0cb
--- /dev/null
@@ -0,0 +1,10 @@
+#
+# Component Makefile
+#
+# This Makefile should, at the very least, just include $(SDK_PATH)/Makefile. By default, 
+# this will take the sources in the src/ directory, compile them and link them into 
+# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable,
+# please read the SDK documents if you need to do this.
+#
+
+COMPONENT_SRCDIRS = .
diff --git a/examples/protocols/asio/chat_server/components/wifi_asio.cpp b/examples/protocols/asio/chat_server/components/wifi_asio.cpp
new file mode 100644 (file)
index 0000000..b90ce26
--- /dev/null
@@ -0,0 +1,136 @@
+/* Common WiFi Init as STA for ASIO examples
+
+   This example code is in the Public Domain (or CC0 licensed, at your option.)
+
+   Unless required by applicable law or agreed to in writing, this
+   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+   CONDITIONS OF ANY KIND, either express or implied.
+*/
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "freertos/event_groups.h"
+#include "esp_system.h"
+#include "esp_wifi.h"
+#include "esp_event_loop.h"
+#include "esp_log.h"
+#include "nvs_flash.h"
+#include "driver/uart.h"
+#include "esp_console.h"
+#include "esp_vfs_dev.h"
+
+#include "lwip/err.h"
+#include "lwip/sys.h"
+
+#include <cstring>
+
+/* The examples use simple WiFi configuration that you can set via
+   'make menuconfig'.
+
+   If you'd rather not, just change the below entries to strings with
+   the config you want - ie #define EXAMPLE_WIFI_SSID "mywifissid"
+*/
+#define EXAMPLE_ESP_WIFI_SSID      CONFIG_ESP_WIFI_SSID
+#define EXAMPLE_ESP_WIFI_PASS      CONFIG_ESP_WIFI_PASSWORD
+
+/* FreeRTOS event group to signal when we are connected*/
+static EventGroupHandle_t wifi_event_group;
+
+/* The event group allows multiple bits for each event,
+   but we only care about one event - are we connected
+   to the AP with an IP? */
+const int WIFI_CONNECTED_BIT = BIT0;
+
+static const char *TAG = "asio example wifi init";
+
+/**
+ *  Definition of ASIO main method, which is called after network initialized
+ */
+void asio_main();
+
+static esp_err_t event_handler(void *ctx, system_event_t *event)
+{
+    switch(event->event_id) {
+    case SYSTEM_EVENT_STA_START:
+        esp_wifi_connect();
+        break;
+    case SYSTEM_EVENT_STA_GOT_IP:
+        ESP_LOGI(TAG, "got ip:%s",
+                 ip4addr_ntoa(&event->event_info.got_ip.ip_info.ip));
+        xEventGroupSetBits(wifi_event_group, WIFI_CONNECTED_BIT);
+        break;
+    case SYSTEM_EVENT_AP_STACONNECTED:
+        ESP_LOGI(TAG, "station:" MACSTR " join, AID=%d",
+                 MAC2STR(event->event_info.sta_connected.mac),
+                 event->event_info.sta_connected.aid);
+        break;
+    case SYSTEM_EVENT_AP_STADISCONNECTED:
+        ESP_LOGI(TAG, "station:" MACSTR "leave, AID=%d",
+                 MAC2STR(event->event_info.sta_disconnected.mac),
+                 event->event_info.sta_disconnected.aid);
+        break;
+    case SYSTEM_EVENT_STA_DISCONNECTED:
+        esp_wifi_connect();
+        xEventGroupClearBits(wifi_event_group, WIFI_CONNECTED_BIT);
+        break;
+    default:
+        break;
+    }
+    return ESP_OK;
+}
+
+void wifi_init_sta()
+{
+    wifi_event_group = xEventGroupCreate();
+
+    tcpip_adapter_init();
+    ESP_ERROR_CHECK(esp_event_loop_init(event_handler, NULL) );
+
+    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
+    ESP_ERROR_CHECK(esp_wifi_init(&cfg));
+    wifi_config_t wifi_config;
+    // zero out the config struct to ensure defaults are setup
+    memset(&wifi_config, 0, sizeof(wifi_sta_config_t));
+    // only copy ssid&password from example config
+    strcpy((char*)(wifi_config.sta.ssid), EXAMPLE_ESP_WIFI_SSID);
+    strcpy((char*)(wifi_config.sta.password), EXAMPLE_ESP_WIFI_PASS);
+
+    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) );
+    ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) );
+    ESP_ERROR_CHECK(esp_wifi_start() );
+
+    ESP_LOGI(TAG, "wifi_init_sta finished.");
+    ESP_LOGI(TAG, "connect to ap SSID:%s password:%s",
+             EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS);
+}
+
+extern "C" void app_main()
+{
+    //Initialize NVS
+    esp_err_t ret = nvs_flash_init();
+    if (ret == ESP_ERR_NVS_NO_FREE_PAGES) {
+      ESP_ERROR_CHECK(nvs_flash_erase());
+      ret = nvs_flash_init();
+    }
+    ESP_ERROR_CHECK(ret);
+    
+    ESP_LOGI(TAG, "ESP_WIFI_MODE_AP");
+    wifi_init_sta();
+
+    // Initialize VFS & UART so we can use std::cout/cin
+    setvbuf(stdin, NULL, _IONBF, 0);
+    setvbuf(stdout, NULL, _IONBF, 0);
+    /* Install UART driver for interrupt-driven reads and writes */
+    ESP_ERROR_CHECK( uart_driver_install( (uart_port_t)CONFIG_CONSOLE_UART_NUM,
+            256, 0, 0, NULL, 0) );
+    /* Tell VFS to use UART driver */
+    esp_vfs_dev_uart_use_driver(CONFIG_CONSOLE_UART_NUM);
+    esp_vfs_dev_uart_set_rx_line_endings(ESP_LINE_ENDINGS_CR);
+    /* Move the caret to the beginning of the next line on '\n' */
+    esp_vfs_dev_uart_set_tx_line_endings(ESP_LINE_ENDINGS_CRLF);
+
+    // wait till we receive IP, so asio realated code can be started
+    xEventGroupWaitBits(wifi_event_group, WIFI_CONNECTED_BIT, 1, 1, portMAX_DELAY);
+
+    // network is ready, let's proceed with ASIO example
+    asio_main();
+}
diff --git a/examples/protocols/asio/chat_server/main/Kconfig.projbuild b/examples/protocols/asio/chat_server/main/Kconfig.projbuild
new file mode 100644 (file)
index 0000000..c0d6fd9
--- /dev/null
@@ -0,0 +1,21 @@
+menu "Example Configuration"
+
+config ESP_WIFI_SSID
+    string "WiFi SSID"
+    default "myssid"
+    help
+       SSID (network name) for the example to connect to.
+
+config ESP_WIFI_PASSWORD
+    string "WiFi Password"
+    default "mypassword"
+    help
+       WiFi password (WPA or WPA2) for the example to use.
+
+config EXAMPLE_PORT
+    string "asio example port number"
+    default "80"
+    help
+    Port number used by ASIO example
+
+endmenu
diff --git a/examples/protocols/asio/chat_server/main/chat_message.hpp b/examples/protocols/asio/chat_server/main/chat_message.hpp
new file mode 100644 (file)
index 0000000..629105b
--- /dev/null
@@ -0,0 +1,91 @@
+//
+// chat_message.hpp
+// ~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef CHAT_MESSAGE_HPP
+#define CHAT_MESSAGE_HPP
+
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+
+class chat_message
+{
+public:
+  enum { header_length = 4 };
+  enum { max_body_length = 512 };
+
+  chat_message()
+    : body_length_(0)
+  {
+  }
+
+  const char* data() const
+  {
+    return data_;
+  }
+
+  char* data()
+  {
+    return data_;
+  }
+
+  std::size_t length() const
+  {
+    return header_length + body_length_;
+  }
+
+  const char* body() const
+  {
+    return data_ + header_length;
+  }
+
+  char* body()
+  {
+    return data_ + header_length;
+  }
+
+  std::size_t body_length() const
+  {
+    return body_length_;
+  }
+
+  void body_length(std::size_t new_length)
+  {
+    body_length_ = new_length;
+    if (body_length_ > max_body_length)
+      body_length_ = max_body_length;
+  }
+
+  bool decode_header()
+  {
+    char header[header_length + 1] = "";
+    std::strncat(header, data_, header_length);
+    body_length_ = std::atoi(header);
+    if (body_length_ > max_body_length)
+    {
+      body_length_ = 0;
+      return false;
+    }
+    return true;
+  }
+
+  void encode_header()
+  {
+    char header[header_length + 1] = "";
+    std::sprintf(header, "%4d", static_cast<int>(body_length_));
+    std::memcpy(data_, header, header_length);
+  }
+
+private:
+  char data_[header_length + max_body_length];
+  std::size_t body_length_;
+};
+
+#endif // CHAT_MESSAGE_HPP
diff --git a/examples/protocols/asio/chat_server/main/chat_server.cpp b/examples/protocols/asio/chat_server/main/chat_server.cpp
new file mode 100644 (file)
index 0000000..05740f4
--- /dev/null
@@ -0,0 +1,214 @@
+//
+// chat_server.cpp
+// ~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#include <cstdlib>
+#include <deque>
+#include <iostream>
+#include <list>
+#include <memory>
+#include <set>
+#include <utility>
+#include "asio.hpp"
+#include "chat_message.hpp"
+
+using asio::ip::tcp;
+
+//----------------------------------------------------------------------
+
+typedef std::deque<chat_message> chat_message_queue;
+
+//----------------------------------------------------------------------
+
+class chat_participant
+{
+public:
+  virtual ~chat_participant() {}
+  virtual void deliver(const chat_message& msg) = 0;
+};
+
+typedef std::shared_ptr<chat_participant> chat_participant_ptr;
+
+//----------------------------------------------------------------------
+
+class chat_room
+{
+public:
+  void join(chat_participant_ptr participant)
+  {
+    participants_.insert(participant);
+    for (auto msg: recent_msgs_)
+      participant->deliver(msg);
+  }
+
+  void leave(chat_participant_ptr participant)
+  {
+    participants_.erase(participant);
+  }
+
+  void deliver(const chat_message& msg)
+  {
+    recent_msgs_.push_back(msg);
+    while (recent_msgs_.size() > max_recent_msgs)
+      recent_msgs_.pop_front();
+
+    for (auto participant: participants_)
+      participant->deliver(msg);
+  }
+
+private:
+  std::set<chat_participant_ptr> participants_;
+  enum { max_recent_msgs = 100 };
+  chat_message_queue recent_msgs_;
+};
+
+//----------------------------------------------------------------------
+
+class chat_session
+  : public chat_participant,
+    public std::enable_shared_from_this<chat_session>
+{
+public:
+  chat_session(tcp::socket socket, chat_room& room)
+    : socket_(std::move(socket)),
+      room_(room)
+  {
+  }
+
+  void start()
+  {
+    room_.join(shared_from_this());
+    do_read_header();
+  }
+
+  void deliver(const chat_message& msg)
+  {
+    bool write_in_progress = !write_msgs_.empty();
+    write_msgs_.push_back(msg);
+    if (!write_in_progress)
+    {
+      do_write();
+    }
+  }
+
+private:
+  void do_read_header()
+  {
+    auto self(shared_from_this());
+    asio::async_read(socket_,
+        asio::buffer(read_msg_.data(), chat_message::header_length),
+        [this, self](std::error_code ec, std::size_t /*length*/)
+        {
+          if (!ec && read_msg_.decode_header())
+          {
+            do_read_body();
+          }
+          else
+          {
+            room_.leave(shared_from_this());
+          }
+        });
+  }
+
+  void do_read_body()
+  {
+    auto self(shared_from_this());
+    asio::async_read(socket_,
+        asio::buffer(read_msg_.body(), read_msg_.body_length()),
+        [this, self](std::error_code ec, std::size_t /*length*/)
+        {
+          if (!ec)
+          {
+            room_.deliver(read_msg_);
+            do_read_header();
+          }
+          else
+          {
+            room_.leave(shared_from_this());
+          }
+        });
+  }
+
+  void do_write()
+  {
+    auto self(shared_from_this());
+    asio::async_write(socket_,
+        asio::buffer(write_msgs_.front().data(),
+          write_msgs_.front().length()),
+        [this, self](std::error_code ec, std::size_t /*length*/)
+        {
+          if (!ec)
+          {
+            write_msgs_.pop_front();
+            if (!write_msgs_.empty())
+            {
+              do_write();
+            }
+          }
+          else
+          {
+            room_.leave(shared_from_this());
+          }
+        });
+  }
+
+  tcp::socket socket_;
+  chat_room& room_;
+  chat_message read_msg_;
+  chat_message_queue write_msgs_;
+};
+
+//----------------------------------------------------------------------
+
+class chat_server
+{
+public:
+  chat_server(asio::io_context& io_context,
+      const tcp::endpoint& endpoint)
+    : acceptor_(io_context, endpoint)
+  {
+    do_accept();
+  }
+
+private:
+  void do_accept()
+  {
+    acceptor_.async_accept(
+        [this](std::error_code ec, tcp::socket socket)
+        {
+          if (!ec)
+          {
+            std::make_shared<chat_session>(std::move(socket), room_)->start();
+          }
+
+          do_accept();
+        });
+  }
+
+  tcp::acceptor acceptor_;
+  chat_room room_;
+};
+
+//----------------------------------------------------------------------
+
+int asio_main()
+{
+    asio::io_context io_context;
+
+    std::list<chat_server> servers;
+
+    {
+      tcp::endpoint endpoint(tcp::v4(), std::atoi(CONFIG_EXAMPLE_PORT));
+      servers.emplace_back(io_context, endpoint);
+    }
+
+    io_context.run();
+
+  return 0;
+}
diff --git a/examples/protocols/asio/chat_server/main/component.mk b/examples/protocols/asio/chat_server/main/component.mk
new file mode 100644 (file)
index 0000000..61f8990
--- /dev/null
@@ -0,0 +1,8 @@
+#
+# Main component makefile.
+#
+# This Makefile can be left empty. By default, it will take the sources in the 
+# src/ directory, compile them and link them into lib(subdirectory_name).a 
+# in the build directory. This behaviour is entirely configurable,
+# please read the ESP-IDF documents if you need to do this.
+#
diff --git a/examples/protocols/asio/chat_server/sdkconfig.defaults b/examples/protocols/asio/chat_server/sdkconfig.defaults
new file mode 100644 (file)
index 0000000..d99552e
--- /dev/null
@@ -0,0 +1 @@
+CONFIG_MAIN_TASK_STACK_SIZE=8192
diff --git a/examples/protocols/asio/tcp_echo_server/Makefile b/examples/protocols/asio/tcp_echo_server/Makefile
new file mode 100644 (file)
index 0000000..2a2839a
--- /dev/null
@@ -0,0 +1,7 @@
+#
+# This is a project Makefile. It is assumed the directory this Makefile resides in is a
+# project subdirectory.
+#
+PROJECT_NAME := asio_tcp_echoserver
+
+include $(IDF_PATH)/make/project.mk
diff --git a/examples/protocols/asio/tcp_echo_server/README.md b/examples/protocols/asio/tcp_echo_server/README.md
new file mode 100644 (file)
index 0000000..a32fd93
--- /dev/null
@@ -0,0 +1,18 @@
+# ASIO tcp echo server example
+
+Simple asio echo server using WiFi STA
+
+## Example workflow
+
+- WiFi STA is started and trying to connect to the access point defined through `make menuconfig`
+- Once connected and acquired IP address, ASIO tcp server is started on port number defined through `make menuconfig`
+- Server receives and echoes back messages transmitted from client
+
+## Running the example
+
+- Run `make menuconfig` to configure the access point's SSID and Password and port number
+- Run `make flash monitor` to build and upload the example to your board and connect to it's serial terminal
+- Wait for WiFi to connect to your access point (note the IP address)
+- You can now send a tcp message and check it is repeated, for example using netcat `nc IP PORT`
+
+See the README.md file in the upper level 'examples' directory for more information about examples.
diff --git a/examples/protocols/asio/tcp_echo_server/asio_tcp_server_test.py b/examples/protocols/asio/tcp_echo_server/asio_tcp_server_test.py
new file mode 100644 (file)
index 0000000..1ab432e
--- /dev/null
@@ -0,0 +1,59 @@
+import re
+import os
+import sys
+from socket import *
+
+
+# this is a test case write with tiny-test-fw.
+# to run test cases outside tiny-test-fw,
+# we need to set environment variable `TEST_FW_PATH`,
+# then get and insert `TEST_FW_PATH` to sys path before import FW module
+test_fw_path = os.getenv("TEST_FW_PATH")
+if test_fw_path and test_fw_path not in sys.path:
+    sys.path.insert(0, test_fw_path)
+
+import TinyFW
+import IDF
+
+
+
+
+@IDF.idf_example_test(env_tag="Example_WIFI")
+def test_examples_protocol_asio_tcp_server(env, extra_data):
+    """
+    steps: |
+      1. join AP
+      2. Start server
+      3. Test connects to server and sends a test message
+      4. Test evaluates received test message from server
+      5. Test evaluates received test message on server stdout
+    """
+    test_msg="echo message from client to server"
+    dut1 = env.get_dut("tcp_echo_server", "examples/protocols/asio/tcp_echo_server")
+    # check and log bin size
+    binary_file = os.path.join(dut1.app.binary_path, "asio_tcp_echoserver.bin")
+    bin_size = os.path.getsize(binary_file)
+    IDF.log_performance("asio_tcp_echoserver_bin_size", "{}KB".format(bin_size//1024))
+    IDF.check_performance("asio_tcp_echoserver_size", bin_size//1024)
+    # 1. start test
+    dut1.start_app()
+    # 2. get the server IP address
+    data = dut1.expect(re.compile(r" sta ip: ([^,]+),"))
+    # 3. create tcp client and connect to server
+    cli = socket(AF_INET,SOCK_STREAM)
+    cli.connect((data[0],80))
+    cli.send(test_msg)
+    data = cli.recv(1024)
+    # 4. check the message received back from the server
+    if (data == test_msg):
+        print("PASS: Received correct message")
+        pass
+    else:
+        print("Failure!")
+        raise ValueError('Wrong data received from asi tcp server: {} (expoected:{})'.format(data, test_msg))
+    # 5. check the client message appears also on server terminal
+    dut1.expect(test_msg)
+
+
+if __name__ == '__main__':
+    test_examples_protocol_asio_tcp_server()
diff --git a/examples/protocols/asio/tcp_echo_server/components/component.mk b/examples/protocols/asio/tcp_echo_server/components/component.mk
new file mode 100644 (file)
index 0000000..b23b0cb
--- /dev/null
@@ -0,0 +1,10 @@
+#
+# Component Makefile
+#
+# This Makefile should, at the very least, just include $(SDK_PATH)/Makefile. By default, 
+# this will take the sources in the src/ directory, compile them and link them into 
+# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable,
+# please read the SDK documents if you need to do this.
+#
+
+COMPONENT_SRCDIRS = .
diff --git a/examples/protocols/asio/tcp_echo_server/components/wifi_asio.cpp b/examples/protocols/asio/tcp_echo_server/components/wifi_asio.cpp
new file mode 100644 (file)
index 0000000..b90ce26
--- /dev/null
@@ -0,0 +1,136 @@
+/* Common WiFi Init as STA for ASIO examples
+
+   This example code is in the Public Domain (or CC0 licensed, at your option.)
+
+   Unless required by applicable law or agreed to in writing, this
+   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+   CONDITIONS OF ANY KIND, either express or implied.
+*/
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "freertos/event_groups.h"
+#include "esp_system.h"
+#include "esp_wifi.h"
+#include "esp_event_loop.h"
+#include "esp_log.h"
+#include "nvs_flash.h"
+#include "driver/uart.h"
+#include "esp_console.h"
+#include "esp_vfs_dev.h"
+
+#include "lwip/err.h"
+#include "lwip/sys.h"
+
+#include <cstring>
+
+/* The examples use simple WiFi configuration that you can set via
+   'make menuconfig'.
+
+   If you'd rather not, just change the below entries to strings with
+   the config you want - ie #define EXAMPLE_WIFI_SSID "mywifissid"
+*/
+#define EXAMPLE_ESP_WIFI_SSID      CONFIG_ESP_WIFI_SSID
+#define EXAMPLE_ESP_WIFI_PASS      CONFIG_ESP_WIFI_PASSWORD
+
+/* FreeRTOS event group to signal when we are connected*/
+static EventGroupHandle_t wifi_event_group;
+
+/* The event group allows multiple bits for each event,
+   but we only care about one event - are we connected
+   to the AP with an IP? */
+const int WIFI_CONNECTED_BIT = BIT0;
+
+static const char *TAG = "asio example wifi init";
+
+/**
+ *  Definition of ASIO main method, which is called after network initialized
+ */
+void asio_main();
+
+static esp_err_t event_handler(void *ctx, system_event_t *event)
+{
+    switch(event->event_id) {
+    case SYSTEM_EVENT_STA_START:
+        esp_wifi_connect();
+        break;
+    case SYSTEM_EVENT_STA_GOT_IP:
+        ESP_LOGI(TAG, "got ip:%s",
+                 ip4addr_ntoa(&event->event_info.got_ip.ip_info.ip));
+        xEventGroupSetBits(wifi_event_group, WIFI_CONNECTED_BIT);
+        break;
+    case SYSTEM_EVENT_AP_STACONNECTED:
+        ESP_LOGI(TAG, "station:" MACSTR " join, AID=%d",
+                 MAC2STR(event->event_info.sta_connected.mac),
+                 event->event_info.sta_connected.aid);
+        break;
+    case SYSTEM_EVENT_AP_STADISCONNECTED:
+        ESP_LOGI(TAG, "station:" MACSTR "leave, AID=%d",
+                 MAC2STR(event->event_info.sta_disconnected.mac),
+                 event->event_info.sta_disconnected.aid);
+        break;
+    case SYSTEM_EVENT_STA_DISCONNECTED:
+        esp_wifi_connect();
+        xEventGroupClearBits(wifi_event_group, WIFI_CONNECTED_BIT);
+        break;
+    default:
+        break;
+    }
+    return ESP_OK;
+}
+
+void wifi_init_sta()
+{
+    wifi_event_group = xEventGroupCreate();
+
+    tcpip_adapter_init();
+    ESP_ERROR_CHECK(esp_event_loop_init(event_handler, NULL) );
+
+    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
+    ESP_ERROR_CHECK(esp_wifi_init(&cfg));
+    wifi_config_t wifi_config;
+    // zero out the config struct to ensure defaults are setup
+    memset(&wifi_config, 0, sizeof(wifi_sta_config_t));
+    // only copy ssid&password from example config
+    strcpy((char*)(wifi_config.sta.ssid), EXAMPLE_ESP_WIFI_SSID);
+    strcpy((char*)(wifi_config.sta.password), EXAMPLE_ESP_WIFI_PASS);
+
+    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) );
+    ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) );
+    ESP_ERROR_CHECK(esp_wifi_start() );
+
+    ESP_LOGI(TAG, "wifi_init_sta finished.");
+    ESP_LOGI(TAG, "connect to ap SSID:%s password:%s",
+             EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS);
+}
+
+extern "C" void app_main()
+{
+    //Initialize NVS
+    esp_err_t ret = nvs_flash_init();
+    if (ret == ESP_ERR_NVS_NO_FREE_PAGES) {
+      ESP_ERROR_CHECK(nvs_flash_erase());
+      ret = nvs_flash_init();
+    }
+    ESP_ERROR_CHECK(ret);
+    
+    ESP_LOGI(TAG, "ESP_WIFI_MODE_AP");
+    wifi_init_sta();
+
+    // Initialize VFS & UART so we can use std::cout/cin
+    setvbuf(stdin, NULL, _IONBF, 0);
+    setvbuf(stdout, NULL, _IONBF, 0);
+    /* Install UART driver for interrupt-driven reads and writes */
+    ESP_ERROR_CHECK( uart_driver_install( (uart_port_t)CONFIG_CONSOLE_UART_NUM,
+            256, 0, 0, NULL, 0) );
+    /* Tell VFS to use UART driver */
+    esp_vfs_dev_uart_use_driver(CONFIG_CONSOLE_UART_NUM);
+    esp_vfs_dev_uart_set_rx_line_endings(ESP_LINE_ENDINGS_CR);
+    /* Move the caret to the beginning of the next line on '\n' */
+    esp_vfs_dev_uart_set_tx_line_endings(ESP_LINE_ENDINGS_CRLF);
+
+    // wait till we receive IP, so asio realated code can be started
+    xEventGroupWaitBits(wifi_event_group, WIFI_CONNECTED_BIT, 1, 1, portMAX_DELAY);
+
+    // network is ready, let's proceed with ASIO example
+    asio_main();
+}
diff --git a/examples/protocols/asio/tcp_echo_server/main/Kconfig.projbuild b/examples/protocols/asio/tcp_echo_server/main/Kconfig.projbuild
new file mode 100644 (file)
index 0000000..c0d6fd9
--- /dev/null
@@ -0,0 +1,21 @@
+menu "Example Configuration"
+
+config ESP_WIFI_SSID
+    string "WiFi SSID"
+    default "myssid"
+    help
+       SSID (network name) for the example to connect to.
+
+config ESP_WIFI_PASSWORD
+    string "WiFi Password"
+    default "mypassword"
+    help
+       WiFi password (WPA or WPA2) for the example to use.
+
+config EXAMPLE_PORT
+    string "asio example port number"
+    default "80"
+    help
+    Port number used by ASIO example
+
+endmenu
diff --git a/examples/protocols/asio/tcp_echo_server/main/component.mk b/examples/protocols/asio/tcp_echo_server/main/component.mk
new file mode 100644 (file)
index 0000000..8c8d52a
--- /dev/null
@@ -0,0 +1,9 @@
+#
+# Main component makefile.
+#
+# This Makefile can be left empty. By default, it will take the sources in the 
+# src/ directory, compile them and link them into lib(subdirectory_name).a 
+# in the build directory. This behaviour is entirely configurable,
+# please read the ESP-IDF documents if you need to do this.
+#
+
diff --git a/examples/protocols/asio/tcp_echo_server/main/echo_server.cpp b/examples/protocols/asio/tcp_echo_server/main/echo_server.cpp
new file mode 100644 (file)
index 0000000..22f171f
--- /dev/null
@@ -0,0 +1,91 @@
+#include "asio.hpp"
+#include <string>
+#include <iostream>
+
+using asio::ip::tcp;
+
+class session
+  : public std::enable_shared_from_this<session>
+{
+public:
+  session(tcp::socket socket)
+    : socket_(std::move(socket))
+  {
+  }
+
+  void start()
+  {
+    do_read();
+  }
+
+private:
+  void do_read()
+  {
+    auto self(shared_from_this());
+    socket_.async_read_some(asio::buffer(data_, max_length),
+        [this, self](std::error_code ec, std::size_t length)
+        {
+          if (!ec)
+          {
+            std::cout << data_ << std::endl;
+            do_write(length);
+          }
+        });
+  }
+
+  void do_write(std::size_t length)
+  {
+    auto self(shared_from_this());
+    asio::async_write(socket_, asio::buffer(data_, length),
+        [this, self](std::error_code ec, std::size_t length)
+        {
+          if (!ec)
+          {
+            do_read();
+          }
+        });
+  }
+
+  tcp::socket socket_;
+  enum { max_length = 1024 };
+  char data_[max_length];
+};
+
+class server
+{
+public:
+  server(asio::io_context& io_context, short port)
+    : acceptor_(io_context, tcp::endpoint(tcp::v4(), port))
+  {
+    do_accept();
+  }
+
+private:
+  void do_accept()
+  {
+    acceptor_.async_accept(
+        [this](std::error_code ec, tcp::socket socket)
+        {
+          if (!ec)
+          {
+            std::make_shared<session>(std::move(socket))->start();
+          }
+
+          do_accept();
+        });
+  }
+
+  tcp::acceptor acceptor_;
+};
+
+
+void asio_main()
+{
+
+    asio::io_context io_context;
+
+    server s(io_context, std::atoi(CONFIG_EXAMPLE_PORT));
+
+    io_context.run();
+
+}
\ No newline at end of file
diff --git a/examples/protocols/asio/tcp_echo_server/sdkconfig.defaults b/examples/protocols/asio/tcp_echo_server/sdkconfig.defaults
new file mode 100644 (file)
index 0000000..d99552e
--- /dev/null
@@ -0,0 +1 @@
+CONFIG_MAIN_TASK_STACK_SIZE=8192
diff --git a/examples/protocols/asio/udp_echo_server/Makefile b/examples/protocols/asio/udp_echo_server/Makefile
new file mode 100644 (file)
index 0000000..c659aea
--- /dev/null
@@ -0,0 +1,7 @@
+#
+# This is a project Makefile. It is assumed the directory this Makefile resides in is a
+# project subdirectory.
+#
+PROJECT_NAME := asio_udp_echoserver
+
+include $(IDF_PATH)/make/project.mk
diff --git a/examples/protocols/asio/udp_echo_server/README.md b/examples/protocols/asio/udp_echo_server/README.md
new file mode 100644 (file)
index 0000000..6cf2c2b
--- /dev/null
@@ -0,0 +1,18 @@
+# ASIO udp echo server example
+
+Simple asio echo server using WiFi STA
+
+## Example workflow
+
+- WiFi STA is started and trying to connect to the access point defined through `make menuconfig`
+- Once connected and acquired IP address, ASIO udp server is started on port number defined through `make menuconfig`
+- Server receives and echoes back messages transmitted from client
+
+## Running the example
+
+- Run `make menuconfig` to configure the access point's SSID and Password and port number
+- Run `make flash monitor` to build and upload the example to your board and connect to it's serial terminal
+- Wait for WiFi to connect to your access point (note the IP address)
+- You can now send a udp message and check it is repeated, for example using netcat `nc -u IP PORT`
+
+See the README.md file in the upper level 'examples' directory for more information about examples.
diff --git a/examples/protocols/asio/udp_echo_server/asio_udp_server_test.py b/examples/protocols/asio/udp_echo_server/asio_udp_server_test.py
new file mode 100644 (file)
index 0000000..490a800
--- /dev/null
@@ -0,0 +1,58 @@
+import re
+import os
+import sys
+from socket import *
+
+
+# this is a test case write with tiny-test-fw.
+# to run test cases outside tiny-test-fw,
+# we need to set environment variable `TEST_FW_PATH`,
+# then get and insert `TEST_FW_PATH` to sys path before import FW module
+test_fw_path = os.getenv("TEST_FW_PATH")
+if test_fw_path and test_fw_path not in sys.path:
+    sys.path.insert(0, test_fw_path)
+
+import TinyFW
+import IDF
+
+
+
+
+@IDF.idf_example_test(env_tag="Example_WIFI")
+def test_examples_protocol_asio_udp_server(env, extra_data):
+    """
+    steps: |
+      1. join AP
+      2. Start server
+      3. Test connects to server and sends a test message
+      4. Test evaluates received test message from server
+      5. Test evaluates received test message on server stdout
+    """
+    test_msg="echo message from client to server"
+    dut1 = env.get_dut("udp_echo_server", "examples/protocols/asio/udp_echo_server")
+    # check and log bin size
+    binary_file = os.path.join(dut1.app.binary_path, "asio_udp_echoserver.bin")
+    bin_size = os.path.getsize(binary_file)
+    IDF.log_performance("asio_udp_echoserver_bin_size", "{}KB".format(bin_size//1024))
+    IDF.check_performance("asio_udp_echoserver_size", bin_size//1024)
+    # 1. start test
+    dut1.start_app()
+    # 2. get the server IP address
+    data = dut1.expect(re.compile(r" sta ip: ([^,]+),"))
+    # 3. create tcp client and connect to server
+    cli = socket(AF_INET, SOCK_DGRAM)
+    cli.connect((data[0], 80))
+    cli.send(test_msg)
+    data = cli.recv(1024)
+    # 4. check the message received back from the server
+    if (data == test_msg):
+        print("PASS: Received correct message")
+        pass
+    else:
+        print("Failure!")
+        raise ValueError('Wrong data received from asi udp server: {} (expoected:{})'.format(data, test_msg))
+    # 5. check the client message appears also on server terminal
+    dut1.expect(test_msg)
+
+if __name__ == '__main__':
+    test_examples_protocol_asio_udp_server()
diff --git a/examples/protocols/asio/udp_echo_server/components/component.mk b/examples/protocols/asio/udp_echo_server/components/component.mk
new file mode 100644 (file)
index 0000000..b23b0cb
--- /dev/null
@@ -0,0 +1,10 @@
+#
+# Component Makefile
+#
+# This Makefile should, at the very least, just include $(SDK_PATH)/Makefile. By default, 
+# this will take the sources in the src/ directory, compile them and link them into 
+# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable,
+# please read the SDK documents if you need to do this.
+#
+
+COMPONENT_SRCDIRS = .
diff --git a/examples/protocols/asio/udp_echo_server/components/wifi_asio.cpp b/examples/protocols/asio/udp_echo_server/components/wifi_asio.cpp
new file mode 100644 (file)
index 0000000..b90ce26
--- /dev/null
@@ -0,0 +1,136 @@
+/* Common WiFi Init as STA for ASIO examples
+
+   This example code is in the Public Domain (or CC0 licensed, at your option.)
+
+   Unless required by applicable law or agreed to in writing, this
+   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+   CONDITIONS OF ANY KIND, either express or implied.
+*/
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "freertos/event_groups.h"
+#include "esp_system.h"
+#include "esp_wifi.h"
+#include "esp_event_loop.h"
+#include "esp_log.h"
+#include "nvs_flash.h"
+#include "driver/uart.h"
+#include "esp_console.h"
+#include "esp_vfs_dev.h"
+
+#include "lwip/err.h"
+#include "lwip/sys.h"
+
+#include <cstring>
+
+/* The examples use simple WiFi configuration that you can set via
+   'make menuconfig'.
+
+   If you'd rather not, just change the below entries to strings with
+   the config you want - ie #define EXAMPLE_WIFI_SSID "mywifissid"
+*/
+#define EXAMPLE_ESP_WIFI_SSID      CONFIG_ESP_WIFI_SSID
+#define EXAMPLE_ESP_WIFI_PASS      CONFIG_ESP_WIFI_PASSWORD
+
+/* FreeRTOS event group to signal when we are connected*/
+static EventGroupHandle_t wifi_event_group;
+
+/* The event group allows multiple bits for each event,
+   but we only care about one event - are we connected
+   to the AP with an IP? */
+const int WIFI_CONNECTED_BIT = BIT0;
+
+static const char *TAG = "asio example wifi init";
+
+/**
+ *  Definition of ASIO main method, which is called after network initialized
+ */
+void asio_main();
+
+static esp_err_t event_handler(void *ctx, system_event_t *event)
+{
+    switch(event->event_id) {
+    case SYSTEM_EVENT_STA_START:
+        esp_wifi_connect();
+        break;
+    case SYSTEM_EVENT_STA_GOT_IP:
+        ESP_LOGI(TAG, "got ip:%s",
+                 ip4addr_ntoa(&event->event_info.got_ip.ip_info.ip));
+        xEventGroupSetBits(wifi_event_group, WIFI_CONNECTED_BIT);
+        break;
+    case SYSTEM_EVENT_AP_STACONNECTED:
+        ESP_LOGI(TAG, "station:" MACSTR " join, AID=%d",
+                 MAC2STR(event->event_info.sta_connected.mac),
+                 event->event_info.sta_connected.aid);
+        break;
+    case SYSTEM_EVENT_AP_STADISCONNECTED:
+        ESP_LOGI(TAG, "station:" MACSTR "leave, AID=%d",
+                 MAC2STR(event->event_info.sta_disconnected.mac),
+                 event->event_info.sta_disconnected.aid);
+        break;
+    case SYSTEM_EVENT_STA_DISCONNECTED:
+        esp_wifi_connect();
+        xEventGroupClearBits(wifi_event_group, WIFI_CONNECTED_BIT);
+        break;
+    default:
+        break;
+    }
+    return ESP_OK;
+}
+
+void wifi_init_sta()
+{
+    wifi_event_group = xEventGroupCreate();
+
+    tcpip_adapter_init();
+    ESP_ERROR_CHECK(esp_event_loop_init(event_handler, NULL) );
+
+    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
+    ESP_ERROR_CHECK(esp_wifi_init(&cfg));
+    wifi_config_t wifi_config;
+    // zero out the config struct to ensure defaults are setup
+    memset(&wifi_config, 0, sizeof(wifi_sta_config_t));
+    // only copy ssid&password from example config
+    strcpy((char*)(wifi_config.sta.ssid), EXAMPLE_ESP_WIFI_SSID);
+    strcpy((char*)(wifi_config.sta.password), EXAMPLE_ESP_WIFI_PASS);
+
+    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) );
+    ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) );
+    ESP_ERROR_CHECK(esp_wifi_start() );
+
+    ESP_LOGI(TAG, "wifi_init_sta finished.");
+    ESP_LOGI(TAG, "connect to ap SSID:%s password:%s",
+             EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS);
+}
+
+extern "C" void app_main()
+{
+    //Initialize NVS
+    esp_err_t ret = nvs_flash_init();
+    if (ret == ESP_ERR_NVS_NO_FREE_PAGES) {
+      ESP_ERROR_CHECK(nvs_flash_erase());
+      ret = nvs_flash_init();
+    }
+    ESP_ERROR_CHECK(ret);
+    
+    ESP_LOGI(TAG, "ESP_WIFI_MODE_AP");
+    wifi_init_sta();
+
+    // Initialize VFS & UART so we can use std::cout/cin
+    setvbuf(stdin, NULL, _IONBF, 0);
+    setvbuf(stdout, NULL, _IONBF, 0);
+    /* Install UART driver for interrupt-driven reads and writes */
+    ESP_ERROR_CHECK( uart_driver_install( (uart_port_t)CONFIG_CONSOLE_UART_NUM,
+            256, 0, 0, NULL, 0) );
+    /* Tell VFS to use UART driver */
+    esp_vfs_dev_uart_use_driver(CONFIG_CONSOLE_UART_NUM);
+    esp_vfs_dev_uart_set_rx_line_endings(ESP_LINE_ENDINGS_CR);
+    /* Move the caret to the beginning of the next line on '\n' */
+    esp_vfs_dev_uart_set_tx_line_endings(ESP_LINE_ENDINGS_CRLF);
+
+    // wait till we receive IP, so asio realated code can be started
+    xEventGroupWaitBits(wifi_event_group, WIFI_CONNECTED_BIT, 1, 1, portMAX_DELAY);
+
+    // network is ready, let's proceed with ASIO example
+    asio_main();
+}
diff --git a/examples/protocols/asio/udp_echo_server/main/Kconfig.projbuild b/examples/protocols/asio/udp_echo_server/main/Kconfig.projbuild
new file mode 100644 (file)
index 0000000..c0d6fd9
--- /dev/null
@@ -0,0 +1,21 @@
+menu "Example Configuration"
+
+config ESP_WIFI_SSID
+    string "WiFi SSID"
+    default "myssid"
+    help
+       SSID (network name) for the example to connect to.
+
+config ESP_WIFI_PASSWORD
+    string "WiFi Password"
+    default "mypassword"
+    help
+       WiFi password (WPA or WPA2) for the example to use.
+
+config EXAMPLE_PORT
+    string "asio example port number"
+    default "80"
+    help
+    Port number used by ASIO example
+
+endmenu
diff --git a/examples/protocols/asio/udp_echo_server/main/component.mk b/examples/protocols/asio/udp_echo_server/main/component.mk
new file mode 100644 (file)
index 0000000..61f8990
--- /dev/null
@@ -0,0 +1,8 @@
+#
+# Main component makefile.
+#
+# This Makefile can be left empty. By default, it will take the sources in the 
+# src/ directory, compile them and link them into lib(subdirectory_name).a 
+# in the build directory. This behaviour is entirely configurable,
+# please read the ESP-IDF documents if you need to do this.
+#
diff --git a/examples/protocols/asio/udp_echo_server/main/udp_echo_server.cpp b/examples/protocols/asio/udp_echo_server/main/udp_echo_server.cpp
new file mode 100644 (file)
index 0000000..5b6607d
--- /dev/null
@@ -0,0 +1,69 @@
+//
+// async_udp_echo_server.cpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#include <cstdlib>
+#include <iostream>
+
+#include "asio.hpp"
+
+using asio::ip::udp;
+
+class server
+{
+public:
+  server(asio::io_context& io_context, short port)
+    : socket_(io_context, udp::endpoint(udp::v4(), port))
+  {
+    do_receive();
+  }
+
+  void do_receive()
+  {
+    socket_.async_receive_from(
+        asio::buffer(data_, max_length), sender_endpoint_,
+        [this](std::error_code ec, std::size_t bytes_recvd)
+        {
+          if (!ec && bytes_recvd > 0)
+          {
+            std::cout << data_ << std::endl;
+            do_send(bytes_recvd);
+          }
+          else
+          {
+            do_receive();
+          }
+        });
+  }
+
+  void do_send(std::size_t length)
+  {
+    socket_.async_send_to(
+        asio::buffer(data_, length), sender_endpoint_,
+        [this](std::error_code /*ec*/, std::size_t  bytes /*bytes_sent*/)
+        {
+          do_receive();
+        });
+  }
+
+private:
+  udp::socket socket_;
+  udp::endpoint sender_endpoint_;
+  enum { max_length = 1024 };
+  char data_[max_length];
+};
+
+void asio_main()
+{
+    asio::io_context io_context;
+
+    server s(io_context, std::atoi(CONFIG_EXAMPLE_PORT));
+
+    io_context.run();
+}
diff --git a/examples/protocols/asio/udp_echo_server/sdkconfig.defaults b/examples/protocols/asio/udp_echo_server/sdkconfig.defaults
new file mode 100644 (file)
index 0000000..d99552e
--- /dev/null
@@ -0,0 +1 @@
+CONFIG_MAIN_TASK_STACK_SIZE=8192
diff --git a/examples/protocols/asio/wifi_init/wifi_asio.cpp b/examples/protocols/asio/wifi_init/wifi_asio.cpp
new file mode 100644 (file)
index 0000000..b90ce26
--- /dev/null
@@ -0,0 +1,136 @@
+/* Common WiFi Init as STA for ASIO examples
+
+   This example code is in the Public Domain (or CC0 licensed, at your option.)
+
+   Unless required by applicable law or agreed to in writing, this
+   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+   CONDITIONS OF ANY KIND, either express or implied.
+*/
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "freertos/event_groups.h"
+#include "esp_system.h"
+#include "esp_wifi.h"
+#include "esp_event_loop.h"
+#include "esp_log.h"
+#include "nvs_flash.h"
+#include "driver/uart.h"
+#include "esp_console.h"
+#include "esp_vfs_dev.h"
+
+#include "lwip/err.h"
+#include "lwip/sys.h"
+
+#include <cstring>
+
+/* The examples use simple WiFi configuration that you can set via
+   'make menuconfig'.
+
+   If you'd rather not, just change the below entries to strings with
+   the config you want - ie #define EXAMPLE_WIFI_SSID "mywifissid"
+*/
+#define EXAMPLE_ESP_WIFI_SSID      CONFIG_ESP_WIFI_SSID
+#define EXAMPLE_ESP_WIFI_PASS      CONFIG_ESP_WIFI_PASSWORD
+
+/* FreeRTOS event group to signal when we are connected*/
+static EventGroupHandle_t wifi_event_group;
+
+/* The event group allows multiple bits for each event,
+   but we only care about one event - are we connected
+   to the AP with an IP? */
+const int WIFI_CONNECTED_BIT = BIT0;
+
+static const char *TAG = "asio example wifi init";
+
+/**
+ *  Definition of ASIO main method, which is called after network initialized
+ */
+void asio_main();
+
+static esp_err_t event_handler(void *ctx, system_event_t *event)
+{
+    switch(event->event_id) {
+    case SYSTEM_EVENT_STA_START:
+        esp_wifi_connect();
+        break;
+    case SYSTEM_EVENT_STA_GOT_IP:
+        ESP_LOGI(TAG, "got ip:%s",
+                 ip4addr_ntoa(&event->event_info.got_ip.ip_info.ip));
+        xEventGroupSetBits(wifi_event_group, WIFI_CONNECTED_BIT);
+        break;
+    case SYSTEM_EVENT_AP_STACONNECTED:
+        ESP_LOGI(TAG, "station:" MACSTR " join, AID=%d",
+                 MAC2STR(event->event_info.sta_connected.mac),
+                 event->event_info.sta_connected.aid);
+        break;
+    case SYSTEM_EVENT_AP_STADISCONNECTED:
+        ESP_LOGI(TAG, "station:" MACSTR "leave, AID=%d",
+                 MAC2STR(event->event_info.sta_disconnected.mac),
+                 event->event_info.sta_disconnected.aid);
+        break;
+    case SYSTEM_EVENT_STA_DISCONNECTED:
+        esp_wifi_connect();
+        xEventGroupClearBits(wifi_event_group, WIFI_CONNECTED_BIT);
+        break;
+    default:
+        break;
+    }
+    return ESP_OK;
+}
+
+void wifi_init_sta()
+{
+    wifi_event_group = xEventGroupCreate();
+
+    tcpip_adapter_init();
+    ESP_ERROR_CHECK(esp_event_loop_init(event_handler, NULL) );
+
+    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
+    ESP_ERROR_CHECK(esp_wifi_init(&cfg));
+    wifi_config_t wifi_config;
+    // zero out the config struct to ensure defaults are setup
+    memset(&wifi_config, 0, sizeof(wifi_sta_config_t));
+    // only copy ssid&password from example config
+    strcpy((char*)(wifi_config.sta.ssid), EXAMPLE_ESP_WIFI_SSID);
+    strcpy((char*)(wifi_config.sta.password), EXAMPLE_ESP_WIFI_PASS);
+
+    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) );
+    ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) );
+    ESP_ERROR_CHECK(esp_wifi_start() );
+
+    ESP_LOGI(TAG, "wifi_init_sta finished.");
+    ESP_LOGI(TAG, "connect to ap SSID:%s password:%s",
+             EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS);
+}
+
+extern "C" void app_main()
+{
+    //Initialize NVS
+    esp_err_t ret = nvs_flash_init();
+    if (ret == ESP_ERR_NVS_NO_FREE_PAGES) {
+      ESP_ERROR_CHECK(nvs_flash_erase());
+      ret = nvs_flash_init();
+    }
+    ESP_ERROR_CHECK(ret);
+    
+    ESP_LOGI(TAG, "ESP_WIFI_MODE_AP");
+    wifi_init_sta();
+
+    // Initialize VFS & UART so we can use std::cout/cin
+    setvbuf(stdin, NULL, _IONBF, 0);
+    setvbuf(stdout, NULL, _IONBF, 0);
+    /* Install UART driver for interrupt-driven reads and writes */
+    ESP_ERROR_CHECK( uart_driver_install( (uart_port_t)CONFIG_CONSOLE_UART_NUM,
+            256, 0, 0, NULL, 0) );
+    /* Tell VFS to use UART driver */
+    esp_vfs_dev_uart_use_driver(CONFIG_CONSOLE_UART_NUM);
+    esp_vfs_dev_uart_set_rx_line_endings(ESP_LINE_ENDINGS_CR);
+    /* Move the caret to the beginning of the next line on '\n' */
+    esp_vfs_dev_uart_set_tx_line_endings(ESP_LINE_ENDINGS_CRLF);
+
+    // wait till we receive IP, so asio realated code can be started
+    xEventGroupWaitBits(wifi_event_group, WIFI_CONNECTED_BIT, 1, 1, portMAX_DELAY);
+
+    // network is ready, let's proceed with ASIO example
+    asio_main();
+}
index 5ff4fb74a3259219934c68ab334f03286530422d..f75acb23409320a8f49aaff739c269e33b582dcd 100644 (file)
@@ -9,5 +9,6 @@ components/mbedtls/mbedtls                          @GENERAL_MIRROR_SERVER@/idf/
 components/micro-ecc/micro-ecc                      @GENERAL_MIRROR_SERVER@/idf/micro-ecc.git                       ALLOW_TO_SYNC_FROM_PUBLIC
 components/nghttp/nghttp2                           @GENERAL_MIRROR_SERVER@/idf/nghttp2.git                         ALLOW_TO_SYNC_FROM_PUBLIC
 components/spiffs/spiffs                            @GENERAL_MIRROR_SERVER@/idf/spiffs.git                          ALLOW_TO_SYNC_FROM_PUBLIC
+components/asio/asio                                @GENERAL_MIRROR_SERVER@/idf/asio.git
 third-party/mruby                                   @GENERAL_MIRROR_SERVER@/idf/mruby.git                           ALLOW_TO_SYNC_FROM_PUBLIC
 third-party/neverbleed                              @GENERAL_MIRROR_SERVER@/idf/neverbleed.git                      ALLOW_TO_SYNC_FROM_PUBLIC