First commit
This commit is contained in:
commit
c270086b31
3 changed files with 1511 additions and 0 deletions
449
GasSensor/GasSensor.ino
Normal file
449
GasSensor/GasSensor.ino
Normal file
|
|
@ -0,0 +1,449 @@
|
||||||
|
#include "rn2xx3.h"
|
||||||
|
#include <SoftwareSerial.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <MsTimer2.h>
|
||||||
|
#include <avr/sleep.h>
|
||||||
|
|
||||||
|
#define LED_OFF HIGH
|
||||||
|
#define LED_ON LOW
|
||||||
|
|
||||||
|
#define MESURE_PERIOD 30
|
||||||
|
|
||||||
|
|
||||||
|
/* DECLARATION DES PINs */
|
||||||
|
const int pinGasSensor = A0;
|
||||||
|
|
||||||
|
|
||||||
|
const int pinInterruptUp = 2;
|
||||||
|
const int pinInterruptDown = 3;
|
||||||
|
|
||||||
|
const int pinRedLED = 4;
|
||||||
|
const int pinBlueLED = 5;
|
||||||
|
const int pinGreenLED = 6;
|
||||||
|
const int pinYellowLED = 7;
|
||||||
|
|
||||||
|
const int pinBuzzer = 9;
|
||||||
|
|
||||||
|
const int pinRX = 10;
|
||||||
|
const int pinTX = 11;
|
||||||
|
const int pinRST = 12;
|
||||||
|
/* FIN DECLARATION DES PINs */
|
||||||
|
|
||||||
|
/* SELECTION DES MODULES UTILISES */
|
||||||
|
const bool useLoRa = true;
|
||||||
|
const bool useLEDs = true;
|
||||||
|
const bool useBuzzer = true;
|
||||||
|
const bool useInterrupts = true;
|
||||||
|
const bool usePeriodic = false;
|
||||||
|
const bool usePowerSaveMode = false;
|
||||||
|
/* FIN SELECTION DES MODULES UTILISES */
|
||||||
|
|
||||||
|
/* VARIABLES GLOBALE D'ETAT */
|
||||||
|
volatile bool gasDetected = false;
|
||||||
|
volatile bool endGasDetected = false;
|
||||||
|
volatile bool lecture = false;
|
||||||
|
/* FIN VARIABLES GLOBALE D'ETAT */
|
||||||
|
|
||||||
|
/* DECLARATION DES VARIABLES GLOBALES */
|
||||||
|
SoftwareSerial mySerial(pinRX, pinTX); // RX, TX
|
||||||
|
rn2xx3 myLora(mySerial);
|
||||||
|
/* FIN DECLARATION DES VARIABLES GLOBALES */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************/
|
||||||
|
/******************************************/
|
||||||
|
/************* DEBUT DU SETUP *************/
|
||||||
|
/******************************************/
|
||||||
|
/******************************************/
|
||||||
|
|
||||||
|
/* Fonction de setup du module LoRa */
|
||||||
|
void setupLoRa(boolean used) {
|
||||||
|
if (used) {
|
||||||
|
mySerial.begin(9600); //serial port to radio
|
||||||
|
|
||||||
|
//reset rn2483
|
||||||
|
pinMode(pinRST, OUTPUT);
|
||||||
|
digitalWrite(pinRST, LOW);
|
||||||
|
delay(500);
|
||||||
|
digitalWrite(pinRST, HIGH);
|
||||||
|
delay(100); //wait for the RN2xx3's startup message
|
||||||
|
mySerial.flush();
|
||||||
|
|
||||||
|
//Autobaud the rn2483 module to 9600. The default would otherwise be 57600.
|
||||||
|
myLora.autobaud();
|
||||||
|
|
||||||
|
//check communication with radio
|
||||||
|
String hweui = myLora.hweui();
|
||||||
|
while(hweui.length() != 16)
|
||||||
|
{
|
||||||
|
Serial.println("Communication with RN2xx3 unsuccessful. Power cycle the board.");
|
||||||
|
Serial.println(hweui);
|
||||||
|
delay(10000);
|
||||||
|
hweui = myLora.hweui();
|
||||||
|
}
|
||||||
|
|
||||||
|
//print out the HWEUI so that we can register it via ttnctl
|
||||||
|
Serial.println("When using OTAA, register this DevEUI: ");
|
||||||
|
Serial.println(myLora.hweui());
|
||||||
|
Serial.println("RN2xx3 firmware version:");
|
||||||
|
Serial.println(myLora.sysver());
|
||||||
|
|
||||||
|
//configure your keys and join the network
|
||||||
|
Serial.println("Trying to join TTN");
|
||||||
|
bool join_result = false;
|
||||||
|
|
||||||
|
const char *appEui = "0000000000000000";
|
||||||
|
const char *appKey = "27AF4F436A03803BE0E96437B8AD270E";
|
||||||
|
|
||||||
|
join_result = myLora.initOTAA(appEui, appKey);
|
||||||
|
|
||||||
|
while(!join_result)
|
||||||
|
{
|
||||||
|
Serial.println("Unable to join. Are your keys correct, and do you have TTN coverage?");
|
||||||
|
delay(60000); //delay a minute before retry
|
||||||
|
join_result = myLora.init();
|
||||||
|
}
|
||||||
|
Serial.println("Successfully joined TTN");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fonction de setup des LEDs */
|
||||||
|
void setupLEDs(boolean used) {
|
||||||
|
if (used) {
|
||||||
|
pinMode(pinRedLED, OUTPUT);
|
||||||
|
digitalWrite(pinRedLED, LED_OFF);
|
||||||
|
pinMode(pinBlueLED, OUTPUT);
|
||||||
|
digitalWrite(pinBlueLED, LED_OFF);
|
||||||
|
pinMode(pinGreenLED, OUTPUT);
|
||||||
|
digitalWrite(pinGreenLED, LED_OFF);
|
||||||
|
pinMode(pinYellowLED, OUTPUT);
|
||||||
|
digitalWrite(pinYellowLED, LED_OFF);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fonction de setup du buzzer */
|
||||||
|
void setupBuzzer(boolean used) {
|
||||||
|
if (used) {
|
||||||
|
pinMode(pinBuzzer, OUTPUT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fonction de setup des interruptions */
|
||||||
|
void setupInterrupt(boolean used) {
|
||||||
|
if (used) {
|
||||||
|
attachInterrupt(digitalPinToInterrupt(pinInterruptUp), setAlertState, RISING);
|
||||||
|
attachInterrupt(digitalPinToInterrupt(pinInterruptDown), resetAlertState, FALLING);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fonction de setup du Timer */
|
||||||
|
void setupPeriodic(boolean used) {
|
||||||
|
if (used) {
|
||||||
|
MsTimer2::set(MESURE_PERIOD*1000, setPeriodicState);
|
||||||
|
MsTimer2::start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fonction de setup du power save mode */
|
||||||
|
void setupPowerSaveMode(boolean used) {
|
||||||
|
if (used) {
|
||||||
|
set_sleep_mode(SLEEP_MODE_PWR_SAVE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fonction d'initialisation globale */
|
||||||
|
void setup()
|
||||||
|
{
|
||||||
|
Serial.begin(9600);
|
||||||
|
|
||||||
|
setupLoRa(useLoRa);
|
||||||
|
setupLEDs(useLEDs);
|
||||||
|
setupBuzzer(useBuzzer);
|
||||||
|
setupInterrupt(useInterrupts);
|
||||||
|
setupPeriodic(usePeriodic);
|
||||||
|
setupPowerSaveMode(usePowerSaveMode);
|
||||||
|
|
||||||
|
|
||||||
|
if (useLEDs) {
|
||||||
|
digitalWrite(pinYellowLED, LED_ON);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************/
|
||||||
|
/******************************************/
|
||||||
|
/************** FIN DU SETUP **************/
|
||||||
|
/******************************************/
|
||||||
|
/******************************************/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************/
|
||||||
|
/******************************************/
|
||||||
|
/************* DEBUT HANDLER **************/
|
||||||
|
/******************************************/
|
||||||
|
/******************************************/
|
||||||
|
|
||||||
|
void setAlertState() {
|
||||||
|
gasDetected = true;
|
||||||
|
endGasDetected = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void resetAlertState() {
|
||||||
|
gasDetected = false;
|
||||||
|
endGasDetected = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setPeriodicState() {
|
||||||
|
lecture = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************/
|
||||||
|
/******************************************/
|
||||||
|
/************** FIN HANDLER ***************/
|
||||||
|
/******************************************/
|
||||||
|
/******************************************/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************/
|
||||||
|
/******************************************/
|
||||||
|
/********** DEBUT FONCTIONS TEST **********/
|
||||||
|
/******************************************/
|
||||||
|
/******************************************/
|
||||||
|
|
||||||
|
void testGasSensor() {
|
||||||
|
Serial.print("Valeur lue sur le capteur : ");
|
||||||
|
Serial.print(readValue());
|
||||||
|
Serial.println(" V");
|
||||||
|
delay(1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
void testLoRa() {
|
||||||
|
if (useLoRa) {
|
||||||
|
static int i = 0;
|
||||||
|
Serial.print("Envoi du TEST n°");
|
||||||
|
Serial.println(i);
|
||||||
|
String toSend = "TEST" + String(i);
|
||||||
|
myLora.tx(toSend);
|
||||||
|
Serial.println("Envoi terminé");
|
||||||
|
i++;
|
||||||
|
} else {
|
||||||
|
Serial.println("Veuillez activer le module LoRa");
|
||||||
|
}
|
||||||
|
delay(20000);
|
||||||
|
}
|
||||||
|
|
||||||
|
void testLEDs() {
|
||||||
|
int pause = 50;
|
||||||
|
if (useLEDs) {
|
||||||
|
digitalWrite(pinYellowLED, LED_OFF); // RED + BLUE
|
||||||
|
delay(pause);
|
||||||
|
digitalWrite(pinBlueLED, LED_ON);
|
||||||
|
delay(pause);
|
||||||
|
digitalWrite(pinRedLED, LED_OFF); // BLUE + GREEN
|
||||||
|
delay(pause);
|
||||||
|
digitalWrite(pinGreenLED, LED_ON);
|
||||||
|
delay(pause);
|
||||||
|
digitalWrite(pinBlueLED, LED_OFF); // GREEN + YELLOW
|
||||||
|
delay(pause);
|
||||||
|
digitalWrite(pinYellowLED, LED_ON);
|
||||||
|
delay(pause);
|
||||||
|
digitalWrite(pinGreenLED, LED_OFF); // YELLOW + RED
|
||||||
|
delay(pause);
|
||||||
|
digitalWrite(pinRedLED, LED_ON);
|
||||||
|
delay(pause);
|
||||||
|
} else {
|
||||||
|
Serial.println("Veuillez activer les LEDs");
|
||||||
|
delay(20000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void testBuzzer() {
|
||||||
|
if (useBuzzer) {
|
||||||
|
playAlertPompier();
|
||||||
|
} else {
|
||||||
|
Serial.println("Veuillez activer le Buzzer");
|
||||||
|
}
|
||||||
|
delay(10000);
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************/
|
||||||
|
/******************************************/
|
||||||
|
/*********** FIN FONCTIONS TEST ***********/
|
||||||
|
/******************************************/
|
||||||
|
/******************************************/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************/
|
||||||
|
/******************************************/
|
||||||
|
/************ DEBUT DU PROCESS ************/
|
||||||
|
/******************************************/
|
||||||
|
/******************************************/
|
||||||
|
|
||||||
|
|
||||||
|
/* Primitive connection TTN */
|
||||||
|
void connectTTN() {
|
||||||
|
if (useLEDs) {
|
||||||
|
digitalWrite(pinBlueLED, LED_ON);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Primitive disconnection TTN */
|
||||||
|
void disconnectTTN() {
|
||||||
|
if (useLEDs) {
|
||||||
|
digitalWrite(pinBlueLED, LED_OFF);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Primitive du buzzer */
|
||||||
|
void playTone(int tone, int duration) {
|
||||||
|
for (long i = 0; i < duration * 1000L; i += tone * 2) {
|
||||||
|
digitalWrite(pinBuzzer, HIGH);
|
||||||
|
delayMicroseconds(tone);
|
||||||
|
digitalWrite(pinBuzzer, LOW);
|
||||||
|
delayMicroseconds(tone);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Primitive du buzzer */
|
||||||
|
void playNote(char note, int duration) {
|
||||||
|
char names[] = { 'c', 'd', 'e', 'f', 'g', 'a', 'b', 'C' };
|
||||||
|
int tones[] = { 1915, 1700, 1519, 1432, 1275, 1136, 1014, 956 };
|
||||||
|
|
||||||
|
// play the tone corresponding to the note name
|
||||||
|
for (int i = 0; i < sizeof(names); i++) {
|
||||||
|
//for (int i = 0; i < 8; i++) {
|
||||||
|
if (names[i] == note) {
|
||||||
|
playTone(tones[i], duration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Primitive du buzzer */
|
||||||
|
void playAlertPompier() {
|
||||||
|
int length = 15; // the number of notes
|
||||||
|
char notes[] = "bababababababa "; // a space represents a rest
|
||||||
|
int beats[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 };
|
||||||
|
int tempo = 900;
|
||||||
|
|
||||||
|
for (int i = 0; i < length; i++) {
|
||||||
|
if (notes[i] == ' ') {
|
||||||
|
delay(beats[i] * tempo); // rest
|
||||||
|
} else {
|
||||||
|
playNote(notes[i], beats[i] * tempo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Primitive de lecture de la valeur du capteur */
|
||||||
|
float readValue() {
|
||||||
|
int value = 0;
|
||||||
|
for (int i = 0; i<10; i++) {
|
||||||
|
value += analogRead(pinGasSensor);
|
||||||
|
}
|
||||||
|
return ((float)value/10.0)*5.0/1024.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Gestion de l'alerte */
|
||||||
|
void gasDetectedHandler() {
|
||||||
|
Serial.println("ALERTE présence de gaz !!!");
|
||||||
|
if (useLEDs) {
|
||||||
|
digitalWrite(pinRedLED, LED_ON);
|
||||||
|
}
|
||||||
|
if (useBuzzer) {
|
||||||
|
playAlertPompier();
|
||||||
|
}
|
||||||
|
if (useLoRa) {
|
||||||
|
connectTTN();
|
||||||
|
String toSend = "ALERTE présence de gaz !!!";
|
||||||
|
myLora.tx(toSend);
|
||||||
|
disconnectTTN();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Gestion de la fin de l'alerte */
|
||||||
|
void gasDetectionEndHandler() {
|
||||||
|
Serial.println("Fin ALERTE présence de gaz !!!");
|
||||||
|
if (useLoRa) {
|
||||||
|
connectTTN();
|
||||||
|
String toSend = "Fin ALERTE présence de gaz !!!";
|
||||||
|
myLora.tx(toSend);
|
||||||
|
disconnectTTN();
|
||||||
|
}
|
||||||
|
if (useLEDs) {
|
||||||
|
digitalWrite(pinRedLED, LED_OFF);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fonction de detection du gaz */
|
||||||
|
void analyseGasAtmosphere() {
|
||||||
|
|
||||||
|
if (lecture) {
|
||||||
|
if (useLEDs) {
|
||||||
|
digitalWrite(pinGreenLED, LED_ON);
|
||||||
|
}
|
||||||
|
float sensorValue = readValue();
|
||||||
|
Serial.println(sensorValue);
|
||||||
|
if (useLoRa) {
|
||||||
|
connectTTN();
|
||||||
|
String sensorValueStr = String(sensorValue, 1);
|
||||||
|
myLora.tx(sensorValueStr);
|
||||||
|
disconnectTTN();
|
||||||
|
}
|
||||||
|
lecture = false;
|
||||||
|
if (useLEDs) {
|
||||||
|
digitalWrite(pinGreenLED, LED_OFF);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gasDetected) {
|
||||||
|
gasDetectedHandler();
|
||||||
|
while (!endGasDetected) {
|
||||||
|
gasDetectedHandler();
|
||||||
|
}
|
||||||
|
gasDetectionEndHandler();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (usePowerSaveMode) {
|
||||||
|
sleep_mode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Processus */
|
||||||
|
void loop(){
|
||||||
|
//testGasSensor();
|
||||||
|
//testLoRa();
|
||||||
|
//testLEDs();
|
||||||
|
//testBuzzer();
|
||||||
|
analyseGasAtmosphere();
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************/
|
||||||
|
/******************************************/
|
||||||
|
/************* FIN DU PROCESS *************/
|
||||||
|
/******************************************/
|
||||||
|
/******************************************/
|
||||||
789
GasSensor/rn2xx3.cpp
Normal file
789
GasSensor/rn2xx3.cpp
Normal file
|
|
@ -0,0 +1,789 @@
|
||||||
|
/*
|
||||||
|
* A library for controlling a Microchip rn2xx3 LoRa radio.
|
||||||
|
*
|
||||||
|
* @Author JP Meijers
|
||||||
|
* @Author Nicolas Schteinschraber
|
||||||
|
* @Date 18/12/2015
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Arduino.h"
|
||||||
|
#include "rn2xx3.h"
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
@param serial Needs to be an already opened Stream ({Software/Hardware}Serial) to write to and read from.
|
||||||
|
*/
|
||||||
|
rn2xx3::rn2xx3(Stream& serial):
|
||||||
|
_serial(serial)
|
||||||
|
{
|
||||||
|
_serial.setTimeout(2000);
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: change to a boolean
|
||||||
|
void rn2xx3::autobaud()
|
||||||
|
{
|
||||||
|
String response = "";
|
||||||
|
|
||||||
|
// Try a maximum of 10 times with a 1 second delay
|
||||||
|
for (uint8_t i=0; i<10 && response==""; i++)
|
||||||
|
{
|
||||||
|
delay(1000);
|
||||||
|
_serial.write((byte)0x00);
|
||||||
|
_serial.write(0x55);
|
||||||
|
_serial.println();
|
||||||
|
// we could use sendRawCommand(F("sys get ver")); here
|
||||||
|
_serial.println("sys get ver");
|
||||||
|
response = _serial.readStringUntil('\n');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
String rn2xx3::sysver()
|
||||||
|
{
|
||||||
|
String ver = sendRawCommand(F("sys get ver"));
|
||||||
|
ver.trim();
|
||||||
|
return ver;
|
||||||
|
}
|
||||||
|
|
||||||
|
RN2xx3_t rn2xx3::configureModuleType()
|
||||||
|
{
|
||||||
|
String version = sysver();
|
||||||
|
String model = version.substring(2,6);
|
||||||
|
switch (model.toInt()) {
|
||||||
|
case 2903:
|
||||||
|
_moduleType = RN2903;
|
||||||
|
break;
|
||||||
|
case 2483:
|
||||||
|
_moduleType = RN2483;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
_moduleType = RN_NA;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return _moduleType;
|
||||||
|
}
|
||||||
|
|
||||||
|
String rn2xx3::hweui()
|
||||||
|
{
|
||||||
|
return (sendRawCommand(F("sys get hweui")));
|
||||||
|
}
|
||||||
|
|
||||||
|
String rn2xx3::appeui()
|
||||||
|
{
|
||||||
|
return ( sendRawCommand(F("mac get appeui") ));
|
||||||
|
}
|
||||||
|
|
||||||
|
String rn2xx3::appkey()
|
||||||
|
{
|
||||||
|
// We can't read back from module, we send the one
|
||||||
|
// we have memorized if it has been set
|
||||||
|
return _appskey;
|
||||||
|
}
|
||||||
|
|
||||||
|
String rn2xx3::deveui()
|
||||||
|
{
|
||||||
|
return (sendRawCommand(F("mac get deveui")));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool rn2xx3::init()
|
||||||
|
{
|
||||||
|
if(_appskey=="0") //appskey variable is set by both OTAA and ABP
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if(_otaa==true)
|
||||||
|
{
|
||||||
|
return initOTAA(_appeui, _appskey);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return initABP(_devAddr, _appskey, _nwkskey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool rn2xx3::initOTAA(String AppEUI, String AppKey, String DevEUI)
|
||||||
|
{
|
||||||
|
_otaa = true;
|
||||||
|
_nwkskey = "0";
|
||||||
|
String receivedData;
|
||||||
|
|
||||||
|
//clear serial buffer
|
||||||
|
while(_serial.available())
|
||||||
|
_serial.read();
|
||||||
|
|
||||||
|
// detect which model radio we are using
|
||||||
|
configureModuleType();
|
||||||
|
|
||||||
|
// reset the module - this will clear all keys set previously
|
||||||
|
switch (_moduleType)
|
||||||
|
{
|
||||||
|
case RN2903:
|
||||||
|
sendRawCommand(F("mac reset"));
|
||||||
|
break;
|
||||||
|
case RN2483:
|
||||||
|
sendRawCommand(F("mac reset 868"));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// we shouldn't go forward with the init
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the Device EUI was given as a parameter, use it
|
||||||
|
// otherwise use the Hardware EUI.
|
||||||
|
if (DevEUI.length() == 16)
|
||||||
|
{
|
||||||
|
_deveui = DevEUI;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
String addr = sendRawCommand(F("sys get hweui"));
|
||||||
|
if( addr.length() == 16 )
|
||||||
|
{
|
||||||
|
_deveui = addr;
|
||||||
|
}
|
||||||
|
// else fall back to the hard coded value in the header file
|
||||||
|
}
|
||||||
|
|
||||||
|
sendRawCommand("mac set deveui "+_deveui);
|
||||||
|
|
||||||
|
// A valid length App EUI was given. Use it.
|
||||||
|
if ( AppEUI.length() == 16 )
|
||||||
|
{
|
||||||
|
_appeui = AppEUI;
|
||||||
|
sendRawCommand("mac set appeui "+_appeui);
|
||||||
|
}
|
||||||
|
|
||||||
|
// A valid length App Key was give. Use it.
|
||||||
|
if ( AppKey.length() == 32 )
|
||||||
|
{
|
||||||
|
_appskey = AppKey; //reuse the same variable as for ABP
|
||||||
|
sendRawCommand("mac set appkey "+_appskey);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_moduleType == RN2903)
|
||||||
|
{
|
||||||
|
sendRawCommand(F("mac set pwridx 5"));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sendRawCommand(F("mac set pwridx 1"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// TTN does not yet support Adaptive Data Rate.
|
||||||
|
// Using it is also only necessary in limited situations.
|
||||||
|
// Therefore disable it by default.
|
||||||
|
sendRawCommand(F("mac set adr off"));
|
||||||
|
|
||||||
|
// Switch off automatic replies, because this library can not
|
||||||
|
// handle more than one mac_rx per tx. See RN2483 datasheet,
|
||||||
|
// 2.4.8.14, page 27 and the scenario on page 19.
|
||||||
|
sendRawCommand(F("mac set ar off"));
|
||||||
|
|
||||||
|
// Semtech and TTN both use a non default RX2 window freq and SF.
|
||||||
|
// Maybe we should not specify this for other networks.
|
||||||
|
// if (_moduleType == RN2483)
|
||||||
|
// {
|
||||||
|
// sendRawCommand(F("mac set rx2 3 869525000"));
|
||||||
|
// }
|
||||||
|
// Disabled for now because an OTAA join seems to work fine without.
|
||||||
|
|
||||||
|
_serial.setTimeout(30000);
|
||||||
|
sendRawCommand(F("mac save"));
|
||||||
|
|
||||||
|
bool joined = false;
|
||||||
|
|
||||||
|
// Only try twice to join, then return and let the user handle it.
|
||||||
|
for(int i=0; i<2 && !joined; i++)
|
||||||
|
{
|
||||||
|
sendRawCommand(F("mac join otaa"));
|
||||||
|
// Parse 2nd response
|
||||||
|
receivedData = _serial.readStringUntil('\n');
|
||||||
|
|
||||||
|
if(receivedData.startsWith("accepted"))
|
||||||
|
{
|
||||||
|
joined=true;
|
||||||
|
delay(1000);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
delay(1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_serial.setTimeout(2000);
|
||||||
|
return joined;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool rn2xx3::initOTAA(uint8_t * AppEUI, uint8_t * AppKey, uint8_t * DevEUI)
|
||||||
|
{
|
||||||
|
String app_eui;
|
||||||
|
String dev_eui;
|
||||||
|
String app_key;
|
||||||
|
char buff[3];
|
||||||
|
|
||||||
|
app_eui="";
|
||||||
|
for (uint8_t i=0; i<8; i++)
|
||||||
|
{
|
||||||
|
sprintf(buff, "%02X", AppEUI[i]);
|
||||||
|
app_eui += String (buff);
|
||||||
|
}
|
||||||
|
|
||||||
|
dev_eui = "0";
|
||||||
|
if (DevEUI) //==0
|
||||||
|
{
|
||||||
|
dev_eui = "";
|
||||||
|
for (uint8_t i=0; i<8; i++)
|
||||||
|
{
|
||||||
|
sprintf(buff, "%02X", DevEUI[i]);
|
||||||
|
dev_eui += String (buff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
app_key="";
|
||||||
|
for (uint8_t i=0; i<16; i++)
|
||||||
|
{
|
||||||
|
sprintf(buff, "%02X", AppKey[i]);
|
||||||
|
app_key += String (buff);
|
||||||
|
}
|
||||||
|
|
||||||
|
return initOTAA(app_eui, app_key, dev_eui);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool rn2xx3::initABP(String devAddr, String AppSKey, String NwkSKey)
|
||||||
|
{
|
||||||
|
_otaa = false;
|
||||||
|
_devAddr = devAddr;
|
||||||
|
_appskey = AppSKey;
|
||||||
|
_nwkskey = NwkSKey;
|
||||||
|
String receivedData;
|
||||||
|
|
||||||
|
//clear serial buffer
|
||||||
|
while(_serial.available())
|
||||||
|
_serial.read();
|
||||||
|
|
||||||
|
configureModuleType();
|
||||||
|
|
||||||
|
switch (_moduleType) {
|
||||||
|
case RN2903:
|
||||||
|
sendRawCommand(F("mac reset"));
|
||||||
|
break;
|
||||||
|
case RN2483:
|
||||||
|
sendRawCommand(F("mac reset 868"));
|
||||||
|
// sendRawCommand(F("mac set rx2 3 869525000"));
|
||||||
|
// In the past we set the downlink channel here,
|
||||||
|
// but setFrequencyPlan is a better place to do it.
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// we shouldn't go forward with the init
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
sendRawCommand("mac set nwkskey "+_nwkskey);
|
||||||
|
sendRawCommand("mac set appskey "+_appskey);
|
||||||
|
sendRawCommand("mac set devaddr "+_devAddr);
|
||||||
|
sendRawCommand(F("mac set adr off"));
|
||||||
|
|
||||||
|
// Switch off automatic replies, because this library can not
|
||||||
|
// handle more than one mac_rx per tx. See RN2483 datasheet,
|
||||||
|
// 2.4.8.14, page 27 and the scenario on page 19.
|
||||||
|
sendRawCommand(F("mac set ar off"));
|
||||||
|
|
||||||
|
if (_moduleType == RN2903)
|
||||||
|
{
|
||||||
|
sendRawCommand("mac set pwridx 5");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sendRawCommand(F("mac set pwridx 1"));
|
||||||
|
}
|
||||||
|
sendRawCommand(F("mac set dr 5")); //0= min, 7=max
|
||||||
|
|
||||||
|
_serial.setTimeout(60000);
|
||||||
|
sendRawCommand(F("mac save"));
|
||||||
|
sendRawCommand(F("mac join abp"));
|
||||||
|
receivedData = _serial.readStringUntil('\n');
|
||||||
|
|
||||||
|
_serial.setTimeout(2000);
|
||||||
|
delay(1000);
|
||||||
|
|
||||||
|
if(receivedData.startsWith("accepted"))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
//with abp we can always join successfully as long as the keys are valid
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TX_RETURN_TYPE rn2xx3::tx(String data)
|
||||||
|
{
|
||||||
|
return txUncnf(data); //we are unsure which mode we're in. Better not to wait for acks.
|
||||||
|
}
|
||||||
|
|
||||||
|
TX_RETURN_TYPE rn2xx3::txBytes(const byte* data, uint8_t size)
|
||||||
|
{
|
||||||
|
char msgBuffer[size*2 + 1];
|
||||||
|
|
||||||
|
char buffer[3];
|
||||||
|
for (unsigned i=0; i<size; i++)
|
||||||
|
{
|
||||||
|
sprintf(buffer, "%02X", data[i]);
|
||||||
|
memcpy(&msgBuffer[i*2], &buffer, sizeof(buffer));
|
||||||
|
}
|
||||||
|
String dataToTx(msgBuffer);
|
||||||
|
return txCommand("mac tx uncnf 1 ", dataToTx, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
TX_RETURN_TYPE rn2xx3::txCnf(String data)
|
||||||
|
{
|
||||||
|
return txCommand("mac tx cnf 1 ", data, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
TX_RETURN_TYPE rn2xx3::txUncnf(String data)
|
||||||
|
{
|
||||||
|
return txCommand("mac tx uncnf 1 ", data, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
TX_RETURN_TYPE rn2xx3::txCommand(String command, String data, bool shouldEncode)
|
||||||
|
{
|
||||||
|
bool send_success = false;
|
||||||
|
uint8_t busy_count = 0;
|
||||||
|
uint8_t retry_count = 0;
|
||||||
|
|
||||||
|
//clear serial buffer
|
||||||
|
while(_serial.available())
|
||||||
|
_serial.read();
|
||||||
|
|
||||||
|
while(!send_success)
|
||||||
|
{
|
||||||
|
//retransmit a maximum of 10 times
|
||||||
|
retry_count++;
|
||||||
|
if(retry_count>10)
|
||||||
|
{
|
||||||
|
return TX_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
_serial.print(command);
|
||||||
|
if(shouldEncode)
|
||||||
|
{
|
||||||
|
sendEncoded(data);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_serial.print(data);
|
||||||
|
}
|
||||||
|
_serial.println();
|
||||||
|
|
||||||
|
String receivedData = _serial.readStringUntil('\n');
|
||||||
|
//TODO: Debug print on receivedData
|
||||||
|
|
||||||
|
if(receivedData.startsWith("ok"))
|
||||||
|
{
|
||||||
|
_serial.setTimeout(30000);
|
||||||
|
receivedData = _serial.readStringUntil('\n');
|
||||||
|
_serial.setTimeout(2000);
|
||||||
|
|
||||||
|
//TODO: Debug print on receivedData
|
||||||
|
|
||||||
|
if(receivedData.startsWith("mac_tx_ok"))
|
||||||
|
{
|
||||||
|
//SUCCESS!!
|
||||||
|
send_success = true;
|
||||||
|
return TX_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if(receivedData.startsWith("mac_rx"))
|
||||||
|
{
|
||||||
|
//example: mac_rx 1 54657374696E6720313233
|
||||||
|
_rxMessenge = receivedData.substring(receivedData.indexOf(' ', 7)+1);
|
||||||
|
send_success = true;
|
||||||
|
return TX_WITH_RX;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if(receivedData.startsWith("mac_err"))
|
||||||
|
{
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
else if(receivedData.startsWith("invalid_data_len"))
|
||||||
|
{
|
||||||
|
//this should never happen if the prototype worked
|
||||||
|
send_success = true;
|
||||||
|
return TX_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if(receivedData.startsWith("radio_tx_ok"))
|
||||||
|
{
|
||||||
|
//SUCCESS!!
|
||||||
|
send_success = true;
|
||||||
|
return TX_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if(receivedData.startsWith("radio_err"))
|
||||||
|
{
|
||||||
|
//This should never happen. If it does, something major is wrong.
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//unknown response
|
||||||
|
//init();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else if(receivedData.startsWith("invalid_param"))
|
||||||
|
{
|
||||||
|
//should not happen if we typed the commands correctly
|
||||||
|
send_success = true;
|
||||||
|
return TX_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if(receivedData.startsWith("not_joined"))
|
||||||
|
{
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
else if(receivedData.startsWith("no_free_ch"))
|
||||||
|
{
|
||||||
|
//retry
|
||||||
|
delay(1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
else if(receivedData.startsWith("silent"))
|
||||||
|
{
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
else if(receivedData.startsWith("frame_counter_err_rejoin_needed"))
|
||||||
|
{
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
else if(receivedData.startsWith("busy"))
|
||||||
|
{
|
||||||
|
busy_count++;
|
||||||
|
|
||||||
|
// Not sure if this is wise. At low data rates with large packets
|
||||||
|
// this can perhaps cause transmissions at more than 1% duty cycle.
|
||||||
|
// Need to calculate the correct constant value.
|
||||||
|
// But it is wise to have this check and re-init in case the
|
||||||
|
// lorawan stack in the RN2xx3 hangs.
|
||||||
|
if(busy_count>=10)
|
||||||
|
{
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
delay(1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else if(receivedData.startsWith("mac_paused"))
|
||||||
|
{
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
else if(receivedData.startsWith("invalid_data_len"))
|
||||||
|
{
|
||||||
|
//should not happen if the prototype worked
|
||||||
|
send_success = true;
|
||||||
|
return TX_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//unknown response after mac tx command
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return TX_FAIL; //should never reach this
|
||||||
|
}
|
||||||
|
|
||||||
|
void rn2xx3::sendEncoded(String input)
|
||||||
|
{
|
||||||
|
char working;
|
||||||
|
char buffer[3];
|
||||||
|
for (unsigned i=0; i<input.length(); i++)
|
||||||
|
{
|
||||||
|
working = input.charAt(i);
|
||||||
|
sprintf(buffer, "%02x", int(working));
|
||||||
|
_serial.print(buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String rn2xx3::base16encode(String input)
|
||||||
|
{
|
||||||
|
char charsOut[input.length()*2+1];
|
||||||
|
char charsIn[input.length()+1];
|
||||||
|
input.trim();
|
||||||
|
input.toCharArray(charsIn, input.length()+1);
|
||||||
|
|
||||||
|
unsigned i = 0;
|
||||||
|
for(i = 0; i<input.length()+1; i++)
|
||||||
|
{
|
||||||
|
if(charsIn[i] == '\0') break;
|
||||||
|
|
||||||
|
int value = int(charsIn[i]);
|
||||||
|
|
||||||
|
char buffer[3];
|
||||||
|
sprintf(buffer, "%02x", value);
|
||||||
|
charsOut[2*i] = buffer[0];
|
||||||
|
charsOut[2*i+1] = buffer[1];
|
||||||
|
}
|
||||||
|
charsOut[2*i] = '\0';
|
||||||
|
String toReturn = String(charsOut);
|
||||||
|
return toReturn;
|
||||||
|
}
|
||||||
|
|
||||||
|
String rn2xx3::getRx() {
|
||||||
|
return _rxMessenge;
|
||||||
|
}
|
||||||
|
|
||||||
|
int rn2xx3::getSNR()
|
||||||
|
{
|
||||||
|
String snr = sendRawCommand(F("radio get snr"));
|
||||||
|
snr.trim();
|
||||||
|
return snr.toInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
String rn2xx3::base16decode(String input)
|
||||||
|
{
|
||||||
|
char charsIn[input.length()+1];
|
||||||
|
char charsOut[input.length()/2+1];
|
||||||
|
input.trim();
|
||||||
|
input.toCharArray(charsIn, input.length()+1);
|
||||||
|
|
||||||
|
unsigned i = 0;
|
||||||
|
for(i = 0; i<input.length()/2+1; i++)
|
||||||
|
{
|
||||||
|
if(charsIn[i*2] == '\0') break;
|
||||||
|
if(charsIn[i*2+1] == '\0') break;
|
||||||
|
|
||||||
|
char toDo[2];
|
||||||
|
toDo[0] = charsIn[i*2];
|
||||||
|
toDo[1] = charsIn[i*2+1];
|
||||||
|
int out = strtoul(toDo, 0, 16);
|
||||||
|
|
||||||
|
if(out<128)
|
||||||
|
{
|
||||||
|
charsOut[i] = char(out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
charsOut[i] = '\0';
|
||||||
|
return charsOut;
|
||||||
|
}
|
||||||
|
|
||||||
|
void rn2xx3::setDR(int dr)
|
||||||
|
{
|
||||||
|
if(dr>=0 && dr<=5)
|
||||||
|
{
|
||||||
|
delay(100);
|
||||||
|
while(_serial.available())
|
||||||
|
_serial.read();
|
||||||
|
_serial.print("mac set dr ");
|
||||||
|
_serial.println(dr);
|
||||||
|
_serial.readStringUntil('\n');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void rn2xx3::sleep(long msec)
|
||||||
|
{
|
||||||
|
_serial.print("sys sleep ");
|
||||||
|
_serial.println(msec);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
String rn2xx3::sendRawCommand(String command)
|
||||||
|
{
|
||||||
|
delay(100);
|
||||||
|
while(_serial.available())
|
||||||
|
_serial.read();
|
||||||
|
_serial.println(command);
|
||||||
|
String ret = _serial.readStringUntil('\n');
|
||||||
|
ret.trim();
|
||||||
|
|
||||||
|
//TODO: Add debug print
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
RN2xx3_t rn2xx3::moduleType()
|
||||||
|
{
|
||||||
|
return _moduleType;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool rn2xx3::setFrequencyPlan(FREQ_PLAN fp)
|
||||||
|
{
|
||||||
|
bool returnValue;
|
||||||
|
|
||||||
|
switch (fp)
|
||||||
|
{
|
||||||
|
case SINGLE_CHANNEL_EU:
|
||||||
|
{
|
||||||
|
if(_moduleType == RN2483)
|
||||||
|
{
|
||||||
|
//mac set rx2 <dataRate> <frequency>
|
||||||
|
//sendRawCommand(F("mac set rx2 5 868100000")); //use this for "strict" one channel gateways
|
||||||
|
sendRawCommand(F("mac set rx2 3 869525000")); //use for "non-strict" one channel gateways
|
||||||
|
sendRawCommand(F("mac set ch dcycle 0 99")); //1% duty cycle for this channel
|
||||||
|
sendRawCommand(F("mac set ch dcycle 1 65535")); //almost never use this channel
|
||||||
|
sendRawCommand(F("mac set ch dcycle 2 65535")); //almost never use this channel
|
||||||
|
|
||||||
|
returnValue = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
returnValue = false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case TTN_EU:
|
||||||
|
{
|
||||||
|
if(_moduleType == RN2483)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* The <dutyCycle> value that needs to be configured can be
|
||||||
|
* obtained from the actual duty cycle X (in percentage)
|
||||||
|
* using the following formula: <dutyCycle> = (100/X) – 1
|
||||||
|
*
|
||||||
|
* 10% -> 9
|
||||||
|
* 1% -> 99
|
||||||
|
* 0.33% -> 299
|
||||||
|
* 8 channels, total of 1% duty cycle:
|
||||||
|
* 0.125% per channel -> 799
|
||||||
|
*
|
||||||
|
* Most of the TTN_EU frequency plan was copied from:
|
||||||
|
* https://github.com/TheThingsNetwork/arduino-device-lib
|
||||||
|
*/
|
||||||
|
|
||||||
|
//RX window 2
|
||||||
|
sendRawCommand(F("mac set rx2 3 869525000"));
|
||||||
|
|
||||||
|
//channel 0
|
||||||
|
sendRawCommand(F("mac set ch dcycle 0 799"));
|
||||||
|
|
||||||
|
//channel 1
|
||||||
|
sendRawCommand(F("mac set ch drrange 1 0 6"));
|
||||||
|
sendRawCommand(F("mac set ch dcycle 1 799"));
|
||||||
|
|
||||||
|
//channel 2
|
||||||
|
sendRawCommand(F("mac set ch dcycle 2 799"));
|
||||||
|
|
||||||
|
//channel 3
|
||||||
|
sendRawCommand(F("mac set ch freq 3 867100000"));
|
||||||
|
sendRawCommand(F("mac set ch drrange 3 0 5"));
|
||||||
|
sendRawCommand(F("mac set ch dcycle 3 799"));
|
||||||
|
sendRawCommand(F("mac set ch status 3 on"));
|
||||||
|
|
||||||
|
//channel 4
|
||||||
|
sendRawCommand(F("mac set ch freq 4 867300000"));
|
||||||
|
sendRawCommand(F("mac set ch drrange 4 0 5"));
|
||||||
|
sendRawCommand(F("mac set ch dcycle 4 799"));
|
||||||
|
sendRawCommand(F("mac set ch status 4 on"));
|
||||||
|
|
||||||
|
//channel 5
|
||||||
|
sendRawCommand(F("mac set ch freq 5 867500000"));
|
||||||
|
sendRawCommand(F("mac set ch drrange 5 0 5"));
|
||||||
|
sendRawCommand(F("mac set ch dcycle 5 799"));
|
||||||
|
sendRawCommand(F("mac set ch status 5 on"));
|
||||||
|
|
||||||
|
//channel 6
|
||||||
|
sendRawCommand(F("mac set ch freq 6 867700000"));
|
||||||
|
sendRawCommand(F("mac set ch drrange 6 0 5"));
|
||||||
|
sendRawCommand(F("mac set ch dcycle 6 799"));
|
||||||
|
sendRawCommand(F("mac set ch status 6 on"));
|
||||||
|
|
||||||
|
//channel 7
|
||||||
|
sendRawCommand(F("mac set ch freq 7 867900000"));
|
||||||
|
sendRawCommand(F("mac set ch drrange 7 0 5"));
|
||||||
|
sendRawCommand(F("mac set ch dcycle 7 799"));
|
||||||
|
sendRawCommand(F("mac set ch status 7 on"));
|
||||||
|
|
||||||
|
returnValue = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
returnValue = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case TTN_US:
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Most of the TTN_US frequency plan was copied from:
|
||||||
|
* https://github.com/TheThingsNetwork/arduino-device-lib
|
||||||
|
*/
|
||||||
|
if(_moduleType == RN2903)
|
||||||
|
{
|
||||||
|
for(int channel=0; channel<72; channel++)
|
||||||
|
{
|
||||||
|
// Build command string. First init, then add int.
|
||||||
|
String command = F("mac set ch status ");
|
||||||
|
command += channel;
|
||||||
|
|
||||||
|
if(channel>=8 && channel<16)
|
||||||
|
{
|
||||||
|
sendRawCommand(command+F(" on"));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sendRawCommand(command+F(" off"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
returnValue = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
returnValue = false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case DEFAULT_EU:
|
||||||
|
{
|
||||||
|
if(_moduleType == RN2483)
|
||||||
|
{
|
||||||
|
//fix duty cycle - 1% = 0.33% per channel
|
||||||
|
sendRawCommand(F("mac set ch dcycle 0 799"));
|
||||||
|
sendRawCommand(F("mac set ch dcycle 1 799"));
|
||||||
|
sendRawCommand(F("mac set ch dcycle 2 799"));
|
||||||
|
|
||||||
|
//disable non-default channels
|
||||||
|
sendRawCommand(F("mac set ch status 3 on"));
|
||||||
|
sendRawCommand(F("mac set ch status 4 on"));
|
||||||
|
sendRawCommand(F("mac set ch status 5 on"));
|
||||||
|
sendRawCommand(F("mac set ch status 6 on"));
|
||||||
|
sendRawCommand(F("mac set ch status 7 on"));
|
||||||
|
|
||||||
|
returnValue = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
returnValue = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
//set default channels 868.1, 868.3 and 868.5?
|
||||||
|
returnValue = false; //well we didn't do anything, so yes, false
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return returnValue;
|
||||||
|
}
|
||||||
273
GasSensor/rn2xx3.h
Normal file
273
GasSensor/rn2xx3.h
Normal file
|
|
@ -0,0 +1,273 @@
|
||||||
|
/*
|
||||||
|
* A library for controlling a Microchip RN2xx3 LoRa radio.
|
||||||
|
*
|
||||||
|
* @Author JP Meijers
|
||||||
|
* @Author Nicolas Schteinschraber
|
||||||
|
* @Date 18/12/2015
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef rn2xx3_h
|
||||||
|
#define rn2xx3_h
|
||||||
|
|
||||||
|
#include "Arduino.h"
|
||||||
|
|
||||||
|
enum RN2xx3_t {
|
||||||
|
RN_NA = 0, // Not set
|
||||||
|
RN2903 = 2903,
|
||||||
|
RN2483 = 2483
|
||||||
|
};
|
||||||
|
|
||||||
|
enum FREQ_PLAN {
|
||||||
|
SINGLE_CHANNEL_EU,
|
||||||
|
TTN_EU,
|
||||||
|
TTN_US,
|
||||||
|
DEFAULT_EU
|
||||||
|
};
|
||||||
|
|
||||||
|
enum TX_RETURN_TYPE {
|
||||||
|
TX_FAIL = 0, // The transmission failed.
|
||||||
|
// If you sent a confirmed message and it is not acked,
|
||||||
|
// this will be the returned value.
|
||||||
|
|
||||||
|
TX_SUCCESS = 1, // The transmission was successful.
|
||||||
|
// Also the case when a confirmed message was acked.
|
||||||
|
|
||||||
|
TX_WITH_RX = 2 // A downlink message was received after the transmission.
|
||||||
|
// This also implies that a confirmed message is acked.
|
||||||
|
};
|
||||||
|
|
||||||
|
class rn2xx3
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A simplified constructor taking only a Stream ({Software/Hardware}Serial) object.
|
||||||
|
* The serial port should already be initialised when initialising this library.
|
||||||
|
*/
|
||||||
|
rn2xx3(Stream& serial);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Transmit the correct sequence to the rn2xx3 to trigger its autobauding feature.
|
||||||
|
* After this operation the rn2xx3 should communicate at the same baud rate than us.
|
||||||
|
*/
|
||||||
|
void autobaud();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get the hardware EUI of the radio, so that we can register it on The Things Network
|
||||||
|
* and obtain the correct AppKey.
|
||||||
|
* You have to have a working serial connection to the radio before calling this function.
|
||||||
|
* In other words you have to at least call autobaud() some time before this function.
|
||||||
|
*/
|
||||||
|
String hweui();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the AppSKey or AppKey used when initializing the radio.
|
||||||
|
* In the case of ABP this function will return the App Session Key.
|
||||||
|
* In the case of OTAA this function will return the App Key.
|
||||||
|
*/
|
||||||
|
String appkey();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* In the case of OTAA this function will return the Application EUI used
|
||||||
|
* to initialize the radio.
|
||||||
|
*/
|
||||||
|
String appeui();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* In the case of OTAA this function will return the Device EUI used to
|
||||||
|
* initialize the radio. This is not necessarily the same as the Hardware EUI.
|
||||||
|
* To obtain the Hardware EUI, use the hweui() function.
|
||||||
|
*/
|
||||||
|
String deveui();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get the RN2xx3's hardware and firmware version number. This is also used
|
||||||
|
* to detect if the module is either an RN2483 or an RN2903.
|
||||||
|
*/
|
||||||
|
String sysver();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initialise the RN2xx3 and join the LoRa network (if applicable).
|
||||||
|
* This function can only be called after calling initABP() or initOTAA().
|
||||||
|
* The sole purpose of this function is to re-initialise the radio if it
|
||||||
|
* is in an unknown state.
|
||||||
|
*/
|
||||||
|
bool init();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initialise the RN2xx3 and join a network using personalization.
|
||||||
|
*
|
||||||
|
* addr: The device address as a HEX string.
|
||||||
|
* Example "0203FFEE"
|
||||||
|
* AppSKey: Application Session Key as a HEX string.
|
||||||
|
* Example "8D7FFEF938589D95AAD928C2E2E7E48F"
|
||||||
|
* NwkSKey: Network Session Key as a HEX string.
|
||||||
|
* Example "AE17E567AECC8787F749A62F5541D522"
|
||||||
|
*/
|
||||||
|
bool initABP(String addr, String AppSKey, String NwkSKey);
|
||||||
|
|
||||||
|
//TODO: initABP(uint8_t * addr, uint8_t * AppSKey, uint8_t * NwkSKey)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initialise the RN2xx3 and join a network using over the air activation.
|
||||||
|
*
|
||||||
|
* AppEUI: Application EUI as a HEX string.
|
||||||
|
* Example "70B3D57ED00001A6"
|
||||||
|
* AppKey: Application key as a HEX string.
|
||||||
|
* Example "A23C96EE13804963F8C2BD6285448198"
|
||||||
|
* DevEUI: Device EUI as a HEX string.
|
||||||
|
* Example "0011223344556677"
|
||||||
|
* If the DevEUI parameter is omitted, the Hardware EUI from module will be used
|
||||||
|
* If no keys, or invalid length keys, are provided, no keys
|
||||||
|
* will be configured. If the module is already configured with some keys
|
||||||
|
* they will be used. Otherwise the join will fail and this function
|
||||||
|
* will return false.
|
||||||
|
*/
|
||||||
|
bool initOTAA(String AppEUI="", String AppKey="", String DevEUI="");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initialise the RN2xx3 and join a network using over the air activation,
|
||||||
|
* using byte arrays. This is useful when storing the keys in eeprom or flash
|
||||||
|
* and reading them out in runtime.
|
||||||
|
*
|
||||||
|
* AppEUI: Application EUI as a uint8_t buffer
|
||||||
|
* AppKey: Application key as a uint8_t buffer
|
||||||
|
* DevEui: Device EUI as a uint8_t buffer (optional - set to 0 to use Hardware EUI)
|
||||||
|
*/
|
||||||
|
bool initOTAA(uint8_t * AppEUI, uint8_t * AppKey, uint8_t * DevEui);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Transmit the provided data. The data is hex-encoded by this library,
|
||||||
|
* so plain text can be provided.
|
||||||
|
* This function is an alias for txUncnf().
|
||||||
|
*
|
||||||
|
* Parameter is an ascii text string.
|
||||||
|
*/
|
||||||
|
TX_RETURN_TYPE tx(String);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Transmit raw byte encoded data via LoRa WAN.
|
||||||
|
* This method expects a raw byte array as first parameter.
|
||||||
|
* The second parameter is the count of the bytes to send.
|
||||||
|
*/
|
||||||
|
TX_RETURN_TYPE txBytes(const byte*, uint8_t);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Do a confirmed transmission via LoRa WAN.
|
||||||
|
*
|
||||||
|
* Parameter is an ascii text string.
|
||||||
|
*/
|
||||||
|
TX_RETURN_TYPE txCnf(String);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Do an unconfirmed transmission via LoRa WAN.
|
||||||
|
*
|
||||||
|
* Parameter is an ascii text string.
|
||||||
|
*/
|
||||||
|
TX_RETURN_TYPE txUncnf(String);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Transmit the provided data using the provided command.
|
||||||
|
*
|
||||||
|
* String - the tx command to send
|
||||||
|
can only be one of "mac tx cnf 1 " or "mac tx uncnf 1 "
|
||||||
|
* String - an ascii text string if bool is true. A HEX string if bool is false.
|
||||||
|
* bool - should the data string be hex encoded or not
|
||||||
|
*/
|
||||||
|
TX_RETURN_TYPE txCommand(String, String, bool);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Change the datarate at which the RN2xx3 transmits.
|
||||||
|
* A value of between 0 and 5 can be specified,
|
||||||
|
* as is defined in the LoRaWan specs.
|
||||||
|
* This can be overwritten by the network when using OTAA.
|
||||||
|
* So to force a datarate, call this function after initOTAA().
|
||||||
|
*/
|
||||||
|
void setDR(int dr);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Put the RN2xx3 to sleep for a specified timeframe.
|
||||||
|
* The RN2xx3 accepts values from 100 to 4294967296.
|
||||||
|
* Rumour has it that you need to do a autobaud() after the module wakes up again.
|
||||||
|
*/
|
||||||
|
void sleep(long msec);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Send a raw command to the RN2xx3 module.
|
||||||
|
* Returns the raw string as received back from the RN2xx3.
|
||||||
|
* If the RN2xx3 replies with multiple line, only the first line will be returned.
|
||||||
|
*/
|
||||||
|
String sendRawCommand(String command);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the module type either RN2903 or RN2483, or NA.
|
||||||
|
*/
|
||||||
|
RN2xx3_t moduleType();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set the active channels to use.
|
||||||
|
* Returns true if setting the channels is possible.
|
||||||
|
* Returns false if you are trying to use the wrong channels on the wrong module type.
|
||||||
|
*/
|
||||||
|
bool setFrequencyPlan(FREQ_PLAN);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the last downlink message HEX string.
|
||||||
|
*/
|
||||||
|
String getRx();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get the RN2xx3's SNR of the last received packet. Helpful to debug link quality.
|
||||||
|
*/
|
||||||
|
int getSNR();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Encode an ASCII string to a HEX string as needed when passed
|
||||||
|
* to the RN2xx3 module.
|
||||||
|
*/
|
||||||
|
String base16encode(String);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Decode a HEX string to an ASCII string. Useful to decode a
|
||||||
|
* string received from the RN2xx3.
|
||||||
|
*/
|
||||||
|
String base16decode(String);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Stream& _serial;
|
||||||
|
|
||||||
|
RN2xx3_t _moduleType = RN_NA;
|
||||||
|
|
||||||
|
//Flags to switch code paths. Default is to use OTAA.
|
||||||
|
bool _otaa = true;
|
||||||
|
|
||||||
|
//The default address to use on TTN if no address is defined.
|
||||||
|
//This one falls in the "testing" address space.
|
||||||
|
String _devAddr = "03FFBEEF";
|
||||||
|
|
||||||
|
// if you want to use another DevEUI than the hardware one
|
||||||
|
// use this deveui for LoRa WAN
|
||||||
|
String _deveui = "0011223344556677";
|
||||||
|
|
||||||
|
//the appeui to use for LoRa WAN
|
||||||
|
String _appeui = "0";
|
||||||
|
|
||||||
|
//the nwkskey to use for LoRa WAN
|
||||||
|
String _nwkskey = "0";
|
||||||
|
|
||||||
|
//the appskey/appkey to use for LoRa WAN
|
||||||
|
String _appskey = "0";
|
||||||
|
|
||||||
|
// The downlink messenge
|
||||||
|
String _rxMessenge = "";
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Auto configure for either RN2903 or RN2483 module
|
||||||
|
*/
|
||||||
|
RN2xx3_t configureModuleType();
|
||||||
|
|
||||||
|
void sendEncoded(String);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
Loading…
Reference in a new issue