Not Writing Records to the Logfile.

Customer Check-In

Despite all the fiddling and stuff like running out of CO2, fixing the pumps…I’m pleased with how my customers are doing in the leaf spa.  Here are pictures from yesterday:

IMG 5401

IMG 5403


IMG 5405 

I’ve been using the Kale and A LOT of basil.

Today’s Post

I thought I had fixed the challenge I was having writing records out to the log file.  Well…I haven’t.  I just looked at the most recent log file and see that it only has 28 rows.  Hmmm….In this post I find and fix a memory leak but first I take a step back to understand how memory is used by the Arduino.  I am glad I did.  I now have a much better conceptual model with where my code is going versus my variables…even though I “sort of” knew this, I did not have a strong enough conceptual model in my head…I guess I lost my memory about (Arduino) memory.

The memory leak turned out to be repeated calls to openFile() in writeSensorDataToLogFile() that created and left hanging around instances of File objects:

void  writeSensorDataToLogFile() {
    File logFile = openFile();  
  if (!logFile) {  
  dtostrf(sensorData.temperatureValue, 4, 1, additionalInfo);
  char *idx = additionalInfo + strlen(additionalInfo);
  *idx++ = ',';
  dtostrf(sensorData.humidityValue, 5, 1, idx);
  idx = additionalInfo + strlen(additionalInfo);
  *idx++ = ',';
  itoa(sensorData.CO2Value, idx, 10);
  writeEventHappened(SensorData, additionalInfo);

Thanks to Those That Went Before

Adafruit: Thank you for your excellent tutorial on “Memories of an Arduino.” I am an extremely happy customer of Adafruit.  I feel great about buying from Adafruit because I feel that a lot of the margins made are given back to us through these great tutorials, youtube videos, friendliness, etc.  Adafruit is NOT a company in which a few (usually white men) are making a ton of money while they lack the compassion shown by Adafruit.  Again THANK YOU.

Open Source

The version of TheLeafSpa.ino that evolved from this post is located at this GitHub location.

Debugging Memory Use

How Memory is Used by the Arduino

I decided to take a step back and appreciate the memory architect used by the Arduino.  It’s like going into a place that does wine sampling.  A moment to reflect on why something is the way it is, and appreciate it for what it is…OK..enough with the analogies.

Adafruit’s excellent section on memory architecture, notes on the Arduino, Flash = Program Memory and SRAM = Data Memory.

The Arduino UNO has only 32K bytes of Flash memory and 2K bytes of SRAM.” Note: for completeness there is also EEPROM.  However, theLeafSpa.ino challenge would be on how much Flash and/or SRAM the code is using. 

Looking at this table, 

I might consider using a Mega instead of the Uno. Or perhaps the Adafruit Feather M0 WiFi – ATSAMD21 + ATWINC1500.  If I switch to the Feather (which is tempting…), I lose the Grove Connector base shield..of course, I could have a Frankenstein way of tying altogether for now…

How Memory is Used by TheLeafSpa.ino

From my previous post, I noted the current amount of program storage (flash) and SRAM (dynamic memory) the compiler and boot loader let me know about:

Sketch uses 24,970 bytes (77%) of program storage space. Maximum is 32,256 bytes.
Global variables use 1,416 bytes (69%) of dynamic memory, leaving 632 bytes for local variables. Maximum is 2,048 bytes.

Hmm…these numbers made me think I don’t have this type of issue:

 but…I underemphasized the term “dynamic” in my thinking.  The amount of SRAM changes.  

Using free_ram()

I had been calling free_ram() at the beginning of the code (see the debug library)…but I wasn’t doing what Bill recommended in the Adafruit article: “SRAM utilization is dynamic and will change over time. So It is important to call free_ram() at various times and from various places in your sketch to see how it changes over time.”

I added a function to add an amount SRAM record to the log file:

  Write the amount of SRAM available after the record has been written.  If there is a change, it's of interest
  to know the length of stringBuffer and additionalInfo.  Are they what I expect them to be?
void writeFreeRamtoStringBuffer() {
  int sram = freeRam();
  itoa(AmtSRAM, stringBuffer, 10);
  char *idx = stringBuffer + strlen(stringBuffer);
  *idx++ = ',';
  itoa(sram, idx, 10);
  idx = stringBuffer + strlen(stringBuffer);
  *idx++ = ',';
  itoa(strlen(stringBuffer), idx, 10);
  idx = stringBuffer + strlen(stringBuffer);
  *idx++ = ',';
  itoa(strlen(additionalInfo), idx, 10);
int freeRam () {
  extern int __heap_start, *__brkval;
  int v;
  return (int)&v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);

The added AmtSRAM records are added within the writeEventHappened function:

void writeEventHappened(logRow_t event, char * additionalInfo) {
  if (event == CardInserted || event == CardRemoved) {
  File logFile = openFile();
  if (!logFile) {
  // an event has a log row type followed by date/time followed by additonal info.
  itoa(event, stringBuffer, 10);
  char *idx = stringBuffer + strlen(stringBuffer);
  *idx++ = ',';
  idx = stringBuffer + strlen(stringBuffer);
  *idx++ = ',';
  strcpy(idx, additionalInfo);

Looking at the log file:


13,459   (67 bytes)
0,3/29/2017,10:4:18,22.1, 84.0,764

13,428   (67 bytes)
0,3/29/2017,10:6:18,22.0, 82.7,1343

13,397   (31 bytes)
0,3/29/2017,10:8:18,21.9, 83.0,967

13,366    (67 bytes)
0,3/29/2017,10:10:18,21.9, 83.5,1168

13,335    (67 bytes)
0,3/29/2017,10:12:18,21.9, 83.7,1301

13,304    (31 bytes)
0,3/29/2017,10:14:18,21.8, 84.0,973

0,3/29/2017,10:16:18,21.8, 84.2,1191


The most glaring thing I notice is SRAM is lost after writing a row that has a record type that is not a sensor reading (i.e.: record type is not 0 and before writing a sensor reading.  I highlighted these occurrences above.

This is how I tracked down the overzealous / needless creation of File Instances:

void  writeSensorDataToLogFile() {
  File logFile = openFile();  
  if (!logFile) {  

The log file now looks like:

0,3/30/2017,1:2:27,18.8, 79.5,523
0,3/30/2017,1:4:27,18.8, 79.6,514
0,3/30/2017,1:6:27,18.8, 79.7,515
0,3/30/2017,1:8:28,18.7, 79.9,514
0,3/30/2017,1:10:28,18.7, 79.7,515
0,3/30/2017,1:12:28,18.7, 79.8,522

I changed the log file row types for the amount of SRAM.  AmtSRAM = record type 11.  The amount of SRAM starts out as 545 bytes prior to writing out the first record (53).  It goes down to 523 before writing out record type 10 (warmup is over).  This is a loss of 22 bytes which remains consistent.  So at this point, I’m not researching deeper.  When a relay gets switched there are some bytes that get lost.  However, these return.  The SRAM stays around 523 bytes.  This will work.  

I enjoyed working on this.  It is always fun to learn and seeing positive changes based on applying what I have learned.

The Leaf Spa Code Got Too Big for the Arduino – When Code Stops Working

Besides adding in the Flow Meter code, I’ve been checking the log files and decided to add additional information such as what were the settings being used for LED, pump, CO2 on/off, etc.

As I added this to the code, I started getting messages that the amount of memory left could lead to unpredictable results.

Drat. The days of “getting this working as quickly as possible” coding on the Arduino Uno platform are over.  Besides, I’ve switched from poking at the code to relying on keeping the plants healthy and happy.  I am getting a lot of use from the log file information…so most likely I will be wanting to add more rows.

While I don’t see myself as an EFFICIENT and AWESOME coder – we all know the type…the person who would be the star of an X-Games Coder Edition…whoa…that is so not me!  But in the spirit of IT MUST WORK SO FIX IT…

I will buckle down on fixing up how strings are handled within theLeafSpa.ino.

Thanks To Those That Went Before

The folks behind the Arduino Uno and the Arduino IDEI am EXTREMELY grateful for the Arduino Uno environment.  The IDE is simple and easy to get started in.  There is a library for every sensor and peripheral.  This environment has saved me massive amounts of prototyping time.

Open Source

This blog post will discuss changes made to TheLeafSpa.ino.  The specific version discussed is found at this GitHub location.

The Arduino Uno Environment

The Arduino Uno environment has enabled so many of us to realize our DIY projects.  There are many pros to working in this environment:

  • Simple.
  • Libraries available for practically every sensor or device a DIYer wants to interact with.
  • A VERY supportive and helpful community.  It is easy to Google and discover how to do exactly what you wish to do.
Like everything that does many things very well, there are a few things that are traded off, including:
  • Lack of source line debugging.
  • Compiled language – compared with Python where you can try something and get immediate results.
  • Limited memory which you will most likely run out as your prototype grows.  This is the case with the Leaf Spa.
  • A programming language that is more C like than a simple language like Python or the extremely well done Swift language.
  • Lack of a “native” Internet support similar to what we’re seeing in the WiPy area.
The trade off I am addressing in this blog post is the limited memory.  I started getting compiles with running out of memory warnings…

Steps To Increase Available Memory

Unfortunately, I didn’t record the benefit of each change I made as I tore through theLeafSpa.ino…but here is a list of the stuff I did.

Removing the Debug Library

The debug library – even when turned off – affected the amount of memory.  The debug library proved extremely useful when initially developing the prototype code.  However, the library became less useful as the code became stable and I started relying on the log file instead of printlns to a serial monitor.

Removing the Use of the String() Object

Environments like Swift and Python make handling strings exceptionally easy.  The Arduino IDE provides a similar simplistic experience through the String() object.  The challenge I ran into is this abstraction took away too much control over how much memory was left.  In an environment in which each additional byte of memory starts to matter, control of the memory outweighed the simplicity of coding (unfortunately).

Sprintf() – a Brief Detour

When I first removed the String() object, I replaced creating a string with ints and floats with sprintf().  However, the code using sprintf() was still too big.

What I ended Up Coding for Strings

The code I am documenting here is located at this GitHub location.  I ended up (perhaps not surprisingly) relies on:

  • character arrays and char pointers.
  • itoa(), dtostrf(), strcpy() for int, float, and string conversions.
  • Using two global char arrays – stringBuffer and additionalInfo to store info that will be written to the log file.
Here is an example of the change I made when I replaced a usage of String():
  itoa(globalSettings.secsBtwnReadings, additionalInfo, 10);
  char *idx = additionalInfo + strlen(additionalInfo);
  *idx++ = ',';
  itoa(globalSettings.targetCO2Level, idx, 10);
  idx = additionalInfo + strlen(additionalInfo);

I use a pointer to position where to copy in the data.  The amount of memory I ended up with:

Sketch uses 24,970 bytes (77%) of program storage space. Maximum is 32,256 bytes.
Global variables use 1,416 bytes (69%) of dynamic memory, leaving 632 bytes for local variables. Maximum is 2,048 bytes.

This is good enough for now.  If I need to reduce program storage space, the next thing I probably will look into is not using dtostrf().    Here is the difference when no dtostrf() is used compared to when it is.

Not used:

void setup() {

void loop() {

Sketch uses 450 bytes (1%) of program storage space. Maximum is 32,256 bytes. Global variables use 9 bytes (0%) of dynamic memory, leaving 2,039 bytes for local variables. Maximum is 2,048 bytes.


void setup() {
  // put your setup code here, to run once:
  float f = 99.9;
  char fStr[6];

void loop() {

Sketch uses 2,022 bytes (6%) of program storage space. Maximum is 32,256 bytes. Global variables use 9 bytes (0%) of dynamic memory, leaving 2,039 bytes for local variables. Maximum is 2,048 bytes.

The code is more windy, but it does do what I want – give back enough RAM to stabilize the code.


pH/EC-983 Product Breakdown -First Post

I love watching Limor/Lady Ada take apart a Consumer Electronic device and explain to us what the chips are and what is going on.  I thought what a great learning opportunity to take something that I have some familiarity with – in this case pH and EC circuits – and find a product to take apart and figure out how the circuit works. 


The product I picked was the ph/EC-983.  I picked up a couple on eBay.


This handy tool integrates a pH and EC probe as well as gives a temperature reading.  It is inexpensive and if it is “good enough” I was thinking it would be super amazing to plunk the probe in the nutrient bath.  I would use firmware to take pH/EC measurements and use the measurements to adjust the pH and nutrients.

I was mostly interested in the bottom piece.  The bottom piece contains the pH and EC probes:

NewImage  NewImage

I was thinking if I could figure out the signals coming out of the probe:


So that I could put the probe in the nutrient bath and connect up a pH/EC circuit to be monitored through firmware.

 Hmmm…I couldn’t pry apart the device in the nice, clean way Lady Ada is able to…so…out came the Dremel!  


The Active Parts

To get started, I looked at the active parts.

These two images show the location of the parts:

Pasted Image 3 24 17 6 16 AM

Pasted Image 3 24 17 10 17 AM

HEF 4053BT

Two of the chips are the HEF 4053BT (data sheet):


From the data sheet: The HEF4053B is a triple single-pole double-throw (SPDT) analog switch, suitable for use as an analog or digital multiplexer/demultiplexer.   This page on Basic Electricity has a great discussion on switches including SPDT switches.

HEF 4011BT

Another is the HEF 4011BT (data sheet):


From the data sheet: The HEF4011B is a quad 2-input NAND gate.


Another chip is a variant on the 062C dual op amp (data sheet):

062C DualOpAmp


The internal ICL7106 (data sheet):


From the data sheet: TheICL7106 is designed to interface with a liquid crystal display(LCD)…


Unfortunately, I got a little too aggressive with the Dremel.  I’m still able to make out this chip is a HEF4030BT (data sheet):


From the data sheet: The HEF4030B is a quad 2-input EXCLUSIVE-OR gate. 

Maxim ?

Drat…the Dremel scratched the markings off this chip.  I can tell it is a Maxim and there looks to be A1601…or A1801…or…:



The SGM3005 XMS (data sheet): 


From the data sheet: The SGM3005 is a dual, low on-resistance, low voltage, bidirectional, single-pole/double-throw (SPDT) CMOS analog switch designed to operate from a single 1.8V to 5.5V power supply.

At this point, my eyes hurt.  Time to stop for now.


Adding a Flow Meter


, ,

Awhile back, the plants were not getting any water/nutrients because the pump relay stopped working.  It became clear to me after this incident that a flow meter monitoring whether the pump was working would be very beneficial to have as part of the Base section of the Leaf Spa.

This post is about incorporating the flow meter into the Leaf Spa.  

Thanks To Those That Went Before

Adafruit – Thank you for your awesome customer service.  Adafruit goes above and beyond any company I know providing sample code, tutorials, excellent forums, products I can trust to work.  Products cost a bit more…but I really want Adafruit and companies who respect their customers and employees.

The Flow Meter

I got a liquid flow meter in the mail from Adafruit yesterday.



How It Works

My goal is to detect whether water is flowing through the sensor.  I’ll do this in the firmware by figuring out if the flow rate is > 0 when the pump is supposed to be on.  I haven’t used a flow meter before so I figured I should know at least a little bit on how it works.

T.K. Hareendran’s blog post, “Working with Water Flow Sensors & Arduino”, gave me great info on the basics of how this type of flow meter works.  For example, I find out the type of sensor I bought is a YF-201 Hall-Effect Water Flow Sensor.  As pointed out in the article:  This sensor sits in line with the water line and contains a pinwheel sensor to measure how much water has moved through it. There is an integrated magnetic Hall-Effect sensor that outputs an electrical pulse with every revolution… By counting the pulses from the output of the sensor, we can easily calculate the water flow rate (in litre/hour – L/hr) using a suitable conversion formula.

I’ll be looking at the flow rate of water when the Leaf Spa firmware turns on the pump relay.  If there is no water flowing, something within the pump system (the pump itself, the relay) is not working.

As usual, Adafruit has provided superior support for the flow meter by making available a sample Arduino sketch at this GitHub location.  Getting this sketch working was easy and gave me a better feel for how the flow meter works with the Arduino.

I’ll blindly take the approach of “if you say so” based on Lady Ada’s comments in her Arduino Sketch:

  // if a plastic sensor use the following calculation
  // Sensor Frequency (Hz) = 7.5 * Q (Liters/min)
  // Liters = Q * time elapsed (seconds) / 60 (seconds/minute)
  // Liters = (Frequency (Pulses/second) / 7.5) * time elapsed (seconds) / 60
  // Liters = Pulses / (7.5 * 60)
  float liters = pulses;
  liters /= 7.5;
  liters /= 60.0;

I’m interested in the flow rate, not in how much water goes through the sensor.  Given what Lady Ada tells me about the flow rate (she’s using Q to represent the flow rate in liters/minute):

Pulses (Hz)) = 7.5 * Flow rate(liters/minute)

Flow rate (liters/minute) = Pulses (HZ)/7.5

Test Code

I’m going to use an external interrupt (the attachInterrupt() API) to let the firmware know when water is flowing through the sensor.  The test sketch that seems to work – FlowMeterTest2.ino – is located at this GitHub location.

Adding Code

Simpson DOHBut first…an OOPS…I thought it would be a “good thing” to update the SD library since I was informed I didn’t have the latest…BUT I FORGOT about what I talked about in this post about SD.end().  Here is the error I got:

Multiple libraries were found for “SD.h”
Used: /Users/margaret/Documents/Arduino/libraries/SD
Not used: /Users/margaret/Documents/Arduino/libraries/SD-master
Not used: /Applications/

seems I’m not lacking SD libraries :-).

Turns out the one being used was the library that I updated yesterday…the SD library folder within my Arduino/libraries folder.  I deleted the SD folder.  Now the firmware is back to using the SD-master library…which includes the end() API.

Bummer – attachInterupt() is only available on pins 2 and 3 on the Arduino Uno.  The firmware is already using these pins (from TheLeafSpa.ino sketch located at this gitHub location (version 8767e1d):

   2 - DHT temp/humidity
   3 - SD card detect
   4 - pump relay
   5 - LED relay
   6 - CO2 relay
   7 - Software Serial
   8 - Software Serial
   10 - SDI chip select
   11 - SDI DI pin
   12 - SDI DO pin
   13 - SDI CLK pin

The firmware is using attachInterrupt() with pin 3.  This means I’m restricted with the current hardware configuration to using pin 2 for the flow meter.

It doesn’t help that Seeed’s Grove Base Shield limits the digital i/o pins further:

I’m changing the pin mappings:

   2 - Flow Meter
   3 - SD card detect
   4 - DHT temp/humidity
   5 - LED relay
   6 - CO2 relay
   7 - Software Serial
   8 - Software Serial
   9 - Pump relay
   10 - SDI chip select
   11 - SDI DI pin
   12 - SDI DO pin
   13 - SDI CLK pin
  • DHT is on pin 4 instead of pin 2.
  • pump relay is on pin 9 instead of pin 4.
  • Flow meter is on pin 2.

 Adding the Flow Meter

It took a bit of emotional effort to cut into the plumbing and insert the flow meter..but it’s done…before and after:

NewImage   NewImage 

Checking the Log File

 Below are two snippets of the log file after adding the flow meter:

1 3/22/2017 11:40:28          
0 3/22/2017 11:41:28 23.5 71.5 1097    
5 3/22/2017 11:41:28          
2 3/22/2017 11:41:28 167.33        


1 3/22/2017 11:55:28          
0 3/22/2017 11:55:29 24.3 70.2 1113    
5 3/22/2017 11:55:29          
6 3/22/2017 11:55:37          
2 3/22/2017 11:56:28 175.33        

1 in the first column means the pump has turned on. 2 means the pump turned off.  The row identifying the pump was turned off now includes a column with the flow rate in liters/minute.  Both are > 0 which means water has gone through the plumbing.  YIPPEE!  The first flow rate shows 167 liters/minute (~44 gallons/minute).  The second shows 175 liters/minute (~46 gallons/minute).

The pump’s data sheet (I am using the 160 GPH model) states the model I am using has a min GPH of 106 and a max of 175.

I measured a WAY HIGHER value…SO…hmmm…

The values for the flow rate ARE NOT ACCURATE however, the Leaf Spa is detecting whether the water is flowing.

So, for what I need it to do, the flow meter is doing ok.  At this point, I do not need the flow rate.  If I did, I would debug this further.

Good better best  Of course, measuring the flow rate as accurately as possible should be part of the Leaf Spa.  But since my goal at this point is to detect water flow, I will not debug now.  Future builds should accurately measure water flow.  Frankly, I am not surprised measurements are off.  I did not do any flow rate accuracy testing before putting the flow meter in.


ChallengesBringOpportunites Yippee!  The flow meter is in place.

Adding a Fan to the Left Side

Customer Check-In

The images show the improved (I hope!) left side with the second fan mounted as well as the plants/fan on the right side:



The right side of the Leaf Spa has a mounted fan blowing air down on the leaves:


None of the plants look stressed.  However, the left side of the Leaf Spa does not.  The Stevia Leaves curl up:


I’m exploring if this leaf stress is caused by the air temperature being too high.  I just turned down the light intensity.  Turning down the light intensity will lower the air temperature a bit.  

In this post I’m focusing on designing and 3D printing a fan  mount that will be attached on the left side of the Grow Chamber.  This way, the left side will be more balanced to the right in terms of air temperature and access to CO2.  I’ll be including a hole for a CO2 tube.  I plan to split the CO2 tubing so that both the left and right side will receive CO2 when the CO2 valve is turned on.  

The Fan Mount

I’ll start with the Fusion 360 model I made – and am using – for the right fan mount.  I will modify the model such that the bolt mounts go through the plastic and attach to the LED Shelf with a nut.  This way, the plastic doesn’t have to handle screw/bolt threading – a weakness on the right Fan mount.


Now that I have the fan mount 3D printed, it’s time to wire up power in a way that both fans share the 12V power supply.


 I put connectors on wires going between the Grow Chamber and the Base because I have this new idea to bling out the Leaf Spa so that I can wheel it outside during the summer.

One improvement down…many more to most likely come!

Increase CO2 -> Decrease the Cost of Lighting


, , , , , , ,

Customer Check-In

This time I used a small tri-pod to hold the camera.  Not ideal, but does show the plants ok.  The plants near the fan are getting a nice air flow.  The Stevia leaves (plants closer to the camera) are curling in. My first guess is the leaves are either getting too much light and/or the temperature is too high.  The leaves themselves are all green, so at this point I do not believe the curling is due to too much nutrients.  I’ll be adjusting the lights based on the discussion in this post.  I’ll address the temperature in a subsequent post.

Increase CO2, Decrease Light Costs

Isn’t it FANTASTIC that increasing the CO2 can decrease the cost of lighting?

I am practicing sustainability.  I am not only interested in the $ cost,  but also how much energy is currently being used by the Leaf Spa and if there are things I can tweak to minimize both costs without compromising healthy leaf growth.

The current amount of power used by the LEDs is 95.8 W.  The amount of energy used up within a 24 hour period = 95.8 W * 20 hours / 1000 = 1.9kWh. From a $ perspective, Chart 2 from the Bureau of Land Statistic’s web page notes 1kW costs us folks in Seattle around $.10. So 1.9kW $.19 per day.

I have not optimized the settings for light intensity in the presence of higher CO2 levels.  Indeed, I haven’t evaluated the current light intensity with what is considered a healthy light intensity for the leaves.

The goal of this post is to see if I can tweak the light intensity to a lower amount such that costs in both energy and money are decreased.

Thanks To Those That Went Before

Dr. Kubota – The lectures in the course –  Greenhouse Plant Physiology & Technology from Dr. Kubota (University of Arizona), inspired me to understand the how and why CO2 benefits plant growth…after all, I want to give plants a Spa experience – so ultimately, only the best for the plants! 🙂

Increasing CO2 Increases Photosynthesis

Dr. Kubota showed us this image when she gave the lecture on the effects of CO2 on plants.  What this image is saying:


given the same amount of light, the photosynthetic response is increased when the CO2 concentration is higher than the normal (of ~400ppm).   Since CO2 level + light drives the photosynthetic response rate, we can get to the same leaf mass with less light if light is supplemented with CO2.  YIPPEE!

Dr. Kubota goes on to note the effects of higher CO2 are somewhat thwarted by the plant’s (partial) closing of stomata.  Which is a bit of a bummer.  However, I was encouraged to see this slide:


 Given the research results for lettuce – and making a wild extrapolation…a guesstimate I most likely shouldn’t be making but will experiment with is using 30% less light. 

Photoperiod and Amount DLI

Currently, the photoperiod is 20 hours.  The DLI is much higher than what  I discussed in a previous post, of a 10 mol·m-2·d-1 target.  Now that I have had time to explore the current set up, I’m going to adjust photoperiod and DLI to accommodate the learnings discussed in the article: Managing Electrical Conductivity (EC) For Hydroponic Basil Production.  While the Leaf Spa is being designed to accommodate many leafy plants, I am assuming  for this round of exploration, basil leaves can represent the leaves of the other plants when it comes to photoperiod, DLI, and CO2 concentration. The article states:

Basil had comparable growth (fresh and dry mass, branches, nodes) across nutrient solution ECs and there were no significant differences (Figure 1).

Figure 1. Sweet basil grown with ECs ranging from 0.5 to 4 mS∙cm−1 under low (~7 mol∙m−2∙d−1) or high (~15 mol∙m−2∙d−1) daily light integrals (DLIs). This photo was taken three weeks after transplanting seedlings into hydroponic systems and treatments.

Although EC has no effect on basil growth, growth increased for all three basil species grown under ~15 mol∙m−2∙d−1 compared to those grown under 7 mol∙m−2∙d−1. There was no interaction between the EC and DLI on basil growth.

New Targets for Photoperiod and DLI

Based on the information in this article and background information that has accumulated in my brain over the past year, assuming a CO2 level of ~ 1200ppm, the new targets are:

  • photoperiod of 16 hours.
  • DLI of 70% 15 mol∙m−2∙d−1 = 10.5 , rounding up to 11 mol∙m−2∙d−1
This amount of DLI + ~1200ppm CO2 should get to the equivalent photosynthetic rate as a DLI of 15 mol·m-2·d-1 + ~ 400ppm.

Current DLI

I’ll be using the calculation for DLI I discussed in this post: DLI = PAR reading  x (3,600 * photoperiod)/1,000,000.  In this post, I gave my initial readings for the Leaf Spa PAR levels.  The readings were far from uniform, ranging from 288µmol/s at an edge to 737µmol/s at the center.

Good better best Using the COB LEDs makes for uneven PAR as shown in the table below.  COBs are perhaps best for lighting when the light source is significantly higher than the canopy.  For the small height of the leaf shelf, smaller LEDs spread uniformly across the bottom of the shelf – or other mechanism (such as using lenses, i.e.: exploring optics) to smooth out the LED’s emittance – should be explored.

I just took measurements at each plant base and then at ~the top of the Stevia plant.

Base       Average Average
  290 280 380 317  
540 680 460 300 495 391
  430 400 250 360  
~Top of Stevia          
540 640 780 350 578  
930 600 810 700 760 613
  540 500 460 500  

The numbers are ball park.  I did not pay attention to getting exact measurements.  Rather these were what I read holding the PAR meter “to about where” I thought was the right spot and taking a reading.  Totally unscientific and not reproducible, but for my needs, good enough.  The top of Stevia refers to the distance seen in this image: 

Pasted Image 3 18 17 7 11 AM

Calculate Current Values

Links for how to calculate DLI when the LED is constant:  

First, I’ll calculate the DLI (DLI = PAR reading (in µmol m-2 d-1) x (3,600 (seconds/hour) * photoperiod in hours )/1,000,000 µmol/mole) for:

  • the base: DLI = (391  * 3600  * 20 )/1000 000 = 28.15 moles/day
  • the top of the Stevia Leaf: DLI = (613 * 3600 * 20)/1000000 = 44.14 moles/day
Whoa!  That’s more than is required.
Good better best I didn’t have a way (or at least couldn’t figure out a way) to calculate the DLI based on the LED’s data sheets.  At best, some vendors such as Cree provide instantaneous PPF – but this tells me how much the LED emits.  It doesn’t tell me how much PPF will smack onto a leaf.  I’m seeing measurements to be in it’s infancy with debates around definitions of acronyms such as PPFD.  Growmau (during this video) came up with a calculation based on a 4’ x 4’ grow space (1.48m2), using a 650w light @ 2.2 µmol/J.  650 * 2.2 = 1,430 µmol/sec emitted. 1,430/1.48 = 966 PPFD avg (¨mol/s/m2)…

Calculate When DLI ~ 11 moles/Day

Currently, the COBs are being run at 100% with a constant current of 1.05A.  If I continue to run the COBs at 100%, the photoperiod – which is currently at 20 hours would be:

  • the base = (319 * 3600 * X )/1000000 = 11 moles/day -> (11 * 1000000)/ (319 * 3600) = 9.6 hours
  • the top of the Stevia Leaf: (11 * 1000000)/(613 * 3600) = 5 hours.
Given unevenness of PAR – both from a plant location within the Leaf Spa as well as how far the leaves are from the LEDs as they grow, I need to pick an approximate value for the PAR to base calculations on.  I want to use a conservative approximate PAR value.  Looking at the measured values, I’m choosing 400 PAR to base calculations at.  
  • PAR = 400 -> (11 * 1000000)/ (400 * 3600) -> ~ 8 hours
hmm….I want the lights to be on for 16 hours. 

Adjusting the LED Driver

The LED Driver I am using is the Meanwell 709-ELG100-C1050A.  I purchased this Meanwell driver from Mouser (product link).  I am very happy with this driver.  However, up until now I hadn’t thought much about lowering the light intensity.

Good better best Future LED driver designs should take into consideration how the light intensity can be adjusted.

As noted in the data sheet:


A models can adjust the current through a built-in potentiometer.  This is the model I bought.  Perhaps a better choice would have been the B model (for about $4 more).  The B model has connections to dim the intensity.  The range the current can be adjusted to is noted within the data sheet:


The C1050A can be adjusted from 525 – 1050mA.

Wouldn’t you know it? The drawing in the data sheet shows the current adjustment POT on the bottom of the case:


To get to 15 moles/day on a 16 hour photoperiod, the (average) PAR should be ~ 200.  This means I can lower the current to the low end.

Amount of Energy – Down by Half

I noted earlier the current amount of power used by the LEDs is 95.8 W.  The amount of energy used up within a 24 hour period = 95.8 W * 20 hours / 1000 = 1.9kWh. $ amounts vary.  Given $.10 / kWh, the cost = $.19 per day.

Lowering the current brought the power used down to 46.7 W.  I also adjusted the photoperiod to 16 hours.  With this setup, the amount of energy used within a 24 hour period = 46.7W * 16 hours / 1000 = .75kWh at $.10 / kWh, the cost = $.08 per day, half as much.


I don’t know if the lower settings are too low.  I’ll keep observing.  I certainly love the idea of 1/2 the energy and have the $ amount to run the LEDs.  Also, the lower setting will have other benefits such as:

  • decreasing the temperature of the Grow Chamber.
  • increasing the lifespan of the LEDs.

That’s it for now.  Time to move on to another adventure.

Customer Check-In


, ,

This post is a reflection  on the growth of the plants that are living their lives in The Leaf Spa.  

March 18th

I came home after five days. 

NewImage NewImage

Whoa – the top leaves of the basil are getting burnt by the LEDs.  And look – I can tell that is a Kale plant…ooh – the Stevia plants grew.  That’s a relief since I just finished reading blog posts on how hard it is to grow Stevia from seed.  YIPPEE!

A couple of observations:

  • The basil plants are too tall for the current LED shelf height.  Should I move the shelf up OR should I trim the basil plants down so the other plants aren’t penalized with less PAR?
  • Perhaps I’m seeing what I want to see, however, the plants closer to the fan appear more mature.  The quality of the Leaf Spa would improve if a second fan was placed on the other side of the Leaf Spa

Good better bestRight now the Leaf Spa has one CPU fan that benefits the plants on the right side of the Leaf Spa.  Putting a fan on the other side would benefit plants on the left side.  It would also be beneficial to release CO2 from both sides.

March 1st

To put plant growth progress into perspective, the image below shows what the plants were doing on March 1st:


I am happy with the plant growth that has occurred within the last 2 1/2 weeks.  However, there is A LOT of room for improvement.  The current results are promising.  I am eager to evolve the Leaf Spa experience into the ideal controlled environment for the plants as I learn more about (and implement) what works best.

Taking MH-Z19 CO2 Readings Using Micropython

Now that I have explored reading the CO2 sensor using PWM using an Arduino (GitHub location of Arduino sketch), I want to do the same using Micropython.  Since I got a nifty WiPy awhile back, I thought I’d start with using this hardware rich micro python platform.  Getting to the punch line, I was able to calculate what appears to be a reasonable CO2 value using Pin interrupts.  I am not sure how reasonable because I cannot test/calibrate with Leaf Spa readings for a few days.  I stumbled over several challenges including my lack of python knowledge.  But…I also became frustrated and lost time because of whimsicalities I ran into using the WiPy PyMakr IDE as well as the WiPy implementation 1.8.7 of pycom I was using.  At this point, I am not convinced micro python is ready for me to use for the Leaf Spa.  However, I plan to continue exploring micro python using Adafruit’s feather Huzzah.


I’ve used PWM for blinking lights and such.  But until the MH-Z19, I haven’t had the opportunity to read PWM high/low signals.  So I thought I’d start by making sure I understood PWM a bit more.  Here’s a nice image:

As noted in the All About Circuits article on PWM:  “a PWM signal is a sequence of periods in which the duration of the logic-high (or logic-low) voltage varies according to external conditions, and these variations can be used to transmit information.”

So the folks who designed and built the MH-Z19 encoded CO2 readings by modulating the ON/HIGH (which determines the OFF) time within a cycle.

As noted in benripley’s excellent post, Three Ways to read a PWM signal, “…what we are really looking for is the length of time the signal remains high for each cycle.  I’ll be looking at two of these to see if I can read the MH-Z19 using PWM:

  • is there an equivalent pulseIn function to the one I used to create this Arduino sketch to read the MH-Z19?  Not really.
  • is there a pin change interrupt API that fires when the pin changes state from high to low and from low to high? Yes.

Calculating the CO2

Now that I feel more comfortable with working with a sensor that sends values through a pwm channel, I wrote this script.  Argh!  My knowledge of python is so low!  But the micro python code running on the WiPy seems to do the trick.  The gist of the code:

  • set the Pin reading the pwm to callback to rising() when the rising part of the pulse is detected.
  • once rising() is called back, start a ms timer that once a falling edge is detected will give us the th value.  Set the Pin to callback to falling() when the rising pulse is detected.
  • once falling() is called back, stop the timer and set th.  Set the Pin to callback to rising() one last time so we can set the tl value.
  • rising() gets called back one last time.  Set tl.
  • calculate the CO2 based on the formula given in the MH-Z19 data sheet (section 7.1):

WiPy Whimsicalities

Identifying the Pin

The way I was able to finally reference the pin I was using for PWM in was time consuming/frustrating.  This is because the documentation (to me) is confusing. 

The documentation of  Pin class within the machine module of the WiPy’s version of micro python  discusses the Pin callback:

from machine import Pin

def pin_handler(arg):
    print("got an interrupt in pin %s" % (

p_in = Pin('P10', mode=Pin.IN, pull=Pin.PULL_UP)
p_in.callback(Pin.IRQ_FALLING | Pin.IRQ_RISING, pin_handler)

That didn’t work for me.  I finally stumbled upon this text:


Contains all Pin objects supported by the expansion board. Examples:

led = Pin(Pin.exp_board.G16, mode=Pin.OUT)

which seems to by in the 1.8.4 WiPy documentation, but missing from the 1.8.7 documentation! ….Grrrrr…..

Figuring pin identification was exacerbated by this statement in the 1.8.7 documentation….Grrrrr…..

On the WiPy board the pins are identified by their string id:

from machine import Pin
g = machine.Pin('GP9', mode=Pin.OUT, pull=None, drive=Pin.MED_POWER, alt=-1)

Using the USB Port to Program the WiPy

I’m plugging in the expansion board into one of my Mac’s two USB ports.  It was very common for me to receive:

> Could not enter friendly repl. (click to attempt to reconnect)

Googling this obtuse message seems to mean PyMakr can’t connect to the expansion board through the USB port.….Grrrrr…..  I put my Mac away for a few hours and come back to it.  The USB port magically starts working.  Hmmm…


Well…I’m stopping my exploration into measuring CO2 using the MH-Z19 connected to a WiPy for now.  When I get back home, I’ll test/calibrate within the Leaf Spa.


Build Log March 12th (Daylight Savings and Different CO2 Sensor)


Today is daylight savings.  Unfortunately, the RTC doesn’t know this and now the time is off by one hour.

Good better best The Leaf Spa needs to accommodate Daylight Savings.  How I’ll do this is TBD.


A Different CO2 Sensor

I recently received two Winsen MH-Z19 NDIR CO2 sensors I ordered from Alibaba.

I am exploring a different CO2 sensor because:

  • The cost is less (I paid $22 for one).
  • The interface to the micro controller does not rely on the UART.  As noted in diy_bloke’s Instructable: The MH-Z19 has 3 ways of reading the CO2 concentration. It has a Tx/Rx UART, It has an analog output and it has a PWM input.  I’m hoping this will make it possible to use with microPython on the esp8266 enabled board.

The data sheet is located here.  Here is an image of the layout:



According to the diagram, the pins 2 and 3 are for UART readings and pin 9 is for PWM readings.

Testing the Board

I used the information provided in this stack exchange to write an Arduino sketch (GitHub location).  Here are results from running the sketch within my work area:

Test 1
th: 166
tl: 838
2000ppm Concentration limit: 328
5000ppm Concentration limit: 820
Test 2
th: 166888
tl: 830489
2000ppm Concentration limit: 331
5000ppm Concentration limit: 829
Test 1
th: 166
tl: 838
2000ppm Concentration limit: 328
5000ppm Concentration limit: 820
Test 2
th: 167489
tl: 829888
2000ppm Concentration limit: 333
5000ppm Concentration limit: 833

I then ran the tests with the sensor inside the Leaf Spa:


Drat…i forgot to capture the results.  Tests 1 and 2 were within error range of each other.  The results were ~ 150ppm lower than the MH-Z16 sensor.  BUT I have not calibrated the MH-Z19.  I want to do that when I get back from vacation.  Note: the MH-Z19 can be read through PWM, however calibration happens through the UART.  Not a big deal…but does point out that not all functionality can be handled through digital i/o. 

My thoughts based on this initial investigation of the MH-Z19 is it is “the best” choice to be the CO2 sensor for the Leaf Spa because:

  • less expensive than the MH-Z16 with enough accuracy.
  • does not need UART to read CO2 data.
  • it is an NDIR CO2 sensor, so it is measuring CO2 instead of estimating – which is what the CCS-811as a VOC sensor does (see wminark’s comment on this blog post).
  • It is more readily available than the CCS-811.  I put an order in for a few CCS-811’s on Digikey months ago…the current estimate is a few weeks from now.  However, that date has changed before.  Perhaps if I was buying in the bulk amounts a cell phone manufacturer needs…but at the small quantities I need, I don’t feel comfortable with my ability to source the CCS-811.

Learning about the MH-Z19 was awesomely fun and useful.  I am excited to try the Leaf Spa firmware using micro python and the ESP8266…!


Build Log for March 10th (the Water Pump Stopped pumping)


, , ,

Customer Check-In

Um…sadly…I’m having a fight with the camera mount I put together.  This is impeding my willingness to take pictures.  Then the pump stopped working which happily this story ends with me able to fix the pump.  So…the pressure was too much to actually take a picture of the plants.  Silly.

What’s Going on with the Pump?

Although the plants look like they are getting nutrients, I’m concerned I haven’t been hearing the water pump through the system…is the pump really turning on / off?

Good better best I will install a flow meter within the path of the water like DIY hacking did on their post.  I just ordered Adafruit’s flow meter.  I did my irrational order before thoroughly checking out if it would work.  I purchased from Adafruit because I value what the company stands for and the amount of knowledge Adafruit shares with the community.

While the plants haven’t shriveled up and died, I haven’t hear the pleasant gurgle of water.  Is the pump working?  To figure this out, I’m taking the following steps:

  • Look at the log file.  Does the log file show recent records that the pump was turned on and off?  What time period is the pump supposed to be on? 
    • The log file (sheet 03102017_1245) DOES show the pump is turning on/off as expected.  The log file records the pump turning on every 15 minutes and staying on for one minute.  The pump should have turned on four times an hour at 3, 18, 33, and 48 minutes past the hour.
  • Stand by the Leaf Spa when the pump is supposed to be on.  Did I hear the relay switch?  Did I hear the pleasant gurgle of water?
    • NOPE.
  • Check if the pump works by plugging it into a power source not connected to the relay.
    • YEP.
I am left to believe the relay switch is no longer working for the pump.
Why Isn’t the Relay Switch Working?
I pop open the wall socket mount and …
Simpson DOHthere are drips of water inside… DRAT… I had placed the relay box at a location where water from a leak in the plumbing could hit the relay box.  A few drops entered through the wall sockets.  Now I’m concerned because of safety.  Some of the relay connections have a small amount of rust on them.
It turns out, the relay being used for the pump stopped working.  I rewired the relays, hooked the box back up.  

ChallengesBringOpportunitesNow the pump, LED, CO2 relays are working.

Changing the Nutrients

Now that the pump is back working, I decided to change the nutrients.  As I do this, I am thinking through how the Leaf Spa system can make it (no 🙂 ) dirt simple.

Right now:

  • I  drain as much of the nutrients that are in the Coleman into a bucket then dispose of the nutrients:
Good better best I am practicing sustainability.  In the future, I will explore using organic fertilizers.  For now, I mix chemicals.  One step at a time.  
  • Clean out the bucket and mix fresh nutrients using my most awesome pH/EC meter to measure in the Jack’s Hydro nutes I use.  I target an EC of around 3 mS/cm and a pH of around 6.1.
Good better bestI will evolve the pH and EC circuit / probe solution I did with the Ladybug Blue Lite and Ladybug Shield (there are previous posts on these projects) to measure and then automate the injection of the nutrients as well as pH UP/DOWN as needed.
  • Get the freshly mixed nutrients into the Coleman.  This time around I tried using this siphon:


and siphon the water from the bucket into the Coleman through one of the net pot pipes (where the plants are).  Sadly, this didn’t work.  Still, I like the idea of not having to lift the lid off the coleman….

Good better best Need to figure out a better way to get the nutrients (or in the future water) from the bucket into the coleman using a pump.

Thinking about the relay – on the one hand, WHOA – how stupid of me to put the box so close to where water could go through the plugs!  On the other hand, it is one more day that gave me the opportunity to learn and solidify my knowledge about wiring relay boxes :-).