posilani dat

Dependencies:   FatFileSystemCpp mbed PowerControl USBHostLite

main.cpp

Committer:
PavelKumpan
Date:
2017-05-23
Revision:
26:5674b8978551
Parent:
25:2f964ebcdfd8

File content as of revision 26:5674b8978551:

/* BRIEF:
 * Connects to wifi and listen on ip given by dhcp on port 2000
 *
 * Commands are send in for #X where X is from {S,E,T,B,R}, all commands are
 * confirmed by mbed by sending #A
 *
 * Use #S to start saving data, #E to stop saving. This can be done repeatedly,
 * mbed saves the data into separated files and generates new swimmer id
 * At the end, use #T to start data transfer, the format of transmitted data is:
 *
 * Header for each swimmer:
 * (int) -1 (int) -1 (int) swimmer_id
 * Data itself
 * (char) 0xAA (int) count 3*(short) acc 3*(float) gyro
 * End of swimmer block
 * (int) -1 (int) -1  
 *
 * where count is number starting on 0, incremented on every packet
 * 0xAA is a sync byte used to avoid confusion when some bytes are lost during
 * transmission (UART on faster reates seems to be unstable)
 * 
 * CHANGELOG 22/10/2015
 * - Refactored code
 * - Now using USB flash instead of SD card 
 * - SIZE of packets to be saved at once changed to 32 (from 1000, saving took too much time, effectively loosing measurements)
 * - Using fwrite to write 32 packets at once (packet size = 16 bytes -> 32*16 = 512 bytes -> USB flash sector size is 512 bytes, thus maximizing fwrite effectivity
 * - Saving of 32 packets takes about 3,5 ms
 * KNOWN ISSUES:
 * - Opening USB disk takes some time, couple first measurements not valid (perhaps open next file right when saving stops ?)
 *
 * CHANGELOG 13/11/2015
 * - Focused on data acquisition
 * - #T commands now requires one byte after ACK to pick which swimmer data to send
 * - When board resets, it deletes all files on root, to make sure old data won't get send
 * - Added new command #B which represents begining of unified scope of data and should be sent right after connecting (clears all files and resets swimmerid) 
 *
 * CHANGELOG 7/1/2016
 * - Integrated MPU6000 IMU library
 * - Rewritten data acquisition to support new IMU
 * - Edited usbhost library to support sandisk flash disk
 *
 * CHANGELOG 12/1/2016
 * - Added delay after chipselect deselection between device bursts (solved gyro data inconsistency)
 *
 * CHANGELOG 6/5/2016
 * - Moved to first real HW (rev 1.0)
 * - Added multipurpose button functionality
 *
 * CHANGELOG 19/10/2016
 * - Added status message
 * - Changed button behaviour
 * - Added background battery monitoring
 * - Added background filesystem monitoring
 *
 * CHANGELOG 22/10/2016
 * - Slightly adjusted wifly communication (added timeouts)
 * - Disabled TCP autoretransmission on wifly
 * - Added send log feature
 *
 * CHANGELOG 05/03/2017
 * - Added define switch for new revision of hardware
 * - HW v1r2 only supports single MPU
 *
 * CHANGELOG 10/03/2017
 * - Added swimmer index to the status message.
 * - Added record file for records, function appendRecord, sendRecords
 * - Added clear root command
 *
 * CHANGELOG 13/03/2017
 * - Removed clearRoot() call from init.
 * - Change representation of the records file to binary.
 * - Add Prepare command - first send prepare, wait for a file open and then send Start.
 *
 * CHANGELOG 20/05/2017
 * - Changed communication protocol - each message have header with its size.
 */

// Includes ====================================================================

#include "mbed.h"
#include "EthernetPowerControl.h"
#include "MSCFileSystem.h"
#include "PowerControl.h"
#include "DirHandle.h"
#include "mpu6000.h"
#include "stdint.h"
#include "stdio.h"
#include "utils.h"
#include "wifi.h"
#include "math.h"

// Revision ====================================================================

// Revision define, it's important to select proper version! (pinmuxes differ)
#define REVISION 1

// Defines =====================================================================

/* Data acquisition related stuff */
//#define SAMPLE_RATE         700000                                              // Sample rate for ADC conversion
#define SAMPLE_RATE         10000
#define SAMPLE_PERIOD       0.01                                                // Setting the period of measuring, 0.01 means 100 times per second
#define SIZE                32                                                  // Setting number of instance in array - for saving to SD card
#define BUFFER_SIZE        (sizeof(int) + sizeof(short)*6)                      //size of one data block

/* Battery related defines */
#define UVLO_THRESHOLD 140U                                                     // Battery low threshold
#define BATTERY_PERIOD 30U                                                      // How often to measure voltage in seconds

/* Button press defines */
#define PRESS_PERIOD_SHUTDOWN   2000U
#define PRESS_PERIOD_LOW        250U
#define PRESS_PERIOD_HIGH       450U

/* Mass storage realted stuff */
#define FSNAME "usb"
#define LOG_FILE "/usb/log.txt"
#define RECORDS_FILE "/usb/records.txt"                                         
#define SPACE_LOW_THRESHOLD 100                                                 // USB Flash disk free threshold 
#define SPACE_PERIOD 300U                                                       // How often to measure free space in seconds

/* Commands swimfly understands */
#define PREPARE_SAVING      'P'
#define SAVING_START        'S'
#define SAVING_STOP         'E'
#define TRANSFER_REQUEST    'T'
#define SCOPE_BEGIN         'B'
#define CHECK_READY         'R'
#define SEND_LOG            'L'
#define SEND_RECORDS        'D'
#define HALT                'H'

/* Comm stuff */
#define SOH 0x01

/* Test mode define (undefine to disable) */
//#define LOG_TEST_MODE

/* Function like macros for leds (bleh) */
#define leds_off() { led_measuring = led_sending = led_working = led_saving = 0; }
#define leds_error() { led_measuring = led_sending = led_working = led_saving = 1; }

// Function to power down magic USB interface chip with new firmware
#define USR_POWERDOWN    (0x104)
int semihost_powerdown() {
    uint32_t arg;
    return __semihost(USR_POWERDOWN, &arg);
}

// Function prototypes =========================================================

/* File handling */
void clearRoot(void);
void appendLog(char textToAppend[]);
void appendRecord(void);
bool doesFileExist(const char *filename);
void spaceMeas(void);
void readSwimmerID(void);

/* Button handlers */
void btnPressCallback(void);
void btnReleaseCallback(void);

/* Power management */
void pllInit(void);
void battMeas(void);
void shutdownHandler(void);

/* Init */
void boardInit(void);

/* Measurement */
void measure(void);
void setMeasuringFlag(void);

/* Command functions */
void prepareSaving(void);
void startSaving(void);
void stopSaving(void);
void transfer(void);
void sendFile(char* name);
void halt(void);
void beginScope(void);
void sendStatus(void);

/* Generic functions */
void blink(void);

// Watchdog class ==============================================================

class Watchdog {
public:
    /* Kick the watchdog with time period setting */
    void setKick(float s) {
        LPC_WDT->WDCLKSEL = 0x1;                                                // Set CLK src to PCLK
        uint32_t clk = SystemCoreClock / 16;                                    // WD has a fixed /4 prescaler, PCLK default is /4
        LPC_WDT->WDTC = s * (float)clk;
        LPC_WDT->WDMOD = 0x3;                                                   // Enabled and Reset
        kick();
    }

    /* Kick the watchdog */
    void kick() {
        LPC_WDT->WDFEED = 0xAA;
        LPC_WDT->WDFEED = 0x55;
    }
};

// Objects & vars ==============================================================

/* Objects */                                                                  
Watchdog wdt;                                                                   // Initialize watchdog
Ticker Saving_Ticker;                                                           // Ticker for interrupts on measuring
Ticker Battery_Ticker;                                                          // Ticker for voltage measurement
Ticker Space_Ticker;                                                            // Ticker for free space measurement
Wifi wifi(p13, p14, p12, p23);                                                  // Wifly module
SPI spi(p5, p6, p7);                                                            // NEW MPU6000 SPI connection
MPU6000 mpu(spi, p8);                                                           // NEW MPU6000
Timer button_timer;                                                             // Button press time counter
Serial pc(USBTX, USBRX);                                                        // USB Serial port

/* LED indicators */
#if REVISION == 1
    DigitalOut led_system(p30);
#elif REVISION == 2
    DigitalOut led_system(p27);
    DigitalOut cs_2(p26);
#endif
DigitalOut led_measuring(LED1);
DigitalOut led_sending(LED2);
DigitalOut led_working(LED3);
DigitalOut led_saving(LED4);

/* System LED notification patterns */
//typedef enum {USER_BOOT, UVLO, DISK_FULL, AOK} blink_t;

/* Power related stuff */
DigitalOut wifly_enable_n(p9);
DigitalOut flash_enable_n(p10);
DigitalOut btn_latch_n(p28);
InterruptIn btn_input(p29);
AnalogIn batt_vol(p15);
volatile uint8_t systemRequest      = 0;
volatile bool userShutdownFlag      = 0;
volatile bool UVLOShutdownFlag      = 0;
volatile uint8_t battLevel          = 150U;                                     // Batt level between 136U/3.4816V and 165U/4.2240V (High byte of 16bit voltage measurement)
volatile uint32_t curTime           = 0;
volatile uint32_t oldTime           = 0;

/* Added for test purposes */
#ifdef LOG_TEST_MODE
DigitalOut flagSetNotify(p19);
DigitalOut measureNotify(p20);
#endif

/* USB && saving related stuffs */
FILE *gfp;                                                                      // Global file pointer
MSCFileSystem logger(FSNAME);                                                   // Initialize USB flash disk
char fname[40];                                                                 // Variable for name of Text file saved on USB flash disk
uint8_t timestamp[8];                                                           // Variable for name of Text file saved on USB flash disk
static volatile uint64_t absolute   = 0;                                        // Variable for numbering saved data
volatile int relative               = 0;                                        // Relative packtets saved number
volatile uint8_t swimmer_id         = 0;                                        // Global swimmer ID                      
volatile bool measuringFlag         = 0;                                        // Flag that is set with interrupt and indicates measurement is to be done
volatile bool savingFlag            = 0;                                        // Flag that is set and indicates data is being saved periodically
volatile bool lowSpaceFlag          = 0;                                        // Flag that is set when below SPACE_THRESHOLD MB's remain free
volatile uint16_t mbFree            = 0;                                        // Variable to store remaining space on USB flash disk

/* Accelerometer buffers */
short int acc_x[SIZE];
short int acc_y[SIZE];
short int acc_z[SIZE];

/* Gyroscope buffers */
short int gyro_x[SIZE];
short int gyro_y[SIZE];
short int gyro_z[SIZE];

/* Command buffers */
char cmd_header[FRAME_HEADER_LEN];
char cmd_data[256];
int cmd_len;

// Functions -> File handling ==================================================

/* Clear all files in root directory */
void clearRoot(void)
{
    char buf[40];                                                               // Buffer for string building
    
    /* Dir related structs & vars */
    DIR *dir;                                                                   // Prepare directory handle
    struct dirent *file;                                                        // Directory entry structure
    dir = opendir("/usb");                                                      // Open root of usb flash disk                                                      
                                                                
    /* Iterate all files beside log file in root directory and delete them */
    while((file = readdir(dir)) != NULL)                                        // Incrementally points to next files, until NULL is reached
    {
        if (strcmp(file->d_name, "log.txt") != 0)                               // Only dele non-log files
        {
            sprintf(buf, "/usb/");
            sprintf(buf+5, file->d_name);
            remove(buf);                                                        // Remove file that is currently being pointed to
        }
    }
      
    /* If file is not present */
    closedir(dir);                                                              // Close dir & return false    
    
    FILE *records;
    records = fopen(RECORDS_FILE, "w");                                         // Create new file for records
    fclose(records);  
}

/* Append line to log file */
void appendLog(char textToAppend[])
{   
    printf("Writing to log file...\r\n");

    /* Append line to log file */
    FILE *log;
    log = fopen(LOG_FILE, "a");                                                 // Open file with log name
    fprintf(log, "%s\r\n", textToAppend);                                       // Append line
    fclose(log);                                                                // Close file
}

/* Append record to records file */
void appendRecord(void)
{   
    printf("Writing to record file...\r\n");

    /* Append line to records file */
    FILE *records;
    records = fopen(RECORDS_FILE, "a");                                         // Open file with records name
    
    uint8_t recordbuffer[9];
    
    recordbuffer[0] = swimmer_id;
    
    for(int i = 0; i < 8; i++)
    {
        recordbuffer[i+1] = timestamp[i];
    }
    
    fwrite(recordbuffer, 1, 9, records);                                        // Write 9 bytes of record
    fflush(records); 
    fclose(records);                                                            // Close file
}

/* Read last swimmer ID */
void readSwimmerID(void)
{   
    size_t fileSize;
    FILE *records;
            
    if(false == doesFileExist(RECORDS_FILE))
    {
        swimmer_id = 0;
        printf("File with records does not exists, so ID set to 0...\r\n");
        swimmer_id = 0;
        printf("New records file created...\r\n");
        records = fopen(RECORDS_FILE, "a");
    }
    else
    {
        printf("Reading last swimmer ID from records file...\r\n");

        /* Append line to records file */
        records = fopen(RECORDS_FILE, "r");                                     // Open file with records name
        
        fseek(records, 0L, SEEK_END);
        fileSize = ftell(records);
        fseek(records, 0L, SEEK_SET);
     
        if(true == ferror(records) || fileSize < 9)
        {
            swimmer_id = 0;
        }
        else
        {
            fseek(records, -9, SEEK_END);
            swimmer_id = (uint8_t)fgetc(records) + 1;
        }
    }
    
    printf("Actual index is %d...\r\n", swimmer_id);
    fclose(records);                                                       
}

/* Sees whether giver file exists on flash disk */
bool doesFileExist(const char *filename)
{
    /* Dir related structs & vars */
    DIR *dir;                                                                   // Prepare directory handle
    struct dirent *file;                                                        // Directory entry structure
    dir = opendir("/usb");                                                      // Open root of usb flash disk                                                      
    
    /* Iterate all available files in root directory */
    while((file = readdir(dir)) != NULL)                                        // Incrementally points to next files, until NULL is reached
    {
        /* Check if it's file we are looking for */
        if(strcmp(filename+5, file->d_name) == 0)                               // +5 moves us beyond /usb/, not cool I know...
        {
            closedir(dir);                                                      // Close dir & return true
            return true;   
        }
    }
    
    /* If file is not present */
    closedir(dir);                                                              // Close dir & return false
    return false;                                                               // If not, return false
}

/* Measures space left and updates global variable value */
void spaceMeas(void)
{
    FATFS* fs;
    DWORD fre_clust;
    f_getfree("0:",&fre_clust,&fs);
    mbFree = (uint16_t)((fs->csize * fs->free_clust) >> 11);    
}

// Functions -> Power management ===============================================

void pllInit(void)
{
    LPC_SC->PLL0CON   = 0x00;             /* PLL0 Disable                    */
    LPC_SC->PLL0FEED  = 0xAA;
    LPC_SC->PLL0FEED  = 0x55;
 
    LPC_SC->CCLKCFG   = 0x00000005;       /* Select Clock Divisor = 6        */
    LPC_SC->PLL0CFG   = 0x0000000B;       /* configure PLL0                  */
    LPC_SC->PLL0FEED  = 0xAA;             /* divide by 1 then multiply by 12 */
    LPC_SC->PLL0FEED  = 0x55;             /* PLL0 frequency = 288,000,000    */
 
    LPC_SC->PLL0CON   = 0x01;             /* PLL0 Enable                     */
    LPC_SC->PLL0FEED  = 0xAA;
    LPC_SC->PLL0FEED  = 0x55;
    while (!(LPC_SC->PLL0STAT & (1<<26)));/* Wait for PLOCK0                 */
 
    LPC_SC->PLL0CON   = 0x03;             /* PLL0 Enable & Connect           */
    LPC_SC->PLL0FEED  = 0xAA;
    LPC_SC->PLL0FEED  = 0x55;
    while (!(LPC_SC->PLL0STAT & ((1<<25) | (1<<24))));/* Wait for PLLC0_STAT & PLLE0_STAT */
 
    SystemCoreClockUpdate();            /* Should now be at 48,000,000    */
    SystemCoreClock = (12000000 * 2 /
                      (((LPC_SC->PLL0STAT >> 16) & 0xFF) + 1) *
                      ((LPC_SC->PLL0STAT & 0x7FFF) + 1)  /
                      ((LPC_SC->CCLKCFG & 0xFF)+ 1));   
}

/* Callback for button press */
void btnPressCallback(void)
{
    /* Resets and starts timer */
    button_timer.reset();
    button_timer.start();
}

/* Callback for button release */
void btnReleaseCallback(void)
{
    /* Stop timer */
    button_timer.stop();
    /* See whether user is trying to shutdown */
    uint32_t buf = button_timer.read_ms();
    if((buf < PRESS_PERIOD_HIGH) && (buf > PRESS_PERIOD_LOW))
    {
        systemRequest += 1;
    }
    else
    {
        led_system = !led_system;
        systemRequest = 0;   
    }
}

/* Does single battery voltage measurement, and sets UVLO flag if voltage too low */
void battMeas(void)
{
    battLevel = batt_vol.read_u16() >> 8;                                       // Store High byte of voltage measurement to global variable 
    if(battLevel < UVLO_THRESHOLD)                                              // Determine whether we have enough juice
    {
        UVLOShutdownFlag = 1;                                                   // Raise Under Voltage Lock Out flag if necessary
    }
}

/* Checkes whether UVLO flag is set and halts board if necessary */
void shutdownHandler(void)
{
     /* Button shutdown handler */
     if(3 == systemRequest)
        {
            halt();                                                             // Halt       
        }
     /* UVLO shutdown handler */   
     if(1 == UVLOShutdownFlag)                                                  // Control of flag that is being set asynchronously if battLevel drops below threshold   
     {
        if (1 == savingFlag)                                                    // If saving's in progress stop it, to make sure file system won't get corrupted
        {
            stopSaving();    
        }
        UVLOShutdownFlag = 0;                                                           
        blink();
        blink();
        appendLog("Battery low halting");
        printf("Battery low, halting.\r\n");
        btn_latch_n = 1;                                                        // Halt
     }
}

// Functions -> Init ===========================================================

/* Prepares board for operation (adc's, gyroscope etc.) */
void boardInit (void)
{   
    //pllInit();
    pc.baud(9600);           
    pc.printf("\r\nBooting...\r\n");                                            // Notify user that board is booting
    pc.printf("SystemCoreClock = %d Hz\r\n", SystemCoreClock);
    
    /* Watchdog */
    if ((LPC_WDT->WDMOD >> 2) & 1)
    {
        led_system = 1;
        wait(1);
        led_system = 0;
    }
    wdt.setKick(15.0);
    
    /* Power related stuff */
    btn_latch_n = 0;                                                            // This latch holds power P-MOS opened in order to provide power to whole board
    wait_ms(20);
    flash_enable_n = 0;
    wait_ms(20);
    wifly_enable_n = 0;
    wifi.init();                                                                // Initialize wifly
    led_measuring = 1;                                                          // Use incremental LED's to notify user about boot stages
    #if REVISION == 2
        cs_2 = 1;
    #endif
    /* IMU init */
    uint8_t name = mpu.init();                                                  // Initializes both gyro and accelerometer
    printf("0x%02X MPUID\r\n", name);
    wait_ms(40);                                                                // Wait for settings to stabilize
    led_sending = 1;                                                            // Light up second LED to notify mpu init ok
    
    /* Power saving features */
    PHY_PowerDown();                                                            // Disable ETH PHY to conserve power (about 40mA)
    semihost_powerdown();                                                       // Disable USB Host to conserve power (about 30mA)
    wait_ms(40);                                                                // Wait a bit
    led_working = 1;                                                            // Light up third LED
    
    /* Measure voltage */
    battMeas();
    printf("%d mV\r\n", (battLevel << 8) / 10);
    spaceMeas();
    printf("%d MB\r\n", mbFree);
    shutdownHandler();
    wait_ms(40);  
    led_saving = 1;
    
    /* Timers & Tickers init */
    Battery_Ticker.attach(&battMeas, BATTERY_PERIOD);
    Space_Ticker.attach(&spaceMeas, SPACE_PERIOD);                                                            
    button_timer.start();                                                       // Start timer that counts lengths of button presses
    btn_input.fall(&btnPressCallback);                                          // Attach button press callback
    btn_input.rise(&btnReleaseCallback);                                        // Attach button release callback
    wait_ms(40);
    
    readSwimmerID();
    
    /* End of init */
    appendLog("Booted");                                                        // Append line to log file
    leds_off();                                                                 // Turn all LEDs off
    blink();                                                                    // Blink system led to notify user
    wdt.setKick(5);
    pc.printf("Ready...\r\n");                                                  // Notify user of end init
}

// Functions -> Measuring ======================================================

/* Main measuring function */
void measure(void)
{
    /* Vars */
    char buffer[SIZE][BUFFER_SIZE];                                             // Buffer
    int pos = 0;                                                                // Position

    /* Read IMU values */
    mpu.valRead(ACCEL, acc_x[relative], acc_y[relative], acc_z[relative]);      // Read Accelerometer values
    mpu.valRead(GYRO, gyro_x[relative], gyro_y[relative],gyro_z[relative]);     // Read Gyroscope values
    
    /* Increment counters */
    absolute++;                                                                 // Increment absolute counter
    relative++;                                                                 // Increment relative counter

    /* After our buffers are full save the data */
    if ( absolute % SIZE == 0 )
    {
        /* Flip led_saving state */
        led_saving = !led_saving;

        /* Handle file error */
        if (gfp == NULL) {
            printf("Unable to append data to file %s\r\n", fname);              // Notify user
            return;                                                             // Return
        }

        /* Iterate all buffer elements */
        for (int k = 0; k < SIZE; k++)
        {
            pos = absolute-SIZE+k;                                              // Increment position
            toBytes(buffer[k], &pos, sizeof(int));                              // Append data to buffer in byte form
            toBytes(buffer[k]+sizeof(int), &acc_x[k], sizeof(short));
            toBytes(buffer[k]+sizeof(int) + sizeof(short), &acc_y[k], sizeof(short));
            toBytes(buffer[k]+sizeof(int) + sizeof(short)*2, &acc_z[k], sizeof(short));
            toBytes(buffer[k]+sizeof(int) + sizeof(short)*3, &gyro_x[k], sizeof(short));
            toBytes(buffer[k]+sizeof(int) + sizeof(short)*4, &gyro_y[k], sizeof(short));
            toBytes(buffer[k]+sizeof(int) + sizeof(short)*5, &gyro_z[k], sizeof(short));                              
        }
        
        /* Write */
        #ifdef LOG_TEST_MODE
        measureNotify = 1;                                                      // Used for saving debug
        #endif
        fwrite(buffer, SIZE, BUFFER_SIZE, gfp);                                 // Write 32 buffer lines (16 bytes) to file (512 bytes total -> one block)
        fflush(gfp);                                                            // Flush to make sure nothing is left in buffer
        #ifdef LOG_TEST_MODE
        measureNotify = 0;                                                      // Used for saving debug
        #endif
    
        /* Reset relative counter */
        relative = 0;
    }
}

/* Sets the measuring flag */
void setMeasuringFlag(void)
{
    #ifdef LOG_TEST_MODE
    flagSetNotify = 1;                                                          // Used for saving debug
    #endif
    measuringFlag = 1;                                                          // Set measuring flag
    #ifdef LOG_TEST_MODE
    flagSetNotify = 0;                                                          // Used for saving debug
    #endif
}

// Functions -> Commands =======================================================

/* Prepares all to acquire data from sensors */
void prepareSaving(void)
{
    for(int i = 0; i < 8; i++)
    {
        timestamp[i] = cmd_data[i];
    }
    
    savingFlag = 1;                                                             // Set saving flag
    /* Open global file with current swimmer ID */
    gfp = fopen(fname, "wb");                                                   // Try to open that file
    if(gfp == NULL) {
        leds_error();                                                           // Turn on all leds
        printf("Unable to open file %s for writing\r\n", fname);                // Notify user
        return;                                                                 // Exit function
    }

    /* Prepare file & vars for data acquisition */
    fprintf(gfp, "%c%c%c", -1, -1, swimmer_id);                                 // Append header to current swimmer output file
    fflush(gfp);

    /* Reset counters */
    absolute = 0;                                                               // Reset absolute counter
    relative = 0;                                                               // Reset relative counter
}

/* Starts to acquire data from sensors */
void startSaving(void)
{
    Saving_Ticker.attach(&setMeasuringFlag, SAMPLE_PERIOD);                     // Attach function that periodically sets the flag
}

/* Stops acquiring data, and save data left in buffers to USB */
void stopSaving(void)
{
    savingFlag = 0;                                                             // Clear saving flag
    /* Stop saving */
    Saving_Ticker.detach();                                                     // Stop setting measuring flag

    /* Try to append data left in buffers */
    if(gfp == NULL) {
        leds_error();                                                           // Turn on all leds
        printf("Unable to open file %s before sending data\r\n", fname);        // Notify user
        return;                                                                 // Exit function
    }

    /* Append data left to buffer */
    int base = ((absolute - (absolute%SIZE)) < 0) ? 0 : absolute - (absolute%SIZE);
    char buffer[relative][BUFFER_SIZE];                                         // packet array
    
    /* Iterate all array elements */
    for (int k = 0; k < relative; k++) {
        int pos = base+k;
        toBytes(buffer[k], &pos, sizeof(int));
        toBytes(buffer[k]+sizeof(int), &acc_x[k], sizeof(short));
        toBytes(buffer[k]+sizeof(int) + sizeof(short), &acc_y[k], sizeof(short));
        toBytes(buffer[k]+sizeof(int) + sizeof(short)*2, &acc_z[k], sizeof(short));
        toBytes(buffer[k]+sizeof(int) + sizeof(short)*3, &gyro_x[k], sizeof(short));
        toBytes(buffer[k]+sizeof(int) + sizeof(short)*4, &gyro_y[k], sizeof(short));
        toBytes(buffer[k]+sizeof(int) + sizeof(short)*5, &gyro_z[k], sizeof(short));
    }
    
    /* Write what's left */
    fwrite(buffer, relative, BUFFER_SIZE, gfp);                                 // Write all data at once
    fflush(gfp);

    /* Append EOF and close file */
    fprintf(gfp, "%c%c", -1,-1);                                                // Write EOF characters
    fflush(gfp);
    fclose(gfp);                                                                // Close file
    
    /* Append this file to the records file */
    appendRecord();
    
    /* Prepare for next swimmer */
    swimmer_id++;                                                               // Increment swimmer ID
    sprintf(fname, "/usb/swimmer%d.txt", swimmer_id);                           // Update current file name
    remove(fname);                                                              // If file with such ID exists remove it to avoid reading old data
}

/* Sends acquired data via Wi-Fi */
void transfer(void)
{
     /* Vars */
    char name[30];                                                              // File name buffer  
    
    /* File transfer prerequisites */
    printf("closing");
    if (gfp != NULL) {
        fclose(gfp);
    }
    printf(" file %d", cmd_data[0]);
    sprintf(name, "/usb/swimmer%d.txt", cmd_data[0]);                           // Create file name based on current swimmer ID
    printf(name);
    sendFile(name);    
}

/* Halts board */
void halt(void)
{
    if (1 == savingFlag)                                                        // If saving's in progress stop it, to make sure file system won't get corrupted
    {
        stopSaving();    
    }
    systemRequest = 0;
    blink();
    btn_latch_n = 1;  
}

/* Begins new scope (clears all data && rests swimmer_id counder) */
void beginScope(void)
{
    /* Clear all files and reset swimmer_id only when not after reset */
    if(swimmer_id != 0)                                                         // Redundancy check
    {
        appendLog("New scope");
        clearRoot();                                                            // Clears all files on root of USB flash disk
        swimmer_id = 0;                                                         // Resets simmer_id counter
        sprintf(fname, "/usb/swimmer%d.txt", swimmer_id);                       // Update current file name
    }
}

// Functions -> Generic functions ==============================================

/* Sends acquired data via Wi-Fi */
void sendFile(char* name)
{
    /* Handle transfer */
    if(doesFileExist(name))                                             // At first check whether file exists (fopen used to freeze mbed)
    {                                                                   // Timeout is used to make sure C# gets ready (sockets are messy, flushing is pain)
        /* Actually try to send file */                                         
        printf("Sending %s...\r\n", name);                              // Notify user which user is being sent
        if (wifi.sendFile(name) != 0)                                   // Send !
        {
            leds_error();                                               // Handle error
            wifi.sendFail();
            printf("Unable to send file.\r\n");                         // Notify user via text also
        }
        else
        {
            printf("File transfer finished.\r\n");                     // Otherwise all is AOK
        }  
    }
    else
    {
        /* In case file doest not exist send NACK */
        wifi.sendNack();
        printf("Requested non-existing file.\r\n");
    }     
}

/* Sends status */
void sendStatus(void)
{
    uint8_t checksum = SOH + savingFlag + battLevel + swimmer_id;               // Calculate checksum
    wifi.sendByte('#');                                                         // #
    wifi.sendByte('A');                                                         // A
    wifi.sendByte(5);                                                           // Send count of the data
    wifi.sendByte(0);
    wifi.sendByte(0);
    wifi.sendByte(0);
    wifi.sendByte(SOH);                                                         // Send start of heading
    wifi.sendByte(savingFlag);                                                  // Send state of saving
    wifi.sendByte(battLevel);                                                   // Sends current battery level
    wifi.sendByte(swimmer_id);                                                  // Sends current swimmer id index
    wifi.sendByte(checksum);                                                    // Send checksum
    
    wifi.waitForAck();
}

void blink(void)
{
    if(led_system)
    {
        led_system = 0;    
    }
    wait_ms(200);
    led_system = 1;
    wait_ms(200);
    led_system = 0;    
}

// Main ========================================================================

int main()
{
    /* Begin by initialization */                                               
    boardInit();                                                                // Initialize board
    
    /* Set zeroth swimmer name */
    sprintf(fname, "/usb/swimmer%d.txt", swimmer_id);                           // Prepare zeroth swimmer filename
    
    /* Main while */
    while (1)
    {
        /* Shutdown handler */
        shutdownHandler();
        /* Read command from wifly */
        cmd_len = wifi.getCmd(cmd_header, cmd_data);
        
        if(cmd_header[1] != 'N')
        {
            printf(">> #%c and %d bytes\r\n", cmd_header[1], cmd_len);
        }
        
        /* Master switch */
        switch (cmd_header[1]) {
            /* start measuring data periodically and save them info file */
            case PREPARE_SAVING:
            {
                wifi.sendAck();
                leds_off();                                                     // Turn off all leds
                prepareSaving();                                                // Prepares all to saving
                printf("Opening file for swimmer %d...\r\n", swimmer_id);       // Notify user
                break;                                                          // Break from switch
            }
            
            /* start measuring data periodically and save them info file */
            case SAVING_START:
            {
                wifi.sendAck();
                leds_off();                                                     // Turn off all leds
                startSaving();                                                  // Actually start saving
                printf("Saving swimmer %d...\r\n", swimmer_id);                 // Notify user
                led_measuring = 1;                                              // Turn on led_measuring
                break;                                                          // Break from switch
            }

            /* stop saving data */
            case SAVING_STOP:
            {
                leds_off();                                                     // Turn off all leds
                stopSaving();                                                   // Actually stop saving
                wifi.sendAck();
                printf("Stopped...\r\n");                                       // Notify user
                break;                                                          // Break from switch
            }

            /* Send all data */
            case TRANSFER_REQUEST:
            {
                leds_off();                                                     // Turn off all leds
                led_sending = 1;                                                // Turn on led_sending
                printf("Sending data...\r\n");                                  // Notify user that data is being sent
                transfer();                                                     // Actually transfer data
                printf("Sending finished...\r\n");                              // Notify user that data was sent successfully
                leds_off();                                                     // Turn off all leds again
                break;                                                          // Break from switch
            }

            /* Begin new scope */
            case SCOPE_BEGIN:
            {
                wifi.sendAck();
                leds_off();                                                     // Turn off all leds
                led_measuring = led_saving = 1;                                 // Turn on two leds to depict special behaviour
                printf("Starting new scope...\r\n");                            // Notify user that new scope is beginning
                beginScope();                                                   // Begin scope ! (clear all data && reset swimmer_id counter)
                leds_off();                                                     // Turn off all leds
                break;                                                          // Break
            }
            
            /* Send log file */
            case SEND_LOG:
            {
                wifi.sendAck();
                leds_off();                                                     // Turn off all leds
                led_measuring = led_saving = 1;                                 // Turn on two leds to depict special behaviour
                printf("Sending log...\r\n");                                   // Notify user that new scope is beginning
                sendFile(LOG_FILE);                                             // Send log file
                leds_off();                                                     // Turn off all leds
                break;                                                          // Break
            }
            
            /* Send records file */
            case SEND_RECORDS:
            {
                leds_off();                                                     // Turn off all leds
                led_measuring = led_saving = 1;                                 // Turn on two leds to depict special behaviour
                printf("Sending records...\r\n");                               // Notify user that new scope is beginning
                sendFile(RECORDS_FILE);                                         // Send records file
                leds_off();                                                     // Turn off all leds
                break;                                                          // Break
            }
            
            /* Halt swimfly */
            case HALT:
            {
                wifi.sendAck();
                leds_off();                                                     // Turn off all leds
                led_measuring = led_saving = 1;                                 // Turn on two leds to depict special behaviour
                halt();                                                         // Halt swimfly
                leds_off();                                                     // Turn off all leds
                break;                                                          // Break
            }
            
            /* Ready */
            case CHECK_READY:
            {
                printf("Sending status...\r\n");                                //
                leds_off();                                                     // Turn off all leds
                sendStatus();                                                   // Sends status message
                break;                                                          // Break
            }
            
            /* Nothing on serial */
            case 'N':
            {
                /* Handle data measuring */
                if(measuringFlag)                                               // Wait for timer to set the flag again and again
                {
                    measure();                                                  // Measure !
                    measuringFlag = 0;                                          // Reset flag
                }
                break;                                                          // Break from switch
            }

            /* Fail (should be handled in some inner function */
            case 'F':
            {
                break;                                                          // Break from switch
            }
            
            /* Nothing on serial */
            case 'A':
            {
                break;                                                          // Break from switch
            }
            
            /* Everything else is ballast */
            default :
            {
                wifi.sendNack();
                leds_error();                                                   // Turn on all leds
                printf("Command %c is unknown!\r\n", cmd_header[1]);            // Notify user
                break;                                                          // Break from switch
            }
        }
        wdt.kick();                                                             // Kick watchdog each iteration
    }
}