Skip to content

Commit 013598a

Browse files
Merge pull request TheThingsNetwork#260 from jpmeijers/master
Class C support
2 parents e3115cc + 93600ef commit 013598a

File tree

7 files changed

+176
-8
lines changed

7 files changed

+176
-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

AUTHORS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
Johan Stokking <johan@thethingsnetwork.org>
22
Fokke Zandbergen <mail@fokkezb.nl>
33
Alessandro Blason <mrblason@gmail.com>
4+
JP Meijers <git@jpmeijers.com>

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

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_t 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: 78 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,36 @@ 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_t p_lw_class)
549551
{
550-
return provision(appEui, appKey) && join(retries, retryDelay);
552+
switch(p_lw_class)
553+
{
554+
555+
case CLASS_A:
556+
{
557+
lw_class = p_lw_class;
558+
return sendMacSet(MAC_CLASS, "a");
559+
}
560+
561+
// case CLASS_B: // Not yet supported. Use default case.
562+
563+
case CLASS_C:
564+
{
565+
bool result = sendMacSet(MAC_CLASS, "c");
566+
// Older firmware does not support Class C. If setting change fails, keep on using Class A.
567+
if(result) lw_class = p_lw_class;
568+
return result;
569+
}
570+
571+
default:
572+
return false;
573+
574+
}
575+
}
576+
577+
bool TheThingsNetwork::join(const char *appEui, const char *appKey, int8_t retries, uint32_t retryDelay, lorawan_class_t p_lw_class)
578+
{
579+
return provision(appEui, appKey) && join(retries, retryDelay) && setClass(p_lw_class);
551580
}
552581

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

604633
ttn_response_t TheThingsNetwork::poll(port_t port, bool confirm)
605634
{
606-
uint8_t payload[] = {0x00};
607-
return sendBytes(payload, 1, port, confirm);
635+
switch(lw_class)
636+
{
637+
638+
case CLASS_A:
639+
{
640+
// Class A: send uplink and wait for rx windows
641+
uint8_t payload[] = {0x00};
642+
return sendBytes(payload, 1, port, confirm);
643+
}
644+
645+
// case CLASS_B: // Not yet supported. Use default case.
646+
647+
case CLASS_C:
648+
{
649+
// Class C: check rx buffer for any recevied data
650+
memset(buffer, 0, sizeof(buffer));
651+
652+
long timeout = this->modemStream->getTimeout();
653+
this->modemStream->setTimeout(100);
654+
this->modemStream->readBytesUntil('\n', buffer, sizeof(buffer));
655+
this->modemStream->setTimeout(timeout);
656+
657+
if (pgmstrcmp(buffer, CMP_MAC_RX) == 0)
658+
{
659+
port_t downlinkPort = receivedPort(buffer + 7);
660+
char *data = buffer + 7 + digits(downlinkPort) + 1;
661+
size_t downlinkLength = strlen(data) / 2;
662+
if (downlinkLength > 0)
663+
{
664+
uint8_t downlink[downlinkLength];
665+
for (size_t i = 0, d = 0; i < downlinkLength; i++, d += 2)
666+
{
667+
downlink[i] = TTN_HEX_PAIR_TO_BYTE(data[d], data[d + 1]);
668+
}
669+
if (messageCallback)
670+
{
671+
messageCallback(downlink, downlinkLength, downlinkPort);
672+
}
673+
}
674+
return TTN_SUCCESSFUL_RECEIVE;
675+
}
676+
}
677+
678+
default:
679+
return TTN_UNSUCESSFUL_RECEIVE;
680+
}
608681
}
609682

610683
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_t
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_t 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_t = 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_t 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)