Demonstration program for Multitech System MTDOT-EVB an evaluation board for the mDot LoRa module

Dependencies:   DOGS102 ISL29011 MMA845x MPL3115A2 NCP5623B libmDot mbed-rtos mbed

main.cpp

Committer:
falingtrea
Date:
2015-11-23
Revision:
9:ed86d5ae29cc
Parent:
7:85445a8cc189

File content as of revision 9:ed86d5ae29cc:

/**
 * @file    main.cpp
 * @brief   Main application for mDot-EVB demo
 * @author  Tim Barr  MultiTech Systems Inc.
 * @version 1.05
 * @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.01 TAB 7/6/15 Removed NULL pointer from evbAmbientLight creation call.
 *
 * 1.02 TAB 7/8/15 Send logo to LCD before attempting connection to LoRa network. Added
 *					information on setting up for public LoRa network. Moved SW setup to
 *					beginning of main. Removed printf call from ISR functions. Added
 *					additional checks for exit_program.
 *
 * 1.03 TAB 7/15/15 Added threads for push button switch debounce.
 *
 * 1.04 TAB 10/22/15 Fixed format error in temperature print to LCD. Corrected error in
 *					public netework setup. Swapped \n and \r in prinf calls because
 *					Windows seems to be picky about the order
 *
 * 1.05 TAB 11/17/15 Changed baud rate for debug from 921k to 115k. Added code to support
 *					 868 MHz band operation. Added mutex around certain mDot radio commands.
 *					 Changed network name and passphrase. Changed pb references to sw to
 *					 match MTDOT-BOX overlay. Changed function of SW1 from end program to
 *					 cycle through power levels and data rates.
 */

#include "mbed.h"
#include "MMA845x.h"
#include "MPL3115A2.h"
#include "ISL29011.h"
#include "NCP5623B.h"
#include "DOGS102.h"
#include "font_6x8.h"
#include "MultiTech_Logo.h"
#include "mDot.h"
#include "rtos.h"
#include <string>
#include <vector>

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 SW1 on EVB
InterruptIn mDot09(PA_11);           			//  GPIO/USB       PB SW2 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

//Serial 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_name and config_network_pass are for private networks.
 */

//Default network server settings
static std::string config_network_name = "Multitech";
static std::string config_network_pass = "Multitech";
static uint8_t config_frequency_sub_band = 3;

/*  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, mdot_freq;
uint8_t  pckt_time = 8;
uint16_t xmit_delay = 8;
char data;
unsigned char test;
char txtstr[17];
int32_t num_whole, mdot_ret, join_delay;
uint32_t pressure;
int16_t num_frac;
uint8_t sf_val = mDot::SF_7;
uint8_t pwr_val = 11; // dBm

MMA845x_DATA accel_data;
MPL3115A2_DATA baro_data;
uint16_t  lux_data;
MMA845x* evbAccel;
MPL3115A2* evbBaro;
ISL29011* evbAmbLight;
NCP5623B* evbBackLight;
DOGS102* evbLCD;
mDot* mdot_radio;
Mutex mdot_mutex;

convert32 convertl;
convert16 converts;

// flags for pushbutton debounce code
bool sw1_low = false;
bool sw2_low = false;
bool toggle_text = false;

void sw1ISR(void);
void sw2ISR(void);
void sw1_debounce(void const *args);
void sw2_debounce(void const *args);
Thread* thread_3;

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

void config_pkt_xmit (void const *args);

int main()
{

    std::vector<uint8_t> mdot_data;
    std::vector<uint8_t> mdot_EUI;
    uint16_t i = 0;
    uint8_t j =0;

    debugUART.baud(115200);
//  mDotUART.baud(9600);    // mdot UART unused but available on external connector

    Thread thread_1(sw1_debounce);						// threads for de-bouncing pushbutton switches
    Thread thread_2(sw2_debounce);

    thread_3 = new Thread(config_pkt_xmit);				// start thread that sends LoRa packet when SW2 pressed

    evbAccel = new MMA845x(mDoti2c,MMA845x::SA0_VSS); 	// setup Accelerometer
    evbBaro = new MPL3115A2(mDoti2c);					// setup Barometric sensor
    evbAmbLight = new ISL29011(mDoti2c);		 		// Setup Ambient Light Sensor
    evbBackLight = new NCP5623B(mDoti2c);				// setup backlight and LED 2 driver chip
    evbLCD = new DOGS102(mDotspi, mDot17, mDot13);		// setup LCD

    /*
     *  Setup SW1 as program stop function
     */
    mDot08.disable_irq();
    mDot08.fall(&sw1ISR);

    /*
     *  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 packet time change
     */
    mDot09.disable_irq();
    mDot09.fall(&sw2ISR);

    /*
     *  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("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);

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

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

    if (mdot_radio) {
        mdot_mutex.lock(); 	// lock mdot before setting configuration

        // reset to default config so we know what state we're in
        mdot_radio->resetConfig();

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

        // Read node ID and frequency band
        mdot_EUI = mdot_radio->getDeviceId();
        mdot_freq = mdot_radio->getFrequencyBand();

        printf("mDot EUI = ");

        for (i=0; i<mdot_EUI.size(); i++) {
            printf("%02x ", mdot_EUI[i]);
        }
        printf("\r\n");
        sprintf(txtstr,"MTDOT EVB");
        printf("mDot Frequency = ");
        if (mdot_freq == mDot::FB_868) {
            printf( "868 MHz\r\n");
            sprintf(txtstr,"%s 868 MHz",txtstr);
        } else {
            printf("915 MHz\r\n");
            sprintf(txtstr,"%s 915 MHz",txtstr);
        }

        evbLCD->writeText(0,3,font_6x8,txtstr,strlen(txtstr));
        sprintf(txtstr,"Sensor Demo");
        evbLCD->writeText(24,4,font_6x8,txtstr,strlen(txtstr));

        evbLCD->endUpdate();


// 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
         */
        if (mdot_freq == mDot::FB_915) {
            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);
            }
        }

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

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

        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

        } 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);
    }

    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);

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

    osDelay (500);			// allows other threads to process
    printf("shutdown LED:\r\n");
    evbBackLight->shutdown();

    osDelay (500);			// allows other threads to process
    printf("Turn on LED2\r\n");
    evbBackLight->setLEDCurrent(16);

    data = evbAccel->getWhoAmI();
    printf("Accelerometer who_am_i value = %x \r\n", data);

    result = evbAccel->getStatus();
    printf("status byte = %x \r\n", result);

    printf("Barometer who_am_i check = %s \r\n", evbBaro->testWhoAmI() ? "TRUE" : "FALSE");

    result = evbBaro->getStatus();
    printf("status byte = %x \r\n", result);

    /*
     *  Setup the Accelerometer for 8g range, 14 bit resolution, Noise reduction off, sample rate 1.56 Hz
     *  normal oversample mode, High pass filter off
     */
    evbAccel->setCommonParameters(MMA845x::RANGE_8g,MMA845x::RES_MAX,MMA845x::LN_OFF,
                                  MMA845x::DR_1_56,MMA845x::OS_NORMAL,MMA845x::HPF_OFF );

    /*
     * Setup the Barometric sensor for post processed Ambient pressure, 4 samples per data acquisition.
     * and a sample taken every second when in active mode
     */
    evbBaro->setParameters(MPL3115A2::DATA_NORMAL, MPL3115A2::DM_BAROMETER, MPL3115A2::OR_16,
                           MPL3115A2::AT_1);
    /*
     * Setup the Ambient Light Sensor for continuous Ambient Light Sensing, 16 bit resolution,
     * and 16000 lux range
     */

    evbAmbLight->setMode(ISL29011::ALS_CONT);
    evbAmbLight->setResolution(ISL29011::ADC_16BIT);
    evbAmbLight->setRange(ISL29011::RNG_16000);

    /*
     * Set the accelerometer for active mode
     */
    evbAccel->activeMode();

    /*
     * Clear the min-max registers in the Barometric Sensor
     */
    evbBaro->clearMinMaxRegs();

    evbBackLight->setLEDCurrent(0);

    /*
     * Main data acquisition loop
     */

	i = 0;
	
    do {
        evbLCD->startUpdate();
        // clear LCD line 0-6 only
        sprintf(txtstr, "                 ");
        for (j=0; j<6; j++)
            evbLCD->writeText(0,j,font_6x8,txtstr,strlen(txtstr));

        /*
         * Test Accelerometer XYZ data ready bit to see if acquisition complete
         */
        do {
            osDelay(100);			// allows other threads to process
            result = evbAccel->getStatus();
        } while ((result & MMA845x::XYZDR) == 0 );

        /*
         * Retrieve and print out accelerometer data
         */
        accel_data = evbAccel->getXYZ();

        sprintf(txtstr, "Accel: x = %d", accel_data._x);
        evbLCD->writeText(0,0,font_6x8,txtstr,strlen(txtstr));
        sprintf(txtstr, "y = %d", accel_data._y);
        evbLCD->writeText(42,1,font_6x8,txtstr,strlen(txtstr));
        sprintf(txtstr, "z = %d", accel_data._z );
        evbLCD->writeText(42,2,font_6x8,txtstr,strlen(txtstr));

        /*
         * Trigger a Pressure reading
         */
        evbBaro->setParameters(MPL3115A2::DATA_NORMAL, MPL3115A2::DM_BAROMETER, MPL3115A2::OR_16,
                               MPL3115A2::AT_1);
        evbBaro->triggerOneShot();

        /*
         * Test barometer device status to see if acquisition is complete
         */
        do {
            osDelay(100);			// allows other threads to process
            result = evbBaro->getStatus();
        } while ((result & MPL3115A2::PTDR) == 0 );

        /*
         * Retrieve and print out barometric pressure
         */
        pressure = evbBaro->getBaroData() >> 12; // convert 32 bit signed to 20 bit unsigned value
        num_whole = pressure >> 2;			// 18 bit integer significant
        num_frac = (pressure & 0x3) * 25;		// 2 bit fractional  0.25 per bit
        sprintf(txtstr,"Press=%ld.%02d Pa", num_whole, num_frac);
        evbLCD->writeText(0,3,font_6x8,txtstr,strlen(txtstr));

        /*
         * Trigger a Altitude reading
         */
        evbBaro->setParameters(MPL3115A2::DATA_NORMAL, MPL3115A2::DM_ALTIMETER, MPL3115A2::OR_16,
                               MPL3115A2::AT_1);
        evbBaro->triggerOneShot();

        /*
         * Test barometer device status to see if acquisition is complete
         */
        do {
            osDelay(100);			// allows other threads to process
            result = evbBaro->getStatus();
        } while ((result & MPL3115A2::PTDR) == 0 );

        /*
         * Retrieve and print out altitude and temperature
         */
        baro_data = evbBaro->getAllData(false);
        baro_data._baro /= 4096;				// convert 32 bit signed to 20 bit signed value
        num_whole = baro_data._baro / 16;		//	18 bit signed significant integer
        num_frac = (baro_data._baro & 0xF) * 625 / 100;		// 4 bit fractional .0625 per bit
        sprintf(txtstr,"Alti=%ld.%02d m", num_whole, num_frac);
        evbLCD->writeText(0,4,font_6x8,txtstr,strlen(txtstr));
        num_whole = baro_data._temp / 16;		// 8 bit signed significant integer
        num_frac = (baro_data._temp & 0x0F) * 625 / 100;		// 4 bit fractional .0625 per bit
        sprintf(txtstr,"Temp=%ld.%03d C", num_whole, num_frac);
        evbLCD->writeText(0,5,font_6x8,txtstr,strlen(txtstr));

        /*
         * retrieve and print out Ambient Light level
         */
        lux_data = evbAmbLight->getData();
        num_whole = lux_data * 24 / 100;		// 16000 lux full scale .24 lux per bit
        num_frac = lux_data * 24 % 100;
        sprintf(txtstr, "Light=%ld.%02d lux", num_whole, num_frac );
        evbLCD->writeText(0,6,font_6x8,txtstr,strlen(txtstr));

        evbLCD->endUpdate();

        printf("finished iteration %d\r\n",(++i));

        /* Transmit data packet based on pckt_time setting and result of
         * mdot_radio->getNextTxMs() function and mutex available. The getNextTxMs()
         * function returns the number of millisecond before another packet can be
         * sent in 868 MHz band. This can vary depending on wther the 10%, 1% or 0.1%
         * channel is used and the time on air for a specific setup of the radio
         */

        if ((xmit_delay <= i) && (mdot_radio->getNextTxMs() == 0) && (mdot_mutex.trylock())) {
            /*
             * Setting TX power and Data Rate for radio just in case user requested by SW2
             */
            mdot_radio->setTxPower(pwr_val);
            mdot_radio->setTxDataRate(sf_val);

            mdot_data.clear();
            mdot_data.push_back(0x1D);			// key for start of data
            mdot_data.push_back(0x0E);			// key for Current Acceleration 3-Axis Value
            converts.f_s = accel_data._x *4;		// shift data 2 bits while retaining sign
            mdot_data.push_back(converts.t_u[1]);	// get 8 MSB of 14 bit value
            converts.f_s = accel_data._y * 4;		// shift data 2 bits while retaining sign
            mdot_data.push_back(converts.t_u[1]);	// get 8 MSB of 14 bit value
            converts.f_s = accel_data._z * 4;		// shift data 2 bits while retaining sign
            mdot_data.push_back(converts.t_u[1]);	// get 8 MSB of 14 bit value
            mdot_data.push_back(0x08);			// key for Current Pressure Value
            convertl.f_u = pressure;				// pressure data is 20 bits unsigned
            mdot_data.push_back(convertl.t_u[2]);
            mdot_data.push_back(convertl.t_u[1]);
            mdot_data.push_back(convertl.t_u[0]);

            /* for 915 MHz band and SF_10 max packet size is 11 bytes.
             * so the payload gets split in two packets here.
             */
            if ((mdot_freq == mDot::FB_915) && (sf_val == mDot::SF_10)) {
            	mdot_ret = mdot_radio->send(mdot_data);
                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);
                    continue;
                } else {
                    printf("successfully sent packet 1 to gateway\r\n");
                }
                mdot_data.clear();		// clear data for next "chunk"
            }


            mdot_data.push_back(0x05);			// key for Current Ambient Light Value
            converts.f_u = lux_data;				// data is 16 bits unsigned
            mdot_data.push_back(converts.t_u[1]);
            mdot_data.push_back(converts.t_u[0]);
            mdot_data.push_back(0x0B);			// key for Current Temperature Value
            converts.f_s = baro_data._temp;		// temperature is signed 12 bit
            mdot_data.push_back(converts.t_u[1]);
            mdot_data.push_back(converts.t_u[0]);
            mdot_data.push_back(0x1D);			// key for end of data

            mdot_ret = mdot_radio->send(mdot_data);
            if (mdot_ret != mDot::MDOT_OK) {
                log_error(mdot_radio, "failed to send", mdot_ret);
                sprintf(txtstr,"PKT Send Failed");
                evbLCD->writeText(0,7,font_6x8,txtstr,strlen(txtstr));
            } else {
                sprintf(txtstr,"SENT DR=%2d Pwr=%2d",(12 - sf_val),pwr_val);
                evbLCD->writeText(0,7,font_6x8,txtstr,strlen(txtstr));
                printf("%s \r\n",txtstr);
            }
            mdot_mutex.unlock(); // unlock mutex after all data transmitted
            xmit_delay = i + pckt_time;
            if (mdot_freq == mDot::FB_868)
            	printf("Next transmit time = %d milliseconds \r\n",mdot_radio->getNextTxMs());

        } else {
            sprintf(txtstr,"     DR=%2d Pwr=%2d     ",(12 - sf_val),pwr_val);
            evbLCD->writeText(0,7,font_6x8,txtstr,strlen(txtstr));
        }
    } while(i < 65000);

    evbBaro->triggerOneShot();

    do {
        osDelay(200);			// allows other threads to process
        result = evbBaro->getStatus();
    } while ((result & MPL3115A2::PTDR) == 0 );

    baro_data = evbBaro->getAllData(true);
    printf ("minBaro=%ld maxBaro=%ld minTemp=%d maxTemp=%d\r\n", baro_data._minbaro, baro_data._maxbaro,
            baro_data._mintemp, baro_data._maxtemp);

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

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

}

/*
 * Sets sw1_low flag. Slag is cleared in sw1_debounce thread
 */
void sw1ISR(void)
{
    if (!sw1_low)
        sw1_low = true;
}

/*
 * Debounces sw1 changes spreading factor and output power
 * Cycles through SF_7-SF_9 for 4 power levels for 915 MHz band
 * Cycles through SF_7-SF_12 for 4 power levels for 868 MHz band
 */
void sw1_debounce(void const *args)
{
    static uint8_t count = 0;
    static uint8_t test_sf = 0;
    static uint8_t test_pwr = 0;

    while (true) {

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

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

            if (((test_sf > 3) && (mdot_freq == mDot::FB_915)) || ((test_sf > 5) && (mdot_freq == mDot::FB_868))) {
                test_sf = 0;
                test_pwr ++;
                if (test_pwr > 3)
                    test_pwr = 0;
            }

            // selects power output level using upper bits for select
            switch(test_pwr) {
                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_sf & 0x07);

            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 sw2_low flag. Flag is cleared in sw2_debounce thread
 */
void sw2ISR(void)
{
    if (!sw2_low)
        sw2_low = true;
}

/*
 * Debounces sw2. Changes packet transmit time to every 2nd,
 * 4th, 8th, or 16th sample when SW2 pushed. For 868 MHz mode
 * the time for next packet value is also tested.
 * Also triggers a thread to transmit a configuration packet
 */
void sw2_debounce(void const *args)
{

    static uint8_t count = 0;

    while (true) {

        if (sw2_low && (mDot09 == 0))
            count++;
        else {
            count = 0;
            sw2_low = false;
        }

        if (count == 5) {

            if (pckt_time >= 4)
                pckt_time /= 2;
            else pckt_time = 16;

            thread_3->signal_set(0x10);		// signal config_pkt_xmit to send packet
        }

        Thread::wait(5);
    }
}

/*
 *  Function that prints 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());
}

/*
 * Thread that is triggered by SW2 ISR. Sends a packet to the LoRa server with the new Packet Transmission time setting
 */
void config_pkt_xmit (void const *args)
{

    std::vector<uint8_t> data;
    uint16_t wait_time;

    while (true) {
        Thread::signal_wait(0x10);		// wait for sw2ISR to signal send
        data.clear();
        data.push_back(0x0F);			// key for Configuration data (packet transmission timer)
        data.push_back(pckt_time);
        
        mdot_mutex.lock();		// lock mdot mutex before packet send

        if ((wait_time = mdot_radio->getNextTxMs()) > 0) // wait for next xmit time
        	osDelay(wait_time);

        if ((mdot_ret = mdot_radio->send(data)) != mDot::MDOT_OK) {
            log_error(mdot_radio, "failed to send config data", mdot_ret);
        } else {
            printf("sent config data to gateway\r\n");
        }
        mdot_mutex.unlock();		// unlock mdot mutex after packet send
    }
}