The objective is to configure your Xenon to work as a standalone device that is NOT connected to, or dependent upon a Mesh network.
When Xenon is breathing white, it indicates the WiFi module is off.
If your Xenon is blinking dark blue, then you need to run the following code once to remove any registration to the Mesh network (good idea to do anyway):
#include "Particle.h"
#include "dct.h"
SYSTEM_MODE(SEMI_AUTOMATIC);
void setup() {
const uint8_t val = 0x01;
dct_write_app_data(&val, DCT_SETUP_DONE_OFFSET, 1);
pinMode(D7, OUTPUT);
digitalWrite(D7, HIGH);
}
void loop() {
}
https://community.particle.io/t/xenon-as-standalone-unit/48336/6
Particle Xenon setup instructions
Using a Particle Xenon Offline
Press and hold the mode button until it blinks dark blue.
Send a ISO 8601 date/time stamp plus floating point value (sensor data) from Xenon to Xenon/Argon/Boron via LoRa. In addition to two Xenon/Argon/Boron devices, you will need the following LoRa hardware:
AF LoRa Radio FeatherWing - RFM95W 900 MHz
For the best range, add one of the following antenna options:
Labeled photo comparison of RP-SMA vs SMA connectors
The LoRa library will not work properly on a Xenon with Particle Device OS versions after 1.4.4 on a Xenon (but it works with Argon).
LoRa FeatherWing |
Argon Boron Xenon |
---|---|
IRQ (INT) | D6 "A" |
CS | D5 "B" |
RST | D4 "C" |
The above "IRQ", "CS", and "RST" references relate to the GPIO assignments when using the RadioHead library.
Two code sketches are shown below, one for a Xenon that will be the "LoRa sender", and another for a Xenon/Argon/Boron "LoRa receiver" (they work reversed as well). Two libraries will be needed:
/*
* Project: LoRa_sender.ino
* Description: Xenon + LoRa sender
* Author: Mark Kiehl / Mechatronic Solutions LLC
* Date: March 2020
*
* Adafruit LoRa RFM95W 900 MHz FeatherWing + Xenon/Argon/Boron
*
* Sends a message via LoRa to another Xenon/Argon/Boron.
* The message consists of a ISO 8601 date/time stamp and a floating
* point value (simulated sensor value).
* For this demo, the original message is acknowledged by sending
* it back to the sender.
* The LED on D7 will turn on when a message is send, and then
* off after the reply is received from the LoRa receiver.
* The LED will blink S-O-S if an error occurs initializing the
* LoRa drivers.
* The receiver should run LoRa_receiver.ino
* LoRa messages are NOT encrypted.
*
* WARNING: Works with Particle device OS v1.4.4 or older (not 1.5.0).
* Device OS 1.5.0 causes Xenon to lockup after 2nd execution of rf95.send().
*/
#include "Particle.h"
#if (PLATFORM_ID == PLATFORM_XENON)
SYSTEM_MODE(MANUAL);
#endif
//////////////////////////////////////////////////////////////////////////////
// 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(1000,5000);
unsigned long timerLast = 0; // timer
//////////////////////////////////////////////////////////////////////////////
// LoRa 900 MHz Radio
// LoRa 900 MHz Radio
// RadioHead library for LoRa Radio
// Add library "RF9X-RK" and "CryptoLW-RK" to "lib" folder.
// https://github.com/rickkas7/RF9X-RK
// https://github.com/rickkas7/CryptoLW-RK
#include <RH_RF95.h>
// Argon/Boron/Xenon: A=D6, B=D5, C=D4, D=D3, E=D2, F=D14
#define RFM95_INT D6 // D6="A"
#define RFM95_CS D5 // D5="B"
#define RFM95_RST D4 // D4="C"
// Define frequency (set later)
#define RF95_FREQ 915.0
// Singleton instance of the radio driver
RH_RF95 rf95(RFM95_CS, RFM95_INT);
//////////////////////////////////////////////////////////////////////////////
byte cData[31]; // 16+1+13+1=31 "20200216T150644Z;+6.534755E+06"
unsigned long successCount = 0;
boolean bStopOnError = false;
//////////////////////////////////////////////////////////////////////////////
void setup() {
pinMode(D7, OUTPUT);
Serial.begin();
waitFor(Serial.isConnected, 30000);
delay(1000);
Serial.printlnf("System version: %s", (const char*)System.version());
if (PLATFORM_ID == PLATFORM_XENON)
Serial.printlnf("Free RAM %d", System.freeMemory());
//////////////////////////////////////////////////////////////////////////////
// LoRa
pinMode(RFM95_RST, OUTPUT);
delay(10);
digitalWrite(RFM95_RST, HIGH);
// manual reset (pull low for 100 us)
digitalWrite(RFM95_RST, LOW);
delay(10);
digitalWrite(RFM95_RST, HIGH);
delay(10);
while (!rf95.init()) {
while (1) blinkERR(D7);
}
// Defaults after init are 434.0MHz, modulation GFSK_Rb250Fd250, +13dbM
// Defaults after init are 434.0MHz, 13dBm, Bw = 125 kHz, Cr = 4/5, Sf = 128chips/symbol, CRC on
if (!rf95.setFrequency(RF95_FREQ)) {
while (1) blinkERR(D7);
}
// 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);
//////////////////////////////////////////////////////////////////////////////
Serial.println("Setup complete");
} // setup()
void loop() {
// Send a message at a random interval between 500 and 5000 ms
//if (timerLast > millis()) timerLast = millis();
if ((millis() - timerLast) > timerInterval) {
digitalWrite(D7, HIGH);
// Create a message to send with random content.
// "20200216T150644Z;++6.534755E+06"
BuildSimulatedSerialSensorData(cData, sizeof(cData));
Serial.printlnf("rf95.send().. '%s'", cData);
// Send a message to rf95_server
rf95.send(cData, sizeof(cData));
delay(10);
// Wait up to 0.1 seconds for the transmitter to complete transmitting
rf95.waitPacketSent(100);
// Now wait for a reply
uint8_t buf[RH_RF95_MAX_MESSAGE_LEN];
uint8_t len = sizeof(buf);
if (rf95.waitAvailableTimeout(1000)) {
// Should be a reply message for us now
if (rf95.recv(buf, &len)) {
digitalWrite(D7, LOW);
// Compare the received data buff to the data sent arrToEncrypt
int matches = 0;
for(unsigned int i=0; i<sizeof(cData); i++){
if(buf[i]==cData[i]){
matches++;
}
}
if (matches == sizeof(cData)) {
successCount++;
Serial.printlnf("successCount = %d", successCount);
digitalWrite(D7, LOW);
} else {
// The receiver didn't send back the same message content as
// what was sent by this sender.
if (bStopOnError == true)
while (1) blinkERR(D7);
}
} else {
// message confirmation from receiver failed!
}
} else {
// reply timeout
}
timerInterval = random(1000,5000);
timerLast = millis();
} // timer
} // loop()
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()
//////////////////////////////////////////////////////////////////////////////
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 (unsigned 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 (unsigned int i=0; i<sizeof(cDouble); i++) {
arr[f] = char(cDouble[i]);
f++;
iLastPos++;
}
arr[iLastPos] = 0x00; // null character
} // BuildSimulatedSerialSensorData()
void BuildDoubleValRandom(byte *arr, unsigned 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 (unsigned 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 (unsigned int i=0; i<strlen(cDbl); i++) {
arr[i] = cDbl[i];
}
arr[len-1] = 0x00; // null character
} // BuildDoubleValRandom()
void BuildIso8601DateTimeRandom(byte *arr, unsigned int len) {
// Updates arr with characters representing a random
// ISO8601 date time formatted as: 20200216T150644Z
for (unsigned int i=0; i<len; i++) {
arr[i] = 0x20; // space character
}
unsigned 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 (unsigned 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("'");
unsigned int f = 0;
for (unsigned 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 (unsigned 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 (unsigned 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
/*
* Project: LoRa_receiver.ino
* Description: Argon + LoRa receiver
* Author: Mark Kiehl / Mechatronic Solutions LLC
* Date: March 2020
*
* Adafruit LoRa RFM95W 900 MHz FeatherWing + Xenon/Argon/Boron
*
* Receives a message via LoRa from another Xenon/Argon/Boron.
* The message consists of a ISO 8601 date/time stamp and a floating
* point value (simulated sensor value).
* For this demo, the original message is acknowledged by sending
* it back to the sender.
* The LED on D7 will turn on when a message is received, and then
* off after the reply is sent to the LoRa sender.
* The LED will blink S-O-S if an error occurs initializing the
* LoRa drivers.
* The sender should run LoRa_sender.ino
* LoRa messages are NOT encrypted.
*
* Tested with Particle Device OS v1.5.2 on Argon.
* WARNING: May require Particle device OS 1.4.4 for Xenon.
*/
#include "Particle.h"
#if (PLATFORM_ID == PLATFORM_XENON)
SYSTEM_MODE(MANUAL);
#endif
//////////////////////////////////////////////////////////////////////////////
// LoRa 900 MHz Radio
// RadioHead library for LoRa Radio
// Add library "RF9X-RK" and "CryptoLW-RK" to "lib" folder.
// https://github.com/rickkas7/RF9X-RK
// https://github.com/rickkas7/CryptoLW-RK
#include <RH_RF95.h>
// Argon/Boron/Xenon: A=D6, B=D5, C=D4, D=D3, E=D2, F=D14
#define RFM95_INT D6 // D6="A"
#define RFM95_CS D5 // D5="B"
#define RFM95_RST D4 // D4="C"
// Define frequency (set later)
#define RF95_FREQ 915.0
// Singleton instance of the radio driver
RH_RF95 rf95(RFM95_CS, RFM95_INT);
//////////////////////////////////////////////////////////////////////////////
unsigned long successCount = 0;
boolean bStopOnError = false;
void setup() {
pinMode(D7, OUTPUT);
Serial.begin();
waitFor(Serial.isConnected, 30000);
Serial.printlnf("System version: %s", System.version().c_str());
pinMode(RFM95_RST, OUTPUT);
delay(10);
digitalWrite(RFM95_RST, HIGH);
// manual reset
digitalWrite(RFM95_RST, LOW);
delay(10);
digitalWrite(RFM95_RST, HIGH);
delay(10);
while (!rf95.init()) {
while (1) blinkERR(D7);
}
Serial.println("rf95.init() successful");
// Defaults after init are 434.0MHz, modulation GFSK_Rb250Fd250, +13dbM
// Defaults after init are 434.0MHz, 13dBm, Bw = 125 kHz, Cr = 4/5, Sf = 128chips/symbol, CRC on
if (!rf95.setFrequency(RF95_FREQ)) {
while (1) blinkERR(D7);
}
// 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);
//////////////////////////////////////////////////////////////////////////////
Serial.println("Setup complete");
} // setup()
// loop() runs over and over again, as quickly as it can execute.
void loop() {
if (rf95.available()) {
// Should be a message for us now
uint8_t buf[RH_RF95_MAX_MESSAGE_LEN];
uint8_t len = sizeof(buf);
if (rf95.recv(buf, &len)) {
digitalWrite(D7, HIGH);
// Signal strength ranges from -15 to -100. Smaller (closer to zero) the better.
Serial.printlnf("Received: '%s'", buf);
// Send a reply (send the data sent back to the Sender)
rf95.send(buf, sizeof(buf));
rf95.waitPacketSent();
successCount++;
Serial.printlnf("successCount = %d", successCount);
digitalWrite(D7, LOW);
} else {
if (bStopOnError == true)
while (1) blinkERR(D7);
}
}
} // loop()
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()
//////////////////////////////////////////////////////////////////////////////
library, RF9X-RK 34. This is a port of the RadioHead Packet Radio Library 7 version 1.89 for RF95/96/97/98 LoRa compatible radios. delete the missing #include "RF9X_RK.h"
Port of the RadioHead RF95/96/97/98 LoRa Packet radio driver for Particle
Arduino LoRa
For Particle: IRQ -> E -> D2; CS -> A -> D6; RST -> F -> A5;
https://github.com/rickkas7/RF9X-RK
How to Use Particle's Powerful Bluetooth API On Your Xenon, Boron or Argon
Particle tutorial on Gen 3 using Bluetooth or NFC
More AF FeatherWing products from Adafruit and from Particle
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.