No Success with First Long Term Measurement

published: March 26, 2017 — last modified: October 23, 2025

The first long term measurement I made, to test the behaviour of the sensor over a longer time range was a failure. After the five days with the device introduced in this post, the readings made absolute no sense.

plant-sensor-data

The sensor was not moved in the flower pot and the plant was once watered at the begin of the measurement. While it looked promising at the begin, the frequency suddenly went down again, which was very irritating. I am still investigating how this could happen.

To get closer to the real measurement of the final plant watering sensor, I started a new approach.

lucky-resistor-1

I soldered a header to one of the LED pads on a fully assembled plant sensor. Next I changed the device for the measurements.

lucky-resistor-2

It is the previous design, using a Adafruit Feather M0 as controller, with the Adalogger FeatherWing  on top.

lucky-resistor-3

Next I added a small prototype board with matching headers for the Feather system and on top the OLED display.

I actually just added an optocoupler to receive signals from the plant watering sensor, but keep it completely insulated. To be able to compare two plant watering sensors, I prepared two inputs. For the first tests, I just like to repeat the long term measurements with one sensor.

A new firmware transfers the current values for the frequency measurement and the battery measurement using pulse length coding as used in the IrDA protocol. Here I use a very slow encoding, using a 1ms pulse for zeros and 2ms pulse for ones.

Code

The reason for this slow encoding is to use interrupts on the receiving side, without much impact to the rest of the code. There is plenty of time between the signal changes of this very slow encoding to process the bits and do all the other work. The plant sensor sends a signal every ~10s with 32bits, so there is no communication for a long time between this bursts.

Here the relevant code segments for the SAM D21 (Feather M0) to receive the bits from the plant sensor using an interrupt.

First I setup a timer to measure the time passed between the interrupts. The method setupCounter() initialises and starts the timer and the method readCounter() reads the current value of the timer.

void setupCounter()
{
  PM->APBCMASK.bit.TCC2_ = 1;
  GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN|GCLK_CLKCTRL_GEN_GCLK0|GCLK_CLKCTRL_ID(GCM_TCC2_TC3);
  while (GCLK->STATUS.bit.SYNCBUSY) {}

  counter->CTRLA.bit.SWRST = 1;
  while (counter->SYNCBUSY.bit.SWRST == 1) {}
  counter->CTRLA.bit.ENABLE = 0;
  counter->CTRLA.bit.PRESCALER = TCC_CTRLA_PRESCALER_DIV256_Val;
  counter->CTRLA.bit.ENABLE = 1;
  while (counter->SYNCBUSY.bit.ENABLE == 1) {}
}

uint16_t readCounter()
{
  counter->CTRLBSET.bit.CMD = TCC_CTRLBSET_CMD_READSYNC_Val;
  while (counter->SYNCBUSY.bit.COUNT == 1) {}
  const uint16_t value = counter->COUNT.bit.COUNT;
  return value;
}

The interrupt is setup using the attachInterupt() methods from the Arduino library. A method onInputChange() is called each time the state on one of the input pins changes. The logic in this method is very simple, it just checks how much time passed between a down/up signal change and shifts the bit into the variable gInputShiftValueA.

void setup()
{
  // ...
  pinMode(11, INPUT_PULLUP);
  pinMode(12, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(11), onInputChange, CHANGE);
  attachInterrupt(digitalPinToInterrupt(12), onInputChange, CHANGE);
  // ...
}

bool gLastInputStateA = false;
uint16_t gLastInputTimeA = 0;
volatile uint32_t gInputShiftValueA = 0;
volatile uint8_t gInputShiftCountA = 0;

void onInputChange()
{
  const uint16_t longBitDelay = 300;
  const bool inputStateA = digitalRead(11);
  if (inputStateA != gLastInputStateA) {
    gLastInputStateA = inputStateA;
    const uint16_t currentTime = readCounter();
    const uint16_t timeDifference = (currentTime - gLastInputTimeA);
    gLastInputTimeA = currentTime;
    if (inputStateA) {
      gInputShiftValueA <<= 1;               if (timeDifference > 300) {
        gInputShiftValueA |= 1;
      }
      gInputShiftCountA += 1;
    }
  }
  // ...
}

In the method readCurrentFrequency() I check if there are enough bits received, check the initial byte for the right value and convert the other bytes to the expected values.

void readCurrentFrequency()
{
  uint32_t inputShiftValueA = 0;
  uint8_t inputShiftCountA = 0;
  noInterrupts();
  if (gInputShiftCountA >= 32) {
    if (gInputShiftCountA == 32) {
      inputShiftCountA = gInputShiftCountA;
      inputShiftValueA = gInputShiftValueA;
    }
    gInputShiftCountA = 0;
    gInputShiftValueA = 0;
  }
  interrupts();
  if (inputShiftCountA == 32 && (inputShiftValueA >> 24) == 0x01) {
    gDeviceFrequencyA = ((inputShiftValueA >> 16) & 0xff);
    gDeviceBatteryA = ((inputShiftValueA >> 8) & 0xff) | ((inputShiftValueA & 0xff) << 8);
  }
}

Please note, I removed all comments from the code segments above to make them more compact for this article.

If you have questions, miss some information or just have any feedback, feel free to add a comment below.

More Posts

How to Automate the Turn-On Time of a Deco Light

Posted on 2018-12-21— How and Why, Learn

I wanted my winter decoration to switch on at dusk without constant adjustments, so I built a simple low-voltage controller using a P-channel MOSFET, a microcontroller and an ambient light sensor. I walk through my design choices, sealing for outdoor use and programming tips—read on if you'd like to build one too.

Read this post

Tall Boxes Ready for Download

Posted on 2020-11-27— 3D Printing, Common

I've finished testing a new set of tall storage boxes designed for 3D printing. They're stackable with my flat and regular boxes, with reinforced walls and improved corners for cleaner prints. Download the free sample set or read the project page and catalog for full designs.

Read this post

Write Less Code using the "auto" Keyword

Posted on 2019-07-12— C++, Improve your Code, Learn

I walk through practical ways I use C++11's auto keyword to reduce repetitive declarations, simplify range-based for loops, lambdas and register access, and make Arduino and embedded code easier to refactor. Read the full post for examples and tips you can apply to your projects.

Read this post

Round Corner Template Set for Woodworking

Posted on 2021-10-29— 3D Printing, Common

I designed a set of 3D-printable round corner templates specifically for woodworking and routing. They’re optimized for 0.4mm nozzles and 0.2mm layers, include snap-in guides, screw holes, and metric/imperial sizes. If you often rout rounded edges, read the full post for printing tips and mounting options.

Read this post

Lucky World – A jump'n'run game for MeggyJr RGB

Posted on 2014-12-17— Common

I've released Lucky World, a compact jump'n'run for the MeggyJr RGB featuring an intro, 16+ levels, secrets and an end scene. I squeezed it into ATmega328P limits and shared the sources under GPLv2. If you're curious, please read on for downloads, requirements, and play instructions.

Read this post

Make your Code Safe and Readable with Flags

Posted on 2018-05-06— C++, Improve your Code

I show how a small C++ flags template makes embedded register bitmasks safer and much easier to read. I'll explain why macros and CMSIS-style definitions lead to errors, and walk through clear C++11 examples and a simple driver. Read on to see the implementation and usage.

Read this post