From 9762b2392ad3928d8643911294e505e78fcc5b2f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Proch=C3=A1zka?=
<90197375+P-R-O-C-H-Y@users.noreply.github.com>
Date: Fri, 11 Nov 2022 12:54:02 +0100
Subject: [PATCH 01/15] LEDC - AnalogWrite new API + ledcAttachPin duty fix
(#7346)
---
cores/esp32/esp32-hal-ledc.c | 31 ++++++++++++++++++++++++++++---
cores/esp32/esp32-hal.h | 2 ++
2 files changed, 30 insertions(+), 3 deletions(-)
diff --git a/cores/esp32/esp32-hal-ledc.c b/cores/esp32/esp32-hal-ledc.c
index 014b08125f6..4b58c1d9a91 100644
--- a/cores/esp32/esp32-hal-ledc.c
+++ b/cores/esp32/esp32-hal-ledc.c
@@ -166,14 +166,15 @@ void ledcAttachPin(uint8_t pin, uint8_t chan)
return;
}
uint8_t group=(chan/8), channel=(chan%8), timer=((chan/2)%4);
-
+ uint32_t duty = ledc_get_duty(group,channel);
+
ledc_channel_config_t ledc_channel = {
.speed_mode = group,
.channel = channel,
.timer_sel = timer,
.intr_type = LEDC_INTR_DISABLE,
.gpio_num = pin,
- .duty = 0,
+ .duty = duty,
.hpoint = 0
};
ledc_channel_config(&ledc_channel);
@@ -211,6 +212,8 @@ uint32_t ledcChangeFrequency(uint8_t chan, uint32_t freq, uint8_t bit_num)
static int8_t pin_to_channel[SOC_GPIO_PIN_COUNT] = { 0 };
static int cnt_channel = LEDC_CHANNELS;
+static uint8_t analog_resolution = 8;
+static int analog_frequency = 1000;
void analogWrite(uint8_t pin, int value) {
// Use ledc hardware for internal pins
if (pin < SOC_GPIO_PIN_COUNT) {
@@ -220,8 +223,8 @@ void analogWrite(uint8_t pin, int value) {
return;
}
pin_to_channel[pin] = cnt_channel--;
+ ledcSetup(cnt_channel, analog_frequency, analog_resolution);
ledcAttachPin(pin, cnt_channel);
- ledcSetup(cnt_channel, 1000, 8);
}
ledcWrite(pin_to_channel[pin] - 1, value);
}
@@ -230,3 +233,25 @@ void analogWrite(uint8_t pin, int value) {
int8_t analogGetChannel(uint8_t pin) {
return pin_to_channel[pin] - 1;
}
+
+void analogWriteFrequency(uint32_t freq) {
+ if (cnt_channel != LEDC_CHANNELS) {
+ for (int channel = LEDC_CHANNELS - 1; channel >= cnt_channel; channel--) {
+ ledcChangeFrequency(channel, freq, analog_resolution);
+ }
+ }
+ analog_frequency = freq;
+}
+
+void analogWriteResolution(uint8_t bits) {
+ if(bits > LEDC_MAX_BIT_WIDTH) {
+ log_w("analogWrite resolution width too big! Setting to maximum %u bits)", LEDC_MAX_BIT_WIDTH);
+ bits = LEDC_MAX_BIT_WIDTH;
+ }
+ if (cnt_channel != LEDC_CHANNELS) {
+ for (int channel = LEDC_CHANNELS - 1; channel >= cnt_channel; channel--) {
+ ledcChangeFrequency(channel, analog_frequency, bits);
+ }
+ }
+ analog_resolution = bits;
+}
diff --git a/cores/esp32/esp32-hal.h b/cores/esp32/esp32-hal.h
index 59dca98cbb9..51ecea405df 100644
--- a/cores/esp32/esp32-hal.h
+++ b/cores/esp32/esp32-hal.h
@@ -93,6 +93,8 @@ void yield(void);
void analogWrite(uint8_t pin, int value);
int8_t analogGetChannel(uint8_t pin);
+void analogWriteFrequency(uint32_t freq);
+void analogWriteResolution(uint8_t bits);
//returns chip temperature in Celsius
float temperatureRead();
From 5737016cddbde4d18a14772885cde0908321a931 Mon Sep 17 00:00:00 2001
From: Sanket Wadekar <67091512+sanketwadekar@users.noreply.github.com>
Date: Mon, 14 Nov 2022 19:57:20 +0530
Subject: [PATCH 02/15] Add enableScenes API in Rainmaker (#7436)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* Added enableScenes API
* Added enableScenes API documentation
* Added enableScenes API to example
Co-authored-by: Jan Procházka <90197375+P-R-O-C-H-Y@users.noreply.github.com>
---
docs/source/api/rainmaker.rst | 18 +++++++++++++++---
.../examples/RMakerCustom/RMakerCustom.ino | 16 +++++++++-------
.../RMakerCustomAirCooler.ino | 16 +++++++++-------
.../RMakerSonoffDualR3/RMakerSonoffDualR3.ino | 1 +
.../examples/RMakerSwitch/RMakerSwitch.ino | 2 ++
libraries/RainMaker/src/RMaker.cpp | 13 +++++++++++--
libraries/RainMaker/src/RMaker.h | 5 +++--
7 files changed, 50 insertions(+), 21 deletions(-)
diff --git a/docs/source/api/rainmaker.rst b/docs/source/api/rainmaker.rst
index ae0c735b00b..1b157a790b7 100644
--- a/docs/source/api/rainmaker.rst
+++ b/docs/source/api/rainmaker.rst
@@ -22,9 +22,6 @@ The key features of ESP RainMaker are:
Additional information about ESP RainMaker can be found `here `__.
-#########################
-Arduino ESP Rainmaker API
-#########################
ESP RainMaker Agent API
-----------------------
@@ -127,6 +124,21 @@ This function will return
1. `ESP_OK` : On success
2. Error in case of failure
+RMaker.enableScenes
+*******************
+
+This API enables the Scenes service for the node. It should be called after `RMaker.initNode()` and before `RMaker.start()`.
+For more information, check `here `__.
+
+.. code-block:: arduino
+
+ esp_err_t enableScenes()
+
+This function will return
+
+1. `ESP_OK` : On success
+2. Error in case of failure
+
RMaker.setTimeZone
******************
diff --git a/libraries/RainMaker/examples/RMakerCustom/RMakerCustom.ino b/libraries/RainMaker/examples/RMakerCustom/RMakerCustom.ino
index f10f858cad7..74b0c39bd3d 100644
--- a/libraries/RainMaker/examples/RMakerCustom/RMakerCustom.ino
+++ b/libraries/RainMaker/examples/RMakerCustom/RMakerCustom.ino
@@ -26,7 +26,7 @@ static Device my_device("Dimmer", "custom.device.dimmer", &gpio_dimmer);
void sysProvEvent(arduino_event_t *sys_event)
{
- switch (sys_event->event_id) {
+ switch (sys_event->event_id) {
case ARDUINO_EVENT_PROV_START:
#if CONFIG_IDF_TARGET_ESP32S2
Serial.printf("\nProvisioning Started with name \"%s\" and PoP \"%s\" on SoftAP\n", service_name, pop);
@@ -34,7 +34,7 @@ void sysProvEvent(arduino_event_t *sys_event)
#else
Serial.printf("\nProvisioning Started with name \"%s\" and PoP \"%s\" on BLE\n", service_name, pop);
printQR(service_name, pop, "ble");
-#endif
+#endif
break;
default:;
}
@@ -63,7 +63,7 @@ void setup()
pinMode(gpio_dimmer, OUTPUT);
digitalWrite(gpio_dimmer, DEFAULT_POWER_MODE);
- Node my_node;
+ Node my_node;
my_node = RMaker.initNode("ESP RainMaker Node");
//Create custom dimmer device
@@ -78,13 +78,13 @@ void setup()
my_device.addParam(level_param);
my_device.addCb(write_callback);
-
- //Add custom dimmer device to the node
+
+ //Add custom dimmer device to the node
my_node.addDevice(my_device);
- //This is optional
+ //This is optional
RMaker.enableOTA(OTA_USING_PARAMS);
- //If you want to enable scheduling, set time zone for your region using setTimeZone().
+ //If you want to enable scheduling, set time zone for your region using setTimeZone().
//The list of available values are provided here https://rainmaker.espressif.com/docs/time-service.html
// RMaker.setTimeZone("Asia/Shanghai");
// Alternatively, enable the Timezone service and let the phone apps set the appropriate timezone
@@ -92,6 +92,8 @@ void setup()
RMaker.enableSchedule();
+ RMaker.enableScenes();
+
RMaker.start();
WiFi.onEvent(sysProvEvent);
diff --git a/libraries/RainMaker/examples/RMakerCustomAirCooler/RMakerCustomAirCooler.ino b/libraries/RainMaker/examples/RMakerCustomAirCooler/RMakerCustomAirCooler.ino
index 5b36ecffd06..aa59c060f70 100644
--- a/libraries/RainMaker/examples/RMakerCustomAirCooler/RMakerCustomAirCooler.ino
+++ b/libraries/RainMaker/examples/RMakerCustomAirCooler/RMakerCustomAirCooler.ino
@@ -43,7 +43,7 @@ static Device my_device("Air Cooler", "my.device.air-cooler", NULL);
void sysProvEvent(arduino_event_t *sys_event)
{
- switch (sys_event->event_id) {
+ switch (sys_event->event_id) {
case ARDUINO_EVENT_PROV_START:
#if CONFIG_IDF_TARGET_ESP32S2
Serial.printf("\nProvisioning Started with name \"%s\" and PoP \"%s\" on SoftAP\n", service_name, pop);
@@ -51,7 +51,7 @@ void sysProvEvent(arduino_event_t *sys_event)
#else
Serial.printf("\nProvisioning Started with name \"%s\" and PoP \"%s\" on BLE\n", service_name, pop);
printQR(service_name, pop, "ble");
-#endif
+#endif
break;
default:;
}
@@ -114,7 +114,7 @@ void setup()
pinMode(gpio_speed, OUTPUT);
analogWrite(gpio_speed, DEFAULT_SPEED);
- Node my_node;
+ Node my_node;
my_node = RMaker.initNode("ESP RainMaker Node");
//Create custom air cooler device
@@ -138,13 +138,13 @@ void setup()
my_device.addParam(mode_param);
my_device.addCb(write_callback);
-
- //Add custom Air Cooler device to the node
+
+ //Add custom Air Cooler device to the node
my_node.addDevice(my_device);
- //This is optional
+ //This is optional
// RMaker.enableOTA(OTA_USING_PARAMS);
- //If you want to enable scheduling, set time zone for your region using setTimeZone().
+ //If you want to enable scheduling, set time zone for your region using setTimeZone().
//The list of available values are provided here https://rainmaker.espressif.com/docs/time-service.html
// RMaker.setTimeZone("Asia/Shanghai");
//Alternatively, enable the Timezone service and let the phone apps set the appropriate timezone
@@ -152,6 +152,8 @@ void setup()
RMaker.enableSchedule();
+ RMaker.enableScenes();
+
RMaker.start();
WiFi.onEvent(sysProvEvent);
diff --git a/libraries/RainMaker/examples/RMakerSonoffDualR3/RMakerSonoffDualR3.ino b/libraries/RainMaker/examples/RMakerSonoffDualR3/RMakerSonoffDualR3.ino
index cf9a58b7111..38b459ffd20 100644
--- a/libraries/RainMaker/examples/RMakerSonoffDualR3/RMakerSonoffDualR3.ino
+++ b/libraries/RainMaker/examples/RMakerSonoffDualR3/RMakerSonoffDualR3.ino
@@ -136,6 +136,7 @@ void setup()
// Alternatively, enable the Timezone service and let the phone apps set the appropriate timezone
RMaker.enableTZService();
RMaker.enableSchedule();
+ RMaker.enableScenes();
//Service Name
for(int i=0; i<17; i=i+8) {
diff --git a/libraries/RainMaker/examples/RMakerSwitch/RMakerSwitch.ino b/libraries/RainMaker/examples/RMakerSwitch/RMakerSwitch.ino
index 2da78ad0744..53d71387b15 100644
--- a/libraries/RainMaker/examples/RMakerSwitch/RMakerSwitch.ino
+++ b/libraries/RainMaker/examples/RMakerSwitch/RMakerSwitch.ino
@@ -81,6 +81,8 @@ void setup()
RMaker.enableSchedule();
+ RMaker.enableScenes();
+
RMaker.start();
WiFi.onEvent(sysProvEvent);
diff --git a/libraries/RainMaker/src/RMaker.cpp b/libraries/RainMaker/src/RMaker.cpp
index fc80e716424..c7e3e921dd5 100644
--- a/libraries/RainMaker/src/RMaker.cpp
+++ b/libraries/RainMaker/src/RMaker.cpp
@@ -3,6 +3,7 @@
#include "RMaker.h"
#include
#include
+#include
bool wifiLowLevelInit(bool persistent);
static esp_err_t err;
@@ -28,12 +29,12 @@ static void event_handler(void *arg, esp_event_base_t event_base, int32_t event_
log_i("Unhandled RainMaker Event:");
}
} else if (event_base == RMAKER_OTA_EVENT) {
- if(event_data == NULL){
+ if (event_data == NULL) {
event_data = (void*)"";
}
switch(event_id) {
case RMAKER_OTA_EVENT_STARTING:
- log_i("Starting OTA : %s", (char*)event_data);
+ log_i("Starting OTA");
break;
case RMAKER_OTA_EVENT_IN_PROGRESS:
log_i("OTA in progress : %s", (char*)event_data);
@@ -146,5 +147,13 @@ esp_err_t RMakerClass::enableOTA(ota_type_t type, const char *cert)
return err;
}
+esp_err_t RMakerClass::enableScenes()
+{
+ err = esp_rmaker_scenes_enable();
+ if (err != ESP_OK) {
+ log_e("Scenes enable failed");
+ }
+ return err;
+}
RMakerClass RMaker;
#endif
diff --git a/libraries/RainMaker/src/RMaker.h b/libraries/RainMaker/src/RMaker.h
index dbfbe5c49f5..69930052f6a 100644
--- a/libraries/RainMaker/src/RMaker.h
+++ b/libraries/RainMaker/src/RMaker.h
@@ -25,9 +25,9 @@ class RMakerClass
{
private:
esp_rmaker_config_t rainmaker_cfg = {false};
-
+
public:
-
+
void setTimeSync(bool val);
Node initNode(const char *name, const char *type = "ESP RainMaker with Arduino");
esp_err_t deinitNode(Node node);
@@ -35,6 +35,7 @@ class RMakerClass
esp_err_t enableSchedule();
esp_err_t enableTZService();
esp_err_t enableOTA(ota_type_t type, const char *cert = ESP_RMAKER_OTA_DEFAULT_SERVER_CERT);
+ esp_err_t enableScenes();
esp_err_t start();
esp_err_t stop();
};
From bf53e2b8d60ed1942748d9edefaedf58d4bde2ec Mon Sep 17 00:00:00 2001
From: RefactorFactory <108912458+RefactorFactory@users.noreply.github.com>
Date: Wed, 16 Nov 2022 03:55:27 -0800
Subject: [PATCH 03/15] How to use USBHID classes with ESP32-S2 and ESP32-S3
(#7214)
These instructions are based on esp32-arduino-lib-builder's build process, including https://github.com/espressif/esp32-arduino-lib-builder/blob/master/tools/update-components.sh which explains how to clone tinyusb.
---
docs/source/esp-idf_component.rst | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/docs/source/esp-idf_component.rst b/docs/source/esp-idf_component.rst
index e46864e011b..fe27d52110f 100644
--- a/docs/source/esp-idf_component.rst
+++ b/docs/source/esp-idf_component.rst
@@ -39,6 +39,21 @@ Installation
.. note:: If you use Arduino with ESP-IDF often, you can place the arduino folder into global components folder.
+If you're targeting the ESP32-S2 or ESP32-S3 and you want to use USBHID classes such as ``USBHID``, ``USBHIDConsumerControl``, ``USBHIDGamepad``, ``USBHIDKeyboard``, ``USBHIDMouse``, ``USBHIDSystemControl``, or ``USBHIDVendor``:
+
+1. Clone these nested repos somewhere:
+
+.. code-block:: bash
+
+ git clone https://github.com/espressif/esp32-arduino-lib-builder.git esp32-arduino-lib-builder && \
+ git clone https://github.com/hathach/tinyusb.git esp32-arduino-lib-builder/components/arduino_tinyusb/tinyusb
+
+2. In the project folder, edit ``CMakeLists.txt`` and add the following before the ``project()`` line:
+
+.. code-block:: bash
+
+ set(EXTRA_COMPONENT_DIRS )
+
Configuration
-------------
From 93a7f4e0db2c189836f85c0a75a7fcd8fad838cd Mon Sep 17 00:00:00 2001
From: Sebastian Bergner <54766531+sebastianbergner@users.noreply.github.com>
Date: Wed, 23 Nov 2022 08:37:08 +0100
Subject: [PATCH 04/15] Add I2C and SPI pin definitions to wt32-eth01 pins
configuration (#7237)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* Add I2C and SPI pin definitions to wt32-eth01 pins
Added missing pins based on testing using a RTC (I2C) and SD card reader (SPI).
* Remove define macros
Co-authored-by: Rodrigo Garcia
Co-authored-by: Jan Procházka <90197375+P-R-O-C-H-Y@users.noreply.github.com>
---
variants/wt32-eth01/pins_arduino.h | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/variants/wt32-eth01/pins_arduino.h b/variants/wt32-eth01/pins_arduino.h
index 7a7742254a2..6cb2a6a5d7a 100644
--- a/variants/wt32-eth01/pins_arduino.h
+++ b/variants/wt32-eth01/pins_arduino.h
@@ -54,12 +54,12 @@ static const uint8_t RX = 3;
//SPI VSPI default pins
static const uint8_t SS = -1;
-static const uint8_t MOSI = 15;
-static const uint8_t MISO = 12;
-static const uint8_t SCK = 14;
+static const uint8_t MOSI = 14;
+static const uint8_t MISO = 15;
+static const uint8_t SCK = 12;
//I2C default pins
-static const uint8_t SDA = 2;
-static const uint8_t SCL = 4;
+static const uint8_t SDA = 33;
+static const uint8_t SCL = 32;
#endif /* Pins_Arduino_h */
From fd72cf46ad6fc1a6de99c1d83ba8eba17d80a4ee Mon Sep 17 00:00:00 2001
From: Sanket Wadekar <67091512+sanketwadekar@users.noreply.github.com>
Date: Fri, 25 Nov 2022 17:50:57 +0530
Subject: [PATCH 05/15] Changed Rainmaker WiFi/Factory reset time. (#7514)
---
libraries/RainMaker/src/RMakerUtils.cpp | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/libraries/RainMaker/src/RMakerUtils.cpp b/libraries/RainMaker/src/RMakerUtils.cpp
index 1d800b72df8..b8af2187c1a 100644
--- a/libraries/RainMaker/src/RMakerUtils.cpp
+++ b/libraries/RainMaker/src/RMakerUtils.cpp
@@ -1,12 +1,13 @@
#include "RMakerUtils.h"
#ifdef CONFIG_ESP_RMAKER_WORK_QUEUE_TASK_STACK
-void RMakerFactoryReset(int seconds)
+#define RESET_DELAY_SEC 2
+void RMakerFactoryReset(int reboot_seconds)
{
- esp_rmaker_factory_reset(0, seconds);
+ esp_rmaker_factory_reset(RESET_DELAY_SEC, reboot_seconds);
}
-void RMakerWiFiReset(int seconds)
+void RMakerWiFiReset(int reboot_seconds)
{
- esp_rmaker_wifi_reset(0, seconds);
+ esp_rmaker_wifi_reset(RESET_DELAY_SEC, reboot_seconds);
}
#endif
\ No newline at end of file
From d82b443b6d5d4779e16655a83f075f3e6028739e Mon Sep 17 00:00:00 2001
From: Jason2866 <24528715+Jason2866@users.noreply.github.com>
Date: Fri, 2 Dec 2022 11:55:07 +0100
Subject: [PATCH 06/15] Ipv6 support v2
* Update header
* Update IPAddress implementation to support V6
* Fix cut and paste error
* Explicit not equals
* Explicitly reference StreamString
* Remove != test (it is causing a conflict)
* IPv6 support: Add proper link-local and SLAAC in STA and WifiMulti
This patch partially depends on:
https://github.com/espressif/esp32-arduino-lib-builder/pull/67
Without this patch we will get only Link local IPv6 (still useful for MDNS and etc).
With patch we will get also global IPv6 address by SLAAC.
By default IPv6 disabled, until it is properly tested.
Tested on BasicHttpClient by adding:
wifiMulti.IPv6(true);
before: wifiMulti.addAP() call
Enabling Core Debug Level: verbose
If IP6 obtained, in logs will be visible:
[ 8028][V][WiFiGeneric.cpp:380] _arduino_event_cb(): IF[0] Got IPv6: IP Index: 0, Zone: 2, fe80:0000:0000:0000:xxxx:xxxx:xxxx:xxxx
[ 8028][D][WiFiGeneric.cpp:852] _eventCallback(): Arduino Event: 8 - STA_GOT_IP6
[ 11028][V][WiFiGeneric.cpp:380] _arduino_event_cb(): IF[0] Got IPv6: IP Index: 1, Zone: 0, 2a0d:yyyy:0000:4000:yyyy:yyyy:yyyy:yyyy
[ 11028][D][WiFiGeneric.cpp:852] _eventCallback(): Arduino Event: 8 - STA_GOT_IP6
This is linked to: https://github.com/espressif/arduino-esp32/issues/6242
Signed-off-by: Denys Fedoryshchenko
* Add IPv6 support to WiFiServer
One of most useful features of IPv6 to have arduino accessible from internet,
without any port forward and etc.
Change is fairly trivial and backward compatible with old code, tested
with WiFiTelnetToSerial and AsyncUDPServer.
Signed-off-by: Denys Fedoryshchenko
* WiFiClient::remoteIP and remoteIP6 IPv6 support
For RemoteIP and AF_INET6 socket i added support ip6 to ip4 mapping,
so .remoteIP will return IPv4 address on dual stack socket, if available.
Scenarios tested:
WiFiTelnetToSerial, wifiMulti.IPv6(true), connect both from IPv4 and IPv6
WiFiTelnetToSerial, wifiMulti.IPv6(true); but set to listen on IPv4 only.
WiFiTelnetToSerial, IPv6 disabled, with or without bind to specific IP4.
AsyncUDPServer, without IPv6 support.
Signed-off-by: Denys Fedoryshchenko
* Add WiFiTelnetToSerialIPv6 example
To demonstrate new abilities of dual stack WiFiServer and
remoteIP6 we add this example.
Signed-off-by: Denys Fedoryshchenko
* Add IPv6 to WifiClient (client)
We need to be able to connect to remote servers over IPv6 too,
and thats need different approach in DNS queries and connect().
As i'm trying to keep maximum compatibility, i introduce different
behaviour if IPv6 is enabled, and backward compatible (as much as possible),
if IPv6 is not enabled.
IN future when IPv6 functions are tested well enough, it can be simplified.
This implementation tested on esp32 in following scenarios using BasicHttpClient:
IPv6 true:
IPv6 only website (caveat 1) - OK
Website with A and AAAA is present (caveat 1) - OK
IPv4 only website - OK
IPv6 not enabled:
IPv6 only website - wont open (expected)
Website with A and AAAA is present - OK, opens over IPv4
IPv4 only website - OK
caveat 1 - sometimes SLAAC is slower than DHCPv4, so we might have
status WL_CONNECTED, but IPv6 global scope is not ready yet.
Signed-off-by: Denys Fedoryshchenko
* WiFiTelnetToSerialIPv6.ino: fix obsolete remoteIP6 call to remoteIP
Example contained API from previous IPv6 implementation, fixing it.
Signed-off-by: Denys Fedoryshchenko
Signed-off-by: Denys Fedoryshchenko
Co-authored-by: Sly Gryphon
Co-authored-by: Denys Fedoryshchenko
---
cores/esp32/IPAddress.cpp | 266 ++++++++++++++++--
cores/esp32/IPAddress.h | 48 ++--
.../WiFiTelnetToSerialIPv6.ino | 132 +++++++++
libraries/WiFi/src/WiFiClient.cpp | 64 ++++-
libraries/WiFi/src/WiFiGeneric.cpp | 54 +++-
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 -
12 files changed, 562 insertions(+), 60 deletions(-)
create mode 100644 libraries/WiFi/examples/WiFiTelnetToSerialIPv6/WiFiTelnetToSerialIPv6.ino
diff --git a/cores/esp32/IPAddress.cpp b/cores/esp32/IPAddress.cpp
index 0575363f254..6766b4a26d2 100644
--- a/cores/esp32/IPAddress.cpp
+++ b/cores/esp32/IPAddress.cpp
@@ -20,78 +20,225 @@
#include
#include
#include
+#include
-IPAddress::IPAddress()
+IPAddress::IPAddress() : IPAddress(IPv4) {}
+
+IPAddress::IPAddress(IPType ip_type)
{
- _address.dword = 0;
+ _type = ip_type;
+ memset(_address.bytes, 0, sizeof(_address.bytes));
}
IPAddress::IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet)
{
- _address.bytes[0] = first_octet;
- _address.bytes[1] = second_octet;
- _address.bytes[2] = third_octet;
- _address.bytes[3] = fourth_octet;
+ _type = IPv4;
+ memset(_address.bytes, 0, sizeof(_address.bytes));
+ _address.bytes[IPADDRESS_V4_BYTES_INDEX] = first_octet;
+ _address.bytes[IPADDRESS_V4_BYTES_INDEX + 1] = second_octet;
+ _address.bytes[IPADDRESS_V4_BYTES_INDEX + 2] = third_octet;
+ _address.bytes[IPADDRESS_V4_BYTES_INDEX + 3] = fourth_octet;
+}
+
+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) {
+ _type = IPv6;
+ _address.bytes[0] = o1;
+ _address.bytes[1] = o2;
+ _address.bytes[2] = o3;
+ _address.bytes[3] = o4;
+ _address.bytes[4] = o5;
+ _address.bytes[5] = o6;
+ _address.bytes[6] = o7;
+ _address.bytes[7] = o8;
+ _address.bytes[8] = o9;
+ _address.bytes[9] = o10;
+ _address.bytes[10] = o11;
+ _address.bytes[11] = o12;
+ _address.bytes[12] = o13;
+ _address.bytes[13] = o14;
+ _address.bytes[14] = o15;
+ _address.bytes[15] = o16;
}
IPAddress::IPAddress(uint32_t address)
{
- _address.dword = address;
+ // IPv4 only
+ _type = IPv4;
+ memset(_address.bytes, 0, sizeof(_address.bytes));
+ _address.dword[IPADDRESS_V4_DWORD_INDEX] = address;
+
+ // NOTE on conversion/comparison and uint32_t:
+ // These conversions are host platform dependent.
+ // There is a defined integer representation of IPv4 addresses,
+ // based on network byte order (will be the value on big endian systems),
+ // e.g. http://2398766798 is the same as http://142.250.70.206,
+ // However on little endian systems the octets 0x83, 0xFA, 0x46, 0xCE,
+ // in that order, will form the integer (uint32_t) 3460758158 .
}
-IPAddress::IPAddress(const uint8_t *address)
+IPAddress::IPAddress(const uint8_t *address) : IPAddress(IPv4, address) {}
+
+IPAddress::IPAddress(IPType ip_type, const uint8_t *address)
{
- memcpy(_address.bytes, address, sizeof(_address.bytes));
+ _type = ip_type;
+ if (ip_type == IPv4) {
+ memset(_address.bytes, 0, sizeof(_address.bytes));
+ memcpy(&_address.bytes[IPADDRESS_V4_BYTES_INDEX], address, sizeof(uint32_t));
+ } else {
+ memcpy(_address.bytes, address, sizeof(_address.bytes));
+ }
}
IPAddress& IPAddress::operator=(const uint8_t *address)
{
- memcpy(_address.bytes, address, sizeof(_address.bytes));
+ // IPv4 only conversion from byte pointer
+ _type = IPv4;
+ memset(_address.bytes, 0, sizeof(_address.bytes));
+ memcpy(&_address.bytes[IPADDRESS_V4_BYTES_INDEX], address, sizeof(uint32_t));
return *this;
}
IPAddress& IPAddress::operator=(uint32_t address)
{
- _address.dword = address;
+ // IPv4 conversion
+ // See note on conversion/comparison and uint32_t
+ _type = IPv4;
+ memset(_address.bytes, 0, sizeof(_address.bytes));
+ _address.dword[IPADDRESS_V4_DWORD_INDEX] = address;
return *this;
}
+bool IPAddress::operator==(const IPAddress& addr) const
+{
+ return (addr._type == _type)
+ && (memcmp(addr._address.bytes, _address.bytes, sizeof(_address.bytes)) == 0);
+}
+
bool IPAddress::operator==(const uint8_t* addr) const
{
- return memcmp(addr, _address.bytes, sizeof(_address.bytes)) == 0;
+ // IPv4 only comparison to byte pointer
+ // Can't support IPv6 as we know our type, but not the length of the pointer
+ return _type == IPv4 && memcmp(addr, &_address.bytes[IPADDRESS_V4_BYTES_INDEX], sizeof(uint32_t)) == 0;
+}
+
+uint8_t IPAddress::operator[](int index) const {
+ if (_type == IPv4) {
+ return _address.bytes[IPADDRESS_V4_BYTES_INDEX + index];
+ }
+ return _address.bytes[index];
+}
+
+uint8_t& IPAddress::operator[](int index) {
+ if (_type == IPv4) {
+ return _address.bytes[IPADDRESS_V4_BYTES_INDEX + index];
+ }
+ return _address.bytes[index];
}
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);
+
+ if (_type == IPv6) {
+ // IPv6 IETF canonical format: compress left-most longest run of two or more zero fields, lower case
+ int8_t longest_start = -1;
+ int8_t longest_length = 1;
+ int8_t current_start = -1;
+ int8_t current_length = 0;
+ for (int8_t f = 0; f < 8; f++) {
+ if (_address.bytes[f * 2] == 0 && _address.bytes[f * 2 + 1] == 0) {
+ if (current_start == -1) {
+ current_start = f;
+ current_length = 1;
+ } else {
+ current_length++;
+ }
+ if (current_length > longest_length) {
+ longest_start = current_start;
+ longest_length = current_length;
+ }
+ } else {
+ current_start = -1;
+ }
+ }
+ for (int f = 0; f < 8; f++) {
+ if (f < longest_start || f >= longest_start + longest_length) {
+ uint8_t c1 = _address.bytes[f * 2] >> 4;
+ uint8_t c2 = _address.bytes[f * 2] & 0xf;
+ uint8_t c3 = _address.bytes[f * 2 + 1] >> 4;
+ uint8_t c4 = _address.bytes[f * 2 + 1] & 0xf;
+ if (c1 > 0) {
+ n += p.print((char)(c1 < 10 ? '0' + c1 : 'a' + c1 - 10));
+ }
+ if (c1 > 0 || c2 > 0) {
+ n += p.print((char)(c2 < 10 ? '0' + c2 : 'a' + c2 - 10));
+ }
+ if (c1 > 0 || c2 > 0 || c3 > 0) {
+ n += p.print((char)(c3 < 10 ? '0' + c3 : 'a' + c3 - 10));
+ }
+ n += p.print((char)(c4 < 10 ? '0' + c4 : 'a' + c4 - 10));
+ if (f < 7) {
+ n += p.print(':');
+ }
+ } else if (f == longest_start) {
+ if (longest_start == 0) {
+ n += p.print(':');
+ }
+ n += p.print(':');
+ }
+ }
+ return n;
+ }
+
+ // IPv4
+ for (int i =0; i < 3; i++)
+ {
+ n += p.print(_address.bytes[IPADDRESS_V4_BYTES_INDEX + i], DEC);
n += p.print('.');
}
- n += p.print(_address.bytes[3], DEC);
+ n += p.print(_address.bytes[IPADDRESS_V4_BYTES_INDEX + 3], DEC);
return n;
}
String IPAddress::toString() const
{
+ if (_type == IPv6)
+ {
+ StreamString s;
+ s.reserve(40);
+ printTo(s);
+ return s;
+ }
+
+ // IPv4
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)
+{
+ if (!fromString4(address))
+ {
+ return fromString6(address);
+ }
+ return true;
+}
+
+bool IPAddress::fromString4(const char *address)
{
// TODO: add support for "a", "a.b", "a.b.c" formats
- uint16_t acc = 0; // Accumulator
+ int16_t acc = -1; // Accumulator
uint8_t dots = 0;
+ memset(_address.bytes, 0, sizeof(_address.bytes));
while (*address)
{
char c = *address++;
if (c >= '0' && c <= '9')
{
- acc = acc * 10 + (c - '0');
+ acc = (acc < 0) ? (c - '0') : acc * 10 + (c - '0');
if (acc > 255) {
// Value out of [0..255] range
return false;
@@ -100,11 +247,15 @@ bool IPAddress::fromString(const char *address)
else if (c == '.')
{
if (dots == 3) {
- // Too much dots (there must be 3 dots)
+ // Too many dots (there must be 3 dots)
return false;
}
- _address.bytes[dots++] = acc;
- acc = 0;
+ if (acc < 0) {
+ /* No value between dots, e.g. '1..' */
+ return false;
+ }
+ _address.bytes[IPADDRESS_V4_BYTES_INDEX + dots++] = acc;
+ acc = -1;
}
else
{
@@ -117,7 +268,80 @@ bool IPAddress::fromString(const char *address)
// Too few dots (there must be 3 dots)
return false;
}
- _address.bytes[3] = acc;
+ if (acc < 0) {
+ /* No value between dots, e.g. '1..' */
+ return false;
+ }
+ _address.bytes[IPADDRESS_V4_BYTES_INDEX + 3] = acc;
+ _type = IPv4;
+ return true;
+}
+
+bool IPAddress::fromString6(const char *address) {
+ uint32_t acc = 0; // Accumulator
+ int colons = 0, double_colons = -1;
+
+ while (*address)
+ {
+ char c = tolower(*address++);
+ if (isalnum(c) && c <= 'f') {
+ 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 (double_colons >= 0) {
+ // :: allowed once
+ return false;
+ }
+ if (*address != '\0' && *(address + 1) == ':') {
+ // ::: not allowed
+ return false;
+ }
+ // remember location
+ double_colons = colons + !!acc;
+ address++;
+ } else if (*address == '\0') {
+ // can't end with a single colon
+ return false;
+ }
+ if (colons == 7)
+ // too many separators
+ return false;
+ _address.bytes[colons * 2] = acc >> 8;
+ _address.bytes[colons * 2 + 1] = acc & 0xff;
+ colons++;
+ acc = 0;
+ }
+ else
+ // Invalid char
+ return false;
+ }
+
+ if (double_colons == -1 && colons != 7) {
+ // Too few separators
+ return false;
+ }
+ if (double_colons > -1 && colons > 6) {
+ // Too many segments (double colon must be at least one zero field)
+ return false;
+ }
+ _address.bytes[colons * 2] = acc >> 8;
+ _address.bytes[colons * 2 + 1] = acc & 0xff;
+ colons++;
+
+ if (double_colons != -1) {
+ for (int i = colons * 2 - double_colons * 2 - 1; i >= 0; i--)
+ _address.bytes[16 - colons * 2 + double_colons * 2 + i] = _address.bytes[double_colons * 2 + i];
+ for (int i = double_colons * 2; i < 16 - colons * 2 + double_colons * 2; i++)
+ _address.bytes[i] = 0;
+ }
+
+ _type = IPv6;
return true;
}
diff --git a/cores/esp32/IPAddress.h b/cores/esp32/IPAddress.h
index 3bedd4f8749..a86bc392978 100644
--- a/cores/esp32/IPAddress.h
+++ b/cores/esp32/IPAddress.h
@@ -26,13 +26,23 @@
// A class to make it easier to handle and pass around IP addresses
+#define IPADDRESS_V4_BYTES_INDEX 12
+#define IPADDRESS_V4_DWORD_INDEX 3
+
+enum IPType
+{
+ IPv4,
+ IPv6
+};
+
class IPAddress: public Printable
{
private:
union {
- uint8_t bytes[4]; // IPv4 address
- uint32_t dword;
+ uint8_t bytes[16];
+ uint32_t dword[4];
} _address;
+ IPType _type;
// 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
@@ -40,41 +50,36 @@ class IPAddress: public Printable
// stored.
uint8_t* raw_address()
{
- return _address.bytes;
+ return _type == IPv4 ? &_address.bytes[IPADDRESS_V4_BYTES_INDEX] : _address.bytes;
}
public:
// Constructors
IPAddress();
+ IPAddress(IPType ip_type);
IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet);
+ 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);
IPAddress(const uint8_t *address);
+ IPAddress(IPType ip_type, const uint8_t *address);
virtual ~IPAddress() {}
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
+ // Overloaded cast operator to allow IPAddress objects to be used where a
+ // uint32_t is expected
operator uint32_t() const
{
- return _address.dword;
- }
- bool operator==(const IPAddress& addr) const
- {
- return _address.dword == addr._address.dword;
+ return _type == IPv4 ? _address.dword[IPADDRESS_V4_DWORD_INDEX] : 0;
}
+
+ bool operator==(const IPAddress& addr) const;
bool operator==(const uint8_t* addr) const;
// 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)
- {
- return _address.bytes[index];
- }
+ uint8_t operator[](int index) const;
+ uint8_t& operator[](int index);
// Overloaded copy operators to allow initialisation of IPAddress objects from other types
IPAddress& operator=(const uint8_t *address);
@@ -83,14 +88,21 @@ class IPAddress: public Printable
virtual size_t printTo(Print& p) const;
String toString() const;
+ IPType type() { return _type; }
+
friend class EthernetClass;
friend class UDP;
friend class Client;
friend class Server;
friend class DhcpClass;
friend class DNSClient;
+
+protected:
+ bool fromString4(const char *address);
+ bool fromString6(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/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 6e56e94ac53..cf8cde4a7b2 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,34 @@ int WiFiClient::connect(IPAddress ip, uint16_t port)
{
return connect(ip,port,_timeout);
}
+
int WiFiClient::connect(IPAddress ip, uint16_t port, int32_t timeout)
{
+ struct sockaddr_storage serveraddr;
_timeout = timeout;
- int sockfd = socket(AF_INET, SOCK_STREAM, 0);
+ int sockfd = -1;
+
+ if (ip.type() == IPv6) {
+ struct sockaddr_in6 *tmpaddr = (struct sockaddr_in6 *)&serveraddr;
+ memset((char *) tmpaddr, 0, sizeof(struct sockaddr_in));
+ 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;
+ memset((char *) tmpaddr, 0, sizeof(struct sockaddr_in));
+ 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 +311,19 @@ int WiFiClient::connect(const char *host, uint16_t port)
int WiFiClient::connect(const char *host, uint16_t port, int32_t timeout)
{
+ 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);
+ } else {
+ IPAddress ip(IPv6, (uint8_t*)&srv6.u_addr.ip6.addr[0]);
+ return connect(ip, port, timeout);
+ }
+ }
IPAddress srv((uint32_t)0);
if(!WiFiGenericClass::hostByName(host, srv)){
return 0;
@@ -562,8 +592,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 a94b1419414..3f8a9d797ca 100644
--- a/libraries/WiFi/src/WiFiGeneric.cpp
+++ b/libraries/WiFi/src/WiFiGeneric.cpp
@@ -943,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
@@ -1446,6 +1446,25 @@ static void wifi_dns_found_callback(const char *name, const ip_addr_t *ipaddr, v
xEventGroupSetBits(_arduino_event_group, WIFI_DNS_DONE_BIT);
}
+/**
+ * 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
@@ -1473,6 +1492,37 @@ int WiFiGenericClass::hostByName(const char* aHostname, IPAddress& aResult)
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);
}
From bca9526b6c497d8a518dd871fe62f96251e96350 Mon Sep 17 00:00:00 2001
From: s-hadinger <49731213+s-hadinger@users.noreply.github.com>
Date: Wed, 7 Dec 2022 19:57:08 +0100
Subject: [PATCH 07/15] Fix toString() for IPv4
---
cores/esp32/IPAddress.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/cores/esp32/IPAddress.cpp b/cores/esp32/IPAddress.cpp
index 6766b4a26d2..0b41bd86f8c 100644
--- a/cores/esp32/IPAddress.cpp
+++ b/cores/esp32/IPAddress.cpp
@@ -212,7 +212,7 @@ String IPAddress::toString() const
// IPv4
char szRet[16];
- sprintf(szRet,"%u.%u.%u.%u", _address.bytes[0], _address.bytes[1], _address.bytes[2], _address.bytes[3]);
+ sprintf(szRet,"%u.%u.%u.%u", _address.bytes[IPADDRESS_V4_BYTES_INDEX], _address.bytes[IPADDRESS_V4_BYTES_INDEX+1], _address.bytes[IPADDRESS_V4_BYTES_INDEX+2], _address.bytes[IPADDRESS_V4_BYTES_INDEX+3]);
return String(szRet);
}
From 5a29acbc90b5c653dc70adf895c06993ae24e0f7 Mon Sep 17 00:00:00 2001
From: s-hadinger <49731213+s-hadinger@users.noreply.github.com>
Date: Fri, 9 Dec 2022 17:06:31 +0100
Subject: [PATCH 08/15] Full support for IPv6
---
cores/esp32/IPAddress.h | 151 +++++++++++++++++++++++++++++++---------
1 file changed, 119 insertions(+), 32 deletions(-)
diff --git a/cores/esp32/IPAddress.h b/cores/esp32/IPAddress.h
index a86bc392978..64fdc9ce79b 100644
--- a/cores/esp32/IPAddress.h
+++ b/cores/esp32/IPAddress.h
@@ -20,75 +20,110 @@
#ifndef IPAddress_h
#define IPAddress_h
-#include
#include
#include
-
-// A class to make it easier to handle and pass around IP addresses
-
-#define IPADDRESS_V4_BYTES_INDEX 12
-#define IPADDRESS_V4_DWORD_INDEX 3
+#include
enum IPType
{
- IPv4,
- IPv6
+ IPv4 = IPADDR_TYPE_V4,
+ IPv6 = IPADDR_TYPE_V6
};
class IPAddress: public Printable
{
private:
- union {
- uint8_t bytes[16];
- uint32_t dword[4];
- } _address;
- IPType _type;
+ 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 _type == IPv4 ? &_address.bytes[IPADDRESS_V4_BYTES_INDEX] : _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(IPType ip_type);
+ IPAddress(const IPAddress& from);
IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet);
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);
- IPAddress(const uint8_t *address);
- IPAddress(IPType ip_type, const uint8_t *address);
- virtual ~IPAddress() {}
+ IPAddress(uint32_t address) { ctor32(address); }
+ IPAddress(unsigned long address) { ctor32(address); }
+ IPAddress(int 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
- // uint32_t is expected
- operator uint32_t() const
- {
- return _type == IPv4 ? _address.dword[IPADDRESS_V4_DWORD_INDEX] : 0;
- }
+ // 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 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;
+ bool operator==(const IPAddress& addr) const {
+ return ip_addr_cmp(&_ip, &addr._ip);
+ }
+ 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;
- uint8_t& operator[](int index);
+ uint8_t operator[](int index) const {
+ return isV4()? *(raw_address() + index): 0;
+ }
+ 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;
- IPType type() { return _type; }
+ 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;
@@ -97,12 +132,64 @@ class IPAddress: public Printable
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 fromString4(const char *address);
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
From 95c4ba036241d337d9e741c5f7a4055629ada082 Mon Sep 17 00:00:00 2001
From: s-hadinger <49731213+s-hadinger@users.noreply.github.com>
Date: Fri, 9 Dec 2022 17:07:05 +0100
Subject: [PATCH 09/15] Full support for IPv6
---
cores/esp32/IPAddress.cpp | 402 ++++++++++++++++----------------------
1 file changed, 170 insertions(+), 232 deletions(-)
diff --git a/cores/esp32/IPAddress.cpp b/cores/esp32/IPAddress.cpp
index 0b41bd86f8c..03c8166dd59 100644
--- a/cores/esp32/IPAddress.cpp
+++ b/cores/esp32/IPAddress.cpp
@@ -22,223 +22,101 @@
#include
#include
-IPAddress::IPAddress() : IPAddress(IPv4) {}
-
-IPAddress::IPAddress(IPType ip_type)
-{
- _type = ip_type;
- memset(_address.bytes, 0, sizeof(_address.bytes));
+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)
{
- _type = IPv4;
- memset(_address.bytes, 0, sizeof(_address.bytes));
- _address.bytes[IPADDRESS_V4_BYTES_INDEX] = first_octet;
- _address.bytes[IPADDRESS_V4_BYTES_INDEX + 1] = second_octet;
- _address.bytes[IPADDRESS_V4_BYTES_INDEX + 2] = third_octet;
- _address.bytes[IPADDRESS_V4_BYTES_INDEX + 3] = fourth_octet;
+ ip_addr_copy(_ip, from._ip);
}
-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) {
- _type = IPv6;
- _address.bytes[0] = o1;
- _address.bytes[1] = o2;
- _address.bytes[2] = o3;
- _address.bytes[3] = o4;
- _address.bytes[4] = o5;
- _address.bytes[5] = o6;
- _address.bytes[6] = o7;
- _address.bytes[7] = o8;
- _address.bytes[8] = o9;
- _address.bytes[9] = o10;
- _address.bytes[10] = o11;
- _address.bytes[11] = o12;
- _address.bytes[12] = o13;
- _address.bytes[13] = o14;
- _address.bytes[14] = o15;
- _address.bytes[15] = o16;
+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(uint32_t address)
-{
- // IPv4 only
- _type = IPv4;
- memset(_address.bytes, 0, sizeof(_address.bytes));
- _address.dword[IPADDRESS_V4_DWORD_INDEX] = address;
-
- // NOTE on conversion/comparison and uint32_t:
- // These conversions are host platform dependent.
- // There is a defined integer representation of IPv4 addresses,
- // based on network byte order (will be the value on big endian systems),
- // e.g. http://2398766798 is the same as http://142.250.70.206,
- // However on little endian systems the octets 0x83, 0xFA, 0x46, 0xCE,
- // in that order, will form the integer (uint32_t) 3460758158 .
+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(const uint8_t *address) : IPAddress(IPv4, address) {}
-
-IPAddress::IPAddress(IPType ip_type, const uint8_t *address)
-{
- _type = ip_type;
- if (ip_type == IPv4) {
- memset(_address.bytes, 0, sizeof(_address.bytes));
- memcpy(&_address.bytes[IPADDRESS_V4_BYTES_INDEX], address, sizeof(uint32_t));
+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, 4);
} else {
- memcpy(_address.bytes, address, sizeof(_address.bytes));
+#if LWIP_IPV6
+ _ip = *IP6_ADDR_ANY;
+#else
+ _ip = *IP_ADDR_ANY;
+#endif
}
-}
-
-IPAddress& IPAddress::operator=(const uint8_t *address)
-{
- // IPv4 only conversion from byte pointer
- _type = IPv4;
- memset(_address.bytes, 0, sizeof(_address.bytes));
- memcpy(&_address.bytes[IPADDRESS_V4_BYTES_INDEX], address, sizeof(uint32_t));
- return *this;
-}
-IPAddress& IPAddress::operator=(uint32_t address)
-{
- // IPv4 conversion
- // See note on conversion/comparison and uint32_t
- _type = IPv4;
- memset(_address.bytes, 0, sizeof(_address.bytes));
- _address.dword[IPADDRESS_V4_DWORD_INDEX] = address;
- return *this;
-}
-
-bool IPAddress::operator==(const IPAddress& addr) const
-{
- return (addr._type == _type)
- && (memcmp(addr._address.bytes, _address.bytes, sizeof(_address.bytes)) == 0);
-}
-
-bool IPAddress::operator==(const uint8_t* addr) const
-{
- // IPv4 only comparison to byte pointer
- // Can't support IPv6 as we know our type, but not the length of the pointer
- return _type == IPv4 && memcmp(addr, &_address.bytes[IPADDRESS_V4_BYTES_INDEX], sizeof(uint32_t)) == 0;
}
-uint8_t IPAddress::operator[](int index) const {
- if (_type == IPv4) {
- return _address.bytes[IPADDRESS_V4_BYTES_INDEX + index];
- }
- return _address.bytes[index];
+void IPAddress::ctor32(uint32_t address) {
+ setV4();
+ v4() = address;
}
-uint8_t& IPAddress::operator[](int index) {
- if (_type == IPv4) {
- return _address.bytes[IPADDRESS_V4_BYTES_INDEX + index];
- }
- return _address.bytes[index];
+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;
-
- if (_type == IPv6) {
- // IPv6 IETF canonical format: compress left-most longest run of two or more zero fields, lower case
- int8_t longest_start = -1;
- int8_t longest_length = 1;
- int8_t current_start = -1;
- int8_t current_length = 0;
- for (int8_t f = 0; f < 8; f++) {
- if (_address.bytes[f * 2] == 0 && _address.bytes[f * 2 + 1] == 0) {
- if (current_start == -1) {
- current_start = f;
- current_length = 1;
- } else {
- current_length++;
- }
- if (current_length > longest_length) {
- longest_start = current_start;
- longest_length = current_length;
- }
- } else {
- current_start = -1;
- }
- }
- for (int f = 0; f < 8; f++) {
- if (f < longest_start || f >= longest_start + longest_length) {
- uint8_t c1 = _address.bytes[f * 2] >> 4;
- uint8_t c2 = _address.bytes[f * 2] & 0xf;
- uint8_t c3 = _address.bytes[f * 2 + 1] >> 4;
- uint8_t c4 = _address.bytes[f * 2 + 1] & 0xf;
- if (c1 > 0) {
- n += p.print((char)(c1 < 10 ? '0' + c1 : 'a' + c1 - 10));
- }
- if (c1 > 0 || c2 > 0) {
- n += p.print((char)(c2 < 10 ? '0' + c2 : 'a' + c2 - 10));
- }
- if (c1 > 0 || c2 > 0 || c3 > 0) {
- n += p.print((char)(c3 < 10 ? '0' + c3 : 'a' + c3 - 10));
- }
- n += p.print((char)(c4 < 10 ? '0' + c4 : 'a' + c4 - 10));
- if (f < 7) {
- n += p.print(':');
- }
- } else if (f == longest_start) {
- if (longest_start == 0) {
- n += p.print(':');
- }
- n += p.print(':');
- }
- }
- return n;
- }
-
- // IPv4
- for (int i =0; i < 3; i++)
- {
- n += p.print(_address.bytes[IPADDRESS_V4_BYTES_INDEX + i], DEC);
- n += p.print('.');
- }
- n += p.print(_address.bytes[IPADDRESS_V4_BYTES_INDEX + 3], DEC);
- return n;
-}
-
-String IPAddress::toString() const
-{
- if (_type == IPv6)
- {
- StreamString s;
- s.reserve(40);
- printTo(s);
- return s;
- }
-
- // IPv4
- char szRet[16];
- sprintf(szRet,"%u.%u.%u.%u", _address.bytes[IPADDRESS_V4_BYTES_INDEX], _address.bytes[IPADDRESS_V4_BYTES_INDEX+1], _address.bytes[IPADDRESS_V4_BYTES_INDEX+2], _address.bytes[IPADDRESS_V4_BYTES_INDEX+3]);
- return String(szRet);
-}
-
-bool IPAddress::fromString(const char *address)
-{
- if (!fromString4(address))
- {
+bool IPAddress::fromString(const char *address) {
+ if (!fromString4(address)) {
+#if LWIP_IPV6
return fromString6(address);
+#else
+ return false;
+#endif
}
return true;
}
-bool IPAddress::fromString4(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
- int16_t acc = -1; // Accumulator
+ uint16_t acc = 0; // Accumulator
uint8_t dots = 0;
- memset(_address.bytes, 0, sizeof(_address.bytes));
while (*address)
{
char c = *address++;
if (c >= '0' && c <= '9')
{
- acc = (acc < 0) ? (c - '0') : acc * 10 + (c - '0');
+ acc = acc * 10 + (c - '0');
if (acc > 255) {
// Value out of [0..255] range
return false;
@@ -247,15 +125,11 @@ bool IPAddress::fromString4(const char *address)
else if (c == '.')
{
if (dots == 3) {
- // Too many dots (there must be 3 dots)
- return false;
- }
- if (acc < 0) {
- /* No value between dots, e.g. '1..' */
+ // Too much dots (there must be 3 dots)
return false;
}
- _address.bytes[IPADDRESS_V4_BYTES_INDEX + dots++] = acc;
- acc = -1;
+ (*this)[dots++] = acc;
+ acc = 0;
}
else
{
@@ -268,23 +142,103 @@ bool IPAddress::fromString4(const char *address)
// Too few dots (there must be 3 dots)
return false;
}
- if (acc < 0) {
- /* No value between dots, e.g. '1..' */
- return false;
- }
- _address.bytes[IPADDRESS_V4_BYTES_INDEX + 3] = acc;
- _type = IPv4;
+ (*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 colons = 0, double_colons = -1;
+ int dots = 0, doubledots = -1;
while (*address)
{
char c = tolower(*address++);
- if (isalnum(c) && c <= 'f') {
+ if (isalnum(c)) {
if (c >= 'a')
c -= 'a' - '0' - 10;
acc = acc * 16 + (c - '0');
@@ -294,27 +248,17 @@ bool IPAddress::fromString6(const char *address) {
}
else if (c == ':') {
if (*address == ':') {
- if (double_colons >= 0) {
+ if (doubledots >= 0)
// :: allowed once
return false;
- }
- if (*address != '\0' && *(address + 1) == ':') {
- // ::: not allowed
- return false;
- }
// remember location
- double_colons = colons + !!acc;
+ doubledots = dots + !!acc;
address++;
- } else if (*address == '\0') {
- // can't end with a single colon
- return false;
}
- if (colons == 7)
+ if (dots == 7)
// too many separators
return false;
- _address.bytes[colons * 2] = acc >> 8;
- _address.bytes[colons * 2 + 1] = acc & 0xff;
- colons++;
+ raw6()[dots++] = PP_HTONS(acc);
acc = 0;
}
else
@@ -322,28 +266,22 @@ bool IPAddress::fromString6(const char *address) {
return false;
}
- if (double_colons == -1 && colons != 7) {
+ if (doubledots == -1 && dots != 7)
// Too few separators
return false;
- }
- if (double_colons > -1 && colons > 6) {
- // Too many segments (double colon must be at least one zero field)
- return false;
- }
- _address.bytes[colons * 2] = acc >> 8;
- _address.bytes[colons * 2 + 1] = acc & 0xff;
- colons++;
-
- if (double_colons != -1) {
- for (int i = colons * 2 - double_colons * 2 - 1; i >= 0; i--)
- _address.bytes[16 - colons * 2 + double_colons * 2 + i] = _address.bytes[double_colons * 2 + i];
- for (int i = double_colons * 2; i < 16 - colons * 2 + double_colons * 2; i++)
- _address.bytes[i] = 0;
+ 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;
}
- _type = IPv6;
+ 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
From e0f5aca9018d9e21a01ba18f68f3b55dc389b2c5 Mon Sep 17 00:00:00 2001
From: s-hadinger <49731213+s-hadinger@users.noreply.github.com>
Date: Fri, 9 Dec 2022 20:52:24 +0100
Subject: [PATCH 10/15] Fixed ambiguous definitions
---
cores/esp32/IPAddress.h | 2 --
1 file changed, 2 deletions(-)
diff --git a/cores/esp32/IPAddress.h b/cores/esp32/IPAddress.h
index 64fdc9ce79b..d6d2e947f67 100644
--- a/cores/esp32/IPAddress.h
+++ b/cores/esp32/IPAddress.h
@@ -55,8 +55,6 @@ class IPAddress: public Printable
IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet);
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(unsigned long address) { ctor32(address); }
- IPAddress(int address) { ctor32(address); }
IPAddress(const uint8_t *address); // v4 only
IPAddress(IPType type, const uint8_t *address);
From a4189bcc7a917432f27dc7af8dc3a050abb07f3b Mon Sep 17 00:00:00 2001
From: s-hadinger <49731213+s-hadinger@users.noreply.github.com>
Date: Fri, 9 Dec 2022 21:35:25 +0100
Subject: [PATCH 11/15] Fix sizeof
---
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 cf8cde4a7b2..fc15ce138d3 100644
--- a/libraries/WiFi/src/WiFiClient.cpp
+++ b/libraries/WiFi/src/WiFiClient.cpp
@@ -224,7 +224,7 @@ int WiFiClient::connect(IPAddress ip, uint16_t port, int32_t timeout)
if (ip.type() == IPv6) {
struct sockaddr_in6 *tmpaddr = (struct sockaddr_in6 *)&serveraddr;
- memset((char *) tmpaddr, 0, sizeof(struct sockaddr_in));
+ memset((char *) tmpaddr, 0, sizeof(struct sockaddr_in6));
sockfd = socket(AF_INET6, SOCK_STREAM, 0);
tmpaddr->sin6_family = AF_INET6;
memcpy(tmpaddr->sin6_addr.un.u8_addr, &ip[0], 16);
From 5970324e80a6ae51085dcb29cd78a8cce41dd6d2 Mon Sep 17 00:00:00 2001
From: s-hadinger <49731213+s-hadinger@users.noreply.github.com>
Date: Fri, 9 Dec 2022 21:52:08 +0100
Subject: [PATCH 12/15] Simplify initilization and remove memset
---
libraries/WiFi/src/WiFiClient.cpp | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/libraries/WiFi/src/WiFiClient.cpp b/libraries/WiFi/src/WiFiClient.cpp
index fc15ce138d3..ac8577fba80 100644
--- a/libraries/WiFi/src/WiFiClient.cpp
+++ b/libraries/WiFi/src/WiFiClient.cpp
@@ -218,20 +218,18 @@ int WiFiClient::connect(IPAddress ip, uint16_t port)
int WiFiClient::connect(IPAddress ip, uint16_t port, int32_t timeout)
{
- struct sockaddr_storage serveraddr;
+ struct sockaddr_storage serveraddr = {};
_timeout = timeout;
int sockfd = -1;
if (ip.type() == IPv6) {
struct sockaddr_in6 *tmpaddr = (struct sockaddr_in6 *)&serveraddr;
- memset((char *) tmpaddr, 0, sizeof(struct sockaddr_in6));
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;
- memset((char *) tmpaddr, 0, sizeof(struct sockaddr_in));
sockfd = socket(AF_INET, SOCK_STREAM, 0);
tmpaddr->sin_family = AF_INET;
tmpaddr->sin_addr.s_addr = ip;
From 7b23209caa1045fc7c45dbfa7d13b3c2d28fbe37 Mon Sep 17 00:00:00 2001
From: s-hadinger <49731213+s-hadinger@users.noreply.github.com>
Date: Fri, 9 Dec 2022 22:50:17 +0100
Subject: [PATCH 13/15] Enable support for IPv6 in UDP
---
libraries/WiFi/src/WiFiUdp.cpp | 73 ++++++++++++++++++++++++++--------
1 file changed, 57 insertions(+), 16 deletions(-)
diff --git a/libraries/WiFi/src/WiFiUdp.cpp b/libraries/WiFi/src/WiFiUdp.cpp
index 476b5a4a8b5..c0042c8659e 100644
--- a/libraries/WiFi/src/WiFiUdp.cpp
+++ b/libraries/WiFi/src/WiFiUdp.cpp
@@ -48,7 +48,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;
}
@@ -60,16 +64,35 @@ 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;
+ memset((char *) tmpaddr, 0, sizeof(struct sockaddr_in));
+ tmpaddr->sin6_family = AF_INET6;
+ tmpaddr->sin6_port = htons(server_port);
+ // memcpy(tmpaddr->sin6_addr.un.u8_addr, &ip[0], 16); // TODO
+ tmpaddr->sin6_addr = in6addr_any;
+ 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);
+ }
+ //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 , (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;
}
@@ -80,7 +103,7 @@ uint8_t WiFiUDP::begin(uint16_t p){
uint8_t WiFiUDP::beginMulticast(IPAddress a, uint16_t p){
if(begin(IPAddress(INADDR_ANY), p)){
- if(a != 0){
+ 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;
@@ -109,11 +132,15 @@ void WiFiUDP::stop(){
}
if(udp_server == -1)
return;
- if(multicast_ip != 0){
+ 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 = IPAddress(INADDR_ANY);
}
close(udp_server);
@@ -174,14 +201,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;
}
From 28501229a7906cb238e1f35f27999719318c95c7 Mon Sep 17 00:00:00 2001
From: Jason2866 <24528715+Jason2866@users.noreply.github.com>
Date: Mon, 12 Dec 2022 11:58:14 +0100
Subject: [PATCH 14/15] Update PlatformIO build script PR 7579
---
tools/platformio-build.py | 94 +++++++++++++--------------------------
1 file changed, 31 insertions(+), 63 deletions(-)
diff --git a/tools/platformio-build.py b/tools/platformio-build.py
index 39bda3a8829..5f3086e0810 100644
--- a/tools/platformio-build.py
+++ b/tools/platformio-build.py
@@ -82,52 +82,41 @@ def get_bootloader_image(variants_dir):
return (
variant_bootloader
if isfile(variant_bootloader)
- else join(
- FRAMEWORK_DIR,
- "tools",
- "sdk",
- build_mcu,
- "bin",
- "bootloader_${__get_board_boot_mode(__env__)}_${__get_board_f_flash(__env__)}.bin",
+ else generate_bootloader_image(
+ join(
+ FRAMEWORK_DIR,
+ "tools",
+ "sdk",
+ build_mcu,
+ "bin",
+ "bootloader_${__get_board_boot_mode(__env__)}_${__get_board_f_flash(__env__)}.elf",
+ )
)
)
-def get_patched_bootloader_image(original_bootloader_image, bootloader_offset):
- patched_bootloader_image = join(env.subst("$BUILD_DIR"), "patched_bootloader.bin")
+def generate_bootloader_image(bootloader_elf):
bootloader_cmd = env.Command(
- patched_bootloader_image,
- original_bootloader_image,
- env.VerboseAction(
- " ".join(
- [
- '"$PYTHONEXE"',
- join(
- platform.get_package_dir("tool-esptoolpy") or "", "esptool.py"
- ),
- "--chip",
- build_mcu,
- "merge_bin",
- "-o",
- "$TARGET",
- "--flash_mode",
- "${__get_board_flash_mode(__env__)}",
- "--flash_freq",
- "${__get_board_f_flash(__env__)}",
- "--flash_size",
- board_config.get("upload.flash_size", "4MB"),
- "--target-offset",
- bootloader_offset,
- bootloader_offset,
- "$SOURCE",
- ]
- ),
- "Updating bootloader headers",
- ),
+ join("$BUILD_DIR", "bootloader.bin"),
+ bootloader_elf,
+ env.VerboseAction(" ".join([
+ '"$PYTHONEXE" "$OBJCOPY"',
+ "--chip", build_mcu, "elf2image",
+ "--flash_mode", "${__get_board_flash_mode(__env__)}",
+ "--flash_freq", "${__get_board_f_flash(__env__)}",
+ "--flash_size", board_config.get("upload.flash_size", "4MB"),
+ "-o", "$TARGET", "$SOURCES"
+ ]), "Building $TARGET"),
)
+
env.Depends("$BUILD_DIR/$PROGNAME$PROGSUFFIX", bootloader_cmd)
- return patched_bootloader_image
+ # Because the Command always returns a NodeList, we have to
+ # access the first element in the list to get the Node object
+ # that actually represents the bootloader image.
+ # Also, this file is later used in generic Python code, so the
+ # Node object in converted to a generic string
+ return str(bootloader_cmd[0])
def add_tinyuf2_extra_image():
@@ -210,34 +199,13 @@ def add_tinyuf2_extra_image():
# Process framework extra images
#
-# Starting with v2.0.4 the Arduino core contains updated bootloader images that have
-# innacurate default headers. This results in bootloops if firmware is flashed via
-# OpenOCD (e.g. debugging or uploading via debug tools). For this reason, before
-# uploading or debugging we need to adjust the bootloader binary according to
-# the values of the --flash-size and --flash-mode arguments.
-# Note: This behavior doesn't occur if uploading is done via esptoolpy, as esptoolpy
-# overrides the binary image headers before flashing.
-
-bootloader_patch_required = bool(
- env.get("PIOFRAMEWORK", []) == ["arduino"]
- and (
- "debug" in env.GetBuildType()
- or env.subst("$UPLOAD_PROTOCOL") in board_config.get("debug.tools", {})
- or env.IsIntegrationDump()
- )
-)
-
-bootloader_image_path = get_bootloader_image(variants_dir)
-bootloader_offset = "0x1000" if build_mcu in ("esp32", "esp32s2") else "0x0000"
-if bootloader_patch_required:
- bootloader_image_path = get_patched_bootloader_image(
- bootloader_image_path, bootloader_offset
- )
-
env.Append(
LIBSOURCE_DIRS=[join(FRAMEWORK_DIR, "libraries")],
FLASH_EXTRA_IMAGES=[
- (bootloader_offset, bootloader_image_path),
+ (
+ "0x1000" if build_mcu in ("esp32", "esp32s2") else "0x0000",
+ get_bootloader_image(variants_dir),
+ ),
("0x8000", join(env.subst("$BUILD_DIR"), "partitions.bin")),
("0xe000", join(FRAMEWORK_DIR, "tools", "partitions", "boot_app0.bin")),
]
From 3515fb3b86d155db2a6f52e23fccbcd6b63949e1 Mon Sep 17 00:00:00 2001
From: s-hadinger <49731213+s-hadinger@users.noreply.github.com>
Date: Tue, 13 Dec 2022 16:34:51 +0100
Subject: [PATCH 15/15] Fix IPv6 size
---
cores/esp32/IPAddress.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/cores/esp32/IPAddress.cpp b/cores/esp32/IPAddress.cpp
index 03c8166dd59..b8f7b7aae1b 100644
--- a/cores/esp32/IPAddress.cpp
+++ b/cores/esp32/IPAddress.cpp
@@ -70,7 +70,7 @@ IPAddress::IPAddress(IPType type, const uint8_t *address) {
memcpy(&this->_ip.u_addr.ip4, address, 4);
} else if (type == IPv6) {
setV6();
- memcpy(&this->_ip.u_addr.ip6.addr[0], address, 4);
+ memcpy(&this->_ip.u_addr.ip6.addr[0], address, 16);
} else {
#if LWIP_IPV6
_ip = *IP6_ADDR_ANY;