diff --git a/lib/libesp32/ESP32-to-ESP8266-compat/src/ESP32Wifi.cpp b/lib/libesp32/ESP32-to-ESP8266-compat/src/ESP32Wifi.cpp index b700f2955..43ec90479 100644 --- a/lib/libesp32/ESP32-to-ESP8266-compat/src/ESP32Wifi.cpp +++ b/lib/libesp32/ESP32-to-ESP8266-compat/src/ESP32Wifi.cpp @@ -91,6 +91,16 @@ bool WiFiClass32::getNetworkInfo(uint8_t i, String &ssid, uint8_t &encType, int3 return WiFi.getNetworkInfo(i, ssid, encType, rssi, bssid, channel); } +// from https://github.com/espressif/arduino-esp32/pull/7520 +static const int WIFI_WANT_IP6_BIT_ALT = BIT15; +bool WiFiClass32::IPv6(bool state) { + if (state) + WiFiGenericClass::setStatusBits(WIFI_WANT_IP6_BIT_ALT); + else + WiFiGenericClass::clearStatusBits(WIFI_WANT_IP6_BIT_ALT); + return true; +} + void wifi_station_disconnect() { // erase ap: empty ssid, ... WiFi.disconnect(true, true); diff --git a/lib/libesp32/ESP32-to-ESP8266-compat/src/ESP8266WiFi.h b/lib/libesp32/ESP32-to-ESP8266-compat/src/ESP8266WiFi.h index f0a8476f6..1c1630b4c 100644 --- a/lib/libesp32/ESP32-to-ESP8266-compat/src/ESP8266WiFi.h +++ b/lib/libesp32/ESP32-to-ESP8266-compat/src/ESP8266WiFi.h @@ -50,6 +50,8 @@ public: static void forceSleepBegin(); static void forceSleepWake(); static bool getNetworkInfo(uint8_t i, String &ssid, uint8_t &encType, int32_t &rssi, uint8_t* &bssid, int32_t &channel, bool &hidden_scan); + + bool IPv6(bool state); // make sure it always exists even with older Arduino framework }; void wifi_station_disconnect(); diff --git a/lib/libesp32/ESP32-to-ESP8266-compat/src/IPAddress46.cpp b/lib/libesp32/ESP32-to-ESP8266-compat/src/IPAddress46.cpp index b0db1a5b8..365ab5801 100644 --- a/lib/libesp32/ESP32-to-ESP8266-compat/src/IPAddress46.cpp +++ b/lib/libesp32/ESP32-to-ESP8266-compat/src/IPAddress46.cpp @@ -45,17 +45,27 @@ #include #include +// Tasmota Logging +extern void AddLog(uint32_t loglevel, PGM_P formatP, ...); +enum LoggingLevels {LOG_LEVEL_NONE, LOG_LEVEL_ERROR, LOG_LEVEL_INFO, LOG_LEVEL_DEBUG, LOG_LEVEL_DEBUG_MORE}; + IPAddress46::IPAddress46(const IPAddress46& from) { ip_addr_copy(_ip, from._ip); } IPAddress46::IPAddress46() { - _ip = *IP_ANY_TYPE; // lwIP's v4-or-v6 generic address +#if LWIP_IPV6 + _ip = *IP6_ADDR_ANY; +#else + _ip = *IP_ADDR_ANY; +#endif + // _ip = *IP_ANY_TYPE; // lwIP's v4-or-v6 generic address } bool IPAddress46::isSet () const { - return !ip_addr_isany(&_ip) && ((*this) != IPADDR_NONE); + return !IP_IS_ANY_TYPE_VAL(_ip); + // return !ip_addr_isany(&_ip) && ((*this) != IPADDR_NONE); } IPAddress46::IPAddress46(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet) { @@ -152,8 +162,8 @@ bool IPAddress46::operator==(const uint8_t* addr) const { size_t IPAddress46::printTo(Print& p) const { size_t n = 0; - if (!isSet()) - return p.print(F("(IP unset)")); + // if (!isSet()) + // return p.print(F("(IP unset)")); #if LWIP_IPV6 if (isV6()) { @@ -267,4 +277,55 @@ bool IPAddress46::fromString6(const char *address) { return true; } +// -------------------------------------------------- +// Get host by name working for IPv6 +// -------------------------------------------------- +#include "lwip/dns.h" + +/** + * DNS callback + * @param name + * @param ipaddr + * @param callback_arg + */ +static void wifi_dns_found_callback(const char *name, const ip_addr_t *ipaddr, void *callback_arg) +{ + if(ipaddr) { + (*reinterpret_cast(callback_arg)) = IPAddress46(ipaddr); + } + WiFiGeneric46::DnsDone(); + // xEventGroupSetBits(_arduino_event_group, WIFI_DNS_DONE_BIT); +} + +int WiFiGeneric46::hostByName(const char* aHostname, IPAddress46& aResult) { + ip_addr_t addr; + aResult = static_cast(INADDR_NONE); + waitStatusBits(WIFI_DNS_IDLE_BIT, 16000); + clearStatusBits(WIFI_DNS_IDLE_BIT | WIFI_DNS_DONE_BIT); + err_t err = dns_gethostbyname_addrtype(aHostname, &addr, &wifi_dns_found_callback, &aResult, LWIP_DNS_ADDRTYPE_DEFAULT); + AddLog(LOG_LEVEL_DEBUG, "WIF: WiFiGeneric46::hostByName err=%i", err); + + if(err == ERR_OK) { + aResult = IPAddress46(&addr); + + if (!aResult.isSet()) { +#if LWIP_IPV6 + aResult.setV6(); +#else + aResult.setV4(); +#endif + } + } else if(err == ERR_INPROGRESS) { + waitStatusBits(WIFI_DNS_DONE_BIT, 15000); //real internal timeout in lwip library is 14[s] + clearStatusBits(WIFI_DNS_DONE_BIT); + } + setStatusBits(WIFI_DNS_IDLE_BIT); + + if(err == ERR_OK) { + AddLog(LOG_LEVEL_DEBUG, "WIF: WiFiGeneric46::hostByName Host: %s IP: %s", aHostname ? aHostname : "", aResult.toString().c_str()); + return 1; + } + return 0; +} + #endif diff --git a/lib/libesp32/ESP32-to-ESP8266-compat/src/IPAddress46.h b/lib/libesp32/ESP32-to-ESP8266-compat/src/IPAddress46.h index 952c3ef7d..8aeff2015 100644 --- a/lib/libesp32/ESP32-to-ESP8266-compat/src/IPAddress46.h +++ b/lib/libesp32/ESP32-to-ESP8266-compat/src/IPAddress46.h @@ -123,7 +123,7 @@ class IPAddress46: public Printable { static bool isValid(const char* arg); friend class EthernetClass; - friend class UDP; + friend class UDP46; friend class Client; friend class Server; friend class DhcpClass; @@ -181,4 +181,18 @@ class IPAddress46: public Printable { bool fromString4(const char *address); }; +// -------------------------------------------------------------------------------- +// We need to create a subclass of WiFiGenericClass to access protected methods +// -------------------------------------------------------------------------------- +#include "WiFiGeneric.h" + +class WiFiGeneric46 : public WiFiGenericClass +{ + public: + WiFiGeneric46() {}; + + static int hostByName(const char *aHostname, IPAddress46 &aResult); + static void DnsDone(void) { setStatusBits(WIFI_DNS_DONE_BIT); }; +}; + #endif // __IPADDRESS46_H diff --git a/lib/libesp32/ESP32-to-ESP8266-compat/src/Udp46.h b/lib/libesp32/ESP32-to-ESP8266-compat/src/Udp46.h new file mode 100644 index 000000000..85e891797 --- /dev/null +++ b/lib/libesp32/ESP32-to-ESP8266-compat/src/Udp46.h @@ -0,0 +1,93 @@ +/* + * 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 udp46_h +#define udp46_h + +#include +#include + +class UDP46: public Stream +{ + +public: + virtual uint8_t begin(uint16_t) =0; // initialize, start listening on specified port. Returns 1 if successful, 0 if there are no sockets available to use + virtual uint8_t beginMulticast(IPAddress46, uint16_t) { return 0; } // initialize, start listening on specified multicast IP address and port. Returns 1 if successful, 0 on failure + virtual void stop() =0; // Finish with the UDP socket + + // Sending UDP packets + + // Start building up a packet to send to the remote host specific in ip and port + // Returns 1 if successful, 0 if there was a problem with the supplied IP address or port + virtual int beginPacket(IPAddress46 ip, uint16_t port) =0; + // Start building up a packet to send to the remote host specific in host and port + // Returns 1 if successful, 0 if there was a problem resolving the hostname or port + virtual int beginPacket(const char *host, uint16_t port) =0; + // Finish off this packet and send it + // Returns 1 if the packet was sent successfully, 0 if there was an error + virtual int endPacket() =0; + // Write a single byte into the packet + virtual size_t write(uint8_t) =0; + // Write size bytes from buffer into the packet + virtual size_t write(const uint8_t *buffer, size_t size) =0; + + // Start processing the next available incoming packet + // Returns the size of the packet in bytes, or 0 if no packets are available + virtual int parsePacket() =0; + // Number of bytes remaining in the current packet + virtual int available() =0; + // Read a single byte from the current packet + virtual int read() =0; + // Read up to len bytes from the current packet and place them into buffer + // Returns the number of bytes read, or 0 if none are available + virtual int read(unsigned char* buffer, size_t len) =0; + // Read up to len characters from the current packet and place them into buffer + // Returns the number of characters read, or 0 if none are available + virtual int read(char* buffer, size_t len) =0; + // Return the next byte from the current packet without moving on to the next byte + virtual int peek() =0; + virtual void flush() =0; // Finish reading the current packet + + // Return the IP address of the host who sent the current incoming packet + virtual IPAddress46 remoteIP() =0; + // Return the port of the host who sent the current incoming packet + virtual uint16_t remotePort() =0; +protected: + uint8_t* rawIPAddress(IPAddress46& addr) + { + return addr.raw_address(); + } +}; + +#endif diff --git a/lib/libesp32/ESP32-to-ESP8266-compat/src/WiFiUdp46.cpp b/lib/libesp32/ESP32-to-ESP8266-compat/src/WiFiUdp46.cpp new file mode 100644 index 000000000..79c34f94f --- /dev/null +++ b/lib/libesp32/ESP32-to-ESP8266-compat/src/WiFiUdp46.cpp @@ -0,0 +1,335 @@ +/* + 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 "WiFiUdp46.h" +#include +#include +#include + +#undef write +#undef read + +// Tasmota Logging +extern void AddLog(uint32_t loglevel, PGM_P formatP, ...); +enum LoggingLevels {LOG_LEVEL_NONE, LOG_LEVEL_ERROR, LOG_LEVEL_INFO, LOG_LEVEL_DEBUG, LOG_LEVEL_DEBUG_MORE}; + +WiFiUDP46::WiFiUDP46() +: udp_server(-1) +, server_port(0) +, remote_port(0) +, tx_buffer(0) +, tx_buffer_len(0) +, rx_buffer(0) +{} + +WiFiUDP46::~WiFiUDP46(){ + stop(); +} + +uint8_t WiFiUDP46::begin(IPAddress46 address, uint16_t port){ + stop(); + server_port = port; + + tx_buffer = new char[1460]; + if(!tx_buffer){ + log_e("could not create tx buffer: %d", errno); + return 0; + } + +#if LWIP_IPV6 + if ((udp_server=socket(AF_INET6, 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; + } + + // AddLog(LOG_LEVEL_DEBUG, "WiFiUDP46::begin socket called"); + 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; + } + + //AddLog(LOG_LEVEL_DEBUG, "WiFiUDP46::begin setsockopt called"); + + struct sockaddr* sock_addr = NULL; + size_t sock_size = 0; + struct sockaddr_in addr; +#if LWIP_IPV6 + struct sockaddr_in6 addr6; + if (address.isV6()) { + // AddLog(LOG_LEVEL_DEBUG, "WiFiUDP46::begin set IPv6"); + memset((char *) &addr6, 0, sizeof(sockaddr_in6)); + addr6.sin6_len = sizeof(sockaddr_in6); + addr6.sin6_family = AF_INET6; + addr6.sin6_port = htons(server_port); + addr6.sin6_addr = *(in6_addr*)(ip_addr_t*)address; + addr6.sin6_addr = in6addr_any; + addr6.sin6_flowinfo = 0; + sock_addr = (struct sockaddr*)&addr6; + sock_size = sizeof(sockaddr_in6); + + // AddLog(LOG_LEVEL_DEBUG, "SOCK_ADDR_TYPE_MATCH(name, sock)=%i", SOCK_ADDR_TYPE_MATCH(sock_addr, sock_size)); + } else +#endif + if (1) { + memset((char *) &addr, 0, sizeof(sockaddr_in)); + addr.sin_family = AF_INET; + addr.sin_port = htons(server_port); + addr.sin_addr.s_addr = (in_addr_t)address; + sock_addr = (struct sockaddr*)&addr; + sock_size = sizeof(sockaddr_in); + } + //AddLog(LOG_LEVEL_DEBUG, "WiFiUDP46::begin udp_server=%p sock_addr=%p sock_size=%i", udp_server, sock_addr, sock_size); + if(bind(udp_server , sock_addr, sock_size) == -1){ + AddLog(LOG_LEVEL_DEBUG, "WIF: WiFiUDP46::begin bind error=%o", errno); + log_e("could not bind socket: %d", errno); + stop(); + return 0; + } + fcntl(udp_server, F_SETFL, O_NONBLOCK); + return 1; +} + +uint8_t WiFiUDP46::begin(uint16_t p){ + return begin(IPAddress46(), p); +} + +uint8_t WiFiUDP46::beginMulticast(IPAddress46 a, uint16_t p){ + if(begin(IPAddress46(), p)){ + if(!ip_addr_isany((ip_addr_t*)a)){ + struct ip_mreq mreq; + mreq.imr_multiaddr.s_addr = (in_addr_t)a; + 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 = a; + } + return 1; + } + return 0; +} + +void WiFiUDP46::stop(){ + if(tx_buffer){ + delete[] 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; + if(!ip_addr_isany((ip_addr_t*)multicast_ip)){ + struct ip_mreq mreq; + mreq.imr_multiaddr.s_addr = (in_addr_t)multicast_ip; + mreq.imr_interface.s_addr = (in_addr_t)0; +#if LWIP_IPV6 + setsockopt(udp_server, IPPROTO_IPV6, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)); +#else + setsockopt(udp_server, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)); +#endif + multicast_ip = IPAddress46(INADDR_ANY); + } + close(udp_server); + udp_server = -1; +} + +int WiFiUDP46::beginMulticastPacket(){ + if(!server_port || multicast_ip == IPAddress46(INADDR_ANY)) + return 0; + remote_ip = multicast_ip; + remote_port = server_port; + return beginPacket(); +} + +int WiFiUDP46::beginPacket(){ + if(!remote_port) + return 0; + + // allocate tx_buffer if is necessary + if(!tx_buffer){ + tx_buffer = new char[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 WiFiUDP46::beginPacket(IPAddress46 ip, uint16_t port){ + remote_ip = ip; + remote_port = port; + return beginPacket(); +} + +int WiFiUDP46::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(IPAddress46((const uint8_t *)(server->h_addr_list[0])), port); +} + +int WiFiUDP46::endPacket(){ + if (remote_ip.isV4()) { + struct sockaddr_in recipient; + recipient.sin_len = sizeof(sockaddr_in); + 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; + } + } else { + struct sockaddr_in6 recipient; + recipient.sin6_len = sizeof(sockaddr_in6); + recipient.sin6_flowinfo = 0; + recipient.sin6_addr = *(in6_addr*)(ip_addr_t*)remote_ip; + // recipient.sin6_family = AF_INET6; + recipient.sin6_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; + } + } + return 1; +} + +size_t WiFiUDP46::write(uint8_t data){ + if(tx_buffer_len == 1460){ + endPacket(); + tx_buffer_len = 0; + } + tx_buffer[tx_buffer_len++] = data; + return 1; +} + +size_t WiFiUDP46::write(const uint8_t *buffer, size_t size){ + size_t i; + for(i=0;i 0) { + rx_buffer = new cbuf(len); + rx_buffer->write(buf, len); + } + delete[] buf; + return len; +} + +int WiFiUDP46::available(){ + if(!rx_buffer) return 0; + return rx_buffer->available(); +} + +int WiFiUDP46::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 WiFiUDP46::read(unsigned char* buffer, size_t len){ + return read((char *)buffer, len); +} + +int WiFiUDP46::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 WiFiUDP46::peek(){ + if(!rx_buffer) return -1; + return rx_buffer->peek(); +} + +void WiFiUDP46::flush(){ + if(!rx_buffer) return; + cbuf *b = rx_buffer; + rx_buffer = 0; + delete b; +} + +IPAddress46 WiFiUDP46::remoteIP(){ + return remote_ip; +} + +uint16_t WiFiUDP46::remotePort(){ + return remote_port; +} diff --git a/lib/libesp32/ESP32-to-ESP8266-compat/src/WiFiUdp46.h b/lib/libesp32/ESP32-to-ESP8266-compat/src/WiFiUdp46.h new file mode 100644 index 000000000..d0436c51e --- /dev/null +++ b/lib/libesp32/ESP32-to-ESP8266-compat/src/WiFiUdp46.h @@ -0,0 +1,77 @@ +/* + * 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 _WIFIUDP46_H_ +#define _WIFIUDP46_H_ + +#include +#include +#include + +class WiFiUDP46 : public UDP46 { +private: + int udp_server; + IPAddress46 multicast_ip; + IPAddress46 remote_ip; + uint16_t server_port; + uint16_t remote_port; + char * tx_buffer; + size_t tx_buffer_len; + cbuf * rx_buffer; +public: + WiFiUDP46(); + ~WiFiUDP46(); + uint8_t begin(IPAddress46 a, uint16_t p); + uint8_t begin(uint16_t p); + uint8_t beginMulticast(IPAddress46 a, uint16_t p); + void stop(); + int beginMulticastPacket(); + int beginPacket(); + int beginPacket(IPAddress46 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); + int parsePacket(); + int available(); + int read(); + int read(unsigned char* buffer, size_t len); + int read(char* buffer, size_t len); + int peek(); + void flush(); + IPAddress46 remoteIP(); + uint16_t remotePort(); +}; + +#endif /* _WIFIUDP46_H_ */ diff --git a/lib/libesp32/berry_tasmota/src/be_udp_lib.cpp b/lib/libesp32/berry_tasmota/src/be_udp_lib.cpp index 27a97189f..9110be68c 100644 --- a/lib/libesp32/berry_tasmota/src/be_udp_lib.cpp +++ b/lib/libesp32/berry_tasmota/src/be_udp_lib.cpp @@ -15,21 +15,25 @@ #include #include -#include +#include #include "be_mapping.h" +// Tasmota Logging +extern void AddLog(uint32_t loglevel, PGM_P formatP, ...); +enum LoggingLevels {LOG_LEVEL_NONE, LOG_LEVEL_ERROR, LOG_LEVEL_INFO, LOG_LEVEL_DEBUG, LOG_LEVEL_DEBUG_MORE}; + extern "C" { // init() - WiFiUDP *be_udp_init_ntv(void) { - return new WiFiUDP(); + WiFiUDP46 *be_udp_init_ntv(void) { + return new WiFiUDP46(); } int32_t be_udp_init(struct bvm *vm) { return be_call_c_func(vm, (void*) &be_udp_init_ntv, "+.p", ""); } // deinit() - void *be_udp_deinit_ntv(WiFiUDP *udp) { + void *be_udp_deinit_ntv(WiFiUDP46 *udp) { if (udp != nullptr) { delete udp; } return nullptr; } @@ -38,20 +42,22 @@ extern "C" { } // udp.begin(address:string, port:int) -> bool - int32_t be_udp_begin_ntv(WiFiUDP *udp, const char *host, int32_t port) { - IPAddress addr((uint32_t)0); - // if no host or host is "" then we defult to INADDR_ANY (0.0.0.0) - if(host && (*host != 0) && !WiFiGenericClass::hostByName(host, addr)){ - return 0; - } + int32_t be_udp_begin_ntv(WiFiUDP46 *udp, int32_t port) { + IPAddress46 addr; + // AddLog(LOG_LEVEL_DEBUG, "BRY: udp.begin listening to '%s'", addr.toString().c_str()); return udp->begin(addr, port); } int32_t be_udp_begin(struct bvm *vm) { - return be_call_c_func(vm, (void*) &be_udp_begin_ntv, "b", ".si"); + if (be_top(vm) >= 3 && be_isstring(vm, 2)) { + // legacy string parameter, now ignored + return be_call_c_func(vm, (void*) &be_udp_begin_ntv, "b", ".-i"); + } else { + return be_call_c_func(vm, (void*) &be_udp_begin_ntv, "b", ".i"); + } } // udp.stop() -> nil - void be_udp_stop_ntv(WiFiUDP *udp) { + void be_udp_stop_ntv(WiFiUDP46 *udp) { udp->stop(); } int32_t be_udp_stop(struct bvm *vm) { @@ -59,23 +65,24 @@ extern "C" { } // udp.begin_multicast(address:string, port:int) -> nil - int32_t be_udp_begin_mcast_ntv(WiFiUDP *udp, const char *host, int32_t port) { - IPAddress addr((uint32_t)0); - if(!WiFiGenericClass::hostByName(host, addr)){ + int32_t be_udp_begin_mcast_ntv(WiFiUDP46 *udp, const char *host, int32_t port) { + IPAddress46 addr; + if(!WiFiGeneric46::hostByName(host, addr)){ return 0; } - return udp->WiFiUDP::beginMulticast(addr, port); + return udp->WiFiUDP46::beginMulticast(addr, port); } int32_t be_udp_begin_mcast(struct bvm *vm) { return be_call_c_func(vm, (void*) &be_udp_begin_mcast_ntv, "b", ".si"); } // udp.send(address:string, port:int, payload:bytes) -> bool - int32_t be_udp_send_ntv(WiFiUDP *udp, const char *host, int32_t port, const uint8_t* buf, int32_t len) { - IPAddress addr((uint32_t)0); - if (!WiFiGenericClass::hostByName(host, addr)){ + int32_t be_udp_send_ntv(WiFiUDP46 *udp, const char *host, int32_t port, const uint8_t* buf, int32_t len) { + IPAddress46 addr; + if (!WiFiGeneric46::hostByName(host, addr)){ return 0; } + // AddLog(LOG_LEVEL_DEBUG, "BRY: udp.begin got host '%s'", addr.toString().c_str()); if (!udp->beginPacket(addr, port)) { return 0; } int bw = udp->write(buf, len); if (!bw) { return 0; } @@ -87,7 +94,7 @@ extern "C" { } // udp.send_multicast(payload:bytes) -> bool - int32_t be_udp_send_mcast_ntv(WiFiUDP *udp, const uint8_t* buf, int32_t len) { + int32_t be_udp_send_mcast_ntv(WiFiUDP46 *udp, const uint8_t* buf, int32_t len) { if (!udp->beginMulticastPacket()) { return 0; } int bw = udp->write(buf, len); if (!bw) { return 0; } @@ -100,7 +107,7 @@ extern "C" { // udp.read() -> bytes or nil int32_t be_udp_read(struct bvm *vm) { - WiFiUDP *udp = (WiFiUDP*) be_convert_single_elt(vm, 1, NULL, NULL); + WiFiUDP46 *udp = (WiFiUDP46*) be_convert_single_elt(vm, 1, NULL, NULL); if (udp->parsePacket()) { int btr = udp->available(); // btr contains the size of bytes_to_read @@ -128,7 +135,7 @@ extern "C" { int32_t btr2 = udp->read(buf, btr); // set remotet ip - IPAddress remote_ip = udp->remoteIP(); + IPAddress46 remote_ip = udp->remoteIP(); be_pushstring(vm, remote_ip.toString().c_str()); be_setmember(vm, 1, "remote_ip"); be_pop(vm, 1); diff --git a/tasmota/tasmota.ino b/tasmota/tasmota.ino index a90a1c069..b5e98c07d 100644 --- a/tasmota/tasmota.ino +++ b/tasmota/tasmota.ino @@ -118,6 +118,8 @@ struct WIFI { bool wifi_test_AP_TIMEOUT = false; bool wifi_Test_Restart = false; bool wifi_Test_Save_SSID2 = false; + // IPv6 support, not guarded with #if LWIP_IPV6 to avoid bloating code with ifdefs + bool ipv6_local_link_called = false; // did we already enable IPv6 Local-Link address, needs to be redone at each reconnect } Wifi; typedef struct { diff --git a/tasmota/tasmota_support/support_command.ino b/tasmota/tasmota_support/support_command.ino index e50167def..ce66bffe1 100644 --- a/tasmota/tasmota_support/support_command.ino +++ b/tasmota/tasmota_support/support_command.ino @@ -802,7 +802,9 @@ void CmndStatus(void) } if ((0 == payload) || (5 == payload)) { - // WifiDumpAddressesIPv6(); +#if LWIP_IPV6 + if (5 == payload) { WifiDumpAddressesIPv6(); } +#endif // LWIP_IPV6 Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS5_NETWORK "\":{\"" D_CMND_HOSTNAME "\":\"%s\",\"" D_CMND_IPADDRESS "\":\"%_I\",\"" D_JSON_GATEWAY "\":\"%_I\",\"" D_JSON_SUBNETMASK "\":\"%_I\",\"" D_JSON_DNSSERVER "1\":\"%_I\",\"" D_JSON_DNSSERVER "2\":\"%_I\",\"" diff --git a/tasmota/tasmota_support/support_wifi.ino b/tasmota/tasmota_support/support_wifi.ino index 8fb65b4ed..439eb752e 100644 --- a/tasmota/tasmota_support/support_wifi.ino +++ b/tasmota/tasmota_support/support_wifi.ino @@ -213,6 +213,9 @@ void WifiBegin(uint8_t flag, uint8_t channel) { #endif // USE_EMULATION WiFi.persistent(false); // Solve possible wifi init errors (re-add at 6.2.1.16 #4044, #4083) +#if LWIP_IPV6 && defined(ESP32) + WiFi.IPv6(true); +#endif #ifdef USE_WIFI_RANGE_EXTENDER if (WiFi.getMode() != WIFI_AP_STA || !RgxApUp()) { // Preserve range extender connections (#17103) @@ -518,6 +521,17 @@ bool WifiHasIP(void) { } void WifiCheckIp(void) { +#if LWIP_IPV6 + if (WL_CONNECTED == WiFi.status()) { + if (!Wifi.ipv6_local_link_called) { + WiFi.enableIpV6(); + Wifi.ipv6_local_link_called = true; + // AddLog(LOG_LEVEL_DEBUG, PSTR("WIF: calling enableIpV6")); + } + + } +#endif + if ((WL_CONNECTED == WiFi.status()) && WifiHasIP()) { WifiSetState(1); Wifi.counter = WIFI_CHECK_SEC; @@ -530,11 +544,6 @@ void WifiCheckIp(void) { Settings->ipv4_address[2] = (uint32_t)WiFi.subnetMask(); Settings->ipv4_address[3] = (uint32_t)WiFi.dnsIP(); Settings->ipv4_address[4] = (uint32_t)WiFi.dnsIP(1); -#if LWIP_IPV6 - // create Link-local address - CreateLinkLocalIPv6(); - AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI "IPv6 Link-Local %s"), WifiGetIPv6LinkLocal().c_str()); -#endif // LWIP_IPV6 // Save current AP parameters for quick reconnect Settings->wifi_channel = WiFi.channel(); @@ -721,10 +730,21 @@ void WifiEnable(void) { //#include // sntp_servermode_dhcp() //#endif // ESP8266 +#ifdef ESP32 +void WifiEvents(arduino_event_t *event); +#endif + void WifiConnect(void) { if (!Settings->flag4.network_wifi) { return; } +#ifdef ESP32 + static bool wifi_event_registered = false; + if (!wifi_event_registered) { + WiFi.onEvent(WifiEvents); // register event listener only once + wifi_event_registered = true; + } +#endif // ESP32 WifiSetState(0); WifiSetOutputPower(); @@ -1039,3 +1059,51 @@ uint64_t WifiGetNtp(void) { ntp_server_id++; // Next server next time return 0; } + +// -------------------------------------------------------------------------------- +// Respond to some Arduino/esp-idf events for better IPv6 support +// -------------------------------------------------------------------------------- +#ifdef ESP32 +#include "IPAddress46.h" +// typedef void (*WiFiEventSysCb)(arduino_event_t *event); +void WifiEvents(arduino_event_t *event) { + switch (event->event_id) { + +#if LWIP_IPV6 + case ARDUINO_EVENT_WIFI_STA_GOT_IP6: + case ARDUINO_EVENT_ETH_GOT_IP6: + { + ip_addr_t ip_addr6; + ip_addr_copy_from_ip6(ip_addr6, event->event_info.got_ip6.ip6_info.ip); + IPAddress46 addr(ip_addr6); + AddLog(LOG_LEVEL_DEBUG, PSTR("WIF: IPv6 %s %s"), addr.isLocal() ? PSTR("Link-Local") : PSTR("Global"), addr.toString().c_str()); + } + break; +#endif // LWIP_IPV6 + case ARDUINO_EVENT_ETH_GOT_IP: + case ARDUINO_EVENT_WIFI_STA_GOT_IP: + { + ip_addr_t ip_addr4; + ip_addr_copy_from_ip4(ip_addr4, event->event_info.got_ip.ip_info.ip); + AddLog(LOG_LEVEL_DEBUG, PSTR("WIF: IPv4 %_I, mask %_I, gateway %_I"), + event->event_info.got_ip.ip_info.ip.addr, + event->event_info.got_ip.ip_info.netmask.addr, + event->event_info.got_ip.ip_info.gw.addr); + + } + break; + + case ARDUINO_EVENT_WIFI_STA_CONNECTED: + // AddLog(LOG_LEVEL_DEBUG, PSTR("WIF: Received ARDUINO_EVENT_WIFI_STA_CONNECTED")); + Wifi.ipv6_local_link_called = false; // not sure if this is needed, make sure link-local is restored at each reconnect + break; + case ARDUINO_EVENT_WIFI_STA_DISCONNECTED: + case ARDUINO_EVENT_WIFI_STA_AUTHMODE_CHANGE: + Wifi.ipv6_local_link_called = false; + break; + + default: + break; + } +} +#endif // ESP32