Browse Source

add lora-gps sketch

GPS tracked SDS011 sensor, transmitting data

via Lora through TTN to opensensemap.org

based on Arduino Mega
master
noerw 4 years ago
parent
commit
ada3c1ea8c
  1. 295
      libraries/HDC100X/HDC100X.cpp
  2. 89
      libraries/HDC100X/HDC100X.h
  3. 52
      libraries/HDC100X/KEYWORDS.txt
  4. 22
      libraries/HDC100X/examples/readTempHumi/readTempHumi.ino
  5. 25
      libraries/HDC100X/examples/test/test.ino
  6. 22
      libraries/HDC100X/keywords.txt
  7. 365
      libraries/IBM_LMIC_framework/README.md
  8. BIN
      libraries/IBM_LMIC_framework/doc/LMiC-v1.5.pdf
  9. 4
      libraries/IBM_LMIC_framework/doc/README.txt
  10. 28
      libraries/IBM_LMIC_framework/doc/release-notes.txt
  11. 162
      libraries/IBM_LMIC_framework/examples/raw/raw.ino
  12. 226
      libraries/IBM_LMIC_framework/examples/ttn-abp/ttn-abp.ino
  13. 173
      libraries/IBM_LMIC_framework/examples/ttn-otaa/ttn-otaa.ino
  14. 207
      libraries/IBM_LMIC_framework/examples/ttn/ttn.ino
  15. 9
      libraries/IBM_LMIC_framework/library.properties
  16. 342
      libraries/IBM_LMIC_framework/src/aes/ideetron/AES-128_V10.cpp
  17. 370
      libraries/IBM_LMIC_framework/src/aes/lmic.c
  18. 127
      libraries/IBM_LMIC_framework/src/aes/other.c
  19. 253
      libraries/IBM_LMIC_framework/src/hal/hal.cpp
  20. 28
      libraries/IBM_LMIC_framework/src/hal/hal.h
  21. 9
      libraries/IBM_LMIC_framework/src/lmic.h
  22. 367
      libraries/IBM_LMIC_framework/src/lmic/aes.c
  23. 83
      libraries/IBM_LMIC_framework/src/lmic/config.h
  24. 91
      libraries/IBM_LMIC_framework/src/lmic/hal.h
  25. 2382
      libraries/IBM_LMIC_framework/src/lmic/lmic.c
  26. 320
      libraries/IBM_LMIC_framework/src/lmic/lmic.h
  27. 391
      libraries/IBM_LMIC_framework/src/lmic/lorabase.h
  28. 129
      libraries/IBM_LMIC_framework/src/lmic/oslmic.c
  29. 288
      libraries/IBM_LMIC_framework/src/lmic/oslmic.h
  30. 851
      libraries/IBM_LMIC_framework/src/lmic/radio.c
  31. 1
      libraries/Lora_Serialization/Brewfile
  32. 21
      libraries/Lora_Serialization/LICENSE
  33. 307
      libraries/Lora_Serialization/README.md
  34. 17
      libraries/Lora_Serialization/example/.eslintrc
  35. 15
      libraries/Lora_Serialization/example/main_encoder.ino
  36. 13
      libraries/Lora_Serialization/example/main_encoder_LoraMessage.ino
  37. 11
      libraries/Lora_Serialization/example/ttn_decoder.js
  38. 8
      libraries/Lora_Serialization/example/ttn_encoder.js
  39. 8
      libraries/Lora_Serialization/example/ttn_encoder_LoraMessage.js
  40. 9
      libraries/Lora_Serialization/library.properties
  41. 54
      libraries/Lora_Serialization/package.json
  42. 99
      libraries/Lora_Serialization/src/LoraEncoder.cpp
  43. 52
      libraries/Lora_Serialization/src/LoraEncoder.h
  44. 72
      libraries/Lora_Serialization/src/LoraMessage.cpp
  45. 32
      libraries/Lora_Serialization/src/LoraMessage.h
  46. 70
      libraries/Lora_Serialization/src/LoraMessage.js
  47. 127
      libraries/Lora_Serialization/src/decoder.js
  48. 126
      libraries/Lora_Serialization/src/encoder.js
  49. 9
      libraries/Lora_Serialization/src/index.js
  50. 7
      libraries/Lora_Serialization/test/.eslintrc.js
  51. 37
      libraries/Lora_Serialization/test/LoraMessage.js
  52. 18
      libraries/Lora_Serialization/test/Makefile
  53. 39
      libraries/Lora_Serialization/test/base.js
  54. 20
      libraries/Lora_Serialization/test/decoder/bitmap.js
  55. 52
      libraries/Lora_Serialization/test/decoder/decode.js
  56. 18
      libraries/Lora_Serialization/test/decoder/humidity.js
  57. 18
      libraries/Lora_Serialization/test/decoder/latLng.js
  58. 23
      libraries/Lora_Serialization/test/decoder/temperature.js
  59. 18
      libraries/Lora_Serialization/test/decoder/uint16.js
  60. 18
      libraries/Lora_Serialization/test/decoder/uint8.js
  61. 18
      libraries/Lora_Serialization/test/decoder/unixtime.js
  62. 19
      libraries/Lora_Serialization/test/encoder/bitmap.js
  63. 48
      libraries/Lora_Serialization/test/encoder/encode.js
  64. 17
      libraries/Lora_Serialization/test/encoder/humidity.js
  65. 22
      libraries/Lora_Serialization/test/encoder/latLng.js
  66. 24
      libraries/Lora_Serialization/test/encoder/temperature.js
  67. 19
      libraries/Lora_Serialization/test/encoder/uint16.js
  68. 19
      libraries/Lora_Serialization/test/encoder/uint8.js
  69. 18
      libraries/Lora_Serialization/test/encoder/unixtime.js
  70. 12
      libraries/Lora_Serialization/test/helpers.cpp
  71. 11
      libraries/Lora_Serialization/test/lib/Catch/.gitattributes
  72. 29
      libraries/Lora_Serialization/test/lib/Catch/.github/issue_template.md
  73. 25
      libraries/Lora_Serialization/test/lib/Catch/.github/pull_request_template.md
  74. 29
      libraries/Lora_Serialization/test/lib/Catch/.gitignore
  75. 232
      libraries/Lora_Serialization/test/lib/Catch/.travis.yml
  76. 271
      libraries/Lora_Serialization/test/lib/Catch/CMakeLists.txt
  77. 23
      libraries/Lora_Serialization/test/lib/Catch/LICENSE_1_0.txt
  78. 23
      libraries/Lora_Serialization/test/lib/Catch/README.md
  79. 45
      libraries/Lora_Serialization/test/lib/Catch/appveyor.yml
  80. BIN
      libraries/Lora_Serialization/test/lib/Catch/catch-hand-icon.png
  81. BIN
      libraries/Lora_Serialization/test/lib/Catch/catch-icon-tiny.png
  82. BIN
      libraries/Lora_Serialization/test/lib/Catch/catch-logo-small.png
  83. 23
      libraries/Lora_Serialization/test/lib/Catch/docs/Readme.md
  84. 136
      libraries/Lora_Serialization/test/lib/Catch/docs/assertions.md
  85. 95
      libraries/Lora_Serialization/test/lib/Catch/docs/build-systems.md
  86. 277
      libraries/Lora_Serialization/test/lib/Catch/docs/command-line.md
  87. 12
      libraries/Lora_Serialization/test/lib/Catch/docs/commercial-users.md
  88. 100
      libraries/Lora_Serialization/test/lib/Catch/docs/configuration.md
  89. 41
      libraries/Lora_Serialization/test/lib/Catch/docs/contributing.md
  90. 99
      libraries/Lora_Serialization/test/lib/Catch/docs/limitations.md
  91. 52
      libraries/Lora_Serialization/test/lib/Catch/docs/logging.md
  92. 103
      libraries/Lora_Serialization/test/lib/Catch/docs/matchers.md
  93. 59
      libraries/Lora_Serialization/test/lib/Catch/docs/opensource-users.md
  94. 72
      libraries/Lora_Serialization/test/lib/Catch/docs/own-main.md
  95. 150
      libraries/Lora_Serialization/test/lib/Catch/docs/release-notes.md
  96. 64
      libraries/Lora_Serialization/test/lib/Catch/docs/slow-compiles.md
  97. 88
      libraries/Lora_Serialization/test/lib/Catch/docs/test-cases-and-sections.md
  98. 32
      libraries/Lora_Serialization/test/lib/Catch/docs/test-fixtures.md
  99. 70
      libraries/Lora_Serialization/test/lib/Catch/docs/tostring.md
  100. 249
      libraries/Lora_Serialization/test/lib/Catch/docs/tutorial.md

295
libraries/HDC100X/HDC100X.cpp

@ -0,0 +1,295 @@
/***********************
This library was written for the Texas Instruments
HDC100X temperature and humidity sensor.
It has been tested for the HDC1000 and the HDC1008
Buy the HDC1008 breakout board at: https://www.tindie.com/stores/RFgermany
This library is made by Florian Roesner.
Released under GNU GPL v2.0 license.
*************************/
//#include "Arduino.h"
#include "HDC100X.h"
//#include "Wire.h"
//######-----------------------------------------------------------------------
//######-----------------------------------------------------------------------
//PUBLIC:
//######-----------------------------------------------------------------------
//######-----------------------------------------------------------------------
HDC100X::HDC100X(){
ownAddr = HDC100X_ADDR1;
dataReadyPin = -1;
}
//-----------------------------------------------------------------------
HDC100X::HDC100X(uint8_t address){
ownAddr = address;
//dataReadyPin = pin;
}
//-----------------------------------------------------------------------
HDC100X::HDC100X(bool addr0, bool addr1){
// set the two bits the way you set the address jumpers
ownAddr = 0b1000000 |(addr0|(addr1<<1));
//dataReadyPin = pin;
}
//######-----------------------------------------------------------------------
//######-----------------------------------------------------------------------
uint8_t HDC100X::begin(uint8_t mode, uint8_t tempRes, uint8_t humiRes, bool heaterState){
int i;
/* sets the mode and resolution and the state of the heater element. care must be taken, because it will change the temperature reading
** in:
** mode: HDC100X_TEMP_HUMI
** tempRes: HDC100X_11BIT/HDC100X_14BIT
** humiRes: HDC100X_8BIT/HDC100X_11BIT/HDC100X_14BIT
** heaterState: ENABLE/DISABLE
** out:
** high byte of the configuration register
*/
Wire.begin();
// test I2C address
Wire.beginTransmission(ownAddr);
i = Wire.endTransmission();
if(i != 0) // error device not found
{
for(int tries=3; tries!=0; tries--)
{
Wire.beginTransmission(HDC100X_ADDR1);
i = Wire.endTransmission();
if(i == 0)
{
ownAddr = HDC100X_ADDR1;
break;
}
delay(20); // wait 20ms
Wire.beginTransmission(HDC100X_ADDR2);
i = Wire.endTransmission();
if(i == 0)
{
ownAddr = HDC100X_ADDR2;
break;
}
delay(20); // wait 20ms
Wire.beginTransmission(HDC100X_ADDR3);
i = Wire.endTransmission();
if(i == 0)
{
ownAddr = HDC100X_ADDR3;
break;
}
delay(20); // wait 20ms
Wire.beginTransmission(HDC100X_ADDR4);
i = Wire.endTransmission();
if(i == 0)
{
ownAddr = HDC100X_ADDR4;
break;
}
delay(20); // wait 20ms
}
}
HDCmode = mode;
return writeConfigData(mode|(tempRes<<2)|humiRes|(heaterState<<5));
}
//-----------------------------------------------------------------------
uint8_t HDC100X::begin(uint8_t mode, uint8_t resulution, bool heaterState){
/* sets the mode, resolution and heaterState. Care must be taken, because it will change the temperature reading
** in:
** mode: HDC100X_TEMP/HDC100X_HUMI
** resolution: HDC100X_8BIT(just for the humidity)/HDC100X_11BIT(both)/HDC100X_14BIT(both)
** heaterState: ENABLE/DISABLE
** out:
** high byte of the configuration register
*/
Wire.begin();
HDCmode = mode;
if(mode == HDC100X_HUMI) return writeConfigData(resulution|(heaterState<<5));
else return writeConfigData((resulution<<2)|(heaterState<<5));
}
//######-----------------------------------------------------------------------
void HDC100X::setAddr(uint8_t address){
/* sets the slave address
** in:
** address: slave address byte
** out:
** none
*/
ownAddr = address;
}
//-----------------------------------------------------------------------
void HDC100X::setAddr(bool addr0, bool addr1){
/* sets the slave address
** in:
** addr0: true/false
** addr1: true/false
** out:
** none
*/
ownAddr = 0b1000000 |(addr0|(addr1<<1));
}
//-----------------------------------------------------------------------
void HDC100X::setDrPin(int8_t pin){
dataReadyPin = pin;
}
//######-----------------------------------------------------------------------
//######-----------------------------------------------------------------------
uint8_t HDC100X::setMode(uint8_t mode, uint8_t tempRes, uint8_t humiRes){
/* sets the mode and resolution
** in:
** mode: HDC100X_TEMP_HUMI
** tempRes: HDC100X_11BIT/HDC100X_14BIT
** humiRes: HDC100X_8BIT/HDC100X_11BIT/HDC100X_14BIT
** out:
** high byte of the configuration register
*/
uint8_t tempReg = getConfigReg() & 0xA0;
HDCmode = mode;
return writeConfigData(tempReg|mode|(tempRes<<2)|humiRes);
}
//-----------------------------------------------------------------------
uint8_t HDC100X::setMode(uint8_t mode, uint8_t resolution){
/* sets the mode and resolution
** in:
** mode: HDC100X_TEMP/HDC100X_HUMI
** resolution: HDC100X_8BIT(just for the humidity)/HDC100X_11BIT(both)/HDC100X_14BIT(both)
** out:
** high byte of the configuration register
*/
uint8_t tempReg = getConfigReg() & 0xA0;
HDCmode = mode;
if(mode == HDC100X_HUMI) return writeConfigData(tempReg|resolution);
return writeConfigData(tempReg|(resolution<<2));
}
//######-----------------------------------------------------------------------
//######-----------------------------------------------------------------------
uint8_t HDC100X::setHeater(bool state){
/* turns on the heater to get rid of condensation. Care must be taken, because it will change the temperature reading
** in:
** state: true/false
** out:
** high byte of the configuration register
*/
uint8_t regData = getConfigReg() & 0x5F;
if(state) return writeConfigData(regData|(state<<5));
return writeConfigData(regData);
}
//######-----------------------------------------------------------------------
//######-----------------------------------------------------------------------
bool HDC100X::battLow(void){
// returns a false if input voltage is higher than 2.8V and if lower a true
if(getConfigReg() & 0x08) return true;
return false;
}
//######-----------------------------------------------------------------------
//######-----------------------------------------------------------------------
float HDC100X::getTemp(void){
// returns the a float number of the temperature in degrees Celsius
if(HDCmode == HDC100X_TEMP || HDCmode == HDC100X_TEMP_HUMI)
return ((float)getRawTemp()/65536.0*165.0-40.0);
return 0.0;
}
//-----------------------------------------------------------------------
float HDC100X::getHumi(void){
// returns the a float number of the humidity in percent
if(HDCmode == HDC100X_HUMI || HDCmode == HDC100X_TEMP_HUMI)
return ((float)getRawHumi()/65536.0*100.0);
return 0.0;
}
//######-----------------------------------------------------------------------
//######-----------------------------------------------------------------------
uint16_t HDC100X::getRawTemp(void){
// returns the raw 16bit data of the temperature register
if(HDCmode == HDC100X_TEMP || HDCmode == HDC100X_TEMP_HUMI)
return read2Byte(HDC100X_TEMP_REG);
return 0;
}
//-----------------------------------------------------------------------
uint16_t HDC100X::getRawHumi(void){
// returns the raw 16bit data of the humidity register
if(HDCmode == HDC100X_HUMI || HDCmode == HDC100X_TEMP_HUMI)
return read2Byte(HDC100X_HUMI_REG);
return 0;
}
//######-----------------------------------------------------------------------
//######-----------------------------------------------------------------------
uint8_t HDC100X::getConfigReg(void){
// returns the high byte of the configuration register
return (read2Byte(HDC100X_CONFIG_REG)>>8);
}
//######-----------------------------------------------------------------------
//######-----------------------------------------------------------------------
uint16_t HDC100X::read2Byte(uint8_t reg){
/* reads two bytes from the defined register
** in:
** reg: HDC100X_TEMP_REG/HDC100X_HUMI_REG/HDC100X_CONFIG_REG/HDC100X_ID1_REG/HDC100X_ID2_REG/HDC100X_ID3_REG
** out:
** two byte of data from the defined register
*/
uint16_t data=0;
setRegister(reg);
Wire.requestFrom(ownAddr, 2U);
if(Wire.available()>=2){
data = Wire.read()<<8;
data += Wire.read();
}
return data;
}
uint8_t HDC100X::writeConfigData(uint8_t config){
/* writes the config byte to the configuration register
** in:
** config: one byte
** out:
** one byte 0:success 1:data too long to fit in transmit buffer 2:received NACK on transmit of address 3:received NACK on transmit of data 4:other error
*/
Wire.beginTransmission(ownAddr);
Wire.write(HDC100X_CONFIG_REG);
Wire.write(config);
Wire.write(0x00); //the last 8 bits are always 0
return Wire.endTransmission();
}
//######-----------------------------------------------------------------------
//######-----------------------------------------------------------------------
//PRIVATE:
//######-----------------------------------------------------------------------
//######-----------------------------------------------------------------------
void HDC100X::setRegister(uint8_t reg){
/* set the register for the next read or write cycle
** in:
** reg: HDC100X_TEMP_REG/HDC100X_HUMI_REG/HDC100X_CONFIG_REG/HDC100X_ID1_REG/HDC100X_ID2_REG/HDC100X_ID3_REG
** out:
** none
*/
Wire.beginTransmission(ownAddr);
Wire.write(reg);
Wire.endTransmission();
delay(10); // wait a little so that the sensor can set its register
}

89
libraries/HDC100X/HDC100X.h

@ -0,0 +1,89 @@
/***********************
This library was written for the Texas Instruments
HDC100X temperature and humidity sensor.
It has been tested for the HDC1000 and the HDC1008
Buy the HDC1008 breakout board at: https://www.tindie.com/stores/RFgermany
This library is made by Florian Roesner.
Released under GNU GPL v2.0 license.
*************************/
#ifndef _HDC100X_H_
#define _HDC100X_H_
#include <inttypes.h>
#include "Wire.h"
#if (ARDUINO >= 100)
#include "Arduino.h"
#else
#include "WProgram.h"
#endif
#define HDC100X_ADDR1 0x43
#define HDC100X_ADDR2 0x40
#define HDC100X_ADDR3 0x41
#define HDC100X_ADDR4 0x42
#define HDC100X_TEMP_REG 0x00
#define HDC100X_HUMI_REG 0x01
#define HDC100X_CONFIG_REG 0x02
#define HDC100X_ID1_REG 0xFB
#define HDC100X_ID2_REG 0xFC
#define HDC100X_ID3_REG 0xFD
#define HDC100X_RST 0x80
#define HDC100X_TEMP_HUMI 0x16
#define HDC100X_HUMI 1
#define HDC100X_TEMP 0
#define HDC100X_14BIT 0x00
#define HDC100X_11BIT 0x01
#define HDC100X_8BIT 0x02
#define DISABLE 0
#define ENABLE 1
class HDC100X{
public:
HDC100X();
HDC100X(uint8_t address);
HDC100X(bool addr0, bool addr1);
uint8_t begin(uint8_t mode, uint8_t tempRes, uint8_t humiRes, bool heaterState);
uint8_t begin(uint8_t mode, uint8_t resulution, bool heaterState);
void setAddr(bool addr0, bool addr1);
void setAddr(uint8_t address);
void setDrPin(int8_t pin);
uint8_t setMode(uint8_t mode, uint8_t tempRes, uint8_t humiRes);
uint8_t setMode(uint8_t mode, uint8_t resolution);
uint8_t setHeater(bool state);
bool battLow(void);
float getTemp(void);
float getHumi(void);
uint16_t getRawTemp(void);
uint16_t getRawHumi(void);
uint8_t getConfigReg(void);
uint16_t read2Byte(uint8_t reg);
uint8_t writeConfigData(uint8_t config);
private:
uint8_t ownAddr;
uint8_t dataReadyPin;
uint8_t HDCmode;
void setRegister(uint8_t reg);
};
#endif //_HDC100X_H_

52
libraries/HDC100X/KEYWORDS.txt

@ -0,0 +1,52 @@
#######################################
# Syntax Coloring Map
#######################################
DISABLE KEYWORD2
ENABLE KEYWORD2
#######################################
# Datatypes (KEYWORD1)
#######################################
HDC100X KEYWORD1
#######################################
# Methods and Functions (KEYWORD2)
#######################################
begin KEYWORD2
setAddr KEYWORD2
setDrPin KEYWORD2
setMode KEYWORD2
setHeater KEYWORD2
battLow KEYWORD2
getTemp KEYWORD2
getHumi KEYWORD2
getRawTemp KEYWORD2
getRawHumi KEYWORD2
getConfigReg KEYWORD2
read2Byte KEYWORD2
setRegister KEYWORD2
writeConfigData KEYWORD2
######################################
# Constants (LITERAL1)
#######################################
HDC100X_DEFAULT_ADDR LITERAL1
HDC100X_TEMP_REG LITERAL1
HDC100X_HUMI_REG LITERAL1
HDC100X_CONFIG_REG LITERAL1
HDC100X_ID1_REG LITERAL1
HDC100X_ID2_REG LITERAL1
HDC100X_ID3_REG LITERAL1
HDC100X_RST LITERAL1
HDC100X_TEMP_HUMI LITERAL1
HDC100X_HUMI LITERAL1
HDC100X_TEMP LITERAL1
HDC100X_14BIT LITERAL1
HDC100X_11BIT LITERAL1
HDC100X_8BIT LITERAL1

22
libraries/HDC100X/examples/readTempHumi/readTempHumi.ino

@ -0,0 +1,22 @@
#include <Wire.h>
#include <HDC100X.h>
HDC100X HDC1(0x43);
#define LED 13
bool state = false;
void setup(){
Serial.begin(9600);
HDC1.begin(HDC100X_TEMP_HUMI,HDC100X_14BIT,HDC100X_14BIT,DISABLE);
}
void loop(){
Serial.print(" Humidity: ");
Serial.print(HDC1.getHumi());
Serial.print("%, Temperature: ");
Serial.print(HDC1.getTemp());
Serial.println("C");
delay(500);
}

25
libraries/HDC100X/examples/test/test.ino

@ -0,0 +1,25 @@
#include <Wire.h>
#include <HDC100X.h>
HDC100X hdc;
#define LED 13
bool state = false;
void setup()
{
Serial.begin(9600);
hdc.begin(HDC100X_TEMP_HUMI,HDC100X_14BIT,HDC100X_14BIT,DISABLE);
}
void loop()
{
Serial.print(" Humidity: ");
Serial.print(hdc.getHumi());
Serial.print("%, Temperature: ");
Serial.print(hdc.getTemp());
Serial.println("C");
delay(500);
}

22
libraries/HDC100X/keywords.txt

@ -0,0 +1,22 @@
#######################################
# Datatypes (KEYWORD1)
#######################################
HDC100X KEYWORD1
#######################################
# Methods and Functions (KEYWORD2)
#######################################
begin KEYWORD2
setAddr KEYWORD2
setDrPin KEYWORD2
setMode KEYWORD2
setHeater KEYWORD2
battLow KEYWORD2
getTemp KEYWORD2
getHumi KEYWORD2
getRawTemp KEYWORD2
getRawHumi KEYWORD2
getConfigReg KEYWORD2
read2Byte KEYWORD2
writeConfigData KEYWORD2

365
libraries/IBM_LMIC_framework/README.md

@ -0,0 +1,365 @@
Arduino-LMIC library
====================
This repository contains the IBM LMIC (LoraMAC-in-C) library, slightly
modified to run in the Arduino environment, allowing using the SX1272,
SX1276 tranceivers and compatible modules (such as some HopeRF RFM9x
modules).
This library mostly exposes the functions defined by LMIC, it makes no
attempt to wrap them in a higher level API that is more in the Arduino
style. To find out how to use the library itself, see the examples, or
see the PDF file in the doc subdirectory.
This library requires Arduino IDE version 1.6.6 or above, since it
requires C99 mode to be enabled by default.
Installing
----------
To install this library:
- install it using the Arduino Library manager ("Sketch" -> "Include
Library" -> "Manage Libraries..."), or
- download a zipfile from github using the "Download ZIP" button and
install it using the IDE ("Sketch" -> "Include Library" -> "Add .ZIP
Library..."
- clone this git repository into your sketchbook/libraries folder.
For more info, see https://www.arduino.cc/en/Guide/Libraries
Features
--------
The LMIC library provides a fairly complete LoRaWAN Class A and Class B
implementation, supporting the EU-868 and US-915 bands. Only a limited
number of features was tested using this port on Arduino hardware, so be
careful when using any of the untested features.
What certainly works:
- Sending packets uplink, taking into account duty cycling.
- Encryption and message integrity checking.
- Receiving downlink packets in the RX2 window.
- Custom frequencies and datarate settings.
- Over-the-air activation (OTAA / joining).
What has not been tested:
- Receiving downlink packets in the RX1 window.
- Receiving and processing MAC commands.
- Class B operation.
If you try one of these untested features and it works, be sure to let
us know (creating a github issue is probably the best way for that).
Configuration
-------------
A number of features can be configured or disabled by editing the
`config.h` file in the library folder. Unfortunately the Arduino
environment does not offer any way to do this (compile-time)
configuration from the sketch, so be careful to recheck your
configuration when you switch between sketches or update the library.
At the very least, you should set the right type of transceiver (SX1272
vs SX1276) in config.h, most other values should be fine at their
defaults.
Supported hardware
------------------
This library is intended to be used with plain LoRa transceivers,
connecting to them using SPI. In particular, the SX1272 and SX1276
families are supported (which should include SX1273, SX1277, SX1278 and
SX1279 which only differ in the available frequencies, bandwidths and
spreading factors). It has been tested with both SX1272 and SX1276
chips, using the Semtech SX1272 evaluation board and the HopeRF RFM92
and RFM95 boards (which supposedly contain an SX1272 and SX1276 chip
respectively).
This library contains a full LoRaWAN stack and is intended to drive
these Transceivers directly. It is *not* intended to be used with
full-stack devices like the Microchip RN2483 and the Embit LR1272E.
These contain a transceiver and microcontroller that implements the
LoRaWAN stack and exposes a high-level serial interface instead of the
low-level SPI transceiver interface.
This library is intended to be used inside the Arduino environment. It
should be architecture-independent, so it should run on "normal" AVR
arduinos, but also on the ARM-based ones, and some success has been seen
running on the ESP8266 board as well. It was tested on the Arduino Uno,
Pinoccio Scout, Teensy LC and 3.x, ESP8266, Arduino 101.
This library an be quite heavy, especially if the fairly small ATmega
328p (such as in the Arduino Uno) is used. In the default configuration,
the available 32K flash space is nearly filled up (this includes some
debug output overhead, though). By disabling some features in `config.h`
(like beacon tracking and ping slots, which are not typically needed),
some space can be freed up. Some work is underway to replace the AES
encryption implementation, which should free up another 8K or so of
flash in the future, making this library feasible to run on a 328p
microcontroller.
Connections
-----------
To make this library work, your Arduino (or whatever Arduino-compatible
board you are using) should be connected to the transceiver. The exact
connections are a bit dependent on the transceiver board and Arduino
used, so this section tries to explain what each connection is for and
in what cases it is (not) required.
Note that the SX1272 module runs at 3.3V and likely does not like 5V on
its pins (though the datasheet is not say anything about this, and my
transceiver did not obviously break after accidentally using 5V I/O for
a few hours). To be safe, make sure to use a level shifter, or an
Arduino running at 3.3V. The Semtech evaluation board has 100 ohm resistors in
series with all data lines that might prevent damage, but I would not
count on that.
### Power
The SX127x transceivers need a supply voltage between 1.8V and 3.9V.
Using a 3.3V supply is typical. Some modules have a single power pin
(like the HopeRF modules, labeled 3.3V) but others expose multiple power
pins for different parts (like the Semtech evaluation board that has
`VDD_RF`, `VDD_ANA` and `VDD_FEM`), which can all be connected together.
Any *GND* pins need to be connected to the Arduino *GND* pin(s).
### SPI
The primary way of communicating with the transceiver is through SPI
(Serial Peripheral Interface). This uses four pins: MOSI, MISO, SCK and
SS. The former three need to be directly connected: so MOSI to MOSI,
MISO to MISO, SCK to SCK. Where these pins are located on your Arduino
varies, see for example the "Connections" section of the [Arduino SPI
documentation](SPI).
The SS (slave select) connection is a bit more flexible. On the SPI
slave side (the transceiver), this must be connect to the pin
(typically) labeled *NSS*. On the SPI master (Arduino) side, this pin
can connect to any I/O pin. Most Arduinos also have a pin labeled "SS",
but this is only relevant when the Arduino works as an SPI slave, which
is not the case here. Whatever pin you pick, you need to tell the
library what pin you used through the pin mapping (see below).
[SPI]: https://www.arduino.cc/en/Reference/SPI
### DIO pins
The DIO (digitial I/O) pins on the transceiver board can be configured
for various functions. The LMIC library uses them to get instant status
information from the transceiver. For example, when a LoRa transmission
starts, the DIO0 pin is configured as a TxDone output. When the
transmission is complete, the DIO0 pin is made high by the transceiver,
which can be detected by the LMIC library.
The LMIC library needs only access to DIO0, DIO1 and DIO2, the other
DIOx pins can be left disconnected. On the Arduino side, they can
connect to any I/O pin, since the current implementation does not use
interrupts or other special hardware features (though this might be
added in the feature, see also the "Timing" section).
In LoRa mode the DIO pins are used as follows:
* DIO0: TxDone and RxDone
* DIO1: RxTimeout
In FSK mode they are used as follows::
* DIO0: PayloadReady and PacketSent
* DIO2: TimeOut
Both modes need only 2 pins, but the tranceiver does not allow mapping
them in such a way that all needed interrupts map to the same 2 pins.
So, if both LoRa and FSK modes are used, all three pins must be
connected.
The pins used on the Arduino side should be configured in the pin
mapping in your sketch (see below).
### Reset
The transceiver has a reset pin that can be used to explicitely reset
it. The LMIC library uses this to ensure the chip is in a consistent
state at startup. In practice, this pin can be left disconnected, since
the transceiver will already be in a sane state on power-on, but
connecting it might prevent problems in some cases.
On the Arduino side, any I/O pin can be used. The pin number used must
be configured in the pin mapping (see below).
### RXTX
The transceiver contains two separate antenna connections: One for RX
and one for TX. A typical transceiver board contains an antenna switch
chip, that allows switching a single antenna between these RX and TX
connections. Such a antenna switcher can typically be told what
position it should be through an input pin, often labeled *RXTX*.
The easiest way to control the antenna switch is to use the *RXTX* pin
on the SX127x transceiver. This pin is automatically set high during TX
and low during RX. For example, the HopeRF boards seem to have this
connection in place, so they do not expose any *RXTX* pins and the pin
can be marked as unused in the pin mapping.
Some boards do expose the antenna switcher pin, and sometimes also the
SX127x *RXTX* pin. For example, the SX1272 evaluation board calls the
former *FEM_CTX* and the latter *RXTX*. Again, simply connecting these
together with a jumper wire is the easiest solution.
Alternatively, or if the SX127x *RXTX* pin is not available, LMIC can be
configured to control the antenna switch. Connect the antenna switch
control pin (e.g. *FEM_CTX* on the Semtech evaluation board) to any I/O
pin on the Arduino side, and configure the pin used in the pin map (see
below). It is not entirely clear why would *not* want the transceiver to
control the antenna directly, though.
### Pin mapping
As described above, most connections can use arbitrary I/O pins on the
Arduino side. To tell the LMIC library about these, a pin mapping struct
is used in the sketch file.
For example, this could look like this:
lmic_pinmap lmic_pins = {
.nss = 6,
.rxtx = LMIC_UNUSED_PIN,
.rst = 5,
.dio = {2, 3, 4},
};
The names refer to the pins on the transceiver side, the numbers refer
to the Arduino pin numbers (to use the analog pins, use constants like
`A0`). For the DIO pins, the three numbers refer to DIO0, DIO1 and DIO2
respectively. Any pins that are not needed should be specified as
`LMIC_UNUSED_PIN`. The nss and dio0 pin is required, the others can
potentially left out (depending on the environments and requirements,
see the notes above for when a pin can or cannot be left out).
The name of this struct must always be `lmic_pins`, which is a special name
recognized by the library.
#### LoRa Nexus by Ideetron
This board uses the following pin mapping:
const lmic_pinmap lmic_pins = {
.nss = 10,
.rxtx = LMIC_UNUSED_PIN,
.rst = LMIC_UNUSED_PIN, // hardwired to AtMega RESET
.dio = {4, 5, 7},
};
Examples
--------
This library currently provides three examples:
- `ttn-abp.ino` shows a basic transmission of a "Hello, world!" message
using the LoRaWAN protocol. It contains some frequency settings and
encryption keys intended for use with The Things Network, but these
also correspond to the default settings of most gateways, so it
should work with other networks and gateways as well. This example
uses activation-by-personalization (ABP, preconfiguring a device
address and encryption keys), and does not employ over-the-air
activation.
Reception of packets (in response to transmission, using the RX1 and
RX2 receive windows is also supported).
- `ttn-otaa.ino` also sends a "Hello, world!" message, but uses over
the air activation (OTAA) to first join a network to establish a
session and security keys. This was tested with The Things Network,
but should also work (perhaps with some changes) for other networks.
- `raw.ino` shows how to access the radio on a somewhat low level,
and allows to send raw (non-LoRaWAN) packets between nodes directly.
This is useful to verify basic connectivity, and when no gateway is
available, but this example also bypasses duty cycle checks, so be
careful when changing the settings.
Timing
------
Unfortunately, the SX127x tranceivers do not support accurate
timekeeping themselves (there is a sequencer that is *almost* sufficient
for timing the RX1 and RX2 downlink windows, but that is only available
in FSK mode, not in LoRa mode). This means that the microcontroller is
responsible for keeping track of time. In particular, it should note
when a packet finished transmitting, so it can open up the RX1 and RX2
receive windows at a fixed time after the end of transmission.
This timing uses the Arduino `micros()` timer, which has a granularity
of 4μs and is based on the primary microcontroller clock. For timing
events, the tranceiver uses its DIOx pins as interrupt outputs. In the
current implementation, these pins are not handled by an actual
interrupt handler, but they are just polled once every LMIC loop,
resulting in a bit inaccuracy in the timestamping. Also, running
scheduled jobs (such as opening up the receive windows) is done using a
polling approach, which might also result in further delays.
Fortunately, LoRa is a fairly slow protocol and the timing of the
receive windows is not super critical. To synchronize transmitter and
receiver, a preamble is first transmitted. Using LoRaWAN, this preamble
consists of 8 symbols, of which the receiver needs to see 4 symbols to
lock on. The current implementation tries to enable the receiver for 5
symbol times at 1.5 symbol after the start of the receive window,
meaning that a inacurracy of plus or minus 2.5 symbol times should be
acceptable.
At the fastest LoRa setting supported by the tranceiver (SF5BW500) a
single preamble symbol takes 64μs, so the receive window timing should
be accurate within 160μs (for LoRaWAN this is SF7BW250, needing accuracy
within 1280μs). This is certainly within a crystal's accuracy, but using
the internal oscillator is probably not feasible (which is 1% - 10%
accurate, depending on calibration). This accuracy should also be
feasible with the polling approach used, provided that the LMIC loop is
run often enough.
It would be good to properly review this code at some point, since it
seems that in some places some offsets and corrections are applied that
might not be appropriate for the Arduino environment. So if reception is
not working, the timing is something to have a closer look at.
The LMIC library was intended to connect the DIO pins to interrupt
lines and run code inside the interrupt handler. However, doing this
opens up an entire can of worms with regard to doing SPI transfers
inside interrupt routines (some of which is solved by the Arduino
`beginTransaction()` API, but possibly not everything). One simpler
alternative could be to use an interrupt handler to just store a
timestamp, and then do the actual handling in the main loop (this
requires modifications of the library to pass a timestamp to the LMIC
`radio_irq_handler()` function).
An even more accurate solution could be to use a dedicated timer with an
input capture unit, that can store the timestamp of a change on the DIO0
pin (the only one that is timing-critical) entirely in hardware.
Unfortunately, timer0, as used by Arduino's `millis()` and `micros()`
functions does not seem to have an input capture unit, meaning a
separate timer is needed for this.
If the main microcontroller does not have a crystal, but uses the
internal oscillator, the clock output of the transceiver (on DIO5) could
be usable to drive this timer instead of the main microcontroller clock,
to ensure the receive window timing is sufficiently accurate. Ideally,
this would use timer2, which supports asynchronous mode (e.g. running
while the microcontroller is sleeping), but that timer does not have an
input capture unit. Timer1 has one, but it seems it will stop running
once the microcontroller sleeps. Running the microcontroller in idle
mode with a slower clock might be feasible, though. Instead of using the
main crystal oscillator of the transceiver, it could be possible to use
the transceiver's internal RC oscillator (which is calibrated against
the transceiver crystal), or to calibrate the microcontroller internal
RC oscillator using the transceiver's clkout. However, that datasheet is
a bit vague on the RC oscillator's accuracy and how to use it exactly
(some registers seem to be FSK-mode only), so this needs some
experiments.
Downlink datarate
-----------------
Note that the datarate used for downlink packets in the RX2 window
defaults to SF12BW125 according to the specification, but some networks
use different values (iot.semtech.com and The Things Network both use
SF9BW). When using personalized activate (ABP), it is your
responsibility to set the right settings, e.g. by adding this to your
sketch (after calling `LMIC_setSession`). `ttn-abp.ino` already does
this.
LMIC.dn2Dr = DR_SF9;
When using OTAA, the network communicates the RX2 settings in the
join accept message, but the LMIC library does not currently process
these settings. Until that is solved (see issue #20), you should
manually set the RX2 rate, *after* joining (see the handling of
`EV_JOINED` in the `ttn-otaa.ino` for an example.
License
-------
Most source files in this repository are made available under the
Eclipse Public License v1.0. The examples which use a more liberal
license. Some of the AES code is available under the LGPL. Refer to each
individual source file for more details.

BIN
libraries/IBM_LMIC_framework/doc/LMiC-v1.5.pdf

4
libraries/IBM_LMIC_framework/doc/README.txt

@ -0,0 +1,4 @@
DISCLAIMER:
Please note that the software is provided AS IS and we cannot
provide support for optimizations, adaptations, integration,
ports to other platforms or device drivers!

28
libraries/IBM_LMIC_framework/doc/release-notes.txt

@ -0,0 +1,28 @@
==============================================================================
LMIC VERSION 1.4 (17-Mar-2015)
-------------------------------
- changed API: inverted port indicator flag in LMIC.txrxFlags
(now TXRX_PORT, previously TXRX_NOPORT)
- fixed offset OFF_CFLIST constant
- changed CRC-16 algorithm for beacons to CCITT(XMODEM) polynomial
- fixed radio driver (low data rate optimization for SF11+SF12 only for BW125)
- fixed timer rollover handling in job queue
==============================================================================
LMIC VERSION 1.5 (8-May-2015)
------------------------------
- fixed condition in convFreq()
- fixed freq*100 bug and freq==0 bug for CFList
- fixed TX scheduling bug
- better support for GNU compiler toolchain
==============================================================================

162
libraries/IBM_LMIC_framework/examples/raw/raw.ino

@ -0,0 +1,162 @@
/*******************************************************************************
* Copyright (c) 2015 Matthijs Kooijman
*
* 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 transmits data on hardcoded channel and receives data
* when not transmitting. Running this sketch on two nodes should allow
* them to communicate.
*******************************************************************************/
#include <lmic.h>
#include <hal/hal.h>
#include <SPI.h>
#if !defined(DISABLE_INVERT_IQ_ON_RX)
#error This example requires DISABLE_INVERT_IQ_ON_RX to be set. Update \
config.h in the lmic library to set it.
#endif
// How often to send a packet. Note that this sketch bypasses the normal
// LMIC duty cycle limiting, so when you change anything in this sketch
// (payload length, frequency, spreading factor), be sure to check if
// this interval should not also be increased.
// See this spreadsheet for an easy airtime and duty cycle calculator:
// https://docs.google.com/spreadsheets/d/1voGAtQAjC1qBmaVuP1ApNKs1ekgUjavHuVQIXyYSvNc
#define TX_INTERVAL 2000
// Pin mapping
const lmic_pinmap lmic_pins = {
.nss = 6,
.rxtx = LMIC_UNUSED_PIN,
.rst = 5,
.dio = {2, 3, 4},
};
// These callbacks are only used in over-the-air activation, so they are
// left empty here (we cannot leave them out completely unless
// DISABLE_JOIN is set in config.h, otherwise the linker will complain).
void os_getArtEui (u1_t* buf) { }
void os_getDevEui (u1_t* buf) { }
void os_getDevKey (u1_t* buf) { }
void onEvent (ev_t ev) {
}
osjob_t txjob;
osjob_t timeoutjob;
static void tx_func (osjob_t* job);
// Transmit the given string and call the given function afterwards
void tx(const char *str, osjobcb_t func) {
os_radio(RADIO_RST); // Stop RX first
delay(1); // Wait a bit, without this os_radio below asserts, apparently because the state hasn't changed yet
LMIC.dataLen = 0;
while (*str)
LMIC.frame[LMIC.dataLen++] = *str++;
LMIC.osjob.func = func;
os_radio(RADIO_TX);
Serial.println("TX");
}
// Enable rx mode and call func when a packet is received
void rx(osjobcb_t func) {
LMIC.osjob.func = func;
LMIC.rxtime = os_getTime(); // RX _now_
// Enable "continuous" RX (e.g. without a timeout, still stops after
// receiving a packet)
os_radio(RADIO_RXON);
Serial.println("RX");
}
static void rxtimeout_func(osjob_t *job) {
digitalWrite(LED_BUILTIN, LOW); // off
}
static void rx_func (osjob_t* job) {
// Blink once to confirm reception and then keep the led on
digitalWrite(LED_BUILTIN, LOW); // off
delay(10);
digitalWrite(LED_BUILTIN, HIGH); // on
// Timeout RX (i.e. update led status) after 3 periods without RX
os_setTimedCallback(&timeoutjob, os_getTime() + ms2osticks(3*TX_INTERVAL), rxtimeout_func);
// Reschedule TX so that it should not collide with the other side's
// next TX
os_setTimedCallback(&txjob, os_getTime() + ms2osticks(TX_INTERVAL/2), tx_func);
Serial.print("Got ");
Serial.print(LMIC.dataLen);
Serial.println(" bytes");
Serial.write(LMIC.frame, LMIC.dataLen);
Serial.println();
// Restart RX
rx(rx_func);
}
static void txdone_func (osjob_t* job) {
rx(rx_func);
}
// log text to USART and toggle LED
static void tx_func (osjob_t* job) {
// say hello
tx("Hello, world!", txdone_func);
// reschedule job every TX_INTERVAL (plus a bit of random to prevent
// systematic collisions), unless packets are received, then rx_func
// will reschedule at half this time.
os_setTimedCallback(job, os_getTime() + ms2osticks(TX_INTERVAL + random(500)), tx_func);
}
// application entry point
void setup() {
Serial.begin(115200);
Serial.println("Starting");
#ifdef VCC_ENABLE
// For Pinoccio Scout boards
pinMode(VCC_ENABLE, OUTPUT);
digitalWrite(VCC_ENABLE, HIGH);
delay(1000);
#endif
pinMode(LED_BUILTIN, OUTPUT);
// initialize runtime env
os_init();
// Set up these settings once, and use them for both TX and RX
#if defined(CFG_eu868)
// Use a frequency in the g3 which allows 10% duty cycling.
LMIC.freq = 869525000;
#elif defined(CFG_us915)
LMIC.freq = 902300000;
#endif
// Maximum TX power
LMIC.txpow = 27;
// Use a medium spread factor. This can be increased up to SF12 for
// better range, but then the interval should be (significantly)
// lowered to comply with duty cycle limits as well.
LMIC.datarate = DR_SF9;
// This sets CR 4/5, BW125 (except for DR_SF7B, which uses BW250)
LMIC.rps = updr2rps(LMIC.datarate);
Serial.println("Started");
Serial.flush();
// setup initial job
os_setCallback(&txjob, tx_func);
}
void loop() {
// execute scheduled jobs and events
os_runloop_once();
}

226
libraries/IBM_LMIC_framework/examples/ttn-abp/ttn-abp.ino

@ -0,0 +1,226 @@
/*******************************************************************************
* Copyright (c) 2015 Thomas Telkamp and Matthijs Kooijman
*
* 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 ABP (Activation-by-personalisation), where a DevAddr and
* Session keys are preconfigured (unlike OTAA, where a DevEUI and
* application key is configured, while the DevAddr and session keys are
* assigned/generated in the over-the-air-activation procedure).
*
* 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 a DevAddr, NwkSKey and
* AppSKey. Each device should have their own unique values for these
* fields.
*
* Do not forget to define the radio type correctly in config.h.
*
*******************************************************************************/
#include <lmic.h>
#include <hal/hal.h>
#include <SPI.h>
// LoRaWAN NwkSKey, network session key
// This is the default Semtech key, which is used by the early prototype TTN
// network.
static const PROGMEM u1_t NWKSKEY[16] = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C };
// LoRaWAN AppSKey, application session key
// This is the default Semtech key, which is used by the early prototype TTN
// network.
static const u1_t PROGMEM APPSKEY[16] = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C };
// LoRaWAN end-device address (DevAddr)
static const u4_t DEVADDR = 0x03FF0001 ; // <-- Change this address for every node!
// These callbacks are only used in over-the-air activation, so they are
// left empty here (we cannot leave them out completely unless
// DISABLE_JOIN is set in config.h, otherwise the linker will complain).
void os_getArtEui (u1_t* buf) { }
void os_getDevEui (u1_t* buf) { }
void os_getDevKey (u1_t* buf) { }
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 = 6,
.rxtx = LMIC_UNUSED_PIN,
.rst = 5,
.dio = {2, 3, 4},
};
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"));
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;
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;
}
}
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(115200);
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();
// Set static session parameters. Instead of dynamically establishing a session
// by joining the network, precomputed session parameters are be provided.
#ifdef PROGMEM
// On AVR, these values are stored in flash and only copied to RAM
// once. Copy them to a temporary buffer here, LMIC_setSession will
// copy them into a buffer of its own again.
uint8_t appskey[sizeof(APPSKEY)];
uint8_t nwkskey[sizeof(NWKSKEY)];
memcpy_P(appskey, APPSKEY, sizeof(APPSKEY));
memcpy_P(nwkskey, NWKSKEY, sizeof(NWKSKEY));
LMIC_setSession (0x1, DEVADDR, nwkskey, appskey);
#else
// If not running an AVR with PROGMEM, just use the arrays directly
LMIC_setSession (0x1, DEVADDR, NWKSKEY, APPSKEY);
#endif
#if defined(CFG_eu868)
// Set up the channels used by the Things Network, which corresponds
// to the defaults of most gateways. Without this, only three base
// channels from the LoRaWAN specification are used, which certainly
// works, so it is good for debugging, but can overload those
// frequencies, so be sure to configure the full frequency range of
// your network here (unless your network autoconfigures them).
// Setting up channels should happen after LMIC_setSession, as that
// configures the minimal channel set.
// NA-US channels 0-71 are configured automatically
LMIC_setupChannel(0, 868100000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band
LMIC_setupChannel(1, 868300000, DR_RANGE_MAP(DR_SF12, DR_SF7B), BAND_CENTI); // g-band
LMIC_setupChannel(2, 868500000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band
LMIC_setupChannel(3, 867100000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band
LMIC_setupChannel(4, 867300000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band
LMIC_setupChannel(5, 867500000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band
LMIC_setupChannel(6, 867700000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band
LMIC_setupChannel(7, 867900000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band
LMIC_setupChannel(8, 868800000, DR_RANGE_MAP(DR_FSK, DR_FSK), BAND_MILLI); // g2-band
// TTN defines an additional channel at 869.525Mhz using SF9 for class B
// devices' ping slots. LMIC does not have an easy way to define set this
// frequency and support for class B is spotty and untested, so this
// frequency is not configured here.
#elif defined(CFG_us915)
// NA-US channels 0-71 are configured automatically
// but only one group of 8 should (a subband) should be active
// TTN recommends the second sub band, 1 in a zero based count.
// https://github.com/TheThingsNetwork/gateway-conf/blob/master/US-global_conf.json
LMIC_selectSubBand(1);
#endif
// Disable link check validation
LMIC_setLinkCheckMode(0);
// TTN uses SF9 for its RX2 window.
LMIC.dn2Dr = DR_SF9;
// Set data rate and transmit power for uplink (note: txpow seems to be ignored by the library)
LMIC_setDrTxpow(DR_SF7,14);
// Start job
do_send(&sendjob);
}
void loop() {
os_runloop_once();
}

173
libraries/IBM_LMIC_framework/examples/ttn-otaa/ttn-otaa.ino

@ -0,0 +1,173 @@
/*******************************************************************************
* Copyright (c) 2015 Thomas Telkamp and Matthijs Kooijman
*
* 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>
// 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]={ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
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]={ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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.
// The key shown here is the semtech default key.
static const u1_t PROGMEM APPKEY[16] = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C };
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 = 6,
.rxtx = LMIC_UNUSED_PIN,
.rst = 5,
.dio = {2, 3, 4},
};
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;
}
}
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();
// Start job (sending automatically starts OTAA too)
do_send(&sendjob);
}
void loop() {
os_runloop_once();
}

207
libraries/IBM_LMIC_framework/examples/ttn/ttn.ino

@ -0,0 +1,207 @@
/*******************************************************************************
* Copyright (c) 2015 Thomas Telkamp and Matthijs Kooijman
*
* 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 (early prototype version of) The Things Network.
*
* Note: LoRaWAN per sub-band duty-cycle limitation is enforced (1% in g1,
* 0.1% in g2).
*
* Change DEVADDR to a unique address!
* See http://thethingsnetwork.org/wiki/AddressSpace
*
* Do not forget to define the radio type correctly in config.h.
*
*******************************************************************************/
#include <lmic.h>
#include <hal/hal.h>
#include <SPI.h>
// LoRaWAN NwkSKey, network session key
// This is the default Semtech key, which is used by the prototype TTN
// network initially.
static const PROGMEM u1_t NWKSKEY[16] = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C };
// LoRaWAN AppSKey, application session key
// This is the default Semtech key, which is used by the prototype TTN
// network initially.
static const u1_t PROGMEM APPSKEY[16] = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C };
// LoRaWAN end-device address (DevAddr)
// See http://thethingsnetwork.org/wiki/AddressSpace
static const u4_t DEVADDR = 0x03FF0001 ; // <-- Change this address for every node!
// These callbacks are only used in over-the-air activation, so they are
// left empty here (we cannot leave them out completely unless
// DISABLE_JOIN is set in config.h, otherwise the linker will complain).
void os_getArtEui (u1_t* buf) { }
void os_getDevEui (u1_t* buf) { }
void os_getDevKey (u1_t* buf) { }
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 = 6,
.rxtx = LMIC_UNUSED_PIN,
.rst = 5,
.dio = {2, 3, 4},
};
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"));
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.dataLen) {
// data received in rx slot after tx
Serial.print(F("Data Received: "));
Serial.write(LMIC.frame+LMIC.dataBeg, LMIC.dataLen);
Serial.println();
}
// Schedule next transmission