Created
March 17, 2016 01:16
-
-
Save SyncChannel/8e10c30f136dd7c765f8 to your computer and use it in GitHub Desktop.
LoRa FeatherWing IOX Adafruit IO Gateway Example Program
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* LoRa FeatherWing IOX Adafruit IO Gateway Example Program | |
* By: Dan Watson | syncchannel.blogspot.com | |
* Date: 3-12-2016 | |
* Version: 0.1 Initial Release | |
* | |
* Example Adafruit IO Gateway Program for the LoRa FeatherWing IOX for Adafruit Feather | |
* Tested with HUZZAH ESP8266 | |
* | |
* This program configures the Feather as a LoRa receiver and Adafruit IO Wi-Fi Gateway. | |
* It is intended as a companion to the LoRA FeatherWing IOX Beacon Example Program. | |
* It can receive the beacon messages and publish them to Adafruit IO using MQTT. | |
* | |
* 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. | |
* | |
* 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 | |
* | |
* The Adafruit MQTT and MQTT_Client libraries & example code are also used: | |
* https://learn.adafruit.com/adafruit-feather-huzzah-esp8266/overview | |
* https://learn.adafruit.com/adafruit-io/mqtt-api | |
* | |
*/ | |
// SPI is used for the RFM module. I2C is used for the MCP-23008. | |
// Numerous includes are required for Adafruit MQTT library | |
#include <SPI.h> | |
#include <Wire.h> | |
#include <ESP8266WiFi.h> | |
#include "Adafruit_MCP23008.h" | |
#include "Adafruit_MQTT.h" | |
#include "Adafruit_MQTT_Client.h" | |
// Adafruit IO and Wi-Fi Access Point info | |
#define WLAN_SSID "your ssid here" | |
#define WLAN_PASS "your password here" | |
#define AIO_SERVER "io.adafruit.com" | |
#define AIO_SERVERPORT 1883 | |
#define AIO_USERNAME "your Adafruit IO user name here" | |
#define AIO_KEY "your Adafruit IO private key here" | |
/************ Global State (you don't need to change this!) ******************/ | |
// Create an ESP8266 WiFiClient class to connect to the MQTT server. | |
WiFiClient client; | |
// Store the MQTT server, username, and password in flash memory. | |
// This is required for using the Adafruit MQTT library. | |
const char MQTT_SERVER[] PROGMEM = AIO_SERVER; | |
const char MQTT_USERNAME[] PROGMEM = AIO_USERNAME; | |
const char MQTT_PASSWORD[] PROGMEM = AIO_KEY; | |
// Setup the MQTT client class by passing in the WiFi client and MQTT server and login details. | |
Adafruit_MQTT_Client mqtt(&client, MQTT_SERVER, AIO_SERVERPORT, MQTT_USERNAME, MQTT_PASSWORD); | |
/****************************** Feeds ***************************************/ | |
// *** These are just examples! Change these to your own feed names. *** | |
const char TEMP_FEED[] PROGMEM = AIO_USERNAME "/feeds/LoRaIOXtemp"; | |
Adafruit_MQTT_Publish tempfeed = Adafruit_MQTT_Publish(&mqtt, TEMP_FEED); | |
const char BATT_FEED[] PROGMEM = AIO_USERNAME "/feeds/LoRaIOXbatt"; | |
Adafruit_MQTT_Publish battfeed = Adafruit_MQTT_Publish(&mqtt, BATT_FEED); | |
const char TEMP_FEED2[] PROGMEM = AIO_USERNAME "/feeds/LoRaIOXtemp2"; | |
Adafruit_MQTT_Publish tempfeed2 = Adafruit_MQTT_Publish(&mqtt, TEMP_FEED2); | |
const char BATT_FEED2[] PROGMEM = AIO_USERNAME "/feeds/LoRaIOXbatt2"; | |
Adafruit_MQTT_Publish battfeed2 = Adafruit_MQTT_Publish(&mqtt, BATT_FEED2); | |
/****************************************************************************/ | |
// Define GPIO pin descriptions | |
#define LED 0 | |
// 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 outputs (for HUZZAH ESP8266) (optional) | |
#define LEDINVERT true | |
// 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 | |
// 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; | |
// Counter to help keep Adafruit IO connection alive | |
uint32_t connectCounter = 0; | |
void setup() | |
{ | |
Serial.begin(9600); | |
Serial.println("LoRa FeatherWing IOX - Adafruit IO Gateway"); | |
//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); | |
//Initialize the RFM module | |
RFM_Init(); | |
Serial.println("Initialized RFM module."); | |
// Connect to WiFi access point. | |
Serial.println(); Serial.println(); | |
Serial.print("Connecting to "); | |
Serial.println(WLAN_SSID); | |
WiFi.begin(WLAN_SSID, WLAN_PASS); | |
while (WiFi.status() != WL_CONNECTED) { | |
delay(500); | |
Serial.print("."); | |
} | |
Serial.println(); | |
Serial.println("WiFi connected"); | |
Serial.println("IP address: "); Serial.println(WiFi.localIP()); | |
Serial.println(""); | |
MQTT_connect(); | |
// 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() | |
{ | |
//Check if the RFM has received a package | |
if(iox.digitalRead(DIO0) == HIGH) | |
{ | |
digitalWrite(LED, HIGH^LEDINVERT); | |
//Get package from RFM | |
unsigned char RFM_Package[PACKAGE_LENGTH]; | |
RFM_ReadPackage(RFM_Package); | |
if (RFM_Package[0] == 0xD7 && RFM_Package[1] == 0xD7) // Valid message | |
{ | |
//Send report over serial | |
Serial.print("Got message from Node 0x"); | |
Serial.println(RFM_Package[2], HEX); | |
Serial.print("Raw Message: [ "); | |
for(int i = 0; i < PACKAGE_LENGTH; i++) | |
{ | |
if (RFM_Package[i] < 0x10) | |
Serial.print("0"); | |
Serial.print(RFM_Package[i], HEX); | |
Serial.print(" "); | |
} | |
Serial.println("]"); | |
if (RFM_Package[3] == 0x01) // This is a temperature report | |
{ | |
float temp = (RFM_Package[4] << 8) | RFM_Package[5]; | |
temp = (temp*3.22 - 400) / 19.5; // Temp in degrees C | |
float batt = (RFM_Package[6] << 8) | RFM_Package[7]; | |
batt = batt*6.44; // *3.22 to convert to mV, *2 due to divider | |
batt = batt/1000; // Convert to volts | |
uint16_t messageCounter = (RFM_Package[8] << 8) | RFM_Package[9]; | |
Serial.print("Temp: "); | |
Serial.print(temp); | |
Serial.print("*C | Batt: "); | |
Serial.print(batt); | |
Serial.print("V | Msg Count: "); | |
Serial.println(messageCounter); | |
bool publishResults = true; | |
if (RFM_Package[2] == 0x20) // We check Node ID to determine which feeds to send the data to | |
{ | |
char pubTemp[7]; | |
dtostrf(temp, 6, 2, pubTemp); // Don't send raw doubles. They will not display correctly | |
if (!tempfeed.publish(pubTemp)) // when graphed in Adafruit IO (currently) | |
publishResults = false; | |
delay(10); | |
char pubBatt[6]; | |
dtostrf(batt, 4, 2, pubBatt); | |
if (!battfeed.publish(pubBatt)) | |
publishResults = false; | |
delay(10); | |
} else if (RFM_Package[2] == 0x21) { // Different node ID, upload to different feeds | |
char pubTemp[7]; | |
dtostrf(temp, 6, 2, pubTemp); | |
if (!tempfeed2.publish(pubTemp)) | |
publishResults = false; | |
delay(10); | |
char pubBatt[6]; | |
dtostrf(batt, 4, 2, pubBatt); | |
if (!battfeed2.publish(pubBatt)) | |
publishResults = false; | |
delay(10); | |
} | |
if (publishResults) | |
{ | |
Serial.println("Data published to Adafruit IO."); | |
} else { | |
Serial.println("FAILED to publish data to Adafruit IO!"); | |
} | |
} | |
} else { // You get here if the message header was not 0xD7 0xD7. Likely due to corrupted package | |
Serial.println("Invalid Message Received!"); | |
} | |
Serial.println(""); | |
delay(50); | |
digitalWrite(LED, LOW^LEDINVERT); | |
} | |
else if (millis() > connectCounter) // Check connection status every 1 second, re-connect as necessary | |
{ | |
connectCounter = millis() + 1000; | |
MQTT_connect(); | |
} | |
} | |
/* | |
* 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 | |
} | |
} | |
// Function to connect and reconnect as necessary to the MQTT server. | |
// Should be called in the loop function and it will take care if connecting. | |
void MQTT_connect() | |
{ | |
int8_t ret; | |
// Stop if already connected. | |
if (mqtt.connected()) { | |
return; | |
} | |
Serial.print("Connecting to MQTT... "); | |
while ((ret = mqtt.connect()) != 0) { // connect will return 0 for connected | |
Serial.println(mqtt.connectErrorString(ret)); | |
Serial.println("Retrying MQTT connection in 5 seconds..."); | |
mqtt.disconnect(); | |
delay(5000); // wait 5 seconds | |
} | |
Serial.println("MQTT Connected!"); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment