MTDOT-EVB link check code for site survey

Dependencies:   DOGS102 GpsParser MTS-Serial NCP5623B libmDot mbed-rtos mbed

main.cpp

Committer:
mfiore
Date:
2015-10-27
Revision:
2:b197488a50dc
Parent:
1:4e3ee9c860e3

File content as of revision 2:b197488a50dc:

/**
 * @file    main.cpp
 * @brief   Main application for mDot-EVB Automated Link Check demo
 * @author  Tim Barr  MultiTech Systems Inc.
 * @version 1.02
 * @see
 *
 * Copyright (c) 2015
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * 1.00 TAB 10/22/15  Copied from MDOT-EVB-LinkCheck version 1.02.
 *					Added mutex around certain mDot radio commands.
 *					Moved ping check code to callable function. Added sweep
 *					mode, re-wrote join code, and modified button operation
 *					Fixed error in Downlink QOS SNR display.
 */

#include "mbed.h"
#include "NCP5623B.h"
#include "DOGS102.h"
#include "font_6x8.h"
#include "MultiTech_Logo.h"
#include "mDot.h"
#include "rtos.h"
#include "GPSPARSER.h"
#include "MTSSerial.h"
#include <string>
#include <vector>
#include <ctime>

enum LED1_COLOR {
    RED = 0,
    GREEN = 1
};

/*
 * union for converting from 32-bit to 4 8-bit values
 */
union convert32 {
    int32_t f_s;		// convert from signed 32 bit int
    uint32_t f_u;		// convert from unsigned 32 bit int
    uint8_t t_u[4];		// convert to 8 bit unsigned array
};

/*
 * union for converting from 16- bit to 2 8-bit values
 */
union convert16 {
    int16_t f_s;		// convert from signed 16 bit int
    uint16_t f_u;		// convert from unsigned 16 bit int
    uint8_t t_u[2];		// convert to 8 bit unsigned array
};

//DigitalIn mDot02(PA_2);          				//  GPIO/UART_TX
//DigitalOut mDot03(PA_3);         				//  GPIO/UART_RX
//DigitalIn mDot04(PA_6);          				//  GPIO/SPI_MISO
//DigitalIn mDot06(PA_8);          				//  GPIO/I2C_SCL
//DigitalIn mDot07(PC_9);         				//  GPIO/I2C_SDA

InterruptIn mDot08(PA_12);           			//  GPIO/USB       PB S1 on EVB
InterruptIn mDot09(PA_11);           			//  GPIO/USB       PB S2 on EVB

//DigitalIn mDot11(PA_7);          				//  GPIO/SPI_MOSI

InterruptIn mDot12(PA_0);          				//  GPIO/UART_CTS  PRESSURE_INT2 on EVB
DigitalOut mDot13(PC_13,1);        				//  GPIO           LCD_C/D
InterruptIn mDot15(PC_1);          				//  GPIO           LIGHT_PROX_INT on EVB
InterruptIn mDot16(PA_1);          				//  GPIO/UART_RTS  ACCEL_INT2 on EVB
DigitalOut mDot17(PA_4,1);         				//  GPIO/SPI_NCS   LCD_CS on EVB

//DigitalIn mDot18(PA_5);          				//  GPIO/SPI_SCK

//DigitalInOut mDot19(PB_0,PIN_INPUT,PullNone,0); // GPIO         PushPull LED Low=Red High=Green set MODE=INPUT to turn off
AnalogIn mDot20(PB_1);             				//  GPIO          Current Sense Analog in on EVB

Serial debugUART(PA_9, PA_10);				// mDot debug UART

MTSSerial mDotUART(PA_2,PA_3);					// mDot external UART mDot02 and mDot03

I2C mDoti2c(PC_9,PA_8);							// mDot External I2C mDot6 and mDot7

SPI mDotspi(PA_7,PA_6,PA_5);					// mDot external SPI mDot11, mDot4, and mDot18

/* **** replace these values with the proper public or private network settings ****
 * config_network_nameand config_network_pass are for private networks.
 */
static std::string config_network_name = "TAB-CubeNet";
static std::string config_network_pass = "1nt3gral";
//static std::string config_network_name = "Arclight";
//static std::string config_network_pass = "default1";
static uint8_t config_frequency_sub_band = 5;

//static std::string config_network_name = "GregCDT8";
//static std::string config_network_pass = "myAEP8chars";
//static uint8_t config_frequency_sub_band = 1;

/*  config_app_id and config_app_key are for public networks.
static uint8_t app_id[8] = {0x00,0x01,0x02,0x03,0x0A,0x0B,0x0C,0x0D};
std::vector<uint8_t> config_app_id;
static uint8_t app_key[16] = {0x00,0x01,0x02,0x03,0x0A,0x0B,0x0C,0x0D};
std::vector<uint8_t> config_app_key;
*/

uint8_t result;
uint8_t data;
uint8_t sf_val = mDot::SF_7;
uint8_t pwr_val = 11; // dBm
uint8_t swp_pwr;
uint8_t swp_sf;

const uint8_t sweep_table [2][4] = {
    { 11, 14, 18, 20},
    {mDot::SF_7, mDot::SF_8, mDot::SF_9, mDot::SF_10}
};

// max size of text string for 6x8 font. Set to 12 if using 8x8 font
char txtstr[17];

int32_t mdot_ret;
int32_t join_delay;

osThreadId mainThreadID;

// flags for push button debounce code
bool pb1_low = false;
bool pb2_low = false;
bool toggle_text = false;
bool main_single = false;
bool main_sweep = false;

NCP5623B* evbBackLight;
DOGS102* evbLCD;

mDot* mdot_radio;
Mutex mdot_mutex;

GPSPARSER* mdot_gps;

convert32 convertL;
convert16 convertS;

void pb1ISR(void);
void pb2ISR(void);
void pb1_debounce(void const *args);
void pb2_debounce(void const *args);

void log_error(mDot* dot, const char* msg, int32_t retval);

uint8_t ping_check(uint8_t set_pwr, uint8_t set_sf, uint8_t pings_per_check = 1);

int main()
{
    std::vector<uint8_t> mdot_EUI;
    uint16_t i = 0;

//  Number of ping attempts per Radio setting set here
    uint8_t number_of_pings = 5;

    debugUART.baud(115200);

    printf ("Start program \r\n");

    Thread thread_1(pb1_debounce);
    Thread thread_2(pb2_debounce);

    printf("Thread init done\r\n");

    mainThreadID = osThreadGetId();

    printf("Device init start\r\n");

    evbBackLight = new NCP5623B(mDoti2c);				// setup backlight and LED 2 driver chip
    evbLCD = new DOGS102(mDotspi, mDot17, mDot13);		// setup LCD

    printf ("Devices init done\r\n");
    /*
     *  Setup SW1 as Data rate and power out select
     */
    mDot08.disable_irq();
    mDot08.fall(&pb1ISR);

    /*
     *  need to call this function after rise or fall because rise/fall sets
     *  mode to PullNone
     */
    mDot08.mode(PullUp);

    mDot08.enable_irq();

    /*
     *  Setup SW2 as send PING
     */
    mDot09.disable_irq();
    mDot09.fall(&pb2ISR);

    /*
     *  need to call this function after rise or fall because rise/fall sets
     *  mode to PullNone
     */
    mDot09.mode(PullUp);

    mDot09.enable_irq();

    /*
    * Setting other InterruptIn pins with Pull Ups
    */
    mDot12.mode(PullUp);
    mDot15.mode(PullUp);
    mDot16.mode(PullUp);

    printf("Switch IRQs set\r\n");

    printf("font table address %p\r\n",&font_6x8);
    printf("bitmap address %p\r\n",&MultiTech_Logo);

// Setup and display logo on LCD
    evbLCD->startUpdate();

    evbLCD->writeBitmap(0,0,MultiTech_Logo);

    sprintf(txtstr,"MTDOT-EVB");
    evbLCD->writeText(24,3,font_6x8,txtstr,strlen(txtstr));
    sprintf(txtstr,"Link Check Demo");
    evbLCD->writeText(6,4,font_6x8,txtstr,strlen(txtstr));

    evbLCD->endUpdate();

    printf("\r\n setup mdot\r\n");

    // get a mDot handle
    mdot_radio = mDot::getInstance();

    if (mdot_radio) {
        // reset to default config so we know what state we're in
        mdot_mutex.lock(); 	// lock mdot before setting configuration
        mdot_radio->resetConfig();

        // Setting up LED1 as activity LED
        mdot_radio->setActivityLedPin(PB_0);
        mdot_radio->setActivityLedEnable(true);

        // Read node ID
        mdot_EUI = mdot_radio->getDeviceId();
        printf("mDot EUI = ");

        for (i=0; i<mdot_EUI.size(); i++) {
            printf("%02x ", mdot_EUI[i]);
        }
        printf("\r\n");

// Setting up the mDot with network information.

        /*
         * This call sets up private or public mode on the MTDOT. Set the function to true if
         * connecting to a public network
         */
        printf("setting Private Network Mode\r\n");
        if ((mdot_ret = mdot_radio->setPublicNetwork(false)) != mDot::MDOT_OK) {
            log_error(mdot_radio, "failed to set Public Network Mode", mdot_ret);
        }

        /*
         * Frequency sub-band is valid for NAM only and for Private networks should be set to a value
         * between 1-8 that matches the the LoRa gateway setting. Public networks use sub-band 0 only.
         * This function can be commented out for EU networks
         */
        printf("setting frequency sub band\r\n");
        if ((mdot_ret = mdot_radio->setFrequencySubBand(config_frequency_sub_band)) != mDot::MDOT_OK) {
            log_error(mdot_radio, "failed to set frequency sub band", mdot_ret);
        }

        /*
         * Setting TX power for radio. Max allowed is +14dBm for EU and +20 dBm for NAM. Default is +11 dBm
         */
        printf("setting TX Power Level to %2d dBm\r\n", pwr_val);
        if ((mdot_ret = mdot_radio->setTxPower(pwr_val)) != mDot::MDOT_OK) {
            log_error(mdot_radio, "failed to set TX power level", mdot_ret);
        }

        /*
         * Setting TX data rate for radio. Max allowed is SF_12 for EU and SF10 dBm for NAM. Default is SF_10
         */
        printf("setting TX data rate to SF_7\r\n");
        if ((mdot_ret = mdot_radio->setTxDataRate(sf_val)) != mDot::MDOT_OK) {
            log_error(mdot_radio, "failed to set TX data rate", mdot_ret);
        }

        sprintf(txtstr,"DR=%2d Pwr=%2d",(12 - sf_val),pwr_val);
        evbLCD->writeText(0,7,font_6x8,txtstr,strlen(txtstr));
        printf("%s \r\n",txtstr);

        /*
         * Setting packet ACK to 1 try.
         */
        printf("setting Packet retry to 1\r\n");
        if ((mdot_ret = mdot_radio->setAck(1)) != mDot::MDOT_OK) {
            log_error(mdot_radio, "failed to set packet retry\r\n", mdot_ret);
        }

        /*
         * setNetworkName is used for private networks.
         * Use setNetworkID(AppID) for public networks
         */

// 		config_app_id.assign(app_id,app_id+7);

        printf("setting network name\r\n");
        if ((mdot_ret = mdot_radio->setNetworkName(config_network_name)) != mDot::MDOT_OK) {
//      if ((mdot_ret = mdot_radio->setNetworkID(config_app_id)) != mDot::MDOT_OK) {
            log_error(mdot_radio, "failed to set network name", mdot_ret);
        }

        /*
         * setNetworkPassphrase is used for private networks
         * Use setNetworkKey for public networks
         */

// 		config_app_key.assign(app_key,app_key+15);

        printf("setting network password\r\n");
        if ((mdot_ret = mdot_radio->setNetworkPassphrase(config_network_pass)) != mDot::MDOT_OK) {
//      if ((mdot_ret = mdot_radio->setNetworkKey(config_app_key)) != mDot::MDOT_OK) {
            log_error(mdot_radio, "failed to set network password", mdot_ret);
        }

        mdot_mutex.unlock();		// unlock mdot mutex before join attempt so SW1 can work

        // attempt to join the network
        printf("joining network\r\n");
        do {
            mdot_mutex.lock();		// lock mdot mutex before join attempt
            mdot_ret = mdot_radio->joinNetwork();
            mdot_mutex.unlock();		// unlock mdot mutex after join attempt so SW1 can work

            if (mdot_ret != mDot::MDOT_OK) {
                log_error(mdot_radio,"failed to join network:", mdot_ret);

                if (toggle_text)
                    sprintf(txtstr," > Join Failed <");
                else
                    sprintf(txtstr," < Join Failed >");

                evbLCD->writeText(0,5,font_6x8,txtstr,strlen(txtstr));

                if (mdot_radio->getFrequencyBand() == mDot::FB_868) {
                    join_delay = mdot_radio->getNextTxMs();
                } else {
                    join_delay = 10;
                }
                printf("delay = %lu\r\n",join_delay);
                osDelay(join_delay + 1);
                toggle_text = !toggle_text;
            }

            /*
             * Setting TX power and Data Rate for radio just in case user requested by SW2
             */
            mdot_mutex.lock();		// lock mdot mutex before setting change
            mdot_radio->setTxPower(pwr_val);
            mdot_radio->setTxDataRate(sf_val);
            mdot_mutex.unlock();		// unlock mdot mutex after settings change so SW1 can work

        } while (mdot_ret != mDot::MDOT_OK);

        sprintf(txtstr,"*Network Joined*");
        evbLCD->writeText(0,5,font_6x8,txtstr,strlen(txtstr));

    } else {
        sprintf(txtstr,"Radio Init Failed!");
        evbLCD->writeText(0,5,font_6x8,txtstr,strlen(txtstr));
        printf("%s\r\n",txtstr);
        exit(1);
    }

    mdot_gps = new GPSPARSER(&mDotUART);

    if (!mdot_gps->gpsDetected()) {
        sprintf(txtstr,"*No GPS Detected*");
        evbLCD->writeText(0,6,font_6x8,txtstr,strlen(txtstr));
        printf ("%s\r\n", txtstr);
    } else {
        main_single = main_sweep = false;
        do {
            osSignalWait(0x10, 2000);
            if (mdot_gps->getLockStatus()) {
                sprintf(txtstr,"!!GPS locked!!");
                evbLCD->writeText(0,6,font_6x8,txtstr,strlen(txtstr));
                printf("%s \r\n",txtstr);
            } else {
                if (toggle_text)
                    sprintf(txtstr," > no GPS lock <");
                else
                    sprintf(txtstr," < no GPS lock >");

                evbLCD->writeText(0,6,font_6x8,txtstr,strlen(txtstr));
                printf("%s \r\n",txtstr);
                toggle_text = !toggle_text;
            }
        } while ( !(mdot_gps->getLockStatus()) && (!main_single && !main_sweep));
    }
    osDelay(200);
    evbBackLight->setPWM(NCP5623B::LED_3,16); // enable LED2 on EVB and set to 50% PWM

    // sets LED2 to 50% max current
    evbBackLight->setLEDCurrent(16);

    osDelay (500);			// allows other threads to process
    printf("shutdown LED:\r\n");
    evbBackLight->shutdown();
    osDelay(500);
    /*
     * Main data acquisition loop
     */
    i = 0;

    do {

        // Main program waits until SW2 is pressed.
        main_single = main_sweep = false;

        sprintf(txtstr,"Ready for Trigger");
        evbLCD->writeText(0,7,font_6x8,txtstr,strlen(txtstr));
        printf ("%s\r\n", txtstr);

        osSignalWait(0x10, osWaitForever);

        if (main_single) {
            evbLCD->clearBuffer();
            sprintf(txtstr,"**Single Ping**");
            evbLCD->writeText(0,4,font_6x8,txtstr,strlen(txtstr));

            result = ping_check(pwr_val, sf_val);

            if (result != 0)
                printf("Ping check completed\r\n");
            else
                printf("Ping check failed \r\n");
        } else if (main_sweep) {

            evbLCD->clearBuffer();
            sprintf(txtstr,"**Ping Sweep**");
            evbLCD->writeText(0,4,font_6x8,txtstr,strlen(txtstr));

            printf("start sweep and shutdown LED:\r\n");
            evbBackLight->shutdown();

            for (swp_pwr = 0; swp_pwr < 4; swp_pwr++) {
                for (swp_sf = 0; swp_sf < 4; swp_sf++) {
                    result = ping_check(sweep_table[0][swp_pwr], sweep_table[1][swp_sf], number_of_pings);

                    if (result != 0)
                        printf("Ping check completed %d attempts\r\n", result);
                    else
                        printf("Ping check failed all attempts\r\n");

                    osDelay(1000);
                }
            }
        } else {
            printf("Got here because of code error.\r\n");
            osDelay(1000);
        }

    } while(i < 1000);

    printf("End of Test\r\n");

    evbLCD->clearBuffer();
    sprintf(txtstr,"Exiting Program");
    evbLCD->writeText(0,4,font_6x8,txtstr,strlen(txtstr));
}

/*
 * Sets pb1_low flag. Flag is cleared in pb1_debounce thread
 */
void pb1ISR(void)
{
    if (!pb1_low)
        pb1_low = true;
}

/*
 * Debounces pb1 PB1 changes spreading factor and output power
 * Cycles through SF_7-SF_10 for 4 power levels
 */
void pb1_debounce(void const *args)
{
    static uint8_t count = 0;
    static uint8_t test_now = 0;

    while (true) {

        if (pb1_low && (mDot08 == 0))
            count++;
        else {
            count = 0;
            pb1_low = false;
        }

        if (count == 5) {
            test_now++;

            if (test_now > 15)   // resets test_now
                test_now = 0;

            // selects power output level using upper bits for select
            switch(test_now >> 2) {
                case 0:
                    pwr_val = 11;
                    break;
                case 1:
                    pwr_val = 14;
                    break;
                case 2:
                    pwr_val = 18;
                    break;
                case 3:
                    pwr_val = 20;
            }

            // sets data rate based on lower bits
            sf_val = mDot::SF_7 - (test_now & 0x03);

            sprintf(txtstr,"DR=%2d Pwr=%2d     ",(12 - sf_val),pwr_val);
            evbLCD->writeText(0,7,font_6x8,txtstr,strlen(txtstr));
            printf("%s \r\n",txtstr);
        }

        Thread::wait(5);
    }
}

/*
 * Sets pb2_low flag. Flag is cleared in pb2_debounce thread
 */
void pb2ISR(void)
{
    if (!pb2_low)
        pb2_low = true;
}

/*
 * Debounces pb2 PB2 forces main program out of thread wait mode
 */
void pb2_debounce(void const *args)
{

    static uint8_t count = 0;

    while (true) {

        if (pb2_low && (mDot09 == 0)) {
            count++;
            if (count == 100) {
                // sets LED2 to 50% max current
                evbBackLight->setPWM(NCP5623B::LED_3,16); // enable LED2 on EVB and set to 50% PWM
                evbBackLight->setLEDCurrent(16);
            }
        } else {
            if ((count > 5) && (count <= 100)) {
                main_single = true;
                osSignalSet(mainThreadID, 0x10);
            } else if (count > 100) {
                main_sweep = true;
                osSignalSet(mainThreadID, 0x10);
            }

            count = 0;
            pb2_low = false;
        }

        Thread::wait(5);
    }
}

/*
 *  Function that print clear text verion of mDot errors
 */
void log_error(mDot* dot, const char* msg, int32_t retval)
{
    printf("%s - %ld:%s, %s\r\n", msg, retval, mDot::getReturnCodeString(retval).c_str(), dot->getLastError().c_str());
}

// return = number of ping checks that passed
uint8_t ping_check(uint8_t set_pwr, uint8_t set_sf, uint8_t pings_per_check)
{

    uint8_t result = 0;
    uint8_t ping_cnt;
    mDot::ping_response ping_data;
    mDot::rssi_stats rssi_data;
    mDot::snr_stats snr_data;
    struct tm gps_timestamp;
    GPSPARSER::longitude evb_longitude;
    GPSPARSER::latitude evb_latitude;
    GPSPARSER::satellite_prn gps_sat_prn;
    uint8_t gps_fix_quality;
    int16_t evb_altitude;
    uint8_t gps_num_satellites;
    uint8_t gps_fix_status;
    std::vector<uint8_t> mdot_data;

    mdot_mutex.lock();		// lock mdot mutex before changing radio parameters
    if ((mdot_ret = mdot_radio->setTxPower(set_pwr)) != mDot::MDOT_OK) {
        log_error(mdot_radio, "failed to set TX power level", mdot_ret);
    }
    if ((mdot_ret = mdot_radio->setTxDataRate(set_sf)) != mDot::MDOT_OK) {
        log_error(mdot_radio, "failed to set DataRate value", mdot_ret);
    }
    mdot_mutex.unlock();		// unlock mdot mutex after changing radio parameters

    for (ping_cnt = 0; ping_cnt < pings_per_check; ping_cnt++) {
        evbLCD->startUpdate();
        evbLCD->clearBuffer();

        mdot_mutex.lock();		// lock mdot mutex before ping
        ping_data = mdot_radio->ping();
        mdot_mutex.unlock();		// unlock mdot mutex after ping

        if (ping_data.status == mDot::MDOT_OK) {
            rssi_data = mdot_radio->getRssiStats();
            snr_data = mdot_radio->getSnrStats();
            gps_fix_status = mdot_gps->getFixStatus();
            gps_fix_quality = mdot_gps->getFixQuality();
            gps_num_satellites = mdot_gps->getNumSatellites();

            printf ("\r\n GPS Fix Status= %d num of sats = %d\r\n", gps_fix_status, gps_num_satellites);

            sprintf(txtstr,"PGOOD DR=%2d P=%2d",(12 - set_sf),set_pwr);
            evbLCD->writeText(0,0,font_6x8,txtstr,strlen(txtstr));
            printf("%s \r\n",txtstr);

            sprintf(txtstr,"UP  %3d dBm %2d.%1d",ping_data.rssi, ping_data.snr/10, abs(ping_data.snr)%10);
            evbLCD->writeText(0,1,font_6x8,txtstr,strlen(txtstr));
            printf("Link Quality %s \r\n",txtstr);

            sprintf(txtstr,"DWN %3d dBm %2d.%02d",rssi_data.last, snr_data.last/4, abs(snr_data.last)%4*25);
            evbLCD->writeText(0,2,font_6x8,txtstr,strlen(txtstr));
            printf("Link Quality %s \r\n",txtstr);

            mdot_data.clear();
            mdot_data.push_back(0x1D);			// key for start of data structure
            mdot_data.push_back(0x1A);			// key for uplink QOS + RF Pwr
            convertS.f_s = ping_data.rssi;
            mdot_data.push_back(convertS.t_u[1]);
            mdot_data.push_back(convertS.t_u[0]);
            mdot_data.push_back((ping_data.snr/10) & 0xFF);
            mdot_data.push_back(set_pwr);

            mdot_data.push_back(0x1B);			// key for downlink QOS
            convertS.f_s=rssi_data.last;
            mdot_data.push_back(convertS.t_u[1]);
            mdot_data.push_back(convertS.t_u[0]);
            mdot_data.push_back(snr_data.last);

            // collect GPS data if GPS device detected
            if (mdot_gps->gpsDetected() && (set_sf != mDot::SF_10)) {

                mdot_data.push_back(0x19);			// key for GPS Lock Status
                data = ( gps_num_satellites << 4 ) | ( gps_fix_status & 0x0F );
                mdot_data.push_back(data);

                if (mdot_gps->getLockStatus()) {			// if gps has a lock
                    evb_longitude = mdot_gps->getLongitude();
                    evb_latitude = mdot_gps->getLatitude();
                    evb_altitude = mdot_gps->getAltitude();
                    gps_timestamp = mdot_gps->getTimestamp();

                    sprintf(txtstr,"%3d %2d %2d.%03d",abs(evb_longitude.degrees),evb_longitude.minutes,(evb_longitude.seconds*6)/1000,(evb_longitude.seconds*6)%1000);

                    if (evb_longitude.degrees < 0)
                        strncat(txtstr," W",2);
                    else
                        strncat(txtstr," E",2);

                    evbLCD->writeText(0,3,font_6x8,txtstr,strlen(txtstr));
                    printf("Longitude: %s \r\n",txtstr);

                    sprintf(txtstr,"%3d %2d %2d.%03d",abs(evb_latitude.degrees),evb_latitude.minutes,(evb_latitude.seconds*6)/1000,(evb_latitude.seconds*6)%1000);

                    if (evb_latitude.degrees < 0)
                        strncat(txtstr," S",2);
                    else
                        strncat(txtstr," N",2);

                    evbLCD->writeText(0,4,font_6x8,txtstr,strlen(txtstr));
                    printf("Latitude:  %s \r\n",txtstr);

                    sprintf(txtstr,"Time %02d:%02d:%02d ",gps_timestamp.tm_hour,gps_timestamp.tm_min,gps_timestamp.tm_sec);
                    evbLCD->writeText(0,5,font_6x8,txtstr,strlen(txtstr));
                    printf("%s \r\n",txtstr);

                    sprintf(txtstr,"Date %02d/%02d/%04d",gps_timestamp.tm_mon + 1,gps_timestamp.tm_mday,gps_timestamp.tm_year +1900);
                    evbLCD->writeText(0,6,font_6x8,txtstr,strlen(txtstr));
                    printf("%s \r\n\r\n",txtstr);

                    // Send GPS data if GSP device locked
                    mdot_data.push_back(0x15);			// key for GPS Latitude
                    mdot_data.push_back(evb_latitude.degrees);
                    mdot_data.push_back(evb_latitude.minutes);
                    convertS.f_s = evb_latitude.seconds;
                    mdot_data.push_back(convertS.t_u[1]);
                    mdot_data.push_back(convertS.t_u[0]);

                    mdot_data.push_back(0x16);			// key for GPS Longitude
                    convertS.f_s = evb_longitude.degrees;
                    mdot_data.push_back(convertS.t_u[1]);
                    mdot_data.push_back(convertS.t_u[0]);

                    mdot_data.push_back(evb_longitude.minutes);
                    convertS.f_s = evb_longitude.seconds;
                    mdot_data.push_back(convertS.t_u[1]);
                    mdot_data.push_back(convertS.t_u[0]);
                } else {
                    sprintf(txtstr,"no GPS lock");
                    evbLCD->writeText(0,4,font_6x8,txtstr,strlen(txtstr));
                    printf("%s \r\n",txtstr);
                }
            }
            // key for end of data structure
            mdot_data.push_back(0x1D);

            // if SF_10 this sends 11 bytes of data, otherwise sends full data packet
            mdot_mutex.lock();		// lock mdot mutex before packet send
            mdot_ret = mdot_radio->send(mdot_data);
            mdot_mutex.unlock();		// unlock mdot mutex after packet send

            if (mdot_ret  != mDot::MDOT_OK) {
                sprintf(txtstr,"mDot Send failed");
                evbLCD->writeText(0,7,font_6x8,txtstr,strlen(txtstr));
                log_error(mdot_radio, txtstr, mdot_ret);
            } else {
                sprintf(txtstr,"mDot Send success");
                evbLCD->writeText(0,7,font_6x8,txtstr,strlen(txtstr));
                printf("successfully sent data to gateway\r\n");
                result++;
            }
        } else {
            sprintf(txtstr,"Ping Check Failed");
            evbLCD->writeText(0,2,font_6x8,txtstr,strlen(txtstr));
            printf("%s \r\n",txtstr);
            sprintf(txtstr,"DR=%2d P=%2d",(12 - set_sf),set_pwr);
            evbLCD->writeText(0,3,font_6x8,txtstr,strlen(txtstr));
            printf("%s \r\n",txtstr);
            strncpy(txtstr,mDot::getReturnCodeString(ping_data.status).c_str(),17);
            evbLCD->writeText(0,4,font_6x8,txtstr,strlen(txtstr));
            printf("%s \r\n",txtstr);
        }

        evbLCD->endUpdate();
        osDelay(1000);
    }
    return result;
}