Demonstration for connecting Multitech mDot/mDotbox to the Telit devicewise platform.

Dependencies:   DOGS102 GpsParser ISL29011 MMA845x MPL3115A2 MTS-Serial NCP5623B mDot_X_NUCLEO_IKS01A1 libmDot-mbed5

main.cpp

Committer:
pferland
Date:
2017-05-16
Revision:
1:de9172a990bd
Parent:
0:5a7579045f49
Child:
2:fc20e16833af

File content as of revision 1:de9172a990bd:

/*================================================================================
 *
 * CLASIFICATION:     **** PUBLIC RELEASE AS-IS APPLICATION EXAMPLE ****
 *
 * SOURCE FILE NAME:  main.cpp
 *
 * DESCRIPTIVE NAME:  MBED Source for MultiTech mDot, EVB, and MDOT-BOX Devices
 *
 * COPYRIGHT:         Copyright 2014-2017, Telit
 *
 *                    LICENSED MATERIAL - PROGRAM PROPERTY OF TELIT
 *                    REFER TO TELIT COPYRIGHT INSTRUCTION
 *
** WEB SITE:          www.telit.com
 *
 * WRITTEN BY:        Eliott Fersko and John Keever
 *
 * DATE:              April 17, 2017
 *
 * VERSION:           1.02
 *
 * FUNCTION:          Provide working example for LoRa 'Sensor-to-Cloud'
 *                    Demonstration using MultiTech LoRa Starter Kit and
 *                    Telit Data and Cloud Platform Services Offerings
 *
 * SOURCE FILE TYPE:  MBED C++
 *
 * FUNCTIONS/ENTRY POINTS:  main
 *
 * INPUT  = None.
 * OUTPUT = None.
 *
 * EXIT-NORMAL = N/A
 * EXIT-ERROR  = N/A
 *
 * EXTERNAL REFERENCES = None.
 *
 * EXTERNAL FUNCTIONS = None.
 *
 * CONTROL BLOCKS = None.
 *
 *================================================================================*/
/*                              LEGAL DISCLAIMER                                  */
/*================================================================================

  Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions
  are met:

    Neither the name of ILS Technology nor Telit nor the names of its
    contributors may be used to endorse or promote products derived
    from this software without specific prior written permission.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

 *================================================================================*/
/*                                 CHANGE LOG                                     */
/*================================================================================*/
/*          |Changed |          |                                                 */
/*  Date    |   by   |Version ID| Comments                                        */
/*----------+--------+----------+-------------------------------------------------*/
/*04-12-2017|JEK     |V1.00     |Original Version - mDot, EVB, MDOT-BOX           */
/*04-14-2017|JEK     |V1.01     |Added IRQ Handlers - SW1/SW2 (Threads?)          */
/*04-17-2017|JEK     |V1.02     |Added Binary LoRa Payload Message Framing Type   */
/*04-24-2017|EMF     |V1.03     |Updated Libraries                                */
/*05-10-2017|EMF     |V1.04     |Added support for ST-Micro MEMS Sensor Shield    */
/*================================================================================*/
/*NOTE:  Please keep the change log up to date!!!                                 */
/*================================================================================*/

#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 "GPSPARSER.h"
#include "MTSSerial.h"
#include "x_nucleo_iks01a1.h"

#include <cmath>
#include <string>
#include <vector>
#include <ctime>
#include <Ticker.h>

#define CALL_METH(obj, meth, param, ret) ((obj == NULL) ?       \
                      ((*(param) = (ret)), 0) : \
                      ((obj)->meth(param))      \
                      )


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

namespace {
    const int MS_INTERVALS = 1000;
}

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

typedef struct {
    int32_t AXIS_X;
    int32_t AXIS_Y;
    int32_t AXIS_Z;
} AxesRaw_TypeDef;


/*
 * 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
AnalogIn current_sensor(PB_1);                  // EVB - GPIO - Current Sensor Analog Input

/* **** 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 = "telitlora";
//static std::string config_network_pass = "telitpass";
//static uint8_t config_frequency_sub_band = 7;



static volatile bool timer_irq_triggered = false;
static volatile bool ff_irq_triggered = false;

static std::string config_network_name = "MTCDT-19186797";
static std::string config_network_pass = "MTCDT-19186797";
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;

// 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 sw1_state = false;
bool sw2_state = false;

uint32_t num_whole;
uint16_t num_frac;

uint32_t pressure;
double current;

bool exit_program = false;

//EVB sensor variables
MMA845x_DATA accel_data;
MPL3115A2_DATA baro_data;
uint16_t  lux_data;
MMA845x* evbAccel;
MPL3115A2* evbBaro;
ISL29011* evbAmbLight;
NCP5623B* evbBackLight;
DOGS102* evbLCD;

//MEMS sensor variables
float TEMPERATURE_Value;
float HUMIDITY_Value;
float PRESSURE_Value;
float PRESSURE_Temp_Value;
AxesRaw_TypeDef MAG_Value;
AxesRaw_TypeDef ACC_Value;
AxesRaw_TypeDef GYR_Value;
char buffer1[32];
char buffer2[32];
char buffer3[32];
char buffer4[32];
unsigned int ret = 0;

mDot* mdot_radio;
Mutex mdot_mutex;

//MEMS shield sensor variables
//static X_NUCLEO_IKS01A1 *mems_expansion_board = X_NUCLEO_IKS01A1::Instance();
X_NUCLEO_IKS01A1 *mems_expansion_board = X_NUCLEO_IKS01A1::Instance(I2C_SDA, I2C_SCL, PC_1);
static Ticker ticker;


GyroSensor *memsGyro;
MotionSensor *memsAccel;
MagneticSensor *memsMag;
HumiditySensor *memsHumidity;
PressureSensor *memsPressure;
TempSensor *memsTemp1;
TempSensor *memsTemp2;


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

MPL3115A2* resetBaro(const MPL3115A2* oldBaro);
void log_error(mDot* dot, const char* msg, int32_t retval);
int32_t sendString(const std::string text);
bool writeValueOrError();

const int FAIL_MAX=15;
int failtime=FAIL_MAX;
int cycle_cnt = 0;

char sensor_text[64];
char lora_temp_string[16];
char lora_alt_string[16];
char lora_press_string[16];
char lora_light_string[16];
char lora_current_string[16];
char lora_humid_string[16];

bool bHasGPS = false;
bool bHasACC = false;
bool bHasLCD = false;

/*** Helper Functions ------------------------------------------------------------ ***/
/* print floats & doubles */
static char *printDouble(char* str, double v, int decimalDigits=2)
{
  int i = 1;
  int intPart, fractPart;
  int len;
  char *ptr;

  /* prepare decimal digits multiplicator */
  for (;decimalDigits!=0; i*=10, decimalDigits--);

  /* calculate integer & fractinal parts */
  intPart = (int)v;
  fractPart = (int)((v-(double)(int)v)*i);

  /* fill in integer part */
  sprintf(str, "%i.", intPart);

  /* prepare fill in of fractional part */
  len = strlen(str);
  ptr = &str[len];

  /* fill in leading fractional zeros */
  for (i/=10;i>1; i/=10, ptr++) {
      if(fractPart >= i) break;
      *ptr = '0';
  }

  /* fill in (rest of) fractional part */
  sprintf(ptr, "%i", fractPart);

  return str;
}
/*===================================================================================
Main Program Logic - Entry
===================================================================================*/
int main()
{
    std::vector<uint8_t> mdot_data;
    std::vector<uint8_t> mdot_EUI;
    uint16_t i = 0;
    uint8_t id1 = 0;

    debugUART.baud(9600);

    printf ("Starting Application...\r\n");

    mainThreadID = osThreadGetId();

    printf("Begin I2C/SPI Device Initialization...\r\n");


    // Use Magnetometer As MEMS sensor shield Enumeration...

    memsMag = mems_expansion_board->magnetometer;
    CALL_METH(memsMag, ReadID, &id1, 0x0);
    printf("Magnetometer ID value = %i \n\r", id1);

    //chcek for default magnetomter ID 61...
    if(id1 == 61)
    {
        printf("Magnetometer Found - MEMS sensor shield detected...\r\n");
        bHasACC = true;

        // Initialize Integrated Sensors...
        printf("Instantiate Magnetometer...\r\n");
        memsMag = mems_expansion_board->magnetometer;

        printf("Instantiate Accelerometer...\r\n");
        memsAccel = mems_expansion_board->GetAccelerometer();

        printf("Instantiate Gyroscope...\r\n");
        memsGyro = mems_expansion_board->GetGyroscope();

        printf("Instantiate Barometric Pressure Sensor...\r\n");
        memsPressure = mems_expansion_board->pt_sensor;

        printf("Instantiate Temperature Pressure Sensor...\r\n");
        //memsTemp1 = mems_expansion_board->ht_sensor;
        memsTemp2 = mems_expansion_board->pt_sensor;

        printf("Instantiate Humidity Sensor...\r\n");
        memsHumidity = mems_expansion_board->ht_sensor;

    }


    else
    {
        printf("No Magnetometer Found...\r\n");
        // Use Accelerometer As MDOT-BOX/EVB Enumeration...
        printf("Instantiate Accelerometer...\r\n");
        evbAccel = new MMA845x(mDoti2c,MMA845x::SA0_VSS);

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

        if( (evbAccel->getStatus() == 1) && (evbAccel->getWhoAmI() == 0) )
        {
            printf("No Accelerometer Found - Basic mDot, Not MDOT-BOX or EVB...\r\n");
        }

        else
        {
            printf("Accelerometer Found - Either MDOT-BOX or EVB...\r\n");
            bHasACC = true;

        // Initialize Integrated Sensors...
            printf("Instantiate Barometric Pressure Sensor...\r\n");
            evbBaro = new MPL3115A2(mDoti2c);       // Setup Barometric Sensor
            printf("Barometric Sensor Status = %d\r\n", evbBaro->getStatus());
            printf("Barometer who_am_i check = %d\r\n", evbBaro->testWhoAmI());
            printf("Barometer who_am_i check = %s\r\n", evbBaro->testWhoAmI() ? "TRUE" : "FALSE");

            if( evbBaro->getStatus() == 0 ) printf("No Barometric Sensor...\r\n");

            printf("Instantiate Ambient Light Sensor...\r\n");
            evbAmbLight = new ISL29011(mDoti2c);    // Setup Ambient Light Sensor

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

            // Support Integrated LCD Display
            bHasLCD = true;

            printf("Instantiate BackLight and LCD Display...\r\n");
            evbBackLight = new NCP5623B(mDoti2c);               // setup backlight and LED 2 driver chip

            evbLCD = new DOGS102(mDotspi, mDot17, mDot13);      // setup LCD Display

            printf("Font Table Address:  %p\r\n",&font_6x8);
            printf("Bitmap Logo 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,"Sensor Demo");
            evbLCD->writeText(18,4,font_6x8,txtstr,strlen(txtstr));

            evbLCD->endUpdate();

            printf("Setup PushButton Interface Handlers...\r\n");

            printf("Launching Debounce Threads...\r\n");
            Thread thread_1(pb1_debounce);
            Thread thread_2(pb2_debounce);
            printf("Debounce Threads Launched...\r\n");

            printf("Set SW1/SW2 IRQs...\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("SW1/SW2 IRQs Set...\r\n");
        }
    }

    printf("I2C/SPI Device Initialization Complete...\r\n");

    printf("\r\nSetup mDot Radio Communications...\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, "ERROR: 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, "ERROR: 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, "ERROR: 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, "ERROR: Failed to set TX Data Rate", mdot_ret);
        }

        // Configure Pushbutton Display Labels...
        sprintf(txtstr,"SF=%2d PWR=%2d",sf_val,pwr_val);
        if( bHasLCD ) 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, "ERROR: 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, "ERROR: 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, "ERROR: 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 LoRa 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,"ERROR: Failed to Join Network:", mdot_ret);

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

                if( bHasLCD ) 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("Join 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);

        printf("Successfully Joined LoRa Network...\r\n");

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

    }
    else
    {
        printf("ERROR:  Unable to Join LoRa Network...\r\n");
        sprintf(txtstr,"Radio Init Failed!");
        if( bHasLCD ) evbLCD->writeText(0,5,font_6x8,txtstr,strlen(txtstr));
        printf("%s\r\n",txtstr);
        exit(1);
    }

    // Check for MDOT-BOX Configuration (GPS)

    if( bHasACC && bHasLCD )
    {
        printf("Instantiate GPS...\r\n");
        mdot_gps = new GPSPARSER(&mDotUART);

        if(!mdot_gps->gpsDetected())
        {
            printf(">>>> No GPS Detected... Not an MDOT-BOX, EVB Identified\r\n");

            sprintf(txtstr,"*No GPS Detected*");
            if( bHasLCD ) evbLCD->writeText(0,6,font_6x8,txtstr,strlen(txtstr));
        }
        else
        {
            // main_single = main_sweep = false;
            do
            {
                // osSignalWait(0x10, 2000);
                if (mdot_gps->getLockStatus())
                {
                    sprintf(txtstr,"!!GPS locked!!");
                    if( bHasLCD ) 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 >");

                    if( bHasLCD ) evbLCD->writeText(0,6,font_6x8,txtstr,strlen(txtstr));
                    printf("%s \r\n",txtstr);
                    toggle_text = !toggle_text;
                }
            } while ( !(mdot_gps->getLockStatus()) );
        }
    }

    printf("Initialization/Configuration Completed...\r\n");

    osDelay(1500);
    if( bHasLCD ) evbBackLight->setPWM(NCP5623B::LED_3,16); // enable LED2 on EVB and set to 50% PWM

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


    // Check for PB1 press during network join attempt
    /*
    if (exit_program)
    {
        printf("PB1 Pressed, Exiting Program...\r\n");
        if( bHasLCD ) evbLCD->clearBuffer();
        sprintf(txtstr,"Exiting Program");
        if( bHasLCD ) evbLCD->writeText(0,4,font_6x8,txtstr,strlen(txtstr));
        exit(1);
    }
    */

    // Enter Main Data Acquisition Loop...
    printf("Processing Data Acquisition Scan Loop...\r\n");

    i = 0;
    cycle_cnt = 100;

    if( bHasLCD ) evbLCD->clearBuffer();

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

            if (failtime==0)
            {
                evbBaro=resetBaro(evbBaro);
                continue;
            }

            // 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, "Accel-Y = %d", accel_data._y);
            evbLCD->writeText(0,1,font_6x8,txtstr,strlen(txtstr));
            sprintf(txtstr, "Accel-Z = %d", accel_data._z );
            evbLCD->writeText(0,2,font_6x8,txtstr,strlen(txtstr));

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

            // Test Barometer Device, Check to see if acquisition is complete
            failtime=FAIL_MAX;
            do
            {
                osDelay(100);           // allows other threads to process
                result = evbBaro->getStatus();
                failtime--;
            } while ((result & MPL3115A2::PTDR) == 0 && failtime > 0 );

            if(failtime==0)
            {
                evbBaro=resetBaro(evbBaro);
                continue;
            }

            // Retrieve and Display 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

            // If failtime reached 0 , indicates that the result might be junk.
            sprintf(txtstr,"Press=%ld.%01d Pa", num_whole, num_frac/10);

            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

            writeValueOrError();                        // will write to  lorapresstring and txtstr

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

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

            if (failtime==0)
            {
                evbBaro=resetBaro(evbBaro);
                continue;
            }

            // Retrieve and Display Altimeter Reading
            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 (JEK Added 60 as SWAG Offset for Boca)
            num_frac = (baro_data._baro & 0xF) * 625 / 100;             // 4 bit fractional .0625 per bit
            sprintf(txtstr,"Alti=%ld.%03d m", num_whole, num_frac);
            sprintf(lora_alt_string,"%ld.%03d", num_whole, num_frac);
            evbLCD->writeText(0,4,font_6x8,txtstr,strlen(txtstr));

            // Retrieve and Display Temperature Reading
            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);
            sprintf(lora_temp_string,"%ld.%03d", num_whole, num_frac);
            evbLCD->writeText(0,5,font_6x8,txtstr,strlen(txtstr));

            // Retrieve and Display 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 );
            sprintf(lora_light_string, "%ld.%02d", num_whole, num_frac );
            evbLCD->writeText(0,6,font_6x8,txtstr,strlen(txtstr));

            // Retrieve and Display Current Sensor Level (EVB Analog Input)
            current = (double) current_sensor * 65535.0;
            sprintf(lora_current_string, "%d", (int) current);

            // Handle Pushbutton #1 (SW1) - This should be handled by the 'pb1_debounce' thread.
            if( pb1_low )
            {
                sw1_state = !sw1_state;
                if( bHasLCD ) evbBackLight->setPWM(NCP5623B::LED_3,0);  // enable LED2 on EVB and set to 0% PWM

                sprintf(txtstr,"PB1 Press-SW1: %d", sw1_state);
                if( bHasLCD ) evbLCD->writeText(0,7,font_6x8,txtstr,strlen(txtstr));
                printf("%s \r\n",txtstr);

                pb1_low = false;
            }

            // Handle Pushbutton #2 (SW2) - This should be handled by the 'pb2_debounce' thread.
             if( pb2_low )
            {
                sw2_state = !sw2_state;
                if( bHasLCD ) evbBackLight->setPWM(NCP5623B::LED_3,16); // enable LED2 on EVB and set to 50% PWM

                sprintf(txtstr,"PB2 Press-SW2: %d", sw2_state);
                if( bHasLCD ) evbLCD->writeText(0,7,font_6x8,txtstr,strlen(txtstr));
                printf("%s \r\n",txtstr);

                pb2_low = false;
            }
        }
        //MEMS
        else if(!bHasLCD && bHasACC)
        {

            std::vector<uint8_t> tx_data;


            // Payload structure for mydevices cayenne:
            // 1 byte Data1 ID
            // 1 Byte Data1 Type
            // N Bytes Data1
            // 1 byte data 2 ID
            // 1 byte data 2 type
            // n Bytes data 2
            // ...

            // formats:
            // Temperature sensor:
            /*
             * IPSO: 3303
             * LPP 103
             * HEX: 67
             * Data size: 2
             * Resolution: 0.1 degres C

             * Humidity sensor
             * IPSO: 3304
             * LPP: 104
             * Hex: 68
             * Datasize: 1
             * Resolution: 0.5% unsigned

             * Barometer/pressure sensor
             * IPSO: 3315
             * LPP: 115
             * Hex: 73
             * Datasize: 2
             * Resolution 0.1hPa unsigned MSB

             * Accelerometer
             * IPSO: 3313
             * LPP: 113
             * Hex: 71
             * Data size: 6
             * Resolution: 0.001G signed MSB per axis

             * Gyrometer
             * IPSO: 3334
             * LPP: 134
             * Hex: 86
             * Data size: 6
             * Resolution: 0.01 degrees/s signed msb per axis
            */

            //temp floats
            float temp_value, humid_value, pressure_value;
            int16_t int_temp_value, int_humid_value, int_pressure_value;

            // HTS221 Humidity sensor

            ret |= (!CALL_METH(memsTemp1, GetTemperature, &temp_value, 0.0f) ? 0x0 : 0x1);
            ret |= (!CALL_METH(memsHumidity, GetHumidity, &humid_value, 0.0f) ? 0x0 : 0x2);;

            /*
            //serialize data and append to packet
            // Cayenne data: temperature; tag is 0x67, 2 bytes signed, 0.1 C/bit
            tx_data.push_back(uint8_t(1)); // data id
            tx_data.push_back(uint8_t(0x67)); // data type - temp
            int_temp_value = floor(temp_value*10 + 0.5f);
            tx_data.push_back(uint8_t( 0xFF & (int_temp_value >> 8)));
            tx_data.push_back(uint8_t(0xFF & int_temp_value));


            tx_data.push_back(uint8_t(2)); // data id
            tx_data.push_back(uint8_t(0x68)); // data type - humidity
            int_humid_value = floor(humid_value * 2.0f + 0.5f);
            tx_data.push_back(uint8_t(0xFF & int_humid_value ));
            */

            ret |= (!CALL_METH(memsPressure, GetPressure, &pressure_value, 0.0f) ? 0x0 : 0x4);;

            /*
            // pressure is reported in mbar, cayenne wants it in 0.1 hPa
            // 1mbar = 1 hPa
            int_pressure_value = floor(pressure_value * 100.0f + 0.5f);

            tx_data.push_back(uint8_t(3)); // data id
            tx_data.push_back(uint8_t(0x73)); // data type - pressure
            int_pressure_value = floor(pressure_value / 0.1f + 0.5f);
            tx_data.push_back(uint8_t(0xFF & (int_pressure_value >> 8)));
            tx_data.push_back(uint8_t(0xFF & int_pressure_value));
            */

            // Get accelerometer data
            int32_t accel_vector[3];
            // returns in mG
            memsAccel->Get_X_Axes(accel_vector);

            /*
            tx_data.push_back(uint8_t(4)); // data id
            tx_data.push_back(uint8_t(0x71)); // data type - accelerometer
            for(int i=0; i<3; i++) {
                tx_data.push_back(uint8_t(0xFF & accel_vector[i]) >> 8);
                tx_data.push_back(uint8_t(0xFF & accel_vector[i]));
            }
            */

            // Get gyro data
            int32_t gyro_vector[3];
            memsGyro->Get_G_Axes(gyro_vector);

            /*
            // gyro reports in milidegrees/sec, cayenne wants centidegrees/sec
            tx_data.push_back(uint8_t(5)); //data id
            tx_data.push_back(uint8_t(0x86)); // data type - gyrometer
            for(int i=0; i<3; i++) {
                gyro_vector[i] /= 10;
                tx_data.push_back(uint8_t(0xFF & (gyro_vector[i] >> 8)));
                tx_data.push_back(uint8_t(0xFF & gyro_vector[i]));
            }
            */

            // Get magnetometer data
            int32_t mag_vector[3];
            memsMag->Get_M_Axes(mag_vector);
            // gyro reports in milidegrees/sec, cayenne wants centidegrees/sec

           /*
            tx_data.push_back(uint8_t(5)); //data id
            tx_data.push_back(uint8_t(0x99)); // data type - mangetometer
            for(int i=0; i<3; i++) {
                mag_vector[i] /= 10;
                tx_data.push_back(uint8_t(0xFF & (mag_vector[i] >> 8)));
                tx_data.push_back(uint8_t(0xFF & mag_vector[i]));
            }
            */


            //send_data(tx_data);

            //---------------------------------------------------------------------------
            // Verbose Text Format:  Least Data Payload, Most Human Readible
            //---------------------------------------------------------------------------

            /*
            if(!bHasLCD && bHasACC)
            {

                sprintf(sensor_text, "accel-x:%d|accel-y:%d|accel-z:%d|gyro-x:%d|gyro-y:%d|gyro-z:%d|mag-x:%d|mag-y:%d|mag-x:%d|press:%s|temp:%s|humid:%s",
                    accel_vector[0],
                    accel_vector[1],
                    accel_vector[2],
                    gyro_vector[0],
                    gyro_vector[1],
                    gyro_vector[2],
                    mag_vector[0],
                    mag_vector[1],
                    mag_vector[2],
                    printDouble(buffer3, pressure_value),
                    printDouble(buffer1, temp_value),
                    printDouble(buffer2, humid_value));
            }
            else
            {
                sprintf(sensor_text, "accel-x:%d|accel-y:%d|accel-z:%d|press:%s|alti:%s|temp:%s|light:%s|moist:%s",
                    accel_data._x,
                    accel_data._y,
                    accel_data._z,
                    lora_press_string,
                    lora_alt_string,
                    lora_temp_string,
                    lora_light_string,
                    lora_current_string);

            }


             if ((mdot_ret = sendString((const std::string)sensor_text)) != mDot::MDOT_OK) {
                log_error(mdot_radio, "ERROR: Failed to Send Data", mdot_ret);
            } else {
                printf("Ok, Successfully Sent Data to Gateway...\r\n");
            }
            */

            //---------------------------------------------------------------------------
            // Byte Vector Format:  More Data Payload, Less Human Readible
            //---------------------------------------------------------------------------

            if(!bHasLCD && bHasACC)
            {

                sprintf(sensor_text, "ax:%d,ay:%d,az:%d,gx:%d,gy:%d,gz:%d,mx:%d,my:%d,mz:%d,p:%s,t:%s,h:%s",
                    accel_vector[0],
                    accel_vector[1],
                    accel_vector[2],
                    gyro_vector[0],
                    gyro_vector[1],
                    gyro_vector[2],
                    mag_vector[0],
                    mag_vector[1],
                    mag_vector[2],
                    printDouble(buffer3, pressure_value),
                    printDouble(buffer1, temp_value),
                    printDouble(buffer2, humid_value));
            }

            else
            {
                sprintf(sensor_text, "x:%d,y:%d,z:%d,p:%s,al:%s,t:%s,l:%s,c:%s",
                    accel_data._x,
                    accel_data._y,
                    accel_data._z,
                    lora_press_string,
                    lora_alt_string,
                    lora_temp_string,
                    lora_light_string,
                    lora_current_string);
            }

            if ((mdot_ret = sendString((const std::string)sensor_text)) != mDot::MDOT_OK) {
                log_error(mdot_radio, "ERROR: Failed to Send Data", mdot_ret);
            } else {
                printf("Ok, Successfully Sent Data to Gateway...\r\n");
            }

            /*
            //---------------------------------------------------------------------------
            // Binary Encoded Format:  Most Data Payload, Not Human Readible
            //---------------------------------------------------------------------------
            mdot_data.clear();
            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]);
            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]);

            if ((mdot_ret = mdot_radio->send(mdot_data)) != mDot::MDOT_OK) {
                log_error(mdot_radio, "ERROR: Failed to Send Data", mdot_ret);
            } else {
                printf("Ok, Successfully Sent Data to Gateway...\r\n");
            }

            */

            osDelay(500);
            sprintf(txtstr,"Scanning...     ");
            if( bHasLCD ) evbLCD->writeText(0,7,font_6x8,txtstr,strlen(txtstr));
            cycle_cnt = 0;
        }

        osDelay(1000);
        cycle_cnt++;

        // Put Thread to Sleep for 30 Seconds...
        // osDelay(30000);

    } while(i < 86400);

    printf("End of Data Collection Cycle (24 Hours) - Ending Application, GoodBye...\r\n");

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

/*** Interrupt Handler Top-Halves ------------------------------------------------------ ***/
/* Called in interrupt context, therefore just set a trigger variable */
static void timer_irq(void) {
    timer_irq_triggered = true;
}

/* Called in interrupt context, therefore just set a trigger variable */
static void ff_irq(void) {
    ff_irq_triggered = true;

    /* Disable IRQ until handled */
    mems_expansion_board->gyro_lsm6ds3->Disable_Free_Fall_Detection_IRQ();
}


/*** Interrupt Handler Bottom-Halves ------------------------------------------------- ***/
/* Handle Free Fall Interrupt
   (here we are in "normal" context, i.e. not in IRQ context)
*/
static void handle_ff_irq(void) {
    printf("\nFree Fall Detected!\n\n");

    /* Re-enable IRQ */
    mems_expansion_board->gyro_lsm6ds3->Enable_Free_Fall_Detection_IRQ();
}

/*===================================================================================
Send String Payload to Conduit Gateway
===================================================================================*/
int32_t sendString(const std::string text)
{
    int32_t ret;
    if (mdot_radio->getNextTxMs() != 0)
    {
        printf("Sending in %lu ms...\r\n", mdot_radio->getNextTxMs());
        return false;
    }

    printf("Sending: '%s'\r\n", text.c_str());
    std::vector<uint8_t> data(text.begin(), text.end());
    if ((ret = mdot_radio->send(data, 1)) != mDot::MDOT_OK)
    {
        log_error(mdot_radio, "ERROR: Failed to Send Data", ret);
    }

    return ret;
}

/*===================================================================================
Interrupt Service Request Handler - Sets pb1_low flag. Flag is cleared in pb1_debounce thread
===================================================================================*/
void pb1ISR(void)
{
    pb1_low = true;
}

/*===================================================================================
Pushbutton Debounce - Debounces pb1 PB1 changes SW1 State Value (Sets LED Off)
===================================================================================*/
void pb1_debounce(void const *args)
{
    printf("Thread pb1_debounce started...\r\n");

    while(true)
    {
        // if( pb1_low && (mDot08 == 0))
        if( pb1_low )
        {
            sprintf(txtstr,"PB1 Pressed...");
            if( bHasLCD ) evbLCD->writeText(0,7,font_6x8,txtstr,strlen(txtstr));
            printf("%s\r\n",txtstr);

            pb1_low = false;
        }

        Thread::wait(50);
    }
}

/*===================================================================================
Interrupt Service Request Handler - Sets pb1_low flag. Flag is cleared in pb1_debounce thread
===================================================================================*/
void pb2ISR(void)
{
    pb2_low = true;
}

/*===================================================================================
Pushbutton Debounce - Debounces pb2 PB2 changes SW2 State Value (Sets LED On)
===================================================================================*/
void pb2_debounce(void const *args)
{
    printf("Thread pb2_debounce started...\r\n");

    while(true)
    {
        // if( pb2_low && (mDot09 == 1))
        if( pb2_low )
        {
            sprintf(txtstr,"PB2 Pressed...");
            if( bHasLCD ) evbLCD->writeText(0,7,font_6x8,txtstr,strlen(txtstr));
            printf("%s \r\n",txtstr);

            pb2_low = false;
        }

        Thread::wait(50);
    }
}

/*===================================================================================
Display Value/Error String of Barometric Pressure Sensor
===================================================================================*/
bool writeValueOrError()
{
    bool res;

    if (failtime==0)
    {
        sprintf(lora_press_string, "%s", "--.--");
        sprintf(txtstr, "%s", "--.--");
        if( bHasLCD ) evbLCD->writeText(0,4,font_6x8,txtstr, strlen(txtstr));
        res=false;
    }
    else
    {
        sprintf(lora_press_string, "%ld.%02d", num_whole, num_frac);
        if( bHasLCD ) evbLCD->writeText(0,3,font_6x8,txtstr,strlen(txtstr));
        res=true;
    }

    return res;
}

/*===================================================================================
Resets Barometric Pressure Sensor
===================================================================================*/
MPL3115A2* resetBaro(const MPL3115A2* oldBaro)
{
    delete oldBaro;
    MPL3115A2* baro = new MPL3115A2(mDoti2c);
    baro->testWhoAmI();

    printf("Resetting barometer.. %x \n\r", baro->getStatus() );
    baro->setParameters(MPL3115A2::DATA_NORMAL, MPL3115A2::DM_BAROMETER, MPL3115A2::OR_16, MPL3115A2::AT_1);
    evbBaro->clearMinMaxRegs();

    return baro;
}

/*===================================================================================
Print clear text verion of mDot/EVB 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());
}