#include "stm32l0xx_hal_conf.h"
#include "stm32l0xx_hal.h"
#include <math.h>
#include "init.h"
#include "sst26.h"
#include "akcelerometr.h"

// ############################################################################################################
// ################################################# DEFINICE #################################################
// ############################################################################################################

#define             ABS(x)                          ((x) > 0 ? (x) : -(x))

#define             FLASH_ADR_POC                   0x010000 
#define             FLASH_ADR_MAX                   0x3FFE00 
#define             FLASH_ADR_KONEC                 0x3FFF00
#define             N_ITEM                          60
#define             PAGE_SIZE                       256

#define             MCU_ID_ADDRESS                  0x1FF80050

#define             MONITOR_PARAM_C                 4
#define             MONITOR_PARAM_C2                7           // 4 pred parametry priznak 2007-2009 
#define             MONITOR_PARAM_C2_MONITORING     7 
#define             PARAM_COUNT                     7           //pocet paranetru 
#define             PARAM_LENGHT                    18 
#define             MONITORING_ITEM_SIZE            11

#define             HW_VERHI                        0           // Zavadec HW_VERHI.HW_VERLO (Zavadec 1.0)
#define             HW_VERLO                        0  
#define             GMS_VERHI                       2           // Firmware GMS_VERHI.GMS_VERLO (Firmware 1.0)
#define             GMS_VERLO                       4           // 2 vycitani 2 minut, 4 pridan runtime

#define             OSAX_U_0                        0 
#define             OSAY_U_1                        1 
#define             OSAZ_U_2                        2 
#define             FILTR_U_3                       3 
#define             FLASH_U_4                       4 
#define             RUNTIME_U_5                     5
#define             PMEZ_U_6                        6

// ############################################################################################################
// ################################################# STRUKTURY ################################################
// ############################################################################################################

// index, typ, prepocet al, prepocet ah, prepocet bl, prepocet bh, operace 
// operace 0 - nedelat nic, 1 - plus, 2 - minus, 3 - krat, 4 - deleno 
// aplikuje se striktne napred operace a, pak operace b 
const uint16_t bb_metadata[] = { 
// 1 2      3    4        5     6       7 
2000, 1,     0, 17530,     0, 17096,  0x0003,   // 1.   Realny cas 
2002, 1,     0, 17204,     0, 16256,  0x0002,   // 2002. Osa X  
2004, 1,     0, 17204,     0, 16256,  0x0002,   // 2004. Osa Y 
2006, 1,     0, 17204,     0, 16256,  0x0002,   // 2004. Osa Z 
};

const uint16_t bb_metadata2[] ={
// prvni A, potom B
//           A       A      B       B      B A    StartBit  LengthBit
//2000, 1,     0,  17530,     0,  17096,   0x0003,   32,         24,       0, 0,    //cas-itemNumber - zruseno pro verzi 2.0
2002, 1,     0, 17204,      0, 16256,    0x0002,    0,          9,       0, 0,  // 2002. Osa X 
2003, 1,     0, 17204,      0, 16256,    0x0002,   10,          9,       0, 0,  // 2004. Osa Y 
2004, 1,     0, 17204,      0, 16256,    0x0002,   20,          9,       0, 0,  // 2004. Osa Z 
2007, 1,     0, 17204,      0, 16256,    0x0000,    9,          1,       0, 0,  // priznak 2007. Osa X 
2008, 1,     0, 17204,      0, 16256,    0x0000,   19,          1,       0, 0,  // priznak 2008. Osa Y
2009, 1,     0, 17204,      0, 16256,    0x0000,   29,          1,       0, 0,  // priznak 2009. Osa Z
};

const uint16_t bb_metadata2_monitoring[] ={
// prvni A, potom B
//           A       A      B       B      B A    StartBit  LengthBit
2000, 1,     0,  17530,     0,  17096,   0x0003,   32,          24,       0, 0,  // cas 
2002, 1,     0, 17204,      0, 16256,    0x0002,    0,          16,       0, 0,  // 2002. Osa X  
2003, 1,     0, 17204,      0, 16256,    0x0002,   16,          16,       0, 0,  // 2004. Osa Y 
2004, 1,     0, 17204,      0, 16256,    0x0002,   32,          16,       0, 0,  // 2004. Osa Z 
2005, 1,     0, 16672,      0, 16256,    0x0000,   64,          16,       0, 0,  // teplota 
2001, 1,     0, 17530,      0, 16256,    0x0004,   48,          16,       0, 0,  // napeti
2006, 1,     0, 17096,      0, 16256,    0x0000,   80,           8,       0, 0,  // kapacita 
};

const uint16_t default_data[] = {
// A -index, B, C - min, D - max, E, F, G - defaultni hodnota
//A B     C      D      E  F  G         G    I
600,2,    1,     180,   1, 0, 16256,    60,  0, // meze osa X
601,2,    1,     180,   1, 0, 16256,    60,  0, // meze osa Y
602,2,    1,     180,   1, 0, 16256,    60,  0, // meze osa Z
603,2,    1,      20,   1, 0, 16256,    5,   0, // nastaveni filtru
604,2,    0,   16127,   1, 0, 16256,    0,   0, // zaplneni FLASH pameti
605,2,    0,   65500,   1, 0, 16256, 65500,  0, // nastaveni RunTime 
606,2,    0,     180,   1, 0, 16256,    0,   0, // meze progres upozornovani
};

// Struktura parametru v RAM/EEPROM 
typedef struct { 
    uint16_t index; 
    uint16_t typ; 
    uint16_t minimum; 
    uint16_t maximum; 
    uint16_t prirustek; 
    uint16_t prepocetl; 
    uint16_t prepoceth; 
    uint16_t hodnota; 
    uint16_t rezerva; 
} TConfigItem; 
// Hromadna struktura parametru 
typedef TConfigItem TConfigArray[PARAM_COUNT + 1]; 

// ############################################################################################################
// ################################################# KOMUNIKACE S PC ##########################################
// ############################################################################################################ 

void Com_ProcessRequest();
void sending (unsigned int lastOp,unsigned long long int lastIndex);
void receiving (unsigned int operace);

// ############################################################################################################
// ################################################# PROMENNE #################################################
// ############################################################################################################

// promenne
volatile            TConfigArray        konfigurace;
extern              RTC_HandleTypeDef   hrtc; 
extern volatile     uint32_t 	        adcArray[150];
extern              uint32_t    		Priznak_Clock;                          // casovani nekonecne smycky po 100ms       
                    uint8_t             Priznak_USB                 = 0;        // 1 pripojeno, 0 nepripojeno
                    uint8_t             Priznak_Zapnuto             = 0;        // 1 bylo zapnuto magnetem, 0 bylo zapnuto USBckem
                    uint8_t             Counter_USB                 = 0;
                    uint16_t            Counter_Magnet              = 0;
                    uint16_t            Counter_Battery             = 200;      // 200 -> nebude probihat ve stejnou dobu jako ukladani dat na FLASH
                    uint16_t            Counter_LED                 = 0;        // counter pro blikani LED pri nabijeni
                    uint8_t             Counter_Buzzer              = 0;        // counter slouzici k ovladani bzucaku
                    uint8_t             Counter_Motor               = 0;        // counter slouzici k ovladani motorku - spravne zapinani, vypinani
                    uint8_t             Uzivatel_Alarm              = 0;        // priznaky vykroceni z mezi 

                    //promenne pro obsluhu akcelerometru
volatile            int16_t             Sklon_Aktual                = 0;
volatile            int16_t             Naklon_Aktual               = 0;
volatile            int16_t             Odklon_Aktual               = 0;
                    int16_t             Max_Aktual                  = 0;
                    
                    int32_t             Sklon_Prumer                = 0;
                    int32_t             Naklon_Prumer               = 0;
                    int32_t             Odklon_Prumer               = 0;
                    
                    int16_t             Sklon_Offset                = 0;
                    int16_t             Naklon_Offset               = 0;
                    int16_t             Odklon_Offset               = 0;
                    
                    int16_t             FiltrX_Max                  = 0;
                    int16_t             FiltrY_Max                  = 0;
                    int16_t             FiltrZ_Max                  = 0;
                    int8_t              Pocet_mereni                = 0;        // pokud predelan buzzer - nevyuzita promenna
                    
                    int16_t            FiltrDataX[20]              ={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};        //pole pro filtr
                    int16_t            FiltrX                      = 0;    //filtrovane data
                    int16_t            FiltrDataY[20]              ={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};                   
                    int16_t            FiltrY                      = 0;                      
                    int16_t            FiltrDataZ[20]              ={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};                    
                    int16_t            FiltrZ                      = 0;                   
                    uint32_t            FiltrCounter                = 0;
                    
                    //nastavitelne promenne
unsigned int                            auxPointer;
                    uint8_t             Filtr                       = 5;     //jak moc filtrovat data (min 1, max 20)
                    uint16_t            MezX                        = 50;    //meze pro upozorneni
                    uint16_t            MezY                        = 50;    //meze pro upozorneni
                    uint16_t            MezZ                        = 50;    //meze pro upozorneni
                    uint16_t            Mez_Progres                 = 0;     //progresivni upozornovani
                    uint16_t            RunTime                     = 0;     //nastavitelna hodnota, jak dlouho pojede
                    
                    //zapis do FLASH
                    uint32_t            Buffer[64]                  ={(180<<20 | 180<<10 | 180)};
                    uint8_t             Pozice_buffer               = 1;    // prvni minuta ma o 1s mene kvuli inicializaci
                    uint8_t             CRCkontrola                 = 0;
                    
                    int8_t              Sekunda_aktualni            = 0;
                    int8_t              Sekunda_minula              = 0;
                     
                    uint32_t            FLASH_addr_aktual           = FLASH_ADR_POC;
                    uint32_t            FLASH_addr_ukonceni         = 0;                   
                
                    //cas
                    uint8_t             WeekDay                     = 0;
                    uint8_t             Month                       = 0;
                    uint8_t             Year                        = 0;
                    uint8_t             Hours                       = 0;
                    uint8_t             Mins                        = 0;
                    uint8_t             Sec                         = 0;
                    uint8_t             Date                        = 0;

 
                    //UART - komunikace s PC
volatile unsigned char                  txActiveBuffer[512];                   //buffer pro odesilani dat UART
volatile unsigned char                  rxPassiveBuffer[255];
extern              uint8_t             status_rxbuffer[20];                    //buffer pro prijata data UART
                    uint8_t             UART_RX_byte                = 0;        //posleni prijaty byte UART (je potreba?)
                    uint32_t            ptrTxATop                   = 0;
unsigned char                           ptrRxPDown                  = 0;
                    uint32_t            crc32input                  = 0;
                    uint32_t            crc32output                 = 0;
                    uint32_t            crcStartIndex               = 0;
                    uint32_t            crcEndIndex                 = 0;
                    uint8_t             NumberOfRuns                = 1;        //pocet runu
                    uint8_t             RunID[5]                    ={100,101,102,103,104}; //id runu
                    uint8_t             idOfReqRun                  = 0;
                    uint8_t             confirmOper                 = 0;
                    uint32_t            time                        = 0;
                    uint16_t            Monitoring_PacketCounter    = 0;
                    //UART - Monitoring 
volatile            int16_t             Temp                        = 0;
volatile            int32_t             Volt                        = 0;
                    uint32_t            Volt_prumer                 = 0;
                    uint32_t            Volt_Counter                = 0;
volatile            uint32_t            Capacity                    = 0;
                    uint32_t            Capacity_actual             = 0;
                    uint32_t            Capacity_counter            = 0;
                    uint8_t             Monitoring                  = 0;                

// ############################################################################################################
// ################################################# MAIN #####################################################
// ############################################################################################################
                
int main (void) 
{
    HAL_Init();
    CLK_1MHz_Init();
    GPIO_Init();


    ADC_NAP_Init();
    BAT_MEAS_Start();

    SPI_Init();
    mgmCRC32_Init();
    HAL_Delay(5); //delay pro zmereni napeti baterie

    Capacity = BAT_CAPACITY(adcArray[2]);   // vypocteni kapacity baterie pri zapnuti

    if((BAT_VOLTAGE(adcArray[2]) < 3800) & !HAL_GPIO_ReadPin(USB_CONNECT))  
    {       
        BAT_MEAS_Stop();
        SOUND_LowBat();
        SOUND_TurnOff();
        HAL_Delay(2000);
        SLEEP();
    }
    else
    {
        BAT_MEAS_Stop();
    }
 
    SystemCoreClockUpdate(); 
    SysTick_Config(System_100ms);	
    PERIFERIE_ON();
    HAL_Delay(5);    //delay power up
    Akcelerometr_Init(); 
    FLASH_Init();
    HAL_Delay(5);   // delay pred prvnim vycitanim hodnot z akcelerometru dle datasheetu
    Akcelerometr_SklonNaklon(&Sklon_Offset, &Naklon_Offset, &Odklon_Offset);        // pocatecni hodnoty
    Sklon_Offset += 180;
    Naklon_Offset += 180;
    Odklon_Offset += 180;
    for(int i = 0; i<20; i++)    //prvotni naplneni pole pro filtraci
    {
        FiltrDataX[i] =  Sklon_Offset;
        FiltrDataY[i] =  Naklon_Offset;
        FiltrDataZ[i] =  Odklon_Offset;
    }
     
    //pokud zapnuto magnetem 
    if(!HAL_GPIO_ReadPin(USB_CONNECT))
    {
        Priznak_Zapnuto = 1;
        SOUND_TurnOn();
        HAL_Delay(500);
        SysTick_Config(System_100ms);
        RTC_Init();
    
        HAL_PWR_EnableBkUpAccess(); 
        HAL_RTCEx_BKUPWrite(&hrtc,  RTC_BKP_DR3, (uint32_t)(Capacity)); //pokud zapnuto magnetem, ulozim kapacitu do BACKUP
        HAL_PWR_DisableBkUpAccess();
    }
    else
    {
        Priznak_Zapnuto = 0;
        RTC_Init();
    }

    // Naplneni Buffru hodnotami
    for(auxPointer = 0; auxPointer<64; auxPointer++)
        Buffer[auxPointer] = (180<<20 | 180<<10 | 180);

    // Nacteni defaultniho nastaveni 
    for (auxPointer = 0; auxPointer < (PARAM_LENGHT*PARAM_COUNT); auxPointer++)
        *(char *)((char *)&konfigurace + auxPointer) = *(char *)((char *)&default_data + auxPointer);
    
    // Kontrola mezi BACK UP registru
    HAL_PWR_EnableBkUpAccess();
    if(HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR0))  //pokud je mez v backup registrech
    {
        konfigurace[OSAX_U_0].hodnota = (HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR0)>>20) & 0b0000001111111111;
        konfigurace[OSAY_U_1].hodnota = (HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR0)>>10) & 0b0000001111111111;
        konfigurace[OSAZ_U_2].hodnota = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR0) & 0b0000001111111111;       
    }
    HAL_PWR_DisableBkUpAccess();
    MezX =  konfigurace[OSAX_U_0].hodnota;       // vycist meze
    MezY =  konfigurace[OSAY_U_1].hodnota;
    MezZ =  konfigurace[OSAZ_U_2].hodnota;

    // Nacteni delky filtru z BACKUP registru
    if(HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR1) & 0x0000FFFF)  // pokud je filtr v backup registrech (low 16 bites)
        konfigurace[FILTR_U_3].hodnota = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR1) & 0x0000FFFF;      
    Filtr = konfigurace[FILTR_U_3].hodnota;     // vycist filtr    

    // Nacteni FLASH adresy z BACKUP registru
    if(HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR2))  // pokud je posledni pozice flash v backupregistrech
        FLASH_addr_aktual = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR2); // vycist flash adresu
    
    // Nacteni progresivni meze z backup registru
    if(HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR1) & 0xFFFF0000) //(high 16 bites)
       konfigurace[PMEZ_U_6].hodnota = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR1)>>16;   
    Mez_Progres = konfigurace[PMEZ_U_6].hodnota;
    
    // Nacteni RunTime z backup registru
    if(HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR4))
    {   
        if(HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR4) == 65501)
            konfigurace[RUNTIME_U_5].hodnota = 0;
        else
            konfigurace[RUNTIME_U_5].hodnota = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR4);
    }
    RunTime = konfigurace[RUNTIME_U_5].hodnota;
    
    if((RunTime == 0) && (!HAL_GPIO_ReadPin(USB_CONNECT)))     // dosel RunTime a neni zapnute USB
    {       
        SOUND_LowBat();
        SOUND_TurnOff();
        HAL_Delay(2000);
        SLEEP();
    }    
// ################################################# FOR #####################################################
	for(;;) /* loop forever */
	{     
        // KONTROLA PRIPOJENI USB + VYPINANI MAGNETEM + MERENI NAPETI BATERIE
        if(Priznak_Clock == 1)  
        {
            // Kontrola baterie 59. a 60. sekundu
            Counter_Battery++;
            if(Counter_Battery == 590)  // zapnout mereni v 59. sekunde
                BAT_MEAS_Start();
            if(Counter_Battery == 600)  // vyhodnotit napeti baterie
            {
                // Kapacita  baterie
               Capacity_actual = BAT_CAPACITY(adcArray[2]);         // vypocet kapacity a ulozeni do BACKUP
               if(Capacity_actual < Capacity)
                    Capacity = Capacity_actual;
               HAL_PWR_EnableBkUpAccess();  // unlock BACKUP
               HAL_RTCEx_BKUPWrite(&hrtc,  RTC_BKP_DR3, (uint32_t)(Capacity)); 
               HAL_PWR_DisableBkUpAccess(); 
               
               // RunTime 
               if((RunTime != 65500) && (RunTime >0))  // pokud nastavim 65500, jede nekonecne
                  RunTime--;
                      
               // Test slabe baterie a vypinani & RunTime    
               BAT_MEAS_Stop();
               if((BAT_VOLTAGE(adcArray[2]) < 3300)|| (RunTime == 0))  
               {                                  
                    SOUND_LowBat();
                    SOUND_TurnOff();
                    
                    // Ulozeni adresy flash, kde se zkoncilo s ukladanim
                    HAL_PWR_EnableBkUpAccess(); 
                    HAL_RTCEx_BKUPWrite(&hrtc,  RTC_BKP_DR2, (uint32_t)(FLASH_addr_aktual));
                    HAL_RTCEx_BKUPWrite(&hrtc,  RTC_BKP_DR4, (uint32_t)(RunTime));
                    HAL_PWR_DisableBkUpAccess(); 
               
                    if(RunTime == 0)
                    {
 //                       SOUND_LowBat();
                        RunTime = 65501;
                        HAL_PWR_EnableBkUpAccess(); 
                        HAL_RTCEx_BKUPWrite(&hrtc,  RTC_BKP_DR4, (uint32_t)(RunTime));
                        HAL_PWR_DisableBkUpAccess();
                    }
               
                    PERIFERIE_OFF();
                    SLEEP();
                }
                Counter_Battery = 0;
            }
        
            // Vypnout magnetem
            if(HAL_GPIO_ReadPin(WAKEUP_IN)) 
            {
                Counter_Magnet++;
                if(Counter_Magnet >=5)         // V2.3 - 20
                {
                    Counter_Magnet = 0;
                    Led_ON();
                    SOUND_TurnOff();

                    //ulozeni adresy flash, kde se zkoncilo s ukladanim & ulozeni RunTime
                    HAL_PWR_EnableBkUpAccess(); 
                    HAL_RTCEx_BKUPWrite(&hrtc,  RTC_BKP_DR2, (uint32_t)(FLASH_addr_aktual));
                    HAL_RTCEx_BKUPWrite(&hrtc,  RTC_BKP_DR4, (uint32_t)(RunTime));
                    HAL_PWR_DisableBkUpAccess(); 
                
                    PERIFERIE_OFF();
                    HAL_Delay(2500);  // cekat 2.5s
                    Led_OFF();
                    GPIO_Deinit();                
                    Periph_Deinit();
                    CLK_Deinit();                    
                    SLEEP();
                }
            }
            else
                Counter_Magnet = 0;
            
            // Doslo k pripojeni USB - nastaveni priznaku
            if(!Priznak_USB & HAL_GPIO_ReadPin(USB_CONNECT))
            {
                Counter_USB++;
                if(Counter_USB >=3)         // V2.3 - 10
                {
                    Counter_USB = 0;
                    Priznak_USB = 1;
                }
            }
            else
                Counter_USB = 0;
                     
        // OBSLUHA AKCELEROMETRU A UKLADANI DO BUFFERU + SIGNALIZACE VYBOCENI Z HRANIC
            if(Priznak_USB == 0)
            {
                PWM_FET_Stop();
                HAL_GPIO_WritePin(MOT_ONOFF ,MOT_OFF);
                Akcelerometr_SklonNaklon(&Sklon_Aktual, &Naklon_Aktual, &Odklon_Aktual);
            
                //osa x                
                FiltrDataX[FiltrCounter%Filtr] = Sklon_Aktual+180;
                FiltrX = 0;
                for(int i = 0; i< Filtr; i++) //suma                   
                   FiltrX += FiltrDataX[i];
                FiltrX = FiltrX / Filtr;      //prumer
                
                if(ABS(FiltrX-Sklon_Offset) > ABS(FiltrX_Max))   //ulozeni maxima
                {
                    FiltrX_Max = FiltrX;
                    FiltrX_Max = FiltrX_Max - Sklon_Offset;
                }
            
                //osa y
                FiltrDataY[FiltrCounter%Filtr] = Naklon_Aktual+180;
                FiltrY = 0;
                for(int i = 0; i< Filtr; i++)
                   FiltrY += FiltrDataY[i];
                FiltrY = FiltrY / Filtr;
                
                if(ABS(FiltrY-Naklon_Offset) > ABS(FiltrY_Max))
                {
                    FiltrY_Max = FiltrY; 
                    FiltrY_Max = FiltrY_Max - Naklon_Offset;
                }
              
                //osa z            
                FiltrDataZ[FiltrCounter%Filtr] = Odklon_Aktual+180;
                FiltrZ = 0;
                for(int i = 0; i< Filtr; i++)                    
                   FiltrZ += FiltrDataZ[i];
                FiltrZ = FiltrZ / Filtr; 
                
                if(ABS(FiltrZ-Odklon_Offset) > ABS(FiltrZ_Max))
                {    
                    FiltrZ_Max = FiltrZ;
                    FiltrZ_Max = FiltrZ_Max - Odklon_Offset; 
                }        
                FiltrCounter++;
                              
                Pocet_mereni ++;
                
                //uprava nerealnych dat
                if(FiltrX_Max >180 | FiltrX_Max < -180)
                    FiltrX_Max = 0;
                if(FiltrY_Max >180 | FiltrY_Max < -180)
                    FiltrY_Max = 0;                
                if(FiltrZ_Max >180 | FiltrZ_Max < -180)
                    FiltrZ_Max = 0;                  
            
                Sekunda_aktualni = RTC_TellSec();
            
                // kazdych 100ms kontrolovat naklon a svitit
                if((ABS(FiltrX_Max) > MezX) | (ABS(FiltrY_Max) > MezY) | (ABS(FiltrZ_Max) > MezZ))            // mimo meze, upozornit
                {
                    // Progresivni upozorneni
                    if((ABS(FiltrX_Max) > MezX+Mez_Progres) | (ABS(FiltrY_Max) > MezY+Mez_Progres) | (ABS(FiltrZ_Max) > MezZ+Mez_Progres)) // vetsi upozorneni
                    {
                        // ovladani bzucaku a blikani s ledkou
                        if(Counter_Buzzer == 0 | (Counter_Buzzer % 5))
                        {                           
                            Led_OFF();
                            PWM_FET_Set(482,105);
                            if(Counter_Buzzer == 0)
                                Led_ON();
                        }
                        else
                            Led_ON();
                        
                        // vibrovat
                        if(Counter_Motor == 0 | !(Counter_Motor % 10))   
                            HAL_GPIO_WritePin(MOT_ONOFF ,MOT_ON);
                   } 
                   else // mensi upozorneni
                   {
                        // ovladani bzucaku a blikani s ledkou
                        Led_OFF();
                        if(Counter_Buzzer == 0 | !(Counter_Buzzer % 10))
                        {                                                     
                            PWM_FET_Set(2000,1000);
                            Led_ON();
                        }
                        
                        // vibrovat
                        if(Counter_Motor == 0 | !(Counter_Motor % 30))   
                            HAL_GPIO_WritePin(MOT_ONOFF ,MOT_ON);
                   }
                    
                    // ukladat priznak o vykroceni z mezi
                    if(ABS(FiltrX_Max) > MezX)
                       Uzivatel_Alarm |= 0b00000001; 
                    if(ABS(FiltrY_Max) > MezY)
                        Uzivatel_Alarm |= 0b00000010;
                    if(ABS(FiltrZ_Max) > MezZ)
                        Uzivatel_Alarm |= 0b00000100;
                    
                    Counter_Motor++; 
                    Counter_Buzzer++;
                }
                else
                {
                    Led_OFF();
                    Counter_Motor = 0;
                    Counter_Buzzer = 0;
                }
            
                // zmena sekundy = ulozit data do bufferu
                if(Sekunda_aktualni-Sekunda_minula)    
                {              
                    Buffer[Sekunda_aktualni] = ((FiltrZ_Max+180) << 20) + ((FiltrY_Max+180) << 10) + (FiltrX_Max+180);   //ulozeni dat
                    
                    // ulozeni infa o alarmu
                    if(Uzivatel_Alarm & 0b00000001) // alarm x
                        Buffer[Sekunda_aktualni] = Buffer[Sekunda_aktualni] | (1<<9);
                    if(Uzivatel_Alarm & 0b00000010) // alarm y
                        Buffer[Sekunda_aktualni] = Buffer[Sekunda_aktualni] | (1<<19);
                    if(Uzivatel_Alarm & 0b00000100) // alarm z
                        Buffer[Sekunda_aktualni] = Buffer[Sekunda_aktualni] | (1<<29);
                
                    CRCkontrola ^= Buffer[Sekunda_aktualni];
                    CRCkontrola ^= Buffer[Sekunda_aktualni]>>8;
                    CRCkontrola ^= Buffer[Sekunda_aktualni]>>16;
                    CRCkontrola ^= Buffer[Sekunda_aktualni]>>24;
               
                    Pozice_buffer++;                      
                
                    Uzivatel_Alarm = 0;
                    Pocet_mereni = 0;
                    FiltrX_Max = 0;
                    FiltrY_Max = 0;
                    FiltrZ_Max = 0;
                }
            
                // 59. sekunda - cas, crc, ukladani celeho bufferu na FLASH
                if((Sekunda_aktualni == 59) & (Sekunda_aktualni-Sekunda_minula))
                {
                    RTC_TellTime(&Year, &WeekDay, &Month, &Date, &Hours, &Mins, &Sec);
                    Sec = 0;    //zapisuju data pro minulou minutu, misto 59 ukladam 0
                
                     Buffer[60] =(Date << 24) + (Hours << 16) + (Mins << 8) + Sec;    // cas
                    CRCkontrola ^= Buffer[60];      
                    CRCkontrola ^= Buffer[60]>>8;
                    CRCkontrola ^= Buffer[60]>>16;
                    CRCkontrola ^= Buffer[60]>>24;
                    Pozice_buffer++;
                
                    Buffer[61] = (WeekDay <<16) + (Year<<8) + Month;          // cas
                    CRCkontrola ^= Buffer[61];      
                    CRCkontrola ^= Buffer[61]>>8;
                    CRCkontrola ^= Buffer[61]>>16;
                    CRCkontrola ^= Buffer[61]>>24;
                    Pozice_buffer++;                
                              
                    Buffer[62] = (BAT_VOLTAGE(adcArray[2])<<16)+(ComputeTemperature(adcArray[0],adcArray[1]));    // napeti baterie + teplota
                    CRCkontrola ^= Buffer[62];      
                    CRCkontrola ^= Buffer[62]>>8;
                    CRCkontrola ^= Buffer[62]>>16;
                    CRCkontrola ^= Buffer[62]>>24;
                    Pozice_buffer++;  

                    Buffer[63] = CRCkontrola;            // ulozeni CRC       
                           
                    Pozice_buffer = 0;
                
                    if(FLASH_addr_aktual < FLASH_ADR_MAX)       // pokud je kam ulozit, ulozit a inkrementovat adresu
                    {
                        FLASH_Write((uint8_t *)Buffer, FLASH_addr_aktual ,256);                                      
                        FLASH_addr_aktual += 256;
                    }
 //                   Led_ON();             // V2.3 - ON

                }
                Sekunda_minula = Sekunda_aktualni;
            }
            Priznak_Clock = 0; // VYNULOVANI PRIZNAKU 100ms !!!!!!!!!!!!
        }   /*end of 100ms priznak */
    
    
        // PO PRIPOJENI USB
        if(Priznak_USB)
        {
            BAT_MEAS_Stop();
            HAL_Delay(1);
            PWM_FET_Stop();
            HAL_GPIO_WritePin(MOT_ONOFF ,MOT_OFF);
            SOUND_USBConnect();
            Led_ON();
 
            //Deinit
            GPIO_Deinit();
            Periph_Deinit();    //deinit SPI, DMA, ADC, UART
        
            CLK_32MHz_Init();
            SystemCoreClockUpdate(); 
 
            //Init
            GPIO_Init();
            ADC_NAP_Init();
            UART1_Init();
            SPI_Init();        
        
            SysTick_Config(System_100ms);
            
            BAT_MEAS_Start();
        
            //ulozeni adresy flash, kde se zkoncilo s ukladanim & cteni ulozene kapacity
            HAL_PWR_EnableBkUpAccess(); 
            HAL_RTCEx_BKUPWrite(&hrtc,  RTC_BKP_DR2, (uint32_t)(FLASH_addr_aktual)); // ukladani adresy
            Capacity = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR3);   // Cteni aktualni kapacity
            HAL_PWR_DisableBkUpAccess();   
            
            FLASH_addr_ukonceni = FLASH_addr_aktual;
            FLASH_addr_aktual = FLASH_ADR_POC;  // presun na zacatek FLASH
            Pozice_buffer = 70;                 // aby byla v prvnim kroku vyctena FLASH (stary protokol)
        
            konfigurace[FLASH_U_4].hodnota = (FLASH_addr_ukonceni - FLASH_ADR_POC) / 256; //vypocet obsazeneho mnozstvi radku (maximum je 16 127 radku)
            
            // smycka pro "pripojeno k USB"
            while(Priznak_USB)
            {
                if(Priznak_Clock == 1)  
                {
                    //vycitat data pouze pri monitoringu (jinak kolize na SPI pri komunikaci s FLASH a akcelerometrem)
                    if(Monitoring)  
                    {
                        Akcelerometr_SklonNaklon(&Sklon_Aktual, &Naklon_Aktual, &Odklon_Aktual);    
                        RTC_TellTime(&Year, &WeekDay, &Month, &Date, &Hours, &Mins, &Sec);
                    }
                    // Filtrovani napeti
                    Volt_prumer = 0;
                    for (Volt_Counter = 0; Volt_Counter < 50; Volt_Counter++) 
                            Volt_prumer += adcArray[(Volt_Counter*3)+2]&0xfff;
                    Volt_prumer /=50;
                    
                    Volt = BAT_VOLTAGE(Volt_prumer);
                

                    // vypocet kapacity
                    Capacity_counter++;
                    if(Capacity_counter == 800) //1x za 80s inkrementovat kapacitu o 1 (132min nabijeni = 7920s ->8000s=100%)
                    {
                        Capacity += 1;
                        if((HAL_GPIO_ReadPin(CHARGE_STAT)==1)&(Capacity >=99))
                            Capacity = 100;
                        else if(Capacity >=99)
                            Capacity = 99;   
                        Capacity_counter = 0;
                    }                       
                    
                    Temp = ComputeTemperature(adcArray[0],adcArray[1]);              // aktualni teplota      
                    
                    //kontrola nabijeni baterie
                    Counter_LED++;
                    if(Capacity!=100 & Counter_LED >= 8)
                    {
                        HAL_GPIO_WritePin(LED_CERVENA ,!HAL_GPIO_ReadPin(LED_CERVENA));
                        Counter_LED = 0;
                    }
                    else if(Capacity == 100)
                    {
                        HAL_GPIO_WritePin(LED_CERVENA ,LED_ON);
                    };
                
                    //osa x                
                    FiltrDataX[FiltrCounter%Filtr] = Sklon_Aktual+180;
                    FiltrX = 0;
                    for(int i = 0; i< Filtr; i++) //suma                   
                        FiltrX += FiltrDataX[i];
                    FiltrX = FiltrX / Filtr;      //prumer               
                    FiltrX_Max = FiltrX-Sklon_Offset+180;
            
                    //osa y
                    FiltrDataY[FiltrCounter%Filtr] = Naklon_Aktual+180;
                    FiltrY = 0;
                    for(int i = 0; i< Filtr; i++)                    
                        FiltrY += FiltrDataY[i];
                    FiltrY = FiltrY / Filtr;              
                    FiltrY_Max = FiltrY-Naklon_Offset+180;  
              
                    //osa z            
                    FiltrDataZ[FiltrCounter%Filtr] = Odklon_Aktual+180;
                    FiltrZ = 0;
                    for(int i = 0; i< Filtr; i++)                    
                        FiltrZ += FiltrDataZ[i];
                    FiltrZ = FiltrZ / Filtr;                
                    FiltrZ_Max = FiltrZ-Odklon_Offset+180; 
                    FiltrCounter++;              
                
                    Priznak_Clock = 0;    
                }
                
                // odpojeni USB
                if(!HAL_GPIO_ReadPin(USB_CONNECT))  
                {
                    Counter_USB++;
                    if(Counter_USB >= 70 & Priznak_Zapnuto == 0) // puvodne spalo - uspat   
                    {   
                        // ulozit kapacitu baterie do BACKUP
                        HAL_PWR_EnableBkUpAccess(); 
                        HAL_RTCEx_BKUPWrite(&hrtc,  RTC_BKP_DR3, (uint32_t)(Capacity)); 
                        HAL_PWR_DisableBkUpAccess(); 
                    
                        Counter_USB = 0;
                        Priznak_USB = 0;
                        CLK_1MHz_Init();
                        SOUND_USBDisconnect();
                        
                        SOUND_TurnOff();
                        PERIFERIE_OFF();
                        SLEEP();
                    }
                    if(Counter_USB >= 70 & Priznak_Zapnuto == 1) // puvodne bezelo - bezet dal
                    {
                        // ulozit kapacitu baterie do BACKUP
                        HAL_PWR_EnableBkUpAccess(); 
                        HAL_RTCEx_BKUPWrite(&hrtc,  RTC_BKP_DR3, (uint32_t)(Capacity)); 
                        HAL_PWR_DisableBkUpAccess(); 
                    
                        BAT_MEAS_Stop();            // ukoncit DMA a ADC
                        HAL_Delay(1);               // pockat az se opravdu DMA a ADC ukonci, jinak se kousne
                        Counter_USB = 0;
                        Priznak_USB = 0;
                        FLASH_addr_aktual = FLASH_addr_ukonceni;
                    
                        // Naplneni Buffru vychozimi hodnotami
                        for(auxPointer = 0; auxPointer<64; auxPointer++)
                            Buffer[auxPointer] = (180<<20 | 180<<10 | 180);
                        
                         // Prvotni naplneni pole pro filtraci
                        for(int i = 0; i<20; i++)   
                        {
                            FiltrDataX[i] =  Sklon_Offset;
                            FiltrDataY[i] =  Naklon_Offset;
                            FiltrDataZ[i] =  Odklon_Offset;
                        } 

                        // Smazani Max hodnot filtru
                        FiltrX_Max = 0;
                        FiltrY_Max = 0;
                        FiltrZ_Max = 0;                
                    
                        // Deinit
                        GPIO_Deinit();
                        Periph_Deinit();    //deinit SPI, DMA, ADC, UART
                    
                        // CLK update
                        CLK_1MHz_Init();
                        SystemCoreClockUpdate();
                    
                        // Init
                        GPIO_Init();
                        ADC_NAP_Init();         // init ADC, treba inicializovat az je DMA a ADC opravdu stopnute
                        SPI_Init();
                    
                        HAL_Delay(250);         // delay 250ms
                        SOUND_USBDisconnect();                          
                        SysTick_Config(System_100ms);                   
                    }
                }
            } /* end of USB while()*/
        }   /*ned of if(priznak_USB)*/
	};  /* end of for(;;) */                      		
}; /* end of main */

// ##########################################################################################################################################
// ###################################################### KOMUNIKACE S PC ###################################################################
// ##########################################################################################################################################

void USART1_IRQHandler(void)
{
    static unsigned char pocet_bytu_1 = 0;
    static unsigned char count_1      = 0;

    if(USART1 -> ISR & USART_ISR_RTOF )
    { 
        if 	((status_rxbuffer[0] == 32) && (status_rxbuffer[2] == 2))
        {
			
			pocet_bytu_1 = status_rxbuffer[1]; 
			
            ptrRxPDown = 0;
			for (count_1 = 0; count_1 < pocet_bytu_1; ++count_1) 
			{
				rxPassiveBuffer[ptrRxPDown++] = status_rxbuffer[3 + count_1];  
			};
			
			 Com_ProcessRequest();
        };    
    
		USART1 -> ICR = USART_ICR_RTOCF;
		DMA1_Channel5 -> CCR   &= ~DMA_CCR_EN;              // DISABLE chanal
		DMA1_Channel5 -> CNDTR  =  40;
		DMA1_Channel5 -> CCR   |=  DMA_CCR_EN;              // ENABLE chanal
	}
	
	UART_RX_byte = USART1->RDR;
	USART1 -> ICR = USART_ICR_ORECF;
	USART1 -> ICR = 0xFFFFFFFF;

}

// ##########################################################################################################################################
// ##########################################################################################################################################

void Com_ProcessRequest() {
   unsigned static int lastOp = 0;
   unsigned static  long long int lastIndex = 0;
   unsigned int rxByte = 0;
   unsigned char aux = 0;

   // Reset vysilace
   ptrRxPDown = 0;

   // Vycteni hlavicky
   rxByte = rxPassiveBuffer[ptrRxPDown++];  

   // Vetveni podle hlavicky
   switch (rxByte) {
     // Cteni
      case 250:
          // Vycteni hlavicky
          rxByte = rxPassiveBuffer[ptrRxPDown++];
          sending (lastOp = rxByte,lastIndex = 0);
          break;
      // Zapis
      case 251:    
          // Prijem dat pro zapis
          rxByte = rxPassiveBuffer[ptrRxPDown++];
          if ((rxByte != 3)&&(rxByte != 32)/*&&(rxByte != 7)*/) 
           {
             sending (0,0);
             break;
           }
         
          receiving (rxByte);
          break;
      // Pozdrzeni operace - opakovani
      case 254:
          sending(lastOp,lastIndex);
          break;
      // Dokonceni operace
      case 255:
          lastIndex += 1;
          sending(lastOp,lastIndex);
          break;
      // Chyba
      default:
          sending (0,0);
          lastOp = 0;
          lastIndex = 0;
          break;
      }  
}

// ##########################################################################################################################################
// ##########################################################################################################################################

/* Odpoved na pozadavek z PC */
void sending (unsigned int lastOp,unsigned long long int lastIndex) {
   unsigned int aux   = 0;
   unsigned int auxrx = 0;
   unsigned char bcc  = 0;

    Monitoring = 0; //priznak pro vypnuty monitoring

   // Cekani na linku
    ptrTxATop = 0;

switch (lastOp) {
      // Nazev zarizeni
      case 1:
                  
          // Hlavicka?      
          txActiveBuffer[ptrTxATop++] = 250;
          txActiveBuffer[ptrTxATop++] = 1;
          txActiveBuffer[ptrTxATop++] = 32;
          bcc = 1 ^ 32;
          for (aux = 0; aux < 32; aux++) {
             txActiveBuffer[ptrTxATop++] = *(((char *)("Tilt monitoring system-TMS"))+aux);
             bcc ^= *(((char *)("Tilt monitoring system-TMS")) + aux);
             }
           txActiveBuffer[ptrTxATop++] = bcc;
          break;
      // ##########################################################################################################################################          
      // Verze zarizeni
      case 2:
          txActiveBuffer[ptrTxATop++] = 250;
          txActiveBuffer[ptrTxATop++] = 2;
          bcc = 2;
          txActiveBuffer[ptrTxATop++] = 190;
          bcc ^= 190;
          txActiveBuffer[ptrTxATop++] = 0;
          bcc ^= 0;
          txActiveBuffer[ptrTxATop++] = HW_VERHI;
          bcc ^= HW_VERHI;
          txActiveBuffer[ptrTxATop++] = HW_VERLO;
          bcc ^= HW_VERLO;
          txActiveBuffer[ptrTxATop++] = GMS_VERHI;
          bcc ^= GMS_VERHI;
          txActiveBuffer[ptrTxATop++] = GMS_VERLO;
          bcc ^= GMS_VERLO;
          txActiveBuffer[ptrTxATop++] = bcc;
          break;

      // ##########################################################################################################################################       
       // Parametry
       case 3:
          txActiveBuffer[ptrTxATop++] = 250;
          txActiveBuffer[ptrTxATop++] = 3;
          if (lastIndex < PARAM_COUNT) {
             bcc = 3;
             bcc ^= txActiveBuffer[ptrTxATop++] = konfigurace[lastIndex].index;
             bcc ^= txActiveBuffer[ptrTxATop++] = konfigurace[lastIndex].index>>8;
             bcc ^= txActiveBuffer[ptrTxATop++] = konfigurace[lastIndex].typ;
             bcc ^= txActiveBuffer[ptrTxATop++] = konfigurace[lastIndex].typ>>8;
             bcc ^= txActiveBuffer[ptrTxATop++] = konfigurace[lastIndex].minimum;
             bcc ^= txActiveBuffer[ptrTxATop++] = konfigurace[lastIndex].minimum>>8;
             bcc ^= txActiveBuffer[ptrTxATop++] = konfigurace[lastIndex].maximum;
             bcc ^= txActiveBuffer[ptrTxATop++] = konfigurace[lastIndex].maximum>>8;
             bcc ^= txActiveBuffer[ptrTxATop++] = konfigurace[lastIndex].prirustek;
             bcc ^= txActiveBuffer[ptrTxATop++] = konfigurace[lastIndex].prirustek>>8;
             bcc ^= txActiveBuffer[ptrTxATop++] = konfigurace[lastIndex].prepocetl;
             bcc ^= txActiveBuffer[ptrTxATop++] = konfigurace[lastIndex].prepocetl>>8;
             bcc ^= txActiveBuffer[ptrTxATop++] = konfigurace[lastIndex].prepoceth;
             bcc ^= txActiveBuffer[ptrTxATop++] = konfigurace[lastIndex].prepoceth>>8;
             bcc ^= txActiveBuffer[ptrTxATop++] = konfigurace[lastIndex].hodnota;
             bcc ^= txActiveBuffer[ptrTxATop++] = konfigurace[lastIndex].hodnota>>8;
             bcc ^= txActiveBuffer[ptrTxATop++] = konfigurace[lastIndex].rezerva;
             bcc ^= txActiveBuffer[ptrTxATop++] = konfigurace[lastIndex].rezerva>>8;             
             } else {
                for (aux = 0; aux < PARAM_LENGHT; aux++)
                   txActiveBuffer[ptrTxATop++] = 0;
                bcc = 3;
                }
          txActiveBuffer[ptrTxATop++] = bcc;
          break;     

      // ##########################################################################################################################################             
      // Metadata
      case 4:
          {
            txActiveBuffer[(ptrTxATop++)&0x1ff] = 250;
            txActiveBuffer[(ptrTxATop++)&0x1ff] = 4;
            bcc = 4;
            // Dalsi komunikace 
              if (lastIndex < MONITOR_PARAM_C) 
              {
                for (aux = 0; aux < 7; aux++) 
                { // metadata pro zakladni parametry
                  txActiveBuffer[(ptrTxATop++)&0x1ff] = bb_metadata[lastIndex*7+aux];
                                                 bcc ^= bb_metadata[lastIndex*7+aux];
                  txActiveBuffer[(ptrTxATop++)&0x1ff] = bb_metadata[lastIndex*7+aux] >> 8;
                                                 bcc ^= bb_metadata[lastIndex*7+aux] >> 8;
                }
              } 
              else 
              {
                for (aux = 0; aux < 14; aux++) txActiveBuffer[(ptrTxATop++)&0x1ff] = 0;
              }
            txActiveBuffer[(ptrTxATop++)&0x1ff] = bcc;
          }
          break;  

      // ##########################################################################################################################################       
         case 5:{
            //uint8_t temp[BB_ITEM_LENGTH];
            uint32_t value;
            uint32_t tempReadAddrr;
            uint32_t tempFlagDataOK;
            uint32_t requestBBIndex;
         
            // nacteni indexu pozadavku na data
            requestBBIndex  = (rxPassiveBuffer[ptrRxPDown++]      );
            requestBBIndex += (rxPassiveBuffer[ptrRxPDown++] <<  8);
            requestBBIndex += (rxPassiveBuffer[ptrRxPDown++] << 16);
            requestBBIndex += (rxPassiveBuffer[ptrRxPDown++] << 24);
            

            if(Pozice_buffer >59)
            {
                FLASH_Read((uint8_t *)Buffer, FLASH_addr_aktual, 256);
                FLASH_addr_aktual += 256;
                Pozice_buffer = 0;
            }         

            //if(requestBBIndex>260000)
            if(Buffer[Pozice_buffer] == 0xFFFFFFFF)     
               tempFlagDataOK = 0;
            else
                tempFlagDataOK = 1;

            // uzavreni vycitani pokud neni co vycitat : 
            if(tempFlagDataOK == 0)
            {
                    txActiveBuffer[ptrTxATop++] = 250;
                    bcc  = 0;
                    bcc ^= txActiveBuffer[ptrTxATop++] = 5;
                    bcc ^= txActiveBuffer[ptrTxATop++] = 0;     // index
                    bcc ^= txActiveBuffer[ptrTxATop++] = 0;     // index
                    bcc ^= txActiveBuffer[ptrTxATop++] = 0;     // index
                    bcc ^= txActiveBuffer[ptrTxATop++] = 0;     // index
                    for (aux = 0; aux < 3; aux++){
                        txActiveBuffer[ptrTxATop++] = 0;
                        txActiveBuffer[ptrTxATop++] = 0;
                    }
                    txActiveBuffer[ptrTxATop++] = bcc; 
                    break;  // vyskoceni
            }            
         
        
            // korektni data : 
            txActiveBuffer[ptrTxATop++] = 250;
            bcc  = txActiveBuffer[ptrTxATop++] = 5;
            bcc ^= txActiveBuffer[ptrTxATop++] = requestBBIndex      ;     // index
            bcc ^= txActiveBuffer[ptrTxATop++] = requestBBIndex >>  8;     // index
            bcc ^= txActiveBuffer[ptrTxATop++] = requestBBIndex >> 16;     // index
            bcc ^= txActiveBuffer[ptrTxATop++] = requestBBIndex >> 24;     // index        
        
            // osa x
            bcc ^=  txActiveBuffer[ptrTxATop++] = (Buffer[Pozice_buffer] >> 20 & 0b0000001111111111);
            bcc ^=  txActiveBuffer[ptrTxATop++] = (Buffer[Pozice_buffer] >> 20 & 0b0000001111111111) >>8;
            
            // osa y
            bcc ^=  txActiveBuffer[ptrTxATop++] = Buffer[Pozice_buffer] >> 10 & 0b0000001111111111;
            bcc ^=  txActiveBuffer[ptrTxATop++] = (Buffer[Pozice_buffer] >> 10 & 0b0000001111111111) >>8;
            
            // osa z
            bcc ^=  txActiveBuffer[ptrTxATop++] = Buffer[Pozice_buffer] & 0b0000001111111111;
            bcc ^=  txActiveBuffer[ptrTxATop++] = (Buffer[Pozice_buffer] & 0b0000001111111111) >>8;          
                
            Pozice_buffer++; 
              
            txActiveBuffer[ptrTxATop++] = bcc; 
          
          break;
          }
      // ########################################################################################################################################## 
      // Monitoring 
      case 9:
          {
          unsigned static long int counter = 1;
         
       
      
          txActiveBuffer[(ptrTxATop++)&0x1ff] = 250;
          txActiveBuffer[(ptrTxATop++)&0x1ff] = 9;
          bcc = 9;          
          txActiveBuffer[(ptrTxATop++)&0x1ff] = counter;
          bcc ^= counter;
          txActiveBuffer[(ptrTxATop++)&0x1ff] = counter>>8;
          bcc ^= counter>>8;
          txActiveBuffer[(ptrTxATop++)&0x1ff] = counter>>16;
          bcc ^= counter>>16;
          txActiveBuffer[(ptrTxATop++)&0x1ff] = counter>>24;
          bcc ^= counter>>24;
          counter += 1;
          
          
          //Osa x Filtrovane data
          txActiveBuffer[(ptrTxATop++)&0x1ff] = FiltrX;
          bcc ^= FiltrX;
          txActiveBuffer[(ptrTxATop++)&0x1ff] = FiltrX>>8;
          bcc ^= FiltrX>>8;
         
          //Osa y Filtrovane data
          txActiveBuffer[(ptrTxATop++)&0x1ff] = FiltrY;
          bcc ^= FiltrY;
          txActiveBuffer[(ptrTxATop++)&0x1ff] = FiltrY>>8;
          bcc ^= FiltrY>>8;
                    
          //Osa z Filtrovane data
          txActiveBuffer[(ptrTxATop++)&0x1ff] = FiltrZ;
          bcc ^= FiltrZ;
          txActiveBuffer[(ptrTxATop++)&0x1ff] = FiltrZ>>8;
          bcc ^= FiltrZ>>8;           
          
          // BCC
          txActiveBuffer[(ptrTxATop++)&0x1ff] = bcc; 
          }
          break;

      // ##########################################################################################################################################       
      // Seriove cislo
      case 17:
            txActiveBuffer[ptrTxATop++] = 250;
            txActiveBuffer[ptrTxATop++] = 17;
            bcc = 17;
            for(aux = 0; aux < 12; aux++)  
                bcc ^= txActiveBuffer[ptrTxATop++] = *((char *)(MCU_ID_ADDRESS + aux));
            txActiveBuffer[ptrTxATop++] = bcc;
            break;           
      
      // ########################################################################################################################################## 
      // vycteni jednotlivych ID RUN
      case 22:
          {
            uint32_t tempID;
            
            // pokud se jedna o prvni dotaz tohoto typu, tak muzu zkontrolovat hlavicku  -- zruseno kvuli appce zakaznika V2.0
            /*if(lastIndex == 0){
                    confirmOper = rxPassiveBuffer[ptrRxPDown++];
                    if(lastOp != confirmOper) {txActiveBuffer[ptrTxATop++] = 253;  break;   } // operace se musi 2x potvrdit -> jinak ERROR   
            }*/
        
            txActiveBuffer[ptrTxATop++] = 250;
            txActiveBuffer[ptrTxATop++] = 22;
      
            if(lastIndex < NumberOfRuns)        {   tempID = RunID[lastIndex];  }                // cislo runu
            else                                {   tempID = 0;                 }                // pokud se posle ID = 0  ... konec komunikace 
        
            txActiveBuffer[ptrTxATop++] = tempID;
            txActiveBuffer[ptrTxATop++] = tempID >>  8;
            txActiveBuffer[ptrTxATop++] = tempID >> 16;
            txActiveBuffer[ptrTxATop++] = tempID >> 24;
        
            // CRC:
            crc32output = mgmCRC32((uint8_t*)txActiveBuffer, 1, (ptrTxATop-1));
        
            txActiveBuffer[ptrTxATop++] = crc32output;    
            txActiveBuffer[ptrTxATop++] = crc32output >>  8;    
            txActiveBuffer[ptrTxATop++] = crc32output >> 16;    
            txActiveBuffer[ptrTxATop++] = crc32output >> 24; 
        
      break;
      }
      // ########################################################################################################################################## 
      // vycteni HISTORIE - ITEMs 
      case 23:{
            uint8_t     Buff[512];
            uint8_t     Pozice_buff             = 0;
            uint8_t     Buffempty_flag_1min     = 0;
            uint8_t     Buffempty_flag_2min     = 0;
      
            uint32_t    startItem;
            uint32_t    item;
            uint32_t    nItemSend;
      
            uint32_t    itemOffset;
            uint32_t    itemAddress;
            uint32_t    actualBlockIndex;
      
            uint32_t    tempAux2;      
      
            uint8_t     emptyFlag;
      
      
            confirmOper = rxPassiveBuffer[ptrRxPDown++];
            if(lastOp != confirmOper) 
            {
                txActiveBuffer[ptrTxATop++] = 253;  
                break;   
            } // operace se musi 2x potvrdit -> jinak ERROR   
        
            crcStartIndex = ptrRxPDown - 2;         // veznu i znaky "23" + "23" 
      
            // startitem = ziskani info o zaznamu, ktery se ma poslat
            idOfReqRun  = rxPassiveBuffer[ptrRxPDown++];
            idOfReqRun |= rxPassiveBuffer[ptrRxPDown++] <<  8;
            idOfReqRun |= rxPassiveBuffer[ptrRxPDown++] << 16;
            idOfReqRun |= rxPassiveBuffer[ptrRxPDown++] << 24;
        
            startItem  = rxPassiveBuffer[ptrRxPDown++];
            startItem |= rxPassiveBuffer[ptrRxPDown++] <<  8;
            startItem |= rxPassiveBuffer[ptrRxPDown++] << 16;
            startItem |= rxPassiveBuffer[ptrRxPDown++] << 24;
        
            // rezerva : 
            ptrRxPDown += 8;     //for (aux = 0; aux < 8; aux++) {     trashByte = rxPassiveBuffer[ptrRxPDown++];    }
        
            crcEndIndex = ptrRxPDown - 1;
        
            //crc
            crc32input  = rxPassiveBuffer[ptrRxPDown++];
            crc32input |= rxPassiveBuffer[ptrRxPDown++] <<  8;
            crc32input |= rxPassiveBuffer[ptrRxPDown++] << 16;
            crc32input |= rxPassiveBuffer[ptrRxPDown++] << 24;
            
            // kontrola shody CRC :
            crc32output = mgmCRC32((uint8_t*)rxPassiveBuffer, crcStartIndex, crcEndIndex);
            if(crc32output != crc32input) 
            {
                 txActiveBuffer[ptrTxATop++] = 253; 
                 break;   
            }   // pokud nesedi kontrolni CRC, tak se ukonci komunikace s chybou

            FLASH_addr_aktual = (startItem/N_ITEM * PAGE_SIZE) + FLASH_ADR_POC;           // vypocet aktualni addr
            //FLASH_Read((uint8_t *)Buff, FLASH_addr_aktual, 256);                          // vycteni flashky, upraveno pro V2.0
            FLASH_Read((uint8_t *)Buff, FLASH_addr_aktual, 512);                          // vycteni flashky - 2 minuty
            time = startItem;                                                             
            
            // ***************ZACATEK ODESILANI DAT***************
            txActiveBuffer[ptrTxATop++] = 250;                  // hlavicka
            txActiveBuffer[ptrTxATop++] = 23;                   // hlavicka
            txActiveBuffer[ptrTxATop++] = RunID[0];             // 100 run ID - odesila 100 + 0 + 0 + 0
            txActiveBuffer[ptrTxATop++] = startItem;
            txActiveBuffer[ptrTxATop++] = startItem >> 8;
            txActiveBuffer[ptrTxATop++] = startItem >> 16;
            txActiveBuffer[ptrTxATop++] = startItem >> 24;           

            // 0  = data pro minutu chybi (0xFF na flash), 1 = ulozena data na flash
            if((Buff[0] == 0xFF)&&(Buff[1] == 0xFF)&&(Buff[2] == 0xFF)&&(Buff[3] == 0xFF)&&(Buff[4] == 0xFF))
                Buffempty_flag_1min = 0;    // prazdna flash
            else
                Buffempty_flag_1min = 1;    // real data
            if((Buff[260] == 0xFF)&&(Buff[261] == 0xFF)&&(Buff[262] == 0xFF)&&(Buff[263] == 0xFF)&&(Buff[264] == 0xFF)) 
                Buffempty_flag_2min = 0;    // prazdna flash
            else
                Buffempty_flag_2min = 1;    // real data
            
            if(!Buffempty_flag_1min && !Buffempty_flag_2min)                //pokud konec dat poslat 0+7+0+reserve(8)+crc(4)
            {
                txActiveBuffer[ptrTxATop++] = 0;                    // number of items
            }
        
            else                                    //neni konec dat
            {
               if(Buffempty_flag_1min && !Buffempty_flag_2min)          // pokud se ma posilat jen posledni minuta
                    txActiveBuffer[ptrTxATop++] = 60;               // number of items
                else                                                // pokud se posilaji 2 minuty
                    txActiveBuffer[ptrTxATop++] = 120;              // number of items
            
                // Reserve 8 byte - 7 vyuzito pro cas:
                txActiveBuffer[ptrTxATop++] =  Buff[245];           // year
                txActiveBuffer[ptrTxATop++] =  Buff[243];           // date
                txActiveBuffer[ptrTxATop++] =  Buff[244];           // month 
                txActiveBuffer[ptrTxATop++] =  Buff[246];           // weekday
                txActiveBuffer[ptrTxATop++] =  Buff[242];           // hours
                txActiveBuffer[ptrTxATop++] =  Buff[241];           // mins
                txActiveBuffer[ptrTxATop++] =  Buff[240];           // sec            
            }            
            
            if(Buffempty_flag_1min) //pokud konec dat, zde uz nic neposlat, jen CRC, jinak naplnit txActiveBuffer
            {
                Pozice_buff = 0;
                for(aux = 0; aux<60; aux++)                         // naplneni 1. minuty
                {
                    txActiveBuffer[ptrTxATop++] =Buff[Pozice_buff++];
                    txActiveBuffer[ptrTxATop++] =Buff[Pozice_buff++];
                    txActiveBuffer[ptrTxATop++] =Buff[Pozice_buff++];
                    txActiveBuffer[ptrTxATop++] =Buff[Pozice_buff++];                   
                }
                
                if(Buffempty_flag_2min) // pokud se nachazi data i pro druhou minutu
                {
                    Pozice_buff += 16;  //posunout ukazatel na buff(data vyctena z flash) o 16 - preskoceni bizuterie na konci bloku ve flash
                
                    for(aux = 0; aux<60; aux++)                         // naplneni 2. minuty
                    {
                        txActiveBuffer[ptrTxATop++] =Buff[Pozice_buff++];
                        txActiveBuffer[ptrTxATop++] =Buff[Pozice_buff++];
                        txActiveBuffer[ptrTxATop++] =Buff[Pozice_buff++];
                        txActiveBuffer[ptrTxATop++] =Buff[Pozice_buff++];                   
                    }
                }
            }   
        
            // CRC:
            crc32output = mgmCRC32((uint8_t*)txActiveBuffer, 1, (ptrTxATop-1));
        
            txActiveBuffer[ptrTxATop++] = crc32output;    
            txActiveBuffer[ptrTxATop++] = crc32output >>  8;    
            txActiveBuffer[ptrTxATop++] = crc32output >> 16;    
            txActiveBuffer[ptrTxATop++] = crc32output >> 24;   
      break;
      }
  
      // ########################################################################################################################################## 
     // metadata
      case 24:{
            static uint8_t idFindFlag;
            static uint8_t metaMonitFlag = 0;
            static uint32_t METAidOfReqRun = 0;
      
            static uint8_t  RUNBBPeriod         = 0;
            static uint8_t  RUNBBRound          = 0;

            static uint8_t  RUNBBuserD01         = 0;
            static uint8_t  RUNBBuserD02         = 0;
            static uint8_t  RUNBBuserD03         = 0;
            static uint8_t  RUNBBuserD04         = 0;
      
            uint16_t tempMeta;
      
            ////////////////////////////////////////////////////////////////////////////////////////////////////
            // pokud se jedna o prvni dotaz tohoto typu, tak vyctu hlavicku (abych ulozil hodnoty do "METAidOfReqRun", "metaMonitFlag"):
            // pozn.: Aplikace posle dotaz na dalsi data pomoci "255", tak uz je "lastIndex" nenulovy !!!
            // promenne "METAidOfReqRun", "metaMonitFlag" ... musi byt STATIC ... aby zustala ulozena posledni hodnota ... 
            if(lastIndex == 0)
            {
               confirmOper = rxPassiveBuffer[ptrRxPDown++];
               if(lastOp != confirmOper)
                   {txActiveBuffer[ptrTxATop++] = 253;  break;   } // operace se musi 2x potvrdit -> jinak ERROR   
              
               crcStartIndex = ptrRxPDown - 2;         // veznu i znaky "24" + "24"

               // vycteni informaci z ProToolu
               METAidOfReqRun  = rxPassiveBuffer[ptrRxPDown++];
               METAidOfReqRun |= rxPassiveBuffer[ptrRxPDown++] <<  8;
               METAidOfReqRun |= rxPassiveBuffer[ptrRxPDown++] << 16;
               METAidOfReqRun |= rxPassiveBuffer[ptrRxPDown++] << 24;
                
               crcEndIndex = ptrRxPDown - 1;
               crc32output = mgmCRC32((uint8_t*)rxPassiveBuffer, crcStartIndex, crcEndIndex);
                
               crc32input  = rxPassiveBuffer[ptrRxPDown++];
               crc32input |= rxPassiveBuffer[ptrRxPDown++] <<  8;
               crc32input |= rxPassiveBuffer[ptrRxPDown++] << 16;
               crc32input |= rxPassiveBuffer[ptrRxPDown++] << 24;
                
               if(crc32output != crc32input) 
                   {txActiveBuffer[ptrTxATop++] = 253;  break;   }   // pokud nesedi kontrolni CRC, tak se ukonci komunikace s chybou 

               // detekce jestli se zadaji METADATA pro monitoring:
               if(METAidOfReqRun == 0xFFFFFFFF)
                   {metaMonitFlag = 1;} 
               else 
                   {metaMonitFlag = 0;}
                
               if(metaMonitFlag == 0)
               {                           
                    idFindFlag = 0;
                                  
               }
            }   // konec IFu prvniho dotazu
        
                  // Metadata pro historii:
                  if(metaMonitFlag == 0)    
                  {    
                    txActiveBuffer[ptrTxATop++] = 250;
                    txActiveBuffer[ptrTxATop++] = 24;
                    txActiveBuffer[ptrTxATop++] = RunID[0];
                    txActiveBuffer[ptrTxATop++] = RunID[0] >> 8;
                    txActiveBuffer[ptrTxATop++] = RunID[0] >> 16;
                    txActiveBuffer[ptrTxATop++] = RunID[0] >> 24;
                
                    if (lastIndex < MONITOR_PARAM_C2) 
                    {
                        for (aux = 0; aux < 11; aux++) 
                        { // metadata pro zakladni parametry
                            txActiveBuffer[(ptrTxATop++)&0x1ff] = bb_metadata2[lastIndex*11+aux];
                            txActiveBuffer[(ptrTxATop++)&0x1ff] = bb_metadata2[lastIndex*11+aux] >> 8;
                        }
                    } 
                    else 
                    {
                        for (aux = 0; aux < 22; aux++) 
                            txActiveBuffer[(ptrTxATop++)&0x1ff] = 0;
                    } 
                   }
                   // Metadata pro monitoring:
                   else if(metaMonitFlag == 1)
                   {
                    txActiveBuffer[ptrTxATop++] = 250;
                    txActiveBuffer[ptrTxATop++] = 24;
                    txActiveBuffer[ptrTxATop++] = 0xFFFFFFFF;
                    txActiveBuffer[ptrTxATop++] = 0xFFFFFFFF >> 8;
                    txActiveBuffer[ptrTxATop++] = 0xFFFFFFFF >> 16;
                    txActiveBuffer[ptrTxATop++] = 0xFFFFFFFF >> 24; 

                    if (lastIndex < MONITOR_PARAM_C2_MONITORING) 
                    {
                        for (aux = 0; aux < 11; aux++) 
                        { // metadata pro zakladni parametry
                            txActiveBuffer[(ptrTxATop++)&0x1ff] = bb_metadata2_monitoring[lastIndex*11+aux];
                            txActiveBuffer[(ptrTxATop++)&0x1ff] = bb_metadata2_monitoring[lastIndex*11+aux] >> 8;
                        }
                    } 
                    else 
                    {
                        for (aux = 0; aux < 22; aux++) 
                            txActiveBuffer[(ptrTxATop++)&0x1ff] = 0;
                    }                    
                   }
          
            // CRC:
            crc32output = mgmCRC32((uint8_t*)txActiveBuffer, 1, (ptrTxATop-1));
          
            txActiveBuffer[ptrTxATop++] = crc32output;    
            txActiveBuffer[ptrTxATop++] = crc32output >>  8;    
            txActiveBuffer[ptrTxATop++] = crc32output >> 16;    
            txActiveBuffer[ptrTxATop++] = crc32output >> 24; 
          
            break;
      }
  
      // ########################################################################################################################################## 
      // monitoring
      case 25:{
          Monitoring = 1;   //priznak pro zapnute monitorovani
      
      
          txActiveBuffer[ptrTxATop++] = 250;
          txActiveBuffer[ptrTxATop++] = 25;
          //Packet counter:
          txActiveBuffer[ptrTxATop++] = Monitoring_PacketCounter;       
          txActiveBuffer[ptrTxATop++] = Monitoring_PacketCounter >> 8;
          txActiveBuffer[ptrTxATop++] = Monitoring_PacketCounter >> 16;
          txActiveBuffer[ptrTxATop++] = Monitoring_PacketCounter >> 24;
          // Item size:
          txActiveBuffer[ptrTxATop++] = MONITORING_ITEM_SIZE;           // item size
          txActiveBuffer[ptrTxATop++] = MONITORING_ITEM_SIZE >> 8;      // 0

          // Reserve 12byte: - 7 vyuzito pro cas + 5 nul
          txActiveBuffer[ptrTxATop++] = Year;
          txActiveBuffer[ptrTxATop++] = Date;
          txActiveBuffer[ptrTxATop++] = Month;
          txActiveBuffer[ptrTxATop++] = WeekDay;
          txActiveBuffer[ptrTxATop++] = Hours;
          txActiveBuffer[ptrTxATop++] = Mins;
          txActiveBuffer[ptrTxATop++] = Sec;

          for(aux = 0; aux<5; aux++)       
              txActiveBuffer[ptrTxATop++] = 0;           

      
          // Data monitoring:
      
          txActiveBuffer[ptrTxATop++] = FiltrX_Max;       
          txActiveBuffer[ptrTxATop++] = FiltrX_Max >> 8;
          txActiveBuffer[ptrTxATop++] = FiltrY_Max;       
          txActiveBuffer[ptrTxATop++] = FiltrY_Max >> 8;      
          txActiveBuffer[ptrTxATop++] = FiltrZ_Max;       
          txActiveBuffer[ptrTxATop++] = FiltrZ_Max >> 8;
          txActiveBuffer[ptrTxATop++] = Volt;           
          txActiveBuffer[ptrTxATop++] = Volt >> 8; 
          txActiveBuffer[ptrTxATop++] = Temp;
          txActiveBuffer[ptrTxATop++] = Temp >> 8;          
          txActiveBuffer[ptrTxATop++] = Capacity;
      
          // CRC:
          crc32output = mgmCRC32((uint8_t*)txActiveBuffer, 1, (ptrTxATop-1));
          
          txActiveBuffer[ptrTxATop++] = crc32output;    
          txActiveBuffer[ptrTxATop++] = crc32output >>  8;    
          txActiveBuffer[ptrTxATop++] = crc32output >> 16;    
          txActiveBuffer[ptrTxATop++] = crc32output >> 24;           
      
          break;
      }
      // ########################################################################################################################################## 
      case 253:
          txActiveBuffer[ptrTxATop++] = 253;
          break;
      case 254:
          txActiveBuffer[ptrTxATop++] = 254;
          break;
      case 255:
          txActiveBuffer[ptrTxATop++] = 255;
          break;
      default :
          txActiveBuffer[ptrTxATop++] = 253;
          break;
    }

    // Spusteni vysilani 
    if(ptrTxATop >0)
        UART1_DMA_SEND(ptrTxATop);  
}

// ##########################################################################################################################################
// ##########################################################################################################################################

// Prijem dat z PC 
void receiving (unsigned int operace) {

static volatile   unsigned int aux   = 0;
static volatile   unsigned int value = 0;
static volatile   unsigned int dest_index = 0;
static volatile   unsigned int bccr  = 0;

   // Cekani na linku
    ptrTxATop = 0;

   // Kontrola platnosti cile - parametry
   if (operace == 3) 
   {
  
       // Nasati indexu
       dest_index = rxPassiveBuffer[ptrRxPDown];
       ptrRxPDown += 1;
       dest_index += rxPassiveBuffer[ptrRxPDown]<<8;
       ptrRxPDown += 1;

       // Nasati hodnoty
       value = rxPassiveBuffer[ptrRxPDown];
       ptrRxPDown += 1;
       value += rxPassiveBuffer[ptrRxPDown]<<8;
       ptrRxPDown += 1;    

      //smazani pameti, pokud prijde 0
      if((dest_index == 604) & (value==0))
      {    
          FLASH_ChipErase();       
          HAL_PWR_EnableBkUpAccess();
          HAL_RTCEx_BKUPWrite(&hrtc,  RTC_BKP_DR2, 0);
          HAL_PWR_DisableBkUpAccess();  
          FLASH_addr_ukonceni = FLASH_ADR_POC;
      }
       // Nasati BCC
       bccr = rxPassiveBuffer[ptrRxPDown];
       ptrRxPDown += 1;
       
       aux = 0;
       while ((aux < PARAM_COUNT)&&(dest_index != konfigurace[aux].index))
           aux++;
       
       if (aux >= PARAM_COUNT) {
          sending (0,0);
          return;
          }
         
       // Nahrani hodnoty a ulozeni 
       konfigurace[aux].hodnota = value;
        

      // Ulozeni do Backup registru 
      HAL_PWR_EnableBkUpAccess();
      HAL_RTCEx_BKUPWrite(&hrtc,  RTC_BKP_DR0, ((uint32_t)konfigurace[OSAX_U_0].hodnota<<20) | ((uint32_t)konfigurace[OSAY_U_1].hodnota<<10) | ((uint32_t)konfigurace[OSAZ_U_2].hodnota)); 
      HAL_RTCEx_BKUPWrite(&hrtc,  RTC_BKP_DR1, ((uint32_t)konfigurace[PMEZ_U_6].hodnota<<16) | ((uint32_t)konfigurace[FILTR_U_3].hodnota));
      HAL_RTCEx_BKUPWrite(&hrtc,  RTC_BKP_DR4, (uint32_t)konfigurace[RUNTIME_U_5].hodnota);
      HAL_PWR_DisableBkUpAccess();  

      MezX = konfigurace[OSAX_U_0].hodnota;
      MezY = konfigurace[OSAX_U_0].hodnota;
      MezZ = konfigurace[OSAX_U_0].hodnota;
      Filtr = konfigurace[FILTR_U_3].hodnota; 
      RunTime = konfigurace[RUNTIME_U_5].hodnota;
      Mez_Progres = konfigurace[PMEZ_U_6].hodnota;
      
      
        // Spusteni vysilani
        txActiveBuffer[(ptrTxATop++)&0x1ff] = 255;
        if(ptrTxATop >0)
            UART1_DMA_SEND(ptrTxATop);

    }
    
    // nastaveni RTC
    if (operace == 32)
    {
        // Nacteni a ulozeni casu
        Year    = rxPassiveBuffer[ptrRxPDown++];
        Date    = rxPassiveBuffer[ptrRxPDown++];
        Month   = rxPassiveBuffer[ptrRxPDown++];
        WeekDay = rxPassiveBuffer[ptrRxPDown++];
        Hours   = rxPassiveBuffer[ptrRxPDown++];
        Mins    = rxPassiveBuffer[ptrRxPDown++];
        Sec     = rxPassiveBuffer[ptrRxPDown++];
        RTC_SetTime(Year, WeekDay, Month, Date, Hours, Mins, Sec);
        
        // Nacteni BCC
        bccr = rxPassiveBuffer[ptrRxPDown++];
    
        // Spusteni vysilani
        txActiveBuffer[(ptrTxATop++)&0x1ff] = 255;
        if(ptrTxATop >0)
            UART1_DMA_SEND(ptrTxATop);    
    }
};

