The Micro:bit is great for small portable projects like a soil moisture sensor. In this tutorial, we will walk through how to program a Micro:bit to make a reliable and consistent soil moisture sensor that will give consistent readings. The goal of this project is to create a program that will give consistent readings between plants. We will be using MakeCode to program the Micro:bit.
To accomplish this project you will need the following parts:
- A Micro:bit
- SparkFun Soil Moisture Sensor (w/ terminal blocks)
- Alligator Clips to Jumper Pin
- Battery Pack (optional)
Making the Circuit
We will be using the SparkFun Soil Moisture Sensor with Terminal Blocks and Alligator clips to connect it to the Micro:bit. There are many different soil moisture sensors available and most will work with this setup, but this combination makes it easy to connect with no soldering.
The three pins on the moisture sensor are marked SIG, GND, and VCC. This indicates the signal output, ground, and power. We will connect the signal line to PIN 0 on the Micro:bit, the GND to GND and the VCC to 3.3V.
Downloading Code to the Micro:bit
To load a program onto the Micro:bit, first connect it to your computer with a micro USB cable. The Micro:bit will appear as a drive on your computer called MICROBIT.
When you are ready to try your code just click the DOWNLOAD button at the bottom left corner of the screen in MakeCode. Save the file inside the MICROBIT drive. If the download saves somewhere else, just drag and drop it into the MICROBIT drive.
The light on the Micro:bit will flash for a few seconds, and when it stops your program will be loaded and the Micro:bit will run it every time its powered up (until you replace it with a new program).
Display a Value When Button A is Pressed
This program will continuously take readings from the soil moisture sensor and display an image to represent how well watered the plant is. When button A is pressed it will display a number reading roughly between 1-100 that represents how wet the soil is so it can be recorded.
Button A Pressed: Does something when button A is pressed down and released again. This is a Bracket type block, which means that it executes whatever is inside it when its condition is met. In this case, the condition is Button A being pressed and released.
Variables are things remembered by the Micro:bit. They can be set to any word or number that you like. The bubble block will return a number, and the rectangular block will change the value of the variable selected. They keep the value of whatever is assigned to them but can be changed at any time. This value can then be accessed elsewhere in the program.
Analog read pin: This will read the connector pin as analog, it will return a value from 0 to 1023. An analog device gives data by changing the amount of voltage it returns on its data wire.
Map: Remaps the specified value from one range to another. This function maps the value of from low to the value of to low, the value of from high to the value of to high, and intermediate values to intermediate values.
This function does not constrain values to the ranges, because out-of-range values are sometimes intended and useful.
In this image, if 1023 was entered into the map, this block would return the number 4. If 512 was entered into the map the block would return the number 2.
The round function rounds the input number to the nearest whole number.
Show number: this block scrolls the number entered across the LED grid on the Micro:bit.
Clear screen: When you command the LEDs to turn on, they will stay on until you command them off again. This block turns all the LEDs off.
Putting the Blocks Together
All elements of code need to be inside a bracket for them to run. We will be using the Input: on button A pressed bracket. Drag and drop it anywhere in the workspace.
To take reliable measurements with the soil moisture sensor (and because it good programming practice) we will create a variable to store the readings from the sensor. We first need to make a variable. Name it “moistureReading”
Once the variable is created new blocks will appear to both call and change the value assigned to the variable. Place a Variable: set moistureReading to block inside the Input: on button A pressed
When we read the sensor it will return a number between 0 and 1023. To make the range a little easier to grasp we will use the Math: map block. This converts a number from one range to another. Place it inside of the Variable: set moistureReading to block.
To take a reading from our sensor we will use the Advanced: Pin: analog read pin P0 block. This will return a value between 0 and 1023 depending on how we the sensor is. Place it in the input portion of the Math: map block.
The soil moisture sensor is made to work with an input voltage of 3.3V to 5V. We will be powering it directly off of the 3.3V pin of the Micro:bit, and this means that no matter how we the soil is the sensor will only return enough voltage to read 750-800. Less voltage in means less voltage out. We can easily account for this in code. Change the from high portion of the range in the Math: map block to 750 to represent the highest number the sensor will return (this might vary slightly from sensor to sensor).
To make the output a number that is easier to understand, we will change the to high portion of the Math: map block to 100. This means that the measurements taken from the sensor will return a 0-100 number. 100 will be fully saturated, and 0 will be completely dry.
To display the numerical value of the reading from the sensor we will place the Basic: show number block at the bottom of our Input: on button A pressed block.
The value returned from the sensor may have a lot of decimal places, and we don’t need to see them all when we take a reading so we will use the Math: round block to make the reading a whole number.
To display the reading we have taken us the Variables: moistureReading call bubble to return the variable that we stored when taking a reading.
Whenever an LED is commanded to turn on in code, it will stay on until commanded to turn off again. We will put the Basic: more: clear screen block at the bottom of our code to turn the screen off after each reading is finished being shown.
Now we have a completed block of code! Download it to your Micro:bit and test it out!
Display an Image to Indicate Readings
Sometimes we don’t need to know the exact numerical value of the water level in the soil, and we just want a quick reference to see if the plant needs to be watered or not.
Additional Blocks Used:
Forever: The forever loop is the most used block in code. The blocks contained within this bracket will be run from top to bottom, and then repeated for as long as the Micro:bit is powered. You will find a loop in almost all programs.
Show LEDs: This block allows you to design your own image to display on the Micro:bit LEDs. Once it is in place you can click the blocks to change them on or off.
Pause: This block is used to pause the program for a set amount of time. It’s easy to forget that the processor in the Micro:bit makes 16 Million changes per second. A program runs far faster than we can perceive, we need to tell the Micro:bit to slow down enough for us to see the results! The time of the pause is in milliseconds. So 1000 = 1 second.
If-then: Logic blocks are what sets a program apart from basic electronics. They allow the program to make decisions! The If-then block has a diamond shape conditional field, that responds to a True or False input. If True then the code inside the brackets will be run, if False then the else portion of the brackets will be run.
Comparison: This block will return a True or False value, it’s commonly used within the If-then block. Placing a variable in one of the value bubbles of this block will compare it to the other number. All diamond blocks return a True or False value.
And: This block will return a True or False value. The and block will only return True if it receives a True from both of its fields.
Putting the Blocks Together
The first part of this code we will take a reading from the sensor, just like with the button press. Either work through the first steps 3-6 of the last section again or just copy the code from the past section. Place a Basic: forever block on the workspace if there is not one already. Right click on the Variable: set moistureReading to block and select Duplicate. Drag the duplicated section of code into the Basic: forever block.
We could keep the mapped range at 0-100, but for the sake of simplicity, let's change the to high value to 3. We will be displaying three different images to indicate the moisture level.
Next, we will insert a short pause using the Basic: pause block. Leave the time to 100 milliseconds
Now add a Logic: if-then block. This will control what image is displayed.
In the condition field of the Logic: if-then block place a comparison block.
Within the Logic: comparison block place the Variables: moistureReading bubble in the first field, change the comparison to less-than-or-equal-to (<=), and the final field to 1. When complete this portion of code will return True whenever the moisture reading is less-than-or-equal-to 1.
We want to check for two conditions rather than just one. Click the plus at the bottom of the Logic: if-then block to add a second condition.
In the second condition field, add a Logic: and block. Place a Logic: comparison block in each field of the Logic: and block, and format them to be moistureReading is greater-than 1, and moistureReading is less-than-or-equal-to 2.
Now place the Basic: show leds blocks into each of the fields in the Logic: if-then block. If the condition of the first field is met that means that the soil is dry, if the second is met then it is somewhat wet, if neither condition is met then the final field will be executed. The final field will represent fully wet. We don’t need to make a comparison statement for the third option because only three results are possible, and we already know it’s not the first or second.
Now the just create an image of your choice for each field that represents the soil moisture level. We’ve made a pot that is either empty, partially full, or full of water.
Now you can download the code and load it onto your Micro:bit. Both sections of code we created can run at the same time on the same Micro:bit. When the Micro:bit is powered the images will constantly display, and when Button A is pressed they will be replaced by the number value of the reading.
If you want to take this project a bit further, you can add the option to calibrate the sensor. One of the shortfalls of this code is that we are assuming that the sensor will return a maximum of 750. The following code includes the option to press the B button to calibrate the sensor. Simply place the sensor in fully wet soil or water, then press the B button. The code will then save the maximum wetness reading and use it in the rest of the code.