之前做的那些1602时钟虽然使用了DS3231来走时,误差不算太大, 但始终会有点误差。加上上次因为DS3231模块设计问题,导致给纽扣电池的电压达到了5V,最终把电池给弄炸了,威力还不小,让我有点害怕,所以决定做一个基于WiFi自动校时的程序。
上网找了一会,没有现成的方案和代码,但是找到了一个WiFi对时的程序,参见:https://github.com/YFROBOT-TM/YFRobot-NTPClock_OneNet,我的1602时钟也是基于这个WiFi授时改动来的。
硬件连接:
这个版本用的1602是那种带按键的模块式的,直接插上板子即可。
代码:
#include <ESP8266WiFi.h>
#include <Time.h>
#include <Timezone.h>
#include "NTP.h"
#include <LiquidCrystal.h>
// Set your WiFi login credentials
#define WIFI_SSID "XXX" // 使用时请修改为当前你的 wifi ssid
#define WIFI_PASS "XXX" // 使用时请修改为当前你的 wifi 密码
#define ledPin 14 // 定义ledPin连接到GPIO14
LiquidCrystal lcd(0, 2, 4, 14, 12, 13);
//年
byte Nian[8] = {0b01000,0b01111,0b10010, 0b01111, 0b01010, 0b11111, 0b00010, 0b00010};
//月
byte Yue[8] = {0b01111, 0b01001, 0b01111, 0b01001, 0b01111, 0b01001, 0b10011, 0b00001};
//日
byte Ri[8] = {0b01111, 0b01001, 0b01001, 0b01111, 0b01001, 0b01001, 0b01111, 0b00000};
// This clock is in the Mountain Time Zone
// Change this for your timezone
// 北京时间时区
#define STD_TIMEZONE_OFFSET +8 // Standard Time offset (-7 is mountain time)
// ***************************************************************
// TimeZone and Daylight Savings Time Rules
// ***************************************************************
// Define daylight savings time rules for the China
TimeChangeRule mySTD = {"", First, Sun, Jan, 0, STD_TIMEZONE_OFFSET * 60};
Timezone myTZ(mySTD, mySTD);
WiFiClient client;
// This function is called once a second
void updateDisplay(void) {
TimeChangeRule *tcr; // Pointer to the time change rule
// Read the current UTC time from the NTP provider
time_t utc = now();
// Convert to local time taking DST into consideration
time_t localTime = myTZ.toLocal(utc, &tcr);
// Map time to pixel positions
int weekdays= weekday(localTime);
int days = day(localTime);
int months = month(localTime);
int years = year(localTime);
int seconds = second(localTime);
int minutes = minute(localTime);
int hours = hour(localTime) ; //12 hour format use : hourFormat12(localTime) isPM()/isAM()
Serial.println("");
Serial.print("Current local time:");
Serial.print(days);
Serial.print("/");
Serial.print(months);
Serial.print("/");
Serial.print(years);
Serial.print(" - ");
Serial.print(hours);
Serial.print(":");
Serial.print(minutes);
Serial.print(":");
Serial.print(seconds);
Serial.print(" - ");
Serial.print(dayStr(weekdays));
Serial.println("");
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Time:");
if(hours<10)
{
lcd.print("0");
}
lcd.print(hours);
lcd.print(":");
if(minutes<10)
{
lcd.print("0");
}
lcd.print(minutes);
lcd.print(":");
if(seconds<10)
{
lcd.print("0");
}
lcd.print(seconds);
lcd.print(" W");
printWeek(weekdays);
lcd.setCursor(0,1);
lcd.print("Date:");
lcd.print(years);
lcd.write(byte(0));
if(months<10)
{
lcd.print("0");
}
lcd.print(months);
lcd.write(byte(1));
if(days<10)
{
lcd.print("0");
}
lcd.print(days);
lcd.write(byte(2));
}
void setup() {
Serial.begin(115200);
pinMode(ledPin, OUTPUT);
delay(10);
lcd.begin(16, 2);
lcd.createChar(0, Nian);
lcd.createChar(1, Yue);
lcd.createChar(2, Ri);
// We start by connecting to a WiFi network
initNTP(WIFI_SSID, WIFI_PASS);
}
// Previous seconds value
time_t previousSecond = 0;
void loop() {
// Update the display only if time has changed
if (timeStatus() != timeNotSet) {
if (second() != previousSecond) {
previousSecond = second();
// Update the display
updateDisplay();
}
}
delay(1000);
}
void printWeek(int week)
{
switch(week)
{
case 1: lcd.print("7");break;
case 2: lcd.print("1");break;
case 3: lcd.print("2");break;
case 4: lcd.print("3");break;
case 5: lcd.print("4");break;
case 6: lcd.print("5");break;
case 7: lcd.print("6");break;
default: lcd.print("E");break;
}
}
NTP.h 文件:
#ifndef NTP_H
#define NTP_H
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <TimeLib.h>
#include <WiFiUdp.h>
#define LOCALPORT 5000 // Local port to listen for UDP packets
#define NTP_PACKET_SIZE 48 // NTP time stamp is in the first 48 bytes of the message
// A UDP instance to let us send and receive packets over UDP
WiFiUDP udp;
byte packetBuffer[NTP_PACKET_SIZE]; // Buffer to hold incoming and outgoing packets
// Don't hardwire the IP address or we won't get the benefits of the time server pool.
const char *ntpServerName ;//= "time.windows.com";//"1.cn.pool.ntp.org";
IPAddress timeServerIP(182,92,12,11); //ONENET -- NTP
// Send an NTP request to the time server at the given address
unsigned long sendNTPpacket(IPAddress& address) {
// Set all bytes in the buffer to 0
memset(packetBuffer, 0, NTP_PACKET_SIZE);
// Initialize values needed to form NTP request
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();
}
// NTP Time Provider Code
time_t getNTPTime() {
int attempts = 10;
// Try multiple attempts to return the NTP time
while (attempts--) {
// Get a server from the pool
if(ntpServerName != NULL){
WiFi.hostByName(ntpServerName, timeServerIP);
}
Serial.printf("Time server IP address: ");
Serial.println(timeServerIP);
while (udp.parsePacket() > 0); // Discard any previously received packets
Serial.println("Transmitted NTP Request");
sendNTPpacket(timeServerIP);
uint32_t beginWait = millis();
while (millis() - beginWait < 1500) {
int size = udp.parsePacket();
if (size >= NTP_PACKET_SIZE) {
Serial.println("Received NTP Response");
udp.read(packetBuffer, NTP_PACKET_SIZE); // Read packet into the buffer
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];
Serial.println("Got the time");
return secsSince1900 - 2208988800UL;
}
delay(10);
}
Serial.println("Retrying NTP request");
delay(4000);
}
Serial.println("No NTP Response");
return 0;
}
// Login to WiFi network and assign the time sync provider
void initNTP(const char *ssid, const char *password) {
// Set station mode
WiFi.mode(WIFI_STA); //set work mode: WIFI_AP /WIFI_STA /WIFI_AP_STA
// Start by connecting to a WiFi network
WiFi.begin(ssid, password);
int i = 0;
while ((WiFi.status() != WL_CONNECTED) && (i++ < 30)) {
delay(500);
Serial.printf(".");
}
if (i == 31) {
Serial.printf("\nCould not connect to: %s\n", ssid);
return;
}
Serial.printf("\nConnected to: %s\n", ssid);
delay(500);
// Login suceeded so set UDP local port
udp.begin(LOCALPORT);
// Set the time provider to NTP
setSyncProvider(getNTPTime);
}
#endif
效果图:
https://github.com/PaulStoffregen/Time
https://github.com/JChristensen/Timezone