Re-imagined RFM node code - All-in-one, low power, OO design
Dec 29, 2015 2:55:11 GMT
dremil likes this
Post by lewishollow on Dec 29, 2015 2:55:11 GMT
Hey everyone -
I forked computourist's repo and added my own new node code. It has all of the existing files from his project, but with some additions. The big ones are all in here -
github.com/dannyoleson/RFM69-MQTT-client/tree/master/RfmNode
When setting up to use this code, put RfmNode.ino as your root sketch, and move all of the other files to wherever you have your libraries for your other includes.
First of all - HUGE props to computourist and papa. Without them, we would be lost.
As a disclaimer, this code suits MY needs and the way I think. This may not suit everybody. However, I was really trying to keep the lay-person in mind throughout my writing of this code. It maintains much of what computourist wrote, it just changes the way it's structured.
I started doing this because I've only recently started really getting into Arduino and embedded device development in general, and I wanted a challenge. The challenges I decided to tackle were -
Make this code more readable, as well as object oriented and modular. I think I accomplished this somewhat. I moved a lot of the logic and static code into other class files and headers to try to keep the main code as clean as possible. There is still a lot of code left there that I would like to move out, but it was starting to consume too much of my time. I had to pull the plug and call it done for the moment!
Make adding and removing supported sensors from a node as easy as possible. This is really the biggest part, I think. If you want to use a light sensor, a reed switch, a button, an actuator, a flame sensor, a gas sensor or a DHT sensor, all you have to do is comment in the defines for them in at the top of RfmNode.ino. (I will add the PIR sensor soon - I forgot about that one) The list looks like this -
//
// ENABLE OR DISABLE DEVICES HERE
//
#define PHOTOSENSORENABLED
#define REEDSWITCHENABLED
#define BUTTONENABLED
#define ACTUATORENABLED
#define FLAMESENSORENABLED
#define GASSENSORENABLED
#define DHTSENSORENABLED
Make adding new sensors as easy as humanly possible. I think I accomplished this as well. For most sensors, there are a few steps -
• Add a new define for your device in the list of devices in the header. This will look like #define MYSENSORENABLED
• Add block for your new sensor with the necessary parameters. These are pin, device ID (make sure to use the appropriate range) and a declaration of the object.
Example-
#ifdef FLAMESENSORENABLED
#define FLAMEPIN A1
#define FLAMESENSORDEVICEID 65
AnalogSensorDataClass *flameSensorData;
#endif //FLAMESENSORENABLED
• In the setup function, add another define block and define your sensor object, along with any requested sensor-type-specific variables. In the case of the flame sensor example, you can set the threshold for whether or not it transmits data. Example -
#ifdef FLAMESENSORENABLED
int flameDeltaThreshold = 20;
flameSensorData = new AnalogSensorDataClass(2 * ONESECOND, flameDeltaThreshold, FLAMEPIN, FLAMESENSORDEVICEID);
#endif //FLAMESENSORENABLED
• if you want the device to send periodically regardless of whether a change has has occurred, you can set periodicSendEnabled to true on the desired device and it will send regularly based on the txInterval (or the override, if the class supports that). Example -
dhtTempSensorData->periodicSendEnabled = true;
• The only time it gets a LITTLE complicated is if you are adding real data sensors, like DHT. They will frequently use a special function to get their data. For these cases, you'll need to write your own function to get the data and then pass it in as a callback. Example -
// in the setup function
dhtTempSensorData = new RealInputDataClass(DHTTEMPDEVICEID, DHTPIN, overrideTxInterval, &getDhtTemperatureFarenheit);
// bottom of the file
float getDhtTemperatureFarenheit()
{
return dht.readTemperature(true);
}
Support running with low power. I was working on this while papa and computourist were doing the same. What they landed on, though, was using long sleep periods with interrupts. I took a different approach and had the node go to sleep at the beginning of every loop for 2 seconds, do work, if any, and then go back to sleep. I did this because in the case of a reed switch, which was my primary digital input device, a HIGH or LOW state would have to trigger a wakeup, which obviously isn't possible. I used this model in combination with radio.sleep. What I ended up with was a power draw of about 3.6 mA, with a jump to ~10 when it wakes up, but then it immediately goes back to 3. This is a big improvement from the 50 mA draw I was at when I started this project! The other nice thing using this model allows is that the uptime still works. Sort of. It basically tracks how many times it's woken up, multiplies that by 2 seconds, and adds that to millis(). I've noticed that the time calculation is off by about -8%, so I'm manually adding 160ms each cycle. This is VERY hacky, and I should feel bad. I have set up solar panels to keep the batteries charged, and on a cloudy day they give me about 5mA. Throw a few sunny days, and this thing could feasibly run on its own indefinitely. I also implemented the ATC functionality to the rfm radio code. It... doesn't seem to do much at all.
Other posts I'll be soon making -
I’ll post a walkthrough for my outdoor solar setup soon. I think you guys will dig it.
If anyone wants me to post my scripts for publishing from the openhab designer on my windows machine to my headless raspberry pi that is running the mqtt broker and the openhab server, let me know. It's made my life WAY easier.
Other disclaimers – I have NOT tested the final version with all sensors. I have a node running with slightly older code that has a flame sensor, a gas sensor, a DHT22 and a light sensor. The other node I was mostly using during development is my outdoor node, and it has a reed switch and a DHT22.
Also – I have not used git very much at all. If I screwed up attribution or something, please let me know!
Please feel free to improve on it, add suggestions or ask questions!
I forked computourist's repo and added my own new node code. It has all of the existing files from his project, but with some additions. The big ones are all in here -
github.com/dannyoleson/RFM69-MQTT-client/tree/master/RfmNode
When setting up to use this code, put RfmNode.ino as your root sketch, and move all of the other files to wherever you have your libraries for your other includes.
First of all - HUGE props to computourist and papa. Without them, we would be lost.
As a disclaimer, this code suits MY needs and the way I think. This may not suit everybody. However, I was really trying to keep the lay-person in mind throughout my writing of this code. It maintains much of what computourist wrote, it just changes the way it's structured.
I started doing this because I've only recently started really getting into Arduino and embedded device development in general, and I wanted a challenge. The challenges I decided to tackle were -
Make this code more readable, as well as object oriented and modular. I think I accomplished this somewhat. I moved a lot of the logic and static code into other class files and headers to try to keep the main code as clean as possible. There is still a lot of code left there that I would like to move out, but it was starting to consume too much of my time. I had to pull the plug and call it done for the moment!
Make adding and removing supported sensors from a node as easy as possible. This is really the biggest part, I think. If you want to use a light sensor, a reed switch, a button, an actuator, a flame sensor, a gas sensor or a DHT sensor, all you have to do is comment in the defines for them in at the top of RfmNode.ino. (I will add the PIR sensor soon - I forgot about that one) The list looks like this -
//
// ENABLE OR DISABLE DEVICES HERE
//
#define PHOTOSENSORENABLED
#define REEDSWITCHENABLED
#define BUTTONENABLED
#define ACTUATORENABLED
#define FLAMESENSORENABLED
#define GASSENSORENABLED
#define DHTSENSORENABLED
Make adding new sensors as easy as humanly possible. I think I accomplished this as well. For most sensors, there are a few steps -
• Add a new define for your device in the list of devices in the header. This will look like #define MYSENSORENABLED
• Add block for your new sensor with the necessary parameters. These are pin, device ID (make sure to use the appropriate range) and a declaration of the object.
Example-
#ifdef FLAMESENSORENABLED
#define FLAMEPIN A1
#define FLAMESENSORDEVICEID 65
AnalogSensorDataClass *flameSensorData;
#endif //FLAMESENSORENABLED
• In the setup function, add another define block and define your sensor object, along with any requested sensor-type-specific variables. In the case of the flame sensor example, you can set the threshold for whether or not it transmits data. Example -
#ifdef FLAMESENSORENABLED
int flameDeltaThreshold = 20;
flameSensorData = new AnalogSensorDataClass(2 * ONESECOND, flameDeltaThreshold, FLAMEPIN, FLAMESENSORDEVICEID);
#endif //FLAMESENSORENABLED
• if you want the device to send periodically regardless of whether a change has has occurred, you can set periodicSendEnabled to true on the desired device and it will send regularly based on the txInterval (or the override, if the class supports that). Example -
dhtTempSensorData->periodicSendEnabled = true;
• The only time it gets a LITTLE complicated is if you are adding real data sensors, like DHT. They will frequently use a special function to get their data. For these cases, you'll need to write your own function to get the data and then pass it in as a callback. Example -
// in the setup function
dhtTempSensorData = new RealInputDataClass(DHTTEMPDEVICEID, DHTPIN, overrideTxInterval, &getDhtTemperatureFarenheit);
// bottom of the file
float getDhtTemperatureFarenheit()
{
return dht.readTemperature(true);
}
Support running with low power. I was working on this while papa and computourist were doing the same. What they landed on, though, was using long sleep periods with interrupts. I took a different approach and had the node go to sleep at the beginning of every loop for 2 seconds, do work, if any, and then go back to sleep. I did this because in the case of a reed switch, which was my primary digital input device, a HIGH or LOW state would have to trigger a wakeup, which obviously isn't possible. I used this model in combination with radio.sleep. What I ended up with was a power draw of about 3.6 mA, with a jump to ~10 when it wakes up, but then it immediately goes back to 3. This is a big improvement from the 50 mA draw I was at when I started this project! The other nice thing using this model allows is that the uptime still works. Sort of. It basically tracks how many times it's woken up, multiplies that by 2 seconds, and adds that to millis(). I've noticed that the time calculation is off by about -8%, so I'm manually adding 160ms each cycle. This is VERY hacky, and I should feel bad. I have set up solar panels to keep the batteries charged, and on a cloudy day they give me about 5mA. Throw a few sunny days, and this thing could feasibly run on its own indefinitely. I also implemented the ATC functionality to the rfm radio code. It... doesn't seem to do much at all.
Other posts I'll be soon making -
I’ll post a walkthrough for my outdoor solar setup soon. I think you guys will dig it.
If anyone wants me to post my scripts for publishing from the openhab designer on my windows machine to my headless raspberry pi that is running the mqtt broker and the openhab server, let me know. It's made my life WAY easier.
Other disclaimers – I have NOT tested the final version with all sensors. I have a node running with slightly older code that has a flame sensor, a gas sensor, a DHT22 and a light sensor. The other node I was mostly using during development is my outdoor node, and it has a reed switch and a DHT22.
Also – I have not used git very much at all. If I screwed up attribution or something, please let me know!
Please feel free to improve on it, add suggestions or ask questions!