LoRa

Quick Links

Antennas   |   FeatherWing LoRa 900 MHz   |   M4 + LoRa FeatherWing   |   M0 + LoRa FeatherWing   |   Using Arduino Cryptography Library   |   Using Spaniakos encryption   |  

 

RadioHead RFM9x Library

FeatherWing LoRa code examples

Current consumption ~120 mA peak during transmit, ~40 mA during active radio listening.   Range of over 1.2 mi line-of-sight with quarter-wave antenna.  

Use "packetized" mode rather than "raw" to keep the code easy.  

 

Antennas

A simple wire of the right length works very well.  

This Edge-Launch SMA Connector for 1.6mm / 0.062" Thick PCBs fits the Adafruit Feather and FeatherWing LoRa products.   Connect this antenna: Right-angle Mini GSM/Cellular Quad-Band Antenna - 2dBi SMA Plug.  

 

FeatherWing LoRa 900 MHz

AF LoRa Radio FeatherWing - RFM95W 900 MHz

AF Tutorial

For the best range, add one of the following antenna options:

Labeled photo comparison of RP-SMA vs SMA connectors

This is only the LoRa module (no microcontroller).   Must wire connections for RST, CS, and INT and then reference them in code.   An interrupt-capable pin is required for IRQ.  

The LoRa FeatherWing has six GPIO pins labeled A through F in the center of the board.   Those pins correspond to particular outside pin locations.   The GPIO pin assignment (e.g. digital output #5) varies from one microcontroller to the next.   Use the table below as a reference between the GPIO pin connections A through F on the LoRa FeatherWing board and GPIO assigments to various microcontrollers.  

LoRa FeatherWing (separate microcontroller)
LoRa
FeatherWing
Argon
Boron
Xenon
Feather
M0
Basic
Feather
M4
Express
Feather
32u4
CS B   (D5) B   (10) B   (10) B   (10)
RST C   D4 MISO A   (11) A   (11) A   (11)
IRQ A   (D6) D   (6) D   (6) SDA   (2)

In the above table, must wire CS, RST, and IRQ to A/B/C/D/E/F/RX/TX/SCL/SDA.   The other table cells without CS/RST/IRQ labels are only for reference.   IRQ = INT.  

For Feather M4 Express, nearly all GPIO pins are interrupt capable, so you could for example use D16 for CS, D17 for RST and D18 for IRQ.

 

LoRa FeatherWing Pins
LoRa
FeatherWing
Pin
Feather
Spec
A D10
B D9
C D6
D D5
E SCL (D21)
F A5/D25 (D19)
RX RX/D0
TX TX/D1
SCL SCL/D21
SDA SDA

NOTE: It is not necessary to use A,B,C,D/E,F,RX,TX,SCL,SDA pins.   Any interrupt capabable GPIO pin may be used.  

 


// Feather M0 (AF #2772) with the 900 MHz LoRa FeatherWing (AF #3231) stacked onto it. 
// M0: A=11; B=10; C=9; D=6; E=5
#define RFM95_CS  10   // "B"   alternative is 8
#define RFM95_RST 11   // "A"   alternative is 4
#define RFM95_INT  6   // "D"   alternative is 3

 

Feather M4 + LoRa FeatherWing

The code that follows shows how to send as fast as possible using LoRa an AES 256 encrypted message between a Adafruit Feather M4 Express (ATSAMD51 ) Adafruit Adafruit LoRa Radio FeatherWing - RFM95W 900 MHz The Arduino Cryptography Library is used for the encryption.   I was easily able to send an encrypted 16 byte 256 AES encrypted message wirelessly between the two devices every 91 ms or a rate of 11 Hz (11 messages per second) with a 0 % error rate (missed transmissions) measured over 12 hours.

The transmitted message consists of a random integer between 0 and 254.   The function itoa() is used to convert the integer to a character array.   A floating point value could be substituted for the integer if the custom dtostrf() function is used. Function dtostrf() is not normally available for the M0 or M4 MCU.  

The received message is un-encrypted, and then converted back to an integer by the atoi() function.   If you are converting to a float, use the atof() function.  

LoRa Sendeer


/*

  Adafruit Feather M4 Express (ATSAMD51)
  LoRa FeatherWing (separate, stacked)

  Feather M4 built-in red LED:
    Blinks for each LoRa Tx
    On constant when when LoRa ack fails.
    Blinks S-O-S on LoRa initialization failure. 

  Sends AES 256 encrypted 16 byte array as quickly as possible
  (streaming) to another LoRa device.  Typical communicaiton
  speed is 91 ms or 11 Hz.  
  
*/


/////////////////////////////////////////////////////////////////////////
const byte pin_built_in_led = 13;  

// DEBUG turns on/off serial output
#define DEBUG false

void blinkERR(byte ledPIN){
  // S-O-S
  const int S = 150, O = 300;
  for(int i = 3; i>0; i--){
    digitalWrite(ledPIN, HIGH);
    delay(S);
    digitalWrite(ledPIN, LOW);
    delay(S);
  }    
  delay(200);
  for(int i = 3; i>0; i--){
    digitalWrite(ledPIN, HIGH);
    delay(O);
    digitalWrite(ledPIN, LOW);
    delay(O);
  }    
  delay(200);
  for(int i = 3; i>0; i--){
    digitalWrite(ledPIN, HIGH);
    delay(S);
    digitalWrite(ledPIN, LOW);
    delay(S);
  }    
  delay(200);
} // blinkERR()


/////////////////////////////////////////////////////////////////////////
//  Crypto - Arduino Cryptography Library

#include <Crypto.h>
#include <AES.h>
#include <string.h>

// key[32] holds 256 bit encryption key (edit below to make your key unique)
// For the best security, the key below should be stored and used from on a Crypto Authentication Chip.

byte key[32] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
                    0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
                    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
                    0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F};

byte plaintext[16];

//cypher[16] stores the encrypted text
byte cypher[16];

//decryptedtext[16] stores decrypted text after decryption
byte decryptedtext[16];

//create an object of AES256 class
AES256 aes256;  

/////////////////////////////////////////////////////////////////////////

/*
  dtostrf - Emulation for dtostrf function from avr-libc
  Copyright (c) 2015 Arduino LLC.  All rights reserved.
  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.
  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.
  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/
// https://github.com/arduino/ArduinoCore-samd/blob/master/cores/arduino/avr/dtostrf.c

/*
char buff[14];
float f = -3.1415926;
Serial.print("f = ");Serial.println(f, 6);
sprintf(buff, "%E", f);
Serial.print("buff = '"); Serial.print(buff); Serial.println("'");
*/

char *dtostrf (double val, signed char width, unsigned char prec, char *sout) {
  asm(".global _printf_float");
  char fmt[20];
  sprintf(fmt, "%%%d.%df", width, prec);
  sprintf(sout, fmt, val);
  return sout;
} // dtostrf()

/////////////////////////////////////////////////////////////////////////

// Download the entire library from (look for .zip file link at top of page): 
//  http://www.airspayce.com/mikem/arduino/RadioHead/index.html


// Feather M4 with LoRa FeatherWing
#include <SPI.h>
#include <RH_RF95.h>
#include <RHReliableDatagram.h>

// LoRa FeatherWing with separate Feather M4 Express
#define RFM95_CS  10  // 10 or 16
#define RFM95_RST 11  // 11 or 17 
#define RFM95_INT 6   // 6 or 18

#define RF95_FREQ 915.0
// Singleton instance of the radio driver
RH_RF95 rf95(RFM95_CS, RFM95_INT);

#define SENDER_ADDRESS 1    // The LoRa device that sends messages to this device
#define RECEIVER_ADDRESS 2  // This LoRa device

// Class to manage message delivery and receipt, using the rf95 declared above
RHReliableDatagram manager(rf95, SENDER_ADDRESS);

byte buf[RH_RF95_MAX_MESSAGE_LEN];
byte arrData[16]; // holds data to be sent via LoRa
unsigned long tx_count = 0;
unsigned long tx_err_count = 0;

//////////////////////////////////////////////////////////////////////////////
// TimerA


unsigned long timer_a_lap = 0;  // timer

void sendLoRaMsg(byte *arr, int len) {

    /*
    #if DEBUG
    Serial.print("arr: '");
    for (int i=0; i<len; i++) {
      Serial.print(char(arr[i]));
    }
    Serial.println("'"); 
    #endif
    */

    // Encrypt the LoRa message to Tx as cypher
    aes256.encryptBlock(cypher,arr);

    tx_count++;
    if (tx_count < 0) tx_count = 1;

    // Persistently send LoR message (timeout after 10 sec)..
    // Method sendtoWait() will wait for an ack from the receipient.
    if (manager.sendtoWait(cypher, len, RECEIVER_ADDRESS))   {
      // The receiver acknowledged the Tx
      digitalWrite(pin_built_in_led, LOW);           
      //#if DEBUG
      //Serial.println("sendtoWait to successful");
      //#endif
    } else {
      tx_err_count++;
      if (tx_err_count < 0) tx_err_count = 1;
      #if DEBUG
      Serial.println("sendtoWait failed!");
      Serial.print("LoRa Tx to "); Serial.print(RECEIVER_ADDRESS); 
      Serial.print(" ; tx_count = "); Serial.print(tx_count);
      Serial.print(" ; tx_err_count: "); 
      Serial.println(tx_err_count);
      #endif
    } // manager.sendtoWait()
    /*
    #if DEBUG
    Serial.print("LoRa Tx to "); Serial.print(RECEIVER_ADDRESS); 
    Serial.print(" ; tx_count = "); Serial.print(tx_count);
    Serial.print(" ; tx_err_count: "); 
    Serial.println(tx_err_count);
    Serial.println();
    #endif
    */
    
} // sendLoRaMsg()


void timerA() {
  // Send a LoRa message consisting of a random number 
  // between 0 and 254. 
  
  if (timer_a_lap > micros())  timer_a_lap = micros();
  // 10000000 us = 10000 ms = 10 sec = 0.1 Hz 
  // 1000000 us = 1000 ms = 1 sec = 1 Hz
  // 100000 us = 100 ms = 0.1 sec = 10 Hz
  // 10000 us = 10 ms = 0.01 sec = 100 Hz
  // 1000 us = 1 ms = 0.001 sec = 1 kHz
  // 100 us = 0.1 ms = 0.0001 sec = 10 kHz
  // 10 us = 0.01 ms = 0.00001 sec = 100 kHz
  // 1 us = 0.001 ms = 0.000001 sec = 1000 kHz
  
  // Tx up to every 1 ms or 1 kHz good
  //if (micros() - timer_a_lap > 10000) { 
    digitalWrite(pin_built_in_led, HIGH); 

    // Generate a random integer between 0 and 254 to send
    int n = random(0,254);
    for (int i=0; i<sizeof(arrData); i++) {
      arrData[i] = 0x20;  // space character
    }
    
    // ITOA() converts int to string
    // char* itoa(int num, char* buffer, int base)
    // The "(char*)" is a type cast that tells the compiler to treat arrData as a
    // char pointer and not as a byte array.  iota expect char* as the 2nd argument.
    itoa(n, (char*)arrData, 10);

    /*
    #if DEBUG
    Serial.print("Sending LoRa msg '");
    for (int i=0; i<sizeof(arrData); i++) {
      Serial.print(char(arrData[i]));
    }
    Serial.print("' to "); Serial.println(RECEIVER_ADDRESS); 
    #endif
    */
    sendLoRaMsg(arrData, sizeof(arrData));

    timer_a_lap = micros(); // reset the timer
  //}
} // timerA()

/////////////////////////////////////////////////////////////////////////


void setup() {

  pinMode(pin_built_in_led, OUTPUT);
  digitalWrite(pin_built_in_led, LOW);

  #if DEBUG
  Serial.begin(115200);
  while (!Serial) {
    delay(100);
    if (millis() % 2)
      digitalWrite(pin_built_in_led, HIGH);
    else
      digitalWrite(pin_built_in_led, LOW);
  }
  digitalWrite(pin_built_in_led, LOW);
  Serial.println("\nSerial ready");
  #endif

  pinMode(RFM95_RST, OUTPUT);
  digitalWrite(RFM95_RST, HIGH);

  // manual reset
  digitalWrite(RFM95_RST, LOW);
  delay(10);
  digitalWrite(RFM95_RST, HIGH);
  delay(10);

  while (!manager.init()) {
    #if DEBUG
    Serial.println("LoRa radio init failed");
    Serial.println("Uncomment '#define SERIAL_DEBUG' in RH_RF95.cpp for detailed debug info");
    #endif
    while (1) {
      blinkERR(pin_built_in_led);
    }
  }
  Serial.println("LoRa radio init OK!");

  // Defaults after init are 434.0MHz, modulation GFSK_Rb250Fd250, +13dbM
  if (!rf95.setFrequency(RF95_FREQ)) {
    #if DEBUG
    Serial.println("setFrequency failed");
    #endif
    while (1) {
      blinkERR(pin_built_in_led);
    }
  }
  #if DEBUG
  Serial.print("Set Freq to: "); Serial.println(RF95_FREQ);
  #endif
  
  // Defaults after init are 434.0MHz, 13dBm, Bw = 125 kHz, Cr = 4/5, Sf = 128chips/symbol, CRC on

  // The default transmitter power is 13dBm, using PA_BOOST.
  // If you are using RFM95/96/97/98 modules which uses the PA_BOOST transmitter pin, then 
  // you can set transmitter powers from 5 to 23 dBm:
  rf95.setTxPower(23, false);

  // Set Key for AES
  aes256.setKey(key,sizeof(key));

  randomSeed(micros());
  Serial.println("setup finished\n");
} // setup()


void loop() {
  unsigned long timer_us = micros();

  // Send the LoRa message as quickly as possible
  digitalWrite(pin_built_in_led, HIGH); 
  // Generate a random integer between 0 and 254 to send
  int n = random(0,254);
  for (int i=0; i<sizeof(arrData); i++) {
    arrData[i] = 0x20;  // space character
  }
  // ITOA() converts int to string
  // char* itoa(int num, char* buffer, int base)
  // The "(char*)" is a type cast that tells the compiler to treat arrData as a
  // char pointer and not as a bype array.  iota expect char* as the 2nd argument.
  itoa(n, (char*)arrData, 10);
  /*
  #if DEBUG
  Serial.print("Sending LoRa msg '");
  for (int i=0; i<sizeof(arrData); i++) {
    Serial.print(char(arrData[i]));
  }
  Serial.print("' to "); Serial.println(RECEIVER_ADDRESS); 
  #endif
  */
  sendLoRaMsg(arrData, sizeof(arrData));

  // Show the time consumed by LoRa Tx..
  // (typically 90600 us = 91 ms = 11 Hz)
  #if DEBUG
  if (micros() - timer_us < 10000) {
    Serial.print(micros() - timer_us);
    Serial.println(" us");
  }
  #endif
  
} // loop()

 

LoRa Receiver


/*
  Adafruit Feather M4 Express (ATSAMD51)
  LoRa FeatherWing (separate, stacked)

  Feather M4 built-in red LED:
    Blinks for each LoRa Rx

*/


/////////////////////////////////////////////////////////////////////////
const byte pin_built_in_led = 13;  

// DEBUG turns on/off serial output
#define DEBUG false

void blinkERR(byte ledPIN){
  // S-O-S
  const int S = 150, O = 300;
  for(int i = 3; i>0; i--){
    digitalWrite(ledPIN, HIGH);
    delay(S);
    digitalWrite(ledPIN, LOW);
    delay(S);
  }    
  delay(200);
  for(int i = 3; i>0; i--){
    digitalWrite(ledPIN, HIGH);
    delay(O);
    digitalWrite(ledPIN, LOW);
    delay(O);
  }    
  delay(200);
  for(int i = 3; i>0; i--){
    digitalWrite(ledPIN, HIGH);
    delay(S);
    digitalWrite(ledPIN, LOW);
    delay(S);
  }    
  delay(200);
} // blinkERR()

/////////////////////////////////////////////////////////////////////////
//  Crypto - Arduino Cryptography Library

#include <Crypto.h>
#include <AES.h>
#include <string.h>

// key[32] holds 256 bit encryption key (edit below to make your key unique)
// For the best security, the key below should be stored and used from on a Crypto Authentication Chip.

byte key[32] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
                    0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
                    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
                    0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F};

//decryptedtext[16] stores decrypted text after decryption
byte decryptedtext[16];

//create an object of AES256 class
AES256 aes256;  

/////////////////////////////////////////////////////////////////////////

// Download the entire library from (look for .zip file link at top of page): 
//  http://www.airspayce.com/mikem/arduino/RadioHead/index.html


// Feather M4 with LoRa FeatherWing
#include <SPI.h>
#include <RH_RF95.h>
#include <RHReliableDatagram.h>

// LoRa FeatherWing with separate Feather M4 Express
#define RFM95_CS  10  // 10 or 16
#define RFM95_RST 11  // 11 or 17 
#define RFM95_INT 6   // 6 or 18

#define RF95_FREQ 915.0
// Singleton instance of the radio driver
RH_RF95 rf95(RFM95_CS, RFM95_INT);

#define SENDER_ADDRESS 1    // The LoRa device that sends messages to this device
#define RECEIVER_ADDRESS 2  // This LoRa device

// Class to manage message delivery and receipt, using the rf95 declared above
RHReliableDatagram manager(rf95, RECEIVER_ADDRESS);

byte buf[RH_RF95_MAX_MESSAGE_LEN];

void LoRaRx() {
  // Wait for a message addressed to us from the client
  if (manager.available())   {
    digitalWrite(pin_built_in_led, HIGH);
    uint8_t len = sizeof(buf);
    uint8_t from;
    
    // Persistently send back a reply, or timeout after 30 sec. 
    if (manager.recvfromAck(buf, &len, &from))  {
      digitalWrite(pin_built_in_led, HIGH);
      // Only accept messages from SENDER_ADDRESS
      if (from == SENDER_ADDRESS) {
        // unencrypt buf
        aes256.decryptBlock(decryptedtext,buf);
        // Get the integer value from the character array buf
        int val = atoi((char*)decryptedtext);
        #if DEBUG         
        Serial.print("decryptedtext: ["); Serial.print(sizeof(decryptedtext)); Serial.print("] '");
        for (int i=0; i<sizeof(decryptedtext); i++) {
          Serial.print(char(decryptedtext[i]));
        }
        Serial.println("'");
        Serial.print("LoRa msg received number: "); Serial.print(val); Serial.print(" from: "); Serial.println(from);           
        #endif
        digitalWrite(pin_built_in_led, LOW);
      } else {
        #if DEBUG
        Serial.print("Ignored LoRa msg from "); Serial.println(from);
        #endif
      } // RECEIVER_ADDRESS
    } else {
      #if DEBUG
      Serial.print("Sending of ack to "); Serial.print(from); Serial.println(" FAILED!");
      #endif
    } // manager.recv()    

    #if DEBUG
    Serial.println(""); 
    #endif

  } // manager.available()  
} // LoRaRx()

/////////////////////////////////////////////////////////////////////////


void setup() {

  pinMode(pin_built_in_led, OUTPUT);
  digitalWrite(pin_built_in_led, LOW);

  #if DEBUG
  Serial.begin(115200);
  while (!Serial) {
    delay(100);
    if (millis() % 2)
      digitalWrite(pin_built_in_led, HIGH);
    else
      digitalWrite(pin_built_in_led, LOW);
  }
  digitalWrite(pin_built_in_led, LOW);
  Serial.println("\nSerial ready");
  #endif

  pinMode(RFM95_RST, OUTPUT);
  digitalWrite(RFM95_RST, HIGH);

  // manual reset
  digitalWrite(RFM95_RST, LOW);
  delay(10);
  digitalWrite(RFM95_RST, HIGH);
  delay(10);

  while (!manager.init()) {
    #if DEBUG
    Serial.println("LoRa radio init failed");
    Serial.println("Uncomment '#define SERIAL_DEBUG' in RH_RF95.cpp for detailed debug info");
    #endif
    while (1) {
      blinkERR(pin_built_in_led);
    }
  }
  Serial.println("LoRa radio init OK!");

  // Defaults after init are 434.0MHz, modulation GFSK_Rb250Fd250, +13dbM
  if (!rf95.setFrequency(RF95_FREQ)) {
    #if DEBUG
    Serial.println("setFrequency failed");
    #endif
    while (1) {
      blinkERR(pin_built_in_led);
    }
  }
  #if DEBUG
  Serial.print("Set Freq to: "); Serial.println(RF95_FREQ);
  #endif
  
  // Defaults after init are 434.0MHz, 13dBm, Bw = 125 kHz, Cr = 4/5, Sf = 128chips/symbol, CRC on

  // The default transmitter power is 13dBm, using PA_BOOST.
  // If you are using RFM95/96/97/98 modules which uses the PA_BOOST transmitter pin, then 
  // you can set transmitter powers from 5 to 23 dBm:
  rf95.setTxPower(23, false);

  pinMode(pin_built_in_led, OUTPUT);
  digitalWrite(pin_built_in_led, LOW);

  // Set Key for AES
  aes256.setKey(key,sizeof(key));

  Serial.println("setup finished\n");
} // setup()


void loop() {

  LoRaRx();
    
} // loop()

 

Feather M0 + LoRa FeatherWing

Below is a complete solution for reliable datagram sending of encrypted messages between one 900 MHz LoRa device and another, both with the Feather M0 Basic Proto Feather.   I could not get the encrypted messaging examples that come with the RadioHead library to work, nor could I get the Rhys Weatherley cryptography library to work.   After trying several Arduino encryption libraries, I found the Spaniakos - AES Encryption Library and after some time I came up with a working example that successfully encryptes and decryptes a byte array with as large as 255 elements using AES 256 encryption.  

The example below was developed for running on a Adafruit Feather M0 with the 900 MHz LoRa radio (both sender and receiver).   Change the pin_LED, RFM95_CS, RFM95_RST, RFM_95_INT assignement to match the SPI settings of your microcontroller (see the AF Tutorial).   The 'Sender' generates a random 31 character message of the format "20200216T150644Z;+6.534755E+06" (an ISO8601 date/time value followed by a simulated sensor value in exponential format with seven significant figures). The Sender encrypts the message, and then sends it via LoRa to the 'Receiver'.   The 'Receiver' receives the encrypted message, decrypts it, and then sends back the decrypted message as a reply to the 'Sender'.   The reply received by the 'Sender' is compared to the original unencrypted message sent.   In a production enviroment, you would not of course send back the unencrypted message.   The process is initiated by the 'Sender", with a new message sent every 100 ms to 5 seconds.  

Code for Sender

// Feather M0 Basic + LoRa 900 MHz
// Encrypted LoRa 900 MHz communications + RadioHead reliable datagram
//
// Reliable Datagram is reliable in the sense that messages are acknowledged by the 
// recipient, and unacknowledged messages are retransmitted until acknowledged or the
// retries are exhausted.

// The message to be sent is encrypted with AES 256 bit encryption using
// Spaniakos - AES Encryption Library.


// M0  (Feather M0)
const byte pin_LED = 13;
const byte pin_BATT = A7;

//////////////////////////////////////////////////////////////////////////////
// 10000 ms = 10 sec = 0.1 Hz 
// 1000 ms = 1 sec = 1 Hz
// 100 ms = 0.1 sec = 10 Hz
// 10 ms = 0.01 sec = 100 Hz
unsigned long timerInterval = random(100,5000);  
unsigned long timerLast = 0;  // timer
//////////////////////////////////////////////////////////////////////////////

// RHReliableDatagram is reliable in the sense that messages are acknowledged by the 
// recipient, and unacknowledged messages are retransmitted until acknowledged or the
// retries are exhausted.

//////////////////////////////////////////////////////////////////////////////
//  LoRa 900 MHz Radio
//  RadioHead library for LoRa Radio
//  http://www.airspayce.com/mikem/arduino/RadioHead/index.html
#include <RHReliableDatagram.h>
#include <RH_RF95.h>
#include <SPI.h>

#define SENDER_ADDRESS 0x1    // This LoRa device
#define RECEIVER_ADDRESS 0x2  // The LoRa device that will receive messages from this device

// Feather M0 (AF #2772) with the 900 MHz LoRa FeatherWing (AF #3231) stacked onto it. 
#define RFM95_CS  10   // "B"   alternative is 8
#define RFM95_RST 11   // "A"   alternative is 4
#define RFM95_INT  6   // "D"   alternative is 3

// Define frequency (set later)
#define RF95_FREQ 915.0
// RH_RF95 (uint8_t slaveSelectPin=SS, uint8_t interruptPin=2, RHGenericSPI &spi=hardware_spi)
RH_RF95 rf95(RFM95_CS, RFM95_INT);
// Class to manage message delivery and receipt, using the rf95 declared above
RHReliableDatagram manager(rf95, SENDER_ADDRESS);
uint8_t buf[RH_RF95_MAX_MESSAGE_LEN]; // holds reply from receiver

//////////////////////////////////////////////////////////////////////////////
// Show serial messages when DEBUG = true, otherwise minimize them.
// WARNING:   I experienced the Feather M0 waiting for the serial monitor to be
//            loaded from the IDE before the sketch would continue to run.  
//            For this reason, I am wrapping serial output around a DEBUG check.
#define DEBUG false
//////////////////////////////////////////////////////////////////////////////
//  Encryption using Spaniakos - AES Encryption Library for Arduino and Raspberry Pi
//  Download library from: https://github.com/spaniakos/AES/archive/master.zip
//  https://spaniakos.github.io/AES/index.html
//  https://www.arduinolab.net/aes-encryptiondecryption-using-arduino-uno/
#include <AES.h>
AES aes;
byte *key = (unsigned char*)"01234567890123456789012345678901"; // encryption key
unsigned long long int myIv = 36753562; // CBC initialization vector; real iv = iv x2 ex: 01234567 = 0123456701234567
byte iv [N_BLOCK] ;
boolean bVerbose = false;
// You must define arrays for encryption / decryption here globally 
// if you intend to populate the contents of strToEncrypt in a loop 
// where the contents change (such as sensor data).
// Define arrays for encryption.  
byte iSizeUnpadded = 31;
byte iSizePadded = 33;  // +17 works for any size. See setup() for calculation of exact padding. 
byte arrToEncrypt[31]; // Array with data to be encrypted. arrToEncrypt[iSizePadded]
byte cipher[33]; // Encrypted arrToEncrypt.  cipher[iSizePadded]
//////////////////////////////////////////////////////////////////////////////
unsigned long successCount = 0;
boolean bStopOnError = false;


void setup() {
  pinMode(pin_LED, OUTPUT);

  #if DEBUG
  Serial.begin(9600);
  while (!Serial) {
    delay(1);
  }
  Serial.println("Serial ready");
  #endif
  
  // declare and initialize the variables for encryption / decryption
  aes.iv_inc();
  // Use the statement below to calculate the exact size for iSizePadded
  //Serial.print("iSizePadded = "); Serial.println(sizeof(arrToEncrypt) + (N_BLOCK - ((sizeof(arrToEncrypt)-1) % 16)));
  
  //////////////////////////////////////////////////////////////////////////////
  //  LoRa 900 MHz Radio
  pinMode(RFM95_RST, OUTPUT);
  delay(10);
  digitalWrite(RFM95_RST, HIGH);
  delay(10);
  // manual reset
  digitalWrite(RFM95_RST, LOW);
  delay(10);
  digitalWrite(RFM95_RST, HIGH);
  delay(10);

  if (!manager.init()) {
    #if DEBUG
    Serial.println("init failed");
    #endif
    while (1) blinkERR(pin_LED);
  }
  #if DEBUG
  Serial.println("LoRa radio init OK!");  
  #endif
  // Defaults after init are 434.0MHz, modulation GFSK_Rb250Fd250, +13dbM
  if (!rf95.setFrequency(RF95_FREQ)) {
    #if DEBUG
    Serial.println("setFrequency failed");
    #endif
    while (1) blinkERR(pin_LED);
  }
  #if DEBUG
  Serial.print("LoRa to: "); Serial.print(RF95_FREQ); Serial.println(" Hz");
  #endif
  
  // Defaults after init are 434.0MHz, 13dBm, Bw = 125 kHz, Cr = 4/5, Sf = 128chips/symbol, CRC on

  // The default transmitter power is 13dBm, using PA_BOOST.
  // If you are using RFM95/96/97/98 modules which uses the PA_BOOST transmitter pin, then 
  // you can set transmitter powers from 5 to 23 dBm:
  rf95.setTxPower(23, false);
  // If you are using Modtronix inAir4 or inAir9,or any other module which uses the
  // You can optionally require this module to wait until Channel Activity
  // Detection shows no activity on the channel before transmitting by setting
  // the CAD timeout to non-zero:
  //  rf95.setCADTimeout(10000);

  blinkLED(pin_LED);
} // setup();


void loop() {

  if (timerLast > millis())  timerLast = millis();
  if ((millis() - timerLast) > timerInterval) {
    // Send a message to manager_server
    blinkLED(pin_LED);
    #if DEBUG
    Serial.println("Sending encrypted data via RadioHead Reliable Datagram");    
    #endif
    digitalWrite(pin_LED, HIGH);

    // "20200216T150644Z;+6.534755E+06"
    BuildSimulatedSerialSensorData(arrToEncrypt, sizeof(arrToEncrypt));
    #if DEBUG
    Serial.print("'");
    for (int i=0; i < sizeof(arrToEncrypt); i++) {
      Serial.print(char(arrToEncrypt[i]));
    }
    Serial.println("'");  
    #endif
    
    int iSizePadded = sizeof(arrToEncrypt) + (N_BLOCK - ((sizeof(arrToEncrypt)-1) % 16)); // length of padded string
    if (iSizePadded > sizeof(cipher)) {
      #if DEBUG
      Serial.print("ERROR - array size of cipher or arrDecryptedPadded is too small!");
      #endif
      while (1) blinkERR(pin_LED);
    }
    // Encrypt arrToEncrypt and update cipher with the result
    aesEncrypt(256, bVerbose);   
    
    if (manager.sendtoWait(cipher, sizeof(cipher), RECEIVER_ADDRESS))   {
      // Now wait for a reply from the server
      uint8_t len = sizeof(buf);
      uint8_t from;   
      if (manager.recvfromAckTimeout(buf, &len, 2000, &from)) {
        #if DEBUG
        Serial.print("Reply from : 0x");Serial.print(from, HEX);
        Serial.print(": '"); Serial.print((char*)buf); Serial.println("'");
        #endif
        // Compare the received data buff to the data sent arrToEncrypt
        int matches = 0;
        for(int i=0; i < sizeof(arrToEncrypt); i++){
          if(buf[i]==arrToEncrypt[i]){     
            matches++;  
          } 
        }
        if (matches == sizeof(arrToEncrypt)) {
          successCount++;
          digitalWrite(pin_LED, LOW); 
        }
        #if DEBUG
        Serial.print(matches/sizeof(arrToEncrypt)*100);
        Serial.print("% match between arrToEncrypt and buff for size = ");
        Serial.print(sizeof(arrToEncrypt)); Serial.print(" bytes  ( encrypt/decrypt count = ");
        Serial.print(successCount); Serial.println(")  ");
        if (matches != sizeof(arrToEncrypt)) {
          Serial.print("ERROR after "); Serial.print(successCount); Serial.println(" encrypt/decrypt cycles");
          Serial.print("sizeof(arrToEncrypt) = "); Serial.println(sizeof(arrToEncrypt));
          Serial.print("matches = "); Serial.println(matches);
          for(int i=0; i < sizeof(arrToEncrypt); i++){
            Serial.print(i); Serial.print(",0x"); Serial.print(arrToEncrypt[i],HEX);
            Serial.print(",0x"); Serial.println(buf[i],HEX);
          }
          while (1);
        }
        Serial.println();
        #endif
      } else {
        #if DEBUG
        Serial.println("No reply, is receiver running?\n");
        #endif
      }
    } else {
      #if DEBUG
      Serial.println("sendtoWait failed\n");
      #endif
    }
    timerInterval = random(100,5000);  
    timerLast = millis();
  } // timer

  yield();

} // loop()


void blinkLED(byte ledPIN){
  //  consumes 300 ms.
  for(int i = 5; i>0; i--){
    digitalWrite(ledPIN, HIGH);
    delay(30);
    digitalWrite(ledPIN, LOW);
    delay(30);
  }    
} // blinkLED()


void blinkERR(byte ledPIN){
  // S-O-S
  const int S = 150, O = 300;
  for(int i = 3; i>0; i--){
    digitalWrite(ledPIN, HIGH);
    delay(S);
    digitalWrite(ledPIN, LOW);
    delay(S);
  }    
  delay(200);
  for(int i = 3; i>0; i--){
    digitalWrite(ledPIN, HIGH);
    delay(O);
    digitalWrite(ledPIN, LOW);
    delay(O);
  }    
  delay(200);
  for(int i = 3; i>0; i--){
    digitalWrite(ledPIN, HIGH);
    delay(S);
    digitalWrite(ledPIN, LOW);
    delay(S);
  }    
  delay(200);
} // blinkERR()

//////////////////////////////////////////////////////////////////////////////
//  Encryption using Spaniakos - AES Encryption Library for Arduino and Raspberry Pi

void aesEncrypt(int bits, boolean bVerbose) {
  //  Encrypts arrToEncrypt based on bits (256) bit encryption and updates
  //  cipher with the encrypted result.   
  aes.set_IV(myIv);
  aes.get_IV(iv);
  unsigned long us = micros ();
  //Serial.print("aesEncrypt aes.get_size() = "); Serial.println(aes.get_size());
  aes.do_aes_encrypt(arrToEncrypt,iSizeUnpadded+1,cipher,key,bits,iv);
  if (bVerbose == true) {
    Serial.print("Encryption took "); Serial.print(micros() - us); Serial.println(" us");
  }
} // aesEncrypt()

//////////////////////////////////////////////////////////////////////////////
// Feather M0 specific functions

/*
  dtostrf - Emulation for dtostrf function from avr-libc
  Copyright (c) 2015 Arduino LLC.  All rights reserved.
  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.
  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.
  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/
// https://github.com/arduino/ArduinoCore-samd/blob/master/cores/arduino/avr/dtostrf.c

/*
char buff[14];
float f = -3.1415926;
Serial.print("f = ");Serial.println(f, 6);
sprintf(buff, "%E", f);
Serial.print("buff = '"); Serial.print(buff); Serial.println("'");
*/

char *dtostrf (double val, signed char width, unsigned char prec, char *sout) {
  asm(".global _printf_float");
  char fmt[20];
  sprintf(fmt, "%%%d.%df", width, prec);
  sprintf(sout, fmt, val);
  return sout;
} // dtostrf()

//////////////////////////////////////////////////////////////////////////////
/*
// "20200216T150644Z;++6.534755E+06"
byte cData[31]; // 16+1+13+1=31
BuildSimulatedSerialSensorData(cData, sizeof(cData));
Serial.print("'");
for (int i=0; i < sizeof(cData); i++) {
  Serial.print(char(cData[i]));
}
Serial.println("'");  
*/

void BuildSimulatedSerialSensorData(byte *arr, int len) {
  //  Populates arr with a simulated serial data string with
  //  a ISO 8601 date time stamp and a sensor value (float).
  //  "20200216T150644Z;+6.534755E+06"

  for (int i=0; i < len; i++) {
    arr[i] = 0x20;  // space character
  }
  
  // Get a random ISO8601 date time string
  byte dateTimeIso8601[16];
  BuildIso8601DateTimeRandom(dateTimeIso8601, sizeof(dateTimeIso8601)); 
  int iLastPos = 0;
  for (int i=0; i < sizeof(dateTimeIso8601); i++) {
    arr[i] = char(dateTimeIso8601[i]);
    iLastPos++;
  }
  iLastPos--;
  //Serial.print("iLastPos = "); Serial.println(iLastPos);
  
  // Add the ";"
  arr[iLastPos+1] = ';';
  iLastPos++;
  //Serial.print("iLastPos = "); Serial.println(iLastPos);

  // Get a random floating point value as a formatted string
  // to scientific notation.
  byte cDouble[14];
  BuildDoubleValRandom(cDouble, sizeof(cDouble));
  int f = iLastPos + 1;
  for (int i=0; i < sizeof(cDouble); i++) {
    arr[f] = char(cDouble[i]);
    f++;
    iLastPos++;
  }
  arr[iLastPos] = 0x00; // null character
} // BuildSimulatedSerialSensorData()

/*
byte cDouble[14];
BuildDoubleValRandom(cDouble, sizeof(cDouble));  
Serial.print("'");
for (int i=0; i < sizeof(cDouble); i++) {
  Serial.print(char(cDouble[i]));
}
Serial.println("'");  
for (int i=0; i < sizeof(cDouble); i++) {
  Serial.print("0x");
  Serial.print(cDouble[i], HEX);
  Serial.print(" ");
}
Serial.println("\n");  
*/

void BuildDoubleValRandom(byte *arr, int len) {
  // Populates arr with a string representation of a 
  // random floating point number formatted in scientific
  // notation with at least 7 significant digits.
  // +6.534755E+06"
  // 
  for (int i=0; i < len; i++) {
    arr[i] = 0x20;  // space character
  }
  long myLong = random(100000000, 999999999);
  long divisor;
  int expnt = random(2,6);
  switch (expnt) {
    case 2:
      divisor = 100;
      break;
    case 3:
      divisor = 1000;
      break;
    case 4:
      divisor = 10000;
      break;
    case 5:
      divisor = 100000;
    default:
      divisor = 10;
  }
  float f = (float)myLong / divisor;
  int sign = random(10);
  if (sign <= 5) {
    f = f * (-1.0);
  }
  //Serial.print("f = "); Serial.println(f, 6);
  char cDbl[len];
  sprintf(cDbl, "%+E", f);  
  //Serial.print("cDbl = '"); Serial.print(cDbl); Serial.println("'");
  //char cDbl[sigFig+8];
  //dtostre(f, cDbl, sigFig, 2);   // 2 = show + sign
  // cDbl = '-1.0002e+05'
  //Serial.print("cDbl = '"); Serial.print(cDbl); Serial.println("'");
  for (int i=0; i < strlen(cDbl); i++) {
    arr[i] = cDbl[i];
  } 
  arr[len-1] = 0x00;  // null character
} // BuildDoubleValRandom()

/*
byte dateTimeIso8601[16];
BuildIso8601DateTimeRandom(dateTimeIso8601, sizeof(dateTimeIso8601)); 
Serial.print("'");
for (int i=0; i < sizeof(dateTimeIso8601); i++) {
  Serial.print(char(dateTimeIso8601[i]));
}
Serial.println("'\n");  
*/

void BuildIso8601DateTimeRandom(byte *arr, int len) {
  // Updates arr with characters representing a random
  // ISO8601 date time formatted as: 20200216T150644Z
  for (int i=0; i < len; i++) {
    arr[i] = 0x20;  // space character
  }
  int iLastPos = 0;
  unsigned long iYearMonthDay = 20200000;
  byte iMonth = random(1, 13);
  iYearMonthDay += iMonth * 100;
  byte iDay = random(1, 29);
  iYearMonthDay += iDay;
  char cYearMonthDay[9];
  sprintf(cYearMonthDay, "%lu", iYearMonthDay);
  //Serial.print("\ncYearMonthDay = '"); Serial.print(cYearMonthDay); Serial.println("'");
  for (int i=0; i < sizeof(cYearMonthDay); i++) {
    arr[i] = cYearMonthDay[i];
  }
  iLastPos = strlen(cYearMonthDay)-1;
  //Serial.print("iLastPos = "); Serial.println(iLastPos);
  // Add the "T"
  arr[iLastPos+1] = 'T';
  iLastPos = iLastPos + 1;  
  // hour
  byte iTime = random(1, 24);
  char cTime[3];
  sprintf(cTime, "%02u", iTime);
  //Serial.print("cTime = '"); Serial.print(cTime); Serial.println("'");
  int f = 0;
  for (int i=iLastPos+1; i < iLastPos+sizeof(cTime); i++) {
    //Serial.print(i); Serial.print(", "); Serial.println(cTime[f]);
    arr[i] = cTime[f];
    f++;
  }
  iLastPos = iLastPos + 2;
  //Serial.print("iLastPos = "); Serial.println(iLastPos);
  // minute
  iTime = random(1, 59);
  sprintf(cTime, "%02u", iTime);
  //Serial.print("cTime = '"); Serial.print(cTime); Serial.println("'");
  f = 0;
  for (int i=iLastPos+1; i < iLastPos+sizeof(cTime); i++) {
    //Serial.print(i); Serial.print(", "); Serial.println(cTime[f]);
    arr[i] = cTime[f];
    f++;
  }
  iLastPos = iLastPos + 2;
  //Serial.print("iLastPos = "); Serial.println(iLastPos);
  // second
  iTime = random(1, 59);
  sprintf(cTime, "%02u", iTime);
  //Serial.print("cTime = '"); Serial.print(cTime); Serial.println("'");
  f = 0;
  for (int i=iLastPos+1; i < iLastPos+sizeof(cTime); i++) {
    //Serial.print(i); Serial.print(", "); Serial.println(cTime[f]);
    arr[i] = cTime[f];
    f++;
  }
  iLastPos = iLastPos + 2;
  //Serial.print("iLastPos = "); Serial.println(iLastPos);
  // Add the "Z"
  arr[iLastPos+1] = 'Z';
  iLastPos = iLastPos + 1;
  //Serial.print("iLastPos = "); Serial.println(iLastPos);
} // BuildIso8601DateTimeRandom

float fGetLgSignedRandFloat() {
  long myLong = random(100000000, 999999999);
  long divisor;
  int expnt = random(2,6);
  switch (expnt) {
    case 2:
      divisor = 100;
      break;
    case 3:
      divisor = 1000;
      break;
    case 4:
      divisor = 10000;
      break;
    case 5:
      divisor = 100000;
    default:
      divisor = 10;
  }
  float f = (float)myLong / divisor;
  int sign = random(10);
  if (sign <= 5) {
    f = f * (-1.0);
  }
  
  return f;
}

long GetLgSignedLong() {
  // long:  -2,147,483,648 to 2,147,483,647
  long myLong = random(2147483647);
  int sign = random(10);
  if (sign <= 5) {
    myLong = myLong * (-1);
  }
  return myLong;
} // GetLgSignedLong()
		

 

Code for Receiver

// Feather M0 Basic + FeatherWing LoRa 900 MHz
// Encrypted LoRa 900 MHz communications + RadioHead reliable datagram
//
// Reliable Datagram is reliable in the sense that messages are acknowledged by the 
// recipient, and unacknowledged messages are retransmitted until acknowledged or the
// retries are exhausted.

// The message to be sent is encrypted with AES 256 bit encryption using
// Spaniakos - AES Encryption Library.


// M0  (Feather M0)
const byte pin_LED = 13;
const byte pin_BATT = A7;

//////////////////////////////////////////////////////////////////////////////
//  LoRa 900 MHz Radio
//  RadioHead library for LoRa Radio
//  http://www.airspayce.com/mikem/arduino/RadioHead/index.html
#include <RHReliableDatagram.h>
#include <RH_RF95.h>
#include <SPI.h>
#define SENDER_ADDRESS 0x1    // The LoRa device that sends messages to this device
#define RECEIVER_ADDRESS 0x2  // This LoRa device
// Feather M0 (AF #2772) with the 900 MHz LoRa FeatherWing (AF #3231) stacked onto it. 
#define RFM95_CS  10   // "B"   alternative is 8
#define RFM95_RST 11   // "A"   alternative is 4
#define RFM95_INT  6   // "D"   alternative is 3
// Define the frequency
#define RF95_FREQ 915.0
// Singleton instance of the radio rf95
RH_RF95 rf95(RFM95_CS, RFM95_INT);
// Class to manage message delivery and receipt, using the rf95 declared above
RHReliableDatagram manager(rf95, RECEIVER_ADDRESS);
byte buf[RH_RF95_MAX_MESSAGE_LEN];

/////////////////////////////////////////////////////////////////////////////\/
// Show serial messages when DEBUG = true, otherwise minimize them.
// WARNING:   I experienced the Feather M0 waiting for the serial monitor to be
//            loaded from the IDE before the sketch would continue to run.  
//            For this reason, I am wrapping serial output around a DEBUG check.
#define DEBUG false
//////////////////////////////////////////////////////////////////////////////
//  Encryption using Spaniakos - AES Encryption Library for Arduino and Raspberry Pi
//  Download library from: https://github.com/spaniakos/AES/archive/master.zip
//  https://spaniakos.github.io/AES/index.html
//  https://www.arduinolab.net/aes-encryptiondecryption-using-arduino-uno/
#include <AES.h>
AES aes;
byte *key = (unsigned char*)"01234567890123456789012345678901"; // encryption key
unsigned long long int myIv = 36753562; // CBC initialization vector; real iv = iv x2 ex: 01234567 = 0123456701234567
byte iv [N_BLOCK] ;
boolean bVerbose = false;
// You must define arrays for encryption / decryption here globally 
// if you intend to populate the contents of strToEncrypt in a loop 
// where the contents change (such as sensor data).
// Define arrays for encryption
byte iSizeUnpadded = 31;
byte iSizePadded = 33;  // =iSizeUnpadded+17 works for any size. See setup() for calculation of exact padding. 
byte cipher[33]; // Encrypted arrToEncrypt.  cipher[iSizePadded]
byte arrDecryptedPadded[33]; // decrypted cipher with padding.  arrDecryptedPadded[iSizePadded]
byte decrypted[31]; // decrypted string without padding.  decrypted[iSizeUnpadded]
//////////////////////////////////////////////////////////////////////////////


void setup() {

  pinMode(pin_LED, OUTPUT);

  #if DEBUG
    Serial.begin(9600);
    while (!Serial) {
      delay(1);
    }
    Serial.println("Serial ready");
  #endif

  // declare and initialize the variables for encryption / decryption
  aes.iv_inc();
  // Use the statement below to calculate the exact size for iSizePadded
  //Serial.print("iSizePadded = "); Serial.println(sizeof(arrToEncrypt) + (N_BLOCK - ((sizeof(arrToEncrypt)-1) % 16)));
  
  //////////////////////////////////////////////////////////////////////////////
  //  LoRa 900 MHz Radio
  pinMode(RFM95_RST, OUTPUT);
  delay(10);
  digitalWrite(RFM95_RST, HIGH);
  delay(10);
  // manual reset
  digitalWrite(RFM95_RST, LOW);
  delay(10);
  digitalWrite(RFM95_RST, HIGH);
  delay(10);
  while (!manager.init()) {
    #if DEBUG
    Serial.println("LoRa radio init failed");
    #endif
    while (1) blinkERR(pin_LED);
  }
  #if DEBUG
  Serial.println("LoRa radio mgr init OK!");
  #endif
  // Defaults after init are 434.0MHz, 13dBm, Bw = 125 kHz, Cr = 4/5, Sf = 128chips/symbol, CRC on
  // Defaults after init are 434.0MHz, modulation GFSK_Rb250Fd250, +13dbM
  if (!rf95.setFrequency(RF95_FREQ)) {
    #if DEBUG
    Serial.println("setFrequency failed");
    #endif
    while (1) blinkERR(pin_LED);
  }
  #if DEBUG
  Serial.print("Set Freq to: "); Serial.println(RF95_FREQ);
  #endif
  
  // Defaults after init are 434.0MHz, 13dBm, Bw = 125 kHz, Cr = 4/5, Sf = 128chips/symbol, CRC on

  // The default transmitter power is 13dBm, using PA_BOOST.
  // If you are using RFM95/96/97/98 modules which uses the PA_BOOST transmitter pin, then 
  // you can set transmitter powers from 5 to 23 dBm:
  rf95.setTxPower(23, false);
  // You can optionally require this module to wait until Channel Activity
  // Detection shows no activity on the channel before transmitting by setting
  // the CAD timeout to non-zero:
  //rf95.setCADTimeout(10000);

  #if DEBUG
  Serial.println("Setup complete\n");
  #endif
  blinkLED(pin_LED);
} // setup()


void loop() {
  if (manager.available())   {
    // Wait for a message addressed to us from the client
    uint8_t len = sizeof(buf);
    uint8_t from;
    if (manager.recvfromAck(buf, &len, &from))  {
      blinkLED(pin_LED);
      digitalWrite(pin_LED, HIGH);
     
      // Decrypt cipher, and update decrypted with the result
      // clear cipher, arrDecryptedPadded, decrypted
      for (int i=0; i < sizeof(cipher); i++) {
        cipher[i] = 0x00;
      }
      for (int i=0; i < sizeof(arrDecryptedPadded); i++) {
        arrDecryptedPadded[i] = 0x00;
      }
      for (int i=0; i < sizeof(decrypted); i++) {
        decrypted[i] = 0x00;
      }      
      // Assign the contents of buf to cipher
      for (int i=0; i < sizeof(cipher); i++) {
        cipher[i] = buf[i];
      }
      // Decrypt cipher, and update decrypted with the result
      aesDecrypt(256, bVerbose);
      #if DEBUG
      Serial.print("From : 0x"); Serial.print(from, HEX); Serial.print(": '");
      for (int i=0; i < sizeof(decrypted); i++) {
        Serial.print(char(decrypted[i]));
      }
      Serial.print("'  RSSI:");
      Serial.println(rf95.lastRssi(),DEC);
      #endif

      // Send the decrypted message back to the originator client (sender).
      // (In production you wouldn't send back the decrypted message)
      if (!manager.sendtoWait(decrypted, sizeof(decrypted), from)) {
        #if DEBUG
        Serial.println("sendtoWait (reply) failed");
        #endif
      } else {
        digitalWrite(pin_LED, LOW);
      }      
    }
  }
} // loop()


void blinkLED(byte ledPIN){
  //  consumes 300 ms.
  for(int i = 5; i>0; i--){
    digitalWrite(ledPIN, HIGH);
    delay(30);
    digitalWrite(ledPIN, LOW);
    delay(30);
  }    
} //blinkLED()

void blinkERR(byte ledPIN){
  // S-O-S
  const int S = 150, O = 300;
  for(int i = 3; i>0; i--){
    digitalWrite(ledPIN, HIGH);
    delay(S);
    digitalWrite(ledPIN, LOW);
    delay(S);
  }    
  delay(200);
  for(int i = 3; i>0; i--){
    digitalWrite(ledPIN, HIGH);
    delay(O);
    digitalWrite(ledPIN, LOW);
    delay(O);
  }    
  delay(200);
  for(int i = 3; i>0; i--){
    digitalWrite(ledPIN, HIGH);
    delay(S);
    digitalWrite(ledPIN, LOW);
    delay(S);
  }    
  delay(200);
} // blinkERR()

//////////////////////////////////////////////////////////////////////////////
//  Encryption using Spaniakos - AES Encryption Library for Arduino and Raspberry Pi

void aesDecrypt(int bits, boolean bVerbose) {
  // Decrypt cipher and write to arrDecryptedPadded
  aes.set_IV(myIv);
  aes.get_IV(iv);
  unsigned long us = micros();
  aes.do_aes_decrypt(cipher,iSizeUnpadded+1,arrDecryptedPadded,key,bits,iv); 
  if (bVerbose == true) {
    Serial.print("Decryption took "); Serial.print(micros() - us); Serial.println(" us");  
  }
  // Create new array decrypted with the unencrypted content  
  // from arrDecryptedPadded excluding the padding. 
  for (int i=0; i < iSizeUnpadded; i++) {
    decrypted[i] = arrDecryptedPadded[i];
  } 
} // aesDecrypt()

//////////////////////////////////////////////////////////////////////////////

 


Do you need help developing or customizing a IoT product for your needs?   Send me an email requesting a free one hour phone / web share consultation.  

 

The information presented on this website is for the author's use only.   Use of this information by anyone other than the author is offered as guidelines and non-professional advice only.   No liability is assumed by the author or this web site.