2017/12/27

Display Temperature on 8-digit 7-segment Max7219 LED Display using DS18B20

Photo



Sketch
/*
pin 13 (D7) is connected to the DataIn 
pin 14 (D5) is connected to the CLK 
pin 15 (D8) is connected to LOAD 

Modified by Befun Hung on Mar. 26, 2017
to correct errors in comments and data displayed
Modified by Befun Hung on Mar. 27, 2017
to add and correct errors in printNumber() by circuits4you.com 
Modified by Befun Hung on Mar. 30, 2017
to display temperature to 8-digits 7-segment module
*/

// 匯入程式庫標頭檔 
#include <OneWire.h>
#include <DallasTemperature.h>
#include "LedControl.h" 

#define DIN 13
#define CLK 14
#define LOAD 15
#define lowLimit 0.0
#define upLimit 37.3
LedControl lc=LedControl(DIN,CLK,LOAD,1);

// Arduino數位腳位2接到1-Wire裝置
#define ONE_WIRE_BUS D4

float myTemp;
long dispTemp;

// 運用程式庫建立物件
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);

void setup(void)
{
   Serial.begin(115200);
   Serial.println("Temperature Sensor");
   // 初始化 
   sensors.begin();

   // Initialize the MAX7219 
   // Serial.println("14CORE TEST CODE FOR LED MAX7219 LED TUBE");
   // Serial.println(" --- Modified by Befun Hung on Mar. 26, 2017");
   lc.shutdown(0,false); // To Enable the Display 
   lc.setIntensity(0,7); // To set the brightness level (0 is min, 15 is max) 
   lc.clearDisplay(0); // To Clear the display register 
  
}

void loop(void)
{
  // 要求匯流排上的所有感測器進行溫度轉換(不過我只有一個) 
  sensors.requestTemperatures();

  // 取得溫度讀數(攝氏)並輸出,
  // 參數0代表匯流排上第0個1-Wire裝置 
  // Serial.println(sensors.getTempCByIndex(0));

  myTemp = (sensors.getTempCByIndex(0));
  if (myTemp<lowLimit) {myTemp=lowLimit;}
  if (myTemp>upLimit)  {myTemp=upLimit;}
  Serial.print(myTemp);
  Serial.print(" C, ");
  dispTemp = (long) round(myTemp * 10);
  printC(dispTemp);
  
  Serial.print(DallasTemperature::toFahrenheit(myTemp));
  Serial.println(" F");
  dispTemp = (long) round(DallasTemperature::toFahrenheit(myTemp) * 10);
  printF(dispTemp);

  delay(1000);
}

void printC( long v)
{
   // Variable value digit 
   int digito6;
   int digito7;
   int digito8;

   // Calculate the value of each digit 
   digito6 = (v % 10) ;
   digito7 = (v / 10 )% 10 ;
   digito8 = (v / 100 )% 10 ;  
  
   // Display the value of each digit in the display 
   lc.setDigit ( 0 , 7 , (byte) digito8, false);
   lc.setDigit ( 0 , 6 , (byte) digito7, true);
   lc.setDigit ( 0 , 5 , (byte) digito6, false);
   lc.setRow(0, 4, B1001110);
}

void printF( long v)
{
   // Variable value digit 
   int digito2;
   int digito3;
   int digito4;

   // Calculate the value of each digit 
   digito2 = (v % 10) ;
   digito3 = (v / 10 )% 10 ;
   digito4 = (v / 100 )% 10 ;  
  
   // Display the value of each digit in the display 
   lc.setDigit ( 0 , 3 , (byte) digito4, false);
   lc.setDigit ( 0 , 2 , (byte) digito3, true);
   lc.setDigit ( 0 , 1 , (byte) digito2, false);
   lc.setRow(0, 0, B1000111);
}

2017/11/06

GPS Synchronized RTC Clock


/*
* BuildAGPSControlledClock20171106.ino modified by Befun Hung 
* -----------------------------------------------------------
* MyFeatherGPS.ino
*
* GPS-driven, digital clock with local and UTC time on 7-segment LED display.
*
* Author: Bruce E. Hall, W8BH
* Date: 01 May 2016
* Hardware: ATMEL 32u4 microcontroller on Adafruit "Feather" board
* Maxim DS 3212 real-time-clock on Adafruit Featherwing
* 4-digit 7-segment displays mounted on Adafruit I2C backpacks
* GPS module (Adafruit Ultimate GPS or UBLOX Neo-6M module)
* Software: Arduino 1.6.5
* Code size: about 17200 bytes
*
* The system keeps time in UTC, supplied by GPS and/or RTC when GPS is unavailable.
* Local time automatically tracks daylight saving time for selected time zone.
* USB serial port outputs time and GPS/RTC status messages.
* Dual display (local & UTC) very handy for a ham radio shack.
*
*/

#include <TimeLib.h>                    // github.com/PaulStoffregen/Time
#include <TinyGPS.h>                   // arduiniana.org/libraries/TinyGPS/
#include <Wire.h>                       // needed for I2C
#include <SoftwareSerial.h>
#include <DS1307RTC.h>                  // github.com/PaulStoffregen/DS1307RTC

#define TIME_SYNC_INTERVAL 180          // 180 seconds = 3 min between GPS time sync attempts
#define CYCLES_PER_RTC_SYNC 20          // 20 cycles = 1 hr between gps->rtc sync

#define RXPin 2
#define TXPin 3
#define ConsoleBaud 115200
#define GPSBaud 9600

// #define SerialGPS Serial1            // use hardware UART on 32u4 Feather
SoftwareSerial SerialGPS(RXPin, TXPin);

// Select timeZone offset from UTC:
const int timeZone = 8;                 // Taipei, Taiwan

TinyGPS gps;                           // gps string-parser object
time_t prevDisplay = 0;                 // when the digital clock was displayed
int cyclesUntilSync = 0;                // counts number of cycles until RTC sync
time_t dstStart = 0;                    // start of DST in unix time
time_t dstEnd = 0;                      // end of DST in unix time
bool gpsLocked = false;                 // indicates recent sync with GPS
int currentYear = 0;                    // used for DST

void setup()
{
   Serial.begin(ConsoleBaud);            // start USB serial output
   SerialGPS.begin(GPSBaud);            // open UART connection to GPS
   //manualTimeSet();                   // allow temporary time setting before gps kicks in
   setSyncProvider(timeSync);           // specify time setting routine
   setSyncInterval(TIME_SYNC_INTERVAL); // periodically get time from gps/rtc
}

//
// Here are the basic clock routines:
// "Loop" updates the display when a new time is available
// "TimeSync" keeps system time in UTC, and updates it with GPS data.
// If GPS is unavailable, time is updated from RTC chip instead.
//

void loop()
{
   while (SerialGPS.available())         // look for GPS data on serial port
   {
      int c=SerialGPS.read();            // get one character at a time
      gps.encode(c);                     // and feed it to gps parser
   }
   if (timeStatus()!= timeNotSet)        // wait until time is set by sync routine
   {
      if (now() != prevDisplay)          // dont update display until time changes
      {
         prevDisplay = now();            // save current time
         updateDST();                    // check DST status
         displayLocalTime();             // show local time on LEDs
         displayUTC();                   // show UTC time on LEDs
         printTime(prevDisplay);         // send time to USB serial port
      }
   }
}

time_t timeSync()
// will change global variable "gpsLocked" according to ability to sync with GPS
// returns time as UTC
{
   tmElements_t tm;
   time_t t = 0;
   int yr;
   unsigned long fixAge;
   
   gpsLocked = false;                               // assume failure to read GPS
   gps.crack_datetime(&yr, &tm.Month, &tm.Day,      // get UTC time from GPS
      &tm.Hour, &tm.Minute, &tm.Second,
      NULL, &fixAge);
      
   if (fixAge==TinyGPS::GPS_INVALID_FIX_TIME)      // GPS online but no satellite fix
   {
      Serial.println("\nNo GPS fix, using RTC");
      t = RTC.get();                                // ...so get from RTC instead
   }
   else if (fixAge > 2000)                          // GPS is offline
   {
      Serial.println("\nGPS offline, using RTC");
      t = RTC.get();                                // ...so get from RTC instead
   }
   else                                             // GPS time is good
   {
      gpsLocked = true;                             // flag success
      tm.Year = yr-1970;                            // convert calendar years to unix years
      Serial.print("\nGPS time OK, ");
      Serial.print(gps.satellites());
      Serial.print(" sats. ");
      if (cyclesUntilSync <=0)                      // time to update RTC yet?
      {
         if (RTC.write(tm))                         // update real-time clock chip
            Serial.print("RTC updated.");
         else Serial.print("RTC update failed.");
         cyclesUntilSync = CYCLES_PER_RTC_SYNC;     // reset counter
      }
      Serial.println();
      t = makeTime(tm);                             // convert to time_t
   }
   cyclesUntilSync --;                              // count-down another cycle
   return t;                                        // return unix time in UTC
}

void manualTimeSet()
// Use this routine to manually set the clock to a specific UTC time.
// Set each tm element to desired value
// Since time is automatic set from gps, this routine is mainly for debugging
{
   tmElements_t tm;
   tm.Year = 2016 - 1970;                           // Year in unix years
   tm.Month = 11;
   tm.Day = 6;
   tm.Hour = 5;
   tm.Minute = 59;
   tm.Second = 30;
   RTC.write(tm);                                   // set RTC to desired time
}

//
// The following routines support automatic daylight saving time adjustment
//
// Daylight Saving Time constants:
// Please set according to current DST transition times
//
// define DST start value same as end value to disable DST time
//
#define DST_START_WEEK 2                            // Second Sunday
#define DST_START_MONTH 3                           // in March
#define DST_START_HOUR 7                            // at 2AM EST = 0700 UTC
#define DST_END_WEEK 1                              // First Sunday
#define DST_END_MONTH 11                            // in November
#define DST_END_HOUR 6                              // at 2AM EDT = 0600 UTC

time_t timeChange(int hr, int wk, int mo, int yr)
// returns unix time of DST transition according to hr, wk, mo, and yr
// Routine first calculates time on first day of month, at specified time
// then adds number of days to first Sunday, then number of additional weeks
{
   tmElements_t tm;                                 // set up time elements struct
   tm.Year = yr - 1970;                             // need unix year, not calendar year
   tm.Month = mo;
   tm.Day = 1;                                      // start on first day of month
   tm.Hour = hr;                                    // use UTC hour, not local hour
   tm.Minute = 0;
   tm.Second = 0;
   time_t t = makeTime(tm);                         // convert to unix time
   int daysTillSunday = (8 - weekday(t)) % 7;       // how many days until first Sunday?
   t += (daysTillSunday + (wk-1)*7)*SECS_PER_DAY;   // adjust time for additional days
   return t;
}

void printDST()
// debug function to show DST start & end times
{
   Serial.print("DST starts at ");
   printTime(dstStart);
   Serial.print("DST ends at ");
   printTime(dstEnd);
}

void initDST(int yr)
// establish start and end times for DST in a given year. Call at least once a year!
{
   dstStart = timeChange(DST_START_HOUR, DST_START_WEEK, DST_START_MONTH, yr);
   dstEnd = timeChange(DST_END_HOUR, DST_END_WEEK, DST_END_MONTH, yr);
}

void updateDST()
{
   int yr = year();                                // what year is it?
   if (yr != currentYear)                          // a new year started, need new DST info
   {
      initDST(yr);                                 // find DST times for new year
      currentYear = yr;                            // save current year
   }
}

bool isDST(time_t t)                               // returns TRUE if time is in DST window
{
   return ((t >= dstStart) && (t < dstEnd));
}

time_t localTime()                                 // returns local time, adjusted for DST
{
   time_t t = now();                               // get UTC time
   if (isDST(t)) t += SECS_PER_HOUR;               // add DST correction
   t += (timeZone * SECS_PER_HOUR);                // add timeZone correction
   return t;
}

//
// The following routine support time display
//

void printTime(time_t t)
// send time string to serial monitor
{
   Serial.print(hour(t));
   printDigits(minute(t));
   printDigits(second(t));
   Serial.print(" ");
   Serial.print(month(t));
   Serial.print("/");
   Serial.print(day(t));
   Serial.print("/");
   Serial.print(year(t));
   Serial.println(" UTC");
}

void printDigits(int digits)
// utility function for digital clock display: prints preceding colon and leading 0
{
   Serial.print(":");
   if(digits < 10)
   Serial.print('0');
   Serial.print(digits);
}

// display UTC time on display device
void displayUTC()
{
   Serial.print(hour());
   Serial.print(":");
   Serial.print(minute());
   Serial.print(":");
   Serial.print(second());
   Serial.println(" utc");
}

// display local time on display device
void displayLocalTime()
{
   time_t t = localTime();                         // convert system time (UTC) to local time

   Serial.print(hour(t));
   Serial.print(":");
   Serial.print(minute(t));
   Serial.print(":");
   Serial.print(second(t));
   Serial.println(" Local");
}

2017/05/14

NTP Server Synchronized LCD DS1307 RTC Clock


Requirements:

1. Arduino Uno R3
2. RTC (DS1307) Sensor Shield
3. W5100 Ethernet Shield
4. LCD Keypad Shield

Schetch:

/*
 * NTPSynchronizedRTC20170514.ino
 */

#include <SPI.h>
#include <Ethernet.h>
#include <EthernetUdp.h>
#include <Time.h>
#include <Wire.h>  
#include <DS1307RTC.h>  // a basic DS1307 library that returns time as a time_t
#include <LiquidCrystal.h>
LiquidCrystal lcd(8,9,4,5,6,7);

char dayOfWeek[9][4] = {"", "SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT"};
time_t t;
int displayAtSecond;

// timeZoneOffset = (Time Zone) * 3600L eg. (+8) * 3600L = 28800L for Taipei, Taiwan
const long timeZoneOffset = 28800L; 
// sync to NTP server every "ntpSyncInterval" seconds, set to 1 hour or more to be reasonable
unsigned long ntpSyncInterval = 3600;
// adjust the sync latency with computer NTP client in seconds 
unsigned long syncLatency = 0;

// Enter a MAC address for your controller bellow.
// Newer Ethernet shields have a MAC address printed on a sticker on the shield
byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
unsigned int localPort = 8888; // local port to listen for UDP packets
IPAddress timeServer(192, 168, 1, 123); // LAN NTP server
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
EthernetUDP Udp;

// keep track of how long ago we updated the NTP server
unsigned long ntpLastUpdate = 0;

void setup()  {
  Serial.begin(115200);
  lcd.begin(16,2);
  lcd.print("*cheaphousetek*");
  lcd.setCursor(0,1);
  setSyncProvider(RTC.get);   // the function to get the time from the RTC
  if(timeStatus()!= timeSet) 
     lcd.print("Unable to sync");
  else
     lcd.print("Sync system time ");
  displayAtSecond = second();
  delay(2000);
  lcd.clear();
  // start Ethernet and UDP
  if (Ethernet.begin(mac) == 0) {
    lcd.setCursor(0,1);
    lcd.print("DHCP failed");
    for (;;);
  }
  Udp.begin(localPort);
}

void loop()
{
  if ((now() - ntpLastUpdate) >= ntpSyncInterval) {
    ntpSyncDS1307();
    // clear seconds displayed once a second when sync is needed, cause seconds blink to notify checking the network status
    lcd.setCursor(9, 1);
    lcd.print("       ");
  }
  
  // for LCD shield to disp date, day of the week, time and temperature once a second
  t = now();
  if (displayAtSecond != second(t)) { 
    digitalClockDisplay(); 
    displayAtSecond = second(t); 
  }
}

void digitalClockDisplay(){
  // digital clock display of the time
  dateDisplay();
  weekdayDisplay();
  timeDisplay();
  // display seconds since last NTP update
  lcd.setCursor(9, 1);
  lcd.print(now()-ntpLastUpdate);
     // Serial.print(now());
     // Serial.print("    ");
     // Serial.println(ntpLastUpdate);
}

void dateDisplay() {
  lcd.setCursor(0,0);
  lcd.print(year(t));
  lcd.print('-');
  if (month(t) < 10) {
    lcd.print('0');
  }
  lcd.print(month(t));
  lcd.print('-');
  if (day(t) < 10) {
    lcd.print('0');
  }
  lcd.print(day(t));
  // lcd.print(' ');
}

void weekdayDisplay() {
  lcd.setCursor(11,0);
  lcd.print(dayOfWeek[weekday()]);
}

void timeDisplay() {
  lcd.setCursor(0,1);
  if (hour(t) < 10) {
    lcd.print('0');
  }
  lcd.print(hour(t));
  lcd.print(':');
  if (minute(t) < 10) {
    lcd.print('0');
  }
  lcd.print(minute(t));
  lcd.print(':');
  if (second(t) < 10) {
    lcd.print('0');
  }
  lcd.print(second(t));
  // lcd.print(' ');
}

void ntpSyncDS1307() {
  sendNTPpacket(timeServer); // send an NTP packet to a time server
  // wait to see if a replay is available
  delay(100);
  if (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 timstamp starts at byte 40 of the received packet and is four bytes,
    // or two words, long. First, extract 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 = 2208988800L;
    // substract seventy years:
    unsigned long epoch = secsSince1900 - seventyYears + timeZoneOffset + syncLatency;
    setTime(epoch);
    RTC.set(epoch);
    ntpLastUpdate = now();
    // clear seconds displayed once ntp sync succeeded
    lcd.setCursor(9, 1);
    lcd.print("       ");
  }
}

// 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 clodk
  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);
  Udp.write(packetBuffer, NTP_PACKET_SIZE);
  Udp.endPacket();
}

2017/02/08

Example Sketches For MakerSwitch Tail ESP8266 WiFi Switch Using Dynamic and Static IP


I have made a batch of wifi controlled switches called MakerSwitch Tail kit built-in with a high quality transformer HLK-PM01 from Hi-Link, a relay circuit, an ESP8266 D1 mini module and a plastic enclosure inspired from PowerSwitch Tail and Sonoff. It use GPIO16 to control the 10A relay in it, so the rated power is 1100W for 110VAC or 2200W for 220VAC. I have run the codes using both dynamic IP and static IP without fail.

Be careful, when re-entering the web page, the status may be wrong. Modify the example code to fit your need.

I have also developed  the sketch built-in the kit for sale in Taiwan acting as MQTT client integrating MQTT protocol and ESP8266 SmartConfig.

Photos





Sketch for Dynamic IP

#include <ESP8266WiFi.h>

const char* ssid = "your-ssid";                     // use ssid of your router
const char* password = "your-password";  // use password for your router

int ledPin = 16; // GPIO16
WiFiServer server(80);

void setup() {
Serial.begin(115200);
delay(10);

pinMode(ledPin, OUTPUT);
digitalWrite(ledPin, LOW);

// Connect to WiFi network
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");

// Start the server
server.begin();
Serial.println("Server started");

// Print the IP address
Serial.print("Use this URL to connect: ");
Serial.print("http://");
Serial.print(WiFi.localIP());
Serial.println("/");

}

void loop() {
// Check if a client has connected
WiFiClient client = server.available();
if (!client) {
return;
}

// Wait until the client sends some data
Serial.println("new client");
while(!client.available()){
delay(1);
}

// Read the first line of the request
String request = client.readStringUntil('\r');
Serial.println(request);
client.flush();

// Match the request

int value = LOW;
if (request.indexOf("/LED=ON") != -1) {
digitalWrite(ledPin, HIGH);
value = HIGH;
}
if (request.indexOf("/LED=OFF") != -1) {
digitalWrite(ledPin, LOW);
value = LOW;
}

// Set ledPin according to the request
//digitalWrite(ledPin, value);

// Return the response
client.println("HTTP/1.1 200 OK");
client.println("Content-Type: text/html");
client.println(""); // do not forget this one
client.println("<!DOCTYPE HTML>");
client.println("<html>");

client.print("Led pin is now: ");

if(value == HIGH) {
client.print("On");
} else {
client.print("Off");
}
client.println("<br><br>");

// use text to control led or switch
// client.println("Click <a href=\"/LED=ON\">here</a> turn the LED on GPIO16 ON<br>");
// client.println("Click <a href=\"/LED=OFF\">here</a> turn the LED on GPIO16 OFF<br>");

// use button to control led or switch
client.println("<a href=\"/LED=ON\"><button>ON</button></a>&nbsp;<a href=\"/LED=OFF\"><button>OFF</button></a></p>");

client.println("</html>");

delay(1);
Serial.println("Client disonnected");
Serial.println("");

}

Sketch for Static IP

#include <ESP8266WiFi.h>

const char* ssid = "your-ssid";                     // use ssid of your router
const char* password = "your-password";  // use password for your router

int ledPin = 16; // GPIO16
WiFiServer server(80);
IPAddress ip(192, 168, 1, 99); // the desired IP address
IPAddress gateway(192, 168, 1, 1); // set gateway to match your network

void setup() {
Serial.begin(115200);
delay(10);

pinMode(ledPin, OUTPUT);
digitalWrite(ledPin, LOW);

Serial.print(F("Setting static ip to : "));
Serial.println(ip);

// Connect to WiFi network
Serial.println();
Serial.println();
Serial.print("Connecting to ");
Serial.println(ssid);

IPAddress subnet(255, 255, 255, 0); // set subnet mask to match your network
WiFi.config(ip, gateway, subnet);

WiFi.begin(ssid, password);

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

// Start the server
server.begin();
Serial.println("Server started");

// Print the IP address
Serial.print("Use this URL to connect: ");
Serial.print("http://");
Serial.print(WiFi.localIP());
Serial.println("/");

}

void loop() {
// Check if a client has connected
WiFiClient client = server.available();
if (!client) {
return;
}

// Wait until the client sends some data
Serial.println("new client");
while(!client.available()){
delay(1);
}

// Read the first line of the request
String request = client.readStringUntil('\r');
Serial.println(request);
client.flush();

// Match the request

int value = LOW;
if (request.indexOf("/LED=ON") != -1) {
digitalWrite(ledPin, HIGH);
value = HIGH;
}
if (request.indexOf("/LED=OFF") != -1) {
digitalWrite(ledPin, LOW);
value = LOW;
}

// Set ledPin according to the request
//digitalWrite(ledPin, value);

// Return the response
client.println("HTTP/1.1 200 OK");
client.println("Content-Type: text/html");
client.println(""); // do not forget this one
client.println("<!DOCTYPE HTML>");
client.println("<html>");

client.print("Led pin is now: ");

if(value == HIGH) {
client.print("On");
} else {
client.print("Off");
}
client.println("<br><br>");

// use text to control led or switch
client.println("Click <a href=\"/LED=ON\">here</a> turn the LED on GPIO16 ON<br>");
client.println("Click <a href=\"/LED=OFF\">here</a> turn the LED on GPIO16 OFF<br>");

// use button to control led or switch
// client.println("<a href=\"/LED=ON\"><button>ON</button></a>&nbsp;<a href=\"/LED=OFF\"><button>OFF</button></a></p>");

client.println("</html>");

delay(1);
Serial.println("Client disonnected");
Serial.println("");

}