Awesome! LoRa Soil sensor

Looking very much forward to this! Any progress on this?

@kamaradclimber Are you planning on releasing this as on official HA integration?

It is available as a custom hacs repository if you want.
I don’t plan to make it evolve much more but would gladly accept suggestions and patches.

I would prefer 1technophile to add this kind of capacity to OMG instead but in the meantime there is a solution.

1 Like

Awesome, will give it a shot. Recently ordered the soil sensor and a LORA-gateway for ESP32.

Hopefully, I will get it all to work. If I can, anybody can :). Keep you posted…

Nevermind… bought a Heltec V3 LORA board… not supported my OMG

You need to compile and maybe change pins

[env:esp32s3-dev-c1-ble]
platform = ${com.esp32_platform}
board = esp32-s3-devkitc-1
...

info from meshtastic:

Same stuff here :man_shrugging:

Maybe LoRaWan

Alright… this is all new for me. Where do I check/set the pins in the code?

I see some pin definitions in main/config_lora.h, but it’s not all of them and the names do not correspond. with the names on the meshtastic github.

Also see you mentioned GitHub - jgromes/RadioLib: Universal wireless communication library for embedded devices on OMG forum. Can you give me some more pointers on how to piece this together? Again… this is new to me…

It will not work with sx1262.

Only supported chip in omg:

Alright… sorry for so many messages in a row…

I got the LILYGO 32v2.1 board and flashed it to the lilygo_ble firmware using option 1 on the OMG website. No issues here and the board is autodiscovered in HA.
I installed the custom repo as made by @kamaradclimber in installed the integration. It detects OMG in HA.

I bought the Makerfabs soil sensor v3 and flashed it with the code as @wmaker published on his Git. Only changed the frequency to 868 and set the update interval to 20 secs (value of 1 in the code).

That’s where it currently ends. HA is not auto-discovering the soil sensor. And I don’t see any MQTT-messages dropping by ( mosquitto_sub -h 192.168.x.x -t home/OpenMQTTGateway_ESP32_LORA/LORAtoMQTT/# -u mqtt -P ********* -v). So, not sure if the soil sensor and OMG are actually communicating.

I got debugging output from the soil sensor:

Code start>>
LoRa Radio Started successfully!
ADC:883
ADC:892
ADC:892
BAT:992
3.20V
BAT:992
3.20V
BAT:992
3.20V
LoRa TX: {"node_id":"ID011318","hum":53.22819,"temp":25.103,"adc":33,"bat":3.196875}
Code end<<

Nothing wrong if you ask me…

I am not as familiar with these prebuilt firmware, but I believe lilygo_ble does not include LoRa.
From looking at this table, I think your best option is ttgo-lora32-v1-868. Its for the V1 board, but it may work for the one you have.

Awesome, got it to work. Took the firmware as @wmaker mentioned and changed the script a bit that he posted before. Basically just the ID of the sensor and the topic of the MQTT messages. And everything came together!

Hoping very much this will be further developed! Would be great if multiple devices got supported and the script is no longer necessary. Not too much work if you ask me, but unfortunately far beyond my skills.

Sorry to see there is not a lot of action here…

Anyway… The Lora has been running indoors for about a month now. I lost about 25% of its battery. Humidity always reports 100%. Other than that is the device running smoothly.

I just received the 3D printed outdoor case. It’s a really tight fit, but curious to see if it works outdoors!

A bit of progress here (not published yet):

  • Added capability to change LORA receiving frequency
  • Added WebUI
  • Added Lilygo v1 display support
  • Lora node identifier as a subtopic per default (per device MQTT topic) as asked by @wmaker

Todo:

  • Configuration through the WebUI for @pricelesstoolkit
  • Auto discovery @kamaradclimber

Is there a reference repository for the sensor node code that I could check and align with, or is it only the code snippets in this post?

As far as I have been able to ascertain, the base node code that most are using is in this thread (click here).

You are certainly welcome to use mine which is similar code, but mostly differs in how the measured values of the soil capacitance is reported.

1 Like

Two of my three sensor also have humidity stuck at some value (right now stuck at 0%).

Here is the Pull request:

If you guys wants to review and comments @wmaker @kamaradclimber

A few things have happened since I was last here. It was necessary to modify my code because of two problems:

  1. Fast dying batteries:

I noticed that a few of my sensors stopped working overnight. The last sign of this was a large number of transmissions:

I believe the issue lies in my code’s sequence. Initially, I begin whith the measurement and then transmit values in loop(). When the voltage falls during data transmission, the chip restarts, sends data again, encounters a voltage drop, and restarts in a repeating cycle. I have fixed the code in loop() and it is now reliable.

  1. Wrong values for dry and wet:
    I have made an error in measuring dry and wet samples. I obtained different readings when measuring in water and wet soil, so I calibrated my sensor with dry and wet soil, not air and water.

Maybe this will help someone make their sensor more reliable - I’ve attached my new code, maybe it will help someone too, even if the code is still crap. I´m to lazy to translate my german comments, sorry. :wink:

Still to do - measuring cycle every three hours. I can’t see that it makes any difference whether I measure every hour or every three hours, with the exception of the extended battery life.


// Arduino Pro Mini
// ATMega326P 3.3V/8M

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

String node_id = String("ID") + "011270";

//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 868.0
#define BANDWIDTH 125.0
#define SPREADING_FACTOR 10
#define CODING_RATE 7
#define OUTPUT_POWER 14
#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;
float bat = 0.0;

bool AHT_init()
{
  bool ret = false;
  Wire.begin();
  delay(100);
  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);
#if DEBUG_OUT_ENABLE
  Serial.print("FREQUENCY: ");
  Serial.println(FREQUENCY);
  Serial.print("BANDWIDTH: ");
  Serial.println(BANDWIDTH);
  Serial.print("SPREADING_FACTOR: ");
  Serial.println(SPREADING_FACTOR);
  Serial.print("CODING_RATE: ");
  Serial.println(CODING_RATE);
  Serial.print("SX127X_SYNC_WORD: ");
  //Serial.println(SX127X_SYNC_WORD);
  Serial.print("OUTPUT_POWER: ");
  Serial.println(OUTPUT_POWER);
  Serial.print("PREAMBLE_LEN:");
  Serial.println(PREAMBLE_LEN);
  Serial.print("GAIN: ");
  Serial.println(GAIN);
#endif

  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");
  Serial.print("Watchdog set to ");
  Serial.print(SLEEP_CYCLE * 8 / 60);
  Serial.println(" Minutes");

#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
  {
    //count init
    count = 0;
    do_some_work();
    all_pins_low();  
  }
  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
}

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;
#if DEBUG_OUT_ENABLE
      Serial.print("ADC raw:");
      Serial.println(sensorValue);
#endif

      Serial.print("Sensor ");
      Serial.println(node_id);
      // 800 = Erde trocken, 600 Erde nass
      //  (Sensorwert, trocken, nass, 0%, 100%)
      // Überprüfe, ob der Sensorwert innerhalb der Grenzen liegt
      if (sensorValue > 800) sensorValue = 800;
      if (sensorValue < 650) sensorValue = 650;

      // Skaliere den Sensorwert zwischen 0 und 100
      sensorValue = 100 - ((sensorValue - 650) * 100 / (800 - 650));

#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);
      bat = (float)batValue * 3.3;
      //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;
  Serial.println("Versorgungsspannung LORA aus");
  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(96);
  //JsonObject& root = jsonBuffer.createObject();
  root["node_id"] = node_id;
  root["hum"] = (String)humidity;
  root["temp"] = (String)temperature;
  root["adc"] = (String)sensorValue;
  root["bat"] = (String)bat;

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