/**
  monitor Solis inverter with ESP8266 and 1602 I2C LCD display
     SCL to D1 | SDA to D2 | Gnd to GND |
     Vcc to 3V or VV  (but NOT Vin)
*/

#include <Arduino.h>
#include <LiquidCrystal_I2C.h>
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#include <ESP8266HTTPClient.h>
#include <WiFiClient.h>

// uncomment next two lines for large Display ( 20x4 )
// bool largeDisplay = true;
// LiquidCrystal_I2C lcd(0x3F, 20, 4); // set the LCD I2C address to 0x3F (typical for 20x4)

// uncomment for small display ( 16x2 )
bool largeDisplay = false;
LiquidCrystal_I2C lcd(0x27, 16, 2); // set the LCD I2C address to 0x27 (typical for 16x2)

// If you don't see anything on the display, there are a few things to check:
// - there is usually an LED on the display, is it on? Check power.
// - the contrast might be too low, there's a screw adjustment on the back
// - it might be very dim - there's usually a link on the LCD's board that enables the backlight
// - You might be using the wrong address - run an I2C scan (plenty of examples online)

int screenWidth;
int screenHeight;
String msg;
<F15>String daily = " No daily power "; // end of day message; initialised for starting with no history

ESP8266WiFiMulti WiFiMulti;

// We will use a simple spinner to indicate that the display is waiting using |/-\ 
// We'll define a couple of special characters for the LCD displays.
// Backslash "\" is usually displayed as a Japanese character
// We'll also create a solid fill block for flashing.

byte slash[8] = {
  B00000,
  B10000,
  B01000,
  B00100,
  B00010,
  B00001,
  B00000,
};

byte fill[8] = {
  B11111,
  B11111,
  B11111,
  B11111,
  B11111,
  B11111,
  B11111,
};

char now[8]; // current power display
char day[8]; // daily power display
char spin[] = {124, 47, 45, 0, 1}; // characters used for spinner

void setup() {

  Serial.begin(115200);
  Serial.println();

  if (largeDisplay) {
    screenWidth = 20; // Screen width (in characters)
    screenHeight = 4; // Screen height (in characters)
  }
  else {
    screenWidth = 16; // Screen width (in characters)
    screenHeight = 2; // Screen height (in characters)
  }

  lcd.init();                           // initialize the lcd
  lcd.begin(screenWidth, screenHeight);
  lcd.backlight();
  
  lcd.createChar(0, slash);  // character 0 for backslash
  lcd.createChar(1, fill);   // character 1 for solid block

  lcd.clear();                       // clear the screen
  lcd.setCursor(0, 0);               // move top left
  lcd.print("Alisdair's");         // print intro text
  lcd.setCursor(0, 1);               //
  lcd.print("Solar Inverter");       //

  for (uint8_t t = 4; t > 0; t--) {
    Serial.printf("[SETUP] WAIT %d...\n", t);
    Serial.flush();
    delay(1000);
  }


  WiFi.mode(WIFI_STA);
  // replace your WiFi SSID and PASSWORD below
  WiFiMulti.addAP("SSID", "PASSWORD");
  lcd.clear();
}

void loop() {
  // wait for WiFi connection
  if ((WiFiMulti.run() == WL_CONNECTED)) {

    WiFiClient client;

    HTTPClient http;
    // read status page from Solis WiFi dongle
    // replace your login, password and Solis dongle's IP address below
    if (http.begin(client, "http://user:password@Solis_wifi_IP/status.html")) {  // HTTP

      //   Serial.print("[HTTP] GET...\n");
      // start connection and send HTTP header
      int httpCode = http.GET();  // httpCode will be negative on error
    
      if (httpCode > 0) {

        // file found at server
        if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY) {
          String payload = http.getString();
          //    Serial.println(payload); // uncomment if you want to see the web page code on the console
          lcd.clear();
          Serial.println("==============");
          Serial.println("== SOLAR PANELS ==");
          if (largeDisplay) {
            msg = "--- SOLAR PANELS ---";  
            lcd.setCursor(0, 0);
            lcd.print(msg);
          }
          else {
            // nothing for small display
          }
          //  nowloc is position of NOW power value in Solis status page
          int nowloc = payload.indexOf("var webdata_now_p") ;
          // strip the value from the web page into a new String
          String nowcropped = payload.substring(nowloc + 21, nowloc + 27);
          // and put it in a char array
          nowcropped.toCharArray(now, 7);
          // replace any unwanted characters with spaces
          for (int c = 0; c < 7; c++) {
            if (now[c] == '"' || now[c] == ';' || now[c] < ' '  ) {
              now[c] = ' ';
            }
          }
          Serial.print( "  NOW (W) : " );
          Serial.print( now );

          if (largeDisplay) {
            msg = "   NOW (W) : " + String(now) ;
            lcd.setCursor(0, 2);
            lcd.print(msg);
          }
          else {
            msg = "NOW (W)   " + String(now) ;
            lcd.setCursor(0, 0);
            lcd.print(msg);
          }
          Serial.println();

          //repeat the above for DAILY power value
          int dayloc = payload.indexOf("var webdata_today_e") ;
          String daycropped = payload.substring(dayloc + 23, dayloc + 29);
          daycropped.toCharArray(day, 7);
          for (int c = 0; c < 7 ; c++) {
            if (day[c] == '"' || day[c] == ';' || day[c] < ' ' ) {
              day[c] = ' ';
            }
          }

          Serial.print( "DAY (KWh) : " );
          Serial.print( day );
          if (largeDisplay) {
            msg = " DAY (KWh) : " + String(day) ;
            daily = msg; // keep for no signal
            lcd.setCursor(0, 3);
            lcd.print(msg);
          }
          else {
            msg = "DAY (KWh) " + String(day) ;
            daily = msg; // keep for no signal
            lcd.setCursor(0, 1);
            lcd.print(msg);
          }
          Serial.println();
        }
      } else {
        
        Serial.println("==  SOLAR PANELS  ==");
        Serial.println("==       NO       ==");
        Serial.println("==   CONNECTION   ==");

        if (largeDisplay) {

          msg = "--  SOLAR PANELS  --";
          lcd.setCursor(0, 0);
          lcd.print(msg);

          msg = "==   NO SIGNAL    ==";
          lcd.setCursor(0, 1);
          lcd.print(msg);

          lcd.setCursor(0, 3);
          lcd.print(daily);  // latest daily total
        }
        else {
          msg = "Solar:No signal";
          lcd.setCursor(0, 0);
          lcd.print(msg);

          lcd.setCursor(0, 1);
          lcd.print(daily); // latest daily total
        }
      }
      http.end();
    }
  }
  spinner();
}

// spinner is visual indicator, flash indicates an update
void spinner() {
  for (int loop = 0; loop < 15; loop++)
  {
    for (int c = 0 ; c < 4 ; c++) {
      if (largeDisplay) {
        lcd.setCursor(19, 3);
      }
      else {
        lcd.setCursor(15, 0);
      }
      lcd.print(spin[c]);
      delay(500);
    }
  }
  if (largeDisplay) {
    lcd.setCursor(19, 3);
  }
  else {
    lcd.setCursor(15, 0);
  }
  lcd.print(spin[4]);
}
