Commit 047e8bb5 authored by Guillaume Gonnet's avatar Guillaume Gonnet
Browse files

Add TTN provider and fix some minor bugs.

parent 7d067d80
# Control commands
TODO.
# Payload format
| Parameter | Value range | Number of bits |
|:---------------------:|:-----------------:|:--------------:|
| Spreading Factor (SF) | 0 - 6 | 3 |
| Coding rate | 0 - 1 | 1 |
| Power | 0 - 10 | 4 |
| Temperature | 128 °C by 0.25 °C | 9 |
| Latitude | | 2 * 8 + 7 = 23 |
| Longitude | | 3 * 8 = 24 |
| Altitude | 0 - 65535 | 2 * 8 = 16 |
This total length of the payload (without LoRaWAN header) is 10 bytes.
## Spreading Factor (SF)
This parameter is the index from the datarate used to send the message (taken
from EU868 regional specifications).
There are 7 values: `0` for `DR_0`, `1` for `DR_1` ... and `6` for `DR_6`. This
parameter can be coded on **3 bits**.
## Coding rate
There are two values available: `0` for a coding rate of `4/5` and `1` for a
coding rate of `4/8`.
## Transmission power
This parameter is the index in the array below (values in dBm).
> `2, 5, 8, 11, 14, 15, 16, 17, 18, 19`
This array has 10 elements so this parameter is coded on **4 bits**.
## Temperature
TODO.
/*
This file decodes a payload message sent on port > 32.
Usage: node decode-payload.js <payload>
Example: node decode-payload.js \
4b20ef23b1c3a9393f034f6527ca71f2
The format of the payload is specified in `payload.md` file.
Copyright (C) 2019, ENSIMAG students
This project is under the MIT license
*/
require; // For NodeJS typings.
// Parse command line.
const args = process.argv.splice(2);
if (args.length != 1) {
console.error("Usage: node decode-payload.js <payload>");
process.exit(1);
}
// Validate input payload.
const payload = Buffer.from(args[0], "hex");
if (payload.length != 10) {
console.error("Input payload must have exaclty 10 bytes.");
process.exit(1);
}
// Value used for the conversion of the position from DMS to decimal.
const MaxNorthPosition = 8388607; // 2^23 - 1
const MaxSouthPosition = 8388608; // -2^23
const MaxEastPosition = 8388607; // 2^23 - 1
const MaxWestPosition = 8388608; // -2^23
// Extract LoRa settings.
let byte = payload.readUInt8(0);
const DataRate = (byte >> 5) & 0b111;
const CodingRate = (byte >> 4) & 0b1;
let Power = byte & 0b1111;
Power = [2, 5, 8, 11, 14, 15, 16, 17, 18, 19][Power];
// Extract temperature.
let Temperature = payload.readUInt16LE(1) & 0x1FF;
Temperature = Temperature * 0.25 - 55;
#!/bin/python3
"""
This script create N devices in TTN.
Copyright (C) 2019, ENSIMAG students
This project is under the MIT license
"""
import urllib.request as request
import json
import time
import binascii
import os
# App ID and API key (https://console.thethingsnetwork.org/applications).
APP_ID = "imag-ballon"
ACCESS_KEY = "ttn-account-v2.jnnrB5dSmog4EtcH0WADpJYu7zt4L3yVGqdumgS6fvE"
# The number of devices to create and prefix.
DEV_START_AT = 0
DEV_COUNT = 256
DEV_PREFIX = "ballon-"
# NwkSKey and AppSKey.
APPEUI = "AF73C914CB963247"
DEVEUI = "AF73C914CB9632"
NWKSKEY = "FC68F3CEB202BEAE8EB322C5E16283FC"
APPSKEY = "2B3FC3ABD194E4CE1161A8E1E0FD59E1"
# Base URL of TTN HTTP API.
BASE_API_URL = "http://eu.thethings.network:8084/"
# Useful API endpoints.
EP_DEVICES = "applications/%s/devices" % APP_ID
def api_call(method, endpoint, data):
"Make a call to the TTN HTTP API."
headers = { "Authorization": "Key " + ACCESS_KEY }
url = BASE_API_URL + endpoint
if not isinstance(data, (str, bytes)):
data = json.dumps(data)
if isinstance(data, str):
data = data.encode("utf-8")
req = request.Request(url, data, headers=headers, method=method)
with request.urlopen(req) as f:
return json.load(f)
def create_device(name, devEUI, devAddr):
"Create a new device."
device = {
"app_id": APP_ID,
"dev_id": name,
"lorawan_device": {
"activation_constraints": "abp",
"app_eui": APPEUI,
"dev_eui": devEUI,
"app_id": APP_ID,
"dev_id": name,
"dev_addr": devAddr,
"f_cnt_down": 0,
"f_cnt_up": 0,
"last_seen": 0,
"nwk_s_key": NWKSKEY,
"app_s_key": APPSKEY,
"disable_f_cnt_check": True,
"uses32_bit_f_cnt": True
} }
print("Creating device: %s" % name)
return api_call("POST", EP_DEVICES, device)
# Create N device to TTN.
for i in range(DEV_START_AT, DEV_START_AT + DEV_COUNT):
name = DEV_PREFIX + ("%d" % i)
deveui = DEVEUI + ("%02X" % i)
devaddr = "260192" + ("%02X" % i)
create_device(name, deveui, devaddr)
time.sleep(0.1)
......@@ -20,7 +20,7 @@ uint8_t bm_dr[] = { DR_0, DR_1, DR_2, DR_3, DR_4, DR_5 };
uint8_t bm_cr[] = { CR_4_5, CR_4_8 };
// Providers to test.
provider_t *bm_pvd[] = { &pvd_orange_bm, &pvd_ttn_bm };
const provider_t *bm_pvd[] = { &pvd_orange_bm, &pvd_ttn_bm };
// Count the number of elements in an array.
......
......@@ -27,7 +27,7 @@ extern uint8_t bm_dr[];
extern uint8_t bm_cr[];
// Providers to test.
extern provider_t *bm_pvd[];
extern const provider_t *bm_pvd[];
/**
......
......@@ -13,7 +13,7 @@ This project is under the MIT license
// All command entries.
static app_cmd_ent_t cmd_entries[] = {
static const app_cmd_ent_t cmd_entries[] = {
{ "d", cmd_debug },
{ "r", cmd_reset },
};
......
......@@ -25,6 +25,7 @@ static void send_message(void)
mcps.power = 19;
mcps.buffer = (uint8_t *)"Ballon!";
mcps.len = 7;
mcps.code_rate = 4;
puts("Sending message ...");
pvd_send(&pvd_orange_cmd, &mcps);
......
......@@ -17,49 +17,50 @@ This project is under the MIT license
// Configure LoRaMAC driver with provider information.
void pvd_otaa_enable(provider_t *pvd)
void pvd_otaa_enable(const provider_t *pvd)
{
pvd_otta_t *otaa = pvd->data;
const pvd_otta_t *otaa = pvd->data;
uint8_t data[32];
// Replay OTAA with join message from EEPROM.
eeprom_read(otaa->eeprom_addr, data, 32);
lorariot_replay_otaa(otaa->appkey, data);
// TODO: read frame counter from EEPROM.
// Read frame counter from EEPROM.
uint32_t counter;
eeprom_read(otaa->eeprom_addr + 32, (uint8_t *)&counter, 4);
lorariot_set_fcnt(counter);
}
// Save frame counter from last uplink message.
void pvd_otaa_update(provider_t *pvd)
void pvd_otaa_update(const provider_t *pvd)
{
// TODO: save frame counter.
const pvd_otta_t *otaa = pvd->data;
(void)pvd;
uint32_t counter = lorariot_get_fcnt();
eeprom_write(otaa->eeprom_addr + 32, (uint8_t *)&counter, 4);
}
// Force (re)joining the network.
void pvd_otaa_reset(provider_t *pvd)
void pvd_otaa_reset(const provider_t *pvd)
{
// Set OTAA information.
pvd_otta_t *otaa = pvd->data;
const pvd_otta_t *otaa = pvd->data;
lorariot_otaa_t otaa_req = { otaa->deveui, otaa->appeui, otaa->appkey, DR_0 };
// If join failed, we can't continue running the application because all
// messages will be lost.
if (loraraiot_otaa(&otaa_req) != LORARIOT_SUCCESS) {
if (lorariot_otaa(&otaa_req) != LORARIOT_SUCCESS) {
DEBUG("[otaa] join error for devui [%02X..]\n", *otaa->deveui);
PANIC("Can't join network using OTAA");
}
// Save OTAA join response data.
uint8_t *data = loraraiot_retrieve_otaa();
uint8_t *data = lorariot_retrieve_otaa();
eeprom_write(otaa->eeprom_addr, data, 32);
// TODO: save frame counter.
// Resend the join message to port 10.
lorariot_mcps_t mcps = LORARIOT_CONFIRMED(DR_0);
mcps.buffer = data;
......@@ -69,4 +70,8 @@ void pvd_otaa_reset(provider_t *pvd)
// Wait the message to be fully sent.
lorariot_waitsend();
// Save frame counter.
uint32_t counter = lorariot_get_fcnt();
eeprom_write(otaa->eeprom_addr + 32, (uint8_t *)&counter, 4);
}
......@@ -17,33 +17,45 @@ This project is under the MIT license
#include <debug.h>
// Orange provider for benchmark.
static pvd_otta_t orange_bm_otta = {
// Orange provider (for benchmark).
static const pvd_otta_t orange_bm_otta = {
EEPROM_ORANGE_BM,
ORANGE_BM_DEVEUI,
ORANGE_BM_APPEUI,
ORANGE_BM_APPKEY
};
provider_t pvd_orange_bm = PVD_IMPLEM_OTAA(orange_bm_otta);
const provider_t pvd_orange_bm = PVD_IMPLEM_OTAA(orange_bm_otta);
// Orange provider for commands.
static pvd_otta_t orange_cmd_otta = {
// Orange provider (for commands).
static const pvd_otta_t orange_cmd_otta = {
EEPROM_ORANGE_CMD,
ORANGE_CMD_DEVEUI,
ORANGE_CMD_APPEUI,
ORANGE_CMD_APPKEY
};
provider_t pvd_orange_cmd = PVD_IMPLEM_OTAA(orange_cmd_otta);
const provider_t pvd_orange_cmd = PVD_IMPLEM_OTAA(orange_cmd_otta);
// TTN provider (for benchmark).
static const pvd_ttn_t ttn_bm_ttn = {
EEPROM_TTN_BM, TTN_BM_COUNT,
TTN_BM_DEVADDR,
TTN_BM_NWKSKEY,
TTN_BM_APPSKEY
};
const provider_t pvd_ttn_bm = PVD_IMPLEM_TTN(ttn_bm_ttn);
// All defined providers.
static provider_t *pvd_all[] = {
//&pvd_orange_bm,
&pvd_orange_cmd
static const provider_t *pvd_all[] = {
&pvd_orange_bm,
//&pvd_orange_cmd,
&pvd_ttn_bm
};
// Number of defined providers.
......
......@@ -14,12 +14,12 @@ This project is under the MIT license
struct provider_t;
typedef void (*pvd_enable_t)(struct provider_t *);
typedef void (*pvd_update_t)(struct provider_t *);
typedef void (*pvd_reset_t)(struct provider_t *);
typedef void (*pvd_enable_t)(const struct provider_t *);
typedef void (*pvd_update_t)(const struct provider_t *);
typedef void (*pvd_reset_t)(const struct provider_t *);
typedef struct provider_t {
void *data; // Provider specific data.
const void *data; // Provider specific data.
pvd_enable_t enable; // Called when the provider is selected.
pvd_update_t update; // Called after a message is sent.
pvd_reset_t reset; // Called to reset the provider.
......@@ -35,22 +35,41 @@ typedef struct pvd_otta_t {
} pvd_otta_t;
// OTAA provider implementation.
void pvd_otaa_enable(provider_t *pvd);
void pvd_otaa_update(provider_t *pvd);
void pvd_otaa_reset(provider_t *pvd);
void pvd_otaa_enable(const provider_t *pvd);
void pvd_otaa_update(const provider_t *pvd);
void pvd_otaa_reset(const provider_t *pvd);
// Feed a provider using default OTAA implementation.
#define PVD_IMPLEM_OTAA(otaa_info) \
{ &(otaa_info), pvd_otaa_enable, pvd_otaa_update, pvd_otaa_reset }
// TTN information.
typedef struct pvd_ttn_t {
uint16_t eeprom_addr;
uint16_t count;
uint32_t devaddr;
uint8_t nwkskey[32];
uint8_t appskey[32];
} pvd_ttn_t;
// TTN provider implementation.
void pvd_ttn_enable(const provider_t *pvd);
void pvd_ttn_update(const provider_t *pvd);
void pvd_ttn_reset(const provider_t *pvd);
// Feed a provider using default TTN implementation.
#define PVD_IMPLEM_TTN(ttn_info) \
{ &(ttn_info), pvd_ttn_enable, pvd_ttn_update, pvd_ttn_reset }
// Orange providers.
extern provider_t pvd_orange_bm;
extern provider_t pvd_orange_cmd;
extern const provider_t pvd_orange_bm;
extern const provider_t pvd_orange_cmd;
// TTN provider.
extern provider_t pvd_ttn_bm;
extern const provider_t pvd_ttn_bm;
/**
......@@ -58,13 +77,13 @@ extern provider_t pvd_ttn_bm;
* @param pvd the provider to use.
* @param mcps MCPS information (data, DR, ...).
*/
void pvd_send(provider_t *pvd, lorariot_mcps_t *mcps);
void pvd_send(const provider_t *pvd, const lorariot_mcps_t *mcps);
/**
* @brief Reset a specific provider.
* @param pvd the provider to reset.
*/
void pvd_reset(provider_t *pvd);
void pvd_reset(const provider_t *pvd);
/**
* @brief Reset all providers.
......
......@@ -13,7 +13,7 @@ This project is under the MIT license
// Current loaded provider.
static provider_t *current_pvd = NULL;
static const provider_t *current_pvd = NULL;
// Mutex on sending.
static mutex_t send_mutex = MUTEX_INIT;
......@@ -24,7 +24,7 @@ static mutex_t send_mutex = MUTEX_INIT;
* @param pvd the provider to use.
* @param mcps MCPS information (data, DR, ...).
*/
void pvd_send(provider_t *pvd, lorariot_mcps_t *mcps)
void pvd_send(const provider_t *pvd, const lorariot_mcps_t *mcps)
{
// Wait for the current LoRaMAC-node operation to be completed.
mutex_lock(&send_mutex);
......@@ -43,6 +43,9 @@ void pvd_send(provider_t *pvd, lorariot_mcps_t *mcps)
// CONFIRMED messages) event given by LoRaMAC-node.
lorariot_waitsend();
// Update the provider.
pvd->update(pvd);
// Unlock one other waiting thread.
mutex_unlock(&send_mutex);
}
......@@ -52,7 +55,7 @@ void pvd_send(provider_t *pvd, lorariot_mcps_t *mcps)
* @brief Reset a specific provider.
* @param pvd the provider to reset.
*/
void pvd_reset(provider_t *pvd)
void pvd_reset(const provider_t *pvd)
{
// Wait for the current LoRaMAC-node operation to be completed.
mutex_lock(&send_mutex);
......
/*
ABP (Activation By Personalization) abstract provider.
Copyright (C) 2019, ENSIMAG students
This project is under the MIT license
*/
#include "providers.h"
#include "app.h"
#include <periph/eeprom.h>
#define ENABLE_DEBUG (1)
#include <debug.h>
// Configure LoRaMAC driver with provider information.
void pvd_ttn_enable(const provider_t *pvd)
{
const pvd_ttn_t *ttn = pvd->data;
// Load current index.
uint32_t index;
eeprom_read(ttn->eeprom_addr, (uint8_t *)&index, 4);
// Set LoRaWAN keys.
lorariot_abp_t abp = { ttn->nwkskey, ttn->appskey, (ttn->devaddr | index) };
lorariot_abp(&abp);
}
// Save frame counter from last uplink message.
void pvd_ttn_update(const provider_t *pvd)
{
const pvd_ttn_t *ttn = pvd->data;
// Update the current index.
uint32_t index;
eeprom_read(ttn->eeprom_addr, (uint8_t *)&index, 4);
index = (index + 1) % ttn->count;
eeprom_write(ttn->eeprom_addr, (uint8_t *)&index, 4);
// Change device address.
lorariot_set_devaddr(ttn->devaddr | index);
}
// Reset DevAddr index.
void pvd_ttn_reset(const provider_t *pvd)
{
const pvd_ttn_t *ttn = pvd->data;
uint32_t index = 0;
eeprom_write(ttn->eeprom_addr, (uint8_t *)&index, 4);
}
......@@ -18,3 +18,6 @@ OTAA join cache takes 32 bytes + 4 bytes for the frame counter.
// EEPROM space for join cache (36 bytes).
#define EEPROM_ORANGE_BM 0
#define EEPROM_ORANGE_CMD 36
// EEPROM space for TTN.
#define EEPROM_TTN_BM 72
......@@ -19,3 +19,11 @@ This project is under the MIT license
#define ORANGE_CMD_DEVEUI { 0x19, 0xC0, 0x81, 0xB5, 0x70, 0x9C, 0x54, 0x55 }
#define ORANGE_CMD_APPEUI { 0x91, 0x5C, 0x55, 0x55, 0xC7, 0xCD, 0x88, 0xBE }
#define ORANGE_CMD_APPKEY { 0x08, 0xAC, 0xA9, 0x4C, 0xA5, 0xFD, 0x3E, 0xF1, 0x11, 0x9D, 0x7B, 0x73, 0x5E, 0xE7, 0x3D, 0x5A }
// TTN provider for benchmark.
#define TTN_BM_DEVADDR 0x26019200
#define TTN_BM_COUNT 256
#define TTN_BM_NWKSKEY { 0xFC, 0x68, 0xF3, 0xCE, 0xB2, 0x02, 0xBE, 0xAE, 0x8E, 0xB3, 0x22, 0xC5, 0xE1, 0x62, 0x83, 0xFC }
#define TTN_BM_APPSKEY { 0x2B, 0x3F, 0xC3, 0xAB, 0xD1, 0x94, 0xE4, 0xCE, 0x11, 0x61, 0xA8, 0xE1, 0xE0, 0xFD, 0x59, 0xE1 }
......@@ -42,7 +42,7 @@
* \param [IN] data - Join response data.
* \param [IN] size - Size of join data response.
*/
void LoRaMacApplyJoin(uint8_t *data, uint8_t size)
void LoRaMacApplyJoin(const uint8_t *data, uint8_t size)
{
LoRaMacJoinComputeSKeys(LoRaMacAppKey, data, LoRaMacDevNonce, LoRaMacNwkSKey, LoRaMacAppSKey);
......@@ -71,7 +71,7 @@ void LoRaMacApplyJoin(uint8_t *data, uint8_t size)
// Apply CF list.
ApplyCFListParams_t applyCFList;
applyCFList.Payload = &data[12];
applyCFList.Payload = (uint8_t *)&data[12];
applyCFList.Size = size - 12; // Size of the regular payload is 12.
RegionApplyCFList(LoRaMacRegion, &applyCFList);
......
......@@ -94,13 +94,18 @@ extern uint8_t LoRaMacAppSKey[16];
extern uint8_t *LoRaMacAppKey;
/*!
* \brief Resets MAC specific parameters to default
*/
void ResetMacParameters(void);
/*!
* \brief Apply join response to current LoRaMac parameters.
*
* \param [IN] data - Join response data.
* \param [IN] size - Size of join data response.
*/
void LoRaMacApplyJoin(uint8_t *data, uint8_t size);
void LoRaMacApplyJoin(const uint8_t *data, uint8_t size);
#endif // __LORAMAC_INT_H__
\ No newline at end of file
......@@ -621,7 +621,7 @@ LoRaMacStatus_t SetTxContinuousWave1( uint16_t timeout, uint32_t frequency, uint
/*!
* \brief Resets MAC specific parameters to default
*/
static void ResetMacParameters( void );
void ResetMacParameters( void );
/*!
* \brief Resets MAC specific parameters to default
......@@ -2042,7 +2042,7 @@ static void CalculateBackOff( uint8_t channel )
AggregatedTimeOff = ( TxTimeOnAir * AggregatedDCycle - TxTimeOnAir );
}
static void ResetMacParameters( void )
void ResetMacParameters( void )
{
IsLoRaMacNetworkJoined = false;
......
......@@ -18,7 +18,6 @@ LGPLv2.1 license.
static void _otaa(lorariot_otaa_t *otaa)
{
DEBUG("[lorariot] making OTAA activation\n");
LoRaMacTestRxWindowsOn(true);