//Płytka: "FireBeetle-ESP32" #include #include #include #include #include "DHT.h" #include "RTClib.h" //file system #include "FS.h" #include "SD.h" #include "SPI.h" #define Version "0.5" #define WIRE Wire #define DHTTYPE DHT22 #define DHTPIN 4 //GPIO04 D4 #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 = 998; 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.06, 4.00, 3.95, 3.90, 3.85, 3.80, 3.70, 3.45, 2.75 }; // 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] = { 4.03, // ~0% 4.06, // ~1% 4.12, // ~5% 4.14, // ~10% 4.16, // ~30% 4.18, // ~60% 4.19, // ~80% 4.20, // ~95% 4.21, // ~98% 4.22 // 100% }; float pPoints[nPoints] = { 0, // 4.03 V 1, // 4.06 V 5, // 4.12 V 10, // 4.14 V 30, // 4.16 V 60, // 4.18 V 80, // 4.19 V 95, // 4.20 V 98, // 4.21 V 100 // 4.22 V }; // 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!"); while (1) ; // Zatrzymaj program, jeśli RTC nie jest dostępny } 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_PULLDOWN); pinMode(BTN_ENTER, INPUT_PULLDOWN); pinMode(BTN_DOWN, INPUT_PULLDOWN); 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 >= 2.0 && currentBatteryVoltage >= 4.0) || currentBatteryVoltage >= 4.10) { 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(); } } //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