:::

3. 搭配LCD1602_I2C製作網路時鐘

器材準備

LCD1602加上I2C模塊後,只需要佔用兩個IO,(SDA與SCL),剛好可用在ESP-01上,露天拍賣價格一組約100元上下,(若LCD1602跟I2C轉接板分開買再自己焊接會更便宜,大概70元左右)

LCD1602_I2C在arduino已有LiquidCrystal_I2C函式庫可用,不過在ESP-01上面並不適用,GOOGLE一下網路上已有大德改寫完成,網址如下:

https://github.com/agnunez/ESP8266-I2C-LCD1602

請先下載函式庫解壓到arduino底下的library資料夾,命名為LiquidCrystal_I2C

裡面附了範例程式,先打開arduino主程式,從檔案>>範例>>LiquidCrystal_I2C>>ESP8266I2CLCD

範例程式如下:其中較重要的說明是SDA接到ESP-01的GPIO0;SCL接到GPIO2

  // ESP8266 with 16x2 i2c LCD
  // Compatible with the Arduino IDE 1.6.6
  // Library https://github.com/agnunez/ESP8266-I2C-LCD1602
  // Original Library https://github.com/fdebrabander/Arduino-LiquidCrystal-I2C-library
  // Modified for ESP8266 with GPIO0-SDA GPIO2-SCL and LCD1206 display
  // edit library and change Wire.begin() by Wire.begin(sda,scl) or other GPIO's used for I2C
  // and access from lcd.begin(sda,scl)
  
#include <LiquidCrystal_I2C.h>
#include <Wire.h> 
  
  LiquidCrystal_I2C lcd(0x27,16,2); // Check I2C address of LCD, normally 0x27 or 0x3F
  
  uint8_t heart[8] = {0x0,0xa,0x1f,0x1f,0xe,0x4,0x0};  // example sprite bitmap
  
  void setup()  {
    lcd.begin(0,2);      // In ESP8266-01, SDA=0, SCL=2               
    lcd.backlight();
    lcd.createChar(1, heart);
  }
  
  void loop()  {
    lcd.home();                // At column=0, row=0
    lcd.print("ESP8266");   
    lcd.setCursor(0, 1);
    lcd.print("LiquidCrystalI2C");
    delay(500);
    lcd.setCursor(10, 0);      // At column=10, row=0
    lcd.write(byte(1));
    delay(500);
    lcd.setCursor(10, 0);      // At column=10, row=0
    lcd.print(" ");            // Wipe sprite
  }

再來,開啟另外一個範例程式,檔案>>範例>>ESP8266WIFI>>NTPClient,這是ESP8266取得網路時間的範例檔

程式內容如下:範例是使用電腦序列埠來顯示時間,不過我們這裡是把ESP-01當作主控板,並不會連接到電腦,所以底下那一堆serial.print()都要刪掉,改用lcd.print()

/*

 Udp NTP Client

 Get the time from a Network Time Protocol (NTP) time server
 Demonstrates use of UDP sendPacket and ReceivePacket
 For more on NTP time servers and the messages needed to communicate with them,
 see http://en.wikipedia.org/wiki/Network_Time_Protocol

 created 4 Sep 2010
 by Michael Margolis
 modified 9 Apr 2012
 by Tom Igoe
 updated for the ESP8266 12 Apr 2015 
 by Ivan Grokhotkov

 This code is in the public domain.

 */

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

char ssid[] = "*************";  //  your network SSID (name)
char pass[] = "********";       // your network password


unsigned int localPort = 2390;      // local port to listen for UDP packets

/* Don't hardwire the IP address or we won't get the benefits of the pool.
 *  Lookup the IP address for the host name instead */
//IPAddress timeServer(129, 6, 15, 28); // time.nist.gov NTP server
IPAddress timeServerIP; // time.nist.gov NTP server address
const char* ntpServerName = "time.nist.gov";

const int NTP_PACKET_SIZE = 48; // NTP time stamp is in the first 48 bytes of the message

byte packetBuffer[ NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets

// A UDP instance to let us send and receive packets over UDP
WiFiUDP udp;

void setup()
{
  Serial.begin(115200);
  Serial.println();
  Serial.println();

  // We start by connecting to a WiFi network
  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.begin(ssid, pass);
  
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());

  Serial.println("Starting UDP");
  udp.begin(localPort);
  Serial.print("Local port: ");
  Serial.println(udp.localPort());
}

void loop()
{
  //get a random server from the pool
  WiFi.hostByName(ntpServerName, timeServerIP); 

  sendNTPpacket(timeServerIP); // send an NTP packet to a time server
  // wait to see if a reply is available
  delay(1000);
  
  int cb = udp.parsePacket();
  if (!cb) {
    Serial.println("no packet yet");
  }
  else {
    Serial.print("packet received, length=");
    Serial.println(cb);
    // We've received a packet, read the data from it
    udp.read(packetBuffer, NTP_PACKET_SIZE); // read the packet into the buffer

    //the timestamp starts at byte 40 of the received packet and is four bytes,
    // or two words, long. First, esxtract the two words:

    unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
    unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);
    // combine the four bytes (two words) into a long integer
    // this is NTP time (seconds since Jan 1 1900):
    unsigned long secsSince1900 = highWord << 16 | lowWord;
    Serial.print("Seconds since Jan 1 1900 = " );
    Serial.println(secsSince1900);

    // now convert NTP time into everyday time:
    Serial.print("Unix time = ");
    // Unix time starts on Jan 1 1970. In seconds, that's 2208988800:
    const unsigned long seventyYears = 2208988800UL;
    // subtract seventy years:
    unsigned long epoch = secsSince1900 - seventyYears;
    // print Unix time:
    Serial.println(epoch);


    // print the hour, minute and second:
    Serial.print("The UTC time is ");       // UTC is the time at Greenwich Meridian (GMT)
    Serial.print((epoch  % 86400L) / 3600); // print the hour (86400 equals secs per day)
    Serial.print(':');
    if ( ((epoch % 3600) / 60) < 10 ) {
      // In the first 10 minutes of each hour, we'll want a leading '0'
      Serial.print('0');
    }
    Serial.print((epoch  % 3600) / 60); // print the minute (3600 equals secs per minute)
    Serial.print(':');
    if ( (epoch % 60) < 10 ) {
      // In the first 10 seconds of each minute, we'll want a leading '0'
      Serial.print('0');
    }
    Serial.println(epoch % 60); // print the second
  }
  // wait ten seconds before asking for the time again
  delay(10000);
}

// send an NTP request to the time server at the given address
unsigned long sendNTPpacket(IPAddress& address)
{
  Serial.println("sending NTP packet...");
  // 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();
}

編輯Arduino程式

自己寫程式功力不行,改別人的程式會一點點,所以綜合上面兩個範例,摻在一起作撒尿牛丸了~~

#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
#include <LiquidCrystal_I2C.h>
#include <Wire.h> 
  
LiquidCrystal_I2C lcd(0x27,16,2); // Check I2C address of LCD, normally 0x27 or 0x3F
  
char ssid[] = "無線網路SSID";  //  your network SSID (name)
char pass[] = "無線網路密碼";       // your network password

unsigned int localPort = 2390;      // local port to listen for UDP packets

IPAddress timeServerIP; // time.nist.gov NTP server address
const char* ntpServerName = "time.nist.gov";

const int NTP_PACKET_SIZE = 48; // NTP time stamp is in the first 48 bytes of the message

byte packetBuffer[ NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets

// A UDP instance to let us send and receive packets over UDP
WiFiUDP udp;

void setup()
{
    lcd.begin(0,2);      // In ESP8266-01, SDA=0, SCL=2               
    lcd.backlight();
  WiFi.begin(ssid, pass);
  udp.begin(localPort);
}

void loop()
{
  //get a random server from the pool
  WiFi.hostByName(ntpServerName, timeServerIP); 

  sendNTPpacket(timeServerIP); // send an NTP packet to a time server
  // wait to see if a reply is available
  delay(1000);
  
  udp.parsePacket();
     // We've received a packet, read the data from it
    udp.read(packetBuffer, NTP_PACKET_SIZE); // read the packet into the buffer

    //the timestamp starts at byte 40 of the received packet and is four bytes,
    // or two words, long. First, esxtract the two words:

    unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
    unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);
    // combine the four bytes (two words) into a long integer
    // this is NTP time (seconds since Jan 1 1900):
    unsigned long secsSince1900 = highWord << 16 | lowWord;
    // now convert NTP time into everyday time:
    // Unix time starts on Jan 1 1970. In seconds, that's 2208988800:
    const unsigned long seventyYears = 2208988800UL;
    // subtract seventy years:
    unsigned long epoch = secsSince1900 - seventyYears;

    lcd.clear();
    // print the hour, minute and second:
    lcd.print("UTC+8 time is ");       // UTC is the time at Greenwich Meridian (GMT)
    lcd.setCursor(0,1);
    lcd.print(((epoch  % 86400L ) / 3600) + 8 ); // print the hour (86400 equals secs per day)
    lcd.print(':');
    if ( ((epoch % 3600) / 60) < 10 ) {
      // In the first 10 minutes of each hour, we'll want a leading '0'
      lcd.print('0');
    }
    lcd.print((epoch  % 3600) / 60); // print the minute (3600 equals secs per minute)
    lcd.print(':');
    if ( (epoch % 60) < 10 ) {
      // In the first 10 seconds of each minute, we'll want a leading '0'
      lcd.print('0');
    }
    lcd.print(epoch % 60); // print the second

  // wait ten seconds before asking for the time again
  delay(9000);
}

// send an NTP request to the time server at the given address
unsigned long sendNTPpacket(IPAddress& address)
{
  //Serial.println("sending NTP packet...");
  // 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();
}

把上述程式內容修改無線網路部份後,上傳到ESP-01,(詳細上傳方式請參閱第1章),接著開始來接線了

電路接線圖

本範例的LCD1602模塊電壓是5V,市面上賣的也有3.3V的版本(比5V貴一點點),接上電源時請先確認您的是5V還是3.3V

接好線路,插上變壓器,若網路連線沒問題等待幾秒即會在螢幕上顯示目前時間了


搜尋

錯誤訊息
未知: Array and string offset access syntax with curly braces is deprecated 在檔案中的第 /include/functions.encoding.php 列 40
未知: Array and string offset access syntax with curly braces is deprecated 在檔案中的第 /include/functions.encoding.php 列 40
未知: Array and string offset access syntax with curly braces is deprecated 在檔案中的第 /include/functions.encoding.php 列 73
未知: Function get_magic_quotes_gpc() is deprecated 在檔案中的第 /class/libraries/vendor/xoops/xmf/src/Request.php 列 505
未知: Function get_magic_quotes_gpc() is deprecated 在檔案中的第 /class/libraries/vendor/xoops/xmf/src/Request.php 列 119
未知: Function get_magic_quotes_gpc() is deprecated 在檔案中的第 /class/libraries/vendor/xoops/xmf/src/Request.php 列 505
未知: Function get_magic_quotes_gpc() is deprecated 在檔案中的第 /class/libraries/vendor/xoops/xmf/src/Request.php 列 119
未知: Function get_magic_quotes_gpc() is deprecated 在檔案中的第 /class/libraries/vendor/xoops/xmf/src/Request.php 列 119
未知: Function get_magic_quotes_gpc() is deprecated 在檔案中的第 /class/libraries/vendor/xoops/xmf/src/Request.php 列 119
通知: Undefined variable: disabled 在檔案中的第 /modules/tad_book3/function.php 列 818
警告: str_repeat(): Second argument has to be greater than or equal to 0 在檔案中的第 /modules/tad_book3/function.php 列 818
通知: Undefined variable: disabled 在檔案中的第 /modules/tad_book3/function.php 列 818
通知: Undefined variable: disabled 在檔案中的第 /modules/tad_book3/function.php 列 818
通知: Undefined variable: disabled 在檔案中的第 /modules/tad_book3/function.php 列 818
通知: Undefined variable: disabled 在檔案中的第 /modules/tad_book3/function.php 列 818
通知: Undefined variable: disabled 在檔案中的第 /modules/tad_book3/function.php 列 818
通知: Undefined variable: disabled 在檔案中的第 /modules/tad_book3/function.php 列 818
通知: Undefined variable: disabled 在檔案中的第 /modules/tad_book3/function.php 列 818
通知: Undefined index: bootstrap 在檔案中的第 /modules/tadtools/preloads/core.php 列 68
已棄用
資料庫語法
0.000077 - SET SQL_BIG_SELECTS = 1
0.000774 - SELECT * FROM config WHERE (`conf_modid` = '0' AND `conf_catid` = '1') ORDER BY conf_order ASC
0.000273 - SELECT sess_data, sess_ip FROM session WHERE sess_id = '81gut10f23cqj4difqeiufhnhv'
0.000337 - SELECT * FROM modules WHERE dirname = 'tad_book3'
0.000608 - SELECT COUNT(*) FROM group_permission WHERE (`gperm_modid` = '1' AND (`gperm_groupid` = '3') AND `gperm_name` = 'module_read' AND `gperm_itemid` = '9')
0.000328 - SELECT * FROM config WHERE (`conf_modid` = '9') ORDER BY conf_order ASC
0.000434 - select a.tbsn,a.title,b.author,a.category,a.page,a.paragraph,a.sort from tad_book3_docs as a left join tad_book3 as b on a.tbsn=b.tbsn where a.tbdsn='41'
0.000210 - SELECT tbsn,title FROM tad_book3 ORDER BY sort
0.000337 - SELECT * FROM config WHERE (`conf_modid` = '0' AND `conf_catid` = '5') ORDER BY conf_order ASC
0.000298 - SELECT COUNT(*) FROM banner
0.000140 - SELECT * FROM banner LIMIT 1, 1
0.004113 - UPDATE banner SET impmade = 1277938 WHERE bid = 2
0.000432 - SELECT * FROM config WHERE (`conf_modid` = '0' AND `conf_catid` = '3') ORDER BY conf_order ASC
0.001389 - SELECT DISTINCT gperm_itemid FROM group_permission WHERE gperm_name = 'block_read' AND gperm_modid = 1 AND gperm_groupid IN (3)
0.001003 - SELECT b.* FROM newblocks b, block_module_link m WHERE m.block_id=b.bid AND b.isactive=1 AND b.visible=1 AND m.module_id IN (0,9) AND b.bid IN (1,2,3,4,5,6,7,8,9,10,11,18,15,16,20,19,58,60,61,73,72,74,71,76,75,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,111,109,110,112,113,114,115,116,117,118,119,120,121,122,123) ORDER BY b.weight, m.block_id
0.000907 - SELECT f.*, s.tpl_source FROM tplfile f LEFT JOIN tplsource s ON s.tpl_id=f.tpl_id WHERE (`tpl_tplset` = 'default' AND `tpl_file` = 'system_block_search.tpl') ORDER BY tpl_refid
0.004143 - update tad_book3_docs set `count` = `count`+1 where tbdsn='41'
0.000362 - select * from tad_book3_docs where tbdsn='41'
0.000190 - select * from tad_book3 where tbsn='11'
0.000280 - select mid from modules where dirname='tad_book3'
0.000571 - desc `tad_book3_data_center` `sort`
0.000257 - select `col_sn`,`data_name`,`data_sort`, `data_value` from `tad_book3_data_center` where `mid`= '9' and `col_name`='read_tbdsn_date' and `col_sn`='41' order by `sort` , `data_sort`
0.001559 - select tbdsn,title,content,category,page,paragraph,sort,enable,uid,from_tbdsn from tad_book3_docs where tbsn='11' and `enable`='1' order by category,page,paragraph,sort
0.000991 - select tbdsn,title,category,page,paragraph,sort from tad_book3_docs where tbsn='11' and (`content` != '' or `from_tbdsn` != 0) and enable='1' order by category,page,paragraph,sort
0.000261 - SELECT * FROM modules WHERE dirname = 'tadtools'
0.000303 - SELECT * FROM config WHERE (`conf_modid` = '4') ORDER BY conf_order ASC
0.000189 - select mid from modules where dirname='tad_book3'
0.000431 - desc `tad_book3_data_center` `sort`
0.000229 - select `col_sn`,`data_name`,`data_sort`, `data_value` from `tad_book3_data_center` where `mid`= '9' and `col_name`='video_tbdsn_date' and `col_sn`='41' order by `sort` , `data_sort`
0.000224 - select groupid,name from groups
0.000481 - select * from `tad_book3_files_center` where `col_name`='mp4' and `col_sn`='41' and `kind`='file' order by sort limit 0,1
0.000368 - select `tt_theme`,`tt_use_bootstrap`,`tt_bootstrap_color`,`tt_theme_kind` from `tadtools_setup` where `tt_theme`='school2015'
0.000496 - SELECT f.*, s.tpl_source FROM tplfile f LEFT JOIN tplsource s ON s.tpl_id=f.tpl_id WHERE (`tpl_tplset` = 'default' AND `tpl_file` = 'tadbook3_index.tpl') ORDER BY tpl_refid
0.000297 - SELECT * FROM modules WHERE dirname = 'tad_themes'
0.000288 - SELECT * FROM config WHERE (`conf_modid` = '3') ORDER BY conf_order ASC
0.000183 - select `tt_bootstrap_color` from tadtools_setup where `tt_theme`='school2015'
0.000326 - select * from tad_themes where `theme_name`='school2015'
0.000235 - select conf_value from config where conf_title ='_MD_AM_DEBUGMODE'
0.000326 - select * from tad_themes_blocks where `theme_id`='7'
0.000200 - select mid from modules where dirname='tad_themes'
0.000524 - desc `tad_themes_data_center` `sort`
0.000163 - select `col_sn`,`data_name`,`data_sort`, `data_value` from `tad_themes_data_center` where `mid`= '3' and `col_name`='theme_id' and `col_sn`='7' order by `sort` , `data_sort`
0.000201 - select conf_value from config where conf_name ='allow_register'
0.000330 - select `mid`, `name`, `dirname` from modules where isactive='1' and hasmain='1' and weight!=0 order by weight
0.000204 - SELECT * FROM modules WHERE dirname = 'tad_blocks'
0.000199 - select conf_value from config where conf_title ='_MD_AM_DEBUGMODE'
0.000270 - select count(*) from priv_msgs where `to_userid` ='0' and `read_msg`=0 group by `to_userid`
0.000394 - select `menuid`, `itemname`, `itemurl`, `target`, `icon`, `link_cate_name`, `link_cate_sn`, `read_group` from tad_themes_menu where of_level='0' and status='1' order by position
0.000177 - select `menuid`, `itemname`, `itemurl`, `target`, `icon`, `link_cate_name`, `link_cate_sn`, `read_group` from tad_themes_menu where of_level='11' and status='1' order by position
0.000155 - select `menuid`, `itemname`, `itemurl`, `target`, `icon`, `link_cate_name`, `link_cate_sn`, `read_group` from tad_themes_menu where of_level='14' and status='1' order by position
0.000146 - select `menuid`, `itemname`, `itemurl`, `target`, `icon`, `link_cate_name`, `link_cate_sn`, `read_group` from tad_themes_menu where of_level='15' and status='1' order by position
0.000141 - select `menuid`, `itemname`, `itemurl`, `target`, `icon`, `link_cate_name`, `link_cate_sn`, `read_group` from tad_themes_menu where of_level='17' and status='1' order by position
0.000162 - select `menuid`, `itemname`, `itemurl`, `target`, `icon`, `link_cate_name`, `link_cate_sn`, `read_group` from tad_themes_menu where of_level='16' and status='1' order by position
0.000143 - select `menuid`, `itemname`, `itemurl`, `target`, `icon`, `link_cate_name`, `link_cate_sn`, `read_group` from tad_themes_menu where of_level='18' and status='1' order by position
0.000454 - select a.* from tad_themes_files_center as a left join tad_themes as b on a.col_sn=b.theme_id where a.`col_name`='slide' and b.`theme_name`='school2015'
0.000258 - SELECT * FROM modules WHERE dirname = 'tad_login'
0.000490 - SELECT * FROM config WHERE (`conf_modid` = '14') ORDER BY conf_order ASC
總計: 57
區塊
搜尋: 沒有快取
總計: 1
額外資訊
包含檔案: 204 檔案
使用記憶體: 6991120 bytes
計時
XOOPS 使用 0.138 秒來載入。
XOOPS Boot 使用 0.029 秒來載入。
Module init 使用 0.008 秒來載入。
XOOPS output init 使用 0.039 秒來載入。
Module display 使用 0.032 秒來載入。
Page rendering 使用 0.028 秒來載入。