Merge branch 'feature/cermak_asio_support' into 'master'
asio component and examples in esp-idf See merge request idf/esp-idf!2509
This commit is contained in:
commit
43baa7f2b9
62 changed files with 2416 additions and 9 deletions
4
.gitmodules
vendored
4
.gitmodules
vendored
|
@ -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
|
||||
|
|
1
components/asio/asio
Submodule
1
components/asio/asio
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit 55efc179b76139c8f9b44bf22a4aba4803f7a7bd
|
6
components/asio/component.mk
Normal file
6
components/asio/component.mk
Normal file
|
@ -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
|
45
components/asio/port/include/esp_asio_config.h
Normal file
45
components/asio/port/include/esp_asio_config.h
Normal file
|
@ -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_
|
39
components/asio/port/include/esp_exception.h
Normal file
39
components/asio/port/include/esp_exception.h
Normal file
|
@ -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_
|
|
@ -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...
|
||||
*/
|
||||
|
@ -1075,7 +1081,8 @@ lwip_recvfrom(int s, void *mem, size_t len, int flags,
|
|||
|
||||
/* Check to see from where the data was.*/
|
||||
if (done) {
|
||||
#if !SOCKETS_DEBUG
|
||||
/* enabling the UDP fix for ESP32 below when SOCKET_DEBUG is off */
|
||||
#if !SOCKETS_DEBUG && !ESP_LWIP
|
||||
if (from && fromlen)
|
||||
#endif /* !SOCKETS_DEBUG */
|
||||
{
|
||||
|
@ -1196,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)
|
||||
{
|
||||
|
@ -3236,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)
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -31,3 +31,10 @@
|
|||
*/
|
||||
|
||||
#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
|
||||
|
|
39
components/newlib/platform_include/errno.h
Normal file
39
components/newlib/platform_include/errno.h
Normal file
|
@ -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_
|
45
components/newlib/platform_include/net/if.h
Normal file
45
components/newlib/platform_include/net/if.h
Normal file
|
@ -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_
|
33
components/newlib/platform_include/pthread.h
Normal file
33
components/newlib/platform_include/pthread.h
Normal file
|
@ -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__
|
32
components/newlib/platform_include/sys/poll.h
Normal file
32
components/newlib/platform_include/sys/poll.h
Normal file
|
@ -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_
|
21
components/newlib/platform_include/sys/uio.h
Normal file
21
components/newlib/platform_include/sys/uio.h
Normal file
|
@ -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_
|
24
components/newlib/platform_include/sys/un.h
Normal file
24
components/newlib/platform_include/sys/un.h
Normal file
|
@ -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_
|
|
@ -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
|
||||
}
|
||||
|
|
10
components/newlib/pthread.c
Normal file
10
components/newlib/pthread.c
Normal file
|
@ -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;
|
||||
}
|
|
@ -14,5 +14,12 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
int ioctl(int fd, int request, ...);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -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
|
||||
|
|
32
docs/en/api-reference/protocols/asio.rst
Normal file
32
docs/en/api-reference/protocols/asio.rst
Normal file
|
@ -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
|
||||
|
||||
|
|
@ -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.
|
||||
|
|
1
docs/zh_CN/api-reference/protocols/asio.rst
Normal file
1
docs/zh_CN/api-reference/protocols/asio.rst
Normal file
|
@ -0,0 +1 @@
|
|||
.. include:: ../../../en/api-reference/protocols/asio.rst
|
7
examples/protocols/asio/chat_client/Makefile
Normal file
7
examples/protocols/asio/chat_client/Makefile
Normal file
|
@ -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
|
19
examples/protocols/asio/chat_client/README.md
Normal file
19
examples/protocols/asio/chat_client/README.md
Normal file
|
@ -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.
|
98
examples/protocols/asio/chat_client/asio_chat_client_test.py
Normal file
98
examples/protocols/asio/chat_client/asio_chat_client_test.py
Normal file
|
@ -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()
|
10
examples/protocols/asio/chat_client/components/component.mk
Normal file
10
examples/protocols/asio/chat_client/components/component.mk
Normal file
|
@ -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 = .
|
136
examples/protocols/asio/chat_client/components/wifi_asio.cpp
Normal file
136
examples/protocols/asio/chat_client/components/wifi_asio.cpp
Normal file
|
@ -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();
|
||||
}
|
27
examples/protocols/asio/chat_client/main/Kconfig.projbuild
Normal file
27
examples/protocols/asio/chat_client/main/Kconfig.projbuild
Normal file
|
@ -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
|
165
examples/protocols/asio/chat_client/main/chat_client.cpp
Normal file
165
examples/protocols/asio/chat_client/main/chat_client.cpp
Normal file
|
@ -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();
|
||||
}
|
91
examples/protocols/asio/chat_client/main/chat_message.hpp
Normal file
91
examples/protocols/asio/chat_client/main/chat_message.hpp
Normal file
|
@ -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
|
8
examples/protocols/asio/chat_client/main/component.mk
Normal file
8
examples/protocols/asio/chat_client/main/component.mk
Normal file
|
@ -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.
|
||||
#
|
1
examples/protocols/asio/chat_client/sdkconfig.defaults
Normal file
1
examples/protocols/asio/chat_client/sdkconfig.defaults
Normal file
|
@ -0,0 +1 @@
|
|||
CONFIG_MAIN_TASK_STACK_SIZE=8192
|
7
examples/protocols/asio/chat_server/Makefile
Normal file
7
examples/protocols/asio/chat_server/Makefile
Normal file
|
@ -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
|
22
examples/protocols/asio/chat_server/README.md
Normal file
22
examples/protocols/asio/chat_server/README.md
Normal file
|
@ -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.
|
56
examples/protocols/asio/chat_server/asio_chat_server_test.py
Normal file
56
examples/protocols/asio/chat_server/asio_chat_server_test.py
Normal file
|
@ -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()
|
10
examples/protocols/asio/chat_server/components/component.mk
Normal file
10
examples/protocols/asio/chat_server/components/component.mk
Normal file
|
@ -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 = .
|
136
examples/protocols/asio/chat_server/components/wifi_asio.cpp
Normal file
136
examples/protocols/asio/chat_server/components/wifi_asio.cpp
Normal file
|
@ -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();
|
||||
}
|
21
examples/protocols/asio/chat_server/main/Kconfig.projbuild
Normal file
21
examples/protocols/asio/chat_server/main/Kconfig.projbuild
Normal file
|
@ -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
|
91
examples/protocols/asio/chat_server/main/chat_message.hpp
Normal file
91
examples/protocols/asio/chat_server/main/chat_message.hpp
Normal file
|
@ -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
|
214
examples/protocols/asio/chat_server/main/chat_server.cpp
Normal file
214
examples/protocols/asio/chat_server/main/chat_server.cpp
Normal file
|
@ -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;
|
||||
}
|
8
examples/protocols/asio/chat_server/main/component.mk
Normal file
8
examples/protocols/asio/chat_server/main/component.mk
Normal file
|
@ -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.
|
||||
#
|
1
examples/protocols/asio/chat_server/sdkconfig.defaults
Normal file
1
examples/protocols/asio/chat_server/sdkconfig.defaults
Normal file
|
@ -0,0 +1 @@
|
|||
CONFIG_MAIN_TASK_STACK_SIZE=8192
|
7
examples/protocols/asio/tcp_echo_server/Makefile
Normal file
7
examples/protocols/asio/tcp_echo_server/Makefile
Normal file
|
@ -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
|
18
examples/protocols/asio/tcp_echo_server/README.md
Normal file
18
examples/protocols/asio/tcp_echo_server/README.md
Normal file
|
@ -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.
|
|
@ -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()
|
|
@ -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 = .
|
136
examples/protocols/asio/tcp_echo_server/components/wifi_asio.cpp
Normal file
136
examples/protocols/asio/tcp_echo_server/components/wifi_asio.cpp
Normal file
|
@ -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();
|
||||
}
|
|
@ -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
|
|
@ -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.
|
||||
#
|
||||
|
91
examples/protocols/asio/tcp_echo_server/main/echo_server.cpp
Normal file
91
examples/protocols/asio/tcp_echo_server/main/echo_server.cpp
Normal file
|
@ -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();
|
||||
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
CONFIG_MAIN_TASK_STACK_SIZE=8192
|
7
examples/protocols/asio/udp_echo_server/Makefile
Normal file
7
examples/protocols/asio/udp_echo_server/Makefile
Normal file
|
@ -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
|
18
examples/protocols/asio/udp_echo_server/README.md
Normal file
18
examples/protocols/asio/udp_echo_server/README.md
Normal file
|
@ -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.
|
|
@ -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()
|
|
@ -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 = .
|
136
examples/protocols/asio/udp_echo_server/components/wifi_asio.cpp
Normal file
136
examples/protocols/asio/udp_echo_server/components/wifi_asio.cpp
Normal file
|
@ -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();
|
||||
}
|
|
@ -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
|
|
@ -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.
|
||||
#
|
|
@ -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();
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
CONFIG_MAIN_TASK_STACK_SIZE=8192
|
136
examples/protocols/asio/wifi_init/wifi_asio.cpp
Normal file
136
examples/protocols/asio/wifi_init/wifi_asio.cpp
Normal file
|
@ -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();
|
||||
}
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue