顯示具有 EPS8266 標籤的文章。 顯示所有文章
顯示具有 EPS8266 標籤的文章。 顯示所有文章

2018年5月6日 星期日

[EPS8266」Node MCU 使用LCD 1602(5V版本),電壓問題解法

之前遇到一個鳥問題,想要使用Node MCU來手驅動LCD 1602時LCD並不正常,

僅有LCD背光亮起,但無法正常顯示內容,查詢後可能是電壓的問題(NODE MCU

,找到一個不需要另外拉線可以使用5V供電的方法。


手上的nodeMCU 為 v3 LoLin (不同版本的NodeMCU腳位可能略有不同)


找了很多資料都需要額外電路,最後發現,其實左邊第三隻腳VV

就是輸出USB供給的電壓(5V),實際量起來大約4.98V,足夠囉。

「nodemcu  lolin」的圖片搜尋結果



2017年3月13日 星期一

[ESP8266] Node MCU + DHT22 (OS X)

最近有個需求如下:

1.需要在工作站偵測溫、溼度。

2.需要用MQTT協定回傳資料。

3.無線傳輸。

4.可以用Arduino IDE來寫程式

因而選用Node MCU 搭配DHT 22 。

以下是Node MCU的連結
http://www.nodemcu.com/index_cn.html

Node MCU 可以用Lua語言進行開發,亦可用Arduino IDE進行開發,本篇使用後者。

本次購買的是 LoLin V3 NodeMcu Lua WIFI Development Board







































































接線圖如下(我買的模組己經裝4.7K做在電路板上了,這只是示意圖)












DHT22 可以運作在3.3 及5V,所以不必擔心電壓問題。

這裡紀錄一個遇到的問題,在程式碼,我指定DHT22 的腳位為2 , 但指的不是D2, 而是GPIO2(D4), 參考下圖(圖片取自網路,如有侵權請來信告知)。

























接下來在Arduino IDE中安裝相關的Lib

Adafruit_Sensor
從以下URL下載ZIP檔
https://github.com/adafruit/Adafruit_Sensor





DHT-sensor-lib

https://github.com/adafruit/DHT-sensor-library

下載此ZIP檔















加入.ZIP程式庫。













找到剛才下載的ZIP檔

























接下來下載pubsubclient
https://github.com/knolleary/pubsubclient






























接下來安裝板子, 打開Arduino的偏好設定,在額外的板子管理員網址加入一行

http://arduino.esp8266.com/stable/package_esp8266com_index.json

按下"好"



























接著到板子管理員














安裝一下ESP8266


















接下來將板子選擇為Node MCU 1.0 (ESP-12E Module)























接下來要安裝一下USB to UART 驅動,它用的不是和Arduino的一樣,需要額外裝,

否則在序列埠中什麼都看不到。

到這個網址,下載驅動程式,安裝完成後一定要"重開機"

http://www.wch.cn/download/CH341SER_ZIP.html

重開機後開啟Arduino ,輸入以下程式碼。每六秒左右,就會送一次溫溼度到MQTT Server

程式碼如下:
#include "DHT.h"
#include <ESP8266WiFi.h>
#include <PubSubClient.h>

#define DHTPIN 2     // what digital pin we're connected to

#define DHTTYPE DHT22   // DHT 22  (AM2302), AM2321

// NOTE: If using a board with 3.3V logic like an Arduino Due connect pin 1
// to 3.3V instead of 5V!
// Connect pin 2 of the sensor to whatever your DHTPIN is
// Connect pin 4 (on the right) of the sensor to GROUND
// Initialize DHT sensor.
// Note that older versions of this library took an optional third parameter to
// tweak the timings for faster processors.  This parameter is no longer needed
// as the current DHT reading algorithm adjusts itself to work on faster procs.
DHT dht(DHTPIN, DHTTYPE);

// Update these with values suitable for your network.
const char* ssid = "your ssid";
const char* password = "your pw";
const char* mqtt_server = "MQTT Server IP";
const char* topic="your Topic";
// WiFi Client 
WiFiClient espClient;
// PubSub Client
PubSubClient client(espClient); long lastMsg = 0; char msg[50]; int value = 0;

void setup() {
  Serial.begin(9600);
  Serial.println("DHTxx test!");
  setup_wifi();
  client.setServer(mqtt_server, 1883);
  client.setCallback(callback);

  dht.begin();
}

void loop() {
  // Wait a few seconds between measurements.
  delay(2000);
 //Check Wi-Fi is Connected 
  if (WiFi.status() != WL_CONNECTED)
  {
    setup_wifi();
  }
  //Check MQTT Connection 
  if (!client.connected()) {
    reconnect();
  }
  client.loop();

  // Reading temperature or humidity takes about 250 milliseconds!
  // Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
  float h = dht.readHumidity();
  // Read temperature as Celsius (the default)
  float t = dht.readTemperature();
  // Read temperature as Fahrenheit (isFahrenheit = true)
  float f = dht.readTemperature(true);

  // Check if any reads failed and exit early (to try again).
  if (isnan(h) || isnan(t) || isnan(f)) {
    Serial.println("Failed to read from DHT sensor!");
    return;
  }

  // Compute heat index in Fahrenheit (the default)
  float hif = dht.computeHeatIndex(f, h);
  // Compute heat index in Celsius (isFahreheit = false)
  float hic = dht.computeHeatIndex(t, h, false);

  Serial.print("Humidity: ");
  Serial.print(h);
  Serial.print(" %\t");
  Serial.print("Temperature: ");
  Serial.print(t);
  Serial.print(" *C ");
  Serial.print(f);
  Serial.print(" *F\t");
  Serial.print("Heat index: ");
  Serial.print(hic);
  Serial.print(" *C ");
  Serial.print(hif);
  Serial.println(" *F");

   long now = millis();
  // read DHT22 sensor every 6 seconds
  if (now - lastMsg > 6000) {
     lastMsg = now;
     String msg=String(h)+","+String(t);
     Serial.println(msg);
     char message[msg.length()];
     msg.toCharArray(message,msg.length());
     //publish sensor data to MQTT broker
    client.publish(topic, message); 
  }
}

void setup_wifi() {
   delay(100);
  // We start by connecting to a WiFi network
    Serial.print("Connecting to ");
    Serial.println(ssid);
    WiFi.begin(ssid, password);
    while (WiFi.status() != WL_CONNECTED) 
    {
      delay(500);
      Serial.print(".");
    }
  randomSeed(micros());
  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
}
// recieve Topic message handler , I don't implements it 
void callback(char* topic, byte* payload, unsigned int length) { } void reconnect() { // Loop until we're reconnected while (!client.connected()) { Serial.print("Attempting MQTT connection..."); // Create a random client ID String clientId = "ESP8266Client-"; clientId += String(random(0xffff), HEX); // Attempt to connect //if you MQTT broker has clientID,username and password //please change following line to if (client.connect(clientId,userName,passWord)) if (client.connect(clientId.c_str())) { Serial.println("connected"); //once connected to MQTT broker, subscribe command if any client.subscribe(topic); } else { Serial.print("failed, rc="); Serial.print(client.state()); Serial.println(" try again in 5 seconds"); // Wait 6 seconds before retrying delay(6000); } } } //end reconnect()


燒錄完成後,建議按一下RST按鈕,讓它重啟。



2016年11月24日 星期四

2016年8月12日 星期五

「ESP8266」STA+SoftAP 心得筆記

1. 當使用Mode 3 (STA+ SoftAP)時,STA無法連進SoftAP。

2.SoftAP 預設只能夠有4個裝置連入,多的就連不上了,但其實他的極限是8個,

   只是必須去改config檔,以下是官網的原文

 ESP8266 soft-AP can connected to 8 stations at most,

 softap_config.max_connection default is 4

http://bbs.espressif.com/viewtopic.php?t=1894

3.SoftAP 本身就是這一個網域的一份子,可以透過UART要求傳連線及資料給網域中的

  其他Client。

4. Mode 3時,STA和SoftAP會在同一個Wi-Fi Channel 中 。(Wi-Fi 有13個Channel)

2016年8月11日 星期四

「ESP8266」Non-OS SDK V.S RTOS SDK

一開始在找Firmware檔案時,發現有二種SDK,找了一些資料才明白差異,紀錄一下。


1. Non-OS SDK

Non-OS SDK 是不基於OS的 SDK,提供 IOT_Demo 和 AT 的編譯

Non-OS SDK 主要使用定時器和回調函數的方式實現各個功能事件的嵌套,

達到特定條件下觸發特定功能函數的目的。Non-OS SDK 使用 espconn 接口

實現網路操作,使用者需要按照 espconn 接口的使用規則進行軟件開發。

如果要透過AT Command,請燒錄這個SDK。


2. RTOS SDK

RTOS SDK 基於 FreeRTOS,在 Github 上開源。

* RTOS 版本 SDK 使用 FreeRTOS 系統,引入 OS 多任務處理的機制,用戶可以使用 

FreeRTOS  的標准接口實現資源管理、循環操作、任務內延時、任務間信息傳遞和同步等面

向任務流程的設計方式。


* RTOS 版本 SDK 的網路操作提供了 BSD Socket API  接口的封裝實現

使用者可以直接按照 Socket API 的使用方式來開發軟件應用,

也可以直接編譯運行其他平台的標准 Socket 應用,有效降低平台切換的學習成本。


* RTOS 版本 SDK 引入了 cJSON 庫,可以更加方便的實現對 JSON 數據包的解析

* RTOS 版本相容 Non-OS SDK 中的 Wi-Fi 接口、Smart Config 接口、

Sniffer 相關接口、系統接口、定時器接口、FOTA 接口和外圍驅動接口,不支持 AT 實現

[EPS8266] 回復AT Command 功能

當你用Arduino燒錄程式後,你會發現,EPS8266 不再回應你的AT COMMAND了。

沒錯,的確如此,因為AT COMMAND的功能被你的Arduino code取代了。

後來我找到方式,其實是可以燒回去的,首先你要找到AT Command Firmware檔。

http://bbs.espressif.com/viewtopic.php?f=46&t=1451

版本 ESP8266_AT_v0.51 基于 ESP8266_NONOS_SDK_V1.5.0

使用這一版本Firmware注意一件事,必須使用新版的ESP8266-01 ,通常是黑色底版。


從當前版本 ESP8266_AT_v0.51 起,AT 固件所需空間增大,無法再使用 4Mbit (512KB) Flash,請使用 8Mbit (1MB) 或以上容量 Flash。


下載下來並解壓縮,內容會如下所示


進入bin才是我們要的內容










接下來開啟readme.txt,會告訴你每一種不容量的版子,你需要燒錄什麼檔案在什麼位置











































以ESP8266-01來看,就是8Mbit(1MB) 512k+512k

燒錄指令如下(需要先入手esptool工具https://github.com/themadinventor/esptool)


sudo esptool.py -p /dev/cu.SLAB_USBtoUART -b 115200 write_flash  0x00000 boot_v1.6.bin 0x01000 ESP8266_NONOS_SDK/bin/at/512+512/user1.1024.new.2.bin 0xfc000 ESP8266_NONOS_SDK/bin/esp_init_data_default.bin 0x7e000 ESP8266_NONOS_SDK/bin/blank.bin 

接著等待它跑

esptool.py v1.0.1
Connecting...
Erasing flash...
Took 0.13s to erase flash block
Wrote 4096 bytes at 0x00000000 in 0.4 seconds (81.1 kbit/s)...
Erasing flash...
Took 2.60s to erase flash block
Wrote 419840 bytes at 0x00001000 in 41.0 seconds (81.9 kbit/s)...
Erasing flash...
Took 0.09s to erase flash block
Wrote 1024 bytes at 0x000fc000 in 0.1 seconds (85.2 kbit/s)...
Erasing flash...
Took 0.15s to erase flash block
Wrote 4096 bytes at 0x0007e000 in 0.4 seconds (85.4 kbit/s)...

Leaving...

整個過程大約30秒。

如果一直顯示無法連接ESP8266,先確認線是否有接好,尤其是GPIO0 必須為GND。

如果還是失敗,先將ESP8266 斷電,重新RUN一次指令,等connecting..出現時立刻上電。

應該這樣幾次就會成功了,建議使用有開關的麵包板供電模組,不要直接用Usb To TTL供電。

2016年7月30日 星期六

[EPS8266] ESP 12E 傳送PH以及溫度資料到ThingSpeak


簡介


ESP 12E 算是EPS 8266 系統列很牛的晶片,也提供比較多的腳位,

找把它拿來替代Arduino取得魚缸中的資料,顯示在LCD上,並傳到ThingSpeak。



PH 2的原因是因為他爬升到穩定需要一點時間,但我20秒才讓他取一次值,

他需要取6次,去頭去尾取中間的平均值,它的取值時間不建議小於800ms,

一般來說校正後放入水中要達到穩要幾分鐘,例如海水缸,它會從0慢慢的爬到

8.x,達到穩定後,水中的PH值改變時,它就能夠快速反應。(當然你如果從8變到10,

還是要爬很久,但如果只是0.X的變化是很快的)

材料

ESP 12E

PH meter

DS18b20 (防水版本)

1602 I2C LCD

杜邦線

麵包板(小)

麵包板電源模組

DC 9V電源供應器

USB to TTL (燒錄程式碼用)

Wi-Fi  AP (讓ESP 12 可以傳資料到ThingSpeak)

ThingSpeak Account

接線


ESP 12E    麵包板      PH meter   DS18b20     LCD

VCC          3.3V                                                  

GND         GND         GND           GND            GND    USB to TTL

CH_PID   3.3V

GPIO 14                                         Data

GPIO 4                                                                  SDA

GPIO 5                                                                  SCL

ADC (A0)                  Data                            

                     5V           VCC          VCC             VCC

------------以下是燒錄時才要加的------
RX                                                                                       TX                                                                                                                
TX                                                                                       RX

GND      GND

GPIO0   GND

這邊有2個重點,

(1)DS18b20需要搭配1個4.7K電阻,詳情請參考[Arduino]DS18B20 防水溫度計
                         
(2)ESP 12 E 的ADC,支援的類比為10bit,但電壓範為是0~1V,這點

比較特別,一開始輸入2V怎麼都輸出1024,查了很多文才發現這個重點。

When your potentiometer is near 0V it prints 0 and when it reaches 1V it should print 1024.

料來源:

http://randomnerdtutorials.com/esp8266-adc-reading-analog-values-with-nodemcu/

但PH meter output 為0~5V analog data, 所以我只好拿起5顆10K電阻做分壓,

因為我用的不是精密電阻,10顆幾塊錢那種,所以我必須再重新校正一次PH meter,

因為電阻會有誤差,然候這樣子會導致你拿到的可能不是1/5的電壓值,所以重新依據

目前的狀況校正。(手邊沒有其他東西可用時才用這招,不是個好方法)


ThingSpeak


建立一個CHANNEL,使用2個欄位如下所示,並且把API KEY貼到程式碼中。





程式碼



#include <LiquidCrystal_I2C.h>
#include <Wire.h>  
#include <ESP8266WiFi.h>
#include <OneWire.h>
#define SensorPin A0           //pH meter Analog output to Arduino Analog Input 0
#define Offset 0.42           //deviation compensate
#define samplingInterval 20
#define printInterval 800
#define ArrayLenth  40    //times of collection
int pHArray[ArrayLenth];   //Store the average value of the sensor feedback
int pHArrayIndex=0;   
OneWire  ds(14);  // on pin 14 (a 4.7K resistor is necessary)
// replace with your channels thingspeak API key,
String apiKey = "your ThingSpeak API Key";
const char* ssid = "your ssid";
const char* password = "your password";

const char* server = "api.thingspeak.com";
LiquidCrystal_I2C lcd(0x27,16,2); // Check I2C address of LCD, normally 0x27 or 0x3F
static const byte degrees_glyph[] = { 0x00, 0x07, 0x05, 0x07, 0x00 };
WiFiClient client;


void setup()  {
lcd.begin(4,5);      // In ESP8266-01, SDA=0, SCL=2               
lcd.backlight();
// Register the custom symbol...
Serial.begin(115200);
delay(10);
Serial.println();
Serial.println();
Serial.print("Connecting to ");
Serial.println(ssid);

WiFi.begin(ssid, password);

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

void loop()  {
byte i;
byte present = 0;
byte type_s;
byte data[12];
byte addr[8];
float celsius, fahrenheit;
int h=20;

if ( !ds.search(addr)) {
    Serial.println("No more addresses.");
    Serial.println();
    ds.reset_search();
    delay(250);
    return;
  }
  
  Serial.print("ROM =");
  for( i = 0; i < 8; i++) {
    Serial.write(' ');
    Serial.print(addr[i], HEX);
  }

  if (OneWire::crc8(addr, 7) != addr[7]) {
      Serial.println("CRC is not valid!");
      return;
  }
  Serial.println();
 
  // the first ROM byte indicates which chip
  switch (addr[0]) {
    case 0x10:
      Serial.println("  Chip = DS18S20");  // or old DS1820
      type_s = 1;
      break;
    case 0x28:
      Serial.println("  Chip = DS18B20");
      type_s = 0;
      break;
    case 0x22:
      Serial.println("  Chip = DS1822");
      type_s = 0;
      break;
    default:
      Serial.println("Device is not a DS18x20 family device.");
      return;
  } 

  ds.reset();
  ds.select(addr);
  ds.write(0x44, 1);        // start conversion, with parasite power on at the end
  
  delay(1000);     // maybe 750ms is enough, maybe not
  // we might do a ds.depower() here, but the reset will take care of it.
  
  present = ds.reset();
  ds.select(addr);    
  ds.write(0xBE);         // Read Scratchpad

  Serial.print("  Data = ");
  Serial.print(present, HEX);
  Serial.print(" ");
  for ( i = 0; i < 9; i++) {           // we need 9 bytes
    data[i] = ds.read();
    Serial.print(data[i], HEX);
    Serial.print(" ");
  }
  Serial.print(" CRC=");
  Serial.print(OneWire::crc8(data, 8), HEX);
  Serial.println();

  // Convert the data to actual temperature
  // because the result is a 16 bit signed integer, it should
  // be stored to an "int16_t" type, which is always 16 bits
  // even when compiled on a 32 bit processor.
  int16_t raw = (data[1] << 8) | data[0];
  if (type_s) {
    raw = raw << 3; // 9 bit resolution default
    if (data[7] == 0x10) {
      // "count remain" gives full 12 bit resolution
      raw = (raw & 0xFFF0) + 12 - data[6];
    }
  } else {
    byte cfg = (data[4] & 0x60);
    // at lower res, the low bits are undefined, so let's zero them
    if (cfg == 0x00) raw = raw & ~7;  // 9 bit resolution, 93.75 ms
    else if (cfg == 0x20) raw = raw & ~3; // 10 bit res, 187.5 ms
    else if (cfg == 0x40) raw = raw & ~1; // 11 bit res, 375 ms
    //// default is 12 bit resolution, 750 ms conversion time
  }
  celsius = (float)raw / 16.0;
  fahrenheit = celsius * 1.8 + 32.0;
  Serial.print("  Temperature = ");
  Serial.print(celsius);
  Serial.print(" Celsius, ");
  Serial.print(fahrenheit);
  Serial.println(" Fahrenheit");
  static unsigned long samplingTime = millis();
  static unsigned long printTime = millis();
  static float pHValue,voltage;
  if(millis()-samplingTime > samplingInterval)
  {
      pHArray[pHArrayIndex++]=analogRead(SensorPin);
      if(pHArrayIndex==ArrayLenth)pHArrayIndex=0;
      voltage = avergearray(pHArray, ArrayLenth)*5.0/1024;
      pHValue = 3.5*voltage+Offset;
      samplingTime=millis();
  }
  if(millis() - printTime > printInterval)   //Every 800 milliseconds, print a numerical, convert the state of the LED indicator
  {
    Serial.print("Voltage:");
    Serial.print(voltage,2);
    Serial.print("    pH value: ");
    Serial.println(pHValue,2);
    printTime=millis();
  }


if (client.connect(server,80)) { // "184.106.153.149" or api.thingspeak.com
String postStr = apiKey;
postStr +="&field1=";
postStr += String(celsius);
postStr +="&field2=";
postStr += String(pHValue);
postStr += "\r\n\r\n";

client.print("POST /update HTTP/1.1\n");
client.print("Host: api.thingspeak.com\n");
client.print("Connection: close\n");
client.print("X-THINGSPEAKAPIKEY: "+apiKey+"\n");
client.print("Content-Type: application/x-www-form-urlencoded\n");
client.print("Content-Length: ");
client.print(postStr.length());
client.print("\n\n");
client.print(postStr);
lcd.setCursor(0, 0);
lcd.print("Temperature:");
lcd.print(celsius,1);
lcd.setCursor(0, 1);
lcd.print("PH Value:");
lcd.print(pHValue,1);
Serial.print("Temperature: ");
Serial.print(celsius);
Serial.print("PH: ");
Serial.print(pHValue);
Serial.println("send to Thingspeak");
}
client.stop();

Serial.println("Waiting…");
// thingspeak needs minimum 15 sec delay between updates
delay(20000);
}
double avergearray(int* arr, int number){
  int i;
  int max,min;
  double avg;
  long amount=0;
  if(number<=0){
    Serial.println("Error number for the array to avraging!/n");
    return 0;
  }
  if(number<5){   //less than 5, calculated directly statistics
    for(i=0;i<number;i++){
      amount+=arr[i];
    }
    avg = amount/number;
    return avg;
  }else{
    if(arr[0]<arr[1]){
      min = arr[0];max=arr[1];
    }
    else{
      min=arr[1];max=arr[0];
    }
    for(i=2;i<number;i++){
      if(arr[i]<min){
        amount+=min;        //arr<min
        min=arr[i];
      }else {
        if(arr[i]>max){
          amount+=max;    //arr>max
          max=arr[i];
        }else{
          amount+=arr[i]; //min<=arr<=max
        }
      }//if
    }//for
    avg = (double)amount/(number-2);
  }//if
  return avg;
}

執行畫面


























PH 尚在未穩定狀態,所以2.9 並不正確,到達穩定要一點時間 ,目前程式每次執行時間

間隔20秒,對PH取值而言太久,需要再做調整,PH到達穩定約10~20分鐘。

下圖是ThingSpeak上的資料(隔了一天)。

2016年7月24日 星期日

[EPS8266] 用ESP 01 搭配LCD做NTP Client


前言


ESP 01 是較長見的EPS8266 的其中一種封裝,共有八支腳


圖片來源:

腳位定義(網路上取得)





























材料


麵包板電源供應模組(支援3.3V以及5V)

EPS 01

I2C  LCD

杜邦線

USB 轉TTL




接線圖

1.燒錄圖。


2.實際運作腳位圖



實際運作照片






Code


注意一下,這裡的#include <LiquidCrystal_I2C.h> 載入的並不是內建的LiquidCrystal,必須使用這個函式庫
https://github.com/agnunez/ESP8266-I2C-LCD1602


/*

 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 <Wire.h> 
#include <LiquidCrystal_I2C.h>
#include <ESP8266WiFi.h>
#include <WiFiUdp.h>

char ssid[] = "GFI-5F-ID";  //  your network SSID (name)
char pass[] = "gfi8158456";       // your network password
char data[8];
char buffer[2];

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;
LiquidCrystal_I2C lcd(0x27, 16, 2);

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

  // initialize the LCD
  lcd.begin(0,2);  // sda=0, scl=2
  
  // Turn on the blacklight and print a message.
  lcd.backlight();
  lcd.print("NTP Client");
  
  // 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);

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