Using Arduino with Parts and Sensors – Plant monitoring & watering device (with micro SD card)

In the last project, I used a micro SD card slot DIP kit to work with an SD card. This time, we will write values to a micro SD card and combine temperature sensor, light sensor, and new soil sensor to enable monitoring and watering a plant on my desk. Will this be helpful? Let’s build it and find out!





















Expected time to complete: 120 minutes

Parts needed

Arduino Pro Mini

Ethernet Shield R3

Breadboard

CdS Sensor (light sensor)

Temperature Sensor (LM61)

Servomotor

Resistor(220Ω)

Nail x2

Pump nozzle

Simultaneous communication with a micro SD card via the Ethernet shield

There are several ways to use a micro SD card in Arduino-based projects. If you want to minimize the work, you can connect to the regular SD card easily by using an SD card shield, Ethernet shield, Arduino wireless SD shield, or any other shield that has an SD slot mounted on it.

This time, we’ll use the micro SD card adapter on the Ethernet shield introduced when we created a web server with Arduino.

It is very convenient because the shield has a micro SD connector and can also communicate via LAN cable.

Set up the Ethernet shield as shown in the linked article. As noted in Diagram 1 below, there are certain pins that can only be used with the LAN adapter and not with the SD card. So, we will have to build our circuit in the other pins.

Using the micro SD card via the Ethernet shield

There are several differences from what we did in the previous article, using micro SD card on the Ethernet shield. The micro SD card’s Pin 2 is used for recording data and is connected to Pin 10 on the Arduino. This is aligned with Pin 4 on the Ethernet shield.

Micro SD pin specification:

Now, let’s program some codes!

These codes read data from the SD card.

Code-Example #1 #include <SPI.h> #include <SD.h> Sd2Card card; SdVolume volume; SdFile root; const int chipSelect = 4; // set to pin 4 if using Ethernet void setup() { Serial.begin(9600); while (!Serial) { ; // wait for serial port to connect. Needed for Leonardo only } Serial.print("

Initializing SD card..."); pinMode(4, OUTPUT); // set to pin 4 if using Ethernet if (!card.init(SPI_HALF_SPEED, chipSelect)) { Serial.println("initialization failed. Things to check:"); Serial.println("* is a card is inserted?"); Serial.println("* Is your wiring correct?"); Serial.println("* did you change the chipSelect pin to match your shield or module?"); return; } else { Serial.println("Wiring is correct and a card is present."); } Serial.print("

Card type: "); switch (card.type()) { case SD_CARD_TYPE_SD1: Serial.println("SD1"); break; case SD_CARD_TYPE_SD2: Serial.println("SD2"); break; case SD_CARD_TYPE_SDHC: Serial.println("SDHC"); break; default: Serial.println("Unknown"); } if (!volume.init(card)) { Serial.println("Could not find FAT16/FAT32 partition.

Make sure you've formatted the card"); return; } uint32_t volumesize; Serial.print("

Volume type is FAT"); Serial.println(volume.fatType(), DEC); Serial.println(); volumesize = volume.blocksPerCluster(); // clusters are collections of blocks volumesize *= volume.clusterCount(); // we'll have a lot of clusters volumesize *= 512; // SD card blocks are always 512 bytes Serial.print("Volume size (bytes): "); Serial.println(volumesize); Serial.print("Volume size (Kbytes): "); volumesize /= 1024; Serial.println(volumesize); Serial.print("Volume size (Mbytes): "); volumesize /= 1024; Serial.println(volumesize); Serial.println("

Files found on the card (name, date and size in bytes): "); root.openRoot(volume); root.ls(LS_R | LS_DATE | LS_SIZE); } void loop( void ) {} 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 #include <SPI.h> #include <SD.h> Sd2Card card ; SdVolume volume ; SdFile root ; const int chipSelect = 4 ; // set to pin 4 if using Ethernet void setup ( ) { Serial . begin ( 9600 ) ; while ( ! Serial ) { ; // wait for serial port to connect. Needed for Leonardo only } Serial . print ( "

Initializing SD card..." ) ; pinMode ( 4 , OUTPUT ) ; // set to pin 4 if using Ethernet if ( ! card . init ( SPI_HALF_SPEED , chipSelect ) ) { Serial . println ( "initialization failed. Things to check:" ) ; Serial . println ( "* is a card is inserted?" ) ; Serial . println ( "* Is your wiring correct?" ) ; Serial . println ( "* did you change the chipSelect pin to match your shield or module?" ) ; return ; } else { Serial . println ( "Wiring is correct and a card is present." ) ; } Serial . print ( "

Card type: " ) ; switch ( card . type ( ) ) { case SD_CARD_TYPE_SD1 : Serial . println ( "SD1" ) ; break ; case SD_CARD_TYPE_SD2 : Serial . println ( "SD2" ) ; break ; case SD_CARD_TYPE_SDHC : Serial . println ( "SDHC" ) ; break ; default : Serial . println ( "Unknown" ) ; } if ( ! volume . init ( card ) ) { Serial . println ( "Could not find FAT16/FAT32 partition.

Make sure you've formatted the card" ) ; return ; } uint32_t volumesize ; Serial . print ( "

Volume type is FAT" ) ; Serial . println ( volume . fatType ( ) , DEC ) ; Serial . println ( ) ; volumesize = volume . blocksPerCluster ( ) ; // clusters are collections of blocks volumesize * = volume . clusterCount ( ) ; // we'll have a lot of clusters volumesize * = 512 ; // SD card blocks are always 512 bytes Serial . print ( "Volume size (bytes): " ) ; Serial . println ( volumesize ) ; Serial . print ( "Volume size (Kbytes): " ) ; volumesize /= 1024 ; Serial . println ( volumesize ) ; Serial . print ( "Volume size (Mbytes): " ) ; volumesize /= 1024 ; Serial . println ( volumesize ) ; Serial . println ( "

Files found on the card (name, date and size in bytes): " ) ; root . openRoot ( volume ) ; root . ls ( LS_R | LS_DATE | LS_SIZE ) ; } void loop ( void ) { }

I was able to read the contents of the micro SD card via the Ethernet shield.

Recording data from the temperature and the light sensors

Now that we have the micro SD card set up, let’s record data from the temperature and the light sensors. If you’ve forgotten how to use the temperature or the light sensors, please refer back to this article where we make a Stevenson screen.

You can see the wiring diagram below:

In the program, the data from the temperature and the light sensors is read using Analog Input and is written on the micro SD card. You set up the SD card using SD.open(“File name”, “Write mode”) from the Arduino SD card library. Once the file is open, the contents must be passed as a File variable and only then can reading or writing to the SD card occur. After all processing is complete, don’t forget to close the file with .close().

Code-Example #2: Writing temperature/light sensor data to the SD card #include <SPI.h> #include <SD.h> const int chipSelect = 4; void setup() { Serial.begin(9600); while (!Serial) { ; // wait for serial port to connect. Needed for Leonardo only } Serial.print("Initializing SD card..."); pinMode(4, OUTPUT); if (!SD.begin(chipSelect)) { Serial.println("Card failed, or not present"); return; } Serial.println("card initialized."); } void loop() { String dataString = ""; for (int analogPin = 0; analogPin < 2; analogPin++) { int sensor = analogRead(analogPin); if (analogPin == 0) { dataString += String(sensor); dataString += ","; } else if(analogPin == 1){ dataString += String(modTemp(sensor)); } } File dataFile = SD.open("DATALOG.TXT", FILE_WRITE); // file name if (dataFile) { // confirms the SD card file was successfully opened (creates a new one if not) dataFile.println(dataString); // writes the contents from dataString dataFile.close(); //closes the file after writing data Serial.println(dataString); } else { Serial.println("error opening datalog.txt"); } } // convert the temperature sensor data to Celsius float modTemp(int analog_val){ float tv = map(analog_val,0,1023,0,5000); // convert the sensor data to voltage float temp = map(tv,300,1600,-30,100) ; return temp; } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 #include <SPI.h> #include <SD.h> const int chipSelect = 4 ; void setup ( ) { Serial . begin ( 9600 ) ; while ( ! Serial ) { ; // wait for serial port to connect. Needed for Leonardo only } Serial . print ( "Initializing SD card..." ) ; pinMode ( 4 , OUTPUT ) ; if ( ! SD . begin ( chipSelect ) ) { Serial . println ( "Card failed, or not present" ) ; return ; } Serial . println ( "card initialized." ) ; } void loop ( ) { String dataString = "" ; for ( int analogPin = 0 ; analogPin < 2 ; analogPin ++ ) { int sensor = analogRead ( analogPin ) ; if ( analogPin == 0 ) { dataString += String ( sensor ) ; dataString += "," ; } else if ( analogPin == 1 ) { dataString += String ( modTemp ( sensor ) ) ; } } File dataFile = SD . open ( "DATALOG.TXT" , FILE_WRITE ) ; // file name if ( dataFile ) { // confirms the SD card file was successfully opened (creates a new one if not) dataFile . println ( dataString ) ; // writes the contents from dataString dataFile . close ( ) ; //closes the file after writing data Serial . println ( dataString ) ; } else { Serial . println ( "error opening datalog.txt" ) ; } } // convert the temperature sensor data to Celsius float modTemp ( int analog_val ) { float tv = map ( analog_val , 0 , 1023 , 0 , 5000 ) ; // convert the sensor data to voltage float temp = map ( tv , 300 , 1600 , - 30 , 100 ) ; return temp ; }

I successfully wrote the data.

Let’s build a plant monitoring device!

Now that we can write and read data from a micro SD card, let’s try to assemble a device!

Indoor plants need continuous care. Sometimes, watering your plants every day can get tedious. I’ve had numerous instances where I’d leave my plants and forget to water them for a long period of time and I would find my plants all droopy or even dead upon my arrival. Today, we’ll solve this problem by developing a watering machine!

Deciding on the design for the plant monitoring device!

First, let’s figure out the design of the device. We want to reduce the burden of watering plants so it’d be nice to have a soil sensor to detect when the soil is dry and automatically water the plant. Also, I want to read values from the temperature and the light sensors simultaneously and record them together on the SD card. We can also predict when the flowers will start blooming based on the accumulated temperature data. We can write a program that will make flower blooming predictions based on the data stored in the SD card. To summarize, we want to:

Detect soil dryness and water the plant

Record soil sensor, temperature sensor, and light sensor data in the SD card

Now, it’s time to build a device that will do these two things.

Making the soil sensor

First, let’s build a soil sensor to measure the soil’s dryness. There are prefabricated Arduino-compatible soil humidity sensors but we’re going to try something simpler in this article. The Arduino-compatible soil humidity sensor has two metal prongs that record a resistance value, which is the sensor reading. If the moisture content of the soil is high, current flows easily and the resistance value is low. Conversely, if the soil is dry, there is less electrical flow so the resistance value is high. When you understand this setup, you can test the this principle with 2 nails rather than using a special sensor.

*If you’re going to use this setup for a long period of time, iron nails will rust and the resistance value will change. In such case, it’s better to use the Arduino-compatible soil humidity sensor or rust proof nails.

Connect 2 nails to the conductive wires. These can be on the same circuit as the light sensor.

First, connect to Analog Pin 2 and read the soil sensor’s value with Analog Input. Next, fill the pot halfway with water. You can see that the value that was at 900-1000 lowers to around 600.

Since the resistance value drops as you add water, you can use these readings to determine the maximum resistance value. Again, high resistance value means the soil is dry. You can test and see what values you get by adding water.

Implementing the watering system

There are various ways to water your plants but I’ve used one that is simple and cost-effective. We can design a system that uses a simple pump nozzle from a bottle with a servo attached. It will be designed so that the servo will pump out the water by pressing the nozzle (Figure 14).

If you have a large plant that requires a lot of water, you can use a battery-powered oil pump. Depending on the size of your plant, there are all sorts of options you can try.

I bought 3 types of shampoo bottles to try. Since the servo I’m using isn’t very powerful, the servo failed to press down on two of the shampoo bottle nozzles. If your servo is weak, you can try to supply power from a separate battery and see if that works.

After much travail, I finally managed to put my watering system together (Figure 15).

Lastly, we need to write a program so we can save the soil sensor readings and water the plant whenever necessary. I set up a rule to pump 5 times whenever the resistance of the soil sensor is or above 700.

Code-Example #3 #include <SPI.h> #include <SD.h> #include <Servo.h> const int chipSelect = 4; Servo myservo; int pos = 0; int sensorValue = 0; // variable to store the value coming from the sensor int sensorPin = A0; void setup() { myservo.attach( 9 ); Serial.begin(9600); while (!Serial) { ; // wait for serial port to connect. Needed for Leonardo only } Serial.print("Initializing SD card..."); pinMode(4, OUTPUT); if (!SD.begin(chipSelect)) { Serial.println("Card failed, or not present"); return; } Serial.println("card initialized."); } void loop() { String dataString = ""; for (int analogPin = 0; analogPin < 3; analogPin++) { int sensor = analogRead(analogPin); if (analogPin == 0) { //光センサの値 dataString += "Light:"; dataString += String(sensor); dataString += ",Temp:"; } else if(analogPin == 1){ //温度センサの値 dataString += String(modTemp(sensor)); dataString += ",Ground:"; } else if(analogPin == 2){ //soil sensor value sensorValue = analogRead(sensorPin); dataString += String(sensorValue); // pump 5 times whenever the soul resistance value is above 700 if(sensorValue > 700){ for(int u=0;u<5;u++){ pos = 180; myservo.write( pos ); delay(1500); pos = 0; myservo.write( pos ); delay(1500); pos = 180; myservo.write( pos ); } } } } File dataFile = SD.open("datalog.txt", FILE_WRITE); if (dataFile) { dataFile.println(dataString); dataFile.close(); Serial.println(dataString); } else { Serial.println("error opening datalog.txt"); } } // Change the temperature sensor value to Celsius float modTemp(int analog_val){ float tv = map(analog_val,0,1023,0,5000); // Change the sensor value to voltage float temp = map(tv,300,1600,-30,100) ; return temp; } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 #include <SPI.h> #include <SD.h> #include <Servo.h> const int chipSelect = 4 ; Servo myservo ; int pos = 0 ; int sensorValue = 0 ; // variable to store the value coming from the sensor int sensorPin = A0 ; void setup ( ) { myservo . attach ( 9 ) ; Serial . begin ( 9600 ) ; while ( ! Serial ) { ; // wait for serial port to connect. Needed for Leonardo only } Serial . print ( "Initializing SD card..." ) ; pinMode ( 4 , OUTPUT ) ; if ( ! SD . begin ( chipSelect ) ) { Serial . println ( "Card failed, or not present" ) ; return ; } Serial . println ( "card initialized." ) ; } void loop ( ) { String dataString = "" ; for ( int analogPin = 0 ; analogPin < 3 ; analogPin ++ ) { int sensor = analogRead ( analogPin ) ; if ( analogPin == 0 ) { //光センサの値 dataString += "Light:"; dataString += String(sensor); dataString += ",Temp:"; } else if(analogPin == 1){ //温度センサの値 dataString += String(modTemp(sensor)); dataString += ",Ground:"; } else if(analogPin == 2){ //soil sensor value sensorValue = analogRead(sensorPin); dataString += String(sensorValue); // pump 5 times whenever the soul resistance value is above 700 if(sensorValue > 700){ for ( int u = 0 ; u < 5 ; u ++ ) { pos = 180 ; myservo . write ( pos ) ; delay ( 1500 ) ; pos = 0 ; myservo . write ( pos ) ; delay ( 1500 ) ; pos = 180 ; myservo . write ( pos ) ; } } } } File dataFile = SD . open ( "datalog.txt" , FILE_WRITE ) ; if ( dataFile ) { dataFile . println ( dataString ) ; dataFile . close ( ) ; Serial . println ( dataString ) ; } else { Serial . println ( "error opening datalog.txt" ) ; } } // Change the temperature sensor value to Celsius float modTemp ( int analog_val ) { float tv = map ( analog_val , 0 , 1023 , 0 , 5000 ) ; // Change the sensor value to voltage float temp = map ( tv , 300 , 1600 , - 30 , 100 ) ; return temp ; }

If you have any comments or questions, please leave them for us at Google +. Follow us there; we will be posting more soon.



















