MTDOT-EVB link check code for site survey

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

Revision:
0:dba397ff987f
Child:
1:4e3ee9c860e3
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Thu Oct 22 19:45:01 2015 +0000
@@ -0,0 +1,775 @@
+/**
+ * @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;
+}
\ No newline at end of file