Hackster is hosting Hackster Holidays, Ep. 2: Livestream & Giveaway Drawing. Start streaming on Friday!Stream Hackster Holidays, Ep. 2 on Friday!
Kyle VanhornHayden Lambert
Published © GPL3+

Fish Tank Monitoring System

This fish tank monitoring system, based on the Particle Photon 2, transmits live tank parameters and sends alert when tank needs a cleaning.

IntermediateFull instructions provided2 hours217
Fish Tank Monitoring System

Things used in this project

Hardware components

Photon 2
Particle Photon 2
×1
Thermistor (NTC 10K/B3950 1%)
×1
CQRobot Ocean: TDS Meter
×1
Jumper wires (generic)
Jumper wires (generic)
×1
Breadboard (generic)
Breadboard (generic)
×1
Resistor 10k ohm
Resistor 10k ohm
×1

Software apps and online services

Particle Build Web IDE
Particle Build Web IDE
ThingSpeak API
ThingSpeak API
Maker service
IFTTT Maker service

Story

Read more

Schematics

Breadboard Circuit Diagram

Schematic

Code

Data Collection Code

Arduino
Code to run on Particle Photon 2 connect to fish tank sensors.
#include <math.h>

// Define Input Pins
const int tdsPin = A0;          // Analog pin connected to the TDS sensor
const int thermistorPin = A2;   // Analog pin connected to the thermistor

// Define the thermistor properties
const int referenceResistor = 9870;  // Resistance of the reference resistor in ohms
const int nominalResistor = 10000;   // Nominal resistance of the thermistor at 25 degrees Celsius
const int nominalTemperature = 25;   // Nominal temperature of the thermistor in degrees Celsius
const int betaCoefficient = 3950;    // Beta coefficient of the thermistor

// Initialize measurement variables
double temperature;
double TDS;

// Variables to log data with to find averages
float totalTDS = 0.0;
float totalTemperature = 0.0;
int samples = 0;


// Initialize fishTankStatus as good
String fishTankStatus = "Good!";


// API Key to send data to Thingspeak
String myWriteAPIKey = "API_KEY";

// Subscribe to the "FishTankStatus" event
void fishTankStatusHandler(const char *event, const char *data) {
  // Update the fish tank status
  fishTankStatus = data;

  // Check the status and take actions
  if (fishTankStatus != "Good!") {
    // Perform actions when the status is not good
    Particle.publish("clean_tank", "Fish tank needs cleaning!"); // Publish clean_tank event to trigger IFTTT notification
  }
}

void setup() {
  // Configure input pins as inputs
  pinMode(tdsPin, INPUT);
  pinMode(thermistorPin, INPUT);
  
  // Register the Particle variables
  Particle.variable("temperature", temperature);
  Particle.variable("TDS", TDS);
  
  // Subscribe to FishTankStatus 
  Particle.subscribe("FishTankStatus", fishTankStatusHandler, MY_DEVICES);
}

void loop() {
    
  // Thermistor Temperature Collection
  int rawADC = analogRead(thermistorPin); // Read the analog value from the thermistor
  float resistance = calculateResistance(rawADC); // Calculate the resistance of the thermistor
  temperature = calculateTemperature(resistance); // Calculate the temperature
  
  // TDS Sensor Data Collection
  float TDS_rawADC = analogRead(tdsPin);
  TDS = calculateTDS(TDS_rawADC, temperature);
  
  // Add values to total
  totalTemperature += temperature;
  totalTDS += TDS;
  samples ++;
  
  // Data collected every 1 sec, 600 samples is 10 minutes of data
  if (samples >= 600) {
      
      // Calculate averages
      float avgTDS = totalTDS / samples;
      float avgTemperature = totalTemperature / samples;
      
      // Publish Data
      Particle.publish("Temperature", String(avgTemperature)); // Publish Temperature Data to the Particle Cloud
      Particle.publish("TDS", String(avgTDS)); // Publish TDS data to the particle Cloud
      
      publishToThingSpeak(avgTDS, avgTemperature); // Publish data as a single message for Thingspeak
      
      // Reset Variables for the next 10 minutes
      totalTemperature = 0.0;
      totalTDS = 0.0;
      samples = 0;
  }
  
 

  delay(1000); // Delay for 1 second
}

float calculateTDS(int adcValue, float temp) {
    // Calcatule the TDS based on the voltage measured 
    float voltage = (adcValue / 4095.0) * 3.3; // Convert ADC value to voltage (assuming 12-bit ADC and 3.3V reference)
    float compensationCoefficient = 1.0 + 0.02 * (temp - 25.0);
    float compensationVoltage = voltage/compensationCoefficient;
    float tdsValue = (133.42 * compensationVoltage * compensationVoltage * compensationVoltage -255.86 * compensationVoltage * compensationVoltage + 857.39 * compensationVoltage) * 0.5;
    return tdsValue;
}

float calculateResistance(int adcValue) {
  // Calculate the resistance of the thermistor using the voltage divider formula
  float voltage = (adcValue / 4095.0) * 3.3; // Convert ADC value to voltage (assuming 12-bit ADC and 3.3V reference)
  float thermistorResistance = (referenceResistor * voltage) / (3.3 - voltage);
  return thermistorResistance;
}

float calculateTemperature(float resistance) {
  // Calculate the temperature using the Steinhart-Hart equation
  float steinhart;
  steinhart = resistance / nominalResistor;      // (R/Ro)
  steinhart = log(steinhart);                    // ln(R/Ro)
  steinhart /= betaCoefficient;                  // 1/B * ln(R/Ro)
  steinhart += 1.0 / (nominalTemperature + 273.15); // + (1/To)
  steinhart = 1.0 / steinhart;                   // Invert
  steinhart -= 273.15;                           // Convert to Celsius
  return steinhart;
}

void publishToThingSpeak(float avgTDS, float avgTemperature) {
    char msg[256]; 
    
    snprintf(msg, sizeof(msg), "{\"1\":\"%.2f\", \"2\":\"%.2f\", \"k\":\"%s\"}", avgTDS, avgTemperature, myWriteAPIKey.c_str());
    
    Particle.publish("data", msg);
}

Data Processing Code

Arduino
Code to run on separate Photon 2 to process TDS and Temperature Data
// Define variables to store received data
float totalTDS = 0.0;
float totalTemperature = 0.0;
int samples = 0;

// Subscribe to the "TDS" event
void tdsHandler(const char *event, const char *data) {
  // Convert the received data to a float
  float receivedTDS = atof(data);

  // Accumulate TDS values
  totalTDS += receivedTDS;
  samples++;
}

// Subscribe to the "Temperature" event
void temperatureHandler(const char *event, const char *data) {
  // Convert the received data to a float
  float receivedTemperature = atof(data);

  // Accumulate temperature values
  totalTemperature += receivedTemperature;
}

void setup() {
  // Subscribe to the "TDS" event
  Particle.subscribe("TDS", tdsHandler, MY_DEVICES);

  // Subscribe to the "Temperature" event
  Particle.subscribe("Temperature", temperatureHandler, MY_DEVICES);
}

void loop() {
    
    
  // Check if an hour has passed
  if (samples >= 6) {
    // Calculate average values
    float avgTDS = totalTDS / samples;
    float avgTemperature = totalTemperature / samples;

    // Print the hourly averages to Serial
    Serial.println("Hourly Averages:");
    Serial.print("TDS: ");
    Serial.println(avgTDS);
    Serial.print("Temperature: ");
    Serial.println(avgTemperature);
    
    Particle.publish("TempHourAvg", String(avgTemperature));
    Particle.publish("TDSHourAvg", String(avgTDS));

    // Check conditions and publish messages
    if (avgTDS > 600) {
      Particle.publish("FishTankStatus", "Fish tank needs cleaning!");
    }
    else {
        Particle.publish("FishTankStatus", "Good!");
    }

    if (avgTemperature > 25 || avgTemperature < 16) {
      Particle.publish("TemperatureStatus", "Check the fish tank temperature!");
    }
    else {
        Particle.publish("TemperatureStatus", "nominal");
    }

    // Reset variables for the next hour
    totalTDS = 0.0;
    totalTemperature = 0.0;
    samples = 0;
  }

  delay(30000); // Wait 30 seconds for the next data point
}

Credits

Kyle Vanhorn

Kyle Vanhorn

1 project • 1 follower
Hayden Lambert

Hayden Lambert

1 project • 1 follower

Comments