/* ********************************************
   Modul : tools.c

   Hilfsroutinen

   Autor : Christian Julius
   ******************************************** */

/* -------------- Standard Include Files ----------------- */
#include <stdbool.h>
#include <stdint.h>

/* --------------- Special Include Files / Librarys ------ */
#include "tm_stm32f4_rtc.h"
#include "tm_stm32f4_disco.h"
#include "tm_stm32f4_nrf24l01_cj.h"
#include "tm_stm32f4_rtc.h"
#include "tm_stm32f4_bmp180.h"
#include "tm_stm32f4_delay.h"

/* -------------- Projekt   Include Files ---------------- */
#include "tools.h"
#include "config.h"
#include "eeprom.h"

/* -------------- Typen Deklarationen--------------------- */
typedef struct mydata {
    float    P_Now,                               // Aktueller Druckwert
             T_Now;                            // Aktueller Temperaturwert

    float    T_MaxInHistory,                              // Maximaler gemessener Temperaturwert
             T_MinInHistory;                              // Minimaler gemessener Temperaturwert

    uint32_t P_MaxInHistory,                              // Maximaler gemessener Druckwert
             P_MinInHistory;                              // Minimaler gemessener Druckwert
    float    P_AvgInHistory;
} mydata_t;

/* -------------- Defintionen ---------------------------- */
#define MBAR_STURMTIEF       970     // kleiner 980 mBar
#define MBAR_TIEF            990     // 990 - 1010
#define MBAR_NEUTRAL         1010    // 1010 - 1016
#define MBARHOCH             1016    // > 1016
#define RF_CHANNEL           10
#define E2P_DATA_VALID      0xaf     // Kennung für gültige Daten
#define E2P_DV_ADR          0xffff   // Adresse der Kennung


/* -------------- Private globale Variablen --------------- */
TM_BMP180_t         BMP180;                      // BMP180 Sensor Daten
event_timer_t       timer;                      // Zeiten (ISR)
uint8_t leds[5] = {LED_OFF,LED_OFF,LED_OFF,LED_OFF,LED_OFF};

/* -------------- Private Functions --------------- */


///////////////////////////////////////////////////////////////
// Interrupthandler für die RTC Wakeup, 1s Zyklus
// Holt die CPU aus dem Stop Modus heraus
///////////////////////////////////////////////////////////////

const uint16_t color[5] = {WLED_ROT1, WLED_ROT2, WLED_GLB, WLED_GRN, WLED_BLAU};

void TM_RTC_RequestHandler()
{
    /* Bedienung der LEDs auf dem Board
       0 =  aus
       1 =  ein
       2 = blinkt mit 1Hz
   */

    for (uint16_t i = 0; i < 5; i++) {
        if      (leds[i] == LED_OFF)   GPIO_ResetPin(GPIOE,color[i]);
        else if (leds[i] == LED_ON)    GPIO_SetPin(GPIOE,color[i]);
        else if (leds[i] == LED_BLINK) GPIO_TogglePin(GPIOE,color[i]);
    }

    // Datum & Zeit in Struct auslesen
    TM_RTC_GetDateTime(&Time, TM_RTC_Format_BIN);
    flags.rtc_1s = true;

    // Timer bereits resettet?
    if (timer.init != 0xaffe)
    {
        timer.init = 0xaffe;
        timer.sec  = Time.seconds;
        timer.min  = Time.minutes;
        timer.min15 = Time.minutes;
        timer.min30 = 0;
        timer.hour = 0;
        timer.day  = 0;

        flags.rtc_1s    = false;
        flags.rtc_1min  = false;
        flags.rtc_1hr   = false;
        flags.rtc_30min = false;
        flags.rtc_15min = false;
        flags.rtc_1day  = false;
        return;
    }

    // Zeiten im 24h Format hochzaehlen, Flags setzen
    if (++timer.sec % 60 == 0) {
        timer.sec = 0;
        flags.rtc_1min = true;

        // 15 Minuten Timer
        if (++timer.min15 % 15 == 0) {
              flags.rtc_15min = true;
              timer.min15 = 0;
        }

        // 30 Minuten Timer
        if (++timer.min30 % 30 == 0 ) {
              flags.rtc_30min = true;
              timer.min30 = 0;
        }

        // 60 Minuten Timer
        if (++timer.min % 60 == 0) {
            timer.min = 0;
            flags.rtc_1hr = true;
            if (++timer.hour > 23)
                timer.hour = 0;
                flags.rtc_1day = true;
                if (++timer.day > 99)
                    timer.day = 0;
        }
    }
}

/*  ==================================================
    Liest den Zustand eines der DIP Switches aus
    Rückgabe: 1 = Switch ON, 0 = Switch OFF
    ================================================== */
uint8_t Get_DIP_Switch(uint8_t nrSwitch) {

    uint8_t status;

    switch (nrSwitch) {
        case 1: status =  GPIO_GetInputPin(GPIOE,DIP_Switch_1);
                break;
        case 2: status =  GPIO_GetInputPin(GPIOE,DIP_Switch_2);
                break;
        default: return 0;
    }

    // Schalter oben gegen Masse?
    if (status == 0)
        return ENABLE;

    return DISABLE;
}


/* /////////////////////////////////////////////////
    Schreibt den gesamten Work Struct ins E2PROM
    Eingabe: Zeiger auf Datenbereich, Anzahl Bytes
    Rückgabe: 0 = alles ok, 1 = Fehler
   ////////////////////////////////////////////// */

uint8_t e2p_Backup_Work(uint8_t *data, size_t nrBytes)
{
    if (!I2C_initialized) {
        e2p_Init_I2C(I2C_CHANNEL);
        I2C_initialized = true;
    }

    // Daten als ungültig markieren
    if (e2p_Write_Byte(E2P_DV_ADR,0xff) != SUCCESS)
        return ERROR;

    // Ganzen Struct wegschreiben
    if (e2p_PageWrite(data,E2P_DATA_START,nrBytes) != SUCCESS) {
        return ERROR;
    }

#ifdef E2P_COMPARE_DATA
    // Vergleiche die Daten, ob sie richtig geschrieben wurden
    uint16_t adr = E2P_DATA_START;
    for (uint16_t i = 0; i< nrBytes; i++) {
           if ( e2p_Read_Byte(adr++) != *(data++) ) {
               return ERROR;
           }
    }
#endif

    // Gültige Kennung einschreiben
    if (e2p_Write_Byte(E2P_DV_ADR,E2P_DATA_VALID) != SUCCESS)
        return ERROR;

    e2p_DeInit_I2C();

    return SUCCESS;
}

/* /////////////////////////////////////////////////
    Liest den Inhalt des E2Proms zurück in den Struct

    Eingabe: target = Zieladresse im Speicher
             nrBytes = Anzahl Bytes zu lesen

    Rückgabe: 0 = alles ok, 1 = Fehler
   ////////////////////////////////////////////// */

uint8_t e2p_Restore_Work(uint8_t *target, size_t nrBytes)
{
    if (!I2C_initialized) {
        e2p_Init_I2C(I2C_CHANNEL);
        I2C_initialized = true;
    }

    // Gültige Daten vorhanden?
    if (e2p_Read_Byte(E2P_DV_ADR) != E2P_DATA_VALID)
          return ERROR;

    // Kopierschleife
    if (e2p_Read_MultiBytes(target,E2P_DATA_START,nrBytes)!= SUCCESS)
            return ERROR;

    return SUCCESS;
}

// ---------------------------------------------------------------------------

// NRF24L01: Warte auf das Ende der Übertragung
static uint8_t NRF24L01_WaitForTransmission()
{
    TM_NRF24L01_Transmit_Status_t transmissionStatus;
    uint32_t timeout = 20000;

    do {
        transmissionStatus = TM_NRF24L01_GetTransmissionStatus();
        if (--timeout == 0) {
            GPIO_ResetPin(GPIOD,LED_ORANGE);
            return ERROR;
        }
    } while (transmissionStatus == TM_NRF24L01_Transmit_Status_Sending);

    return SUCCESS;

}

//////////////////////////////////////////////
// Sendet einen Datensatz über das Funkmodul
//////////////////////////////////////////////

static uint8_t TxAddress[] = { 0x02, 0x02, 0x19, 0x68, 0x00 };   // Adresse des Empfänger Moduls
static uint8_t MyAddress[] = { 0x30, 0x09, 0x20, 0x02, 0x00 };  // Meine Moduladresse

uint8_t __attribute__((optimize(0))) tls_RFSendWork(Work_t *Data)
{
    TM_NRF24L01_Transmit_Status_t transmissionStatus;
    mydata_t tx;

    if (sizeof(tx) > 32)
        return ERROR;

    TM_NRF24L01_Init(RF_CHANNEL,sizeof(tx));
    TM_NRF24L01_SetRF(TM_NRF24L01_DataRate_250k,TM_NRF24L01_OutputPower_0dBm);    // RF Modul einstellen

    // Datensatz zusammenbauen
    tx.P_Now     = Data->P_Now;
    tx.T_Now  = Data->T_Now;
    tx.P_AvgInHistory    = Data->P_AvgInHistory;
    tx.P_MaxInHistory    = Data->P_MaxInHistory;
    tx.P_MinInHistory    = Data->P_MinInHistory;
    tx.T_MaxInHistory    = Data->T_MaxInHistory;
    tx.T_MinInHistory    = Data->T_MinInHistory;

    TM_NRF24L01_SetMyAddress(MyAddress);        // Setze RX Adresse
    TM_NRF24L01_SetTxAddress(TxAddress);        // Setze TX Adresse

    // Senden und warten bis fertig
    GPIO_SetPin(GPIOD,LED_ORANGE);

    uint32_t timeout = 20000;
    TM_NRF24L01_Transmit((uint8_t*)&tx);
    if (NRF24L01_WaitForTransmission()!=SUCCESS)
        return ERROR;

    // LED aus, Sender aus
    GPIO_ResetPin(GPIOD,LED_ORANGE);
    TM_NRF24L01_PowerDown();

    return SUCCESS;

}

//////////////////////////////////////////////////////
// Sendet einen großen Datenblock als einzelne Pakete
//////////////////////////////////////////////////////

uint8_t tls_RFTransmitMultiBytes(uint8_t* data, uint16_t count)
{
    #define PAKETSIZE   32
    TM_NRF24L01_Transmit_Status_t transmissionStatus;
    uint16_t blocks, rest;

    blocks     = count / PAKETSIZE;
    rest       = count - (blocks * PAKETSIZE);

    // RF Modul einstellen
    TM_NRF24L01_Init(RF_CHANNEL,PAKETSIZE);
    TM_NRF24L01_SetRF(TM_NRF24L01_DataRate_1M,TM_NRF24L01_OutputPower_0dBm);
    TM_NRF24L01_SetMyAddress(MyAddress);
    TM_NRF24L01_SetTxAddress(TxAddress);

    // Ganze Blocks übertragen
    GPIO_SetPin(GPIOD,LED_ORANGE);
    for (uint16_t i = 0;i < blocks; i++)
    {
        TM_NRF24L01_Transmit(data);
        if (NRF24L01_WaitForTransmission()!=SUCCESS)
            return ERROR;

        data += PAKETSIZE;
        // Empfänger Zeit lassen zu verarbeiten
        Delayms(1);
    }

    // Rest übertragen
    TM_NRF24L01_Init(RF_CHANNEL,rest);
    TM_NRF24L01_SetRF(TM_NRF24L01_DataRate_1M,TM_NRF24L01_OutputPower_0dBm);
    TM_NRF24L01_Transmit(data);
    if (NRF24L01_WaitForTransmission()!=SUCCESS)
         return ERROR;

    GPIO_ResetPin(GPIOD,LED_ORANGE);
    return SUCCESS;
}

////////////////////////////////////////////////////////
// Auslesen des Drucksensors für Druck und Temperatur
////////////////////////////////////////////////////////

uint8_t Init_BMP180()
{
 // BMP180 Sensor initialisieren
    if (TM_BMP180_Init(&BMP180) != TM_BMP180_Result_Ok)
            return ERROR;

    return SUCCESS;
}


float BMP180_ReadTemperature()
{
        /* Start temperature conversion */
        TM_BMP180_StartTemperature(&BMP180);

        /* Wait delay */
        Delay(BMP180.Delay);

        /* Read temperature first */
        TM_BMP180_ReadTemperature(&BMP180);

        return BMP180.Temperature-1.3;
}

float BMP180_ReadPressure()
{
        /* Start pressure conversion at ultra high resolution */
        TM_BMP180_StartPressure(&BMP180, TM_BMP180_Oversampling_HighResolution);
        Delay(BMP180.Delay);

        /* Read pressure value */
        TM_BMP180_ReadPressure(&BMP180);

        // Auf mBar Meereshöhe umrechnen
        double pressure_normal = (double)BMP180.Pressure / 100;

        if ((pressure_normal < MIN_PRES) || (pressure_normal > MAX_PRES))
            return 1013.0;

        return (pressure_normal / pow(1-(0.0065 * HOEHE_ORT) / 288.15, 5.255));
}

// Den Zustand fuer eine LED setzen
// Zustand= ein/aus/blink
void tls_SetWetterLED(uint8_t nr, uint8_t status)
{
    if (nr > 4) return;
    leds[nr] = status;
}

// Alle LED aus
void tls_ResetWetterLEDs()
{
    for (uint8_t i = 0; i < 5; i++)
    leds[i] = LED_OFF;
}

