I'm using an Arduino Uno R3 and my sensors are:
pH: PH-4502C
Temperature: DS18B20
Waterflow: YF-S401
TDS (Total Dissolved Solids): SEN0244
the LoRa that I'm using is this: Ra-02 Ai Thinker SX1278
all connected with the use of jumper wires into the Arduino.
I also have 3 motors that are connected to each of their own relay modules.
Here is my overall code:
// imports library
#include <Arduino.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <SPI.h> //Import SPI librarey
#include <RH_RF95.h> // RF95 from RadioHead Library
#define RFM95_RST 9 //RST of Lora connected to pin 9
// // Change to 434.0 or other frequency, must match RX's freq!
#define RF95_FREQ 433.0
// Singleton instance of the radio driver
RH_RF95 rf95;
// Water Pump
const int pumpPin = A5; // initialize digital pin A5 for pump control
// pH Motors
int motorUp = 8;
int motorDown = 6;
// pH Sensor
int pHSense = 7;
int samples = 10;
float adc_resolution = 1024.0;
// Temperature Sensor
const int SENSOR_PIN = 3; // Arduino pin connected to DS18B20 sensor's DQ pin
OneWire oneWire(SENSOR_PIN); // setup a oneWire instance
DallasTemperature tempSensor(&oneWire); // pass oneWire to DallasTemperature library
float tempCelsius; // temperature in Celsius
float tempFahrenheit; // temperature in Fahrenheit
// TDS Sensor
#define TdsSensorPin A3
#define VREF 5.0 // analog reference voltage(Volt) of the ADC
#define SCOUNT 30 // sum of sample point
int analogBuffer[SCOUNT]; // store the analog value in the array, read from ADC
int analogBufferTemp[SCOUNT];
int analogBufferIndex = 0,copyIndex = 0;
float averageVoltage = 0,tdsValue = 0, temperature = 25;
// Waterflow Sensor
int sensorPin = 5;
volatile long pulse;
unsigned long lastTime;
float volume;
void setup() {
// Water Pump
pinMode(pumpPin, OUTPUT);
// Set baud rate
Serial .begin(9600);
delay(100);
// pH sensor
Serial.println("Hydroponics Project");
// pH Motors
pinMode(motorUp, OUTPUT); //sets the digital pin as output
pinMode(motorDown, OUTPUT); //sets the digital pin as output
// Temperature Sensor
tempSensor.begin(); // initialize the sensor
// TDS Sensor
pinMode(TdsSensorPin, INPUT);
// Waterflow Sensor
pinMode(sensorPin, INPUT);
attachInterrupt(digitalPinToInterrupt(sensorPin), increase, RISING);
// LoRa
pinMode(RFM95_RST, OUTPUT);
digitalWrite(RFM95_RST, LOW);
delay(10);
digitalWrite(RFM95_RST, HIGH);
delay(10);
Serial.println("Arduino LoRa TX Test!");
// Initialize LoRa Module
while (!rf95.init()) {
Serial.println("LoRa radio init failed");
while (1);
}
while(!rf95.setFrequency(RF95_FREQ)){
Serial.println("LoRa radio FREQ failed");
while (1);
}
Serial.print("Set Freq to: "); Serial.println(RF95_FREQ);
rf95.setTxPower(23,false);
}
// pH Sensor and Motors
float ph(float voltage) {
return 7 + ((2.5 - voltage) / 0.18);
}
#define STABLE_PH_DURATION 300000 // 5 minutes in milliseconds
#define LOWER_PH_THRESHOLD 5.5 // Modify this value as needed
#define HIGHER_PH_THRESHOLD 6.5 // Modify this value as needed
bool motorActivated = false;
unsigned long timerStart = 0;
bool phStable = false;
void resetTimer() {
timerStart = millis();
}
void loop() {
waterPumpFunction();
phSensorFunctionHigh();
phSensorFunctionLow();
tempSensorFunction();
tdsSensorFunction();
waterFlowSensorFunction();
}
// Water pump
void waterPumpFunction(){
// W A T E R P U M P
digitalWrite(pumpPin, HIGH); // send signal from pin 9 to circuit
delay(0);
digitalWrite(pumpPin, LOW);
delay(5000);
}
// End of Water pump
// pH Sensor
void phSensorFunctionHigh() {
int measurings = 0;
for (int i = 0; i < samples; i++) {
measurings += analogRead(pHSense);
delay(500);
}
float voltage = 5 / adc_resolution * measurings / samples;
float currentPH = ph(voltage);
Serial.print("pH = ");
Serial.println(currentPH);
// Check if pH is lower than thresholds
if (currentPH < LOWER_PH_THRESHOLD) {
if (!motorActivated) {
motorFunctionPhUp(); // Activate the motor
motorActivated = true;
resetTimer();
}
} else {
motorActivated = false;
}
// Check if pH has been stable for 5 minutes
if (millis() - timerStart >= STABLE_PH_DURATION && motorActivated) {
phStable = true;
// Stop motor activation
motorActivated = false;
}
// Check if pH becomes unstable again
if (phStable && (currentPH < LOWER_PH_THRESHOLD)) {
phStable = false;
resetTimer();
}
// Lora sending values of pH to Raspberry Pi
String finalph = "ph=" + String(currentPH);
loRaSender(finalph);
}
void phSensorFunctionLow(){
int measurings = 0;
for (int i = 0; i < samples; i++) {
measurings += analogRead(pHSense);
delay(500);
}
float voltage = 5 / adc_resolution * measurings / samples;
float currentPH = ph(voltage);
Serial.print("pH = ");
Serial.println(currentPH);
// Check if pH is higher than threshold
if (currentPH > HIGHER_PH_THRESHOLD) {
if (!motorActivated) {
motorFunctionPhDown(); // Activate the motor
motorActivated = true;
resetTimer();
}
} else {
motorActivated = false;
}
// Check if pH has been stable for 5 minutes
if (millis() - timerStart >= STABLE_PH_DURATION && motorActivated) {
phStable = true;
// Stop motor activation
motorActivated = false;
}
// Check if pH becomes unstable again
if (phStable && (currentPH > HIGHER_PH_THRESHOLD)) {
phStable = false;
resetTimer();
}
// Lora sending values of pH to Raspberry Pi
String finalph = "ph=" + String(currentPH);
loRaSender(finalph);
}
void motorFunctionPhUp(){
Serial.println("Trigger the peristaltic to dose the nutrient");
digitalWrite(motorUp,HIGH); //turns the LED on
delay(3000);
digitalWrite(motorUp,LOW);
delay(3000);
}
void motorFunctionPhDown(){
Serial.println("Trigger the peristaltic to dose the nutrient");
digitalWrite(motorDown,HIGH); //turns the LED on
delay(3000);
digitalWrite(motorDown,LOW);
delay(3000);
}
// end of pH Sensor & pH Motors
// Temperature Sensor
void tempSensorFunction(){
// T E M P S E N S O R
unsigned long previousMillis = 0;
const long interval = 1000; // Interval in milliseconds
unsigned long currentMillis = millis();
tempSensor.requestTemperatures(); // send the command to get temperatures
tempCelsius = tempSensor.getTempCByIndex(0); // read temperature in Celsius
tempFahrenheit = tempCelsius * 9 / 5 + 32; // convert Celsius to Fahrenheit
Serial.print("Temperature= ");
Serial.print(tempCelsius); // print the temperature in Celsius
Serial.print("°C");
Serial.print(" ~ "); // separator between Celsius and Fahrenheit
Serial.print(tempFahrenheit); // print the temperature in Fahrenheit
Serial.println("°F");
String tempString = "Temperature="+String(tempCelsius)+"~"+String(tempFahrenheit);
loRaSender(tempString);
}
// end of Temperature Sensor
// TDS Sensor
void tdsSensorFunction()
{
static unsigned long analogSampleTimepoint = millis();
if (millis()-analogSampleTimepoint > 40U) //every 40 milliseconds,read the analog value from the ADC
{
analogSampleTimepoint = millis();
analogBuffer[analogBufferIndex] = analogRead(TdsSensorPin); //read the analog value and store into the buffer
analogBufferIndex++;
if (analogBufferIndex == SCOUNT)
analogBufferIndex = 0;
}
static unsigned long printTimepoint = millis();
if (millis()-printTimepoint > 800U)
{
printTimepoint = millis();
for (copyIndex = 0; copyIndex < SCOUNT; copyIndex++)
analogBufferTemp[copyIndex] = analogBuffer[copyIndex];
averageVoltage = getMedianNum(analogBufferTemp, SCOUNT) * (float)VREF / 1024.0; // read the analog value more stable by the median filtering algorithm, and convert to voltage value
float compensationCoefficient = 1.0 + 0.02 * (temperature - 25.0); //temperature compensation formula: fFinalResult(25^C) = fFinalResult(current)/(1.0+0.02*(fTP-25.0));
float compensationVolatge = averageVoltage / compensationCoefficient; //temperature compensation
tdsValue = (133.42 * compensationVolatge * compensationVolatge * compensationVolatge - 255.86 * compensationVolatge * compensationVolatge + 857.39 * compensationVolatge) * 0.5; //convert voltage value to tds value
Serial.print("TDS Value=");
Serial.print(tdsValue, 0);
Serial.println("ppm");
}
// Lora sending values of TDS value to Rasberry pi
String tdsString = "TDS value="+String(tdsValue);
loRaSender(tdsString);
}
int getMedianNum(int bArray[], int iFilterLen)
{
int bTab[iFilterLen];
for (byte i = 0; i < iFilterLen; i++)
bTab[i] = bArray[i];
int i, j, bTemp;
for (j = 0; j < iFilterLen - 1; j++)
{
for (i = 0; i < iFilterLen - j - 1; i++)
{
if (bTab[i] > bTab[i + 1])
{
bTemp = bTab[i];
bTab[i] = bTab[i + 1];
bTab[i + 1] = bTemp;
}
}
}
if ((iFilterLen & 1) > 0)
bTemp = bTab[(iFilterLen - 1) / 2];
else
bTemp = (bTab[iFilterLen / 2] + bTab[iFilterLen / 2 - 1]) / 2;
return bTemp;
}
// End of TDS Sensor
// Waterflow Sensor
void waterFlowSensorFunction() {
volume = pulse * 60 / 7.5;
String literString = "";
if (millis() >= (lastTime + 1000)) {
pulse = 0;
lastTime = millis();
literString = "L/hour="+String(volume);
}
Serial.print("L/hour=");
Serial.println(volume);
loRaSender(literString);
}
void increase() {
pulse++;
}
// End of Waterflow Sensor
// LoRa Sender & Receiver
void loRaSender(String message){
char* messageString = message.c_str();
// if(rf95.available()){
// // previousMillis = currentMillis;
rf95.available();
Serial.println(rf95.send((uint8_t*)messageString, strlen(messageString)));
Serial.println(rf95.waitPacketSent());
rf95.sleep();
rf95.setModeIdle();
delay(3000);
// Serial.println("LoRa sent to rasberry pi server sucessfully");
}
void loRaReceiver(){
if (rf95.available())
{
// Should be a message for us now
uint8_t buf[RH_RF95_MAX_MESSAGE_LEN];
uint8_t len = sizeof(buf);
if (rf95.recv(buf, &len))
{
RH_RF95::printBuffer("Received: ", buf, len);
Serial.print("Got: ");
Serial.println((char*)buf);
Serial.print("RSSI: ");
Serial.println(rf95.lastRssi(), DEC);
}
else
{
Serial.println("Receive failed");
}
}
}
// End of LoRa Sender & Receiver