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
Revision:
0:f2716e543d97
Child:
1:04fd63382b03
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/TARGET_MOTE_L152RC/hal.cpp	Tue Jun 02 19:04:29 2015 +0000
@@ -0,0 +1,326 @@
+/* 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();
+    }
+}
+