Colibri LoraWAN

From Stm32World Wiki
Revision as of 08:24, 19 September 2023 by Niclas (talk | contribs)
Jump to navigation Jump to search


STM has a few MCUs with the Semtech LoRa radio technology built-in and they have released a "LoraWAN" Middleware package for STM32CubeMX. And it wasn't totally obvious how to get that operational.

First of all, inside CubeMX there are a lot of details in the middleware package that one needs to follow, such as setting up DMA for USART Tx, not generating MX_Init function calls and making init code not static. That was the easy bit.

We are using RAK3172 module in the Colibri MCU1 and RAK Wireless provide the low-level radio drivers. Those are not particularly well-written, and should be re-written. The problem is that LoraWAN middleware will over-write them on each code generation, and it is easy to forget that this happens and "suddenly" LoraWAN doesn't work after using CubeMX for something unrelated. Not going to create a working BSP now, but that is probably what should be done. (RAK Wireless claims that it won't work with BSP.)

The RAK3172 is built on the STM32WLE5 chip from STM.

See "Setting up RAK3172" for information to set up the STM32CubeMX part. Below are about customization for Colibri.

seNvmInit structure

We should set up the initial SecureElement into Flash, either from external tool via `st-flash` or in the C main() function

Here are the starting address for the whole structure and the offset for each member. This depends on LoraWAN version and is VERY FRAGILE over time.

              seNvmInit: 0x803F800
                 DevEUI: 0x0  [8 bytes]
                JoinEui: 0x8  [8 bytes]
            DevAddrOTAA: 0x10  [4 bytes]
             DevAddrABP: 0x14  [4 bytes]
      KeyList[00].KeyID: 0x18  [1 bytes]
   KeyList[00].KeyValue: 0x19  [16 bytes]
      KeyList[01].KeyID: 0x29  [1 bytes]
   KeyList[01].KeyValue: 0x2A  [16 bytes]
      KeyList[02].KeyID: 0x3A  [1 bytes]
   KeyList[02].KeyValue: 0x3B  [16 bytes]
      KeyList[03].KeyID: 0x4B  [1 bytes]
   KeyList[03].KeyValue: 0x4C  [16 bytes]
      KeyList[04].KeyID: 0x5C  [1 bytes]
   KeyList[04].KeyValue: 0x5D  [16 bytes]
      KeyList[05].KeyID: 0x6D  [1 bytes]
   KeyList[05].KeyValue: 0x6E  [16 bytes]
      KeyList[06].KeyID: 0x7E  [1 bytes]
   KeyList[06].KeyValue: 0x7F  [16 bytes]
      KeyList[07].KeyID: 0x8F  [1 bytes]
   KeyList[07].KeyValue: 0x90  [16 bytes]

printAddresses() code

Quick and dirty. Can be size optimized if always kept in program.

void printAddresses(const SecureElementNvmData_t *ctx)
{
    MW_LOG( TS_OFF, VLEVEL_M, "            seNvmInit: 0x%X\r\n", ctx );
    MW_LOG( TS_OFF, VLEVEL_M, "               DevEUI: 0x%X  [%d bytes]\r\n", ((uint32_t)&ctx->SeNvmDevJoinKey.DevEui) - ((uint32_t)ctx), sizeof(ctx->SeNvmDevJoinKey.DevEui) );
    MW_LOG( TS_OFF, VLEVEL_M, "              JoinEui: 0x%X  [%d bytes]\r\n", ((uint32_t)&ctx->SeNvmDevJoinKey.JoinEui) - ((uint32_t)ctx), sizeof(ctx->SeNvmDevJoinKey.JoinEui) );
    MW_LOG( TS_OFF, VLEVEL_M, "          DevAddrOTAA: 0x%X  [%d bytes]\r\n", ((uint32_t)&ctx->SeNvmDevJoinKey.DevAddrOTAA) - ((uint32_t)ctx), sizeof(ctx->SeNvmDevJoinKey.DevAddrOTAA) );
    MW_LOG( TS_OFF, VLEVEL_M, "           DevAddrABP: 0x%X  [%d bytes]\r\n", ((uint32_t)&ctx->SeNvmDevJoinKey.DevAddrABP) - ((uint32_t)ctx), sizeof(ctx->SeNvmDevJoinKey.DevAddrABP) );
    for( int i=0; i<NUM_OF_KEYS; i++ )
    {
        MW_LOG(TS_OFF, VLEVEL_M, "    KeyList[%02d].KeyID: 0x%X  [%d bytes]\r\n", i, ((uint32_t) &ctx->KeyList[i].KeyID) - ((uint32_t) ctx), sizeof(ctx->KeyList[i].KeyID));
        MW_LOG(TS_OFF, VLEVEL_M, " KeyList[%02d].KeyValue: 0x%X  [%d bytes]\r\n", i, ((uint32_t) &ctx->KeyList[i].KeyValue) - ((uint32_t) ctx), sizeof(ctx->KeyList[i].KeyValue));
    }
    MW_LOG( TS_OFF, VLEVEL_M, "                Crc32: 0x%X  [%d bytes]\r\n", ((uint32_t)&ctx->Crc32) - ((uint32_t)ctx), sizeof(ctx->Crc32));
}

Loader Script (STM32WLE5CCUX_FLASH.ld)

(Removed copyright notices to save space here)

ENTRY(Reset_Handler)

/* Highest address of the user mode stack */
_estack = ORIGIN(RAM) + LENGTH(RAM); /* end of "RAM" Ram type memory */

_Min_Heap_Size = 0x200; /* required amount of heap  */
_Min_Stack_Size = 0x400; /* required amount of stack */

/* Memories definition */
MEMORY
{
  RAM    (xrw)   : ORIGIN = 0x20000000, LENGTH = 64K
  FLASH   (rx)          : ORIGIN = 0x08000000, LENGTH = 160K
  FLASH_FORTH (rwx)     : ORIGIN = 0x08028000, LENGTH = 92K
  LORAWAN_CONTEXT (rwx) : ORIGIN = 0x0803F000, LENGTH = 2K
  LORAWAN_FACTORY (rwx) : ORIGIN = 0x0803F800, LENGTH = 2K
}

/* Sections */
SECTIONS
{
  /* The startup code into "FLASH" Rom type memory */
  .isr_vector :
  {
    . = ALIGN(4);
    KEEP(*(.isr_vector)) /* Startup code */
    . = ALIGN(4);
  } >FLASH

  /* The program code and other data into "FLASH" Rom type memory */
  .text :
  {
    . = ALIGN(4);
    *(.text)           /* .text sections (code) */
    *(.text*)          /* .text* sections (code) */
    *(.glue_7)         /* glue arm to thumb code */
    *(.glue_7t)        /* glue thumb to arm code */
    *(.eh_frame)

    KEEP (*(.init))
    KEEP (*(.fini))

    . = ALIGN(4);
    _etext = .;        /* define a global symbols at end of code */
  } >FLASH

  /* Constant data into "FLASH" Rom type memory */
  .rodata :
  {
    . = ALIGN(4);
    *(.rodata)         /* .rodata sections (constants, strings, etc.) */
    *(.rodata*)        /* .rodata* sections (constants, strings, etc.) */
    . = ALIGN(4);
  } >FLASH

  .ARM.extab   : {
    . = ALIGN(4);
    *(.ARM.extab* .gnu.linkonce.armextab.*)
    . = ALIGN(4);
  } >FLASH

  .ARM : {
    . = ALIGN(4);
    __exidx_start = .;
    *(.ARM.exidx*)
    __exidx_end = .;
    . = ALIGN(4);
  } >FLASH

  .preinit_array     :
  {
    . = ALIGN(4);
    PROVIDE_HIDDEN (__preinit_array_start = .);
    KEEP (*(.preinit_array*))
    PROVIDE_HIDDEN (__preinit_array_end = .);
    . = ALIGN(4);
  } >FLASH

  .init_array :
  {
    . = ALIGN(4);
    PROVIDE_HIDDEN (__init_array_start = .);
    KEEP (*(SORT(.init_array.*)))
    KEEP (*(.init_array*))
    PROVIDE_HIDDEN (__init_array_end = .);
    . = ALIGN(4);
  } >FLASH

  .fini_array :
  {
    . = ALIGN(4);
    PROVIDE_HIDDEN (__fini_array_start = .);
    KEEP (*(SORT(.fini_array.*)))
    KEEP (*(.fini_array*))
    PROVIDE_HIDDEN (__fini_array_end = .);
    . = ALIGN(4);
  } >FLASH

/*
.fill :
  {
    ForthFlashStart = .;
    FILL(0xFFFFFFFF);
    . = ORIGIN(FLASH_FORTH) + LENGTH(FLASH_FORTH) - 1;
    BYTE(0xFF)
    ForthFlashEnd = .;
  } >FLASH_FORTH
*/
  .context :
  {
    LORAWAN_NVM_CONTEXT = .;
  } >LORAWAN_CONTEXT

  .factorySettings :
  {
    seNvmInit = .;
  } >LORAWAN_FACTORY

  /* Used by the startup to initialize data */
  _sidata = LOADADDR(.data);

  /* Initialized data sections into "RAM" Ram type memory */
  .data :
  {
    . = ALIGN(4);
    _sdata = .;        /* create a global symbol at data start */
    *(.data)           /* .data sections */
    *(.data*)          /* .data* sections */
    *(.RamFunc)        /* .RamFunc sections */
    *(.RamFunc*)       /* .RamFunc* sections */

    . = ALIGN(4);
    _edata = .;        /* define a global symbol at data end */

  } >RAM AT> FLASH

  /* Uninitialized data section into "RAM" Ram type memory */
  . = ALIGN(4);
  .bss :
  {
    /* This is used by the startup in order to initialize the .bss section */
    _sbss = .;         /* define a global symbol at bss start */
    __bss_start__ = _sbss;
    *(.bss)
    *(.bss*)
    *(COMMON)

    . = ALIGN(4);
    _ebss = .;         /* define a global symbol at bss end */
    __bss_end__ = _ebss;
  } >RAM

 .forth_ram :
  {
    . = ALIGN(8);
    __Forth_RAM_start__ = .;
    FILL(0x0);
    . = (ORIGIN(RAM) + LENGTH(RAM) - 1) - (_Min_Heap_Size + _Min_Stack_Size);
    BYTE(0x0);
    . = ALIGN(8);
    __Forth_RAM_end__ = .;
    __Forth_RAM_size__ = __Forth_RAM_end__ - __Forth_RAM_start__;
  } >RAM

  /* User_heap_stack section, used to check that there is enough "RAM" Ram  type memory left */
  ._user_heap_stack :
  {
    . = ALIGN(8);
    PROVIDE ( end = . );
    PROVIDE ( _end = . );
    . = . + _Min_Heap_Size;
    . = . + _Min_Stack_Size;
    . = ALIGN(8);
  } >RAM

  /* Remove information from the compiler libraries */
  /DISCARD/ :
  {
    libc.a ( * )
    libm.a ( * )
    libgcc.a ( * )
  }

  .ARM.attributes 0 : { *(.ARM.attributes) }
}