Przeprowadz audyt programu



#include <Wire.h>
#include <Adafruit_SSD1306.h>
#include <EEPROM.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <avr/sleep.h>
#include <avr/interrupt.h>

#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);

#define ONE_WIRE_BUS 2
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);

#define VIBRATION_PIN 3
#define SELECT_BUTTON_PIN 4

#define EEPROM_FILTER_TYPE_ADDR 0
#define EEPROM_MANUAL_TEMP_ADDR 10
#define EEPROM_TIMER_DURATION_SETTING_ADDR 20
#define EEPROM_AUTO_TEMP_MODE_ADDR 30
#define EEPROM_TOTAL_RUNTIME_ADDR 40
#define EEPROM_TOTAL_POINTS_ADDR 50

#define MAX_FILTER_POINTS 1000.0f
#define TIMER_DURATION_DEFAULT 600000UL
#define DEBOUNCE_DELAY 50
#define TEMP_UPDATE_INTERVAL 3000
#define TEMP_CONVERSION_TIME 100
#define DISPLAY_UPDATE_INTERVAL 300
#define EEPROM_SAVE_INTERVAL 600000UL
#define EEPROM_STEP_DELAY 5
#define SCREEN_TIMEOUT 30000
#define SLEEP_TIMEOUT 60000
#define VIBRATION_OFF_DELAY 5000
#define WELCOME_SCREEN_DURATION 5000

struct InterpolationPoint {
    float temp;
    float rate;
};

InterpolationPoint tridentPoints[] = {
    {10, 0.00857f}, {15, 0.01164f}, {20, 0.01500f},
    {25, 0.02000f}, {30, 0.02600f}, {35, 0.03391f}, {40, 0.04105f}
};

InterpolationPoint p21Points[] = {
    {10, 0.0010f}, {15, 0.012f}, {20, 0.01700f},
    {25, 0.02200f}, {30, 0.02800f}, {35, 0.036f}, {40, 0.044f}
};

InterpolationPoint p31Points[] = {
    {10, 0.0015f}, {15, 0.018f}, {20, 0.01900f},
    {25, 0.02900f}, {30, 0.03200f}, {35, 0.041f}, {40, 0.047f}
};

InterpolationPoint p41Points[] = {
    {10, 0.0017f}, {15, 0.019f}, {20, 0.02200f},
    {25, 0.02800f}, {30, 0.03600f}, {35, 0.042f}, {40, 0.048f}
};

const int NUM_POINTS = 7;

uint8_t filterType = 0;
float currentLiveTemp = 20.0f;
float manualTempSetting = 20.0f;
float effectiveTemp = 20.0f;
bool autoTempMode = true;

unsigned long totalRuntime = 0;
float totalPointsUsed = 0;

unsigned long timerDurationSetting = TIMER_DURATION_DEFAULT;
unsigned long timerRemaining = TIMER_DURATION_DEFAULT;
bool timerRunning = false;
bool alarmActive = false;

unsigned long lastVibrationTime = 0;
bool vibrationDetected = false;

unsigned long lastRuntimeUpdate = 0;
unsigned long runtimeAccumulator = 0;

enum TempState { TEMP_IDLE, TEMP_REQUESTED, TEMP_READING };
TempState tempState = TEMP_IDLE;
unsigned long tempRequestTime = 0;
unsigned long lastTempUpdate = 0;

enum DisplayState { DISP_IDLE, DISP_DRAWING, DISP_SENDING };
DisplayState displayState = DISP_IDLE;
unsigned long lastDisplayUpdate = 0;
bool displayUpdateNeeded = true;

enum EEPROMState { EEPROM_IDLE, EEPROM_SAVE_RUNTIME, EEPROM_SAVE_POINTS };
EEPROMState eepromState = EEPROM_IDLE;
unsigned long lastEEPROMSave = 0;
unsigned long eepromStepTime = 0;

unsigned long lastActivityTime = 0;
bool displayEnabled = true;
bool deepSleepMode = false;
unsigned long lastVibrationStopTime = 0;
bool saveTriggeredByVibration = false;

bool showWelcomeScreen = false;
unsigned long welcomeScreenEndTime = 0;
const char* welcomeLine1 = "K-TIMER";
const char* welcomeLine2 = "Kompressor Timer";

bool selectButtonPressed = false;
bool lastSelectState = HIGH;
unsigned long selectPressStart = 0;
unsigned long lastSelectDebounce = 0;

enum Screen { MAIN_SCREEN, TIMER_SCREEN, TEMP_SETTING_SCREEN, FILTER_SETTING_SCREEN };
Screen currentScreen = MAIN_SCREEN;

bool isCharging = false;
unsigned long lastChargeToggle = 0;

float getConsumptionRate(float temp) {
    InterpolationPoint* points;
    switch(filterType) {
        case 0: points = tridentPoints; break;
        case 1: points = p21Points; break;
        case 2: points = p31Points; break;
        case 3: points = p41Points; break;
        default: points = tridentPoints; break;
    }

    if (temp <= points[0].temp) return points[0].rate;
    if (temp >= points[NUM_POINTS-1].temp) return points[NUM_POINTS-1].rate;

    for(int i = 0; i < NUM_POINTS-1; i++) {
        if (temp > points[i].temp && temp <= points[i+1].temp) {
            float t1 = points[i].temp;
            float v1 = points[i].rate;
            float t2 = points[i+1].temp;
            float v2 = points[i+1].rate;
            return v1 + ((temp - t1) / (t2 - t1)) * (v2 - v1);
        }
    }
    return points[0].rate;
}

void updateEffectiveTemp() {
    if (autoTempMode) {
        effectiveTemp = currentLiveTemp;
    } else {
        effectiveTemp = manualTempSetting;
    }
}

int getBatteryPercentage() {
    if (millis() - lastChargeToggle > 5000) {
        isCharging = !isCharging;
        lastChargeToggle = millis();
    }

    static int battery = 85;
    if (isCharging) {
        battery = min(battery + 1, 100);
    } else {
        battery = max(battery - 1, 20);
    }
    return battery;
}

void enableDisplay() {
    if (!displayEnabled) {
        display.ssd1306_command(SSD1306_DISPLAYON);
        display.ssd1306_command(SSD1306_SETCONTRAST);
        display.ssd1306_command(0xFF);
        displayEnabled = true;
        displayUpdateNeeded = true;
        lastActivityTime = millis();
    }
}

void disableDisplay() {
    if (displayEnabled) {
        display.ssd1306_command(SSD1306_SETCONTRAST);
        display.ssd1306_command(0);
        display.ssd1306_command(SSD1306_DISPLAYOFF);
        displayEnabled = false;
    }
}

void enterSleepMode() {
    if (!deepSleepMode) {
        disableDisplay();
        set_sleep_mode(SLEEP_MODE_PWR_DOWN);
        sleep_enable();
        attachInterrupt(digitalPinToInterrupt(SELECT_BUTTON_PIN), wakeUp, CHANGE);
        attachInterrupt(digitalPinToInterrupt(VIBRATION_PIN), wakeUp, CHANGE);
        sleep_cpu();
        sleep_disable();
        detachInterrupt(digitalPinToInterrupt(SELECT_BUTTON_PIN));
        detachInterrupt(digitalPinToInterrupt(VIBRATION_PIN));
        deepSleepMode = false;
        showWelcomeScreen = true;
        welcomeScreenEndTime = millis() + WELCOME_SCREEN_DURATION;
        lastActivityTime = millis();
        enableDisplay();
    }
}

void wakeUp() {
    sleep_disable();
}

void updateActivityStatus() {
    lastActivityTime = millis();
}

void updateVibrationStatus() {
    bool currentVibration = digitalRead(VIBRATION_PIN) == HIGH;
    unsigned long now = millis();
    if (currentVibration) {
        vibrationDetected = true;
        lastVibrationTime = now;
        lastVibrationStopTime = 0;
        updateActivityStatus();
    } else {
        if (vibrationDetected && (now - lastVibrationTime > 1000)) {
            vibrationDetected = false;
            lastVibrationStopTime = now;
            saveTriggeredByVibration = true;
        }
    }
}

void updateSelectButton() {
    unsigned long now = millis();
    bool currentSelectState = digitalRead(SELECT_BUTTON_PIN);
    if (currentSelectState != lastSelectState) {
        lastSelectDebounce = now;
    }
    if ((now - lastSelectDebounce) > DEBOUNCE_DELAY) {
        if (currentSelectState == LOW && !selectButtonPressed) {
            selectButtonPressed = true;
            selectPressStart = now;
        } else if (currentSelectState == HIGH && selectButtonPressed) {
            selectButtonPressed = false;
            unsigned long pressDuration = now - selectPressStart;
            handleButtonRelease(pressDuration);
            updateActivityStatus();
        }
    }
    lastSelectState = currentSelectState;
}

void handleButtonRelease(unsigned long pressDuration) {
    if (currentScreen == MAIN_SCREEN) {
        if (pressDuration >= 10000 && pressDuration < 30000) {
            currentScreen = TEMP_SETTING_SCREEN;
        } else if (pressDuration >= 30000) {
            currentScreen = FILTER_SETTING_SCREEN;
        } else if (pressDuration < 1000) {
            currentScreen = TIMER_SCREEN;
        }
    } else if (currentScreen == TIMER_SCREEN) {
        if (pressDuration < 1000) {
            currentScreen = MAIN_SCREEN;
        }
    } else if (currentScreen == TEMP_SETTING_SCREEN) {
        if (pressDuration >= 10000) {
            currentScreen = MAIN_SCREEN;
        } else if (pressDuration < 1000) {
            if (!autoTempMode) {
                manualTempSetting += 5.0f;
                if (manualTempSetting > 40.0f) manualTempSetting = 10.0f;
                saveSettingsToEEPROM();
            }
        }
    } else if (currentScreen == FILTER_SETTING_SCREEN) {
        if (pressDuration >= 10000) {
            currentScreen = MAIN_SCREEN;
        } else if (pressDuration < 1000) {
            filterType = (filterType + 1) % 4;
            saveSettingsToEEPROM();
        }
    }
}

void updateTimer() {
    if (timerRunning && !alarmActive) {
        unsigned long now = millis();
        static unsigned long lastTimerUpdate = 0;
        if (lastTimerUpdate == 0) {
            lastTimerUpdate = now;
            return;
        }
        unsigned long elapsed = now - lastTimerUpdate;
        lastTimerUpdate = now;
        if (timerRemaining > elapsed) {
            timerRemaining -= elapsed;
        } else {
            timerRemaining = 0;
            timerRunning = false;
            alarmActive = true;
            displayUpdateNeeded = true;
        }
    }
}

void prepareDisplayBuffer() {
    if (!displayEnabled) return;
    if (showWelcomeScreen) {
        display.clearDisplay();
        display.setTextColor(SSD1306_WHITE);
        display.setTextSize(2);
        display.setCursor(20, 20);
        display.println(welcomeLine1);
        display.setTextSize(2);
        display.setCursor(20, 40);
        display.println(welcomeLine2);
    } else {
        display.clearDisplay();
        display.setTextColor(SSD1306_WHITE);
        switch(currentScreen) {
            case MAIN_SCREEN:
                prepareMainDisplay();
                break;
            case TIMER_SCREEN:
                prepareTimerDisplay();
                break;
            case TEMP_SETTING_SCREEN:
                prepareTempSettingDisplay();
                break;
            case FILTER_SETTING_SCREEN:
                prepareFilterSettingDisplay();
                break;
        }
    }
}

void prepareMainDisplay() {
    int batteryPercent = getBatteryPercentage();
    static unsigned long lastBlink = 0;
    static bool blinkState = false;
    unsigned long totalMinutes = totalRuntime / 60000;
    unsigned int hours = totalMinutes / 60;
    unsigned int minutes = totalMinutes % 60;
    display.setTextSize(3);
    display.setCursor(4, 0);
    if (hours < 100) display.print("0");
    if (hours < 10) display.print("0");
    display.print(hours);
    display.print("h");
    display.setTextSize(2);
    display.setCursor(100, 0);
    if (minutes < 10) display.print("0");
    display.print(minutes);
    display.setCursor(100, 15);
    display.setTextSize(1);
    display.print("MIN.");
    float remainingPoints = MAX_FILTER_POINTS - totalPointsUsed;
    if(remainingPoints <= 0) {
        display.setTextSize(1);
        display.setCursor(4, 32);
        display.println("!! REPLACE FILTER !!");
    } else {
        display.setTextSize(2);
        display.setCursor(4, 28);
        display.print("F: ");
        display.print(remainingPoints, 1);
    }
    display.setTextSize(2);
    display.setCursor(4, 50);
    display.print(int(effectiveTemp));
    display.print((char)247);
    display.print("C");
    display.setTextSize(1);
    display.setCursor(60, 56);
    display.print(autoTempMode ? "AUTO" : "MAN");
    if (vibrationDetected) {
        int animFrame = (millis() / 300) % 4;
        display.fillCircle(100, 56, 2, SSD1306_WHITE);
        if (animFrame >= 1) display.drawCircle(100, 56, 4, SSD1306_WHITE);
        if (animFrame >= 2) display.drawCircle(100, 56, 6, SSD1306_WHITE);
        if (animFrame >= 3) display.drawCircle(100, 56, 8, SSD1306_WHITE);
    }
    display.drawRect(89, 49, 31, 15, SSD1306_WHITE);
    display.drawRect(119, 53, 3, 6, SSD1306_WHITE);
    int batteryWidth = map(batteryPercent, 0, 100, 0, 27);
    display.fillRect(91, 51, batteryWidth, 11, SSD1306_WHITE);
    static int chargeAnimPos = 0;
    if (isCharging) {
        display.fillRect(91 + chargeAnimPos, 51, 2, 11, SSD1306_BLACK);
        chargeAnimPos = (chargeAnimPos + 1) % 28;
        display.fillRect(91 + chargeAnimPos, 51, 2, 11, SSD1306_WHITE);
        display.drawLine(117, 50, 119, 53, SSD1306_WHITE);
        display.drawLine(119, 53, 117, 56, SSD1306_WHITE);
    }
    if (batteryPercent < 20) {
        if (millis() - lastBlink > 500) {
            blinkState = !blinkState;
            lastBlink = millis();
        }
        display.setTextColor(blinkState ? SSD1306_WHITE : SSD1306_BLACK);
    } else {
        display.setTextColor(SSD1306_WHITE);
    }
    display.setTextSize(1);
    if (batteryPercent == 100) display.setCursor(93, 53);
    else if (batteryPercent >= 10) display.setCursor(99, 53);
    else display.setCursor(105, 53);
    display.print(batteryPercent);
    display.print("%");
    display.setTextColor(SSD1306_WHITE);
}

void prepareTimerDisplay() {
    display.setTextSize(2);
    display.setCursor(35, 0);
    display.println("TIMER");
    unsigned int minutes = timerRemaining / 60000;
    unsigned int seconds = (timerRemaining % 60000) / 1000;
    display.setTextSize(4);
    display.setCursor(5, 22);
    if (minutes < 10) display.print("0");
    display.print(minutes);
    display.print(":");
    if (seconds < 10) display.print("0");
    display.print(seconds);
    display.setTextSize(1);
    if (alarmActive) {
        display.setCursor(25, 55);
        display.print("!!! ALARM !!!");
    } else if (timerRunning) {
        display.setCursor(30, 55);
        display.print("COUNTING...");
    } else {
        display.setCursor(27, 55);
        display.print("HOLD 1s START");
    }
}

void prepareTempSettingDisplay() {
    display.setTextSize(2);
    display.setCursor(15, 0);
    display.println("TEMPERATURE");
    display.setTextSize(3);
    display.setCursor(30, 20);
    if (autoTempMode) {
        display.print("AUTO");
        display.setTextSize(2);
        display.setCursor(35, 45);
        display.print(int(currentLiveTemp));
        display.print((char)247);
        display.print("C");
    } else {
        display.print(int(manualTempSetting));
        display.print((char)247);
        display.print("C");
    }
    display.setTextSize(1);
    display.setCursor(5, 55);
    if (autoTempMode) {
        display.print("HOLD 2s: MANUAL MODE");
    } else {
        display.print("CLICK: +5C, HOLD 2s: AUTO");
    }
}

void prepareFilterSettingDisplay() {
    display.setTextSize(2);
    display.setCursor(15, 0);
    display.println("FILTER SETT.");
    display.setTextSize(2);
    display.setCursor(15, 20);
    display.print("TYPE: ");
    switch(filterType) {
        case 0: display.print("TRIDENT"); break;
        case 1: display.print("P21"); break;
        case 2: display.print("P31"); break;
        case 3: display.print("P41"); break;
    }
    display.setTextSize(2);
    display.setCursor(15, 40);
    display.print("CAPACITY: ");
    display.print(MAX_FILTER_POINTS);
    display.print(" PT");
    display.setTextSize(1);
    display.setCursor(5, 55);
    display.print("HOLD 2s: RESET");
}

void updateDisplayAsync() {
    unsigned long now = millis();
    if (showWelcomeScreen) {
        if (now >= welcomeScreenEndTime) {
            showWelcomeScreen = false;
            currentScreen = MAIN_SCREEN;
            displayUpdateNeeded = true;
            lastActivityTime = millis();
        } else {
            display.clearDisplay();
            display.setTextColor(SSD1306_WHITE);
            display.setTextSize(2);
            display.setCursor(20, 20);
            display.println(welcomeLine1);
            display.setTextSize(2);
            display.setCursor(20, 40);
            display.println(welcomeLine2);
            display.display();
        }
        return;
    }
    if (!displayEnabled) return;
    switch (displayState) {
        case DISP_IDLE:
            if (displayUpdateNeeded || (now - lastDisplayUpdate >= DISPLAY_UPDATE_INTERVAL)) {
                displayState = DISP_DRAWING;
            }
            break;
        case DISP_DRAWING:
            prepareDisplayBuffer();
            displayState = DISP_SENDING;
            break;
        case DISP_SENDING:
            display.display();
            displayState = DISP_IDLE;
            lastDisplayUpdate = now;
            break;
    }
}

void updateEEPROMAsync() {
    unsigned long now = millis();
    bool saveCondition = (now - lastEEPROMSave > EEPROM_SAVE_INTERVAL) || (saveTriggeredByVibration && now - lastVibrationStopTime > VIBRATION_OFF_DELAY);
    if (saveCondition && eepromState == EEPROM_IDLE) {
        eepromState = EEPROM_SAVE_RUNTIME;
        eepromStepTime = now;
    }
    switch (eepromState) {
        case EEPROM_SAVE_RUNTIME:
            if (now - eepromStepTime >= EEPROM_STEP_DELAY) {
                EEPROM.put(EEPROM_TOTAL_RUNTIME_ADDR, totalRuntime);
                eepromState = EEPROM_SAVE_POINTS;
                eepromStepTime = now;
            }
            break;
        case EEPROM_SAVE_POINTS:
            if (now - eepromStepTime >= EEPROM_STEP_DELAY) {
                EEPROM.put(EEPROM_TOTAL_POINTS_ADDR, totalPointsUsed);
                eepromState = EEPROM_IDLE;
            }
            break;
    }
}

void saveSettingsToEEPROM() {
    EEPROM.update(EEPROM_FILTER_TYPE_ADDR, filterType);
    EEPROM.put(EEPROM_MANUAL_TEMP_ADDR, manualTempSetting);
    EEPROM.put(EEPROM_TIMER_DURATION_SETTING_ADDR, timerDurationSetting);
    EEPROM.update(EEPROM_AUTO_TEMP_MODE_ADDR, autoTempMode ? 1 : 0);
}

void loadSettingsFromEEPROM() {
    filterType = EEPROM.read(EEPROM_FILTER_TYPE_ADDR);
    EEPROM.get(EEPROM_MANUAL_TEMP_ADDR, manualTempSetting);
    EEPROM.get(EEPROM_TIMER_DURATION_SETTING_ADDR, timerDurationSetting);
    EEPROM.get(EEPROM_TOTAL_RUNTIME_ADDR, totalRuntime);
    EEPROM.get(EEPROM_TOTAL_POINTS_ADDR, totalPointsUsed);
    autoTempMode = EEPROM.read(EEPROM_AUTO_TEMP_MODE_ADDR);
}

void setup() {
    pinMode(VIBRATION_PIN, INPUT_PULLUP);
    pinMode(SELECT_BUTTON_PIN, INPUT_PULLUP);
    Serial.begin(9600);
    if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
        Serial.println(F("SSD1306 allocation failed"));
        while(true);
    }
    sensors.begin();
    sensors.setResolution(9);
    loadSettingsFromEEPROM();
    updateEffectiveTemp();
    unsigned long now = millis();
    lastRuntimeUpdate = now;
    lastVibrationTime = now;
    lastEEPROMSave = now;
    if (autoTempMode) {
        sensors.requestTemperatures();
        tempState = TEMP_REQUESTED;
        tempRequestTime = now;
    }
    enableDisplay();
    showWelcomeScreen = true;
    welcomeScreenEndTime = millis() + WELCOME_SCREEN_DURATION;
}

void loop() {
    unsigned long now = millis();
    if (deepSleepMode) {
        enterSleepMode();
    }
    updateVibrationStatus();
    if (displayEnabled) {
        updateRuntimePrecise();
        updateSelectButton();
        updateTimer();
        updateTemperatureAsync();
    }
    updateDisplayAsync();
    updateEEPROMAsync();
}