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