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/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(); 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/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 ------------- 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(); }; 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 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..ac8577fba80 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) { + 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; + 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) { + 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 +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 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); } 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; } 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")), ] 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 */