Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependencies: GeoPosition Senet_Packet lib_gps lib_mma8451q lib_mpl3115a2 lib_sx9500 lmic_MOTE_L152RC mbed-src
main.cpp
- Committer:
- shaunkrnelson
- Date:
- 2016-04-22
- Revision:
- 2:b9b2840d0d8b
- Parent:
- 1:fbf2fb2c2718
- Child:
- 3:35e8af53c6cf
File content as of revision 2:b9b2840d0d8b:
#include "mbed.h" #include "lmic.h" #include "debug.h" #include "gps.h" #include "mpl3115a2.h" #include "mma8451q.h" #include "sx9500.h" #include "GeoPosition.h" #include "senet_packet.h" // Automatically generated by the program_device script #include "Commissioning.h" #define NORAM_MOTE_DEVICE_TYPE 0x786532 #define MAJOR 1 #define MINOR 1 #define POINT 1 #define BUILD_NUMBER 0 #define DEVELOPER_ID 0 // provide application router ID (8 bytes, LSBF) void os_getArtEui (u1_t* buf) { //memcpy(buf, APPEUI, 8); LMIC_reverse_memcpy(buf, reverse_APPEUI, 8); } // provide device ID (8 bytes, LSBF) void os_getDevEui (u1_t* buf) { //memcpy(buf, DEVEUI, 8); LMIC_reverse_memcpy(buf, reverse_DEVEUI, 8); } // provide device key (16 bytes) void os_getDevKey (u1_t* buf) { memcpy(buf, DEVKEY, 16); } typedef enum { MOTE_NONE = 0, MOTE_V2, MOTE_V3 } mote_version_e; /*********************************************************************** * END LORA Device configuration ***********************************************************************/ // Following is peripheral configuration GPS gps(PB_6, PB_7, PB_11); GeoPosition lastTxPos; // I2C Bus connecting sensors I2C i2c(I2C_SDA, I2C_SCL); DigitalIn i2c_int_pin(PB_4); // Accelerometer MMA8451Q mma8451q(i2c, i2c_int_pin); // Altimeter MPL3115A2 mpl3115a2(i2c, i2c_int_pin); // Proximity sensor SX9500 sx9500(i2c, PA_9, PA_10); // Applicative transmit datarate u1_t dr = 0; // Applicative transmit power u1_t pw = 0; // LEDs #define LED_RED PB_1 #define LED_YEL PB_10 #define LED_GREEN PC_3 #define LED_USR PA_5 DigitalOut ledRed(LED_RED); DigitalOut ledYellow(LED_YEL); DigitalOut ledGreen(LED_GREEN); DigitalOut ledUsr(LED_USR); DigitalOut pc_7(PC_7); DigitalIn pc_1(PC_1); AnalogIn *bat; uint8_t ReadBatteryLevel() { uint8_t level = 0; if(bat != NULL) level = ((bat->read_u16() >> 8) + (bat->read_u16() >> 9))>>5; debug("Battery level: %d\r\n", level); return level; } // Get Mote version mote_version_e get_mote_version() { static mote_version_e mote_version = MOTE_NONE; if (mote_version == MOTE_NONE) { pc_7 = 1; char first = pc_1; pc_7 = 0; if (first && !pc_1) { printf("v2\r\n"); mote_version = MOTE_V2; bat = new AnalogIn(PA_0); } else { printf("v3\r\n"); mote_version = MOTE_V3; bat = new AnalogIn(PA_1); } } return mote_version; } void board_peripherals_init() { ledRed = 1; ledYellow = 1; ledGreen = 1; ledUsr = 0; // Start GPS gps.init(); if(get_mote_version() == MOTE_V3) gps.en_invert = false; else gps.en_invert = true; gps.enable(false); GPIOA->MODER |= 0x01415500; // unused pins as outputs: PA4, PA5, PA6, PA7, PA8, (PA11,PA12 USB) //printf("GPIOA->MODER:%08x\r\n", GPIOA->MODER); GPIOB->MODER |= 0x00000401; // unused pins as outputs: PB0(HDR_DIO1), PB5 (PB10 pulled hi by LED), PB3-T_SWO //printf("GPIOB->MODER:%08x\r\n", GPIOB->MODER); GPIOC->MODER |= 0x00000045; // unused pins as outputs: PC0(hdr_fem_csd) PC1(hdr_fem_ctx) PC3(SPI3_enable) //printf("GPIOC->MODER:%08x\r\n", GPIOC->MODER); sx9500.RegProxCtrl0.bits.sensor_en = 3; // only CS0 and CS1 connected sx9500.write(SX9500_REG_PROXCTRL0, sx9500.RegProxCtrl0.octet); // set PROXTHRESH to 80 because CS1 has 48 showing always on PROXDIFF sx9500.write(SX9500_REG_PROXCTRL6, 0x04); // Enable accelerometer orientation detection #define DEBUG #ifdef DEBUG mma8451q.verbose = true; #endif mma8451q.orient_detect(); mma8451q.set_active(true); mpl3115a2.init(); mpl3115a2.SetModeStandby(); gps.enable(true); } // application commmon bool gps_service() { gps.have_fix = false; if (gps.enabled()) { for(int32_t i = 0; i < 10; i++) { gps.service(); if (gps.have_fix == true) { return true; } else wait(.1); } } return false; } static void restore_hsi() { RCC_OscInitTypeDef osc_init; /* if HSI was shut off in deep sleep (needed for AnalogIn) */ HAL_RCC_GetOscConfig(&osc_init); if (osc_init.HSIState != RCC_HSI_ON) { // Enable the HSI (to clock the ADC) osc_init.OscillatorType = RCC_OSCILLATORTYPE_HSI; osc_init.HSIState = RCC_HSI_ON; osc_init.PLL.PLLState = RCC_PLL_NONE; HAL_RCC_OscConfig(&osc_init); } } u1_t PrepareDataFrame( SenetPacketT type ) { u1_t pktLen = 0; restore_hsi(); switch (type) { case SELF_ID_PACKET: { // Self Id packet type serialized to the LMIC frame buffer SelfIdPacket packet(LMIC.frame, MAX_LEN_FRAME); // Device Type packet.setDeviceType(NORAM_MOTE_DEVICE_TYPE, get_mote_version()); // Software version packet.setSwVersion(MAJOR, MINOR, POINT, BUILD_NUMBER, DEVELOPER_ID); // Battery level packet.setBatteryLevel(ReadBatteryLevel()); // Serialize packet to LMIC transmit buffer pktLen = packet.serialize(); break; } case GPS_PACKET: default: { // Gps packet type serialized to the LMIC frame buffer GpsPacket packet(LMIC.frame, MAX_LEN_FRAME); // set packet transmit power packet.setTxPower(pw); // Set packet coordinates uint16_t altitudeGps = atoi(gps.NmeaGpsData.NmeaAltitude); packet.setCoordinates(gps.LatitudeBinary, gps.LongitudeBinary, altitudeGps); lastTxPos.set(gps.Latitude,gps.Longitude); // Serialize packet pktLen = packet.serialize(); break; } } return pktLen; } void displayBuildInfo() { debug("Range & Site Tester\r\n"); debug("Version %02d.%02d.%02d.%04d.%02d\r\n",MAJOR,MINOR,POINT,BUILD_NUMBER,DEVELOPER_ID); debug("\r\nDevEUI: %02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x\r\n", reverse_DEVEUI[0], reverse_DEVEUI[1], reverse_DEVEUI[2], reverse_DEVEUI[3], reverse_DEVEUI[4], reverse_DEVEUI[5], reverse_DEVEUI[6], reverse_DEVEUI[7]); debug("AppEUI: %02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x\r\n", reverse_APPEUI[0], reverse_APPEUI[1], reverse_APPEUI[2], reverse_APPEUI[3], reverse_APPEUI[4], reverse_APPEUI[5], reverse_APPEUI[6], reverse_APPEUI[7]); } void startLMIC() { // reset MAC state LMIC_reset(); // start joining LMIC_startJoining(); } // Operational mode enum OperMode { // Range testing RANGE_TEST, // Site testing SITE_TEST, // Sending self-id packet SELF_ID, // No mode set IDLE }; // OS job management objects static osjob_t statusLedJob; static osjob_t txLedJob; static osjob_t orientationJob; static osjob_t sendFrameJob; // Mote orientation is checked periodically. The mote test mode // is selected by orientation. When mote is vertical it is in // site test mode and range test mode when horizontal. // Note that changing mote mode will abort previous mode testing #define ORIENTATION_CHECK_PERIOD 5 // Debounce orientation change #define ORIENTATION_DEBOUNCE_TIME 5 #define STATUS_FAST_BLINK 1 #define STATUS_SLOW_BLINK 4 #define STATUS_OK_PERIOD 5 static u4_t txCount = 0; static ostime_t lastTestDoneT = 0; static bool ackRequested = false; static OperMode operMode = IDLE; static OperMode txMode = IDLE; ////////////////////////////////////////////////// // Range test configuration ////////////////////////////////////////////////// #define RANGE_TXCYCLE_TRANSMITS 8 #define RANGE_NEXT_TEST_LONG 560 #define RANGE_NEXT_TEST_NOACK 140 #define RANGE_NEXT_TEST_DISTANCE 80 #define RANGE_NEXT_TEST_MIN_TIME 80 #define RANGE_NEXT_TX 5 #define RANGE_START_TEST_DISTANCE 30 // meters #define RANGE_NEXT_TEST_DISTANCE_T 15 ////////////////////////////////////////////////// // Site test configuration ////////////////////////////////////////////////// #define SITE_TX_PW1 14 #define SITE_TX_PW2 20 #define SITE_TX_PW3 30 #define SITE_GREEN_LED_PW SITE_TX_PW1 #define SITE_YELLOW_LED_PW SITE_TX_PW2 #define SITE_RED_LED_PW SITE_TX_PW3 static u1_t siteDrs[2] = {0,4}; static u1_t sitePws[3] = {SITE_TX_PW1, SITE_TX_PW2, SITE_TX_PW3}; static u1_t siteAck[3] = {0,0,0}; static u1_t siteDr = 0; static u1_t sitePw = 0; static u1_t siteDrPwAttempts = 0; #define SITE_DR_MAX sizeof(siteDrs) #define SITE_PW_MAX sizeof(sitePws) #define SITE_PW_DR_ATTEMPTS 3 static void txLedCb(osjob_t *job) { if(operMode == RANGE_TEST) { ledRed = 1; } else if(operMode == SITE_TEST) { switch(pw) { case SITE_GREEN_LED_PW: ledGreen = 1; break; case SITE_YELLOW_LED_PW: ledYellow = 1; break; case SITE_RED_LED_PW: ledRed = 1; break; } } } static void onSendFrame(osjob_t* j) { u1_t pktSize = 0; // Return if still transmitting. Next transmit will be scheduled by onEvent() when tx finishes if (LMIC.opmode & OP_TXRXPEND) return; if(operMode == RANGE_TEST) { // Select dr and tx power switch(txCount&0x07) { case 0: dr = 3; pw = 14; ackRequested = true; // set this on the first tx of the group ledYellow = 0; ledGreen = 0; break; case 1: dr = 0; pw = 14; break; case 2: dr = 4; pw = 20; break; case 3: dr = 3; pw = 20; break; case 4: dr = 0; pw = 20; break; case 5: dr = 4; pw = 26; break; case 6: dr = 3; pw = 30; break; case 7: dr = 0; pw = 30; break; } // Blink transmit LED ledRed = 0; os_setTimedCallback( &txLedJob, os_getTime() + 1, txLedCb); } else if (operMode == SITE_TEST) { ackRequested = true; if((siteDr < SITE_DR_MAX) && (sitePw < SITE_PW_MAX)) { dr = siteDrs[siteDr]; pw = sitePws[sitePw]; } // Restarting test else { debug("Bad dr=%d or pw=%d index\r\n", siteDr, sitePw); siteDr = 0; sitePw = 0; } // Blink tx LED switch(pw) { case SITE_GREEN_LED_PW: ledGreen = 0; break; case SITE_YELLOW_LED_PW: ledYellow = 0; break; case SITE_RED_LED_PW: ledRed = 0; break; } os_setTimedCallback( &txLedJob, os_getTime() + 1, txLedCb); } // Send GPS data for(int32_t i=0; i<3; i++) { if(gps_service() == true) { pktSize = PrepareDataFrame(GPS_PACKET); break; } } // if unable to get GPS, send self-id if(0 == pktSize) pktSize = PrepareDataFrame(SELF_ID_PACKET); // Set next dr and tx power LMIC_setDrTxpow(dr,pw); txCount++; txMode = operMode; debug("onSendFrame mode=%d: dr=%d, pw=%d, ack=%d\r\n",operMode, dr, pw, ackRequested); LMIC_setTxData2(1, LMIC.frame, pktSize, ackRequested); } static void checkIfRangeTestStart(osjob_t *job) { const ostime_t waitT = ackRequested ? RANGE_NEXT_TEST_NOACK : RANGE_NEXT_TEST_LONG; const ostime_t startT = lastTestDoneT + sec2osticks(waitT); debug("%u seconds elapsed since last test\r\n",osticks2ms(os_getTime()-lastTestDoneT)/1000); if(os_getTime() >= startT) { os_setCallback( &sendFrameJob, onSendFrame ); } else if(gps_service()) { // Do we have last tx coordinates if((lastTxPos.latitude() == 0) && (lastTxPos.longitude() == 0)) { // Set last tx position to current location lastTxPos.set(gps.Latitude,gps.Longitude); } else { GeoPosition location(gps.Latitude, gps.Longitude); double distance = location.distance(lastTxPos); if(distance >= RANGE_START_TEST_DISTANCE) { debug("position change %f >= %u\r\n",distance, RANGE_START_TEST_DISTANCE); os_setCallback( &sendFrameJob, onSendFrame ); return; } } } // Schedule next check ostime_t nextChkT = os_getTime() + sec2osticks(RANGE_NEXT_TEST_DISTANCE_T); // If distance check time less than test start, schedule distance check if(nextChkT <= startT) { os_setTimedCallback( &sendFrameJob, nextChkT, checkIfRangeTestStart); } // Else test start less than distance check time else { os_setTimedCallback( &sendFrameJob, startT, onSendFrame); } } static void preTestStart(osjob_t *job) { static u1_t wait = 0; // onEvent will initiate test on join or tx complete if(((LMIC.opmode & OP_JOINING) != 0) || ((LMIC.opmode & OP_TXRXPEND) != 0)) return; if(wait < 6) { if((wait & 1) == 0) { ledRed = 0; ledGreen = 0; ledYellow = 0; } else { ledRed = 1; ledGreen = 1; ledYellow = 1; } wait++; os_setTimedCallback( &sendFrameJob, os_getTime() + sec2osticks(1), preTestStart); } else { wait = 0; os_setCallback( &sendFrameJob, onSendFrame); } } // Protocol event handler void onEvent (ev_t ev) { debug_event(ev); switch(ev) { // network joined, session established case EV_JOINED: { displayBuildInfo(); // // Disable link check validation LMIC_setLinkCheckMode(false); // Start transmitting os_setCallback( &sendFrameJob, onSendFrame); break; } // scheduled data sent (optionally data received) case EV_TXCOMPLETE: // Check transmitted frame mode matches running mode. If not, skip results // and start sending frames for current mode if(txMode != operMode) { // Prepare to start test os_setCallback( &sendFrameJob, preTestStart); } else if(operMode == RANGE_TEST) { // Check ack received if((LMIC.txrxFlags & (TXRX_DNW1|TXRX_DNW2) )!= 0 ) { ackRequested = false; ledYellow = 1; } if(0 == (txCount & 0x07)) { // Schedule job to check for range test restart lastTestDoneT = os_getTime(); os_setTimedCallback( &sendFrameJob, lastTestDoneT + sec2osticks(RANGE_NEXT_TEST_MIN_TIME), checkIfRangeTestStart); debug("Check for test restart in %u seconds\r\n",RANGE_NEXT_TEST_MIN_TIME); // Turn off transmit cycle indicator led ledGreen = 1; } else { os_setTimedCallback( &sendFrameJob, os_getTime() + sec2osticks(RANGE_NEXT_TX), onSendFrame); } } else if(operMode == SITE_TEST) { // Ack received if(((LMIC.txrxFlags & (TXRX_DNW1|TXRX_DNW2) ) != 0 )) { siteAck[sitePw] = 1; // Turn on LED switch(pw) { case SITE_GREEN_LED_PW: ledGreen = 0; break; case SITE_YELLOW_LED_PW: ledYellow = 0; break; case SITE_RED_LED_PW: ledRed = 0; break; } // Set next tx power first transmit rate sitePw++; siteDrPwAttempts = 0; } // No ACK else if(++siteDrPwAttempts >= SITE_PW_DR_ATTEMPTS) { sitePw++; siteDrPwAttempts = 0; } if(sitePw >= SITE_PW_MAX) { sitePw=0; siteDr++; if(siteDr >= SITE_DR_MAX) siteDr=0; } if(siteAck[sitePw] == 0) { os_setTimedCallback( &sendFrameJob, os_getTime() + sec2osticks(1), onSendFrame ); } else { bool foundNACK = false; for(uint32_t i=sitePw; i < SITE_PW_MAX;i++) { if(siteAck[i] == 0) { sitePw = i; os_setTimedCallback( &sendFrameJob, os_getTime() + sec2osticks(1), onSendFrame ); foundNACK = true; break; } } if(foundNACK == false) { siteDr++; if(siteDr >= SITE_DR_MAX) siteDr=0; for(uint32_t i=0; i < sitePw;i++) { if(siteAck[i] == 0) { sitePw = i; os_setTimedCallback( &sendFrameJob, os_getTime() + sec2osticks(1), onSendFrame ); break; } } } } } break; default: break; } } static void statusLedCb(osjob_t *job) { ostime_t sleep; if(ledUsr == 1) { if((LMIC.opmode & OP_JOINING) != 0) { ledUsr = 0; sleep = STATUS_FAST_BLINK; } else if(gps.have_fix == false) { ledUsr = 0; sleep = STATUS_SLOW_BLINK; } else { sleep = STATUS_OK_PERIOD; } } else { ledUsr = 1; sleep = 1; } os_setTimedCallback( &statusLedJob, os_getTime() + sec2osticks(sleep), statusLedCb ); } static void orientationJobCb(osjob_t *job) { static MMA_orientation last; MMA_orientation current; int sleepTime = ORIENTATION_CHECK_PERIOD; mma8451q.service(); current = mma8451q.getOrientation(); if(current != last) { last = current; sleepTime = ORIENTATION_DEBOUNCE_TIME; } else { const bool currIsVertical = (current.left && (current.front||current.back) && !current.low); const OperMode mode = currIsVertical ? SITE_TEST : RANGE_TEST; if(mode != operMode) { debug("Change to %s mode\r\n", mode==SITE_TEST?"Site":"Range"); // onEvent will start test if currently joining or transmitting if(((LMIC.opmode & OP_JOINING) == 0) && ((LMIC.opmode & OP_TXRXPEND) == 0)) { if(operMode != IDLE) { os_setCallback( &sendFrameJob, preTestStart); } else { os_setCallback( &sendFrameJob, onSendFrame); } } // Set mode operMode = mode; // Reset test indicator LEDs ledRed = 1; ledGreen = 1; ledYellow = 1; // Reset test state txCount = 0; siteDr = 0; sitePw = 0; memset(siteAck, 0, sizeof(siteAck)); } } os_setTimedCallback( &orientationJob, os_getTime() + sec2osticks(sleepTime), orientationJobCb ); } // Kick off initial jobs void application_init() { // Start status led job os_setCallback(&statusLedJob, statusLedCb); // Start orientation job - Test will start once orientation is determined os_setCallback(&orientationJob, orientationJobCb); // Get current gps coordinates if(gps_service() == true) lastTxPos.set(gps.Latitude,gps.Longitude); else lastTxPos.set(0,0); } int main(void) { // initialize runtime env os_init(); // initialize debug system debug_init(); // Display build info displayBuildInfo(); // initialize peripherals board_peripherals_init(); // Application initialization application_init(); // Start protocol startLMIC(); // execute scheduled jobs and events os_runloop(); // (not reached) }