From 91680c21ca580082c24f0b0fb57e3d92b6b3b78f Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Mon, 20 Feb 2023 21:29:25 +0100 Subject: [PATCH 01/10] Tasmota changes for core 2.0.7 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Tasmota changes * Tasmota 2.0.6 with IPv6 support * IPv6 support * Ethernet driver JL1101 Co-authored-by: Danii Mirsanov * Update install-platformio-esp32.sh * Fix conflicts * Tasmota repos * overlooked `timeout` change to `timeout_ms` * Sets correct code for UART baud rate detection Co-authored-by: Rodrigo Garcia * General examples updates (#7727) (#196) * Remove inline code comments for the struct description * Renamed ESPNow examples * Changed example from C++ class to C struct * Updated MultiHomedServers * Updated BT Serial examples * Added short overview of exaples - missing Enterprise * Added draft of Enterprise header note * Fixed note in README * Fixed compilation error * Changed FunctionalInterrupt example * Fixes * Returned FunctionalInterrupts to original state + added modified version as new example Co-authored-by: Tomáš Pilný <34927466+PilnyTomas@users.noreply.github.com> --------- Co-authored-by: Rodrigo Garcia Co-authored-by: Tomáš Pilný <34927466+PilnyTomas@users.noreply.github.com> --- .github/scripts/install-arduino-core-esp32.sh | 2 +- .github/scripts/install-platformio-esp32.sh | 6 +- .github/workflows/push.yml | 8 - CMakeLists.txt | 17 -- README.md | 41 +-- cores/esp32/Esp.cpp | 4 + cores/esp32/IPAddress.cpp | 254 ++++++++++++++---- cores/esp32/IPAddress.h | 145 ++++++++-- cores/esp32/USBCDC.cpp | 2 +- libraries/Ethernet/src/ETH.cpp | 3 + libraries/Ethernet/src/ETH.h | 2 +- libraries/Update/src/Updater.cpp | 2 + .../WiFiTelnetToSerialIPv6.ino | 132 +++++++++ libraries/WiFi/src/WiFiClient.cpp | 62 ++++- libraries/WiFi/src/WiFiGeneric.cpp | 103 +++++-- libraries/WiFi/src/WiFiGeneric.h | 7 + libraries/WiFi/src/WiFiMulti.cpp | 7 + libraries/WiFi/src/WiFiMulti.h | 2 + libraries/WiFi/src/WiFiSTA.cpp | 13 + libraries/WiFi/src/WiFiSTA.h | 1 + libraries/WiFi/src/WiFiServer.cpp | 27 +- libraries/WiFi/src/WiFiServer.h | 1 - libraries/WiFi/src/WiFiUdp.cpp | 173 +++++++++--- tools/platformio-build.py | 11 - 24 files changed, 789 insertions(+), 236 deletions(-) mode change 100755 => 100644 .github/scripts/install-platformio-esp32.sh create mode 100644 libraries/WiFi/examples/WiFiTelnetToSerialIPv6/WiFiTelnetToSerialIPv6.ino diff --git a/.github/scripts/install-arduino-core-esp32.sh b/.github/scripts/install-arduino-core-esp32.sh index 8584da5b6e2..cc685711e15 100755 --- a/.github/scripts/install-arduino-core-esp32.sh +++ b/.github/scripts/install-arduino-core-esp32.sh @@ -20,7 +20,7 @@ if [ ! -d "$ARDUINO_ESP32_PATH" ]; then ln -s $GITHUB_WORKSPACE esp32 else echo "Cloning Core Repository..." - git clone https://github.com/espressif/arduino-esp32.git esp32 > /dev/null 2>&1 + git clone https://github.com/tasmota/arduino-esp32.git esp32 > /dev/null 2>&1 fi #echo "Updating Submodules ..." diff --git a/.github/scripts/install-platformio-esp32.sh b/.github/scripts/install-platformio-esp32.sh old mode 100755 new mode 100644 index b3f7b081ba1..be2e8eb56cd --- a/.github/scripts/install-platformio-esp32.sh +++ b/.github/scripts/install-platformio-esp32.sh @@ -1,7 +1,7 @@ #!/bin/bash export PLATFORMIO_ESP32_PATH="$HOME/.platformio/packages/framework-arduinoespressif32" -PLATFORMIO_ESP32_URL="https://github.com/platformio/platform-espressif32.git" +PLATFORMIO_ESP32_URL="https://github.com/tasmota/platform-espressif32.git" TOOLCHAIN_VERSION="8.4.0+2021r2-patch5" ESPTOOLPY_VERSION="~1.40400.0" @@ -40,12 +40,12 @@ replace_script+="data['packages']['tool-esptoolpy']['version']='$ESPTOOLPY_VERSI replace_script+="fp.seek(0);fp.truncate();json.dump(data, fp, indent=2);fp.close()" python -c "$replace_script" -if [ "$GITHUB_REPOSITORY" == "espressif/arduino-esp32" ]; then +if [ "$GITHUB_REPOSITORY" == "tasmota/arduino-esp32" ]; then echo "Linking Core..." ln -s $GITHUB_WORKSPACE "$PLATFORMIO_ESP32_PATH" else echo "Cloning Core Repository ..." - git clone --recursive https://github.com/espressif/arduino-esp32.git "$PLATFORMIO_ESP32_PATH" > /dev/null 2>&1 + git clone --recursive https://github.com/tasmota/arduino-esp32.git "$PLATFORMIO_ESP32_PATH" > /dev/null 2>&1 fi echo "PlatformIO for ESP32 has been installed" diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index fecf743dfc9..63507ae35a3 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -13,14 +13,6 @@ concurrency: cancel-in-progress: true jobs: - - cmake-check: - name: Check cmake file - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - run: bash ./.github/scripts/check-cmakelists.sh - # Ubuntu build-arduino-linux: name: Arduino ${{ matrix.chunk }} on ubuntu-latest diff --git a/CMakeLists.txt b/CMakeLists.txt index 2e9e0ccd3d4..c4a671fc3fb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -88,17 +88,9 @@ set(LIBRARY_SRCS libraries/HTTPClient/src/HTTPClient.cpp libraries/HTTPUpdate/src/HTTPUpdate.cpp libraries/LittleFS/src/LittleFS.cpp - libraries/Insights/src/Insights.cpp libraries/I2S/src/I2S.cpp libraries/NetBIOS/src/NetBIOS.cpp libraries/Preferences/src/Preferences.cpp - libraries/RainMaker/src/RMaker.cpp - libraries/RainMaker/src/RMakerNode.cpp - libraries/RainMaker/src/RMakerParam.cpp - libraries/RainMaker/src/RMakerDevice.cpp - libraries/RainMaker/src/RMakerType.cpp - libraries/RainMaker/src/RMakerQR.cpp - libraries/RainMaker/src/RMakerUtils.cpp libraries/SD_MMC/src/SD_MMC.cpp libraries/SD/src/SD.cpp libraries/SD/src/sd_diskio.cpp @@ -185,11 +177,9 @@ set(includedirs libraries/HTTPClient/src libraries/HTTPUpdate/src libraries/LittleFS/src - libraries/Insights/src libraries/I2S/src libraries/NetBIOS/src libraries/Preferences/src - libraries/RainMaker/src libraries/SD_MMC/src libraries/SD/src libraries/SimpleBLE/src @@ -251,13 +241,6 @@ endfunction() maybe_add_component(esp-dsp) -if(CONFIG_ESP_INSIGHTS_ENABLED) - maybe_add_component(esp_insights) -endif() -if(CONFIG_ESP_RMAKER_WORK_QUEUE_TASK_STACK) - maybe_add_component(esp_rainmaker) - maybe_add_component(qrcode) -endif() if(IDF_TARGET MATCHES "esp32s2|esp32s3" AND CONFIG_TINYUSB_ENABLED) maybe_add_component(arduino_tinyusb) endif() diff --git a/README.md b/README.md index 72496f2d170..e0faf6f2f9f 100644 --- a/README.md +++ b/README.md @@ -1,30 +1,7 @@ -# Arduino core for the ESP32, ESP32-S2, ESP32-S3 and ESP32-C3 +# Tasmota Arduino core for the ESP32, ESP32-S2, ESP32-S3 and ESP32-C3 -![Build Status](https://github.com/espressif/arduino-esp32/workflows/ESP32%20Arduino%20CI/badge.svg) [![Documentation Status](https://readthedocs.com/projects/espressif-arduino-esp32/badge/?version=latest)](https://docs.espressif.com/projects/arduino-esp32/en/latest/?badge=latest) -### Need help or have a question? Join the chat at [Gitter](https://gitter.im/espressif/arduino-esp32) or [open a new Discussion](https://github.com/espressif/arduino-esp32/discussions) - -## Contents - - - [Development Status](#development-status) - - [Development Planning](#development-planning) - - [Documentation](#documentation) - - [Supported Chips](#supported-chips) - - [Decoding exceptions](#decoding-exceptions) - - [Issue/Bug report template](#issuebug-report-template) - - [Contributing](#contributing) - -### Development Status - -Latest Stable Release [![Release Version](https://img.shields.io/github/release/espressif/arduino-esp32.svg?style=plastic)](https://github.com/espressif/arduino-esp32/releases/latest/) [![Release Date](https://img.shields.io/github/release-date/espressif/arduino-esp32.svg?style=plastic)](https://github.com/espressif/arduino-esp32/releases/latest/) [![Downloads](https://img.shields.io/github/downloads/espressif/arduino-esp32/latest/total.svg?style=plastic)](https://github.com/espressif/arduino-esp32/releases/latest/) - -Latest Development Release [![Release Version](https://img.shields.io/github/release/espressif/arduino-esp32/all.svg?style=plastic)](https://github.com/espressif/arduino-esp32/releases/) [![Release Date](https://img.shields.io/github/release-date-pre/espressif/arduino-esp32.svg?style=plastic)](https://github.com/espressif/arduino-esp32/releases/) [![Downloads](https://img.shields.io/github/downloads-pre/espressif/arduino-esp32/latest/total.svg?style=plastic)](https://github.com/espressif/arduino-esp32/releases/) - -### Development Planning - -Our Development is fully tracked on this public **[Roadmap 🎉](https://github.com/orgs/espressif/projects/3)** - -For even more information you can take a look at [Sprint Meeting notes](https://github.com/espressif/arduino-esp32/discussions/categories/sprints-meeting-notes) or join [Monthly Community Meetings 🔔](https://github.com/espressif/arduino-esp32/discussions/categories/monthly-community-meetings) +### [![GitHub Releases](https://img.shields.io/github/downloads/tasmota/arduino-esp32/total?label=downloads)](https://github.com/tasmota/arduino-esp32/releases/latest) ### Documentation @@ -44,17 +21,3 @@ Visit the [supported chips](https://docs.espressif.com/projects/arduino-esp32/en ### Decoding exceptions You can use [EspExceptionDecoder](https://github.com/me-no-dev/EspExceptionDecoder) to get meaningful call trace. - -### Issue/Bug report template - -Before reporting an issue, make sure you've searched for similar one that was already created. Also make sure to go through all the issues labelled as [Type: For reference](https://github.com/espressif/arduino-esp32/issues?q=is%3Aissue+label%3A%22Type%3A+For+reference%22+). - -Finally, if you are sure no one else had the issue, follow the **Issue template** or **Feature request template** while reporting any [new Issue](https://github.com/espressif/arduino-esp32/issues/new/choose). - -### Contributing - -We welcome contributions to the Arduino ESP32 project! - -See [contributing](https://docs.espressif.com/projects/arduino-esp32/en/latest/contributing.html) in the documentation for more information on how to contribute to the project. - -> We would like to have this repository in a polite and friendly atmosphere, so please be kind and respectful to others. For more details, look at [Code of Conduct](https://github.com/espressif/arduino-esp32/blob/master/CODE_OF_CONDUCT.md). diff --git a/cores/esp32/Esp.cpp b/cores/esp32/Esp.cpp index 4825b0d931c..bf14b2a6acc 100644 --- a/cores/esp32/Esp.cpp +++ b/cores/esp32/Esp.cpp @@ -240,6 +240,10 @@ String EspClass::getSketchMD5() md5.add(buf.get(), readBytes); lengthLeft -= readBytes; offset += readBytes; + + #if CONFIG_FREERTOS_UNICORE + delay(1); // Fix solo WDT + #endif } md5.calculate(); result = md5.toString(); diff --git a/cores/esp32/IPAddress.cpp b/cores/esp32/IPAddress.cpp index 0575363f254..b8f7b7aae1b 100644 --- a/cores/esp32/IPAddress.cpp +++ b/cores/esp32/IPAddress.cpp @@ -20,68 +20,93 @@ #include #include #include +#include -IPAddress::IPAddress() -{ - _address.dword = 0; +IPAddress::IPAddress() { +#if LWIP_IPV6 + _ip = *IP6_ADDR_ANY; +#else + _ip = *IP_ADDR_ANY; +#endif + // _ip = *IP_ANY_TYPE; // lwIP's v4-or-v6 generic address } -IPAddress::IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet) +IPAddress::IPAddress(const IPAddress& from) { - _address.bytes[0] = first_octet; - _address.bytes[1] = second_octet; - _address.bytes[2] = third_octet; - _address.bytes[3] = fourth_octet; + ip_addr_copy(_ip, from._ip); } -IPAddress::IPAddress(uint32_t address) -{ - _address.dword = address; +IPAddress::IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet) { + setV4(); + (*this)[0] = first_octet; + (*this)[1] = second_octet; + (*this)[2] = third_octet; + (*this)[3] = fourth_octet; } -IPAddress::IPAddress(const uint8_t *address) -{ - memcpy(_address.bytes, address, sizeof(_address.bytes)); +IPAddress::IPAddress(uint8_t o1, uint8_t o2, uint8_t o3, uint8_t o4, uint8_t o5, uint8_t o6, uint8_t o7, uint8_t o8, uint8_t o9, uint8_t o10, uint8_t o11, uint8_t o12, uint8_t o13, uint8_t o14, uint8_t o15, uint8_t o16) { + setV6(); + (*this)[0] = o1; + (*this)[1] = o2; + (*this)[2] = o3; + (*this)[3] = o4; + (*this)[4] = o5; + (*this)[5] = o6; + (*this)[6] = o7; + (*this)[7] = o8; + (*this)[8] = o9; + (*this)[9] = o10; + (*this)[10] = o11; + (*this)[11] = o12; + (*this)[12] = o13; + (*this)[13] = o14; + (*this)[14] = o15; + (*this)[15] = o16; } -IPAddress& IPAddress::operator=(const uint8_t *address) -{ - memcpy(_address.bytes, address, sizeof(_address.bytes)); - return *this; +IPAddress::IPAddress(IPType type, const uint8_t *address) { + if (type == IPv4) { + setV4(); + memcpy(&this->_ip.u_addr.ip4, address, 4); + } else if (type == IPv6) { + setV6(); + memcpy(&this->_ip.u_addr.ip6.addr[0], address, 16); + } else { +#if LWIP_IPV6 + _ip = *IP6_ADDR_ANY; +#else + _ip = *IP_ADDR_ANY; +#endif + } + } -IPAddress& IPAddress::operator=(uint32_t address) -{ - _address.dword = address; - return *this; +void IPAddress::ctor32(uint32_t address) { + setV4(); + v4() = address; } -bool IPAddress::operator==(const uint8_t* addr) const -{ - return memcmp(addr, _address.bytes, sizeof(_address.bytes)) == 0; +IPAddress::IPAddress(const uint8_t *address) { + setV4(); + (*this)[0] = address[0]; + (*this)[1] = address[1]; + (*this)[2] = address[2]; + (*this)[3] = address[3]; } -size_t IPAddress::printTo(Print& p) const -{ - size_t n = 0; - for(int i = 0; i < 3; i++) { - n += p.print(_address.bytes[i], DEC); - n += p.print('.'); +bool IPAddress::fromString(const char *address) { + if (!fromString4(address)) { +#if LWIP_IPV6 + return fromString6(address); +#else + return false; +#endif } - n += p.print(_address.bytes[3], DEC); - return n; + return true; } -String IPAddress::toString() const -{ - char szRet[16]; - sprintf(szRet,"%u.%u.%u.%u", _address.bytes[0], _address.bytes[1], _address.bytes[2], _address.bytes[3]); - return String(szRet); -} - -bool IPAddress::fromString(const char *address) -{ - // TODO: add support for "a", "a.b", "a.b.c" formats +bool IPAddress::fromString4(const char *address) { + // TODO: (IPv4) add support for "a", "a.b", "a.b.c" formats uint16_t acc = 0; // Accumulator uint8_t dots = 0; @@ -103,7 +128,7 @@ bool IPAddress::fromString(const char *address) // Too much dots (there must be 3 dots) return false; } - _address.bytes[dots++] = acc; + (*this)[dots++] = acc; acc = 0; } else @@ -117,9 +142,146 @@ bool IPAddress::fromString(const char *address) // Too few dots (there must be 3 dots) return false; } - _address.bytes[3] = acc; + (*this)[3] = acc; + + setV4(); + return true; +} + +IPAddress& IPAddress::operator=(const uint8_t *address) { + setV4(); + v4() = *reinterpret_cast(address); + return *this; +} + +IPAddress& IPAddress::operator=(uint32_t address) { + setV4(); + v4() = address; + return *this; +} + +bool IPAddress::operator==(const uint8_t* addr) const { + return isV4() && v4() == *reinterpret_cast(addr); +} + +size_t IPAddress::printTo(Print& p) const { + size_t n = 0; + + // if (!isSet()) + // return p.print(F("(IP unset)")); + +#if LWIP_IPV6 + if (isV6()) { + int count0 = 0; + for (int i = 0; i < 8; i++) { + uint16_t bit = PP_NTOHS(raw6()[i]); + if (bit || count0 < 0) { + n += p.printf("%x", bit); + if (count0 > 0) + // no more hiding 0 + count0 = -8; + } else + count0++; + if ((i != 7 && count0 < 2) || count0 == 7) + n += p.print(':'); + } + return n; + } +#endif + + for(int i = 0; i < 4; i++) { + n += p.print((*this)[i], DEC); + if (i != 3) + n += p.print('.'); + } + return n; +} + +String IPAddress::toString() const +{ + StreamString sstr; +#if LWIP_IPV6 + if (isV6()) + sstr.reserve(40); // 8 shorts x 4 chars each + 7 colons + nullterm + else +#endif + sstr.reserve(16); // 4 bytes with 3 chars max + 3 dots + nullterm, or '(IP unset)' + printTo(sstr); + return sstr; +} + +bool IPAddress::isValid(const String& arg) { + return IPAddress().fromString(arg); +} + +bool IPAddress::isValid(const char* arg) { + return IPAddress().fromString(arg); +} + +const IPAddress INADDR46_ANY; // generic "0.0.0.0" for IPv4 & IPv6 +const IPAddress INADDR46_NONE(255,255,255,255); + +void IPAddress::clear() { + (*this) = INADDR46_ANY; +} + +/**************************************/ + +#if LWIP_IPV6 + +bool IPAddress::fromString6(const char *address) { + // TODO: test test test + + uint32_t acc = 0; // Accumulator + int dots = 0, doubledots = -1; + + while (*address) + { + char c = tolower(*address++); + if (isalnum(c)) { + if (c >= 'a') + c -= 'a' - '0' - 10; + acc = acc * 16 + (c - '0'); + if (acc > 0xffff) + // Value out of range + return false; + } + else if (c == ':') { + if (*address == ':') { + if (doubledots >= 0) + // :: allowed once + return false; + // remember location + doubledots = dots + !!acc; + address++; + } + if (dots == 7) + // too many separators + return false; + raw6()[dots++] = PP_HTONS(acc); + acc = 0; + } + else + // Invalid char + return false; + } + + if (doubledots == -1 && dots != 7) + // Too few separators + return false; + raw6()[dots++] = PP_HTONS(acc); + + if (doubledots != -1) { + for (int i = dots - doubledots - 1; i >= 0; i--) + raw6()[8 - dots + doubledots + i] = raw6()[doubledots + i]; + for (int i = doubledots; i < 8 - dots + doubledots; i++) + raw6()[i] = 0; + } + + setV6(); return true; } +#endif // LWIP_IPV6 // declared one time - as external in IPAddress.h -IPAddress INADDR_NONE(0, 0, 0, 0); +IPAddress INADDR_NONE(0, 0, 0, 0); // TODO diff --git a/cores/esp32/IPAddress.h b/cores/esp32/IPAddress.h index 3bedd4f8749..d6d2e947f67 100644 --- a/cores/esp32/IPAddress.h +++ b/cores/esp32/IPAddress.h @@ -20,77 +20,174 @@ #ifndef IPAddress_h #define IPAddress_h -#include #include #include +#include -// A class to make it easier to handle and pass around IP addresses +enum IPType +{ + IPv4 = IPADDR_TYPE_V4, + IPv6 = IPADDR_TYPE_V6 +}; class IPAddress: public Printable { private: - union { - uint8_t bytes[4]; // IPv4 address - uint32_t dword; - } _address; + ip_addr_t _ip; // Access the raw byte array containing the address. Because this returns a pointer // to the internal structure rather than a copy of the address this function should only // be used when you know that the usage of the returned uint8_t* will be transient and not // stored. - uint8_t* raw_address() - { - return _address.bytes; + uint8_t* raw_address() { + return reinterpret_cast(&v4()); } + const uint8_t* raw_address() const { + return reinterpret_cast(&v4()); + } + + void ctor32 (uint32_t); public: // Constructors IPAddress(); + IPAddress(const IPAddress& from); IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet); - IPAddress(uint32_t address); - IPAddress(const uint8_t *address); - virtual ~IPAddress() {} + IPAddress(uint8_t o1, uint8_t o2, uint8_t o3, uint8_t o4, uint8_t o5, uint8_t o6, uint8_t o7, uint8_t o8, uint8_t o9, uint8_t o10, uint8_t o11, uint8_t o12, uint8_t o13, uint8_t o14, uint8_t o15, uint8_t o16); + IPAddress(uint32_t address) { ctor32(address); } + IPAddress(const uint8_t *address); // v4 only + IPAddress(IPType type, const uint8_t *address); bool fromString(const char *address); bool fromString(const String &address) { return fromString(address.c_str()); } // Overloaded cast operator to allow IPAddress objects to be used where a pointer // to a four-byte uint8_t array is expected - operator uint32_t() const - { - return _address.dword; + operator uint32_t() const { return isV4()? v4(): (uint32_t)0; } + operator uint32_t() { return isV4()? v4(): (uint32_t)0; } + + // generic IPv4 wrapper to uint32-view like arduino loves to see it + const uint32_t& v4() const { return ip_2_ip4(&_ip)->addr; } // for raw_address(const) + uint32_t& v4() { return ip_2_ip4(&_ip)->addr; } + + bool operator==(const IPAddress& addr) const { + return ip_addr_cmp(&_ip, &addr._ip); } - bool operator==(const IPAddress& addr) const - { - return _address.dword == addr._address.dword; + bool operator!=(const IPAddress& addr) const { + return !ip_addr_cmp(&_ip, &addr._ip); + } + bool operator==(uint32_t addr) const { + return isV4() && v4() == addr; } + // bool operator==(unsigned long addr) const { + // return isV4() && v4() == (uint32_t)addr; + // } + bool operator!=(uint32_t addr) const { + return !(isV4() && v4() == addr); + } + // bool operator!=(unsigned long addr) const { + // return isV4() && v4() != (uint32_t)addr; + // } bool operator==(const uint8_t* addr) const; + int operator>>(int n) const { + return isV4()? v4() >> n: 0; + } + // Overloaded index operator to allow getting and setting individual octets of the address - uint8_t operator[](int index) const - { - return _address.bytes[index]; + uint8_t operator[](int index) const { + return isV4()? *(raw_address() + index): 0; } - uint8_t& operator[](int index) - { - return _address.bytes[index]; + uint8_t& operator[](int index) { + setV4(); + return *(raw_address() + index); } // Overloaded copy operators to allow initialisation of IPAddress objects from other types IPAddress& operator=(const uint8_t *address); IPAddress& operator=(uint32_t address); + IPAddress& operator=(const IPAddress&) = default; + + IPType type() const { return (IPType)_ip.type; } virtual size_t printTo(Print& p) const; String toString() const; + void clear(); + + /* + check if input string(arg) is a valid IPV4 address or not. + return true on valid. + return false on invalid. + */ + static bool isValid(const String& arg); + static bool isValid(const char* arg); + friend class EthernetClass; friend class UDP; friend class Client; friend class Server; friend class DhcpClass; friend class DNSClient; + + operator ip_addr_t () const { return _ip; } + operator const ip_addr_t*() const { return &_ip; } + operator ip_addr_t*() { return &_ip; } + + bool isV4() const { return IP_IS_V4_VAL(_ip); } + void setV4() { IP_SET_TYPE_VAL(_ip, IPADDR_TYPE_V4); } + + bool isLocal() const { return ip_addr_islinklocal(&_ip); } + bool isAny() const { return ip_addr_isany_val(_ip); } + +#if LWIP_IPV6 + IPAddress(const ip_addr_t& lwip_addr) { ip_addr_copy(_ip, lwip_addr); } + IPAddress(const ip_addr_t* lwip_addr) { ip_addr_copy(_ip, *lwip_addr); } + + IPAddress& operator=(const ip_addr_t& lwip_addr) { ip_addr_copy(_ip, lwip_addr); return *this; } + IPAddress& operator=(const ip_addr_t* lwip_addr) { ip_addr_copy(_ip, *lwip_addr); return *this; } + + uint16_t* raw6() + { + setV6(); + return reinterpret_cast(ip_2_ip6(&_ip)); + } + + const uint16_t* raw6() const + { + return isV6()? reinterpret_cast(ip_2_ip6(&_ip)): nullptr; + } + + // when not IPv6, ip_addr_t == ip4_addr_t so this one would be ambiguous + // required otherwise + operator const ip4_addr_t*() const { return isV4()? ip_2_ip4(&_ip): nullptr; } + + bool isV6() const { return IP_IS_V6_VAL(_ip); } + void setV6() { + IP_SET_TYPE_VAL(_ip, IPADDR_TYPE_V6); + ip6_addr_clear_zone(ip_2_ip6(&_ip)); + } + +protected: + bool fromString6(const char *address); + +#else + + // allow portable code when IPv6 is not enabled + + uint16_t* raw6() { return nullptr; } + const uint16_t* raw6() const { return nullptr; } + bool isV6() const { return false; } + void setV6() { } + +#endif + +protected: + bool fromString4(const char *address); }; // changed to extern because const declaration creates copies in BSS of INADDR_NONE for each CPP unit that includes it extern IPAddress INADDR_NONE; +extern IPAddress IN6ADDR_ANY; + #endif diff --git a/cores/esp32/USBCDC.cpp b/cores/esp32/USBCDC.cpp index ccf5180d7c7..5d50001115d 100644 --- a/cores/esp32/USBCDC.cpp +++ b/cores/esp32/USBCDC.cpp @@ -74,7 +74,7 @@ void tud_cdc_tx_complete_cb(uint8_t itf){ static void ARDUINO_ISR_ATTR cdc0_write_char(char c){ if(devices[0] != NULL){ - devices[0]->write(c); + tud_cdc_n_write_char(0, c); } } diff --git a/libraries/Ethernet/src/ETH.cpp b/libraries/Ethernet/src/ETH.cpp index 166f212f3a7..5728ddbe01f 100644 --- a/libraries/Ethernet/src/ETH.cpp +++ b/libraries/Ethernet/src/ETH.cpp @@ -286,6 +286,9 @@ bool ETHClass::begin(uint8_t phy_addr, int power, int mdc, int mdio, eth_phy_typ case ETH_PHY_DP83848: eth_phy = esp_eth_phy_new_dp83848(&phy_config); break; + case ETH_PHY_JL1101: + eth_phy = esp_eth_phy_new_jl1101(&phy_config); + break; #if CONFIG_ETH_SPI_ETHERNET_DM9051 case ETH_PHY_DM9051: eth_phy = esp_eth_phy_new_dm9051(&phy_config); diff --git a/libraries/Ethernet/src/ETH.h b/libraries/Ethernet/src/ETH.h index 7fc14f59cb3..b5e4cfaeda4 100644 --- a/libraries/Ethernet/src/ETH.h +++ b/libraries/Ethernet/src/ETH.h @@ -53,7 +53,7 @@ typedef enum { ETH_CLOCK_GPIO0_IN, ETH_CLOCK_GPIO0_OUT, ETH_CLOCK_GPIO16_OUT, ETH_CLOCK_GPIO17_OUT } eth_clock_mode_t; #endif -typedef enum { ETH_PHY_LAN8720, ETH_PHY_TLK110, ETH_PHY_RTL8201, ETH_PHY_DP83848, ETH_PHY_DM9051, ETH_PHY_KSZ8041, ETH_PHY_KSZ8081, ETH_PHY_MAX } eth_phy_type_t; +typedef enum { ETH_PHY_LAN8720, ETH_PHY_TLK110, ETH_PHY_RTL8201, ETH_PHY_DP83848, ETH_PHY_DM9051, ETH_PHY_KSZ8041, ETH_PHY_KSZ8081, ETH_PHY_JL1101, ETH_PHY_MAX } eth_phy_type_t; #define ETH_PHY_IP101 ETH_PHY_TLK110 class ETHClass { diff --git a/libraries/Update/src/Updater.cpp b/libraries/Update/src/Updater.cpp index 30d88cb8ff1..93eb506254c 100644 --- a/libraries/Update/src/Updater.cpp +++ b/libraries/Update/src/Updater.cpp @@ -386,6 +386,8 @@ size_t UpdateClass::writeStream(Stream &data) { if((_bufferLen == remaining() || _bufferLen == SPI_FLASH_SEC_SIZE) && !_writeBuffer()) return written; written += toRead; + + delay(1); // Fix solo WDT } return written; } diff --git a/libraries/WiFi/examples/WiFiTelnetToSerialIPv6/WiFiTelnetToSerialIPv6.ino b/libraries/WiFi/examples/WiFiTelnetToSerialIPv6/WiFiTelnetToSerialIPv6.ino new file mode 100644 index 00000000000..05e02771dae --- /dev/null +++ b/libraries/WiFi/examples/WiFiTelnetToSerialIPv6/WiFiTelnetToSerialIPv6.ino @@ -0,0 +1,132 @@ +/* + WiFiTelnetToSerial - Example Transparent UART to Telnet Server for ESP32 + + Copyright (c) 2017 Hristo Gochkov. All rights reserved. + This file is part of the ESP32 WiFi library for Arduino environment. + + 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 +#include + +WiFiMulti wifiMulti; + +//Even this example state IPv6, it is dual stack and compatible with IPv4 too + +//how many clients should be able to telnet to this ESP32 +#define MAX_SRV_CLIENTS 1 +const char* ssid = "**********"; +const char* password = "**********"; + +WiFiServer server(23); +WiFiClient serverClients[MAX_SRV_CLIENTS]; + +void setup() { + Serial.begin(115200); + Serial.println("\nConnecting"); + + wifiMulti.IPv6(true); + wifiMulti.addAP(ssid, password); + wifiMulti.addAP("ssid_from_AP_2", "your_password_for_AP_2"); + wifiMulti.addAP("ssid_from_AP_3", "your_password_for_AP_3"); + + Serial.println("Connecting Wifi "); + for (int loops = 10; loops > 0; loops--) { + if (wifiMulti.run() == WL_CONNECTED) { + Serial.println(""); + Serial.print("WiFi connected "); + Serial.print("IP address: "); + Serial.println(WiFi.localIP()); + break; + } + else { + Serial.println(loops); + delay(1000); + } + } + if (wifiMulti.run() != WL_CONNECTED) { + Serial.println("WiFi connect failed"); + delay(1000); + ESP.restart(); + } + + //start UART and the server + Serial1.begin(9600); + server.begin(); + server.setNoDelay(true); + + Serial.print("Ready! Use 'telnet "); + Serial.print(WiFi.localIP()); + Serial.println(" 23' to connect"); +} + +void loop() { + uint8_t i; + if (wifiMulti.run() == WL_CONNECTED) { + //check if there are any new clients + if (server.hasClient()){ + for(i = 0; i < MAX_SRV_CLIENTS; i++){ + //find free/disconnected spot + if (!serverClients[i] || !serverClients[i].connected()){ + if(serverClients[i]) serverClients[i].stop(); + serverClients[i] = server.available(); + if (!serverClients[i]) Serial.println("available broken"); + Serial.print("New client: "); + Serial.print(i); Serial.print(' '); + Serial.println(serverClients[i].remoteIP()); + break; + } + } + if (i >= MAX_SRV_CLIENTS) { + //no free/disconnected spot so reject + server.available().stop(); + } + } + //check clients for data + for(i = 0; i < MAX_SRV_CLIENTS; i++){ + if (serverClients[i] && serverClients[i].connected()){ + if(serverClients[i].available()){ + //get data from the telnet client and push it to the UART + while(serverClients[i].available()) Serial1.write(serverClients[i].read()); + } + } + else { + if (serverClients[i]) { + serverClients[i].stop(); + } + } + } + //check UART for data + if(Serial1.available()){ + size_t len = Serial1.available(); + uint8_t sbuf[len]; + Serial1.readBytes(sbuf, len); + //push UART data to all connected telnet clients + for(i = 0; i < MAX_SRV_CLIENTS; i++){ + if (serverClients[i] && serverClients[i].connected()){ + serverClients[i].write(sbuf, len); + delay(1); + } + } + } + } + else { + Serial.println("WiFi not connected!"); + for(i = 0; i < MAX_SRV_CLIENTS; i++) { + if (serverClients[i]) serverClients[i].stop(); + } + delay(1000); + } +} diff --git a/libraries/WiFi/src/WiFiClient.cpp b/libraries/WiFi/src/WiFiClient.cpp index 9e2a85f5dea..ee704c2dbca 100644 --- a/libraries/WiFi/src/WiFiClient.cpp +++ b/libraries/WiFi/src/WiFiClient.cpp @@ -23,6 +23,11 @@ #include #include +#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))) + #define WIFI_CLIENT_DEF_CONN_TIMEOUT_MS (3000) #define WIFI_CLIENT_MAX_WRITE_RETRY (10) #define WIFI_CLIENT_SELECT_TIMEOUT_US (1000000) @@ -210,22 +215,32 @@ int WiFiClient::connect(IPAddress ip, uint16_t port) { return connect(ip,port,_timeout); } + int WiFiClient::connect(IPAddress ip, uint16_t port, int32_t timeout_ms) { + struct sockaddr_storage serveraddr = {}; _timeout = timeout_ms; - int sockfd = socket(AF_INET, SOCK_STREAM, 0); + int sockfd = -1; + + 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); + } else { + 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 (sockfd < 0) { log_e("socket: %d", errno); return 0; } fcntl( sockfd, F_SETFL, fcntl( sockfd, F_GETFL, 0 ) | O_NONBLOCK ); - uint32_t ip_addr = ip; - struct sockaddr_in serveraddr; - memset((char *) &serveraddr, 0, sizeof(serveraddr)); - serveraddr.sin_family = AF_INET; - memcpy((void *)&serveraddr.sin_addr.s_addr, (const void *)(&ip_addr), 4); - serveraddr.sin_port = htons(port); fd_set fdset; struct timeval tv; FD_ZERO(&fdset); @@ -294,6 +309,19 @@ int WiFiClient::connect(const char *host, uint16_t port) int WiFiClient::connect(const char *host, uint16_t port, int32_t timeout_ms) { + if (WiFiGenericClass::getStatusBits() & WIFI_WANT_IP6_BIT) { + ip_addr_t srv6; + if(!WiFiGenericClass::hostByName6(host, srv6)){ + return 0; + } + if (srv6.type == IPADDR_TYPE_V4) { + IPAddress ip(srv6.u_addr.ip4.addr); + return connect(ip, port, timeout_ms); + } else { + IPAddress ip(IPv6, (uint8_t*)&srv6.u_addr.ip6.addr[0]); + return connect(ip, port, timeout_ms); + } + } IPAddress srv((uint32_t)0); if(!WiFiGenericClass::hostByName(host, srv)){ return 0; @@ -562,8 +590,24 @@ IPAddress WiFiClient::remoteIP(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 IPAddress((uint32_t)(s->sin_addr.s_addr)); + + // 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)); + } + + // 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+12); + } else { + return IPAddress(IPv6, (uint8_t*)(saddr6->sin6_addr.s6_addr)); + } + } + log_e("WiFiClient::remoteIP Not AF_INET or AF_INET6?"); + return (IPAddress(0,0,0,0)); } uint16_t WiFiClient::remotePort(int fd) const diff --git a/libraries/WiFi/src/WiFiGeneric.cpp b/libraries/WiFi/src/WiFiGeneric.cpp index 1b74d322739..95648589a36 100644 --- a/libraries/WiFi/src/WiFiGeneric.cpp +++ b/libraries/WiFi/src/WiFiGeneric.cpp @@ -188,20 +188,18 @@ esp_err_t set_esp_interface_ip(esp_interface_t interface, IPAddress local_ip=IPA 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_SUBNET_MASK, + err = tcpip_adapter_dhcps_option( + (tcpip_adapter_dhcp_option_mode_t)TCPIP_ADAPTER_OP_SET, + (tcpip_adapter_dhcp_option_id_t)ESP_NETIF_SUBNET_MASK, (void*)&info.netmask.addr, sizeof(info.netmask.addr) ); if(err){ log_e("DHCPS Set Netmask Failed! 0x%04x", err); return err; } - err = esp_netif_dhcps_option( - esp_netif, - ESP_NETIF_OP_SET, - ESP_NETIF_REQUESTED_IP_ADDRESS, + err = tcpip_adapter_dhcps_option( + (tcpip_adapter_dhcp_option_mode_t)TCPIP_ADAPTER_OP_SET, + (tcpip_adapter_dhcp_option_id_t)REQUESTED_IP_ADDRESS, (void*)&lease, sizeof(dhcps_lease_t) ); if(err){ @@ -945,8 +943,8 @@ esp_err_t WiFiGenericClass::_eventCallback(arduino_event_t *event) } else if(event->event_id == ARDUINO_EVENT_WIFI_STA_CONNECTED) { WiFiSTAClass::_setStatus(WL_IDLE_STATUS); setStatusBits(STA_CONNECTED_BIT); - - //esp_netif_create_ip6_linklocal(esp_netifs[ESP_IF_WIFI_STA]); + if (getStatusBits() & WIFI_WANT_IP6_BIT) + esp_netif_create_ip6_linklocal(esp_netifs[ESP_IF_WIFI_STA]); } else if(event->event_id == ARDUINO_EVENT_WIFI_STA_DISCONNECTED) { uint8_t reason = event->event_info.wifi_sta_disconnected.reason; // Reason 0 causes crash, use reason 1 (UNSPECIFIED) instead @@ -1450,35 +1448,82 @@ static void wifi_dns_found_callback(const char *name, const ip_addr_t *ipaddr, v } /** - * Resolve the given hostname to an IP address. If passed hostname is an IP address, it will be parsed into IPAddress structure. - * @param aHostname Name to be resolved or string containing IP address + * IPv6 compatible DNS callback + * @param name + * @param ipaddr + * @param callback_arg + */ +static void wifi_dns6_found_callback(const char *name, const ip_addr_t *ipaddr, void *callback_arg) +{ + struct dns_api_msg *msg = (struct dns_api_msg *)callback_arg; + + if(ipaddr && !msg->result) { + msg->ip_addr = *ipaddr; + msg->result = 1; + } else { + msg->result = -1; + } + xEventGroupSetBits(_arduino_event_group, WIFI_DNS_DONE_BIT); +} + +/** + * 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 WiFiGenericClass::hostByName(const char* aHostname, IPAddress& aResult) { - if (!aResult.fromString(aHostname)) - { - ip_addr_t addr; - aResult = static_cast(0); - waitStatusBits(WIFI_DNS_IDLE_BIT, 16000); - clearStatusBits(WIFI_DNS_IDLE_BIT | WIFI_DNS_DONE_BIT); - err_t err = dns_gethostbyname(aHostname, &addr, &wifi_dns_found_callback, &aResult); - if(err == ERR_OK && addr.u_addr.ip4.addr) { - aResult = addr.u_addr.ip4.addr; - } 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((uint32_t)aResult == 0){ - log_e("DNS Failed for %s", aHostname); - } + ip_addr_t addr; + aResult = static_cast(0); + waitStatusBits(WIFI_DNS_IDLE_BIT, 16000); + clearStatusBits(WIFI_DNS_IDLE_BIT | WIFI_DNS_DONE_BIT); + err_t err = dns_gethostbyname(aHostname, &addr, &wifi_dns_found_callback, &aResult); + if(err == ERR_OK && addr.u_addr.ip4.addr) { + aResult = addr.u_addr.ip4.addr; + } 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((uint32_t)aResult == 0){ + log_e("DNS Failed for %s", aHostname); } return (uint32_t)aResult != 0; } +/** + * Resolve the given hostname to an IP6 address. + * @param aHostname Name to be resolved + * @param aResult IPv6Address structure to store the returned IP address + * @return 1 if aHostname was successfully converted to an IP address, + * else error code + */ +int WiFiGenericClass::hostByName6(const char* aHostname, ip_addr_t& aResult) +{ + ip_addr_t addr; + struct dns_api_msg arg; + + memset(&arg, 0x0, sizeof(arg)); + waitStatusBits(WIFI_DNS_IDLE_BIT, 16000); + clearStatusBits(WIFI_DNS_IDLE_BIT | WIFI_DNS_DONE_BIT); + + err_t err = dns_gethostbyname_addrtype(aHostname, &addr, &wifi_dns6_found_callback, + &arg, LWIP_DNS_ADDRTYPE_IPV6_IPV4); + if(err == ERR_OK) { + aResult = addr; + } 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); + if (arg.result == 1) { + aResult = arg.ip_addr; + } + } + setStatusBits(WIFI_DNS_IDLE_BIT); + return (uint32_t)err == ERR_OK || (err == ERR_INPROGRESS && arg.result == 1); +} + IPAddress WiFiGenericClass::calculateNetworkID(IPAddress ip, IPAddress subnet) { IPAddress networkID; diff --git a/libraries/WiFi/src/WiFiGeneric.h b/libraries/WiFi/src/WiFiGeneric.h index 2f670a34d05..f969cfc7266 100644 --- a/libraries/WiFi/src/WiFiGeneric.h +++ b/libraries/WiFi/src/WiFiGeneric.h @@ -138,6 +138,7 @@ static const int WIFI_SCANNING_BIT = BIT11; static const int WIFI_SCAN_DONE_BIT= BIT12; static const int WIFI_DNS_IDLE_BIT = BIT13; static const int WIFI_DNS_DONE_BIT = BIT14; +static const int WIFI_WANT_IP6_BIT = BIT15; typedef enum { WIFI_RX_ANT0 = 0, @@ -151,6 +152,11 @@ typedef enum { WIFI_TX_ANT_AUTO } wifi_tx_ant_t; +struct dns_api_msg { + ip_addr_t ip_addr; + int result; +}; + class WiFiGenericClass { public: @@ -212,6 +218,7 @@ class WiFiGenericClass public: static int hostByName(const char *aHostname, IPAddress &aResult); + static int hostByName6(const char *aHostname, ip_addr_t& aResult); static IPAddress calculateNetworkID(IPAddress ip, IPAddress subnet); static IPAddress calculateBroadcast(IPAddress ip, IPAddress subnet); diff --git a/libraries/WiFi/src/WiFiMulti.cpp b/libraries/WiFi/src/WiFiMulti.cpp index 3d69e481293..9e7f03c6531 100644 --- a/libraries/WiFi/src/WiFiMulti.cpp +++ b/libraries/WiFi/src/WiFiMulti.cpp @@ -30,6 +30,7 @@ WiFiMulti::WiFiMulti() { + ipv6_support = false; } WiFiMulti::~WiFiMulti() @@ -160,6 +161,8 @@ uint8_t WiFiMulti::run(uint32_t connectTimeout) log_i("[WIFI] Connecting BSSID: %02X:%02X:%02X:%02X:%02X:%02X SSID: %s Channel: %d (%d)", bestBSSID[0], bestBSSID[1], bestBSSID[2], bestBSSID[3], bestBSSID[4], bestBSSID[5], bestNetwork.ssid, bestChannel, bestNetworkDb); WiFi.begin(bestNetwork.ssid, bestNetwork.passphrase, bestChannel, bestBSSID); + if (ipv6_support == true) + WiFi.IPv6(true); status = WiFi.status(); auto startTime = millis(); @@ -202,3 +205,7 @@ uint8_t WiFiMulti::run(uint32_t connectTimeout) return status; } + +void WiFiMulti::IPv6(bool state) { + ipv6_support = state; +} diff --git a/libraries/WiFi/src/WiFiMulti.h b/libraries/WiFi/src/WiFiMulti.h index 38ddb5d9f95..bbeb78dc860 100644 --- a/libraries/WiFi/src/WiFiMulti.h +++ b/libraries/WiFi/src/WiFiMulti.h @@ -42,10 +42,12 @@ class WiFiMulti bool addAP(const char* ssid, const char *passphrase = NULL); + void IPv6(bool state); uint8_t run(uint32_t connectTimeout=5000); private: std::vector APlist; + bool ipv6_support; }; #endif /* WIFICLIENTMULTI_H_ */ diff --git a/libraries/WiFi/src/WiFiSTA.cpp b/libraries/WiFi/src/WiFiSTA.cpp index 7bcafea1d3e..d8075868475 100644 --- a/libraries/WiFi/src/WiFiSTA.cpp +++ b/libraries/WiFi/src/WiFiSTA.cpp @@ -732,6 +732,19 @@ bool WiFiSTAClass::enableIpV6() return esp_netif_create_ip6_linklocal(get_esp_interface_netif(ESP_IF_WIFI_STA)) == ESP_OK; } +/** + * Enable IPv6 support on the station interface. + * @return true on success + */ +bool WiFiSTAClass::IPv6(bool state) +{ + if (state) + WiFiGenericClass::setStatusBits(WIFI_WANT_IP6_BIT); + else + WiFiGenericClass::clearStatusBits(WIFI_WANT_IP6_BIT); + return true; +} + /** * Get the station interface IPv6 address. * @return IPv6Address diff --git a/libraries/WiFi/src/WiFiSTA.h b/libraries/WiFi/src/WiFiSTA.h index b8bb855c198..6892c996b30 100644 --- a/libraries/WiFi/src/WiFiSTA.h +++ b/libraries/WiFi/src/WiFiSTA.h @@ -84,6 +84,7 @@ class WiFiSTAClass uint8_t subnetCIDR(); bool enableIpV6(); + bool IPv6(bool state); IPv6Address localIPv6(); // STA WiFi info diff --git a/libraries/WiFi/src/WiFiServer.cpp b/libraries/WiFi/src/WiFiServer.cpp index db21858125b..ad2cb7fb364 100644 --- a/libraries/WiFi/src/WiFiServer.cpp +++ b/libraries/WiFi/src/WiFiServer.cpp @@ -47,8 +47,8 @@ WiFiClient WiFiServer::available(){ _accepted_sockfd = -1; } else { - struct sockaddr_in _client; - int cs = sizeof(struct sockaddr_in); + struct sockaddr_in6 _client; + int cs = sizeof(struct sockaddr_in6); #ifdef ESP_IDF_VERSION_MAJOR client_sock = lwip_accept(sockfd, (struct sockaddr *)&_client, (socklen_t*)&cs); #else @@ -76,14 +76,23 @@ void WiFiServer::begin(uint16_t port, int enable){ if(port){ _port = port; } - struct sockaddr_in server; - sockfd = socket(AF_INET , SOCK_STREAM, 0); + 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.sin_family = AF_INET; - server.sin_addr.s_addr = _addr; - server.sin_port = htons(_port); + server.sin6_family = AF_INET6; + if (_addr.type() == IPv4) { + log_e("---------------- 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 { + log_e("---------------- IPv6"); + memcpy(server.sin6_addr.s6_addr, (uint8_t*)&_addr[0], 16); + } + memset(server.sin6_addr.s6_addr, 0x0, 16); + server.sin6_port = htons(_port); if(bind(sockfd, (struct sockaddr *)&server, sizeof(server)) < 0) return; if(listen(sockfd , _max_clients) < 0) @@ -106,8 +115,8 @@ bool WiFiServer::hasClient() { if (_accepted_sockfd >= 0) { return true; } - struct sockaddr_in _client; - int cs = sizeof(struct sockaddr_in); + struct sockaddr_in6 _client; + int cs = sizeof(struct sockaddr_in6); #ifdef ESP_IDF_VERSION_MAJOR _accepted_sockfd = lwip_accept(sockfd, (struct sockaddr *)&_client, (socklen_t*)&cs); #else diff --git a/libraries/WiFi/src/WiFiServer.h b/libraries/WiFi/src/WiFiServer.h index 346986abad5..d933231bdbb 100644 --- a/libraries/WiFi/src/WiFiServer.h +++ b/libraries/WiFi/src/WiFiServer.h @@ -37,7 +37,6 @@ class WiFiServer : public Server { public: void listenOnLocalhost(){} - // _addr(INADDR_ANY) is the same as _addr() ==> 0.0.0.0 WiFiServer(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("WiFiServer::WiFiServer(port=%d, ...)", port); } diff --git a/libraries/WiFi/src/WiFiUdp.cpp b/libraries/WiFi/src/WiFiUdp.cpp index 556f9a8afcc..03990fd726e 100644 --- a/libraries/WiFi/src/WiFiUdp.cpp +++ b/libraries/WiFi/src/WiFiUdp.cpp @@ -50,7 +50,11 @@ uint8_t WiFiUDP::begin(IPAddress address, uint16_t port){ return 0; } +#if LWIP_IPV6 + if ((udp_server=socket(address.isV6() ? 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; } @@ -62,38 +66,80 @@ uint8_t WiFiUDP::begin(IPAddress address, uint16_t port){ return 0; } - struct sockaddr_in addr; - memset((char *) &addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_port = htons(server_port); - addr.sin_addr.s_addr = (in_addr_t)address; - if(bind(udp_server , (struct sockaddr*)&addr, sizeof(addr)) == -1){ + struct sockaddr_storage serveraddr = {}; + size_t sock_size = 0; +#if LWIP_IPV6 + if (address.isV6()) { + struct sockaddr_in6 *tmpaddr = (struct sockaddr_in6 *)&serveraddr; + ip_addr_t * ip_addr = (ip_addr_t*) address; + memset((char *) tmpaddr, 0, sizeof(struct sockaddr_in)); + tmpaddr->sin6_family = AF_INET6; + tmpaddr->sin6_port = htons(server_port); + inet6_addr_from_ip6addr(&tmpaddr->sin6_addr, ip_2_ip6(ip_addr)); + tmpaddr->sin6_flowinfo = 0; + sock_size = sizeof(sockaddr_in6); + } else +#endif + if (1) { + 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 WiFiUDP::begin(uint16_t p){ - return begin(IPAddress(INADDR_ANY), p); + return begin(IPAddress(), p); } uint8_t WiFiUDP::beginMulticast(IPAddress a, uint16_t p){ - if(begin(IPAddress(INADDR_ANY), p)){ - if(a != 0){ - 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) { + if(begin(IPAddress(), p)){ + ip_addr_t * ip_addr = (ip_addr_t*) a; + if (ip_addr_ismulticast(ip_addr)) { +#if LWIP_IPV6 + if (IP_IS_V6_VAL(*ip_addr)) { + struct ipv6_mreq mreq; + bool joined = false; + inet6_addr_from_ip6addr(&mreq.ipv6mr_multiaddr, ip_2_ip6(ip_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 + if (1) { + 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 1; } return 0; } @@ -111,19 +157,37 @@ void WiFiUDP::stop(){ } if(udp_server == -1) return; - if(multicast_ip != 0){ - 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)); - multicast_ip = IPAddress(INADDR_ANY); + ip_addr_t * mcast_addr = (ip_addr_t*) multicast_ip; + if (!ip_addr_isany(mcast_addr)) { +#if LWIP_IPV6 + if (IP_IS_V6(mcast_addr)) { + struct ipv6_mreq mreq; + inet6_addr_from_ip6addr(&mreq.ipv6mr_multiaddr, ip_2_ip6(mcast_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_LEAVE_GROUP, &mreq, sizeof(mreq)); + } + } + } else +#endif + if (1) { + 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 WiFiUDP::beginMulticastPacket(){ - if(!server_port || multicast_ip == IPAddress(INADDR_ANY)) + if(!server_port || multicast_ip == IPAddress()) return 0; remote_ip = multicast_ip; remote_port = server_port; @@ -176,14 +240,28 @@ int WiFiUDP::beginPacket(const char *host, uint16_t port){ } int WiFiUDP::endPacket(){ - 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 (remote_ip.isV4()) { + 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; + } + } else { + struct sockaddr_in6 recipient; + 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; } @@ -207,22 +285,43 @@ size_t WiFiUDP::write(const uint8_t *buffer, size_t size){ int WiFiUDP::parsePacket(){ if(rx_buffer) return 0; - struct sockaddr_in si_other; - int slen = sizeof(si_other) , len; - char *buf = (char *)malloc(1460); - if(!buf) { + struct sockaddr_storage si_other_storage; // enough storage for v4 and v6 + socklen_t slen = sizeof(sockaddr_storage); + int len; + char * buf = new char[1460]; + if(!buf){ return 0; } - if ((len = recvfrom(udp_server, buf, 1460, MSG_DONTWAIT, (struct sockaddr *) &si_other, (socklen_t *)&slen)) == -1){ - free(buf); + if ((len = recvfrom(udp_server, buf, 1460, MSG_DONTWAIT, (struct sockaddr *) &si_other_storage, (socklen_t *)&slen)) == -1){ + delete[] buf; if(errno == EWOULDBLOCK){ return 0; } log_e("could not receive data: %d", errno); return 0; } - remote_ip = IPAddress(si_other.sin_addr.s_addr); - remote_port = ntohs(si_other.sin_port); + 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); // force IPv6 + ip_addr_t *ip_addr = (ip_addr_t*) remote_ip; + /* Dual-stack: Unmap IPv4 mapped IPv6 addresses */ + if (IP_IS_V6_VAL(*ip_addr) && ip6_addr_isipv4mappedipv6(ip_2_ip6(ip_addr))) { + unmap_ipv4_mapped_ipv6(ip_2_ip4(ip_addr), ip_2_ip6(ip_addr)); + IP_SET_TYPE_VAL(*ip_addr, IPADDR_TYPE_V4); + } + remote_port = ntohs(si_other.sin6_port); + } +#endif // LWIP_IPV6=1 + else { + remote_ip = *IP_ADDR_ANY; + remote_port = 0; + } if (len > 0) { rx_buffer = new(std::nothrow) cbuf(len); rx_buffer->write(buf, len); diff --git a/tools/platformio-build.py b/tools/platformio-build.py index e3c7b50dbb8..2ee2d92894f 100644 --- a/tools/platformio-build.py +++ b/tools/platformio-build.py @@ -25,7 +25,6 @@ # Extends: https://github.com/platformio/platform-espressif32/blob/develop/builder/main.py from os.path import abspath, basename, isdir, isfile, join -from copy import deepcopy from SCons.Script import DefaultEnvironment, SConscript env = DefaultEnvironment() @@ -237,13 +236,3 @@ def add_tinyuf2_extra_image(): ), ) env.Depends("$BUILD_DIR/$PROGNAME$PROGSUFFIX", partition_table) - -# -# Adjust the `esptoolpy` command in the `ElfToBin` builder with firmware checksum offset -# - -action = deepcopy(env["BUILDERS"]["ElfToBin"].action) -action.cmd_list = env["BUILDERS"]["ElfToBin"].action.cmd_list.replace( - "-o", "--elf-sha256-offset 0xb0 -o" -) -env["BUILDERS"]["ElfToBin"].action = action From a54a0ee96bc39affd6f3cf8e70c58fede560241b Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Mon, 20 Feb 2023 21:56:31 +0100 Subject: [PATCH 02/10] Fix change --- libraries/WiFi/src/WiFiUdp.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/WiFi/src/WiFiUdp.cpp b/libraries/WiFi/src/WiFiUdp.cpp index 03990fd726e..de45ae22616 100644 --- a/libraries/WiFi/src/WiFiUdp.cpp +++ b/libraries/WiFi/src/WiFiUdp.cpp @@ -288,12 +288,12 @@ int WiFiUDP::parsePacket(){ struct sockaddr_storage si_other_storage; // enough storage for v4 and v6 socklen_t slen = sizeof(sockaddr_storage); int len; - char * buf = new char[1460]; - if(!buf){ + 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){ - delete[] buf; + free(buf); if(errno == EWOULDBLOCK){ return 0; } From 536ec6981a1227d3a0ac26c08b21ac575e58869a Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Thu, 9 Mar 2023 20:08:18 +0100 Subject: [PATCH 03/10] remove duplicate fix --- libraries/WiFi/src/WiFiClient.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/WiFi/src/WiFiClient.cpp b/libraries/WiFi/src/WiFiClient.cpp index ee704c2dbca..8e0edf80f69 100644 --- a/libraries/WiFi/src/WiFiClient.cpp +++ b/libraries/WiFi/src/WiFiClient.cpp @@ -246,7 +246,7 @@ int WiFiClient::connect(IPAddress ip, uint16_t port, int32_t timeout_ms) FD_ZERO(&fdset); FD_SET(sockfd, &fdset); tv.tv_sec = _timeout / 1000; - tv.tv_usec = (_timeout % 1000) * 1000; + tv.tv_usec = 0; #ifdef ESP_IDF_VERSION_MAJOR int res = lwip_connect(sockfd, (struct sockaddr*)&serveraddr, sizeof(serveraddr)); From b1ab70ab1409a46fbfc2232e647a199658a7c8f8 Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Sat, 8 Apr 2023 12:54:11 +0200 Subject: [PATCH 04/10] Delete README.md --- README.md | 66 ------------------------------------------------------- 1 file changed, 66 deletions(-) delete mode 100644 README.md diff --git a/README.md b/README.md deleted file mode 100644 index 46631266dd8..00000000000 --- a/README.md +++ /dev/null @@ -1,66 +0,0 @@ -# Arduino core for the ESP32, ESP32-S2, ESP32-S3 and ESP32-C3 - -![Build Status](https://github.com/espressif/arduino-esp32/workflows/ESP32%20Arduino%20CI/badge.svg) [![Documentation Status](https://readthedocs.com/projects/espressif-arduino-esp32/badge/?version=latest)](https://docs.espressif.com/projects/arduino-esp32/en/latest/?badge=latest) -[![External Libraries Test](https://github.com/espressif/arduino-esp32/actions/workflows/lib.yml/badge.svg?branch=master&event=schedule)](https://github.com/espressif/arduino-esp32/actions/workflows/lib.yml?link=http://https://github.com/espressif/arduino-esp32/blob/master/LIBRARIES_TEST.md) - -### Need help or have a question? Join the chat at [Gitter](https://gitter.im/espressif/arduino-esp32) or [open a new Discussion](https://github.com/espressif/arduino-esp32/discussions) - -## Contents - - - [Development Status](#development-status) - - [Development Planning](#development-planning) - - [Documentation](#documentation) - - [Supported Chips](#supported-chips) - - [Decoding exceptions](#decoding-exceptions) - - [Issue/Bug report template](#issuebug-report-template) - - [Contributing](#contributing) - -### Development Status - -Latest Stable Release [![Release Version](https://img.shields.io/github/release/espressif/arduino-esp32.svg?style=plastic)](https://github.com/espressif/arduino-esp32/releases/latest/) [![Release Date](https://img.shields.io/github/release-date/espressif/arduino-esp32.svg?style=plastic)](https://github.com/espressif/arduino-esp32/releases/latest/) [![Downloads](https://img.shields.io/github/downloads/espressif/arduino-esp32/latest/total.svg?style=plastic)](https://github.com/espressif/arduino-esp32/releases/latest/) - -Latest Development Release [![Release Version](https://img.shields.io/github/release/espressif/arduino-esp32/all.svg?style=plastic)](https://github.com/espressif/arduino-esp32/releases/) [![Release Date](https://img.shields.io/github/release-date-pre/espressif/arduino-esp32.svg?style=plastic)](https://github.com/espressif/arduino-esp32/releases/) [![Downloads](https://img.shields.io/github/downloads-pre/espressif/arduino-esp32/latest/total.svg?style=plastic)](https://github.com/espressif/arduino-esp32/releases/) - -### Development Planning - -Our Development is fully tracked on this public **[Roadmap 🎉](https://github.com/orgs/espressif/projects/3)** - -For even more information you can take a look at [Sprint Meeting notes](https://github.com/espressif/arduino-esp32/discussions/categories/sprints-meeting-notes) or join [Monthly Community Meetings 🔔](https://github.com/espressif/arduino-esp32/discussions/categories/monthly-community-meetings) - -### Documentation - -You can use the [Arduino-ESP32 Online Documentation](https://docs.espressif.com/projects/arduino-esp32/en/latest/) to get all information about this project. - -* [Getting Started](https://docs.espressif.com/projects/arduino-esp32/en/latest/getting_started.html) -* [Installing (Windows, Linux and macOS)](https://docs.espressif.com/projects/arduino-esp32/en/latest/installing.html) -* [Libraries](https://docs.espressif.com/projects/arduino-esp32/en/latest/libraries.html) -* [Arduino as an ESP-IDF component](https://docs.espressif.com/projects/arduino-esp32/en/latest/esp-idf_component.html) -* [FAQ](https://docs.espressif.com/projects/arduino-esp32/en/latest/faq.html) -* [Troubleshooting](https://docs.espressif.com/projects/arduino-esp32/en/latest/troubleshooting.html) - -### Supported Chips - -Visit the [supported chips](https://docs.espressif.com/projects/arduino-esp32/en/latest/getting_started.html#supported-soc-s) documentation to see the list of current supported ESP32 SoCs. - -### Decoding exceptions - -You can use [EspExceptionDecoder](https://github.com/me-no-dev/EspExceptionDecoder) to get meaningful call trace. - -### Issue/Bug report template - -Before reporting an issue, make sure you've searched for similar one that was already created. Also make sure to go through all the issues labelled as [Type: For reference](https://github.com/espressif/arduino-esp32/issues?q=is%3Aissue+label%3A%22Type%3A+For+reference%22+). - -Finally, if you are sure no one else had the issue, follow the **Issue template** or **Feature request template** while reporting any [new Issue](https://github.com/espressif/arduino-esp32/issues/new/choose). - -### External libraries compilation test - -We have set-up CI testing for external libraries for ESP32 Arduino core. You can check test results in the file [LIBRARIES_TEST](https://github.com/espressif/arduino-esp32/blob/master/LIBRARIES_TEST.md). -For more information and how to add your library to the test see [external library testing](https://docs.espressif.com/projects/arduino-esp32/en/latest/external_libraries_test.html) in the documentation. - -### Contributing - -We welcome contributions to the Arduino ESP32 project! - -See [contributing](https://docs.espressif.com/projects/arduino-esp32/en/latest/contributing.html) in the documentation for more information on how to contribute to the project. - -> We would like to have this repository in a polite and friendly atmosphere, so please be kind and respectful to others. For more details, look at [Code of Conduct](https://github.com/espressif/arduino-esp32/blob/master/CODE_OF_CONDUCT.md). From edc457abefd75d50f809bfb48f1865f834783d3e Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Sat, 8 Apr 2023 12:54:33 +0200 Subject: [PATCH 05/10] Create README.md --- README.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 00000000000..e0faf6f2f9f --- /dev/null +++ b/README.md @@ -0,0 +1,23 @@ +# Tasmota Arduino core for the ESP32, ESP32-S2, ESP32-S3 and ESP32-C3 + + +### [![GitHub Releases](https://img.shields.io/github/downloads/tasmota/arduino-esp32/total?label=downloads)](https://github.com/tasmota/arduino-esp32/releases/latest) + +### Documentation + +You can use the [Arduino-ESP32 Online Documentation](https://docs.espressif.com/projects/arduino-esp32/en/latest/) to get all information about this project. + +* [Getting Started](https://docs.espressif.com/projects/arduino-esp32/en/latest/getting_started.html) +* [Installing (Windows, Linux and macOS)](https://docs.espressif.com/projects/arduino-esp32/en/latest/installing.html) +* [Libraries](https://docs.espressif.com/projects/arduino-esp32/en/latest/libraries.html) +* [Arduino as an ESP-IDF component](https://docs.espressif.com/projects/arduino-esp32/en/latest/esp-idf_component.html) +* [FAQ](https://docs.espressif.com/projects/arduino-esp32/en/latest/faq.html) +* [Troubleshooting](https://docs.espressif.com/projects/arduino-esp32/en/latest/troubleshooting.html) + +### Supported Chips + +Visit the [supported chips](https://docs.espressif.com/projects/arduino-esp32/en/latest/getting_started.html#supported-soc-s) documentation to see the list of current supported ESP32 SoCs. + +### Decoding exceptions + +You can use [EspExceptionDecoder](https://github.com/me-no-dev/EspExceptionDecoder) to get meaningful call trace. From dfa5e8dbffbd42d72778b2206d09be7596972637 Mon Sep 17 00:00:00 2001 From: Tyeth Gundry Date: Mon, 10 Apr 2023 13:21:20 +0100 Subject: [PATCH 06/10] Update installing to use CDN backed github links (#7876) This increases the reliability of users hitting the boardsmanager urls, improving speed and avoiding request throttling, by using the github CDN frontend as mentioned in this old ticket: https://github.com/espressif/arduino-esp32/issues/3505#issuecomment-1438813967 Co-authored-by: Me No Dev --- docs/source/installing.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/installing.rst b/docs/source/installing.rst index 6120973fb87..c3bd268949b 100644 --- a/docs/source/installing.rst +++ b/docs/source/installing.rst @@ -25,11 +25,11 @@ This is the way to install Arduino-ESP32 directly from the Arduino IDE. - Stable release link:: - https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json + https://espressif.github.io/arduino-esp32/package_esp32_index.json - Development release link:: - https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_dev_index.json + https://espressif.github.io/arduino-esp32/package_esp32_dev_index.json .. note:: From ab7ce5a51172e9bdde4f6792124bae96cb6e3f6b Mon Sep 17 00:00:00 2001 From: Sanket Wadekar <67091512+sanketwadekar@users.noreply.github.com> Date: Mon, 10 Apr 2023 20:16:02 +0530 Subject: [PATCH 07/10] Add Insights in Rainmaker Switch example (#8011) * insights: add support for custom transport * Added insights in rainmaker switch example --- CMakeLists.txt | 1 + libraries/Insights/src/Insights.cpp | 9 ++- libraries/Insights/src/Insights.h | 2 +- .../examples/RMakerSwitch/RMakerSwitch.ino | 4 + libraries/RainMaker/src/AppInsights.cpp | 81 +++++++++++++++++++ libraries/RainMaker/src/AppInsights.h | 12 +++ 6 files changed, 106 insertions(+), 3 deletions(-) create mode 100644 libraries/RainMaker/src/AppInsights.cpp create mode 100644 libraries/RainMaker/src/AppInsights.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 851d1488eca..2518b0be093 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -99,6 +99,7 @@ set(LIBRARY_SRCS libraries/RainMaker/src/RMakerType.cpp libraries/RainMaker/src/RMakerQR.cpp libraries/RainMaker/src/RMakerUtils.cpp + libraries/RainMaker/src/AppInsights.cpp libraries/SD_MMC/src/SD_MMC.cpp libraries/SD/src/SD.cpp libraries/SD/src/sd_diskio.cpp diff --git a/libraries/Insights/src/Insights.cpp b/libraries/Insights/src/Insights.cpp index 0ec65e86727..49de5b2035a 100644 --- a/libraries/Insights/src/Insights.cpp +++ b/libraries/Insights/src/Insights.cpp @@ -49,13 +49,18 @@ ESPInsightsClass::~ESPInsightsClass(){ end(); } -bool ESPInsightsClass::begin(const char *auth_key, const char *node_id, uint32_t log_type, bool alloc_ext_ram){ +bool ESPInsightsClass::begin(const char *auth_key, const char *node_id, uint32_t log_type, bool alloc_ext_ram, bool use_default_transport){ if(!initialized){ if(log_type == 0xFFFFFFFF){ log_type = (ESP_DIAG_LOG_TYPE_ERROR | ESP_DIAG_LOG_TYPE_WARNING | ESP_DIAG_LOG_TYPE_EVENT); } esp_insights_config_t config = {.log_type = log_type, .node_id = node_id, .auth_key = auth_key, .alloc_ext_ram = alloc_ext_ram}; - esp_err_t err = esp_insights_init(&config); + esp_err_t err = ESP_OK; + if (use_default_transport) { + err = esp_insights_init(&config); + } else { + err = esp_insights_enable(&config); + } if (err != ESP_OK) { log_e("Failed to initialize ESP Insights, err:0x%x", err); } diff --git a/libraries/Insights/src/Insights.h b/libraries/Insights/src/Insights.h index b950b937b14..d572c35a012 100644 --- a/libraries/Insights/src/Insights.h +++ b/libraries/Insights/src/Insights.h @@ -90,7 +90,7 @@ class ESPInsightsClass ESPInsightsClass(); ~ESPInsightsClass(); - bool begin(const char *auth_key, const char *node_id = NULL, uint32_t log_type = 0xFFFFFFFF, bool alloc_ext_ram = false); + bool begin(const char *auth_key, const char *node_id = NULL, uint32_t log_type = 0xFFFFFFFF, bool alloc_ext_ram = false, bool use_default_transport = true); void end(); bool send(); const char * nodeID(); diff --git a/libraries/RainMaker/examples/RMakerSwitch/RMakerSwitch.ino b/libraries/RainMaker/examples/RMakerSwitch/RMakerSwitch.ino index 32a1a28601f..688feed9bf7 100644 --- a/libraries/RainMaker/examples/RMakerSwitch/RMakerSwitch.ino +++ b/libraries/RainMaker/examples/RMakerSwitch/RMakerSwitch.ino @@ -2,6 +2,7 @@ #include "RMaker.h" #include "WiFi.h" #include "WiFiProv.h" +#include "AppInsights.h" #define DEFAULT_POWER_MODE true const char *service_name = "PROV_1234"; @@ -98,6 +99,9 @@ void setup() RMaker.enableSchedule(); RMaker.enableScenes(); + // Enable ESP Insights. Insteads of using the default http transport, this function will + // reuse the existing MQTT connection of Rainmaker, thereby saving memory space. + initAppInsights(); RMaker.enableSystemService(SYSTEM_SERV_FLAGS_ALL, 2, 2, 2); diff --git a/libraries/RainMaker/src/AppInsights.cpp b/libraries/RainMaker/src/AppInsights.cpp new file mode 100644 index 00000000000..591f5e65dcc --- /dev/null +++ b/libraries/RainMaker/src/AppInsights.cpp @@ -0,0 +1,81 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "sdkconfig.h" +#include +#if defined(CONFIG_ESP_INSIGHTS_ENABLED) && defined(CONFIG_ESP_RMAKER_WORK_QUEUE_TASK_STACK) +#include "Arduino.h" +#include "AppInsights.h" +#include "Insights.h" +#include +#include +#include +#include +#include + +extern "C" { + bool esp_rmaker_mqtt_is_budget_available(); +} + +#define INSIGHTS_TOPIC_SUFFIX "diagnostics/from-node" +#define INSIGHTS_TOPIC_RULE "insights_message_delivery" + +static void _rmakerCommonEventHandler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) +{ + if (event_base != RMAKER_COMMON_EVENT) { + return; + } + esp_insights_transport_event_data_t data; + switch(event_id) { + case RMAKER_MQTT_EVENT_PUBLISHED: + memset(&data, 0, sizeof(data)); + data.msg_id = *(int *)event_data; + esp_event_post(INSIGHTS_EVENT, INSIGHTS_EVENT_TRANSPORT_SEND_SUCCESS, &data, sizeof(data), portMAX_DELAY); + break; + default: + break; + } +} + +static int _appInsightsDataSend(void *data, size_t len) +{ + char topic[128]; + int msg_id = -1; + if (data == NULL) { + return 0; + } + char *node_id = esp_rmaker_get_node_id(); + if (!node_id) { + return -1; + } + if (esp_rmaker_mqtt_is_budget_available() == false) { + return ESP_FAIL; + } + esp_rmaker_create_mqtt_topic(topic, sizeof(topic), INSIGHTS_TOPIC_SUFFIX, INSIGHTS_TOPIC_RULE); + esp_rmaker_mqtt_publish(topic, data, len, RMAKER_MQTT_QOS1, &msg_id); + return msg_id; +} + +bool initAppInsights(uint32_t log_type, bool alloc_ext_ram) +{ + char *node_id = esp_rmaker_get_node_id(); + esp_insights_transport_config_t transport; + transport.userdata = NULL; + transport.callbacks.data_send = _appInsightsDataSend; + transport.callbacks.init = NULL; + transport.callbacks.deinit = NULL; + transport.callbacks.connect = NULL; + transport.callbacks.disconnect = NULL; + esp_insights_transport_register(&transport); + esp_event_handler_register(RMAKER_COMMON_EVENT, ESP_EVENT_ANY_ID, _rmakerCommonEventHandler, NULL); + return Insights.begin(NULL, node_id, log_type, alloc_ext_ram, false); +} +#else +bool initAppInsights(uint32_t log_type, bool alloc_ext_ram) +{ + return false; +} +#endif diff --git a/libraries/RainMaker/src/AppInsights.h b/libraries/RainMaker/src/AppInsights.h new file mode 100644 index 00000000000..ddb32f1e266 --- /dev/null +++ b/libraries/RainMaker/src/AppInsights.h @@ -0,0 +1,12 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once +#include "sdkconfig.h" +#include "Arduino.h" +#include + +bool initAppInsights(uint32_t log_type = 0xffffffff, bool alloc_ext_ram = false); From c7eeeda5067a6a83306b92cb733c1773cff860ae Mon Sep 17 00:00:00 2001 From: Dogus Cendek Date: Mon, 10 Apr 2023 18:20:37 +0300 Subject: [PATCH 08/10] Update UploadMode Config of Deneyap Kart 1A v2 (#8046) --- boards.txt | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/boards.txt b/boards.txt index a8f1725380f..48c0b8b940a 100644 --- a/boards.txt +++ b/boards.txt @@ -17817,12 +17817,12 @@ deneyapkart1Av2.menu.DFUOnBoot.default.build.dfu_on_boot=0 deneyapkart1Av2.menu.DFUOnBoot.dfu=Enabled (Requires USB-OTG Mode) deneyapkart1Av2.menu.DFUOnBoot.dfu.build.dfu_on_boot=1 -deneyapkart1Av2.menu.UploadMode.default=UART0 / Hardware CDC -deneyapkart1Av2.menu.UploadMode.default.upload.use_1200bps_touch=false -deneyapkart1Av2.menu.UploadMode.default.upload.wait_for_upload_port=false deneyapkart1Av2.menu.UploadMode.cdc=USB-OTG CDC (TinyUSB) deneyapkart1Av2.menu.UploadMode.cdc.upload.use_1200bps_touch=true deneyapkart1Av2.menu.UploadMode.cdc.upload.wait_for_upload_port=true +deneyapkart1Av2.menu.UploadMode.default=UART0 / Hardware CDC +deneyapkart1Av2.menu.UploadMode.default.upload.use_1200bps_touch=false +deneyapkart1Av2.menu.UploadMode.default.upload.wait_for_upload_port=false deneyapkart1Av2.menu.PartitionScheme.default=Default 4MB with spiffs (1.2MB APP/1.5MB SPIFFS) deneyapkart1Av2.menu.PartitionScheme.default.build.partitions=default @@ -22714,5 +22714,3 @@ crabik_slot_esp32_s3.menu.EraseFlash.all=Enabled crabik_slot_esp32_s3.menu.EraseFlash.all.upload.erase_cmd=-e ############################################################## - - From 224e778b8ed7728a2b104c98c232f34fa34a357c Mon Sep 17 00:00:00 2001 From: Rodrigo Garcia Date: Mon, 10 Apr 2023 17:43:09 -0300 Subject: [PATCH 09/10] Makes Gamepad example able to be tested with Windows 10/11 (#8058) * Makes sure it can be tested with Windows 10/11 Initial code had no effect with Win10/11 because BUTTON_START was not recognized. This change makes it visible in the Windows Game Controller Testing TAB. * Examples tests all USB gamepad APIs. It is possible to change the selected gamepad button when pressing BOOT (long/short press). The selected button is used as parameter to change R/L Stick and Trigger as well as the Hat. --- libraries/USB/examples/Gamepad/Gamepad.ino | 34 ++++++++++++++++++++-- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/libraries/USB/examples/Gamepad/Gamepad.ino b/libraries/USB/examples/Gamepad/Gamepad.ino index dad75723797..fa2a2a3bc6f 100644 --- a/libraries/USB/examples/Gamepad/Gamepad.ino +++ b/libraries/USB/examples/Gamepad/Gamepad.ino @@ -14,13 +14,41 @@ void setup() { pinMode(buttonPin, INPUT_PULLUP); Gamepad.begin(); USB.begin(); + Serial.begin(115200); + Serial.println("\n==================\nUSB Gamepad Testing\n==================\n"); + Serial.println("Press BOOT Button to activate the USB gamepad."); + Serial.println("Longer press will change the affected button and controls."); + Serial.println("Shorter press/release just activates the button and controls."); } void loop() { + static uint8_t padID = 0; + static long lastPress = 0; + int buttonState = digitalRead(buttonPin); - if ((buttonState != previousButtonState) && (buttonState == LOW)) { - Gamepad.pressButton(BUTTON_START); - Gamepad.releaseButton(BUTTON_START); + if (buttonState != previousButtonState) { + if (buttonState == LOW) { // BOOT Button pressed + Gamepad.pressButton(padID); // Buttons 1 to 32 + Gamepad.leftStick(padID << 3, padID << 3); // X Axis, Y Axis + Gamepad.rightStick(-(padID << 2), padID << 2); // Z Axis, Z Rotation + Gamepad.leftTrigger(padID << 4); // X Rotation + Gamepad.rightTrigger(-(padID << 4)); // Y Rotation + Gamepad.hat((padID & 0x7) + 1); // Point of View Hat + log_d("Pressed PadID [%d]", padID); + lastPress = millis(); + } else { + Gamepad.releaseButton(padID); + Gamepad.leftStick(0, 0); + Gamepad.rightStick(0, 0); + Gamepad.leftTrigger(0); + Gamepad.rightTrigger(0); + Gamepad.hat(HAT_CENTER); + log_d("Released PadID [%d]\n", padID); + if (millis() - lastPress > 300) { + padID = (padID + 1) & 0x1F; + log_d("Changed padID to %d\n", padID); + } + } } previousButtonState = buttonState; } From 610aa22dfe07914308d8b080199f5fab498fb085 Mon Sep 17 00:00:00 2001 From: s-hadinger <49731213+s-hadinger@users.noreply.github.com> Date: Wed, 12 Apr 2023 19:21:14 +0200 Subject: [PATCH 10/10] Fix erratic problem when sending IPv6 UDP packet --- libraries/WiFi/src/WiFiUdp.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/WiFi/src/WiFiUdp.cpp b/libraries/WiFi/src/WiFiUdp.cpp index de45ae22616..2e6c8c63cb5 100644 --- a/libraries/WiFi/src/WiFiUdp.cpp +++ b/libraries/WiFi/src/WiFiUdp.cpp @@ -257,6 +257,7 @@ int WiFiUDP::endPacket(){ recipient.sin6_addr = *(in6_addr*)(ip_addr_t*)remote_ip; recipient.sin6_family = AF_INET6; recipient.sin6_port = htons(remote_port); + recipient.sin6_scope_id = 0; 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);