查看: 2176|回复: 2

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

[复制链接]
  • TA的每日心情
    开心
    2019-8-19 21:30
  • 签到天数: 100 天

    [LV.6]常住居民II

    发表于 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 金币  [记录]

    打赏作者鼓励一下!
  • TA的每日心情
    开心
    2019-7-17 13:13
  • 签到天数: 264 天

    [LV.8]以坛为家I

    发表于 2018-8-20 14:40 | 显示全部楼层
    我手里没有美科现成的模块,看我看这几种模块的其他产品应该有,出差回来测试一下,谢谢topdog师兄分享。
    打赏作者鼓励一下!
  • TA的每日心情
    开心
    2019-8-19 21:30
  • 签到天数: 100 天

    [LV.6]常住居民II

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

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

    本版积分规则

    热门推荐

    OLED 128*64自制可达10000000个选项的菜单(已更新)
    OLED 128*64自制可达10000
    OLED 128*64自制可达10000000个选项的菜单 温馨提示: 建议占个楼再食用本帖子
    这个怎么整?标点都是英文的
    这个怎么整?标点都是英文
    a=a+1改成a++也不行
    [WiFiduino-8266开发板测试]三、测试IO口
    [WiFiduino-8266开发板测
    首先测试IO口的关系,板载正面的D0~D15[D14,D15没有,相同的位置写的是D4,D5],反面
    求助,arduino nano发热严重
    求助,arduino nano发热严
    我有一块arduino nano板,装在一个扩展板上(如下图),用扩展板上的外接电源接口(资
    [限时福利]5分钟带你快速了解新一代开发板:M5STACK
    [限时福利]5分钟带你快速
    一、什么是M5Stack M5Stack是一种模块化、可堆叠扩展的开发板,每个模块
    Copyright   ©2015-2016  Arduino中文社区  Powered by©Discuz!   ( 蜀ICP备14017632号-3 )
    快速回复 返回顶部 返回列表