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

IMG_3735.jpeg

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

IMG_3711.jpeg

· 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

IMG_3743.jpeg

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

IMG_3739.jpeg
IMG_3738.jpeg

Set up

2) Using the Arduino Uno attach the SD module on top and add the SD shield on top again

Wiring the Components

IMG_3741.jpeg

Set Up

3) DHT11 Temperature and Humidity sensor into D2 (digital) and VCC-> 5V to breadboard and GND à GND to breadboard

Wiring the Components

IMG_3742.jpeg

Set Up

4) Button digital pin D3

Wiring the Components

IMG_3744.jpeg

Set Up

5) Turbidity sensor into the SD shield in A0 (analog) and VCC -> 5V to breadboard and GND -> GND breadboard

Wiring the Components

IMG_3745.jpeg

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

IMG_3746.jpeg
IMG_3748.jpeg

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:

  1. DHT temperature + humidity sensor
  2. Turbidity + TDS analog inputs
  3. Button and chainable RGB LED
  4. SD card CS pin

Global Variables

  1. dht — Reads air temperature and humidity
  2. leds — RGB chainable LED indicator
  3. bucketType — CHANGING TEXT field for substrate (Control / Clay / Sand / Gravel)
  4. timeOfDay — CHANGING TEXT field for sampling period (Morning_1, Night_2, etc.)
  5. logging — Tracks if device is actively collecting
  6. readingCount — Ensures only 10 readings per run once per minute

Setup()

  1. Starts Serial + sensors
  2. Initializes the SD card
  3. Creates a CSV file and writes a header row
  4. Turns the LED green to show the logger is ready

Loop()

  1. Waits for button press to start logging
  2. When pressed:
  3. Changes LED to orange
  4. Begins reading sensors
  5. Collects 10 data points, saving each to SD as a new CSV row
  6. After 10 readings:
  7. Logging stops
  8. LED returns to green

collectReading()

  1. Reads temperature (°C), humidity (%), TDS, and turbidity(%)
  2. Opens the CSV file
  3. Writes one row:
  4. Bucket_Type,Time_of_Day,Temp_C,Humidity_%,TDS_ppm,Turbidity
  5. Closes the file


Upload the Code

IMG_3711.jpeg

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

IMG_3533.jpeg
IMG_3708.jpeg

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.