LMIC for MOTE_L152RC

Dependents:   lmic_transmit

LoRa WAN in C for NA-mote 72

Currently version 1.5


LoRaWAN network configuration for end-device

The following three pieces of information uniquely identifies end-device to network to allow over-the-air activation. These are stored in the end-device prior to join procedure.

AppEUI

Uniquely identifies application provider of end-device.

Least-significant byte first, 8 bytes. Use LMIC_reverse_memcpy() for AppEUI to keep same byte order as that on lora server.

example C code

static const u1_t APPEUI[8]  = { 0x01, 0x00, 0x01, 0x00, 0x00, 0x0C, 0x25, 0x00 };

This is copied into LMIC by os_getArtEui() callback function in application.

DevEUI

End-device ID, unique to each end-node.

Least-significant byte first, 8 bytes. Use LMIC_reverse_memcpy() for DevEUI to keep same byte order as that on lora server.

example C code

static const u1_t DEVEUI[8]  = { 0x00, 0x00, 0x00, 0x00, 0x01, 0x0C, 0x25, 0x00 }; 

This is copied into LMIC by os_getDevEui() callback function in application.

AppKey (aka DevKey)

128-bit (16byte) AES key.

example C code

static const u1_t DEVKEY[16] = { 0xe4, 0x72, 0x71, 0xc5, 0xf5, 0x30, 0xa9, 0x9f, 0xcf, 0xc4, 0x0e, 0xab, 0xea, 0xd7, 0x19, 0x42 };

This is copied into LMIC by os_getDevKey() callback function in application.

Using over-the air activation, the end-device (LMIC) performs a join procedure every time it starts for first time, or has lost session context information. When join procedure has successfully completed, the end-device will have a network session key (NwkSKey) and an application session key (AppSKey), which are used for encryption and message integrity check.


configuration with http://us01-iot.semtech.com/

  • log in to server
  • click on Applications
  • find your application and click it
  • go to configure motes
  • to create a mote, you may enter a new DevEUI
    • you may copy-paste the 16byte application key from an already existing mote, if you desire.

Information

DevEUI is entered in reverse order into C-code from that shown on server (unique device ID).

AppEUI is entered in reverse order into C-code from that shown on server.

AppEUI is equivalent to "Application"

transmit power limits

FCC Part 15 rules permit one watt of transmit power when more than 50 channels are used. When received by a 64-channel gateway, the maximum power may be used.

However, if end-device is sending to a 8-channel gateway (single SX1301), the maximum transmit power permitted is +20dBm.

To configure LMIC for use with 8-channel gateway, CHNL_HYBRID should be defined in in config.h, and should be undefined for use with 64-channel gateway.

CHNL_HYBRID125KHz500KHz
defined valuechannelschannel
00 to 764
18 to 1565
216 to 2366
324 to 3167
432 to 3968
540 to 4769
648 to 5570
756 to 6371
undef0 to 6364 to 71

TARGET_MOTE_L152RC/hal.cpp

Committer:
dudmuck
Date:
2015-06-02
Revision:
0:f2716e543d97
Child:
1:04fd63382b03

File content as of revision 0:f2716e543d97:

/* HAL for MOTE_L152RC */

#include "mbed.h"
#include "oslmic.h"
#include "debug.h"

#define RADIO_MOSI      PB_15
#define RADIO_MISO      PB_14
#define RADIO_SCLK      PB_13
#define RADIO_NSS       PB_12
#define RESET_PIN       PC_2

#define RFSW1                    PC_4 //NorAm_Mote RFSwitch_CNTR_1
#define RFSW2                    PC_13 //NorAm_Mote RFSwitch_CNTR_2

static DigitalOut nss(RADIO_NSS);
static SPI spi(RADIO_MOSI, RADIO_MISO, RADIO_SCLK); // (mosi, miso, sclk)

static DigitalInOut rst(RESET_PIN);

//DigitalOut RFSwitch_CNTR_2(PC_13);
//static DigitalOut rxtx(PC_4); // RFSwitch_CNTR_1
DigitalOut rfsw1(RFSW1);
DigitalOut rfsw2(RFSW2);

static DigitalOut hdr_fem_ctx(PC_7);

static InterruptIn dio0(PC_6);
static InterruptIn dio1(PC_10);
static InterruptIn dio2(PC_11); 

extern RTC_HandleTypeDef RtcHandle;

// HAL state
static struct {
    int irqlevel;
} HAL;

void radio_irq_handler (u1_t dio);

static void dio0Irq( void ){
    radio_irq_handler( 0 );
}
static void dio1Irq( void ){
    radio_irq_handler( 1 );
}
static void dio2Irq( void ){
    radio_irq_handler( 2 );
}

void hal_disableIRQs()
{
    __disable_irq();
    HAL.irqlevel++;
}

void hal_enableIRQs()
{
    if (--HAL.irqlevel == 0) {
        __enable_irq();
    }
}

void hal_failed ()
{
    while (1)
        asm("nop");
}

static void rtc_wkup_irq(void)
{
    HAL_RTCEx_WakeUpTimerIRQHandler(&RtcHandle);
}

void HAL_RTCEx_WakeUpTimerEventCallback(RTC_HandleTypeDef *hrtc)
{
    /* Clear Wake Up Flag */
    __HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);
}

/*void HAL_RCC_CCSCallback()
{
    for (;;) asm("nop");
}*/


volatile uint32_t /*rcc_cr_a, rcc_cr_b,*/ rcc_cr_c;
void hal_init (void)
{
    memset(&HAL, 0x00, sizeof(HAL));
    hal_disableIRQs();

#if USE_SMTC_RADIO_DRIVER

#else
    // configure input lines
    dio0.mode(PullDown);
    dio0.rise(dio0Irq);
    dio0.enable_irq();
    dio1.mode(PullDown);   
    dio1.rise(dio1Irq);
    dio1.enable_irq();
    dio2.mode(PullDown);
    dio2.rise(dio2Irq);
    dio2.enable_irq();
    // configure reset line
    rst.input();
    // configure spi
    spi.frequency(8000000);
    spi.format(8, 0);
    nss = 1;
    //RFSwitch_CNTR_2 = 1;
#endif

    set_time(0);    // initialize RTC

    /* Enable Ultra low power mode */
    HAL_PWREx_EnableUltraLowPower();

    /* Enable the fast wake up from Ultra low power mode */
    HAL_PWREx_EnableFastWakeUp();

    __HAL_RTC_WAKEUPTIMER_CLEAR_FLAG(&RtcHandle, RTC_FLAG_WUTF);

    NVIC_SetVector(RTC_WKUP_IRQn, (uint32_t)rtc_wkup_irq);
    NVIC_EnableIRQ(RTC_WKUP_IRQn);

    hdr_fem_ctx = 0;

    hal_enableIRQs();
    
    GPIOA->MODER |= 0x01415500;     // unused pins as outputs: PA4, PA5, PA6, PA7, PA8, (PA11,PA12 USB)
    GPIOB->MODER |= 0x00000401;     // unused pins as outputs: PB0(HDR_DIO1), PB5 (PB10 pulled hi by LED), PB3-T_SWO
    GPIOC->MODER |= 0x00000041;    // unused pins as outputs: PC0(HDR_FEM_CSD), PC3(SPI3_enable)     
    

}

u4_t hal_ticks ()
{
    RTC_DateTypeDef dateStruct;
    RTC_TimeTypeDef timeStruct;
    struct tm timeinfo;
    uint16_t sub_seconds;

    RtcHandle.Instance = RTC;

    // Read actual date and time
    // Warning: the time must be read first!
    HAL_RTC_GetTime(&RtcHandle, &timeStruct, FORMAT_BIN);
    HAL_RTC_GetDate(&RtcHandle, &dateStruct, FORMAT_BIN);
    sub_seconds = 16384 - timeStruct.SubSeconds;    // RTC_SSR counts down

    // Setup a tm structure based on the RTC
    timeinfo.tm_wday = dateStruct.WeekDay;
    timeinfo.tm_mon  = dateStruct.Month - 1;
    timeinfo.tm_mday = dateStruct.Date;
    timeinfo.tm_year = dateStruct.Year + 100;
    timeinfo.tm_hour = timeStruct.Hours;
    timeinfo.tm_min  = timeStruct.Minutes;
    timeinfo.tm_sec  = timeStruct.Seconds;

    // Convert to timestamp
    time_t t = mktime(&timeinfo);

    t &= 0xffff;
    // 14: SSR is driven at 16384Hz
    return (t << 14) | sub_seconds;
}

void hal_waitUntil (u4_t time)
{
    while (hal_ticks() < time)
        asm("nop");
}


volatile char deep_sleep;
/* return 1 if target time is soon, return 0 if timer was programmed */
u1_t hal_checkTimer (u4_t time)
{
    int d = time - hal_ticks();

    HAL_RTCEx_DeactivateWakeUpTimer(&RtcHandle);
    __HAL_RTC_WAKEUPTIMER_CLEAR_FLAG(&RtcHandle, RTC_FLAG_WUTF);

    if (d < 0x10000) {  // less than 4s
        deep_sleep = 0;
        if (d < 4)
            return 1;   // very soon
        if (d > ms2osticks(100)) {
            d -= 13;    // HSE_PLL startup time
            deep_sleep = 1;
        }
        // 61.035us steps
        HAL_RTCEx_SetWakeUpTimer_IT(&RtcHandle, d, RTC_WAKEUPCLOCK_RTCCLK_DIV2);
    } else if (d < 0x20000) {  // less than 8s
        d -= 6;    // HSE_PLL startup time
        deep_sleep = 1;
        // 122us steps
        HAL_RTCEx_SetWakeUpTimer_IT(&RtcHandle, d >> 1, RTC_WAKEUPCLOCK_RTCCLK_DIV4);
    } else if (d < 0x40000) {  // less than 16s
        deep_sleep = 1;
        // 244us steps
        HAL_RTCEx_SetWakeUpTimer_IT(&RtcHandle, d >> 2, RTC_WAKEUPCLOCK_RTCCLK_DIV8);
    } else if (d < 0x80000) {  // less than 32s
        deep_sleep = 1;
        // 488us steps
        HAL_RTCEx_SetWakeUpTimer_IT(&RtcHandle, d >> 3, RTC_WAKEUPCLOCK_RTCCLK_DIV16);
    } else {
        deep_sleep = 1;
        // 1s steps to 18hours
        HAL_RTCEx_SetWakeUpTimer_IT(&RtcHandle, d >> 14, RTC_WAKEUPCLOCK_CK_SPRE_16BITS);
        /* RTC_WAKEUPCLOCK_CK_SPRE_17BITS: 18h to 36h */
        /*for (;;)
            asm("nop");*/
    }
    
    return 0;
}

//#define SLEEP_DEBUG 1

void hal_sleep ()
{
#ifdef SLEEP_DEBUG
    u4_t start_tick, end_tick;
    volatile uint32_t time_asleep;
#endif /* SLEEP_DEBUG */
    
#ifdef USE_DEBUGGER
    HAL_EnableDBGStopMode();
    if (!DBGMCU->CR & DBGMCU_CR_DBG_STOP)
        for (;;) asm("nop");
#endif /* USE_DEBUGGER */

    if (deep_sleep)
        debug_done();   // wait here if debug still printing

    if (__HAL_RTC_WAKEUPTIMER_GET_FLAG(&RtcHandle, RTC_FLAG_WUTF) == 0) {
        // set gpio for sleep
#ifdef SLEEP_DEBUG
        start_tick = hal_ticks();
#endif /* SLEEP_DEBUG */
        hdr_fem_ctx = 1;

        if (deep_sleep) {
#ifndef USE_DEBUGGER
            /* PA13 to undriven JTMS/SWDIO pin (from AF0 to GPIO), and PA2 */
            GPIOA->MODER &= 0xf7ffffdf;
            GPIOB->MODER &= 0xffffdfff; // PB6 UART_TX to input
#endif            
            deepsleep();    // blocks until waking
#ifndef USE_DEBUGGER
            /* PA13 back to JTMS/SWDIO pin (from GPIO to AF0), and PA2 */
            GPIOA->MODER |= 0x08000020;
            GPIOB->MODER |= 0x00002000; // PB6 input to UART_TX
#endif            
        } else
            sleep();    // blocks until waking

        hdr_fem_ctx = 0;
#ifdef SLEEP_DEBUG
        end_tick = hal_ticks();
        time_asleep = end_tick - start_tick;
        printf("%d\r\n", time_asleep);
#endif /* SLEEP_DEBUG */
        // restore gpio from sleep
    }
}

void hal_pin_nss (u1_t val)
{
    nss = val;
}

u1_t hal_spi (u1_t out)
{
    return spi.write(out);
}

// 0=RX     1=TX
/*void hal_pin_rxtx (u1_t val)
{
    rxtx = !val;
}*/
#define OPMODE_LORA      0x80
#define OPMODE_MASK      0x07
#define OPMODE_SLEEP     0x00
#define OPMODE_STANDBY   0x01
#define OPMODE_FSTX      0x02
#define OPMODE_TX        0x03
#define OPMODE_FSRX      0x04
#define OPMODE_RX        0x05
#define OPMODE_RX_SINGLE 0x06 
#define OPMODE_CAD       0x07 
void hal_opmode(u1_t mode, u1_t pa_boost)
{
    if (mode == OPMODE_TX) {  // start of transmission
        // assuming always using PA_BOOST
        if (pa_boost) {
            rfsw2 = 0;
            rfsw1 = 1;
        } else {
            rfsw2 = 1;
            rfsw1 = 0;          
        }
    } else if (mode == OPMODE_RX || mode == OPMODE_RX_SINGLE || mode == OPMODE_CAD) { // start of reception
        rfsw2 = 1;
        rfsw1 = 1;              
    } else { // RF switch shutdown
        rfsw2 = 0;
        rfsw1 = 0;
    }
}

void hal_pin_rst (u1_t val)
{
    if (val == 0 || val == 1) { // drive pin
        rst.output();
        rst = val;
    } else { // keep pin floating
        rst.input();
    }
}