├── LICENSE ├── README.md ├── platformio.ini └── src ├── ESP32_NTP.ino └── local_config.h /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ESP32_W5500_NTP_CLIENT 2 | An implementation of the Ethernet library UdpNtpClient example program ...for the ESP32 and W5500 3 | 4 | 5 | ## What is this and what does it do? 6 | This is just a simple, working version of the Ethernet library UdpNtpClient example program to get you going with the ESP32 and hardwired Ethernet, using the common (and readily available) W5500 module. The hardware interconnection list (pin-to-pin) is also included below. 7 | 8 | It only initializes and uses the hardwired Ethernet connection, *not* the WiFi. It will connect to the specified NTP server pool and display the UTC time, then go into a short sleep loop (printing occasional "." characters, so that you can see it's still running), before repeating. 9 | 10 | This is basically a 30-minute project which will result in a working, hardwired Ethernet module connected to your ESP32. 11 | 12 | [Here are some photos of the prototype board and sample output](https://esp8266hints.wordpress.com/2019/04/05/esp32-w5500-simple-working-example "ESP32 + W5500 — Simple Working Example") 13 | 14 | 15 | ### The code 16 | This is a PlatformIO build (and if you haven't tried PlatformIO yet, you really should, it's great!), but is easily adapted to the Arduino IDE, if that happens to be your weapon of choice. 17 | 18 | Under PlatformIO, the platformio.ini file will automatically download and install the correct Ethernet library for you when you type "pio run" for the first time. 19 | 20 | The setup() section of the code does have some clunky hardware checks, so pay attention to the serial output from your ESP32 when you first run it; it will try to guide you to solutions for (simple!) hardware issues. 21 | 22 | **You do** need to update the src/local_config.h file to fit with *your* specific home network; it will not work unless it can look up the IP address of the remote NTP server (via DNS) and find a valid route to it (via your gateway). 23 | 24 | **Optionally**, you can change the definition of "timeServer[]" (also in src/local_config.h) to select a server pool for your geogrphical region (this will usually improve reliability by reducing the chance of lost/slow replies). 25 | 26 | 27 | ### DHCP 28 | The original ESP8266 version of this project timed-out and crashed when configured to use DHCP rather than a hard-coded IP. I would recommend you stick with a hard-coded IP for initial testing, as I haven't tried DHCP at all, yet (let me know how it goes if you do). 29 | 30 | 31 | ### Practical notes 32 | The physical connections between the ESP32 and W5500 are different, but simpler than the ESP8266 version (you don't need to buffer SCS chip-select signal on the ESP32). 33 | 34 | - **GPIO23** <--> **MOSI** 35 | - **GPIO19** <--> **MISO** 36 | - **GPIO18** <--> **SCLK** 37 | - **GPIO5** <--> **SCS** 38 | - **GPIO26** <--> **RESET** 39 | 40 | GPIO26 was chosen for the reset driver pin (from the ESP32 to the W5500) simply because it didn't clash with any of the other peripheral devices I intended to use and didn't have existing pull-up or pull-down requirements that might cause problems at power-up (as GPIO15 does on the ESP8266, for instance). 41 | 42 | As always, you do need a good solid DC supply for both boards and a good ground connection between them in addition to the data lines listed above. 43 | 44 | 45 | ### Troubleshooting 46 | - A common problem when wiring up this circuit on breadboard is that the W5500 doesn't get the assigned IP address at start-up, or displays all zeros or all ones, or apparently random numbers at each start up. These are all symptoms of bad physical connections between the boards. Try replacing each jumper wire, one by one and testing in between. If that doesn't work, try moving the jumpers (and the modules) to a different row of holes. 47 | - As already mentioned, a bad power-supply, bad grounding between the boards and/or a lack of decoupling/smoothing capacitors can also cause hard to find, intermittent problems. 48 | - The display doesn't always display the time, just the sleep loop. -- This is usually a symptom of busy networks and the use of the inherently unreliable UDP protocol in the example code. Sometimes a reply to the NTP request gets lost, or doesn't arrive in time to be printed. There's no error checking or retry code built into this simple demo. You can probably improve performance by changing to an NTP server closer to you (ie:- use the country servers defined for your location, or if you're lucky enough to have one, use an existing NTP server on your local network). 49 | 50 | -------------------------------------------------------------------------------- /platformio.ini: -------------------------------------------------------------------------------- 1 | ; PlatformIO Project Configuration File 2 | ; 3 | ; Build options: build flags, source filter 4 | ; Upload options: custom upload port, speed and extra flags 5 | ; Library options: dependencies, extra library storages 6 | ; Advanced options: extra scripting 7 | ; 8 | ; Please visit documentation for the other options and examples 9 | ; https://docs.platformio.org/page/projectconf.html 10 | 11 | [env:esp32doit-devkit-v1] 12 | platform = espressif32 13 | board = esp32doit-devkit-v1 14 | framework = arduino 15 | lib_deps = 16 | Ethernet 17 | upload_port = /dev/ttyUSB0 18 | 19 | -------------------------------------------------------------------------------- /src/ESP32_NTP.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id: ESP32_NTP.ino,v 1.8 2019/04/04 04:48:23 gaijin Exp $ 3 | * 4 | * UDP NTP client example program. 5 | * 6 | * Get the time from a Network Time Protocol (NTP) time server 7 | * Demonstrates use of UDP sendPacket and ReceivePacket 8 | * 9 | * Created: 04 Sep 2010 by Michael Margolis 10 | * Modified: 09 Apr 2012 by Tom Igoe 11 | * Modified: 02 Sep 2015 by Arturo Guadalupi 12 | * Munged: 04 Apr 2019 by PuceBaboon (for the ESP32 with a W5500 module) 13 | * 14 | */ 15 | 16 | #include 17 | #include 18 | #include 19 | #include "local_config.h" // <--- Change settings for YOUR network here. 20 | 21 | 22 | 23 | const int NTP_PACKET_SIZE = 48; // NTP time stamp is in the first 48 bytes of the message. 24 | byte packetBuffer[NTP_PACKET_SIZE]; // Buffer for both incoming and outgoing packets. 25 | 26 | // A UDP instance to let us send and receive packets over UDP. 27 | EthernetUDP Udp; 28 | 29 | 30 | /* 31 | * Wiz W5500 reset function. Change this for the specific reset 32 | * sequence required for your particular board or module. 33 | */ 34 | void WizReset() { 35 | Serial.print("Resetting Wiz W5500 Ethernet Board... "); 36 | pinMode(RESET_P, OUTPUT); 37 | digitalWrite(RESET_P, HIGH); 38 | delay(250); 39 | digitalWrite(RESET_P, LOW); 40 | delay(50); 41 | digitalWrite(RESET_P, HIGH); 42 | delay(350); 43 | Serial.println("Done."); 44 | } 45 | 46 | 47 | /* 48 | * This is a crock. It's here in an effort 49 | * to help people debug hardware problems with 50 | * their W5100 ~ W5500 board setups. It's 51 | * a copy of the Ethernet library enums and 52 | * should, at the very least, be regenerated 53 | * from Ethernet.h automatically before the 54 | * compile starts (that's a TODO item). 55 | * 56 | */ 57 | /* 58 | * Print the result of the hardware status enum 59 | * as a string. 60 | * Ethernet.h currently contains these values:- 61 | * 62 | * enum EthernetHardwareStatus { 63 | * EthernetNoHardware, 64 | * EthernetW5100, 65 | * EthernetW5200, 66 | * EthernetW5500 67 | * }; 68 | * 69 | */ 70 | void prt_hwval(uint8_t refval) { 71 | switch (refval) { 72 | case 0: 73 | Serial.println("No hardware detected."); 74 | break; 75 | case 1: 76 | Serial.println("WizNet W5100 detected."); 77 | break; 78 | case 2: 79 | Serial.println("WizNet W5200 detected."); 80 | break; 81 | case 3: 82 | Serial.println("WizNet W5500 detected."); 83 | break; 84 | default: 85 | Serial.println 86 | ("UNKNOWN - Update espnow_gw.ino to match Ethernet.h"); 87 | } 88 | } 89 | 90 | 91 | /* 92 | * Print the result of the ethernet connection 93 | * status enum as a string. 94 | * Ethernet.h currently contains these values:- 95 | * 96 | * enum EthernetLinkStatus { 97 | * Unknown, 98 | * LinkON, 99 | * LinkOFF 100 | * }; 101 | * 102 | */ 103 | void prt_ethval(uint8_t refval) { 104 | switch (refval) { 105 | case 0: 106 | Serial.println("Uknown status."); 107 | break; 108 | case 1: 109 | Serial.println("Link flagged as UP."); 110 | break; 111 | case 2: 112 | Serial.println("Link flagged as DOWN. Check cable connection."); 113 | break; 114 | default: 115 | Serial.println 116 | ("UNKNOWN - Update espnow_gw.ino to match Ethernet.h"); 117 | } 118 | } 119 | 120 | 121 | void setup() { 122 | Serial.begin(115200); 123 | delay(500); 124 | Serial.println("\n\tUDP NTP Client v3.0\r\n"); 125 | 126 | // Use Ethernet.init(pin) to configure the CS pin. 127 | Ethernet.init(5); // GPIO5 on the ESP32. 128 | WizReset(); 129 | 130 | /* 131 | * Network configuration - all except the MAC are optional. 132 | * 133 | * IMPORTANT NOTE - The mass-produced W5500 boards do -not- 134 | * have a built-in MAC address (depite 135 | * comments to the contrary elsewhere). You 136 | * -must- supply a MAC address here. 137 | */ 138 | Serial.println("Starting ETHERNET connection..."); 139 | Ethernet.begin(eth_MAC, eth_IP, eth_DNS, eth_GW, eth_MASK); 140 | 141 | delay(200); 142 | 143 | Serial.print("Ethernet IP is: "); 144 | Serial.println(Ethernet.localIP()); 145 | 146 | /* 147 | * Sanity checks for W5500 and cable connection. 148 | */ 149 | Serial.print("Checking connection."); 150 | bool rdy_flag = false; 151 | for (uint8_t i = 0; i <= 20; i++) { 152 | if ((Ethernet.hardwareStatus() == EthernetNoHardware) 153 | || (Ethernet.linkStatus() == LinkOFF)) { 154 | Serial.print("."); 155 | rdy_flag = false; 156 | delay(80); 157 | } else { 158 | rdy_flag = true; 159 | break; 160 | } 161 | } 162 | if (rdy_flag == false) { 163 | Serial.println 164 | ("\n\r\tHardware fault, or cable problem... cannot continue."); 165 | Serial.print("Hardware Status: "); 166 | prt_hwval(Ethernet.hardwareStatus()); 167 | Serial.print(" Cable Status: "); 168 | prt_ethval(Ethernet.linkStatus()); 169 | while (true) { 170 | delay(10); // Halt. 171 | } 172 | } else { 173 | Serial.println(" OK"); 174 | } 175 | 176 | Udp.begin(localPort); 177 | } 178 | 179 | void loop() { 180 | sendNTPpacket(timeServer); // Send an NTP packet to the time server. 181 | 182 | // Wait to see if a reply is available. 183 | delay(1000); 184 | if (Udp.parsePacket()) { 185 | // We've received a packet, read the data from it. 186 | Udp.read(packetBuffer, NTP_PACKET_SIZE); // Read the packet into the buffer. 187 | 188 | // The timestamp starts at byte 40 of the received packet and is four bytes, 189 | // or two words, long. First, extract the two words. 190 | unsigned long highWord = word(packetBuffer[40], packetBuffer[41]); 191 | unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]); 192 | 193 | // Next, combine the four bytes (two words) into a long integer. 194 | // This is NTP time (seconds since Jan 1 1900). 195 | unsigned long secsSince1900 = highWord << 16 | lowWord; 196 | Serial.print("Seconds since Jan 1 1900 = "); 197 | Serial.println(secsSince1900); 198 | 199 | // Now convert NTP time into everyday time. 200 | Serial.print("Unix time = "); 201 | // Unix time starts on Jan 1 1970. In seconds, that's 2208988800. 202 | const unsigned long seventyYears = 2208988800UL; 203 | // Subtract seventy years. 204 | unsigned long epoch = secsSince1900 - seventyYears; 205 | // ...and then print Unix time. 206 | Serial.println(epoch); 207 | 208 | 209 | // Print the hour, minute and second. 210 | Serial.print("The UTC time is "); // UTC is the time at Greenwich Meridian (GMT). 211 | Serial.print((epoch % 86400L) / 3600); // Print the hour (86400 equals secs per day). 212 | Serial.print(':'); 213 | if (((epoch % 3600) / 60) < 10) { 214 | // For the first 10 minutes of each hour, insert a leading '0' character. 215 | Serial.print('0'); 216 | } 217 | Serial.print((epoch % 3600) / 60); // Print the minute (3600 equals secs per minute). 218 | Serial.print(':'); 219 | if ((epoch % 60) < 10) { 220 | // For the first 10 seconds of each minute, insert a leading '0' character. 221 | Serial.print('0'); 222 | } 223 | Serial.println(epoch % 60); // Print the second. 224 | } 225 | 226 | // Wait for a while before asking for the time again. 227 | Serial.print("Sleeping: "); 228 | for (uint8_t i = 0; i < SLEEP_SECS; i++) { 229 | delay(1000); // One second delay "ticks". 230 | Serial.print("."); 231 | } 232 | Serial.println("\n\r---\n\r"); 233 | 234 | // You only need to call maintain if you're using DHCP. 235 | // Ethernet.maintain(); 236 | } 237 | 238 | // Send an NTP request to the time server at the given address (defined in local_conf.h). 239 | void sendNTPpacket(const char *address) { 240 | // Set all bytes in the buffer to 0. 241 | memset(packetBuffer, 0, NTP_PACKET_SIZE); 242 | 243 | // Initialize values needed to form NTP request 244 | // (see http://en.wikipedia.org/wiki/Network_Time_Protocol). 245 | packetBuffer[0] = 0b11100011; // LI, Version, Mode 246 | packetBuffer[1] = 0; // Stratum, or type of clock 247 | packetBuffer[2] = 6; // Polling Interval 248 | packetBuffer[3] = 0xEC; // Peer Clock Precision 249 | // 8 bytes of zero for Root Delay & Root Dispersion 250 | packetBuffer[12] = 49; 251 | packetBuffer[13] = 0x4E; 252 | packetBuffer[14] = 49; 253 | packetBuffer[15] = 52; 254 | 255 | // All NTP fields have been given values, now 256 | // send a packet requesting a timestamp. 257 | Udp.beginPacket(address, 123); // NTP requests are to port 123 258 | Udp.write(packetBuffer, NTP_PACKET_SIZE); 259 | Udp.endPacket(); 260 | } 261 | -------------------------------------------------------------------------------- /src/local_config.h: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id: local_config.h,v 1.5 2019/04/04 23:06:53 gaijin Exp $ 3 | * 4 | * Change settings below to customize for -YOUR- local network. 5 | * 6 | */ 7 | 8 | 9 | /* 10 | * W5500 "hardware" MAC address. 11 | */ 12 | uint8_t eth_MAC[] = { 0x02, 0xF0, 0x0D, 0xBE, 0xEF, 0x01 }; 13 | 14 | 15 | /* 16 | * Define the static network settings for this gateway's ETHERNET connection 17 | * on your LAN. These values must match YOUR SPECIFIC LAN. The "eth_IP" 18 | * is the IP address for this gateway's ETHERNET port. 19 | */ 20 | IPAddress eth_IP(192, 168, 1, 100); // *** CHANGE THIS to something relevant for YOUR LAN. *** 21 | IPAddress eth_MASK(255, 255, 255, 0); // Subnet mask. 22 | IPAddress eth_DNS(192, 168, 1, 2); // *** CHANGE THIS to match YOUR DNS server. *** 23 | IPAddress eth_GW(192, 168, 1, 1); // *** CHANGE THIS to match YOUR Gateway (router). *** 24 | 25 | 26 | #define RESET_P 26 // Tie the Wiz820io/W5500 reset pin to ESP32 GPIO26 pin. 27 | 28 | const uint16_t localPort = 55432; // Local port for UDP packets. 29 | 30 | /* 31 | * Choose the NTP server pool for your geographical region for best 32 | * performance (fewer lost packets). 33 | * 34 | * *** Uncomment only one of the following "timeServer[]" defines. *** 35 | */ 36 | const char timeServer[] = "pool.ntp.org"; // Default NTP server pool. 37 | // const char timeServer[] = "africa.pool.ntp.org"; // Regional server pool. 38 | // const char timeServer[] = "asia.pool.ntp.org"; // Regional server pool. 39 | // const char timeServer[] = "europe.pool.ntp.org"; // Regional server pool. 40 | // const char timeServer[] = "north-america.pool.ntp.org"; // Regional server pool. 41 | // const char timeServer[] = "oceania.pool.ntp.org"; // Regional server pool. 42 | // const char timeServer[] = "south-america.pool.ntp.org"; // Regional server pool. 43 | // const char timeServer[] = "time.nist.gov"; // Original example target server (least preferred). 44 | 45 | const uint8_t SLEEP_SECS = 15; // Number of seconds to sleep between queries to the time 46 | // server. Please don't set this any lower than 10 unless 47 | // timeServer[] is a local NTP server on -your- network. 48 | 49 | 50 | --------------------------------------------------------------------------------