Skip to content

Commit

Permalink
Merge pull request #260 from jpmeijers/master
Browse files Browse the repository at this point in the history
Class C support
  • Loading branch information
johanstokking authored May 11, 2020
2 parents e3115cc + 93600ef commit 013598a
Show file tree
Hide file tree
Showing 7 changed files with 176 additions and 8 deletions.
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ script:
- test/verify arduino:avr:leonardo examples/PassThrough/PassThrough.ino
- test/verify arduino:avr:leonardo examples/QuickStart/QuickStart.ino
- test/verify arduino:avr:leonardo examples/Receive/Receive.ino
- test/verify arduino:avr:leonardo examples/ReceiveClassC/ReceiveClassC.ino
- test/verify arduino:avr:leonardo examples/SendOTAA/SendOTAA.ino
- test/verify arduino:avr:leonardo examples/Sensors/DHT/DHT.ino
- test/verify arduino:avr:leonardo examples/Sensors/LightSensor/LightSensor.ino
Expand Down
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
Johan Stokking <johan@thethingsnetwork.org>
Fokke Zandbergen <mail@fokkezb.nl>
Alessandro Blason <mrblason@gmail.com>
JP Meijers <git@jpmeijers.com>
19 changes: 18 additions & 1 deletion docs/TheThingsNetwork.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,14 +95,15 @@ See the [Receive](https://github.com/TheThingsNetwork/arduino-device-lib/blob/ma
Activate the device via OTAA (default).
```c
bool join(const char *appEui, const char *appKey, int8_t retries = -1, uint32_t retryDelay = 10000);
bool join(const char *appEui, const char *appKey, int8_t retries = -1, uint32_t retryDelay = 10000, lorawan_class = CLASS_A);
bool join(int8_t retries = -1, uint32_t retryDelay = 10000);
```

- `const char *appEui`: Application EUI the device is registered to.
- `const char *appKey`: Application Key assigned to the device.
- `int8_t retries = -1`: Number of times to retry after failed or unconfirmed join. Defaults to `-1` which means infinite.
- `uint32_t retryDelay = 10000`: Delay in ms between attempts. Defaults to 10 seconds.
- `lorawan_class = CLASS_A`: The LoRaWAN class to use for downlink message reception.

Returns `true` or `false` depending on whether it received confirmation that the activation was successful before the maximum number of attempts.

Expand All @@ -127,6 +128,22 @@ Call the method with no arguments if the device's LoRa module comes with pre-fla
See the [SendABP](https://github.com/TheThingsNetwork/arduino-device-lib/blob/master/examples/SendABP/SendABP.ino) example.
## Method: `setClass`
Change the downlink receive LoRaWAN Class. Class C is only supported in firmware version 1.0.5 and up. For other firmware versions this method will have no effect.
```c
bool setClass(lorawan_class p_lw_class);
```

- `lorawan_class p_lw_class`: The LoRaWAN class. Either `CLASS_A` or `CLASS_C`.

Returns `true` if the change was successful, or `false` if not successful.

The receive window only opens after a transmit. Therefore Class C receive will only start after calling `sendBytes()`.

See the [ReceiveClassC](https://github.com/TheThingsNetwork/arduino-device-lib/blob/master/examples/ReceiveClassC/ReceiveClassC.ino) example.

## Method: `sendBytes`

Send a message to the application using raw bytes.
Expand Down
60 changes: 60 additions & 0 deletions examples/ReceiveClassC/ReceiveClassC.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#include <TheThingsNetwork.h>

// Set your AppEUI and AppKey
const char *appEui = "0000000000000000";
const char *appKey = "00000000000000000000000000000000";

#define loraSerial Serial1
#define debugSerial Serial

// Replace REPLACE_ME with TTN_FP_EU868 or TTN_FP_US915
#define freqPlan REPLACE_ME

TheThingsNetwork ttn(loraSerial, debugSerial, freqPlan);

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

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

// Set callback for incoming messages
ttn.onMessage(message);

debugSerial.println("-- STATUS");
ttn.showStatus();

debugSerial.println("-- JOIN");
ttn.join(appEui, appKey, -1, 10000, CLASS_C);

// Class C RX only takes affect after a TX
uint8_t payload[] = {0x00};
ttn.sendBytes(payload, 1);
}

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

// Check for received data.
ttn.poll();

// When using Class C we can poll as quickly as we can, as we only check the serial buffer.
//delay(1000);
}

void message(const uint8_t *payload, size_t size, port_t port)
{
debugSerial.println("-- MESSAGE");
debugSerial.print("Received " + String(size) + " bytes on port " + String(port) + ":");

for (int i = 0; i < size; i++)
{
debugSerial.print(" " + String(payload[i]));
}

debugSerial.println();
}
6 changes: 6 additions & 0 deletions keywords.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ TheThingsNode KEYWORD1
ttn_port_t KEYWORD1
ttn_response_t KEYWORD1
ttn_fp_t KEYWORD1
lorawan_class_t KEYWORD1

#######################################
# Methods and Functions (KEYWORD2)
Expand All @@ -23,6 +24,7 @@ onMessage KEYWORD2
provision KEYWORD2
join KEYWORD2
personalize KEYWORD2
setClass KEYWORD2
sendBytes KEYWORD2
poll KEYWORD2

Expand Down Expand Up @@ -91,3 +93,7 @@ TTN_CYAN LITERAL1
TTN_MAGENTA LITERAL1
TTN_WHITE LITERAL1
TTN_BLACK LITERAL1

CLASS_A LITERAL1
CLASS_B LITERAL1
CLASS_C LITERAL1
83 changes: 78 additions & 5 deletions src/TheThingsNetwork.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -192,8 +192,9 @@ const char mac_rx2[] PROGMEM = "rx2";
const char mac_ch[] PROGMEM = "ch";
const char mac_gwnb[] PROGMEM = "gwnb";
const char mac_mrgn[] PROGMEM = "mrgn";
const char mac_class[] PROGMEM = "class";

const char *const mac_options[] PROGMEM = {mac_devaddr, mac_deveui, mac_appeui, mac_nwkskey, mac_appskey, mac_appkey, mac_pwridx, mac_dr, mac_adr, mac_bat, mac_retx, mac_linkchk, mac_rxdelay1, mac_rxdelay2, mac_band, mac_ar, mac_rx2, mac_ch, mac_gwnb, mac_mrgn};
const char *const mac_options[] PROGMEM = {mac_devaddr, mac_deveui, mac_appeui, mac_nwkskey, mac_appskey, mac_appkey, mac_pwridx, mac_dr, mac_adr, mac_bat, mac_retx, mac_linkchk, mac_rxdelay1, mac_rxdelay2, mac_band, mac_ar, mac_rx2, mac_ch, mac_gwnb, mac_mrgn, mac_class};

#define MAC_DEVADDR 0
#define MAC_DEVEUI 1
Expand All @@ -215,6 +216,7 @@ const char *const mac_options[] PROGMEM = {mac_devaddr, mac_deveui, mac_appeui,
#define MAC_CH 17
#define MAC_GWNB 18
#define MAC_MRGN 19
#define MAC_CLASS 20

const char mac_join_mode_otaa[] PROGMEM = "otaa";
const char mac_join_mode_abp[] PROGMEM = "abp";
Expand Down Expand Up @@ -545,9 +547,36 @@ bool TheThingsNetwork::join(int8_t retries, uint32_t retryDelay)
return false;
}

bool TheThingsNetwork::join(const char *appEui, const char *appKey, int8_t retries, uint32_t retryDelay)
bool TheThingsNetwork::setClass(lorawan_class_t p_lw_class)
{
return provision(appEui, appKey) && join(retries, retryDelay);
switch(p_lw_class)
{

case CLASS_A:
{
lw_class = p_lw_class;
return sendMacSet(MAC_CLASS, "a");
}

// case CLASS_B: // Not yet supported. Use default case.

case CLASS_C:
{
bool result = sendMacSet(MAC_CLASS, "c");
// Older firmware does not support Class C. If setting change fails, keep on using Class A.
if(result) lw_class = p_lw_class;
return result;
}

default:
return false;

}
}

bool TheThingsNetwork::join(const char *appEui, const char *appKey, int8_t retries, uint32_t retryDelay, lorawan_class_t p_lw_class)
{
return provision(appEui, appKey) && join(retries, retryDelay) && setClass(p_lw_class);
}

ttn_response_t TheThingsNetwork::sendBytes(const uint8_t *payload, size_t length, port_t port, bool confirm, uint8_t sf)
Expand Down Expand Up @@ -603,8 +632,52 @@ ttn_response_t TheThingsNetwork::sendBytes(const uint8_t *payload, size_t length

ttn_response_t TheThingsNetwork::poll(port_t port, bool confirm)
{
uint8_t payload[] = {0x00};
return sendBytes(payload, 1, port, confirm);
switch(lw_class)
{

case CLASS_A:
{
// Class A: send uplink and wait for rx windows
uint8_t payload[] = {0x00};
return sendBytes(payload, 1, port, confirm);
}

// case CLASS_B: // Not yet supported. Use default case.

case CLASS_C:
{
// Class C: check rx buffer for any recevied data
memset(buffer, 0, sizeof(buffer));

long timeout = this->modemStream->getTimeout();
this->modemStream->setTimeout(100);
this->modemStream->readBytesUntil('\n', buffer, sizeof(buffer));
this->modemStream->setTimeout(timeout);

if (pgmstrcmp(buffer, CMP_MAC_RX) == 0)
{
port_t downlinkPort = receivedPort(buffer + 7);
char *data = buffer + 7 + digits(downlinkPort) + 1;
size_t downlinkLength = strlen(data) / 2;
if (downlinkLength > 0)
{
uint8_t downlink[downlinkLength];
for (size_t i = 0, d = 0; i < downlinkLength; i++, d += 2)
{
downlink[i] = TTN_HEX_PAIR_TO_BYTE(data[d], data[d + 1]);
}
if (messageCallback)
{
messageCallback(downlink, downlinkLength, downlinkPort);
}
}
return TTN_SUCCESSFUL_RECEIVE;
}
}

default:
return TTN_UNSUCESSFUL_RECEIVE;
}
}

void TheThingsNetwork::showStatus()
Expand Down
14 changes: 12 additions & 2 deletions src/TheThingsNetwork.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ enum ttn_response_t
TTN_ERROR_SEND_COMMAND_FAILED = (-1),
TTN_ERROR_UNEXPECTED_RESPONSE = (-10),
TTN_SUCCESSFUL_TRANSMISSION = 1,
TTN_SUCCESSFUL_RECEIVE = 2
TTN_SUCCESSFUL_RECEIVE = 2,
TTN_UNSUCESSFUL_RECEIVE = 3
};

enum ttn_fp_t
Expand All @@ -47,6 +48,13 @@ enum ttn_fp_t
TTN_FP_IN865_867
};

enum lorawan_class_t
{
CLASS_A,
CLASS_B,
CLASS_C
};

class TheThingsNetwork
{
private:
Expand All @@ -59,6 +67,7 @@ class TheThingsNetwork
char buffer[512];
bool baudDetermined = false;
void (*messageCallback)(const uint8_t *payload, size_t size, port_t port);
lorawan_class_t lw_class = CLASS_A;

void clearReadBuffer();
size_t readLine(char *buffer, size_t size, uint8_t attempts = 3);
Expand Down Expand Up @@ -99,10 +108,11 @@ class TheThingsNetwork
uint16_t getVDD();
void onMessage(void (*cb)(const uint8_t *payload, size_t size, port_t port));
bool provision(const char *appEui, const char *appKey);
bool join(const char *appEui, const char *appKey, int8_t retries = -1, uint32_t retryDelay = 10000);
bool join(const char *appEui, const char *appKey, int8_t retries = -1, uint32_t retryDelay = 10000, lorawan_class_t = CLASS_A);
bool join(int8_t retries = -1, uint32_t retryDelay = 10000);
bool personalize(const char *devAddr, const char *nwkSKey, const char *appSKey);
bool personalize();
bool setClass(lorawan_class_t p_lw_class);
ttn_response_t sendBytes(const uint8_t *payload, size_t length, port_t port = 1, bool confirm = false, uint8_t sf = 0);
ttn_response_t poll(port_t port = 1, bool confirm = false);
void sleep(uint32_t mseconds);
Expand Down

0 comments on commit 013598a

Please sign in to comment.