// Visual Micro is in vMicro>General>Tutorial Mode
// 
/*
    Name:       Teensy4.1_Datalogger new.ino
    Created:	03.05.2022 12:04:32
    Author:     GAMINGMASHEEN\Julian Graf
*/

#include <SdFat.h>
#include <TimeLib.h>
#include <Bounce.h>

#define SD_FAT_TYPE 3

#ifndef SDCARD_SS_PIN
const uint8_t SD_CS_PIN = SS;
#else  // SDCARD_SS_PIN
// Assume built-in SD is used.
const uint8_t SD_CS_PIN = SDCARD_SS_PIN;
#endif  // SDCARD_SS_PIN

#if HAS_SDIO_CLASS
#define SD_CONFIG SdioConfig(FIFO_SDIO)
#elif ENABLE_DEDICATED_SPI
#define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI)
#else  // HAS_SDIO_CLASS
#define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI)
#endif  // HAS_SDIO_CLASS

#if SD_FAT_TYPE == 0
SdFat sd;
File file;
#elif SD_FAT_TYPE == 1
SdFat32 sd;
File32 file;
#elif SD_FAT_TYPE == 2
SdExFat sd;
ExFile file;
#elif SD_FAT_TYPE == 3
SdFs sd;
FsFile file;
#else  // SD_FAT_TYPE
#error Invalid SD_FAT_TYPE
#endif  // SD_FAT_TYPE

// Define User Types below here or use a .h file
//
const char software_name[] = "Software: Teensy_datalog V.2";
const int min_voltage_batterie = 13;
const int fixed_resistor_temperatur = 500;

const int power_Temp_sensor = 34, power_Windfahne = 36, LED_Fail = 24, 
          LED_Write = 5, LED_Normal = 6, LED_Batterie = 7,
          taster_manuell_speichern = 28, Windfahne = 20, T_sensor_input = 17, Batterie_input = 38;

int last_second, last_minute, last_hour;


struct calculations {
private:

    float summ;
    float square_summ;
    float cubic_summ;

public:

    void calculate(float speed_per_second[60], int amount_saved) {
        summ = 0;
        square_summ = 0;
        cubic_summ = 0;

        for (int i = 0; i < amount_saved; i++) {
            summ = summ + speed_per_second[i];
            square_summ = square_summ + pow(speed_per_second[i], 2);
            cubic_summ = cubic_summ + pow(speed_per_second[i], 3);
        }
        arithmetic_mean = summ / float(amount_saved);
        square_mean = pow((square_summ / float(amount_saved)), (1 / 2.0));
        cubic_mean = pow((cubic_mean / float(amount_saved)), (1 / 3.0));

        summ = 0;
        square_summ = 0;
        cubic_summ = 0;

        for (int i = 0; i < amount_saved; i++) {
            summ = summ + pow((speed_per_second[i] - arithmetic_mean), 2);
            square_summ = square_summ + pow((speed_per_second[i] - square_mean), 2);
            cubic_summ = cubic_summ + pow((speed_per_second[i] - cubic_mean), 2);
            speed_min = min(speed_min, speed_per_second[i]);
            speed_max = max(speed_max, speed_per_second[i]);
        }
        arithmetic_deviation = pow((summ / float(amount_saved - 1)), (1 / 2.0));
        square_deviation = pow((square_summ / float(amount_saved - 1)), (1 / 2.0));
        cubic_deviation = pow((cubic_summ / float(amount_saved - 1)), (1 / 2.0));

        seconds_skipped = 60 - amount_saved;
    }
    
    float arithmetic_mean;
    float arithmetic_deviation;
    
    float square_mean;
    float square_deviation;
    
    float cubic_mean;
    float cubic_deviation;

    float speed_min;
    float speed_max;

    int seconds_skipped;
};


struct anemomenter_maessurement {
public: 

    void setup(int pin) {
        this->reed_contact = Bounce(pin, 10);
    }

    void meassure() {
        if (reed_contact.update() && reed_contact.fallingEdge()) {
            count_per_second++;
        }
    }
    
    void save_wind_speed() {
        wind_speed_per_second[saved_seconds] = 0.4 * count_per_second;
        saved_seconds++;
    }

    void calculate() {
        values[saved_minutes].calculate(wind_speed_per_second, saved_seconds);
        saved_seconds = 0;
        saved_minutes++;
    }

    void file_print() {

        for (int i = 0; i < saved_minutes; i++) {
            file.printf("Min: %f,\tMax: %f,\t", values[i].speed_min, values[i].speed_max);
            file.printf("Arith. Mittel: % f,\tStandard Abw.: %f\t", values[i].arithmetic_mean, values[i].arithmetic_deviation);
            file.printf("Quadr. Mittel: % f,\tStandard Abw.: %f\t", values[i].square_mean, values[i].square_deviation);
            file.printf("Kub. Mittel: %f,\tStandard Abw.: %f\t", values[i].cubic_mean, values[i].cubic_deviation);
            file.printf("�bersprungene Sek.: %i\n", values[i].seconds_skipped);
        }
        
        file.printf("�bersprungene Min.: %i\n", 60 - saved_minutes);

        saved_minutes = 0;
    }
    
private: 
    int count_per_second = 0;
    int saved_seconds = 0;
    int saved_minutes = 0;
    float wind_speed_per_second[60];

    Bounce reed_contact;

    calculations values[60];

}anemometer_1, anemometer_2, anemometer_3;



// Define Function Prototypes that use User Types below here or use a .h file
//


// Define Functions below here or use other .ino or cpp files
//

void dateTime(uint16_t* date, uint16_t* time, uint8_t* ms10) {

    // Return date using FS_DATE macro to format fields.
    *date = FS_DATE(year(), month(), day());

    // Return time using FS_TIME macro to format fields.
    *time = FS_TIME(hour(), minute(), second());

    // Return low time bits in units of 10 ms.
    *ms10 = second() & 1 ? 100 : 0;
}

void write_sd() {
    char file_name[50];

    FsDateTime::setCallback(dateTime);
    
    sprintf(file_name, "Windmessmast-%d.%d.%d_%d:%d.txt", year(), month(), day(), hour(), minute());
    sd.begin(SD_CONFIG);
    if (file.open(file_name, FILE_WRITE)) {
        Serial.println("Start SD schreiben");

        file.println("Messdaten von Windmessmasst"); 
        file.println();
        file.println("Data logger : Teensy 4.1");
        file.println(software_name);
        file.println();

        file.println("Anemometer_1 Werte:");
        anemometer_1.file_print();
        file.println("Anemometer_2 Werte:");
        anemometer_2.file_print();
        file.println("Anemometer_3 Werte:");
        anemometer_3.file_print();

        file.close();
        Serial.println("Ende des Schreibvorgangs");
    }
    
}
void every_second() {

    anemometer_1.save_wind_speed();
    anemometer_2.save_wind_speed();
    anemometer_3.save_wind_speed();

    last_second = second();
}

void every_minute() {
    anemometer_1.calculate();
    anemometer_2.calculate();
    anemometer_3.calculate();

    last_minute = minute();
}

void every_hour() {

    write_sd();
    last_hour = hour();
}

// The setup() function runs once each time the micro-controller starts
void setup()
{
    //set input and output
    pinMode(Windfahne, INPUT);
    pinMode(Batterie_input, INPUT);
    pinMode(T_sensor_input, INPUT);
    pinMode(taster_manuell_speichern, INPUT);
    pinMode(LED_Write, OUTPUT);
    pinMode(LED_Fail, OUTPUT);
    pinMode(LED_Normal, OUTPUT);
    pinMode(LED_Batterie, OUTPUT);
    pinMode(power_Temp_sensor, OUTPUT);
    pinMode(power_Windfahne, OUTPUT);

    setSyncProvider((getExternalTime)Teensy3Clock.get());

    Serial.begin(9600);
    Serial.println("Teensy 4.1-Datalogger gestartet");
    if (timeStatus() != timeSet) {
        Serial.println("Fehler bei Synchronisieren der Uhrzeit mit der RTC");
        digitalWrite(LED_Fail, HIGH);
        return;
    }
    Serial.println("Uhrzeit erfolgreich mit der RTC synchronisiert");
    if (!sd.begin(SD_CONFIG)) {
        digitalWrite(LED_Fail, HIGH);
        sd.initErrorHalt(&Serial);
    }
    
    anemometer_1.setup(2);
    anemometer_2.setup(9);
    anemometer_3.setup(22);


    Serial.println("Messung startet");
    last_second = second();
    while (last_second == second()) {};
    last_second = second();
    last_minute = minute();
    last_hour = hour();
    
}

// Add the main program code into the continuous loop() function
void loop()
{
    anemometer_1.meassure();
    anemometer_2.meassure();
    anemometer_3.meassure();

    if (second() != last_second) {
        every_second();
        if (minute() != last_minute) {
            every_minute();
            if (hour() != last_hour) {
                every_hour();
            }
        }        
    }
}