Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve handling of needsHardReset flag and add method to check valid module connected to serial port #282

Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 34 additions & 2 deletions docs/TheThingsNetwork.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,14 @@ Gets the provisioned AppEUI. The AppEUI is set using `provision()` or `join()`.
size_t getAppEui(char *buffer, size_t size);
```

## Method: `getVersion`

Gets the response from a `sys get ver` command (i.e. the hardware model, the the software version, etc.).

```c
size_t getVersion(char *buffer, size_t size);
```

## Method: `showStatus`

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

```c
bool personalize(const char *devAddr, const char *nwkSKey, const char *appSKey);
bool personalize(const char *devAddr, const char *nwkSKey, const char *appSKey, bool reset_first);
bool personalize();
```

- `const char *devAddr`: Device Address assigned to the device.
- `const char *nwkSKey`: Network Session Key assigned to the device for identification.
- `const char *appSKey`: Application Session Key assigned to the device for encryption.
- `bool reset_first`: Soft reset the module before performing any other action. Default is `true`.

Returns `true` or `false` depending on whether the activation was successful.

Expand Down Expand Up @@ -192,11 +201,12 @@ See the [Receive](https://github.com/TheThingsNetwork/arduino-device-lib/blob/ma
Sets the information needed to activate the device via OTAA, without actually activating. Call join() without the first 2 arguments to activate.

```c
bool provision(const char *appEui, const char *appKey);
bool provision(const char *appEui, const char *appKey, bool reset_first);
```

- `const char *appEui`: Application Identifier for the device.
- `const char *appKey`: Application Key assigned to the device.
- `bool reset_first`: Soft reset the module before performing any other action. Default is `true`.

## Method: `sleep`

Expand Down Expand Up @@ -434,6 +444,28 @@ When transmitting in LoRaWan, we usually operate on a TX window and two RX windo
bool setRX1Delay(uint16_t delay);
```

## Method: `checkValidModuleConnected`

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.

```c
bool checkValidModuleConnected(bool autobaud_first);
```

- `bool autobaud_first`: Perform a call to `autoBaud()` before checking connection. Default is `false`.

Returns:

* `false` if no response was received (i.e. `needsHardReset` is `true`)
* `false` if the module is invalid (unsupported), i.e. **not** one of the following:
* `RN2483`
* `RN2483A`
* `RN2903`
* `RN2903AS`
* `true` if the module responded (i.e. `needsHardReset` is `false`) and is valid (supported).

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

# Additional for statistics

## Method: `getRSSI`
Expand Down
73 changes: 73 additions & 0 deletions examples/CheckModule/CheckModule.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
#include <TheThingsNetwork.h>

// Set your DevAddr, NwkSKey, AppSKey and the frequency plan
const char *devAddr = "00000000";
const char *nwkSKey = "00000000000000000000000000000000";
const char *appSKey = "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)
;

// RN2XX3 reset pin connected to Arduino pin 12
pinMode(12, OUTPUT);
digitalWrite(12, HIGH);
// hard reset module and wait for startup
debugSerial.println("-- CHECK COMM");
ttn.resetHard(12);
delay(100);
// check if a valid module responded
// (if no module is connected or wiring is bad, checkValidModuleConnected() will
// take about ~30s to return (another ~30s if autobaud_first is true))
if(!ttn.checkValidModuleConnected(true))
{
if(ttn.needsHardReset)
{
debugSerial.println("Module unresponsive, please power cycle or hard reset board!");
}
else
{
debugSerial.println("Module unsupported!"); // module must be RN2483, RN2483A, RN2903, RN2903AS
}
while(true) // stop code execution
{
;
}
}

// do an ABP join
debugSerial.println("-- PERSONALIZE");
// false is added as fourth argument to the personalize() call so that it
// does not perform a soft reset, because the module was already hard reset before via pin 12.
ttn.personalize(devAddr, nwkSKey, appSKey, false);

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

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

// Prepare payload of 1 byte to indicate LED status
byte payload[1];
payload[0] = (digitalRead(LED_BUILTIN) == HIGH) ? 1 : 0;

// Send it off
ttn.sendBytes(payload, sizeof(payload));

delay(10000);
}
68 changes: 59 additions & 9 deletions src/TheThingsNetwork.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,11 @@ const char mac_tx_ok[] PROGMEM = "mac_tx_ok";
const char mac_rx[] PROGMEM = "mac_rx";
const char mac_err[] PROGMEM = "mac_err";
const char rn2483[] PROGMEM = "RN2483";
const char rn2483a[] PROGMEM = "RN2483A";
const char rn2903[] PROGMEM = "RN2903";
const char rn2903as[] PROGMEM = "RN2903AS";

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

#define CMP_OK 0
#define CMP_ON 1
Expand All @@ -36,6 +39,9 @@ const char *const compare_table[] PROGMEM = {ok, on, off, accepted, mac_tx_ok, m
#define CMP_MAC_RX 5
#define CMP_MAC_ERR 6
#define CMP_RN2483 7
#define CMP_RN2483A 8
#define CMP_RN2903 9
#define CMP_RN2903AS 10

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

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};
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};

#define ERR_INVALID_SF 0
#define ERR_INVALID_FP 1
Expand All @@ -123,18 +130,21 @@ const char *const error_msg[] PROGMEM = {invalid_sf, invalid_fp, unexpected_resp
#define ERR_KEY_LENGTH 8
#define ERR_CHECK_CONFIGURATION 9
#define ERR_NO_RESPONSE 10
#define ERR_INVALID_MODULE 11

const char personalize_accepted[] PROGMEM = "Personalize accepted. Status: ";
const char join_accepted[] PROGMEM = "Join accepted. Status: ";
const char successful_transmission[] PROGMEM = "Successful transmission";
const char successful_transmission_received[] PROGMEM = "Successful transmission. Received ";
const char valid_module[] PROGMEM = "Valid module connected.";

const char *const success_msg[] PROGMEM = {personalize_accepted, join_accepted, successful_transmission, successful_transmission_received};
const char *const success_msg[] PROGMEM = {personalize_accepted, join_accepted, successful_transmission, successful_transmission_received, valid_module};

#define SCS_PERSONALIZE_ACCEPTED 0
#define SCS_JOIN_ACCEPTED 1
#define SCS_SUCCESSFUL_TRANSMISSION 2
#define SCS_SUCCESSFUL_TRANSMISSION_RECEIVED 3
#define SCS_VALID_MODULE 4

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

size_t TheThingsNetwork::getVersion(char *buffer, size_t size)
{
return readResponse(SYS_TABLE, SYS_TABLE, SYS_GET_VER, buffer, size);
}

uint16_t TheThingsNetwork::getVDD()
{
if (readResponse(SYS_TABLE, SYS_TABLE, SYS_GET_VDD, buffer, sizeof(buffer)) > 0) {
Expand Down Expand Up @@ -632,22 +647,25 @@ void TheThingsNetwork::autoBaud()

void TheThingsNetwork::reset(bool adr)
{
// autobaud and send "sys reset"
autoBaud();
readResponse(SYS_TABLE, SYS_RESET, buffer, sizeof(buffer));

// autobaud (again, because baudrate was reset with "sys reset") and get HW model and SW version
autoBaud();
readResponse(SYS_TABLE, SYS_TABLE, SYS_GET_VER, buffer, sizeof(buffer));
getVersion(buffer, sizeof(buffer));

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

// set DEVEUI as HWEUI
readResponse(SYS_TABLE, SYS_TABLE, SYS_GET_HWEUI, buffer, sizeof(buffer));
sendMacSet(MAC_DEVEUI, buffer);
// set ADR
setADR(adr);
this->needsHardReset = false;
}

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

bool TheThingsNetwork::personalize(const char *devAddr, const char *nwkSKey, const char *appSKey)
bool TheThingsNetwork::personalize(const char *devAddr, const char *nwkSKey, const char *appSKey, bool reset_first)
{
reset(adr);
if(reset_first) {
reset(adr);
}
if (strlen(devAddr) != 8 || strlen(appSKey) != 32 || strlen(nwkSKey) != 32)
{
debugPrintMessage(ERR_MESSAGE, ERR_KEY_LENGTH);
Expand Down Expand Up @@ -703,9 +723,11 @@ bool TheThingsNetwork::personalize()
return true;
}

bool TheThingsNetwork::provision(const char *appEui, const char *appKey)
bool TheThingsNetwork::provision(const char *appEui, const char *appKey, bool reset_first)
{
reset(adr);
if(reset_first) {
reset(adr);
}
if (strlen(appEui) != 16 || strlen(appKey) != 32)
{
debugPrintMessage(ERR_MESSAGE, ERR_KEY_LENGTH);
Expand Down Expand Up @@ -917,6 +939,34 @@ void TheThingsNetwork::showStatus()
debugPrintIndex(SHOW_RX_DELAY_2, buffer);
}

bool TheThingsNetwork::checkValidModuleConnected(bool autobaud_first)
{
// check if we want to autobaud first
if(autobaud_first)
{
autoBaud();
}
// send "sys get ver" to check if (and what) module is connected
getVersion(buffer, sizeof(buffer));
// check if we got a response (whatever it might be)
// needsHardReset flag is set by readLine() (called at some point down the line by getVersion())
if(this->needsHardReset)
{
return false; // no response
}
// buffer contains "RN2xx3[xx] x.x.x ...", getting only model (RN2xx3[xx])
char *model = strtok(buffer, " ");
debugPrintIndex(SHOW_MODEL, model);
// check if module is valid (must be RN2483, RN2483A, RN2903 or RN2903AS)
if(pgmstrcmp(model, CMP_RN2483) == 0 || pgmstrcmp(model, CMP_RN2483A) == 0 || pgmstrcmp(model, CMP_RN2903) == 0 || pgmstrcmp(model, CMP_RN2903AS) == 0)
{
debugPrintMessage(SUCCESS_MESSAGE, SCS_VALID_MODULE);
return true; // module responded and is valid (recognized/supported)
}
debugPrintMessage(ERR_MESSAGE, ERR_INVALID_MODULE);
return false; // module responded but is invalid (unrecognized/unsupported)
}

void TheThingsNetwork::configureEU868()
{
sendMacSet(MAC_RX2, 3, 869525000);
Expand Down
6 changes: 4 additions & 2 deletions src/TheThingsNetwork.h
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ class TheThingsNetwork
void showStatus();
size_t getHardwareEui(char *buffer, size_t size);
size_t getAppEui(char *buffer, size_t size);
size_t getVersion(char *buffer, size_t size);
enum ttn_modem_status_t getStatus();
uint16_t getVDD();
int16_t getRSSI();
Expand All @@ -156,10 +157,10 @@ class TheThingsNetwork
bool getChannelStatus (uint8_t channel);
ttn_response_code_t getLastError();
void onMessage(void (*cb)(const uint8_t *payload, size_t size, port_t port));
bool provision(const char *appEui, const char *appKey);
bool provision(const char *appEui, const char *appKey, bool reset_first = true);
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(const char *devAddr, const char *nwkSKey, const char *appSKey, bool reset_first = true);
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);
Expand All @@ -180,6 +181,7 @@ class TheThingsNetwork
bool setRX1Delay(uint16_t delay);
bool setFCU(uint32_t fcu);
bool setFCD(uint32_t fcd);
bool checkValidModuleConnected(bool autobaud_first = false);
};

#endif