Skip to content

Commit 4eb8762

Browse files
authored
Improve handling of needsHardReset flag and add method to check valid module connected to serial port (#282)
* Removed reset of needsHardReset flag in reset() function. Added checkValidModuleConnected() to check for correct wiring and supported module. Added getVersion to obtain response of sys get ver. Updated documentation. Added CheckModule example to showcase usage of checkValidModuleConnected(). * Updated naming scheme to match TTN camelCase convention
1 parent c5d62cf commit 4eb8762

File tree

4 files changed

+170
-13
lines changed

4 files changed

+170
-13
lines changed

docs/TheThingsNetwork.md

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,14 @@ Gets the provisioned AppEUI. The AppEUI is set using `provision()` or `join()`.
5454
size_t getAppEui(char *buffer, size_t size);
5555
```
5656
57+
## Method: `getVersion`
58+
59+
Gets the response from a `sys get ver` command (i.e. the hardware model, the the software version, etc.).
60+
61+
```c
62+
size_t getVersion(char *buffer, size_t size);
63+
```
64+
5765
## Method: `showStatus`
5866

5967
Writes information about the device and LoRa module to `debugStream`.
@@ -114,13 +122,14 @@ Call the method without the first two arguments if the device's LoRa module come
114122
Activate the device via ABP.
115123

116124
```c
117-
bool personalize(const char *devAddr, const char *nwkSKey, const char *appSKey);
125+
bool personalize(const char *devAddr, const char *nwkSKey, const char *appSKey, bool reset_first);
118126
bool personalize();
119127
```
120128
121129
- `const char *devAddr`: Device Address assigned to the device.
122130
- `const char *nwkSKey`: Network Session Key assigned to the device for identification.
123131
- `const char *appSKey`: Application Session Key assigned to the device for encryption.
132+
- `bool reset_first`: Soft reset the module before performing any other action. Default is `true`.
124133
125134
Returns `true` or `false` depending on whether the activation was successful.
126135
@@ -192,11 +201,12 @@ See the [Receive](https://github.com/TheThingsNetwork/arduino-device-lib/blob/ma
192201
Sets the information needed to activate the device via OTAA, without actually activating. Call join() without the first 2 arguments to activate.
193202

194203
```c
195-
bool provision(const char *appEui, const char *appKey);
204+
bool provision(const char *appEui, const char *appKey, bool reset_first);
196205
```
197206
198207
- `const char *appEui`: Application Identifier for the device.
199208
- `const char *appKey`: Application Key assigned to the device.
209+
- `bool reset_first`: Soft reset the module before performing any other action. Default is `true`.
200210
201211
## Method: `sleep`
202212
@@ -434,6 +444,28 @@ When transmitting in LoRaWan, we usually operate on a TX window and two RX windo
434444
bool setRX1Delay(uint16_t delay);
435445
```
436446

447+
## Method: `checkValidModuleConnected`
448+
449+
Checks if a valid module is connected to the configured serial port. Useful to check for connectivity with a supported module before performing any other actions.
450+
451+
```c
452+
bool checkValidModuleConnected(bool autobaud_first);
453+
```
454+
455+
- `bool autobaud_first`: Perform a call to `autoBaud()` before checking connection. Default is `false`.
456+
457+
Returns:
458+
459+
* `false` if no response was received (i.e. `needsHardReset` is `true`)
460+
* `false` if the module is invalid (unsupported), i.e. **not** one of the following:
461+
* `RN2483`
462+
* `RN2483A`
463+
* `RN2903`
464+
* `RN2903AS`
465+
* `true` if the module responded (i.e. `needsHardReset` is `false`) and is valid (supported).
466+
467+
See the [CheckModule](https://github.com/TheThingsNetwork/arduino-device-lib/blob/master/examples/CheckModule/CheckModule.ino) example.
468+
437469
# Additional for statistics
438470
439471
## Method: `getRSSI`

examples/CheckModule/CheckModule.ino

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
#include <TheThingsNetwork.h>
2+
3+
// Set your DevAddr, NwkSKey, AppSKey and the frequency plan
4+
const char *devAddr = "00000000";
5+
const char *nwkSKey = "00000000000000000000000000000000";
6+
const char *appSKey = "00000000000000000000000000000000";
7+
8+
#define loraSerial Serial1
9+
#define debugSerial Serial
10+
11+
// Replace REPLACE_ME with TTN_FP_EU868 or TTN_FP_US915
12+
#define freqPlan REPLACE_ME
13+
14+
TheThingsNetwork ttn(loraSerial, debugSerial, freqPlan);
15+
16+
void setup()
17+
{
18+
loraSerial.begin(57600);
19+
debugSerial.begin(9600);
20+
21+
// Wait a maximum of 10s for Serial Monitor
22+
while (!debugSerial && millis() < 10000)
23+
;
24+
25+
// RN2XX3 reset pin connected to Arduino pin 12
26+
pinMode(12, OUTPUT);
27+
digitalWrite(12, HIGH);
28+
// hard reset module and wait for startup
29+
debugSerial.println("-- CHECK COMM");
30+
ttn.resetHard(12);
31+
delay(100);
32+
// check if a valid module responded
33+
// (if no module is connected or wiring is bad, checkValidModuleConnected() will
34+
// take about ~30s to return (another ~30s if autobaud_first is true))
35+
if(!ttn.checkValidModuleConnected(true))
36+
{
37+
if(ttn.needsHardReset)
38+
{
39+
debugSerial.println("Module unresponsive, please power cycle or hard reset board!");
40+
}
41+
else
42+
{
43+
debugSerial.println("Module unsupported!"); // module must be RN2483, RN2483A, RN2903, RN2903AS
44+
}
45+
while(true) // stop code execution
46+
{
47+
;
48+
}
49+
}
50+
51+
// do an ABP join
52+
debugSerial.println("-- PERSONALIZE");
53+
// false is added as fourth argument to the personalize() call so that it
54+
// does not perform a soft reset, because the module was already hard reset before via pin 12.
55+
ttn.personalize(devAddr, nwkSKey, appSKey, false);
56+
57+
debugSerial.println("-- STATUS");
58+
ttn.showStatus();
59+
}
60+
61+
void loop()
62+
{
63+
debugSerial.println("-- LOOP");
64+
65+
// Prepare payload of 1 byte to indicate LED status
66+
byte payload[1];
67+
payload[0] = (digitalRead(LED_BUILTIN) == HIGH) ? 1 : 0;
68+
69+
// Send it off
70+
ttn.sendBytes(payload, sizeof(payload));
71+
72+
delay(10000);
73+
}

src/TheThingsNetwork.cpp

Lines changed: 59 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,11 @@ const char mac_tx_ok[] PROGMEM = "mac_tx_ok";
2525
const char mac_rx[] PROGMEM = "mac_rx";
2626
const char mac_err[] PROGMEM = "mac_err";
2727
const char rn2483[] PROGMEM = "RN2483";
28+
const char rn2483a[] PROGMEM = "RN2483A";
29+
const char rn2903[] PROGMEM = "RN2903";
30+
const char rn2903as[] PROGMEM = "RN2903AS";
2831

29-
const char *const compare_table[] PROGMEM = {ok, on, off, accepted, mac_tx_ok, mac_rx, mac_err, rn2483};
32+
const char *const compare_table[] PROGMEM = {ok, on, off, accepted, mac_tx_ok, mac_rx, mac_err, rn2483, rn2483a, rn2903, rn2903as};
3033

3134
#define CMP_OK 0
3235
#define CMP_ON 1
@@ -36,6 +39,9 @@ const char *const compare_table[] PROGMEM = {ok, on, off, accepted, mac_tx_ok, m
3639
#define CMP_MAC_RX 5
3740
#define CMP_MAC_ERR 6
3841
#define CMP_RN2483 7
42+
#define CMP_RN2483A 8
43+
#define CMP_RN2903 9
44+
#define CMP_RN2903AS 10
3945

4046
// CMP OK
4147
const char busy[] PROGMEM = "busy";
@@ -109,8 +115,9 @@ const char response_is_not_ok[] PROGMEM = "Response is not OK: ";
109115
const char error_key_length[] PROGMEM = "One or more keys are of invalid length.";
110116
const char check_configuration[] PROGMEM = "Check your coverage, keys and backend status.";
111117
const char no_response[] PROGMEM = "No response from RN module.";
118+
const char invalid_module[] PROGMEM = "Invalid module (must be RN2xx3[xx]).";
112119

113-
const char *const error_msg[] PROGMEM = {invalid_sf, invalid_fp, unexpected_response, send_command_failed, join_failed, join_not_accepted, personalize_not_accepted, response_is_not_ok, error_key_length, check_configuration, no_response};
120+
const char *const error_msg[] PROGMEM = {invalid_sf, invalid_fp, unexpected_response, send_command_failed, join_failed, join_not_accepted, personalize_not_accepted, response_is_not_ok, error_key_length, check_configuration, no_response, invalid_module};
114121

115122
#define ERR_INVALID_SF 0
116123
#define ERR_INVALID_FP 1
@@ -123,18 +130,21 @@ const char *const error_msg[] PROGMEM = {invalid_sf, invalid_fp, unexpected_resp
123130
#define ERR_KEY_LENGTH 8
124131
#define ERR_CHECK_CONFIGURATION 9
125132
#define ERR_NO_RESPONSE 10
133+
#define ERR_INVALID_MODULE 11
126134

127135
const char personalize_accepted[] PROGMEM = "Personalize accepted. Status: ";
128136
const char join_accepted[] PROGMEM = "Join accepted. Status: ";
129137
const char successful_transmission[] PROGMEM = "Successful transmission";
130138
const char successful_transmission_received[] PROGMEM = "Successful transmission. Received ";
139+
const char valid_module[] PROGMEM = "Valid module connected.";
131140

132-
const char *const success_msg[] PROGMEM = {personalize_accepted, join_accepted, successful_transmission, successful_transmission_received};
141+
const char *const success_msg[] PROGMEM = {personalize_accepted, join_accepted, successful_transmission, successful_transmission_received, valid_module};
133142

134143
#define SCS_PERSONALIZE_ACCEPTED 0
135144
#define SCS_JOIN_ACCEPTED 1
136145
#define SCS_SUCCESSFUL_TRANSMISSION 2
137146
#define SCS_SUCCESSFUL_TRANSMISSION_RECEIVED 3
147+
#define SCS_VALID_MODULE 4
138148

139149
const char radio_prefix[] PROGMEM = "radio";
140150
const char radio_set[] PROGMEM = "set";
@@ -381,6 +391,11 @@ size_t TheThingsNetwork::getHardwareEui(char *buffer, size_t size)
381391
return readResponse(SYS_TABLE, SYS_TABLE, SYS_GET_HWEUI, buffer, size);
382392
}
383393

394+
size_t TheThingsNetwork::getVersion(char *buffer, size_t size)
395+
{
396+
return readResponse(SYS_TABLE, SYS_TABLE, SYS_GET_VER, buffer, size);
397+
}
398+
384399
uint16_t TheThingsNetwork::getVDD()
385400
{
386401
if (readResponse(SYS_TABLE, SYS_TABLE, SYS_GET_VDD, buffer, sizeof(buffer)) > 0) {
@@ -632,22 +647,25 @@ void TheThingsNetwork::autoBaud()
632647

633648
void TheThingsNetwork::reset(bool adr)
634649
{
650+
// autobaud and send "sys reset"
635651
autoBaud();
636652
readResponse(SYS_TABLE, SYS_RESET, buffer, sizeof(buffer));
637653

654+
// autobaud (again, because baudrate was reset with "sys reset") and get HW model and SW version
638655
autoBaud();
639-
readResponse(SYS_TABLE, SYS_TABLE, SYS_GET_VER, buffer, sizeof(buffer));
656+
getVersion(buffer, sizeof(buffer));
640657

641658
// buffer contains "RN2xx3[xx] x.x.x ...", splitting model from version
642659
char *model = strtok(buffer, " ");
643660
debugPrintIndex(SHOW_MODEL, model);
644661
char *version = strtok(NULL, " ");
645662
debugPrintIndex(SHOW_VERSION, version);
646663

664+
// set DEVEUI as HWEUI
647665
readResponse(SYS_TABLE, SYS_TABLE, SYS_GET_HWEUI, buffer, sizeof(buffer));
648666
sendMacSet(MAC_DEVEUI, buffer);
667+
// set ADR
649668
setADR(adr);
650-
this->needsHardReset = false;
651669
}
652670

653671
void TheThingsNetwork::resetHard(uint8_t resetPin){
@@ -671,9 +689,11 @@ void TheThingsNetwork::onMessage(void (*cb)(const uint8_t *payload, size_t size,
671689
messageCallback = cb;
672690
}
673691

674-
bool TheThingsNetwork::personalize(const char *devAddr, const char *nwkSKey, const char *appSKey)
692+
bool TheThingsNetwork::personalize(const char *devAddr, const char *nwkSKey, const char *appSKey, bool resetFirst)
675693
{
676-
reset(adr);
694+
if(resetFirst) {
695+
reset(adr);
696+
}
677697
if (strlen(devAddr) != 8 || strlen(appSKey) != 32 || strlen(nwkSKey) != 32)
678698
{
679699
debugPrintMessage(ERR_MESSAGE, ERR_KEY_LENGTH);
@@ -703,9 +723,11 @@ bool TheThingsNetwork::personalize()
703723
return true;
704724
}
705725

706-
bool TheThingsNetwork::provision(const char *appEui, const char *appKey)
726+
bool TheThingsNetwork::provision(const char *appEui, const char *appKey, bool resetFirst)
707727
{
708-
reset(adr);
728+
if(resetFirst) {
729+
reset(adr);
730+
}
709731
if (strlen(appEui) != 16 || strlen(appKey) != 32)
710732
{
711733
debugPrintMessage(ERR_MESSAGE, ERR_KEY_LENGTH);
@@ -917,6 +939,34 @@ void TheThingsNetwork::showStatus()
917939
debugPrintIndex(SHOW_RX_DELAY_2, buffer);
918940
}
919941

942+
bool TheThingsNetwork::checkValidModuleConnected(bool autoBaudFirst)
943+
{
944+
// check if we want to autobaud first
945+
if(autoBaudFirst)
946+
{
947+
autoBaud();
948+
}
949+
// send "sys get ver" to check if (and what) module is connected
950+
getVersion(buffer, sizeof(buffer));
951+
// check if we got a response (whatever it might be)
952+
// needsHardReset flag is set by readLine() (called at some point down the line by getVersion())
953+
if(this->needsHardReset)
954+
{
955+
return false; // no response
956+
}
957+
// buffer contains "RN2xx3[xx] x.x.x ...", getting only model (RN2xx3[xx])
958+
char *model = strtok(buffer, " ");
959+
debugPrintIndex(SHOW_MODEL, model);
960+
// check if module is valid (must be RN2483, RN2483A, RN2903 or RN2903AS)
961+
if(pgmstrcmp(model, CMP_RN2483) == 0 || pgmstrcmp(model, CMP_RN2483A) == 0 || pgmstrcmp(model, CMP_RN2903) == 0 || pgmstrcmp(model, CMP_RN2903AS) == 0)
962+
{
963+
debugPrintMessage(SUCCESS_MESSAGE, SCS_VALID_MODULE);
964+
return true; // module responded and is valid (recognized/supported)
965+
}
966+
debugPrintMessage(ERR_MESSAGE, ERR_INVALID_MODULE);
967+
return false; // module responded but is invalid (unrecognized/unsupported)
968+
}
969+
920970
void TheThingsNetwork::configureEU868()
921971
{
922972
sendMacSet(MAC_RX2, 3, 869525000);

src/TheThingsNetwork.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ class TheThingsNetwork
140140
void showStatus();
141141
size_t getHardwareEui(char *buffer, size_t size);
142142
size_t getAppEui(char *buffer, size_t size);
143+
size_t getVersion(char *buffer, size_t size);
143144
enum ttn_modem_status_t getStatus();
144145
uint16_t getVDD();
145146
int16_t getRSSI();
@@ -156,10 +157,10 @@ class TheThingsNetwork
156157
bool getChannelStatus (uint8_t channel);
157158
ttn_response_code_t getLastError();
158159
void onMessage(void (*cb)(const uint8_t *payload, size_t size, port_t port));
159-
bool provision(const char *appEui, const char *appKey);
160+
bool provision(const char *appEui, const char *appKey, bool resetFirst = true);
160161
bool join(const char *appEui, const char *appKey, int8_t retries = -1, uint32_t retryDelay = 10000, lorawan_class_t = CLASS_A);
161162
bool join(int8_t retries = -1, uint32_t retryDelay = 10000);
162-
bool personalize(const char *devAddr, const char *nwkSKey, const char *appSKey);
163+
bool personalize(const char *devAddr, const char *nwkSKey, const char *appSKey, bool resetFirst = true);
163164
bool personalize();
164165
bool setClass(lorawan_class_t p_lw_class);
165166
ttn_response_t sendBytes(const uint8_t *payload, size_t length, port_t port = 1, bool confirm = false, uint8_t sf = 0);
@@ -180,6 +181,7 @@ class TheThingsNetwork
180181
bool setRX1Delay(uint16_t delay);
181182
bool setFCU(uint32_t fcu);
182183
bool setFCD(uint32_t fcd);
184+
bool checkValidModuleConnected(bool autoBaudFirst = false);
183185
};
184186

185187
#endif

0 commit comments

Comments
 (0)