#include <DallasTemperature.h>
#include <WiFiClientSecure.h>
#include <esp_adc_cal.h>

#define uS_to_S 1000000LL // Převod z mikrosekund na sekundu

#define V_REF 1146 // Manuálně naměřená referenční hodnota pro ADC - u každého ESP32 jiná (viz http://esp-idf.readthedocs.io/en/latest/api-reference/peripherals/adc.html)

#define SwitchPin 13
#define temperaturePin 33
#define batteryPin ADC1_CHANNEL_3 // baterie, GPIO 39
#define currentInPin ADC1_CHANNEL_6 // vstup 4-20mA, GPIO 34
#define voltageInPin ADC1_CHANNEL_7 // vstup 0-10V, GPIO 35

const float batteryDivider = 2.045; // Realný poměr děliče napětí pro měření napětí na baterii (2:1)
const float voltageInDivider = 3.331; // Realný poměr děliče napětí pro měření vstupu 0-10 V - odlišný oproti teorii -> pro 3,03:1 nebylo možné na českém trhu najít vhodné rezistory 6,7 a 3,3 kOhm (proto použit 7 a 3kOhm)

#define host "homesp.cz"
#define httpsPort 443

// Certifikát pro zabezpečenou komunikaci - ověření pomocí certifikační autority
const char* root_ca =
    "-----BEGIN CERTIFICATE-----\n"\
    "MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/\n"\
    "MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT\n"\
    "DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow\n"\
    "SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT\n"\
    "GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC\n"\
    "AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF\n"\
    "q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8\n"\
    "SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0\n"\
    "Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA\n"\
    "a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj\n"\
    "/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T\n"\
    "AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG\n"\
    "CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv\n"\
    "bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k\n"\
    "c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw\n"\
    "VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC\n"\
    "ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz\n"\
    "MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu\n"\
    "Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF\n"\
    "AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo\n"\
    "uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/\n"\
    "wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu\n"\
    "X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG\n"\
    "PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6\n"\
    "KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg==\n"\
    "-----END CERTIFICATE-----\n";

RTC_DATA_ATTR bool did_once = false;
RTC_DATA_ATTR int cnt_retry = 0;
RTC_DATA_ATTR char ssid[33], password[33], seconds_to_sleep[6];

String data;

OneWire bus(temperaturePin);
DallasTemperature temp_sensor(&bus);

void setResolution(int res) {
    DeviceAddress address;

    digitalWrite(SwitchPin, HIGH);
    delay(500);
    temp_sensor.begin();
    temp_sensor.getAddress(address, 0);
    temp_sensor.setResolution(address, res); // Nastavení rozlišení
    digitalWrite(SwitchPin, LOW);
}

void connectToWiFi() {
    int cnt_delay = 0;

    WiFi.enableSTA(true);
    WiFi.begin(ssid, password);

    while (WiFi.status() != WL_CONNECTED) {
        if (cnt_delay > 50) { // Ošetření náhodného nepřipojení k WiFi
            if (cnt_retry > 3) {
                cnt_retry = 0;
                esp_deep_sleep(uS_to_S * 3600); // Ošetření dlouhodobého nepřipojení k WiFi, zkouší se znovu po 1 hodině (výpadek Wi-Fi routeru)
            }
            cnt_retry++;
            esp_deep_sleep(uS_to_S * 1); // Restart se zapamatováním údajů v RTC paměti
        }
        delay(100);
        cnt_delay++;
    }
}

void sendValues() {
    WiFiClientSecure client;

    client.setCACert(root_ca);
    client.connect(host, httpsPort);
    if (client.connected()) { // Testování připojení k serveru
        client.println("POST /add.php HTTP/1.1");
        client.println("Host: homesp.cz");
        client.println("Content-Type: application/x-www-form-urlencoded");
        client.println("Content-Length: " + String(data.length()));
        client.println();
        client.println(data);

        while (client.connected()) {
            String line = client.readStringUntil('\n');
            if (line == "\r") break; // Čekání, dokud není přijat header (data odeslána)
        }
    }
}

float getTemp() {
    float temperature;

    temp_sensor.begin();
    do {
        temp_sensor.requestTemperaturesByIndex(0);
        temperature = temp_sensor.getTempCByIndex(0);
    } while (temperature == 85); // Čekání, dokud není provedena koverze

    return temperature;
}

float getADCValue(adc1_channel_t Pin) {
    esp_adc_cal_characteristics_t characteristics;

    adc1_config_width(ADC_WIDTH_11Bit);
    adc1_config_channel_atten(Pin, ADC_ATTEN_11db);
    esp_adc_cal_get_characteristics(V_REF, ADC_ATTEN_DB_11, ADC_WIDTH_BIT_11, &characteristics); // Kalibrační funkce AD úřevodníku

    float voltage = adc1_to_voltage(Pin, &characteristics);

    switch (Pin) {
    case batteryPin:
        voltage *= batteryDivider;
        if (voltage < 2800) esp_deep_sleep_start(); // Vybitá baterie, spánek
        return voltage / 1000;
    case currentInPin:
        float current;
        if (voltage < 660) current = 0; // Hodnota pod 4 mA = 0 mA
        else current = voltage / 165;
        return current;
    case voltageInPin:
        voltage *= voltageInDivider;
        if (voltage < 457) voltage = 0; // Nastavení 0 kvůli nepřesnosti ADC (měří od 137, respektive 457 mV, kvůli kalibrační funkci)
        return voltage / 1000;
    }
}

void getValues() {
    digitalWrite(SwitchPin, HIGH);
    delay(500);
    data = "temperature=" + String(getTemp(), 1) + "&battery=" + String(getADCValue(batteryPin), 2) + "&current=" + String(getADCValue(currentInPin), 2) + "&voltage=" + String(getADCValue(voltageInPin), 2);
    digitalWrite(SwitchPin, LOW);
}

void readInput(char array[], int array_length) {
    String output;

    while (Serial.available() == 0) {}
    output = Serial.readStringUntil('\n');
    output[output.length() - 1] = 0; // Umazání escape sekvence (ENTER)
    output.toCharArray(array, array_length);
}

void setup() {
    if (did_once == false) {
        setResolution(9); // Nastavení rozlišení na 0,5 °C

        Serial.begin(115200);
        Serial.setTimeout(5000);

        Serial.print("SSID: ");
        readInput(ssid, 33);
        Serial.print("Heslo: ");
        readInput(password, 33);
        Serial.print("Doba spánku (s): ");
        readInput(seconds_to_sleep, 6);

        did_once = true;
    }
    pinMode(SwitchPin, OUTPUT);
    getValues();
    connectToWiFi();
    sendValues();
    esp_deep_sleep(uS_to_S * atoi(seconds_to_sleep));
}

void loop(void) {
    // Nikdy se nespustí
}
