Evaluating Inorganic Substrates in Mesocosms Using an Arduino Data Logger
by crava7 in Circuits > Arduino
149 Views, 1 Favorites, 0 Comments
Evaluating Inorganic Substrates in Mesocosms Using an Arduino Data Logger
Have you ever wanted to build your own environmental data-logging system? This project will guide you through how to create a fully functional mesocosm water-quality data logger using an Arduino, temperature/humidity sensor, TDS probe, and turbidity sensor. With the press of a button, the system collects measurements and records them to an SD card, making it simple to monitor water conditions over time.
Unlike many tutorials that only show basic sensor reading, this project integrates structured data collection, on-board logging, and visual status feedback using an RGB indicator LED. Measurements such as temperature (°C), humidity (%), turbidity (%), and TDS (ppm) can be gathered at regular intervals or upon user command. All sensor readings are automatically stored as rows in a .CSV file, allowing for easy import into programs like Excel or R for further data analysis.
Supplies
· Arduino Uno
· DHT11 Temperature/Humidity Sensor
· DF-Robot-style Turbidity Sensor (A0)
· TDS Water-quality sensor (A2)
· SD-card module
· SD-card sheild
· MicroSD card
· SD-card
· Push button (logging trigger)
· Grove Chainable LED (status display of temp)
· 15 Wires
· Breadboard
· USB cable
· Power source (laptop)
· Mac Adapter (if using Mac)
· Buckets
· Inorganic Substrates (clay, gravel, sand)
· Water (control)
Wiring the Components
Set Up
TIP BEFORE WIRING: SD-card module goes to CS pin → digital pin D4 (do not put any other sensors into the pin the CS is using) for some can be CS pin -> digital pin 10
1) VCC and GND running from the shield to the breadboard
Wiring the Components
Set up
2) Using the Arduino Uno attach the SD module on top and add the SD shield on top again
Wiring the Components
Set Up
3) DHT11 Temperature and Humidity sensor into D2 (digital) and VCC-> 5V to breadboard and GND à GND to breadboard
Wiring the Components
Set Up
4) Button digital pin D3
Wiring the Components
Set Up
5) Turbidity sensor into the SD shield in A0 (analog) and VCC -> 5V to breadboard and GND -> GND breadboard
Wiring the Components
Set Up
6) TDS sensor into the SD shield in A2 (analog) and VCC -> 5V to breadboard and GND-> GND to breadboard
Wiring the Components
Set Up
7) Chainable LED for data digital pin D8, clock digital pin D9, VCC -> 5V to breadboard and GNDàGND to breadboard
Coding the Arduino
//libraries needed
#include <DHT.h>
#include <SPI.h>
#include <SD.h>
#include <ChainableLED.h>
//assigned pins
#define DHTPIN 2
#define DHTTYPE DHT11
#define BUTTON_PIN 3
#define CSPIN 4 //CSPIN for Arduino is 4 sometimes can be 10
#define TURBIDITYPIN A0
#define TDSPIN A2
DHT dht(DHTPIN, DHTTYPE);
ChainableLED leds(8, 9, 1);
File dataFile;
//set for calibration
float tdsOffset = 0.0;
float turbidityClearV = 0.0;
//button ON
bool lastButtonState = HIGH;
void setup() {
Serial.begin(9600); //need this
dht.begin(); //need this
pinMode(BUTTON_PIN, INPUT_PULLUP);
Serial.println("Initializing SD..."); //tells me its talking to the SD card
if (!SD.begin(CSPIN)) {
Serial.println("SD card init failed!"); //tells me if there is an error
while (1);
}
Serial.println("SD card initialized!"); //tells me its ready
dataFile = SD.open("data.csv", FILE_WRITE); //open csv
delay(50);
if (dataFile) {
dataFile.println("Bucket_Type,Day,Time_of_Day,Temp_C,Humidity_%,TDS_ppm,Turbidity_%"); //creates header
dataFile.close(); //always close
delay(50); //delay to get it to catch up
}
//TDS calibration
int rawTDS = analogRead(TDSPIN);
tdsOffset = rawTDS * 5.0 / 1024.0;
//Turbidity calibration
Serial.println("Place sensor in clear water for turbidity calibration...");
delay(5000);
int rawTurb = analogRead(TURBIDITYPIN);
turbidityClearV = rawTurb * 5.0 / 1023.0;
Serial.println("Setup complete! Press button to start logging."); //tells me to press button after calibration is done
}
void loop() {
bool current = digitalRead(BUTTON_PIN); //waits for button to be on
//button
if (lastButtonState == HIGH && current == LOW) {
Serial.println("Button pressed -> Starting data collection"); //starts data logging after button is pressed
//change these based on bucket I'm collecting from VERY IMPORTANT
collectData("Sand", 5, "Morning_2");
Serial.println("Data collection complete!"); //after 10 readings says data collection complete
}
lastButtonState = current;
delay(20);
}
//function for data collection
void collectData(String bucketType, int day, String timeLabel) {
float sumTemp = 0, sumHum = 0, sumTDS = 0, sumTurb = 0;
for (int i = 1; i <= 10; i++) {
float temp = dht.readTemperature();
float hum = dht.readHumidity();
if (isnan(temp) || isnan(hum)) {
Serial.println("DHT read failed");
delay(60000);
continue;
}
//Chainable LED
if (temp < 25) leds.setColorHSB(0.3, 0.5, 1.0, 0.5); //color is blue if < 25 TempC
else if (temp < 35) leds.setColorHSB(0.15, 0.9, 1.0, 0.5); //color orange is if < 35 TempC
else leds.setColorHSB(0, 0.9, 1.0, 0.5); // color is red if any other TempC
//Read turbidity and convert voltage to %
int rawTurb = analogRead(TURBIDITYPIN);
float voltage = rawTurb * 5.0 / 1023.0;
float perc_turbidity = (1.0 - (voltage / turbidityClearV)) * 100.0;
perc_turbidity = constrain(perc_turbidity, 0, 100);
//TDS voltage to ppm
int rawTDS = analogRead(TDSPIN);
float tdsVoltage = rawTDS * 5.0 / 1024.0 - tdsOffset;
if (tdsVoltage < 0) tdsVoltage = 0;
float tdsValue = (133.42 * pow(tdsVoltage, 3)
- 255.86 * pow(tdsVoltage, 2)
+ 857.39 * tdsVoltage) * 0.5;
//Talk to SD card named csv data
dataFile = SD.open("data.csv", FILE_WRITE);
delay(50);
//printing to csv
if (dataFile) {
dataFile.print(bucketType); dataFile.print(",");
dataFile.print(day); dataFile.print(",");
dataFile.print(timeLabel); dataFile.print(",");
dataFile.print(temp, 2); dataFile.print(",");
dataFile.print(hum, 2); dataFile.print(",");
dataFile.print(tdsValue, 2); dataFile.print(",");
dataFile.println(perc_turbidity, 2);
dataFile.flush(); //force to write to SD
delay(50);
dataFile.close(); //always close
delay(50);
} else {
Serial.println("SD ERROR"); //tell me if error
}
// have to collect sums of each group before take average
sumTemp += temp;
sumHum += hum;
sumTDS += tdsValue;
sumTurb += perc_turbidity;
Serial.print("Reading ");
Serial.println(i);
delay(60000); // wait 1 minute
}
//Write averages to SD
dataFile = SD.open("data.csv", FILE_WRITE);
delay(50);
if (dataFile) {
dataFile.print(bucketType); dataFile.print(",");
dataFile.print(day); dataFile.print(",");
dataFile.print(timeLabel); dataFile.print(",");
dataFile.print(sumTemp / 10.0, 2); dataFile.print(",");
dataFile.print(sumHum / 10.0, 2); dataFile.print(",");
dataFile.print(sumTDS / 10.0, 2); dataFile.print(",");
dataFile.println(sumTurb / 10.0, 2);
dataFile.flush(); //force write
delay(50);
dataFile.close(); //always close!!!
delay(50);
}
Serial.println("Averages saved!"); //tell me they are saved
}
//can unplug once you see averages saved
Includes & Defines
Required sensor and SD card libraries are included. Pin assignments are defined for:
- DHT temperature + humidity sensor
- Turbidity + TDS analog inputs
- Button and chainable RGB LED
- SD card CS pin
Global Variables
- dht — Reads air temperature and humidity
- leds — RGB chainable LED indicator
- bucketType — CHANGING TEXT field for substrate (Control / Clay / Sand / Gravel)
- timeOfDay — CHANGING TEXT field for sampling period (Morning_1, Night_2, etc.)
- logging — Tracks if device is actively collecting
- readingCount — Ensures only 10 readings per run once per minute
Setup()
- Starts Serial + sensors
- Initializes the SD card
- Creates a CSV file and writes a header row
- Turns the LED green to show the logger is ready
Loop()
- Waits for button press to start logging
- When pressed:
- Changes LED to orange
- Begins reading sensors
- Collects 10 data points, saving each to SD as a new CSV row
- After 10 readings:
- Logging stops
- LED returns to green
collectReading()
- Reads temperature (°C), humidity (%), TDS, and turbidity(%)
- Opens the CSV file
- Writes one row:
- Bucket_Type,Time_of_Day,Temp_C,Humidity_%,TDS_ppm,Turbidity
- Closes the file
Upload the Code
1) Connect your Arduino to your laptop using a USB cable.
2) Open the Arduino IDE.
3) Copy and paste the data-logger code into a new sketch.
4) Before uploading, update the Bucket_Type and Time_of_Day labels in the code so your data file will be correctly tagged for that run.
5) From the Tools menu, select the correct board (e.g., Arduino Nano / Nano Every) and COM port.
6) Click the Upload button to send the code to the Arduino.
7) Once the upload is complete, the device is ready for testing and data collection.
Testing Your Data Logger
After uploading the code, keep the Arduino connected to your laptop. This will be its only power source during data collection.
Once powered, the system will automatically initialize the sensors and SD card. The calibration of the sensors will happen automatically before data collection so have some clear water nearby. The LED will indicate that the logger is ready.
Before starting a new run, be sure to update the Bucket_Type (Control, Clay, Sand, Gravel) and Time_of_Day (Morning_1, Morning_2, Night_1, Night_2) in the code so your output file is properly labeled.
Press the button to begin data collection.
The logger will automatically take 10 readings—>recording temperature (°C), humidity (%), TDS (ppm), and turbidity(%) and save them to a CSV file on the SD card. It will also tell you in the serial monitor that the readings are being taken and tell you when averages and data collection is complete.
While logging is active, the LED will change color to show that the device is collecting data. When the 10 readings are complete, the data file will close automatically, and the LED will return to its standby state.
At this point you can disconnect the Arduino from your computer, remove the SD card, and open the CSV file to view your measurements.