Skip to content

Commit 5f71b57

Browse files
committed
Add Class C support
1 parent 8a7ee08 commit 5f71b57

File tree

6 files changed

+158
-8
lines changed

6 files changed

+158
-8
lines changed

.travis.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ script:
2323
- test/verify arduino:avr:leonardo examples/PassThrough/PassThrough.ino
2424
- test/verify arduino:avr:leonardo examples/QuickStart/QuickStart.ino
2525
- test/verify arduino:avr:leonardo examples/Receive/Receive.ino
26+
- test/verify arduino:avr:leonardo examples/ReceiveClassC/ReceiveClassC.ino
2627
- test/verify arduino:avr:leonardo examples/SendOTAA/SendOTAA.ino
2728
- test/verify arduino:avr:leonardo examples/Sensors/DHT/DHT.ino
2829
- test/verify arduino:avr:leonardo examples/Sensors/LightSensor/LightSensor.ino

docs/TheThingsNetwork.md

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,14 +95,15 @@ See the [Receive](https://github.com/TheThingsNetwork/arduino-device-lib/blob/ma
9595
Activate the device via OTAA (default).
9696
9797
```c
98-
bool join(const char *appEui, const char *appKey, int8_t retries = -1, uint32_t retryDelay = 10000);
98+
bool join(const char *appEui, const char *appKey, int8_t retries = -1, uint32_t retryDelay = 10000, lorawan_class = CLASS_A);
9999
bool join(int8_t retries = -1, uint32_t retryDelay = 10000);
100100
```
101101

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

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

@@ -127,6 +128,22 @@ Call the method with no arguments if the device's LoRa module comes with pre-fla
127128
128129
See the [SendABP](https://github.com/TheThingsNetwork/arduino-device-lib/blob/master/examples/SendABP/SendABP.ino) example.
129130
131+
## Method: `setClass`
132+
133+
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 nto affect.
134+
135+
```c
136+
bool setClass(lorawan_class p_lw_class);
137+
```
138+
139+
- `lorawan_class p_lw_class`: The LoRaWAN class. Either `CLASS_A` or `CLASS_C`.
140+
141+
Returns `true` if the change was successful, or `false` if not successful.
142+
143+
The receive window only opens after a transmit. Therefore Class C receive will only start after calling `sendBytes()`.
144+
145+
See the [ReceiveClassC](https://github.com/TheThingsNetwork/arduino-device-lib/blob/master/examples/ReceiveClassC/ReceiveClassC.ino) example.
146+
130147
## Method: `sendBytes`
131148

132149
Send a message to the application using raw bytes.
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
#include <TheThingsNetwork.h>
2+
3+
// Set your AppEUI and AppKey
4+
//const char *appEui = "0004A30B001A5756";
5+
const char *appEui = "0004A30B001EC935";
6+
const char *appKey = "657CBC96D7E6F3D9CD9762CFF95A18E8";
7+
8+
9+
#define loraSerial Serial2
10+
#define debugSerial SerialUSB
11+
12+
// Replace REPLACE_ME with TTN_FP_EU868 or TTN_FP_US915
13+
#define freqPlan TTN_FP_EU868
14+
15+
TheThingsNetwork ttn(loraSerial, debugSerial, freqPlan);
16+
17+
void setup()
18+
{
19+
loraSerial.begin(57600);
20+
debugSerial.begin(9600);
21+
22+
// Wait a maximum of 10s for Serial Monitor
23+
while (!debugSerial && millis() < 10000)
24+
;
25+
26+
// Set callback for incoming messages
27+
ttn.onMessage(message);
28+
29+
debugSerial.println("-- STATUS");
30+
ttn.showStatus();
31+
32+
debugSerial.println("-- JOIN");
33+
ttn.join(appEui, appKey, -1, 10000, CLASS_C);
34+
35+
// Class C RX only takes affect after a TX
36+
uint8_t payload[] = {0x00};
37+
ttn.sendBytes(payload, 1);
38+
}
39+
40+
void loop()
41+
{
42+
debugSerial.println("-- LOOP");
43+
44+
// Send single byte to poll for incoming messages
45+
ttn.poll();
46+
47+
// When using Class C we can poll as quickly as we can, as we only check the serial buffer.
48+
//delay(1000);
49+
}
50+
51+
void message(const uint8_t *payload, size_t size, port_t port)
52+
{
53+
debugSerial.println("-- MESSAGE");
54+
debugSerial.print("Received " + String(size) + " bytes on port " + String(port) + ":");
55+
56+
for (int i = 0; i < size; i++)
57+
{
58+
debugSerial.print(" " + String(payload[i]));
59+
}
60+
61+
debugSerial.println();
62+
}

keywords.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ TheThingsNode KEYWORD1
1313
ttn_port_t KEYWORD1
1414
ttn_response_t KEYWORD1
1515
ttn_fp_t KEYWORD1
16+
lorawan_class KEYWORD1
1617

1718
#######################################
1819
# Methods and Functions (KEYWORD2)
@@ -23,6 +24,7 @@ onMessage KEYWORD2
2324
provision KEYWORD2
2425
join KEYWORD2
2526
personalize KEYWORD2
27+
setClass KEYWORD2
2628
sendBytes KEYWORD2
2729
poll KEYWORD2
2830

@@ -91,3 +93,7 @@ TTN_CYAN LITERAL1
9193
TTN_MAGENTA LITERAL1
9294
TTN_WHITE LITERAL1
9395
TTN_BLACK LITERAL1
96+
97+
CLASS_A LITERAL1
98+
CLASS_B LITERAL1
99+
CLASS_C LITERAL1

src/TheThingsNetwork.cpp

Lines changed: 59 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -192,8 +192,9 @@ const char mac_rx2[] PROGMEM = "rx2";
192192
const char mac_ch[] PROGMEM = "ch";
193193
const char mac_gwnb[] PROGMEM = "gwnb";
194194
const char mac_mrgn[] PROGMEM = "mrgn";
195+
const char mac_class[] PROGMEM = "class";
195196

196-
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};
197+
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};
197198

198199
#define MAC_DEVADDR 0
199200
#define MAC_DEVEUI 1
@@ -215,6 +216,7 @@ const char *const mac_options[] PROGMEM = {mac_devaddr, mac_deveui, mac_appeui,
215216
#define MAC_CH 17
216217
#define MAC_GWNB 18
217218
#define MAC_MRGN 19
219+
#define MAC_CLASS 20
218220

219221
const char mac_join_mode_otaa[] PROGMEM = "otaa";
220222
const char mac_join_mode_abp[] PROGMEM = "abp";
@@ -545,9 +547,25 @@ bool TheThingsNetwork::join(int8_t retries, uint32_t retryDelay)
545547
return false;
546548
}
547549

548-
bool TheThingsNetwork::join(const char *appEui, const char *appKey, int8_t retries, uint32_t retryDelay)
550+
bool TheThingsNetwork::setClass(lorawan_class p_lw_class)
549551
{
550-
return provision(appEui, appKey) && join(retries, retryDelay);
552+
if(p_lw_class == CLASS_C) {
553+
bool result = sendMacSet(MAC_CLASS, "c");
554+
// Only remember Class C if set was successful.
555+
// Older firmware does not support class c, so keep on using Class A logic.
556+
if(result) lw_class = p_lw_class;
557+
return result;
558+
} else if(p_lw_class == CLASS_A) {
559+
lw_class = p_lw_class;
560+
return sendMacSet(MAC_CLASS, "a");
561+
} else {
562+
return false;
563+
}
564+
}
565+
566+
bool TheThingsNetwork::join(const char *appEui, const char *appKey, int8_t retries, uint32_t retryDelay, lorawan_class p_lw_class)
567+
{
568+
return provision(appEui, appKey) && join(retries, retryDelay) && setClass(p_lw_class);
551569
}
552570

553571
ttn_response_t TheThingsNetwork::sendBytes(const uint8_t *payload, size_t length, port_t port, bool confirm, uint8_t sf)
@@ -603,8 +621,44 @@ ttn_response_t TheThingsNetwork::sendBytes(const uint8_t *payload, size_t length
603621

604622
ttn_response_t TheThingsNetwork::poll(port_t port, bool confirm)
605623
{
606-
uint8_t payload[] = {0x00};
607-
return sendBytes(payload, 1, port, confirm);
624+
if(lw_class == CLASS_C) {
625+
// If class c, check rx buffer for any recevied data
626+
627+
memset(buffer, 0, sizeof(buffer));
628+
629+
long timeout = this->modemStream->getTimeout();
630+
this->modemStream->setTimeout(100);
631+
this->modemStream->readBytesUntil('\n', buffer, sizeof(buffer));
632+
this->modemStream->setTimeout(timeout);
633+
634+
if (pgmstrcmp(buffer, CMP_MAC_RX) == 0)
635+
{
636+
port_t downlinkPort = receivedPort(buffer + 7);
637+
char *data = buffer + 7 + digits(downlinkPort) + 1;
638+
size_t downlinkLength = strlen(data) / 2;
639+
if (downlinkLength > 0)
640+
{
641+
uint8_t downlink[downlinkLength];
642+
for (size_t i = 0, d = 0; i < downlinkLength; i++, d += 2)
643+
{
644+
downlink[i] = TTN_HEX_PAIR_TO_BYTE(data[d], data[d + 1]);
645+
}
646+
if (messageCallback)
647+
{
648+
messageCallback(downlink, downlinkLength, downlinkPort);
649+
}
650+
}
651+
return TTN_SUCCESSFUL_RECEIVE;
652+
}
653+
}
654+
else if(lw_class == CLASS_A) {
655+
// If class a send uplink and wait for rx windows
656+
uint8_t payload[] = {0x00};
657+
return sendBytes(payload, 1, port, confirm);
658+
}
659+
else {
660+
return TTN_UNSUCESSFUL_RECEIVE;
661+
}
608662
}
609663

610664
void TheThingsNetwork::showStatus()

src/TheThingsNetwork.h

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ enum ttn_response_t
3333
TTN_ERROR_SEND_COMMAND_FAILED = (-1),
3434
TTN_ERROR_UNEXPECTED_RESPONSE = (-10),
3535
TTN_SUCCESSFUL_TRANSMISSION = 1,
36-
TTN_SUCCESSFUL_RECEIVE = 2
36+
TTN_SUCCESSFUL_RECEIVE = 2,
37+
TTN_UNSUCESSFUL_RECEIVE = 3
3738
};
3839

3940
enum ttn_fp_t
@@ -47,6 +48,13 @@ enum ttn_fp_t
4748
TTN_FP_IN865_867
4849
};
4950

51+
enum lorawan_class
52+
{
53+
CLASS_A,
54+
CLASS_B,
55+
CLASS_C
56+
};
57+
5058
class TheThingsNetwork
5159
{
5260
private:
@@ -59,6 +67,7 @@ class TheThingsNetwork
5967
char buffer[512];
6068
bool baudDetermined = false;
6169
void (*messageCallback)(const uint8_t *payload, size_t size, port_t port);
70+
lorawan_class lw_class = CLASS_A;
6271

6372
void clearReadBuffer();
6473
size_t readLine(char *buffer, size_t size, uint8_t attempts = 3);
@@ -99,10 +108,11 @@ class TheThingsNetwork
99108
uint16_t getVDD();
100109
void onMessage(void (*cb)(const uint8_t *payload, size_t size, port_t port));
101110
bool provision(const char *appEui, const char *appKey);
102-
bool join(const char *appEui, const char *appKey, int8_t retries = -1, uint32_t retryDelay = 10000);
111+
bool join(const char *appEui, const char *appKey, int8_t retries = -1, uint32_t retryDelay = 10000, lorawan_class = CLASS_A);
103112
bool join(int8_t retries = -1, uint32_t retryDelay = 10000);
104113
bool personalize(const char *devAddr, const char *nwkSKey, const char *appSKey);
105114
bool personalize();
115+
bool setClass(lorawan_class p_lw_class);
106116
ttn_response_t sendBytes(const uint8_t *payload, size_t length, port_t port = 1, bool confirm = false, uint8_t sf = 0);
107117
ttn_response_t poll(port_t port = 1, bool confirm = false);
108118
void sleep(uint32_t mseconds);

0 commit comments

Comments
 (0)