Browse Source
Add LoRaWAN TheThingsNetwork OTAA example, but it doesn't work :-/
Add LoRaWAN TheThingsNetwork OTAA example, but it doesn't work :-/
On a side note, the LMIC library code is *horrible*. It completely takes over, is almost completely undocumented, and the examples don't even work......master
1 changed files with 249 additions and 0 deletions
@ -0,0 +1,249 @@ |
|||
/*******************************************************************************
|
|||
* Copyright (c) 2015 Thomas Telkamp and Matthijs Kooijman |
|||
* Copyright (c) 2018 Terry Moore, MCCI |
|||
* |
|||
* Permission is hereby granted, free of charge, to anyone |
|||
* obtaining a copy of this document and accompanying files, |
|||
* to do whatever they want with them without any restriction, |
|||
* including, but not limited to, copying, modification and redistribution. |
|||
* NO WARRANTY OF ANY KIND IS PROVIDED. |
|||
* |
|||
* This example sends a valid LoRaWAN packet with payload "Hello, |
|||
* world!", using frequency and encryption settings matching those of |
|||
* the The Things Network. |
|||
* |
|||
* This uses OTAA (Over-the-air activation), where where a DevEUI and |
|||
* application key is configured, which are used in an over-the-air |
|||
* activation procedure where a DevAddr and session keys are |
|||
* assigned/generated for use with all further communication. |
|||
* |
|||
* Note: LoRaWAN per sub-band duty-cycle limitation is enforced (1% in |
|||
* g1, 0.1% in g2), but not the TTN fair usage policy (which is probably |
|||
* violated by this sketch when left running for longer)! |
|||
|
|||
* To use this sketch, first register your application and device with |
|||
* the things network, to set or generate an AppEUI, DevEUI and AppKey. |
|||
* Multiple devices can use the same AppEUI, but each device has its own |
|||
* DevEUI and AppKey. |
|||
* |
|||
* Do not forget to define the radio type correctly in config.h. |
|||
* |
|||
*******************************************************************************/ |
|||
|
|||
#include <lmic.h>
|
|||
#include <hal/hal.h>
|
|||
#include <SPI.h>
|
|||
|
|||
#define UPLINK_CHANNEL 1
|
|||
|
|||
//
|
|||
// For normal use, we require that you edit the sketch to replace FILLMEIN
|
|||
// with values assigned by the TTN console. However, for regression tests,
|
|||
// we want to be able to compile these scripts. The regression tests define
|
|||
// COMPILE_REGRESSION_TEST, and in that case we define FILLMEIN to a non-
|
|||
// working but innocuous value.
|
|||
//
|
|||
|
|||
// This EUI must be in little-endian format, so least-significant-byte
|
|||
// first. When copying an EUI from ttnctl output, this means to reverse
|
|||
// the bytes. For TTN issued EUIs the last bytes should be 0xD5, 0xB3,
|
|||
// 0x70.
|
|||
static const u1_t PROGMEM APPEUI[8] = { 0x4D, 0x41, 0x01, 0xD0, 0x7E, 0xD5, 0xB3, 0x70 }; |
|||
void os_getArtEui (u1_t* buf) { memcpy_P(buf, APPEUI, 8);} |
|||
|
|||
// This should also be in little endian format, see above.
|
|||
static const u1_t PROGMEM DEVEUI[8] = { 0xA1, 0x4C, 0x99, 0xC5, 0x8D, 0xA1, 0x2E, 0x00 }; |
|||
void os_getDevEui (u1_t* buf) { memcpy_P(buf, DEVEUI, 8);} |
|||
|
|||
// This key should be in big endian format (or, since it is not really a
|
|||
// number but a block of memory, endianness does not really apply). In
|
|||
// practice, a key taken from ttnctl can be copied as-is.
|
|||
static const u1_t PROGMEM APPKEY[16] = { 0xCB, 0x95, 0x29, 0x6C, 0x57, 0x88, 0x24, 0xCD, 0xBA, 0x6A, 0x90, 0xA8, 0x8E, 0x99, 0x7C, 0x64 }; |
|||
void os_getDevKey (u1_t* buf) { memcpy_P(buf, APPKEY, 16);} |
|||
|
|||
static uint8_t mydata[] = "Hello, world!"; |
|||
static osjob_t sendjob; |
|||
|
|||
// Schedule TX every this many seconds (might become longer due to duty
|
|||
// cycle limitations).
|
|||
const unsigned TX_INTERVAL = 60; |
|||
|
|||
// Pin mapping
|
|||
const lmic_pinmap lmic_pins = { |
|||
.nss = 10, |
|||
.rxtx = LMIC_UNUSED_PIN, |
|||
.rst = 5, |
|||
.dio = {2, 3, 4}, |
|||
}; |
|||
|
|||
|
|||
// Disables all channels, except for the one defined above, and sets the
|
|||
// data rate (SF). This only affects uplinks; for downlinks the default
|
|||
// channels or the configuration from the OTAA Join Accept are used.
|
|||
//
|
|||
// Not LoRaWAN compliant; FOR TESTING ONLY!
|
|||
//
|
|||
void forceTxSingleChannelDr(int channel, int data_rate) { |
|||
for(int i=0; i<9; i++) { // For EU; for US use i<71
|
|||
if(i != channel) { |
|||
LMIC_disableChannel(i); |
|||
} |
|||
} |
|||
// Set data rate (SF) and transmit power for uplink
|
|||
LMIC_setDrTxpow(data_rate, 14); |
|||
} |
|||
|
|||
void onEvent (ev_t ev) { |
|||
Serial.print(os_getTime()); |
|||
Serial.print(": "); |
|||
switch(ev) { |
|||
case EV_SCAN_TIMEOUT: |
|||
Serial.println(F("EV_SCAN_TIMEOUT")); |
|||
break; |
|||
case EV_BEACON_FOUND: |
|||
Serial.println(F("EV_BEACON_FOUND")); |
|||
break; |
|||
case EV_BEACON_MISSED: |
|||
Serial.println(F("EV_BEACON_MISSED")); |
|||
break; |
|||
case EV_BEACON_TRACKED: |
|||
Serial.println(F("EV_BEACON_TRACKED")); |
|||
break; |
|||
case EV_JOINING: |
|||
Serial.println(F("EV_JOINING")); |
|||
break; |
|||
case EV_JOINED: |
|||
Serial.println(F("EV_JOINED")); |
|||
{ |
|||
u4_t netid = 0; |
|||
devaddr_t devaddr = 0; |
|||
u1_t nwkKey[16]; |
|||
u1_t artKey[16]; |
|||
LMIC_getSessionKeys(&netid, &devaddr, nwkKey, artKey); |
|||
Serial.print("netid: "); |
|||
Serial.println(netid, DEC); |
|||
Serial.print("devaddr: "); |
|||
Serial.println(devaddr, HEX); |
|||
Serial.print("artKey: "); |
|||
for (int i=0; i<sizeof(artKey); ++i) { |
|||
Serial.print(artKey[i], HEX); |
|||
} |
|||
Serial.println(""); |
|||
Serial.print("nwkKey: "); |
|||
for (int i=0; i<sizeof(nwkKey); ++i) { |
|||
Serial.print(nwkKey[i], HEX); |
|||
} |
|||
Serial.println(""); |
|||
} |
|||
forceTxSingleChannelDr(UPLINK_CHANNEL, /*data_rate*/DR_SF7); |
|||
// Disable link check validation (automatically enabled
|
|||
// during join, but because slow data rates change max TX
|
|||
// size, we don't use it in this example.
|
|||
LMIC_setLinkCheckMode(0); |
|||
break; |
|||
/*
|
|||
|| This event is defined but not used in the code. No |
|||
|| point in wasting codespace on it. |
|||
|| |
|||
|| case EV_RFU1: |
|||
|| Serial.println(F("EV_RFU1")); |
|||
|| break; |
|||
*/ |
|||
case EV_JOIN_FAILED: |
|||
Serial.println(F("EV_JOIN_FAILED")); |
|||
break; |
|||
case EV_REJOIN_FAILED: |
|||
Serial.println(F("EV_REJOIN_FAILED")); |
|||
break; |
|||
case EV_TXCOMPLETE: |
|||
Serial.println(F("EV_TXCOMPLETE (includes waiting for RX windows)")); |
|||
if (LMIC.txrxFlags & TXRX_ACK) |
|||
Serial.println(F("Received ack")); |
|||
if (LMIC.dataLen) { |
|||
Serial.print(F("Received ")); |
|||
Serial.print(LMIC.dataLen); |
|||
Serial.println(F(" bytes of payload")); |
|||
} |
|||
// Schedule next transmission
|
|||
os_setTimedCallback(&sendjob, os_getTime()+sec2osticks(TX_INTERVAL), do_send); |
|||
break; |
|||
case EV_LOST_TSYNC: |
|||
Serial.println(F("EV_LOST_TSYNC")); |
|||
break; |
|||
case EV_RESET: |
|||
Serial.println(F("EV_RESET")); |
|||
break; |
|||
case EV_RXCOMPLETE: |
|||
// data received in ping slot
|
|||
Serial.println(F("EV_RXCOMPLETE")); |
|||
break; |
|||
case EV_LINK_DEAD: |
|||
Serial.println(F("EV_LINK_DEAD")); |
|||
break; |
|||
case EV_LINK_ALIVE: |
|||
Serial.println(F("EV_LINK_ALIVE")); |
|||
break; |
|||
/*
|
|||
|| This event is defined but not used in the code. No |
|||
|| point in wasting codespace on it. |
|||
|| |
|||
|| case EV_SCAN_FOUND: |
|||
|| Serial.println(F("EV_SCAN_FOUND")); |
|||
|| break; |
|||
*/ |
|||
case EV_TXSTART: |
|||
Serial.println(F("EV_TXSTART")); |
|||
break; |
|||
default: |
|||
Serial.print(F("Unknown event: ")); |
|||
Serial.println((unsigned) ev); |
|||
break; |
|||
} |
|||
} |
|||
|
|||
void do_send(osjob_t* j){ |
|||
// Check if there is not a current TX/RX job running
|
|||
if (LMIC.opmode & OP_TXRXPEND) { |
|||
Serial.println(F("OP_TXRXPEND, not sending")); |
|||
} else { |
|||
// Prepare upstream data transmission at the next possible time.
|
|||
LMIC_setTxData2(1, mydata, sizeof(mydata)-1, 0); |
|||
Serial.println(F("Packet queued")); |
|||
} |
|||
// Next TX is scheduled after TX_COMPLETE event.
|
|||
} |
|||
|
|||
void setup() { |
|||
Serial.begin(9600); |
|||
Serial.println(F("Starting")); |
|||
|
|||
#ifdef VCC_ENABLE
|
|||
// For Pinoccio Scout boards
|
|||
pinMode(VCC_ENABLE, OUTPUT); |
|||
digitalWrite(VCC_ENABLE, HIGH); |
|||
delay(1000); |
|||
#endif
|
|||
|
|||
// LMIC init
|
|||
os_init(); |
|||
// Reset the MAC state. Session and pending data transfers will be discarded.
|
|||
LMIC_reset(); |
|||
|
|||
// Make LMIC initialize the default channels, choose a channel, and
|
|||
// schedule the OTAA join
|
|||
LMIC_startJoining(); |
|||
|
|||
// LMIC will already have decided to send on one of the 3 default
|
|||
// channels; ensure it uses the one we want
|
|||
LMIC.txChnl = UPLINK_CHANNEL; |
|||
|
|||
// ...and make sure we see the EV_JOINING event being logged
|
|||
os_runloop_once(); |
|||
|
|||
// Start job (sending automatically starts OTAA too)
|
|||
do_send(&sendjob); |
|||
} |
|||
|
|||
void loop() { |
|||
os_runloop_once(); |
|||
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue