I was planning on migrating the system over to an Argon-Xenon mesh network after I had it working on Photon (only because I am more comfortable with Photon and have not worked with the mesh devices).
That plan was brought forward in order to solve this issue.
I now have an Argon that is always on and connected to power. This gateway received Mesh.publish messages from the Nodes and forwards specific ones on to the MQTT broker.
The Nodes (Xenon’s) are all running on battery and sleep most of the time.
I solve the problem buy the following
The Xenon’s - Nodes
- all have a constant int containing their current firmware version.
- Each time they wake up they send out a Mesh.publish(“awake”).
- They subscribe to Mesh.subscribe(“firmware”) messages that contain the current firmware version available.
- If this version is higher than the current running version then they publish a message stating ready for upgrade and run an infinite loop while(true). This makes it possible to flash new firmware.
The Argon - Gateway
- Has a Particle function that allows the int meshFirmware to be changed. When new mesh firmware is available then I update this variable. This also defaults to 1 if the Argon is reset.
- It subscribes to Mesh.subscribe(“awake”) and pushes out a Mesh.publish(“firmware”, meshFrimware) immediately to catch the Xenon before it goes to sleep.
Argon code - not yet parsing mqtt messages to broker, that is the next step.
int meshFirmware = 1;
void setup() {
Serial.begin(9600);
pinMode(D7, OUTPUT);
System.on(button_status, button_handler);
Mesh.subscribe("toggle-led", toggleLed);
Mesh.subscribe("tempLog", mqttParser);
Mesh.subscribe("tempBatt", mqttParser);
Mesh.subscribe("awake", meshDeviceAwake);
if(!Particle.function("updateMeshFirmware", updateMeshFirmware)){
Particle.publish("Function ERROR", "sensorsJSON", 60, PRIVATE);
}
}
void loop() {
}
void button_handler(system_event_t event, int duration, void* ) {
if (!duration) {
// Just pressed.
Mesh.publish("toggle-led");
Serial.println("Button push published!");
digitalWrite(D7, HIGH);
} else {
// Button released.
digitalWrite(D7, LOW);
}
}
void toggleLed(const char *event, const char *data) {
digitalWrite(D7, HIGH);
delay(500);
digitalWrite(D7, LOW);
}
void mqttParser(const char *event, const char *data) {
Serial.printlnf("Topic: %s Payload: %s",event,data);
}
void meshDeviceAwake(const char *event, const char *data) {
Serial.println("Mesh devise is awake. Sending current firmware version");
Mesh.publish("firmware",String(meshFirmware));
}
// update firmware version number to Mesh devices
int updateMeshFirmware(String versionStr)
{
versionStr.trim();
Serial.print("New Mesh firmware version: '");
Serial.print(versionStr);
Serial.print("'");
// make sure input is a number
if(isValidNumber(versionStr)){
// send to Mesh devices
meshFirmware = versionStr.toInt();
Serial.println(" is a valid number");
return 1;
}
else{
Serial.println(" is NOT a valid number");
return -1;
}
}
boolean isValidNumber(String numStr){
// test that each char is a digit
for(int i=0;i<numStr.length();i++){
if(numStr.charAt(i)<'0' || numStr.charAt(i)>'9'){
return false;
}
}
return true;
}
Xenon code
// This #include statement was automatically added by the Particle IDE.
#include <DS18B20.h>
#include <math.h>
const int firmwareVersion = 1;
const int pinOneWire = D2;
const int MAXRETRY = 10;
const int sleepTime = 10; //1800; // 30 min
DS18B20 ds18b20(pinOneWire);
uint8_t sensorAddress[8];
float sensorTemp;
float battVolt;
time_t lastReadTime;
String sensorsJSON;
String batteryJSON;
void setup() {
Serial.begin(115200);
Mesh.subscribe("firmware", checkFirmware);
setAddress();
}
void loop() {
System.sleep(D1,RISING,sleepTime);
// wait until Mesh connects and is ready
while(!Mesh.ready()){}
// send message that i am awake
Mesh.publish("awake", String(firmwareVersion));
sensorTemp = getTemp(sensorAddress);
if(!isnan(sensorTemp)){
lastReadTime = Time.now();
}else{
lastReadTime = 0;
}
updateSensorsJSON();
Serial.println(sensorsJSON);
Mesh.publish("tempLog", sensorsJSON);
// delay(2000);
// get battery details
battVolt = analogRead(BATT) * 0.0011224;
updateBatteryJSON();
Serial.println(batteryJSON);
Mesh.publish("tempBatt", batteryJSON);
delay(1000);
}
void setAddress(){
boolean srchResult = false;
while(!srchResult){
ds18b20.resetsearch(); // initialise for sensor search
srchResult = ds18b20.search(sensorAddress);
}
}
String addressString(){
char szInfo[16];
snprintf(szInfo, sizeof(szInfo), "%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x", sensorAddress[0],
sensorAddress[1],
sensorAddress[2],
sensorAddress[3],
sensorAddress[4],
sensorAddress[5],
sensorAddress[6],
sensorAddress[7]);
return szInfo;
}
double getTemp(uint8_t addr[8]) {
// Serial.print("getTemp() ");
double _temp;
int i = 0;
do {
_temp = ds18b20.getTemperature(addr);
} while (!ds18b20.crcCheck() && MAXRETRY > i++);
if (i < MAXRETRY) {
//_temp = ds18b20.convertToFahrenheit(_temp);
// Serial.println(_temp);
}
else {
_temp = NAN;
// Serial.println("Invalid reading");
}
return _temp;
}
/*************************************
* create JSON valid string representation
* for temp, address, and time
********************************/
void updateSensorsJSON(){
// initialise temporary string
String json = "";
// if sensor is valid
if(lastReadTime!=0){
//start array element
json = String(json + "{");
// address
json = String(json + "\"address\":\"" + addressString() +"\",");
//temp
json = String(json + "\"temp\":\"" + sensorTemp +"\",");
//lastReadTime
json = String(json + "\"lastReadTime\":\"" + lastReadTime +"\"");
//close array element
json = String(json + "}");
}
//update global variable
sensorsJSON = json;
}
/*************************************
* create JSON valid string representation
* for battery voltage, address, and time
********************************/
void updateBatteryJSON(){
// initialise temporary string
String json = "";
// if sensor is valid
if(lastReadTime!=0){
//start array element
json = String(json + "{");
// address
json = String(json + "\"address\":\"" + addressString() +"\",");
//temp
json = String(json + "\"battery\":\"" + battVolt +"\",");
//lastReadTime
json = String(json + "\"lastReadTime\":\"" + lastReadTime +"\"");
//close array element
json = String(json + "}");
}
//update global variable
batteryJSON = json;
}
// handler for Mesh.subscribe('firmware')
// check value of payload against firmwareVersion
// if newer version is available then do not sleep
void checkFirmware(const char *event, const char *data) {
if(atoi(data)>firmwareVersion){
// new firmware is available
// send mesage to notify that device is ready to update
Mesh.publish("tempLog", String::format("Device %s ie ready to update",System.deviceID()));
// loop and do nothing until updated. ie do not go to sleep
while(true){}
}
}
Any comments would be welcome. Also would it be better to create a new thread about OTA updates and Sleep, as that was the main problem that I wanted to address.