|
本帖最后由 topdog 于 2018-9-19 22:30 编辑
首届中国国际进口博览会将于2018年11月5日至10日在上海举办,为提醒工作进度,按时完成各项任务,需要制作精确计时的倒计时钟。
时钟的布局是标题:shanghai expo ;第二行倒计时天数:countdown;第三行现在的时间;第四行现在的年月日星期几。
一、ntp校准rtc时间
(1)获取esp8266 AT命令固件,并且更新到最新版本;
(2)把coreUSB,esp8266,rtc层叠起来,刷入coreUSB_ESP8266_RTC_NTP.ino;
a;esp8266的串口在管脚0,1,在不跳线的情况下,用coreUSB最适合。
b;esp8266使用AT命令组建udp向pool.ntp.org发出ntp请求,mcu对回文进行截取产生unixtime,作为mcu的系统基准时间,再对pcd8563进行校时操作。
c;串口打印的时间是mcu基于time产生的实时时间。
//使用mcookie coreUSB esp8266 pcf8563层叠,给rtc wifi校时
#include <TimeLib.h>
#include <ESP8266.h>
#include <Wire.h>
#include <Rtc_Pcf8563.h>
#define EspSerial Serial1
#define UARTSPEED 115200
#define SSID "你的wifi名称"
#define PASSWORD "你的wifi密码"
#define HOST_NAME "pool.ntp.org"
#define HOST_PORT (123)
const int timeZone = 8; // Central beijing Time
#define SECS_PER_HOUR (3600UL)
ESP8266 wifi(&EspSerial);
Rtc_Pcf8563 Rtc;
void setup()
{
Serial.begin(115200);
while (!Serial) ; // Needed for Leonardo only
delay(250);
Serial.println("Microduino ESP8266 AT setup TimeNTP begin \r\n");
WifiInit(EspSerial, UARTSPEED);
Serial.print("FW Version:");
Serial.println(wifi.getVersion().c_str());
if (wifi.setOprToStationSoftAP()) {
Serial.print("to station + softap ok\r\n");
} else {
Serial.print("to station + softap err\r\n");
}
if (wifi.joinAP(SSID, PASSWORD)) {
Serial.print("Join AP success\r\n");
Serial.print("IP: ");
Serial.println(wifi.getLocalIP().c_str());
} else {
Serial.print("Join AP failure\r\n");
}
if (wifi.enableMUX()) {
Serial.print("multiple ok\r\n");
} else {
Serial.print("multiple err\r\n");
}
Serial.print("setup end\r\n");
setSyncProvider(getNtpTime);
Rtc.initClock();
Rtc.setDateTime(day(), weekday(), month(),false, year()%100,hour(), minute(),second());
}
time_t prevDisplay = 0; // when the digital clock was displayed
void loop()
{
if (timeStatus() != timeNotSet) {
if (now() != prevDisplay) { //update the display only if time has changed
prevDisplay = now( );
digitalClockDisplay();
}
}
}
void digitalClockDisplay(){
// digital clock display of the time
Serial.print(hour());
printDigits(minute());
printDigits(second());
Serial.print(" ");
Serial.print(day());
Serial.print(" ");
Serial.print(month());
Serial.print(" ");
Serial.print(year());
Serial.println();
}
void printDigits(int digits){
Serial.print(":");
if(digits < 10)
Serial.print('0');
Serial.print(digits);
}
/*-------- NTP code ----------*/
time_t getNtpTime()
{
uint8_t buffer[128] = {0};
static uint8_t mux_id = 0;
if (wifi.registerUDP(mux_id, HOST_NAME, HOST_PORT)) {
Serial.print("register udp ");
Serial.print(mux_id);
Serial.println(" ok");
} else {
Serial.print("register udp ");
Serial.print(mux_id);
Serial.println(" err");
}
static const char PROGMEM
timeReqA[] = { 227, 0, 6, 236 },
timeReqB[] = { 49, 78, 49, 52 };
// Assemble and issue request packet
uint8_t buf[48];
memset(buf, 0, sizeof(buf));
memcpy_P( buf , timeReqA, sizeof(timeReqA));
memcpy_P(&buf[12], timeReqB, sizeof(timeReqB));
// send request packet
wifi.send(mux_id, (const uint8_t*)buf, 48);
uint32_t len = wifi.recv(mux_id, buffer, sizeof(buffer), 10000);
if (len > 0) {
for (uint32_t i = 0; i < len; i++) {
unsigned long secsSince1900;
secsSince1900 = (unsigned long)buffer[40] << 24;
secsSince1900 |=(unsigned long)buffer[41] << 16;
secsSince1900 |=(unsigned long)buffer[42] << 8;
secsSince1900 |=(unsigned long)buffer[43];
Serial.print("Received:[");
Serial.print("Unix timestamp:");
Serial.print(secsSince1900);
Serial.print("]\r\n");
Serial.println( );
return (secsSince1900 - 2208988800UL + timeZone * SECS_PER_HOUR);
}
}
if (wifi.unregisterUDP(mux_id)) {
Serial.print("unregister udp ");
Serial.print(mux_id);
Serial.println(" ok");
} else {
Serial.print("unregister udp ");
Serial.print(mux_id);
Serial.println(" err");
}
delay(1000);
mux_id++;
if (mux_id >= 5) {
mux_id = 0;
}
}
(3)打开串口观察一下,确保ntp时间已经写入pcf8563。
二、制作倒计时钟
(1)把core,rtc,oled和电池盒层叠;
(2)烧入程序core_RTC_oled_shanghaiexpo_countdown.ino;
a;pcd8563的寄存器是bcd码的比较特殊,其他操作与i2c相同。此处需要注意。
b;开幕在2018年11月5日,11月5日从1月1日开始计算是全年的第几天?这里提供两个方法:
一个比较野路子,打开 http://mistupid.com/calendar/dayofyear.htm 输入你要计算的年月日就可以算出11月5日是全年的第309天。
另一个是使用自定义函数计算,每月月底前累积天数是固定的,前一个月的累积天数加上当日天数就是全年的第几天了,11月5日就是10月底总天数304天加上11月的5天等于309天,此外在考虑月份是不是2月份并且是不是闰年,闰年的判断只需要计算能被4整除。(%100和%400都是世纪年的计算,本例毫无意义不必考虑。)
上述两种方法不能混用:lol
c;b项减去c项得出的天数就是倒计时天数。
d;其他的倒计时路径是(24-hh):(60-mm):(60-ss),大家自己去思考吧。
#include <U8glib.h>
#include <Wire.h>
#include <Rtc_Pcf8563.h>
Rtc_Pcf8563 rtc;
U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_NONE);
//int DayInYear_end = 309; //http://mistupid.com/calendar/dayofyear.htm 2008/11/5
int DaysInMonth [12] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
void setup() {
Serial.begin(115200);
}
void loop()
{
byte weekday =rtc.getWeekday();
byte century_begin =rtc.getCentury();
byte year_begin = rtc.getYear();
byte month_begin = rtc.getMonth();
Serial.println(month_begin);
byte day_begin = rtc.getDay();
Serial.println(day_begin);
int DayInYear_begin =DayInYear(2018,month_begin,day_begin);
int countdown = DayInYear(2018,11,5) - DayInYear_begin;
//int countdown = DayInYear_end - DayInYear_begin;
u8g.firstPage();
do {
u8g.setFont(u8g_font_gdb11); // https://github.com/olikraus/u8glib/wiki/fontsize
u8g.setPrintPos(0,12); //年显示定位,先x,后y。
u8g.print("SHANGHAI EXPO");
u8g.setPrintPos(2,27);
u8g.print("countdown: ");
u8g.setPrintPos(98,27);
u8g.print(countdown);
u8g.setPrintPos(2,45);
u8g.print("NOW:");
u8g.setPrintPos(55,45);
u8g.print(rtc.formatTime(RTCC_TIME_HMS));
u8g.setPrintPos(1,63);
u8g.print(rtc.formatDate(RTCC_DATE_ASIA));
u8g.setPrintPos(90,63);
switch(weekday){
case 1: u8g.print( "SUN"); break;
case 2: u8g.print( "MON"); break;
case 3: u8g.print( "TUE"); break;
case 4: u8g.print( "WED"); break;
case 5: u8g.print( "THUR"); break;
case 6: u8g.print( "FRI"); break;
case 0: u8g.print( "SAT"); break;
default: break;
}
}while( u8g.nextPage());
delay(1000);
}
static int DayInYear(int y, int m, int d) {
int days =DaysInMonth[m - 1] + d;
if (m == 2 && y % 4 == 0)
days +=1;
return days ;
}
(3)观察运行情况。
根据实际情况修改一下屏幕显示的行间距。这个例子可以做成各种纪念日的提示器。希望你喜欢。
|
|