3.3.7
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
name=Networking
|
||||
version=3.3.7
|
||||
author=Hristo Gochkov
|
||||
maintainer=Hristo Gochkov <hristo@espressif.com>
|
||||
sentence=General network management library.
|
||||
paragraph=This library holds all common functionality of the different network interfaces.
|
||||
category=Communication
|
||||
url=
|
||||
architectures=esp32
|
||||
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "NetworkInterface.h"
|
||||
#include "NetworkEvents.h"
|
||||
#include "NetworkManager.h"
|
||||
|
||||
#include "NetworkClient.h"
|
||||
#include "NetworkServer.h"
|
||||
#include "NetworkUdp.h"
|
||||
@@ -0,0 +1,690 @@
|
||||
/*
|
||||
Client.h - Client class for Raspberry Pi
|
||||
Copyright (c) 2016 Hristo Gochkov All right reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "NetworkClient.h"
|
||||
#include "NetworkManager.h"
|
||||
#include <lwip/sockets.h>
|
||||
#include <lwip/netdb.h>
|
||||
#include <errno.h>
|
||||
|
||||
#ifndef IN6_IS_ADDR_V4MAPPED
|
||||
#define IN6_IS_ADDR_V4MAPPED(a) ((((__const uint32_t *)(a))[0] == 0) && (((__const uint32_t *)(a))[1] == 0) && (((__const uint32_t *)(a))[2] == htonl(0xffff)))
|
||||
#endif
|
||||
|
||||
#define WIFI_CLIENT_DEF_CONN_TIMEOUT_MS (3000)
|
||||
#define WIFI_CLIENT_MAX_WRITE_RETRY (10)
|
||||
#define WIFI_CLIENT_SELECT_TIMEOUT_US (1000000)
|
||||
#define WIFI_CLIENT_FLUSH_BUFFER_SIZE (1024)
|
||||
|
||||
#undef connect
|
||||
#undef write
|
||||
#undef read
|
||||
|
||||
class NetworkClientRxBuffer {
|
||||
private:
|
||||
size_t _size;
|
||||
uint8_t *_buffer;
|
||||
size_t _pos;
|
||||
size_t _fill;
|
||||
int _fd;
|
||||
bool _failed;
|
||||
|
||||
size_t r_available() {
|
||||
if (_fd < 0) {
|
||||
return 0;
|
||||
}
|
||||
int count;
|
||||
#ifdef ESP_IDF_VERSION_MAJOR
|
||||
int res = lwip_ioctl(_fd, FIONREAD, &count);
|
||||
#else
|
||||
int res = lwip_ioctl_r(_fd, FIONREAD, &count);
|
||||
#endif
|
||||
if (res < 0) {
|
||||
_failed = true;
|
||||
return 0;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
size_t fillBuffer() {
|
||||
if (!_buffer) {
|
||||
_buffer = (uint8_t *)malloc(_size);
|
||||
if (!_buffer) {
|
||||
log_e("Not enough memory to allocate buffer");
|
||||
_failed = true;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if (_fill && _pos == _fill) {
|
||||
_fill = 0;
|
||||
_pos = 0;
|
||||
}
|
||||
if (!_buffer || _size <= _fill || !r_available()) {
|
||||
return 0;
|
||||
}
|
||||
int res = recv(_fd, _buffer + _fill, _size - _fill, MSG_DONTWAIT);
|
||||
if (res < 0) {
|
||||
if (errno != EWOULDBLOCK) {
|
||||
_failed = true;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
_fill += res;
|
||||
return res;
|
||||
}
|
||||
|
||||
public:
|
||||
NetworkClientRxBuffer(int fd, size_t size = 1436) : _size(size), _buffer(NULL), _pos(0), _fill(0), _fd(fd), _failed(false) {
|
||||
//_buffer = (uint8_t *)malloc(_size);
|
||||
}
|
||||
|
||||
~NetworkClientRxBuffer() {
|
||||
free(_buffer);
|
||||
}
|
||||
|
||||
bool failed() {
|
||||
return _failed;
|
||||
}
|
||||
|
||||
int read(uint8_t *dst, size_t len) {
|
||||
if (!dst || !len || (_pos == _fill && !fillBuffer())) {
|
||||
return _failed ? -1 : 0;
|
||||
}
|
||||
size_t a = _fill - _pos;
|
||||
if (len <= a || ((len - a) <= (_size - _fill) && fillBuffer() >= (len - a))) {
|
||||
if (len == 1) {
|
||||
*dst = _buffer[_pos];
|
||||
} else {
|
||||
memcpy(dst, _buffer + _pos, len);
|
||||
}
|
||||
_pos += len;
|
||||
return len;
|
||||
}
|
||||
size_t left = len;
|
||||
size_t toRead = a;
|
||||
uint8_t *buf = dst;
|
||||
memcpy(buf, _buffer + _pos, toRead);
|
||||
_pos += toRead;
|
||||
left -= toRead;
|
||||
buf += toRead;
|
||||
while (left) {
|
||||
if (!fillBuffer()) {
|
||||
return len - left;
|
||||
}
|
||||
a = _fill - _pos;
|
||||
toRead = (a > left) ? left : a;
|
||||
memcpy(buf, _buffer + _pos, toRead);
|
||||
_pos += toRead;
|
||||
left -= toRead;
|
||||
buf += toRead;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
int peek() {
|
||||
if (_pos == _fill && !fillBuffer()) {
|
||||
return -1;
|
||||
}
|
||||
return _buffer[_pos];
|
||||
}
|
||||
|
||||
size_t available() {
|
||||
return _fill - _pos + r_available();
|
||||
}
|
||||
|
||||
void clear() {
|
||||
if (r_available()) {
|
||||
_pos = _fill;
|
||||
while (fillBuffer()) {
|
||||
_pos = _fill;
|
||||
}
|
||||
}
|
||||
_pos = 0;
|
||||
_fill = 0;
|
||||
}
|
||||
};
|
||||
|
||||
class NetworkClientSocketHandle {
|
||||
private:
|
||||
int sockfd;
|
||||
|
||||
public:
|
||||
NetworkClientSocketHandle(int fd) : sockfd(fd) {}
|
||||
|
||||
~NetworkClientSocketHandle() {
|
||||
close();
|
||||
}
|
||||
|
||||
void close() {
|
||||
if (sockfd >= 0) {
|
||||
::close(sockfd);
|
||||
sockfd = -1;
|
||||
}
|
||||
}
|
||||
|
||||
int fd() {
|
||||
return sockfd;
|
||||
}
|
||||
};
|
||||
|
||||
NetworkClient::NetworkClient() : _rxBuffer(nullptr), _connected(false), _sse(false), _timeout(WIFI_CLIENT_DEF_CONN_TIMEOUT_MS), next(NULL) {}
|
||||
|
||||
NetworkClient::NetworkClient(int fd) : _connected(true), _timeout(WIFI_CLIENT_DEF_CONN_TIMEOUT_MS), next(NULL) {
|
||||
clientSocketHandle.reset(new NetworkClientSocketHandle(fd));
|
||||
_rxBuffer.reset(new NetworkClientRxBuffer(fd));
|
||||
}
|
||||
|
||||
NetworkClient::~NetworkClient() {}
|
||||
|
||||
void NetworkClient::stop() {
|
||||
if (clientSocketHandle) {
|
||||
clientSocketHandle->close();
|
||||
}
|
||||
clientSocketHandle = NULL;
|
||||
_rxBuffer = NULL;
|
||||
_connected = false;
|
||||
_lastReadTimeout = 0;
|
||||
_lastWriteTimeout = 0;
|
||||
}
|
||||
|
||||
int NetworkClient::connect(IPAddress ip, uint16_t port) {
|
||||
return connect(ip, port, _timeout);
|
||||
}
|
||||
|
||||
int NetworkClient::connect(IPAddress ip, uint16_t port, int32_t timeout_ms) {
|
||||
struct sockaddr_storage serveraddr = {};
|
||||
_timeout = timeout_ms;
|
||||
int sockfd = -1;
|
||||
|
||||
#if CONFIG_LWIP_IPV6
|
||||
if (ip.type() == IPv6) {
|
||||
struct sockaddr_in6 *tmpaddr = (struct sockaddr_in6 *)&serveraddr;
|
||||
sockfd = socket(AF_INET6, SOCK_STREAM, 0);
|
||||
tmpaddr->sin6_family = AF_INET6;
|
||||
memcpy(tmpaddr->sin6_addr.un.u8_addr, &ip[0], 16);
|
||||
tmpaddr->sin6_port = htons(port);
|
||||
tmpaddr->sin6_scope_id = ip.zone();
|
||||
} else {
|
||||
#endif
|
||||
struct sockaddr_in *tmpaddr = (struct sockaddr_in *)&serveraddr;
|
||||
sockfd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
tmpaddr->sin_family = AF_INET;
|
||||
tmpaddr->sin_addr.s_addr = ip;
|
||||
tmpaddr->sin_port = htons(port);
|
||||
#if CONFIG_LWIP_IPV6
|
||||
}
|
||||
#endif
|
||||
if (sockfd < 0) {
|
||||
log_e("socket: %d", errno);
|
||||
return 0;
|
||||
}
|
||||
fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFL, 0) | O_NONBLOCK);
|
||||
|
||||
fd_set fdset;
|
||||
struct timeval tv;
|
||||
FD_ZERO(&fdset);
|
||||
FD_SET(sockfd, &fdset);
|
||||
tv.tv_sec = _timeout / 1000;
|
||||
tv.tv_usec = (_timeout % 1000) * 1000;
|
||||
|
||||
#ifdef ESP_IDF_VERSION_MAJOR
|
||||
int res = lwip_connect(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));
|
||||
#else
|
||||
int res = lwip_connect_r(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));
|
||||
#endif
|
||||
if (res < 0 && errno != EINPROGRESS) {
|
||||
log_e("connect on fd %d, errno: %d, \"%s\"", sockfd, errno, strerror(errno));
|
||||
close(sockfd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
res = select(sockfd + 1, nullptr, &fdset, nullptr, _timeout < 0 ? nullptr : &tv);
|
||||
if (res < 0) {
|
||||
log_e("select on fd %d, errno: %d, \"%s\"", sockfd, errno, strerror(errno));
|
||||
close(sockfd);
|
||||
return 0;
|
||||
} else if (res == 0) {
|
||||
log_i("select returned due to timeout %d ms for fd %d", _timeout, sockfd);
|
||||
close(sockfd);
|
||||
return 0;
|
||||
} else {
|
||||
int sockerr;
|
||||
socklen_t len = (socklen_t)sizeof(int);
|
||||
res = getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &sockerr, &len);
|
||||
|
||||
if (res < 0) {
|
||||
log_e("getsockopt on fd %d, errno: %d, \"%s\"", sockfd, errno, strerror(errno));
|
||||
close(sockfd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (sockerr != 0) {
|
||||
log_e("socket error on fd %d, errno: %d, \"%s\"", sockfd, sockerr, strerror(sockerr));
|
||||
close(sockfd);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
#define ROE_WIFICLIENT(x, msg) \
|
||||
{ \
|
||||
if (((x) < 0)) { \
|
||||
log_e("Setsockopt '" msg "'' on fd %d failed. errno: %d, \"%s\"", sockfd, errno, strerror(errno)); \
|
||||
return 0; \
|
||||
} \
|
||||
}
|
||||
ROE_WIFICLIENT(setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)), "SO_SNDTIMEO");
|
||||
ROE_WIFICLIENT(setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)), "SO_RCVTIMEO");
|
||||
|
||||
// These are also set in NetworkClientSecure, should be set here too?
|
||||
//ROE_WIFICLIENT(setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, &enable, sizeof(enable)),"TCP_NODELAY");
|
||||
//ROE_WIFICLIENT (setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &enable, sizeof(enable)),"SO_KEEPALIVE");
|
||||
|
||||
fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFL, 0) & (~O_NONBLOCK));
|
||||
clientSocketHandle.reset(new NetworkClientSocketHandle(sockfd));
|
||||
_rxBuffer.reset(new NetworkClientRxBuffer(sockfd));
|
||||
|
||||
_connected = true;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int NetworkClient::connect(const char *host, uint16_t port) {
|
||||
return connect(host, port, _timeout);
|
||||
}
|
||||
|
||||
int NetworkClient::connect(const char *host, uint16_t port, int32_t timeout_ms) {
|
||||
IPAddress srv((uint32_t)0);
|
||||
if (!Network.hostByName(host, srv)) {
|
||||
return 0;
|
||||
}
|
||||
return connect(srv, port, timeout_ms);
|
||||
}
|
||||
|
||||
int NetworkClient::setSocketOption(int option, char *value, size_t len) {
|
||||
return setSocketOption(SOL_SOCKET, option, (const void *)value, len);
|
||||
}
|
||||
|
||||
int NetworkClient::setSocketOption(int level, int option, const void *value, size_t len) {
|
||||
int res = setsockopt(fd(), level, option, value, len);
|
||||
if (res < 0) {
|
||||
log_e("fail on %d, errno: %d, \"%s\"", fd(), errno, strerror(errno));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
int NetworkClient::getSocketOption(int level, int option, const void *value, size_t size) {
|
||||
int res = getsockopt(fd(), level, option, (char *)value, (socklen_t *)&size);
|
||||
if (res < 0) {
|
||||
log_e("fail on fd %d, errno: %d, \"%s\"", fd(), errno, strerror(errno));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
int NetworkClient::setOption(int option, int *value) {
|
||||
return setSocketOption(IPPROTO_TCP, option, (const void *)value, sizeof(int));
|
||||
}
|
||||
|
||||
int NetworkClient::getOption(int option, int *value) {
|
||||
socklen_t size = sizeof(int);
|
||||
int res = getsockopt(fd(), IPPROTO_TCP, option, (char *)value, &size);
|
||||
if (res < 0) {
|
||||
log_e("fail on fd %d, errno: %d, \"%s\"", fd(), errno, strerror(errno));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void NetworkClient::setConnectionTimeout(uint32_t milliseconds) {
|
||||
_timeout = milliseconds;
|
||||
}
|
||||
|
||||
int NetworkClient::setNoDelay(bool nodelay) {
|
||||
int flag = nodelay;
|
||||
return setOption(TCP_NODELAY, &flag);
|
||||
}
|
||||
|
||||
bool NetworkClient::getNoDelay() {
|
||||
int flag = 0;
|
||||
getOption(TCP_NODELAY, &flag);
|
||||
return flag;
|
||||
}
|
||||
|
||||
size_t NetworkClient::write(uint8_t data) {
|
||||
return write(&data, 1);
|
||||
}
|
||||
|
||||
int NetworkClient::read() {
|
||||
uint8_t data = 0;
|
||||
int res = read(&data, 1);
|
||||
if (res < 0) {
|
||||
return res;
|
||||
}
|
||||
if (res == 0) { // No data available.
|
||||
return -1;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
void NetworkClient::flush() {
|
||||
clear();
|
||||
}
|
||||
|
||||
size_t NetworkClient::write(const uint8_t *buf, size_t size) {
|
||||
int res = 0;
|
||||
int retry = WIFI_CLIENT_MAX_WRITE_RETRY;
|
||||
int socketFileDescriptor = fd();
|
||||
size_t totalBytesSent = 0;
|
||||
size_t bytesRemaining = size;
|
||||
|
||||
if (!_connected || (socketFileDescriptor < 0)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (retry) {
|
||||
//use select to make sure the socket is ready for writing
|
||||
fd_set set;
|
||||
struct timeval tv;
|
||||
FD_ZERO(&set); // empties the set
|
||||
FD_SET(socketFileDescriptor, &set); // adds FD to the set
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = WIFI_CLIENT_SELECT_TIMEOUT_US;
|
||||
retry--;
|
||||
|
||||
if (_lastWriteTimeout != _timeout) {
|
||||
if (fd() >= 0) {
|
||||
struct timeval timeout_tv;
|
||||
timeout_tv.tv_sec = _timeout / 1000;
|
||||
timeout_tv.tv_usec = (_timeout % 1000) * 1000;
|
||||
if (setSocketOption(SO_SNDTIMEO, (char *)&timeout_tv, sizeof(struct timeval)) >= 0) {
|
||||
_lastWriteTimeout = _timeout;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (select(socketFileDescriptor + 1, NULL, &set, NULL, &tv) < 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (FD_ISSET(socketFileDescriptor, &set)) {
|
||||
res = send(socketFileDescriptor, (void *)buf, bytesRemaining, MSG_DONTWAIT);
|
||||
if (res > 0) {
|
||||
totalBytesSent += res;
|
||||
if (totalBytesSent >= size) {
|
||||
//completed successfully
|
||||
retry = 0;
|
||||
} else {
|
||||
buf += res;
|
||||
bytesRemaining -= res;
|
||||
retry = WIFI_CLIENT_MAX_WRITE_RETRY;
|
||||
}
|
||||
} else if (res < 0) {
|
||||
log_e("fail on fd %d, errno: %d, \"%s\"", fd(), errno, strerror(errno));
|
||||
if (errno != EAGAIN) {
|
||||
//if resource was busy, can try again, otherwise give up
|
||||
stop();
|
||||
res = 0;
|
||||
retry = 0;
|
||||
}
|
||||
} else {
|
||||
// Try again
|
||||
}
|
||||
}
|
||||
}
|
||||
return totalBytesSent;
|
||||
}
|
||||
|
||||
size_t NetworkClient::write_P(PGM_P buf, size_t size) {
|
||||
return write(buf, size);
|
||||
}
|
||||
|
||||
size_t NetworkClient::write(Stream &stream) {
|
||||
uint8_t *buf = (uint8_t *)malloc(1360);
|
||||
if (!buf) {
|
||||
return 0;
|
||||
}
|
||||
size_t toRead = 0, toWrite = 0, written = 0;
|
||||
size_t available = stream.available();
|
||||
while (available) {
|
||||
toRead = (available > 1360) ? 1360 : available;
|
||||
toWrite = stream.readBytes(buf, toRead);
|
||||
written += write(buf, toWrite);
|
||||
available = stream.available();
|
||||
}
|
||||
free(buf);
|
||||
return written;
|
||||
}
|
||||
|
||||
int NetworkClient::read(uint8_t *buf, size_t size) {
|
||||
if (_lastReadTimeout != _timeout) {
|
||||
if (fd() >= 0) {
|
||||
struct timeval timeout_tv;
|
||||
timeout_tv.tv_sec = _timeout / 1000;
|
||||
timeout_tv.tv_usec = (_timeout % 1000) * 1000;
|
||||
if (setSocketOption(SO_RCVTIMEO, (char *)&timeout_tv, sizeof(struct timeval)) >= 0) {
|
||||
_lastReadTimeout = _timeout;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int res = -1;
|
||||
if (_rxBuffer) {
|
||||
res = _rxBuffer->read(buf, size);
|
||||
if (_rxBuffer->failed()) {
|
||||
log_e("fail on fd %d, errno: %d, \"%s\"", fd(), errno, strerror(errno));
|
||||
stop();
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
size_t NetworkClient::readBytes(char *buffer, size_t length) {
|
||||
size_t left = length, sofar = 0;
|
||||
int r = 0, to = millis() + getTimeout();
|
||||
while (left) {
|
||||
r = read((uint8_t *)buffer + sofar, left);
|
||||
if (r < 0) {
|
||||
// Error has occurred
|
||||
break;
|
||||
}
|
||||
if (r > 0) {
|
||||
// We got some data
|
||||
left -= r;
|
||||
sofar += r;
|
||||
to = millis() + getTimeout();
|
||||
} else {
|
||||
// We got no data
|
||||
if (millis() >= to) {
|
||||
// We have waited for data enough
|
||||
log_w("Timeout waiting for data on fd %d", fd());
|
||||
break;
|
||||
}
|
||||
// Allow other tasks to run
|
||||
delay(2);
|
||||
}
|
||||
}
|
||||
return sofar;
|
||||
}
|
||||
|
||||
int NetworkClient::peek() {
|
||||
int res = -1;
|
||||
if (fd() >= 0 && _rxBuffer) {
|
||||
res = _rxBuffer->peek();
|
||||
if (_rxBuffer->failed()) {
|
||||
log_e("fail on fd %d, errno: %d, \"%s\"", fd(), errno, strerror(errno));
|
||||
stop();
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
int NetworkClient::available() {
|
||||
if (fd() < 0 || !_rxBuffer) {
|
||||
return 0;
|
||||
}
|
||||
int res = _rxBuffer->available();
|
||||
if (_rxBuffer->failed()) {
|
||||
log_e("fail on fd %d, errno: %d, \"%s\"", fd(), errno, strerror(errno));
|
||||
stop();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void NetworkClient::clear() {
|
||||
if (_rxBuffer != nullptr) {
|
||||
_rxBuffer->clear();
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t NetworkClient::connected() {
|
||||
if (fd() == -1 && _connected) {
|
||||
stop();
|
||||
}
|
||||
if (_connected) {
|
||||
uint8_t dummy;
|
||||
int res = recv(fd(), &dummy, 1, MSG_DONTWAIT | MSG_PEEK);
|
||||
// avoid unused var warning by gcc
|
||||
(void)res;
|
||||
// recv only sets errno if res is <= 0
|
||||
if (res <= 0) {
|
||||
switch (errno) {
|
||||
case EWOULDBLOCK:
|
||||
case ENOENT: //caused by vfs
|
||||
_connected = true;
|
||||
break;
|
||||
case ENOTCONN:
|
||||
case EPIPE:
|
||||
case ECONNRESET:
|
||||
case ECONNREFUSED:
|
||||
case ECONNABORTED:
|
||||
_connected = false;
|
||||
log_d("Disconnected: RES: %d, ERR: %d", res, errno);
|
||||
break;
|
||||
default:
|
||||
log_i("Unexpected: RES: %d, ERR: %d", res, errno);
|
||||
_connected = true;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
_connected = true;
|
||||
}
|
||||
}
|
||||
return _connected;
|
||||
}
|
||||
|
||||
IPAddress NetworkClient::remoteIP(int fd) const {
|
||||
struct sockaddr_storage addr;
|
||||
socklen_t len = sizeof addr;
|
||||
getpeername(fd, (struct sockaddr *)&addr, &len);
|
||||
|
||||
// IPv4 socket, old way
|
||||
if (((struct sockaddr *)&addr)->sa_family == AF_INET) {
|
||||
struct sockaddr_in *s = (struct sockaddr_in *)&addr;
|
||||
return IPAddress((uint32_t)(s->sin_addr.s_addr));
|
||||
}
|
||||
|
||||
#if CONFIG_LWIP_IPV6
|
||||
// IPv6, but it might be IPv4 mapped address
|
||||
if (((struct sockaddr *)&addr)->sa_family == AF_INET6) {
|
||||
struct sockaddr_in6 *saddr6 = (struct sockaddr_in6 *)&addr;
|
||||
if (IN6_IS_ADDR_V4MAPPED(saddr6->sin6_addr.un.u32_addr)) {
|
||||
return IPAddress(IPv4, (uint8_t *)saddr6->sin6_addr.s6_addr + IPADDRESS_V4_BYTES_INDEX);
|
||||
} else {
|
||||
return IPAddress(IPv6, (uint8_t *)(saddr6->sin6_addr.s6_addr), saddr6->sin6_scope_id);
|
||||
}
|
||||
}
|
||||
log_e("NetworkClient::remoteIP Not AF_INET or AF_INET6?");
|
||||
#endif
|
||||
return (IPAddress(0, 0, 0, 0));
|
||||
}
|
||||
|
||||
uint16_t NetworkClient::remotePort(int fd) const {
|
||||
struct sockaddr_storage addr;
|
||||
socklen_t len = sizeof addr;
|
||||
getpeername(fd, (struct sockaddr *)&addr, &len);
|
||||
struct sockaddr_in *s = (struct sockaddr_in *)&addr;
|
||||
return ntohs(s->sin_port);
|
||||
}
|
||||
|
||||
IPAddress NetworkClient::remoteIP() const {
|
||||
return remoteIP(fd());
|
||||
}
|
||||
|
||||
uint16_t NetworkClient::remotePort() const {
|
||||
return remotePort(fd());
|
||||
}
|
||||
|
||||
IPAddress NetworkClient::localIP(int fd) const {
|
||||
struct sockaddr_storage addr;
|
||||
socklen_t len = sizeof addr;
|
||||
getsockname(fd, (struct sockaddr *)&addr, &len);
|
||||
|
||||
// IPv4 socket, old way
|
||||
if (((struct sockaddr *)&addr)->sa_family == AF_INET) {
|
||||
struct sockaddr_in *s = (struct sockaddr_in *)&addr;
|
||||
return IPAddress((uint32_t)(s->sin_addr.s_addr));
|
||||
}
|
||||
|
||||
#if CONFIG_LWIP_IPV6
|
||||
// IPv6, but it might be IPv4 mapped address
|
||||
if (((struct sockaddr *)&addr)->sa_family == AF_INET6) {
|
||||
struct sockaddr_in6 *saddr6 = (struct sockaddr_in6 *)&addr;
|
||||
if (IN6_IS_ADDR_V4MAPPED(saddr6->sin6_addr.un.u32_addr)) {
|
||||
return IPAddress(IPv4, (uint8_t *)saddr6->sin6_addr.s6_addr + IPADDRESS_V4_BYTES_INDEX);
|
||||
} else {
|
||||
return IPAddress(IPv6, (uint8_t *)(saddr6->sin6_addr.s6_addr), saddr6->sin6_scope_id);
|
||||
}
|
||||
}
|
||||
log_e("NetworkClient::localIP Not AF_INET or AF_INET6?");
|
||||
#endif
|
||||
return (IPAddress(0, 0, 0, 0));
|
||||
}
|
||||
|
||||
uint16_t NetworkClient::localPort(int fd) const {
|
||||
struct sockaddr_storage addr;
|
||||
socklen_t len = sizeof addr;
|
||||
getsockname(fd, (struct sockaddr *)&addr, &len);
|
||||
struct sockaddr_in *s = (struct sockaddr_in *)&addr;
|
||||
return ntohs(s->sin_port);
|
||||
}
|
||||
|
||||
IPAddress NetworkClient::localIP() const {
|
||||
return localIP(fd());
|
||||
}
|
||||
|
||||
uint16_t NetworkClient::localPort() const {
|
||||
return localPort(fd());
|
||||
}
|
||||
|
||||
bool NetworkClient::operator==(const NetworkClient &rhs) {
|
||||
return clientSocketHandle == rhs.clientSocketHandle && remotePort() == rhs.remotePort() && remoteIP() == rhs.remoteIP();
|
||||
}
|
||||
|
||||
int NetworkClient::fd() const {
|
||||
if (clientSocketHandle == NULL) {
|
||||
return -1;
|
||||
} else {
|
||||
return clientSocketHandle->fd();
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkClient::setSSE(bool sse) {
|
||||
_sse = sse;
|
||||
}
|
||||
|
||||
bool NetworkClient::isSSE() {
|
||||
return _sse;
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
Client.h - Base class that provides Client
|
||||
Copyright (c) 2011 Adrian McEwen. All right reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Arduino.h"
|
||||
#include "Client.h"
|
||||
#include <memory>
|
||||
|
||||
class NetworkClientSocketHandle;
|
||||
class NetworkClientRxBuffer;
|
||||
|
||||
class ESPLwIPClient : public Client {
|
||||
public:
|
||||
virtual void setConnectionTimeout(uint32_t milliseconds) = 0;
|
||||
using Client::connect;
|
||||
virtual int connect(IPAddress ip, uint16_t port, int32_t timeout) = 0;
|
||||
virtual int connect(const char *host, uint16_t port, int32_t timeout) = 0;
|
||||
};
|
||||
|
||||
class NetworkClient : public ESPLwIPClient {
|
||||
protected:
|
||||
std::shared_ptr<NetworkClientSocketHandle> clientSocketHandle = nullptr;
|
||||
std::shared_ptr<NetworkClientRxBuffer> _rxBuffer = nullptr;
|
||||
bool _connected = false;
|
||||
bool _sse = false;
|
||||
int _timeout;
|
||||
int _lastWriteTimeout = 0;
|
||||
int _lastReadTimeout = 0;
|
||||
|
||||
public:
|
||||
NetworkClient *next;
|
||||
NetworkClient();
|
||||
NetworkClient(int fd);
|
||||
~NetworkClient();
|
||||
int connect(IPAddress ip, uint16_t port);
|
||||
int connect(IPAddress ip, uint16_t port, int32_t timeout_ms);
|
||||
int connect(const char *host, uint16_t port);
|
||||
int connect(const char *host, uint16_t port, int32_t timeout_ms);
|
||||
size_t write(uint8_t data);
|
||||
size_t write(const uint8_t *buf, size_t size);
|
||||
size_t write_P(PGM_P buf, size_t size);
|
||||
size_t write(Stream &stream);
|
||||
[[deprecated("Use clear() instead.")]]
|
||||
void flush(); // Print::flush tx
|
||||
int available();
|
||||
int read();
|
||||
int read(uint8_t *buf, size_t size);
|
||||
size_t readBytes(char *buffer, size_t length);
|
||||
size_t readBytes(uint8_t *buffer, size_t length) {
|
||||
return readBytes((char *)buffer, length);
|
||||
}
|
||||
int peek();
|
||||
void clear(); // clear rx
|
||||
void stop();
|
||||
uint8_t connected();
|
||||
void setSSE(bool sse);
|
||||
bool isSSE();
|
||||
|
||||
operator bool() {
|
||||
return connected();
|
||||
}
|
||||
bool operator==(const bool value) {
|
||||
return bool() == value;
|
||||
}
|
||||
bool operator!=(const bool value) {
|
||||
return bool() != value;
|
||||
}
|
||||
bool operator==(const NetworkClient &);
|
||||
bool operator!=(const NetworkClient &rhs) {
|
||||
return !this->operator==(rhs);
|
||||
};
|
||||
|
||||
virtual int fd() const;
|
||||
|
||||
int setSocketOption(int option, char *value, size_t len);
|
||||
int setSocketOption(int level, int option, const void *value, size_t len);
|
||||
int getSocketOption(int level, int option, const void *value, size_t size);
|
||||
int setOption(int option, int *value);
|
||||
int getOption(int option, int *value);
|
||||
void setConnectionTimeout(uint32_t milliseconds);
|
||||
int setNoDelay(bool nodelay);
|
||||
bool getNoDelay();
|
||||
|
||||
IPAddress remoteIP() const;
|
||||
IPAddress remoteIP(int fd) const;
|
||||
uint16_t remotePort() const;
|
||||
uint16_t remotePort(int fd) const;
|
||||
IPAddress localIP() const;
|
||||
IPAddress localIP(int fd) const;
|
||||
uint16_t localPort() const;
|
||||
uint16_t localPort(int fd) const;
|
||||
|
||||
//friend class NetworkServer;
|
||||
using Print::write;
|
||||
};
|
||||
@@ -0,0 +1,417 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <new> //std::nothrow
|
||||
|
||||
#include "NetworkEvents.h"
|
||||
#include "NetworkManager.h"
|
||||
#include "esp_task.h"
|
||||
#include "esp32-hal.h"
|
||||
|
||||
#ifndef ARDUINO_NETWORK_EVENT_TASK_STACK_SIZE
|
||||
#define ARDUINO_NETWORK_EVENT_TASK_STACK_SIZE 4096
|
||||
#endif
|
||||
|
||||
NetworkEvents::NetworkEvents() : _arduino_event_group(NULL), _arduino_event_queue(NULL), _arduino_event_task_handle(NULL) {}
|
||||
|
||||
NetworkEvents::~NetworkEvents() {
|
||||
if (_arduino_event_task_handle != NULL) {
|
||||
vTaskDelete(_arduino_event_task_handle);
|
||||
_arduino_event_task_handle = NULL;
|
||||
}
|
||||
if (_arduino_event_group != NULL) {
|
||||
vEventGroupDelete(_arduino_event_group);
|
||||
_arduino_event_group = NULL;
|
||||
}
|
||||
if (_arduino_event_queue != NULL) {
|
||||
arduino_event_t *event = NULL;
|
||||
// consume queue
|
||||
while (xQueueReceive(_arduino_event_queue, &event, 0) == pdTRUE) {
|
||||
delete event;
|
||||
}
|
||||
vQueueDelete(_arduino_event_queue);
|
||||
_arduino_event_queue = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t _initial_bits = 0;
|
||||
|
||||
bool NetworkEvents::initNetworkEvents() {
|
||||
if (!_arduino_event_group) {
|
||||
_arduino_event_group = xEventGroupCreate();
|
||||
if (!_arduino_event_group) {
|
||||
log_e("Network Event Group Create Failed!");
|
||||
return false;
|
||||
}
|
||||
xEventGroupSetBits(_arduino_event_group, _initial_bits);
|
||||
}
|
||||
|
||||
if (!_arduino_event_queue) {
|
||||
_arduino_event_queue = xQueueCreate(32, sizeof(arduino_event_t *));
|
||||
if (!_arduino_event_queue) {
|
||||
log_e("Network Event Queue Create Failed!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t err = esp_event_loop_create_default();
|
||||
if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) {
|
||||
log_e("esp_event_loop_create_default failed!");
|
||||
return err;
|
||||
}
|
||||
|
||||
if (!_arduino_event_task_handle) {
|
||||
xTaskCreateUniversal(
|
||||
[](void *self) {
|
||||
static_cast<NetworkEvents *>(self)->_checkForEvent();
|
||||
},
|
||||
"arduino_events", // label
|
||||
ARDUINO_NETWORK_EVENT_TASK_STACK_SIZE, // event task's stack size
|
||||
this, ESP_TASKD_EVENT_PRIO - 1, &_arduino_event_task_handle, ARDUINO_EVENT_RUNNING_CORE
|
||||
);
|
||||
if (!_arduino_event_task_handle) {
|
||||
log_e("Network Event Task Start Failed!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NetworkEvents::postEvent(const arduino_event_t *data) {
|
||||
if (data == NULL || _arduino_event_queue == NULL) {
|
||||
return false;
|
||||
}
|
||||
arduino_event_t *event = new (std::nothrow) arduino_event_t();
|
||||
if (event == NULL) {
|
||||
log_e("Arduino Event Malloc Failed!");
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(event, data, sizeof(arduino_event_t));
|
||||
if (xQueueSend(_arduino_event_queue, &event, portMAX_DELAY) != pdPASS) {
|
||||
log_e("Arduino Event Send Failed!");
|
||||
delete event; // release mem on error
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void NetworkEvents::_checkForEvent() {
|
||||
// this task can't run without the queue
|
||||
if (_arduino_event_queue == NULL) {
|
||||
_arduino_event_task_handle = NULL;
|
||||
vTaskDelete(NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
arduino_event_t *event = NULL;
|
||||
// wait for an event on a queue
|
||||
if (xQueueReceive(_arduino_event_queue, &event, portMAX_DELAY) != pdTRUE) {
|
||||
continue;
|
||||
}
|
||||
if (event == NULL) {
|
||||
continue;
|
||||
}
|
||||
log_v("Network Event: %d - %s", event->event_id, eventName(event->event_id));
|
||||
|
||||
#if defined NETWORK_EVENTS_MUTEX && SOC_CPU_CORES_NUM > 1
|
||||
std::unique_lock<std::mutex> lock(_mtx);
|
||||
#endif // defined NETWORK_EVENTS_MUTEX && SOC_CPU_CORES_NUM > 1
|
||||
|
||||
// iterate over registered callbacks
|
||||
for (auto &i : _cbEventList) {
|
||||
if (i.cb || i.fcb || i.scb) {
|
||||
if (i.event == (arduino_event_id_t)event->event_id || i.event == ARDUINO_EVENT_MAX) {
|
||||
if (i.cb) {
|
||||
i.cb((arduino_event_id_t)event->event_id);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (i.fcb) {
|
||||
i.fcb((arduino_event_id_t)event->event_id, (arduino_event_info_t)event->event_info);
|
||||
continue;
|
||||
}
|
||||
|
||||
i.scb(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if defined NETWORK_EVENTS_MUTEX && SOC_CPU_CORES_NUM > 1
|
||||
lock.unlock();
|
||||
#endif // defined NETWORK_EVENTS_MUTEX && SOC_CPU_CORES_NUM > 1
|
||||
|
||||
// release the event object's memory
|
||||
delete event;
|
||||
}
|
||||
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
template<typename T, typename... U> static size_t getStdFunctionAddress(std::function<T(U...)> f) {
|
||||
typedef T(fnType)(U...);
|
||||
fnType **fnPointer = f.template target<fnType *>();
|
||||
if (fnPointer != nullptr) {
|
||||
return (size_t)*fnPointer;
|
||||
}
|
||||
return (size_t)fnPointer;
|
||||
}
|
||||
|
||||
network_event_handle_t NetworkEvents::onEvent(NetworkEventCb cbEvent, arduino_event_id_t event) {
|
||||
if (!cbEvent) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined NETWORK_EVENTS_MUTEX && SOC_CPU_CORES_NUM > 1
|
||||
std::lock_guard<std::mutex> lock(_mtx);
|
||||
#endif // defined NETWORK_EVENTS_MUTEX && SOC_CPU_CORES_NUM > 1
|
||||
|
||||
_cbEventList.emplace_back(++_current_id, cbEvent, nullptr, nullptr, event);
|
||||
return _cbEventList.back().id;
|
||||
}
|
||||
|
||||
network_event_handle_t NetworkEvents::onEvent(NetworkEventFuncCb cbEvent, arduino_event_id_t event) {
|
||||
if (!cbEvent) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined NETWORK_EVENTS_MUTEX && SOC_CPU_CORES_NUM > 1
|
||||
std::lock_guard<std::mutex> lock(_mtx);
|
||||
#endif // defined NETWORK_EVENTS_MUTEX && SOC_CPU_CORES_NUM > 1
|
||||
|
||||
_cbEventList.emplace_back(++_current_id, nullptr, cbEvent, nullptr, event);
|
||||
return _cbEventList.back().id;
|
||||
}
|
||||
|
||||
network_event_handle_t NetworkEvents::onEvent(NetworkEventSysCb cbEvent, arduino_event_id_t event) {
|
||||
if (!cbEvent) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined NETWORK_EVENTS_MUTEX && SOC_CPU_CORES_NUM > 1
|
||||
std::lock_guard<std::mutex> lock(_mtx);
|
||||
#endif // defined NETWORK_EVENTS_MUTEX && SOC_CPU_CORES_NUM > 1
|
||||
|
||||
_cbEventList.emplace_back(++_current_id, nullptr, nullptr, cbEvent, event);
|
||||
return _cbEventList.back().id;
|
||||
}
|
||||
|
||||
network_event_handle_t NetworkEvents::onSysEvent(NetworkEventCb cbEvent, arduino_event_id_t event) {
|
||||
if (!cbEvent) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined NETWORK_EVENTS_MUTEX && SOC_CPU_CORES_NUM > 1
|
||||
std::lock_guard<std::mutex> lock(_mtx);
|
||||
#endif // defined NETWORK_EVENTS_MUTEX && SOC_CPU_CORES_NUM > 1
|
||||
|
||||
_cbEventList.emplace(_cbEventList.begin(), ++_current_id, cbEvent, nullptr, nullptr, event);
|
||||
return _cbEventList.front().id;
|
||||
}
|
||||
|
||||
network_event_handle_t NetworkEvents::onSysEvent(NetworkEventFuncCb cbEvent, arduino_event_id_t event) {
|
||||
if (!cbEvent) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined NETWORK_EVENTS_MUTEX && SOC_CPU_CORES_NUM > 1
|
||||
std::lock_guard<std::mutex> lock(_mtx);
|
||||
#endif // defined NETWORK_EVENTS_MUTEX && SOC_CPU_CORES_NUM > 1
|
||||
|
||||
_cbEventList.emplace(_cbEventList.begin(), ++_current_id, nullptr, cbEvent, nullptr, event);
|
||||
return _cbEventList.front().id;
|
||||
}
|
||||
|
||||
network_event_handle_t NetworkEvents::onSysEvent(NetworkEventSysCb cbEvent, arduino_event_id_t event) {
|
||||
if (!cbEvent) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined NETWORK_EVENTS_MUTEX && SOC_CPU_CORES_NUM > 1
|
||||
std::lock_guard<std::mutex> lock(_mtx);
|
||||
#endif // defined NETWORK_EVENTS_MUTEX && SOC_CPU_CORES_NUM > 1
|
||||
|
||||
_cbEventList.emplace(_cbEventList.begin(), ++_current_id, nullptr, nullptr, cbEvent, event);
|
||||
return _cbEventList.front().id;
|
||||
}
|
||||
|
||||
void NetworkEvents::removeEvent(NetworkEventCb cbEvent, arduino_event_id_t event) {
|
||||
if (!cbEvent) {
|
||||
return;
|
||||
}
|
||||
|
||||
#if defined NETWORK_EVENTS_MUTEX && SOC_CPU_CORES_NUM > 1
|
||||
std::lock_guard<std::mutex> lock(_mtx);
|
||||
#endif // defined NETWORK_EVENTS_MUTEX && SOC_CPU_CORES_NUM > 1
|
||||
|
||||
_cbEventList.erase(
|
||||
std::remove_if(
|
||||
_cbEventList.begin(), _cbEventList.end(),
|
||||
[cbEvent, event](const NetworkEventCbList_t &e) {
|
||||
return e.cb == cbEvent && e.event == event;
|
||||
}
|
||||
),
|
||||
_cbEventList.end()
|
||||
);
|
||||
}
|
||||
|
||||
void NetworkEvents::removeEvent(NetworkEventFuncCb cbEvent, arduino_event_id_t event) {
|
||||
if (!cbEvent) {
|
||||
return;
|
||||
}
|
||||
|
||||
#if defined NETWORK_EVENTS_MUTEX && SOC_CPU_CORES_NUM > 1
|
||||
std::lock_guard<std::mutex> lock(_mtx);
|
||||
#endif // defined NETWORK_EVENTS_MUTEX && SOC_CPU_CORES_NUM > 1
|
||||
|
||||
_cbEventList.erase(
|
||||
std::remove_if(
|
||||
_cbEventList.begin(), _cbEventList.end(),
|
||||
[cbEvent, event](const NetworkEventCbList_t &e) {
|
||||
return getStdFunctionAddress(e.fcb) == getStdFunctionAddress(cbEvent) && e.event == event;
|
||||
}
|
||||
),
|
||||
_cbEventList.end()
|
||||
);
|
||||
}
|
||||
|
||||
void NetworkEvents::removeEvent(NetworkEventSysCb cbEvent, arduino_event_id_t event) {
|
||||
if (!cbEvent) {
|
||||
return;
|
||||
}
|
||||
|
||||
#if defined NETWORK_EVENTS_MUTEX && SOC_CPU_CORES_NUM > 1
|
||||
std::lock_guard<std::mutex> lock(_mtx);
|
||||
#endif // defined NETWORK_EVENTS_MUTEX && SOC_CPU_CORES_NUM > 1
|
||||
|
||||
_cbEventList.erase(
|
||||
std::remove_if(
|
||||
_cbEventList.begin(), _cbEventList.end(),
|
||||
[cbEvent, event](const NetworkEventCbList_t &e) {
|
||||
return e.scb == cbEvent && e.event == event;
|
||||
}
|
||||
),
|
||||
_cbEventList.end()
|
||||
);
|
||||
}
|
||||
|
||||
void NetworkEvents::removeEvent(network_event_handle_t id) {
|
||||
#if defined NETWORK_EVENTS_MUTEX && SOC_CPU_CORES_NUM > 1
|
||||
std::lock_guard<std::mutex> lock(_mtx);
|
||||
#endif // defined NETWORK_EVENTS_MUTEX && SOC_CPU_CORES_NUM > 1
|
||||
|
||||
_cbEventList.erase(
|
||||
std::remove_if(
|
||||
_cbEventList.begin(), _cbEventList.end(),
|
||||
[id](const NetworkEventCbList_t &e) {
|
||||
return e.id == id;
|
||||
}
|
||||
),
|
||||
_cbEventList.end()
|
||||
);
|
||||
}
|
||||
|
||||
int NetworkEvents::setStatusBits(int bits) {
|
||||
if (!_arduino_event_group) {
|
||||
_initial_bits |= bits;
|
||||
return _initial_bits;
|
||||
}
|
||||
return xEventGroupSetBits(_arduino_event_group, bits);
|
||||
}
|
||||
|
||||
int NetworkEvents::clearStatusBits(int bits) {
|
||||
if (!_arduino_event_group) {
|
||||
_initial_bits &= ~bits;
|
||||
return _initial_bits;
|
||||
}
|
||||
return xEventGroupClearBits(_arduino_event_group, bits);
|
||||
}
|
||||
|
||||
int NetworkEvents::getStatusBits() const {
|
||||
if (!_arduino_event_group) {
|
||||
return _initial_bits;
|
||||
}
|
||||
return xEventGroupGetBits(_arduino_event_group);
|
||||
}
|
||||
|
||||
int NetworkEvents::waitStatusBits(int bits, uint32_t timeout_ms) {
|
||||
if (!_arduino_event_group) {
|
||||
return 0;
|
||||
}
|
||||
return xEventGroupWaitBits(
|
||||
_arduino_event_group, // The event group being tested.
|
||||
bits, // The bits within the event group to wait for.
|
||||
pdFALSE, // bits should be cleared before returning.
|
||||
pdTRUE, // Don't wait for all bits, any bit will do.
|
||||
timeout_ms / portTICK_PERIOD_MS
|
||||
)
|
||||
& bits; // Wait a maximum of timeout_ms for any bit to be set.
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Convert arduino_event_id_t to a C string.
|
||||
* @param [in] id The event id to be converted.
|
||||
* @return A string representation of the event id.
|
||||
* @note: arduino_event_id_t values as of Mar 2023 (arduino-esp32 r2.0.7) are: 0-39 (ARDUINO_EVENT_MAX=40) and are defined in WiFiGeneric.h.
|
||||
*/
|
||||
const char *NetworkEvents::eventName(arduino_event_id_t id) {
|
||||
switch (id) {
|
||||
case ARDUINO_EVENT_ETH_START: return "ETH_START";
|
||||
case ARDUINO_EVENT_ETH_STOP: return "ETH_STOP";
|
||||
case ARDUINO_EVENT_ETH_CONNECTED: return "ETH_CONNECTED";
|
||||
case ARDUINO_EVENT_ETH_DISCONNECTED: return "ETH_DISCONNECTED";
|
||||
case ARDUINO_EVENT_ETH_GOT_IP: return "ETH_GOT_IP";
|
||||
case ARDUINO_EVENT_ETH_LOST_IP: return "ETH_LOST_IP";
|
||||
case ARDUINO_EVENT_ETH_GOT_IP6: return "ETH_GOT_IP6";
|
||||
case ARDUINO_EVENT_PPP_START: return "PPP_START";
|
||||
case ARDUINO_EVENT_PPP_STOP: return "PPP_STOP";
|
||||
case ARDUINO_EVENT_PPP_CONNECTED: return "PPP_CONNECTED";
|
||||
case ARDUINO_EVENT_PPP_DISCONNECTED: return "PPP_DISCONNECTED";
|
||||
case ARDUINO_EVENT_PPP_GOT_IP: return "PPP_GOT_IP";
|
||||
case ARDUINO_EVENT_PPP_LOST_IP: return "PPP_LOST_IP";
|
||||
case ARDUINO_EVENT_PPP_GOT_IP6: return "PPP_GOT_IP6";
|
||||
#if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED
|
||||
case ARDUINO_EVENT_WIFI_OFF: return "WIFI_OFF";
|
||||
case ARDUINO_EVENT_WIFI_READY: return "WIFI_READY";
|
||||
case ARDUINO_EVENT_WIFI_SCAN_DONE: return "SCAN_DONE";
|
||||
case ARDUINO_EVENT_WIFI_STA_START: return "STA_START";
|
||||
case ARDUINO_EVENT_WIFI_STA_STOP: return "STA_STOP";
|
||||
case ARDUINO_EVENT_WIFI_STA_CONNECTED: return "STA_CONNECTED";
|
||||
case ARDUINO_EVENT_WIFI_STA_DISCONNECTED: return "STA_DISCONNECTED";
|
||||
case ARDUINO_EVENT_WIFI_STA_AUTHMODE_CHANGE: return "STA_AUTHMODE_CHANGE";
|
||||
case ARDUINO_EVENT_WIFI_STA_GOT_IP: return "STA_GOT_IP";
|
||||
case ARDUINO_EVENT_WIFI_STA_GOT_IP6: return "STA_GOT_IP6";
|
||||
case ARDUINO_EVENT_WIFI_STA_LOST_IP: return "STA_LOST_IP";
|
||||
case ARDUINO_EVENT_WIFI_AP_START: return "AP_START";
|
||||
case ARDUINO_EVENT_WIFI_AP_STOP: return "AP_STOP";
|
||||
case ARDUINO_EVENT_WIFI_AP_STACONNECTED: return "AP_STACONNECTED";
|
||||
case ARDUINO_EVENT_WIFI_AP_STADISCONNECTED: return "AP_STADISCONNECTED";
|
||||
case ARDUINO_EVENT_WIFI_AP_STAIPASSIGNED: return "AP_STAIPASSIGNED";
|
||||
case ARDUINO_EVENT_WIFI_AP_PROBEREQRECVED: return "AP_PROBEREQRECVED";
|
||||
case ARDUINO_EVENT_WIFI_AP_GOT_IP6: return "AP_GOT_IP6";
|
||||
case ARDUINO_EVENT_WIFI_FTM_REPORT: return "FTM_REPORT";
|
||||
case ARDUINO_EVENT_WPS_ER_SUCCESS: return "WPS_ER_SUCCESS";
|
||||
case ARDUINO_EVENT_WPS_ER_FAILED: return "WPS_ER_FAILED";
|
||||
case ARDUINO_EVENT_WPS_ER_TIMEOUT: return "WPS_ER_TIMEOUT";
|
||||
case ARDUINO_EVENT_WPS_ER_PIN: return "WPS_ER_PIN";
|
||||
case ARDUINO_EVENT_WPS_ER_PBC_OVERLAP: return "WPS_ER_PBC_OVERLAP";
|
||||
case ARDUINO_EVENT_SC_SCAN_DONE: return "SC_SCAN_DONE";
|
||||
case ARDUINO_EVENT_SC_FOUND_CHANNEL: return "SC_FOUND_CHANNEL";
|
||||
case ARDUINO_EVENT_SC_GOT_SSID_PSWD: return "SC_GOT_SSID_PSWD";
|
||||
case ARDUINO_EVENT_SC_SEND_ACK_DONE: return "SC_SEND_ACK_DONE";
|
||||
case ARDUINO_EVENT_PROV_INIT: return "PROV_INIT";
|
||||
case ARDUINO_EVENT_PROV_DEINIT: return "PROV_DEINIT";
|
||||
case ARDUINO_EVENT_PROV_START: return "PROV_START";
|
||||
case ARDUINO_EVENT_PROV_END: return "PROV_END";
|
||||
case ARDUINO_EVENT_PROV_CRED_RECV: return "PROV_CRED_RECV";
|
||||
case ARDUINO_EVENT_PROV_CRED_FAIL: return "PROV_CRED_FAIL";
|
||||
case ARDUINO_EVENT_PROV_CRED_SUCCESS: return "PROV_CRED_SUCCESS";
|
||||
#endif
|
||||
default: return "";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,312 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "sdkconfig.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_event.h"
|
||||
#include "esp_netif_types.h"
|
||||
#if CONFIG_ETH_ENABLED
|
||||
#include "esp_eth_driver.h"
|
||||
#endif
|
||||
#include <functional>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "freertos/event_groups.h"
|
||||
#if defined NETWORK_EVENTS_MUTEX && SOC_CPU_CORES_NUM > 1
|
||||
#include <mutex>
|
||||
#endif // defined NETWORK_EVENTS_MUTEX && SOC_CPU_CORES_NUM > 1
|
||||
|
||||
#if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED
|
||||
#include "esp_wifi_types.h"
|
||||
#include "esp_smartconfig.h"
|
||||
#if CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI
|
||||
#include "network_provisioning/network_config.h"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED
|
||||
constexpr int WIFI_SCANNING_BIT = BIT0;
|
||||
constexpr int WIFI_SCAN_DONE_BIT = BIT1;
|
||||
#endif
|
||||
|
||||
#define NET_HAS_IP6_GLOBAL_BIT 0
|
||||
|
||||
ESP_EVENT_DECLARE_BASE(ARDUINO_EVENTS);
|
||||
|
||||
typedef enum {
|
||||
ARDUINO_EVENT_NONE = 0,
|
||||
ARDUINO_EVENT_ETH_START,
|
||||
ARDUINO_EVENT_ETH_STOP,
|
||||
ARDUINO_EVENT_ETH_CONNECTED,
|
||||
ARDUINO_EVENT_ETH_DISCONNECTED,
|
||||
ARDUINO_EVENT_ETH_GOT_IP,
|
||||
ARDUINO_EVENT_ETH_LOST_IP,
|
||||
ARDUINO_EVENT_ETH_GOT_IP6,
|
||||
#if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED
|
||||
ARDUINO_EVENT_WIFI_OFF = 100,
|
||||
ARDUINO_EVENT_WIFI_READY,
|
||||
ARDUINO_EVENT_WIFI_SCAN_DONE,
|
||||
ARDUINO_EVENT_WIFI_FTM_REPORT,
|
||||
ARDUINO_EVENT_WIFI_STA_START = 110,
|
||||
ARDUINO_EVENT_WIFI_STA_STOP,
|
||||
ARDUINO_EVENT_WIFI_STA_CONNECTED,
|
||||
ARDUINO_EVENT_WIFI_STA_DISCONNECTED,
|
||||
ARDUINO_EVENT_WIFI_STA_AUTHMODE_CHANGE,
|
||||
ARDUINO_EVENT_WIFI_STA_GOT_IP,
|
||||
ARDUINO_EVENT_WIFI_STA_GOT_IP6,
|
||||
ARDUINO_EVENT_WIFI_STA_LOST_IP,
|
||||
ARDUINO_EVENT_WIFI_AP_START = 130,
|
||||
ARDUINO_EVENT_WIFI_AP_STOP,
|
||||
ARDUINO_EVENT_WIFI_AP_STACONNECTED,
|
||||
ARDUINO_EVENT_WIFI_AP_STADISCONNECTED,
|
||||
ARDUINO_EVENT_WIFI_AP_STAIPASSIGNED,
|
||||
ARDUINO_EVENT_WIFI_AP_PROBEREQRECVED,
|
||||
ARDUINO_EVENT_WIFI_AP_GOT_IP6,
|
||||
ARDUINO_EVENT_WPS_ER_SUCCESS = 140,
|
||||
ARDUINO_EVENT_WPS_ER_FAILED,
|
||||
ARDUINO_EVENT_WPS_ER_TIMEOUT,
|
||||
ARDUINO_EVENT_WPS_ER_PIN,
|
||||
ARDUINO_EVENT_WPS_ER_PBC_OVERLAP,
|
||||
ARDUINO_EVENT_SC_SCAN_DONE = 150,
|
||||
ARDUINO_EVENT_SC_FOUND_CHANNEL,
|
||||
ARDUINO_EVENT_SC_GOT_SSID_PSWD,
|
||||
ARDUINO_EVENT_SC_SEND_ACK_DONE,
|
||||
ARDUINO_EVENT_PROV_INIT = 160,
|
||||
ARDUINO_EVENT_PROV_DEINIT,
|
||||
ARDUINO_EVENT_PROV_START,
|
||||
ARDUINO_EVENT_PROV_END,
|
||||
ARDUINO_EVENT_PROV_CRED_RECV,
|
||||
ARDUINO_EVENT_PROV_CRED_FAIL,
|
||||
ARDUINO_EVENT_PROV_CRED_SUCCESS,
|
||||
#endif
|
||||
ARDUINO_EVENT_PPP_START = 200,
|
||||
ARDUINO_EVENT_PPP_STOP,
|
||||
ARDUINO_EVENT_PPP_CONNECTED,
|
||||
ARDUINO_EVENT_PPP_DISCONNECTED,
|
||||
ARDUINO_EVENT_PPP_GOT_IP,
|
||||
ARDUINO_EVENT_PPP_LOST_IP,
|
||||
ARDUINO_EVENT_PPP_GOT_IP6,
|
||||
ARDUINO_EVENT_MAX
|
||||
} arduino_event_id_t;
|
||||
|
||||
typedef union {
|
||||
ip_event_ap_staipassigned_t wifi_ap_staipassigned;
|
||||
ip_event_got_ip_t got_ip;
|
||||
ip_event_got_ip_t lost_ip;
|
||||
ip_event_got_ip6_t got_ip6;
|
||||
#if CONFIG_ETH_ENABLED
|
||||
esp_eth_handle_t eth_started;
|
||||
esp_eth_handle_t eth_stopped;
|
||||
esp_eth_handle_t eth_connected;
|
||||
esp_eth_handle_t eth_disconnected;
|
||||
#endif
|
||||
#if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED
|
||||
wifi_event_sta_scan_done_t wifi_scan_done;
|
||||
wifi_event_sta_authmode_change_t wifi_sta_authmode_change;
|
||||
wifi_event_sta_connected_t wifi_sta_connected;
|
||||
wifi_event_sta_disconnected_t wifi_sta_disconnected;
|
||||
wifi_event_sta_wps_er_pin_t wps_er_pin;
|
||||
wifi_event_sta_wps_fail_reason_t wps_fail_reason;
|
||||
wifi_event_ap_probe_req_rx_t wifi_ap_probereqrecved;
|
||||
wifi_event_ap_staconnected_t wifi_ap_staconnected;
|
||||
wifi_event_ap_stadisconnected_t wifi_ap_stadisconnected;
|
||||
wifi_event_ftm_report_t wifi_ftm_report;
|
||||
#endif
|
||||
#if SOC_WIFI_SUPPORTED
|
||||
wifi_sta_config_t prov_cred_recv;
|
||||
#if CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI
|
||||
network_prov_wifi_sta_fail_reason_t prov_fail_reason;
|
||||
#endif
|
||||
smartconfig_event_got_ssid_pswd_t sc_got_ssid_pswd;
|
||||
#endif
|
||||
} arduino_event_info_t;
|
||||
|
||||
/**
|
||||
* @brief struct combines arduino event id and event's data object
|
||||
*
|
||||
*/
|
||||
struct arduino_event_t {
|
||||
arduino_event_id_t event_id;
|
||||
arduino_event_info_t event_info;
|
||||
};
|
||||
|
||||
// type aliases
|
||||
using NetworkEventCb = void (*)(arduino_event_id_t event);
|
||||
using NetworkEventFuncCb = std::function<void(arduino_event_id_t event, arduino_event_info_t info)>;
|
||||
using NetworkEventSysCb = void (*)(arduino_event_t *event);
|
||||
using network_event_handle_t = size_t;
|
||||
|
||||
/**
|
||||
* @brief Class that provides network events callback handling
|
||||
* it registers user callback functions for event handling,
|
||||
* maintains the queue of events and propagates the event among subscribed callbacks
|
||||
* callback are called in the context of a dedicated task consuming the queue
|
||||
*
|
||||
*/
|
||||
class NetworkEvents {
|
||||
public:
|
||||
NetworkEvents();
|
||||
~NetworkEvents();
|
||||
|
||||
/**
|
||||
* @brief register callback function to be executed on arduino event(s)
|
||||
* @note if same handler is registered twice or more than same handler would be called twice or more times
|
||||
*
|
||||
* @param cbEvent static callback function
|
||||
* @param event event to process, any event by default
|
||||
* @return network_event_handle_t
|
||||
*/
|
||||
network_event_handle_t onEvent(NetworkEventCb cbEvent, arduino_event_id_t event = ARDUINO_EVENT_MAX);
|
||||
|
||||
/**
|
||||
* @brief register functional callback to be executed on arduino event(s)
|
||||
* also used for lambda callbacks
|
||||
* @note if same handler is registered twice or more than same handler would be called twice or more times
|
||||
*
|
||||
* @param cbEvent static callback function
|
||||
* @param event event to process, any event by default
|
||||
* @return network_event_handle_t
|
||||
*/
|
||||
network_event_handle_t onEvent(NetworkEventFuncCb cbEvent, arduino_event_id_t event = ARDUINO_EVENT_MAX);
|
||||
|
||||
/**
|
||||
* @brief register static system callback to be executed on arduino event(s)
|
||||
* callback function would be supplied with a pointer to arduino_event_t structure as an argument
|
||||
*
|
||||
* @note if same handler is registered twice or more than same handler would be called twice or more times
|
||||
*
|
||||
* @param cbEvent static callback function
|
||||
* @param event event to process, any event by default
|
||||
* @return network_event_handle_t
|
||||
*/
|
||||
network_event_handle_t onEvent(NetworkEventSysCb cbEvent, arduino_event_id_t event = ARDUINO_EVENT_MAX);
|
||||
|
||||
/**
|
||||
* @brief unregister static function callback
|
||||
* @note a better way to unregister callbacks is to save/unregister via network_event_handle_t
|
||||
*
|
||||
* @param cbEvent static callback function
|
||||
* @param event event to process, any event by default
|
||||
*/
|
||||
void removeEvent(NetworkEventCb cbEvent, arduino_event_id_t event = ARDUINO_EVENT_MAX);
|
||||
|
||||
/**
|
||||
* @brief unregister functional callback
|
||||
* @note a better way to unregister callbacks is to save/unregister via network_event_handle_t
|
||||
* @note this does not work for lambda's! Do unregister via network_event_handle_t
|
||||
*
|
||||
* @param cbEvent functional callback
|
||||
* @param event event to process, any event by default
|
||||
*/
|
||||
void removeEvent(NetworkEventFuncCb cbEvent, arduino_event_id_t event = ARDUINO_EVENT_MAX)
|
||||
__attribute__((deprecated("removing functional callbacks via pointer is deprecated, use removeEvent(network_event_handle_t) instead")));
|
||||
|
||||
/**
|
||||
* @brief unregister static system function callback
|
||||
* @note a better way to unregister callbacks is to save/unregister via network_event_handle_t
|
||||
*
|
||||
* @param cbEvent static callback function
|
||||
* @param event event to process, any event by default
|
||||
*/
|
||||
void removeEvent(NetworkEventSysCb cbEvent, arduino_event_id_t event = ARDUINO_EVENT_MAX);
|
||||
|
||||
/**
|
||||
* @brief unregister event callback via handler
|
||||
*
|
||||
* @param cbEvent static callback function
|
||||
* @param event event to process, any event by default
|
||||
*/
|
||||
void removeEvent(network_event_handle_t event_handle);
|
||||
|
||||
/**
|
||||
* @brief get a human-readable name of an event by it's id
|
||||
*
|
||||
* @param id event id code
|
||||
* @return const char* event name string
|
||||
*/
|
||||
static const char *eventName(arduino_event_id_t id);
|
||||
|
||||
/**
|
||||
* @brief post an event to the queue
|
||||
* and propagade and event to subscribed handlers
|
||||
* @note posting an event will trigger context switch from a lower priority task
|
||||
*
|
||||
* @param event a pointer to arduino_event_t struct
|
||||
* @return true if event was queued susccessfuly
|
||||
* @return false on memrory allocation error or queue is full
|
||||
*/
|
||||
bool postEvent(const arduino_event_t *event);
|
||||
|
||||
int getStatusBits() const;
|
||||
int waitStatusBits(int bits, uint32_t timeout_ms);
|
||||
int setStatusBits(int bits);
|
||||
int clearStatusBits(int bits);
|
||||
|
||||
friend class ESP_NetworkInterface;
|
||||
friend class ETHClass;
|
||||
friend class PPPClass;
|
||||
#if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED
|
||||
friend class STAClass;
|
||||
friend class APClass;
|
||||
friend class WiFiGenericClass;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool initNetworkEvents();
|
||||
// same as onEvent() but places newly added handler at the beginning of registered events list
|
||||
network_event_handle_t onSysEvent(NetworkEventCb cbEvent, arduino_event_id_t event = ARDUINO_EVENT_MAX);
|
||||
// same as onEvent() but places newly added handler at the beginning of registered events list
|
||||
network_event_handle_t onSysEvent(NetworkEventFuncCb cbEvent, arduino_event_id_t event = ARDUINO_EVENT_MAX);
|
||||
// same as onEvent() but places newly added handler at the beginning of registered events list
|
||||
network_event_handle_t onSysEvent(NetworkEventSysCb cbEvent, arduino_event_id_t event = ARDUINO_EVENT_MAX);
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief an object holds callback's definitions:
|
||||
* - callback id
|
||||
* - callback function pointers
|
||||
* - binded event id
|
||||
*
|
||||
*/
|
||||
struct NetworkEventCbList_t {
|
||||
network_event_handle_t id;
|
||||
NetworkEventCb cb;
|
||||
NetworkEventFuncCb fcb;
|
||||
NetworkEventSysCb scb;
|
||||
arduino_event_id_t event;
|
||||
|
||||
explicit NetworkEventCbList_t(
|
||||
network_event_handle_t id, NetworkEventCb cb = nullptr, NetworkEventFuncCb fcb = nullptr, NetworkEventSysCb scb = nullptr,
|
||||
arduino_event_id_t event = ARDUINO_EVENT_MAX
|
||||
)
|
||||
: id(id), cb(cb), fcb(fcb), scb(scb), event(event) {}
|
||||
};
|
||||
|
||||
// define initial id's value
|
||||
network_event_handle_t _current_id{0};
|
||||
|
||||
EventGroupHandle_t _arduino_event_group;
|
||||
QueueHandle_t _arduino_event_queue;
|
||||
TaskHandle_t _arduino_event_task_handle;
|
||||
|
||||
// registered events callbacks container
|
||||
std::vector<NetworkEventCbList_t> _cbEventList;
|
||||
|
||||
#if defined NETWORK_EVENTS_MUTEX && SOC_CPU_CORES_NUM > 1
|
||||
// container access mutex
|
||||
std::mutex _mtx;
|
||||
#endif // defined NETWORK_EVENTS_MUTEX && SOC_CPU_CORES_NUM > 1
|
||||
|
||||
/**
|
||||
* @brief task function that picks events from an event queue and calls registered callbacks
|
||||
*
|
||||
*/
|
||||
void _checkForEvent();
|
||||
};
|
||||
@@ -0,0 +1,886 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include "NetworkInterface.h"
|
||||
#include "NetworkManager.h"
|
||||
#include "esp_netif.h"
|
||||
#include "esp_netif_defaults.h"
|
||||
#include "esp_system.h"
|
||||
#include "lwip/ip_addr.h"
|
||||
#include "lwip/err.h"
|
||||
#include "lwip/netif.h"
|
||||
#include "dhcpserver/dhcpserver.h"
|
||||
#include "dhcpserver/dhcpserver_options.h"
|
||||
#include "esp32-hal-log.h"
|
||||
|
||||
static NetworkInterface *_interfaces[ESP_NETIF_ID_MAX] = {NULL, NULL, NULL, NULL, NULL, NULL};
|
||||
static esp_event_handler_instance_t _ip_ev_instance = NULL;
|
||||
|
||||
static NetworkInterface *getNetifByEspNetif(esp_netif_t *esp_netif) {
|
||||
for (int i = 0; i < ESP_NETIF_ID_MAX; ++i) {
|
||||
if (_interfaces[i] != NULL && _interfaces[i]->netif() == esp_netif) {
|
||||
return _interfaces[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
NetworkInterface *getNetifByID(Network_Interface_ID id) {
|
||||
if (id < ESP_NETIF_ID_MAX) {
|
||||
return _interfaces[id];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#if CONFIG_LWIP_HOOK_IP6_INPUT_CUSTOM
|
||||
extern "C" int lwip_hook_ip6_input(struct pbuf *p, struct netif *inp) __attribute__((weak));
|
||||
extern "C" int lwip_hook_ip6_input(struct pbuf *p, struct netif *inp) {
|
||||
if (ip6_addr_isany_val(inp->ip6_addr[0].u_addr.ip6)) {
|
||||
// We don't have an LL address -> eat this packet here, so it won't get accepted on input netif
|
||||
pbuf_free(p);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void _ip_event_cb(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) {
|
||||
(void)arg;
|
||||
if (event_base == IP_EVENT) {
|
||||
NetworkInterface *netif = NULL;
|
||||
if (event_id == IP_EVENT_STA_GOT_IP || event_id == IP_EVENT_ETH_GOT_IP || event_id == IP_EVENT_PPP_GOT_IP) {
|
||||
ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data;
|
||||
netif = getNetifByEspNetif(event->esp_netif);
|
||||
} else if (event_id == IP_EVENT_STA_LOST_IP || event_id == IP_EVENT_PPP_LOST_IP || event_id == IP_EVENT_ETH_LOST_IP) {
|
||||
ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data;
|
||||
netif = getNetifByEspNetif(event->esp_netif);
|
||||
} else if (event_id == IP_EVENT_GOT_IP6) {
|
||||
ip_event_got_ip6_t *event = (ip_event_got_ip6_t *)event_data;
|
||||
netif = getNetifByEspNetif(event->esp_netif);
|
||||
} else if (event_id == IP_EVENT_AP_STAIPASSIGNED) {
|
||||
ip_event_ap_staipassigned_t *event = (ip_event_ap_staipassigned_t *)event_data;
|
||||
netif = getNetifByEspNetif(event->esp_netif);
|
||||
}
|
||||
if (netif != NULL) {
|
||||
netif->_onIpEvent(event_id, event_data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkInterface::_onIpEvent(int32_t event_id, void *event_data) {
|
||||
arduino_event_t arduino_event;
|
||||
arduino_event.event_id = ARDUINO_EVENT_MAX;
|
||||
if (event_id == _got_ip_event_id) {
|
||||
setStatusBits(ESP_NETIF_HAS_IP_BIT);
|
||||
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_VERBOSE
|
||||
ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data;
|
||||
log_v(
|
||||
"%s Got %sIP: " IPSTR " MASK: " IPSTR " GW: " IPSTR, desc(), event->ip_changed ? "New " : "Same ", IP2STR(&event->ip_info.ip),
|
||||
IP2STR(&event->ip_info.netmask), IP2STR(&event->ip_info.gw)
|
||||
);
|
||||
#endif
|
||||
memcpy(&arduino_event.event_info.got_ip, event_data, sizeof(ip_event_got_ip_t));
|
||||
#if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED
|
||||
if (_interface_id == ESP_NETIF_ID_STA) {
|
||||
arduino_event.event_id = ARDUINO_EVENT_WIFI_STA_GOT_IP;
|
||||
} else
|
||||
#endif
|
||||
if (_interface_id == ESP_NETIF_ID_PPP) {
|
||||
arduino_event.event_id = ARDUINO_EVENT_PPP_GOT_IP;
|
||||
} else if (_interface_id >= ESP_NETIF_ID_ETH && _interface_id < ESP_NETIF_ID_MAX) {
|
||||
arduino_event.event_id = ARDUINO_EVENT_ETH_GOT_IP;
|
||||
}
|
||||
} else if (event_id == _lost_ip_event_id) {
|
||||
clearStatusBits(ESP_NETIF_HAS_IP_BIT);
|
||||
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_VERBOSE
|
||||
log_v("%s Lost IP", desc());
|
||||
#endif
|
||||
memcpy(&arduino_event.event_info.lost_ip, event_data, sizeof(ip_event_got_ip_t));
|
||||
#if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED
|
||||
if (_interface_id == ESP_NETIF_ID_STA) {
|
||||
arduino_event.event_id = ARDUINO_EVENT_WIFI_STA_LOST_IP;
|
||||
} else
|
||||
#endif
|
||||
if (_interface_id == ESP_NETIF_ID_PPP) {
|
||||
arduino_event.event_id = ARDUINO_EVENT_PPP_LOST_IP;
|
||||
} else if (_interface_id >= ESP_NETIF_ID_ETH && _interface_id < ESP_NETIF_ID_MAX) {
|
||||
arduino_event.event_id = ARDUINO_EVENT_ETH_LOST_IP;
|
||||
}
|
||||
#if CONFIG_LWIP_IPV6
|
||||
} else if (event_id == IP_EVENT_GOT_IP6) {
|
||||
ip_event_got_ip6_t *event = (ip_event_got_ip6_t *)event_data;
|
||||
esp_ip6_addr_type_t addr_type = esp_netif_ip6_get_addr_type(&event->ip6_info.ip);
|
||||
if (addr_type == ESP_IP6_ADDR_IS_GLOBAL) {
|
||||
setStatusBits(ESP_NETIF_HAS_GLOBAL_IP6_BIT);
|
||||
} else if (addr_type == ESP_IP6_ADDR_IS_LINK_LOCAL) {
|
||||
setStatusBits(ESP_NETIF_HAS_LOCAL_IP6_BIT);
|
||||
}
|
||||
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_VERBOSE
|
||||
static const char *addr_types[] = {"UNKNOWN", "GLOBAL", "LINK_LOCAL", "SITE_LOCAL", "UNIQUE_LOCAL", "IPV4_MAPPED_IPV6"};
|
||||
log_v(
|
||||
"IF %s Got IPv6: Interface: %d, IP Index: %d, Type: %s, Zone: %d, Address: " IPV6STR, desc(), _interface_id, event->ip_index, addr_types[addr_type],
|
||||
event->ip6_info.ip.zone, IPV62STR(event->ip6_info.ip)
|
||||
);
|
||||
#endif
|
||||
memcpy(&arduino_event.event_info.got_ip6, event_data, sizeof(ip_event_got_ip6_t));
|
||||
#if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED
|
||||
if (_interface_id == ESP_NETIF_ID_STA) {
|
||||
arduino_event.event_id = ARDUINO_EVENT_WIFI_STA_GOT_IP6;
|
||||
} else if (_interface_id == ESP_NETIF_ID_AP) {
|
||||
arduino_event.event_id = ARDUINO_EVENT_WIFI_AP_GOT_IP6;
|
||||
} else
|
||||
#endif
|
||||
if (_interface_id == ESP_NETIF_ID_PPP) {
|
||||
arduino_event.event_id = ARDUINO_EVENT_PPP_GOT_IP6;
|
||||
} else if (_interface_id >= ESP_NETIF_ID_ETH && _interface_id < ESP_NETIF_ID_MAX) {
|
||||
arduino_event.event_id = ARDUINO_EVENT_ETH_GOT_IP6;
|
||||
}
|
||||
#endif /* CONFIG_LWIP_IPV6 */
|
||||
#if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED
|
||||
} else if (event_id == IP_EVENT_AP_STAIPASSIGNED && _interface_id == ESP_NETIF_ID_AP) {
|
||||
setStatusBits(ESP_NETIF_HAS_IP_BIT);
|
||||
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_VERBOSE
|
||||
ip_event_ap_staipassigned_t *event = (ip_event_ap_staipassigned_t *)event_data;
|
||||
log_v(
|
||||
"%s Assigned IP: " IPSTR " to MAC: %02X:%02X:%02X:%02X:%02X:%02X", desc(), IP2STR(&event->ip), event->mac[0], event->mac[1], event->mac[2], event->mac[3],
|
||||
event->mac[4], event->mac[5]
|
||||
);
|
||||
#endif
|
||||
arduino_event.event_id = ARDUINO_EVENT_WIFI_AP_STAIPASSIGNED;
|
||||
memcpy(&arduino_event.event_info.wifi_ap_staipassigned, event_data, sizeof(ip_event_ap_staipassigned_t));
|
||||
#endif
|
||||
}
|
||||
|
||||
if (arduino_event.event_id < ARDUINO_EVENT_MAX) {
|
||||
Network.postEvent(&arduino_event);
|
||||
}
|
||||
}
|
||||
|
||||
NetworkInterface::NetworkInterface()
|
||||
: _esp_netif(NULL), _interface_event_group(NULL), _initial_bits(0), _got_ip_event_id(-1), _lost_ip_event_id(-1), _interface_id(ESP_NETIF_ID_MAX) {}
|
||||
|
||||
NetworkInterface::~NetworkInterface() {
|
||||
destroyNetif();
|
||||
}
|
||||
|
||||
IPAddress NetworkInterface::calculateNetworkID(IPAddress ip, IPAddress subnet) const {
|
||||
IPAddress networkID;
|
||||
|
||||
for (size_t i = 0; i < 4; i++) {
|
||||
networkID[i] = subnet[i] & ip[i];
|
||||
}
|
||||
|
||||
return networkID;
|
||||
}
|
||||
|
||||
IPAddress NetworkInterface::calculateBroadcast(IPAddress ip, IPAddress subnet) const {
|
||||
IPAddress broadcastIp;
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
broadcastIp[i] = ~subnet[i] | ip[i];
|
||||
}
|
||||
|
||||
return broadcastIp;
|
||||
}
|
||||
|
||||
uint8_t NetworkInterface::calculateSubnetCIDR(IPAddress subnetMask) const {
|
||||
uint8_t CIDR = 0;
|
||||
|
||||
for (uint8_t i = 0; i < 4; i++) {
|
||||
if (subnetMask[i] == 0x80) { // 128
|
||||
CIDR += 1;
|
||||
} else if (subnetMask[i] == 0xC0) { // 192
|
||||
CIDR += 2;
|
||||
} else if (subnetMask[i] == 0xE0) { // 224
|
||||
CIDR += 3;
|
||||
} else if (subnetMask[i] == 0xF0) { // 242
|
||||
CIDR += 4;
|
||||
} else if (subnetMask[i] == 0xF8) { // 248
|
||||
CIDR += 5;
|
||||
} else if (subnetMask[i] == 0xFC) { // 252
|
||||
CIDR += 6;
|
||||
} else if (subnetMask[i] == 0xFE) { // 254
|
||||
CIDR += 7;
|
||||
} else if (subnetMask[i] == 0xFF) { // 255
|
||||
CIDR += 8;
|
||||
}
|
||||
}
|
||||
|
||||
return CIDR;
|
||||
}
|
||||
|
||||
int NetworkInterface::setStatusBits(int bits) {
|
||||
if (!_interface_event_group) {
|
||||
_initial_bits |= bits;
|
||||
return _initial_bits;
|
||||
}
|
||||
return xEventGroupSetBits(_interface_event_group, bits);
|
||||
}
|
||||
|
||||
int NetworkInterface::clearStatusBits(int bits) {
|
||||
if (!_interface_event_group) {
|
||||
_initial_bits &= ~bits;
|
||||
return _initial_bits;
|
||||
}
|
||||
return xEventGroupClearBits(_interface_event_group, bits);
|
||||
}
|
||||
|
||||
int NetworkInterface::getStatusBits() const {
|
||||
if (!_interface_event_group) {
|
||||
return _initial_bits;
|
||||
}
|
||||
return xEventGroupGetBits(_interface_event_group);
|
||||
}
|
||||
|
||||
int NetworkInterface::waitStatusBits(int bits, uint32_t timeout_ms) const {
|
||||
if (!_interface_event_group) {
|
||||
return 0;
|
||||
}
|
||||
bits = xEventGroupWaitBits(
|
||||
_interface_event_group, // The event group being tested.
|
||||
bits, // The bits within the event group to wait for.
|
||||
pdFALSE, // bits should be cleared before returning.
|
||||
pdTRUE, // Don't wait for all bits, any bit will do.
|
||||
timeout_ms / portTICK_PERIOD_MS
|
||||
)
|
||||
& bits; // Wait a maximum of timeout_ms for any bit to be set.
|
||||
return bits;
|
||||
}
|
||||
|
||||
void NetworkInterface::destroyNetif() {
|
||||
if (_ip_ev_instance != NULL) {
|
||||
bool do_not_unreg_ev_handler = false;
|
||||
for (int i = 0; i < ESP_NETIF_ID_MAX; ++i) {
|
||||
if (_interfaces[i] != NULL && _interfaces[i] != this) {
|
||||
do_not_unreg_ev_handler = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!do_not_unreg_ev_handler) {
|
||||
if (esp_event_handler_unregister(IP_EVENT, ESP_EVENT_ANY_ID, &_ip_event_cb) == ESP_OK) {
|
||||
_ip_ev_instance = NULL;
|
||||
log_v("Unregistered event handler");
|
||||
} else {
|
||||
log_e("Failed to unregister event handler");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (_esp_netif != NULL) {
|
||||
esp_netif_destroy(_esp_netif);
|
||||
_esp_netif = NULL;
|
||||
}
|
||||
if (_interface_event_group != NULL) {
|
||||
vEventGroupDelete(_interface_event_group);
|
||||
_interface_event_group = NULL;
|
||||
_initial_bits = 0;
|
||||
}
|
||||
for (int i = 0; i < ESP_NETIF_ID_MAX; ++i) {
|
||||
if (_interfaces[i] != NULL && _interfaces[i] == this) {
|
||||
_interfaces[i] = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool NetworkInterface::initNetif(Network_Interface_ID interface_id) {
|
||||
if (_esp_netif == NULL || interface_id >= ESP_NETIF_ID_MAX) {
|
||||
return false;
|
||||
}
|
||||
_interface_id = interface_id;
|
||||
_got_ip_event_id = esp_netif_get_event_id(_esp_netif, ESP_NETIF_IP_EVENT_GOT_IP);
|
||||
_lost_ip_event_id = esp_netif_get_event_id(_esp_netif, ESP_NETIF_IP_EVENT_LOST_IP);
|
||||
_interfaces[_interface_id] = this;
|
||||
|
||||
if (_interface_event_group == NULL) {
|
||||
_interface_event_group = xEventGroupCreate();
|
||||
if (!_interface_event_group) {
|
||||
log_e("Interface Event Group Create Failed!");
|
||||
return false;
|
||||
}
|
||||
setStatusBits(_initial_bits);
|
||||
}
|
||||
|
||||
if (_ip_ev_instance == NULL && esp_event_handler_instance_register(IP_EVENT, ESP_EVENT_ANY_ID, &_ip_event_cb, NULL, &_ip_ev_instance)) {
|
||||
log_e("event_handler_instance_register for IP_EVENT Failed!");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NetworkInterface::started() const {
|
||||
return (getStatusBits() & ESP_NETIF_STARTED_BIT) != 0;
|
||||
}
|
||||
|
||||
bool NetworkInterface::connected() const {
|
||||
return (getStatusBits() & ESP_NETIF_CONNECTED_BIT) != 0;
|
||||
}
|
||||
|
||||
bool NetworkInterface::hasIP() const {
|
||||
return (getStatusBits() & (ESP_NETIF_HAS_IP_BIT | ESP_NETIF_HAS_STATIC_IP_BIT)) != 0;
|
||||
}
|
||||
|
||||
bool NetworkInterface::hasLinkLocalIPv6() const {
|
||||
return (getStatusBits() & ESP_NETIF_HAS_LOCAL_IP6_BIT) != 0;
|
||||
}
|
||||
|
||||
bool NetworkInterface::hasGlobalIPv6() const {
|
||||
return (getStatusBits() & ESP_NETIF_HAS_GLOBAL_IP6_BIT) != 0;
|
||||
}
|
||||
|
||||
bool NetworkInterface::enableIPv6(bool en) {
|
||||
#if CONFIG_LWIP_IPV6
|
||||
if (en) {
|
||||
setStatusBits(ESP_NETIF_WANT_IP6_BIT);
|
||||
if (_esp_netif != NULL && connected()) {
|
||||
// If we are already connected, try to enable IPv6 immediately
|
||||
esp_err_t err = esp_netif_create_ip6_linklocal(_esp_netif);
|
||||
if (err != ESP_OK) {
|
||||
log_e("Failed to enable IPv6 Link Local on %s: [%d] %s", desc(), err, esp_err_to_name(err));
|
||||
} else {
|
||||
log_v("Enabled IPv6 Link Local on %s", desc());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
clearStatusBits(ESP_NETIF_WANT_IP6_BIT);
|
||||
}
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool NetworkInterface::dnsIP(uint8_t dns_no, IPAddress ip) {
|
||||
if (_esp_netif == NULL || dns_no >= ESP_NETIF_DNS_MAX) {
|
||||
return false;
|
||||
}
|
||||
esp_netif_flags_t flags = esp_netif_get_flags(_esp_netif);
|
||||
if (flags & ESP_NETIF_DHCP_SERVER && dns_no > 0) {
|
||||
log_e("Server interfaces can have only one DNS server.");
|
||||
return false;
|
||||
}
|
||||
esp_netif_dns_info_t d;
|
||||
// ToDo: can this work with IPv6 addresses?
|
||||
d.ip.type = IPADDR_TYPE_V4;
|
||||
if (static_cast<uint32_t>(ip) != 0) {
|
||||
d.ip.u_addr.ip4.addr = static_cast<uint32_t>(ip);
|
||||
} else {
|
||||
d.ip.u_addr.ip4.addr = 0;
|
||||
}
|
||||
if (esp_netif_set_dns_info(_esp_netif, (esp_netif_dns_type_t)dns_no, &d) != ESP_OK) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NetworkInterface::config(IPAddress local_ip, IPAddress gateway, IPAddress subnet, IPAddress dns1, IPAddress dns2, IPAddress dns3) {
|
||||
if (_esp_netif == NULL) {
|
||||
return false;
|
||||
}
|
||||
esp_err_t err = ESP_OK;
|
||||
esp_netif_ip_info_t info;
|
||||
esp_netif_dns_info_t d1;
|
||||
esp_netif_dns_info_t d2;
|
||||
esp_netif_dns_info_t d3;
|
||||
d1.ip.type = IPADDR_TYPE_V4;
|
||||
d2.ip.type = IPADDR_TYPE_V4;
|
||||
d3.ip.type = IPADDR_TYPE_V4;
|
||||
|
||||
if (static_cast<uint32_t>(local_ip) != 0) {
|
||||
info.ip.addr = static_cast<uint32_t>(local_ip);
|
||||
info.gw.addr = static_cast<uint32_t>(gateway);
|
||||
info.netmask.addr = static_cast<uint32_t>(subnet);
|
||||
d1.ip.u_addr.ip4.addr = static_cast<uint32_t>(dns1);
|
||||
d2.ip.u_addr.ip4.addr = static_cast<uint32_t>(dns2);
|
||||
d3.ip.u_addr.ip4.addr = static_cast<uint32_t>(dns3);
|
||||
} else {
|
||||
info.ip.addr = 0;
|
||||
info.gw.addr = 0;
|
||||
info.netmask.addr = 0;
|
||||
d1.ip.u_addr.ip4.addr = 0;
|
||||
d2.ip.u_addr.ip4.addr = 0;
|
||||
d3.ip.u_addr.ip4.addr = 0;
|
||||
}
|
||||
|
||||
esp_netif_flags_t flags = esp_netif_get_flags(_esp_netif);
|
||||
if (flags & ESP_NETIF_DHCP_SERVER) {
|
||||
|
||||
// Set DNS Server
|
||||
if (d2.ip.u_addr.ip4.addr != 0) {
|
||||
err = esp_netif_set_dns_info(_esp_netif, ESP_NETIF_DNS_MAIN, &d2);
|
||||
if (err) {
|
||||
log_e("Netif Set DNS Info Failed! 0x%04x: %s", err, esp_err_to_name(err));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Stop DHCPS
|
||||
err = esp_netif_dhcps_stop(_esp_netif);
|
||||
if (err && err != ESP_ERR_ESP_NETIF_DHCP_ALREADY_STOPPED) {
|
||||
log_e("DHCPS Stop Failed! 0x%04x: %s", err, esp_err_to_name(err));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set IPv4, Netmask, Gateway
|
||||
err = esp_netif_set_ip_info(_esp_netif, &info);
|
||||
if (err) {
|
||||
log_e("Netif Set IP Failed! 0x%04x: %s", err, esp_err_to_name(err));
|
||||
return false;
|
||||
}
|
||||
|
||||
dhcps_lease_t lease;
|
||||
lease.enable = true;
|
||||
uint8_t CIDR = calculateSubnetCIDR(subnet);
|
||||
log_v(
|
||||
"SoftAP: %s | Gateway: %s | DHCP Start: %s | Netmask: %s", local_ip.toString().c_str(), gateway.toString().c_str(), dns1.toString().c_str(),
|
||||
subnet.toString().c_str()
|
||||
);
|
||||
// netmask must have room for at least 12 IP addresses (AP + GW + 10 DHCP Leasing addresses)
|
||||
// netmask also must be limited to the last 8 bits of IPv4, otherwise this function won't work
|
||||
// IDF NETIF checks netmask for the 3rd byte: https://github.com/espressif/esp-idf/blob/master/components/esp_netif/lwip/esp_netif_lwip.c#L1857-L1862
|
||||
if (CIDR > 28 || CIDR < 24) {
|
||||
log_e("Bad netmask. It must be from /24 to /28 (255.255.255. 0<->240)");
|
||||
return false; // ESP_FAIL if initializing failed
|
||||
}
|
||||
#define _byte_swap32(num) (((num >> 24) & 0xff) | ((num << 8) & 0xff0000) | ((num >> 8) & 0xff00) | ((num << 24) & 0xff000000))
|
||||
// The code below is ready for any netmask, not limited to 255.255.255.0
|
||||
uint32_t netmask = _byte_swap32(info.netmask.addr);
|
||||
uint32_t ap_ipaddr = _byte_swap32(info.ip.addr);
|
||||
uint32_t dhcp_ipaddr = _byte_swap32(static_cast<uint32_t>(dns1));
|
||||
dhcp_ipaddr = dhcp_ipaddr == 0 ? ap_ipaddr + 1 : dhcp_ipaddr;
|
||||
uint32_t leaseStartMax = ~netmask - 10;
|
||||
// there will be 10 addresses for DHCP to lease
|
||||
lease.start_ip.addr = dhcp_ipaddr;
|
||||
lease.end_ip.addr = lease.start_ip.addr + 10;
|
||||
// Check if local_ip is in the same subnet as the dhcp leasing range initial address
|
||||
if ((ap_ipaddr & netmask) != (dhcp_ipaddr & netmask)) {
|
||||
log_e(
|
||||
"The AP IP address (%s) and the DHCP start address (%s) must be in the same subnet", local_ip.toString().c_str(),
|
||||
IPAddress(_byte_swap32(dhcp_ipaddr)).toString().c_str()
|
||||
);
|
||||
return false; // ESP_FAIL if initializing failed
|
||||
}
|
||||
// prevents DHCP lease range to overflow subnet range
|
||||
if ((dhcp_ipaddr & ~netmask) >= leaseStartMax) {
|
||||
// make first DHCP lease addr stay in the beginning of the netmask range
|
||||
lease.start_ip.addr = (dhcp_ipaddr & netmask) + 1;
|
||||
lease.end_ip.addr = lease.start_ip.addr + 10;
|
||||
log_w("DHCP Lease out of range - Changing DHCP leasing start to %s", IPAddress(_byte_swap32(lease.start_ip.addr)).toString().c_str());
|
||||
}
|
||||
// Check if local_ip is within DHCP range
|
||||
if (ap_ipaddr >= lease.start_ip.addr && ap_ipaddr <= lease.end_ip.addr) {
|
||||
log_e(
|
||||
"The AP IP address (%s) can't be within the DHCP range (%s -- %s)", local_ip.toString().c_str(),
|
||||
IPAddress(_byte_swap32(lease.start_ip.addr)).toString().c_str(), IPAddress(_byte_swap32(lease.end_ip.addr)).toString().c_str()
|
||||
);
|
||||
return false; // ESP_FAIL if initializing failed
|
||||
}
|
||||
// Check if gateway is within DHCP range
|
||||
uint32_t gw_ipaddr = _byte_swap32(info.gw.addr);
|
||||
bool gw_in_same_subnet = (gw_ipaddr & netmask) == (ap_ipaddr & netmask);
|
||||
if (gw_in_same_subnet && gw_ipaddr >= lease.start_ip.addr && gw_ipaddr <= lease.end_ip.addr) {
|
||||
log_e(
|
||||
"The GatewayP address (%s) can't be within the DHCP range (%s -- %s)", gateway.toString().c_str(),
|
||||
IPAddress(_byte_swap32(lease.start_ip.addr)).toString().c_str(), IPAddress(_byte_swap32(lease.end_ip.addr)).toString().c_str()
|
||||
);
|
||||
return false; // ESP_FAIL if initializing failed
|
||||
}
|
||||
// all done, just revert back byte order of DHCP lease range
|
||||
lease.start_ip.addr = _byte_swap32(lease.start_ip.addr);
|
||||
lease.end_ip.addr = _byte_swap32(lease.end_ip.addr);
|
||||
log_v("DHCP Server Range: %s to %s", IPAddress(lease.start_ip.addr).toString().c_str(), IPAddress(lease.end_ip.addr).toString().c_str());
|
||||
err = esp_netif_dhcps_option(_esp_netif, ESP_NETIF_OP_SET, ESP_NETIF_REQUESTED_IP_ADDRESS, (void *)&lease, sizeof(dhcps_lease_t));
|
||||
if (err) {
|
||||
log_e("DHCPS Set Lease Failed! 0x%04x: %s", err, esp_err_to_name(err));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Offer DNS to DHCP clients
|
||||
if (d2.ip.u_addr.ip4.addr != 0) {
|
||||
dhcps_offer_t dhcps_dns_value = OFFER_DNS;
|
||||
err = esp_netif_dhcps_option(_esp_netif, ESP_NETIF_OP_SET, ESP_NETIF_DOMAIN_NAME_SERVER, &dhcps_dns_value, sizeof(dhcps_dns_value));
|
||||
if (err) {
|
||||
log_e("Netif Set DHCP Option Failed! 0x%04x: %s", err, esp_err_to_name(err));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Start DHCPS
|
||||
err = esp_netif_dhcps_start(_esp_netif);
|
||||
if (err) {
|
||||
log_e("DHCPS Start Failed! 0x%04x: %s", err, esp_err_to_name(err));
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// Stop DHCPC
|
||||
err = esp_netif_dhcpc_stop(_esp_netif);
|
||||
if (err != ESP_OK && err != ESP_ERR_ESP_NETIF_DHCP_ALREADY_STOPPED) {
|
||||
log_e("DHCP could not be stopped! Error: 0x%04x: %s", err, esp_err_to_name(err));
|
||||
return false;
|
||||
}
|
||||
|
||||
clearStatusBits(ESP_NETIF_HAS_IP_BIT | ESP_NETIF_HAS_STATIC_IP_BIT);
|
||||
|
||||
// Set IPv4, Netmask, Gateway
|
||||
err = esp_netif_set_ip_info(_esp_netif, &info);
|
||||
if (err != ERR_OK) {
|
||||
log_e("ETH IP could not be configured! Error: 0x%04x: %s", err, esp_err_to_name(err));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set DNS Servers
|
||||
esp_netif_set_dns_info(_esp_netif, ESP_NETIF_DNS_MAIN, &d1);
|
||||
esp_netif_set_dns_info(_esp_netif, ESP_NETIF_DNS_BACKUP, &d2);
|
||||
esp_netif_set_dns_info(_esp_netif, ESP_NETIF_DNS_FALLBACK, &d3);
|
||||
|
||||
// Start DHCPC if static IP was set
|
||||
if (info.ip.addr == 0) {
|
||||
err = esp_netif_dhcpc_start(_esp_netif);
|
||||
if (err != ESP_OK && err != ESP_ERR_ESP_NETIF_DHCP_ALREADY_STARTED) {
|
||||
log_w("DHCP could not be started! Error: 0x%04x: %s", err, esp_err_to_name(err));
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
setStatusBits(ESP_NETIF_HAS_STATIC_IP_BIT);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const char *NetworkInterface::getHostname() const {
|
||||
if (_esp_netif == NULL) {
|
||||
return "";
|
||||
}
|
||||
const char *hostname;
|
||||
if (esp_netif_get_hostname(_esp_netif, &hostname)) {
|
||||
return NULL;
|
||||
}
|
||||
return hostname;
|
||||
}
|
||||
|
||||
bool NetworkInterface::setHostname(const char *hostname) const {
|
||||
if (_esp_netif == NULL) {
|
||||
return false;
|
||||
}
|
||||
return esp_netif_set_hostname(_esp_netif, hostname) == 0;
|
||||
}
|
||||
|
||||
bool NetworkInterface::linkUp() const {
|
||||
if (_esp_netif == NULL) {
|
||||
return false;
|
||||
}
|
||||
return esp_netif_is_netif_up(_esp_netif);
|
||||
}
|
||||
|
||||
const char *NetworkInterface::ifkey(void) const {
|
||||
if (_esp_netif == NULL) {
|
||||
return "";
|
||||
}
|
||||
return esp_netif_get_ifkey(_esp_netif);
|
||||
}
|
||||
|
||||
const char *NetworkInterface::desc(void) const {
|
||||
if (_esp_netif == NULL) {
|
||||
return "";
|
||||
}
|
||||
return esp_netif_get_desc(_esp_netif);
|
||||
}
|
||||
|
||||
String NetworkInterface::impl_name(void) const {
|
||||
if (_esp_netif == NULL) {
|
||||
return String("");
|
||||
}
|
||||
char netif_name[8];
|
||||
esp_err_t err = esp_netif_get_netif_impl_name(_esp_netif, netif_name);
|
||||
if (err != ESP_OK) {
|
||||
log_e("Failed to get netif impl_name: 0x%04x %s", err, esp_err_to_name(err));
|
||||
return String("");
|
||||
}
|
||||
return String(netif_name);
|
||||
}
|
||||
|
||||
int NetworkInterface::impl_index() const {
|
||||
if (_esp_netif == NULL) {
|
||||
return -1;
|
||||
}
|
||||
return esp_netif_get_netif_impl_index(_esp_netif);
|
||||
}
|
||||
|
||||
/**
|
||||
* Every netif has a parameter named route_prio, you can refer to file esp_netif_defaults.h.
|
||||
* A higher value of route_prio indicates a higher priority.
|
||||
* The active interface with highest priority will be used for default route (gateway).
|
||||
* Defaults are: STA=100, BR=70, ETH=50, PPP=20, AP/NAN=10
|
||||
*/
|
||||
int NetworkInterface::getRoutePrio() const {
|
||||
if (_esp_netif == NULL) {
|
||||
return -1;
|
||||
}
|
||||
return esp_netif_get_route_prio(_esp_netif);
|
||||
}
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 0)
|
||||
int NetworkInterface::setRoutePrio(int prio) {
|
||||
if (_esp_netif == NULL) {
|
||||
return -1;
|
||||
}
|
||||
return esp_netif_set_route_prio(_esp_netif, prio);
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* This API overrides the automatic configuration of the default interface based on the route_prio
|
||||
* If the selected netif is set default using this API, no other interface could be set-default disregarding
|
||||
* its route_prio number (unless the selected netif gets destroyed)
|
||||
*/
|
||||
bool NetworkInterface::setDefault() {
|
||||
if (_esp_netif == NULL) {
|
||||
return false;
|
||||
}
|
||||
esp_err_t err = esp_netif_set_default_netif(_esp_netif);
|
||||
if (err != ESP_OK) {
|
||||
log_e("Failed to set default netif: 0x%04x %s", err, esp_err_to_name(err));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NetworkInterface::isDefault() const {
|
||||
if (_esp_netif == NULL) {
|
||||
return false;
|
||||
}
|
||||
return esp_netif_get_default_netif() == _esp_netif;
|
||||
}
|
||||
|
||||
uint8_t *NetworkInterface::macAddress(uint8_t *mac) const {
|
||||
if (!mac || _esp_netif == NULL || _interface_id == ESP_NETIF_ID_PPP) {
|
||||
return NULL;
|
||||
}
|
||||
esp_err_t err = esp_netif_get_mac(_esp_netif, mac);
|
||||
if (err != ESP_OK) {
|
||||
log_e("Failed to get netif mac: 0x%04x %s", err, esp_err_to_name(err));
|
||||
return NULL;
|
||||
}
|
||||
return mac;
|
||||
}
|
||||
|
||||
String NetworkInterface::macAddress(void) const {
|
||||
uint8_t mac[6] = {0, 0, 0, 0, 0, 0};
|
||||
char macStr[18] = {0};
|
||||
macAddress(mac);
|
||||
sprintf(macStr, "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
||||
return String(macStr);
|
||||
}
|
||||
|
||||
IPAddress NetworkInterface::localIP() const {
|
||||
if (_esp_netif == NULL) {
|
||||
return IPAddress();
|
||||
}
|
||||
esp_netif_ip_info_t ip;
|
||||
if (esp_netif_get_ip_info(_esp_netif, &ip)) {
|
||||
return IPAddress();
|
||||
}
|
||||
return IPAddress(ip.ip.addr);
|
||||
}
|
||||
|
||||
IPAddress NetworkInterface::subnetMask() const {
|
||||
if (_esp_netif == NULL) {
|
||||
return IPAddress();
|
||||
}
|
||||
esp_netif_ip_info_t ip;
|
||||
if (esp_netif_get_ip_info(_esp_netif, &ip)) {
|
||||
return IPAddress();
|
||||
}
|
||||
return IPAddress(ip.netmask.addr);
|
||||
}
|
||||
|
||||
IPAddress NetworkInterface::gatewayIP() const {
|
||||
if (_esp_netif == NULL) {
|
||||
return IPAddress();
|
||||
}
|
||||
esp_netif_ip_info_t ip;
|
||||
if (esp_netif_get_ip_info(_esp_netif, &ip)) {
|
||||
return IPAddress();
|
||||
}
|
||||
return IPAddress(ip.gw.addr);
|
||||
}
|
||||
|
||||
IPAddress NetworkInterface::dnsIP(uint8_t dns_no) const {
|
||||
if (_esp_netif == NULL || dns_no >= ESP_NETIF_DNS_MAX) {
|
||||
return IPAddress();
|
||||
}
|
||||
esp_netif_dns_info_t d;
|
||||
if (esp_netif_get_dns_info(_esp_netif, (esp_netif_dns_type_t)dns_no, &d) != ESP_OK) {
|
||||
return IPAddress();
|
||||
}
|
||||
if (d.ip.type == ESP_IPADDR_TYPE_V6) {
|
||||
// IPv6 from 4x uint32_t; byte order based on IPV62STR() in esp_netif_ip_addr.h
|
||||
// log_v("DNS got IPv6: " IPV6STR, IPV62STR(d.ip.u_addr.ip6));
|
||||
uint32_t addr0 esp_netif_htonl(d.ip.u_addr.ip6.addr[0]);
|
||||
uint32_t addr1 esp_netif_htonl(d.ip.u_addr.ip6.addr[1]);
|
||||
uint32_t addr2 esp_netif_htonl(d.ip.u_addr.ip6.addr[2]);
|
||||
uint32_t addr3 esp_netif_htonl(d.ip.u_addr.ip6.addr[3]);
|
||||
return IPAddress(
|
||||
(uint8_t)(addr0 >> 24) & 0xFF, (uint8_t)(addr0 >> 16) & 0xFF, (uint8_t)(addr0 >> 8) & 0xFF, (uint8_t)addr0 & 0xFF, (uint8_t)(addr1 >> 24) & 0xFF,
|
||||
(uint8_t)(addr1 >> 16) & 0xFF, (uint8_t)(addr1 >> 8) & 0xFF, (uint8_t)addr1 & 0xFF, (uint8_t)(addr2 >> 24) & 0xFF, (uint8_t)(addr2 >> 16) & 0xFF,
|
||||
(uint8_t)(addr2 >> 8) & 0xFF, (uint8_t)addr2 & 0xFF, (uint8_t)(addr3 >> 24) & 0xFF, (uint8_t)(addr3 >> 16) & 0xFF, (uint8_t)(addr3 >> 8) & 0xFF,
|
||||
(uint8_t)addr3 & 0xFF, d.ip.u_addr.ip6.zone
|
||||
);
|
||||
}
|
||||
// IPv4 from single uint32_t
|
||||
// log_v("DNS IPv4: " IPSTR, IP2STR(&d.ip.u_addr.ip4));
|
||||
return IPAddress(d.ip.u_addr.ip4.addr);
|
||||
}
|
||||
|
||||
IPAddress NetworkInterface::broadcastIP() const {
|
||||
if (_esp_netif == NULL) {
|
||||
return IPAddress();
|
||||
}
|
||||
esp_netif_ip_info_t ip;
|
||||
if (esp_netif_get_ip_info(_esp_netif, &ip)) {
|
||||
return IPAddress();
|
||||
}
|
||||
return calculateBroadcast(IPAddress(ip.gw.addr), IPAddress(ip.netmask.addr));
|
||||
}
|
||||
|
||||
IPAddress NetworkInterface::networkID() const {
|
||||
if (_esp_netif == NULL) {
|
||||
return IPAddress();
|
||||
}
|
||||
esp_netif_ip_info_t ip;
|
||||
if (esp_netif_get_ip_info(_esp_netif, &ip)) {
|
||||
return IPAddress();
|
||||
}
|
||||
return calculateNetworkID(IPAddress(ip.gw.addr), IPAddress(ip.netmask.addr));
|
||||
}
|
||||
|
||||
uint8_t NetworkInterface::subnetCIDR() const {
|
||||
if (_esp_netif == NULL) {
|
||||
return (uint8_t)0;
|
||||
}
|
||||
esp_netif_ip_info_t ip;
|
||||
if (esp_netif_get_ip_info(_esp_netif, &ip)) {
|
||||
return (uint8_t)0;
|
||||
}
|
||||
return calculateSubnetCIDR(IPAddress(ip.netmask.addr));
|
||||
}
|
||||
|
||||
#if CONFIG_LWIP_IPV6
|
||||
IPAddress NetworkInterface::linkLocalIPv6() const {
|
||||
if (_esp_netif == NULL) {
|
||||
return IPAddress(IPv6);
|
||||
}
|
||||
static esp_ip6_addr_t addr;
|
||||
if (esp_netif_get_ip6_linklocal(_esp_netif, &addr)) {
|
||||
return IPAddress(IPv6);
|
||||
}
|
||||
return IPAddress(IPv6, (const uint8_t *)addr.addr, addr.zone);
|
||||
}
|
||||
|
||||
IPAddress NetworkInterface::globalIPv6() const {
|
||||
if (_esp_netif == NULL) {
|
||||
return IPAddress(IPv6);
|
||||
}
|
||||
static esp_ip6_addr_t addr;
|
||||
if (esp_netif_get_ip6_global(_esp_netif, &addr)) {
|
||||
return IPAddress(IPv6);
|
||||
}
|
||||
return IPAddress(IPv6, (const uint8_t *)addr.addr, addr.zone);
|
||||
}
|
||||
#endif
|
||||
|
||||
size_t NetworkInterface::printTo(Print &out) const {
|
||||
size_t bytes = 0;
|
||||
if (_esp_netif == NULL) {
|
||||
return bytes;
|
||||
}
|
||||
if (isDefault()) {
|
||||
bytes += out.print("*");
|
||||
}
|
||||
const char *dscr = esp_netif_get_desc(_esp_netif);
|
||||
if (dscr != NULL) {
|
||||
bytes += out.print(dscr);
|
||||
}
|
||||
bytes += out.print(":");
|
||||
if (esp_netif_is_netif_up(_esp_netif)) {
|
||||
bytes += out.print(" <UP");
|
||||
} else {
|
||||
bytes += out.print(" <DOWN");
|
||||
}
|
||||
bytes += printDriverInfo(out);
|
||||
bytes += out.print(">");
|
||||
|
||||
bytes += out.print(" (");
|
||||
esp_netif_flags_t flags = esp_netif_get_flags(_esp_netif);
|
||||
if (flags & ESP_NETIF_DHCP_CLIENT) {
|
||||
bytes += out.print("DHCPC");
|
||||
if (getStatusBits() & ESP_NETIF_HAS_STATIC_IP_BIT) {
|
||||
bytes += out.print("_OFF");
|
||||
}
|
||||
}
|
||||
if (flags & ESP_NETIF_DHCP_SERVER) {
|
||||
bytes += out.print("DHCPS");
|
||||
}
|
||||
if (flags & ESP_NETIF_FLAG_AUTOUP) {
|
||||
bytes += out.print(",AUTOUP");
|
||||
}
|
||||
if (flags & ESP_NETIF_FLAG_GARP) {
|
||||
bytes += out.print(",GARP");
|
||||
}
|
||||
if (flags & ESP_NETIF_FLAG_EVENT_IP_MODIFIED) {
|
||||
bytes += out.print(",IP_MOD");
|
||||
}
|
||||
if (flags & ESP_NETIF_FLAG_IS_PPP) {
|
||||
bytes += out.print(",PPP");
|
||||
}
|
||||
if (flags & ESP_NETIF_FLAG_IS_BRIDGE) {
|
||||
bytes += out.print(",BRIDGE");
|
||||
}
|
||||
if (flags & ESP_NETIF_FLAG_MLDV6_REPORT) {
|
||||
bytes += out.print(",V6_REP");
|
||||
}
|
||||
bytes += out.print(")");
|
||||
|
||||
bytes += out.print(" PRIO: ");
|
||||
bytes += out.print(getRoutePrio());
|
||||
bytes += out.println("");
|
||||
|
||||
bytes += out.print(" ");
|
||||
bytes += out.print("ether ");
|
||||
bytes += out.print(macAddress());
|
||||
bytes += out.println();
|
||||
|
||||
bytes += out.print(" ");
|
||||
bytes += out.print("inet ");
|
||||
bytes += out.print(localIP());
|
||||
bytes += out.print(" netmask ");
|
||||
bytes += out.print(subnetMask());
|
||||
bytes += out.print(" broadcast ");
|
||||
bytes += out.print(broadcastIP());
|
||||
bytes += out.println();
|
||||
|
||||
bytes += out.print(" ");
|
||||
bytes += out.print("gateway ");
|
||||
bytes += out.print(gatewayIP());
|
||||
bytes += out.print(" dns ");
|
||||
bytes += out.print(dnsIP());
|
||||
bytes += out.println();
|
||||
|
||||
#if CONFIG_LWIP_IPV6
|
||||
static const char *types[] = {"UNKNOWN", "GLOBAL", "LINK_LOCAL", "SITE_LOCAL", "UNIQUE_LOCAL", "IPV4_MAPPED_IPV6"};
|
||||
esp_ip6_addr_t if_ip6[CONFIG_LWIP_IPV6_NUM_ADDRESSES];
|
||||
int v6addrs = esp_netif_get_all_ip6(_esp_netif, if_ip6);
|
||||
for (int i = 0; i < v6addrs; ++i) {
|
||||
bytes += out.print(" ");
|
||||
bytes += out.print("inet6 ");
|
||||
bytes += IPAddress(IPv6, (const uint8_t *)if_ip6[i].addr, if_ip6[i].zone).printTo(out, true);
|
||||
bytes += out.print(" type ");
|
||||
bytes += out.print(types[esp_netif_ip6_get_addr_type(&if_ip6[i])]);
|
||||
bytes += out.println();
|
||||
}
|
||||
#endif
|
||||
|
||||
return bytes;
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "esp_netif_types.h"
|
||||
#include "esp_event.h"
|
||||
#include "Arduino.h"
|
||||
#include "Printable.h"
|
||||
|
||||
typedef enum {
|
||||
ESP_NETIF_ID_STA,
|
||||
ESP_NETIF_ID_AP,
|
||||
ESP_NETIF_ID_PPP,
|
||||
ESP_NETIF_ID_ETH0,
|
||||
ESP_NETIF_ID_ETH1,
|
||||
ESP_NETIF_ID_ETH2,
|
||||
ESP_NETIF_ID_MAX
|
||||
} Network_Interface_ID;
|
||||
|
||||
static const int ESP_NETIF_STARTED_BIT = BIT0;
|
||||
static const int ESP_NETIF_CONNECTED_BIT = BIT1;
|
||||
static const int ESP_NETIF_HAS_IP_BIT = BIT2;
|
||||
static const int ESP_NETIF_HAS_LOCAL_IP6_BIT = BIT3;
|
||||
static const int ESP_NETIF_HAS_GLOBAL_IP6_BIT = BIT4;
|
||||
static const int ESP_NETIF_WANT_IP6_BIT = BIT5;
|
||||
static const int ESP_NETIF_HAS_STATIC_IP_BIT = BIT6;
|
||||
|
||||
#define ESP_NETIF_ID_ETH ESP_NETIF_ID_ETH0
|
||||
|
||||
class NetworkInterface : public Printable {
|
||||
public:
|
||||
NetworkInterface();
|
||||
virtual ~NetworkInterface();
|
||||
|
||||
// For server interfaces (WiFi AP), dns1 is the DHCP lease range start and dns2 is the DNS. dns3 is not used
|
||||
bool config(
|
||||
IPAddress local_ip = (uint32_t)0x00000000, IPAddress gateway = (uint32_t)0x00000000, IPAddress subnet = (uint32_t)0x00000000,
|
||||
IPAddress dns1 = (uint32_t)0x00000000, IPAddress dns2 = (uint32_t)0x00000000, IPAddress dns3 = (uint32_t)0x00000000
|
||||
);
|
||||
bool dnsIP(uint8_t dns_no, IPAddress ip);
|
||||
|
||||
const char *getHostname() const;
|
||||
bool setHostname(const char *hostname) const;
|
||||
|
||||
bool started() const;
|
||||
bool connected() const;
|
||||
bool hasIP() const;
|
||||
bool hasLinkLocalIPv6() const;
|
||||
bool hasGlobalIPv6() const;
|
||||
bool enableIPv6(bool en = true);
|
||||
|
||||
bool linkUp() const;
|
||||
const char *ifkey() const;
|
||||
const char *desc() const;
|
||||
String impl_name() const;
|
||||
int impl_index() const;
|
||||
int getRoutePrio() const;
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 0)
|
||||
int setRoutePrio(int prio);
|
||||
#endif
|
||||
bool setDefault();
|
||||
bool isDefault() const;
|
||||
|
||||
uint8_t *macAddress(uint8_t *mac) const;
|
||||
String macAddress() const;
|
||||
IPAddress localIP() const;
|
||||
IPAddress subnetMask() const;
|
||||
IPAddress gatewayIP() const;
|
||||
IPAddress dnsIP(uint8_t dns_no = 0) const;
|
||||
IPAddress broadcastIP() const;
|
||||
IPAddress networkID() const;
|
||||
uint8_t subnetCIDR() const;
|
||||
#if CONFIG_LWIP_IPV6
|
||||
IPAddress linkLocalIPv6() const;
|
||||
IPAddress globalIPv6() const;
|
||||
#endif
|
||||
|
||||
size_t printTo(Print &out) const;
|
||||
|
||||
esp_netif_t *netif() {
|
||||
return _esp_netif;
|
||||
}
|
||||
int getStatusBits() const;
|
||||
int waitStatusBits(int bits, uint32_t timeout_ms) const;
|
||||
|
||||
protected:
|
||||
esp_netif_t *_esp_netif;
|
||||
EventGroupHandle_t _interface_event_group;
|
||||
int _initial_bits;
|
||||
int32_t _got_ip_event_id;
|
||||
int32_t _lost_ip_event_id;
|
||||
Network_Interface_ID _interface_id;
|
||||
|
||||
bool initNetif(Network_Interface_ID interface_id);
|
||||
void destroyNetif();
|
||||
int setStatusBits(int bits);
|
||||
int clearStatusBits(int bits);
|
||||
|
||||
// virtual void getMac(uint8_t* mac) = 0;
|
||||
virtual size_t printDriverInfo(Print &out) const = 0;
|
||||
|
||||
public:
|
||||
void _onIpEvent(int32_t event_id, void *event_data);
|
||||
|
||||
private:
|
||||
IPAddress calculateNetworkID(IPAddress ip, IPAddress subnet) const;
|
||||
IPAddress calculateBroadcast(IPAddress ip, IPAddress subnet) const;
|
||||
uint8_t calculateSubnetCIDR(IPAddress subnetMask) const;
|
||||
};
|
||||
@@ -0,0 +1,212 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include "NetworkManager.h"
|
||||
#include "IPAddress.h"
|
||||
#include "esp_netif.h"
|
||||
#include "lwip/dns.h"
|
||||
#include "esp_mac.h"
|
||||
#include "netdb.h"
|
||||
|
||||
NetworkManager::NetworkManager() {}
|
||||
|
||||
NetworkInterface *getNetifByID(Network_Interface_ID id);
|
||||
|
||||
bool NetworkManager::begin() {
|
||||
static bool initialized = false;
|
||||
if (!initialized) {
|
||||
initialized = true;
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
uint8_t mac[8];
|
||||
if (esp_base_mac_addr_get(mac) != ESP_OK && esp_efuse_mac_get_default(mac) == ESP_OK) {
|
||||
esp_base_mac_addr_set(mac);
|
||||
}
|
||||
#endif
|
||||
initialized = esp_netif_init() == ESP_OK;
|
||||
if (!initialized) {
|
||||
log_e("esp_netif_init failed!");
|
||||
}
|
||||
}
|
||||
if (initialized) {
|
||||
initNetworkEvents();
|
||||
}
|
||||
return initialized;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the given hostname to an IP address.
|
||||
* @param aHostname Name to be resolved
|
||||
* @param aResult IPAddress structure to store the returned IP address
|
||||
* @return 1 if aIPAddrString was successfully converted to an IP address,
|
||||
* else error code
|
||||
*/
|
||||
int NetworkManager::hostByName(const char *aHostname, IPAddress &aResult) {
|
||||
static bool hasGlobalV6 = false;
|
||||
static bool hasGlobalV4 = false;
|
||||
err_t err = ERR_OK;
|
||||
const char *servname = "0";
|
||||
struct addrinfo *res;
|
||||
|
||||
aResult = static_cast<uint32_t>(0);
|
||||
|
||||
// First check if the host parses as a literal address
|
||||
if (aResult.fromString(aHostname)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// This should generally check if we have a global address assigned to one of the interfaces.
|
||||
// If such address is not assigned, there is no point in trying to get V6 from DNS as we will not be able to reach it.
|
||||
bool hasGlobalV6Now = false;
|
||||
bool hasGlobalV4Now = false;
|
||||
for (int i = 0; i < ESP_NETIF_ID_MAX; ++i) {
|
||||
NetworkInterface *iface = getNetifByID((Network_Interface_ID)i);
|
||||
if (iface != NULL) {
|
||||
if (iface->hasGlobalIPv6()) {
|
||||
hasGlobalV6Now = true;
|
||||
}
|
||||
if (iface->hasIP()) {
|
||||
hasGlobalV4Now = true;
|
||||
}
|
||||
}
|
||||
if (hasGlobalV6Now && hasGlobalV4Now) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If the state of IP addresses has changed, clear the DNS cache
|
||||
if (hasGlobalV6 != hasGlobalV6Now || hasGlobalV4 != hasGlobalV4Now) {
|
||||
hasGlobalV6 = hasGlobalV6Now;
|
||||
hasGlobalV4 = hasGlobalV4Now;
|
||||
dns_clear_cache();
|
||||
log_d("Clearing DNS cache");
|
||||
}
|
||||
|
||||
struct addrinfo hints;
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
|
||||
#if CONFIG_LWIP_IPV6
|
||||
// **Workaround**
|
||||
// LWIP AF_UNSPEC always prefers IPv4 and doesn't check what network is
|
||||
// available. See https://github.com/espressif/esp-idf/issues/13255
|
||||
// Until that is fixed, as a work around if we have a global scope IPv6,
|
||||
// then we check IPv6 only first.
|
||||
if (hasGlobalV6) {
|
||||
hints.ai_family = AF_INET6;
|
||||
err = lwip_getaddrinfo(aHostname, servname, &hints, &res);
|
||||
|
||||
if (err == ERR_OK) {
|
||||
struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)res->ai_addr;
|
||||
// As an array of u8_t
|
||||
aResult = IPAddress(IPv6, ipv6->sin6_addr.s6_addr);
|
||||
log_d("DNS found IPv6 first %s", aResult.toString().c_str());
|
||||
lwip_freeaddrinfo(res);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
// **End Workaround**
|
||||
#endif
|
||||
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
err = lwip_getaddrinfo(aHostname, servname, &hints, &res);
|
||||
|
||||
if (err == ERR_OK) {
|
||||
#if CONFIG_LWIP_IPV6
|
||||
if (res->ai_family == AF_INET6) {
|
||||
struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)res->ai_addr;
|
||||
// As an array of u8_t
|
||||
aResult = IPAddress(IPv6, ipv6->sin6_addr.s6_addr);
|
||||
log_d("DNS found IPv6 %s", aResult.toString().c_str());
|
||||
} else {
|
||||
#endif
|
||||
struct sockaddr_in *ipv4 = (struct sockaddr_in *)res->ai_addr;
|
||||
// As a single u32_t
|
||||
aResult = IPAddress(ipv4->sin_addr.s_addr);
|
||||
log_d("DNS found IPv4 %s", aResult.toString().c_str());
|
||||
#if CONFIG_LWIP_IPV6
|
||||
}
|
||||
#endif
|
||||
|
||||
lwip_freeaddrinfo(res);
|
||||
return 1;
|
||||
}
|
||||
|
||||
log_e("DNS Failed for '%s' with error '%d'", aHostname, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
uint8_t *NetworkManager::macAddress(uint8_t *mac) {
|
||||
esp_base_mac_addr_get(mac);
|
||||
return mac;
|
||||
}
|
||||
|
||||
String NetworkManager::macAddress(void) {
|
||||
uint8_t mac[6];
|
||||
char macStr[18] = {0};
|
||||
macAddress(mac);
|
||||
sprintf(macStr, "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
||||
return String(macStr);
|
||||
}
|
||||
|
||||
static char default_hostname[32] = {
|
||||
0,
|
||||
};
|
||||
|
||||
const char *NetworkManager::getHostname() {
|
||||
if (default_hostname[0] == 0) {
|
||||
uint8_t eth_mac[6];
|
||||
esp_base_mac_addr_get(eth_mac);
|
||||
snprintf(default_hostname, 32, "%s%02X%02X%02X", CONFIG_IDF_TARGET "-", eth_mac[3], eth_mac[4], eth_mac[5]);
|
||||
}
|
||||
return (const char *)default_hostname;
|
||||
}
|
||||
|
||||
bool NetworkManager::setHostname(const char *name) {
|
||||
if (name) {
|
||||
snprintf(default_hostname, 32, "%s", name);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NetworkManager::setDefaultInterface(NetworkInterface &ifc) {
|
||||
return ifc.setDefault();
|
||||
}
|
||||
|
||||
NetworkInterface *NetworkManager::getDefaultInterface() {
|
||||
esp_netif_t *netif = esp_netif_get_default_netif();
|
||||
for (int i = 0; i < ESP_NETIF_ID_MAX; ++i) {
|
||||
NetworkInterface *iface = getNetifByID((Network_Interface_ID)i);
|
||||
if (iface != NULL && iface->netif() == netif) {
|
||||
return iface;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool NetworkManager::isOnline() {
|
||||
for (int i = 0; i < ESP_NETIF_ID_MAX; ++i) {
|
||||
if (i != ESP_NETIF_ID_AP) {
|
||||
NetworkInterface *iface = getNetifByID((Network_Interface_ID)i);
|
||||
if (iface != NULL && iface->connected() && (iface->hasIP() || iface->hasGlobalIPv6())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t NetworkManager::printTo(Print &out) const {
|
||||
size_t bytes = 0;
|
||||
|
||||
for (int i = 0; i < ESP_NETIF_ID_MAX; ++i) {
|
||||
NetworkInterface *iface = getNetifByID((Network_Interface_ID)i);
|
||||
if (iface != NULL && iface->netif() != NULL) {
|
||||
bytes += out.println(*iface);
|
||||
}
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
NetworkManager Network;
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "NetworkEvents.h"
|
||||
#include "NetworkInterface.h"
|
||||
#include "IPAddress.h"
|
||||
#include "WString.h"
|
||||
|
||||
class NetworkManager : public NetworkEvents, public Printable {
|
||||
public:
|
||||
NetworkManager();
|
||||
|
||||
bool begin();
|
||||
int hostByName(const char *aHostname, IPAddress &aResult);
|
||||
uint8_t *macAddress(uint8_t *mac);
|
||||
String macAddress();
|
||||
|
||||
bool setDefaultInterface(NetworkInterface &ifc);
|
||||
NetworkInterface *getDefaultInterface();
|
||||
|
||||
// Returns true if any interface (except AP) has assigned IPv4 or global IPv6
|
||||
bool isOnline();
|
||||
|
||||
size_t printTo(Print &out) const;
|
||||
|
||||
static const char *getHostname();
|
||||
static bool setHostname(const char *hostname);
|
||||
static bool hostname(const String &aHostname) {
|
||||
return setHostname(aHostname.c_str());
|
||||
}
|
||||
};
|
||||
|
||||
extern NetworkManager Network;
|
||||
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
Server.cpp - Server class for Raspberry Pi
|
||||
Copyright (c) 2016 Hristo Gochkov All right reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#include "NetworkServer.h"
|
||||
#include <lwip/sockets.h>
|
||||
#include <lwip/netdb.h>
|
||||
|
||||
#undef write
|
||||
#undef close
|
||||
|
||||
int NetworkServer::setTimeout(uint32_t seconds) {
|
||||
struct timeval tv;
|
||||
tv.tv_sec = seconds;
|
||||
tv.tv_usec = 0;
|
||||
if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(struct timeval)) < 0) {
|
||||
return -1;
|
||||
}
|
||||
return setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, (char *)&tv, sizeof(struct timeval));
|
||||
}
|
||||
|
||||
NetworkClient NetworkServer::available() {
|
||||
return accept();
|
||||
}
|
||||
|
||||
NetworkClient NetworkServer::accept() {
|
||||
if (!_listening) {
|
||||
return NetworkClient();
|
||||
}
|
||||
int client_sock;
|
||||
if (_accepted_sockfd >= 0) {
|
||||
client_sock = _accepted_sockfd;
|
||||
_accepted_sockfd = -1;
|
||||
} else {
|
||||
#if CONFIG_LWIP_IPV6
|
||||
struct sockaddr_in6 _client;
|
||||
int cs = sizeof(struct sockaddr_in6);
|
||||
#else
|
||||
struct sockaddr_in _client;
|
||||
int cs = sizeof(struct sockaddr_in);
|
||||
#endif
|
||||
#ifdef ESP_IDF_VERSION_MAJOR
|
||||
client_sock = lwip_accept(sockfd, (struct sockaddr *)&_client, (socklen_t *)&cs);
|
||||
#else
|
||||
client_sock = lwip_accept_r(sockfd, (struct sockaddr *)&_client, (socklen_t *)&cs);
|
||||
#endif
|
||||
}
|
||||
if (client_sock >= 0) {
|
||||
int val = 1;
|
||||
if (setsockopt(client_sock, SOL_SOCKET, SO_KEEPALIVE, (char *)&val, sizeof(int)) == ESP_OK) {
|
||||
val = _noDelay;
|
||||
if (setsockopt(client_sock, IPPROTO_TCP, TCP_NODELAY, (char *)&val, sizeof(int)) == ESP_OK) {
|
||||
return NetworkClient(client_sock);
|
||||
}
|
||||
}
|
||||
}
|
||||
return NetworkClient();
|
||||
}
|
||||
|
||||
void NetworkServer::begin(uint16_t port) {
|
||||
begin(port, 1);
|
||||
}
|
||||
|
||||
void NetworkServer::begin(uint16_t port, int enable) {
|
||||
if (_listening) {
|
||||
return;
|
||||
}
|
||||
if (port) {
|
||||
_port = port;
|
||||
}
|
||||
#if CONFIG_LWIP_IPV6
|
||||
struct sockaddr_in6 server;
|
||||
sockfd = socket(AF_INET6, SOCK_STREAM, 0);
|
||||
if (sockfd < 0) {
|
||||
return;
|
||||
}
|
||||
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int));
|
||||
server.sin6_family = AF_INET6;
|
||||
if (_addr.type() == IPv4) {
|
||||
memcpy(server.sin6_addr.s6_addr + 11, (uint8_t *)&_addr[0], 4);
|
||||
server.sin6_addr.s6_addr[10] = 0xFF;
|
||||
server.sin6_addr.s6_addr[11] = 0xFF;
|
||||
} else {
|
||||
memcpy(server.sin6_addr.s6_addr, (uint8_t *)&_addr[0], 16);
|
||||
}
|
||||
memset(server.sin6_addr.s6_addr, 0x0, 16);
|
||||
server.sin6_port = htons(_port);
|
||||
#else
|
||||
struct sockaddr_in server;
|
||||
memset(&server, 0x0, sizeof(sockaddr_in));
|
||||
server.sin_family = AF_INET;
|
||||
sockfd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (sockfd < 0) {
|
||||
return;
|
||||
}
|
||||
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int));
|
||||
memcpy((uint8_t *)&(server.sin_addr.s_addr), (uint8_t *)&_addr[0], 4);
|
||||
server.sin_port = htons(_port);
|
||||
#endif
|
||||
if (bind(sockfd, (struct sockaddr *)&server, sizeof(server)) < 0) {
|
||||
return;
|
||||
}
|
||||
if (listen(sockfd, _max_clients) < 0) {
|
||||
return;
|
||||
}
|
||||
fcntl(sockfd, F_SETFL, O_NONBLOCK);
|
||||
_listening = true;
|
||||
_noDelay = false;
|
||||
_accepted_sockfd = -1;
|
||||
}
|
||||
|
||||
void NetworkServer::setNoDelay(bool nodelay) {
|
||||
_noDelay = nodelay;
|
||||
}
|
||||
|
||||
bool NetworkServer::getNoDelay() {
|
||||
return _noDelay;
|
||||
}
|
||||
|
||||
bool NetworkServer::hasClient() {
|
||||
if (_accepted_sockfd >= 0) {
|
||||
return true;
|
||||
}
|
||||
#if CONFIG_LWIP_IPV6
|
||||
struct sockaddr_in6 _client;
|
||||
int cs = sizeof(struct sockaddr_in6);
|
||||
#else
|
||||
struct sockaddr _client;
|
||||
int cs = sizeof(struct sockaddr);
|
||||
#endif
|
||||
#ifdef ESP_IDF_VERSION_MAJOR
|
||||
_accepted_sockfd = lwip_accept(sockfd, (struct sockaddr *)&_client, (socklen_t *)&cs);
|
||||
#else
|
||||
_accepted_sockfd = lwip_accept_r(sockfd, (struct sockaddr *)&_client, (socklen_t *)&cs);
|
||||
#endif
|
||||
if (_accepted_sockfd >= 0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void NetworkServer::end() {
|
||||
#ifdef ESP_IDF_VERSION_MAJOR
|
||||
lwip_close(sockfd);
|
||||
#else
|
||||
lwip_close_r(sockfd);
|
||||
#endif
|
||||
sockfd = -1;
|
||||
_listening = false;
|
||||
}
|
||||
|
||||
void NetworkServer::close() {
|
||||
end();
|
||||
}
|
||||
|
||||
void NetworkServer::stop() {
|
||||
end();
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
Server.h - Server class for Raspberry Pi
|
||||
Copyright (c) 2016 Hristo Gochkov All right reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "Arduino.h"
|
||||
#include "Server.h"
|
||||
#include "NetworkClient.h"
|
||||
#include "IPAddress.h"
|
||||
|
||||
class NetworkServer {
|
||||
private:
|
||||
int sockfd;
|
||||
int _accepted_sockfd = -1;
|
||||
IPAddress _addr;
|
||||
uint16_t _port;
|
||||
uint8_t _max_clients;
|
||||
bool _listening;
|
||||
bool _noDelay = false;
|
||||
|
||||
public:
|
||||
void listenOnLocalhost() {}
|
||||
|
||||
NetworkServer(uint16_t port = 80, uint8_t max_clients = 4)
|
||||
: sockfd(-1), _accepted_sockfd(-1), _addr(), _port(port), _max_clients(max_clients), _listening(false), _noDelay(false) {
|
||||
log_v("NetworkServer::NetworkServer(port=%d, ...)", port);
|
||||
}
|
||||
NetworkServer(const IPAddress &addr, uint16_t port = 80, uint8_t max_clients = 4)
|
||||
: sockfd(-1), _accepted_sockfd(-1), _addr(addr), _port(port), _max_clients(max_clients), _listening(false), _noDelay(false) {
|
||||
log_v("NetworkServer::NetworkServer(addr=%s, port=%d, ...)", addr.toString().c_str(), port);
|
||||
}
|
||||
~NetworkServer() {
|
||||
end();
|
||||
}
|
||||
NetworkClient available() __attribute__((deprecated("Renamed to accept().")));
|
||||
NetworkClient accept();
|
||||
void begin(uint16_t port = 0);
|
||||
void begin(uint16_t port, int reuse_enable);
|
||||
void setNoDelay(bool nodelay);
|
||||
bool getNoDelay();
|
||||
bool hasClient();
|
||||
|
||||
void end();
|
||||
void close();
|
||||
void stop();
|
||||
operator bool() {
|
||||
return _listening;
|
||||
}
|
||||
int setTimeout(uint32_t seconds);
|
||||
};
|
||||
@@ -0,0 +1,418 @@
|
||||
/*
|
||||
Udp.cpp - UDP class for Raspberry Pi
|
||||
Copyright (c) 2016 Hristo Gochkov All right reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "NetworkUdp.h"
|
||||
#include <new> //std::nothrow
|
||||
#include <lwip/sockets.h>
|
||||
#include <lwip/netdb.h>
|
||||
#include <errno.h>
|
||||
|
||||
#undef write
|
||||
#undef read
|
||||
|
||||
NetworkUDP::NetworkUDP() : udp_server(-1), server_port(0), remote_port(0), tx_buffer(0), tx_buffer_len(0), rx_buffer(0) {}
|
||||
|
||||
NetworkUDP::~NetworkUDP() {
|
||||
stop();
|
||||
}
|
||||
|
||||
uint8_t NetworkUDP::begin(IPAddress address, uint16_t port) {
|
||||
stop();
|
||||
|
||||
server_port = port;
|
||||
|
||||
tx_buffer = (char *)malloc(1460);
|
||||
if (!tx_buffer) {
|
||||
log_e("could not create tx buffer: %d", errno);
|
||||
return 0;
|
||||
}
|
||||
tx_buffer_len = 0;
|
||||
|
||||
#if LWIP_IPV6
|
||||
if ((udp_server = socket((address.type() == IPv6) ? AF_INET6 : AF_INET, SOCK_DGRAM, 0)) == -1) {
|
||||
#else
|
||||
if ((udp_server = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
|
||||
#endif
|
||||
log_e("could not create socket: %d", errno);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int yes = 1;
|
||||
if (setsockopt(udp_server, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0) {
|
||||
log_e("could not set socket option: %d", errno);
|
||||
stop();
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct sockaddr_storage serveraddr = {};
|
||||
size_t sock_size = 0;
|
||||
#if LWIP_IPV6
|
||||
if (address.type() == IPv6) {
|
||||
struct sockaddr_in6 *tmpaddr = (struct sockaddr_in6 *)&serveraddr;
|
||||
ip_addr_t addr;
|
||||
address.to_ip_addr_t(&addr);
|
||||
memset((char *)tmpaddr, 0, sizeof(struct sockaddr_in));
|
||||
tmpaddr->sin6_family = AF_INET6;
|
||||
tmpaddr->sin6_port = htons(server_port);
|
||||
tmpaddr->sin6_scope_id = addr.u_addr.ip6.zone;
|
||||
inet6_addr_from_ip6addr(&tmpaddr->sin6_addr, ip_2_ip6(&addr));
|
||||
tmpaddr->sin6_flowinfo = 0;
|
||||
sock_size = sizeof(sockaddr_in6);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
struct sockaddr_in *tmpaddr = (struct sockaddr_in *)&serveraddr;
|
||||
memset((char *)tmpaddr, 0, sizeof(struct sockaddr_in));
|
||||
tmpaddr->sin_family = AF_INET;
|
||||
tmpaddr->sin_port = htons(server_port);
|
||||
tmpaddr->sin_addr.s_addr = (in_addr_t)address;
|
||||
sock_size = sizeof(sockaddr_in);
|
||||
}
|
||||
if (bind(udp_server, (sockaddr *)&serveraddr, sock_size) == -1) {
|
||||
log_e("could not bind socket: %d", errno);
|
||||
stop();
|
||||
return 0;
|
||||
}
|
||||
fcntl(udp_server, F_SETFL, O_NONBLOCK);
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint8_t NetworkUDP::begin(uint16_t p) {
|
||||
return begin(IPAddress(), p);
|
||||
}
|
||||
|
||||
uint8_t NetworkUDP::beginMulticast(IPAddress address, uint16_t p) {
|
||||
if (begin(IPAddress(), p)) {
|
||||
ip_addr_t addr;
|
||||
address.to_ip_addr_t(&addr);
|
||||
if (ip_addr_ismulticast(&addr)) {
|
||||
#if LWIP_IPV6
|
||||
if (address.type() == IPv6) {
|
||||
struct ipv6_mreq mreq;
|
||||
bool joined = false;
|
||||
inet6_addr_from_ip6addr(&mreq.ipv6mr_multiaddr, ip_2_ip6(&addr));
|
||||
|
||||
// iterate on each interface
|
||||
for (netif *intf = netif_list; intf != nullptr; intf = intf->next) {
|
||||
mreq.ipv6mr_interface = intf->num + 1;
|
||||
if (intf->name[0] != 'l' || intf->name[1] != 'o') { // skip 'lo' local interface
|
||||
int ret = setsockopt(udp_server, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq));
|
||||
if (ret >= 0) {
|
||||
joined = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!joined) {
|
||||
log_e("could not join igmp: %d", errno);
|
||||
stop();
|
||||
return 0;
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
struct ip_mreq mreq;
|
||||
mreq.imr_multiaddr.s_addr = (in_addr_t)address;
|
||||
mreq.imr_interface.s_addr = INADDR_ANY;
|
||||
if (setsockopt(udp_server, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) {
|
||||
log_e("could not join igmp: %d", errno);
|
||||
stop();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
multicast_ip = address;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void NetworkUDP::stop() {
|
||||
if (tx_buffer) {
|
||||
free(tx_buffer);
|
||||
tx_buffer = NULL;
|
||||
}
|
||||
tx_buffer_len = 0;
|
||||
if (rx_buffer) {
|
||||
cbuf *b = rx_buffer;
|
||||
rx_buffer = NULL;
|
||||
delete b;
|
||||
}
|
||||
if (udp_server == -1) {
|
||||
return;
|
||||
}
|
||||
ip_addr_t addr;
|
||||
multicast_ip.to_ip_addr_t(&addr);
|
||||
if (!ip_addr_isany(&addr)) {
|
||||
#if LWIP_IPV6
|
||||
if (multicast_ip.type() == IPv6) {
|
||||
struct ipv6_mreq mreq;
|
||||
inet6_addr_from_ip6addr(&mreq.ipv6mr_multiaddr, ip_2_ip6(&addr));
|
||||
|
||||
// iterate on each interface
|
||||
for (netif *intf = netif_list; intf != nullptr; intf = intf->next) {
|
||||
mreq.ipv6mr_interface = intf->num + 1;
|
||||
if (intf->name[0] != 'l' || intf->name[1] != 'o') { // skip 'lo' local interface
|
||||
setsockopt(udp_server, IPPROTO_IPV6, IPV6_LEAVE_GROUP, &mreq, sizeof(mreq));
|
||||
}
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
struct ip_mreq mreq;
|
||||
mreq.imr_multiaddr.s_addr = (in_addr_t)multicast_ip;
|
||||
mreq.imr_interface.s_addr = (in_addr_t)0;
|
||||
setsockopt(udp_server, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq));
|
||||
}
|
||||
// now common code for v4/v6
|
||||
multicast_ip = IPAddress();
|
||||
}
|
||||
close(udp_server);
|
||||
udp_server = -1;
|
||||
}
|
||||
|
||||
int NetworkUDP::beginMulticastPacket() {
|
||||
if (!server_port || multicast_ip == IPAddress()) {
|
||||
return 0;
|
||||
}
|
||||
remote_ip = multicast_ip;
|
||||
remote_port = server_port;
|
||||
return beginPacket();
|
||||
}
|
||||
|
||||
int NetworkUDP::beginPacket() {
|
||||
if (!remote_port) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// allocate tx_buffer if is necessary
|
||||
if (!tx_buffer) {
|
||||
tx_buffer = (char *)malloc(1460);
|
||||
if (!tx_buffer) {
|
||||
log_e("could not create tx buffer: %d", errno);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
tx_buffer_len = 0;
|
||||
|
||||
// check whereas socket is already open
|
||||
if (udp_server != -1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ((udp_server = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
|
||||
log_e("could not create socket: %d", errno);
|
||||
return 0;
|
||||
}
|
||||
|
||||
fcntl(udp_server, F_SETFL, O_NONBLOCK);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int NetworkUDP::beginPacket(IPAddress ip, uint16_t port) {
|
||||
remote_ip = ip;
|
||||
remote_port = port;
|
||||
return beginPacket();
|
||||
}
|
||||
|
||||
int NetworkUDP::beginPacket(const char *host, uint16_t port) {
|
||||
struct hostent *server;
|
||||
server = gethostbyname(host);
|
||||
if (server == NULL) {
|
||||
log_e("could not get host from dns: %d", errno);
|
||||
return 0;
|
||||
}
|
||||
return beginPacket(IPAddress((const uint8_t *)(server->h_addr_list[0])), port);
|
||||
}
|
||||
|
||||
int NetworkUDP::endPacket() {
|
||||
ip_addr_t addr;
|
||||
remote_ip.to_ip_addr_t(&addr);
|
||||
|
||||
if (remote_ip.type() == IPv4) {
|
||||
struct sockaddr_in recipient;
|
||||
recipient.sin_addr.s_addr = (uint32_t)remote_ip;
|
||||
recipient.sin_family = AF_INET;
|
||||
recipient.sin_port = htons(remote_port);
|
||||
int sent = sendto(udp_server, tx_buffer, tx_buffer_len, 0, (struct sockaddr *)&recipient, sizeof(recipient));
|
||||
if (sent < 0) {
|
||||
log_e("could not send data: %d", errno);
|
||||
return 0;
|
||||
}
|
||||
#if LWIP_IPV6
|
||||
} else {
|
||||
struct sockaddr_in6 recipient;
|
||||
recipient.sin6_flowinfo = 0;
|
||||
recipient.sin6_addr = *(in6_addr *)(ip_addr_t *)(&addr);
|
||||
recipient.sin6_family = AF_INET6;
|
||||
recipient.sin6_port = htons(remote_port);
|
||||
recipient.sin6_scope_id = remote_ip.zone();
|
||||
int sent = sendto(udp_server, tx_buffer, tx_buffer_len, 0, (struct sockaddr *)&recipient, sizeof(recipient));
|
||||
if (sent < 0) {
|
||||
log_e("could not send data: %d", errno);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
size_t NetworkUDP::write(uint8_t data) {
|
||||
if (tx_buffer_len == 1460) {
|
||||
endPacket();
|
||||
tx_buffer_len = 0;
|
||||
}
|
||||
tx_buffer[tx_buffer_len++] = data;
|
||||
return 1;
|
||||
}
|
||||
|
||||
size_t NetworkUDP::write(const uint8_t *buffer, size_t size) {
|
||||
size_t i;
|
||||
for (i = 0; i < size; i++) {
|
||||
write(buffer[i]);
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
void NetworkUDP::flush() {
|
||||
clear();
|
||||
}
|
||||
|
||||
int NetworkUDP::parsePacket() {
|
||||
if (rx_buffer) {
|
||||
return 0;
|
||||
}
|
||||
struct sockaddr_storage si_other_storage; // enough storage for v4 and v6
|
||||
socklen_t slen = sizeof(sockaddr_storage);
|
||||
int len = 0;
|
||||
if (ioctl(udp_server, FIONREAD, &len) == -1) {
|
||||
log_e("could not check for data in buffer length: %d", errno);
|
||||
return 0;
|
||||
}
|
||||
if (!len) {
|
||||
return 0;
|
||||
}
|
||||
char *buf = (char *)malloc(1460);
|
||||
if (!buf) {
|
||||
return 0;
|
||||
}
|
||||
if ((len = recvfrom(udp_server, buf, 1460, MSG_DONTWAIT, (struct sockaddr *)&si_other_storage, (socklen_t *)&slen)) == -1) {
|
||||
free(buf);
|
||||
if (errno == EWOULDBLOCK) {
|
||||
return 0;
|
||||
}
|
||||
log_e("could not receive data: %d", errno);
|
||||
return 0;
|
||||
}
|
||||
if (si_other_storage.ss_family == AF_INET) {
|
||||
struct sockaddr_in &si_other = (sockaddr_in &)si_other_storage;
|
||||
remote_ip = IPAddress(si_other.sin_addr.s_addr);
|
||||
remote_port = ntohs(si_other.sin_port);
|
||||
}
|
||||
#if LWIP_IPV6
|
||||
else if (si_other_storage.ss_family == AF_INET6) {
|
||||
struct sockaddr_in6 &si_other = (sockaddr_in6 &)si_other_storage;
|
||||
remote_ip = IPAddress(IPv6, (uint8_t *)&si_other.sin6_addr, si_other.sin6_scope_id); // force IPv6
|
||||
ip_addr_t addr;
|
||||
remote_ip.to_ip_addr_t(&addr);
|
||||
/* Dual-stack: Unmap IPv4 mapped IPv6 addresses */
|
||||
if (remote_ip.type() == IPv6 && ip6_addr_isipv4mappedipv6(ip_2_ip6(&addr))) {
|
||||
unmap_ipv4_mapped_ipv6(ip_2_ip4(&addr), ip_2_ip6(&addr));
|
||||
IP_SET_TYPE_VAL(addr, IPADDR_TYPE_V4);
|
||||
remote_ip.from_ip_addr_t(&addr);
|
||||
}
|
||||
remote_port = ntohs(si_other.sin6_port);
|
||||
} else {
|
||||
remote_ip = ip_addr_any.u_addr.ip4.addr;
|
||||
remote_port = 0;
|
||||
}
|
||||
#else
|
||||
else {
|
||||
remote_ip = ip_addr_any.addr;
|
||||
remote_port = 0;
|
||||
}
|
||||
#endif // LWIP_IPV6=1
|
||||
if (len > 0) {
|
||||
rx_buffer = new (std::nothrow) cbuf(len);
|
||||
rx_buffer->write(buf, len);
|
||||
}
|
||||
free(buf);
|
||||
return len;
|
||||
}
|
||||
|
||||
int NetworkUDP::available() {
|
||||
if (!rx_buffer) {
|
||||
return 0;
|
||||
}
|
||||
return rx_buffer->available();
|
||||
}
|
||||
|
||||
int NetworkUDP::read() {
|
||||
if (!rx_buffer) {
|
||||
return -1;
|
||||
}
|
||||
int out = rx_buffer->read();
|
||||
if (!rx_buffer->available()) {
|
||||
cbuf *b = rx_buffer;
|
||||
rx_buffer = 0;
|
||||
delete b;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
int NetworkUDP::read(unsigned char *buffer, size_t len) {
|
||||
return read((char *)buffer, len);
|
||||
}
|
||||
|
||||
int NetworkUDP::read(char *buffer, size_t len) {
|
||||
if (!rx_buffer) {
|
||||
return 0;
|
||||
}
|
||||
int out = rx_buffer->read(buffer, len);
|
||||
if (!rx_buffer->available()) {
|
||||
cbuf *b = rx_buffer;
|
||||
rx_buffer = 0;
|
||||
delete b;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
int NetworkUDP::peek() {
|
||||
if (!rx_buffer) {
|
||||
return -1;
|
||||
}
|
||||
return rx_buffer->peek();
|
||||
}
|
||||
|
||||
void NetworkUDP::clear() {
|
||||
if (!rx_buffer) {
|
||||
return;
|
||||
}
|
||||
cbuf *b = rx_buffer;
|
||||
rx_buffer = 0;
|
||||
delete b;
|
||||
}
|
||||
|
||||
IPAddress NetworkUDP::remoteIP() {
|
||||
return remote_ip;
|
||||
}
|
||||
|
||||
uint16_t NetworkUDP::remotePort() {
|
||||
return remote_port;
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Udp.cpp: Library to send/receive UDP packets.
|
||||
*
|
||||
* NOTE: UDP is fast, but has some important limitations (thanks to Warren Gray for mentioning these)
|
||||
* 1) UDP does not guarantee the order in which assembled UDP packets are received. This
|
||||
* might not happen often in practice, but in larger network topologies, a UDP
|
||||
* packet can be received out of sequence.
|
||||
* 2) UDP does not guard against lost packets - so packets *can* disappear without the sender being
|
||||
* aware of it. Again, this may not be a concern in practice on small local networks.
|
||||
* For more information, see http://www.cafeaulait.org/course/week12/35.html
|
||||
*
|
||||
* MIT License:
|
||||
* Copyright (c) 2008 Bjoern Hartmann
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* bjoern@cs.stanford.edu 12/30/2008
|
||||
*/
|
||||
|
||||
#ifndef _NETWORKUDP_H_
|
||||
#define _NETWORKUDP_H_
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <Udp.h>
|
||||
#include <cbuf.h>
|
||||
|
||||
class NetworkUDP : public UDP {
|
||||
private:
|
||||
int udp_server;
|
||||
IPAddress multicast_ip;
|
||||
IPAddress remote_ip;
|
||||
uint16_t server_port;
|
||||
uint16_t remote_port;
|
||||
char *tx_buffer;
|
||||
size_t tx_buffer_len;
|
||||
cbuf *rx_buffer;
|
||||
|
||||
public:
|
||||
NetworkUDP();
|
||||
~NetworkUDP();
|
||||
uint8_t begin(IPAddress a, uint16_t p);
|
||||
uint8_t begin(uint16_t p);
|
||||
uint8_t beginMulticast(IPAddress a, uint16_t p);
|
||||
void stop();
|
||||
int beginMulticastPacket();
|
||||
int beginPacket();
|
||||
int beginPacket(IPAddress ip, uint16_t port);
|
||||
int beginPacket(const char *host, uint16_t port);
|
||||
int endPacket();
|
||||
size_t write(uint8_t);
|
||||
size_t write(const uint8_t *buffer, size_t size);
|
||||
[[deprecated("Use clear() instead.")]]
|
||||
void flush(); // Print::flush tx
|
||||
int parsePacket();
|
||||
int available();
|
||||
int read();
|
||||
int read(unsigned char *buffer, size_t len);
|
||||
int read(char *buffer, size_t len);
|
||||
int peek();
|
||||
void clear(); // clear rx
|
||||
IPAddress remoteIP();
|
||||
uint16_t remotePort();
|
||||
};
|
||||
|
||||
#endif /* _NETWORKUDP_H_ */
|
||||
Reference in New Issue
Block a user