上海进出口博览会倒计时钟-Arduino中文社区 - Powered by Discuz!

Arduino中文社区

 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 5004|回复: 2

上海进出口博览会倒计时钟

[复制链接]
发表于 2018-8-16 00:48 | 显示全部楼层 |阅读模式
本帖最后由 topdog 于 2018-9-19 22:30 编辑

首届中国国际进口博览会将于2018年11月5日至10日在上海举办,为提醒工作进度,按时完成各项任务,需要制作精确计时的倒计时钟。
吉祥物.JPG

时钟的布局是标题:shanghai expo ;第二行倒计时天数:countdown;第三行现在的时间;第四行现在的年月日星期几。
效果.JPG

一、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进行校时操作。

pcf8563内部结构.JPG
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。
esp8266串口显示.JPG

二、制作倒计时钟

(1)把core,rtc,oled和电池盒层叠;
(2)烧入程序core_RTC_oled_shanghaiexpo_countdown.ino;
a;pcd8563的寄存器是bcd码的比较特殊,其他操作与i2c相同。此处需要注意。

寄存器结构.JPG
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)观察运行情况。
根据实际情况修改一下屏幕显示的行间距。这个例子可以做成各种纪念日的提示器。希望你喜欢。




shanghaiexpo_countdown.rar

14.73 KB, 下载次数: 1

售价: 1 金币  [记录]

发表于 2018-8-20 14:40 | 显示全部楼层
我手里没有美科现成的模块,看我看这几种模块的其他产品应该有,出差回来测试一下,谢谢topdog师兄分享。
 楼主| 发表于 2018-8-21 21:36 | 显示全部楼层
沧海笑1122 发表于 2018-8-20 14:40
我手里没有美科现成的模块,看我看这几种模块的其他产品应该有,出差回来测试一下,谢谢topdog师兄分享。 ...

谢谢沧海老师的指导,一起学习一起进步。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|Archiver|手机版|Arduino中文社区

GMT+8, 2024-12-1 01:12 , Processed in 0.093591 second(s), 20 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表