Files
Arduino/ESP32/TempHumLoggerHomeKit/TempHumLoggerHomeKit.ino
2025-02-22 18:39:11 +01:00

529 lines
14 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.28"
#define WIRE Wire
#define DHTTYPE DHT22
#define DHTPIN 4
#define SD_CS 5
#define BTN_UP 33
#define BTN_ENTER 34
#define BTN_DOWN 35
#define VOLT_IN 36
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);
RTC_DS3231 rtc; // Obiekt dla DS3231
int menuL0 = 10;
int menuL1 = 10;
int menuL2 = 10;
float hum;
float temp;
float heat_idx;
int secs;
int mins;
int hourss;
int dayss;
int months;
int years;
int minsToSet = -1;
int hoursToSet = -1;
int adcVoltValue;
float measuredVoltage = 0;
float batteryVoltage = 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];
String formatNumber(float number, int decimalPlaces = 1 ) {
String str = String(number, decimalPlaces);
str.replace('.', ',');
return str;
}
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.println("Version:");
display.setCursor(0, 8);
display.println(Version);
display.setCursor(0, 16);
display.println("DHT22");
display.setCursor(32, 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();
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");
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;
//setup END
//######################
}
void loop() {
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);
hum = dht.readHumidity();
temp = dht.readTemperature();
// 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");
} else if ((lastBatteryVoltage < currentBatteryVoltage && currentBatteryVoltage >= 2.0) || currentBatteryVoltage >= 4.06 ){
display.print("Charging");
} else if (lastBatteryVoltage > currentBatteryVoltage && currentBatteryVoltage >= 2.0 ) {
display.print("Discharge");
} else {
display.print("---");
}
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, 6, SSD1306_WHITE);
display.fillRect(124, 17, 5, 4, SSD1306_WHITE);
batteryBarWidth = int(((float(batteryPercent)/100)*122));
if (batteryBarWidth >122){
batteryBarWidth = 122;
}
display.fillRect(1, 17, batteryBarWidth, 5, SSD1306_WHITE);
if (batteryVoltage < 2 ){
display.setCursor(60,8);
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 (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 >= 8) {
if (menuL0 > 8 && 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;
}
}
adcVoltValue = analogRead(VOLT_IN); // Odczyt wartości ADC z GPIO36
measuredVoltage = (adcVoltValue * adcMaxVoltage) / adcResolution;
batteryVoltage = (measuredVoltage * voltageDividerFactor)+0.1;
// Log napiećia docelowoe procent baterii
// kiedy zaden przycisk nie jest wcisniety
if (secs % 15 == 0) {
lastBatteryVoltage = currentBatteryVoltage;
currentBatteryVoltage = batteryVoltage;
delay(500);
}
if (mins % 1 == 0 && secs == 0) {
File file = SD.open("/GeneralLog.txt", FILE_APPEND);
if (file) {
display.fillRect(0, 0, 124, 64, SSD1306_WHITE);
file.print(dateString);
file.print(" Battery %: ");
file.println(batteryPercent);
file.close();
}
}
//zapis na SD:
if (mins % 1 == 0 && secs == 0) {
File file = SD.open("/TempHumLog.txt", FILE_APPEND);
if (file) {
display.fillRect(0, 0, 124, 64, SSD1306_WHITE);
file.print(leadZero(dayss));
file.print("/");
file.print(leadZero(months));
file.print("/");
file.print(years);
file.print("; ");
file.print(hourss);
file.print(":");
file.print(leadZero(mins));
file.print("; ");
file.print(formatNumber(temp));
file.print("; ");
file.print(formatNumber(hum));
file.print("; ");
file.println(formatNumber(heat_idx));
file.close();
delay(1000);
}
}
display.display();
delay(150);
yield();
}
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ść
}
//Main feature:
// RTC z możliwością ustawienia godziny
// odczyt wilgotności, temperatury i temperatury odcuwalnej
// zasilanie bateryjne
// menu
//2DO zapis na karcie SD