2018年3月17日 星期六

NTP 網路時鐘的製作ESP8266 + LCD

#Arduino #ESP8266 #OLED #LCD #I2C #IIC #NTP #網路時鐘 #Clock #李進衛
I2C LCD 



Wemos D1 Mini Board 
Wemos D1 Mini Board購買

函數庫下載位址:

程式碼如下
//#include <LiquidCrystal_I2C.h>

 /* 網路時鐘,配合4位顯示LED範例
 * 請注意:本範例必須配合已焊控制板的4位LED哦!
 *
 * 網拍賣場:
 * http://goods.ruten.com.tw/item/show?21649712601470
 *
 * WeMos D1 R2:
 * http://goods.ruten.com.tw/item/show?21639776266588
 *
 * 程式整理:Jason--> 2018-03-05 by lee chin Wei append  I2C LCD
 * http://blog.geeks.tw
 *
 * 本範例要配合Frankie Chu寫的Digital Tube。
 *
 * 下載網址:
 * https://brainy-bits.com/wp-content/uploads/2015/01/DigitalTube_Library.zip
 * 程式設計:李進衛
 * 本範例要配合 Paul Stoffregen寫的Time函式庫:
 * https://github.com/PaulStoffregen/Time
 */

#include <TimeLib.h>
#include <ESP8266WiFi.h>
#include <WiFiUdp.h>

#include <TM1637.h> // "
#include <Wire.h> // IIC communication I2C使用的函數
//#include <avr/io.h>
#include <LiquidCrystal_I2C.h>

#define CLK D6 //CLK pin,可自行決定接到哪個PIN
#define DIO D7 //DIO pin,可自行決定接到哪個PIN
TM1637 tm1637(CLK,DIO);
LiquidCrystal_I2C lcd(0x3f,16,2); // Check I2C address of LCD, normally 0x27 or 0x3F

int point_flag =0;

const char ssid[] = "your network SSID";  //  your network SSID (name)
const char pass[] = "your network password";       // your network password
char daysOfTheWeek[7][12] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
// NTP Server:
static const char ntpServerName[] = "us.pool.ntp.org";

const int timeZone = 8;     //台灣時區+8

int hourH = 0;     //
int hourL = 0;     //
int minH = 0;     //
int minL = 0;     //
int secH = 0;     //
int secL = 0;     //
int monH = 0;
int monL = 0;
int dayH = 0;
int dayL = 0;
int dayOfWeek = 0;
//建立與NTP的連線
WiFiUDP Udp;
unsigned int localPort = 8888;  // local port to listen for UDP packets

time_t getNtpTime();

void setup()
{
  tm1637.init();
  tm1637.set(BRIGHT_TYPICAL); //BRIGHT_TYPICAL = 2,BRIGHT_DARKEST = 0,BRIGHTEST = 7;

  tm1637.display(0,0);  //設定每一位燈號顯示的內容,參數1:燈號,參數2:顯示的數字
  tm1637.display(1,0);
  tm1637.display(2,0);
  tm1637.display(3,0);
  Wire.begin(D14,D15);  //一定要加這一行還可以正常顯示出來 (0,2); //sda=0 | D3, scl=2 | D4
  lcd.backlight();
  lcd.clear();
//  lcd.noBlink();
  lcd.home();                // At column=0, row=0

  Serial.begin(115200);
  while (!Serial) ; // Needed for Leonardo only
  delay(250);

  //建立WiFi連線
  Serial.println("NTP 網路時鐘");
  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.begin(ssid, pass);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.print("IP number assigned by DHCP is ");
  Serial.println(WiFi.localIP());
  Serial.println("Starting UDP");
  Udp.begin(localPort);
  Serial.print("Local port: ");
  Serial.println(Udp.localPort());
  Serial.println("waiting for sync");
  setSyncProvider(getNtpTime);
  setSyncInterval(300);
}

time_t prevDisplay = 0; // 記錄上一次的時間,如果相同,LED就不用更新

void loop()
{
  if (timeStatus() != timeNotSet) {
    if (now() != prevDisplay) { //時間變化就顯示
      prevDisplay = now();   //把目前時間丟給prevDisplay
//      Serial.println(now());
      digitalClockDisplay(); //呼叫顯示時間的副程式
    }
  }
}

void digitalClockDisplay()
{
//    lcd.home();                // At column=0, row=0
  //設定每一位燈號顯示的內容,參數1:燈號,參數2:顯示的數字
  tm1637.display(0,hour() / 10);
  tm1637.display(1,hour() % 10);
  tm1637.display(2,minute() / 10);
  tm1637.display(3,minute() % 10);
//==============================================
  monH = (month()/10);
  monL = (month()%10);
  dayH = (day()/10);
  dayL = (day()%10);

  hourH = (hour()/10) ;
//  hourH = hourH/10;
//  Serial.print(hourH);
  hourL = (hour()%10);
//  Serial.print(hourL);
  minH = (minute() / 10);
//  Serial.print(minH);

  minL = (minute() % 10);
//  Serial.print(hourL);

  secH = (second() /10);
//  Serial.print(secH);

  secL = (second() %10);
//  Serial.println(secL);

//================================================
    lcd.setCursor(0, 0);
    lcd.print(year());
    lcd.setCursor(4,0);
    lcd.print("/");
    lcd.setCursor(5,0);
    lcd.print(monH,DEC);
    lcd.setCursor(6,0);
    lcd.print(monL,DEC);
    lcd.setCursor(7,0);
    lcd.print("/");
    lcd.setCursor(8,0);
    lcd.print(dayH,DEC);
    lcd.setCursor(9,0);
    lcd.print(dayL,DEC);
 
// dayOfWeek = weekday();
    lcd.setCursor(11,0);
lcd.print(daysOfTheWeek[weekday()-1]);
/*
switch(dayOfWeek){
  case 1:
    lcd.setCursor(11,0);
    lcd.print("Sun");

//    Serial.println("Sunday");
    break;
  case 2:
    lcd.setCursor(11,0);
    lcd.print("Mon");
//    Serial.println("Monday");
    break;
  case 3:
    lcd.setCursor(11,0);
    lcd.print("Tue");
//    Serial.println("Tuesday");
    break;
  case 4:
    lcd.setCursor(11,0);
    lcd.print("Wed");

//    Serial.println("Wednesday");
    break;
  case 5:
    lcd.setCursor(11,0);
    lcd.print("Thu");
//    Serial.println("Thursday");
    break;
  case 6:
    lcd.setCursor(11,0);
    lcd.print("Fri");

 //   Serial.println("Friday");
    break;
  case 7:
    lcd.setCursor(11,0);
    lcd.print("Sat");

//    Serial.println("Saturday");
    break;
  } 

  */
 
 

//  Serial.print(year());
//  Serial.print("/");
//  Serial.print(month());
//  Serial.print("/");
//  Serial.print(day());
 // Serial.print("===>");
 // Serial.println(weekday());
    lcd.setCursor(0, 1);
    lcd.print(hourH,DEC);
    lcd.setCursor(1, 1);
    lcd.print(hourL,DEC);
    lcd.setCursor(2, 1);
    lcd.print(":");
    lcd.setCursor(3, 1);
    lcd.print(minH,DEC);
    lcd.setCursor(4, 1);
    lcd.print(minL,DEC);
    lcd.setCursor(5, 1);
    lcd.print(":");
     lcd.setCursor(6, 1);
    lcd.print(secH,DEC);
     lcd.setCursor(7, 1);
    lcd.print(secL,DEC);
     lcd.setCursor(16,1);
 
//    lcd.home();                // At column=0, row=0
 // lcd.noBlink();

 // Serial.print(hour());
 // Serial.print(":");
 // Serial.print(minute());
 // Serial.print(":");
 // Serial.println(second());
 
  //顯示時和分之間的:,如果目前是顯示,就設定下次不顯示
  if(point_flag){
    tm1637.point(POINT_OFF);
    point_flag = 0;
  }else{
    tm1637.point(POINT_ON);
    point_flag = 1;
  }
}


/*-------- NTP 程式碼 ----------*/

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; // NTP server's ip address

  while (Udp.parsePacket() > 0) ; // discard any previously received packets
  Serial.println("Transmit NTP Request");
  // get a random server from the pool
  WiFi.hostByName(ntpServerName, ntpServerIP);
  Serial.print(ntpServerName);
  Serial.print(": ");
  Serial.println(ntpServerIP);
  sendNTPpacket(ntpServerIP);
  uint32_t beginWait = millis();
  while (millis() - beginWait < 1500) {
    int size = Udp.parsePacket();
    if (size >= NTP_PACKET_SIZE) {
      Serial.println("Receive 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];
      return secsSince1900 - 2208988800UL + timeZone * SECS_PER_HOUR;
    }
  }
  Serial.println("No NTP Response :-(");
  return 0; // return 0 if unable to get the time
}

// 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();
}

2018年3月16日 星期五

網路時鍾

有一天突發奇想想弄個不用調整的時鐘,還要OLED顯示的因此製作了這個專題
#clock
#NTP
#網路時鍾
#時鐘
#OLED
#SSD1306

網路時鍾


這個程式實驗過可以使用在D1 mini 的模組


0.96”白色OLED I2C 位址0x3C
D1 mini ESP8266
小鄭那裏買的
ESP8266_oled_ssd1306_master.zip
Oled 函數庫
Time 函數庫
Pin
Pin
Function
ESP-8266 Pin
TX
TXD
TXD
RX
RXD
RXD
A0
Analog input, max 3.3V input
A0
D0
IO
GPIO16
D1
IO, SCL
GPIO5
D2
IO, SDA
GPIO4
D3
IO,10k Pull-up
GPIO0
D4
IO, 10k pull-up, BUILTIN_LED
GPIO2
D5
IO, SCK
GPIO14
D6
IO, MISO
GPIO12
D7
IO, MOSI
GPIO13
D8
IO,10k pull-down, SS
GPIO15
G
Ground
GND
5V
5V
3V3
3.3V
3.3V
RST
Reset
RST
程式碼如下:

//#include
// OLED 網路時鐘 0.96“ 白色 位址0x3c
//
/* 網路時鐘,配合4位顯示LED範例
* 程式整理:Jason--> 2018-03-05 by lee chin Wei append  I2C LCD
* http://blog.geeks.tw
*
* 本範例要配合 Paul Stoffregen寫的Time函式庫:
* https://github.com/PaulStoffregen/Time
*/


#include
#include
#include


//#include // "
#include // IIC communication I2C使用的函數
//#include
//#include
#include "SSD1306.h" // alias for `#include "SSD1306Wire.h"`
SSD1306  display(0x3c, D2, D1); // D1 mini SDA: D2 , SCL : D1 設定I2C pin


//#define CLK D6 //CLK pin,可自行決定接到哪個PIN
//#define DIO D7 //DIO pin,可自行決定接到哪個PIN
//TM1637 tm1637(CLK,DIO);
//LiquidCrystal_I2C lcd(0x3f,16,2); // Check I2C address of LCD, normally 0x27 or 0x3F


int point_flag =0;


const char ssid[] = "your network SSID";  // your network SSID (name)
const char pass[] = "your passwork";       // your network password
char daysOfTheWeek[7][12] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
String date;
String t;
const char * days[] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"} ;
//const char * months[] = {"Jan", "Feb", "Mar", "Apr", "May", "June", "July", "Aug", "Sep", "Oct", "Nov", "Dec"} ;
const char * months[] = {"01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12"} ;
const char * ampm[] = {"AM", "PM"} ;


// NTP Server:
static const char ntpServerName[] = "us.pool.ntp.org";


const int timeZone = 8;     //台灣時區+8


int hourH = 0;     //
int hourL = 0;     //
int minH = 0;     //
int minL = 0;     //
int secH = 0;     //
int secL = 0;     //
int monH = 0;
int monL = 0;
int dayH = 0;
int dayL = 0;
int dayOfWeek = 0;
int cnt = 0;
//建立與NTP的連線
WiFiUDP Udp;
unsigned int localPort = 8888;  // local port to listen for UDP packets


time_t getNtpTime();


void setup()
{
 // Initialising the UI will init the display too.
 display.init();


 display.flipScreenVertically();
 display.setFont(ArialMT_Plain_10);


//  tm1637.init();
//  tm1637.set(BRIGHT_TYPICAL); //BRIGHT_TYPICAL = 2,BRIGHT_DARKEST = 0,BRIGHTEST = 7;
 
//  tm1637.display(0,0);  //設定每一位燈號顯示的內容,參數1:燈號,參數2:顯示的數字
//  tm1637.display(1,0);
//  tm1637.display(2,0);
//  tm1637.display(3,0);
// Wire.begin(D2,D1);  // D1 mini SDA D2 , SCL D1 一定要加這一行還可以正常顯示出來 (0,2); //sda=0 | D3, scl=2 | D4
//  lcd.backlight();
//  lcd.clear();
//  lcd.home();                // At column=0, row=0
 Serial.begin(115200);
 while (!Serial) ; // Needed for Leonardo only
 delay(250);


 //建立WiFi連線
 Serial.println("NTP 網路時鐘");
 Serial.print("Connecting to ");
 Serial.println(ssid);
 WiFi.begin(ssid, pass);


 while (WiFi.status() != WL_CONNECTED) {
   delay(500);
   Serial.print(".");
 }


 Serial.print("IP number assigned by DHCP is ");
 Serial.println(WiFi.localIP());
 Serial.println("Starting UDP");
 Udp.begin(localPort);
 Serial.print("Local port: ");
 Serial.println(Udp.localPort());
 Serial.println("waiting for sync");
 setSyncProvider(getNtpTime);
 setSyncInterval(300);
}


time_t prevDisplay = 0; // 記錄上一次的時間,如果相同,LED就不用更新


void loop()
{


 if (timeStatus() != timeNotSet) {
   if (now() != prevDisplay) { //時間變化就顯示
     prevDisplay = now();   //把目前時間丟給prevDisplay
//      Serial.println(now());
     digitalClockDisplay(); //呼叫顯示時間的副程式
   }
 }
}


void digitalClockDisplay()
{
//    lcd.home();                // At column=0, row=0
 //設定每一位燈號顯示的內容,參數1:燈號,參數2:顯示的數字
//  tm1637.display(0,hour() / 10);  
//  tm1637.display(1,hour() % 10);
//  tm1637.display(2,minute() / 10);
//  tm1637.display(3,minute() % 10);
//==============================================
   // now format the Time variables into strings with proper names for month, day etc
   date = String(year())+"/"+String(months[month()-1])+"/"+String(day())+"   "+String(days[weekday()-1]);
//    date += days[weekday()-1];
//    date += ", ";
//    date += months[month()-1];
//    date += " ";
//    date += day();
//    date += ", ";
//    date += year();


   // format the time to 12-hour format with AM/PM and no seconds
t = String(hour())+":"+twoDigits(minute())+":"+twoDigits(second());
//    t += String(hour());
//    t += ":";
//    if(minute() < 10)  // add a zero if minute is under 10
//      t += "0";
//    t += String( minute());
//    t += ":";
// if (second()<10 span="">
// t +="0";
// t += String(second());
// t += ampm[isPM(local)];


   // Font Demo1
   // create more fonts at http://oleddisplay.squix.ch/
   display.clear();
   display.setTextAlignment(TEXT_ALIGN_LEFT);
//顯示時和分之間的:,如果目前是顯示,就設定下次不顯示
 if(point_flag){
//  tm1637.point(POINT_OFF);


   point_flag = 0;
 }else{
//    tm1637.point(POINT_ON);
//    display.setTextAlignment(TEXT_ALIGN_LEFT);
   display.setFont(ArialMT_Plain_10);
   display.drawString(0, 0, "Prog by LEECW");
   display.setFont(ArialMT_Plain_10);
   display.drawString(0, 10,date);


   point_flag = 1;
 }   


   display.setFont(ArialMT_Plain_24);
   display.drawString(0,32,t);
 display.display();


}


// utility function for digital clock display: prints leading 0
String twoDigits(int digits){
 if(digits < = 10) {
   String i = '0'+String(digits);
   return i;
 }
 else {
   return String(digits);
 }
}
/*-------- NTP 程式碼 ----------*/


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; // NTP server's ip address


 while (Udp.parsePacket() > 0) ; // discard any previously received packets
 Serial.println("Transmit NTP Request");
 // get a random server from the pool
 WiFi.hostByName(ntpServerName, ntpServerIP);
 Serial.print(ntpServerName);
 Serial.print(": ");
 Serial.println(ntpServerIP);
 sendNTPpacket(ntpServerIP);
 uint32_t beginWait = millis();
 while (millis() - beginWait < 1500) {
   int size = Udp.parsePacket();
   if (size > = NTP_PACKET_SIZE) {
     Serial.println("Receive 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];
     return secsSince1900 - 2208988800UL + timeZone * SECS_PER_HOUR;
   }
 }
 Serial.println("No NTP Response :-(");
 return 0; // return 0 if unable to get the time
}


// 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();

}

WEMOS D1 MINI ESP8266 製作 NTP + 網路斷線自動重置裝置

我的同事建議我將 NTP 加上網路斷線偵測可以重新reset的功能