Merge pull request #9524 from aabadie/pr/pkg/semtech-loramac_eeprom
pkg/semtech-loramac: use internal eeprom to store lorawan config (deveui, appeui, etc)
This commit is contained in:
commit
3849c4b3b7
@ -41,6 +41,10 @@
|
||||
#include "LoRaMac.h"
|
||||
#include "region/Region.h"
|
||||
|
||||
#ifdef MODULE_PERIPH_EEPROM
|
||||
#include "periph/eeprom.h"
|
||||
#endif
|
||||
|
||||
#define ENABLE_DEBUG (0)
|
||||
#include "debug.h"
|
||||
|
||||
@ -297,6 +301,119 @@ static void mlme_indication(MlmeIndication_t *indication)
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef MODULE_PERIPH_EEPROM
|
||||
static inline void _set_uplink_counter(semtech_loramac_t *mac, uint8_t *counter)
|
||||
{
|
||||
uint32_t counter4 = ((uint32_t)counter[0] << 24 |
|
||||
(uint32_t)counter[1] << 16 |
|
||||
(uint32_t)counter[2] << 8 |
|
||||
counter[3]);
|
||||
|
||||
DEBUG("[semtech-loramac] uplink counter: %" PRIu32 " \n", counter4);
|
||||
|
||||
mutex_lock(&mac->lock);
|
||||
MibRequestConfirm_t mibReq;
|
||||
mibReq.Type = MIB_UPLINK_COUNTER;
|
||||
mibReq.Param.UpLinkCounter = counter4;
|
||||
LoRaMacMibSetRequestConfirm(&mibReq);
|
||||
mutex_unlock(&mac->lock);
|
||||
}
|
||||
|
||||
static inline void _read_loramac_config(semtech_loramac_t *mac)
|
||||
{
|
||||
DEBUG("[semtech-loramac] reading configuration from EEPROM\n");
|
||||
|
||||
uint8_t magic[SEMTECH_LORAMAC_EEPROM_MAGIC_LEN] = {0};
|
||||
size_t pos = SEMTECH_LORAMAC_EEPROM_START;
|
||||
|
||||
pos += eeprom_read(pos, magic, SEMTECH_LORAMAC_EEPROM_MAGIC_LEN);
|
||||
if (strncmp((char*)magic, "RIOT", SEMTECH_LORAMAC_EEPROM_MAGIC_LEN) != 0) {
|
||||
DEBUG("[semtech-loramac] no configuration present on EEPROM "
|
||||
"(invalid magic '%s')\n", magic);
|
||||
return;
|
||||
}
|
||||
|
||||
/* LoRaWAN EUIs, Keys and device address */
|
||||
pos += eeprom_read(pos, mac->deveui, LORAMAC_DEVEUI_LEN);
|
||||
pos += eeprom_read(pos, mac->appeui, LORAMAC_APPEUI_LEN);
|
||||
pos += eeprom_read(pos, mac->appkey, LORAMAC_APPKEY_LEN);
|
||||
pos += eeprom_read(pos, mac->appskey, LORAMAC_APPSKEY_LEN);
|
||||
pos += eeprom_read(pos, mac->nwkskey, LORAMAC_NWKSKEY_LEN);
|
||||
pos += eeprom_read(pos, mac->devaddr, LORAMAC_DEVADDR_LEN);
|
||||
|
||||
/* uplink counter, mainly used for ABP activation */
|
||||
uint8_t counter[4] = { 0 };
|
||||
pos += eeprom_read(pos, counter, 4);
|
||||
_set_uplink_counter(mac, counter);
|
||||
}
|
||||
|
||||
static inline void _save_uplink_counter(semtech_loramac_t *mac)
|
||||
{
|
||||
size_t pos = SEMTECH_LORAMAC_EEPROM_START +
|
||||
SEMTECH_LORAMAC_EEPROM_MAGIC_LEN +
|
||||
LORAMAC_DEVEUI_LEN + LORAMAC_APPEUI_LEN +
|
||||
LORAMAC_APPKEY_LEN + LORAMAC_APPSKEY_LEN +
|
||||
LORAMAC_NWKSKEY_LEN + LORAMAC_DEVADDR_LEN;
|
||||
|
||||
uint8_t counter[4];
|
||||
mutex_lock(&mac->lock);
|
||||
MibRequestConfirm_t mibReq;
|
||||
mibReq.Type = MIB_UPLINK_COUNTER;
|
||||
LoRaMacMibGetRequestConfirm(&mibReq);
|
||||
counter[0] = (uint8_t)(mibReq.Param.UpLinkCounter >> 24);
|
||||
counter[1] = (uint8_t)(mibReq.Param.UpLinkCounter >> 16);
|
||||
counter[2] = (uint8_t)(mibReq.Param.UpLinkCounter >> 8);
|
||||
counter[3] = (uint8_t)(mibReq.Param.UpLinkCounter);
|
||||
mutex_unlock(&mac->lock);
|
||||
pos += eeprom_write(pos, counter, 4);
|
||||
}
|
||||
|
||||
void semtech_loramac_save_config(semtech_loramac_t *mac)
|
||||
{
|
||||
DEBUG("[semtech-loramac] saving configuration on EEPROM\n");
|
||||
|
||||
uint8_t magic[] = SEMTECH_LORAMAC_EEPROM_MAGIC;
|
||||
size_t pos = SEMTECH_LORAMAC_EEPROM_START;
|
||||
pos += eeprom_write(pos, magic, SEMTECH_LORAMAC_EEPROM_MAGIC_LEN);
|
||||
|
||||
/* Store EUIs, Keys and device address */
|
||||
pos += eeprom_write(pos, mac->deveui, LORAMAC_DEVEUI_LEN);
|
||||
pos += eeprom_write(pos, mac->appeui, LORAMAC_APPEUI_LEN);
|
||||
pos += eeprom_write(pos, mac->appkey, LORAMAC_APPKEY_LEN);
|
||||
pos += eeprom_write(pos, mac->appskey, LORAMAC_APPSKEY_LEN);
|
||||
pos += eeprom_write(pos, mac->nwkskey, LORAMAC_NWKSKEY_LEN);
|
||||
pos += eeprom_write(pos, mac->devaddr, LORAMAC_DEVADDR_LEN);
|
||||
|
||||
/* save uplink counter, mainly used for ABP activation */
|
||||
_save_uplink_counter(mac);
|
||||
}
|
||||
|
||||
void semtech_loramac_erase_config(void)
|
||||
{
|
||||
DEBUG("[semtech-loramac] erasing configuration on EEPROM\n");
|
||||
|
||||
uint8_t magic[SEMTECH_LORAMAC_EEPROM_MAGIC_LEN];
|
||||
size_t pos = SEMTECH_LORAMAC_EEPROM_START;
|
||||
eeprom_read(pos, magic, SEMTECH_LORAMAC_EEPROM_MAGIC_LEN);
|
||||
if (strcmp((char*)magic, "RIOT") != 0) {
|
||||
DEBUG("[semtech-loramac] no configuration present on EEPROM\n");
|
||||
return;
|
||||
}
|
||||
|
||||
MibRequestConfirm_t mibReq;
|
||||
size_t uplink_counter_len = sizeof(mibReq.Param.UpLinkCounter);
|
||||
|
||||
size_t end = (pos + SEMTECH_LORAMAC_EEPROM_MAGIC_LEN +
|
||||
LORAMAC_DEVEUI_LEN + LORAMAC_APPEUI_LEN +
|
||||
LORAMAC_APPKEY_LEN + LORAMAC_APPSKEY_LEN +
|
||||
LORAMAC_NWKSKEY_LEN + LORAMAC_DEVADDR_LEN +
|
||||
uplink_counter_len);
|
||||
for (size_t p = pos; p < end; p++) {
|
||||
eeprom_write_byte(p, 0);
|
||||
}
|
||||
}
|
||||
#endif /* MODULE_PERIPH_EEPROM */
|
||||
|
||||
void _init_loramac(semtech_loramac_t *mac,
|
||||
LoRaMacPrimitives_t * primitives, LoRaMacCallback_t *callbacks)
|
||||
{
|
||||
@ -317,6 +434,9 @@ void _init_loramac(semtech_loramac_t *mac,
|
||||
semtech_loramac_set_tx_port(mac, LORAMAC_DEFAULT_TX_PORT);
|
||||
semtech_loramac_set_tx_mode(mac, LORAMAC_DEFAULT_TX_MODE);
|
||||
mac->link_chk.available = false;
|
||||
#ifdef MODULE_PERIPH_EEPROM
|
||||
_read_loramac_config(mac);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void _join_otaa(semtech_loramac_t *mac)
|
||||
@ -434,6 +554,12 @@ static void _send(semtech_loramac_t *mac, void *arg)
|
||||
msg.content.value = (uint8_t)status;
|
||||
msg_send(&msg, semtech_loramac_pid);
|
||||
}
|
||||
#ifdef MODULE_PERIPH_EEPROM
|
||||
else {
|
||||
/* save the uplink counter */
|
||||
_save_uplink_counter(mac);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void _semtech_loramac_call(semtech_loramac_func_t func, void *arg)
|
||||
|
||||
@ -444,6 +444,41 @@ void semtech_loramac_set_rx2_dr(semtech_loramac_t *mac, uint8_t dr);
|
||||
*/
|
||||
uint8_t semtech_loramac_get_rx2_dr(semtech_loramac_t *mac);
|
||||
|
||||
#ifdef MODULE_PERIPH_EEPROM
|
||||
/**
|
||||
* @brief The magic number used to identify the LoRaWAN configuration
|
||||
*/
|
||||
#ifndef SEMTECH_LORAMAC_EEPROM_MAGIC
|
||||
#define SEMTECH_LORAMAC_EEPROM_MAGIC {0x52, 0x49, 0x4F, 0x54} /* RIOT */
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief The magic number length used to identify the LoRaWAN configuration
|
||||
*/
|
||||
#ifndef SEMTECH_LORAMAC_EEPROM_MAGIC_LEN
|
||||
#define SEMTECH_LORAMAC_EEPROM_MAGIC_LEN 4
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Start position of LoRaWAN configuration stored in eeprom
|
||||
*/
|
||||
#ifndef SEMTECH_LORAMAC_EEPROM_START
|
||||
#define SEMTECH_LORAMAC_EEPROM_START (0)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Saves the current LoRaWAN configuration to the internal EEPROM
|
||||
*
|
||||
* @param[in] mac Pointer to the mac
|
||||
*/
|
||||
void semtech_loramac_save_config(semtech_loramac_t *mac);
|
||||
|
||||
/**
|
||||
* @brief Erases any stored LoRaWAN configuration from the internal EEPROM
|
||||
*/
|
||||
void semtech_loramac_erase_config(void);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -18,6 +18,8 @@ USEMODULE += shell
|
||||
USEMODULE += shell_commands
|
||||
USEMODULE += fmt
|
||||
|
||||
FEATURES_OPTIONAL += periph_eeprom
|
||||
|
||||
CFLAGS += -DREGION_$(LORA_REGION)
|
||||
CFLAGS += -DLORAMAC_ACTIVE_REGION=LORAMAC_REGION_$(LORA_REGION)
|
||||
|
||||
|
||||
@ -25,8 +25,9 @@ you use this one.
|
||||
|
||||
Once your application and device are created and registered, you'll have
|
||||
several information (provided by the LoRaWAN provider):
|
||||
* The type of join procedure: ABP (Activation by personnalization) or OTAA (Over
|
||||
The Air Activation)
|
||||
|
||||
* The type of join procedure: ABP (Activation by personnalization) or OTAA (
|
||||
Over The Air Activation)
|
||||
* The device EUI: an 8 bytes array
|
||||
* The application EUI: an 8 bytes array
|
||||
* The application key: a 16 bytes array
|
||||
@ -48,9 +49,9 @@ board.
|
||||
|
||||
Depending on the type of radio device, set the `LORA_DRIVER` variable accordingly:
|
||||
For example:
|
||||
```
|
||||
|
||||
LORA_DRIVER=sx1272 make BOARD=nucleo-f411re -C pkg/semtech-loramac flash term
|
||||
```
|
||||
|
||||
will build the application for a nucleo-f411re with an SX1272 based mbed LoRa shield.
|
||||
|
||||
The SX1276 is the default value.
|
||||
@ -58,16 +59,15 @@ The SX1276 is the default value.
|
||||
The other parameter that has to be set at build time is the geographic region:
|
||||
`EU868`, `US915`, etc. See LoRaWAN regional parameters for more information.
|
||||
|
||||
```
|
||||
LORA_REGION=US915 LORA_DRIVER=sx1272 make BOARD=nucleo-f411re -C pkg/semtech-loramac flash term
|
||||
```
|
||||
|
||||
will build the application for a nucleo-f411re with an SX1272 based mbed LoRa shield
|
||||
for US915 region.
|
||||
|
||||
The default region is `EU868`.
|
||||
|
||||
|
||||
## Using the shell
|
||||
|
||||
This application provides the `loramac` command for configuring the MAC,
|
||||
joining a network and sending/receiving data to/from a LoRaWAN network.
|
||||
`join` and `tx` subcommands are blocking until the MAC is done. Class A
|
||||
@ -76,32 +76,32 @@ is activated by default.
|
||||
### Joining with Over The Air Activation
|
||||
|
||||
* Set your device EUI, application EUI, application key:
|
||||
```
|
||||
> loramac set deveui AAAAAAAAAAAAAAAA
|
||||
> loramac set appeui BBBBBBBBBBBBBBBB
|
||||
> loramac set appkey CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
|
||||
```
|
||||
|
||||
> loramac set deveui AAAAAAAAAAAAAAAA
|
||||
> loramac set appeui BBBBBBBBBBBBBBBB
|
||||
> loramac set appkey CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
|
||||
|
||||
* Join a network using the OTAA procedure:
|
||||
```
|
||||
> loramac join otaa
|
||||
Join procedure succeeded!
|
||||
```
|
||||
|
||||
> loramac join otaa
|
||||
Join procedure succeeded!
|
||||
|
||||
### Joining with Activation By Personalization
|
||||
|
||||
OTAA is always prefered in real world scenarios.
|
||||
However, ABP can be practical for testing or workshops.
|
||||
|
||||
* Set your Device Address, Network Session Key , Application Session Key:
|
||||
```
|
||||
> loramac set devaddr AAAAAAAA
|
||||
> loramac set nwkskey BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
|
||||
> loramac set appskey CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
|
||||
```
|
||||
|
||||
> loramac set devaddr AAAAAAAA
|
||||
> loramac set nwkskey BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
|
||||
> loramac set appskey CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
|
||||
|
||||
* Join a network using the ABP procedure:
|
||||
```
|
||||
> loramac join abp
|
||||
Join procedure succeeded!
|
||||
```
|
||||
|
||||
> loramac join abp
|
||||
Join procedure succeeded!
|
||||
|
||||
Saving frame counters in flash memory is not (yet) supported.
|
||||
Before sending data, it's necessary to clear frame counters in
|
||||
Network Server! Otherwise uplink messages won't work.
|
||||
@ -111,49 +111,61 @@ If using TTN with ABP make sure to set the correct datarate for RX2.
|
||||
Otherwise confirmed and downlink messages won't work.
|
||||
The datarate for RX2 should be DR3 (SF9BW125) as seen in
|
||||
[TTN LoRaWAN Frequencies Overview](https://www.thethingsnetwork.org/docs/lorawan/frequency-plans.html):
|
||||
```
|
||||
> loramac set rx2_dr 3
|
||||
```
|
||||
|
||||
> loramac set rx2_dr 3
|
||||
|
||||
### Sending and receiving data
|
||||
|
||||
* Send confirmable data on port 2 (cnf and port are optional):
|
||||
```
|
||||
> loramac tx This\ is\ RIOT! cnf 2
|
||||
```
|
||||
|
||||
> loramac tx This\ is\ RIOT! cnf 2
|
||||
|
||||
* Send unconfirmable data on port 10:
|
||||
```
|
||||
> loramac tx This\ is\ RIOT! uncnf 10
|
||||
```
|
||||
|
||||
> loramac tx This\ is\ RIOT! uncnf 10
|
||||
|
||||
When using Class A (default mode) downlink messages will be received in
|
||||
the next uplink:
|
||||
```
|
||||
> loramac tx hello
|
||||
Data received: RIOT, port: 1
|
||||
```
|
||||
|
||||
> loramac tx hello
|
||||
Data received: RIOT, port: 1
|
||||
|
||||
### Other shell commands
|
||||
|
||||
* Save the device LoRaWAN configuration (EUIs and keys) in EEPROM (if provided
|
||||
by the microcontroller):
|
||||
|
||||
> loramac save
|
||||
|
||||
On the next device reboot, these parameters will be automatically re-read
|
||||
from the EEPROM non-volatile storage and thus you can join directly the
|
||||
network without entering them again from the command line.
|
||||
|
||||
* Remove previously stored LoRaWAN configuration:
|
||||
|
||||
> loramac erase
|
||||
|
||||
* Switch the default datarate index (from 0 to 16). 5 is for SF7, BW125:
|
||||
```
|
||||
> loramac set dr 5
|
||||
```
|
||||
|
||||
> loramac set dr 5
|
||||
|
||||
* Switch to adaptive data rate:
|
||||
```
|
||||
> loramac set adr on
|
||||
```
|
||||
|
||||
> loramac set adr on
|
||||
|
||||
* Perform a Link Check command (will be triggered in the next transmission):
|
||||
```
|
||||
> loramac link_check
|
||||
```
|
||||
|
||||
> loramac link_check
|
||||
|
||||
The list of available commands:
|
||||
```
|
||||
> help
|
||||
help
|
||||
Command Description
|
||||
---------------------------------------
|
||||
loramac control the loramac stack
|
||||
reboot Reboot the node
|
||||
```
|
||||
|
||||
> help
|
||||
help
|
||||
Command Description
|
||||
---------------------------------------
|
||||
loramac control the loramac stack
|
||||
reboot Reboot the node
|
||||
|
||||
|
||||
On the TTN web console, you can follow the activation and the data
|
||||
sent/received to/from a node.
|
||||
@ -166,28 +178,26 @@ See the
|
||||
for more information.
|
||||
|
||||
* Let's use [mosquitto](https://mosquitto.org/) clients. They can be installed
|
||||
on Ubuntu using:
|
||||
```
|
||||
sudo apt install mosquitto-clients
|
||||
```
|
||||
on Ubuntu using:
|
||||
|
||||
sudo apt install mosquitto-clients
|
||||
|
||||
* Subscribe to data raised by any node from any application:
|
||||
```
|
||||
mosquitto_sub -h eu.thethings.network -p 1883 -u <your username> -P <your password> -t '+/devices/+/up'
|
||||
```
|
||||
|
||||
mosquitto_sub -h eu.thethings.network -p 1883 -u <your username> -P <your password> -t '+/devices/+/up'
|
||||
|
||||
* Publish some data to one of the node:
|
||||
```
|
||||
mosquitto_pub -h eu.thethings.network -p 1883 -u <your username> -P <your password> -t '<application name>/devices/<device name>/down' -m '{"port":2, "payload_raw":"VGhpcyBpcyBSSU9UIQ=="}'
|
||||
```
|
||||
|
||||
mosquitto_pub -h eu.thethings.network -p 1883 -u <your username> -P <your password> -t '<application name>/devices/<device name>/down' -m '{"port":2, "payload_raw":"VGhpcyBpcyBSSU9UIQ=="}'
|
||||
|
||||
After sending some data from the node, the subscribed MQTT client will display:
|
||||
```
|
||||
{"app_id":"<your application>","dev_id":"<your node>","hardware_serial":"XXXXXXXXXXXX","port":2,"counter":7,"confirmed":true,"payload_raw":"dGVzdA==","metadata":{"time":"2017-12-14T09:47:24.84548586Z","frequency":868.1,"modulation":"LORA","data_rate":"SF12BW125","coding_rate":"4/5","gateways":[{"gtw_id":"eui-xxxxxxxx","timestamp":3910359076,"time":"2017-12-14T09:47:24.85112Z","channel":0,"rssi":-10,"snr":12.2,"rf_chain":1,"latitude":48.715027,"longitude":2.2059395,"altitude":157,"location_source":"registry"}]}}
|
||||
```
|
||||
|
||||
{"app_id":"<your application>","dev_id":"<your node>","hardware_serial":"XXXXXXXXXXXX","port":2,"counter":7,"confirmed":true,"payload_raw":"dGVzdA==","metadata": {"time":"2017-12-14T09:47:24.84548586Z","frequency":868.1,"modulation":"LORA","data_rate":"SF12BW125","coding_rate":"4/5","gateways":[{"gtw_id":"eui-xxxxxxxx","timestamp":3910359076, "time":"2017-12-14T09:47:24.85112Z","channel":0,"rssi":-10,"snr":12.2,"rf_chain":1,"latitude":48.715027,"longitude":2.2059395,"altitude":157,"location_source":"registry"}]}}
|
||||
|
||||
The payload sent is in the `payload_raw` json field and is formated in base64
|
||||
(`dGVzdA==` in this example).
|
||||
|
||||
The node will also print the data received:
|
||||
```
|
||||
|
||||
> loramac tx test
|
||||
Data received: This is RIOT!
|
||||
```
|
||||
|
||||
@ -35,7 +35,11 @@ static char print_buf[LORAMAC_APPKEY_LEN * 2 + 1];
|
||||
|
||||
static void _loramac_usage(void)
|
||||
{
|
||||
puts("Usage: loramac <get|set|join|tx|link_check>");
|
||||
puts("Usage: loramac <get|set|join|tx|link_check"
|
||||
#ifdef MODULE_PERIPH_EEPROM
|
||||
"|save|erase"
|
||||
#endif
|
||||
">");
|
||||
}
|
||||
|
||||
static void _loramac_join_usage(void)
|
||||
@ -462,6 +466,24 @@ static int _cmd_loramac(int argc, char **argv)
|
||||
semtech_loramac_request_link_check(&loramac);
|
||||
puts("Link check request scheduled");
|
||||
}
|
||||
#ifdef MODULE_PERIPH_EEPROM
|
||||
else if (strcmp(argv[1], "save") == 0) {
|
||||
if (argc > 2) {
|
||||
_loramac_usage();
|
||||
return 1;
|
||||
}
|
||||
|
||||
semtech_loramac_save_config(&loramac);
|
||||
}
|
||||
else if (strcmp(argv[1], "erase") == 0) {
|
||||
if (argc > 2) {
|
||||
_loramac_usage();
|
||||
return 1;
|
||||
}
|
||||
|
||||
semtech_loramac_erase_config();
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
_loramac_usage();
|
||||
return 1;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user