// Simula comandos do Bluetooth através da porta Serial
// Comandos:
// L -> Liga LED; D -> Desliga LED; R -> Cicla (atual, min, max); C -> Cicla (°C, F)
// P -> Reseta ESP (p/ testar armazenamento, demora pra resetar)

#include <LiquidCrystal_I2C.h>
#include <DHT.h>
#include <Preferences.h>

#define UI unsigned int
#define DHT_TYPE DHT22
#define DHT_PIN 5
#define LED_PIN 4
#define FPS 5

UI now = 0, prev = 0, last_update = 0;
bool celsius = true;
int records = 0;
char dado;
float temp, humd, recs[4];

byte grau[8] = {0b00110, 0b01001, 0b01001, 0b00110,
                0b00000, 0b00000, 0b00000, 0b00000
               };
byte setaBaixo[8] = {0b00000, 0b00100, 0b00100, 0b00100,
                     0b10101, 0b01110, 0b00100, 0b00000
                    };
byte setaCima[8] = {0b00000, 0b00100, 0b01110, 0b10101,
                    0b00100, 0b00100, 0b00100, 0b00000
                   };

LiquidCrystal_I2C lcd(0x27, 16, 2);
DHT dht(DHT_PIN, DHT_TYPE);
Preferences rV;

void setup() {
  Serial.begin(9600);
  pinMode(LED_PIN, OUTPUT);
  Serial.println("OK");
  lcd.init();
  lcd.backlight();
  lcd.createChar(0, grau);
  lcd.createChar(1, setaBaixo);
  lcd.createChar(2, setaCima);
  dht.begin();
  rV.begin("my-app", false);
  if (!rV.isKey("vals")) firstInit();
  initialize();
}

void firstInit() {
  Serial.println("First Initilization");
  float t = dht.readTemperature();
  float h = dht.readHumidity();
  rV.putFloat("minT", t);
  rV.putFloat("maxT", t);
  rV.putFloat("minH", h);
  rV.putFloat("maxH", h);
  rV.putBool("vals", true);
}

void initialize() {
  recs[0] = rV.getFloat("minT", 20.0);
  recs[1] = rV.getFloat("maxT", 20.0);
  recs[2] = rV.getFloat("minH", 50.0);
  recs[3] = rV.getFloat("maxH", 50.0);
  rV.end();
}

void loop() {
  now = millis();
  readBluetooth();
  updateVals();
  printLCD();
  delay(20);
}

void readBluetooth() {
  if (Serial.available()) {
    dado = Serial.read();
    Serial.write(dado);
    writeBluetooth();
  }
}

void writeBluetooth() {
  if (dado == 'L') {
    digitalWrite(LED_PIN, HIGH);
    Serial.println("Led ON via APP");
  } else if (dado == 'D') {
    digitalWrite(LED_PIN, LOW);
    Serial.println("Led OFF via APP");
  } else if (dado == 'C') {
    celsius = !celsius;
    String c = celsius ? "Celsius" : "Fahrenheit";
    String comp = "Temperatura em " + c + " no LCD";
    Serial.println(comp);
  } else if (dado == 'R') {
    records = (records + 1 > 2) ? 0 : records + 1;
    String r = "atuais";
    if (records) r = (records == 1) ? "mínimos" : "máximos";
    String comp = "Valores " + r + " no LCD";
    Serial.println(comp);
  }
  if (dado == 'P') {
    delay(1000);
    ESP.restart();
  }
}

void updateVals() {
  if (now - last_update < 1000) return; // DHT update rate 1s?
  temp = dht.readTemperature();
  humd = dht.readHumidity();
  Serial.printf("*G%.1f,%.1f*\n", temp, humd);
  checkChanges();
  last_update = now;
}

void checkChanges() {
  rV.begin("my-app", false);
  if (temp < recs[0]) {
    recs[0] = temp;
    rV.putFloat("minT", temp);
  }
  if (temp > recs[1]) {
    recs[1] = temp;
    rV.putFloat("maxT", temp);
  }
  if (humd < recs[2]) {
    recs[2] = humd;
    rV.putFloat("minH", humd);
  }
  if (humd > recs[3]) {
    recs[3] = humd;
    rV.putFloat("maxH", humd);
  }
  rV.end();
}

void printLCD() {
  if (now - prev < (1000 / FPS)) return; // FPS
  float t = temp;
  if (records) t = (records == 1) ? recs[0] : recs[1];
  float h = humd;
  if (records) h = (records == 1) ? recs[2] : recs[3];
  lcd.clear();
  lcd.setCursor(0, 0);
  if (records == 1) lcd.print("\x01");
  if (records == 2) lcd.print("\x02");
  if (celsius) lcd.print("Temp.: " + String(t, 1) + "\x08" + "C");
  else lcd.print("Temp.: " + String((int)(t * 9 / 5) + 32) + "F");
  lcd.setCursor(0, 1);
  if (records == 1) lcd.print("\x01");
  if (records == 2) lcd.print("\x02");
  lcd.print("Umid.: " + String(h, 1) + " %");
  prev = now;
}