#include "SoftwareSerial.h"             // SoftwareSerial
#include <TinyWireM.h>                  // I2C Master lib for ATTinys which use USI
#include "LiquidCrystal_I2C.h"          // for LCD w/ GPIO MODIFIED for the ATtiny85

// Software Serial
#define Rx PB3
#define Tx PB4
SoftwareSerial SS(Rx, Tx);

// LCD
#define GPIO_ADDR 0x27
#define LCD_chars 20
#define LCD_lines 4
LiquidCrystal_I2C LCD(GPIO_ADDR, LCD_chars, LCD_lines);

// Button
#define BTN PB1

// Variablen
bool BTN_CurrState = false, BTN_PrevState = false, newPage = false, newData = false;
byte page = 0; // Zähler für Seiten

// Timer
unsigned long PrevMillis;
int DebounceInterval = 150;

// MPPT-Variablen
int V;     // mV - Main or channel 1 (battery) voltage
int VPV;   // mV - Panel voltage
int PPV;   // W - Panel power
int I;     // mA - Main or channel 1 battery current
//int IL;    // mA - Load current
//char LOAD;  //  - Load output state (ON/OFF)
//char Relay; //  - Relay state
//char OR;    //  - Off reason
float H19;   // 0.01 kWh - Yield total (user resettable counter)
float H20;   // 0.01 kWh - Yield today
int H21;   // W - Maximum power today
float H22;   // 0.01 kWh - Yield yesterday
int H23;   // W - Maximum power yesterday
//int ERR;   //  - Error code
int CS;    //  - State of operation
//char FW;    //  - Firmware version (16 bit)
//char PID;   //  - Product ID
//char SER#;  //  - Serial number
//char HSDS;  //  - Day sequence number (0..364)
//char MPPT;  //  - Tracker operation mode


void setup() {
  // Software Serial starten
  pinMode(Rx, INPUT);
  SS.begin(19200);

  // I2C starten
  TinyWireM.begin();

  // LCD starten
  LCD.init();
  LCD.backlight();

  // Button initialisieren
  pinMode(BTN, INPUT_PULLUP);

  // Begrüßung ausgeben
  char txtHallo [15] = {"Habe die Ehre!"};
  LCD.setCursor((LCD_chars / 2) - (sizeof(txtHallo) / 2), 1);
  LCD.print(txtHallo);
  delay(1000);
  
  newPage = true;
  UpdateLCD_page();
}


void loop() {
  //ReceiveData(); // Daten vom MPPT empfangen
  CheckBTN(); // Button abfragen (--> Seitenwechsel); Debounce-Zeit: 150ms
  UpdateLCD_page(); // Bei Button-Druck: Seitenwechsel
  UpdateLCD_data(); // Daten kontinuierlich auf LCD ausgeben
}


void ReceiveData() {
  String strLabel, strValue;
  char charValue[10];

  if (SS.available() > 0 && newData == false) { // wenn Daten da sind, dann
    // Label auslesen
    strLabel = SS.readStringUntil("\t");

    if (strLabel != "Checksum") {
      // Wert auslesen & in Array schreiben
      strValue = SS.readStringUntil("\r\n");
      strValue.toCharArray(charValue, sizeof(charValue));

      // Wert umwandeln
      if (strLabel == "V") {
        V = atoi(charValue) / 1000.0;
      }
      if (strLabel == "VPV") {
        VPV = atoi(charValue) / 1000.0;
      }
      if (strLabel == "PPV") {
        PPV = atoi(charValue);
      }
      if (strLabel == "I") {
        I = atoi(charValue) / 1000.0;
      }
      if (strLabel == "H19") {
        H19 = atof(charValue) * 1000;
      }
      if (strLabel == "H20") {
        H20 = atof(charValue) * 1000;
      }
      if (strLabel == "H21") {
        H21 = atoi(charValue);
      }
      if (strLabel == "H22") {
        H22 = atof(charValue) * 1000;
      }
      if (strLabel == "H23") {
        H23 = atoi(charValue);
      }
      if (strLabel == "CS") {
        CS = atoi(charValue);
      }
    } else if (strLabel == "Checksum") {
      // Flag für UpdateLCD setzen
      newData = true;
    }
  }
}

void CheckBTN() {
  if (millis() - PrevMillis >= DebounceInterval) {
    // Button abfragen (LOW = gedrückt)
    BTN_CurrState = digitalRead(BTN) == LOW;

    // Wenn Button gerade LOW ist & vorher HIGH war, dann wurde gedrückt --> "page" umblättern
    if ((BTN_CurrState == true) && (BTN_CurrState != BTN_PrevState)) {
      switch (page) {
        case 0: page = 1; break;
        case 1: page = 0; break;
      }
      // Flag für UpdateLCD_page setzen
      newPage = true;
    }
    // ButtonState aktualisieren
    BTN_PrevState = BTN_CurrState;
    // Timer aktualisieren
    PrevMillis = millis();
  }
}

void UpdateLCD_page() {
  if (newPage == true) {
    // LCD leeren
    LCD.clear();

    switch (page) {
      case 0: // page "Current Values"
        LCD.setCursor(0, 0);
        LCD.print("MPPT:");

        //LCD.setCursor(0, 1);

        // Panel Voltage
        LCD.setCursor(0, 2);
        LCD.print("PnV:");

        // Panel Watt
        LCD.setCursor(0, 3);
        LCD.print("PnW:");

        // Battery Voltage
        LCD.setCursor(10, 2);
        LCD.print("BtV:");

        // Battery Current
        LCD.setCursor(10, 3);
        LCD.print("BtC:");
        break; // Ende case 0

      case 1: // page "History/ Max Values"
        LCD.setCursor(7, 0);
        LCD.print("TDA");

        if (H19 < 100) {
          LCD.setCursor(12, 0);
        } else {
          LCD.setCursor(11, 0);
        }
        LCD.print("YDA");

        if (H19 < 100) {
          LCD.setCursor(17, 0);
        } else {
          LCD.setCursor(16, 0);
        }
        LCD.print("SUM");

        LCD.setCursor(0, 2);
        LCD.print("maxW:");

        LCD.setCursor(0, 3);
        LCD.print("YldWh:");

        LCD.setCursor(19, 3);
        LCD.print("k");

        break; // Ende case 1
    }
    // Flag für UpdateLCD_page zurücksetzen
    newPage = false;
  }
}

void UpdateLCD_data() {
  if (newData == true) {
    switch (page) {
      case 0: // Daten page "Current Values"
        // alte Daten löschen
        LCD.setCursor(6, 0);
        LCD.print("              ");
        LCD.setCursor(5, 2);
        LCD.print("     ");
        LCD.setCursor(16, 2);
        LCD.print("    ");
        LCD.setCursor(5, 3);
        LCD.print("     ");
        LCD.setCursor(16, 3);
        LCD.print("    ");

        // neue Daten ausgeben
        // MPPT-Status
        LCD.setCursor(6, 0);
        switch (CS) {
          case 0: LCD.print("Off"); break;
          case 2: LCD.print("Fault"); break;
          case 3: LCD.print("Bulk"); break;
          case 4: LCD.print("Absorption"); break;
          case 5: LCD.print("Float"); break;
          case 7: LCD.print("Equalize (man)"); break;
          case 245: LCD.print("Starting-Up"); break;
          case 247: LCD.print("Equalize (aut)"); break;
          case 252: LCD.print("Ext. Ctrl"); break;
        }

        // Panel Voltage (VPV)
        if (VPV >= 10) {
          LCD.setCursor(6, 2);
        } else {
          LCD.setCursor(7, 2);
        }
        LCD.print(VPV);

        // Panel Watt (PPV)
        if (PPV >= 100) {
          LCD.setCursor(5, 3);
        } else if (PPV >= 10) {
          LCD.setCursor(6, 3);
        } else {
          LCD.setCursor(7, 3);
        }
        LCD.print(PPV);

        // Battery Voltage (V)
        LCD.setCursor(16, 2);
        LCD.print(V);

        // Battery Current (I)
        if (I >= 0) {
          LCD.setCursor(17, 3);
        } else {
          LCD.setCursor(16, 3);
        }
        LCD.print(I);
        break; // Ende case 0

      case 1: // page "History/ Max Values"
        // alte Daten löschen
        LCD.setCursor(7, 2);
        LCD.print("             ");
        LCD.setCursor(7, 3);
        LCD.print("            ");

        // neue Daten ausgeben
        // max Power today (H21)
        if (H21 >= 100) {
          LCD.setCursor(7, 2);
        } else if (H21 >= 10) {
          LCD.setCursor(8, 2);
        } else {
          LCD.setCursor(9, 2);
        }
        LCD.print(H21);

        // max Power yesterday (H23)
        if (H19 >= 100) { // H19 = Yield total
          if (H23 >= 100) {
            LCD.setCursor(11, 2);

          } else if (H23 >= 10) {
            LCD.setCursor(12, 2);
          } else {
            LCD.setCursor(13, 2);
          }
        } else {
          if (H23 >= 100) {
            LCD.setCursor(12, 2);
          } else if (H23 >= 10) {
            LCD.setCursor(13, 2);
          } else {
            LCD.setCursor(14, 2);
          }
        }
        LCD.print(H23);

        // Yield today (H20)
        if (H21 >= 100) {
          LCD.setCursor(7, 3);
        } else if (H21 >= 10) {
          LCD.setCursor(8, 3);
        } else {
          LCD.setCursor(9, 3);
        }
        LCD.print(H20);

        // Yield yesterday (H22)
        if (H19 >= 100) { // H19 = Yield total
          if (H22 >= 100) {
            LCD.setCursor(11, 3);
          } else if (H22 >= 10) {
            LCD.setCursor(12, 3);
          } else {
            LCD.setCursor(13, 3);
          }
        } else {
          if (H22 >= 100) {
            LCD.setCursor(12, 3);
          } else if (H22 >= 10) {
            LCD.setCursor(13, 3);
          } else {
            LCD.setCursor(14, 3);
          }
        }
        LCD.print(H22);

        // Yield total (H19)
        if (H19 >= 100) {
          LCD.setCursor(16, 3);

        } else if (H19 >= 10) {
          LCD.setCursor(17, 3);
        } else {
          LCD.setCursor(18, 3);
        }
        LCD.print(H19);

        break; // Ende case 1
    }
    // Flag für UpdateLCD_data zurücksetzen
    newData = false;
  }
}
ATTINY8520PU