//*****************************************************************************
//
// qs_iot.c - Quickstart application that connects to a cloud server.
//
// Copyright (c) 2013-2020 Texas Instruments Incorporated.  All rights reserved.
// Software License Agreement
// 
// Texas Instruments (TI) is supplying this software for use solely and
// exclusively on TI's microcontroller products. The software is owned by
// TI and/or its suppliers, and is protected under applicable copyright
// laws. You may not combine this software with "viral" open-source
// software in order to form a larger program.
// 
// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS.
// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT
// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY
// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
// DAMAGES, FOR ANY REASON WHATSOEVER.
// 
// This is part of revision 2.2.0.286 of the EK-TM4C1294XL Firmware Package.
//
//*****************************************************************************

#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "inc/hw_ints.h"
#include "inc/hw_adc.h"
#include "driverlib/adc.h"
#include "driverlib/gpio.h"
#include "driverlib/pin_map.h"
#include "driverlib/rom.h"
#include "driverlib/rom_map.h"
#include "driverlib/sysctl.h"
#include "driverlib/systick.h"
#include "driverlib/uart.h"
#include "driverlib/interrupt.h"
#include "driverlib/timer.h"
#include "utils/cmdline.h"
#include "utils/uartstdio.h"
#include "utils/ustdlib.h"
#include "utils/lwiplib.h"
#include "drivers/pinout.h"
#include "drivers/buttons.h"
#include "drivers/exosite_hal_lwip.h"
#include "drivers/eth_client_lwip.h"
#include "exosite.h"
#include "stats.h"
#include "qs_iot.h"
#include "requests.h"
#include "commands.h"
#include "exosite_meta.h"

//*****************************************************************************
//
//! \addtogroup example_list
//! <h1>Internet of Things Quickstart (qs_iot)</h1>
//!
//!
//! This application records various information about user activity on the
//! board, and periodically reports it to a cloud server managed by Exosite.
//! In order to use all of the features of this application, you will need to
//! have an account with Exosite, and make sure that the device you are using
//! is registered to your Exosite profile with its original MAC address from
//! the factory.
//!
//! If you do not yet have an Exosite account, you can create one at
//! http://ti.exosite.com.  The web interface there will help guide you through
//! the account creation process.  There is also information in the Quickstart
//! document that is shipped along with the EK-TM4C1294XL evaluation kit.
//!
//! This application uses a command-line based interface through a virtual COM
//! port on UART 0, with the settings 115,200-8-N-1.  This application also
//! requires a wired Ethernet connection with internet access to perform
//! cloud-connected activities.
//!
//! Once the application is running you should be able to see program output
//! over the virtual COM port, and interact with the command-line.  The command
//! line will allow you to see the information being sent to and from Exosite's
//! servers, change the state of LEDs, and play a game of tic-tac-toe.  If you
//! have internet connectivity issues, need to find your MAC address, or need
//! to re-activate your EK-TM4C1294XL board with Exosite, the command line
//! interface also has options to support these operations.  Type
//! 'help' at the command prompt to see a list of available commands.
//!
//! If your local internet connection requires the use of a proxy server, you
//! will need to enter a command over the virtual COM port terminal before the
//! device will be able to connect to Exosite.  When prompted by the
//! application, type 'setproxy help' for information on how to configure the
//! proxy.  Alternatively, you may uncomment the define statements below for
//! "CUSTOM_PROXY" settings, fill in the correct information for your local
//! http proxy server, and recompile this example.  This will permanently set
//! your proxy as the default connection point.
//!
//
//*****************************************************************************

//*****************************************************************************
//
// The error routine that is called if the driver library encounters an error.
//
//*****************************************************************************
#ifdef DEBUG
void
__error__(char *pcFilename, uint32_t ui32Line)
{
}
#endif

//*****************************************************************************
//
// Global macro definitions.
//
//*****************************************************************************
#define MAX_SYNC_RETRIES        10

// #define CUSTOM_PROXY
// #define PROXY_ADDRESS           "your.proxy.address"
// #define PROXY_PORT              80

//*****************************************************************************
//
// Exosite Address
//
//*****************************************************************************
#define STR_HOST "devmode-m2.exosite.io"

//*****************************************************************************
//
// Global variables that will be linked to Exosite.
//
//*****************************************************************************
uint32_t g_ui32SW1Presses = 0;
uint32_t g_ui32SW2Presses = 0;
uint32_t g_ui32InternalTempF = 0;
uint32_t g_ui32InternalTempC = 0;
uint32_t g_ui32TimerIntCount = 0;
uint32_t g_ui32SecondsOnTime = 0;
uint32_t g_ui32LEDD1 = 0;
uint32_t g_ui32LEDD2 = 0;
char g_pcLocation[50] = "";
char g_pcContactEmail[100] = "";
char g_pcAlert[140] = "";

//*****************************************************************************
//
// Global structures used to interface with Exosite.
//
//*****************************************************************************
tStat g_sSW1Presses =
    {"SW1-presses", &g_ui32SW1Presses, "usrsw1", INT, WRITE_ONLY};

tStat g_sSW2Presses =
    {"SW2-presses", &g_ui32SW2Presses, "usrsw2", INT, WRITE_ONLY};

tStat g_sInternalTempC =
    {"Temp(C)", &g_ui32InternalTempC, "temp", INT, WRITE_ONLY};

tStat g_sSecondsOnTime =
    {"Time since reset", &g_ui32SecondsOnTime, "ontime", INT, WRITE_ONLY};

tStat g_sLEDD1 =
    {"LED D1", &g_ui32LEDD1, "led", INT, READ_WRITE};

tStat g_sLEDD2 =
    {"LED D2", &g_ui32LEDD2, "ledd2", INT, READ_WRITE};


//*****************************************************************************
//
// Global array of pointers to all tStat structures to be synced with Exosite.
//
//*****************************************************************************
tStat *g_psDeviceStatistics[NUM_STATS]=
{
    &g_sSW1Presses,
    &g_sSW2Presses,
    &g_sInternalTempC,
    &g_sSecondsOnTime,
    &g_sLEDD1,
    NULL
};

uint32_t g_puicmdUpdate[NUM_STATS] = {0,0,0,0,0,0};

//*****************************************************************************
//
// Global variable to keep track of the system clock.
//
//*****************************************************************************
uint32_t g_ui32SysClock = 0;

//*****************************************************************************
//
// Global variable to keep track of the IP address.
//
//*****************************************************************************
uint32_t g_ui32IPAddr;

//*****************************************************************************
//
// Flags to keep track of application state.
//
//*****************************************************************************
bool g_bPrintingData = false;
bool g_bGameActive = false;
volatile bool g_bOnline = false;
bool g_bPidValid = false;

//*****************************************************************************
//
// Global variables to keep track of the error conditions.
//
//*****************************************************************************
enum
{
    ERR_IP_ADDR,
    ERR_CIK,
    ERR_UNKNOWN,
    ERR_NO_ERR
} g_ui32Err = ERR_NO_ERR;

uint32_t g_ui32LinkRetries = 0;

//*****************************************************************************
//
// Input buffer for the command line interpreter.
//
//*****************************************************************************
char g_cInput[APP_INPUT_BUF_SIZE];

//*****************************************************************************
//
// Given a list of statistics, prints each item to the UART.
//
//*****************************************************************************
void
PrintStats(tStat **psStats)
{
    uint32_t ui32Index;
    char pcStatValue[256];
    char *pcStatName;

    //
    // Loop over all statistics in the list.
    //
    for(ui32Index = 0; psStats[ui32Index] != NULL; ui32Index++)
    {
        if(psStats[ui32Index]->pcName)
        {
            //
            // For each statistic, print the name and current value to the UART.
            //
            pcStatName = psStats[ui32Index]->pcName;
            StatPrintValue(psStats[ui32Index], pcStatValue);

            UARTprintf("%25s= %s\n", pcStatName, pcStatValue);
        }
    }
}

//*****************************************************************************
//
// Prints the current MAC address to the UART.
//
//*****************************************************************************
void
PrintMac(void)
{
    uint8_t ui8Idx;
    uint8_t pui8MACAddr[6];

    //
    // Get the MAC address from the Ethernet Client layer.
    //
    EthClientMACAddrGet(pui8MACAddr);

    UARTprintf("Current MAC: ");

    //
    // Extract each pair of characters and print them to the UART.
    //
    for(ui8Idx = 0; ui8Idx < 6; ui8Idx++)
    {
        UARTprintf("%02x", pui8MACAddr[ui8Idx]);
    }

    UARTprintf("\n");
}

//*****************************************************************************
//
// This function prints a list of local statistics for this board.
//
//*****************************************************************************
void
PrintAllData(void)
{
    char cExositeCIK[CIK_LENGTH];

    if(UARTPeek('\r') != -1)
    {
        g_bPrintingData = false;

        //
        // Get a user command back.
        //
        UARTgets(g_cInput, APP_INPUT_BUF_SIZE);

        //
        // Print a prompt.
        //
        UARTprintf("\n> ");

        return;
    }

    UARTprintf("\033[2J\033[H");
    UARTprintf("Welcome to the Connected LaunchPad!!\n");
    UARTprintf("Internet of Things Demo\n");
    UARTprintf("Type 'help' for help.\n\n");

    //
    // Print out the MAC address for reference.
    //
    PrintMac();

    //
    // Check to see if we already have a CIK/Token, and print it to the UART
    //
    if(Exosite_GetCIK(cExositeCIK))
    {
        UARTprintf("Current CIK/Token: %s\n", cExositeCIK);
    }
    else
    {
        UARTprintf("No CIK/Token found. Connect to Exosite to obtain one.\n");
    }

    //
    // Check to see how many times (if any) we've failed to connect to the
    // server.
    //
    if((g_ui32LinkRetries == 0) && g_bOnline)
    {
        //
        // For zero failures, report a "Link OK".
        //
        UARTprintf("Link Status: OK\n");
    }
    else if((g_ui32LinkRetries < MAX_SYNC_RETRIES) && g_bOnline)
    {
        //
        // For the first few failures, report that we are trying to
        // re-establish a link.
        //
        UARTprintf("Link Status: Lost (Retries: %d)\n", g_ui32LinkRetries);
    }
    else
    {
        //
        // If we have exceeded the maximum number of retries, show status as
        // offline.
        //
        UARTprintf("Link Status: Offline");
    }

    //
    // Print some header text.
    //
    UARTprintf("\nCollected Statistics\n");
    UARTprintf("--------------------\n");

    PrintStats(g_psDeviceStatistics);

    UARTprintf("\nPress Enter to return to the command prompt...\n");

    UARTFlushTx(0);

    return;
}

//*****************************************************************************
//
// Prints a help message to the UART to help with troubleshooting Exosite
// connection issues.
//
//*****************************************************************************
void
PrintConnectionHelp(void)
{
    UARTprintf("Troubleshooting Exosite Connection:\n\n");

    UARTprintf("    + Make sure you are connected to the internet.\n\n");

    UARTprintf("    + Make sure you have created an Exosite profile.\n\n");

    UARTprintf("    + Make sure you have a \"Connected Launchpad\" device\n");
    UARTprintf("      created in your Exosite profile.\n\n");

    UARTprintf("    + Make sure your that your board's MAC address is\n");
    UARTprintf("      correctly registered with your exosite profile.\n\n");

    UARTprintf("    + If you have a CIK/Token, make sure it matches the CIK/Token\n");
    UARTprintf("      for this device in your online profile with Exosite.\n\n");

    UARTprintf("    + If you have a proxy, make sure to configure it using\n");
    UARTprintf("      this terminal. Type 'setproxy help' to get started.\n");
    UARTprintf("      Once the proxy is set, type 'activate' to obtain a\n");
    UARTprintf("      new CIK/Token, or 'connect' to connect to exosite using\n");
    UARTprintf("      an existing CIK/Token.\n\n");

    UARTprintf("    + Make sure your device is available for provisioning.\n");
    UARTprintf("      If you are not sure that provisioning is enabled,\n");
    UARTprintf("      check the Read Me First documentation or the online\n");
    UARTprintf("      exosite portal for more information.\n\n");
}

//*****************************************************************************
//
// Attempts to find a CIK/Token in the EEPROM. Reports the status of this 
// operation to the UART.
//
//*****************************************************************************
bool
GetEEPROMCIK(void)
{
    char pcExositeCIK[50];

    //
    // Try to read the CIK/Token from EEPROM, and alert the user based on what 
    // we find.
    //
    if(Exosite_GetCIK(pcExositeCIK))
    {
        //
        // If a CIK/Token is found, continue on to make sure that the CIK/Token
        // is valid.		
        //
        UARTprintf("CIK/Token found in EEPROM storage.\n\nCIK/Token: %s\n\n",
                   pcExositeCIK);
    }
    else
    {
        //
        // If a CIK/Token was not found, return immediately and indicate the 
		// failure.
        //
        UARTprintf("No CIK/Token found in EEPROM.\n");
        return 0;
    }

    UARTprintf("Connecting to Exosite...\n");

    //
    // If a CIK/Token was found, try to sync with Exosite.  This should tell 
    // us if the CIK/Token is valid or not.
    //
    if(SyncWithExosite(g_psDeviceStatistics))
    {
        //
        // If the sync worked, the CIK/Token is valid.  Alert the caller.
        //
        UARTprintf("Connected! Type 'stats' to see data for this board.");
        return 1;
    }
    else
    {
        //
        // If the sync failed, the CIK/Token is probably invalid, so pass the 
        // error back to the caller.
        //
        UARTprintf("Initial sync failed. CIK/Token may be invalid.\n");
        return 0;
    }
}

//*****************************************************************************
//
// Attempts to provision a new CIK/Token through a request to Exosite's 
// servers. This should be used when Exosite's CIK/Token does not match the 
// CIK/Token for this device, or when a CIK/Token is not found in EEPROM.
//
//*****************************************************************************
bool
ProvisionCIK(void)
{
    uint32_t ui32Idx;
    char pcExositeCIK[50];

    //
    // If we get here, no CIK/Token was found in EEPROM storage.  We may need 
    // to obtain a CIK/Token from the server.
    //
    UARTprintf("Connecting to exosite to obtain a new CIK/Token... \n");

    //
    // Try to activate with Exosite a few times.  If we succeed move on with 
    // the new CIK/Token. Otherwise, fail.
    //
    for(ui32Idx = 0; ui32Idx < 1; ui32Idx++)
    {
        if(Exosite_Activate())
        {
            //
            // If exosite gives us a CIK/Token, send feedback to the user.
            //
            UARTprintf("CIK/Token acquired!\n\n");

            if(Exosite_GetCIK(pcExositeCIK))
            {
                UARTprintf("CIK/Token: %s\n\n", pcExositeCIK);
                UARTprintf("Connected! ");
                UARTprintf("Type 'stats' to see data for this board.");
            }
            else
            {
                //
                // This shouldn't ever happen, but print an error message in
                // case it does.
                //
                UARTprintf("ERROR reading new CIK/Token from EEPROM.\n");
            }

            //
            // Return "true" indicating that we found a valid CIK/Token.
            //
            return true;
        }
        else
        {
            //
            // If the activation fails, wait at least one second before
            // retrying.
            //
            //MAP_SysCtlDelay(g_ui32SysClock/3);
            if(Exosite_StatusCode() == EXO_STATUS_CONFLICT)
            {
                //
                // This can occur if the MAC address for this board has already
                // been activated, and the device has not been re-enabled for a
                // new CIK/Token.
                //
                UARTprintf("\nExosite reported that this device is not\n");
                UARTprintf("available for provisioning. Check to make sure\n");
                UARTprintf("that you have the correct MAC address, and that\n");
                UARTprintf("this device is enabled for provisioning in your\n");
                UARTprintf("Exosite profile.\n\n");

                return false;
            }
        }
    }

    //
    // Exosite didn't respond, so let the user know.
    //
    UARTprintf("No CIK/Token could be obtained.\n\n");

    PrintConnectionHelp();

    //
    // Return "false", indicating that no CIK/Token was found.
    //
    return false;
}

//*****************************************************************************
//
// Attempts to provision a new CIK/Token through a request to Exosite's 
// servers. This should be used when Exosite's CIK/Token does not match the 
// CIK/Token for this device, or when a CIK/Token is not found in EEPROM.
//
//*****************************************************************************
bool
LocateValidCIK(void)
{
    //
    // Try to obtain a valid CIK/Token.
    //
    UARTprintf("Locating CIK/Token... ");

    //
    // Check the EEPROM for a valid CIK/Token first.  If none can be found
    // there, try to provision a CIK/Token from exosite.  If we can obtain a
    // CIK/Token, make sure to set the global state variable that indicates
    // that we can connect to exosite.
    //
    if(GetEEPROMCIK())
    {
        return true;
    }
    else if(ProvisionCIK())
    {
        return true;
    }
    else
    {
        //
        // If both cases above fail, return false, indicating that we did not
        // find a CIK/Token.
        //
        return false;
    }
}

//*****************************************************************************
//
// Takes a reading from the internal temperature sensor, and updates the
// corresponding global statistics.
//
//*****************************************************************************
void
UpdateInternalTemp(void)
{
    uint32_t pui32ADC0Value[1], ui32TempValueC, ui32TempValueF;

    //
    // Take a temperature reading with the ADC.
    //
    MAP_ADCProcessorTrigger(ADC0_BASE, 3);

    //
    // Wait for the ADC to finish taking the sample.
    //
    while(!MAP_ADCIntStatus(ADC0_BASE, 3, false))
    {
    }

    //
    // Clear the interrupt.
    //
    MAP_ADCIntClear(ADC0_BASE, 3);

    //
    // Read the analog voltage measurement.
    //
    MAP_ADCSequenceDataGet(ADC0_BASE, 3, pui32ADC0Value);

    //
    // Convert the measurement to degrees Celcius and Fahrenheit, and save to
    // the global state variables.
    //
    ui32TempValueC = ((1475 * 4096) - (2250 * pui32ADC0Value[0])) / 40960;
    g_ui32InternalTempC = ui32TempValueC;
    ui32TempValueF = ((ui32TempValueC * 9) + 160) / 5;
    g_ui32InternalTempF = ui32TempValueF;
}

//*****************************************************************************
//
// Polls the buttons, and updates global state accordingly.
//
//*****************************************************************************
void
UpdateButtons(void)
{
    uint8_t ui8Buttons, ui8ButtonsChanged;

    //
    // Check the current debounced state of the buttons.
    //
    ui8Buttons = ButtonsPoll(&ui8ButtonsChanged,0);

    //
    // If either button has been pressed, record that status to the
    // corresponding global variable.
    //
    if(BUTTON_PRESSED(USR_SW1, ui8Buttons, ui8ButtonsChanged))
    {
        g_ui32SW1Presses++;
    }
    else if(BUTTON_PRESSED(USR_SW2, ui8Buttons, ui8ButtonsChanged))
    {
        g_ui32SW2Presses++;
    }
}

//*****************************************************************************
//
// Turns LEDs on or off based on global state variables.
//
//*****************************************************************************
void
UpdateLEDs(void)
{
    //
    // If either LED's global flag is set, turn that LED on.  Otherwise, turn
    // them off.
    //
    if(g_ui32LEDD1)
    {
        MAP_GPIOPinWrite(GPIO_PORTN_BASE, GPIO_PIN_1, GPIO_PIN_1);
    }
    else
    {
        MAP_GPIOPinWrite(GPIO_PORTN_BASE, GPIO_PIN_1, 0);
    }

    if(g_ui32LEDD2)
    {
        MAP_GPIOPinWrite(GPIO_PORTN_BASE, GPIO_PIN_0, GPIO_PIN_0);
    }
    else
    {
        MAP_GPIOPinWrite(GPIO_PORTN_BASE, GPIO_PIN_0, 0);
    }
}

//*****************************************************************************
//
// Prompts the user for a command, and blocks while waiting for the user's
// input. This function will return after the execution of a single command.
//
//*****************************************************************************
void
CheckForUserCommands(void)
{
    int iStatus;

    //
    // Peek to see if a full command is ready for processing.
    //
    if(UARTPeek('\r') == -1)
    {
        //
        // If not, return so other functions get a chance to run.
        //
        return;
    }

    //
    // If we do have commands, process them immediately in the order they were
    // received.
    //
    while(UARTPeek('\r') != -1)
    {
        //
        // Get a user command back.
        //
        UARTgets(g_cInput, APP_INPUT_BUF_SIZE);

        //
        // Process the received command.
        //
        iStatus = CmdLineProcess(g_cInput);

        //
        // Handle the case of bad command.
        //
        if(iStatus == CMDLINE_BAD_CMD)
        {
            UARTprintf("Bad command!\n");
        }

        //
        // Handle the case of too many arguments.
        //
        else if(iStatus == CMDLINE_TOO_MANY_ARGS)
        {
            UARTprintf("Too many arguments for command processor!\n");
        }
    }

    //
    // Print a prompt.
    //
    UARTprintf("\n> ");

}


//*****************************************************************************
//
// Interrupt handler for Timer0A.
//
// This function will be called periodically on the expiration of Timer0A It
// performs periodic tasks, such as looking for input on the physical buttons,
// and reporting usage statistics to the cloud.
//
//*****************************************************************************
void
Timer0IntHandler(void)
{
    //
    // Clear the timer interrupt.
    //
    MAP_TimerIntClear(TIMER0_BASE, TIMER_TIMA_TIMEOUT);

    //
    // Keep track of the number of times this interrupt handler has been
    // called.
    //
    g_ui32TimerIntCount++;

    //
    // Poll the GPIOs for the buttons to check for press events.  Update global
    // variables as necessary.
    //
    UpdateButtons();

    if((!g_bPrintingData) && (!g_bGameActive))
    {
        CheckForUserCommands();
    }

    //
    // Once per second, perform the following operations.
    //
    if(!(g_ui32TimerIntCount % APP_TICKS_PER_SEC))
    {
        //
        // Keep track of the total seconds of on-time.
        //
        g_ui32SecondsOnTime++;

        //
        // Take a reading from the internal temperature sensor.
        //
        UpdateInternalTemp();

        //
        // Set the LEDs to the correct state.
        //
        UpdateLEDs();

        //
        // Check to see if we have any on-going actions that require the UART.
        //
        if(g_bPrintingData)
        {
            //
            // If the user has requested a data print-out, perform that here.
            //
            PrintAllData();
        }

    }

    //
    // Make sure the running tally of the number of interrupts doesn't
    // overflow.
    //
    if(g_ui32TimerIntCount == (20 * APP_TICKS_PER_SEC))
    {
        //
        // Reset the interrupt count to zero.
        //
        g_ui32TimerIntCount = 0;

    }

}

//*****************************************************************************
//
// Configures Timer 0 as a general purpose, periodic timer for handling button
// presses.
//
//*****************************************************************************
void
ConfigureTimer0(void)
{
    //
    // Enable the peripherals used by this example.
    //
    MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0);

    //
    // Configure the two 32-bit periodic timers.
    //
    MAP_TimerConfigure(TIMER0_BASE, TIMER_CFG_PERIODIC);
    MAP_TimerLoadSet(TIMER0_BASE, TIMER_A, g_ui32SysClock / APP_TICKS_PER_SEC);

    //
    // Lower the priority of this interrupt.
    //
    MAP_IntPriorityGroupingSet(4);
    MAP_IntPrioritySet(INT_TIMER0A, 0xE0);

    //
    // Setup the interrupts for the timer timeouts.
    //
    MAP_IntEnable(INT_TIMER0A);
    MAP_TimerIntEnable(TIMER0_BASE, TIMER_TIMA_TIMEOUT);
}

//*****************************************************************************
//
// Enables and configures ADC0 to read the internal temperature sensor into
// sample sequencer 3.
//
//*****************************************************************************
void
ConfigureADC0(void)
{
    //
    // Enable clock to ADC0.
    //
    MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0);

    //
    // Configure ADC0 Sample Sequencer 3 for processor trigger operation.
    //
    MAP_ADCSequenceConfigure(ADC0_BASE, 3, ADC_TRIGGER_PROCESSOR, 0);

    //
    // Increase the hold time of this sample sequencer to account for the
    // temperature sensor erratum (ADC#09).
    //
    HWREG(ADC0_BASE + ADC_O_SSTSH3) = 0x4;

    //
    // Configure ADC0 sequencer 3 for a single sample of the temperature
    // sensor.
    //
    MAP_ADCSequenceStepConfigure(ADC0_BASE, 3, 0, ADC_CTL_TS | ADC_CTL_IE |
                                 ADC_CTL_END);

    //
    // Enable the sequencer.
    //
    MAP_ADCSequenceEnable(ADC0_BASE, 3);

    //
    // Clear the interrupt bit for sequencer 3 to make sure it is not set
    // before the first sample is taken.
    //
    MAP_ADCIntClear(ADC0_BASE, 3);
}


//*****************************************************************************
//
// Main function.
//
//*****************************************************************************
int
main(void)
{
    uint32_t ui32Timeout, ui32CIKRetries, ui32IPAddr;
    char pcExositePID[20];

    //
    // Run from the PLL at 120 MHz.
    // Note: SYSCTL_CFG_VCO_240 is a new setting provided in TivaWare 2.2.x and
    // later to better reflect the actual VCO speed due to SYSCTL#22.
    //
    g_ui32SysClock = MAP_SysCtlClockFreqSet((SYSCTL_XTAL_25MHZ |
                                             SYSCTL_OSC_MAIN |
                                             SYSCTL_USE_PLL |
                                             SYSCTL_CFG_VCO_240), 120000000);


    //
    // Set the pinout for the board, including required pins for Ethernet
    // operation.
    //
    PinoutSet(1,0);

    //
    // Enable the UART, clear the terminal, and print a brief message.
    //
    UARTStdioConfig(0, 115200, g_ui32SysClock);

    //
    // Configure necessary peripherals.
    //
    ConfigureTimer0();
    ConfigureADC0();

    //
    // Take an initial reading of the internal temperature
    //
    UpdateInternalTemp();

    //
    // Initialize the buttons
    //
    ButtonsInit();

    //
    // If a proxy has been pre-defined, enable it here.
    //
#ifdef CUSTOM_PROXY
    usprintf(g_pcProxyAddress, PROXY_ADDRESS);
    g_ui16ProxyPort = PROXY_PORT;
    g_bUseProxy = true;
#endif

    //
    // Clear the screen, and print a welcome message.
    //
    UARTprintf("\033[2J\033[H");
    UARTprintf("Welcome to the Connected LaunchPad!!\n");
    UARTprintf("Internet of Things Demo\n");
    UARTprintf("Type \'help\' for help.\n\n");

    //
    // Initialize the meta data structure.
    //
    exosite_meta_init(0);

    //
    // Start with the assumption that we are not online yet.
    //
    g_bOnline = false;

    //
    // Assume that the IP address is not acquired.
    //
    g_ui32IPAddr = 0;
    ui32IPAddr = 0;
    g_ui32Err = ERR_IP_ADDR;

    //
    // Clear the error variables.
    //
    ui32CIKRetries = 0;

    //
    // Enable interrupts and start the timer. This will enable the UART console
    // input, and also enable updates to the various cloud-enabled variables.
    //
    MAP_IntMasterEnable();
    MAP_TimerEnable(TIMER0_BASE, TIMER_A);

    //
    // Check if a valid PID is stored in the EEprom
    //
    if (Exosite_GetPID(pcExositePID)) {
    	g_bPidValid = true;
    	UARTprintf("PID found in EEPROM storage.\n\nCurrent PID: %s\n",
    			pcExositePID);

    	//
    	// <PRODUCT_ID>.devmode-m2.exosite.io forms the full URL to
    	// synchronize with the Exosite cloud
        //
    	memcpy(g_pcExositeAddr ,pcExositePID ,strlen(pcExositePID));
    	strcat(g_pcExositeAddr, ".");
    	strcat(g_pcExositeAddr, STR_HOST);

    } else {
    	g_bPidValid = false;
        
    	//
    	// If a PID was not found, return immediately and indicate the failure.
    	//
    	UARTprintf("No PID or invalid PID found in EEPROM.\n");
    	UARTprintf("Obtain PID from Exosite and type:\n\n");
    	UARTprintf(">pid <PID> on the command line\n\n");
        
        //
        // Print a prompt
        //
        UARTprintf("\n> ");

    	//
    	// If there is no valid PID then wait here until the user enters the PID.
    	// User will enter PID by typing on the command line:
    	// >pid <PRODCUT_PID>
    	// Once the PID is entered, it will be programmed to the EEprom and the g_bPidValid
    	// will be set to true
        //
    	while(!g_bPidValid);
    }

    //
    // Initialize Exosite layer to allow Exosite-based user commands later.
    //
    Exosite_Init(IF_ENET, 0);

    //
    // Print the MAC address, which users will need to register with Exosite.
    //
    PrintMac();

    //
    // Notify the user that we are obtaining an IP address.
    //
    UARTprintf("\nObtaining IP... \n");

    //
    // Loop a few times to make sure that DHCP has time to find an IP.
    //
    for(ui32Timeout = 10; ui32Timeout > 0; ui32Timeout--)
    {
        //
        // Check to see if we have an IP yet.
        //
        if((lwIPLocalIPAddrGet() != 0xffffffff) &&
           (lwIPLocalIPAddrGet() != 0x00000000))
        {
            //
            // Report that we found an IP address.
            //
            UARTprintf("IP Address Acquired: ");
            ui32IPAddr = lwIPLocalIPAddrGet();
            UARTprintf("%d.",ui32IPAddr & 0xFF );
            UARTprintf("%d.",ui32IPAddr >> 8 & 0xFF );
            UARTprintf("%d.",ui32IPAddr >> 16 & 0xFF );
            UARTprintf("%d\n",ui32IPAddr >> 24 & 0xFF );

            //
            // Update the global IP Address variable.  Also update the global
            // error variable.
            //
            g_ui32IPAddr = ui32IPAddr;
            g_ui32Err = ERR_NO_ERR;

            //
            // If we can find and validate a CIK/Token with Exosite, set the 
            // flag to indicate have a valid connection to the cloud.
            //
            g_bOnline = LocateValidCIK();

            //
            // Print a prompt
            //
            UARTprintf("\n> ");

            break;
        }
        else if(ui32Timeout == 0)
        {
            //
            // Alert the user if it takes a long time to find an IP address.
            // An IP address can still be found later, so this is not an
            // indication of failure.
            //
            UARTprintf("No IP address found, continuing \n"
                       "to search in the background\n");
        }

        //
        // Delay a second to allow DHCP to find us an IP address.
        //
        MAP_SysCtlDelay(g_ui32SysClock / 3);
    }

    //
    // If we don't have a valid exosite connection, let the user know that the
    // device is "offline" and not performing any data synchronization with the
    // cloud.
    //
    if(!g_bOnline)
    {
        UARTprintf("Continuing in offline mode.\n\n");
    }

    //
    // Main application loop.
    //
    while(1)
    {
        //
        // Only run the following loop if we have a valid connection to
        // Exosite.
        //
        if(g_bOnline)
        {
            //
            // Attempt to sync data with Exosite
            //
            if(SyncWithExosite(g_psDeviceStatistics))
            {
                //
                // If the sync is successful, reset the "retries" count to
                // zero.
                //
                g_ui32LinkRetries = 0;
            }
            else if(Exosite_StatusCode() == EXO_STATUS_NOAUTH)
            {
                //
                // Failed for having an old CIK/Token.  Flush the UART output, and
                // stop any data-printing operation.
                //
                g_bPrintingData = 0;
                UARTFlushTx(0);

                //
                // Alert the user of the expired CIK/Token.
                //
                UARTprintf("\nCIK/Token no longer valid.\n");
                UARTprintf("The application will automatically try to ");
                UARTprintf("acquire a new CIK/Token.\n");
                UARTprintf("\n> ");

                //
                // We did connect to Exosite, so the link is still valid, but
                // data syncing will not work.  Do not increment the number of
                // link retries, but do consider the board "offline" for data
                // syncing.
                //
                g_bOnline = false;
                g_ui32Err = ERR_CIK;
                ui32CIKRetries = 0;

                //
                // Put the LEDs in error indicating mode.  Turn LED D1 off.
                // LED D2 is used as error indicator in this case.
                //
                g_ui32LEDD1 = 0;
            }
            else
            {
                //
                // Sync failed for some other reason.  Since we don't know the
                // reason for this failure, try to sync with Exosite server
                // again.  Increment the failure counter to aid in debug.
                //
                g_ui32LinkRetries++;
            }

            //
            // Check if IP address has changed.
            //
            ui32IPAddr = lwIPLocalIPAddrGet();
            if(g_ui32IPAddr != ui32IPAddr)
            {
                //
                // Yes, then let the app know that link is lost and we are
                // offline.
                //
                g_bOnline = false;
                g_ui32Err = ERR_IP_ADDR;

                //
                // Alert the user that IP Address is lost.
                //
                g_bPrintingData = 0;
                UARTFlushTx(0);
                UARTprintf("\nIP Address lost/changed.\n");

                //
                // Put the LEDs in error indicating mode.  Turn LED D2 off.
                // LED D1 is used as the error indicator in this case.
                //
                g_ui32LEDD2 = 0;
            }
        }
        else
        {
            //
            // If we are here it means that connection to Exosite is lost.
            // Handle the different cases that could have caused this.
            //
            switch(g_ui32Err)
            {
                case ERR_IP_ADDR:
                {
                    //
                    // Check if IP address has been acquired.
                    //
                    ui32IPAddr = lwIPLocalIPAddrGet();
                    if((ui32IPAddr == 0xffffffff) || (ui32IPAddr == 0x00000000))
                    {
                        //
                        // No - Delay a second to allow DHCP to find an IP
                        // address.
                        //
                        MAP_SysCtlDelay(g_ui32SysClock / 3);

                        //
                        // Change the state of LED D1 so that it blinks,
                        // indicating that IP Address is lost.
                        //
                        g_ui32LEDD1 ^= 1;
                        break;
                    }

                    //
                    // Report the IP address to the user.
                    //
                    UARTprintf("IP Address Acquired: ");
                    UARTprintf("%d.",ui32IPAddr & 0xFF );
                    UARTprintf("%d.",ui32IPAddr >> 8 & 0xFF );
                    UARTprintf("%d.",ui32IPAddr >> 16 & 0xFF );
                    UARTprintf("%d\n",ui32IPAddr >> 24 & 0xFF );

                    //
                    // Update the global IP Address variable.
                    //
                    g_ui32IPAddr = ui32IPAddr;

                    //
                    // Check if the CIK/Token is valid.  This also helps in 
                    // verifying connection with Exosite.
                    //
                    g_bOnline = LocateValidCIK();
                    if(!g_bOnline)
                    {
                        //
                        // Failed to connect to Exosite.  Assume that CIK/Token
                        // is not valid.
                        //
                        g_ui32Err = ERR_CIK;
                        ui32CIKRetries = 0;
                        break;
                    }

                    //
                    // Print a prompt.
                    //
                    UARTprintf("\n> ");

                    //
                    // Reset the necessary variables to inform the application
                    // that the error is cleared.
                    //
                    g_ui32Err = ERR_NO_ERR;
                    g_ui32LinkRetries = 0;

                    break;
                }

                case ERR_CIK:
                {
                    //
                    // Attempt to acquire a new CIK/Token from Exosite.
                    //
                    if(ProvisionCIK())
                    {
                        //
                        // Success - notify the application that we are
                        // connected to Exosite.  Also reset the necessary
                        // error variables.
                        //
                        g_bOnline = true;
                        g_ui32Err = ERR_NO_ERR;
                        ui32CIKRetries = 0;

                        break;
                    }

                    //
                    // Since we failed to acquire a new CIK/Token, try a few 
                    // times before giving up.
                    //
                    ui32CIKRetries++;
                    if(ui32CIKRetries > 5)
                    {
                        //
                        // Report to the user.
                        //
                        UARTprintf("\nCIK/Token no longer valid.  Tried %d times ",
                                   ui32CIKRetries);
                        UARTprintf("to acquire new CIK/Token but failed.\n");
                        UARTprintf("Log in to exosite to check on the ");
                        UARTprintf("status of your device.\n");

                        //
                        // Failed to acquire new CIK/Token after trying a few 
                        // times. So stop trying to acquire new CIK/Token.
                        //
                        g_ui32Err = ERR_UNKNOWN;
                        ui32CIKRetries = 0;
                    }

                    //
                    // Change the state of LED D2 so that it blinks, indicating
                    // that CIK/Token is no longer valid.
                    //
                    g_ui32LEDD2 ^= 1;
                    break;
                }

                case ERR_UNKNOWN:
                {
                    //
                    // Report to the user.
                    //
                    UARTprintf("\nFailed to connect to Exosite due to ");
                    UARTprintf("unknown reason.\n");

                    //
                    // Display IP address.
                    //
                    UARTprintf("IP Address: ");
                    ui32IPAddr = lwIPLocalIPAddrGet();
                    UARTprintf("%d.",ui32IPAddr & 0xFF );
                    UARTprintf("%d.",ui32IPAddr >> 8 & 0xFF );
                    UARTprintf("%d.",ui32IPAddr >> 16 & 0xFF );
                    UARTprintf("%d\n",ui32IPAddr >> 24 & 0xFF );

                    UARTprintf("Trying to sync with Exosite Server again.\n");
                    UARTprintf("\n> ");

                    //
                    // Set the appropriate flags to retry syncing with Exosite
                    // server.
                    //
                    g_bOnline = true;
                    g_ui32Err = ERR_NO_ERR;

                    break;
                }

                case ERR_NO_ERR:
                default:
                {
                    break;
                }
            }
        }
    }
}

