diff --git a/Adafruit-Feather-M0-RFM95/longfi-us915/README.md b/Adafruit-Feather-M0-RFM95/longfi-us915/README.md index bbf3918..3423904 100644 --- a/Adafruit-Feather-M0-RFM95/longfi-us915/README.md +++ b/Adafruit-Feather-M0-RFM95/longfi-us915/README.md @@ -1,16 +1,31 @@ # Adafruit Feather M0 RFM95 -## Installing the MCCI LMIC Library - -From the Arduino IDE, open the Library Manager (Sketch->Include Library->Manage Libraries). In the search box, type MCCI and select the MCCI LoRaWAN LMIC library should be the first result. - -## Board Support - -### Feather M0 RFM95 - Adafruit - -1. Add Adafruit package index, instructions [here](https://learn.adafruit.com/adafruit-feather-m0-radio-with-lora-radio-module/setup). -2. Install both Arduino and Adafruit SAMD board support, instructions [here](https://learn.adafruit.com/adafruit-feather-m0-radio-with-lora-radio-module/using-with-arduino-ide#install-samd-support-6-5). -3. (Windows Only) Install drivers, instructions [here](https://learn.adafruit.com/adafruit-feather-m0-radio-with-lora-radio-module/using-with-arduino-ide#install-drivers-windows-7-and-8-only-6-11) - [Adafruit Feather M0 with RFM95 Product Page](https://www.adafruit.com/product/3178) -[Adafruit Feather M0 with RFM95 Datasheets & Files](https://learn.adafruit.com/adafruit-feather-m0-radio-with-lora-radio-module/downloads) \ No newline at end of file +[Adafruit Feather M0 with RFM95 Datasheets & Files](https://learn.adafruit.com/adafruit-feather-m0-radio-with-lora-radio-module/downloads). + +## Required Arduino Libraries + +### Important +Make sure you do not have other LMIC arduino libraries installed, otherwise you will experience +build errors. You can find your Arduino libraries for your operating system below, simply delete the directory if you would like to remove a library. + +linux: /home/{user}/Arduino/libraries +windows: Documents/Arduino/libraries +mac os: Documents/Arduino/libraries + +From the Arduino IDE, open the Library Manager (Sketch->Include Library->Manage Libraries). In the search box, type the library name below and install the latest version. + +[IBM LMIC framework](https://github.com/matthijskooijman/arduino-lmic) + +### Required Configuration +This library requires that a config file be setup properly. After you have installed the `IBM LMIC framework` library, navigate to it's directory on your operating system found below. Next, replace the `config.h` file in this directory with this [config.h](https://github.com/helium/longfi-arduino/blob/master/Sparkfun-Pro-RF/longfi-us915/config.h) file. + +linux: /home/{user}/Arduino/libraries/IBM_LMIC_framework/src/lmic +windows: Documents/Arduino/libraries/IBM_LMIC_framework/src/lmic +mac os: Documents/Arduino/libraries/IBM_LMIC_framework/src/lmic + +## Required Arduino Board Support + +1. Install Arduino SAMD Support, instructions [here](https://learn.adafruit.com/adafruit-feather-m0-radio-with-lora-radio-module/using-with-arduino-ide#install-samd-support-6-5). +2. Install Adafruit SAMD Support, instructions [here](https://learn.adafruit.com/adafruit-feather-m0-radio-with-lora-radio-module/using-with-arduino-ide#install-adafruit-samd-6-7). +3. (Windows 7 & 8 Only) Install drivers, instructions [here](https://learn.adafruit.com/adafruit-feather-m0-radio-with-lora-radio-module/using-with-arduino-ide#install-drivers-windows-7-and-8-only-6-11) \ No newline at end of file diff --git a/Adafruit-Feather-M0-RFM95/longfi-us915/config.h b/Adafruit-Feather-M0-RFM95/longfi-us915/config.h new file mode 100644 index 0000000..62da07f --- /dev/null +++ b/Adafruit-Feather-M0-RFM95/longfi-us915/config.h @@ -0,0 +1,83 @@ +#ifndef _lmic_config_h_ +#define _lmic_config_h_ + +// In the original LMIC code, these config values were defined on the +// gcc commandline. Since Arduino does not allow easily modifying the +// compiler commandline, use this file instead. + +// #define CFG_eu868 1 +#define CFG_us915 1 +// This is the SX1272/SX1273 radio, which is also used on the HopeRF +// RFM92 boards. +//#define CFG_sx1272_radio 1 +// This is the SX1276/SX1277/SX1278/SX1279 radio, which is also used on +// the HopeRF RFM95 boards. +#define CFG_sx1276_radio 1 + +// 16 μs per tick +// LMIC requires ticks to be 15.5μs - 100 μs long +#define US_PER_OSTICK_EXPONENT 4 +#define US_PER_OSTICK (1 << US_PER_OSTICK_EXPONENT) +#define OSTICKS_PER_SEC (1000000 / US_PER_OSTICK) + +// Set this to 1 to enable some basic debug output (using printf) about +// RF settings used during transmission and reception. Set to 2 to +// enable more verbose output. Make sure that printf is actually +// configured (e.g. on AVR it is not by default), otherwise using it can +// cause crashing. +#define LMIC_DEBUG_LEVEL 0 + +// Enable this to allow using printf() to print to the given serial port +// (or any other Print object). This can be easy for debugging. The +// current implementation only works on AVR, though. +//#define LMIC_PRINTF_TO Serial + +// Any runtime assertion failures are printed to this serial port (or +// any other Print object). If this is unset, any failures just silently +// halt execution. +#define LMIC_FAILURE_TO Serial + +// Uncomment this to disable all code related to joining +//#define DISABLE_JOIN +// Uncomment this to disable all code related to ping +//#define DISABLE_PING +// Uncomment this to disable all code related to beacon tracking. +// Requires ping to be disabled too +//#define DISABLE_BEACONS + +// Uncomment these to disable the corresponding MAC commands. +// Class A +//#define DISABLE_MCMD_DCAP_REQ // duty cycle cap +//#define DISABLE_MCMD_DN2P_SET // 2nd DN window param +//#define DISABLE_MCMD_SNCH_REQ // set new channel +// Class B +//#define DISABLE_MCMD_PING_SET // set ping freq, automatically disabled by DISABLE_PING +//#define DISABLE_MCMD_BCNI_ANS // next beacon start, automatical disabled by DISABLE_BEACON + +// In LoRaWAN, a gateway applies I/Q inversion on TX, and nodes do the +// same on RX. This ensures that gateways can talk to nodes and vice +// versa, but gateways will not hear other gateways and nodes will not +// hear other nodes. By uncommenting this macro, this inversion is +// disabled and this node can hear other nodes. If two nodes both have +// this macro set, they can talk to each other (but they can no longer +// hear gateways). This should probably only be used when debugging +// and/or when talking to the radio directly (e.g. like in the "raw" +// example). +//#define DISABLE_INVERT_IQ_ON_RX + +// This allows choosing between multiple included AES implementations. +// Make sure exactly one of these is uncommented. +// +// This selects the original AES implementation included LMIC. This +// implementation is optimized for speed on 32-bit processors using +// fairly big lookup tables, but it takes up big amounts of flash on the +// AVR architecture. +// #define USE_ORIGINAL_AES +// +// This selects the AES implementation written by Ideetroon for their +// own LoRaWAN library. It also uses lookup tables, but smaller +// byte-oriented ones, making it use a lot less flash space (but it is +// also about twice as slow as the original). +#define USE_IDEETRON_AES + +#endif // _lmic_config_h_ diff --git a/Adafruit-Feather-M0-RFM95/longfi-us915/longfi-us915.ino b/Adafruit-Feather-M0-RFM95/longfi-us915/longfi-us915.ino index 4074b96..b6cc54e 100644 --- a/Adafruit-Feather-M0-RFM95/longfi-us915/longfi-us915.ino +++ b/Adafruit-Feather-M0-RFM95/longfi-us915/longfi-us915.ino @@ -1,6 +1,5 @@ /******************************************************************************* * 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, @@ -10,311 +9,177 @@ * * This example sends a valid LoRaWAN packet with payload "Hello, * world!", using frequency and encryption settings matching those of - * the The Things Network. It's pre-configured for the Adafruit - * Feather M0 LoRa. + * 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. * *******************************************************************************/ -/******************************************************************************* - * - * For Helium developers, follow the Arduino Quickstart guide: - * https://developer.helium.com/device/arduino-quickstart - * TLDR: register your device on the console: - * https://console.helium.com/devices - * - * The App EUI (as lsb) and App Key (as msb) get inserted below. - * - *******************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include #include +#include +#include -// This is the "App EUI" in Helium. Make sure it is little-endian (lsb). -static const u1_t PROGMEM APPEUI[8] = {FILL_ME_IN}; -void os_getArtEui(u1_t *buf) { memcpy_P(buf, APPEUI, 8); } +// 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]={ FILL_ME_IN }; +void os_getArtEui (u1_t* buf) { memcpy_P(buf, APPEUI, 8);} -// This should also be in little endian format -// These are user configurable values and Helium console permits anything -static const u1_t PROGMEM DEVEUI[8] = {FILL_ME_IN}; -void os_getDevEui(u1_t *buf) { memcpy_P(buf, DEVEUI, 8); } +// This should also be in little endian format, see above. +static const u1_t PROGMEM DEVEUI[8]={ FILL_ME_IN }; +void os_getDevEui (u1_t* buf) { memcpy_P(buf, DEVEUI, 8);} -// This is the "App Key" in Helium. It is big-endian (msb). -static const u1_t PROGMEM APPKEY[16] = {FILL_ME_IN}; -void os_getDevKey(u1_t *buf) { memcpy_P(buf, APPKEY, 16); } +// 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. +// The key shown here is the semtech default key. +static const u1_t PROGMEM APPKEY[16] = { FILL_ME_IN }; +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; +const unsigned TX_INTERVAL = 20; // Pin mapping -// -// Adafruit BSPs are not consistent -- m0 express defs ARDUINO_SAMD_FEATHER_M0, -// m0 defs ADAFRUIT_FEATHER_M0 -// -#if defined(ARDUINO_SAMD_FEATHER_M0) || defined(ADAFRUIT_FEATHER_M0) -// Pin mapping for Adafruit Feather M0 LoRa, etc. const lmic_pinmap lmic_pins = { .nss = 8, .rxtx = LMIC_UNUSED_PIN, .rst = 4, - .dio = {3, 6, LMIC_UNUSED_PIN}, - .rxtx_rx_active = 0, - .rssi_cal = 8, // LBT cal for the Adafruit Feather M0 LoRa, in dB - .spi_freq = 8000000, + .dio = {3, 6, 2}, }; -#elif defined(ARDUINO_AVR_FEATHER32U4) -// Pin mapping for Adafruit Feather 32u4 LoRa, etc. -// Just like Feather M0 LoRa, but uses SPI at 1MHz; and that's only -// because MCCI doesn't have a test board; probably higher frequencies -// will work. -const lmic_pinmap lmic_pins = { - .nss = 8, - .rxtx = LMIC_UNUSED_PIN, - .rst = 4, - .dio = {7, 6, LMIC_UNUSED_PIN}, - .rxtx_rx_active = 0, - .rssi_cal = 8, // LBT cal for the Adafruit Feather 32U4 LoRa, in dB - .spi_freq = 1000000, -}; -#elif defined(ARDUINO_CATENA_4551) -// Pin mapping for Murata module / Catena 4551 -const lmic_pinmap lmic_pins = { - .nss = 7, - .rxtx = 29, - .rst = 8, - .dio = - { - 25, // DIO0 (IRQ) is D25 - 26, // DIO1 is D26 - 27, // DIO2 is D27 - }, - .rxtx_rx_active = 1, - .rssi_cal = 10, - .spi_freq = 8000000 // 8MHz -}; -#elif defined(MCCI_CATENA_4610) -#include "arduino_lmic_hal_boards.h" -const lmic_pinmap lmic_pins = *Arduino_LMIC::GetPinmap_Catena4610(); -#elif defined(ARDUINO_DISCO_L072CZ_LRWAN1) -const lmic_pinmap lmic_pins = *Arduino_LMIC::GetPinmap_Disco_L072cz_Lrwan1(); -#else -#error "Unknown target" -#endif -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_JOIN_TXCOMPLETE: - Serial.println(F("EV_JOIN_TXCOMPLETE")); - 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 (size_t i = 0; i < sizeof(artKey); ++i) { - if (i != 0) - Serial.print("-"); - Serial.print(artKey[i], HEX); - } - Serial.println(""); - Serial.print("nwkKey: "); - for (size_t i = 0; i < sizeof(nwkKey); ++i) { - if (i != 0) - Serial.print("-"); - Serial.print(nwkKey[i], HEX); - } - Serial.println(""); +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")); + + // Disable link check validation (automatically enabled + // during join, but not supported by TTN at this time). + LMIC_setLinkCheckMode(0); + break; + 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; + 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.println(F("Received ")); + Serial.println(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; + default: + Serial.println(F("Unknown event")); + break; } - // 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; - 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.println(F("Received ")); - Serial.println(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 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() { - delay(5000); - while (!Serial) - ; - Serial.begin(9600); - Serial.println(F("Starting")); + Serial.begin(9600); + Serial.println(F("Starting")); -#if defined(ARDUINO_DISCO_L072CZ_LRWAN1) - SPI.setMOSI(RADIO_MOSI_PORT); - SPI.setMISO(RADIO_MISO_PORT); - SPI.setSCLK(RADIO_SCLK_PORT); - SPI.setSSEL(RADIO_NSS_PORT); -// SPI.begin(); -#endif + #ifdef VCC_ENABLE + // For Pinoccio Scout boards + pinMode(VCC_ENABLE, OUTPUT); + digitalWrite(VCC_ENABLE, HIGH); + delay(1000); + #endif -#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(); + + // allow much more clock error than the X/1000 default. See: + // https://github.com/mcci-catena/arduino-lorawan/issues/74#issuecomment-462171974 + // https://github.com/mcci-catena/arduino-lmic/commit/42da75b56#diff-16d75524a9920f5d043fe731a27cf85aL633 + // the X/1000 means an error rate of 0.1%; the above issue discusses using + // values up to 10%. so, values from 10 (10% error, the most lax) to 1000 + // (0.1% error, the most strict) can be used. + LMIC_setClockError(1 * MAX_CLOCK_ERROR / 40); - // LMIC init - os_init(); - // Reset the MAC state. Session and pending data transfers will be discarded. - LMIC_reset(); + LMIC_selectSubBand(6); + LMIC_setLinkCheckMode(0); + LMIC_setDrTxpow(DR_SF7, 14); - // allow much more clock error than the X/1000 default. See: - // https://github.com/mcci-catena/arduino-lorawan/issues/74#issuecomment-462171974 - // https://github.com/mcci-catena/arduino-lmic/commit/42da75b56#diff-16d75524a9920f5d043fe731a27cf85aL633 - // the X/1000 means an error rate of 0.1%; the above issue discusses using - // values up to 10%. so, values from 10 (10% error, the most lax) to 1000 - // (0.1% error, the most strict) can be used. - LMIC_setClockError(1 * MAX_CLOCK_ERROR / 40); - - LMIC_setLinkCheckMode(0); - LMIC_setDrTxpow(DR_SF8, 20); - LMIC_selectSubBand(6); - - // Start job (sending automatically starts OTAA too) - do_send(&sendjob); + // Start job (sending automatically starts OTAA too) + do_send(&sendjob); } -void loop() { os_runloop_once(); } - -namespace Arduino_LMIC { - -class HalConfiguration_Disco_L072cz_Lrwan1_t : public HalConfiguration_t { -public: - enum DIGITAL_PINS : uint8_t { - PIN_SX1276_NSS = 37, - PIN_SX1276_NRESET = 33, - PIN_SX1276_DIO0 = 38, - PIN_SX1276_DIO1 = 39, - PIN_SX1276_DIO2 = 40, - PIN_SX1276_RXTX = 21, - }; - - virtual bool queryUsingTcxo(void) override { return false; }; -}; -// save some typing by bringing the pin numbers into scope -static HalConfiguration_Disco_L072cz_Lrwan1_t myConfig; - -static const HalPinmap_t myPinmap = { - .nss = HalConfiguration_Disco_L072cz_Lrwan1_t::PIN_SX1276_NSS, - .rxtx = HalConfiguration_Disco_L072cz_Lrwan1_t::PIN_SX1276_RXTX, - .rst = HalConfiguration_Disco_L072cz_Lrwan1_t::PIN_SX1276_NRESET, - - .dio = - { - HalConfiguration_Disco_L072cz_Lrwan1_t::PIN_SX1276_DIO0, - HalConfiguration_Disco_L072cz_Lrwan1_t::PIN_SX1276_DIO1, - HalConfiguration_Disco_L072cz_Lrwan1_t::PIN_SX1276_DIO2, - }, - .rxtx_rx_active = 1, - .rssi_cal = 10, - .spi_freq = 8000000, /* 8MHz */ - .pConfig = &myConfig}; - -}; // end namespace Arduino_LMIC \ No newline at end of file +void loop() { + os_runloop_once(); +} \ No newline at end of file