Giter VIP home page Giter VIP logo

arduino-device-lib's Introduction

The Things Network Arduino Library

Build Status

This is an Arduino Library for Arduino devices like The Things Uno and Node to communicate via The Things Network.

At the moment this library requires devices to feature a Microchip RN2xx3 module.

Installation

Documentation

Examples

The library comes with examples. After installing the library you need to restart the Arduino IDE before they can be found under File > Examples > TheThingsNetwork.

arduino-device-lib's People

Contributors

akshaim avatar alexbn71 avatar amedeebulle avatar asanchezdelc avatar avbentem avatar cimm avatar danalvarez avatar dzmen avatar flhofer avatar fokkezb avatar frankleonrose avatar hallard avatar johanstokking avatar jpmeijers avatar jstrobel avatar per1234 avatar rgm3 avatar savnik avatar tftelkamp avatar xoseperez avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

arduino-device-lib's Issues

API: Default to Serial and Serial1

The library obviously has to work with any Arduino device, but we can simply sketches for The Things Uno and Node by defaulting to Serial for debug and Serial1 for the modem.

Current:

#define debugSerial Serial
#define loraSerial Serial1

void setup() {
  debugSerial.begin(115200);
  loraSerial.begin(57600);

  ttn.init(loraSerial, debugSerial);

  ttn.showStatus();
}

For TT Uno/Node this could be

void setup() {
  ttn.showStatus();
}

If you'd need to override for an other device you'd still call ttn.init():

ttn.init(loraSerial, debugSerial);

Since it would feel to not use init() if you'd use defaults, or if you only need to override one of the two it might be better to dump init() all together and have explicit setters:

ttn.setDebugStream(debugSerial);
ttn.setModemStream(debugSerial);

Need to configure rx2 settings and extra channels for ABP

Set RX2 data rate to SF9:

"mac set rx2 3 869525000"

For these frequencies, configure channels 3-7:

867100000
867300000
867500000
867700000
867900000

mac set ch freq
mac set ch drrange 0 5
mac set ch status on

The for all channels, 0-7, set:

mac set ch dcycle 799

Additionally, change the drrange for channel 1 (868.3 Mhz) to include SF7BW250:

mac set ch drrange 1 0 6

Move `TTN_HEX_` to cpp

TTN_HEX_PAIR_TO_BYTE is only used on one place in the .cpp and should not be defined in the header.

Bug: invalid_param in reset

I saw this in the logs:

Sending: mac set pwridx 1
Sending: ������������Invalid SF5
Response is not OK: invalid_param

API: Remove length as argument of sendBytes

Current:

int sendBytes(const byte* buffer, int length, int port = 1, bool confirm = false);

Simplified:

int sendBytes(const byte* buffer, int port = 1, bool confirm = false);
int sendBytes(const byte* buffer, bool confirm = false);

Reasoning:

  • Examples have ttn.sendBytes(payload, sizeof(payload)) so why not do sizeof(payload) in the lib and go from 4 to 3 args.
  • With an additional version without port you can ask for confirm without having to set port.

Node: Find a test to force user to select the right board

Currently the sketch will compile fine if you (still) have Leonardo Uno selected, but then brick the Node.

It would be great if we could have some kind of code in the Node library that will cause the compile the fail if the right board is not selected.

Use strings for keys

  if(lora.set_device_address("A88D5E97")) Serial.println("Address OK");
  if(lora.set_network_session_key("4230874028704F12BC3890D32D7A0355")) Serial.println("SESSIONKEY OK");
  if(lora.set_application_session_key("75B4C26938D4271F7903E3930CD14769")) Serial.println("APPKEY OK");

Simplify: Automatically delay where needed

All examples use delay() with various values between different calls to other ttn methods. If I remember correctly from the TNW Hack Battle leaving this out could break the sketch.

If these delays are required, why not handle this in the library so that sketches are more cleaner and simple?

Before:

void setup() {
  // ..

  delay(1000);

  ttn.init(loraSerial, debugSerial);
  ttn.reset();
  ttn.personalize(devAddr, nwkSKey, appSKey);

  delay(6000);

  ttn.showStatus();

  debugPrintLn("Setup for The Things Network complete");

  delay(1000);
}

After:

void setup() {
  // ..

  ttn.init(loraSerial, debugSerial);
  ttn.reset();
  ttn.personalize(devAddr, nwkSKey, appSKey);
  ttn.showStatus();

  debugPrintLn("Setup for The Things Network complete");
}

Node: LoRa module unreachable after upload

When I upload the following:

#include <TheThingsNetwork.h>

const byte appEui[8] = { 0x70, 0xB3, 0xD5, 0x7E, 0xF0, 0x00, 0x00, 0xF0 };
const byte appKey[16] =  { 0x42, 0x95, 0x3C, 0x34, 0xB2, 0xAB, 0x0E, 0x03, 0x1B, 0x09, 0xDB, 0x09, 0x6C, 0x08, 0x63, 0x41 };

#define lora Serial1
#define debug Serial

TheThingsNetwork ttn(lora, debug, TTN_FP_EU868);

void setup() {  
  lora.begin(57600);
  debug.begin(9600);

  while (!debug);
  ttn.showStatus();
}

void loop()
{
  ttn.showStatus();

  delay(10000);
}

The Serial Monitor shows that the LoRa module is unreachable here:

EUI: 

When I power off/on it works fine.

This does not seem to happen with the Tweetonig_RN2483 library. But once the LoRa module is unreachable and I upload a sketch with the Tweetonig_RN2483 library it also hangs. So it seems our library can get the LoRa module in a bad state.

set correct duty-cycle(s) after join

From the RN2483 documentation:

"The default settings consider only the three default channels (0-2), and their default duty cycle is 0.33%. If a new channel is created either by the server or by the user, all the channels (including the default ones) must be updated by the user in terms of duty cycle to comply with the ETSI regulations."

In multiple ways this is not a good implementation, but we have to work with it.

After a join, the arduino-library should set the correct duty cycles.

First, count the number of active channels (#totalchan) using the "mac get ch status" command for channels 0-15.

The duty cycle value (dcycle) to be configured on a channel is defined as: = (100/X) – 1

X is the intended duty cycle in percent. For EU we need to configure a band total duty cycle of 1%, so per channel a duty cycle of 1/#totalchan.

For each active channel, set the dcycle value to (100/(1/#totalchan)) – 1, using:

mac set ch dcycle

Simplified formula: (100*#totalchan)-1 for a band duty-cyle of 1%.

Hide wait process in setup

  // Wait a maximum of 10s for Serial Monitor
  while (!debugSerial && millis() < 10000);

I think this line can be put in the ttn.init() function.
If it create a problem, we can add a third parameter to init to disable the wait.
We can also create a separate function in ttn to do so.

Node: Issues with readLine() like we had on Uno earlier

On the Node only, we still have a timing issue with reading and setting values:

-- PERSONALIZE
Version is RN2483 1.0.1 Dec 15 2015 09:38:09, model is RN2483
Sending: mac set deveui RN2483 1.0.1 Dec 15 2015 09:38:09
Response is not OK: err
Sending: mac set adr off
Response is not OK: invalid_param
Sending: mac set devaddr with 4 bytes
Response is not OK: invalid_param
Sending: mac set nwkskey with 16 bytes
Sending: mac set appskey with 16 bytes
Response is not OK: invalid_param
Sending: mac set rx2 3 869525000
Response is not OK: invalid_param
Sending: mac set ch dcycle 0 799
Response is not OK: invalid_param
Sending: mac set ch dcycle 1 799
Sending: mac set ch dcycle 2 799
Sending: mac set ch freq 3 867100000
Sending: mac set ch drrange 3 0 5
Sending: mac set ch status 3 on
Sending: mac set ch dcycle 3 799
Sending: mac set ch freq 4 867300000
Sending: mac set ch drrange 4 0 5
Sending: mac set ch status 4 on
Sending: mac set ch dcycle 4 799
Sending: mac set ch freq 5 867500000
Sending: mac set ch drrange 5 0 5
Sending: mac set ch status 5 on
Sending: mac set ch dcycle 5 799
Sending: mac set ch freq 6 867700000
Sending: mac set ch drrange 6 0 5
Sending: mac set ch status 6 on
Sending: mac set ch dcycle 6 799
Sending: mac set ch freq 7 867900000
Sending: mac set ch drrange 7 0 5
Sending: mac set ch status 7 on
Sending: mac set ch dcycle 7 799
Sending: mac set ch drrange 1 0 6
Sending: mac set pwridx 1
Sending: mac set dr 5
Sending: mac join abp
Response is not OK: keys_not_init
Personalize not accepted: 
-- STATUS
EUI: 0004A30B001B618C
Battery: 3294
AppEUI: 70B3D57EF000003E
DevEUI: 0004A30B001B618C
Band: 868
Data Rate: 5
RX Delay 1: 1000
RX Delay 2: 2000
-- LOOP
Sending: mac tx uncnf 1 with 1 bytes
Response is not OK: not_joined
Send command failed

invalid_param when setting "mac set dr 5"

I wondered why the module did not change to SF7 as declared as default in TheThingsnetwork.h
I built in some additional println statements and I found out that I always got the response "invalid_param"
Is this my fault or is this a bug?
Do we have to call "mac set dr 5" in another location of the code?
thank you

Node: Hangs when you use `TheThingsNetwork.*` inside of `TheThingsNode.on*`

Example:

#include <TheThingsNetwork.h>
#include <TheThingsNode.h>

#define loraSerial Serial1
#define debugSerial Serial

TheThingsNetwork ttn(loraSerial, debugSerial, TTN_FP_EU868);
TheThingsNode node;

int count = 0;

void setup() {
  loraSerial.begin(57600);
  debugSerial.begin(9600);

  while (!debugSerial && millis() < 10000);

  node.onButtonRelease(onButtonRelease);

  node.setColor(TTN_GREEN);
}

void onButtonRelease() {
  debugSerial.println("-- BUTTON RELEASE " + String(count));

  // using a var outside the function scope works fine
  count++;

  // using a function of node works fine
  node.setColor(TTN_BLUE);

  // Enable the next line and the Node will hang.
  // The above println won't show up in Serial Monitor,
  // but the LED does change to blue.
  //ttn.showStatus();
}

void loop() {
  debugSerial.println("-- LOOP");

  delay(10000);
}

API: Provide public debugPrint(Ln)

All examples define:

#define modemSerial Serial1
#define debugSerial Serial
// ..
#define debugPrintLn(...) { if (debugSerial) debugSerial.println(__VA_ARGS__); }
#define debugPrint(...) { if (debugSerial) debugSerial.print(__VA_ARGS__); }
// ..
ttn.init(loraSerial, debugSerial);
// ..
debugPrintLn("Setup for The Things Network complete");

The library also defines these functions and uses the debugSerial passed to init().

Could we make the debugPrint(Ln) of the library public so that sketches simplify to:

#define modemSerial Serial1
#define debugSerial Serial
// ..
ttn.init(loraSerial, debugSerial);
// ..
ttn.debugPrintLn("Setup for The Things Network complete");

API: Allow user to register function for downlink

Receiving a response is not straight forward. You have to send to receive and sendBytes() only returns the length. Then you'll have to use ttn.downlinkPort and ttn.downlink to get the rest of the info.

The Wire library lets you register a function to be called when a message is received. This is a much more friendly API already, although the function still doesn't receive all info as arguments, which I think it should.

Current:

void loop() {
  // Send a byte
  byte buf[1];
  buf[0] = 20;
  int downlinkBytes = ttn.sendBytes(buf, 1);

  if (downlinkBytes > 0) {
    debugPrintLn("Received " + String(downlinkBytes) + " bytes on port " + String(ttn.downlinkPort) + ":")

    for (int i = 0; i < downlinkBytes; i++) {
      debugPrint(String(ttn.downlink[i]) + " ");
    }

    debugPrintLn();
  }

  delay(20000);
}

Proposed:

void setup() {
  // ..

  ttn.onMessage(receiveMessage);
}

void loop() {
  // send a byte to poll
  var poll = { x00 };
  ttn.sendByes(poll, 1);
}

void receiveEvent(const byte* buffer, int length, int port) {
    debugPrintLn("Received " + String(length) + " bytes on port " + String(port) + ":")

    for (int i = 0; i < downlinkBytes; i++) {
      debugPrint(String(buffer[i]) + " ");
    }

    debugPrintLn();
}

Use enum for `TTN_FP_` instead of typedef

#define TTN_FP_EU868 1
#define TTN_FP_US915 2
// ..
typedef unsigned long   fp_ttn_t;
// ..
fp_ttn_t fp;

should become

enum TTN_FP: byte
{
  TTN_FP_EU868
  TTN_FP_US915
}
// ..
TTN_FP fp;

so that the constructor can check for valid args:

TheThingsNetwork(Stream& modemStream, Stream& debugStream, TTN_FP fp, int sf = TTN_DEFAULT_SF, int fsb = TTN_DEFAULT_FSB); 

Update to latest The Things Uno SDK

This is an old fork of The Things Uno SDK. The current version, in TheThingsNetwork/sdk/devices/TheThingsUno is 0.15+ afaik, with downlink support.

Initialize library for specific frequency plan

Issues #21 and #22 are frequency plan specific, but the library does not accommodate for frequency plans yet. Instead, it only checks the RN module. We cannot assume that RN2483 is used in EU868.

We should define frequency plans, e.g. #define TTN_EU868 1, and initialize the library with this frequency plan. Instead of checking the RN module, it should assert that the RN module matches the frequency plan (e.g. TTN_EU868 needs RN2483), and based on the frequency plan, configure the channels and duty cycle.

Node: Refactor interrupts and use deep sleep

#99 is not a bug but an interrupt limitation.

  • In the interrupts attached in the Node class set a flag.
  • Introduce a node.loop() method that needs to be called in loop().
  • In node.loop() see what flags were set and call the functions registered via node.on*()
  • Tell the user not to use delay() in the loop because it will keep the processor busy with NOOPs and drain the battery. Instead, node.loop() needs to use deep sleep to really power down the processor and set an interrupt to wake up after a maximum of 8 seconds. For longer delays it needs to wait to be woken up X times.

See the example by Sven:

TTN-Node_Demo.ino.zip

Redefine warnings on verify and upload (which still finishes)

When I use the current release in the old repo or this - after #2 - up to date moved version I get the following error when verifying or uploading an example:

/var/folders/cv/b296rfgs7t7bqsl6r03mg_4h0000gn/T/arduino_modified_sketch_365412/Downlink.ino:10:0: warning: "debugPrintLn" redefined
 #define debugPrintLn(...) { if (debugSerial) debugSerial.println(__VA_ARGS__); }
 ^
In file included from /var/folders/cv/b296rfgs7t7bqsl6r03mg_4h0000gn/T/arduino_modified_sketch_365412/Downlink.ino:1:0:
/Users/fokkezb/Documents/Arduino/libraries/TheThingsNetwork/src/TheThingsNetwork.h:23:0: note: this is the location of the previous definition
 #define debugPrintLn(...) { if (debugStream) debugStream->println(__VA_ARGS__); }
 ^
/var/folders/cv/b296rfgs7t7bqsl6r03mg_4h0000gn/T/arduino_modified_sketch_365412/Downlink.ino:11:0: warning: "debugPrint" redefined
 #define debugPrint(...) { if (debugSerial) debugSerial.print(__VA_ARGS__); }
 ^
In file included from /var/folders/cv/b296rfgs7t7bqsl6r03mg_4h0000gn/T/arduino_modified_sketch_365412/Downlink.ino:1:0:
/Users/fokkezb/Documents/Arduino/libraries/TheThingsNetwork/src/TheThingsNetwork.h:24:0: note: this is the location of the previous definition
 #define debugPrint(...) { if (debugStream) debugStream->print(__VA_ARGS__); }
 ^

But when I remove the definitions from the example it fails with:

Arduino: 1.6.10 (Mac OS X), Board:"Arduino Leonardo"

In file included from /var/folders/cv/b296rfgs7t7bqsl6r03mg_4h0000gn/T/arduino_modified_sketch_641601/Downlink.ino:1:0:
/var/folders/cv/b296rfgs7t7bqsl6r03mg_4h0000gn/T/arduino_modified_sketch_641601/Downlink.ino: In function 'void setup()':
/Users/fokkezb/Documents/Arduino/libraries/TheThingsNetwork/src/TheThingsNetwork.h:23:33: error: 'debugStream' was not declared in this scope
 #define debugPrintLn(...) { if (debugStream) debugStream->println(__VA_ARGS__); }
                                 ^
/var/folders/cv/b296rfgs7t7bqsl6r03mg_4h0000gn/T/arduino_modified_sketch_641601/Downlink.ino:26:3: note: in expansion of macro 'debugPrintLn'
   debugPrintLn("Setup for The Things Network complete");
   ^
/var/folders/cv/b296rfgs7t7bqsl6r03mg_4h0000gn/T/arduino_modified_sketch_641601/Downlink.ino: In function 'void loop()':
/Users/fokkezb/Documents/Arduino/libraries/TheThingsNetwork/src/TheThingsNetwork.h:23:33: error: 'debugStream' was not declared in this scope
 #define debugPrintLn(...) { if (debugStream) debugStream->println(__VA_ARGS__); }
                                 ^
/var/folders/cv/b296rfgs7t7bqsl6r03mg_4h0000gn/T/arduino_modified_sketch_641601/Downlink.ino:38:5: note: in expansion of macro 'debugPrintLn'
     debugPrintLn("Received " + String(downlinkBytes) + " bytes")
     ^
/Users/fokkezb/Documents/Arduino/libraries/TheThingsNetwork/src/TheThingsNetwork.h:24:31: error: 'debugStream' was not declared in this scope
 #define debugPrint(...) { if (debugStream) debugStream->print(__VA_ARGS__); }
                               ^
/var/folders/cv/b296rfgs7t7bqsl6r03mg_4h0000gn/T/arduino_modified_sketch_641601/Downlink.ino:41:7: note: in expansion of macro 'debugPrint'
       debugPrint(String(ttn.downlink[i]) + " ");
       ^
/Users/fokkezb/Documents/Arduino/libraries/TheThingsNetwork/src/TheThingsNetwork.h:23:33: error: 'debugStream' was not declared in this scope
 #define debugPrintLn(...) { if (debugStream) debugStream->println(__VA_ARGS__); }
                                 ^
/var/folders/cv/b296rfgs7t7bqsl6r03mg_4h0000gn/T/arduino_modified_sketch_641601/Downlink.ino:43:5: note: in expansion of macro 'debugPrintLn'
     debugPrintLn();
     ^
exit status 1

@johanstokking any idea?

Introduce join provisioning

When provisioning the device during runtime (e.g. through serial, WiFi or Bluetooth), we need to be able to set the AppEUI and AppKey, without having to join, or let it reset before join.

Proposed API:

bool provision(const byte appEui[8], const byte appKey[16]);

This method does a mac set appeui, mac set deveui and mac save, see command reference.

Then, you can call just join() in setup() or loop(), that uses the AppEUI and AppKey from memory, and thus only takes retries, as in #39

API: Add poll() as alias for sendBytes()

Messages can only be received in response to a message send.

To make this less confusing we should add a method poll([port]) which simply calls sendBytes() with a single byte. The developer won't have to construct a byte array and think about what to send if all he cares for is receive.

ttn.poll();

Node: Add temperature alerts

        sensor.setTLOWER(configuration.tempLowerLimit);
        sensor.setTUPPER(configuration.tempUpperLimit);
        sensor.setTCRIT(55); // risk of battery failure above 55C
        attachPCINT(digitalPinToPCINT(TMP_ALERT), temperatureInterrupt, FALLING);
        sensor.configureAlert();

API: Make reset() private and call it from join() and personalize()

All examples call ttn.reset() before they call ttn.join() or ttn.personalize(). If this is a required step, why not make it private and call it from these two methods.

Current:

ttn.reset();
ttn.personalize(devAddr, nwkSKey, appSKey);

Result:

ttn.personalize(devAddr, nwkSKey, appSKey);

To @johanstokking's note, if the device comes pre-flashed with keys you'd call the methods with no arguments:

while (ttn.join()) {
}

The methods should then not call reset() and skip the mac set commands.

Node: Setup TTN class via Node class

Since the Node class is specific to our device we can do something like the following and hide all Serial and TTN stuff:

#include <TheThingsNode.h>

const byte appEui[8] = { 0x70, 0xB3, 0xD5, 0x7E, 0xF0, 0x00, 0x00, 0x3E };
const byte appKey[16] = { 0x2A, 0xDE, 0x11, 0xDE, 0x50, 0x49, 0xB3, 0xBD, 0xFD, 0x1C, 0x75, 0x5C, 0xA4, 0xAC, 0x37, 0x7F };

TheThingsNode *node;

void setup() {

  node = TheThingsNode::setup();

  node->configNetwork(TTN_FP_EU868);

  node->ttn.showStatus();

  node->println("Hello");
}

What do you think @johanstokking ?

API: sendBytes should return confirmation or error codes

now sendBytes returns an integer containing the number of bytes received downlinkLength,
or in case of unconfirmed 0.

Is it easier for the enduser to still return an integer but composed of 2 parts :
msb signed byte containing the result of the send , eg 0 for ok, -1 for timeout, -2 for busy etc
lsb unsigned byte containing the received downlinkLength

Document global usage

The library comes with a bunch of examples but we need to describe the general usage (init, authenticate, send/receive) on the highest possible level in the README.

Track airtime used per day

TTN has a 30 second fair access policy. The nodes should be able to keep track of their daily airtime usage to ensure they don't violate it.

It would be nice if we implement this in the arduino-library, as an example for people how to track this.

The node can also make decisions (e.g. spreading factor) based on remaining airtime.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.