Squirrels are cute and cuddly, but once they've found and seized your bird feeders, many people change their minds about them.
Instead of fighting them away from bird feeders give them their own squirrel feeding stations: Research has shown that squirrels are very territorial. When fed continuously away from your bird feeders, they will mark the area as their own and protect it from other squirrels entering the area.
To keep these tiny fury critters happy with their feeders, you have to monitor the food supply. If a feeder remains empty or the food supply has been exhausted for some time, squirrels will move on to other food sources (back to your bird feeders!).
One solution would be to equip squirrels with smartphones, teach them how to use and let them notify the owner of the feeding box (or the public to increase the pressure), that the box has been running out of nuts and immediate refill is required - but maybe this might be too time and cost consuming.
An alternative approach is to equip the squirrel feeding box with sensors connected to a MKR FOX 1200 board for monitoring food supply and consumption, even in remote locations (e.g. large parks or gardens) with no network coverage and no nearby power supply.
II. PlanningThe BoardThe Arduino MKRFOX1200 combines the functionality and computional power of the Arduino Zero, with SigFox’s global LPWA network. Featuring Microchip SAM D21 32-bit Cortex-M0+ microcontroller as core and an ATA8520 RF transmitter module, the MKRFOX1200 is capable of running for over six months on a pair of standard AA 1.5V batteries.
This makes the board an excellent choice for unattended operation in remote areas.
The SensorFirst considerations regarding the use of load cells or an ultrasonic ranging sensor (e.g. HC-SR04) were quickly abandoned: A "construction" attaching the squirrel feeder to/with the load cell turned out to be unstable and "mechanically challenging"; The usual ultrasonic ranging sensors are unreliable in small narrow containers and do not work with 3.3V.
Finally the choice fell on an optical time-of-flight distance ranging sensor to determine the fill-level of the feeder box by measuring the distance range between the lid and the bottom of the box respectively the top of the food (nuts) in box.
The VL6180X (see also SparkFun ToF Range Finder Sensor - VL6180 or Adafruit VL6180X Time of Flight Distance Ranging Sensor (VL6180)) is a high-accuracy proximity sensor using the time-of-Flight (ToF) principle (see TOF camera) to measure the absolute distance to a target independent of target reflectance and uninfluenced by its color and surface.
This makes it a good choice for this project to measure the distance between the box's lid and the food top level respectively the bottom of the box - both targets with in-homogeneous surface and color.
Alternatively the VL53L0X (the "big sister" of the VL6180X) can be used for larger (height) feeder boxes.
Operational Considerations- To reduce power consumption the monitoring device should be only active and transmitting when triggered (i.e. after opening and closing the lid of the box).
- Basic calibration of the sensor should be done easily in the field.
To recognize the opening/closing of the a reed switch (and magnet) or a tilt switch can be "integrated" into the lid.
To indicate/trigger a basic calibration measurement - empty box / full box - two buttons are used.
III. ImplementationIntegrating the ToF sensor
The ToF sensor is small and easy to use in the break board configuration provided by SparkFun or Adafruit equipped with a regulator and level shifting. It can be used with any 3-5V power or logic microcontroller with no worries. Communicating to the sensor is done over I2C with some simple commands and Adafruit provides a good tutorial and a nice and easy library with examples for Arduino. The sensor breakout is connected to GND, VCC, SCA (D11) and SCL (D12) on the MKR Fox 1200.
The handling of the sensor is quite straightforward and based on the examples from the library.
float vl_lux = 0;
uint8_t vl_range = 0;
uint8_t vl_status = 0;
void vlSetup() {
if ( debug == true ) {
Serial1.println("INFO: TOFDRS: VL6180X: Setup ...");
}
if (! vl.begin()) {
if ( debug == true ) {
Serial1.println("FATAL: TOFDRS: Failed to find sensor!");
}
while (1);
}
void vlRead() {
if ( debug == true ) {
Serial1.println("INFO: VL6180X: Reading ...");
}
vl_lux = vl.readLux(VL6180X_ALS_GAIN_5);
vl_range = vl.readRange();
vl_status = vl.readRangeStatus();
if ( debug == true ) {
Serial1.print("INFO: VL6180X: Lux: "); Serial1.println(vl_lux);
if (vl_status == VL6180X_ERROR_NONE) {
Serial1.print("INFO: VL6180X: Range: "); Serial1.println(vl_range);
}
if ((vl_status >= VL6180X_ERROR_SYSERR_1) && (vl_status <= VL6180X_ERROR_SYSERR_5)) {
Serial1.println("ERROR: VL6180X: System error");
}
else if (vl_status == VL6180X_ERROR_ECEFAIL) {
Serial1.println("ERROR: VL6180X: ECE failure");
}
else if (vl_status == VL6180X_ERROR_NOCONVERGE) {
Serial1.println("ERROR: VL6180X: No convergence");
}
else if (vl_status == VL6180X_ERROR_RANGEIGNORE) {
Serial1.println("ERROR: VL6180X: Ignoring range");
}
else if (vl_status == VL6180X_ERROR_SNR) {
Serial1.println("ERROR: VL6180X: Signal/Noise error");
}
else if (vl_status == VL6180X_ERROR_RAWUFLOW) {
Serial1.println("ERROR: VL6180X: Raw reading underflow");
}
else if (vl_status == VL6180X_ERROR_RAWOFLOW) {
Serial1.println("ERROR: VL6180X: Raw reading overflow");
}
else if (vl_status == VL6180X_ERROR_RANGEUFLOW) {
Serial1.println("ERROR: VL6180X: Range reading underflow");
}
else if (vl_status == VL6180X_ERROR_RANGEOFLOW) {
Serial1.println("ERROR: VL6180X: Range reading overflow");
}
}
}
Logic and triggers
To reduce power consumption the monitoring device will be only active and transmitting when triggered by
- activation a tilt switch when opening and closing the lid of the box or
- pressing a button to perform a calibration.
The basic control flow and part of the setup of the implementation is based on the Event trigger tutorial for the MKR Fox 1200 on arduino.cc. Instead of using two buttons, a tilt switch and three buttons are used to wake up the board and to send a message via SigFox.
The message to be sent is composed of
- a mode string wich can be CL, CH, or RR,
- a status code (unsigned short integer) reflecting the sensor reading status code (vl_status in the code above),
- the actual range reading from the sensor (vl_range),
- the lowest range reading from the sensor when calibrated,
- the highest range reading from the sensor when calibrated and
- the fill-level (0-100) calculation based on these three range values.
typedef struct __attribute__ ((packed)) sigfox_message {
uint8_t mode[3] = {0};
uint8_t status = 0;
uint8_t value = 0;
uint8_t value_low = 0;
uint8_t value_high = 0;
uint8_t level = 0;
} SigfoxMessage;
Pressing any button will wake up the device and trigger range measurement and message transmission.
- Pressing button S1 will be regarded as calibration of the highest range value to be measured when the fill-level is empty. The actual reading will be remembered as highest range value ( value_high ) and the mode string to be sent is CH.
- Pressing button S2 will be regarded as calibration of the lowest range value to be measured when the fill-level is full. The actual reading will be remembered as lowest range value ( value_low ) and the mode string to be sent is CL.
- Pressing button S3 (for testing) or activating the tilt switch S4 will be regarded as regular " withdrawal" from the box. There will be a delay (10 seconds) to let the "withdrawal operation" be completed before reading the range value. The actual reading ( value ) the mode string RR are being sent.
.
.
.
// Getting here means that an event was received ...
SigFox.begin();
if (debug == true) {
Serial1.println("INFO: Event occured on trigger " + String(trigger_id));
}
delay(100);
// Wait a moment ...
if ( trigger_id == 3 ) {
waitAndBlink(1000, 10); // Wait (and blink) for 10 seconds
}
// Read ToFDRS ...
vlRead();
// Build message:
switch (trigger_id) {
case 1:
// Regarding this as calibration event for low range value ...
message.mode[0] = 'C' ; message.mode[1] = 'L'; // CL = Calibration Low
message.value_low = vl_range;
break;
case 2:
// Regarding this as calibration event for high range value ...
message.mode[0] = 'C' ; message.mode[1] = 'H'; // CH = Calibration High
message.value_high = vl_range;
break;
case 3:
// Regarding this as the regular reading for range value ...
message.mode[0] = 'R' ; message.mode[1] = 'R'; // RR = Regular Reading
break;
}
message.status = vl_status;
message.value = vl_range;
//Calculate fill-level from range values ...
float level = 0;
if ( message.value_high != message.value_low ) {
float v = message.value;
float h = message.value_high;
float l = message.value_low;
level = (v - h) / (l - h) * 100;
Serial1.print("INFO: Operation: Fill-level is calculated as level = ");
Serial1.println(level);
}
message.level = level < 0 ? 0 : (uint8_t) level;
if ( debug == true ) {
Serial1.print("INFO: Operation: Message is < ");
Serial1.print((char *) message.mode);
Serial1.print(" + ");
Serial1.print(message.status);
Serial1.print(" + ");
Serial1.print(message.value);
Serial1.print(" + ");
Serial1.print(message.value_low);
Serial1.print(" + ");
Serial1.print(message.value_high);
Serial1.print(" + ");
Serial1.print(message.level);
Serial1.println(" >");
}
// Send message ...
if ( debug == true ) {
Serial1.println("INFO: SigFox: Sending ...");
}
SigFox.beginPacket();
SigFox.write((uint8_t*)&message, sizeof(message));
int ret = SigFox.endPacket();
// Back to standby
if ( debug == true ) {
Serial1.println("INFO: SigFox: Standby ...");
}
SigFox.end();
.
.
.
The prototype setup (with a 3.3V USB-to-serial converter connected for debugging) looked like this:
The actual squirrel feeder box can be the typical standard model: Wooden box with acrylic glass front and lift up lid. You can either construct and build your own or you purchase one.
The model used in this project has a removable tin cover on the lid, which made it quite easy to integrate the ToF sensor and a tilt switch into it.
- Remove the tin cover;
- Drill two "long" holes (8 mm) from the small back of the lid about half-way to the front;
- Drill another hole (8 mm) from the bottom of the lid through to the top at the end of one of the two holes as a "peephole" for the ToF sensor;
- Cut out the hole on the top of the lid to make some space for the sensor.
- Place the sensor in the cut out hole, facing down, so that it can "look" through the hole, wire it and lead the wires through the long hole out to the back of the lid.
- Place tilt switch in the other long hole in the back of the lid.
- Optional (but recommended): Use some sealing to fix/seal the sensor in place.
- Replace the tin cover.
For better protection the wires are wrapped with heat shrink tubing.
The whole electronic stuff:
- Breadboard with MKR Fox 1200 and buttons,
- battery clip with 2 AA 1.5V batteries,
- antenna connector,
- an optional switch for arming/disarming the tilt switch (for re-filling)
- and the optional USB-to-serial converter (for testing, can/should be removed in normal operation mode)
is stuffed into a wooden cigar box with two 8mm holes drilled into it:
- One at the top for the wiring for the sensor and the tilt switch and
- one at the side for the SMA connector for the GSM antenna.
The wires for the ToF sensor and the tilt switch are lead through the hole in the top of the cigar box and the cigar box itself is attached to the squirrel feeder box (wood screws).
Arduino Web Editor
The sketch for this project was created with the Arduino Web Editor. This saves you from installing and configuring the board and the libraries in your local Arduino IDE. You can find the sketch of this project here.
SigFox
Before you can use your Arduino MKR Fox 1200 board with SigFox, you have to create an account and register your device at the SigFox backend.
To do this follow the tutorial First Configuration at arduino.cc.
Once you have registered your board it might be also a good idea to follow the Event trigger tutorial to, since the basic control flow and parts of the project's setup is based on this example and it makes you familiar with the callback configuration at the SigFox backend.
ThingSpeak
Assuming you already have a ThingSpeak account (if not, you have to create one) ...
At ThingSpeak:
- Create a new channel
- Add 6 fields: range, r_high, r_low, level, mode, status (actually you would need just one field containing the level, but if you want to do further data processing on the ThingSpeak platform the other components might be also of interest)
- Note the Write API Key for the new channel
- Goto Apps > Actions > ThingTweet and link a Twitter account to you ThingSpeak
- Goto Apps > Actions > React and create a new React with condition type Numeric for your channel and the level field with condition is less or equal to and a number value for the level (e.g. 5 %) a squirrel might consider to low to be happy with. Then select action ThingTweet, enter the tweet text and select the the Twitter account you have linked.
SigFox
At SigFox
- Goto DEVICE TYPE and in the Device Type - List click on the Name 'Arduino n/a kit'
- Goto CALLBACKS and click on New
- Select Type DATA UPLINK and Channel URL
- Tick Send duplicate
- Enter Custom playload config: mode::char:3 status::uint:8 value::uint:8 valueL::uint:8 valueH::uint:8 level::uint:8
- Enter URL pattern: https://api.thingspeak.com/update?api_key=THEWRITEAPIKEYYOUHAVENOTED&field1={customData#value}&field2={customData#valueL}&field3={customData#valueH}&field4={customData#level}&field5={customData#mode}&field6={customData#status} and replace THEWRITEAPIKEYYOUHAVENOTED with the Write API Key from your ThingSpeak channel
- Select Use HTTP Method GET
The following slides might help to illustrate the whole procedure:
If you've done everything correctly you might soon read some more tweets with #outofnuts ... ;o)
IV. Operation;o)
For the owner: The Re-Fill Procedure
- Open the cigar box;
- Optional: Disarm the tilt switch;
- Check if the feeder box is empty;
- If the feeder box is empty, press the S2 button (white) while the lid is closed to calibrate the distance ranging measurement for the empty feeder;
- Fill in food (nuts!);
- If the feeder box is full, press the S1 button (grey) while the lid is closed to calibrate the distance ranging measurement for the full feeder.
- Optional: Test measurement by pressing the S3 button (red);
- Optional (mandatory, if you disarmed it before): Arm the tilt switch;
- Close the cigar box;
For the squirrel: The Withdrawal Procedure
- Open the lid;
- Get out a nut;
- Close the lid;
- Consume the nut immediately or hide it somewhere for later consumption;
- Enjoy!
Comments