Project tutorial

Squirrel Feeder Tweet © GPL3+

What if the squirrels in your garden would post a tweet on twitter when the squirrel feeder is running out of nuts?

  • 309 views
  • 0 comments
  • 2 respects

Components and supplies

Abx00014 featured xjsqphgdlv
Arduino MKR Fox 1200
×1
12785 01
SparkFun ToF Range Finder Sensor - VL6180
for example - you may also use the Adafruit version (see below)
×1
Omron b3f 1000 image 75px
SparkFun Pushbutton switch 12mm
×3
Adafruit Tilt Sensor (Tilt ball switch)
×1
12002 04
Breadboard (generic)
×1
11026 02
Jumper wires (generic)
×1
U.FL Mini PCI to RP-SMA Pigtail Antenna Cable
×1
GSM antenna
check that it can accept frequencies in the SigFox's range (868 Mhz)
×1
2aa%20battery%20holder%20for%20rtc
UDOO 2AA Battery Holder for RTC
×1
12731 01
SparkFun USB to Serial Breakout - FT232RL
optional, useful for debugging
×1
Wodden Squirrel Feeder
for example ... - if you don't want to build your own
×1
Wooden Cigar Box
×1
Self-Countersunk Woodscrews 4 x 30mm
×1
Heat Shrink Tubing
×1
Hazelnuts
for testing ...
×42
Battery AA 1.5V
×2

Necessary tools and machines

Hy gluegun
Hot glue gun (generic)
Electric Screw Driver / Drill Machine
Wood Drill Bit 8mm
Cutter Knife

Apps and online services

About this project

I. The Idea

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. Planning

The Board

The 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 Sensor

First 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. Implementation

Integrating 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 Box ...

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).

... and the Cloud

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!

Custom parts and enclosures

Setup and wiring ...
Just a photo ...
Setup and wiring (png) ndbjrozy2x
Final Prototype ...
Just a photo ...
Connected squirrel feeder (png) ybfq31t1um

Schematics

Remote fill-level monitor
Fritzing image
Remote fill level monitor bb speviuazjb
Remote fill-level monitor (Fritzing)
Fritzing file
remote_fill-level_monitor_NRh5jRDlji.fzz
remote-fill-level-monitor
GitHub repository

Code

RemoteFillLevelMonitorMkrFox1200.inoArduino
Just the sketch for use in your local IDE ...
/*
**
** RemoteFillLevelMonitorMkrFox1200
**
*/


/*

:Author: 3magku
:Date: 14/11/2017
:Revision: version#
:License: Public Domain

This sketch is based on the Arduino >TUTORIALS > Examples from Libraries > SigFox > EventTrigger - Example
at https://www.arduino.cc/en/Tutorial/SigFoxEventTrigger.

See also the tutorial at https://www.arduino.cc/en/Tutorial/SigFoxFirstConfiguration for first configuration 
and registration of your MKRFox1200 board.

*/

/*
   Arduino MKR Fox 1200 / VL53L0X/VL6180X / SigFox

   Board:   Arduino MKRFox1200
   Sensor:  VL6180X Time of Flight Distance Ranging Sensor (VL6180) (Adafruit breakout) or
            VL53L0X Time of Flight Distance Ranging Sensor (VL530X) (Adafruit breakout)
   Wiring:
    VL___0X   GND   ->  GND       MKRFox1200
              SCL   ->  D11 (SCL)
              SDA   ->  D12 (SCA)
              VIN   ->  3,3V
    Switches on: D0, D1 and D7 to GND
*/

/*
* Choose Time of Flight Distance Ranging Sensor (TOFDRS): VL53L0X or VL6180X 
* or leave undefined for testing SigFox connectivity only.
*/
#define TOFDRS_VL6180X
//#define TOFDRS_VL53L0X

/* *** imports *************************************************************** */

// VL53L0X & VL6180X:
#include <Wire.h>
#ifdef TOFDRS_VL53L0X
#include <Adafruit_VL53L0X.h>
#elif defined TOFDRS_VL6180X
#include "Adafruit_VL6180X.h"
#else
#warning "No ToFDRS defined!"
#endif

// SigFox
#include <SigFox.h>

// LowPower
#include <ArduinoLowPower.h>

/* *** globals *************************************************************** */

#ifdef TOFDRS_VL53L0X
Adafruit_VL53L0X vl = Adafruit_VL53L0X();
#elif defined TOFDRS_VL6180X
Adafruit_VL6180X vl = Adafruit_VL6180X();
#else
#warning "No ToFDRS defined!"
#endif

//
float vl_lux = 0;
uint8_t vl_range = 0;
uint8_t vl_status = 0;

// Set debug to false to enable continuous mode
// and disable serial prints
int debug = true;

// SigFox message
/*
    ATTENTION - the structure we are going to send MUST
    be declared "packed" otherwise we'll get padding mismatch
    on the sent data - see http://www.catb.org/esr/structure-packing/#_structure_alignment_and_padding
    for more details
*/
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;
// stub for message which will be sent
SigfoxMessage message;

// Trigger
volatile int trigger_id = 0;

// LED
#define LED_BUILTIN 6 // MKRFox1200 LED pin

/* *** setup **************************************************************** */

void setup() {
  // LED
  pinMode(LED_BUILTIN, OUTPUT);

  // Debugging ...
  if (debug == true) {
    /* ATTENTION:
       Using Serial1 instead than Serial, since on waking up from standby
       the USB port could get confused and become unvailable for the host.
       To copy from Serial1 connect a  3.3V USB-to-serial converter to
       pins 13-14 (TX-RX).
    */
    Serial1.begin(115200);
    while (!Serial1) {}
    Serial1.println("INFO: Starting ...");
  }

  // SigFox ...
  if ( debug == true ) {
    Serial1.println("INFO: SigFox: Initializing ...");
  }
  if (!SigFox.begin()) {
    if ( debug == true ) {
      Serial1.println("ERROR: SigFox: Failed to initialize!");
    }
    // Initialization failure, trying to reboot ...
    reboot();
  }

  if (debug == true) {
    // Enable SigFox debug prints and LED indication
    SigFox.debug();
  }

  // Send module to standby until we need to send a message
  if ( debug == true ) {
    Serial1.println("INFO: SigFox: Standby ...");
  }
  SigFox.end();

  // Trigger setup:
  // Pins 0, 1  and 7 are connected to a switch and enable the interrupt on voltage falling event
  pinMode(0, INPUT_PULLUP);
  LowPower.attachInterruptWakeup(0, triggerEvent1, FALLING);
  pinMode(1, INPUT_PULLUP);
  LowPower.attachInterruptWakeup(1, triggerEvent2, FALLING);
  pinMode(7, INPUT_PULLUP);
  LowPower.attachInterruptWakeup(7, triggerEvent3, FALLING);

  // ToFDRS setup:
  vlSetup();
}

/* *** loop ***************************************************************** */

void loop()
{
  // Sleep until an event is recognized ...
  if ( debug == true ) {
    Serial1.println("INFO: Sleeping ...");
  }
  LowPower.sleep();

  // 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();

  // Transmission status ...
  if (debug == true) {
    if (ret > 0) {
      Serial1.println("ERROR: SigFox: No transmission");
    } else {
      Serial1.println("INFO: SigFox: Transmission ok");
    }
    Serial1.println(SigFox.status(SIGFOX));
    Serial1.println(SigFox.status(ATMEL));
    // Loop forever if we are testing for a single event
    // while (1) {};
  }
}

/* *** functions ************************************************************ */

// Trigger callback

void triggerEvent1() {
  trigger_id = 1;
}

void triggerEvent2() {
  trigger_id = 2;
}

void triggerEvent3() {
  trigger_id = 3;
}

// ToFDSR

void vlSetup() {
  if ( debug == true ) {
#ifdef TOFDRS_VL53L0X
    Serial1.println("INFO: TOFDRS: VL53L0X: Setup ...");
#elif defined TOFDRS_VL6180X
    Serial1.println("INFO: TOFDRS: VL6180X: Setup ...");
#else
    Serial1.println("WARNING: No ToFDRS defined!!!");
#endif
  }
#if defined(TOFDRS_VL53L0X) || defined(TOFDRS_VL6180X)
  if (! vl.begin()) {
    if ( debug == true ) {
      Serial1.println("FATAL: TOFDRS: Failed to find sensor!");
    }
    while (1);
  }
#endif
  if ( debug == true ) {
    Serial1.println("INFO: ToFDRS: Sensor initialized!");
  }
}

#ifdef TOFDRS_VL53L0X
void vlRead() {
  VL53L0X_RangingMeasurementData_t measure;
  if ( debug == true ) {
    Serial1.println("INFO: VL53L0X: Reading ...");
  }
  vl.rangingTest(&measure, debug); // pass in 'true' to get debug data printout!
  vl_status = measure.RangeStatus;
  if (measure.RangeStatus != 4) {  // phase failures have incorrect data
    vl_range = measure.RangeMilliMeter;
    if ( debug == true ) {
      Serial.print("INFO: VL53L0X: Range: "); Serial.println(measure.RangeMilliMeter);
    }
  } else {
    vl_range = 0;
    if ( debug == true ) {
      Serial.println("ERROR: VL53L0X: Out of range!");
    }
  }
  vl_lux = 0;
}
#elif defined TOFDRS_VL6180X
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");
    }
  }
}
#else
void vlRead() {
  vl_status = 0;
  vl_lux = 42;
  switch (trigger_id) {
    case 1:
      vl_range = 42;
      break;
    case 2:
      vl_range = 168;
      break;
    case 3:
      vl_range = vl_range + 7;
      break;
  }
  if ( debug == true ) {
    Serial1.println("WARNING: No ToFDRS defined!!!");
    Serial1.print("INFO: DUMMY: Lux: "); Serial1.println(vl_lux);
    Serial1.print("INFO: DUMMY: Range: "); Serial1.println(vl_range);
  }
}
#endif

// Auxiliary

void waitAndBlink(int msec, int count) {
  for (int i = 0; i < count; i++) {
    if ( debug == true ) {
      Serial1.print("INFO: Waiting for ");
      Serial1.print(msec * (count - i));
      Serial1.println(" milliseconds ...");
    }
    digitalWrite(LED_BUILTIN, HIGH);
    delay(msec / 2);
    digitalWrite(LED_BUILTIN, LOW);
    delay(msec / 2);
  }
}

// System

void reboot() {
  NVIC_SystemReset();
  while (1);
}
RemoteFillLevelMonitorMkrFox1200
Arduino Web Editor Sketch
remote-fill-level-monitor
GitHub repository

Comments

Similar projects you might like

Music Reactive LED Strip

Project showcase by buzzandy

  • 85 views
  • 1 comment
  • 7 respects

Pavlov's Cat

Project tutorial by Arduino

  • 192 views
  • 0 comments
  • 1 respect

Arduino Obstacle Avoidance Robot with Ultrasonic HC-SR04

Project tutorial by Jorge Rancé

  • 777 views
  • 1 comment
  • 11 respects

Using Finite State Machines

by Gustavo Gonnet

  • 6,974 views
  • 2 comments
  • 18 respects

Visual Accelerometer

Project tutorial by Reid Paulhus

  • 226 views
  • 1 comment
  • 5 respects

Alexa: "Your Clothes Are Dry"

Project in progress by TNunnster

  • 1,699 views
  • 0 comments
  • 6 respects
Add projectSign up / Login