diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..ee0c3dc --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,75 @@ +## Contributing to AsyncUdp_ESP32_ENC + +### Reporting Bugs + +Please report bugs in AsyncUdp_ESP32_ENC if you find them. + +However, before reporting a bug please check through the following: + +* [Existing Open Issues](https://github.com/khoih-prog/AsyncUdp_ESP32_ENC/issues) - someone might have already encountered this. + +If you don't find anything, please [open a new issue](https://github.com/khoih-prog/AsyncUdp_ESP32_ENC/issues/new). + +### How to submit a bug report + +Please ensure to specify the following: + +* Arduino IDE version (e.g. 1.8.19) or Platform.io version +* `ESP32` Core Version (e.g. ESP32 v2.0.5) +* Contextual information (e.g. what you were trying to achieve) +* Simplest possible steps to reproduce +* Anything that might be relevant in your opinion, such as: + * Operating system (Windows, Ubuntu, etc.) and the output of `uname -a` + * Network configuration + + +### Example + +``` +Arduino IDE version: 1.8.19 +ESP32 Core Version 2.0.5 +OS: Ubuntu 20.04 LTS +Linux xy-Inspiron-3593 5.15.0-53-generic #59~20.04.1-Ubuntu SMP Thu Oct 20 15:10:22 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux + +Context: +I encountered a crash while using this library +Steps to reproduce: +1. ... +2. ... +3. ... +4. ... +``` + +### Additional context + +Add any other context about the problem here. + +--- + +### Sending Feature Requests + +Feel free to post feature requests. It's helpful if you can explain exactly why the feature would be useful. + +There are usually some outstanding feature requests in the [existing issues list](https://github.com/khoih-prog/AsyncUdp_ESP32_ENC/issues?q=is%3Aopen+is%3Aissue+label%3Aenhancement), feel free to add comments to them. + +--- + +### Sending Pull Requests + +Pull Requests with changes and fixes are also welcome! + +Please use the `astyle` to reformat the updated library code as follows (demo for Ubuntu Linux) + +1. Change directory to the library GitHub + +``` +xy@xy-Inspiron-3593:~$ cd Arduino/xy/AsyncUdp_ESP32_ENC_GitHub/ +xy@xy-Inspiron-3593:~/Arduino/xy/AsyncUdp_ESP32_ENC_GitHub$ +``` + +2. Issue astyle command + +``` +xy@xy-Inspiron-3593:~/Arduino/xy/AsyncUdp_ESP32_ENC_GitHub$ bash utils/restyle.sh +``` + diff --git a/LICENSE b/LICENSE index f288702..94a9ed0 100644 --- a/LICENSE +++ b/LICENSE @@ -1,7 +1,7 @@ GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 - Copyright (C) 2007 Free Software Foundation, Inc. + Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. @@ -645,7 +645,7 @@ the "copyright" line and a pointer to where the full notice is found. GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program. If not, see . + along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. @@ -664,11 +664,11 @@ might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see -. +. The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read -. +. diff --git a/LibraryPatches/esp32/cores/esp32/Server.h b/LibraryPatches/esp32/cores/esp32/Server.h new file mode 100644 index 0000000..2a677df --- /dev/null +++ b/LibraryPatches/esp32/cores/esp32/Server.h @@ -0,0 +1,35 @@ +/* + Server.h - Base class that provides Server + Copyright (c) 2011 Adrian McEwen. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef server_h +#define server_h + +#include "Print.h" + +class Server: public Print +{ + public: + // KH, change to fix compiler error for EthernetWebServer + // error: cannot declare field 'EthernetWebServer::_server' to be of abstract type 'EthernetServer' + // virtual void begin(uint16_t port=0) =0; + //virtual void begin() = 0; + void begin() {}; +}; + +#endif diff --git a/changelog.md b/changelog.md new file mode 100644 index 0000000..473ecb8 --- /dev/null +++ b/changelog.md @@ -0,0 +1,35 @@ +# AsyncUdp_ESP32_ENC + + +[![arduino-library-badge](https://www.ardu-badge.com/badge/AsyncUdp_ESP32_ENC.svg?)](https://www.ardu-badge.com/AsyncUdp_ESP32_ENC) +[![GitHub release](https://img.shields.io/github/release/khoih-prog/AsyncUdp_ESP32_ENC.svg)](https://github.com/khoih-prog/AsyncUdp_ESP32_ENC/releases) +[![contributions welcome](https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat)](#Contributing) +[![GitHub issues](https://img.shields.io/github/issues/khoih-prog/AsyncUdp_ESP32_ENC.svg)](http://github.com/khoih-prog/AsyncUdp_ESP32_ENC/issues) + + +Donate to my libraries using BuyMeACoffee + + + + +--- +--- + +## Table of Contents + +* [Changelog](#changelog) + * [Initial Releases v2.0.0](#initial-releases-v200) + +--- +--- + +## Changelog + +### Initial Releases v2.0.0 + +1. Initial coding to port [AsyncUDP](https://github.com/espressif/arduino-esp32/tree/master/libraries/AsyncUDP) to ESP32 boards using LwIP ENC28J60 Ethernet +2. Add more examples. +3. Add debugging features. +4. Bump up to v2.0.0 to sync with [AsyncUDP v2.0.0](https://github.com/espressif/arduino-esp32/tree/master/libraries/AsyncUDP). + + diff --git a/examples/AsyncUDPClient/AsyncUDPClient.ino b/examples/AsyncUDPClient/AsyncUDPClient.ino new file mode 100644 index 0000000..0513125 --- /dev/null +++ b/examples/AsyncUDPClient/AsyncUDPClient.ino @@ -0,0 +1,175 @@ +/**************************************************************************************************************************** + Async_UdpClient.ino + + AsyncUDP_ESP32_ENC is a Async UDP library for the the ESP32 with Ethernet ENC28J60 + + Based on and modified from ESPAsyncUDP Library (https://github.com/me-no-dev/ESPAsyncUDP) + Built by Khoi Hoang https://github.com/khoih-prog/AsyncUDP_ESP32_ENC + Licensed under GPLv3 license + *****************************************************************************************************************************/ + +#if !( defined(ESP32) ) + #error This code is designed for (ESP32 + ENC28J60) to run on ESP32 platform! Please check your Tools->Board setting. +#endif + +#include + +#define ASYNC_UDP_ESP32_ENC_DEBUG_PORT Serial + +// Use from 0 to 4. Higher number, more debugging messages and memory usage. +#define _ASYNC_UDP_ESP32_ENC_LOGLEVEL_ 1 + +////////////////////////////////////////////////////////// + +// Optional values to override default settings +//#define SPI_HOST 1 +//#define SPI_CLOCK_MHZ 8 + +// Must connect INT to GPIOxx or not working +//#define INT_GPIO 4 + +//#define MISO_GPIO 19 +//#define MOSI_GPIO 23 +//#define SCK_GPIO 18 +//#define CS_GPIO 5 + +////////////////////////////////////////////////////////// + +#include + +IPAddress remoteIPAddress = IPAddress(192, 168, 2, 112); + +#define UDP_REMOTE_PORT 5698 + +///////////////////////////////////////////// + +// Enter a MAC address and IP address for your controller below. +#define NUMBER_OF_MAC 20 + +byte mac[][NUMBER_OF_MAC] = +{ + { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0x01 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xBE, 0x02 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0x03 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xBE, 0x04 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0x05 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xBE, 0x06 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0x07 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xBE, 0x08 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0x09 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xBE, 0x0A }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0x0B }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xBE, 0x0C }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0x0D }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xBE, 0x0E }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0x0F }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xBE, 0x10 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0x11 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xBE, 0x12 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0x13 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xBE, 0x14 }, +}; + +// Select the IP address according to your local network +IPAddress myIP(192, 168, 2, 232); +IPAddress myGW(192, 168, 2, 1); +IPAddress mySN(255, 255, 255, 0); + +// Google DNS Server IP +IPAddress myDNS(8, 8, 8, 8); + +///////////////////////////////////////////// + +AsyncUDP udp; + +void parsePacket(AsyncUDPPacket packet) +{ + Serial.print("UDP Packet Type: "); + Serial.print(packet.isBroadcast() ? "Broadcast" : packet.isMulticast() ? "Multicast" : "Unicast"); + Serial.print(", From: "); + Serial.print(packet.remoteIP()); + Serial.print(":"); + Serial.print(packet.remotePort()); + Serial.print(", To: "); + Serial.print(packet.localIP()); + Serial.print(":"); + Serial.print(packet.localPort()); + Serial.print(", Length: "); + Serial.print(packet.length()); + Serial.print(", Data: "); + Serial.write(packet.data(), packet.length()); + Serial.println(); + //reply to the client + packet.printf("Got %u bytes of data", packet.length()); +} + +void setup() +{ + Serial.begin(115200); + + while (!Serial && (millis() < 5000)); + + Serial.print(F("\nStart Async_UDPClient on ")); + Serial.print(ARDUINO_BOARD); + Serial.print(F(" with ")); + Serial.println(SHIELD_TYPE); + Serial.println(WEBSERVER_ESP32_ENC_VERSION); + Serial.println(ASYNC_UDP_ESP32_ENC_VERSION); + + Serial.setDebugOutput(true); + + ET_LOGWARN(F("Default SPI pinout:")); + ET_LOGWARN1(F("MOSI:"), MOSI_GPIO); + ET_LOGWARN1(F("MISO:"), MISO_GPIO); + ET_LOGWARN1(F("SCK:"), SCK_GPIO); + ET_LOGWARN1(F("CS:"), CS_GPIO); + ET_LOGWARN1(F("INT:"), INT_GPIO); + ET_LOGWARN1(F("SPI Clock (MHz):"), SPI_CLOCK_MHZ); + ET_LOGWARN(F("=========================")); + + /////////////////////////////////// + + // To be called before ETH.begin() + ESP32_ENC_onEvent(); + + // start the ethernet connection and the server: + // Use DHCP dynamic IP and random mac + uint16_t index = millis() % NUMBER_OF_MAC; + + //bool begin(int MISO_GPIO, int MOSI_GPIO, int SCLK_GPIO, int CS_GPIO, int INT_GPIO, int SPI_CLOCK_MHZ, + // int SPI_HOST, uint8_t *ENC28J60_Mac = ENC28J60_Default_Mac); + //ETH.begin( MISO_GPIO, MOSI_GPIO, SCK_GPIO, CS_GPIO, INT_GPIO, SPI_CLOCK_MHZ, SPI_HOST ); + ETH.begin( MISO_GPIO, MOSI_GPIO, SCK_GPIO, CS_GPIO, INT_GPIO, SPI_CLOCK_MHZ, SPI_HOST, mac[index] ); + + // Static IP, leave without this line to get IP via DHCP + //bool config(IPAddress local_ip, IPAddress gateway, IPAddress subnet, IPAddress dns1 = 0, IPAddress dns2 = 0); + //ETH.config(myIP, myGW, mySN, myDNS); + + ESP32_ENC_waitForConnect(); + + /////////////////////////////////// + + // Client address + Serial.print("Async_UDPClient started @ IP address: "); + Serial.println(ETH.localIP()); + + if (udp.connect(remoteIPAddress, UDP_REMOTE_PORT)) + { + Serial.println("UDP connected"); + + udp.onPacket([](AsyncUDPPacket packet) + { + parsePacket( packet); + }); + + //Send unicast + udp.print("Hello Server!"); + } +} + +void loop() +{ + delay(10000); + //Send broadcast on port UDP_REMOTE_PORT = 1234 + udp.broadcastTo("Anyone here?", UDP_REMOTE_PORT); +} diff --git a/examples/AsyncUDPMulticastServer/AsyncUDPMulticastServer.ino b/examples/AsyncUDPMulticastServer/AsyncUDPMulticastServer.ino new file mode 100644 index 0000000..01050ef --- /dev/null +++ b/examples/AsyncUDPMulticastServer/AsyncUDPMulticastServer.ino @@ -0,0 +1,173 @@ +/**************************************************************************************************************************** + AsyncUDPMulticastServer.ino + + AsyncUDP_ESP32_ENC is a Async UDP library for the the ESP32 with Ethernet ENC28J60 + + Based on and modified from ESPAsyncUDP Library (https://github.com/me-no-dev/ESPAsyncUDP) + Built by Khoi Hoang https://github.com/khoih-prog/AsyncUDP_ESP32_ENC + Licensed under GPLv3 license + *****************************************************************************************************************************/ + +#if !( defined(ESP32) ) + #error This code is designed for (ESP32 + ENC28J60) to run on ESP32 platform! Please check your Tools->Board setting. +#endif + +#include + +#define ASYNC_UDP_ESP32_ENC_DEBUG_PORT Serial + +// Use from 0 to 4. Higher number, more debugging messages and memory usage. +#define _ASYNC_UDP_ESP32_ENC_LOGLEVEL_ 1 + +////////////////////////////////////////////////////////// + +// Optional values to override default settings +//#define SPI_HOST 1 +//#define SPI_CLOCK_MHZ 8 + +// Must connect INT to GPIOxx or not working +//#define INT_GPIO 4 + +//#define MISO_GPIO 19 +//#define MOSI_GPIO 23 +//#define SCK_GPIO 18 +//#define CS_GPIO 5 + +////////////////////////////////////////////////////////// + +#include + +///////////////////////////////////////////// + +// Enter a MAC address and IP address for your controller below. +#define NUMBER_OF_MAC 20 + +byte mac[][NUMBER_OF_MAC] = +{ + { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0x01 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xBE, 0x02 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0x03 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xBE, 0x04 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0x05 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xBE, 0x06 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0x07 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xBE, 0x08 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0x09 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xBE, 0x0A }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0x0B }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xBE, 0x0C }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0x0D }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xBE, 0x0E }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0x0F }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xBE, 0x10 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0x11 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xBE, 0x12 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0x13 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xBE, 0x14 }, +}; + +// Select the IP address according to your local network +IPAddress myIP(192, 168, 2, 232); +IPAddress myGW(192, 168, 2, 1); +IPAddress mySN(255, 255, 255, 0); + +// Google DNS Server IP +IPAddress myDNS(8, 8, 8, 8); + +///////////////////////////////////////////// + +AsyncUDP udp; + +void parsePacket(AsyncUDPPacket packet) +{ + Serial.print("UDP Packet Type: "); + Serial.print(packet.isBroadcast() ? "Broadcast" : packet.isMulticast() ? "Multicast" : "Unicast"); + Serial.print(", From: "); + Serial.print(packet.remoteIP()); + Serial.print(":"); + Serial.print(packet.remotePort()); + Serial.print(", To: "); + Serial.print(packet.localIP()); + Serial.print(":"); + Serial.print(packet.localPort()); + Serial.print(", Length: "); + Serial.print(packet.length()); + Serial.print(", Data: "); + Serial.write(packet.data(), packet.length()); + Serial.println(); + //reply to the client + packet.printf("Got %u bytes of data", packet.length()); +} + +void setup() +{ + Serial.begin(115200); + + while (!Serial && (millis() < 5000)); + + Serial.print(F("\nStart AsyncUDPMulticastServer on ")); + Serial.print(ARDUINO_BOARD); + Serial.print(F(" with ")); + Serial.println(SHIELD_TYPE); + Serial.println(WEBSERVER_ESP32_ENC_VERSION); + Serial.println(ASYNC_UDP_ESP32_ENC_VERSION); + + Serial.setDebugOutput(true); + + ET_LOGWARN(F("Default SPI pinout:")); + ET_LOGWARN1(F("MOSI:"), MOSI_GPIO); + ET_LOGWARN1(F("MISO:"), MISO_GPIO); + ET_LOGWARN1(F("SCK:"), SCK_GPIO); + ET_LOGWARN1(F("CS:"), CS_GPIO); + ET_LOGWARN1(F("INT:"), INT_GPIO); + ET_LOGWARN1(F("SPI Clock (MHz):"), SPI_CLOCK_MHZ); + ET_LOGWARN(F("=========================")); + + /////////////////////////////////// + + // To be called before ETH.begin() + ESP32_ENC_onEvent(); + + // start the ethernet connection and the server: + // Use DHCP dynamic IP and random mac + uint16_t index = millis() % NUMBER_OF_MAC; + + //bool begin(int MISO_GPIO, int MOSI_GPIO, int SCLK_GPIO, int CS_GPIO, int INT_GPIO, int SPI_CLOCK_MHZ, + // int SPI_HOST, uint8_t *ENC28J60_Mac = ENC28J60_Default_Mac); + //ETH.begin( MISO_GPIO, MOSI_GPIO, SCK_GPIO, CS_GPIO, INT_GPIO, SPI_CLOCK_MHZ, SPI_HOST ); + ETH.begin( MISO_GPIO, MOSI_GPIO, SCK_GPIO, CS_GPIO, INT_GPIO, SPI_CLOCK_MHZ, SPI_HOST, mac[index] ); + + // Static IP, leave without this line to get IP via DHCP + //bool config(IPAddress local_ip, IPAddress gateway, IPAddress subnet, IPAddress dns1 = 0, IPAddress dns2 = 0); + //ETH.config(myIP, myGW, mySN, myDNS); + + ESP32_ENC_waitForConnect(); + + /////////////////////////////////// + + // Client address + Serial.print("Async_UDPClient started @ IP address: "); + Serial.println(ETH.localIP()); + + + if (udp.listenMulticast(IPAddress(239, 1, 2, 3), 1234)) + { + Serial.print("UDP Listening on IP: "); + Serial.println(ETH.localIP()); + + udp.onPacket([](AsyncUDPPacket packet) + { + parsePacket(packet); + }); + + //Send multicast + udp.print("Hello!"); + } +} + +void loop() +{ + delay(1000); + //Send multicast + udp.print("Anyone here?"); +} diff --git a/examples/AsyncUDPServer/AsyncUDPServer.ino b/examples/AsyncUDPServer/AsyncUDPServer.ino new file mode 100644 index 0000000..08e8408 --- /dev/null +++ b/examples/AsyncUDPServer/AsyncUDPServer.ino @@ -0,0 +1,164 @@ +/**************************************************************************************************************************** + Async_UdpServer.ino + + AsyncUDP_ESP32_ENC is a Async UDP library for the the ESP32 with Ethernet ENC28J60 + + Based on and modified from ESPAsyncUDP Library (https://github.com/me-no-dev/ESPAsyncUDP) + Built by Khoi Hoang https://github.com/khoih-prog/AsyncUDP_ESP32_ENC + Licensed under GPLv3 license + *****************************************************************************************************************************/ + +#if !( defined(ESP32) ) + #error This code is designed for (ESP32 + ENC28J60) to run on ESP32 platform! Please check your Tools->Board setting. +#endif + +#include + +#define ASYNC_UDP_ESP32_ENC_DEBUG_PORT Serial + +// Use from 0 to 4. Higher number, more debugging messages and memory usage. +#define _ASYNC_UDP_ESP32_ENC_LOGLEVEL_ 1 + +////////////////////////////////////////////////////////// + +// Optional values to override default settings +//#define SPI_HOST 1 +//#define SPI_CLOCK_MHZ 8 + +// Must connect INT to GPIOxx or not working +//#define INT_GPIO 4 + +//#define MISO_GPIO 19 +//#define MOSI_GPIO 23 +//#define SCK_GPIO 18 +//#define CS_GPIO 5 + +////////////////////////////////////////////////////////// + +#include + +///////////////////////////////////////////// + +// Enter a MAC address and IP address for your controller below. +#define NUMBER_OF_MAC 20 + +byte mac[][NUMBER_OF_MAC] = +{ + { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0x01 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xBE, 0x02 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0x03 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xBE, 0x04 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0x05 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xBE, 0x06 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0x07 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xBE, 0x08 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0x09 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xBE, 0x0A }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0x0B }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xBE, 0x0C }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0x0D }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xBE, 0x0E }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0x0F }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xBE, 0x10 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0x11 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xBE, 0x12 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0x13 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xBE, 0x14 }, +}; + +// Select the IP address according to your local network +IPAddress myIP(192, 168, 2, 232); +IPAddress myGW(192, 168, 2, 1); +IPAddress mySN(255, 255, 255, 0); + +// Google DNS Server IP +IPAddress myDNS(8, 8, 8, 8); + +///////////////////////////////////////////// + +AsyncUDP udp; + +void setup() +{ + Serial.begin(115200); + + while (!Serial && (millis() < 5000)); + + Serial.print(F("\nStart Async_UdpServer on ")); + Serial.print(ARDUINO_BOARD); + Serial.print(F(" with ")); + Serial.println(SHIELD_TYPE); + Serial.println(WEBSERVER_ESP32_ENC_VERSION); + Serial.println(ASYNC_UDP_ESP32_ENC_VERSION); + + Serial.setDebugOutput(true); + + ET_LOGWARN(F("Default SPI pinout:")); + ET_LOGWARN1(F("MOSI:"), MOSI_GPIO); + ET_LOGWARN1(F("MISO:"), MISO_GPIO); + ET_LOGWARN1(F("SCK:"), SCK_GPIO); + ET_LOGWARN1(F("CS:"), CS_GPIO); + ET_LOGWARN1(F("INT:"), INT_GPIO); + ET_LOGWARN1(F("SPI Clock (MHz):"), SPI_CLOCK_MHZ); + ET_LOGWARN(F("=========================")); + + /////////////////////////////////// + + // To be called before ETH.begin() + ESP32_ENC_onEvent(); + + // start the ethernet connection and the server: + // Use DHCP dynamic IP and random mac + uint16_t index = millis() % NUMBER_OF_MAC; + + //bool begin(int MISO_GPIO, int MOSI_GPIO, int SCLK_GPIO, int CS_GPIO, int INT_GPIO, int SPI_CLOCK_MHZ, + // int SPI_HOST, uint8_t *ENC28J60_Mac = ENC28J60_Default_Mac); + //ETH.begin( MISO_GPIO, MOSI_GPIO, SCK_GPIO, CS_GPIO, INT_GPIO, SPI_CLOCK_MHZ, SPI_HOST ); + ETH.begin( MISO_GPIO, MOSI_GPIO, SCK_GPIO, CS_GPIO, INT_GPIO, SPI_CLOCK_MHZ, SPI_HOST, mac[index] ); + + // Static IP, leave without this line to get IP via DHCP + //bool config(IPAddress local_ip, IPAddress gateway, IPAddress subnet, IPAddress dns1 = 0, IPAddress dns2 = 0); + //ETH.config(myIP, myGW, mySN, myDNS); + + ESP32_ENC_waitForConnect(); + + /////////////////////////////////// + + // Client address + Serial.print("AsyncUDPServer started @ IP address: "); + Serial.println(ETH.localIP()); + + if (udp.listen(1234)) + { + Serial.print("UDP Listening on IP: "); + Serial.println(ETH.localIP()); + + udp.onPacket([](AsyncUDPPacket packet) + { + Serial.print("UDP Packet Type: "); + Serial.print(packet.isBroadcast() ? "Broadcast" : packet.isMulticast() ? "Multicast" : "Unicast"); + Serial.print(", From: "); + Serial.print(packet.remoteIP()); + Serial.print(":"); + Serial.print(packet.remotePort()); + Serial.print(", To: "); + Serial.print(packet.localIP()); + Serial.print(":"); + Serial.print(packet.localPort()); + Serial.print(", Length: "); + Serial.print(packet.length()); + Serial.print(", Data: "); + Serial.write(packet.data(), packet.length()); + Serial.println(); + //reply to the client + packet.printf("Got %u bytes of data", packet.length()); + }); + } +} + +void loop() +{ + delay(1000); + //Send broadcast + udp.broadcast("Anyone here?"); +} diff --git a/examples/AsyncUdpNTPClient/AsyncUdpNTPClient.ino b/examples/AsyncUdpNTPClient/AsyncUdpNTPClient.ino new file mode 100644 index 0000000..b37fe8a --- /dev/null +++ b/examples/AsyncUdpNTPClient/AsyncUdpNTPClient.ino @@ -0,0 +1,244 @@ +/**************************************************************************************************************************** + AsyncUdpNTPClient.ino + + AsyncUDP_ESP32_ENC is a Async UDP library for the the ESP32 with Ethernet ENC28J60 + + Based on and modified from ESPAsyncUDP Library (https://github.com/me-no-dev/ESPAsyncUDP) + Built by Khoi Hoang https://github.com/khoih-prog/AsyncUDP_ESP32_ENC + Licensed under GPLv3 license + *****************************************************************************************************************************/ + +#if !( defined(ESP32) ) + #error This code is designed for (ESP32 + ENC28J60) to run on ESP32 platform! Please check your Tools->Board setting. +#endif + +#include + +#define ASYNC_UDP_ESP32_ENC_DEBUG_PORT Serial + +// Use from 0 to 4. Higher number, more debugging messages and memory usage. +#define _ASYNC_UDP_ESP32_ENC_LOGLEVEL_ 1 + +////////////////////////////////////////////////////////// + +// Optional values to override default settings +//#define SPI_HOST 1 +//#define SPI_CLOCK_MHZ 8 + +// Must connect INT to GPIOxx or not working +//#define INT_GPIO 4 + +//#define MISO_GPIO 19 +//#define MOSI_GPIO 23 +//#define SCK_GPIO 18 +//#define CS_GPIO 5 + +////////////////////////////////////////////////////////// + +#include + +///////////////////////////////////////////// + +// Enter a MAC address and IP address for your controller below. +#define NUMBER_OF_MAC 20 + +byte mac[][NUMBER_OF_MAC] = +{ + { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0x01 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xBE, 0x02 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0x03 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xBE, 0x04 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0x05 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xBE, 0x06 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0x07 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xBE, 0x08 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0x09 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xBE, 0x0A }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0x0B }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xBE, 0x0C }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0x0D }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xBE, 0x0E }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0x0F }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xBE, 0x10 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0x11 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xBE, 0x12 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0x13 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xBE, 0x14 }, +}; + +// Select the IP address according to your local network +IPAddress myIP(192, 168, 2, 232); +IPAddress myGW(192, 168, 2, 1); +IPAddress mySN(255, 255, 255, 0); + +// Google DNS Server IP +IPAddress myDNS(8, 8, 8, 8); + +///////////////////////////////////////////// + +#include + +// 0.ca.pool.ntp.org +IPAddress timeServerIP = IPAddress(208, 81, 1, 244); +// time.nist.gov +//IPAddress timeServerIP = IPAddress(132, 163, 96, 1); + +#define NTP_REQUEST_PORT 123 + +const int NTP_PACKET_SIZE = 48; // NTP timestamp is in the first 48 bytes of the message + +byte packetBuffer[NTP_PACKET_SIZE]; // buffer to hold incoming and outgoing packets + +// A UDP instance to let us send and receive packets over UDP +AsyncUDP Udp; + +// send an NTP request to the time server at the given address +void createNTPpacket(void) +{ + Serial.println("============= createNTPpacket ============="); + + // set all bytes in the buffer to 0 + memset(packetBuffer, 0, NTP_PACKET_SIZE); + // Initialize values needed to form NTP request + // (see URL above for details on the packets) + + packetBuffer[0] = 0b11100011; // LI, Version, Mode + packetBuffer[1] = 0; // Stratum, or type of clock + packetBuffer[2] = 6; // Polling Interval + packetBuffer[3] = 0xEC; // Peer Clock Precision + + // 8 bytes of zero for Root Delay & Root Dispersion + packetBuffer[12] = 49; + packetBuffer[13] = 0x4E; + packetBuffer[14] = 49; + packetBuffer[15] = 52; +} + +void parsePacket(AsyncUDPPacket packet) +{ + struct tm ts; + char buf[80]; + + memcpy(packetBuffer, packet.data(), sizeof(packetBuffer)); + + Serial.print("Received UDP Packet Type: "); + Serial.println(packet.isBroadcast() ? "Broadcast" : packet.isMulticast() ? "Multicast" : "Unicast"); + Serial.print("From: "); + Serial.print(packet.remoteIP()); + Serial.print(":"); + Serial.print(packet.remotePort()); + Serial.print(", To: "); + Serial.print(packet.localIP()); + Serial.print(":"); + Serial.print(packet.localPort()); + Serial.print(", Length: "); + Serial.print(packet.length()); + Serial.println(); + + unsigned long highWord = word(packetBuffer[40], packetBuffer[41]); + unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]); + + // combine the four bytes (two words) into a long integer + // this is NTP time (seconds since Jan 1 1900): + unsigned long secsSince1900 = highWord << 16 | lowWord; + + Serial.print(F("Seconds since Jan 1 1900 = ")); + Serial.println(secsSince1900); + + // now convert NTP time into )everyday time: + Serial.print(F("Epoch/Unix time = ")); + + // Unix time starts on Jan 1 1970. In seconds, that's 2208988800: + const unsigned long seventyYears = 2208988800UL; + + // subtract seventy years: + unsigned long epoch = secsSince1900 - seventyYears; + time_t epoch_t = epoch; //secsSince1900 - seventyYears; + + // print Unix time: + Serial.println(epoch); + + // print the hour, minute and second: + Serial.print(F("The UTC/GMT time is ")); // UTC is the time at Greenwich Meridian (GMT) + + ts = *localtime(&epoch_t); + strftime(buf, sizeof(buf), "%a %Y-%m-%d %H:%M:%S %Z", &ts); + Serial.println(buf); +} + +void sendNTPPacket(void) +{ + createNTPpacket(); + //Send unicast + Udp.write(packetBuffer, sizeof(packetBuffer)); +} + +void setup() +{ + Serial.begin(115200); + + while (!Serial && (millis() < 5000)); + + Serial.print(F("\nStart AsyncUdpNTPClient on ")); + Serial.print(ARDUINO_BOARD); + Serial.print(F(" with ")); + Serial.println(SHIELD_TYPE); + Serial.println(WEBSERVER_ESP32_ENC_VERSION); + Serial.println(ASYNC_UDP_ESP32_ENC_VERSION); + + Serial.setDebugOutput(true); + + ET_LOGWARN(F("Default SPI pinout:")); + ET_LOGWARN1(F("MOSI:"), MOSI_GPIO); + ET_LOGWARN1(F("MISO:"), MISO_GPIO); + ET_LOGWARN1(F("SCK:"), SCK_GPIO); + ET_LOGWARN1(F("CS:"), CS_GPIO); + ET_LOGWARN1(F("INT:"), INT_GPIO); + ET_LOGWARN1(F("SPI Clock (MHz):"), SPI_CLOCK_MHZ); + ET_LOGWARN(F("=========================")); + + /////////////////////////////////// + + // To be called before ETH.begin() + ESP32_ENC_onEvent(); + + // start the ethernet connection and the server: + // Use DHCP dynamic IP and random mac + uint16_t index = millis() % NUMBER_OF_MAC; + + //bool begin(int MISO_GPIO, int MOSI_GPIO, int SCLK_GPIO, int CS_GPIO, int INT_GPIO, int SPI_CLOCK_MHZ, + // int SPI_HOST, uint8_t *ENC28J60_Mac = ENC28J60_Default_Mac); + //ETH.begin( MISO_GPIO, MOSI_GPIO, SCK_GPIO, CS_GPIO, INT_GPIO, SPI_CLOCK_MHZ, SPI_HOST ); + ETH.begin( MISO_GPIO, MOSI_GPIO, SCK_GPIO, CS_GPIO, INT_GPIO, SPI_CLOCK_MHZ, SPI_HOST, mac[index] ); + + // Static IP, leave without this line to get IP via DHCP + //bool config(IPAddress local_ip, IPAddress gateway, IPAddress subnet, IPAddress dns1 = 0, IPAddress dns2 = 0); + //ETH.config(myIP, myGW, mySN, myDNS); + + ESP32_ENC_waitForConnect(); + + /////////////////////////////////// + + // Client address + Serial.print("AsyncUdpNTPClient started @ IP address: "); + Serial.println(ETH.localIP()); + + //NTP requests are to port NTP_REQUEST_PORT = 123 + if (Udp.connect(timeServerIP, NTP_REQUEST_PORT)) + { + Serial.println("UDP connected"); + + Udp.onPacket([](AsyncUDPPacket packet) + { + parsePacket(packet); + }); + } +} + +void loop() +{ + sendNTPPacket(); + + // wait 60 seconds before asking for the time again + delay(60000); +} diff --git a/examples/AsyncUdpSendReceive/AsyncUdpSendReceive.ino b/examples/AsyncUdpSendReceive/AsyncUdpSendReceive.ino new file mode 100644 index 0000000..5ea63e8 --- /dev/null +++ b/examples/AsyncUdpSendReceive/AsyncUdpSendReceive.ino @@ -0,0 +1,262 @@ +/**************************************************************************************************************************** + AsyncUDPSendReceive.ino + + AsyncUDP_ESP32_ENC is a Async UDP library for the the ESP32 with Ethernet ENC28J60 + + Based on and modified from ESPAsyncUDP Library (https://github.com/me-no-dev/ESPAsyncUDP) + Built by Khoi Hoang https://github.com/khoih-prog/AsyncUDP_ESP32_ENC + Licensed under GPLv3 license + *****************************************************************************************************************************/ + +#if !( defined(ESP32) ) + #error This code is designed for (ESP32 + ENC28J60) to run on ESP32 platform! Please check your Tools->Board setting. +#endif + +#include + +#define ASYNC_UDP_ESP32_ENC_DEBUG_PORT Serial + +// Use from 0 to 4. Higher number, more debugging messages and memory usage. +#define _ASYNC_UDP_ESP32_ENC_LOGLEVEL_ 1 + +////////////////////////////////////////////////////////// + +// Optional values to override default settings +//#define SPI_HOST 1 +//#define SPI_CLOCK_MHZ 8 + +// Must connect INT to GPIOxx or not working +//#define INT_GPIO 4 + +//#define MISO_GPIO 19 +//#define MOSI_GPIO 23 +//#define SCK_GPIO 18 +//#define CS_GPIO 5 + +////////////////////////////////////////////////////////// + +#include + +///////////////////////////////////////////// + +// Enter a MAC address and IP address for your controller below. +#define NUMBER_OF_MAC 20 + +byte mac[][NUMBER_OF_MAC] = +{ + { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0x01 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xBE, 0x02 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0x03 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xBE, 0x04 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0x05 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xBE, 0x06 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0x07 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xBE, 0x08 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0x09 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xBE, 0x0A }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0x0B }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xBE, 0x0C }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0x0D }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xBE, 0x0E }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0x0F }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xBE, 0x10 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0x11 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xBE, 0x12 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0x13 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0xBE, 0x14 }, +}; + +// Select the IP address according to your local network +IPAddress myIP(192, 168, 2, 232); +IPAddress myGW(192, 168, 2, 1); +IPAddress mySN(255, 255, 255, 0); + +// Google DNS Server IP +IPAddress myDNS(8, 8, 8, 8); + +///////////////////////////////////////////// + +#include + +// 0.ca.pool.ntp.org +IPAddress timeServerIP = IPAddress(208, 81, 1, 244); +// time.nist.gov +//IPAddress timeServerIP = IPAddress(132, 163, 96, 1); + +#define NTP_REQUEST_PORT 123 + +char ReplyBuffer[] = "ACK"; // a string to send back + +char timeServer[] = "time.nist.gov"; // NTP server + +const int NTP_PACKET_SIZE = 48; // NTP timestamp is in the first 48 bytes of the message + +byte packetBuffer[NTP_PACKET_SIZE]; // buffer to hold incoming and outgoing packets + +// A UDP instance to let us send and receive packets over UDP +AsyncUDP Udp; + +void sendACKPacket(void) +{ + Serial.println("============= sendACKPacket ============="); + + // Send unicast ACK to the same remoteIP and remotePort we received the packet + // The AsyncUDP_STM32 library will take care of the correct IP and port based on pcb + Udp.write((uint8_t *) ReplyBuffer, sizeof(ReplyBuffer)); +} + +// send an NTP request to the time server at the given address +void createNTPpacket(void) +{ + Serial.println("============= createNTPpacket ============="); + + // set all bytes in the buffer to 0 + memset(packetBuffer, 0, NTP_PACKET_SIZE); + // Initialize values needed to form NTP request + // (see URL above for details on the packets) + + packetBuffer[0] = 0b11100011; // LI, Version, Mode + packetBuffer[1] = 0; // Stratum, or type of clock + packetBuffer[2] = 6; // Polling Interval + packetBuffer[3] = 0xEC; // Peer Clock Precision + + // 8 bytes of zero for Root Delay & Root Dispersion + packetBuffer[12] = 49; + packetBuffer[13] = 0x4E; + packetBuffer[14] = 49; + packetBuffer[15] = 52; +} + +void sendNTPPacket(void) +{ + createNTPpacket(); + //Send unicast + Udp.write(packetBuffer, sizeof(packetBuffer)); +} + +void parsePacket(AsyncUDPPacket packet) +{ + struct tm ts; + char buf[80]; + + memcpy(packetBuffer, packet.data(), sizeof(packetBuffer)); + + Serial.print("Received UDP Packet Type: "); + Serial.println(packet.isBroadcast() ? "Broadcast" : packet.isMulticast() ? "Multicast" : "Unicast"); + Serial.print("From: "); + Serial.print(packet.remoteIP()); + Serial.print(":"); + Serial.print(packet.remotePort()); + Serial.print(", To: "); + Serial.print(packet.localIP()); + Serial.print(":"); + Serial.print(packet.localPort()); + Serial.print(", Length: "); + Serial.print(packet.length()); + Serial.println(); + + unsigned long highWord = word(packetBuffer[40], packetBuffer[41]); + unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]); + + // combine the four bytes (two words) into a long integer + // this is NTP time (seconds since Jan 1 1900): + unsigned long secsSince1900 = highWord << 16 | lowWord; + + Serial.print(F("Seconds since Jan 1 1900 = ")); + Serial.println(secsSince1900); + + // now convert NTP time into )everyday time: + Serial.print(F("Epoch/Unix time = ")); + + // Unix time starts on Jan 1 1970. In seconds, that's 2208988800: + const unsigned long seventyYears = 2208988800UL; + + // subtract seventy years: + unsigned long epoch = secsSince1900 - seventyYears; + time_t epoch_t = epoch; //secsSince1900 - seventyYears; + + // print Unix time: + Serial.println(epoch); + + // print the hour, minute and second: + Serial.print(F("The UTC/GMT time is ")); // UTC is the time at Greenwich Meridian (GMT) + + ts = *localtime(&epoch_t); + strftime(buf, sizeof(buf), "%a %Y-%m-%d %H:%M:%S %Z", &ts); + Serial.println(buf); + + // send a reply, to the IP address and port that sent us the packet we received + sendACKPacket(); +} + +void setup() +{ + Serial.begin(115200); + + while (!Serial && (millis() < 5000)); + + Serial.print(F("\nStart AsyncUDPSendReceive on ")); + Serial.print(ARDUINO_BOARD); + Serial.print(F(" with ")); + Serial.println(SHIELD_TYPE); + Serial.println(WEBSERVER_ESP32_ENC_VERSION); + Serial.println(ASYNC_UDP_ESP32_ENC_VERSION); + + Serial.setDebugOutput(true); + + ET_LOGWARN(F("Default SPI pinout:")); + ET_LOGWARN1(F("MOSI:"), MOSI_GPIO); + ET_LOGWARN1(F("MISO:"), MISO_GPIO); + ET_LOGWARN1(F("SCK:"), SCK_GPIO); + ET_LOGWARN1(F("CS:"), CS_GPIO); + ET_LOGWARN1(F("INT:"), INT_GPIO); + ET_LOGWARN1(F("SPI Clock (MHz):"), SPI_CLOCK_MHZ); + ET_LOGWARN(F("=========================")); + + /////////////////////////////////// + + // To be called before ETH.begin() + ESP32_ENC_onEvent(); + + // start the ethernet connection and the server: + // Use DHCP dynamic IP and random mac + uint16_t index = millis() % NUMBER_OF_MAC; + + //bool begin(int MISO_GPIO, int MOSI_GPIO, int SCLK_GPIO, int CS_GPIO, int INT_GPIO, int SPI_CLOCK_MHZ, + // int SPI_HOST, uint8_t *ENC28J60_Mac = ENC28J60_Default_Mac); + //ETH.begin( MISO_GPIO, MOSI_GPIO, SCK_GPIO, CS_GPIO, INT_GPIO, SPI_CLOCK_MHZ, SPI_HOST ); + ETH.begin( MISO_GPIO, MOSI_GPIO, SCK_GPIO, CS_GPIO, INT_GPIO, SPI_CLOCK_MHZ, SPI_HOST, mac[index] ); + + // Static IP, leave without this line to get IP via DHCP + //bool config(IPAddress local_ip, IPAddress gateway, IPAddress subnet, IPAddress dns1 = 0, IPAddress dns2 = 0); + //ETH.config(myIP, myGW, mySN, myDNS); + + ESP32_ENC_waitForConnect(); + + /////////////////////////////////// + + // Client address + Serial.print("AsyncUDPSendReceive started @ IP address: "); + Serial.println(ETH.localIP()); + + Serial.println(F("\nStarting connection to server...")); + + //NTP requests are to port NTP_REQUEST_PORT = 123 + if (Udp.connect(timeServerIP, NTP_REQUEST_PORT)) + { + Serial.println("UDP connected"); + + Udp.onPacket([](AsyncUDPPacket packet) + { + parsePacket(packet); + }); + } +} + +void loop() +{ + sendNTPPacket(); + + // wait 60 seconds before asking for the time again + delay(60000); +} diff --git a/examples/multiFileProject/multiFileProject.cpp b/examples/multiFileProject/multiFileProject.cpp new file mode 100644 index 0000000..71a7fee --- /dev/null +++ b/examples/multiFileProject/multiFileProject.cpp @@ -0,0 +1,12 @@ +/**************************************************************************************************************************** + multiFileProject.cpp + AsyncUDP_ESP32_ENC is a Async UDP library for the the ESP32 with Ethernet ENC28J60 + + Based on and modified from ESPAsyncUDP Library (https://github.com/me-no-dev/ESPAsyncUDP) + Built by Khoi Hoang https://github.com/khoih-prog/AsyncUDP_ESP32_ENC + Licensed under GPLv3 license +*****************************************************************************************************************************/ + +// To demo how to include files in multi-file Projects + +#include "multiFileProject.h" diff --git a/examples/multiFileProject/multiFileProject.h b/examples/multiFileProject/multiFileProject.h new file mode 100644 index 0000000..cfd83eb --- /dev/null +++ b/examples/multiFileProject/multiFileProject.h @@ -0,0 +1,17 @@ +/**************************************************************************************************************************** + multiFileProject.h + AsyncUDP_ESP32_ENC is a Async UDP library for the the ESP32 with Ethernet ENC28J60 + + Based on and modified from ESPAsyncUDP Library (https://github.com/me-no-dev/ESPAsyncUDP) + Built by Khoi Hoang https://github.com/khoih-prog/AsyncUDP_ESP32_ENC + Licensed under GPLv3 license +*****************************************************************************************************************************/ + +// To demo how to include files in multi-file Projects + +#pragma once + +#define _ASYNC_UDP_ESP32_ENC_LOGLEVEL_ 1 + +// Can be included as many times as necessary, without `Multiple Definitions` Linker Error +#include "AsyncUDP_ESP32_ENC.hpp" diff --git a/examples/multiFileProject/multiFileProject.ino b/examples/multiFileProject/multiFileProject.ino new file mode 100644 index 0000000..c45a419 --- /dev/null +++ b/examples/multiFileProject/multiFileProject.ino @@ -0,0 +1,72 @@ +/**************************************************************************************************************************** + multiFileProject.ino + AsyncUDP_ESP32_ENC is a Async UDP library for the the ESP32 with Ethernet ENC28J60 + + Based on and modified from ESPAsyncUDP Library (https://github.com/me-no-dev/ESPAsyncUDP) + Built by Khoi Hoang https://github.com/khoih-prog/AsyncUDP_ESP32_ENC + Licensed under GPLv3 license +*****************************************************************************************************************************/ + +// To demo how to include files in multi-file Projects + +#include "multiFileProject.h" + +// Can be included as many times as necessary, without `Multiple Definitions` Linker Error +#include "AsyncUDP_ESP32_ENC.h" + +// Select the IP address according to your local network +IPAddress myIP(192, 168, 2, 232); +IPAddress myGW(192, 168, 2, 1); +IPAddress mySN(255, 255, 255, 0); + +// Google DNS Server IP +IPAddress myDNS(8, 8, 8, 8); + +void setup() +{ + Serial.begin(115200); + + while (!Serial && (millis() < 5000)); + + Serial.print(F("\nStart multiFileProject on ")); + Serial.print(ARDUINO_BOARD); + Serial.print(F(" with ")); + Serial.println(SHIELD_TYPE); + Serial.println(WEBSERVER_ESP32_ENC_VERSION); + Serial.println(ASYNC_UDP_ESP32_ENC_VERSION); + + Serial.setDebugOutput(true); + + ET_LOGWARN(F("Default SPI pinout:")); + ET_LOGWARN1(F("MOSI:"), MOSI_GPIO); + ET_LOGWARN1(F("MISO:"), MISO_GPIO); + ET_LOGWARN1(F("SCK:"), SCK_GPIO); + ET_LOGWARN1(F("CS:"), CS_GPIO); + ET_LOGWARN1(F("INT:"), INT_GPIO); + ET_LOGWARN1(F("SPI Clock (MHz):"), SPI_CLOCK_MHZ); + ET_LOGWARN(F("=========================")); + + /////////////////////////////////// + + // To be called before ETH.begin() + ESP32_ENC_onEvent(); + + //bool begin(int MISO_GPIO, int MOSI_GPIO, int SCLK_GPIO, int CS_GPIO, int INT_GPIO, int SPI_CLOCK_MHZ, + // int SPI_HOST, uint8_t *ENC28J60_Mac = ENC28J60_Default_Mac); + ETH.begin( MISO_GPIO, MOSI_GPIO, SCK_GPIO, CS_GPIO, INT_GPIO, SPI_CLOCK_MHZ, SPI_HOST ); + + // Static IP, leave without this line to get IP via DHCP + //bool config(IPAddress local_ip, IPAddress gateway, IPAddress subnet, IPAddress dns1 = 0, IPAddress dns2 = 0); + ETH.config(myIP, myGW, mySN, myDNS); + + ESP32_ENC_waitForConnect(); + + /////////////////////////////////// + + Serial.print("You're OK now"); +} + +void loop() +{ + // put your main code here, to run repeatedly: +} diff --git a/keywords.txt b/keywords.txt new file mode 100644 index 0000000..fcc6ab0 --- /dev/null +++ b/keywords.txt @@ -0,0 +1,70 @@ +####################################### +# Syntax Coloring Map For Ultrasound +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +AsyncUDP KEYWORD1 +AsyncUDPPacket KEYWORD1 +AsyncUDPMessage KEYWORD1 +ip_addr_t KEYWORD1 + +AuPacketHandlerFunction KEYWORD1 +AuPacketHandlerFunctionWithArg KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +################### +# AsyncUDPMessage +################### + +write KEYWORD2 +space KEYWORD2 +data KEYWORD2 +length KEYWORD2 +flush KEYWORD2 + +################### +# AsyncUDPPacket +################### + +data KEYWORD2 +length KEYWORD2 +isBroadcast KEYWORD2 +isMulticast KEYWORD2 +localIP KEYWORD2 +localPort KEYWORD2 +remoteIP KEYWORD2 +remotePort KEYWORD2 +send KEYWORD2 +write KEYWORD2 + +################### +# AsyncUDPPacket +################### + +onPacket KEYWORD2 +listen KEYWORD2 +listenMulticast KEYWORD2 +connect KEYWORD2 +close KEYWORD2 +write KEYWORD2 +broadcastTo KEYWORD2 +broadcast KEYWORD2 +sendTo KEYWORD2 +send KEYWORD2 +connected KEYWORD2 + +################### +# Functions +################### + +ESP32_ENC_Event KEYWORD2 + +####################################### +# Constants (LITERAL1) +####################################### diff --git a/library.json b/library.json new file mode 100644 index 0000000..33ee47a --- /dev/null +++ b/library.json @@ -0,0 +1,46 @@ +{ + "name":"AsyncUdp_ESP32_ENC", + "version": "2.0.0", + "description":"Fully Asynchronous UDP Library for ESP32 boards using LwIP ENC28J60 Ethernet. The library is easy to use and includes support for Unicast, Broadcast and Multicast environments", + "keywords":"communication, data, async, udp, ntp, time, time-server, server, client, multicast, broadcast, webserver, esp32, esp32-s2, esp32-c3, enc28j60, lwip, udp-server, udp-multicast-server", + "authors": + [ + { + "name": "Hristo Gochkov", + "url": "https://github.com/me-no-dev" + }, + { + "name": "Khoi Hoang", + "url": "https://github.com/khoih-prog", + "email": "khoih.prog@gmail.com", + "maintainer": true + } + ], + "repository": + { + "type": "git", + "url": "https://github.com/khoih-prog/AsyncUdp_ESP32_ENC" + }, + "homepage": "https://github.com/khoih-prog/AsyncUdp_ESP32_ENC", + "export": { + "exclude": [ + "linux", + "extras", + "tests" + ] + }, + "dependencies": + [ + { + "owner": "khoih-prog", + "name": "WebServer_ESP32_ENC", + "version": "^1.5.1", + "platforms": "espressif32" + } + ], + "license": "LGPL-3.0", + "frameworks": "*", + "platforms": ["espressif32"], + "examples": "examples/*/*/*.ino", + "headers": [ "AsyncUdp_ESP32_ENC.h", "AsyncUdp_ESP32_ENC.hpp" ] +} diff --git a/library.properties b/library.properties new file mode 100644 index 0000000..82d60ff --- /dev/null +++ b/library.properties @@ -0,0 +1,13 @@ +name=AsyncUdp_ESP32_ENC +version=2.0.0 +author=Hristo Gochkov,Khoi Hoang +maintainer=Khoi Hoang +sentence=Fully Asynchronous UDP Library for ESP32 boards using LwIP ENC28J60 Ethernet +paragraph=The library is easy to use and includes support for Unicast, Broadcast and Multicast environments. +category=Communication,AsyncWebServer,Async,UDP +url=https://github.com/khoih-prog/AsyncUdp_ESP32_ENC +repository=https://github.com/khoih-prog/AsyncUdp_ESP32_ENC +architectures=esp32 +depends=WebServer_ESP32_ENC +license=GPLv3 +includes=AsyncUdp_ESP32_ENC.h, AsyncUdp_ESP32_ENC.hpp diff --git a/platformio/platformio.ini b/platformio/platformio.ini new file mode 100644 index 0000000..e7e32df --- /dev/null +++ b/platformio/platformio.ini @@ -0,0 +1,155 @@ +;PlatformIO Project Configuration File +; +; Build options: build flags, source filter +; Upload options: custom upload port, speed and extra flags +; Library options: dependencies, extra library storages +; Advanced options: extra scripting +; +; Please visit documentation for the other options and examples +; https://docs.platformio.org/page/projectconf.html + +[platformio] +; ============================================================ +; chose environment: +; ESP32 +; esp32s2 +; esp32s3 +; esp32c3 + +; ============================================================ +default_envs = ESP32 + +[env] +; ============================================================ +; Serial configuration +; choose upload speed, serial-monitor speed +; ============================================================ +upload_speed = 921600 +;upload_port = COM11 +;monitor_speed = 9600 +;monitor_port = COM11 + +; Checks for the compatibility with frameworks and dev/platforms +lib_compat_mode = strict +lib_ldf_mode = chain+ +;lib_ldf_mode = deep+ + +lib_deps = +; PlatformIO 4.x +; WebServer_ESP32_ENC@~1.5.1 +; PlatformIO 5.x + khoih-prog/WebServer_ESP32_ENC@~1.5.1 + +build_flags = +; set your debug output (default=Serial) + -D DEBUG_ESP_PORT=Serial +; comment the following line to enable WiFi debugging + -D NDEBUG + +[env:ESP32] +platform = espressif32 +framework = arduino + +; ============================================================ +; Board configuration +; choose your board by uncommenting one of the following lines +; ============================================================ +;board = esp32cam +;board = alksesp32 +;board = featheresp32 +;board = espea32 +;board = bpi-bit +;board = d-duino-32 +board = esp32doit-devkit-v1 +;board = pocket_32 +;board = fm-devkit +;board = pico32 +;board = esp32-evb +;board = esp32-gateway +;board = esp32-pro +;board = esp32-poe +;board = oroca_edubot +;board = onehorse32dev +;board = lopy +;board = lopy4 +;board = wesp32 +;board = esp32thing +;board = sparkfun_lora_gateway_1-channel +;board = ttgo-lora32-v1 +;board = ttgo-t-beam +;board = turta_iot_node +;board = lolin_d32 +;board = lolin_d32_pro +;board = lolin32 +;board = wemosbat +;board = widora-air +;board = xinabox_cw02 +;board = iotbusio +;board = iotbusproteus +;board = nina_w10 + +[env:esp32s2] +platform = espressif32 +framework = arduino + +; toolchain download links see +; refer "name": "xtensa-esp32s2-elf-gcc","version": "gcc8_4_0-esp-2021r1" section of +; https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_dev_index.json +; e.g. Windows: https://github.com/espressif/crosstool-NG/releases/download/esp-2021r1/xtensa-esp32s2-elf-gcc8_4_0-esp-2021r1-win32.zip +platform_packages = + toolchain-xtensa32s2@file://C:\Users\Max\Downloads\xtensa-esp32s2-elf + framework-arduinoespressif32@https://github.com/espressif/arduino-esp32.git#a4118ea88987c28aac3a49bcb9cc5d6c0acc6f3f + platformio/tool-esptoolpy @ ~1.30100 +framework = arduino +board = esp32dev +board_build.mcu = esp32s2 +board_build.partitions = huge_app.csv +board_build.variant = esp32s2 +board_build.f_cpu = 240000000L +board_build.f_flash = 80000000L +board_build.flash_mode = qio +board_build.arduino.ldscript = esp32s2_out.ld +build_unflags = + -DARDUINO_ESP32_DEV + -DARDUINO_VARIANT="esp32" +build_flags = + -DARDUINO_ESP32S2_DEV + -DARDUINO_VARIANT="esp32s2" + + +[env:esp32s3] +platform = espressif32 +framework = arduino + +board_build.mcu = esp32s3 +board_build.partitions = huge_app.csv +board_build.variant = esp32s3 +board_build.f_cpu = 240000000L +board_build.f_flash = 80000000L +board_build.flash_mode = qio +board_build.arduino.ldscript = esp32s3_out.ld +build_unflags = + -DARDUINO_ESP32_DEV + -DARDUINO_VARIANT="esp32" +build_flags = + -DARDUINO_ESP32S3_DEV + -DARDUINO_VARIANT="esp32s3" + + +[env:esp32sc3] +platform = espressif32 +framework = arduino + +board_build.mcu = esp32c3 +board_build.partitions = huge_app.csv +board_build.variant = esp32c3 +board_build.f_cpu = 160000000L +board_build.f_flash = 80000000L +board_build.flash_mode = qio +board_build.arduino.ldscript = esp32c3_out.ld +build_unflags = + -DARDUINO_ESP32_DEV + -DARDUINO_VARIANT="esp32" +build_flags = + -DARDUINO_ESP32S3_DEV + -DARDUINO_VARIANT="esp32c3" diff --git a/src/AsyncUDP_ESP32_ENC.h b/src/AsyncUDP_ESP32_ENC.h new file mode 100644 index 0000000..feb197a --- /dev/null +++ b/src/AsyncUDP_ESP32_ENC.h @@ -0,0 +1,72 @@ +/**************************************************************************************************************************** + AsyncUdp_ESP32_ENC.h + + AsyncUDP_ESP32_ENC is a Async UDP library for the ESP32_ENC (ESP32 + LAN8720) + + Based on and modified from ESPAsyncUDP Library (https://github.com/me-no-dev/ESPAsyncUDP) + Built by Khoi Hoang https://github.com/khoih-prog/AsyncUDP_ESP32_ENC + Licensed under MIT license + + Version: 2.0.0 + + Version Modified By Date Comments + ------- ----------- ---------- ----------- + 2.0.0 K Hoang 28/11/2022 Initial coding for ESP32_ENC. Bump up version to v2.0.0 to sync with AsyncUDP v2.0.0 + *****************************************************************************************************************************/ + +#pragma once + +#ifndef ASYNC_UDP_ESP32_ENC_H +#define ASYNC_UDP_ESP32_ENC_H + +//////////////////////////////////////////////// + +#if ( ( defined(ESP_ARDUINO_VERSION_MAJOR) && (ESP_ARDUINO_VERSION_MAJOR >= 2) ) && ( ARDUINO_ESP32_GIT_VER != 0x46d5afb1 ) ) + +#if (_ASYNC_UDP_ESP32_ENC_LOGLEVEL_ > 2) + #warning Using code for ESP32 core v2.0.0+ in AsyncUdp_ESP32_ENC.h +#endif + +#define ASYNC_UDP_ESP32_ENC_VERSION "AsyncUdp_ESP32_ENC v2.0.0 for core v2.0.0+" + +extern "C" +{ +#include "lwip/ip_addr.h" +#include "freertos/queue.h" +#include "freertos/semphr.h" +} + +#else + +#if (_ASYNC_UDP_ESP32_ENC_LOGLEVEL_ > 2) + #warning Using code for ESP32 core v1.0.6- in AsyncUdp_ESP32_ENC.h +#endif + +#define ASYNC_UDP_ESP32_ENC_VERSION "AsyncUdp_ESP32_ENC v2.0.0 for core v1.0.6-" + +extern "C" +{ +#include "lwip/ip_addr.h" +#include +#include "freertos/queue.h" +#include "freertos/semphr.h" +} +#endif + +#include "IPAddress.h" +#include "IPv6Address.h" +#include "Print.h" +#include + +//////////////////////////////////////////////// + +#include // https://github.com/khoih-prog/WebServer_ESP32_ENC + +#include "AsyncUDP_ESP32_ENC_Debug.h" + +#include "AsyncUDP_ESP32_ENC.hpp" +#include "AsyncUDP_ESP32_ENC_Impl.h" + +//////////////////////////////////////////////// + +#endif //ASYNC_UDP_ESP32_ENC_H diff --git a/src/AsyncUDP_ESP32_ENC.hpp b/src/AsyncUDP_ESP32_ENC.hpp new file mode 100644 index 0000000..d9c5bdd --- /dev/null +++ b/src/AsyncUDP_ESP32_ENC.hpp @@ -0,0 +1,183 @@ +/**************************************************************************************************************************** + AsyncUdp_ESP32_ENC.hpp + + AsyncUDP_ESP32_ENC is a Async UDP library for the ESP32_ENC (ESP32 + LAN8720) + + Based on and modified from ESPAsyncUDP Library (https://github.com/me-no-dev/ESPAsyncUDP) + Built by Khoi Hoang https://github.com/khoih-prog/AsyncUDP_ESP32_ENC + Licensed under MIT license + + Version: 2.0.0 + + Version Modified By Date Comments + ------- ----------- ---------- ----------- + 2.0.0 K Hoang 28/11/2022 Initial coding for ESP32_ENC. Bump up version to v2.0.0 to sync with AsyncUDP v2.0.0 + *****************************************************************************************************************************/ + +#pragma once + +#ifndef ASYNC_UDP_ESP32_ENC_HPP +#define ASYNC_UDP_ESP32_ENC_HPP + +//////////////////////////////////////////////// + +#include // https://github.com/khoih-prog/WebServer_ESP32_ENC + +class AsyncUDP; +class AsyncUDPPacket; +class AsyncUDPMessage; +struct udp_pcb; +struct pbuf; +struct netif; + +typedef std::function AuPacketHandlerFunction; +typedef std::function AuPacketHandlerFunctionWithArg; + +//////////////////////////////////////////////// + +class AsyncUDPMessage : public Print +{ + protected: + uint8_t * _buffer; + size_t _index; + size_t _size; + + public: + AsyncUDPMessage(size_t size = CONFIG_TCP_MSS); + virtual ~AsyncUDPMessage(); + + size_t write(const uint8_t *data, size_t len); + size_t write(uint8_t data); + size_t space(); + uint8_t * data(); + size_t length(); + void flush(); + + operator bool() + { + return _buffer != NULL; + } +}; + +//////////////////////////////////////////////// + +class AsyncUDPPacket : public Stream +{ + protected: + + AsyncUDP * _udp; + pbuf * _pb; + tcpip_adapter_if_t _if; + ip_addr_t _localIp; + uint16_t _localPort; + ip_addr_t _remoteIp; + uint16_t _remotePort; + uint8_t _remoteMac[6]; + uint8_t * _data; + size_t _len; + size_t _index; + + public: + AsyncUDPPacket(AsyncUDPPacket &packet); + AsyncUDPPacket(AsyncUDP *udp, pbuf *pb, const ip_addr_t *addr, uint16_t port, struct netif * netif); + virtual ~AsyncUDPPacket(); + + uint8_t * data(); + size_t length(); + bool isBroadcast(); + bool isMulticast(); + bool isIPv6(); + + tcpip_adapter_if_t interface(); + + IPAddress localIP(); + IPv6Address localIPv6(); + uint16_t localPort(); + IPAddress remoteIP(); + IPv6Address remoteIPv6(); + uint16_t remotePort(); + void remoteMac(uint8_t * mac); + + size_t send(AsyncUDPMessage &message); + + int available(); + size_t read(uint8_t *data, size_t len); + int read(); + int peek(); + void flush(); + + size_t write(const uint8_t *data, size_t len); + size_t write(uint8_t data); +}; + +//////////////////////////////////////////////// + +class AsyncUDP : public Print +{ + protected: + + udp_pcb *_pcb; + //xSemaphoreHandle _lock; + bool _connected; + esp_err_t _lastErr; + AuPacketHandlerFunction _handler; + + bool _init(); + void _recv(udp_pcb *upcb, pbuf *pb, const ip_addr_t *addr, uint16_t port, struct netif * netif); + + public: + + AsyncUDP(); + virtual ~AsyncUDP(); + + void onPacket(AuPacketHandlerFunctionWithArg cb, void * arg = NULL); + void onPacket(AuPacketHandlerFunction cb); + + bool listen(const ip_addr_t *addr, uint16_t port); + bool listen(const IPAddress addr, uint16_t port); + bool listen(const IPv6Address addr, uint16_t port); + bool listen(uint16_t port); + + bool listenMulticast(const ip_addr_t *addr, uint16_t port, uint8_t ttl = 1, tcpip_adapter_if_t tcpip_if = TCPIP_ADAPTER_IF_MAX); + bool listenMulticast(const IPAddress addr, uint16_t port, uint8_t ttl = 1, tcpip_adapter_if_t tcpip_if = TCPIP_ADAPTER_IF_MAX); + bool listenMulticast(const IPv6Address addr, uint16_t port, uint8_t ttl = 1, tcpip_adapter_if_t tcpip_if = TCPIP_ADAPTER_IF_MAX); + + bool connect(const ip_addr_t *addr, uint16_t port); + bool connect(const IPAddress addr, uint16_t port); + bool connect(const IPv6Address addr, uint16_t port); + + void close(); + + size_t writeTo(const uint8_t *data, size_t len, const ip_addr_t *addr, uint16_t port, tcpip_adapter_if_t tcpip_if = TCPIP_ADAPTER_IF_MAX); + size_t writeTo(const uint8_t *data, size_t len, const IPAddress addr, uint16_t port, tcpip_adapter_if_t tcpip_if = TCPIP_ADAPTER_IF_MAX); + size_t writeTo(const uint8_t *data, size_t len, const IPv6Address addr, uint16_t port, tcpip_adapter_if_t tcpip_if = TCPIP_ADAPTER_IF_MAX); + size_t write(const uint8_t *data, size_t len); + size_t write(uint8_t data); + + size_t broadcastTo(uint8_t *data, size_t len, uint16_t port, tcpip_adapter_if_t tcpip_if = TCPIP_ADAPTER_IF_MAX); + size_t broadcastTo(const char * data, uint16_t port, tcpip_adapter_if_t tcpip_if = TCPIP_ADAPTER_IF_MAX); + size_t broadcast(uint8_t *data, size_t len); + size_t broadcast(const char * data); + + size_t sendTo(AsyncUDPMessage &message, const ip_addr_t *addr, uint16_t port, tcpip_adapter_if_t tcpip_if = TCPIP_ADAPTER_IF_MAX); + size_t sendTo(AsyncUDPMessage &message, const IPAddress addr, uint16_t port, tcpip_adapter_if_t tcpip_if = TCPIP_ADAPTER_IF_MAX); + size_t sendTo(AsyncUDPMessage &message, const IPv6Address addr, uint16_t port, tcpip_adapter_if_t tcpip_if = TCPIP_ADAPTER_IF_MAX); + size_t send(AsyncUDPMessage &message); + + size_t broadcastTo(AsyncUDPMessage &message, uint16_t port, tcpip_adapter_if_t tcpip_if = TCPIP_ADAPTER_IF_MAX); + size_t broadcast(AsyncUDPMessage &message); + + IPAddress listenIP(); + IPv6Address listenIPv6(); + + bool connected(); + esp_err_t lastErr(); + operator bool(); + + static void _s_recv(void *arg, udp_pcb *upcb, pbuf *p, const ip_addr_t *addr, uint16_t port, struct netif * netif); +}; + +//////////////////////////////////////////////// + + +#endif //ASYNC_UDP_ESP32_ENC_HPP diff --git a/src/AsyncUDP_ESP32_ENC_Debug.h b/src/AsyncUDP_ESP32_ENC_Debug.h new file mode 100644 index 0000000..0b2c74c --- /dev/null +++ b/src/AsyncUDP_ESP32_ENC_Debug.h @@ -0,0 +1,93 @@ +/**************************************************************************************************************************** + AsyncUDP_Debug_ESP32_ENC.h + + AsyncUDP_ESP32_ENC is a Async UDP library for the ESP32_ENC (ESP32 + LAN8720) + + Based on and modified from ESPAsyncUDP Library (https://github.com/me-no-dev/ESPAsyncUDP) + Built by Khoi Hoang https://github.com/khoih-prog/AsyncUDP_ESP32_ENC + Licensed under MIT license + + Version: 2.0.0 + + Version Modified By Date Comments + ------- ----------- ---------- ----------- + 2.0.0 K Hoang 28/11/2022 Initial coding for ESP32_ENC. Bump up version to v2.0.0 to sync with AsyncUDP v2.0.0 + *****************************************************************************************************************************/ + +#pragma once + +#ifndef Async_UDP_ESP32_ENC_Debug_H +#define Async_UDP_ESP32_ENC_Debug_H + +#ifdef ASYNC_UDP_ESP32_ENC_DEBUG_PORT + #define ASYNC_UDP_DBG_PORT ASYNC_UDP_ESP32_ENC_DEBUG_PORT +#else + #define ASYNC_UDP_DBG_PORT Serial +#endif + +// Change _ASYNC_UDP_ESP32_ENC_LOGLEVEL_ to set tracing and logging verbosity +// 0: DISABLED: no logging +// 1: ERROR: errors +// 2: WARN: errors and warnings +// 3: INFO: errors, warnings and informational (default) +// 4: DEBUG: errors, warnings, informational and debug + +#ifndef _ASYNC_UDP_ESP32_ENC_LOGLEVEL_ + #define _ASYNC_UDP_ESP32_ENC_LOGLEVEL_ 0 +#endif + +/////////////////////////////////////// + +const char ASYNC_UDP_MARK[] = "[UDP] "; +const char ASYNC_UDP_SP[] = " "; + +#define ASYNC_UDP_PRINT ASYNC_UDP_DBG_PORT.print +#define ASYNC_UDP_PRINTLN ASYNC_UDP_DBG_PORT.println +#define ASYNC_UDP_FLUSH ASYNC_UDP_DBG_PORT.flush + +#define ASYNC_UDP_PRINT_MARK ASYNC_UDP_PRINT(ASYNC_UDP_MARK) +#define ASYNC_UDP_PRINT_SP ASYNC_UDP_PRINT(ASYNC_UDP_SP) + +/////////////////////////////////////// + +#define ASYNC_UDP_LOG(x) { ASYNC_UDP_PRINTLN(x); } +#define ASYNC_UDP_LOG0(x) { ASYNC_UDP_PRINT(x); } +#define ASYNC_UDP_LOG1(x,y) { ASYNC_UDP_PRINT(x); ASYNC_UDP_PRINTLN(y); } +#define ASYNC_UDP_LOG2(x,y,z) { ASYNC_UDP_PRINT(x); ASYNC_UDP_PRINT(y); ASYNC_UDP_PRINTLN(z); } +#define ASYNC_UDP_LOG3(x,y,z,w) { ASYNC_UDP_PRINT(x); ASYNC_UDP_PRINT(y); ASYNC_UDP_PRINT(z); ASYNC_UDP_PRINTLN(w); } + +/////////////////////////////////////// + +#define UDP_LOGERROR(x) if(_ASYNC_UDP_ESP32_ENC_LOGLEVEL_>0) { ASYNC_UDP_PRINT_MARK; ASYNC_UDP_PRINTLN(x); } +#define UDP_LOGERROR0(x) if(_ASYNC_UDP_ESP32_ENC_LOGLEVEL_>0) { ASYNC_UDP_PRINT(x); } +#define UDP_LOGERROR1(x,y) if(_ASYNC_UDP_ESP32_ENC_LOGLEVEL_>0) { ASYNC_UDP_PRINT_MARK; ASYNC_UDP_PRINT(x); ASYNC_UDP_PRINT_SP; ASYNC_UDP_PRINTLN(y); } +#define UDP_LOGERROR2(x,y,z) if(_ASYNC_UDP_ESP32_ENC_LOGLEVEL_>0) { ASYNC_UDP_PRINT_MARK; ASYNC_UDP_PRINT(x); ASYNC_UDP_PRINT_SP; ASYNC_UDP_PRINT(y); ASYNC_UDP_PRINT_SP; ASYNC_UDP_PRINTLN(z); } +#define UDP_LOGERROR3(x,y,z,w) if(_ASYNC_UDP_ESP32_ENC_LOGLEVEL_>0) { ASYNC_UDP_PRINT_MARK; ASYNC_UDP_PRINT(x); ASYNC_UDP_PRINT_SP; ASYNC_UDP_PRINT(y); ASYNC_UDP_PRINT_SP; ASYNC_UDP_PRINT(z); ASYNC_UDP_PRINT_SP; ASYNC_UDP_PRINTLN(w); } + +/////////////////////////////////////// + +#define UDP_LOGWARN(x) if(_ASYNC_UDP_ESP32_ENC_LOGLEVEL_>1) { ASYNC_UDP_PRINT_MARK; ASYNC_UDP_PRINTLN(x); } +#define UDP_LOGWARN0(x) if(_ASYNC_UDP_ESP32_ENC_LOGLEVEL_>1) { ASYNC_UDP_PRINT(x); } +#define UDP_LOGWARN1(x,y) if(_ASYNC_UDP_ESP32_ENC_LOGLEVEL_>1) { ASYNC_UDP_PRINT_MARK; ASYNC_UDP_PRINT(x); ASYNC_UDP_PRINT_SP; ASYNC_UDP_PRINTLN(y); } +#define UDP_LOGWARN2(x,y,z) if(_ASYNC_UDP_ESP32_ENC_LOGLEVEL_>1) { ASYNC_UDP_PRINT_MARK; ASYNC_UDP_PRINT(x); ASYNC_UDP_PRINT_SP; ASYNC_UDP_PRINT(y); ASYNC_UDP_PRINT_SP; ASYNC_UDP_PRINTLN(z); } +#define UDP_LOGWARN3(x,y,z,w) if(_ASYNC_UDP_ESP32_ENC_LOGLEVEL_>1) { ASYNC_UDP_PRINT_MARK; ASYNC_UDP_PRINT(x); ASYNC_UDP_PRINT_SP; ASYNC_UDP_PRINT(y); ASYNC_UDP_PRINT_SP; ASYNC_UDP_PRINT(z); ASYNC_UDP_PRINT_SP; ASYNC_UDP_PRINTLN(w); } + +/////////////////////////////////////// + +#define UDP_LOGINFO(x) if(_ASYNC_UDP_ESP32_ENC_LOGLEVEL_>2) { ASYNC_UDP_PRINT_MARK; ASYNC_UDP_PRINTLN(x); } +#define UDP_LOGINFO0(x) if(_ASYNC_UDP_ESP32_ENC_LOGLEVEL_>2) { ASYNC_UDP_PRINT(x); } +#define UDP_LOGINFO1(x,y) if(_ASYNC_UDP_ESP32_ENC_LOGLEVEL_>2) { ASYNC_UDP_PRINT_MARK; ASYNC_UDP_PRINT(x); ASYNC_UDP_PRINT_SP; ASYNC_UDP_PRINTLN(y); } +#define UDP_LOGINFO2(x,y,z) if(_ASYNC_UDP_ESP32_ENC_LOGLEVEL_>2) { ASYNC_UDP_PRINT_MARK; ASYNC_UDP_PRINT(x); ASYNC_UDP_PRINT_SP; ASYNC_UDP_PRINT(y); ASYNC_UDP_PRINT_SP; ASYNC_UDP_PRINTLN(z); } +#define UDP_LOGINFO3(x,y,z,w) if(_ASYNC_UDP_ESP32_ENC_LOGLEVEL_>2) { ASYNC_UDP_PRINT_MARK; ASYNC_UDP_PRINT(x); ASYNC_UDP_PRINT_SP; ASYNC_UDP_PRINT(y); ASYNC_UDP_PRINT_SP; ASYNC_UDP_PRINT(z); ASYNC_UDP_PRINT_SP; ASYNC_UDP_PRINTLN(w); } + +/////////////////////////////////////// + +#define UDP_LOGDEBUG(x) if(_ASYNC_UDP_ESP32_ENC_LOGLEVEL_>3) { ASYNC_UDP_PRINT_MARK; ASYNC_UDP_PRINTLN(x); } +#define UDP_LOGDEBUG0(x) if(_ASYNC_UDP_ESP32_ENC_LOGLEVEL_>3) { ASYNC_UDP_PRINT(x); } +#define UDP_LOGDEBUG1(x,y) if(_ASYNC_UDP_ESP32_ENC_LOGLEVEL_>3) { ASYNC_UDP_PRINT_MARK; ASYNC_UDP_PRINT(x); ASYNC_UDP_PRINT_SP; ASYNC_UDP_PRINTLN(y); } +#define UDP_LOGDEBUG2(x,y,z) if(_ASYNC_UDP_ESP32_ENC_LOGLEVEL_>3) { ASYNC_UDP_PRINT_MARK; ASYNC_UDP_PRINT(x); ASYNC_UDP_PRINT_SP; ASYNC_UDP_PRINT(y); ASYNC_UDP_PRINT_SP; ASYNC_UDP_PRINTLN(z); } +#define UDP_LOGDEBUG3(x,y,z,w) if(_ASYNC_UDP_ESP32_ENC_LOGLEVEL_>3) { ASYNC_UDP_PRINT_MARK; ASYNC_UDP_PRINT(x); ASYNC_UDP_PRINT_SP; ASYNC_UDP_PRINT(y); ASYNC_UDP_PRINT_SP; ASYNC_UDP_PRINT(z); ASYNC_UDP_PRINT_SP; ASYNC_UDP_PRINTLN(w); } + +/////////////////////////////////////// + +#endif //Async_UDP_ESP32_ENC_Debug_H diff --git a/src/AsyncUDP_ESP32_ENC_Impl.h b/src/AsyncUDP_ESP32_ENC_Impl.h new file mode 100644 index 0000000..a1b5e93 --- /dev/null +++ b/src/AsyncUDP_ESP32_ENC_Impl.h @@ -0,0 +1,1399 @@ +/**************************************************************************************************************************** + AsyncUdp_ESP32_ENC_Impl.h + + AsyncUDP_ESP32_ENC is a Async UDP library for the ESP32_ENC (ESP32 + LAN8720) + + Based on and modified from ESPAsyncUDP Library (https://github.com/me-no-dev/ESPAsyncUDP) + Built by Khoi Hoang https://github.com/khoih-prog/AsyncUDP_ESP32_ENC + Licensed under MIT license + + Version: 2.0.0 + + Version Modified By Date Comments + ------- ----------- ---------- ----------- + 2.0.0 K Hoang 28/11/2022 Initial coding for ESP32_ENC. Bump up version to v2.0.0 to sync with AsyncUDP v2.0.0 + *****************************************************************************************************************************/ + +#pragma once + +#ifndef ASYNC_UDP_ESP32_ENC_IMPL_H +#define ASYNC_UDP_ESP32_ENC_IMPL_H + +extern "C" +{ +#include "lwip/opt.h" +#include "lwip/inet.h" +#include "lwip/udp.h" +#include "lwip/igmp.h" +#include "lwip/ip_addr.h" +#include "lwip/mld6.h" +#include "lwip/prot/ethernet.h" +#include +#include +} + +#include "lwip/priv/tcpip_priv.h" + +/* + typedef int32_t esp_err_t; + + // + #define ESP_OK 0 + #define ESP_FAIL -1 + + #define ESP_ERR_NO_MEM 0x101 // Out of memory + #define ESP_ERR_INVALID_ARG 0x102 // Invalid argument + #define ESP_ERR_INVALID_STATE 0x103 // Invalid state + #define ESP_ERR_INVALID_SIZE 0x104 // Invalid size + #define ESP_ERR_NOT_FOUND 0x105 // Requested resource not found + #define ESP_ERR_NOT_SUPPORTED 0x106 // Operation or feature not supported + #define ESP_ERR_TIMEOUT 0x107 // Operation timed out + #define ESP_ERR_INVALID_RESPONSE 0x108 // Received response was invalid + #define ESP_ERR_INVALID_CRC 0x109 // CRC or checksum was invalid + #define ESP_ERR_INVALID_VERSION 0x10A // Version was invalid + #define ESP_ERR_INVALID_MAC 0x10B // MAC address was invalid + + #define ESP_ERR_WIFI_BASE 0x3000 // Starting number of WiFi error codes + #define ESP_ERR_MESH_BASE 0x4000 // Starting number of MESH error codes +*/ + +typedef struct +{ + struct tcpip_api_call_data call; + udp_pcb * pcb; + const ip_addr_t *addr; + uint16_t port; + struct pbuf *pb; + struct netif *netif; + err_t err; +} udp_api_call_t; + +static err_t _udp_connect_api(struct tcpip_api_call_data *api_call_msg) +{ + udp_api_call_t * msg = (udp_api_call_t *)api_call_msg; + msg->err = udp_connect(msg->pcb, msg->addr, msg->port); + + UDP_LOGDEBUG1(F("_udp_connect_api: Error ="), msg->err); + + return msg->err; +} + +static err_t _udp_connect(struct udp_pcb *pcb, const ip_addr_t *addr, u16_t port) +{ + udp_api_call_t msg; + msg.pcb = pcb; + msg.addr = addr; + msg.port = port; + tcpip_api_call(_udp_connect_api, (struct tcpip_api_call_data*)&msg); + + UDP_LOGDEBUG1(F("_udp_connect: Error ="), msg.err); + + return msg.err; +} + +static err_t _udp_disconnect_api(struct tcpip_api_call_data *api_call_msg) +{ + udp_api_call_t * msg = (udp_api_call_t *)api_call_msg; + msg->err = 0; + udp_disconnect(msg->pcb); + + UDP_LOGDEBUG1(F("_udp_disconnect_api: Error ="), msg->err); + + return msg->err; +} + +static void _udp_disconnect(struct udp_pcb *pcb) +{ + udp_api_call_t msg; + msg.pcb = pcb; + tcpip_api_call(_udp_disconnect_api, (struct tcpip_api_call_data*)&msg); +} + +static err_t _udp_remove_api(struct tcpip_api_call_data *api_call_msg) +{ + udp_api_call_t * msg = (udp_api_call_t *)api_call_msg; + msg->err = 0; + udp_remove(msg->pcb); + + UDP_LOGDEBUG1(F("_udp_remove_api: Error ="), msg->err); + + return msg->err; +} + +static void _udp_remove(struct udp_pcb *pcb) +{ + udp_api_call_t msg; + msg.pcb = pcb; + tcpip_api_call(_udp_remove_api, (struct tcpip_api_call_data*)&msg); +} + +static err_t _udp_bind_api(struct tcpip_api_call_data *api_call_msg) +{ + udp_api_call_t * msg = (udp_api_call_t *)api_call_msg; + msg->err = udp_bind(msg->pcb, msg->addr, msg->port); + + UDP_LOGDEBUG1(F("_udp_bind_api: Error ="), msg->err); + + return msg->err; +} + +static err_t _udp_bind(struct udp_pcb *pcb, const ip_addr_t *addr, u16_t port) +{ + udp_api_call_t msg; + msg.pcb = pcb; + msg.addr = addr; + msg.port = port; + tcpip_api_call(_udp_bind_api, (struct tcpip_api_call_data*)&msg); + + UDP_LOGDEBUG1(F("_udp_bind: Error ="), msg.err); + + return msg.err; +} + +static err_t _udp_sendto_api(struct tcpip_api_call_data *api_call_msg) +{ + udp_api_call_t * msg = (udp_api_call_t *)api_call_msg; + msg->err = udp_sendto(msg->pcb, msg->pb, msg->addr, msg->port); + + UDP_LOGDEBUG1(F("_udp_sendto_api: Error ="), msg->err); + + return msg->err; +} + +static err_t _udp_sendto(struct udp_pcb *pcb, struct pbuf *pb, const ip_addr_t *addr, u16_t port) +{ + udp_api_call_t msg; + msg.pcb = pcb; + msg.addr = addr; + msg.port = port; + msg.pb = pb; + tcpip_api_call(_udp_sendto_api, (struct tcpip_api_call_data*)&msg); + + UDP_LOGDEBUG1(F("_udp_sendto: Error ="), msg.err); + + return msg.err; +} + +static err_t _udp_sendto_if_api(struct tcpip_api_call_data *api_call_msg) +{ + udp_api_call_t * msg = (udp_api_call_t *)api_call_msg; + msg->err = udp_sendto_if(msg->pcb, msg->pb, msg->addr, msg->port, msg->netif); + + UDP_LOGDEBUG1(F("_udp_sendto_if_api: Error ="), msg->err); + + return msg->err; +} + +static err_t _udp_sendto_if(struct udp_pcb *pcb, struct pbuf *pb, const ip_addr_t *addr, u16_t port, + struct netif *netif) +{ + udp_api_call_t msg; + msg.pcb = pcb; + msg.addr = addr; + msg.port = port; + msg.pb = pb; + msg.netif = netif; + tcpip_api_call(_udp_sendto_if_api, (struct tcpip_api_call_data*)&msg); + + UDP_LOGDEBUG1(F("_udp_sendto_if: Error ="), msg.err); + + return msg.err; +} + +//////////////////////////////////////////////// + +typedef struct +{ + void *arg; + udp_pcb *pcb; + pbuf *pb; + const ip_addr_t *addr; + uint16_t port; + struct netif * netif; +} lwip_event_packet_t; + +static xQueueHandle _udp_queue; +static volatile TaskHandle_t _udp_task_handle = NULL; + +static void _udp_task(void *pvParameters) +{ + lwip_event_packet_t * e = NULL; + + for (;;) + { + if (xQueueReceive(_udp_queue, &e, portMAX_DELAY) == pdTRUE) + { + if (!e->pb) + { + free((void*)(e)); + continue; + } + + AsyncUDP::_s_recv(e->arg, e->pcb, e->pb, e->addr, e->port, e->netif); + free((void*)(e)); + } + } + + _udp_task_handle = NULL; + vTaskDelete(NULL); +} + +static bool _udp_task_start() +{ + if (!_udp_queue) + { + _udp_queue = xQueueCreate(32, sizeof(lwip_event_packet_t *)); + + if (!_udp_queue) + { + return false; + } + } + + if (!_udp_task_handle) + { + xTaskCreateUniversal(_udp_task, "async_udp", 4096, NULL, CONFIG_ARDUINO_UDP_TASK_PRIORITY, + (TaskHandle_t*)&_udp_task_handle, CONFIG_ARDUINO_UDP_RUNNING_CORE); + + if (!_udp_task_handle) + { + return false; + } + } + + return true; +} + +static bool _udp_task_post(void *arg, udp_pcb *pcb, pbuf *pb, const ip_addr_t *addr, uint16_t port, struct netif *netif) +{ + if (!_udp_task_handle || !_udp_queue) + { + return false; + } + + lwip_event_packet_t * e = (lwip_event_packet_t *)malloc(sizeof(lwip_event_packet_t)); + + if (!e) + { + return false; + } + + e->arg = arg; + e->pcb = pcb; + e->pb = pb; + e->addr = addr; + e->port = port; + e->netif = netif; + + if (xQueueSend(_udp_queue, &e, portMAX_DELAY) != pdPASS) + { + free((void*)(e)); + + return false; + } + + return true; +} + +static void _udp_recv(void *arg, udp_pcb *pcb, pbuf *pb, const ip_addr_t *addr, uint16_t port) +{ + while (pb != NULL) + { + pbuf * this_pb = pb; + pb = pb->next; + this_pb->next = NULL; + + if (!_udp_task_post(arg, pcb, this_pb, addr, port, ip_current_input_netif())) + { + pbuf_free(this_pb); + } + } +} + +/* + static bool _udp_task_stop() + { + if(!_udp_task_post(NULL, NULL, NULL, NULL, 0, NULL)) + { + return false; + } + + while(_udp_task_handle) + { + vTaskDelay(10); + } + + lwip_event_packet_t * e; + + while (xQueueReceive(_udp_queue, &e, 0) == pdTRUE) + { + if(e->pb) + { + pbuf_free(e->pb); + } + + free((void*)(e)); + } + + vQueueDelete(_udp_queue); + _udp_queue = NULL; + } +*/ + +//////////////////////////////////////////////// + +#define UDP_MUTEX_LOCK() //xSemaphoreTake(_lock, portMAX_DELAY) +#define UDP_MUTEX_UNLOCK() //xSemaphoreGive(_lock) + +//////////////////////////////////////////////// + +AsyncUDPMessage::AsyncUDPMessage(size_t size) +{ + _index = 0; + + if (size > CONFIG_TCP_MSS) + { + size = CONFIG_TCP_MSS; + } + + _size = size; + _buffer = (uint8_t *)malloc(size); +} + +//////////////////////////////////////////////// + +AsyncUDPMessage::~AsyncUDPMessage() +{ + if (_buffer) + { + free(_buffer); + } +} + +//////////////////////////////////////////////// + +size_t AsyncUDPMessage::write(const uint8_t *data, size_t len) +{ + if (_buffer == NULL) + { + UDP_LOGDEBUG(F("write: Error NULL _buffer")); + + return 0; + } + + size_t s = space(); + + if (len > s) + { + len = s; + } + + memcpy(_buffer + _index, data, len); + _index += len; + + return len; +} + +//////////////////////////////////////////////// + +size_t AsyncUDPMessage::write(uint8_t data) +{ + return write(&data, 1); +} + +//////////////////////////////////////////////// + +size_t AsyncUDPMessage::space() +{ + if (_buffer == NULL) + { + UDP_LOGDEBUG(F("space: Error NULL _buffer")); + + return 0; + } + + return _size - _index; +} + +//////////////////////////////////////////////// + +uint8_t * AsyncUDPMessage::data() +{ + return _buffer; +} + +//////////////////////////////////////////////// + +size_t AsyncUDPMessage::length() +{ + return _index; +} + +//////////////////////////////////////////////// + +void AsyncUDPMessage::flush() +{ + _index = 0; +} + +//////////////////////////////////////////////// + +AsyncUDPPacket::AsyncUDPPacket(AsyncUDPPacket &packet) +{ + _udp = packet._udp; + _pb = packet._pb; + _if = packet._if; + _data = packet._data; + _len = packet._len; + _index = 0; + + memcpy(&_remoteIp, &packet._remoteIp, sizeof(ip_addr_t)); + memcpy(&_localIp, &packet._localIp, sizeof(ip_addr_t)); + _localPort = packet._localPort; + _remotePort = packet._remotePort; + memcpy(_remoteMac, packet._remoteMac, 6); + + pbuf_ref(_pb); +} + +//////////////////////////////////////////////// + +AsyncUDPPacket::AsyncUDPPacket(AsyncUDP *udp, pbuf *pb, const ip_addr_t *raddr, uint16_t rport, struct netif * ntif) +{ + _udp = udp; + _pb = pb; + _if = TCPIP_ADAPTER_IF_MAX; + _data = (uint8_t*)(pb->payload); + _len = pb->len; + _index = 0; + + pbuf_ref(_pb); + + //memcpy(&_remoteIp, raddr, sizeof(ip_addr_t)); + _remoteIp.type = raddr->type; + _localIp.type = _remoteIp.type; + + eth_hdr* eth = NULL; + udp_hdr* udphdr = (udp_hdr *)(_data - UDP_HLEN); + _localPort = ntohs(udphdr->dest); + _remotePort = ntohs(udphdr->src); + + if (_remoteIp.type == IPADDR_TYPE_V4) + { + eth = (eth_hdr *)(_data - UDP_HLEN - IP_HLEN - SIZEOF_ETH_HDR); + struct ip_hdr * iphdr = (struct ip_hdr *)(_data - UDP_HLEN - IP_HLEN); + _localIp.u_addr.ip4.addr = iphdr->dest.addr; + _remoteIp.u_addr.ip4.addr = iphdr->src.addr; + } + else + { + eth = (eth_hdr *)(_data - UDP_HLEN - IP6_HLEN - SIZEOF_ETH_HDR); + struct ip6_hdr * ip6hdr = (struct ip6_hdr *)(_data - UDP_HLEN - IP6_HLEN); + memcpy(&_localIp.u_addr.ip6.addr, (uint8_t *)ip6hdr->dest.addr, 16); + memcpy(&_remoteIp.u_addr.ip6.addr, (uint8_t *)ip6hdr->src.addr, 16); + } + + memcpy(_remoteMac, eth->src.addr, 6); + + struct netif * netif = NULL; + void * nif = NULL; + int i; + + for (i = 0; i < TCPIP_ADAPTER_IF_MAX; i++) + { + tcpip_adapter_get_netif ((tcpip_adapter_if_t)i, &nif); + netif = (struct netif *)nif; + + if (netif && netif == ntif) + { + _if = (tcpip_adapter_if_t)i; + break; + } + } +} + +//////////////////////////////////////////////// + +AsyncUDPPacket::~AsyncUDPPacket() +{ + pbuf_free(_pb); +} + +//////////////////////////////////////////////// + +uint8_t * AsyncUDPPacket::data() +{ + return _data; +} + +//////////////////////////////////////////////// + +size_t AsyncUDPPacket::length() +{ + return _len; +} + +//////////////////////////////////////////////// + +int AsyncUDPPacket::available() +{ + return _len - _index; +} + +//////////////////////////////////////////////// + +size_t AsyncUDPPacket::read(uint8_t *data, size_t len) +{ + size_t i; + size_t a = _len - _index; + + if (len > a) + { + len = a; + } + + for (i = 0; i < len; i++) + { + data[i] = read(); + } + + return len; +} + +//////////////////////////////////////////////// + +int AsyncUDPPacket::read() +{ + if (_index < _len) + { + return _data[_index++]; + } + + return -1; +} + +//////////////////////////////////////////////// + +int AsyncUDPPacket::peek() +{ + if (_index < _len) + { + return _data[_index]; + } + + return -1; +} + +//////////////////////////////////////////////// + +void AsyncUDPPacket::flush() +{ + _index = _len; +} + +//////////////////////////////////////////////// + +tcpip_adapter_if_t AsyncUDPPacket::interface() +{ + return _if; +} + +//////////////////////////////////////////////// + +IPAddress AsyncUDPPacket::localIP() +{ + if (_localIp.type != IPADDR_TYPE_V4) + { + return IPAddress(); + } + + return IPAddress(_localIp.u_addr.ip4.addr); +} + +//////////////////////////////////////////////// + +IPv6Address AsyncUDPPacket::localIPv6() +{ + if (_localIp.type != IPADDR_TYPE_V6) + { + return IPv6Address(); + } + + return IPv6Address(_localIp.u_addr.ip6.addr); +} + +//////////////////////////////////////////////// + +uint16_t AsyncUDPPacket::localPort() +{ + return _localPort; +} + +//////////////////////////////////////////////// + +IPAddress AsyncUDPPacket::remoteIP() +{ + if (_remoteIp.type != IPADDR_TYPE_V4) + { + return IPAddress(); + } + + return IPAddress(_remoteIp.u_addr.ip4.addr); +} + +//////////////////////////////////////////////// + +IPv6Address AsyncUDPPacket::remoteIPv6() +{ + if (_remoteIp.type != IPADDR_TYPE_V6) + { + return IPv6Address(); + } + + return IPv6Address(_remoteIp.u_addr.ip6.addr); +} + +//////////////////////////////////////////////// + +uint16_t AsyncUDPPacket::remotePort() +{ + return _remotePort; +} + +//////////////////////////////////////////////// + +void AsyncUDPPacket::remoteMac(uint8_t * mac) +{ + memcpy(mac, _remoteMac, 6); +} + +//////////////////////////////////////////////// + +bool AsyncUDPPacket::isIPv6() +{ + return _localIp.type == IPADDR_TYPE_V6; +} + +//////////////////////////////////////////////// + +bool AsyncUDPPacket::isBroadcast() +{ + if (_localIp.type == IPADDR_TYPE_V6) + { + return false; + } + + uint32_t ip = _localIp.u_addr.ip4.addr; + + return ip == 0xFFFFFFFF || ip == 0 || (ip & 0xFF000000) == 0xFF000000; +} + +//////////////////////////////////////////////// + +bool AsyncUDPPacket::isMulticast() +{ + return ip_addr_ismulticast(&(_localIp)); +} + +//////////////////////////////////////////////// + +size_t AsyncUDPPacket::write(const uint8_t *data, size_t len) +{ + if (!data) + { + UDP_LOGDEBUG(F("AsyncUDPPacket::write: Error NULL data")); + + return 0; + } + + return _udp->writeTo(data, len, &_remoteIp, _remotePort, _if); +} + +//////////////////////////////////////////////// + +size_t AsyncUDPPacket::write(uint8_t data) +{ + return write(&data, 1); +} + +//////////////////////////////////////////////// + +size_t AsyncUDPPacket::send(AsyncUDPMessage &message) +{ + return write(message.data(), message.length()); +} + +//////////////////////////////////////////////// + +bool AsyncUDP::_init() +{ + if (_pcb) + { + return true; + } + + _pcb = udp_new(); + + if (!_pcb) + { + return false; + } + + //_lock = xSemaphoreCreateMutex(); + udp_recv(_pcb, &_udp_recv, (void *) this); + + return true; +} + +//////////////////////////////////////////////// + +AsyncUDP::AsyncUDP() +{ + _pcb = NULL; + _connected = false; + _lastErr = ERR_OK; + _handler = NULL; +} + +//////////////////////////////////////////////// + +AsyncUDP::~AsyncUDP() +{ + close(); + UDP_MUTEX_LOCK(); + + udp_recv(_pcb, NULL, NULL); + _udp_remove(_pcb); + _pcb = NULL; + + UDP_MUTEX_UNLOCK(); + //vSemaphoreDelete(_lock); +} + +//////////////////////////////////////////////// + +void AsyncUDP::close() +{ + UDP_MUTEX_LOCK(); + + if (_pcb != NULL) + { + if (_connected) + { + _udp_disconnect(_pcb); + } + + _connected = false; + //todo: unjoin multicast group + } + + UDP_MUTEX_UNLOCK(); +} + +//////////////////////////////////////////////// + +bool AsyncUDP::connect(const ip_addr_t *addr, uint16_t port) +{ + if (!_udp_task_start()) + { + log_e("failed to start task"); + UDP_LOGERROR(F("AsyncUDP::connect: failed to start task")); + + return false; + } + + if (!_init()) + { + UDP_LOGERROR(F("AsyncUDP::connect: failed to init")); + + return false; + } + + close(); + UDP_MUTEX_LOCK(); + + _lastErr = _udp_connect(_pcb, addr, port); + + if (_lastErr != ERR_OK) + { + UDP_MUTEX_UNLOCK(); + + UDP_LOGERROR(F("AsyncUDP::connect: _udp_connect failed")); + + return false; + } + + _connected = true; + + UDP_MUTEX_UNLOCK(); + + return true; +} + +//////////////////////////////////////////////// + +bool AsyncUDP::listen(const ip_addr_t *addr, uint16_t port) +{ + if (!_udp_task_start()) + { + log_e("failed to start task"); + UDP_LOGERROR(F("AsyncUDP::listen: failed to start task")); + + return false; + } + + if (!_init()) + { + UDP_LOGERROR(F("AsyncUDP::listen: failed to init")); + + return false; + } + + close(); + + if (addr) + { + IP_SET_TYPE_VAL(_pcb->local_ip, addr->type); + IP_SET_TYPE_VAL(_pcb->remote_ip, addr->type); + } + + UDP_MUTEX_LOCK(); + + if (_udp_bind(_pcb, addr, port) != ERR_OK) + { + UDP_MUTEX_UNLOCK(); + + UDP_LOGERROR(F("AsyncUDP::listen: failed to _udp_bind")); + + return false; + } + + _connected = true; + + UDP_MUTEX_UNLOCK(); + + return true; +} + +//////////////////////////////////////////////// + +static esp_err_t joinMulticastGroup(const ip_addr_t *addr, bool join, + tcpip_adapter_if_t tcpip_if = TCPIP_ADAPTER_IF_MAX) +{ + struct netif * netif = NULL; + + if (tcpip_if < TCPIP_ADAPTER_IF_MAX) + { + void * nif = NULL; + esp_err_t err = tcpip_adapter_get_netif(tcpip_if, &nif); + + if (err) + { + UDP_LOGERROR(F("joinMulticastGroup: failed to get_netif")); + + return ESP_ERR_INVALID_ARG; + } + + netif = (struct netif *)nif; + + if (addr->type == IPADDR_TYPE_V4) + { + if (join) + { + if (igmp_joingroup_netif(netif, (const ip4_addr *) & (addr->u_addr.ip4))) + { + UDP_LOGERROR(F("joinMulticastGroup: IPv4 failed to joingroup")); + + return ESP_ERR_INVALID_STATE; + } + } + else + { + if (igmp_leavegroup_netif(netif, (const ip4_addr *) & (addr->u_addr.ip4))) + { + UDP_LOGERROR(F("joinMulticastGroup: IPv4 failed to leavegroup")); + + return ESP_ERR_INVALID_STATE; + } + } + } + else + { + if (join) + { + if (mld6_joingroup_netif(netif, &(addr->u_addr.ip6))) + { + UDP_LOGERROR(F("joinMulticastGroup: IPv6 failed to joingroup")); + + return ESP_ERR_INVALID_STATE; + } + } + else + { + if (mld6_leavegroup_netif(netif, &(addr->u_addr.ip6))) + { + UDP_LOGERROR(F("joinMulticastGroup: IPv6 failed to leavegroup")); + + return ESP_ERR_INVALID_STATE; + } + } + } + } + else + { + if (addr->type == IPADDR_TYPE_V4) + { + if (join) + { + if (igmp_joingroup((const ip4_addr *)IP4_ADDR_ANY, (const ip4_addr *) & (addr->u_addr.ip4))) + { + return ESP_ERR_INVALID_STATE; + } + } + else + { + if (igmp_leavegroup((const ip4_addr *)IP4_ADDR_ANY, (const ip4_addr *) & (addr->u_addr.ip4))) + { + return ESP_ERR_INVALID_STATE; + } + } + } + else + { + if (join) + { + if (mld6_joingroup((const ip6_addr *)IP6_ADDR_ANY, &(addr->u_addr.ip6))) + { + return ESP_ERR_INVALID_STATE; + } + } + else + { + if (mld6_leavegroup((const ip6_addr *)IP6_ADDR_ANY, &(addr->u_addr.ip6))) + { + return ESP_ERR_INVALID_STATE; + } + } + } + } + + return ESP_OK; +} + +//////////////////////////////////////////////// + +bool AsyncUDP::listenMulticast(const ip_addr_t *addr, uint16_t port, uint8_t ttl, tcpip_adapter_if_t tcpip_if) +{ + if (!ip_addr_ismulticast(addr)) + { + UDP_LOGERROR(F("listenMulticast: not addr_ismulticast")); + + return false; + } + + if (joinMulticastGroup(addr, true, tcpip_if) != ERR_OK) + { + UDP_LOGERROR(F("listenMulticast: error joinMulticast")); + + return false; + } + + if (!listen(NULL, port)) + { + UDP_LOGERROR1(F("listenMulticast: error listen to port ="), port); + + return false; + } + + UDP_MUTEX_LOCK(); + + _pcb->mcast_ttl = ttl; + _pcb->remote_port = port; + ip_addr_copy(_pcb->remote_ip, *addr); + //ip_addr_copy(_pcb->remote_ip, ip_addr_any_type); + + UDP_MUTEX_UNLOCK(); + + return true; +} + +//////////////////////////////////////////////// + +size_t AsyncUDP::writeTo(const uint8_t * data, size_t len, const ip_addr_t * addr, uint16_t port, + tcpip_adapter_if_t tcpip_if) +{ + if (!_pcb) + { + UDP_MUTEX_LOCK(); + + _pcb = udp_new(); + + UDP_MUTEX_UNLOCK(); + + if (_pcb == NULL) + { + return 0; + } + } + + if (len > CONFIG_TCP_MSS) + { + len = CONFIG_TCP_MSS; + } + + _lastErr = ERR_OK; + pbuf* pbt = pbuf_alloc(PBUF_TRANSPORT, len, PBUF_RAM); + + if (pbt != NULL) + { + uint8_t* dst = reinterpret_cast(pbt->payload); + memcpy(dst, data, len); + + UDP_MUTEX_LOCK(); + + if (tcpip_if < TCPIP_ADAPTER_IF_MAX) + { + void * nif = NULL; + tcpip_adapter_get_netif((tcpip_adapter_if_t)tcpip_if, &nif); + + if (!nif) + { + _lastErr = _udp_sendto(_pcb, pbt, addr, port); + } + else + { + _lastErr = _udp_sendto_if(_pcb, pbt, addr, port, (struct netif *)nif); + } + } + else + { + _lastErr = _udp_sendto(_pcb, pbt, addr, port); + } + + UDP_MUTEX_UNLOCK(); + + pbuf_free(pbt); + + if (_lastErr < ERR_OK) + { + UDP_LOGERROR1(F("AsyncUDP::writeTo: _lastErr ="), _lastErr); + + return 0; + } + + UDP_LOGDEBUG1(F("AsyncUDP::writeTo: len ="), len); + + return len; + } + + UDP_LOGERROR(F("AsyncUDP::writeTo: Error NULL pbt")); + + return 0; +} + +//////////////////////////////////////////////// + +void AsyncUDP::_recv(udp_pcb *upcb, pbuf *pb, const ip_addr_t *addr, uint16_t port, struct netif * netif) +{ + while (pb != NULL) + { + pbuf * this_pb = pb; + pb = pb->next; + this_pb->next = NULL; + + if (_handler) + { + AsyncUDPPacket packet(this, this_pb, addr, port, netif); + _handler(packet); + } + + pbuf_free(this_pb); + } +} + +//////////////////////////////////////////////// + +void AsyncUDP::_s_recv(void *arg, udp_pcb *upcb, pbuf *p, const ip_addr_t *addr, uint16_t port, struct netif * netif) +{ + reinterpret_cast(arg)->_recv(upcb, p, addr, port, netif); +} + +//////////////////////////////////////////////// + +bool AsyncUDP::listen(uint16_t port) +{ + return listen(IP_ANY_TYPE, port); +} + +//////////////////////////////////////////////// + +bool AsyncUDP::listen(const IPAddress addr, uint16_t port) +{ + ip_addr_t laddr; + laddr.type = IPADDR_TYPE_V4; + laddr.u_addr.ip4.addr = addr; + + return listen(&laddr, port); +} + +//////////////////////////////////////////////// + +bool AsyncUDP::listenMulticast(const IPAddress addr, uint16_t port, uint8_t ttl, tcpip_adapter_if_t tcpip_if) +{ + ip_addr_t laddr; + laddr.type = IPADDR_TYPE_V4; + laddr.u_addr.ip4.addr = addr; + + return listenMulticast(&laddr, port, ttl, tcpip_if); +} + +//////////////////////////////////////////////// + +bool AsyncUDP::connect(const IPAddress addr, uint16_t port) +{ + ip_addr_t daddr; + daddr.type = IPADDR_TYPE_V4; + daddr.u_addr.ip4.addr = addr; + + return connect(&daddr, port); +} + +//////////////////////////////////////////////// + +size_t AsyncUDP::writeTo(const uint8_t *data, size_t len, const IPAddress addr, uint16_t port, + tcpip_adapter_if_t tcpip_if) +{ + ip_addr_t daddr; + daddr.type = IPADDR_TYPE_V4; + daddr.u_addr.ip4.addr = addr; + + return writeTo(data, len, &daddr, port, tcpip_if); +} + +//////////////////////////////////////////////// + +IPAddress AsyncUDP::listenIP() +{ + if (!_pcb || _pcb->remote_ip.type != IPADDR_TYPE_V4) + { + return IPAddress(); + } + + return IPAddress(_pcb->remote_ip.u_addr.ip4.addr); +} + +//////////////////////////////////////////////// + +bool AsyncUDP::listen(const IPv6Address addr, uint16_t port) +{ + ip_addr_t laddr; + laddr.type = IPADDR_TYPE_V6; + memcpy((uint8_t*)(laddr.u_addr.ip6.addr), (const uint8_t*)addr, 16); + + return listen(&laddr, port); +} + +//////////////////////////////////////////////// + +bool AsyncUDP::listenMulticast(const IPv6Address addr, uint16_t port, uint8_t ttl, tcpip_adapter_if_t tcpip_if) +{ + ip_addr_t laddr; + laddr.type = IPADDR_TYPE_V6; + memcpy((uint8_t*)(laddr.u_addr.ip6.addr), (const uint8_t*)addr, 16); + + return listenMulticast(&laddr, port, ttl, tcpip_if); +} + +//////////////////////////////////////////////// + +bool AsyncUDP::connect(const IPv6Address addr, uint16_t port) +{ + ip_addr_t daddr; + daddr.type = IPADDR_TYPE_V6; + memcpy((uint8_t*)(daddr.u_addr.ip6.addr), (const uint8_t*)addr, 16); + + return connect(&daddr, port); +} + +//////////////////////////////////////////////// + +size_t AsyncUDP::writeTo(const uint8_t *data, size_t len, const IPv6Address addr, uint16_t port, + tcpip_adapter_if_t tcpip_if) +{ + ip_addr_t daddr; + daddr.type = IPADDR_TYPE_V6; + memcpy((uint8_t*)(daddr.u_addr.ip6.addr), (const uint8_t*)addr, 16); + + return writeTo(data, len, &daddr, port, tcpip_if); +} + +//////////////////////////////////////////////// + +IPv6Address AsyncUDP::listenIPv6() +{ + if (!_pcb || _pcb->remote_ip.type != IPADDR_TYPE_V6) + { + return IPv6Address(); + } + + return IPv6Address(_pcb->remote_ip.u_addr.ip6.addr); +} + +//////////////////////////////////////////////// + +size_t AsyncUDP::write(const uint8_t *data, size_t len) +{ + return writeTo(data, len, &(_pcb->remote_ip), _pcb->remote_port); +} + +//////////////////////////////////////////////// + +size_t AsyncUDP::write(uint8_t data) +{ + return write(&data, 1); +} + +//////////////////////////////////////////////// + +size_t AsyncUDP::broadcastTo(uint8_t *data, size_t len, uint16_t port, tcpip_adapter_if_t tcpip_if) +{ + return writeTo(data, len, IP_ADDR_BROADCAST, port, tcpip_if); +} + +//////////////////////////////////////////////// + +size_t AsyncUDP::broadcastTo(const char * data, uint16_t port, tcpip_adapter_if_t tcpip_if) +{ + return broadcastTo((uint8_t *)data, strlen(data), port, tcpip_if); +} + +//////////////////////////////////////////////// + +size_t AsyncUDP::broadcast(uint8_t *data, size_t len) +{ + if (_pcb->local_port != 0) + { + return broadcastTo(data, len, _pcb->local_port); + } + + return 0; +} + +//////////////////////////////////////////////// + +size_t AsyncUDP::broadcast(const char * data) +{ + return broadcast((uint8_t *)data, strlen(data)); +} + +//////////////////////////////////////////////// + +size_t AsyncUDP::sendTo(AsyncUDPMessage &message, const ip_addr_t *addr, uint16_t port, tcpip_adapter_if_t tcpip_if) +{ + if (!message) + { + return 0; + } + + return writeTo(message.data(), message.length(), addr, port, tcpip_if); +} + +//////////////////////////////////////////////// + +size_t AsyncUDP::sendTo(AsyncUDPMessage &message, const IPAddress addr, uint16_t port, tcpip_adapter_if_t tcpip_if) +{ + if (!message) + { + return 0; + } + + return writeTo(message.data(), message.length(), addr, port, tcpip_if); +} + +//////////////////////////////////////////////// + +size_t AsyncUDP::sendTo(AsyncUDPMessage &message, const IPv6Address addr, uint16_t port, tcpip_adapter_if_t tcpip_if) +{ + if (!message) + { + return 0; + } + + return writeTo(message.data(), message.length(), addr, port, tcpip_if); +} + +//////////////////////////////////////////////// + +size_t AsyncUDP::send(AsyncUDPMessage &message) +{ + if (!message) + { + return 0; + } + + return writeTo(message.data(), message.length(), &(_pcb->remote_ip), _pcb->remote_port); +} + +//////////////////////////////////////////////// + +size_t AsyncUDP::broadcastTo(AsyncUDPMessage &message, uint16_t port, tcpip_adapter_if_t tcpip_if) +{ + if (!message) + { + return 0; + } + + return broadcastTo(message.data(), message.length(), port, tcpip_if); +} + +//////////////////////////////////////////////// + +size_t AsyncUDP::broadcast(AsyncUDPMessage &message) +{ + if (!message) + { + return 0; + } + + return broadcast(message.data(), message.length()); +} + +//////////////////////////////////////////////// + +AsyncUDP::operator bool() +{ + return _connected; +} + +//////////////////////////////////////////////// + +bool AsyncUDP::connected() +{ + return _connected; +} + +//////////////////////////////////////////////// + +esp_err_t AsyncUDP::lastErr() +{ + return _lastErr; +} + +//////////////////////////////////////////////// + +void AsyncUDP::onPacket(AuPacketHandlerFunctionWithArg cb, void * arg) +{ + onPacket(std::bind(cb, arg, std::placeholders::_1)); +} + +//////////////////////////////////////////////// + +void AsyncUDP::onPacket(AuPacketHandlerFunction cb) +{ + _handler = cb; +} + +//////////////////////////////////////////////// + + +#endif // ASYNC_UDP_ESP32_ENC_IMPL_H diff --git a/utils/astyle_library.conf b/utils/astyle_library.conf new file mode 100644 index 0000000..8a73bc2 --- /dev/null +++ b/utils/astyle_library.conf @@ -0,0 +1,70 @@ +# Code formatting rules for Arduino libraries, modified from for KH libraries: +# +# https://github.com/arduino/Arduino/blob/master/build/shared/examples_formatter.conf +# + +# astyle --style=allman -s2 -t2 -C -S -xW -Y -M120 -f -p -xg -H -xb -c --xC120 -xL *.h *.cpp *.ino + +--mode=c +--lineend=linux +--style=allman + +# -r or -R +#--recursive + +# -c => Converts tabs into spaces +convert-tabs + +# -s2 => 2 spaces indentation +--indent=spaces=2 + +# -t2 => tab =2 spaces +#--indent=tab=2 + +# -C +--indent-classes + +# -S +--indent-switches + +# -xW +--indent-preproc-block + +# -Y => indent classes, switches (and cases), comments starting at column 1 +--indent-col1-comments + +# -M120 => maximum of 120 spaces to indent a continuation line +--max-continuation-indent=120 + +# -xC120 => max‑code‑length will break a line if the code exceeds # characters +--max-code-length=120 + +# -f => +--break-blocks + +# -p => put a space around operators +--pad-oper + +# -xg => Insert space padding after commas +--pad-comma + +# -H => put a space after if/for/while +pad-header + +# -xb => Break one line headers (e.g. if/for/while) +--break-one-line-headers + +# -c => Converts tabs into spaces +#--convert-tabs + +# if you like one-liners, keep them +#keep-one-line-statements + +# -xV +--attach-closing-while + +#unpad-paren + +# -xp +remove-comment-prefix + diff --git a/utils/restyle.sh b/utils/restyle.sh new file mode 100644 index 0000000..bcd846f --- /dev/null +++ b/utils/restyle.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +for dir in . ; do + find $dir -type f \( -name "*.c" -o -name "*.h" -o -name "*.cpp" -o -name "*.ino" \) -exec astyle --suffix=none --options=./utils/astyle_library.conf \{\} \; +done +