LMIC transmit example for NAmote-72 with GPS

Dependencies:   lib_gps lib_mpl3115a2 lmic_MOTE_L152RC mbed

Deprecated and replaced with

Import programLoRaWAN-NAMote72-Application-Demo

Demonstration of Class-A LoRaWAN device using NAMote-72

LoRaWAN Network Configuration (in Config.h)

For Over the Air (OTA) activation of an End-Device, it must be configured with the following parameters:

  1. DEVEUI (8 Bytes) : Fist 3 Bytes is the Organizationally Unique Identifier (OUI) followed by 5 bytes of unique ID.
  2. APPEUI (8 Bytes)
  3. APPKey (or DEVKEY) (16 Bytes) The parameters can be entered as shown in the figure below /media/uploads/ubhat/nwkconfig.png

LoRaWAN Transmission Configuration (in Config.h)

  • Inter-Frame Delay : One can change the delay between each frame transmission using MS_DELAY_NEXT_TX It is advisable that MS_DELAY_NEXT_TX is greater than or equal to 3sec. /media/uploads/ubhat/txtiming.png
  • Payload Length : The lenght of the payload (in bytes) to be transmitted can be configured using PAYLOAD_LENGTH /media/uploads/ubhat/payload.png
  • Data Rate : The data rate can be configured as per LoRaWAN specification using the paramter FIXED_DR /media/uploads/ubhat/datarate.png
  • Channel Configuration : In the case where the End-Device is transmitting over frequencies corresponding to a block of 8 channels, the block can be specified using CHNL_HYBRID. The value 0 corresponds to Block A, 1 corresponds to Block B and so on. /media/uploads/ubhat/hybrid.png When the End-Device transmits over more than 50 channels, CHNL_HYBRID needs to be commented out /media/uploads/ubhat/frequencyhopping.png
  • Transmit Power : The power of the data to be transmitted can be configured using the parameter FIXED_TX_POWER. The maximum transmit power allowed is as per FCC regulation, depending upon the mode of transmission. /media/uploads/ubhat/txpower.png

Serial Terminal Display

By using a serial port connection using applications such as teraterm or putty, one can view the status of the End-Device. Once the End-Device Joins the network, transmission parameters such as payload data, transmit power, battery level etc. are displayed on the terminal.

/media/uploads/ubhat/teraterm.png

Default Application Payload

This application defaults to sending uplink data to logical port 5. The application payload consists of: /media/uploads/jknapp_smtc/payload.png

Sample Application Payload Calculation for Longitude/Latitude

Payload => 00 19 F6 352BBA A94C20 FFFF

Temperature Calculation

19H => 2510

Temp = 25/2 = 12.5 oC

Battery Level

FFH => 100 %

F6H => 96.5 %

Longitude Calculation

longitude = A94C20H => 1109507210

longitudinal coordinate = -360 + (longitude10 x 180/(223))

longitudinal coordinate = -121.93

Latitude Calculation

latitude = 352BBAH = 348460210

latitude coordinate = (latitude10 x 90/(223-1))

latitude coordinate = 37.39

main.cpp

Committer:
ubhat
Date:
2016-03-02
Revision:
8:26682f47bff1
Parent:
6:0b2b2d196ddd

File content as of revision 8:26682f47bff1:

/*******************************************************************************
 * Copyright (c) 2014-2015 IBM Corporation.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    IBM Zurich Research Lab - initial API, implementation and documentation
 *******************************************************************************/

#include "lmic.h"
#include "debug.h"
#include <mbed.h>
#include "gps.h"
#include "mpl3115a2.h"

#define FHS_MAX_POW     30      /* Frequency Hopping Max Power */
#define DMTS_MAX_POW    26      /* Digital Modulation Max Power */
#define HS_MAX_POW      20      /* Hybrid System Max Power */

bool joined;
typedef enum {
    MOTE_NONE = 0,
    MOTE_V2,
    MOTE_V3
} mote_version_e;
mote_version_e mote_version = MOTE_NONE;

DigitalOut gps_en(PB_11);   // dis/enables voltage divider on PA_1
DigitalOut pc_7(PC_7);
DigitalIn pc_1(PC_1);
AnalogIn *bat;
#define LOW_BAT_THRESHOLD   3.45
#define AIN_VREF        3.3     // stm32 internal refernce
#define AIN_VBAT_DIV    2       // resistor divider

#define LED_ON          0
#define LED_OFF         1
static DigitalOut led_red(PB_1);
static DigitalOut led_yellow(PB_10);
//static DigitalOut led_green(PC_3);
static DigitalOut led_usr(PA_5);


GPS gps(PB_6, PB_7, PB_11);
I2C i2c(I2C_SDA, I2C_SCL);
DigitalIn i2c_int_pin(PB_4);
MPL3115A2 mpl3115a2(i2c, i2c_int_pin);
volatile bool AppLedStateOn = false;


//////////////////////////////////////////////////
// CONFIGURATION (FOR APPLICATION CALLBACKS BELOW)
//////////////////////////////////////////////////

static const u1_t app_eui[ 8] = { APPEUI };
//static       u1_t dev_eui[ 8] = { OUI, 0x00, 0xff, 0xff, 0xff, 0xff };
static       u1_t dev_eui[ 8] = { DEVEUI };
static const u1_t dev_key[16] = { DEVKEY };


//////////////////////////////////////////////////
// APPLICATION CALLBACKS
//////////////////////////////////////////////////

// provide application router ID (8 bytes, LSBF)
void os_getArtEui (u1_t* buf) {
    LMIC_reverse_memcpy(buf, app_eui, sizeof(app_eui));
}

// provide device ID (8 bytes, LSBF)
void os_getDevEui (u1_t* buf) {
    LMIC_reverse_memcpy(buf, dev_eui, sizeof(dev_eui));
}

// provide device key (16 bytes)
void os_getDevKey (u1_t* buf) {
    memcpy(buf, dev_key, sizeof(dev_key));
}


//////////////////////////////////////////////////
// MAIN - INITIALIZATION AND STARTUP
//////////////////////////////////////////////////
void get_mote_version()
{
    char first;
      
    pc_7 = 1;
    first = pc_1;
    pc_7 = 0;
    if (first && !pc_1) {
        gps.en_invert = 1;
        mote_version = MOTE_V2;
        bat = new AnalogIn(PA_0);       
    } else {
        mote_version = MOTE_V3;
        bat = new AnalogIn(PA_1);
        //gps.en_invert = 0;        
        gps_en = 0;
    }
}

//////////////////////////////////////////////////
// Display network parameters (DevEUI, AppEUI etc)
//////////////////////////////////////////////////
void DisplayNetworkParam()
{
    int i;

    if(mote_version == MOTE_V2)
        printf("\r\nNA Mote Ver. 2\r\n");
    else
        if(mote_version == MOTE_V3)
            printf("\r\nNA Mote Ver. 3\r\n");
        else
            printf("\r\nNA Mote Ver. NOT DEFINED\r\n");
        
    printf("DEVEUI:");
    for (i = 0; i < sizeof(dev_eui); i++) {
        printf("%02x", dev_eui[i]);
        if (i < sizeof(dev_eui)-1)
            printf("-");
    }
    printf("\r\n");
    
    printf("APPEUI:");
    for (i = 0; i < sizeof(app_eui); i++) {
        printf("%02x", app_eui[i]);
        if (i < sizeof(app_eui)-1)
            printf("-");        
    }
    printf("\r\n");
        
    printf("DEVKEY:");
    for (i = 0; i < sizeof(dev_key); i++) {
        printf("%02x ", dev_key[i]);
    }
    printf("\r\n"); 
}

void DisplayData(uint8_t *pData, int len)
{
    int i;

    for(i = 0; i < len; i++)
        printf("%x ", pData[i]);
    printf("\r\n");
}

osjob_t nJoinedJob;

static void on_not_joined(osjob_t* j)
{   
    if (joined)
        return;

    DisplayNetworkParam();
    
    os_setTimedCallback( &nJoinedJob, os_getTime() + ms2osticks(7000), on_not_joined );
}


// initial job
static void initfunc (osjob_t* j)
{
    u4_t* id;
    u4_t* u4_ptr;
    
    u4_ptr = (u4_t*)&dev_eui[4];   // [4] to [7]

    if(*u4_ptr == 0)
    {
        id = (u4_t*)0x1ff800d0; // STM32L1xx Cat3
        *u4_ptr = *id;
        id = (u4_t*)0x1ff800d4;
        *u4_ptr ^= *id;
        id = (u4_t*)0x1ff800e4;
        *u4_ptr ^= *id;    
    }
    
    get_mote_version(); 
    gps.init();
        
    gps.enable(1);
    mpl3115a2.init();       
    
    // reset MAC state
    LMIC_reset();
    
    os_setTimedCallback( &nJoinedJob, os_getTime() + ms2osticks(3000), on_not_joined );
    joined = false;
    // start joining
    LMIC_startJoining();
    
    led_red = LED_ON;   // indicate joining
    led_yellow = LED_OFF;
    //led_green = LED_OFF;
    // init done - onEvent() callback will be invoked...
}


// application entry point
int main () {
    osjob_t initjob;

    // initialize runtime env
    os_init();
    // initialize debug library
    debug_init();
    // setup initial job
    os_setCallback(&initjob, initfunc);
    // execute scheduled jobs and events
    os_runloop();
    // (not reached)
    return 0;
}

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

osjob_t sendFrameJob;
osjob_t indicateJob;

static void tx_ind_cb(osjob_t* j)
{
    led_red = LED_OFF;    
}

static void onSendFrame (osjob_t* j)
{
    uint16_t altitudeGps;
    
    if (LMIC.opmode & OP_TXRXPEND) {
        return;
    }
    
#ifdef FIXED_DR
    LMIC.datarate = FIXED_DR;
#endif
    
#ifdef FIXED_TX_POWER
    LMIC.txpow = FIXED_TX_POWER;
#endif
    
    restore_hsi();
    
    if(LMIC.datarate == DR_SF8C)
        LMIC.txpow_limit = DMTS_MAX_POW;
    else
#ifdef CHNL_HYBRID
        LMIC.txpow_limit = HS_MAX_POW;
#else
        LMIC.txpow_limit = FHS_MAX_POW;
#endif

    if( LMIC.txpow > LMIC.txpow_limit)
        LMIC.txpow = LMIC.txpow_limit;
    
    float volts = bat->read()*AIN_VREF*AIN_VBAT_DIV;
    if (volts < LOW_BAT_THRESHOLD) {
        if (LMIC.txpow_limit > 20)
            LMIC.txpow_limit = 20;
        if (LMIC.txpow > 20)
            LMIC.txpow = 20;
    }
    
    ///////////////////////////////////////////////////////////////////    
    gps.service();
    mpl3115a2.ReadTemperature();    

    uint8_t tmpData[PAYLOAD_LENGTH] = {0};

    tmpData[0] = 15-(LMIC.txpow>>1);
    tmpData[0] <<= 4;
#ifdef CHNL_HYBRID
    tmpData[0] |= 0x04;  // 8ch: set bit2
#else
    tmpData[0] |= 0x08;  // 64ch: set bit3
#endif

    tmpData[0] |= AppLedStateOn & 1; // (bit 0 == 1) => LED on
    tmpData[1] = (int)mpl3115a2.Temperature; // Signed degrees Celcius in half degree units. So,  +/-63 C
    tmpData[2] = (bat->read_u16() >> 8) + (bat->read_u16() >> 9) ; // per LoRaMAC spec; 0=Charging; 1...254 = level, 255 = N/A
    
    tmpData[3] = ( gps.LatitudeBinary >> 16 ) & 0xFF;
    tmpData[4] = ( gps.LatitudeBinary >> 8 ) & 0xFF;
    tmpData[5] = gps.LatitudeBinary & 0xFF;
    tmpData[6] = ( gps.LongitudeBinary >> 16 ) & 0xFF;
    tmpData[7] = ( gps.LongitudeBinary >> 8 ) & 0xFF;
    tmpData[8] = gps.LongitudeBinary & 0xFF;

    altitudeGps = atoi(gps.NmeaGpsData.NmeaAltitude);
    tmpData[9] = ( altitudeGps >> 8 ) & 0xFF;
    tmpData[10] = altitudeGps & 0xFF;
    if (PAYLOAD_LENGTH > 11) {
        for (int i = 11; i < PAYLOAD_LENGTH; i++)
            tmpData[i] = i - 10;
    }

    for(int i = 0; i < PAYLOAD_LENGTH; i++)
        LMIC.frame[i] = tmpData[i];
        
    // port, buffer, buffer length, need_ack
    LMIC_setTxData2(5, LMIC.frame, PAYLOAD_LENGTH, 0);

    led_red = LED_ON;

    // Display Info

    DisplayNetworkParam();

    printf("Seq# %d\r\n",LMIC.seqnoUp-1);

    printf("TX Data: ");
    DisplayData(tmpData, PAYLOAD_LENGTH);

    printf("TX Power: %d dBm\r\n",LMIC.txpow);

    printf("Battery: %f Volts\r\n",volts);

    os_setTimedCallback( &indicateJob, os_getTime() + ms2osticks(30), tx_ind_cb );  
    
}


//////////////////////////////////////////////////
// LMIC EVENT CALLBACK
//////////////////////////////////////////////////

void onEvent (ev_t ev)
{
    debug_event(ev);
    
    switch(ev) {
        case EV_JOINED:
            // network joined, session established
            debug_val("netid = ", LMIC.netid);
            joined = true;
            led_red = LED_OFF;   // indicate joined
            goto tx;
        case EV_TXCOMPLETE:
            // scheduled data sent (optionally data received)
            if(LMIC.dataLen) { // data received in rx slot after tx
                led_yellow = led_yellow ? 0 : 1;
                if (LMIC.dataLen == 1) { // set LED state if exactly one byte is received
                    led_usr = LMIC.frame[LMIC.dataBeg] & 0x01; 
                }
                debug_buf(LMIC.frame+LMIC.dataBeg, LMIC.dataLen);
            }
tx:
            os_setTimedCallback( &sendFrameJob, os_getTime() + ms2osticks(MS_DELAY_NEXT_TX), onSendFrame );           
            break;
        default:
            led_red = LED_ON;   // indicate not joined
            break;
    } // ..switch(ev)
}