/**********************************************************************
    Mark Butcher    Bsc (Hons) MPhil MIET

    M.J.Butcher Consulting
    Birchstrasse 20f,    CH-5406, Rütihof
    Switzerland

    www.uTasker.com    Skype: M_J_Butcher

    ---------------------------------------------------------------------
    File:        application.c
    Project:     uTasker Demonstration project
    ---------------------------------------------------------------------
    Copyright (C) M.J.Butcher Consulting 2004..2011
    *********************************************************************

    16.02.2007 Add SMTP LOGIN support
    18.02.2006 Add SMTP parameter settings
    01.03.2007 Add TFTP demo
    16.03.2007 Improved Global Timer demo
    03.06.2007 Add FTP user controllable FTP timeout                     {1}
    23.06.2007 Add IRQ test                                              {2}
    11.08.2007 Add RTC test (M5223X)                                     {3}
    10.09.2007 Add external RTC IIC test                                 {4}
    15.09.2007 Modify IRQ priorities for M5223X                          {5}
    23.09.2007 Add simple test of SPI FLASH (including multiple chips)   {6}
    12.10.2007 Add USB initialisation                                    {7}
    29.10.2007 Add UART RTC control when working in HW flow control mode {8}
    06.11.2007 Add NetBIOS support - start server and specify name       {9}
    20.11.2007 Allow operation from fixed parameters when no parameter system available {10}
    24.12.2007 Add intensive I2C test mode                               {11}
    02.01.2008 Add IRQ test for SAM7X (IRQ and FIQ)                      {12}
    21.01.2008 Add IRQ test for STR91                                    {13}
    18.02.2008 Extend SPI test for external file system                  {14}
    13.03.2008 Correct time server increment on connection close         {15}
    18.03.2008 Correct sizeof() use                                      {16}
    10.04.2008 Add ADC test for M5223X                                   {17}
    14.04.2008 Add optional LCD read test                                {18}
    20.04.2008 Add Coldfire PIT interrupt test                           {19}
    21.04.2008 Add Coldfire DMA TIMER interrupt test                     {20}
    13.05.2008 Add NE64 TIMER interrupt test                             {21}
    13.05.2008 Modified USB initialisation to start USB task             {22}
    02.07.2008 Adjust IRQ test setup for SAM7X                           {23}
    08.08.2008 Add USB demo menu support                                 {24}
    28.08.2008 Correct PORT_INTERRUPT initialisation when DS1307 test defined {25}
    29.10.2008 Add general purpose timer test for M5223X                 {26}
    30.10.2008 Add MODBUS initialisation                                 {27}
    07.12.2008 Modify SAM7X IRQ test to verify IRQ and port interrupts   {28}
    22.12.2008 Use ADC_SAMPLING_SPEED macro                              {29}
    23.12.2008 Adapt ADC test for SAM7X                                  {30}
    21.01.2009 Add fnFilterUserFile() - removed to uFile.c on 07.04.2009 {31}
    05.02.2009 Remove some serial interface code when DEMO_UART not defined {32}
    01.03.2009 Adapt simple timer test                                   {33}
    17.03.2009 IRQ test activated for all processors, with config for LM3Sxxxx {34}
    30.03.2009 Add initialisation of ucSetRTC to avoid GCC using memcpy(){35}
    31.03.2009 Add ADC demo for Luminary                                 {36}
    07.04.2009 Add user file entries                                     {37}
    03.05.2009 Add OLED, GLCD and TFT options                            {38}
    10.05.2009 Add TEST_CAN                                              {39}
    29.05.2009 Add timer priority initialisation                         {40}
    03.06.2009 Add LPC23XX port interrupt test support                   {41}
    04.06.2009 Remove FULL_DUPLEX from LAN default setting               {42}
    10.06.2009 Remove UART DCE setting and rename usConfig to Config in UART configuration {43}
    01.07.2009 Adapt for STRING_OPTIMISATION configuration               {44}
    16.07.2009 Optionally retrieve the MAC address from the LM3Sxxxx user data {45}
    19.07.2009 Optional test code included to reduce file size and aid overview {46}
    06.08.2009 MODBUS USB slave support added                            {47}
    06.08.2009 Add OLED graphical demo                                   {48}
    06.08.2009 Correct missing include of ADC_timers.h                   {49}
    08.08.2009 Add SNTP demo                                             {50}
    09.08.2009 Add further graphic functions                             {51}
    23.11.2009 Add SD-card support                                       {52}
    30.12.2009 Add M522XX NMI handling code                              {53}
    05.02.2010 temp_pars->temp_parameters.ucServers changed to .usServers
    12.02.2010 Move fnInitModbus() into the validated call               {54}
    05.07.2010 Move user file initialisation to be independent of validation phase {55}
    28.09.2010 Add TEST_SENSIRION                                        {56}
    03.02.2011 Renamed fnInitCAN() to fnInitCANInterface()               {57}

*/


/* =================================================================== */
/*                           include files                             */
/* =================================================================== */

#include "config.h"

#include "application_lcd.h"                                             // {46} LCD tests
#include "ADC_Timers.h"                                                  // {46} ADC and timer tests
#include "iic_tests.h"                                                   // {46} iic tests
#include "Port_Interrupts.h"                                             // {46} port interrupt tests
#include "can_tests.h"                                                   // {46} CAN tests


/* =================================================================== */
/*                          local definitions                          */
/* =================================================================== */

/*****************************************************************************/
// The project includes a variety of quick tests which can be activated here
/*****************************************************************************/

//#define TEST_MSG_MODE                                                  // test UART in message mode
//#define TEST_MSG_CNT_MODE                                              // test UART in message counter mode
#ifdef SUPPORT_DISTRIBUTED_NODES
  //#define TEST_DISTRIBUTED_TX                                          // test some uNetwork messages
#endif
#ifdef GLOBAL_TIMER_TASK
  //#define TEST_GLOBAL_TIMERS                                           // test global timer operation
#endif
#ifdef USE_UDP
  //#define DEMO_UDP                                                     // add UDP echo demo
#endif
#ifdef USE_TFTP
  //#define TEST_TFTP                                                    // TFTP test
#endif
#if defined (_M5223X) && !defined (_M521X) && defined (SUPPORT_RTC)
  //#define RTC_TEST                                                     // test RTC function
#endif
//#define TEST_SPI_FLASH                                                 // {6} simple test of SPI FLASH devices (disable for normal operation!)



#define OWN_TASK                  TASK_APPLICATION

#define STATE_INIT                0x00                                   // task states
#define STATE_ACTIVE              0x01
#define STATE_DELAYING            0x02
#define STATE_VALIDATING          0x04
#define STATE_BLOCKED             0x08
#define STATE_ESCAPING            0x10
#define STATE_RESTARTING          0x20
#define STATE_TESTING             0x40
#define STATE_POLLING             0x80

#define E_TIMER_1                  1                                     // local events
#define E_TIMER_SEND_ETHERNET      2
#define E_TIMER_VALIDATE           3
#define E_TIMER_FREE_SCAN          4
#define E_TIMER_FREE_SCAN_SERIAL   5
#define E_QUIT_SERIAL_COMMAND_MODE 6
#define E_DEBUG                    7
#define E_SENT_TEST_EMAIL          8

#define E_SHIFT_DISPLAY            9
#define E_NEXT_PIC                 10
#define E_NEXT_PHOTO               11

#define E_TIMER_TEST_10S           20
#define E_TIMER_TEST_10MS          E_TIMER_TEST_10S                      // use same event number as 10s timer
#define E_TIMER_TEST_3S            21
#define E_TIMER_TEST_3MS           E_TIMER_TEST_3S                       // use same event number as 3s timer
#define E_TIMER_TEST_5S            22
#define E_TIMER_TEST_5MS           E_TIMER_TEST_5S                       // use same event number as 5s timer
#define E_TIMER_TEST_4S            23
#define E_TIMER_TEST_4MS           E_TIMER_TEST_4S                       // use same event number as second 4s timer

#define E_SECOND_TICK              30

#define IRQ1_EVENT                 40
#define IRQ4_EVENT                 41
#define IRQ5_EVENT                 42
#define IRQ7_EVENT                 43
#define IRQ11_EVENT                44
#define CAPTURE_COMPLETE_EVENT     45                                    // {26}

#define E_TIMER_TX_NOW             60

#define ADC_HIGH_0                 70                                    // {17}
#define ADC_HIGH_1                 71
#define ADC_HIGH_2                 72
#define ADC_HIGH_3                 73
#define ADC_HIGH_4                 74
#define ADC_HIGH_5                 75
#define ADC_HIGH_6                 76
#define ADC_HIGH_7                 77
#define ADC_LOW_0                  78
#define ADC_LOW_1                  79
#define ADC_LOW_2                  80
#define ADC_LOW_3                  81
#define ADC_LOW_4                  82
#define ADC_LOW_5                  83
#define ADC_LOW_6                  84
#define ADC_LOW_7                  85
#define ADC_ZERO_CROSS_0           86
#define ADC_ZERO_CROSS_1           87
#define ADC_ZERO_CROSS_2           88
#define ADC_ZERO_CROSS_3           89
#define ADC_ZERO_CROSS_4           90
#define ADC_ZERO_CROSS_5           91
#define ADC_ZERO_CROSS_6           92
#define ADC_ZERO_CROSS_7           93
#define ADC_TRIGGER                94

#define ADC_TRIGGER_5_ZERO         100
#define ADC_TRIGGER_6_ZERO         101
#define ADC_TRIGGER_5_LOW          102
#define ADC_TRIGGER_6_LOW          103
#define ADC_TRIGGER_5_HIGH         104
#define ADC_TRIGGER_6_HIGH         105

#define E_NEXT_SAMPLE              108                                   // {30}

#define ADC_TRIGGER_0              110                                   // {30}
#define ADC_TRIGGER_1              111
#define ADC_TRIGGER_2              112
#define ADC_TRIGGER_3              113
#define ADC_TRIGGER_4              114
#define ADC_TRIGGER_5              115
#define ADC_TRIGGER_6              116
#define ADC_TRIGGER_7              117

#define E_NEXT_SSC_TEST            120
#define E_NEXT_TIME_SYNC           121

#define E_NEXT_SENSOR_REQUEST      122                                   // {56}

#ifdef DEMO_UDP
    #define UDP_BUFFER_SIZE        512                                   // buffer size for UDP test message
    #define MY_UDP_PORT            9999                                  // test UDP port
#endif
#if defined USE_TIME_SERVER
    #define GMT_OFFSET (signed int)(1 * 60 * 60)                         // offset time from GMT in seconds
    #define NUMBER_OF_TIME_SERVERS 3
    #define SECONDS_24_HOURS       86400
    #define REL_9_1_2007           (unsigned long)0xc94d598b //(unsigned long)(3377286011)
#elif defined USE_SNTP
    #define REFERENCE_TIME (0xce25e413 - (55 + 21*60 + 23*60*60))        // 6th August 2009 at 0:0:0 (23:21:55)
#endif



/* =================================================================== */
/*                      local structure definitions                    */
/* =================================================================== */

#ifdef DEMO_UDP
    typedef struct stUDP_MESSAGE
    {
        unsigned short usLength;
        UDP_HEADER     tUDP_Header;                                      // reserve header space
        unsigned char  ucUDP_Message[UDP_BUFFER_SIZE];                   // reserve message space
    } UDP_MESSAGE;
#endif
#ifdef USE_SNTP                                                          // {50}
    typedef struct stSNTP_MESSAGE
    {
        UDP_HEADER     tUDP_Header;                                      // reserve header space
        NTP_FRAME      sntp_data_content;                                // reserve message space
    } SNTP_MESSAGE;

    typedef struct stSNTP_SERVER_DETAILS
    {
        unsigned char ucAvoidServer;
        unsigned char server_ip_address[IPV4_LENGTH];
    } SNTP_SERVER_DETAILS;
#endif

/* =================================================================== */
/*                 local function prototype declarations               */
/* =================================================================== */

static void fnValidatedInit(void);

#ifdef DEMO_UDP
    static void fnConfigUDP(void);
    static int  fnUDPListner(USOCKET c, unsigned char uc, unsigned char *ucIP, unsigned short us, unsigned char *data, unsigned short us2);
#endif
#ifdef TEST_GLOBAL_TIMERS
    static void fnStartGlobalTimers(void);
    static void fnHandleGlobalTimers(unsigned char ucTimerEvent);
#endif
#ifdef TEST_DISTRIBUTED_TX
    static void fnSendDist(void);
#endif
#if defined USE_TIME_SERVER
    #ifdef SUPPORT_LCD
        static void fnDisplayTime(unsigned long ulPresentTime);
    #endif
    static int  fnTimeListener(USOCKET Socket, unsigned char ucEvent, unsigned char *ucIp_Data, unsigned short usPortLen);
#elif defined USE_SNTP                                                   // {50}
    static int  fnSNTP_client(USOCKET c, unsigned char uc, unsigned char *ucIP, unsigned short us, unsigned char *data, unsigned short us2);
    static void fnRequestSNTP_time(int);
    static int  fnUpdateSNTP_time(NTP_FRAME *ntp_frame, unsigned short usLength);
#endif
#ifdef USE_SMTP
    static const CHAR *fnEmailTest(unsigned char ucEvent, unsigned short *usData); // e-mail call back handler
    #ifdef USE_DNS
        static void fnDNSListner(unsigned char ucEvent, unsigned char *ptrIP);
    #endif
#endif
#ifdef USE_SNMP
    static int fnSNMP_callback(unsigned char ucEvent, unsigned char *data, unsigned short usLength);
#endif
#ifdef TEST_TFTP
    static void tftp_listener(unsigned short usError, CHAR *error_text);
    static void fnTransferTFTP(void);
#endif
#ifdef RTC_TEST
    static void fnTestRTC(void);
#endif
#if (defined SPI_SW_UPLOAD || (defined (SPI_FILE_SYSTEM) && defined (FLASH_FILE_SYSTEM))) && defined TEST_SPI_FLASH
    static void fnTestSPIFLASH(void);
#endif
#if defined SUPPORT_GLCD && (defined MB785_GLCD_MODE || defined AVR32_EVK1105 || defined IDM_L35_B || defined M52259_TOWER || defined OLIMEX_LPC2478_STK || (defined OLIMEX_LPC1766_STK && defined NOKIA_GLCD_MODE)) && defined SDCARD_SUPPORT
    static void fnDisplayPhoto(int iOpen);
#endif

/* =================================================================== */
/*                             constants                               */
/* =================================================================== */

// The application is responsible for defining the IP configuration - here are the default settings
//
static const NETWORK_PARAMETERS network_default = {
    (AUTO_NEGOTIATE /*| FULL_DUPLEX*/ | RX_FLOW_CONTROL),                // {42} usNetworkOptions - see driver.h for other possibilities
    {0x00, 0x00, 0x00, 0x00, 0x00, 0x00},                                // ucOurMAC - when no other value can be read from parameters this will be used
    { 192, 168, 0, 3 },                                                  // ucOurIP - our default IP address
    { 255, 255, 255, 0 },                                                // ucNetMask - Our default network mask
    { 192, 168, 0, 1 },                                                  // ucDefGW - Our default gateway
    { 192, 168, 0, 1 },                                                  // ucDNS_server - Our default DNS server
#ifdef USE_IPV6
    { _IP6_ADD_DIGIT(0xfe80), _IP6_ADD_DIGIT(0x0000), _IP6_ADD_DIGIT(0x0000), _IP6_ADD_DIGIT(0x0000), _IP6_ADD_DIGIT(0xe1c1), _IP6_ADD_DIGIT(0xabb6), _IP6_ADD_DIGIT(0xac36), _IP6_ADD_DIGIT(0x43fd) },  // default IPV6 address
#endif
};

// The default user settings (factory settings)
//
const PARS cParameters = {
    PARAMETER_BLOCK_VERSION,                                             // version number for parameter validity control
    (unsigned short)(2*60),                                              // default telnet_timeout - 2 minutes
    (CHAR_8 + NO_PARITY + ONE_STOP + USE_XON_OFF + CHAR_MODE),           // {43} serial interface settings
    23,                                                                  // TELNET port number
    (/*ACTIVE_DHCP + */ACTIVE_LOGIN + ACTIVE_FTP_SERVER /*+ ACTIVE_FTP_LOGIN*/ + ACTIVE_WEB_SERVER + ACTIVE_TELNET_SERVER + SMTP_LOGIN), // active servers (ACTIVE_DHCP and ACTIVE_FTP_LOGIN disabled)
    SERIAL_BAUD_115200,                                                  // baud rate of serial interface
    {0,0,0,0},                                                           // trusted dial out IP address (null IP means no checking)
    {'A', 'D', 'M', 'I', 'N', 0, ' ', ' '},                              // default user name - & or null terminator closes sequence
    {'u', 'T', 'a', 's', 'k', 'e', 'r', '&'},                            // default user password - & or null terminator closes sequence
#if defined _M5225X
    {'K', 'I', 'R', 'I', 'N', '3', 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
#elif defined _KINETIS
    {'K', 'I', 'N', 'E', 'T', 'I', 'S', 0,0,0,0,0,0,0,0,0,0,0,0,0,0},
#else
    {'u', 'T', 'a', 's', 'k', 'e', 'r', ' ', 'N', 'u', 'm', 'b', 'e', 'r', ' ', '1',0,0,0,0,0},
#endif
    80,                                                                  // flow control at 80% high water
    20,                                                                  // flow control at 20% low water
#if defined _KINETIS
    (MAPPED_DEMO_LED_1 | MAPPED_DEMO_LED_2),                             // user port DDR value
    (MAPPED_DEMO_LED_1 | MAPPED_DEMO_LED_2),                             // user port value of outputs
#else
    #if defined _LM3SXXXX
    (BLINK_LED),                                                         // user port DDR value
    #else
    (DEMO_LED_1 | DEMO_LED_2),                                           // user port DDR value
    #endif
    (DEMO_LED_1 | DEMO_LED_2),                                           // user port value of outputs
#endif
    0,                                                                   // second set of user defined outputs
#ifdef SMTP_PARAMETERS
    {'U', 's', 'e', 'r', ' ', 'n', 'a', 'm', 'e', 0,0,0,0,0,0,0,0,0,0,0,0,0,0},
    {'P', 'a', 's', 's', ' ', 'w', 'o', 'r', 'd', 0,0},
    {'M', 'y', 'A', 'd', 'd', 'r', 'e', 's', 's', '@', 'u', 'T', 'a', 's', 'k', 'e', 'r', '.', 'c', 'o', 'm', 0,0,0,0,0,0,0,0,0,0},
    {'m', 'a', 'i', 'l', '.', 'p', 'r', 'o', 'v', 'i', 'd', 'e', 'r', '.', 'c', 'o', 'm', 0,0,0,0,0,0,0,0,0,0,0,0,0,0},

    #ifdef USE_DNS
    {0,0,0,0},
    #else
    SMTP_PROVIDER_IP_ADDRESS,
    #endif
#endif
    50,                                                                  // default LCD contrast PWM value
    95                                                                   // default LCD backlight PWM intensity
};

#ifdef SUPPORT_KEY_SCAN                                                  // support up to 4 x 4 for test purposes
    static const char *cKey[] = {
      "Key 1 pressed\r\n",                                               // First column, Row 1 press
      "Key 1 released\r\n",                                              //               Row 1 release
      "Key 4 pressed\r\n",                                               //               Row 2 press
      "Key 4 released\r\n",                                              //               Row 2 release
      "Key 7 pressed\r\n",                                               //               Row 3 press
      "Key 7 released\r\n",                                              //               Row 3 release
    #if KEY_ROWS > 3
      "Key * pressed\r\n",                                               //               Row 4 press
      "Key * released\r\n",                                              //               Row 4 release
    #endif

      "Key 2 pressed\r\n",                                               // 2nd column,   Row 1 press
      "Key 2 released\r\n",                                              //               Row 1 release
      "Key 5 pressed\r\n",                                               //               Row 2 press
      "Key 5 released\r\n",                                              //               Row 2 release
      "Key 8 pressed\r\n",                                               //               Row 3 press
      "Key 8 released\r\n",                                              //               Row 3 release
    #if KEY_ROWS > 3
      "Key 0 pressed\r\n",                                               //               Row 4 press
      "Key 0 released\r\n",                                              //               Row 4 release
    #endif

      "Key 3 pressed\r\n",                                               // 3rd column,   Row 1 press
      "Key 3 released\r\n",                                              //               Row 1 release
      "Key 6 pressed\r\n",                                               //               Row 2 press
      "Key 6 released\r\n",                                              //               Row 2 release
      "Key 9 pressed\r\n",                                               //               Row 3 press
      "Key 9 released\r\n",                                              //               Row 3 release
    #if KEY_ROWS > 3
      "Key # pressed\r\n",                                               //               Row 4 press
      "Key # released\r\n",                                              //               Row 4 release
    #endif

      "Key A pressed\r\n",                                               // 4th column, Row 1 press
      "Key A released\r\n",                                              //               Row 1 release
      "Key B pressed\r\n",                                               //               Row 2 press
      "Key B released\r\n",                                              //               Row 2 release
      "Key C pressed\r\n",                                               //               Row 3 press
      "Key C released\r\n",                                              //               Row 3 release
    #if KEY_ROWS > 3
      "Key D pressed\r\n",                                               //               Row 4 press
      "Key D released\r\n"                                               //               Row 4 release
    #endif
    };
#endif

#ifdef USE_TIME_SERVER                                                   // these are the IP addresses of a couple of well known Internet Time Servers from which we try to get the present time from
    static const unsigned char ucTimeServers[NUMBER_OF_TIME_SERVERS][IPV4_LENGTH] = {
        {129,6,15,28},                                                   // time-a.nist.gov 129.6.15.28 NIST, Gaithersburg, Maryland
        {132,163,4,101},                                                 // time-a.timefreq.bldrdoc.gov 132.163.4.101 NIST, Boulder, Colorado
        {216,200,93,8},                                                  // nist1-dc.glassey.com 216.200.93.8 Abovenet, Virginia
    };
#elif defined USE_SNTP
    static const SNTP_TIME zero_time = {0};
#endif

#ifdef USE_SNMP
    static const unsigned char ucIP_SNMP_manager[IPV4_LENGTH] = {192, 168, 0, 1}; // SNMP manager address
    static const CHAR cSNMP_community[] = "public";
    static const CHAR cSNMP_enterprise[] = {0x2b, 6, 1,4,1,4,1,2,15};
#endif

#if defined USE_SMTP
    static const CHAR cUserDomain[]   = OUR_USER_DOMAIN;
    static const CHAR cSubject[]      = EMAIL_SUBJECT;
    static const CHAR cEmailText[]    = EMAIL_CONTENT;
    #ifndef SMTP_PARAMETERS
        static const CHAR cSender[]       = SENDERS_EMAIL_ADDRESS;
        static const CHAR cUserName[]     = SMTP_ACCOUNT_NAME;
        static const CHAR cUserPassword[] = SMTP_PASSWORD;
        #ifdef USE_DNS
            const CHAR cSMTP_provider[]   = SMTP_PROVIDER_ADDRESS;       // our smtp provider's server
        #endif
    #endif
#endif

#if defined INTERNAL_USER_FILES                                          // {37}
    #include "app_user_files.h"                                          // include consts as used by the user files
#endif

/* =================================================================== */
/*                     global variable definitions                     */
/* =================================================================== */

PARS *parameters = 0;
TEMPPARS *temp_pars = 0;
NETWORK_PARAMETERS network;                                             // used network values
NETWORK_PARAMETERS network_flash;                                       // these are the values really in FLASH
#ifdef SERIAL_INTERFACE
    QUEUE_HANDLE SerialPortID;                                          // serial port handle
#endif
#ifdef USE_TIME_SERVER
    unsigned long ulPresentTime = 0;
#endif
#ifdef USE_SMTP
    CHAR cEmailAdd[41] = DEFAULT_DESTINATION_EMAIL_ADDRESS;
#endif


/* =================================================================== */
/*                      local variable definitions                     */
/* =================================================================== */

#ifdef TEST_TFTP
    static unsigned char ucTFTP_server_ip[IPV4_LENGTH] = {192, 168, 0, 102};
#endif
#ifdef TEST_GPT
    static unsigned long ulCaptureList[GPT_CAPTURES];                    // make space for capture values
#endif
#ifdef DEMO_UDP
    static unsigned char ucUDP_IP_Address[IPV4_LENGTH] = {192, 168, 0, 102}; // address to send UDP test frames to
    static USOCKET MyUDP_Socket;
    static UDP_MESSAGE *ptrUDP_Frame;
#endif
#if defined SUPPORT_LCD && !defined USE_TIME_SERVER
    static signed char cShiftTest = LCD_TEST_STARTING;
#endif
#ifdef TEST_DISTRIBUTED_TX
    static unsigned long ulLost_uNetFrames = 0;
    static unsigned long ulLost_uNetSync   = 0;
#endif
#if defined USE_TIME_SERVER
    static unsigned char ucTimeServerTry = 0;
    static USOCKET TIME_TCP_socket;
#elif defined USE_SNTP                                                   // {50}
    static USOCKET SNTP_Socket;
    static unsigned char ucServerIndex = 0;                              // the server presently being requested from
    static SNTP_SERVER_DETAILS SNTP_ServerList[] = {                     // list of time servers to request from
        {0, {194,0,229,52}},                                             // stratum 1 - ntpstm.netbone-digital.com St. Moritz
        {0, {131,188,3,220}},                                            // stratum 1 - ntp0.fau.de University Erlangen-Nuernberg, D-91058 Erlangen, FRG
        {0, {194,42,48,120}},                                            // stratum 2 - clock.tix.ch CH-8005 Zurich, Switzerland
        {0, {207,46,232,182}},                                           // time-b.nist.gov
    };
    #define MAX_SNTP_SERVERS (sizeof(SNTP_ServerList)/sizeof(SNTP_SERVER_DETAILS))
    static SNTP_TIME clientRequestTime;                                  // time of last client request
    static unsigned short usFractionOffset = 0;                          // offset to hardware timer counter and its interrupt
#endif
#ifdef USE_SMTP
    #ifdef USE_DNS
        unsigned char ucSMTP_server[] = {0,0,0,0};                       // IP address or smtp server after it has been resolved
        static unsigned char ucSMTP_Retry;                               // DNS retry counter
    #else
        unsigned char ucSMTP_server[] = SMTP_PROVIDER_IP_ADDRESS;
    #endif
#endif
#if defined SUPPORT_GLCD && (defined MB785_GLCD_MODE || defined AVR32_EVK1105 || defined IDM_L35_B || defined M52259_TOWER || defined OLIMEX_LPC2478_STK || (defined OLIMEX_LPC1766_STK && defined NOKIA_GLCD_MODE)) && defined SDCARD_SUPPORT
    static UTDIRECTORY *ptr_utDirectory = 0;                             // pointer to a directory object
    static int iPollingMode = 0;
    static UTFILE utFile;
    static UTFILEINFO utFileInfo;
#endif

static QUEUE_HANDLE save_handle = NETWORK_HANDLE;                        // temporary debug handle backup
static int iAppState = STATE_INIT;                                       // task state








// Application task
//
extern void fnApplication(TTASKTABLE *ptrTaskTable)
{
    QUEUE_HANDLE        PortIDInternal = ptrTaskTable->TaskID;           // queue ID for task input
    unsigned char       ucInputMessage[RX_BUFFER_SIZE];                  // reserve space for receiving messages
    #if (defined SERIAL_INTERFACE && defined DEMO_UART) || (defined CAN_INTERFACE && defined TEST_CAN) || defined TEST_IIC // {32}{39}
    QUEUE_TRANSFER Length;
    #endif

    if (!iAppState) {
        parameters = uMalloc(sizeof(PARS));                              // get RAM for a local copy of device parameters
        temp_pars  = uMalloc(sizeof(TEMPPARS));                          // get space for a backup of all modifiable parameters
        if (fnGetOurParameters(0) == TEMPORARY_PARAM_SET) {
            uTaskerMonoTimer( OWN_TASK, (DELAY_LIMIT)(2*60*SEC), E_TIMER_VALIDATE );// we have test parameters and wait for them to be validated else reset
            iAppState = STATE_VALIDATING;
        }
        else {
            iAppState = STATE_ACTIVE;                                    // not validating so start work
            fnValidatedInit();
        }
#if defined INTERNAL_USER_FILES                                          // {37}{55}
    #if defined EMBEDDED_USER_FILES
        if (fnActivateEmbeddedUserFiles("1", USER_FILE_IN_INTERNAL_FLASH) == 0) { // if valid embedded user file space is found activate it, else use code embedded version
            fnEnterUserFiles((USER_FILE *)user_files);                   // user_files defined in app_user_files.h
        }
    #else
        fnEnterUserFiles((USER_FILE *)user_files);                       // user_files defined in app_user_files.h
    #endif
#endif
#ifdef USE_MAINTENANCE
        fnInitialisePorts();                                             // set up ports as required by the user
#endif
        uTaskerStateChange(TASK_DEBUG, UTASKER_ACTIVATE);
#if defined SERIAL_INTERFACE && defined DEMO_UART                        // {32} this serial interface is used for debug output and menu based control
        if (!fnSetNewSerialMode(FOR_I_O)) {                              // open serial port for I/O
            return;                                                      // if the serial port could not be opened we quit
        }
        DebugHandle = SerialPortID;                                      // assign our serial interface as debug port
        fnDebugMsg(WELCOME_MESSAGE_UART);
#endif
#if (defined SPI_SW_UPLOAD || (defined (SPI_FILE_SYSTEM) && defined (FLASH_FILE_SYSTEM))) && defined TEST_SPI_FLASH // {14}
        fnTestSPIFLASH();
#endif
#if defined TEST_IIC || defined TEST_DS1307 || defined TEST_SENSIRION    // {56}
        fnConfigIIC_Interface();
#endif
#ifdef USE_DHCP
        if (temp_pars->temp_parameters.usServers & ACTIVE_DHCP) {
            fnStartDHCP((UTASK_TASK)(/*FORCE_INIT | */OWN_TASK));        // activate DHCP
        }
#endif
#ifdef USE_HTTP
        fnConfigureAndStartWebServer();
#endif
#ifdef USE_FTP
        fnConfigureFtpServer(FTP_TIMEOUT);                               // {1}
#endif
#ifdef _WINDOWS
        fnSimulateLinkUp();                                              // Ethernet link up simulation
#endif
#ifdef TEST_GLOBAL_TIMERS
        fnStartGlobalTimers();
#endif
#ifdef DEMO_UDP
        fnConfigUDP();                                                   // configure a TEST UDP socket
#endif
#ifdef USE_NETBIOS
        fnStartNetBIOS_Server(parameters->cDeviceIDName);                // {9}
#endif
#ifdef TEST_DISTRIBUTED_TX
        OurNetworkNumber = 1;                                            // set a valid network address for ourselves
        fnSendDist();
#endif
#ifdef USE_SNMP
        fnStartSNMP(fnSNMP_callback, (unsigned char *)ucIP_SNMP_manager);
        fnSendSNMPTrap(SNMP_COLDSTART, 0);                               // send cold start trap to SNMP manager
#endif
#ifdef USE_PPP
        uTaskerStateChange(TASK_PPP, UTASKER_ACTIVATE);                  // start the PPP task
#endif
#ifdef USB_INTERFACE
        uTaskerStateChange(TASK_USB, UTASKER_ACTIVATE);                  // start USB task
#endif
#if defined SDCARD_SUPPORT && !defined MB785_GLCD_MODE
        uTaskerStateChange(TASK_MASS_STORAGE, UTASKER_ACTIVATE);         // {52} start mass storage task
#endif
#if defined IRQ_TEST
        fnInitIRQ();
#endif
#ifdef RTC_TEST                                                          // {3}
        fnTestRTC();
#endif
#define _ADC_TIMER_INIT
    #include "ADC_Timers.h"                                              // ADC and timer initialisation
#undef  _ADC_TIMER_INIT

#if defined CAN_INTERFACE && defined TEST_CAN                            // {39}
        fnInitCANInterface();                                            // {57}
#endif
    }
#if defined SUPPORT_GLCD && (defined MB785_GLCD_MODE || defined AVR32_EVK1105 || defined IDM_L35_B || defined M52259_TOWER || defined OLIMEX_LPC2478_STK || (defined OLIMEX_LPC1766_STK && NOKIA_GLCD_MODE)) && defined SDCARD_SUPPORT
    else {
        if (iPollingMode != 0) {
            fnDisplayPhoto(0);                                           // polling operation
        }
    }
#endif

    while (fnRead(PortIDInternal, ucInputMessage, HEADER_LENGTH)) {      // check input queue
        switch (ucInputMessage[MSG_SOURCE_TASK]) {                       // switch depending on message source
        case TIMER_EVENT:
            if (E_TIMER_VALIDATE == ucInputMessage[MSG_TIMER_EVENT]) {
#if defined USE_PARAMETER_BLOCK && defined ACTIVE_FILE_SYSTEM
                fnDelPar(INVALIDATE_TEST_PARAMETER_BLOCK);               // validation timer fired before new parameters were verified. We delete the temporary parameters and restart with the original or defaults
#endif
                fnResetBoard();
            }
#if defined TEST_SENSIRION                                               // {56}
            else if (E_NEXT_SENSOR_REQUEST == ucInputMessage[MSG_TIMER_EVENT]) {
                fnNextSensorRequest();
            }
#endif
#ifdef SERIAL_INTERFACE
            else if (E_QUIT_SERIAL_COMMAND_MODE == ucInputMessage[MSG_TIMER_EVENT]) {
                static const CHAR ucCOMMAND_MODE_TIMEOUT[] = "Connection timed out\r\n";
                fnWrite(SerialPortID, (unsigned char *)ucCOMMAND_MODE_TIMEOUT, (sizeof(ucCOMMAND_MODE_TIMEOUT)-1));
            }
#endif
#if /*defined MODBUS_DELAYED_RESPONSE &&*/ defined USE_MODBUS
            else if (E_TEST_MODBUS_DELAY == ucInputMessage[MSG_TIMER_EVENT]) {
                extern void fnNextTest(void);
                fnNextTest();
              //fnMODBUS_delayed_response(usDelayedReference); // test delayed response
            }
#endif
#if defined USE_TIME_SERVER
            else if (E_SECOND_TICK == ucInputMessage[MSG_TIMER_EVENT]) {
                if (++ulPresentTime > SECONDS_24_HOURS) {
                    ulPresentTime = 0;
                }
    #ifdef SUPPORT_LCD
                fnDisplayTime(ulPresentTime);
    #endif
                uTaskerMonoTimer( OWN_TASK, (DELAY_LIMIT)(1*SEC), E_SECOND_TICK );
            }
#elif defined USE_SNTP
            else if (E_NEXT_TIME_SYNC == ucInputMessage[MSG_TIMER_EVENT]) {
                fnRequestSNTP_time(1);                                   // request next synchronisation from next server
            }
#endif
            else if (E_TIMER_SW_DELAYED_RESET == ucInputMessage[ MSG_TIMER_EVENT ]) {
                fnResetBoard();                                          // delayed reset to allow rest page to be served
            }
#if defined SUPPORT_LCD || defined SUPPORT_GLCD || defined SUPPORT_OLED  // {38}{48}
    #define HANDLE_TIMERS                                                // messages from the LCD task are handled here - see the file application_lcd.h
    #include "application_lcd.h"                                         // include timer handling from LCD task
    #undef HANDLE_TIMERS
#endif
#define _ADC_TIMER_TIMER_EVENTS                                          // {49}
    #include "ADC_Timers.h"                                              // include timer handling by ADC demo
#undef _ADC_TIMER_TIMER_EVENTS
#ifdef SUPPORT_DELAY_WEB_SERVING
            else if (E_SERVE_PAGE == ucInputMessage[ MSG_TIMER_EVENT ]) {
                fnServeDelayed('7', 0);
            }
#endif
#ifdef TEST_GLOBAL_TIMERS
            else {
                fnHandleGlobalTimers(ucInputMessage[ MSG_TIMER_EVENT ]);
            }
#endif
            break;

        case INTERRUPT_EVENT:
            switch (ucInputMessage[MSG_INTERRUPT_EVENT]) {
            case TX_FREE:
                if (iAppState == STATE_BLOCKED) {                        // the TCP buffer we were waiting for has become free
                    iAppState = STATE_ACTIVE;
                }
                break;
#define _CAN_INT_EVENTS
    #include "can_tests.h"                                               // CAN interrupt event handling - specific
#undef _CAN_INT_EVENTS

#ifdef USE_DHCP
            case DHCP_SUCCESSFUL:                                        // we can now use the network connection
    #ifdef TEST_TFTP
                fnTransferTFTP();
    #endif
                break;

            case DHCP_MISSING_SERVER:
                fnStopDHCP();                                            // DHCP server is missing so stop and continue with backup address (if available)
                break;
#endif
#ifdef TEST_DISTRIBUTED_TX
            case UNETWORK_FRAME_LOSS:
                ulLost_uNetFrames++;
                break;
            case UNETWORK_SYNC_LOSS:
                ulLost_uNetSync++;
                break;
#endif
#define _ADC_TIMER_INT_EVENTS_1
    #include "ADC_Timers.h"                                              // ADC and timer interrupt event handling - specific
#undef _ADC_TIMER_INT_EVENTS_1
            default:
#ifdef SUPPORT_KEY_SCAN
                if ((KEY_EVENT_COL_1_ROW_1_PRESSED <= ucInputMessage[MSG_INTERRUPT_EVENT]) && (KEY_EVENT_COL_4_ROW_4_RELEASED >= ucInputMessage[MSG_INTERRUPT_EVENT])) {
                    fnDebugMsg((char *)cKey[ucInputMessage[MSG_INTERRUPT_EVENT] - KEY_EVENT_COL_1_ROW_1_PRESSED]); // key press or release
                    break;
                }
#endif
#define _ADC_TIMER_INT_EVENTS_2
    #include "ADC_Timers.h"                                              // ADC and timer interrupt event handling - ranges
#undef _ADC_TIMER_INT_EVENTS_2
#define _PORT_INTS_EVENTS
    #include "Port_Interrupts.h"                                         // port interrupt timer interrupt event handling - ranges
#undef _PORT_INTS_EVENTS
                break;
            }
            break;

#if defined SUPPORT_LCD || defined SUPPORT_GLCD || defined SUPPORT_OLED  // {38}{48}
    #define HANDLE_LCD_MESSAGES                                          // messages from the LCD task are handled here - see the file application_lcd.h
    #include "application_lcd.h"                                         // include message handling from LCD task
    #undef HANDLE_LCD_MESSAGES
#endif

#if defined USE_UDP && (defined DEMO_UDP || defined USE_SNTP)            // {50} ARP will only need to resolve if we initiate sending - here we resent the test frame after the destination has been resolved
        case  TASK_ARP:
            fnRead( PortIDInternal, ucInputMessage, ucInputMessage[MSG_CONTENT_LENGTH]); // read the contents
            switch (ucInputMessage[ 0 ]) {                               // ARP sends us either ARP resolution success or failed
            case ARP_RESOLUTION_SUCCESS:                                 // IP address has been resolved (repeat UDP frame).
    #ifdef USE_SNTP
                fnRequestSNTP_time(0);                                   // repeat SNTP request after (gateway) address has been resolved
    #else
                fnSendUDP(MyUDP_Socket, ucUDP_IP_Address, MY_UDP_PORT, (unsigned char*)&ptrUDP_Frame->tUDP_Header, UDP_BUFFER_SIZE, OWN_TASK);
    #endif
                break;

            case ARP_RESOLUTION_FAILED:                                  // IP address could not be resolved...
                break;
            }
            break;
#endif

        default:
            fnRead( PortIDInternal, ucInputMessage, ucInputMessage[MSG_CONTENT_LENGTH]); // flush any unexpected messages (assuming they arrived from another task)
            break;

        }
    }

#if defined SERIAL_INTERFACE && defined DEMO_UART                        // {32}
    #ifdef TEST_MSG_MODE
        #ifdef TEST_MSG_CNT_MODE
    while (fnMsgs(SerialPortID) != 0) {
        unsigned char ucLength;
        fnRead( SerialPortID, &ucLength, 1);                             // get message length
        Length = fnRead( SerialPortID, ucInputMessage, ucLength);
        fnEchoInput(ucInputMessage, ucLength);
    }
        #else
    while (fnMsgs(SerialPortID)) {
        Length = fnRead( SerialPortID, ucInputMessage, MEDIUM_MESSAGE);
        fnEchoInput(ucInputMessage, Length);
    }
        #endif
    #else
    if ((iAppState & (STATE_ACTIVE | STATE_DELAYING | STATE_ESCAPING | STATE_RESTARTING | STATE_VALIDATING)) && ((Length = fnMsgs(SerialPortID)) != 0)) {
        while ((Length = fnRead(SerialPortID, ucInputMessage, MEDIUM_MESSAGE)) != 0) {
        #ifdef USB_INTERFACE                                             // {24}
            if (usUSB_state & ES_USB_RS232_MODE) {
                fnSendToUSB(ucInputMessage, Length);                     // send input to USB interface
                continue;
            }
        #endif
            fnEchoInput(ucInputMessage, Length);
            if (usData_state == ES_NO_CONNECTION) {
                if (fnCommandInput(ucInputMessage, Length, SOURCE_SERIAL)) {
                    if (fnInitiateLogin(ES_SERIAL_LOGIN) == TELNET_ON_LINE) {
                        static const CHAR ucCOMMAND_MODE_BLOCKED[] = "Command line blocked\r\n";
                        fnWrite(SerialPortID, (unsigned char *)ucCOMMAND_MODE_BLOCKED, sizeof(ucCOMMAND_MODE_BLOCKED));
                    }
                }
            }
            else {
                fnCommandInput(ucInputMessage, Length, SOURCE_SERIAL);
            }
        }
    }
    #endif
#endif


#define _IIC_READ_CODE
    #include "iic_tests.h"                                               // include IIC code to handle reception
#undef _IIC_READ_CODE
#define _PORT_NMI_CHECK                                                  // {53}
    #include "Port_Interrupts.h"                                         // port interrupt timer interrupt event handling - ranges
#undef _PORT_NMI_CHECK
}

extern void fnSetDefaultNetwork(NETWORK_PARAMETERS *ptrNetPars)
{
    uMemcpy(ptrNetPars, &network_default, sizeof(NETWORK_PARAMETERS));
}


#ifdef SERIAL_INTERFACE
extern void fnFlushSerialRx(void)
{
    #ifdef SUPPORT_FLUSH
    fnFlush(SerialPortID, FLUSH_RX);
    #endif
    iAppState = STATE_ACTIVE;
}
#endif

#ifdef USE_MAINTENANCE
extern void fnGotoNextState(unsigned short usNextState)
{
    switch (usData_state = usNextState) {
    case ES_SERIAL_COMMAND_MODE:                                         // when we move to serial command state we start an activity timer of fixed 5 minutes so that the link times out if the user forgets to close it using the quit command
        uTaskerMonoTimer( OWN_TASK, (DELAY_LIMIT)(5*60*SEC), E_QUIT_SERIAL_COMMAND_MODE );
        break;

    #if defined SERIAL_INTERFACE
    case ES_SERIAL_LOGIN:                                                // when we move to serial command state we start an activity timer of fixed 1 minute so that the link times out if the user forgets to continue
        uTaskerMonoTimer( OWN_TASK, (DELAY_LIMIT)(1*60*SEC), E_QUIT_SERIAL_COMMAND_MODE );
        DebugHandle = SerialPortID;                                      // we ensure that the debug handle is pointing to the serial interface
        break;
    #endif

    case ES_STARTING_COMMAND_MODE:
    case ES_BINARY_DATA_MODE:
    case ES_DATA_MODE:
    case ES_NO_CONNECTION:
        uTaskerStopTimer(OWN_TASK);
        break;
    }
}
#endif

#if defined _LM3SXXXX && defined MAC_FROM_USER_REG                       // {45}
static void fnGetUserMAC(void)
{
    if ((USER_REG0 != 0xffffffff) && (USER_REG1 != 0xffffffff)) {        // set the MAC address from the LM user registers if not empty
		network.ucOurMAC[0] = (unsigned char)(USER_REG0);                // collect the MAC address as saved by the LP Flasher
		network.ucOurMAC[1] = (unsigned char)(USER_REG0 >> 8);
		network.ucOurMAC[2] = (unsigned char)(USER_REG0 >> 16);
		network.ucOurMAC[3] = (unsigned char)(USER_REG0 >> 24);
		network.ucOurMAC[4] = (unsigned char)(USER_REG1 >> 8);
		network.ucOurMAC[5] = (unsigned char)(USER_REG1 >> 16);
	}
}
#endif

extern void fnGetEthernetPars(void)
{
#ifdef USE_PARAMETER_BLOCK
    // First check whether there are temporary values to be tested. If not try to take valid parameters.
    // If there are no parameters the default values will be used
    if (fnGetPar((PAR_NETWORK | TEMPORARY_PARAM_SET), (unsigned char *)&network, sizeof(NETWORK_PARAMETERS)) < 0) {
        if (fnGetPar(PAR_NETWORK, (unsigned char *)&network, sizeof(NETWORK_PARAMETERS)) < 0) {
            fnSetDefaultNetwork(&network);                               // if no parameters are available, load the default set
        }
    }
    #if defined _LM3SXXXX && defined MAC_FROM_USER_REG                   // {45}
    fnGetUserMAC();
    #endif
#else
    fnSetDefaultNetwork(&network);                                       // no parameters block available, load the default set
#endif

#ifdef LAN_REPORT_ACTIVITY
    network.usNetworkOptions &= ~LAN_LEDS;
#else
    network.usNetworkOptions |= LAN_LEDS;
#endif
}

#ifdef USE_PARAMETER_BLOCK
static void fnRetrieveAllParameters(unsigned short usTemp)
{
#ifdef USE_PARAMETER_BLOCK                                               // device parameters
    fnGetPar((unsigned short)(PAR_DEVICE | usTemp), (unsigned char *)parameters, sizeof(PARS));
#endif
}

static unsigned short fnGetOurParameters_1(void)
{
    unsigned short usTemp = TEMPORARY_PARAM_SET;

    // Get our variables from the parameter FLASH. If there are test variables use them and inform that we are doing so.
    // Of there are only valid variables uses these. If there are no parameters continue using the defaults.
    // Note that the network options and the MAC address have already been set by the Ethernet task.
    if (fnGetPar((PAR_NETWORK | TEMPORARY_PARAM_SET), (unsigned char *)&network, sizeof(NETWORK_PARAMETERS)) < 0) {
        if (fnGetPar(PAR_NETWORK, (unsigned char *)&network, sizeof(NETWORK_PARAMETERS)) < 0) {
            fnSetDefaultNetwork(&network);                               // if no parameters are available, load the default set
            uMemcpy(parameters, (unsigned char *)&cParameters, sizeof(PARS)); // no valid parameters available - set defaults
    #if defined _LM3SXXXX && defined MAC_FROM_USER_REG                   // {45}
            fnGetUserMAC();
    #endif
            return 0;
        }
        usTemp = 0;
    }

    fnRetrieveAllParameters(usTemp);
    #if defined _LM3SXXXX && defined MAC_FROM_USER_REG                   // {45}
    fnGetUserMAC();
    #endif
    return usTemp;
}
#endif

extern unsigned short fnGetOurParameters(int iCase)
{
#ifdef USE_PARAMETER_BLOCK
    unsigned short usTemp;

    if (iCase == 1) {
        NETWORK_PARAMETERS network_back;                                 // backup of possibly DHCP modified values
        uMemcpy(&network_back, &network, sizeof (NETWORK_PARAMETERS));   // backup the working network parameters
        usTemp = fnGetOurParameters_1();                                 // get the original set from FLASH
        uMemcpy(&temp_pars->temp_network, &network, sizeof(NETWORK_PARAMETERS)); // make a backup copy of all parameters for modification
        if (parameters->usServers & ACTIVE_DHCP) {
            uMemcpy(network.ucDefGW, network_back.ucDefGW, IPV4_LENGTH); // {16} correct sizeof(IPV4_LENGTH) to IPV4_LENGTH
            uMemcpy(network.ucNetMask, network_back.ucNetMask, IPV4_LENGTH);
            uMemcpy(network.ucOurIP, network_back.ucOurIP, IPV4_LENGTH);
        }
    }
    else {
        usTemp = fnGetOurParameters_1();
        uMemcpy(&temp_pars->temp_network, &network, sizeof(NETWORK_PARAMETERS)); // make a backup copy of all parameters for modification
    }
    uMemcpy(&network_flash, &temp_pars->temp_network, sizeof (NETWORK_PARAMETERS));
    uMemcpy(&temp_pars->temp_parameters, parameters, sizeof(PARS));
    if (temp_pars->temp_parameters.ucParVersion != PARAMETER_BLOCK_VERSION) { // either we have found parameters belonging to another project or else a new version. Take the defaults in this case.
        uMemcpy(&temp_pars->temp_parameters, &cParameters, sizeof(PARS));
    }
    return usTemp;
#else
    uMemcpy(&temp_pars->temp_parameters, &cParameters, sizeof(PARS));    // {10}
    return 0;
#endif
}

#if defined SUPPORT_GLCD && (defined MB785_GLCD_MODE || defined AVR32_EVK1105 || defined IDM_L35_B || defined M52259_TOWER || defined OLIMEX_LPC2478_STK || (defined OLIMEX_LPC1766_STK && defined NOKIA_GLCD_MODE)) && defined SDCARD_SUPPORT
static void fnDisplayPhoto(int iOpen)
{
    unsigned char ucTemp[512];                                           // load size equal to SD card sector for best operation
    if (iOpen != 0) {
        utFile.ptr_utDirObject = ptr_utDirectory;
        utOpenFile(utFileInfo.cFileName, &utFile, (UTFAT_OPEN_FOR_READ));// open the file for reading
        uTaskerStateChange(OWN_TASK, UTASKER_GO);                        // go to polling mode
        iPollingMode = 1;
    }
    utReadFile(&utFile, ucTemp, sizeof(ucTemp));                         // read a sector
    fnDisplayBitmap(ucTemp, utFile.usLastReadWriteLength);               // display the content
    if (utFile.usLastReadWriteLength < sizeof(ucTemp)) {
        uTaskerStateChange(OWN_TASK, UTASKER_STOP);                      // exit polling mode
        iPollingMode = 0;
    }
}
#endif

#ifdef USE_SNTP

static unsigned long ulUTC_second;
// Hardware timer interrupted once every second
//
static void timer_sec_int(void)
{
    ulUTC_second++;
}

static void fnSynchroniseLocalTime(SNTP_TIME *NewTime)
{
    static TIMER_INTERRUPT_SETUP timer_setup = {0};                  // interrupt configuration parameters
    timer_setup.int_type = TIMER_INTERRUPT;
    timer_setup.int_priority = PRIORITY_TIMERS;
    timer_setup.int_handler = timer_sec_int;
    timer_setup.timer_reference = 2;                                 // timer channel 2
    timer_setup.timer_mode = (TIMER_PERIODIC | TIMER_FORCE_FRACTION);// period timer interrupt
    timer_setup.timer_value = 1000;                                  // 1s periodic interrupt
    usFractionOffset = (unsigned short)(NewTime->ulFraction/TIMER_PERIOD_FULL_SCALE); // the fraction of a second offset to second interrupt and counter value
    uDisable_Interrupt();                                            // ensure the interrupt can not overflow before setting the seconds
    fnConfigureInterrupt((void *)&timer_setup);                      // enter interrupt for counting seconds
    ulUTC_second = NewTime->ulSeconds;
    uEnable_Interrupt();
}
#endif

// These initialisations are only performed when validated, either at startup or on validation
//
static void fnValidatedInit(void)
{
#if defined USE_TIME_SERVER                                              // do not initiate when validating
    #ifndef SUPPORT_LCD
/*  The following shows how to prime the address of the gateway so that no ARP is required to resolve it - of course the values must match the gateway!
    unsigned char server_ip[IPV4_LENGTH] = {192, 168, 0, 1};
    unsigned char server_mac[MAC_LENGTH] = {0x00, 0x0d, 0x88, 0xe7, 0x6a, 0x49};
    fnAddARP(server_ip, server_mac, ARP_FIXED_IP);                       */
    if ((TIME_TCP_socket = fnGetTCP_Socket(TOS_MINIMISE_DELAY, TCP_DEFAULT_TIMEOUT, fnTimeListener)) >= 0) {
        fnTCP_Connect(TIME_TCP_socket, (unsigned char *)&ucTimeServers[ucTimeServerTry++], TIME_PORT, 0, 0);
    }
    #endif
#elif defined USE_SNTP                                                   // {50}
    if (!((SNTP_Socket = fnGetUDP_socket(TOS_MINIMISE_DELAY, fnSNTP_client, (UDP_OPT_SEND_CS | UDP_OPT_CHECK_CS))) < 0)) {
        const SNTP_TIME default_time = {REFERENCE_TIME, 0};
        fnSynchroniseLocalTime((SNTP_TIME *)&default_time);              // set a default time for use until the synchronised time is known
        fnBindSocket(SNTP_Socket, SNTP_PORT);                            // bind socket
        fnRequestSNTP_time(0);                                           // start request of time from SNTP server
    }
#endif
#if defined SUPPORT_LCD || (defined SUPPORT_GLCD && !defined GLCD_COLOR) || defined SUPPORT_OLED || defined SUPPORT_TFT // {38}
    #if defined MB785_GLCD_MODE && defined SDCARD_SUPPORT                // allow the mass storage to initialise before starting the LCD (on same SPI interface on the STM3210C-EVAL)
    if (!(_READ_PORT_MASK(E, PORTE_BIT0))) {                             // if the SD card is detected, don't start the LCD task yet but wait until the card has beein initialised
        uTaskerStateChange(TASK_MASS_STORAGE, UTASKER_ACTIVATE);         // start mass storage task
        uTaskerMonoTimer(TASK_LCD, (DELAY_LIMIT)(0.25*SEC), 0xff );      // start the LCD task after giving the SD card initialisation time to complete
    }
    else {
        uTaskerStateChange(TASK_LCD, UTASKER_ACTIVATE);                  // start LCD task only when not validating
    }
    #elif (defined IDM_L35_B || defined M52259_TOWER || defined AVR32_EVK1105 || defined OLIMEX_LPC2478_STK) && defined SDCARD_SUPPORT
    uTaskerMonoTimer(TASK_LCD, (DELAY_LIMIT)(0.25*SEC), 0xff );          // start the LCD task after giving the SD card initialisation time to complete
    #else
    uTaskerStateChange(TASK_LCD, UTASKER_ACTIVATE);                      // start LCD task only when not validating
    #endif
#endif
#if defined (USE_SMTP) && !defined (USE_DNS) && defined (SMTP_PARAMETERS)
    uMemcpy(ucSMTP_server, temp_pars->temp_parameters.ucSMTP_server_ip, IPV4_LENGTH);
#endif
#if defined USE_MODBUS && !defined MODBUS_USB_SLAVE                      // {54}{47}
    fnInitModbus();                                                      // {27} initialise MODBUS
#endif
}

extern int fnAreWeValidating(void)
{
    return (iAppState == STATE_VALIDATING);
}

extern void fnWeHaveBeenValidated(void)
{
    iAppState = STATE_ACTIVE;
    uTaskerStopTimer(OWN_TASK);
    fnValidatedInit();
}


extern void fnSaveDebugHandle(int iState)
{
    save_handle = DebugHandle;                                           // push present debug handle
    switch (iState) {                                                    // {24}
#if defined SERIAL_INTERFACE
    case SOURCE_SERIAL:
        DebugHandle = SerialPortID;
        break;
#endif
#if defined USB_INTERFACE && defined USE_MAINTENANCE
    case SOURCE_USB:
        fnSetUSB_debug();                                                // select the USB connection as debug channel
        break;
#endif
    default:
        DebugHandle = NETWORK_HANDLE;
        break;
    }
}


extern void fnRestoreDebugHandle(void)
{
    DebugHandle = save_handle;                                           // pop debug handle
}

#if defined SERIAL_INTERFACE && defined DEMO_UART                        // {32}
extern QUEUE_HANDLE fnSetNewSerialMode(unsigned char ucDriverMode)
{
    TTYTABLE tInterfaceParameters;                                       // table for passing information to driver
    tInterfaceParameters.Channel = DEMO_UART;                            // set UART channel for serial use
    tInterfaceParameters.ucSpeed = temp_pars->temp_parameters.ucSerialSpeed; // baud rate
    tInterfaceParameters.Rx_tx_sizes.RxQueueSize = RX_BUFFER_SIZE;       // input buffer size
    tInterfaceParameters.Rx_tx_sizes.TxQueueSize = TX_BUFFER_SIZE;       // output buffer size
    tInterfaceParameters.Task_to_wake = OWN_TASK;                        // wake self when messages have been received
    #ifdef SUPPORT_FLOW_HIGH_LOW
    tInterfaceParameters.ucFlowHighWater = temp_pars->temp_parameters.ucFlowHigh;// set the flow control high and low water levels in %
    tInterfaceParameters.ucFlowLowWater = temp_pars->temp_parameters.ucFlowLow;
    #endif
    tInterfaceParameters.Config = temp_pars->temp_parameters.usSerialMode; // {43}
    #ifdef TEST_MSG_MODE
    tInterfaceParameters.Config |= (MSG_MODE);
        #if defined (TEST_MSG_CNT_MODE) && defined (SUPPORT_MSG_CNT)
    tInterfaceParameters.Config |= (MSG_MODE_RX_CNT);
        #endif
    tInterfaceParameters.Config &= ~USE_XON_OFF;
    tInterfaceParameters.ucMessageTerminator = '\r';
    #endif
    #ifdef SERIAL_SUPPORT_DMA
    tInterfaceParameters.ucDMAConfig = 0;                                // disable DMA
  //tInterfaceParameters.ucDMAConfig = UART_TX_DMA;                      // activate DMA on transmission
  //tInterfaceParameters.ucDMAConfig = (UART_RX_DMA/* | UART_RX_DMA_HALF_BUFFER*/); // test half buffer DMA reception
    #endif
    #ifdef SUPPORT_HW_FLOW
    tInterfaceParameters.Config |= RTS_CTS;                              // test RTS/CTS operation
    #endif
    if ((SerialPortID = fnOpen(TYPE_TTY, ucDriverMode, &tInterfaceParameters)) != 0) { // open or change the channel with defined configurations (initially inactive)
        fnDriver(SerialPortID, (TX_ON | RX_ON), 0);                      // enable rx and tx
        if (tInterfaceParameters.Config & RTS_CTS) {                     // {8}
            fnDriver(SerialPortID, (MODIFY_INTERRUPT | ENABLE_CTS_CHANGE), 0 ); // activate CTS interrupt when working with HW flow control (this returns also the present control line states)
            fnDriver(SerialPortID, (MODIFY_CONTROL | SET_RTS), 0 );      // activate RTS line when working with HW flow control
        }
    }
    return SerialPortID;
}
#endif


#ifdef USE_SMTP
extern void fnSendEmail(int iRepeat)
{
    #ifdef USE_DNS
    if (!uMemcmp(ucSMTP_server, cucNullMACIP, IPV4_LENGTH)) {
    #ifdef SMTP_PARAMETERS
        fnResolveHostName((CHAR *)temp_pars->temp_parameters.ucSMTP_server, fnDNSListner); // resolve SMTP server address so that we can send
    #else
        fnResolveHostName(cSMTP_provider, fnDNSListner);                 // resolve SMTP server address so that we can send
    #endif
    }
    else {
        fnConnectSMTP(ucSMTP_server, (unsigned char)(temp_pars->temp_parameters.usServers & SMTP_LOGIN), fnEmailTest); // initiate Email transmission
    }
    if (!iRepeat) {
        ucSMTP_Retry = 2;
    }
    #else
    fnConnectSMTP(ucSMTP_server, (unsigned char)(temp_pars->temp_parameters.usServers & SMTP_LOGIN), fnEmailTest); // initiate Email transmission
    #endif
}

// Email call back handler
//
static const CHAR *fnEmailTest(unsigned char ucEvent, unsigned short *usData)
{
    switch (ucEvent) {
    case SMTP_GET_DOMAIN:
        return cUserDomain;

    #ifdef USE_SMTP_AUTHENTICATION
        #ifdef SMTP_PARAMETERS
    case SMTP_USER_NAME:
        return (const CHAR*)temp_pars->temp_parameters.ucSMTP_user_name;

    case SMTP_USER_PASS:
        return (const CHAR*)temp_pars->temp_parameters.ucSMTP_password;
        #else
    case SMTP_USER_NAME:
        return cUserName;

    case SMTP_USER_PASS:
        return cUserPassword;
        #endif
    #endif

    case SMTP_GET_SENDER:
    #ifndef SMTP_PARAMETERS
        return cSender;
    #else
        return (CHAR *)temp_pars->temp_parameters.ucSMTP_user_email;
    #endif

    case SMTP_GET_DESTINATION:
        return cEmailAdd;

    case SMTP_GET_SUBJECT:
        return cSubject;

    case SMTP_SEND_MESSAGE:
        {
        unsigned short usEmailPosition = *usData;
        if (usEmailPosition >= (sizeof(cEmailText) - 1)) {               // here we can send our Email message
            *usData = 0;
            return 0;                                                    // email has been completely sent, inform that there is no more
        }
        else {
            *usData = (sizeof(cEmailText) - 1) - usEmailPosition;        // remaining length
        }
        return (const CHAR *)&cEmailText[usEmailPosition];               // give a pointer to the next part of message
        }

    case SMTP_MAIL_SUCCESSFULLY_SENT:                                    // Our email has been successfully sent!!
        break;

  //case ERROR_SMTP_LOGIN_FAILED:                                        // user name and password incorrect
  //case ERROR_SMTP_LOGIN_NOT_SUPPORTED:                                 // we are trying to use login but the server doesn't support it
  //case ERROR_SMTP_POLICY_REJECT:                                       // we are not using login but the server insists on it
  //case ERROR_SMTP_TIMEOUT:                                             // connection timed out
    default:                                                             // an error has occurred
    #ifdef USE_DNS
        uMemcpy(ucSMTP_server, cucNullMACIP, IPV4_LENGTH);               // we reset the IP address since it may be that the server has changed it address
        if (ucSMTP_Retry) {
            ucSMTP_Retry--;
            fnSendEmail(1);                                              // retry
        }
    #endif
        break;
    }
    return 0;
}
#endif                                                                   // #endif USE_SMTP


#if defined USE_TIME_SERVER
    #ifdef SUPPORT_LCD
static void fnDisplayTime(unsigned long ulPresentTime)
{
    CHAR cNewTimeLCD[17];

    cNewTimeLCD[0] = (signed char)0x80;                                  // position to start of first line
    uMemset(&cNewTimeLCD[1], ' ', 16);

    fnAddTime(&cNewTimeLCD[5]);

    fnDoLCD_com_text(E_LCD_COMMAND_TEXT, (unsigned char *)cNewTimeLCD, 17);
}
    #endif                                                               // #endif SUPPORT_LCD

static int fnTimeListener(USOCKET Socket, unsigned char ucEvent, unsigned char *ucIp_Data, unsigned short usPortLen)
{
    #ifdef SUPPORT_LCD
    static const CHAR cErrorLCDGW[] = "\x01 No gateway?";                // TCP socket error message
    static const CHAR cNextServerLCD[] = "\x01 Try next server?";        // next server try text
    static const CHAR cNoTimeLCD[] = "\x01 Sorry no time";               // failed text
    #endif

    switch (ucEvent) {
    case TCP_EVENT_ARP_RESOLUTION_FAILED:
    #ifdef SUPPORT_LCD
        fnDoLCD_com_text(E_LCD_COMMAND_TEXT, (unsigned char *)cErrorLCDGW, (sizeof(cErrorLCDGW) - 1));
    #endif
        break;

    case TCP_EVENT_DATA:                                                 // a time server sends the time in seconds from 0:0:0 1900 and terminates
        ulPresentTime = *ucIp_Data++;
        ulPresentTime <<= 8;
        ulPresentTime |= *ucIp_Data++;
        ulPresentTime <<= 8;
        ulPresentTime |= *ucIp_Data++;
        ulPresentTime <<= 8;
        ulPresentTime |= (*ucIp_Data);

        ulPresentTime -= REL_9_1_2007;                                   // relative to 0:0:0 on 9.1.2007
        ulPresentTime += GMT_OFFSET;
        while (ulPresentTime >= SECONDS_24_HOURS) {
            ulPresentTime -= SECONDS_24_HOURS;                           // convert to seconds of day
        }
    #ifdef SUPPORT_LCD
        fnDisplayTime(ulPresentTime);
    #endif
        uTaskerMonoTimer( OWN_TASK, (DELAY_LIMIT)(1*SEC), E_SECOND_TICK );
        break;

    case TCP_EVENT_CLOSE:
    case TCP_EVENT_CLOSED:
        if (ulPresentTime) {
            break;
        }
                                                                         // if remote server closed before we received the time, try next
    case TCP_EVENT_ABORT:                                                // no connection was established
        if (ucTimeServerTry < NUMBER_OF_TIME_SERVERS) {
            fnTCP_Connect(TIME_TCP_socket, (unsigned char *)&ucTimeServers[ucTimeServerTry++], TIME_PORT, 0, 0); // {15} ucTimeServerTry incremented after use and not before
    #ifdef SUPPORT_LCD
            fnDoLCD_com_text(E_LCD_COMMAND_TEXT, (unsigned char *)cNextServerLCD, (sizeof(cNextServerLCD) - 1));
    #endif
        }
    #ifdef SUPPORT_LCD
        else {
            fnDoLCD_com_text(E_LCD_COMMAND_TEXT, (unsigned char *)cNoTimeLCD, (sizeof(cNoTimeLCD) - 1));
        }
    #endif
        break;
    }
    return APP_ACCEPT;
}
#elif defined USE_SNTP

static SNTP_TIME sntp_update_time = {0xce253ff0, 0x06f69446};

static void fnSetTimeField(unsigned char *time_field, SNTP_TIME *sntp_time_stamp)
{
    *time_field++ = (unsigned char)(sntp_time_stamp->ulSeconds >> 24);
    *time_field++ = (unsigned char)(sntp_time_stamp->ulSeconds >> 16);
    *time_field++ = (unsigned char)(sntp_time_stamp->ulSeconds >> 8);
    *time_field++ = (unsigned char)(sntp_time_stamp->ulSeconds);
    *time_field++ = (unsigned char)(sntp_time_stamp->ulFraction >> 24);
    *time_field++ = (unsigned char)(sntp_time_stamp->ulFraction >> 16);
    *time_field++ = (unsigned char)(sntp_time_stamp->ulFraction >> 8);
    *time_field   = (unsigned char)(sntp_time_stamp->ulFraction);
}

static void fnDisplayUTC(SNTP_TIME *utc_time)
{
    unsigned long ulDays;
    unsigned long ulHours;
    unsigned long ulMinutes;
    unsigned long ulSeconds  = utc_time->ulSeconds;
    unsigned long ulFraction = utc_time->ulFraction;

    fnDebugHex(utc_time->ulSeconds, (WITH_LEADIN | sizeof(utc_time->ulSeconds)));
    fnDebugMsg(":");
    fnDebugHex(utc_time->ulFraction, (WITH_LEADIN | sizeof(utc_time->ulFraction)));

    ulSeconds -= REFERENCE_TIME;                                         // reference from 6th August 2009 at 0:0:0
    ulDays = (ulSeconds / (60*60*24));                                   // days elapsed
    ulSeconds -= (ulDays * (60*60*24));
    ulHours = (ulSeconds / (60*60));                                     // additional hours elapsed
    ulSeconds -= (ulHours * (60*60));
    ulMinutes = (ulSeconds / 60);                                        // additional minutes elapsed
    ulSeconds -= (ulMinutes * 60);
    fnDebugDec(ulDays, (WITH_SPACE));                                    // days elapsed since reference data
    fnDebugMsg(":");
    fnDebugDec(ulHours, 0);                                              // hours elapsed since reference data
    fnDebugMsg(":");
    fnDebugDec(ulMinutes, LEADING_ZERO);                                 // minutes elapsed since reference data
    fnDebugMsg(":");
    fnDebugDec(ulSeconds, LEADING_ZERO);                                 // minutes elapsed since reference data
    fnDebugMsg(".");
    fnDebugHex(ulFraction, (WITH_CR_LF | 4));
}

// Get the present time stamp
//
static void fnGetPresentTime(SNTP_TIME *PresentTime)
{
    uDisable_Interrupt();                                                // don't allow second interrupt to disturb, but don't stop timer either
    PresentTime->ulSeconds  = ulUTC_second;                              // present seconds value
    PresentTime->ulFraction = GET_TIMER_PRESENT_VALUE();
    if (PresentTime->ulFraction < 100) {                                 // is it possible that the timer overflowed while reading present value?
        uEnable_Interrupt();                                             // if a second overflow occurred during the protected region it will be take place now
        PresentTime->ulSeconds  = ulUTC_second;                          // present seconds value, possibly after interrupt increment
        uDisable_Interrupt();                                            // block again
    }
    uEnable_Interrupt();                                                 // allow timer to interrupt again since the values have been captured and are synchronised
    PresentTime->ulFraction += usFractionOffset;                         // respect offset to the local timer (can only be positive)
    if (PresentTime->ulFraction >= TIMER_PERIOD_FULL_SCALE) {
        PresentTime->ulSeconds++;
        PresentTime->ulFraction -= TIMER_PERIOD_FULL_SCALE;
    }
    PresentTime->ulFraction *= TIMER_PERIOD_FULL_SCALE;                  // convert to fraction of a second, assuming 16 bit full scale
}

static void fnRequestSNTP_time(int iTryNext)
{
    int iJumpedServers = 0;
    SNTP_MESSAGE sntp_message = {{0}};
    while ((iTryNext != 0) || (SNTP_ServerList[ucServerIndex].ucAvoidServer != 0)) {
        if (++ucServerIndex >= MAX_SNTP_SERVERS) {
            ucServerIndex = 0;
        }
        if (++iJumpedServers >= MAX_SNTP_SERVERS) {                      // avoid all servers on avoid list
            SNTP_ServerList[ucServerIndex].ucAvoidServer = 0;
            break;
        }
        iTryNext = 0;
    }
    sntp_message.sntp_data_content.ucFlags = (NTP_FLAG_LI_CLOCK_NOT_SYNCHRONISED | NTP_FLAG_VN_3 | NTP_FLAG_MODE_CLIENT);
    sntp_message.sntp_data_content.ucPeerPollingInterval = 0x0a;         // 1024 seconds
    sntp_message.sntp_data_content.ucPeerClockPrecision = 0xfa;          // ??
    sntp_message.sntp_data_content.ucRootDispersion[0] = 0x00;           // ??
    sntp_message.sntp_data_content.ucRootDispersion[1] = 0x01;           // ??
    sntp_message.sntp_data_content.ucRootDispersion[2] = 0x03;           // ??
    sntp_message.sntp_data_content.ucRootDispersion[3] = 0xfe;           // ??
    fnSetTimeField(sntp_message.sntp_data_content.ucReferenceClockUpdateTime, &sntp_update_time);
    fnGetPresentTime(&clientRequestTime);
    fnSetTimeField(sntp_message.sntp_data_content.ucTransmitTimeStamp, &clientRequestTime);
    fnSendUDP(SNTP_Socket, SNTP_ServerList[ucServerIndex].server_ip_address, SNTP_PORT, (unsigned char *)&sntp_message, sizeof(sntp_message.sntp_data_content), OWN_TASK); // request time from SNTP server
    fnDebugMsg("\r\nTime requested at ");
    fnDisplayUTC(&clientRequestTime);
    uMemcpy(&sntp_update_time, &clientRequestTime, sizeof(sntp_update_time));
    uTaskerMonoTimer(OWN_TASK, (DELAY_LIMIT)(15.0*SEC), E_NEXT_TIME_SYNC);
}


// Subtract a more ancient time stamp from a more recent
// Note that this is simplified for positive results only but a simple manipulation of the client times
// is adequate to esure this
//
static void fnSubtractTime(SNTP_TIME *result, SNTP_TIME *input, SNTP_TIME *minus)
{
    result->ulSeconds = (input->ulSeconds - minus->ulSeconds);
    result->ulFraction = (input->ulFraction - minus->ulFraction);
    if (input->ulFraction < minus->ulFraction) {
        result->ulSeconds--;
    }
}

static void fnAdditionTime(SNTP_TIME *result, SNTP_TIME *input, SNTP_TIME *add)
{
    unsigned long ulFraction;
    result->ulSeconds = (input->ulSeconds + add->ulSeconds);
    ulFraction = (input->ulFraction + add->ulFraction);
    if (ulFraction < input->ulFraction) {
        result->ulSeconds++;
    }
    result->ulFraction = ulFraction;
}

static void fnDiv2Time(SNTP_TIME *result, SNTP_TIME *input)
{
    register unsigned long ulIntermediateSeconds;
    ulIntermediateSeconds = (input->ulSeconds/2);
    result->ulFraction = (input->ulFraction/2);
    if ((ulIntermediateSeconds * 2) != input->ulSeconds) {
        result->ulFraction += 0x80000000;                                // add half to the fraction result
        if (result->ulFraction < 0x80000000) {                           // check for overrun
            ulIntermediateSeconds++;
        }
    }
    result->ulSeconds = ulIntermediateSeconds;
}

static int fnUpdateSNTP_time(NTP_FRAME *ntp_frame, unsigned short usLength)
{
    if (ntp_frame->ucPeerClockStratum == STRATUM_KISS_O_DEATH) {
        return USE_OTHER_SNTP_SERVER;                                    // the server is requesting that we no longer use it
    }
    if ((ntp_frame->ucFlags & NTP_FLAG_LI_MASK) == NTP_FLAG_LI_CLOCK_NOT_SYNCHRONISED) {
        return SNTP_SERVER_NOT_SYNCHRONISED;                             // server is not synchronised
    }
    if (!(uMemcmp(ntp_frame->ucTransmitTimeStamp, &zero_time, sizeof(SNTP_TIME)))) {
        return SNTP_SERVER_FORMAT_ERROR;                                 // reject invalid time stamp
    }
    if (((ntp_frame->ucFlags & NTP_FLAG_MODE_MASK) != NTP_FLAG_MODE_SERVER) && ((ntp_frame->ucFlags & NTP_FLAG_MODE_MASK) != NTP_FLAG_MODE_BROADCAST)) {
        return SNTP_SERVER_MODE_INVALID;                                 // reject any sources which are not servers or broadcast servers
    }
    else {
        unsigned long ulSecondsShift = 0;
        SNTP_TIME Calculate;
        SNTP_TIME PresentTime;
        SNTP_TIME RoundTripTime;
        SNTP_TIME SystemClockOffset;
        SNTP_TIME ServerTransmitTimeStamp;
        SNTP_TIME ServerReceiveTimeStamp;
        ServerTransmitTimeStamp.ulSeconds   = (ntp_frame->ucTransmitTimeStamp[0] << 24);
        ServerTransmitTimeStamp.ulSeconds  |= (ntp_frame->ucTransmitTimeStamp[1] << 16);
        ServerTransmitTimeStamp.ulSeconds  |= (ntp_frame->ucTransmitTimeStamp[2] << 8);
        ServerTransmitTimeStamp.ulSeconds  |= (ntp_frame->ucTransmitTimeStamp[3]);
        ServerTransmitTimeStamp.ulFraction  = (ntp_frame->ucTransmitTimeStamp[4] << 24);
        ServerTransmitTimeStamp.ulFraction |= (ntp_frame->ucTransmitTimeStamp[5] << 16);
        ServerTransmitTimeStamp.ulFraction |= (ntp_frame->ucTransmitTimeStamp[6] << 8);
        ServerTransmitTimeStamp.ulFraction |= (ntp_frame->ucTransmitTimeStamp[7]);

        ServerReceiveTimeStamp.ulSeconds    = (ntp_frame->ucReceiveTimeStamp[0] << 24);
        ServerReceiveTimeStamp.ulSeconds   |= (ntp_frame->ucReceiveTimeStamp[1] << 16);
        ServerReceiveTimeStamp.ulSeconds   |= (ntp_frame->ucReceiveTimeStamp[2] << 8);
        ServerReceiveTimeStamp.ulSeconds   |= (ntp_frame->ucReceiveTimeStamp[3]);
        ServerReceiveTimeStamp.ulFraction   = (ntp_frame->ucReceiveTimeStamp[4] << 24);
        ServerReceiveTimeStamp.ulFraction  |= (ntp_frame->ucReceiveTimeStamp[5] << 16);
        ServerReceiveTimeStamp.ulFraction  |= (ntp_frame->ucReceiveTimeStamp[6] << 8);
        ServerReceiveTimeStamp.ulFraction  |= (ntp_frame->ucReceiveTimeStamp[7]);

        fnGetPresentTime(&PresentTime);                                  // the present local time stamp

        fnDebugMsg("\r\nServer rx: ");
        fnDisplayUTC(&ServerReceiveTimeStamp);

        fnDebugMsg("Server tx: ");
        fnDisplayUTC(&ServerTransmitTimeStamp);

        fnDebugMsg("Received at: ");
        fnDisplayUTC(&PresentTime);

        if (ServerReceiveTimeStamp.ulSeconds >= (clientRequestTime.ulSeconds - 2)) { // ensure that the correction that is needed is always positive (by manipulating the client time stamps if necessary)
            ulSecondsShift = ((ServerReceiveTimeStamp.ulSeconds - clientRequestTime.ulSeconds) + 2);
            clientRequestTime.ulSeconds -= ulSecondsShift;
            PresentTime.ulSeconds -= ulSecondsShift;
        }
        if (ServerTransmitTimeStamp.ulSeconds >= (PresentTime.ulSeconds - 2)) {
            unsigned long ulNewSecondsShift;
            ulNewSecondsShift = ((ServerTransmitTimeStamp.ulSeconds - PresentTime.ulSeconds) + 2);
            clientRequestTime.ulSeconds -= ulNewSecondsShift;
            PresentTime.ulSeconds -= ulNewSecondsShift;
            ulSecondsShift += ulNewSecondsShift;
        }

        fnSubtractTime(&RoundTripTime, &PresentTime, &clientRequestTime);// PresentTime - clientRequestTime
        fnSubtractTime(&Calculate, &ServerTransmitTimeStamp, &ServerReceiveTimeStamp); // ServerTransmitTimeStamp - ServerReceiveTimeStamp
        fnSubtractTime(&RoundTripTime, &RoundTripTime, &Calculate);      // round trip time

        fnSubtractTime(&SystemClockOffset, &ServerReceiveTimeStamp, &clientRequestTime);// ServerReceiveTimeStamp - clientRequestTime
        fnSubtractTime(&Calculate, &ServerTransmitTimeStamp, &PresentTime); // ServerTransmitTimeStamp - PresentTime
        fnAdditionTime(&SystemClockOffset, &SystemClockOffset, &Calculate); // (ServerReceiveTimeStamp - clientRequestTime) + (ServerTransmitTimeStamp - PresentTime)
        fnDiv2Time(&SystemClockOffset, &SystemClockOffset);              // divide to obtain the system clock offset
        fnAdditionTime(&PresentTime, &PresentTime, &SystemClockOffset);
        fnSynchroniseLocalTime(&PresentTime);                            // synchronise local time
        fnDebugMsg("\r\nNew time: ");
        fnDisplayUTC(&PresentTime);
    }
    return SNTP_SYNCHRONISED;
}

// SNTP UDP client - reception call back function
//
extern int fnSNTP_client(USOCKET SocketNr, unsigned char ucEvent, unsigned char *ucIP, unsigned short usPortNr, unsigned char *data, unsigned short usLength)
{
    switch (ucEvent) {
    case UDP_EVENT_RXDATA:
        if (uMemcmp(SNTP_ServerList[ucServerIndex].server_ip_address, ucIP, IPV4_LENGTH) != 0) {
            break;                                                       // ignore if not from expected IP address
        }
        fnUpdateSNTP_time((NTP_FRAME *)data, usLength);                  // synchronise the local time
        break;

    case UDP_EVENT_PORT_UNREACHABLE:                                     // we have received information that this port is not available at the destination so quit
        break;
    }
    return 0;
}
#endif                                                                   // #endif USE_SNTP


#if defined (USE_SMTP) && defined (USE_DNS)
static void fnDNSListner(unsigned char ucEvent, unsigned char *ptrIP)
{
    switch (ucEvent) {
    case DNS_EVENT_SUCCESS:
    #ifdef USE_SMTP
        uMemcpy(ucSMTP_server, ptrIP, IPV4_LENGTH);                      // save the IP address which has just been resolved
        fnConnectSMTP(ucSMTP_server, (unsigned char)(temp_pars->temp_parameters.usServers & SMTP_LOGIN), fnEmailTest); // initiate Email transmission
    #endif
        break;

    default:                                                             // DNS error message
        break;
    }
}
#endif


#if defined SUPPORT_LCD || (defined SUPPORT_GLCD || defined SUPPORT_OLED) // {38}{48}
    #define LCD_MESSAGE_ROUTINES                                         // message transmission routines to the LCD task - see the file application_lcd.h
    #include "application_lcd.h"                                         // include support routines
    #undef LCD_MESSAGE_ROUTINES
#endif


#ifdef TEST_GLOBAL_TIMERS
// Test code allowing the use of global timers to be evaluated
//
static void fnStartGlobalTimers(void)
{
    CONFIG_TIMER_TEST_LEDS();
    TIMER_TEST_LED_ON();
    TIMER_TEST_LED2_ON();
#ifdef GLOBAL_HARDWARE_TIMER
    uTaskerGlobalMonoTimer( (UTASK_TASK)(OWN_TASK | HARDWARE_TIMER), (CLOCK_LIMIT)(10*MILLISEC),  E_TIMER_TEST_10MS ); // start a 10ms timer
    uTaskerGlobalMonoTimer( (UTASK_TASK)(OWN_TASK | HARDWARE_TIMER), (CLOCK_LIMIT)(40*MILLISEC),   E_TIMER_TEST_3MS );  // start a 3ms timer
  //uTaskerGlobalMonoTimer( (UTASK_TASK)(OWN_TASK | HARDWARE_TIMER), (CLOCK_LIMIT)(5*MILLISEC),   E_TIMER_TEST_5MS );  // start a 5ms timer
#else
    uTaskerGlobalMonoTimer( OWN_TASK, (CLOCK_LIMIT)(10*SEC),  E_TIMER_TEST_10S );   // start a 10s timer
    uTaskerGlobalMonoTimer( OWN_TASK, (CLOCK_LIMIT)(3*SEC),   E_TIMER_TEST_3S );    // start a 3s timer
    uTaskerGlobalMonoTimer( OWN_TASK, (CLOCK_LIMIT)(5*SEC),   E_TIMER_TEST_5S );    // start a 5s timer
#endif
}

// Test timer event handler
//
static void fnHandleGlobalTimers(unsigned char ucTimerEvent)
{
    switch (ucTimerEvent) {
    case E_TIMER_TEST_3S:
        TIMER_TEST_LED_OFF();
#ifdef GLOBAL_HARDWARE_TIMER
        uTaskerGlobalMonoTimer( (UTASK_TASK)(OWN_TASK | HARDWARE_TIMER), (CLOCK_LIMIT)(3*MILLISEC),  E_TIMER_TEST_3MS );  // restart 3ms timer
#else
        uTaskerGlobalMonoTimer( OWN_TASK, (CLOCK_LIMIT)(3*SEC),  E_TIMER_TEST_3S ); // restart 3s timer
#endif
        break;

    case E_TIMER_TEST_5S:
        TIMER_TEST_LED_ON();
#ifdef GLOBAL_HARDWARE_TIMER
        uTaskerGlobalStopTimer( (UTASK_TASK)(OWN_TASK | HARDWARE_TIMER), E_TIMER_TEST_3MS); // kill the 3ms timer
        uTaskerGlobalMonoTimer( (UTASK_TASK)(OWN_TASK | HARDWARE_TIMER), (CLOCK_LIMIT)(4*MILLISEC),   E_TIMER_TEST_10MS ); // shorten 10 timer
        uTaskerGlobalMonoTimer( (UTASK_TASK)(OWN_TASK | HARDWARE_TIMER), (CLOCK_LIMIT)(4*MILLISEC),   E_TIMER_TEST_4MS );  // start a new 4ms timer
#else
        uTaskerGlobalStopTimer( OWN_TASK, E_TIMER_TEST_3S);             // kill the 3s timer
        uTaskerGlobalMonoTimer( OWN_TASK, (CLOCK_LIMIT)(4*SEC),   E_TIMER_TEST_10S ); // shorten 10s timer
        uTaskerGlobalMonoTimer( OWN_TASK, (CLOCK_LIMIT)(4*SEC),   E_TIMER_TEST_4S );  // start a new 4s timer
#endif
        break;

    case E_TIMER_TEST_10S:
        TIMER_TEST_LED_OFF();
        break;

    case E_TIMER_TEST_4S:
        TIMER_TEST_LED2_OFF();
        break;

    default:
        break;
    }
}

#endif

#ifdef DEMO_UDP
// configure socket for use with UDP protocol
//
static void fnConfigUDP(void)
{
    if (!((MyUDP_Socket = fnGetUDP_socket(TOS_MINIMISE_DELAY, fnUDPListner, (UDP_OPT_SEND_CS | UDP_OPT_CHECK_CS))) < 0)) {
        fnBindSocket(MyUDP_Socket, MY_UDP_PORT);                         // bind socket
        ptrUDP_Frame    = uMalloc(sizeof(UDP_MESSAGE));                  // get some memory for UDP frame
    }
    else {
        return;                                                          // no socket - this must never happen (ensure that enough user UDP sockets have been defined - USER_UDP_SOCKETS in config.h)!!
    }
}

// UDP data server - reception call back function
//
extern int fnUDPListner(USOCKET SocketNr, unsigned char ucEvent, unsigned char *ucIP, unsigned short usPortNr, unsigned char *data, unsigned short usLength)
{
    switch (ucEvent) {
    case UDP_EVENT_RXDATA:
        //if (usPortNr != MY_UDP_PORT) break;                            // ignore false ports
        //if (uMemcmp(ucIP, ucUDP_IP_Address, IPV4_LENGTH)) break;       // ignore if not from expected IP address

        //if (usLength <= UDP_BUFFER_SIZE) {                             // ignore frames which are too large
            //uMemcpy(&ptrUDP_Frame->ucUDP_Message, data, usLength);     // send the received UDP frame back
            //fnSendUDP(MyUDP_Socket, ucUDP_IP_Address, MY_UDP_PORT, (unsigned char*)&ptrUDP_Frame->tUDP_Header, usLength, OWN_TASK);
        //}
        fnSendUDP(MyUDP_Socket, ucIP, usPortNr, (data - sizeof(UDP_HEADER)), usLength, OWN_TASK); // echo back from transmitting IP and port
        break;

    case UDP_EVENT_PORT_UNREACHABLE:                                     // we have received information that this port is not available at the destination so quit
        break;
    }
    return 0;
}
#endif

#ifdef USE_SNMP
static int fnSNMP_callback(unsigned char ucEvent, unsigned char *data, unsigned short usLength)
{
    switch (ucEvent) {
    case SNMP_COMMUNITY_CHECK:
        return (uMemcmp(cSNMP_community, data, usLength));               // check that the SNMP request belongs to our community

    case SNMP_GET_COMMUNITY:
        uMemcpy(data, cSNMP_community, (sizeof(cSNMP_community) - 1));   // add community details to SNMP trap
        return (sizeof(cSNMP_community) - 1);

    case SNMP_GET_ENTERPRISE:                                            // add enterprise details to SNMP trap
        uMemcpy(data, cSNMP_enterprise, (sizeof(cSNMP_enterprise)));
        return (sizeof(cSNMP_enterprise));
    }
    return 0;
}
#endif

#ifdef TEST_TFTP
static void tftp_listener(unsigned short usError, CHAR *error_text)
{
    switch (usError) {
    case TFTP_ARP_RESOLVED:                                              // we should repeat the transfer since the TFTP server IP address has been resolved by ARP
        fnTransferTFTP();
        break;

    case TFTP_ARP_RESOLUTION_FAILED:                                     // ARP failed, the server doesn't exist
        break;

    case TFTP_FILE_EQUALITY:                                             // file transfered from TFTP is identical to file already in file system
        break;

    case TFTP_FILE_NOT_EQUAL:                                            // file transfered from TFTP is different from the file already in file system
        break;

    case TFTP_TRANSFER_WRITE_COMPLETE:                                   // write completed successfully
        break;

    case TFTP_TRANSFER_READ_COMPLETE:                                    // read completed successfully
        break;

    case TFTP_TRANSFER_DID_NOT_START:                                    // TFTP server available but it didn't start the transfer
    case TFTP_DESTINATION_UNREACHABLE:
    case TFTP_FILE_NOT_FOUND:                                            // requested file was not found on the server
        fnStopTFTP_server();                                             // abort any activity
        break;
    }
}

// Test TFTP transfer
//
static void fnTransferTFTP()
{
    //fnStartTFTP_client(tftp_listener, ucTFTP_server_ip, TFTP_GET, "test.txt", '0'); // get a file (text.txt) from TFTP server and save it locally (to file '0')
    //fnStartTFTP_client(tftp_listener, ucTFTP_server_ip, TFTP_GET_COMPARE, "test.txt", '0'); // get a file (text.txt) from TFTP server and compare it to local file ('0')
    fnStartTFTP_client(tftp_listener, ucTFTP_server_ip, TFTP_PUT, "test1.txt", '0');  // transfer local file ('0') to TFTP server and save it there as (test1.txt)
}
#endif


#define _IIC_RTC_CODE
    #include "iic_tests.h"                                               // include IIC RTC code to save and retrieve the time and convert format as well as handle a second interrupt
#undef _IIC_RTC_CODE
#define _IIC_SENSOR_CODE                                                 // {56}
    #include "iic_tests.h"                                               // include IIC sensor routines
#undef _IIC_SENSOR_CODE
#define _IIC_INIT_CODE
    #include "iic_tests.h"                                               // include IIC test initialisation routine
#undef _IIC_INIT_CODE

#ifdef TEST_DISTRIBUTED_TX
static void fnSendDist(void)
{
    unsigned char ucMessage[ HEADER_LENGTH + 1 ];

    ucMessage[ MSG_DESTINATION_NODE ] = OurNetworkNumber + 1;            // destination node
    ucMessage[ MSG_SOURCE_NODE ]      = OurNetworkNumber;                // own node
    ucMessage[ MSG_DESTINATION_TASK ] = TASK_LCD;                        // destination task
    ucMessage[ MSG_SOURCE_TASK ]      = OWN_TASK;                        // own task
    ucMessage[ MSG_CONTENT_LENGTH ]   = 1;                               // message length
    ucMessage[ MSG_CONTENT_COMMAND ]  = 0x55;                            // test data

    fnWrite( INTERNAL_ROUTE, ucMessage, (QUEUE_TRANSFER)(1 + HEADER_LENGTH));// send message to defined task
    ucMessage[ MSG_CONTENT_COMMAND ]  = 0xaa;                            // test data
    fnWrite( INTERNAL_ROUTE, ucMessage, (QUEUE_TRANSFER)(1 + HEADER_LENGTH));// send message to defined task
    ucMessage[ MSG_CONTENT_COMMAND ]  = 0xbb;                            // test data
    fnWrite( INTERNAL_ROUTE, ucMessage, (QUEUE_TRANSFER)(1 + HEADER_LENGTH));// send message to defined task
}
#endif

// The user has the chance to configure things very early after startup (Note - heap not yet available!)
//
extern void fnUserHWInit(void)
{
#ifdef SC16IS7XX_CLOCK_FROM_TIMER
    CONFIG_SC16IS7XX_CLOCK();
#endif
#ifdef LAN_REPORT_ACTIVITY
    CONFIGURE_LAN_LEDS();                                                // configure and drive ports
#endif
#ifdef SUPPORT_KEY_SCAN
    INIT_KEY_SCAN();                                                     // general initialisation for key scan
#endif
#if (defined SPECIAL_LCD_DEMO || defined SUPPORT_GLCD) && !defined SUPPORT_TFT // {38} configure GLCD ports and drive RST line if required
    CONFIGURE_GLCD();
#endif
#if defined MB785_GLCD_MODE && defined SDCARD_SUPPORT
    _CONFIG_PORT_INPUT(E, PORTE_BIT0, (INPUT_PULL_UP | PULLUP_BIT0));
#endif
#if defined ETH_INTERFACE
    #if defined M52259EVB                                                // this board has a DP83640 PHY, which requires a 167ms powerup stabilisation delay. The reset is help low for this period. It is released when configuring the Ethernet connection.
    RESET_RCR |= FRCRSTOUT;                                              // assert RSTO
    #elif defined RESET_PHY
    ASSERT_PHY_RST();                                                    // immediately set PHY to reset state
    CONFIG_PHY_STRAPS();                                                 // configure the required strap options - the reset line will be de-asserted during the Ethernet configuration
    #endif
#endif
}

#define _PORT_INT_CODE
    #include "Port_Interrupts.h"                                         // port interrupt configuration code and interrupt handling
#undef  _PORT_INT_CODE

#ifdef RTC_TEST                                                          // {3}

// Interrupt once a minute
//
static void test_minute_tick(void)
{
    static int iBlick = 0;

    RTC_SETUP rtc_setup;
    rtc_setup.command = RTC_GET_TIME;

    fnConfigureRTC((void *)&rtc_setup);                                  // get the present time

    if (iBlick == 0) {
        iBlick = 1;
        TIMER_TEST_LED_OFF();
    }
    else {
        iBlick = 0;
        TIMER_TEST_LED_ON();
    }
}

// Interrupt at alarm time
//
static void test_alarm(void)
{
    RTC_SETUP rtc_setup;
    rtc_setup.command = RTC_GET_TIME;

    fnConfigureRTC((void *)&rtc_setup);                                  // get the present time

    TIMER_TEST_LED_ON();
    TIMER_TEST_LED2_OFF();
}

// Interrupt at stopwatch count down
//
static void test_stopwatch(void)
{
    RTC_SETUP rtc_setup;
    rtc_setup.command = (RTC_TICK_MIN | RTC_DISABLE);

    fnConfigureRTC((void *)&rtc_setup);                                  // disable further minute TICKs

    rtc_setup.command = RTC_TICK_SEC;                                    // change to second TICKs
    rtc_setup.int_handler = test_minute_tick;                            // re-use this interrupt routine

    fnConfigureRTC((void *)&rtc_setup);

    TIMER_TEST_LED2_ON();
}

static void fnTestRTC(void)
{
    RTC_SETUP rtc_setup;

    CONFIG_TIMER_TEST_LEDS();                                            // drive some LEDs for visibility
    TIMER_TEST_LED_ON();
    TIMER_TEST_LED2_ON();

    rtc_setup.command = RTC_TIME_SETTING;                                // set the present time to the RTC (this could be collected from a timer server and is a fixed value here)
    rtc_setup.usDays    = 5;
    rtc_setup.hours     = 3;
    rtc_setup.ucMinutes = 23;
    rtc_setup.ucSeconds = 53;

    fnConfigureRTC((void *)&rtc_setup);                                  // set the time

    rtc_setup.command = RTC_TICK_MIN;                                    // configure periodic interrupts - once a minute
    rtc_setup.int_handler = test_minute_tick;
    fnConfigureRTC((void *)&rtc_setup);                                  // set a minute interrupt rate (first expected after 7 seconds)

    rtc_setup.command = RTC_ALARM_TIME;                                  // set an alarm time
    rtc_setup.int_handler = test_alarm;
    rtc_setup.ucMinutes = 24;
    rtc_setup.ucSeconds = 14;
    fnConfigureRTC((void *)&rtc_setup);                                  // set an alarm interrupt rate (expected after 21 seconds)

    rtc_setup.command = RTC_STOPWATCH_GO;                                // set a stop watch time (minutes to nearest minute)
    rtc_setup.int_handler = test_stopwatch;
    rtc_setup.ucMinutes = 2;
    fnConfigureRTC((void *)&rtc_setup);                                  // set 2 minute stop watch (expected after 67 seconds)
}
#endif

#define _ADC_TIMER_ROUTINES                                              // include ADC configuration and interrupt handlers
    #include "ADC_Timers.h"                                              // as well as PIT configuration and handling
#undef  _ADC_TIMER_ROUTINES                                              // and DMA timer, GPT timer and gstandard timer configuration and handling


#if (defined SPI_SW_UPLOAD || (defined (SPI_FILE_SYSTEM) && defined (FLASH_FILE_SYSTEM))) && defined TEST_SPI_FLASH
// Quick test of SPI FLASH. Ensure that this is disabled for real work with the uFileSystem in SPI FLASH otherwise the first page is corrupted
//
static void fnTestSPIFLASH(void)
{
     #ifdef SPI_SW_UPLOAD                                                // {14}
         #define SPI_CHIP_0_ADDRESS   uFILE_SYSTEM_END
     #else
         #define SPI_CHIP_0_ADDRESS   (MEMORY_RANGE_POINTER) SPI_FLASH_START
     #endif
     #define SPI_CHIP_1_ADDRESS   (SPI_CHIP_0_ADDRESS + SPI_DATA_FLASH_0_SIZE)
     #define SPI_CHIP_2_ADDRESS   (SPI_CHIP_0_ADDRESS + SPI_DATA_FLASH_0_SIZE + SPI_DATA_FLASH_1_SIZE)
     #define SPI_CHIP_3_ADDRESS   (SPI_CHIP_0_ADDRESS + SPI_DATA_FLASH_0_SIZE + SPI_DATA_FLASH_1_SIZE + SPI_DATA_FLASH_2_SIZE)
     unsigned char test_buffer1[SPI_FLASH_PAGE_LENGTH];
     unsigned char test_buffer2[SPI_FLASH_PAGE_LENGTH];

     #define TEST_DEVICE      (SPI_CHIP_0_ADDRESS + 52)
     //#define TEST_DEVICE SPI_CHIP_1_ADDRESS
     //#define TEST_DEVICE SPI_CHIP_2_ADDRESS
     //#define TEST_DEVICE SPI_CHIP_3_ADDRESS
     //#define TEST_DEVICE SPI_CHIP_4_ADDRESS

     if (!fnSPI_Flash_available()) {                                     // check that all expected devices are present:
         fnDebugMsg("Main SPI Flash not detected - quit\r\n");
         return;
     }
    #ifdef SPI_FLASH_MULTIPLE_CHIPS
     if (fnSPI_FlashExt_available(1) != 0) {
         fnDebugMsg("First SPI Flash extension detected\r\n");
     }
     if (fnSPI_FlashExt_available(2) != 0) {
         fnDebugMsg("Second SPI Flash extension detected\r\n");
     }
     if (fnSPI_FlashExt_available(3) != 0) {
         fnDebugMsg("Third SPI Flash extension detected\r\n");
     }
    #endif

     uMemset(test_buffer1, 0x55, SPI_FLASH_PAGE_LENGTH);
     uMemset(test_buffer2, 0xaa, SPI_FLASH_PAGE_LENGTH);
     fnWriteBytesFlash(TEST_DEVICE, test_buffer1, SPI_FLASH_PAGE_LENGTH);
     fnGetParsFile(TEST_DEVICE, test_buffer2, SPI_FLASH_PAGE_LENGTH);
     if (!(uMemcmp(test_buffer1, test_buffer2, SPI_FLASH_PAGE_LENGTH))) {
         fnDebugMsg("FLASH TEST OK\r\n");
         fnEraseFlashSector(TEST_DEVICE, (SPI_FLASH_PAGE_LENGTH));
         uMemset(test_buffer2, 0xff, SPI_FLASH_PAGE_LENGTH);
         fnGetParsFile(TEST_DEVICE, test_buffer1, SPI_FLASH_PAGE_LENGTH);
         if (!(uMemcmp(test_buffer1, test_buffer2, SPI_FLASH_PAGE_LENGTH))) {
             fnDebugMsg("ALSO DELETE OK\r\n");
         }
     }
     else {
         fnDebugMsg("FLASH TEST FAIL\r\n");
     }
}
#endif

#define _CAN_INIT_CODE
    #include "can_tests.h"                                               // CAN initialiation code and transmission routine
#undef _CAN_INIT_CODE

