MTDOT-EVB link check code for site survey

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

main.cpp

Committer:
falingtrea
Date:
2015-10-22
Revision:
0:dba397ff987f
Child:
1:4e3ee9c860e3

File content as of revision 0:dba397ff987f:

/**
 * @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;
}