Skip to content

Instantly share code, notes, and snippets.

@SyncChannel
Last active October 24, 2020 02:05
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save SyncChannel/b5c099a8eb8e7235aa8c to your computer and use it in GitHub Desktop.
Save SyncChannel/b5c099a8eb8e7235aa8c to your computer and use it in GitHub Desktop.
LoRa FeatherWing IOX Beacon Mode Example Program
/* LoRa FeatherWing IOX Beacon Mode Example Program
* By: Dan Watson | syncchannel.blogspot.com
* Date: 3-12-2016
* Version: 0.1 Initial Release
*
* Example Beacon Mode Program for the LoRa FeatherWing IOX for Adafruit Feather
* Tested with Feather M0, 32U4, and HUZZAH ESP8266
* However, you only have one analog input on HUZZAH (1V input max!)
*
* This program configures the Feather as a transmit-only beacon. It transmits a payload of data
* at a set interval using the LoRa long range wireless protocol. This program is intended as
* a companion to the LoRA FeatherWing IOX Tranceiver Example Program, which can receive the
* beacon transmissions, or the Adafruit IO Gateway Program, which can also upload the data to
* your Adafruit IO feeds
*
* This program is adapted from the RFM95W_Nexus library created by Gerben den Hartog
* It has been adapted from his code to work specifically on Adafruit Feather with
* the LoRa FeatherWing. Credit is given to Gerben for is useful library and work!
* Check out his full github repo if you are interested in LoRaWAN.
*
* Based on a library by Gerben den Hartog
* https://github.com/Ideetron/RFM95W_Nexus
* http://www.ideetron.nl/LoRa
*
* This program also uses the Adafruit MCP-23008 library, which can be downloaded in
* the Arduino Library Manager. See these pages for more info:
* https://github.com/adafruit/Adafruit-MCP23008-library
* https://www.adafruit.com/products/593
*
*/
// SPI is used for the RFM module. I2C is used for the MCP-23008.
#include <SPI.h>
#include <Wire.h>
#include "Adafruit_MCP23008.h"
// Define GPIO pin descriptions
#define LED 13 //13 for M0/32U4, 0 for HUZZAH ESP8266
// Also define the pin you selected for the MCP-23008 interrupt, if desired
// You don't need it when using the Adafruit library
// Define I/O expander connections to RFM module. These are hard wired.
#define DIO0 5
#define DIO1 6
#define DIO2 7
#define DIO3 3
#define DIO4 4
#define DIO5 0
#define CS 2
#define RFM_RESET 1
// Set this true to invert the LED output (for HUZZAH ESP8266) (optional)
#define LEDINVERT false
// Set frequency of RFM module
// Available frequencies depend on which RFM module you have
#define RFM_FREQ_MHZ 915000000
// Define the length of the user message payload in bytes
#define PACKAGE_LENGTH 10
// Message counter will increment every time a message is sent
uint16_t messageCounter = 0;
// Holder for the user data payload bytes
uint8_t RFM_Tx_Package[PACKAGE_LENGTH];
// The Adafruit MCP-23008 library is very easy to use. To access a pin on the RFM module,
// use the standard digitalRead and digitalWrite commands with "iox." in front.
Adafruit_MCP23008 iox;
void setup()
{
//Initialize SPI
SPI.begin();
SPI.beginTransaction(SPISettings(4000000,MSBFIRST,SPI_MODE0));
iox.begin(); // Default address
//Initialize I/O pins on MCP-23008
iox.pinMode(DIO0,INPUT);
iox.pinMode(DIO5,INPUT);
iox.pinMode(CS,OUTPUT);
// Initialize normal GPIO on the Feather
pinMode(LED,OUTPUT);
// Pull chip select high for now
iox.digitalWrite(CS,HIGH);
// Minimum of 100ms recommended to allow RFM module to start up
delay(200);
// Load header info into the payload
RFM_Tx_Package[0] = 0xD7; // First two bytes are always D7 D7
RFM_Tx_Package[1] = 0xD7;
RFM_Tx_Package[2] = 0x20; // Node address
RFM_Tx_Package[3] = 0x01; // Message type 0x01 = Temperature Report
RFM_Tx_Package[4] = 0x00; // Will be High byte of sensor data
RFM_Tx_Package[5] = 0x00; // Will be Low byte of sensor data
RFM_Tx_Package[6] = 0x00; // Will be High byte of battery voltage reading
RFM_Tx_Package[7] = 0x00; // Will be Low byte of battery voltage reading
RFM_Tx_Package[8] = 0x00; // Will be High byte of message counter
RFM_Tx_Package[9] = 0x00; // Will be Low byte of message counter
//Initialize the RFM module
RFM_Init();
// Flash LED on Feather to notify user that setup is complete
for (int i = 0; i <= 3; i++)
{
digitalWrite(LED, HIGH^LEDINVERT);
delay(100);
digitalWrite(LED, LOW^LEDINVERT);
delay(200);
}
}
void loop()
{
uint32_t gotMillis = millis() + 5000;
digitalWrite(LED, HIGH^LEDINVERT);
messageCounter++;
// Get sensor reading (MCP-9701 temperature sensor) and battery voltage reading
// The data will be sent as raw analog readings. The receiving node will do conversions
uint16_t sensor_holder = analogRead(A0); // Raw sensor reading
uint16_t batt_holder = analogRead(A7); // Battery voltage reading. Read A7 for Feather M0, and A9 for 32U4
// Update the payload contents
RFM_Tx_Package[4] = (sensor_holder >> 8);
RFM_Tx_Package[5] = sensor_holder;
RFM_Tx_Package[6] = (batt_holder >> 8);
RFM_Tx_Package[7] = batt_holder;
RFM_Tx_Package[8] = (messageCounter >> 8);
RFM_Tx_Package[9] = messageCounter;
// Send the payload!
RFM_SendPackage(RFM_Tx_Package);
// At higher Spreading Factors, the transmission takes long enough to make the LED blink visible
// Add a slight delay if you want the LED notification of a transmission to be longer
// delay(50);
digitalWrite(LED, LOW^LEDINVERT);
while(gotMillis > millis())
{
// Make the main loop take five seconds to complete every time
// For ESP8266, include a small delay to prevent the watchdog from timing out
delay(10);
}
}
/*
* Function to initialize the RFM module.
* It is highly recommended that you read the datasheet for the module, there are lots of
* settings you can adjust to improve the wireless link parameters for your specific usage
* http://www.hoperf.com/upload/rf/RFM95_96_97_98W.pdf
*/
void RFM_Init()
{
//Set RFM module in sleep mode to change to LoRa mode
RFM_Write(0x01,0x00);
//Set LoRa mode
RFM_Write(0x01,0x80);
//Set RFM module in standby mode to change more settings
RFM_Write(0x01,0x81);
while(iox.digitalRead(DIO5) == LOW)
{
//Wait for mode ready
}
// Set carrier frequency
// RFM_FREQ_MHZ / 61.035 Hz = 3 byte value to load
unsigned long freqHolder = RFM_FREQ_MHZ / 61.035; // round down
uint8_t freqLSB = freqHolder;
uint8_t freq2B = (freqHolder >> 8);
uint8_t freqMSB = (freqHolder >> 16);
RFM_Write(0x06,freqMSB);
RFM_Write(0x07,freq2B);
RFM_Write(0x08,freqLSB);
//Set maximum transmit power settings
RFM_Write(0x09,0xFF);
//Set bandwith 250 kHz, Coding rate = 4/8, Implicit header mode
RFM_Write(0x1D,0x89);
//Spreading factor 11, PayloadCRC on
RFM_Write(0x1E,0xB4);
//*NOTE: If you decide to change to Spreading Factor 6, you also need to set the following
//*two registers by uncommenting these lines
//RFM_Write(0x31,0xC5);
//RFM_Write(0x37,0x0C);
//Preamble length 0x0018 + 4 = 28
RFM_Write(0x20,0x00);
RFM_Write(0x21,0x18);
//Payload length
RFM_Write(0x22,PACKAGE_LENGTH);
//Set RFM in continues receive
RFM_Write(0x01,0x85);
while(iox.digitalRead(DIO5) == LOW)
{
//Wait for mode ready
}
}
/* This function is used to read a value from a specific register of the RFM module
* Arguments: RFM_Adress Adress of the register to read
* Return: The value of the register is returned
*/
unsigned char RFM_Read(unsigned char RFM_Address)
{
unsigned char RFM_Data;
//Set CS pin low to start SPI communication
iox.digitalWrite(CS,LOW);
//Send Address
SPI.transfer(RFM_Address);
//Send 0x00 to receive the answer from the RFM
RFM_Data = SPI.transfer(0x00);
//Set CS high to end communication
iox.digitalWrite(CS,HIGH);
//Return received data
return RFM_Data;
}
/* This function is used to write a value to a specific register of the RFM module
* Arguments: RFM_Adress Adress of the register to be written
* RFM_Data Data that will be written
*/
void RFM_Write(unsigned char RFM_Address, unsigned char RFM_Data)
{
//Set CS pin low to start communication
iox.digitalWrite(CS,LOW);
//Send Addres with MSB 1 to make it a write command
SPI.transfer(RFM_Address | 0x80);
//Send Data
SPI.transfer(RFM_Data);
//Set CS pin high to end communication
iox.digitalWrite(CS,HIGH);
}
/* This function is used to send a message payload
* Arguments: *RFM_Package Pointer to the array with the data to send
*/
void RFM_SendPackage(unsigned char *RFM_Package)
{
//Switch RFM to standby
RFM_Write(0x01,0x81);
while(iox.digitalRead(DIO5) == LOW)
{
//Wait for mode ready
}
//Set SPI pointer to Tx base address
RFM_Write(0x0D,0x80);
//Switch DIO to TxDone
RFM_Write(0x40,0x40);
//Write payload in to the FIFO
for(int i = 0; i < PACKAGE_LENGTH; i++)
{
RFM_Write(0x00,*RFM_Package);
RFM_Package++;
}
//Switch RFM to TX
RFM_Write(0x01,0x83);
while(iox.digitalRead(DIO0) == LOW)
{
//Wait for Tx Done
}
//Clear interrupt
RFM_Write(0x12,0x08);
//Set DIO0 to RxDone
RFM_Write(0x40,0x00);
//Set RFM in continues receive
RFM_Write(0x01,0x85);
while(iox.digitalRead(DIO5) == LOW)
{
//Wait for mode ready
}
}
/* This function is used to receive a message/payload from the RFM module
* Arguments: *RFM_Package Pointer to the array with the data to send
*/
void RFM_ReadPackage(unsigned char *RFM_Package)
{
unsigned char RFM_Interrupt;
unsigned char RFM_PackageLocation;
//Get interrupt register
RFM_Interrupt = RFM_Read(0x12);
//Switch RFM to Standby
RFM_Write(0x01,0x81);
while(iox.digitalRead(DIO5) == LOW)
{
//Wait for mode ready
}
//Clear interrupt register
RFM_Write(0x12,0x60);
//Get package location
RFM_PackageLocation = RFM_Read(0x10);
//Set SPI pointer to Packagelocation
RFM_Write(0x0D,RFM_PackageLocation);
//Get message
for(int i = 0; i < PACKAGE_LENGTH; i++)
{
*RFM_Package = RFM_Read(0x00);
RFM_Package++;
}
//Switch RFM to receive
RFM_Write(0x01,0x85);
while(iox.digitalRead(DIO5) == LOW)
{
//Wait for mode ready
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment