This commit is contained in:
2026-05-22 21:52:50 +03:00
commit be7c60e4dd
1854 changed files with 583428 additions and 0 deletions
+9
View File
@@ -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
+14
View File
@@ -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"
+690
View File
@@ -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;
}
+112
View File
@@ -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;
};
+417
View File
@@ -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 "";
}
}
+312
View File
@@ -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();
};
+886
View File
@@ -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;
}
+112
View File
@@ -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;
};
+212
View File
@@ -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;
+37
View File
@@ -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;
+172
View File
@@ -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();
}
+65
View File
@@ -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);
};
+418
View File
@@ -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;
}
+80
View File
@@ -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_ */