Files
Arduino/ESP32/TempHumLoggerHomeKit/TempHumLoggerHomeKit.ino
Kamil Siejka 36fa111b49 Wyrównanie wartości x3
Poprawa Excel
2025-02-27 14:34:14 +01:00

763 lines
21 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#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.43"
#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 16 //GPIO36 D17
#define DHTPIN_H 17 //GPIO36 D16
//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;
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 intervalTempWrite = 1;
int minsToSet = -1;
int hoursToSet = -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('.', ',');
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(115200);
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);
display.println("Interval:");
display.setCursor(60, 8);
display.println(intervalTempWrite);
display.setCursor(70, 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!");
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;
//setup END
//######################
}
void loop() {
currentMillis = millis();
display.clearDisplay();
////DIAGNOSTIC menu nr diag
// display.setCursor(75, 16);
// display.println(menuL0);
// display.setCursor(90, 16);
// display.println(menuL1);
// display.setCursor(105, 16);
// display.println(menuL2);
temp = dht.readTemperature();
tempH = (dhtH.readTemperature()-0.16); //kalibracja
tempM = (dhtM.readTemperature()+0.08); //kalibracja
tempL = (dhtL.readTemperature()+0.0);
hum = dht.readHumidity();
humH = (dhtH.readHumidity()+0.0);
humM = (dhtM.readHumidity()-3.2); //kalibracja
humL = (dhtL.readHumidity()+1.7); //kalibracja
// 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");
}
if (menuL1 == 9) {
display.setCursor(0, 8);
delay(100);
display.print("Ustaw godziny");
delay(150);
if (digitalRead(BTN_ENTER) == HIGH and menuL2 == 10) {
menuL2 = 9;
}
if (menuL2 == 9){
display.setCursor(0,16);
display.print("Godzina:");
display.setCursor(45,16);
if (hoursToSet == -1){
hoursToSet = now.hour();
}
display.print(hoursToSet);
delay(100);
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 == 8) {
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 == 7 ) {
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 == 6) {
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 (digitalRead(BTN_DOWN) == HIGH and menuL0 >= 7) {
if (menuL0 > 7 && menuL1 == 10 ) {
menuL0 = menuL0 - 1;
}
if (menuL1 > 6 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 (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 == 0) {
File file = SD.open("/GeneralLog.txt", FILE_APPEND);
if (file) {
display.fillRect(0, 0, 128, 64, 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 (mins % intervalTempWrite == 0 && secs == 0) {
File file = SD.open("/TempHumLog.txt", FILE_APPEND);
if (file) {
display.fillRect(0, 0, 124, 64, 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);
}
}
display.display();
delay(150);
yield();
}
//Main feature:
// RTC z możliwością ustawienia godziny
// odczyt wilgotności, temperatury i temperatury odcuwalnej
// zasilanie bateryjne wraz z obsługą poziomu baterii i estymowanym czasem pracy na baterii
// menu
//2DO zapis na karcie SD