/*
 * Leonardo ETH IOT Base station
 * 
 * Copyleft Lumir Vanek
 * 
 * Updated: 5.6.2017
 */

#include <Wire.h>
#include <LCD.h>
#include <LiquidCrystal_I2C.h>
#include <Ethernet2.h> // The Leonardo ETH uses a w5500 ethernet controller. The standard ethernet library will fail !
#include <EthernetClient.h>
#include <avr/pgmspace.h>

#define _USE_RTC

#ifdef _USE_RTC
  #include "RTClib.h"
  RTC_DS1307 rtc;
#endif

// I2C bus adresses
#define I2C_LCD1           0x27       // LCD 20x4 driven by HD44780, connected to I2C module PCF8574T (7bit I2C address)
#define I2C_PCF8574_PORT1  0x24       // I2C expander PCF8574P (7bit I2C address)

// My Leonardo ETH have a MAC address printed on a sticker on the PCB
byte mac[] = {
  0x90, 0xA2, 0xDA, 0x10, 0xFF, 0x04
};

// IOT service site information
const char http_ip[] = "10.5.73.x";   
const int http_port = 8080;

/*
 * Display messages and JAX-RS service names are stored to flash (program) memory to save SRAM.
 */
const char msg_welcome[] PROGMEM = "Leonardo Home Brain";
const char msg_fw_version[] PROGMEM = "SW 7.6.2017";
const char msg_test_displ1[] PROGMEM = "123456789ABCEFGH";
const char msg_test_displ2[] PROGMEM = "!@#$%^&*()_-<>{}[]";
const char txt_dm_hms[] PROGMEM = "%02d.%02d. %02d:%02d:%02d";
// JAX-RS Services paths, as defined using @Path anotation in JAVA code
const char get_last_value_txt1[] PROGMEM = "get-last-value-txt1"; // JAX-RS Service that returns Sensor shorted name + value
const char get_last_value_txt2[] PROGMEM = "get-last-value-txt2"; // JAX-RS Service that returns Sensor value + unit
const char get_sensor_name[] PROGMEM = "get-sensor-name";         // JAX-RS Service that returns Sensor name
const char get_last_state_txt[] PROGMEM =  "get-last-state-txt"; // JAX-RS Service that returns Relay state

PROGMEM const char* const string_table[] =
{   
    msg_welcome, msg_fw_version, msg_test_displ1, msg_test_displ2, txt_dm_hms, get_last_value_txt1, get_last_value_txt2, get_sensor_name, get_last_state_txt
};

// Index constants for obtaining Strings from PROGMEM defined above
#define msg_ix_welcome              1
#define msg_ix_fw_version           2
#define msg_ix_test_displ1          3
#define msg_ix_test_displ2          4
#define txt_ix_dm_hms               5
#define svc_ix_get_last_value_txt1  6 
#define svc_ix_get_last_value_txt2  7 
#define svc_ix_get_sensor_name      8
#define svc_ix_get_last_state_txt   9

/*
 * LCD connected via PCF8574T
 */
#define En_pin  2
#define Rw_pin  1
#define Rs_pin  0
#define D4_pin  4
#define D5_pin  5
#define D6_pin  6
#define D7_pin  7
#define BACKLIGHT_PIN    3

// DEM 20486 FGH-PW LCD MODULE
LiquidCrystal_I2C  lcd1(I2C_LCD1, En_pin, Rw_pin, Rs_pin, D4_pin, D5_pin, D6_pin, D7_pin, BACKLIGHT_PIN, POSITIVE);

/*
 * HD44780 LCD User-Defined Graphics
 * http://www.frank4dd.com/howto/rabbit/hd44780lcd-to-rabbit-rcm4010.htm
 */
byte custom_smiley[8] = {
  0x00,    //.....
  0x0A,   //.#.#.
  0x00,   //.....
  0x04,   //..#..
  0x00,   //.....
  0x11,   //#...#
  0x0E,   //.###.
  0x00    //.....
};

byte custom_frowny[8] = {
  0x00,   //.....
  0x0A,   //.#.#.
  0x00,   //.....
  0x04,   //..#..
  0x00,   //.....
  0x0E,   //.###.
  0x11,   //#...#
  0x00    //.....
};

byte custom_temperature[8] = //icon for termometer
{
    B00100,
    B01010,
    B01010,
    B01110,
    B01110,
    B11111,
    B11111,
    B01110
};

byte custom_humidity[8] = //icon for water droplet
{
    B00100,
    B00100,
    B01010,
    B01010,
    B10001,
    B10001,
    B10001,
    B01110,
};

byte custom_whitebox[8] = //icon for void box
{
    B11111,
    B10001,
    B10001,
    B10001,
    B10001,
    B10001,
    B10001,
    B11111,
};

byte custom_menuicon[8] = //icon for menu symbol
{
    B11111,
    B00000,
    B11111,
    B00000,
    B11111,
    B00000,
    B11111,
    B00000,
};

byte custom_degree[8] = //icon for degree symbol
{
    B00110,
    B01001,
    B01001,
    B00110,
    B00000,
    B00000,
    B00000,
    B00000,
};

#define char_smiley      0
#define char_frowny      1
#define char_temperature 2
#define char_humidity    3
#define char_whitebox    4
#define char_menuicon    5
#define char_degree      6

EthernetClient client;

//************* Gargenhouse ************************** 
const char sensorCode_DallasTemp_01[] = "LV_TEMP_01";

//************* Outdoor ****************************** 
const char sensorCode_DallasTemp_02[] = "LV_TEMP_02";

//************* Greenhouse *************************** 
const char sensorCode_DallasTemp_03[] = "LV_TEMP_03";  
const char sensorCode_BMP180Temp_07[] = "LV_TEMP_07";
const char sensorCode_BMP180Pres_02[] = "LV_PRES_02";
const char sensorCode_BMP180Atm_02[] = "LV_ATM_02";
const char sensorCode_BH1750Light_01[] = "LV_LIGH_01";

const char sensorCode_Si7021Hum_02[] = "LV_HUM_02";
const char sensorCode_Si7021Temp_10[] = "LV_TEMP_10";

//************* Basen ******************************** 
const char sensorCode_DallasTemp_04[] = "LV_TEMP_04"; // Air
const char sensorCode_DallasTemp_08[] = "LV_TEMP_08"; // Water
const char sensorCode_DallasTemp_11[] = "LV_TEMP_11"; // Solar panel

//************ AQ Station ***************************
const char sensorCode_Si7021Hum_03[] = "LV_HUM_03";

//************* Testboard **************************** 
const char sensorCode_DallasTemp_06[] = "LV_TEMP_06";
const char sensorCode_BMP180Temp_05[] = "LV_TEMP_05";
const char sensorCode_BMP180Pres_01[] = "LV_PRES_01";
const char sensorCode_BMP180Atm_01[] = "LV_ATM_01";
const char sensorCode_Si7021Hum_01[] = "LV_HUM_01";
const char sensorCode_Si7021Temp_09[] = "LV_TEMP_09";

//************* Relays ************************** 
const char relayCode_01[] = "LV_RELAY_01";
const char relayCode_02[] = "LV_RELAY_02";


// Current LCD display page
int currentPage = 0;

// Buttons connected to PORT C
#define A1 4
#define A2 5
#define A3 6
#define A4 7
#define A5 8
#define NXT 9

/*
 * Content of I2C expander PCF8574P on expand shield
 * 
 * bit 0 - inside small blue LED
 * bit 1 - outside FLUX LED
 * bit 2 - outside FLUX LED
 * bit 3 - outside FLUX LED
 * bit 4 - outside FLUX LED
 * bit 5 - outside FLUX LED
 * bit 6 - Relay LV_RELAY_01 Workroom
 * bit 7 - Relay LV_RELAY_02 Outdoor 
 */
byte port1data = 0x00;

#ifdef _USE_CCS811
  #define I2C_CCS811 0x5B //Default I2C Address
  CCS811 ccs811(I2C_CCS811);
  const char sensorCode_CCS811VOC[] = "LV_VOC_01";
  const char sensorCode_CCS811CO2[] = "LV_CO2_01";
#endif


void setup() 
{
    // Input pins for front panel buttons
    pinMode(A1, INPUT_PULLUP); 
    pinMode(A2, INPUT_PULLUP);  
    pinMode(A3, INPUT_PULLUP);
    pinMode(A4, INPUT_PULLUP); 
    pinMode(A5, INPUT_PULLUP); 
    pinMode(NXT, INPUT_PULLUP);
    
    // Current LCD display page init
    currentPage = 0;
  
    // Open serial communications and wait for port to open:
    Serial.begin(115200);
 
    /* 
     * Initialize PCF8574 - the I²C 8-bit expander 
     */
    setPort1(B00000001, false); // Blue LED ON

    /*
     *  Initialize the LCD
     */
    lcd1.begin(20, 4); // 20 chars, 4 lines
    lcd1.createChar(char_smiley, custom_smiley);
    lcd1.createChar(char_frowny, custom_frowny);
    lcd1.createChar(char_temperature, custom_temperature);
    lcd1.createChar(char_humidity, custom_humidity);
    lcd1.createChar(char_menuicon, custom_menuicon);
    lcd1.createChar(char_degree, custom_degree);
    lcd1.createChar(char_whitebox, custom_whitebox);

        /*
     * Setup RTC
     */
#ifdef _USE_RTC
    rtc.begin();

    if (! rtc.isrunning())
    {
      Serial.println("RTC is NOT running!");
  
      // following line sets the RTC to the date & time this sketch was compiled
      rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
    }

    /*
     * Uncomment to set RTC to compile datetime. Then comment it back and load sketch again 
     * to prevent set wrong datetime after reset !
     */
    //rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
#endif

    /*
     * Hello message
     */
    sayHello();
    /// test1_Port1(1);
    
    /* 
     * Start the Ethernet connection: 
     */
    if (Ethernet.begin(mac) == 0) 
    {
      Serial.println("Failed to configure Ethernet using DHCP");
      // no point in carrying on, so do nothing forevermore:
      for(;;)
        ;
    }

    // give the Ethernet shield a second to initialize:
    delay(1000);
  
    // print your local IP address:
    printIPAddress();

    Serial.println("\nStarting connection to server...");
    if(client.connect(http_ip, http_port))
    {
        Serial.println("Connected to server\n");
    }
    else
    {
      Serial.println("connection failed");
      return false;
    }

    Serial.println();
}

void loop() 
{
    checkConnection();
  
    String line1 = String((char) char_frowny);
    String line2 = String((char) char_frowny);
    String line3 = String((char) char_frowny);
    String line4 = String((char) char_frowny);
    String relay;
    boolean success = true;
    
    /*
     * Relay 1 - bit 6
     */
    success &= getValue(getStringNonPadded(svc_ix_get_last_state_txt), relayCode_01, relay);
    Serial.println("\nRelay 1: ");
    Serial.println(relay);

    if ((relay.charAt(0) == '1') && ((port1data & B01000000) == 0x00))
    {
      port1data |= B01000000; // Switch Relay 1 ON
      Serial.println("\nSwitching Relay 1 ON\n");
    }
    else if ((relay.charAt(0) == '0') && ((port1data & B01000000) != 0x00))
    {
      port1data &= B10111111; // Switch Relay 1 OFF
      Serial.println("\nSwitching Relay 1 OFF\n");
    }

    /*
     * Relay 2 - bit 7
     */
    success &= getValue(getStringNonPadded(svc_ix_get_last_state_txt), relayCode_02, relay);
    Serial.println("\nRelay 2: ");
    Serial.println(relay);

    if ((relay.charAt(0) == '1') && ((port1data & B10000000) == 0x00))
    {
      port1data |= B10000000; // Switch Relay 2 ON
      Serial.println("\nSwitching Relay 2 ON\n");
    }
    else if ((relay.charAt(0) == '0') && ((port1data & B10000000) != 0x00))
    {
      port1data &= B01111111; // Switch Relay 2 OFF
      Serial.println("\nSwitching Relay 2 OFF\n");
    }

    port1data &= B11000000; // Switch Flux LED 1 OFF
    if (digitalRead(A1) == LOW) { port1data |= B00000010; }  
    if (digitalRead(A2) == LOW) { port1data |= B00000100; }  
    if (digitalRead(A3) == LOW) { port1data |= B00001000; }  
    if (digitalRead(A4) == LOW) { port1data |= B00010000; }
    if (digitalRead(A5) == LOW) { port1data |= B00100000; }  
    setPort1(port1data, false);

    
    /*
     * Read current page values and display it on LCD display
     */
    switch (currentPage)
    {
        case 0: // ======================= BMP180 Temp. =============================================================== 
                currentPage = 1;
                
                line1 = String("Teploty BMP180:");
                success &= getValue(getStringNonPadded(svc_ix_get_last_value_txt1), sensorCode_DallasTemp_02, line2); // Outdoor
                success &= getValue(getStringNonPadded(svc_ix_get_last_value_txt1), sensorCode_DallasTemp_04, line3); // Basen air
                success &= getValue(getStringNonPadded(svc_ix_get_last_value_txt1), sensorCode_BMP180Temp_07, line4); // Greenhouse
                break;
                
        case 1: // ======================= Greenhouse ================================================================= 
                currentPage = 2;
                
                if (digitalRead(A1) == LOW)
                {
                  Serial.print(F("\nA1 pressed\n"));
                  line1 = String(F("Teploty Sklenik:  A1"));
                  success &= getValue(getStringNonPadded(svc_ix_get_last_value_txt1), sensorCode_BMP180Temp_07, line2); // Greenhouse BMP180
                  success &= getValue(getStringNonPadded(svc_ix_get_last_value_txt1), sensorCode_Si7021Temp_10, line3); // Greenhouse Si7021
                  success &= getValue(getStringNonPadded(svc_ix_get_last_value_txt1), sensorCode_DallasTemp_03, line4); // Greenhouse Dallas  
                }
                else
                {
                  Serial.print(F("\nA1 NOT pressed\n"));
                  return;
                }
                break;
        
        case 2: // ======================= Basen ====================================================================== 
                currentPage = 3;
                
                if (digitalRead(A2) == LOW)
                {
                  Serial.print(F("\nA2 pressed\n"));
                  line1 = String(F("Teploty Bazen:    A2"));
                  success &= getValue(getStringNonPadded(svc_ix_get_last_value_txt1), sensorCode_DallasTemp_04, line2); // Basen air
                  success &= getValue(getStringNonPadded(svc_ix_get_last_value_txt1), sensorCode_DallasTemp_08, line3); // Basen water
                  success &= getValue(getStringNonPadded(svc_ix_get_last_value_txt1), sensorCode_DallasTemp_11, line4); // Solar Panel
                }
                else
                {
                  Serial.print(F("\nA2 NOT pressed\n"));
                  return;
                }
                
                break;
        case 3: // ======================= Humidity =================================================================== 
                currentPage = 4;
                
                if (digitalRead(A3) == LOW)
                {
                  Serial.print(F("A3 pressed\n"));
                  success &= getValue(getStringNonPadded(svc_ix_get_sensor_name), sensorCode_Si7021Hum_02, line1); // Greenhouse
                  success &= getValue(getStringNonPadded(svc_ix_get_last_value_txt2), sensorCode_Si7021Hum_02, line2);
                  success &= getValue(getStringNonPadded(svc_ix_get_sensor_name), sensorCode_Si7021Hum_03, line3); // AQ Station
                  success &= getValue(getStringNonPadded(svc_ix_get_last_value_txt2), sensorCode_Si7021Hum_03, line4);
                }
                else
                {
                  Serial.print(F("\nA3 NOT pressed\n"));
                  return;
                }
                break;
                  
        case 4: // ======================= Pressure =================================================================== 
                currentPage = 5;
                
                if (digitalRead(A4) == LOW)
                {
                  Serial.print(F("\nA4 pressed\n"));
                  success &= getValue(getStringNonPadded(svc_ix_get_sensor_name), sensorCode_BMP180Pres_01, line1);
                  success &= getValue(getStringNonPadded(svc_ix_get_last_value_txt2), sensorCode_BMP180Pres_01, line2);
                  success &= getValue(getStringNonPadded(svc_ix_get_sensor_name), sensorCode_BMP180Pres_02, line3);
                  success &= getValue(getStringNonPadded(svc_ix_get_last_value_txt2), sensorCode_BMP180Pres_02, line4);
                }
                else
                {
                  Serial.print(("\nA4 NOT pressed\n"));
                  return;
                }
                break;

        case 5: // ======================= Testboard ================================================================== 
                currentPage = 0;
                
                if (digitalRead(A5) == LOW)
                {
                  Serial.print(F("\nA5 pressed\n"));
                  //success &= getValue(getStringNonPadded(svc_ix_get_last_value_txt1), sensorCode_Si7021Temp_09, line1); // Testboard
                  success &= getValue(getStringNonPadded(svc_ix_get_last_value_txt1), sensorCode_BMP180Temp_05, line2); // Testboard
                  success &= getValue(getStringNonPadded(svc_ix_get_last_value_txt1), sensorCode_DallasTemp_06, line3); // Testboard
                  //success &= getValue(getStringNonPadded(svc_ix_get_last_value_txt1), sensorCode_Si7021Hum_01, line4);  // Testboard
                }
                else
                {
                  Serial.print(F("\nA5 NOT pressed\n"));
                  return;
                }
                break;
    }

    if (!success)
    {
        clearLCD(lcd1);
        lcd1.print("Error !");
    }
    
    clearLCD(lcd1);
    lcd1.print(getStringPadded(msg_ix_welcome));
    
#ifdef _USE_RTC
    printRTC(1);
#endif
    printPort1(3);
    delay(5000);  // wait for a 5 seconds
    
    clearLCD(lcd1);
    
    /*if(currentPage == 3)
    {
       lcd1.setCursor(18, 0);
       lcd1.print("A2");
    }
    else*/
    if(currentPage == 4)
    {
       lcd1.setCursor(18, 0);
       lcd1.print("A3");
    }
    else if(currentPage == 5)
    {
       lcd1.setCursor(18, 0);
       lcd1.print("A4");
    }
    
    lcd1.setCursor(0, 0);
    lcd1.print(line1);
    lcd1.setCursor(0, 1);
    lcd1.print(line2);
    lcd1.setCursor(0, 2);
    lcd1.print(line3);
    lcd1.setCursor(0, 3);
    lcd1.print(line4);     
   
    /**
     * Wait 8 sec
     */
     for (int i = 0; i < 32; i++)
     {
        delay(250);                       // wait for a 8 seconds

        if (digitalRead(NXT) == LOW)
        { 
          Serial.print(F("NXT pressed\n"));

          lcd1.setCursor(16, 0);
          lcd1.print("Next");
       
          break;
        }
     }   
}

/*
 * Clear given LCD: 
 *
 */
void clearLCD(LiquidCrystal_I2C & lcd)
{
    lcd.clear();
    lcd.home();
}

/*
 *  Hello message and 3 beeps
 */
void sayHello()
{
    clearLCD(lcd1);
    lcd1.print(getStringPadded(msg_ix_welcome));
    lcd1.setCursor(0, 1); 
    lcd1.print(getStringPadded(msg_ix_fw_version));

#ifdef _USE_RTC
    printRTC(2);
#endif
    
    for(int i = 0; i < 5; i++)
    {
      lcd1.backlight();
      delay(250);
      lcd1.noBacklight();
      
      setPort1(B00000010, false);
      delay(200);
      setPort1(B00000100, false);
      delay(200);
      setPort1(B00001000, false);
      delay(200);
      setPort1(B00010000, false);
      delay(200);
      setPort1(B00100000, false);
      delay(200);
      setPort1(B00000000, false);

      if (digitalRead(NXT) == LOW)
      { 
          break;
      }
    }

    lcd1.backlight();

    
}

#ifdef _USE_RTC

void printRTC(int row)
{
    char buf[25];
    DateTime now = rtc.now();
        
    sprintf(buf, "%02d.%02d. %02d:%02d:%02d", now.day(), now.month(), now.hour(), now.minute(), now.second());
    String dateTime = String(buf);
    
    lcd1.setCursor(0, row);
    lcd1.print(dateTime);
}

#endif
void printIPAddress()
{
    Serial.print("My IP address: ");
    lcd1.setCursor(0, 2);
    lcd1.print("IP: ");
    
    for (byte thisByte = 0; thisByte < 4; thisByte++) 
    {
      // print the value of each byte of the IP address:
      Serial.print(Ethernet.localIP()[thisByte], DEC);
      
      String s(Ethernet.localIP()[thisByte]);
      lcd1.print(s); 

      if (thisByte < 3)
      {
        Serial.print(".");
        lcd1.print(".");
      }
    }
    Serial.println();
}

void checkConnection()
{
    // if there are incoming bytes available
    // from the server, read them and print them:
    if (client.available()) {
      char c = client.read();
      Serial.print(c);
    }
  
    if (!client.connected()) 
    {
      client.stop();
      delay(1000);
      Serial.println("\nReconnecting to server...");
      if(client.connect(http_ip, http_port))
      {
          Serial.println("Connected to server\n");
      }
      else
      {
        Serial.println("connection failed");
        return false;
      }
    }
    delay(1000);
}

/*
 * Get message stored to flash (program) memory, padded to LCD lime size
 */
const char* getStringPadded(int index)
{
    static char buffer[21];    // make sure this is large enough for the largest string it must hold
    strcpy_P(buffer, (char*) pgm_read_word(&(string_table[index-1]))); // Necessary casts and dereferencing, just copy. 

    while (strlen(buffer) < 20)
    {
      int len = strlen(buffer);
      buffer[len] = ' ';
      buffer[len+1] = 0x00;
    }

    return buffer;
}

/*
 * Get message stored to flash (program) memory
 */
const char* getStringNonPadded(int index)
{
    static char buffer[25];    // make sure this is large enough for the largest string it must hold
    strcpy_P(buffer, (char*) pgm_read_word(&(string_table[index-1]))); // Necessary casts and dereferencing, just copy. 

    return buffer;
}

/*
 * Append spaces to rest of line to fit LCD 20 chars
 */
String padRight(String &line)
{
    while(line.length() < 20) { line = line + " "; }
    return line;
}

/*
 * Return byte as String representation
 */
void getByteAsString(String &_string, byte _byte)
{
    String binary = String(_byte, BIN);
    while(binary.length() < 8) { binary = "0" + binary; }

    binary.replace('0', (char) char_whitebox);
    binary.replace('1', (char) 0xff);

    String hexa = String(_byte, HEX);
    while(hexa.length() < 2) { hexa = "0" + hexa; }
    
    _string = _string + binary + " 0x" + hexa;
}

/**
 * Get last value of given sensor from IOT service
 */
boolean getValue(const char* service, const char * sensorCode, String &dest)
{
    int nTry = 5;
    bool success;
    do
    {
      success = tryGetValue(service, sensorCode, dest);
      if (!success) { checkConnection(); }
    } while (success == false && nTry-- > 0);

    return success;
}

/**
 * Get last value of given sensor from IOT service
 */
bool tryGetValue(const char* service, const char * sensorCode, String &dest)
{
    setPort1(port1data | B00000001, false); // Blue LED ON
  
    bool success = false; 
    
     
    String url = String(F("/IotService/rest/"));
    url += service;
    url += "?code=";
    url += sensorCode;

    Serial.println("GET data to URL: ");
    Serial.println(url);

    // Make an HTTP GET request
    
    /*Serial.println("GET ") << url << F(" HTTP/1.1\r\n");
    Serial.println("Host: ") << http_ip << F("\r\n"); 
    Serial.println("Connection: close\r\n");
    Serial.println("User-Agent: FishinoWiFi/1.1\r\n");
    Serial.println();*/

    client.print(F("GET "));
    client.print(url);
    client.println(F(" HTTP/1.1"));
    client.print("Host: ");
    client.print(http_ip);
    client.print(F("\r\n")); 
    //client.print("Connection: close\r\n");
    client.println("User-Agent: LeonardoETH/1.1");
    client.println();

    //String reply = String("");
    /*delay(1000);

    if (!client.available())
    {
      delay(2000);
    }*/
    
    while(client.available())
    {
      String line = client.readStringUntil('\r');

      if ((line.charAt(1) == '#') && (line.length() <= 25))
      {      
        //Serial.print(line);
        dest = line.substring(3, line.length());
        success = true;
      }
      //Serial.println(line);
    }

    setPort1(port1data & B11111110, false); // Blue LED OFF
    return success;
}

/**
 * Send value to IOT service
 */
void sendValue(const char * sensorCode, float value)
{  
    String url = "/IotService/rest/insert-value/" + String(sensorCode) + "/" + String(value);

    Serial.print("GET data to URL: ");
    Serial.println(url);

    // Make an HTTP GET request
    client.print( "GET " + url + " HTTP/1.1\r\n");
    client.print("Host: " + String(http_ip) + "\r\n"); 
    //client.print("Connection: close\r\n");
    client.println();
  
    //delay(100);
    while(client.available())
    {
      String line = client.readStringUntil('\r');
      //Serial.print(line);
    }
    
    Serial.println();
    //Serial.println("Connection closed");
}

/*
 * Set global variable 'port1data' to external I2C PCF8574 Port 2
 */
void setPort1(boolean printIt)
{
    Wire.beginTransmission(I2C_PCF8574_PORT1);
    Wire.write(port1data); 
    Wire.endTransmission();
    
    if(printIt) printPort1(2);
}

/*
 * Set global variable 'port2data' to external I2C PCF8574 Port 2
 */
void setPort1(byte _port1data, boolean printIt)
{
    port1data = _port1data;
    Wire.beginTransmission(I2C_PCF8574_PORT1);
    Wire.write(port1data); 
    Wire.endTransmission();
    
    if(printIt) printPort1(3);
}

/*
 * Print external I2C PCF8574 Port 2 (global variable 'port1data')
 */
void printPort1(int row)
{
     String _string = String("P1:"); 
     getByteAsString(_string, port1data);
     lcd1.setCursor(0, row);
     lcd1.print(padRight(_string));
}

/*
 * Test external I2C PCF8574 Port - play with FLUX LED's
 */
void test1_Port1(unsigned int nCount)
{
    while(/*(getKey() == 0) &&*/ (nCount-- > 0))
    {
       port1data = 0x00;
       do
       {
         port1data = port1data << 1;
         port1data |= B00000001;
                 
         setPort1(true);
         delay(500);
         /**if(getKey() != 0) return;*/
                 
       } while(port1data != 0xFF);
                 
       do
       {
         port1data = port1data << 1;
         port1data &= B11111110;
                 
         setPort1(true);
         delay(500);
         /*if(getKey() != 0) return;*/
                 
       } while(port1data != 0x00);
    }
}