[WIP] Add RAK12035VB Soil Moisture Sensor support

Introduce the RAK12035 sensor as an environmental telemetry sensor,
including necessary calibration checks and default values. Update
relevant files to integrate the sensor into the existing telemetry system.

This hardware is not just one module, but a couple.. RAK12023 and
RAK12035 is the component stack, the RAK12023 does not seem to matter
much and allows for multiple RAK12035 devices to be used.

Co-Authored-By: @Justin-Mann
This commit is contained in:
Tom Fifield 2025-03-07 20:22:52 +08:00
parent 7c3eddebc2
commit 0c41f90ef5
16 changed files with 225 additions and 9 deletions

View File

@ -186,6 +186,15 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
// -----------------------------------------------------------------------------
#define FT6336U_ADDR 0x48
// -----------------------------------------------------------------------------
// RAK12035VB Soil Monitor (using RAK12023 up to 3 RAK12035 monitors can be connected)
// - the default i2c address for this sensor is 0x20, and users are instructed to
// set 0x21 and 0x22 for the second and third sensor if present.
// -----------------------------------------------------------------------------
#define RAK120351_ADDR 0x20
#define RAK120352_ADDR 0x21
#define RAK120353_ADDR 0x22
// -----------------------------------------------------------------------------
// BIAS-T Generator
// -----------------------------------------------------------------------------

View File

@ -69,6 +69,7 @@ class ScanI2C
DFROBOT_RAIN,
DPS310,
LTR390UV,
RAK12035,
} DeviceType;
// typedef uint8_t DeviceAddress;

View File

@ -417,9 +417,21 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
logFoundDevice("BMA423", (uint8_t)addr.address);
}
break;
case TCA9535_ADDR:
case RAK120352_ADDR:
case RAK120353_ADDR:
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x02), 1);
if (registerValue == addr.address) { // RAK12035 returns its I2C address at 0x02 (eg 0x20)
type = RAK12035;
logFoundDevice("RAK12035", (uint8_t)addr.address);
} else {
type = TCA9535;
logFoundDevice("TCA9535", (uint8_t)addr.address);
}
break;
SCAN_SIMPLE_CASE(LSM6DS3_ADDR, LSM6DS3, "LSM6DS3", (uint8_t)addr.address);
SCAN_SIMPLE_CASE(TCA9535_ADDR, TCA9535, "TCA9535", (uint8_t)addr.address);
SCAN_SIMPLE_CASE(TCA9555_ADDR, TCA9555, "TCA9555", (uint8_t)addr.address);
SCAN_SIMPLE_CASE(VEML7700_ADDR, VEML7700, "VEML7700", (uint8_t)addr.address);
SCAN_SIMPLE_CASE(TSL25911_ADDR, TSL2591, "TSL2591", (uint8_t)addr.address);

View File

@ -643,6 +643,7 @@ void setup()
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::DFROBOT_RAIN, meshtastic_TelemetrySensorType_DFROBOT_RAIN);
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::LTR390UV, meshtastic_TelemetrySensorType_LTR390UV);
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::DPS310, meshtastic_TelemetrySensorType_DPS310);
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::RAK12035, meshtastic_TelemetrySensorType_RAK12035);
i2cScanner.reset();
#endif

View File

@ -41,6 +41,9 @@
#include "Sensor/SHTC3Sensor.h"
#include "Sensor/TSL2591Sensor.h"
#include "Sensor/VEML7700Sensor.h"
#ifdef RAK4630
#include "Sensor/RAK12035Sensor.h"
#endif
BMP085Sensor bmp085Sensor;
BMP280Sensor bmp280Sensor;
@ -63,6 +66,9 @@ DFRobotGravitySensor dfRobotGravitySensor;
NAU7802Sensor nau7802Sensor;
BMP3XXSensor bmp3xxSensor;
CGRadSensSensor cgRadSens;
#ifdef RAK4630
RAK12035Sensor rak12035Sensor;
#endif
#endif
#ifdef T1000X_SENSOR_EN
#include "Sensor/T1000xSensor.h"
@ -72,6 +78,7 @@ T1000xSensor t1000xSensor;
#include "Sensor/IndicatorSensor.h"
IndicatorSensor indicatorSensor;
#endif
#define FAILED_STATE_SENSOR_READ_MULTIPLIER 10
#define DISPLAY_RECEIVEID_MEASUREMENTS_ON_SCREEN true
@ -171,6 +178,11 @@ int32_t EnvironmentTelemetryModule::runOnce()
result = rak9154Sensor.runOnce();
#endif
#ifdef RAK4630
if (rak12035Sensor.hasSensor()) {
result = rak12035Sensor.runOnce();
}
#endif
#endif
}
// it's possible to have this module enabled, only for displaying values on the screen.
@ -498,6 +510,12 @@ bool EnvironmentTelemetryModule::getEnvironmentTelemetry(meshtastic_Telemetry *m
valid = valid && rak9154Sensor.getMetrics(m);
hasSensor = true;
#endif
#ifdef RAK4630
if (rak12035Sensor.hasSensor()) {
valid = valid && rak12035Sensor.getMetrics(m);
hasSensor = true;
}
#endif
#endif
return valid && hasSensor;
}
@ -552,6 +570,9 @@ bool EnvironmentTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
LOG_INFO("Send: radiation=%fµR/h", m.variant.environment_metrics.radiation);
LOG_INFO("Send: soil_temperature=%f, soil_moisture=%u", m.variant.environment_metrics.soil_temperature,
m.variant.environment_metrics.soil_moisture);
sensor_read_error_count = 0;
meshtastic_MeshPacket *p = allocDataProtobuf(m);
@ -710,6 +731,13 @@ AdminMessageHandleResult EnvironmentTelemetryModule::handleAdminMessageForModule
if (result != AdminMessageHandleResult::NOT_HANDLED)
return result;
}
#ifdef RAK4630
if (rak12035Sensor.hasSensor()) {
result = rak12035Sensor.handleAdminMessage(mp, request, response);
if (result != AdminMessageHandleResult::NOT_HANDLED)
return result;
}
#endif
#endif
return result;
}

View File

@ -0,0 +1,118 @@
#include "configuration.h"
#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && defined(RAK4630)
#include "../mesh/generated/meshtastic/telemetry.pb.h"
#include "RAK12035Sensor.h"
RAK12035Sensor::RAK12035Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_RAK12035, "RAK12035") {}
int32_t RAK12035Sensor::runOnce()
{
LOG_INFO("Init sensor: %s", sensorName);
if (!hasSensor()) {
return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
}
sensor.set_sensor_addr(RAK120351_ADDR);
sensor.begin(nodeTelemetrySensorsMap[sensorType].first);
// Get sensor firmware version
uint8_t data = 0;
sensor.get_sensor_version(&data);
LOG_INFO("Sensor Firmware version: %i", data);
if (data != 0) {
LOG_DEBUG("RAK12035Sensor Init Succeed");
status = true;
} else {
LOG_ERROR("RAK12035Sensor Init Failed");
status = false;
}
sensor.sensor_sleep();
return initI2CSensor();
}
void RAK12035Sensor::setup()
{
// Set the calibration values
// Reading the saved calibration values from the sensor.
uint16_t zero_val = 0;
uint16_t hundred_val = 0;
uint16_t default_zero_val = 550;
uint16_t default_hundred_val = 420;
sensor.sensor_on();
delay(200);
sensor.get_dry_cal(&zero_val);
sensor.get_wet_cal(&hundred_val);
delay(200);
if (zero_val == 0 || zero_val <= hundred_val) {
LOG_ERROR("Dry calibration value is %d", zero_val);
LOG_ERROR("Wet calibration value is %d", hundred_val);
LOG_ERROR("This does not make sense. Youc can recalibrate this sensor using the calibration sketch included here: "
"https://github.com/RAKWireless/RAK12035_SoilMoisture.");
LOG_ERROR("For now, setting default calibration value for Dry Calibration: %d", default_zero_val);
sensor.set_dry_cal(default_zero_val);
sensor.get_dry_cal(&zero_val);
LOG_ERROR("Dry calibration reset complete. New value is %d", zero_val);
}
if (hundred_val == 0 || hundred_val >= zero_val) {
LOG_ERROR("Dry calibration value is %d", zero_val);
LOG_ERROR("Wet calibration value is %d", hundred_val);
LOG_ERROR("This does not make sense. Youc can recalibrate this sensor using the calibration sketch included here: "
"https://github.com/RAKWireless/RAK12035_SoilMoisture.");
LOG_ERROR("For now, setting default calibration value for Wet Calibration: %d", default_hundred_val);
sensor.set_wet_cal(default_hundred_val);
sensor.get_wet_cal(&hundred_val);
LOG_ERROR("Wet calibration reset complete. New value is %d", hundred_val);
}
sensor.sensor_sleep();
delay(200);
LOG_INFO("Dry calibration value is %d", zero_val);
LOG_INFO("Wet calibration value is %d", hundred_val);
}
bool RAK12035Sensor::getMetrics(meshtastic_Telemetry *measurement)
{
measurement->variant.environment_metrics.has_soil_temperature = true;
measurement->variant.environment_metrics.has_soil_moisture = true;
uint8_t moisture = 0;
uint16_t temp = 0;
bool success = false;
sensor.sensor_on();
delay(200);
success = sensor.get_sensor_moisture(&moisture);
delay(200);
success = sensor.get_sensor_temperature(&temp);
delay(200);
sensor.sensor_sleep();
if (success == false) {
LOG_ERROR("Failed to read sensor data");
return false;
}
LOG_INFO("Successful read from sensor Temperature: %.2f, Moisture: %ld%", (double)(temp / 10), moisture);
measurement->variant.environment_metrics.soil_temperature = (float)(temp / 10);
measurement->variant.environment_metrics.soil_moisture = moisture;
LOG_INFO("Check if the original temperature and moisture (relative_humidity) are being used.. if not just use them for the "
"soil monitoring.");
if (!measurement->variant.environment_metrics.has_temperature) {
LOG_INFO("Overwrite the temp metrics (not being set right now and this will allow the soil temp value to be used in the "
"client interface).");
measurement->variant.environment_metrics.has_temperature = true;
measurement->variant.environment_metrics.temperature = (float)(temp / 10);
}
if (!measurement->variant.environment_metrics.has_relative_humidity) {
LOG_INFO("Overwrite the moisture metrics (not being used for air humidity and this will allow the soil humidity to "
"appear in the client interfaces without adjustments).");
measurement->variant.environment_metrics.has_relative_humidity = true;
measurement->variant.environment_metrics.relative_humidity = (float)moisture;
}
return true;
}
#endif

View File

@ -0,0 +1,29 @@
#pragma once
#include "configuration.h"
#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && defined(RAK4630)
#ifndef _MT_RAK12035VBSENSOR_H
#define _MT_RAK12035VBSENSOR_H
#endif
#include "../mesh/generated/meshtastic/telemetry.pb.h"
#include "RAK12035_SoilMoisture.h"
#include "TelemetrySensor.h"
#include <Arduino.h>
class RAK12035Sensor : public TelemetrySensor
{
private:
RAK12035 sensor;
protected:
virtual void setup() override;
public:
RAK12035Sensor();
virtual int32_t runOnce() override;
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
};
#endif

View File

@ -20,6 +20,7 @@ lib_deps =
https://github.com/RAKWireless/RAK13800-W5100S.git#1.0.2
rakwireless/RAKwireless NCP5623 RGB LED library@^1.0.2
https://github.com/RAKWireless/RAK12034-BMX160.git#dcead07ffa267d3c906e9ca4a1330ab989e957e2
beegee-tokyo/RAK12035_SoilMoisture@^1.0.3
; If not set we will default to uploading over serial (first it forces bootloader entry by talking 1200bps to cdcacm)
; Note: as of 6/2013 the serial/bootloader based programming takes approximately 30 seconds

View File

@ -90,6 +90,8 @@ static const uint8_t A7 = PIN_A7;
// Other pins
#define PIN_AREF (2)
#define PIN_NFC1 (9)
#define WB_IO5 PIN_NFC1
#define WB_IO4 (4)
#define PIN_NFC2 (10)
static const uint8_t AREF = PIN_AREF;
@ -217,6 +219,7 @@ SO GPIO 39/TXEN MAY NOT BE DEFINED FOR SUCCESSFUL OPERATION OF THE SX1262 - TG
// enables 3.3V periphery like GPS or IO Module
// Do not toggle this for GPS power savings
#define PIN_3V3_EN (34)
#define WB_IO2 PIN_3V3_EN
// RAK1910 GPS module
// If using the wisblock GPS module and pluged into Port A on WisBlock base

View File

@ -17,6 +17,7 @@ lib_deps =
melopero/Melopero RV3028@^1.1.0
rakwireless/RAKwireless NCP5623 RGB LED library@^1.0.2
beegee-tokyo/RAKwireless RAK12034@^1.0.0
beegee-tokyo/RAK12035_SoilMoisture@^1.0.3
debug_tool = jlink
; If not set we will default to uploading over serial (first it forces bootloader entry by talking 1200bps to cdcacm)
;upload_protocol = jlink

View File

@ -90,6 +90,8 @@ static const uint8_t A7 = PIN_A7;
// Other pins
#define PIN_AREF (2)
#define PIN_NFC1 (9)
#define WB_IO5 PIN_NFC1
#define WB_IO4 (4)
#define PIN_NFC2 (10)
static const uint8_t AREF = PIN_AREF;
@ -188,6 +190,7 @@ static const uint8_t SCK = PIN_SPI_SCK;
// enables 3.3V periphery like GPS or IO Module
#define PIN_3V3_EN (34)
#define WB_IO2 PIN_3V3_EN
// RAK1910 GPS module
// If using the wisblock GPS module and pluged into Port A on WisBlock base

View File

@ -19,6 +19,7 @@ lib_deps =
melopero/Melopero RV3028@^1.1.0
rakwireless/RAKwireless NCP5623 RGB LED library@^1.0.2
beegee-tokyo/RAKwireless RAK12034@^1.0.0
beegee-tokyo/RAK12035_SoilMoisture@^1.0.3
debug_tool = jlink
; If not set we will default to uploading over serial (first it forces bootloader entry by talking 1200bps to cdcacm)
;upload_protocol = jlink

View File

@ -69,7 +69,9 @@ static const uint8_t A7 = PIN_A7;
// Other pins
#define PIN_AREF (2)
// #define PIN_NFC1 (9)
#define PIN_NFC1 (9)
#define WB_IO5 PIN_NFC1
#define WB_IO4 (4)
// #define PIN_NFC2 (10)
static const uint8_t AREF = PIN_AREF;
@ -160,6 +162,7 @@ static const uint8_t SCK = PIN_SPI_SCK;
// enables 3.3V periphery like GPS or IO Module
#define PIN_3V3_EN (34)
#define WB_IO2 PIN_3V3_EN
// NO GPS
#undef GPS_RX_PIN

View File

@ -90,6 +90,8 @@ static const uint8_t A7 = PIN_A7;
// Other pins
#define PIN_AREF (2)
#define PIN_NFC1 (9)
#define WB_IO5 PIN_NFC1
#define WB_IO4 (4)
#define PIN_NFC2 (10)
static const uint8_t AREF = PIN_AREF;
@ -217,6 +219,7 @@ SO GPIO 39/TXEN MAY NOT BE DEFINED FOR SUCCESSFUL OPERATION OF THE SX1262 - TG
// enables 3.3V periphery like GPS or IO Module
// Do not toggle this for GPS power savings
#define PIN_3V3_EN (34)
#define WB_IO2 PIN_3V3_EN
// RAK1910 GPS module
// If using the wisblock GPS module and pluged into Port A on WisBlock base

View File

@ -23,6 +23,7 @@ lib_deps =
bodmer/TFT_eSPI
beegee-tokyo/RAKwireless RAK12034@^1.0.0
beegee-tokyo/RAK14014-FT6336U @ 1.0.1
beegee-tokyo/RAK12035_SoilMoisture@^1.0.3
debug_tool = jlink
; If not set we will default to uploading over serial (first it forces bootloader entry by talking 1200bps to cdcacm)
;upload_protocol = jlink

View File

@ -90,6 +90,8 @@ static const uint8_t A7 = PIN_A7;
// Other pins
#define PIN_AREF (2)
#define PIN_NFC1 (9)
#define WB_IO5 PIN_NFC1
#define WB_IO4 (4)
#define PIN_NFC2 (10)
static const uint8_t AREF = PIN_AREF;