//Płytka: "FireBeetle-ESP32"
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include "DHT.h"
#include "RTClib.h"
//file system
#include "FS.h"
#include "SD.h"
#include "SPI.h"

#define Version "0.8"
#define WIRE Wire
#define DHTTYPE DHT22
#define DHTPIN 2  //GPIO02    D2
#define SD_CS 5   //GPIO05    D5

#define BTN_UP 33     //GPIO33    D33
#define BTN_ENTER 34  //GPIO34    D34
#define BTN_DOWN 35   //GPIO35    D35
#define VOLT_IN 36    //GPIO36    VP

#define FAN_PIN 14   //GPIO36    D14
#define DHTPIN_L 13  //GPIO36    D13
#define DHTPIN_M 17  //GPIO36    D17
#define DHTPIN_H 16  //GPIO36    D16
// SDA D21
// SCL D22



//Wyświetlacz/Czytnik SD/RTC
//MOSI                    //GPIO23    D23
//SLC                     //GPIO22    D22
//SDA                     //GPIO21    D21
//MISO                    //GPIO19    D19
//SCK                     //GPIO18    D18

const float voltageDividerFactor = 1.51;  //1.56;
const float adcMaxVoltage = 3.3;
const int adcResolution = 4095;

Adafruit_SSD1306 display = Adafruit_SSD1306(128, 32, &WIRE);
DHT dht(DHTPIN, DHTTYPE);
DHT dhtL(DHTPIN_L, DHT22);
DHT dhtM(DHTPIN_M, DHT22);
DHT dhtH(DHTPIN_H, DHT22);

RTC_DS3231 rtc;  // Obiekt dla DS3231
int menuL0 = 10;
int menuL1 = 10;
int menuL2 = 10;

int  sd_error = 0;

float temp = 0.0;
float tempH = 0.0;
float tempHCalc = 0.0;
float lastTempH = 0.0;
float tempM = 0.0;
float tempMCalc = 0.0;
float lastTempM = 0.0;
float tempL = 0.0;
float tempLCalc = 0.0;
float lastTempL = 0.0;

float hum = 0.0;
float humH = 0.0;
float humHCalc = 0.0;
float lastHumH = 0.0;
float humM = 0.0;
float humMCalc = 0.0;
float lastHumM = 0.0;
float humL = 0.0;
float humLCalc = 0.0;
float lastHumL = 0.0;

float heat_idx;

int secs;
int mins;
int hourss;
int dayss;
int months;
int years;

//##########################################################################################################################
int intervalTempIdx = 4;
int intervalTempWrite = 0;
int intervalTempWriteMins = 0;
int intervalTempWriteHours = 0;


int minsToSet = -1;
int hoursToSet = -1;
int intervalToSet = -1;
int adcVoltValue;

float measuredVoltage = 0;
float batteryVoltage = 0;
float quickBatteryVoltage = 0;
float quickBatteryVoltage2 = 0;
float quickBatteryVoltage3 = 0;
float quickBatteryVoltage4 = 0;
float currentBatteryVoltage = 0;
float lastBatteryVoltage = 0;
int battLifeMins = 4000;
int estHours = 0;
int estMins = 0;

int batteryPercent = 0;
int batteryBarWidth = 0;
char dateString[21];
std::string chrgState;

unsigned long previousMillis = 0;
const unsigned long interval = 1000;
unsigned long currentMillis = millis();


String formatNumber(float number, int decimalPlaces = 1) {
  String str = String(number, decimalPlaces);
  str.replace('.', ',');
  if (number < 10) {
    str = "0" + str;
  }
  return str;
}


String leadZero(float number) {
  String str = String(int(number));
  str.trim();
  if (number < 10) {
    str = "0" + str;
  }
  return str;
}



float voltageToPercentage(float voltage) {
  // Reprezentatywne punkty (wolt, procent)
  const int nPoints = 9;
  // Napięcia – uporządkowane malejąco
  float vPoints[nPoints] = { 4.180, 4.115, 4.060, 4.005, 3.951, 3.896, 3.787, 3.514, 2.750 };
  // Odpowiednie poziomy naładowania
  float pPoints[nPoints] = { 100, 95, 90, 80, 70, 60, 50, 10, 0 };
  if (voltage >= vPoints[0])
    return pPoints[0];
  if (voltage <= vPoints[nPoints - 1])
    return pPoints[nPoints - 1];
  for (int i = 0; i < nPoints - 1; i++) {
    if (voltage <= vPoints[i] && voltage > vPoints[i + 1]) {
      // Obliczamy współczynnik interpolacji
      float fraction = (voltage - vPoints[i + 1]) / (vPoints[i] - vPoints[i + 1]);
      // Interpolacja liniowa między punktami
      return pPoints[i + 1] + fraction * (pPoints[i] - pPoints[i + 1]);
    }
  }
  return 0;  // Domyślnie – choć powinno się tu już nie dojść
}

// Funkcja liniowej interpolacji
float interpolate(float x, float x0, float x1, float y0, float y1) {
  return y0 + (x - x0) * (y1 - y0) / (x1 - x0);
}

// Funkcja przeliczająca napięcie (w V) na procent naładowania baterii w trakcie ładowania
float chargeVoltageToPercentage(float voltage) {
  // Przykładowe punkty (napięcie w V rosnąco)
  // Dopasuj je do własnych obserwacji / charakterystyki baterii
  const int nPoints = 10;
  float vPoints[nPoints] = { 3.100, 3.274, 3.621, 3.737, 3.853, 3.968, 4.026, 4.084, 4.142, 4.200 };

  float pPoints[nPoints] = { 0, 1, 5, 10, 30, 60, 80, 95, 98, 100 };


  // 1. Jeśli napięcie jest poniżej najniższego punktu, zwróć minimalny procent
  if (voltage <= vPoints[0]) {
    return pPoints[0];
  }

  // 2. Jeśli napięcie przekracza najwyższy punkt, zwróć maksymalny procent
  if (voltage >= vPoints[nPoints - 1]) {
    return pPoints[nPoints - 1];
  }

  // 3. Znajdź przedział, w którym mieści się zmierzone napięcie
  for (int i = 0; i < nPoints - 1; i++) {
    if (voltage >= vPoints[i] && voltage < vPoints[i + 1]) {
      // Interpolacja liniowa między sąsiednimi punktami
      return interpolate(voltage, vPoints[i], vPoints[i + 1], pPoints[i], pPoints[i + 1]);
    }
  }
  // Nie powinniśmy tu trafić, ale na wszelki wypadek:
  return 0.0;
}

void setup() {
  Serial.begin(9600);
  Wire.begin(21, 22);
  analogSetAttenuation(ADC_11db);

  if (!rtc.begin()) {
    Serial.println("Nie znaleziono DS3231 RTC!");
  }
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);  // Address 0x3C for 128x32
  display.setTextSize(1);
  display.setTextColor(SSD1306_WHITE);
  display.clearDisplay();
  display.setCursor(0, 0);
  display.println("Version:");
  display.setCursor(60, 0);
  display.println(Version);
  display.setCursor(0, 8);
  if (intervalTempIdx == 1) {
    intervalTempWrite = 1;
  } else if (intervalTempIdx == 2) {
    intervalTempWrite = 5;
  } else if (intervalTempIdx == 3) {
    intervalTempWrite = 15;
  } else if (intervalTempIdx == 4) {
    intervalTempWrite = 60;
  } else {
    intervalTempWrite = 360;
  }
  display.println("Interval:");
  display.setCursor(80, 8);
  display.println(intervalTempWrite);
  display.setCursor(90, 8);
  display.println("min");
  display.setCursor(0, 16);

  display.println("DHT22");
  display.setCursor(40, 16);
  display.println("RTC: DS3231");
  display.setCursor(0, 24);
  display.println("Battery: ");
  display.setCursor(55, 24);
  display.println(int(floor(float(battLifeMins) / 60)));
  display.setCursor(70, 24);
  display.println("h");
  display.display();
  delay(500);
  dht.begin();
  dhtL.begin();
  dhtM.begin();
  dhtH.begin();
  pinMode(BTN_UP, INPUT);
  pinMode(BTN_ENTER, INPUT);
  pinMode(BTN_DOWN, INPUT);
  rtc.now();
  DateTime now = rtc.now();
  years = now.year();
  months = now.month();
  dayss = now.day();
  hourss = now.hour();
  mins = now.minute();
  secs = now.second();

  display.clearDisplay();
  display.setCursor(0, 0);
  if (!SD.begin(SD_CS)) {
    display.println("Blad inicjaliz SD!");
    sd_error = 1;
    display.display();
    delay(1000);
  } else {
    Serial.println("Karta SD wykryta!");
  }
  display.setCursor(0, 16);


  if (SD.exists("/TempHumLog.txt")) {
  } else {
    File file = SD.open("/TempHumLog.txt", FILE_APPEND);
    file.println("Data; Godzina; Temp; Humi; Feel,TempH,HumH, TempM, HumM, TempL, HumL");
    file.close();
  }
  sprintf(dateString, "%02d/%02d/%4d   %02d:%02d:%02d", now.day(), now.month(), now.year(), now.hour(), now.minute(), now.second());
  if (SD.exists("/GeneralLog.txt")) {
  } else {
    File file = SD.open("/GeneralLog.txt", FILE_APPEND);
    file.print("Utworzono:  ");
    file.print(dateString);
    file.print(" Wersja: ");
    file.println(Version);
    file.close();
  }
  File file = SD.open("/GeneralLog.txt", FILE_APPEND);
  file.print("Uruchomienie:  ");
  file.print(dateString);
  file.print(" Wersja: ");
  file.println(Version);
  file.close();


  display.display();
  delay(500);

  adcVoltValue = analogRead(VOLT_IN);  // Odczyt wartości ADC z GPIO36
  measuredVoltage = (adcVoltValue * adcMaxVoltage) / adcResolution;
  batteryVoltage = (measuredVoltage * voltageDividerFactor) + 0.1;
  currentBatteryVoltage = batteryVoltage;
  quickBatteryVoltage = batteryVoltage;
  quickBatteryVoltage2 = batteryVoltage;
  quickBatteryVoltage3 = batteryVoltage;
  quickBatteryVoltage4 = batteryVoltage;

  tempH = (dhtH.readTemperature() - 0.16);
  lastTempH = tempH;
  tempM = (dhtM.readTemperature() + 0.08);
  lastTempM = tempM;
  tempL = (dhtL.readTemperature() + 0.0);
  lastTempL = tempL;
  tempHCalc = (tempH + lastTempH) / 2;
  tempMCalc = (tempM + lastTempM) / 2;
  tempLCalc = (tempL + lastTempL) / 2;
  humHCalc = (humH + lastHumH) / 2;
  humMCalc = (humM + lastHumM) / 2;
  humLCalc = (humL + lastHumL) / 2;

  if (isnan(tempH)) {
    lastTempH = 0.0;
    tempH = lastTempH;
  }
  if (isnan(tempM)) {
    lastTempM = 0.0;
    tempM = lastTempM;
  }
  if (isnan(tempL)) {
    lastTempL = 0.0;
    tempL = lastTempL;
  }
  if (isnan(humH)) {
    lastHumH = 0.0;
    humH = lastHumH;
  }
  if (isnan(humM)) {
    lastHumM = 0.0;
    humM = lastHumM;
  }
  if (isnan(humL)) {
    lastHumL = 0.0;
    humL = lastHumL;
  }
  // rtc.adjust(DateTime(2025 , 8, 7, 17, 25, 0));

  //setup END
  //######################################################################################################################################################################
}

void loop() {
  currentMillis = millis();
  display.clearDisplay();

  temp = dht.readTemperature();
  tempH = (dhtH.readTemperature() - 0.16);  //kalibracja
  if (isnan(tempH)) { tempH = lastTempH; }
  tempM = (dhtM.readTemperature() + 0.08);  //kalibracja
  if (isnan(tempM)) { tempM = lastTempM; }
  tempL = (dhtL.readTemperature() + 0.0);
  if (isnan(tempL)) { tempL = lastTempL; }
  hum = dht.readHumidity();
  humH = (dhtH.readHumidity() + 0.0);
  if (isnan(humH)) { humH = lastHumH; }
  humM = (dhtM.readHumidity() - 3.2);  //kalibracja
  if (isnan(humM)) { humM = lastHumM; }
  humL = (dhtL.readHumidity() + 1.7);  //kalibracja
  if (isnan(humL)) { humL = lastHumL; }

  if (intervalTempIdx == 1) {
    intervalTempWrite = 1;
  } else if (intervalTempIdx == 2) {
    intervalTempWrite = 5;
  } else if (intervalTempIdx == 3) {
    intervalTempWrite = 15;
  } else if (intervalTempIdx == 4) {
    intervalTempWrite = 60;
  } else {
    intervalTempWrite = 360;
  }
  // Compute heat index in Celsius (isFahreheit = false)
  heat_idx = dht.computeHeatIndex(temp, hum, false);

  //DATE
  DateTime now = rtc.now();
  years = now.year();
  months = now.month();
  dayss = now.day();
  hourss = now.hour();
  mins = now.minute();
  secs = now.second();
  sprintf(dateString, "%02d/%02d/%4d   %02d:%02d:%02d", now.day(), now.month(), now.year(), now.hour(), now.minute(), now.second());


  if (menuL0 == 10) {
    //  ############# MENU TEMP

    display.setCursor(0, 0);
    display.println(dateString);

    //HUM&TEMP
    //#########################################
    display.setCursor(0, 8);
    display.println("Wilgotnosc:");
    display.setCursor(85, 8);
    display.println(hum);
    display.setCursor(120, 8);
    display.println("%");

    display.setCursor(0, 16);
    display.println("Temperatura:");
    display.setCursor(85, 16);
    display.println(temp);
    display.setCursor(120, 16);
    display.println("C");

    display.setCursor(0, 24);
    display.println("Odczuwalna:");
    display.setCursor(85, 24);
    display.println(heat_idx);
    display.setCursor(120, 24);
    display.println("C");
  }



  //dla indexu: Caution:  > 27*C
  //     Extreme Caution: > 32 *C
  //              Danger: > 40 *C
  //      Extreme Danger: > 52 *C
  //#########################################



  if (menuL0 == 9) {
    if (digitalRead(BTN_ENTER) == HIGH and menuL1 == 10) {
      menuL1 = 9;
    }
    display.setCursor(0, 0);
    sprintf(dateString, "%02d/%02d/%4d   %02d:%02d:%02d", now.day(), now.month(), now.year(), now.hour(), now.minute(), now.second());
    display.println(dateString);
    if (menuL1 == 10) {
      display.setCursor(0, 8);
      display.print("Enter edycja czasu");
      display.setCursor(0, 18);
      display.print("Inwerwal: ");

      if (intervalTempIdx == 1) {
        intervalTempWrite = 1;
      } else if (intervalTempIdx == 2) {
        intervalTempWrite = 5;
      } else if (intervalTempIdx == 3) {
        intervalTempWrite = 15;
      } else if (intervalTempIdx == 4) {
        intervalTempWrite = 60;
      } else {
        intervalTempWrite = 360;
      }
      if (intervalTempWrite >= 60) {
        display.setCursor(60, 18);
        display.print((intervalTempWrite / 60));
        display.setCursor(80, 18);
        display.print("godz");
      } else {
        display.setCursor(60, 18);
        display.print((intervalTempWrite));
        display.setCursor(80, 18);
        display.print("min");
      }
    }
    if (menuL1 == 9) {
      display.setCursor(0, 8);
      delay(100);
      display.print("Ustaw Interwal");
      delay(100);

      if (digitalRead(BTN_ENTER) == HIGH and menuL2 == 10) {
        menuL2 = 9;
      }
      if (menuL2 == 9) {
        display.setCursor(0, 16);
        display.print("Interwal:");
        display.setCursor(64, 16);
        if (intervalToSet == -1) {
          intervalToSet = intervalTempIdx;
        }
        if (intervalToSet == 1) {
          intervalTempWrite = 1;
        } else if (intervalToSet == 2) {
          intervalTempWrite = 5;
        } else if (intervalToSet == 3) {
          intervalTempWrite = 15;
        } else if (intervalToSet == 4) {
          intervalTempWrite = 60;
        } else {
          intervalTempWrite = 360;
        }
        if (intervalTempWrite >= 60) {
          display.setCursor(60, 16);
          display.print((intervalTempWrite / 60));
          display.setCursor(80, 16);
          display.print("godz");
        } else {
          display.setCursor(60, 16);
          display.print((intervalTempWrite));
          display.setCursor(80, 16);
          display.print("min");
        }
        delay(250);

        if (digitalRead(BTN_ENTER) == HIGH) {
          menuL2 = 10;
          intervalTempIdx = intervalToSet;
          intervalToSet = -1;
        }
      }
    }
    if (menuL1 == 8) {
      display.setCursor(0, 8);
      display.print("Ustaw godziny");

      if (digitalRead(BTN_ENTER) == HIGH and menuL2 == 10) {
        menuL2 = 9;
      }

      if (menuL2 == 9) {
        display.setCursor(0, 16);
        display.print("Godzina:");
        display.setCursor(50, 16);
        if (hoursToSet == -1) {
          hoursToSet = now.hour();
        }
        display.print(hoursToSet);
        delay(250);

        if (digitalRead(BTN_ENTER) == HIGH) {
          // tu ustawianie czasu
          menuL2 = 10;
          rtc.adjust(DateTime(now.year(), now.month(), now.day(), hoursToSet, now.minute(), now.second()));
          hoursToSet = -1;
        }
      }
    }


    if (menuL1 == 7) {
      display.setCursor(0, 8);
      display.print("Ustaw minuty");

      if (digitalRead(BTN_ENTER) == HIGH and menuL2 == 10) {
        menuL2 = 9;
      }

      if (menuL2 == 9) {
        display.setCursor(0, 16);
        display.print("Minuta:");
        display.setCursor(45, 16);
        if (minsToSet == -1) {
          minsToSet = now.minute();
        }
        display.print(minsToSet);
        delay(250);

        if (digitalRead(BTN_ENTER) == HIGH) {
          // tu ustawianie czasu
          menuL2 = 10;
          rtc.adjust(DateTime(now.year(), now.month(), now.day(), now.hour(), minsToSet, now.second()));
          minsToSet = -1;
        }
      }

      if (menuL2 == 6) {
        display.setCursor(0, 24);
        display.print("Wyjdz");
        if (digitalRead(BTN_ENTER) == HIGH) {
          menuL2 = 10;
        }
      }
    }
    if (menuL1 == 6) {
      display.setCursor(0, 8);
      display.print("Ustaw sekundy na 0");


      if (digitalRead(BTN_ENTER) == HIGH and menuL2 == 10) {
        menuL2 = 9;
      }

      if (menuL2 == 9) {
        display.setCursor(0, 16);
        display.print("Sekundy = 0");
        delay(250);
        if (digitalRead(BTN_ENTER) == HIGH) {
          menuL2 = 10;
          rtc.adjust(DateTime(now.year(), now.month(), now.day(), now.hour(), now.minute(), 0));
          minsToSet = -1;
        }
      }
    }

    if (menuL1 == 5) {
      display.setCursor(0, 8);
      display.print("Wyjdz");
      if (digitalRead(BTN_ENTER) == HIGH) {
        menuL1 = 10;
      }
    }
  }


  if (menuL0 == 8) {
    display.setCursor(0, 0);
    display.println(dateString);
    display.setCursor(0, 8);
    display.print("Battery");
    display.setCursor(50, 8);
    display.print(batteryPercent);
    display.setCursor(70, 8);
    display.print("%");
    display.setCursor(80, 8);
    display.print("V:");
    display.setCursor(95, 8);
    display.print(batteryVoltage);

    display.setCursor(0, 24);
    if (currentBatteryVoltage >= 4.20) {
      display.print("Full");
      chrgState = "FUL";
    } else if ((lastBatteryVoltage < currentBatteryVoltage && currentBatteryVoltage >= 3.1)|| currentBatteryVoltage >= 4.20) {
      display.print("Charging");
      chrgState = "CHR";
    } else if (lastBatteryVoltage >= currentBatteryVoltage && currentBatteryVoltage >= 2.0) {
      display.print("Discharge");
      chrgState = "DSG";
    } else {
      display.print("xxx");
      chrgState = "xxx";
    }
    if (chrgState == "CHR") {
      batteryPercent = chargeVoltageToPercentage(currentBatteryVoltage);
    } else {
      batteryPercent = voltageToPercentage(currentBatteryVoltage);
    }


    display.setCursor(60, 24);
    display.print("Est:");
    estHours = int(floor(((float(batteryPercent) / 100) * battLifeMins) / 60));
    display.setCursor(85, 24);
    display.print(leadZero(estHours));
    display.setCursor(99, 24);
    display.print("h");
    estMins = ((float(batteryPercent) / 100) * battLifeMins) - (estHours * 60);
    display.setCursor(107, 24);
    display.print(leadZero(estMins));
    display.setCursor(120, 24);
    display.print("m");


    display.drawRect(0, 16, 124, 7, SSD1306_WHITE);
    display.fillRect(124, 17, 5, 5, SSD1306_WHITE);
    batteryBarWidth = int(((float(batteryPercent) / 100) * 122));
    if (batteryBarWidth > 122) {
      batteryBarWidth = 122;
    }
    display.fillRect(1, 17, batteryBarWidth, 6, SSD1306_WHITE);
    if (batteryVoltage < 2) {
      display.setCursor(60, 15);
      display.print("xxx");
    }

    if (digitalRead(BTN_UP) == HIGH) {
      display.setCursor(0, 24);
      display.print("UP");
    }
    if (digitalRead(BTN_ENTER) == HIGH) {
      display.setCursor(30, 24);
      display.print("ENTER");
    }
    if (digitalRead(BTN_DOWN) == HIGH) {
      display.setCursor(60, 24);
      display.print("DOWN");
    }
  }
  if (menuL0 == 7) {
    display.setCursor(0, 0);
    display.println(dateString);
    display.setCursor(0, 8);
    display.println("TemH");
    display.setCursor(30, 8);
    display.println(tempH, 1);
    display.setCursor(55, 8);
    display.println("C");
    display.setCursor(65, 8);
    display.println("HumH");
    display.setCursor(95, 8);
    display.println(humH, 1);
    display.setCursor(120, 8);
    display.println("%");

    display.setCursor(0, 16);
    display.println("TemM");
    display.setCursor(30, 16);
    display.println(tempM, 1);
    display.setCursor(55, 16);
    display.println("C");
    display.setCursor(65, 16);
    display.println("HumM");
    display.setCursor(95, 16);
    display.println(humM, 1);
    display.setCursor(120, 16);
    display.println("%");

    display.setCursor(0, 24);
    display.println("TemL");
    display.setCursor(30, 24);
    display.println(tempL, 1);
    display.setCursor(55, 24);
    display.println("C");
    display.setCursor(65, 24);
    display.println("HumL");
    display.setCursor(95, 24);
    display.println(humL, 1);
    display.setCursor(120, 24);
    display.println("%");
  }

  if (digitalRead(BTN_UP) == HIGH and menuL0 < 10) {
    if (menuL0 < 10 && menuL1 == 10) {
      menuL0 = menuL0 + 1;
    }
    if (menuL1 < 9 && menuL2 == 10) {
      menuL1 = menuL1 + 1;
    }
    if (menuL2 < 9 and menuL0 != 10 and menuL1 != 10) {
      menuL2 = menuL2 + 1;
    }
    if (menuL2 == 9 and minsToSet < 59) {
      minsToSet = minsToSet + 1;
    }
    if (menuL2 == 9 and hoursToSet < 23) {
      hoursToSet = hoursToSet + 1;
    }
    if (menuL2 == 9 and intervalToSet < 5) {
      intervalToSet = intervalToSet + 1;
    }
  }

  if (digitalRead(BTN_DOWN) == HIGH and menuL0 >= 7) {
    if (menuL0 > 7 && menuL1 == 10) {
      menuL0 = menuL0 - 1;
    }
    if (menuL1 > 5 and menuL1 <= 9 && menuL2 == 10) {
      menuL1 = menuL1 - 1;
    }
    if (menuL2 > 6 and menuL2 < 9 and menuL0 != 10 and menuL1 != 10) {
      menuL2 = menuL2 - 1;
    }
    if (menuL2 == 9 and minsToSet > 1) {
      minsToSet = minsToSet - 1;
    }
    if (menuL2 == 9 and hoursToSet > 1) {
      hoursToSet = hoursToSet - 1;
    }
    if (menuL2 == 9 and intervalToSet > 1) {
      intervalToSet = intervalToSet - 1;
    }
  }


  if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;
    adcVoltValue = analogRead(VOLT_IN);  // Odczyt wartości ADC z GPIO36
    measuredVoltage = (adcVoltValue * adcMaxVoltage) / adcResolution;
    quickBatteryVoltage4 = quickBatteryVoltage3;
    quickBatteryVoltage3 = quickBatteryVoltage2;
    quickBatteryVoltage2 = quickBatteryVoltage;
    quickBatteryVoltage = (measuredVoltage * voltageDividerFactor) + 0.1;
    batteryVoltage = (quickBatteryVoltage + quickBatteryVoltage2 + quickBatteryVoltage3 + quickBatteryVoltage4) / 4;
  }

  //  Log napiećia docelowoe procent baterii
  //  kiedy zaden przycisk  nie jest wcisniety
  if (abs(batteryVoltage - currentBatteryVoltage) >= 0.14 && digitalRead(BTN_DOWN) == LOW && digitalRead(BTN_UP) == LOW && digitalRead(BTN_ENTER) == LOW) {
    lastBatteryVoltage = currentBatteryVoltage;
    currentBatteryVoltage = batteryVoltage;
  }

  if (secs % 15 == 0) {
    lastBatteryVoltage = currentBatteryVoltage;
    currentBatteryVoltage = batteryVoltage;
    delay(500);
  }
  //zapis na SD poziom baterii
  if (mins % 1 == 0 && secs == 7) {
    File file = SD.open("/GeneralLog.txt", FILE_APPEND);
    if (file) {
      display.fillRect(0, 32, 128, 32, SSD1306_WHITE);
      file.print(dateString);
      file.print(" Battery %:  ");
      file.print(batteryPercent);
      file.print(" V:  ");
      file.println(formatNumber(currentBatteryVoltage, 2));
      file.close();
      delay(850);
    }
  }



  //zapis na SD temp i hum
  if (intervalTempWrite >= 60) {
    intervalTempWriteHours = (intervalTempWrite / 60);
  } else {
    intervalTempWriteHours = -1;
  }
  if (((intervalTempWriteHours == -1 && mins % intervalTempWrite == 0) or (hourss % intervalTempWriteHours == 0 && mins == 0)) && secs == 0) {
    // mins % intervalTempWrite == 0 && secs == 0) {  
    File file = SD.open("/TempHumLog.txt", FILE_APPEND);
    if (file) {
      display.fillRect(0, 0, 64, 32, SSD1306_WHITE);
      tempHCalc = (tempH + lastTempH) / 2;
      tempMCalc = (tempM + lastTempM) / 2;
      tempLCalc = (tempL + lastTempL) / 2;
      humHCalc = (humH + lastHumH) / 2;
      humMCalc = (humM + lastHumM) / 2;
      humLCalc = (humL + lastHumL) / 2;
      lastTempH = tempH;
      lastTempM = tempM;
      lastTempL = tempL;
      lastHumH = humH;
      lastHumM = humM;
      lastHumL = humL;
      file.print(leadZero(dayss));
      file.print("/");
      file.print(leadZero(months));
      file.print("/");
      file.print(years);
      file.print("; ");
      file.print(leadZero(hourss));
      file.print(":");
      file.print(leadZero(mins));
      file.print("; ");
      file.print(formatNumber(temp));
      file.print("; ");
      file.print(formatNumber(hum));
      file.print("; ");
      file.print(formatNumber(heat_idx));
      file.print("; ");
      file.print(formatNumber(tempHCalc));
      file.print("; ");
      file.print(formatNumber(humHCalc));
      file.print("; ");
      file.print(formatNumber(tempMCalc));
      file.print("; ");
      file.print(formatNumber(humMCalc));
      file.print("; ");
      file.print(formatNumber(tempLCalc));
      file.print("; ");
      file.println(formatNumber(humLCalc));
      file.close();
      delay(1000);
    }
  }
  if (sd_error == 1){
    display.drawLine(0, 0,128, 32, SSD1306_WHITE);
  }
  display.display();
  delay(150);
  yield();
}

//Main feature:
// RTC z możliwością ustawienia godziny, zmiany interwału zapisu
// odczyt wilgotności, temperatury i temperatury odcuwalnej
// zasilanie bateryjne wraz z obsługą poziomu baterii i estymowanym czasem pracy na baterii
// menu