How do you know when it’s time to water your desk plant?

This is how I read the moisture level of my fiddle back ficus desk plant from the internet.


  • Fiddle Back Ficus – I purchased mine on ebay for $15, it came shipped in the post. Larger ones can fetch $60+
  • A microcontroller, I used a CC3200 but any microcontroller will do. I’ll try and keep the guide device-agnostic. $5-25. The CC3200 can be programmed through the very arduino like energia IDE. [].

    I have described a non-wifi non-cc3200 version of this code  here:

  • Moisture sensor with amplifier, I used this one
  • Thingspeak/Xively – This is the quickest way to just make sensor data available. Thingspeak seems to be the most open with Xively being quite limited without a commercial plan.


  • Program & play


Less than 1 hour

Quantity produced:



plantmoisture – energia – this will connect to wifi and post the current moisture and temperature to the

How to:


Moisture probe -> amplifier -> microcontroller

The moisture probe in the plant

The probe connects to the amplifier, the amplifier to the microcontroller.

The CC3200 development board has pinouts as per:

Conveniently, it’s VBUS-GND-INPUT-INPUT along pins 21-22-23(marked as P57 on the board)-24(P60). This means we can plug the moisture sensor amplifier straight into the back of the dev board.

The amplifier has a small potentiometer adjustment on it, this only effects the threshhold at which the D0 (digital output) switches on and off, as well as the LED on the board. I felt that was kind of arbitrary and useless so I haven’t used it. We will just be reading the analogue output into pin 24.


Sign up for thinkspeak and create a channel. Set it up so you have 3 variables, we want to monitor moisture (from pin A24) and since the board has a temperature sensor, we may as well read that too.


#ifndef __CC3200R1M1RGC__
// Do not include SPI for CC3200 LaunchPad
#include <SPI.h>
#include <WiFi.h>
#include <Wire.h>
#include "Adafruit_TMP006.h"
#include <stdlib.h>

// ThingSpeak Settings
char thingSpeakAddress[] = "";
String writeAPIKey = "APIKEY"; // put your API key from thingspeak here, it needs to be a WRITE API key
const int updateThingSpeakInterval = 300 * 1000; // Time interval in milliseconds to update ThingSpeak (number of seconds * 1000 = interval)

//buffer for float to string
char buffer[25];
// your network name also called SSID
char ssid[] = "ssid";
// your network password
char password[] = "pass";

// initialize the library instance:
WiFiClient client;

unsigned long lastConnectionTime = 0; // last time you connected to the server, in milliseconds
boolean lastConnected = false; // state of the connection last time through the main loop
const unsigned long postingInterval = 300*1000; //delay between updates
int failedCounter = 0;

int water = 0;
int sensorPin = 24; // select the input pin for the potentiometer
int sensorValue = 0; // variable to store the value coming from the sensor\
Adafruit_TMP006 tmp006(0x41); // start with a diferent i2c address!
void setup() {
//Initialize serial and wait for port to open:

// attempt to connect to Wifi network:
Serial.print("Attempting to connect to Network named: ");
// print the network name (SSID);
// Connect to WPA/WPA2 network. Change this line if using open or WEP network:
WiFi.begin(ssid, password);
while ( WiFi.status() != WL_CONNECTED) {
// print dots while we wait to connect

if (! tmp006.begin()) {
Serial.println("No sensor found");
while (1);

Serial.println("\nYou're connected to the network");
Serial.println("Waiting for an ip address");

while (WiFi.localIP() == INADDR_NONE) {
// print dots while we wait for an ip addresss

Serial.println("\nIP Address obtained");
void loop() {
if (digitalRead(PUSH1)){
Serial.println("Button1: Force update");
lastConnectionTime = postingInterval+1;
if (digitalRead(PUSH2)){
Serial.println("Button2: Water");
lastConnectionTime = postingInterval+1;
water = 1;

// if there's incoming data from the net connection.
// send it out the serial port. This is for debugging
// purposes only:
while (client.available()) {
char c =;

// if there's no net connection, but there was one last time
// through the loop, then stop the client:
if (!client.connected() && lastConnected) {

// if you're not connected, and ten seconds have passed since
// your last connection, then connect again and send data:
if (!client.connected() && (millis() - lastConnectionTime > postingInterval)) {
// read the temp sensor:
float objt = tmp006.readObjTempC();
float diet = tmp006.readDieTempC();

// read the value from the sensor:
sensorValue = analogRead(sensorPin);
float scaledValue = ((((2600.0 - 1400.0) - (sensorValue - 1400.0))/(2600.0 - 1400.0)) * 100.0);

String sdiet = dtostrf(diet,3,3,buffer);
String sscaledValue = dtostrf(scaledValue,3,3,buffer);
String swater = dtostrf(water,3,3,buffer);

Serial.println("Sent: field1=" + sscaledValue + "&field2" + sdiet + "&field3=" + swater);

updateThingSpeak("field1=" + sscaledValue + "&field2" + sdiet + "&field3=" + swater);
water = 0;

// store the state of the connection for next time through
// the loop:
lastConnected = client.connected();
void updateThingSpeak(String tsData)
if (client.connect(thingSpeakAddress, 80))
client.print("POST /update HTTP/1.1\n");
client.print("Connection: close\n");
client.print("X-THINGSPEAKAPIKEY: "+writeAPIKey+"\n");
client.print("Content-Type: application/x-www-form-urlencoded\n");
client.print("Content-Length: ");
Serial.println(">>TSDATALength=" + tsData.length());

Serial.println(">>TSDATA=" + tsData);

lastConnectionTime = millis();

if (client.connected())
Serial.println("Connecting to ThingSpeak...");

failedCounter = 0;

Serial.println("Connection to ThingSpeak failed ("+String(failedCounter, DEC)+")");


Serial.println("Connection to ThingSpeak Failed ("+String(failedCounter, DEC)+")");

lastConnectionTime = millis();

void printWifiStatus() {
// print the SSID of the network you're attached to:
Serial.print("SSID: ");


You can see that in addition to reading and publishing the moisture and temperature, we’re also publishing a water status.

When PUSH2 (a pushbutton on the front of the board) is pressed, the microcontroller will set the water variable to 1 and publish it to thingspeak before resetting the variable to 0. Press this button whenever you water the plant and you can see quantify you have added water to the pot.

PUSH1 is set up to immediately publish an update, useful if you’re  testing it for the first time and don’t want to wait 5 minutes.

The only other interesting part of the code is the scaling of the sensor value. I wanted a 0-100% moisture reading so I used the following:

sensorValue = analogRead(sensorPin);
float scaledValue = ((((2600.0 - 1400.0) - (sensorValue - 1400.0))/(2600.0 - 1400.0)) * 100.0);

Basically, read the value from the pin correlates the the voltage returned by the probe. The value goes from 0-4095 (where 4095 is full scale 12-bit). I wanted a 0-100% moisture reading so I waited until the plant was very dry and read the unscaled value (2600) then watered the plant to saturation and read the value again (1400). Assuming it’s linear between these two values allows me to return 0-100% to thingspeak for graphing and display on gauges.

Assuming everything is working correctly, you should see data coming through on thingspeak:

You can check out the current status of the plant right now at: