This is a presentation of a complete end-to-end, solar-powered outdoor sensor hub that publishes data to a public dashboard. It is comprised of four distinct subsystems.
1) The MAX32620FTHR I2C sensor hub with 2 Chirp! soil moisture sensors, 1 temperature / humidity sensor, a single 18650 cell, solar panels, car charger buck regulator, and an RFM95W SPI buss LoRa module. The MAX32620FTHR is programmed with an LMiC Arduino sketch that uplinks readings to a "The Things Network" (TTN) gateway.
The Chirp! soil moisture sensor is open source, uses an ATtiny44A and can be reprogrammed to only work as an I2C sensor. The battery holder, switch and buzzer are removed and the board potted in a section of pvc pipe so it is waterproof and may be completely buried. I use avr-gcc and an older STK-500 with AVRStudio 4.0 to reprogram it, but any Atmel ISP programmer can do it also. Since there are two on the same bus one will need the address changed - which is easy since the existing firmware has a command to change it in EEPROM. A short mbed.com project for the MAX32620FTHR can do the job.
To experiment with more I2C sensors, an Adafruit HTU21D-F device is on the buss at 0x40 and is easy to read with the available library. It returns float values for temperature and humidity, but we round and cast them to uint_8 (so negative temperatures are not yet supported!) for minimum LoRaWAN payload as we are limited to 30 seconds of airtime a day. We transmit 7 bytes every 30 minutes, 2 for each soil sensor, and one each for temp, humidity and SOC. There is an online airtime calculator to assist with that, but for 7 bytes we are using only half the allotted airtime, so it could transmit every 15 minutes instead of every 30 (1800 seconds in the sketch).
For the I2C buss pullup resistors on sda,sdc to 3v3, I had to play with the values for the number of devices and length of cables and ended up using 1k5 ohm, that seems to work reliably for the extra cable capacitance. A discussion of that subject is here.
We use the MAX77650 Arduino library to enable charging, and set the current CHG_CC to 300mA and voltage CHG_CV to 4.1V. We also have to pull the power hold pin high to run off battery power.
We also use a modified MAX17055 fuel gauge library - fixed to use the correct I2C bus (wire2) and Rsense resistor (0.05 Ohm instead of 0.01) - to set the battery capacity (1800mAH), and read and transmit the battery state of charge for monitoring, as that is actually the more interesting variable in the experiment :)
2) The next subsystem is The Things Network single channel gateway - or an existing neighborhood gateway from the map. My gateway location has been known to receive data from a 12Km distance, so the solar powered sensor hub can be quite a distance away from it. Building a TTN gatway is the subject of another project, like this one or my version that uses the Hologram Nova cellular data dongle. Here is the single channel gateway guide and code for the esp8266 that I am using in this project.
You will need to create an account, get on the console, create an application and register a new device, set it for ABP Activation by Personalization which generates a device address, network session key and and an application session key. Those need to be compiled into the MAX32620FTHR Arduino sketch in order to be recognized by TTN.
3) An Amazon Web Services cloud instance running a Mongo DB to save data, a node.js script to monitor TTN queue and save incoming data in the MongoDB, and a python script that periodically runs to update the Beebotte dashboard with the latest data from MongoDB. A server inside a home firewall would work as well as it subscribes to a TTN message queue and writes to the Beebotte api. I just got tired of restarting things every time the power goes out.
node.js script -- reads from TTN and writes to MongoDB. Also saves humidity readings from National Weather Service via darksky.net api. I run this under a pm2 process manager so it will get restarted if it ever fails.
python script -- reads from MongoDB and updates Beebotte dashboard. The python script runs every 30 minutes via cron.
4) Beebotte dashboard - Has a channel called 'maxmultichirp' with resources: chirp1, chirp1, soc, temperature and humidity, and a public dash board to chart those values.
A fun part of any project is when it is finished and can be tested, played with and performance charted. Here is a few days under solar power. This uses oversize panels, two 6v 4.5W units I had from a hamfest, so not much sunlight is needed to restore full charge. They are currently turned down to see how long it takes to get below 50% state of charge and how long in our currently cloudy rainy condition it takes to recharge. Also testing the soil moisture sensors to calibrate their range.
Actually the slope of the discharge at night looks like a pretty constant 0.6 % per hour - so at that rate, with the solar panels covered the system should reach 50% in about 3 and a half days.
_______________________________________________________
Since we have an extension, I though we would examine the i2c buss closer. Actually I had an i2c buss failure after a few days due to water in one Chirp! moisture sensor, and while troubleshooting I found the Maxim library defaults in Wire.cpp TwoWire::begin() to 400KHz.
I2CM_Init(i2cm, &i2cm_sys_cfg, I2CM_SPEED_400KHZ);
Which is good but we are not as concerned for speed so much as a long buss length with a lot of devices and reliability, so to set lower speed we can simply use, for the default buss
Wire.setClock(100000);
Here is a comparison of the two speeds using just the Adafruit HTU sensor with 1 foot of phone cable
8/5/2018 - Added another sensor, a Grove light sensor I had laying around from a starter kit. It uses an analog signal proportional to ambiant light level and was easy to connect to the MAX32620FTHR port A0 (AIN0), 3v3 and ground, and two new lines in the sketch to collect two more bytes for the LoRaWAN payload to send off to chart.
uint16_t lux = analogRead(A0); // read light sensor
memcpy(mydata+7,&lux,sizeof(lux)); // put light in bytes 7&8
It
will be interesting to watch the change in daily pattern of light and dark as the seasons change into fall and winter. I have noticed there is enough power from ambient light during cloudy days to power the unit if not charge the battery, the state of charge stays flat. On sunny days the battery charges up in a few hours.
Improvement - added code to the node.js db insert script to call the python update web dashboard script directly instead of running it from cron. That way the dashboard always shows the latest sensor reading.
const exec = require("child_process").exec
...
else { console.log("Update callback - closing db");
db.close();
exec('/home/centos/pyscripts/beebotte_11.py').unref(); }
Note and discussion about scripts -- I have many devices sending LoRaWAN data thru The Things Network similarly. In fact, this project is inspired by having a number of separate sensors, each with their own battery, arduino pro mini and an rfm95w, so why not consolidate them with a single sensor hub. Also it is an out-shoot of a Smart Garden Irrigation controller focus on sensing soil moisture to help decide if watering is necessary or not. Anyway, the node.js script fires on any of those devices, not just those in this project dashhboard so it was updating way to often.
The MongoDB collection name is based on the message queue topic which is the Things Network device name
var coll = topic.split('/')[1]+'_'+topic.split('/')[2]+'_rh';
collection = db.collection(coll);
Since my TTN Device ID for the MAX32620FTHR project is 'maxmultichrp1' - the db collection name is 'devices_maxmultichrp1_rh' (rh tacked on the end for also saving the weather service relative humidity in the record). Therefore, to prevent too many updates to the web dashboard when other sensors publish data to the TTN message queue, a conditional check for the collection can be put around the python script execution
// call script to update beebotte dashboard with latest data here instead
// of async in cron
// but only if the collection is devices_maxmultichrp1_rh
if ( coll == 'devices_maxmultichrp1_rh' ) {
exec('/home/centos/pyscripts/beebotte_11.py').unref(); }
_______________________________________________________
Notes about The Things Network - Only a few days after publishing this project the data from TTN stopped flowing, at 2AM, without me changing anything. Looking at configuration more closely I noticed my Single Channel Gateway was using us-west.thethings.net correctly, but the application was still set for eu, which had worked for the past year and a half. After creating a new application with the proper region, and recreating the device with a new ID and updating scripts it is working again. Added notes in the node.js script to better describe the region and authentication used, Application ID for 'username' and Access Keys for 'password'.
_______________________________________________________
Future improvements - in the lmic code there are stub functions for sleep
~/Arduino/libraries/lmic/src/hal/hal.cpp
void hal_sleep () {
// Not implemented
}
// check and rewind for target time
u1_t hal_checkTimer (u4_t time) {
// No need to schedule wakeup, since we're not sleeping
return delta_time(time) <= 0;
}
If the MAX32620FTHR could actually be put into low power sleep I think the battery savings would be tremendous, with only a small amount of energy needed every collect and transmit period.
Comments