CircuitPython Code

Are you new to using CircuitPython? No worries, there is a full getting started guide here.

To edit the CircuitPython code and receive realtime data from our sensors in the REPL, Adafruit suggests using the Mu Editor. You can learn about Mu and installation in this tutorial.

Open up the Mu editor or an editor of your choice with a REPL. (REPL = read-evaluate-print-loop, and it's what we'll need to use to access the values of our moisture and temperature levels so we can calibrate our sensors).

Import Libraries and Initialize Values

We'll begin the program by importing the necessary libraries and initialize the variables we'll be using.

If you think you might be missing a library, you'll be covered by installing the whole library package. Find out how in the CircuitPython Essentials Guide on CircuitPlayground Libraries.

  • DRY_VALUE  and WET_VALUE should be calibrated and will be slightly different for everyone. We'll talk about how to calibrate this later.
  • tempThreshhold is in Celcius and is the minimum temperature needed for ideal compost conditions. Feel free to change this to room temperature when testing the prototype to make sure everything works. Don't forget to change back to 43!
Download: file
# Author: Isaac Wellish 
# Code adapted from Tony Dicola's CircuitPython code on using the DS18x20 temperature sensor-
# as well as John Park's CircuitPython code on determining soil moisture from nails

from adafruit_onewire.bus import OneWireBus
from adafruit_ds18x20 import DS18X20
import time
import board 
import simpleio
import touchio
import neopixel
import analogio
from simpleio import map_range


#Initialize neopixels
pixels = neopixel.NeoPixel(board.NEOPIXEL, 10, brightness=.1)

#set variables for capacitive touch inputs, later used for soil moisture variables
touch = touchio.TouchIn(board.A1)
touch2 = touchio.TouchIn(board.A2)
 
DRY_VALUE = 3100  # calibrate this by hand!
WET_VALUE = 4000  # calibrate this by hand!
tempThreshhold = 43 #celius temperature of threshold for ideal compost temperature

 
# Initialize one-wire bus on board pin A3.
ow_bus = OneWireBus(board.A3)
 
# Scan for sensors and grab the first one found.
ds18 = DS18X20(ow_bus, ow_bus.scan()[0])

#Initialize the light senor on board to use for neopixel brightness later
light = analogio.AnalogIn(board.LIGHT)

The Main Program Loop: Finding the Average Soil Moisture & Temperature Levels

  • To have a more accurate representation of what the moisture level is in the compost, we place the nails in separate locations in the compost and take the average of both moisture readings.
  • Print average moisture and temperature levels.
Download: file
# Main loop 
while True:
    
    ###SOIL MOISTURE READINGS
    
    #set variables for capacitive touch inputs for nails to take in soil moisture levels
    value_A1 = touch.raw_value
    value_A2 = touch2.raw_value
    
    #take the average of both moisture levels
    avgMoist = value_A1 + value_A2 / 2
    print("Moisture level:",(avgMoist,))
 
    
 
    ###TEMPERATURE READINGS
    
    #variable for temperature
    compostTemp = ds18.temperature
    
    #print the temperature
    print('Temperature: {0:0.3f}C'.format(compostTemp))

Determining the State of the Compost with NeoPixels

  • We will be using the on board NeoPixels as visual feedback for the information the temperature and moisture sensors are giving us. The colors let us know how to best help the compost. 
  • Ideally we would throw in a wifi module and make the project Internet of Things friendly to be able to see the real sensor data without having to look at a box on the compost bin but I wanted to keep this project a little more simple. I will most likely be adding IoT functionality to this project later!
  • When the compost is not hot enough and too dry, the colors yellow and red will be displayed alternating. This color combination means you must add food scraps to counterbalance the dryness. You must also turn the compost to mix in the new food scraps as well as give the organisms inside some oxygen so they can better breakdown the compost, releasing heat and increasing the temperature.
  • When the compost is not hot enough and too wet, the colors yellow and blue will be displayed alternating. This color combination means you must add dry carbon-based material like leaves and wood chips to counterbalance the wet stuff. You must turn the compost as well for the same reasons as above.
  • When the compost is not hot enough but at the right moisture level (so close!), the colors yellow and green will be displayed alternating. This color combination means the moisture is at the ideal level however the compost isn't hot enough. Give the compost a couple turns to bring some oxygen to those hungry organisms in there!
  • When the compost is at the ideal temperature level, regardless of moisture, all the NeoPixels will be green. We have ideal conditions! This means you don't have to do anything! 

Compost Condition Indication with If Statements

Download: file
###IF STATEMENTS TO DETERMINE STATE OF COMPOST

    
    #RED & YELLOW = TOO COLD & TOO DRY
    if((compostTemp<tempThreshhold) and (avgMoist<DRY_VALUE)):
        pixels[0] = (255,0,0) # red
        pixels[1] = (255,255,0) #yellow
        pixels[2] = (255,0,0)
        pixels[3] = (255,255,0)
        pixels[4] = (255,0,0)
        pixels[5] = (255,255,0)
        pixels[6] = (255,0,0)
        pixels[7] = (255,255,0)
        pixels[8] = (255,0,0)
        pixels[9] = (255,255,0)
        
        print("Not hot enough, too dry")
    
    #BLUE & YELLOW = TOO COLD & TOO WET
    elif((compostTemp<tempThreshhold) and (avgMoist>WET_VALUE)):
        pixels[0] = (0,0,255) # blue
        pixels[1] = (255,255,0) #yellow
        pixels[2] = (0,0,255)
        pixels[3] = (255,255,0)
        pixels[4] = (0,0,255)
        pixels[5] = (255,255,0)
        pixels[6] = (0,0,255)
        pixels[7] = (255,255,0)
        pixels[8] = (0,0,255)
        pixels[9] = (255,255,0)
        print("Not hot enough, too wet")
        
    #GREEN & YELLOW = TOO COLD & MOISTURE LEVEL OPTIMUM
    elif((compostTemp<tempThreshhold) and (avgMoist >DRY_VALUE and avgMoist<WET_VALUE)):
        pixels[0] = (0,255,0) # green
        pixels[1] = (255,255,0) #yellow
        pixels[2] = (0,255,0)
        pixels[3] = (255,255,0)
        pixels[4] = (0,255,0)
        pixels[5] = (255,255,0)
        pixels[6] = (0,255,0)
        pixels[7] = (255,255,0)
        pixels[8] = (0,255,0)
        pixels[9] = (255,255,0)
        print("Not hot enough, right moisture level")
        
    #ALL GREEN = COMPOST AT OPTIMUM TEMPERATURE & MOISTURE
    elif(compostTemp>tempThreshhold):
        pixels.fill((0,255,0))# green
        print("Compost Ready")

Using The Light Sensor to Dim or Brighten NeoPixels

  • To decrease the amount of power needed for running this program on the CPX (Circuit Playground Express) and make it easier on the eyes to see the NeoPixel colors, we will use the on board light sensor to brighten the NeoPixels when it's brighter out and dim the NeoPixels when it's darker out.
  • We'll use a mapping function to map the range of the light sensor to the range of NeoPixel brightness.
  • We then use that mapped value as the level of brightness for the NeoPixels to show on the board.
  • Lastly we pause the program for three seconds to save power. We don't need to be updating the program and sensor data any more than this. If you'd like to save power even more, feel free to change this amount of time!

*It would make sense to show the NeoPixels ("pixels.show()") at the end of the program but for some reason the NeoPixels would only update correctly if the show() function was executed before updating the brightness level. This works fine with a three second pause in between while loop runs but will become more and more faulty once the update time is greatly increased. The brightness should update well enough as long as the update time is less than a minute or so. Please let us know if you have figured out a better fix!

Download: file
###LIGHTING CONFIGURATION
    
    #print value of light sensor
    print((light.value,))
    
    #map light snesor range to neopixel brightness range
    peak = map_range(light.value, 2000, 62000, 0.01, 0.3)
    
    
    #print neopixel brightness levels
    print(peak)
    
    #show neopixels
    pixels.show()
    
    #update neopixel brightness based on level of exposed light
    pixels = neopixel.NeoPixel(board.NEOPIXEL, 10, brightness=peak)
    
    #pause for three seconds
    time.sleep(3)
    
    
    
###END PROGRAM

Here's the full program:

# Author: Isaac Wellish
# Code adapted from Tony Dicola's CircuitPython code using the DS18x20 temp sensor
# as well as John Park's CircuitPython code determining soil moisture from nails

import time
from adafruit_onewire.bus import OneWireBus
from adafruit_ds18x20 import DS18X20
import board
import touchio
import neopixel
import analogio
from simpleio import map_range

# Initialize neopixels
pixels = neopixel.NeoPixel(board.NEOPIXEL, 10, brightness=.1)

# set variables for capacitive touch inputs, later used for soil moisture variables
touch = touchio.TouchIn(board.A1)
touch2 = touchio.TouchIn(board.A2)

DRY_VALUE = 3100  # calibrate this by hand!
WET_VALUE = 4000  # calibrate this by hand!
tempThreshhold = 43 #celius temperature of threshold for ideal compost temperature

# Initialize one-wire bus on board pin A3.
ow_bus = OneWireBus(board.A3)

# Scan for sensors and grab the first one found.
ds18 = DS18X20(ow_bus, ow_bus.scan()[0])

# Initialize the light senor on board to use for neopixel brightness later
light = analogio.AnalogIn(board.LIGHT)

# Main loop
while True:

    # SOIL MOISTURE READINGS

    # set capacitive touch inputs for nails to take in soil moisture levels
    value_A1 = touch.raw_value
    value_A2 = touch2.raw_value

    # take the average of both moisture levels
    avgMoist = value_A1 + value_A2 / 2
    print("Moisture level:",(avgMoist,))

    # TEMPERATURE READINGS

    # variable for temperature
    compostTemp = ds18.temperature

    # print the temperature
    print('Temperature: {0:0.3f}C'.format(compostTemp))

    # IF STATEMENTS TO DETERMINE STATE OF COMPOST

    # RED & YELLOW = TOO COLD & TOO DRY
    if((compostTemp<tempThreshhold) and (avgMoist<DRY_VALUE)):
        pixels[0] = (255,0,0)    # red
        pixels[1] = (255,255,0)  # yellow
        pixels[2] = (255,0,0)
        pixels[3] = (255,255,0)
        pixels[4] = (255,0,0)
        pixels[5] = (255,255,0)
        pixels[6] = (255,0,0)
        pixels[7] = (255,255,0)
        pixels[8] = (255,0,0)
        pixels[9] = (255,255,0)

        print("Not hot enough, too dry")

    # BLUE & YELLOW = TOO COLD & TOO WET
    elif((compostTemp<tempThreshhold) and (avgMoist>WET_VALUE)):
        pixels[0] = (0,0,255)    # blue
        pixels[1] = (255,255,0)  # yellow
        pixels[2] = (0,0,255)
        pixels[3] = (255,255,0)
        pixels[4] = (0,0,255)
        pixels[5] = (255,255,0)
        pixels[6] = (0,0,255)
        pixels[7] = (255,255,0)
        pixels[8] = (0,0,255)
        pixels[9] = (255,255,0)
        print("Not hot enough, too wet")

    # GREEN & YELLOW = TOO COLD & MOISTURE LEVEL OPTIMUM
    elif((compostTemp<tempThreshhold) and (avgMoist >DRY_VALUE and avgMoist<WET_VALUE)):
        pixels[0] = (0,255,0)    # green
        pixels[1] = (255,255,0)  # yellow
        pixels[2] = (0,255,0)
        pixels[3] = (255,255,0)
        pixels[4] = (0,255,0)
        pixels[5] = (255,255,0)
        pixels[6] = (0,255,0)
        pixels[7] = (255,255,0)
        pixels[8] = (0,255,0)
        pixels[9] = (255,255,0)
        print("Not hot enough, right moisture level")

    # ALL GREEN = COMPOST AT OPTIMUM TEMPERATURE & MOISTURE
    elif compostTemp > tempThreshhold:
        pixels.fill((0,255,0))  # green
        print("Compost Ready")

    # LIGHTING CONFIGURATION

    # print value of light sensor
    print((light.value,))

    # map light snesor range to neopixel brightness range
    peak = map_range(light.value, 2000, 62000, 0.01, 0.3)

    # print neopixel brightness levels
    print(peak)

    # show neopixels
    pixels.show()

    # update neopixel brightness based on level of exposed light
    pixels = neopixel.NeoPixel(board.NEOPIXEL, 10, brightness=peak)

    # pause for three seconds
    time.sleep(3)

# END PROGRAM

Enough Programming, Let's Test it Out!

This guide was first published on Aug 29, 2018. It was last updated on Aug 29, 2018. This page (CircuitPython Code) was last updated on Oct 23, 2019.