Hydroponics System Automate using Arduino and RPi with LoRa SX1278

image

It sometimes works, it loops through all the sensors and then once it comes back to the start the sending of data just stops. I tried chopping it up per sensor and all their individual codes work fine but once they're combined it just stops reading and sending the data to the Raspberry Pi.

Is there anyone who knows how LoRa SX1278 works?

There are volunteers on here that know how a LoRa SX1278.

If you want help, then you will need to post your code and detail which Arduino you are using etc ......

1 Like

I'm using an Arduino Uno R3 and my sensors are:

pH: PH-4502C
image

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

The UNO is a 5V logic level Arduino.

The Ra-02 Ai Thinker SX1278 is a 3.3V Logic level module I believe.

So what logic level conversion circuitry are you using between the UNO and the LoRa module ?

Long term you will find it a lot easier to use 3.3V Arduinos for a lot of modern sensors.

The LoRa is connected to the 3.3V pin of the Arduino board.

That does not turn the 5V logic level pins from the UNO into 3.3V logic level pins.

Do a Google search on 'Arduino logic level conversion' for details.

And on some UNOs, the 3.3V pin, cannot provide enough current to run a device such as a RF transmitter, you need a seperate 3.3V supply.

1 Like

Thank you for this, just ordered a Bi-directional logic level converter. Hopefully this fixes the problem I'm having. Really appreciate it!

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.