mirror of https://github.com/meshcore-dev/MeshCore
committed by
GitHub
5 changed files with 691 additions and 2 deletions
@ -0,0 +1,554 @@ |
|||||
|
/*----------------------------------------------------------------------*
|
||||
|
* RAK12035_SoilMoistureSensor.cpp - Arduino library for the Sensor * |
||||
|
* version of I2C Soil Moisture Sensor version from Chrirp * |
||||
|
* (https://github.com/Miceuz/i2c-moisture-sensor). *
|
||||
|
* * |
||||
|
* Ingo Fischer 11Nov2015 * |
||||
|
* https://github.com/Apollon77/I2CSoilMoistureSensor *
|
||||
|
* * |
||||
|
* Ken Privitt 8Feb2026 * |
||||
|
* Adapted for MeshCore Firmware Stack * |
||||
|
* * |
||||
|
* MIT license * |
||||
|
* * |
||||
|
* This file contains a collection of routines to access the * |
||||
|
* RAK12035 Soil Moisture Sensor via I2C. The sensor provides * |
||||
|
* Soil Temperature and capacitance-based Soil Moisture Readings. * |
||||
|
* * |
||||
|
*----------------------------------------------------------------------*/ |
||||
|
|
||||
|
#include "RAK12035_SoilMoisture.h" |
||||
|
#include "MeshCore.h" |
||||
|
#include <Wire.h> |
||||
|
|
||||
|
/*----------------------------------------------------------------------*
|
||||
|
* Constructor. * |
||||
|
*----------------------------------------------------------------------*/ |
||||
|
// RAK12035_SoilMoisture(uint8_t addr)
|
||||
|
//
|
||||
|
// Accepts the I2C Address to look for the RAK12035
|
||||
|
// Initializes the I2C to null (will be setup later in Wire.Begin()
|
||||
|
//
|
||||
|
// No hardware is touched in the constructor.
|
||||
|
// I2C communication is deferred until begin() is called.
|
||||
|
//------------------------------------------------------------------------------
|
||||
|
|
||||
|
RAK12035_SoilMoisture::RAK12035_SoilMoisture(uint8_t addr) |
||||
|
{ |
||||
|
_addr = addr; // Save the sensor's I2C address
|
||||
|
_i2c = nullptr; // Bus not assigned yet; must be set in begin()
|
||||
|
} |
||||
|
|
||||
|
//------------------------------------------------------------------------------
|
||||
|
// setup()
|
||||
|
//------------------------------------------------------------------------------
|
||||
|
// setup(TwoWire &i2c)
|
||||
|
//
|
||||
|
// Assigns the I2C bus that this driver instance will use. This allows the
|
||||
|
// application to choose between Wire, Wire1, or any other TwoWire instance
|
||||
|
// supported by the platform.
|
||||
|
//
|
||||
|
// No I2C communication occurs here; setup() simply stores the pointer so that
|
||||
|
// begin() and all register‑level operations know which bus to use.
|
||||
|
//------------------------------------------------------------------------------
|
||||
|
void RAK12035_SoilMoisture::setup(TwoWire &i2c) |
||||
|
|
||||
|
{ |
||||
|
_i2c = &i2c; // assigns the bus pointer
|
||||
|
_i2c->begin(); // Initialize the bus to Wire or Wire1
|
||||
|
} |
||||
|
|
||||
|
//------------------------------------------------------------------------------
|
||||
|
// RAK12035 Soil Moisture begin()
|
||||
|
//------------------------------------------------------------------------------
|
||||
|
//
|
||||
|
// Performs initialization of the RAK12035 soil‑moisture sensor. This
|
||||
|
// routine assumes that the application has already selected the I2C bus via
|
||||
|
// setup() and that the bus has been initialized externally (Wire.begin()).
|
||||
|
// It uses the passed in I2C Address (default 0x20)
|
||||
|
//
|
||||
|
// *** This code does not supprt three sensors ***
|
||||
|
// The RAK12023 has three connectors, but each of the sensors attached must
|
||||
|
// all have a different I2C addresses.
|
||||
|
// This code has a function to set the I2C adress of a sensor
|
||||
|
// and currently only supports one address 0x20 (the default).
|
||||
|
// To support three sensors, EnvironmentSensorManager would need to be modified
|
||||
|
// to support multiple instances of the RAK12035_SoilMoisture class,
|
||||
|
// each with a different address. (0x20, 0x21, 0x22)
|
||||
|
// The begin() function would need to be modified to loop through the three addresses
|
||||
|
//
|
||||
|
// DEBUG STATEMENTS: Can be enabled by uncommenting or adding:
|
||||
|
// File: varients/rak4631 platformio.ini
|
||||
|
// Section example: [env:RAK_4631_companion_radio_ble]
|
||||
|
// Enable Debug statements: -D MESH_DEBUG=1
|
||||
|
//
|
||||
|
//------------------------------------------------------------------------------
|
||||
|
bool RAK12035_SoilMoisture::begin(uint8_t addr) |
||||
|
{ |
||||
|
// MESH_DEBUG_PRINTLN("begin() - Start of RAK12035 initialization");
|
||||
|
// MESH_DEBUG_PRINTLN("begin() - RAK12035 passed in Address %02X", addr);
|
||||
|
|
||||
|
// 1. Ensure setup() was called
|
||||
|
if (_i2c == nullptr) { |
||||
|
MESH_DEBUG_PRINTLN("RAK12035 ERROR: I2C bus not set!"); |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
uint16_t _dry_cal = 200; |
||||
|
uint16_t _wet_cal = 600; |
||||
|
uint8_t _version = 0; |
||||
|
uint8_t _addr; // The I2C address to be used (passed in parameter)
|
||||
|
|
||||
|
/*------------------------------------------------------------------------------------------
|
||||
|
* Set Calibration values - This is done with custom a firmware version |
||||
|
* |
||||
|
* USE the Build Flag: -D ENABLE_RAK12035_CALIBRATION = 1 |
||||
|
* OR |
||||
|
* Change the value to 1 in the RAK12035_SoilMoisture.h file |
||||
|
* |
||||
|
* Calibration Procedure: |
||||
|
* 1) Flash the the Calibration version of the firmware. |
||||
|
* 2) Leave the sensor dry, power up the device. |
||||
|
* 3) After detecting the RAK12035 this firmware will display calibration data on Channel 3 |
||||
|
* |
||||
|
* Frequency = Current Capacitance Value |
||||
|
* Temperature = Current Wet calibration value |
||||
|
* Power = Current Dry calibration value |
||||
|
* |
||||
|
* 4) Click refresh several times. This will take a capacitance reading and if it is |
||||
|
* greater than the current Dry value it will store it in the sensor |
||||
|
* The value will bounce a little as you click refresh, but it eventually settles down (a few clicks) |
||||
|
* the stored value will stabalize at it's Maximum value. |
||||
|
* |
||||
|
* 5) Put the sensor in water. |
||||
|
* |
||||
|
* 6) Click refresh several times. This will take a capacitance reading and if it is |
||||
|
* less than the current Wet value it will store it in the sensor |
||||
|
* The value will bounce a little as you click refresh, but it eventually settles down (a few clicks) |
||||
|
* the stored value will stabalize at it's Minimum value. |
||||
|
* |
||||
|
* 7) The Sensor is now calibrated, turn off the device. |
||||
|
* |
||||
|
* 8) Reflash the device with the non-Calibration Firmware, Data will be shown on Channel 2 |
||||
|
* |
||||
|
*------------------------------------------------------------------------------------------ |
||||
|
*/ |
||||
|
|
||||
|
#if ENABLE_RAK12035_CALIBRATION |
||||
|
uint16_t _wet = 2000; // A high value the should be out of the normal Wet range
|
||||
|
set_humidity_full(_wet); |
||||
|
|
||||
|
uint16_t _dry = 50; // A low value the should be out of the normal Dry range
|
||||
|
set_humidity_zero(_dry); |
||||
|
#endif |
||||
|
|
||||
|
/*--------------------------------------------------------------------------------
|
||||
|
* |
||||
|
* Check if a sensor is present and return true if found, false if not present |
||||
|
* |
||||
|
*-------------------------------------------------------------------------------- |
||||
|
*/ |
||||
|
if (query_sensor()) { |
||||
|
MESH_DEBUG_PRINTLN("begin() - Sensor responded with valid version"); |
||||
|
return true; |
||||
|
} |
||||
|
else { |
||||
|
MESH_DEBUG_PRINTLN("begin() - Sensor version FAIL"); |
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/*---------------------------------------------------------------------------------
|
||||
|
* |
||||
|
* Below are all the routines to execute the various I2C commands supported |
||||
|
* by the RAK12035 sensor |
||||
|
* |
||||
|
*--------------------------------------------------------------------------------*/ |
||||
|
|
||||
|
uint16_t RAK12035_SoilMoisture::get_sensor_capacitance() //Command 01 - (r) 2 byte
|
||||
|
{ |
||||
|
uint8_t buf[2] = {0}; |
||||
|
if (!read_rak12035(SOILMOISTURESENSOR_GET_CAPACITANCE, buf, 2)) { |
||||
|
MESH_DEBUG_PRINTLN("Function 1: get_capacitance() FAIL: Bad data returned = %02X %02X", buf[0], buf[1]); |
||||
|
return (buf[0] << 8) | buf[1]; // return raw for debugging
|
||||
|
} |
||||
|
uint16_t cap = (buf[0] << 8) | buf[1]; |
||||
|
MESH_DEBUG_PRINTLN("Function 1: get_capacitance() SUCCESS: Capacitance = %d", cap); |
||||
|
return cap; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
uint8_t RAK12035_SoilMoisture::get_I2C_address() //Command 02 - (r) 1 byte
|
||||
|
{ |
||||
|
uint8_t addr = 0; |
||||
|
if (!read_rak12035(SOILMOISTURESENSOR_GET_I2C_ADDR, &addr, 1)) { |
||||
|
MESH_DEBUG_PRINTLN("Function 2: get_I2C_address() FAIL: Bad data returned = %02X", addr); |
||||
|
return addr; // return raw for debugging
|
||||
|
} |
||||
|
MESH_DEBUG_PRINTLN("Function 2: get_I2C_address() SUCCESS: I2C Address = %02X", addr); |
||||
|
return addr; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
bool RAK12035_SoilMoisture::set_sensor_addr(uint8_t addr) //Command 03 - (w) 1 byte
|
||||
|
{ |
||||
|
if (!write_rak12035(SOILMOISTURESENSOR_SET_I2C_ADDR, &addr, 1)) { |
||||
|
MESH_DEBUG_PRINTLN("Function 3: set_I2C_address() FAIL: Could not set new address %02X", addr); |
||||
|
return false; |
||||
|
} |
||||
|
MESH_DEBUG_PRINTLN("Function 3: set_I2C_address() SUCCESS: New address = %02X", addr); |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
uint8_t RAK12035_SoilMoisture::get_sensor_version() // Command 04 - 1 byte
|
||||
|
{ |
||||
|
uint8_t v = 0; |
||||
|
|
||||
|
read_rak12035(SOILMOISTURESENSOR_GET_VERSION, &v, 1); |
||||
|
if (!read_rak12035(SOILMOISTURESENSOR_GET_VERSION, &v, 1)) { |
||||
|
MESH_DEBUG_PRINTLN("Function 4: get_sensor_version() FAIL: Bad data returned = %02X", v); |
||||
|
return v; |
||||
|
} |
||||
|
MESH_DEBUG_PRINTLN("Function 4: get_sensor_version() SUCCESS: Version = %02X", v); |
||||
|
return v; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
float RAK12035_SoilMoisture::get_sensor_temperature() //Command 05 - (r) 2 bytes
|
||||
|
{ |
||||
|
uint8_t buf[2] = {0}; |
||||
|
if (!read_rak12035(SOILMOISTURESENSOR_GET_TEMPERATURE, buf, 2)) { |
||||
|
MESH_DEBUG_PRINTLN("Function 5: get_temperature() FAIL: Bad data returned = %02X %02X", buf[0], buf[1]); |
||||
|
return (buf[0] << 8) | buf[1]; // raw data returned for debugging 0XFFFF is error
|
||||
|
} |
||||
|
// Sensor returns a 16-bit signed integer (°C * 10)
|
||||
|
int16_t raw = (buf[0] << 8) | buf[1]; |
||||
|
float tempC = raw / 10.0f; |
||||
|
MESH_DEBUG_PRINTLN("Function 5: get_temperature() SUCCESS: Raw=%04X Temp=%.1f C", raw, tempC); |
||||
|
return tempC; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
bool RAK12035_SoilMoisture::sensor_sleep() //Command 06 - (w) 1 byte
|
||||
|
{ |
||||
|
uint8_t tmp = 0; |
||||
|
if (!write_rak12035(SOILMOISTURESENSOR_SET_SLEEP, &tmp, 1)) { |
||||
|
MESH_DEBUG_PRINTLN("Function 6: sensor_sleep() FAIL: Could not send sleep command"); |
||||
|
return false; |
||||
|
} |
||||
|
MESH_DEBUG_PRINTLN("Function 6: sensor_sleep() SUCCESS: Sensor acknowledged sleep command"); |
||||
|
|
||||
|
// Optional: turn off sensor power AFTER successful sleep command
|
||||
|
|
||||
|
// This has been commented out due to a pin name conflict with the Heltec v3
|
||||
|
// This will need to be resolved if this funstion is to be utilized in the future
|
||||
|
/*
|
||||
|
digitalWrite(WB_IO2, LOW); |
||||
|
*/ |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
bool RAK12035_SoilMoisture::set_humidity_full(uint16_t full) //Command 07 - (w) 2 bytes
|
||||
|
{ |
||||
|
uint8_t buf[2]; |
||||
|
buf[0] = (full >> 8) & 0xFF; // High byte
|
||||
|
buf[1] = full & 0xFF; // Low byte
|
||||
|
|
||||
|
if (!write_rak12035(SOILMOISTURESENSOR_SET_WET_CAL, buf, 2)) { |
||||
|
MESH_DEBUG_PRINTLN("Function 7: set_humidity_full() FAIL: Could not set wet calibration value" |
||||
|
); |
||||
|
return false; |
||||
|
} |
||||
|
MESH_DEBUG_PRINTLN("Function 7: set_humidity_full() SUCCESS: New Full = %04X", full); |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
bool RAK12035_SoilMoisture::set_humidity_zero(uint16_t zero) //Command 08 - (w) 2 bytes
|
||||
|
{ |
||||
|
uint8_t buf[2]; |
||||
|
buf[0] = (zero >> 8) & 0xFF; // High byte
|
||||
|
buf[1] = zero & 0xFF; // Low byte
|
||||
|
|
||||
|
if (!write_rak12035(SOILMOISTURESENSOR_SET_DRY_CAL, buf, 2)) { |
||||
|
MESH_DEBUG_PRINTLN("Function 8: set_humidity_zero() FAIL: Could not set dry calibration value"); |
||||
|
return false; |
||||
|
} |
||||
|
MESH_DEBUG_PRINTLN("Function 8: set_humidity_zero() SUCCESS: New Zero = %04X", zero); |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
uint8_t RAK12035_SoilMoisture::get_sensor_moisture() //Command 09 - (r) 1 byte
|
||||
|
{ |
||||
|
// Load calibration values from sensor
|
||||
|
_wet_cal = get_humidity_full(); |
||||
|
_dry_cal = get_humidity_zero(); |
||||
|
|
||||
|
MESH_DEBUG_PRINTLN("Function 9: get_moisture() - Read from sensor or calculate from capacitance"); |
||||
|
|
||||
|
// Read sensor version
|
||||
|
uint8_t v = get_sensor_version(); |
||||
|
|
||||
|
// If version > 2, read moisture directly from the sensor
|
||||
|
if (v > 2) { |
||||
|
MESH_DEBUG_PRINTLN("Version > 02 - Reading moisture directly from sensor"); |
||||
|
uint8_t moisture = get_sensor_humid(); |
||||
|
MESH_DEBUG_PRINTLN("get_moisture() Direct Read = %d%%", moisture); |
||||
|
return moisture; |
||||
|
} |
||||
|
// Otherwise calculate moisture from capacitance
|
||||
|
MESH_DEBUG_PRINTLN("Calculating moisture from capacitance"); |
||||
|
|
||||
|
uint16_t cap = get_sensor_capacitance(); |
||||
|
|
||||
|
// Clamp capacitance between calibration points
|
||||
|
if (_dry_cal < _wet_cal) { |
||||
|
if (cap <= _dry_cal) cap = _dry_cal; |
||||
|
if (cap >= _wet_cal) cap = _wet_cal; |
||||
|
|
||||
|
float pct = (_wet_cal - cap) * 100.0f / (_wet_cal - _dry_cal); |
||||
|
if (pct > 100.0f) pct = 100.0f; |
||||
|
|
||||
|
MESH_DEBUG_PRINTLN("get_moisture Case 1() Calculated = %d%%", (uint8_t)pct); |
||||
|
return (uint8_t)pct; |
||||
|
} else { |
||||
|
if (cap >= _dry_cal) cap = _dry_cal; |
||||
|
if (cap <= _wet_cal) cap = _wet_cal; |
||||
|
|
||||
|
float pct = (_dry_cal - cap) * 100.0f / (_dry_cal - _wet_cal); |
||||
|
if (pct > 100.0f) pct = 100.0f; |
||||
|
|
||||
|
MESH_DEBUG_PRINTLN("get_moisture Case 2() Calculated = %d%%", (uint8_t)pct); |
||||
|
return (uint8_t)pct; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
uint8_t RAK12035_SoilMoisture::get_sensor_humid() //Command 09 - (r) 1 byte
|
||||
|
{ |
||||
|
uint8_t moisture = 0; |
||||
|
|
||||
|
if (!read_rak12035(SOILMOISTURESENSOR_GET_MOISTURE, &moisture, 1)) { |
||||
|
MESH_DEBUG_PRINTLN("Function 9: get_sensor_humid() FAIL: Bad data returned = %02X", moisture); |
||||
|
return moisture; // raw fallback
|
||||
|
} |
||||
|
MESH_DEBUG_PRINTLN("Function 9: get_sensor_humid() SUCCESS: Moisture = %d%%",moisture); |
||||
|
return moisture; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
uint16_t RAK12035_SoilMoisture::get_humidity_full() //Command 0A - (r) 2 bytes
|
||||
|
{ |
||||
|
uint8_t buf[2] = {0}; |
||||
|
|
||||
|
if (!read_rak12035(SOILMOISTURESENSOR_GET_WET_CAL, buf, 2)) { |
||||
|
MESH_DEBUG_PRINTLN("Function A: get_humidity_full() FAIL: Bad data returned = %02X%02X", buf[0], buf[1]); |
||||
|
return 0xFFFF; // error indicator
|
||||
|
} |
||||
|
|
||||
|
uint16_t full = (buf[0] << 8) | buf[1]; |
||||
|
|
||||
|
MESH_DEBUG_PRINTLN("Function A: get_humidity_full() SUCCESS: Full = %04X = %d", full, full); |
||||
|
return full; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
uint16_t RAK12035_SoilMoisture::get_humidity_zero() //Command 0B - 2 bytes
|
||||
|
{ |
||||
|
uint8_t buf[2] = {0}; |
||||
|
|
||||
|
if (!read_rak12035(SOILMOISTURESENSOR_GET_DRY_CAL, buf, 2)) { |
||||
|
MESH_DEBUG_PRINTLN("Function B: get_humidity_zero() FAIL: Bad data returned = %02X%02X", buf[0], buf[1]); |
||||
|
return 0xFFFF; // error indicator
|
||||
|
} |
||||
|
|
||||
|
uint16_t zero = (buf[0] << 8) | buf[1]; |
||||
|
|
||||
|
MESH_DEBUG_PRINTLN("Function B: get_humidity_zero() SUCCESS: Zero = %04X = %d", zero, zero); |
||||
|
return zero; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
/*------------------------------------------------------------------------------------------*
|
||||
|
* getEvent() - High-level function to read both moisture and temperature in one call. * |
||||
|
*------------------------------------------------------------------------------------------* |
||||
|
* This function reads the moisture percentage and temperature from the sensor and returns * |
||||
|
* them via output parameters. This may be used for the telemerty delivery in the MeshCore * |
||||
|
* firmware, with a single function to get all sensor data. * |
||||
|
* * |
||||
|
* The function returns true if both readings were successfully obtained, or false if any * |
||||
|
* error occurred during I2C communication. * |
||||
|
* * |
||||
|
* This function is currently not used * |
||||
|
*------------------------------------------------------------------------------------------*/ |
||||
|
bool RAK12035_SoilMoisture::getEvent(uint8_t *humidity, uint16_t *temp) |
||||
|
{ |
||||
|
// Read moisture (0-100%)
|
||||
|
uint8_t moist = get_sensor_moisture(); |
||||
|
if (moist == 0xFF) //error indicator
|
||||
|
return false; |
||||
|
MESH_DEBUG_PRINTLN("getEvent() - Humidity = %d", moist); |
||||
|
*humidity = moist; |
||||
|
|
||||
|
//Read temperature (degrees C)
|
||||
|
uint16_t t = get_sensor_temperature(); |
||||
|
if (t == 0XFFFF) // error indicator
|
||||
|
return false; |
||||
|
|
||||
|
*temp = t; |
||||
|
MESH_DEBUG_PRINTLN("getEvent() - Temperature = %d", t); |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
/*------------------------------------------------------------------------------------------*
|
||||
|
* Sensor Power Management and Reset Routines |
||||
|
* |
||||
|
* These routines manage the power and reset state of the sensor. The sensor_on() routine is |
||||
|
* designed to power on the sensor and wait for it to become responsive, while the reset() |
||||
|
* routine toggles the reset pin and waits for the sensor to respond with a valid version. |
||||
|
* |
||||
|
* They are for a future sensor power management function. |
||||
|
*------------------------------------------------------------------------------------------*/ |
||||
|
|
||||
|
bool RAK12035_SoilMoisture::sensor_on() |
||||
|
{ |
||||
|
uint8_t data; |
||||
|
// This has been commented out due to a pin name conflict with the Heltec v3
|
||||
|
// This will need to be resolved if this funstion is to be utilized in the future
|
||||
|
|
||||
|
/*
|
||||
|
pinMode(WB_IO2, OUTPUT); |
||||
|
digitalWrite(WB_IO2, HIGH); //Turn on Sensor Power
|
||||
|
|
||||
|
pinMode(WB_IO4, OUTPUT); //Set IO4 Pin to Output (connected to *reset on sensor)
|
||||
|
digitalWrite(WB_IO4, LOW); //*reset - Reset the Sensor
|
||||
|
delay(1); //Wait for the minimum *reset, 1mS is longer than required minimum
|
||||
|
digitalWrite(WB_IO4, HIGH); //Deassert Reset
|
||||
|
|
||||
|
delay(10); // Wait for the sensor code to complete initialization
|
||||
|
*/ |
||||
|
uint8_t v = 0; |
||||
|
time_t timeout = millis(); |
||||
|
while ((!query_sensor())) //Wait for sensor to respond to I2C commands,
|
||||
|
{ //indicating it is ready
|
||||
|
if ((millis() - timeout) > 50){ //0.5 second timeout for sensor to respond
|
||||
|
MESH_DEBUG_PRINTLN("reset() - Timeout, no response from I2C commands"); |
||||
|
return false; |
||||
|
} |
||||
|
else { |
||||
|
delay(10); //delay 10mS
|
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
bool RAK12035_SoilMoisture::reset() |
||||
|
{ |
||||
|
// This function is for a future Sensor Power Management function.
|
||||
|
// When power is reapplied this will reset the sensor and wait for it to respond
|
||||
|
// with a valid version.
|
||||
|
//
|
||||
|
// The Atmel 8495 Microcoltroller: Reset input. A low level on this pin for longer than
|
||||
|
// the minimum pulse length will generate a reset, even if the clock is not
|
||||
|
// running and provided the reset pin has not been disabled. The minimum pulse length is
|
||||
|
// given in Table 25-5 on page 240. 2000ns = .002mS
|
||||
|
// Shorter pulses are not guaranteed to generate a reset.
|
||||
|
//
|
||||
|
// Power is never removed so the Sensor reset was removed and is not needed,
|
||||
|
// But might be needed if power is ever switched off. Here is tested code.
|
||||
|
|
||||
|
// This has been commented out due to a pin name conflict with the Heltec v3
|
||||
|
// This will need to be resolved if this funstion is to be utilized in the future
|
||||
|
|
||||
|
/*
|
||||
|
pinMode(WB_IO4, OUTPUT); //Set IO4 Pin to Output (connected to *reset on sensor)
|
||||
|
MESH_DEBUG_PRINTLN("Assert *reset (Low) for 1 mS"); |
||||
|
digitalWrite(WB_IO4, LOW); //Reset the Sensor
|
||||
|
delay(1); //Wait for the minimum *reset, 1mS is longer than required minimum
|
||||
|
MESH_DEBUG_PRINTLN("reset() - De-assert *reset (High)"); |
||||
|
digitalWrite(WB_IO4, HIGH); // Deassert Reset
|
||||
|
*/ |
||||
|
|
||||
|
MESH_DEBUG_PRINTLN("reset() - Begin poling in 100mS intervals for a non-zero version"); |
||||
|
uint32_t start_time = millis(); |
||||
|
MESH_DEBUG_PRINTLN("reset() - Timeout, Start Time: %d milliseconds", start_time); |
||||
|
|
||||
|
const uint32_t timeout_ms = 500; // Wait for 0.5 seconds
|
||||
|
uint32_t start = millis(); |
||||
|
|
||||
|
while (true) { |
||||
|
if (query_sensor()) { |
||||
|
MESH_DEBUG_PRINTLN("reset() - First Pass, Sensor responded with valid version"); |
||||
|
uint32_t stop_time = millis(); |
||||
|
MESH_DEBUG_PRINTLN("reset() - Timeout, Stop Time: %d mS", stop_time); |
||||
|
MESH_DEBUG_PRINTLN("reset() - Timeout, Duration: %d mS", (stop_time - start_time)); |
||||
|
|
||||
|
return true; |
||||
|
} |
||||
|
if (millis() - start > timeout_ms) { |
||||
|
MESH_DEBUG_PRINTLN("reset() - Timeout waiting for valid sensor version"); |
||||
|
uint32_t stop_time = millis(); |
||||
|
MESH_DEBUG_PRINTLN("reset() - Timeout, Stop Time: %d mS", stop_time); |
||||
|
MESH_DEBUG_PRINTLN("reset() - Timeout, Duration: %d mS", (stop_time - start_time)); |
||||
|
return false; |
||||
|
} |
||||
|
delay(100); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
bool RAK12035_SoilMoisture::query_sensor() |
||||
|
{ |
||||
|
uint8_t v = 0; |
||||
|
v = get_sensor_version(); |
||||
|
|
||||
|
// Treat 0x00 and 0xFF as invalid / bootloader / garbage
|
||||
|
if (v == 0x00 || v == 0xFF) { |
||||
|
MESH_DEBUG_PRINTLN("query_sensor() FAIL: Version value invalid: %02X", v); |
||||
|
return false; |
||||
|
} |
||||
|
MESH_DEBUG_PRINTLN("query_sensor() SUCCESS: Sensor Present, Version = %02X", v); |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
/*------------------------------------------------------------------------------------------*
|
||||
|
* Below are the low-level I2C read and write functions. These handle the actual |
||||
|
* communication with the sensor registers. The higher-level functions call these |
||||
|
* to perform specific tasks. |
||||
|
*------------------------------------------------------------------------------------------*/ |
||||
|
|
||||
|
bool RAK12035_SoilMoisture::read_rak12035(uint8_t cmd, uint8_t *data, uint8_t length) |
||||
|
{ |
||||
|
_i2c->beginTransmission(_addr); |
||||
|
_i2c->write(cmd); // <-- COMMAND, not register index
|
||||
|
if (_i2c->endTransmission() != 0) |
||||
|
return false; |
||||
|
|
||||
|
delay(20); |
||||
|
|
||||
|
int received = _i2c->requestFrom(_addr, length); |
||||
|
if (received != length) |
||||
|
return false; |
||||
|
|
||||
|
for (int i = 0; i < length; i++) |
||||
|
data[i] = _i2c->read(); |
||||
|
|
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
bool RAK12035_SoilMoisture::write_rak12035(uint8_t cmd, uint8_t *data, uint8_t length) |
||||
|
{ |
||||
|
_i2c->beginTransmission(_addr); |
||||
|
_i2c->write(cmd); // <-- COMMAND, not register index
|
||||
|
|
||||
|
for (uint8_t i = 0; i < length; i++) |
||||
|
_i2c->write(data[i]); |
||||
|
|
||||
|
if (_i2c->endTransmission() != 0) |
||||
|
return false; |
||||
|
|
||||
|
delay(20); |
||||
|
return true; |
||||
|
} |
||||
@ -0,0 +1,88 @@ |
|||||
|
/**
|
||||
|
* @file RAK12035_SoilMoisture.h |
||||
|
* @author Bernd Giesecke (bernd.giesecke@rakwireless.com) |
||||
|
* @brief Header file for Class RAK12035 |
||||
|
* @version 0.1 |
||||
|
* @date 2021-11-20 |
||||
|
* |
||||
|
* Updates for MeshCore integration |
||||
|
* Ken Privitt |
||||
|
* 2/26/2026 |
||||
|
* |
||||
|
* @copyright Copyright (c) 2021 |
||||
|
* |
||||
|
*/ |
||||
|
#ifndef RAK12035_SOILMOISTURE_H |
||||
|
#define RAK12035_SOILMOISTURE_H |
||||
|
#endif |
||||
|
|
||||
|
#ifndef ENABLE_RAK12025_CALIBRATION |
||||
|
#define ENABLE_RAK12025_CALIBRATION = 0 // Used to generate Calibration Version of Firmware
|
||||
|
|
||||
|
#include <Arduino.h> |
||||
|
#include <Wire.h> |
||||
|
|
||||
|
#define RAK12035_I2C_ADDR_DEFAULT 0x20 |
||||
|
#define RAK12035_0_ADDR 0x20 |
||||
|
#define RAK12035_1_ADDR 0x21 |
||||
|
#define RAK12035_2_ADDR 0x22 |
||||
|
|
||||
|
// Command codes used by the RAK12035 firmware
|
||||
|
#define SOILMOISTURESENSOR_GET_CAPACITANCE 0x01 // (r) 2 bytes
|
||||
|
#define SOILMOISTURESENSOR_GET_I2C_ADDR 0x02 // (r) 1 bytes
|
||||
|
#define SOILMOISTURESENSOR_SET_I2C_ADDR 0x03 // (w) 1 bytes
|
||||
|
#define SOILMOISTURESENSOR_GET_VERSION 0x04 // (r) 1 bytes
|
||||
|
#define SOILMOISTURESENSOR_GET_TEMPERATURE 0x05 // (r) 2 bytes
|
||||
|
#define SOILMOISTURESENSOR_SET_SLEEP 0x06 // (w) 1 bytes
|
||||
|
#define SOILMOISTURESENSOR_SET_WET_CAL 0x07 // (w) 2 bytes
|
||||
|
#define SOILMOISTURESENSOR_SET_DRY_CAL 0x08 // (w) 2 bytes
|
||||
|
#define SOILMOISTURESENSOR_GET_MOISTURE 0x09 // (r) 1 bytes
|
||||
|
#define SOILMOISTURESENSOR_GET_WET_CAL 0x0A // (r) 2 bytes
|
||||
|
#define SOILMOISTURESENSOR_GET_DRY_CAL 0x0B // (r) 2 bytes
|
||||
|
|
||||
|
class RAK12035_SoilMoisture |
||||
|
{ |
||||
|
public: |
||||
|
RAK12035_SoilMoisture(uint8_t addr = RAK12035_I2C_ADDR_DEFAULT); |
||||
|
|
||||
|
void setup(TwoWire& i2c); |
||||
|
bool begin(uint8_t addr); |
||||
|
bool getEvent(uint8_t *humidity, uint16_t *temperature); |
||||
|
|
||||
|
uint16_t get_sensor_capacitance(); //Command 01 - (r) 2 byte
|
||||
|
uint8_t get_I2C_address(); //Command 02 - (r) 1 byte
|
||||
|
bool set_sensor_addr(uint8_t addr); //Command 03 - (w) 1 byte
|
||||
|
uint8_t get_sensor_version(); //Command 04 - (r) 1 byte
|
||||
|
float get_sensor_temperature(); //Command 05 - (r) 2 bytes
|
||||
|
bool sensor_sleep(); //Command 06 - (w) 1 byte
|
||||
|
bool set_humidity_full(uint16_t hundred_val); //Command 07 - (w) 2 bytes
|
||||
|
bool set_humidity_zero(uint16_t zero_val); //Command 08 - (w) 2 bytes
|
||||
|
uint8_t get_sensor_moisture(); //Command 09 - (r) 1 byte
|
||||
|
uint8_t get_sensor_humid(); //Command 09 - (r) 1 byte
|
||||
|
uint16_t get_humidity_full(); //Command 0A - (r) 2 bytes
|
||||
|
uint16_t get_humidity_zero(); //Command 0B - (r) 2 bytes
|
||||
|
|
||||
|
bool read_rak12035(uint8_t cmd, uint8_t *data, uint8_t length); |
||||
|
bool write_rak12035(uint8_t cmd, uint8_t *data, uint8_t length); |
||||
|
|
||||
|
bool query_sensor(); |
||||
|
bool sensor_on(); |
||||
|
bool reset(); |
||||
|
|
||||
|
uint16_t _dry_cal; |
||||
|
uint16_t _wet_cal; |
||||
|
|
||||
|
private: |
||||
|
bool read_reg(uint8_t reg, uint8_t *data, uint8_t len); |
||||
|
bool write_reg(uint8_t reg, uint8_t *data, uint8_t len); |
||||
|
|
||||
|
TwoWire *_i2c = &Wire; |
||||
|
uint8_t _addr; |
||||
|
|
||||
|
uint16_t default_dry_cal = 2000; |
||||
|
uint16_t default_wet_cal = 50; |
||||
|
uint8_t _capacitance = 0; |
||||
|
uint16_t _temperature = 0; |
||||
|
uint8_t _moisture = 0; |
||||
|
}; |
||||
|
#endif |
||||
Loading…
Reference in new issue