Awesome! LoRa Soil sensor

I think @thebang2 may be confusing your repo for the official openmqtt gateway repo.

@kamaradclimber & @nickrout : I take it all back, you are right of course - the code is not in the current repository of OpenMQTTGateway. I will try this again, sorry for the inconvenience! :roll_eyes:

Wohooo, it works! :partying_face: Thanks @kamaradclimber , the buggy code was the reason. I also changed the code for sensor version V3 - I’m not the best programmer either (in fact not even a good one), so I mixed the original code from makerfabs and the code from @Refuge in void send_lora()
:

→ Lora Soil Moisture Sensor V3:

#include <SPI.h>
#include <Wire.h>
#include <RadioLib.h>
#include <avr/wdt.h>
#include <avr/sleep.h>
#include "I2C_AHT10.h"
//#include <LoRa.h>
#include <ArduinoJson.h>

#define NODENAME "LORA_POWER_1"
String node_id = String("ID") + "010442";

//Set sleep time, when value is 1 almost sleep 20s,when value is 450, almost 1 hour.
//#define SLEEP_CYCLE 450
#define SLEEP_CYCLE 38    //5 Minuten

//Lora set
//Set Lora frequency
// #define FREQUENCY 434.0
#define FREQUENCY 868.0
//#define FREQUENCY 915.0

#define BANDWIDTH 125.0
#define SPREADING_FACTOR 9
#define CODING_RATE 7
#define OUTPUT_POWER 10
#define PREAMBLE_LEN 8
#define GAIN 0

//328p
#define DIO0 2
#define DIO1 6

#define LORA_RST 4
#define LORA_CS 10

#define SPI_MOSI 11
#define SPI_MISO 12
#define SPI_SCK 13

//pin set
#define VOLTAGE_PIN A3
#define PWM_OUT_PIN 9
#define SENSOR_POWER_PIN 5
#define ADC_PIN A2

#define DEBUG_OUT_ENABLE 1

SX1276 radio = new Module(LORA_CS, DIO0, LORA_RST, DIO1);
AHT10 humiditySensor;

String jsonoutput = "";  // string für json-übertragung
bool readSensorStatus = false;
int sensorValue = 0; // variable to store the value coming from the sensor
int batValue = 0;    // the voltage of battery
int count = 0;
int ADC_O_1;           // ADC Output First 8 bits
int ADC_O_2;           // ADC Output Next 2 bits
int16_t packetnum = 0; // packet counter, we increment per xmission
float temperature = 0.0;
float humidity = 0.0;

bool AHT_init()
{
  bool ret = false;
  Wire.begin();
  if (humiditySensor.begin() == false)
  {
#if DEBUG_OUT_ENABLE
    Serial.println("AHT10 not detected. Please check wiring. Freezing.");
#endif
  }

  if (humiditySensor.available() == true)
  {
    temperature = humiditySensor.getTemperature();
    humidity = humiditySensor.getHumidity();
    ret = true;
  }
  if (isnan(humidity) || isnan(temperature))
  {
#if DEBUG_OUT_ENABLE
    Serial.println(F("Failed to read from AHT sensor!"));
#endif
  }
  return ret;
}
void Lora_init()
{
  int state = radio.begin(FREQUENCY, BANDWIDTH, SPREADING_FACTOR, CODING_RATE, SX127X_SYNC_WORD, OUTPUT_POWER, PREAMBLE_LEN, GAIN);
  Serial.println(state);
  if (state == ERR_NONE)
  {
#if DEBUG_OUT_ENABLE
    Serial.println(F("success!"));
#endif
  }
  else
  {
#if DEBUG_OUT_ENABLE
    Serial.print(F("failed, code "));
    Serial.println(state);
#endif
    // while (true)
    //     ;
  }
}
void setup()
{
#if DEBUG_OUT_ENABLE
  Serial.begin(115200);
  Serial.println("Soil start.");
  Serial.print(FREQUENCY);
  Serial.println(" Mhz");

#endif
  delay(100);

  // set up Timer 1
  pinMode(PWM_OUT_PIN, OUTPUT);

  TCCR1A = bit(COM1A0);            // toggle OC1A on Compare Match
  TCCR1B = bit(WGM12) | bit(CS10); // CTC, scale to clock
  OCR1A = 1;

  pinMode(LORA_RST, OUTPUT);
  digitalWrite(LORA_RST, HIGH);
  delay(100);

  pinMode(SENSOR_POWER_PIN, OUTPUT);
  digitalWrite(SENSOR_POWER_PIN, HIGH); //Sensor power on
  delay(100);

  Lora_init();

  Wire.begin();
  if (humiditySensor.begin() == false)
  {

#if DEBUG_OUT_ENABLE
    Serial.println("AHT10 not detected. Please check wiring. Freezing.");
#endif
  }
#if DEBUG_OUT_ENABLE
  else
    Serial.println("AHT10 acknowledged.");
#endif

  do_some_work();
  //setup over
#if DEBUG_OUT_ENABLE
  Serial.println("[Set]Sleep Mode Set");
#endif
  low_power_set();
}

void loop()
{
  wdt_disable();

  if (count > SLEEP_CYCLE) //(7+1) x 8S  450
  {
#if DEBUG_OUT_ENABLE
    //code start
    Serial.println("Code start>>");
#endif

    do_some_work();
    all_pins_low();

#if DEBUG_OUT_ENABLE
    //code end
    Serial.println("Code end<<");
#endif
    //count init
    count = 0;
  }

  low_power_set();
}

ISR(WDT_vect)
{
#if DEBUG_OUT_ENABLE
  Serial.print("[Watch dog]");
  Serial.println(count);
#endif
  delay(100);
  count++;
  //wdt_reset();
  wdt_disable(); // disable watchdog
}

//Set low power mode and into sleep
void low_power_set()
{
  all_pins_low();
  delay(10);
  // disable ADC
  ADCSRA = 0;

  sleep_enable();
  watchdog_init();
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  delay(10);
  noInterrupts();
  sleep_enable();

  // turn off brown-out enable in software
  MCUCR = bit(BODS) | bit(BODSE);
  MCUCR = bit(BODS);
  interrupts();

  sleep_cpu();
  sleep_disable();
}

//Enable watch dog
void watchdog_init()
{
  // clear various "reset" flags
  MCUSR = 0;
  // allow changes, disable reset
  WDTCSR = bit(WDCE) | bit(WDE);
  WDTCSR = bit(WDIE) | bit(WDP3) | bit(WDP0); // set WDIE, and 8 seconds delay
  wdt_reset();                                // pat the dog
}

void do_some_work()
{

  digitalWrite(SENSOR_POWER_PIN, HIGH); // Sensor/RF95 power on
  digitalWrite(LORA_RST, HIGH);
  delay(5);
  pinMode(PWM_OUT_PIN, OUTPUT);    //digitalWrite(PWM_OUT_PIN, LOW);
  TCCR1A = bit(COM1A0);            // toggle OC1A on Compare Match
  TCCR1B = bit(WGM12) | bit(CS10); // CTC, scale to clock
  OCR1A = 1;                       // compare A register value (5000 * clock speed / 1024).When OCR1A == 1, PWM is 2MHz

  Lora_init();
  delay(50);

  //ADC2  AVCC as reference voltage
  ADMUX = _BV(REFS0) | _BV(MUX1);

  //ADC2  internal 1.1V as ADC reference voltage
  //ADMUX = _BV(REFS1) |_BV(REFS0) | _BV(MUX1);

  // 8  分频
  ADCSRA = _BV(ADEN) | _BV(ADPS1) | _BV(ADPS0);
  delay(50);
  for (int i = 0; i < 3; i++)
  {
    //start ADC conversion
    ADCSRA |= (1 << ADSC);

    delay(10);

    if ((ADCSRA & 0x40) == 0)
    {
      ADC_O_1 = ADCL;
      ADC_O_2 = ADCH;

      sensorValue = (ADC_O_2 << 8) + ADC_O_1;
      ADCSRA |= 0x40;

      //Mod for value from in 0 to 100
      // 1000 = Air dry, 500 ultrawet
      sensorValue = (sensorValue - 500) / 5;
      // End

#if DEBUG_OUT_ENABLE
      Serial.print("ADC:");
      Serial.println(sensorValue);
#endif

      if (readSensorStatus == false)
        readSensorStatus = AHT_init();
    }
    ADCSRA |= (1 << ADIF); //reset as required
    delay(50);
  }

  //ADC3  internal 1.1V as ADC reference voltage
  ADMUX = _BV(REFS1) | _BV(REFS0) | _BV(MUX1) | _BV(MUX0);

  delay(50);
  for (int i = 0; i < 3; i++)
  {
    //start ADC conversion
    ADCSRA |= (1 << ADSC);

    delay(10);

    if ((ADCSRA & 0x40) == 0)
    {
      ADC_O_1 = ADCL;
      ADC_O_2 = ADCH;

      batValue = (ADC_O_2 << 8) + ADC_O_1;
      ADCSRA |= 0x40;

#if DEBUG_OUT_ENABLE
      Serial.print("BAT:");
      Serial.println(batValue);
      float bat = (float)batValue * 3.3;
      bat = bat / 1024.0;
      Serial.print(bat);
      Serial.println("V");
#endif
    }
    ADCSRA |= (1 << ADIF); //reset as required
    delay(50);
  }
  send_lora();
  delay(1000);
  radio.sleep();

  packetnum++;
  readSensorStatus = false;
  digitalWrite(SENSOR_POWER_PIN, LOW); // Sensor/RF95 power off
  delay(100);
}

void all_pins_low()
{
  pinMode(PWM_OUT_PIN, INPUT);
  pinMode(A4, INPUT_PULLUP);
  pinMode(A5, INPUT_PULLUP);

  delay(50);
}

void send_lora()
{

  // Create json object for transfer
  DynamicJsonDocument root(256);
  root["node_id"] = node_id;
  root["hum"] = (String)humidity;
  root["temp"] = (String)temperature;
  root["adc"] = (String)sensorValue;
  root["bat"] = (String)batValue;

  serializeJson(root, jsonoutput);
#if DEBUG_OUT_ENABLE
  serializeJson(root, Serial);
#endif
  radio.transmit(jsonoutput);
jsonoutput = "";
}

→ HomeAssistant Sensor.yaml:

# Beispielstring {"rssi":-81,"snr":13.25,"pferror":-5714,"packetSize":76,"message":"{\"node_id\":\"ID010442\",\"hum\":\"47.21\",\"temp\":\"26.46\",\"adc\":\"930\",\"bat\":\"1023\"}"}
    - platform: mqtt
      name: "ID010442_humidity"
      state_topic: "home/OpenMQTTGateway/LORAtoMQTT"
      value_template: >-
        {% if (value_json.message|from_json).node_id == "ID010442" %}
          {{ (value_json.message|from_json).hum }}
        {% else %}
          {{ is_state_attr("sensor.soil_01_Humidity") | round(2) }}
        {% endif %}
      unit_of_measurement: '%'
      icon: mdi:water-percent

    - platform: mqtt
      name: "ID010442_temperature"
      state_topic: "home/OpenMQTTGateway/LORAtoMQTT"
      value_template: >-
        {% if (value_json.message|from_json).node_id == "ID010442" %}
          {{ (value_json.message|from_json).temp }}
        {% else %}
          {{ is_state_attr("sensor.soil_01_Temperature") | round(2) }}
        {% endif %}
      unit_of_measurement: '°C'

    - platform: mqtt
      name: "ID010442_ADC"
      state_topic: "home/OpenMQTTGateway/LORAtoMQTT"
      value_template: >-
        {% if (value_json.message|from_json).node_id == "ID010442" %}
          {{ (value_json.message|from_json).adc }}
        {% else %}
          {{ is_state_attr("sensor.soil_01_adc") }}
        {% endif %}
      unit_of_measurement: 'µS/cm'
      icon: mdi:flower

    - platform: mqtt
      name: "ID010442_BAT"
      state_topic: "home/OpenMQTTGateway/LORAtoMQTT"
      value_template: >-
        {% if (value_json.message|from_json).node_id == "ID010442" %}
          {{ (value_json.message|from_json).bat }}
        {% else %}
          {{ is_state_attr("sensor.soil_01_battery") }}
        {% endif %}
      unit_of_measurement: 'V'
      icon: mdi:battery

I´ve modified the code in that way, that the value for moisture is in a range from 0 to 100.
Next step is to merge the thre custom sensors in HomeAssistant to one with multiple attributes.

Thanks for updating the code for V3. I am having problems compiling it.
I keep getting this error and I am not sure what’s the culprit.

Any idea how to get it to work? Which versions of the libraries are you using?

Try Radiolib 4.6.0 .
I have edited the code today, please be sure to use this one shown above.

I just updated the code and the library. It is finally working!
I had to also update ArduinoJson to the latest available version.

However, now I am having problems with open mqtt gateway as it seems the soil sensor is not communicating at all with the esp32. They are both the same frequency but and I have patched it to include @kamaradclimber’s fix.

Any idea on what could be the problem? I have tried multiple things and the soil sensor is not communicating to the gateway no matter what I do!

My patch has been merged upstream yesterday so you can go back to official repository (on development branch).

2 Likes

Hello thebang2, i use your code. The first transmission is okay. With the second transmission i have an issue {“node_id”:“ID010214”,“hum”:“38.08”,“temp”:“27.36”,“adc”:“73”,“bat”:“808”} {“node_id”:“ID010214”,“hum”:“34.84”,“temp”:“26.36”,“adc”:“73”, the values comes double.
The first line are the values from the first transmission. Any Idea ?

Thanks Flowermike

Forgot that first, code above is updated.


String jsonoutput = "";

I have tried everything I could, changed gateways to no avail. For some reason the soil sensor is not communicating with the gateway at all. I am sure they are the same frequency and I have tried multiple devices/sensors. Can someone help me please?
Discovery is in the gateway is set to True. Soil sensor is working fine (via serial connection) but it seems that the problem is within the lora connection between the devices.

Lora Temperature/ Humidity/ Soil Moisture Sensor V3 | Makerfabs

Is it waterproof enough to be used outside?

Given the advertising

suit for applications for smart-farm, irrigation, agriculture, etc.

you’d hope so, but there doesn’t seem to be an IP rating.

1 Like

I’m using two of them outside. It has rained and I have watered (litteraly: put water on top of it), they still work.

1 Like

Be sure you have the newest firmware flashed on your gateway. You can check what´s going on at your gateway with MQTT Explorer: http://mqtt-explorer.com/

I am unable to get my soil sensor to communicate with my gateway. I can see the messages getting “sent” via the Arduino serial console. I can see my gateway is online via MQTT Explorer, but I never see a message published to the topic LORAtoMQTT I have them both on the 915 channel. Is there any other module I need to enable other than Lora in OMG gateway. I am running the branch from kamaradclimber. I also tried the main dev branch from OMG repository. Any help would be appreciated. I have tried multiple sensors and gateways and different versions from this thread, no luck. This is what I am trying to use as the gateway: heltec-wifi-lora-32-915.
Thanks.

I finally got it working, I had to update config_LORA.h in OMG to match the 915 band.

//Default parameters used when the parameters are not set in the json data
#define LORA_BAND             915E6
#define LORA_SIGNAL_BANDWIDTH 125E3

I was having the issue where the mqtt string kept appending until it eventually the sensor would just stop sending. I adjusted the code from @thebang2 to clear out the jsonoutput before each write. So far seems to be working.

void send_lora()
{

  // Create json object for transfer
  DynamicJsonDocument root(256);
  String jsonoutput = "";
  //JsonObject& root = jsonBuffer.createObject();
  root["node_id"] = node_id;
  root["hum"] = (String)humidity;
  root["temp"] = (String)temperature;
  root["adc"] = (String)sensorValue;
  root["bat"] = (String)batValue;

I´m sorry for that, I have updated the origin code.

1 Like

No worries, thanks for all your great work!!! I could have never even got remotely close to what you did. :grinning:

What is the battery life like? I’m looking for an alternative to my Mi Flora sensors.