Sumit Kumar
Published © MIT

AquaMon - Aquaponics and Fish Tank Monitoring with Tuya IoT

Marine species are extremely sensitive to changes in environmental conditions. AquaMon IoT device and app helps your fishes to grow better.

BeginnerFull instructions provided6 hours7,261
AquaMon - Aquaponics and Fish Tank Monitoring with Tuya IoT

Things used in this project

Hardware components

Arduino UNO
Arduino UNO
×1
NodeMCU ESP8266 Breakout Board
NodeMCU ESP8266 Breakout Board
×1
Adafruit Waterproof DS18B20 Digital temperature sensor
Adafruit Waterproof DS18B20 Digital temperature sensor
×1
Seeed Studio Grove - PH Sensor Kit (E-201C-Blue )
×1
Seeed Studio Grove - TDS Sensor/Meter For Water Quality (Total Dissolved Solids)
×1
SparkFun Low Current Lithium Ion Battery Pack - 2.5Ah (USB)
×1
Breadboard (generic)
Breadboard (generic)
×1
Jumper wires (generic)
Jumper wires (generic)
×1

Software apps and online services

Arduino IDE
Arduino IDE
Tuya IoT Platform
Tuya_TYDA_Install_V2.4.0

Hand tools and fabrication machines

Tape, Double Sided
Tape, Double Sided
Soldering iron (generic)
Soldering iron (generic)

Story

Read more

Schematics

Circuit

Working

Code

AquaMon Firmware

Arduino
/**
 * Project: AquaMon (Aquarium Monitoing to help you understand your marine friends needs)
 * Author: Sumit
 **/

#include <OneWire.h>
#include <DallasTemperature.h>
#include <TuyaWifi.h>
#include <SoftwareSerial.h>

// Data wire is plugged into digital pin 2 on the Arduino
#define ONE_WIRE_BUS 2
// Setup a oneWire instance to communicate with any OneWire device
OneWire oneWire(ONE_WIRE_BUS);  
// Pass oneWire reference to DallasTemperature library
DallasTemperature sensors(&oneWire);

#define PHPin A2           //pH meter Analog output to Arduino Analog Input 0
#define Offset 41.02740741      //deviation compensate
#define samplingInterval 20
#define publishInterval 500
#define ArrayLenth  40    //times of collection

int pHArray[ArrayLenth];   //Store the average value of the sensor feedback
int pHArrayIndex = 0;

#define TDSPin A3

SoftwareSerial conn(10, 11); // RX, TX
TuyaWifi my_device(&conn);
/* Current LED status */
unsigned char led_state = 0;
/* Connect network button pin */
int key_pin = 7;

/* Data point define */
#define DPID_Switch 101
#define DPID_Ph_Sensor_Data 112
#define DPID_TDS_Sensor_Data 113
#define DPID_Water_Temperature 114
/* Stores all DPs and their types. PS: array[][0]:dpid, array[][1]:dp type. 
 *                                     dp type(TuyaDefs.h) : DP_TYPE_RAW, 
 *                                     DP_TYPE_BOOL, DP_TYPE_VALUE, DP_TYPE_STRING, DP_TYPE_ENUM, DP_TYPE_BITMAP
*/
unsigned char dp_array[][2] =
{
  {DPID_Switch, DP_TYPE_BOOL},
  {DPID_Ph_Sensor_Data, DP_TYPE_VALUE},
  {DPID_TDS_Sensor_Data, DP_TYPE_VALUE},
  {DPID_Water_Temperature, DP_TYPE_VALUE},
};

unsigned char pid[] = {"2dhjbzgya8mk2sv3"};
unsigned char mcu_ver[] = {"3.1.4"};

/* last time */
unsigned long last_time = 0;
int phVal = 0;
int tdsVal = 0;
int wTemp = 0;

void setup()
{  
  Serial.begin(9600);
  conn.begin(9600);
  sensors.begin();  // Start up the library
  //Initialize networking keys.
  pinMode(key_pin, INPUT_PULLUP);
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, LOW);

  //Enter the PID and MCU software version
  my_device.init(pid, mcu_ver);
  //incoming all DPs and their types array, DP numbers
  my_device.set_dp_cmd_total(dp_array, 4);
  //register DP download processing callback function
  my_device.dp_process_func_register(dp_process);
  //register upload all DP callback function
  my_device.dp_update_all_func_register(dp_update_all);
  delay(300);
  last_time = millis();  
}

void loop()
{
  my_device.uart_service();
  
  //Enter the connection network mode when Pin7 is pressed.
  if (digitalRead(key_pin) == LOW) {
    delay(80);
    if (digitalRead(key_pin) == LOW) {
      my_device.mcu_set_wifi_mode(SMART_CONFIG);
    }
  }
  /* LED blinks when network is being connected */
  if ((my_device.mcu_get_wifi_work_state() != WIFI_LOW_POWER) && 
  (my_device.mcu_get_wifi_work_state() != WIFI_CONN_CLOUD) && 
  (my_device.mcu_get_wifi_work_state() != WIFI_SATE_UNKNOW)) {
    if (millis()- last_time >= 500) {
      last_time = millis();

      if (led_state == LOW) {
        led_state = HIGH;
      } else {
        led_state = LOW;
      }
      digitalWrite(LED_BUILTIN, led_state);
    }
  }

  phVal = calcPhVal();
  tdsVal = calcTDSVal();
  wTemp = waterTemp();

  /* report the pH, TDS and temperature data */
  if ((my_device.mcu_get_wifi_work_state() == WIFI_CONNECTED) || (my_device.mcu_get_wifi_work_state() == WIFI_CONN_CLOUD)) 
  {
    my_device.mcu_dp_update(DPID_Switch, true, 1);
    my_device.mcu_dp_update(DPID_Ph_Sensor_Data, phVal, 1);
    my_device.mcu_dp_update(DPID_TDS_Sensor_Data, tdsVal, 1);
    my_device.mcu_dp_update(DPID_Water_Temperature, wTemp, 1);
  }
  delay(500);  
}

/**
 * @description: DP download callback function.
 * @param {unsigned char} dpid
 * @param {const unsigned char} value
 * @param {unsigned short} length
 * @return {unsigned char}
 */
unsigned char dp_process(unsigned char dpid,const unsigned char value[], unsigned short length){
  /* all DP only report, if you want to take actions based upon user interactions with app modify dp_process*/
  return SUCCESS;
}

/**
 * @description: Upload all DP status of the current device.
 * @param {*}
 * @return {*}
 */
void dp_update_all(void)
{
  my_device.mcu_dp_update(DPID_Switch, true, 1);
  my_device.mcu_dp_update(DPID_Ph_Sensor_Data, phVal, 1);
  my_device.mcu_dp_update(DPID_TDS_Sensor_Data, tdsVal, 1);
  my_device.mcu_dp_update(DPID_Water_Temperature, wTemp, 1);
}

double avergearray(int* arr, int number) {
  int i;
  int max, min;
  double avg;
  long amount = 0;
  if (number <= 0) {
    //Serial.println("Error number for the array to avraging!/n");
    return 0;
  }
  if (number < 5) { //less than 5, calculated directly statistics
    for (i = 0; i < number; i++) {
      amount += arr[i];
    }
    avg = amount / number;
    return avg;
  } else {
    if (arr[0] < arr[1]) {
      min = arr[0]; max = arr[1];
    }
    else {
      min = arr[1]; max = arr[0];
    }
    for (i = 2; i < number; i++) {
      if (arr[i] < min) {
        amount += min;      //arr<min
        min = arr[i];
      } else {
        if (arr[i] > max) {
          amount += max;  //arr>max
          max = arr[i];
        } else {
          amount += arr[i]; //min<=arr<=max
        }
      }//if
    }//for
    avg = (double)amount / (number - 2);
  }//if
  return avg;
}

double calcPhVal()
{
  static unsigned long samplingTime = millis();
  static unsigned long publishTime = millis();
  static float pHValue, voltage;
  if (millis() - samplingTime > samplingInterval)
  {
    pHArray[pHArrayIndex++] = analogRead(PHPin);
    if (pHArrayIndex == ArrayLenth)pHArrayIndex = 0;
    voltage = avergearray(pHArray, ArrayLenth) * 5.0 / 1024;
    pHValue = -19.18518519 * voltage + Offset;
    samplingTime = millis();
  }
  if (millis() - publishTime > publishInterval)  //Every 800 milliseconds, publish value
  {
     Serial.print("Voltage:");
     Serial.print(voltage, 2);
     Serial.print("    pH value: ");
     Serial.println(pHValue, 2);
    publishTime = millis();
    return pHValue;
  }
}

double calcTDSVal()
{
    int sensorValue = analogRead(TDSPin);
    float Voltage = sensorValue*5/1024.0; //Convert analog reading to Voltage
    float tdsValue=(133.42/Voltage*Voltage*Voltage - 255.86*Voltage*Voltage + 857.39*Voltage)*0.5; //Convert voltage value to TDS value
        Serial.print("TDS Value = "); 
        Serial.print(tdsValue);
        Serial.println(" ppm");
    delay(100);
    return tdsValue;    
}

double waterTemp()
{
  sensors.requestTemperatures(); 
  float wTemp = sensors.getTempCByIndex(0);
  Serial.print("temp: ");
  Serial.println(wTemp);
  delay(100);
  return wTemp;
}

Credits

Sumit Kumar

Sumit Kumar

32 projects • 94 followers
19 y/o. My daily routine involves dealing with electronics, code, distributed storage and cloud APIs.

Comments