==== Железо ==== [[https://ru.aliexpress.com/item/2015-New-product-Wireless-module-NodeMcu-Lua-Wifi-Nodemcu-WIFI-Network-Development-Board-Based-ESP8266-High/32341789414.html|WAVGAT Nodemcu WIFI Network Development Board Based ESP8266]]\\ [[https://ru.aliexpress.com/item/4-Digit-LED-Display-Tube-7-segments/32591091564.html|RobotDyn 4-Digit LED 0.56″ Display Tube GREEN]] (clock, double dots), 7-segments, TM1637, disp. size 50x19mm\\ С этим не работает, то ли железка дохлая, то ли [[http://arduinolab.pw/index.php/2016/08/03/chasy-na-esp8266-i-tm1637-s-sinxronizaciej-s-ntp-serverom-cherez-internet/|код]] кривой - [[https://ru.aliexpress.com/item/1PCS-PCF8563-PCF8563T-8563-IIC-Real-Time-Clock-RTC-Module-Board-For-Arduino/32811343173.html|PCF8563 IIC Real Time Clock RTC Module Board For Arduino]] ==== Подключение ==== Схема выводов NodeMCU:\\ {{:hardware:nodemcu-pinout.png?direct&400|}} Дисплей:\\ {{:hardware:esp8266_m1637.jpg?direct&400|}} ==== Прошивка и ПО ==== Драйвер - https://github.com/nodemcu/nodemcu-devkit/tree/master/Drivers Прошиватор, идёт вместе с прошивкой: https://github.com/nodemcu/nodemcu-flasher/archive/master.zip\\ Запускать Win64\Release\ESP8266Flasher.exe.\\ Arduino IDE: https://www.arduino.cc/en/Main/Software\\ Arduino IDE -> Preferences -> Boards Manager URLs, добавить ссылку\\ http://arduino.esp8266.com/stable/package_esp8266com_index.json, нажать OK.\\ Далее Tools -> Boards -> Boards Manager\\ В поле поиска вставьте esp8266, выберите ESP8266 Community и нажмите кнопку Установить [[https://ngin.pro/smart-house/241-nachalo-raboty-s-esp8266-lilon-nodemcu-v3-polnoe-rukovodstvo-dlya-iot-v-kachestve-servera.html|Начало работы с ESP8266]] ==== Код ==== Взят отсюда: https://steve.fi/Hardware/d1-ntp-clock/ В Arduino IDE нужно скачать библиотеку поддержки дисплея Grove 4-digit display by Seeed Studio (поиск по 1637), библиотеку WiFiManager (её можно закомментировать в коде и не ставить), а также библиотеку времени [[https://github.com/PaulStoffregen/Time|Time]] (в репозитории не нашёл, качал вручную, папку нужно класть в %userprofile%\Documents\Arduino\libraries). Изменения: * Часовой пояс изменён на московский (+3) * NTP-сервер заменён на pool.ntp.org * Ноги для дисплея заменены на CLK D6 (GPIO12), DIO D5 (GPIO14). * Яркость дисплея выставлена на BRIGHT_TYPICAL (2 из диапазона 0-7) * Синхронизация с сервером - раз в 129600 сек (36 часов), как рекомендовано в [[https://www.pool.ntp.org/tos.html|NTP Pool Project Terms of Service for End-Users]]. // NTP-Based Clock - https://steve.fi/Hardware/ // // This is a simple program which uses WiFi & an 4x7-segment display // to show the current time, complete with blinking ":". // // Steve // -- // // // WiFi & over the air updates // #include #include // // NTP uses UDP. // #include // // The display-interface // #include "TM1637.h" // // Time & NTP // #include "TimeLib.h" // // The name of this project. // // Used for: // Access-Point name, in config-mode // OTA name. // #define PROJECT_NAME "NTP-CLOCK" // // The timezone - comment out to stay at GMT. // #define TIME_ZONE (+3) // // Should we enable debugging (via serial-console output) ? // // Use either `#undef DEBUG`, or `#define DEBUG`. // #define DEBUG // // If we did then DEBUG_LOG will log a string, otherwise // it will be ignored as a comment. // #ifdef DEBUG # define DEBUG_LOG(x) Serial.print(x) #else # define DEBUG_LOG(x) #endif // // Use the user-friendly WiFI library? // // If you do not want to use this then comment out the following line: // #define WIFI_MANAGER // // Otherwise define a SSID / Password // #ifdef WIFI_MANAGER # include "WiFiManager.h" #else # define WIFI_SSID "ssid" # define WIFI_PASS "password" #endif // // UDP-socket & local-port for replies. // WiFiUDP Udp; unsigned int localPort = 2390; // // The NTP-server we use. // static const char ntpServerName[] = "pool.ntp.org"; // // Pin definitions for TM1637 and can be changed to other ports // #define CLK D6 #define DIO D5 TM1637 tm1637(CLK, DIO); // // This function is called when the device is powered-on. // void setup() { // Enable our serial port. Serial.begin(115200); // initialize the display tm1637.init(); // We want to see ":" between the digits. tm1637.point(true); // // Set the intensity - valid choices include: // // BRIGHT_DARKEST = 0 // BRIGHT_TYPICAL = 2 // BRIGHT_BRIGHTEST = 7 // tm1637.set(BRIGHT_TYPICAL); // // Handle WiFi setup // #ifdef WIFI_MANAGER WiFiManager wifiManager; wifiManager.autoConnect(PROJECT_NAME); #else // // Connect to the WiFi network, and set a sane // hostname so we can ping it by name. // WiFi.mode(WIFI_STA); WiFi.hostname(PROJECT_NAME); WiFi.begin(WIFI_SSID, WIFI_PASS); // // Show that we are connecting to the WiFi. // DEBUG_LOG("WiFi connecting: "); // // Try to connect to WiFi, constantly. // while (WiFi.status() != WL_CONNECTED) { DEBUG_LOG("."); delay(500); } // // Now we are connected show the local IP address. // DEBUG_LOG("\nWiFi connected "); DEBUG_LOG(WiFi.localIP()); DEBUG_LOG("\n"); #endif // // Ensure our UDP port is listening for receiving NTP-replies // Udp.begin(localPort); // // But now we are done, so we will setup the clock. // // We provide the time via NTP, and resync every 36 hours, // as recommended at https://www.pool.ntp.org/tos.html // This is more than enough in most cases. // setSyncProvider(getNtpTime); setSyncInterval(129600); // // The final step is to allow over the air updates // // This is documented here: // https://randomnerdtutorials.com/esp8266-ota-updates-with-arduino-ide-over-the-air/ // // Hostname defaults to esp8266-[ChipID] // ArduinoOTA.setHostname(PROJECT_NAME); ArduinoOTA.onStart([]() { DEBUG_LOG("OTA Start\n"); }); ArduinoOTA.onEnd([]() { DEBUG_LOG("OTA End\n"); }); ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) { char buf[16]; memset(buf, '\0', sizeof(buf)); snprintf(buf, sizeof(buf) - 1, "Upgrade - %02u%% ", (progress / (total / 100))); DEBUG_LOG(buf); DEBUG_LOG("\n"); }); ArduinoOTA.onError([](ota_error_t error) { DEBUG_LOG("Error - "); DEBUG_LOG(error); DEBUG_LOG(" "); if (error == OTA_AUTH_ERROR) DEBUG_LOG("Auth Failed\n"); else if (error == OTA_BEGIN_ERROR) DEBUG_LOG("Begin Failed\n"); else if (error == OTA_CONNECT_ERROR) DEBUG_LOG("Connect Failed\n"); else if (error == OTA_RECEIVE_ERROR) DEBUG_LOG("Receive Failed\n"); else if (error == OTA_END_ERROR) DEBUG_LOG("End Failed\n"); }); // // Ensure the OTA process is running & listening. // ArduinoOTA.begin(); } // // This function is called continously, and is responsible // for flashing the ":", and otherwise updating the display. // // We rely on the background NTP-updates to actually make sure // that that works. // void loop() { static char buf[10] = { '\0' }; static char prev[10] = { '\0' }; static long last_read = 0; static bool flash = true; // // Get the current hour/min // time_t nw = now(); int cur_hour = hour(nw); int cur_min = minute(nw); // // Format them in a useful way. // sprintf(buf, "%02d%02d", cur_hour, cur_min); // // If the current "hourmin" is different to // that we displayed last loop .. // if (strcmp(buf, prev) != 0) { // Update the display tm1637.display(0, buf[0] - '0'); tm1637.display(1, buf[1] - '0'); tm1637.display(2, buf[2] - '0'); tm1637.display(3, buf[3] - '0'); // And cache it strcpy(prev , buf); } // // The preceeding piece of code would // have ensured the display only updated // when the hour/min changed. // // However note that we nuke the cached // value every second - solely so we can // blink the ":". // // Sigh long now = millis(); if ((last_read == 0) || (abs(now - last_read) > 1000)) { // Invert the "show :" flag flash = !flash; // Apply it. tm1637.point(flash); // However note that the ":" will not redraw // unless/until you update. So we will // force that to happen. memset(prev, '\0', sizeof(prev)); last_read = now; } } /*-------- NTP code ----------*/ const int NTP_PACKET_SIZE = 48; // NTP time is in the first 48 bytes of message byte packetBuffer[NTP_PACKET_SIZE]; //buffer to hold incoming & outgoing packets time_t getNtpTime() { IPAddress ntpServerIP; // discard any previously received packets while (Udp.parsePacket() > 0) ; DEBUG_LOG("Initiating NTP sync\n"); // get a random server from the pool WiFi.hostByName(ntpServerName, ntpServerIP); DEBUG_LOG(ntpServerName); DEBUG_LOG(" -> "); DEBUG_LOG(ntpServerIP); DEBUG_LOG("\n"); sendNTPpacket(ntpServerIP); delay(50); uint32_t beginWait = millis(); while ((millis() - beginWait) < 5000) { DEBUG_LOG("#"); int size = Udp.parsePacket(); if (size >= NTP_PACKET_SIZE) { DEBUG_LOG("Received NTP Response\n"); Udp.read(packetBuffer, NTP_PACKET_SIZE); unsigned long secsSince1900; // convert four bytes starting at location 40 to a long integer secsSince1900 = (unsigned long)packetBuffer[40] << 24; secsSince1900 |= (unsigned long)packetBuffer[41] << 16; secsSince1900 |= (unsigned long)packetBuffer[42] << 8; secsSince1900 |= (unsigned long)packetBuffer[43]; // Now convert to the real time. unsigned long now = secsSince1900 - 2208988800UL; #ifdef TIME_ZONE DEBUG_LOG("Adjusting time : "); DEBUG_LOG(TIME_ZONE); DEBUG_LOG("\n"); now += (TIME_ZONE * SECS_PER_HOUR); #endif return (now); } delay(50); } DEBUG_LOG("NTP-sync failed\n"); return 0; } // send an NTP request to the time server at the given address void sendNTPpacket(IPAddress &address) { // 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; // all NTP fields have been given values, now // you can send a packet requesting a timestamp: Udp.beginPacket(address, 123); //NTP requests are to port 123 Udp.write(packetBuffer, NTP_PACKET_SIZE); Udp.endPacket(); }