RAK12035_SoilMoisture.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554
  1. /*----------------------------------------------------------------------*
  2. * RAK12035_SoilMoistureSensor.cpp - Arduino library for the Sensor *
  3. * version of I2C Soil Moisture Sensor version from Chrirp *
  4. * (https://github.com/Miceuz/i2c-moisture-sensor). *
  5. * *
  6. * Ingo Fischer 11Nov2015 *
  7. * https://github.com/Apollon77/I2CSoilMoistureSensor *
  8. * *
  9. * Ken Privitt 8Feb2026 *
  10. * Adapted for MeshCore Firmware Stack *
  11. * *
  12. * MIT license *
  13. * *
  14. * This file contains a collection of routines to access the *
  15. * RAK12035 Soil Moisture Sensor via I2C. The sensor provides *
  16. * Soil Temperature and capacitance-based Soil Moisture Readings. *
  17. * *
  18. *----------------------------------------------------------------------*/
  19. #include "RAK12035_SoilMoisture.h"
  20. #include "MeshCore.h"
  21. #include <Wire.h>
  22. /*----------------------------------------------------------------------*
  23. * Constructor. *
  24. *----------------------------------------------------------------------*/
  25. // RAK12035_SoilMoisture(uint8_t addr)
  26. //
  27. // Accepts the I2C Address to look for the RAK12035
  28. // Initializes the I2C to null (will be setup later in Wire.Begin()
  29. //
  30. // No hardware is touched in the constructor.
  31. // I2C communication is deferred until begin() is called.
  32. //------------------------------------------------------------------------------
  33. RAK12035_SoilMoisture::RAK12035_SoilMoisture(uint8_t addr)
  34. {
  35. _addr = addr; // Save the sensor's I2C address
  36. _i2c = nullptr; // Bus not assigned yet; must be set in begin()
  37. }
  38. //------------------------------------------------------------------------------
  39. // setup()
  40. //------------------------------------------------------------------------------
  41. // setup(TwoWire &i2c)
  42. //
  43. // Assigns the I2C bus that this driver instance will use. This allows the
  44. // application to choose between Wire, Wire1, or any other TwoWire instance
  45. // supported by the platform.
  46. //
  47. // No I2C communication occurs here; setup() simply stores the pointer so that
  48. // begin() and all register‑level operations know which bus to use.
  49. //------------------------------------------------------------------------------
  50. void RAK12035_SoilMoisture::setup(TwoWire &i2c)
  51. {
  52. _i2c = &i2c; // assigns the bus pointer
  53. _i2c->begin(); // Initialize the bus to Wire or Wire1
  54. }
  55. //------------------------------------------------------------------------------
  56. // RAK12035 Soil Moisture begin()
  57. //------------------------------------------------------------------------------
  58. //
  59. // Performs initialization of the RAK12035 soil‑moisture sensor. This
  60. // routine assumes that the application has already selected the I2C bus via
  61. // setup() and that the bus has been initialized externally (Wire.begin()).
  62. // It uses the passed in I2C Address (default 0x20)
  63. //
  64. // *** This code does not support three sensors ***
  65. // The RAK12023 has three connectors, but each of the sensors attached must
  66. // all have different I2C addresses.
  67. // This code has a function to set the I2C address of a sensor
  68. // and currently only supports one address 0x20 (the default).
  69. // To support three sensors, EnvironmentSensorManager would need to be modified
  70. // to support multiple instances of the RAK12035_SoilMoisture class,
  71. // each with a different address. (0x20, 0x21, 0x22)
  72. // The begin() function would need to be modified to loop through the three addresses
  73. //
  74. // DEBUG STATEMENTS: Can be enabled by uncommenting or adding:
  75. // File: variants/rak4631 platformio.ini
  76. // Section example: [env:RAK_4631_companion_radio_ble]
  77. // Enable Debug statements: -D MESH_DEBUG=1
  78. //
  79. //------------------------------------------------------------------------------
  80. bool RAK12035_SoilMoisture::begin(uint8_t addr)
  81. {
  82. // MESH_DEBUG_PRINTLN("begin() - Start of RAK12035 initialization");
  83. // MESH_DEBUG_PRINTLN("begin() - RAK12035 passed in Address %02X", addr);
  84. // 1. Ensure setup() was called
  85. if (_i2c == nullptr) {
  86. MESH_DEBUG_PRINTLN("RAK12035 ERROR: I2C bus not set!");
  87. return false;
  88. }
  89. uint16_t _dry_cal = 200;
  90. uint16_t _wet_cal = 600;
  91. uint8_t _version = 0;
  92. uint8_t _addr; // The I2C address to be used (passed in parameter)
  93. /*------------------------------------------------------------------------------------------
  94. * Set Calibration values - This is done with custom a firmware version
  95. *
  96. * USE the Build Flag: -D ENABLE_RAK12035_CALIBRATION = 1
  97. * OR
  98. * Change the value to 1 in the RAK12035_SoilMoisture.h file
  99. *
  100. * Calibration Procedure:
  101. * 1) Flash the Calibration version of the firmware.
  102. * 2) Leave the sensor dry, power up the device.
  103. * 3) After detecting the RAK12035 this firmware will display calibration data on Channel 3
  104. *
  105. * Frequency = Current Capacitance Value
  106. * Temperature = Current Wet calibration value
  107. * Power = Current Dry calibration value
  108. *
  109. * 4) Click refresh several times. This will take a capacitance reading and if it is
  110. * greater than the current Dry value it will store it in the sensor
  111. * The value will bounce a little as you click refresh, but it eventually settles down (a few clicks)
  112. * the stored value will stabalize at it's Maximum value.
  113. *
  114. * 5) Put the sensor in water.
  115. *
  116. * 6) Click refresh several times. This will take a capacitance reading and if it is
  117. * less than the current Wet value it will store it in the sensor
  118. * The value will bounce a little as you click refresh, but it eventually settles down (a few clicks)
  119. * the stored value will stabalize at it's Minimum value.
  120. *
  121. * 7) The Sensor is now calibrated, turn off the device.
  122. *
  123. * 8) Reflash the device with the non-Calibration Firmware, Data will be shown on Channel 2
  124. *
  125. *------------------------------------------------------------------------------------------
  126. */
  127. #if ENABLE_RAK12035_CALIBRATION
  128. uint16_t _wet = 2000; // A high value the should be out of the normal Wet range
  129. set_humidity_full(_wet);
  130. uint16_t _dry = 50; // A low value the should be out of the normal Dry range
  131. set_humidity_zero(_dry);
  132. #endif
  133. /*--------------------------------------------------------------------------------
  134. *
  135. * Check if a sensor is present and return true if found, false if not present
  136. *
  137. *--------------------------------------------------------------------------------
  138. */
  139. if (query_sensor()) {
  140. MESH_DEBUG_PRINTLN("begin() - Sensor responded with valid version");
  141. return true;
  142. }
  143. else {
  144. MESH_DEBUG_PRINTLN("begin() - Sensor version FAIL");
  145. return false;
  146. }
  147. }
  148. /*---------------------------------------------------------------------------------
  149. *
  150. * Below are all the routines to execute the various I2C commands supported
  151. * by the RAK12035 sensor
  152. *
  153. *--------------------------------------------------------------------------------*/
  154. uint16_t RAK12035_SoilMoisture::get_sensor_capacitance() //Command 01 - (r) 2 byte
  155. {
  156. uint8_t buf[2] = {0};
  157. if (!read_rak12035(SOILMOISTURESENSOR_GET_CAPACITANCE, buf, 2)) {
  158. MESH_DEBUG_PRINTLN("Function 1: get_capacitance() FAIL: Bad data returned = %02X %02X", buf[0], buf[1]);
  159. return (buf[0] << 8) | buf[1]; // return raw for debugging
  160. }
  161. uint16_t cap = (buf[0] << 8) | buf[1];
  162. MESH_DEBUG_PRINTLN("Function 1: get_capacitance() SUCCESS: Capacitance = %d", cap);
  163. return cap;
  164. }
  165. uint8_t RAK12035_SoilMoisture::get_I2C_address() //Command 02 - (r) 1 byte
  166. {
  167. uint8_t addr = 0;
  168. if (!read_rak12035(SOILMOISTURESENSOR_GET_I2C_ADDR, &addr, 1)) {
  169. MESH_DEBUG_PRINTLN("Function 2: get_I2C_address() FAIL: Bad data returned = %02X", addr);
  170. return addr; // return raw for debugging
  171. }
  172. MESH_DEBUG_PRINTLN("Function 2: get_I2C_address() SUCCESS: I2C Address = %02X", addr);
  173. return addr;
  174. }
  175. bool RAK12035_SoilMoisture::set_sensor_addr(uint8_t addr) //Command 03 - (w) 1 byte
  176. {
  177. if (!write_rak12035(SOILMOISTURESENSOR_SET_I2C_ADDR, &addr, 1)) {
  178. MESH_DEBUG_PRINTLN("Function 3: set_I2C_address() FAIL: Could not set new address %02X", addr);
  179. return false;
  180. }
  181. MESH_DEBUG_PRINTLN("Function 3: set_I2C_address() SUCCESS: New address = %02X", addr);
  182. return true;
  183. }
  184. uint8_t RAK12035_SoilMoisture::get_sensor_version() // Command 04 - 1 byte
  185. {
  186. uint8_t v = 0;
  187. read_rak12035(SOILMOISTURESENSOR_GET_VERSION, &v, 1);
  188. if (!read_rak12035(SOILMOISTURESENSOR_GET_VERSION, &v, 1)) {
  189. MESH_DEBUG_PRINTLN("Function 4: get_sensor_version() FAIL: Bad data returned = %02X", v);
  190. return v;
  191. }
  192. MESH_DEBUG_PRINTLN("Function 4: get_sensor_version() SUCCESS: Version = %02X", v);
  193. return v;
  194. }
  195. float RAK12035_SoilMoisture::get_sensor_temperature() //Command 05 - (r) 2 bytes
  196. {
  197. uint8_t buf[2] = {0};
  198. if (!read_rak12035(SOILMOISTURESENSOR_GET_TEMPERATURE, buf, 2)) {
  199. MESH_DEBUG_PRINTLN("Function 5: get_temperature() FAIL: Bad data returned = %02X %02X", buf[0], buf[1]);
  200. return (buf[0] << 8) | buf[1]; // raw data returned for debugging 0XFFFF is error
  201. }
  202. // Sensor returns a 16-bit signed integer (°C * 10)
  203. int16_t raw = (buf[0] << 8) | buf[1];
  204. float tempC = raw / 10.0f;
  205. MESH_DEBUG_PRINTLN("Function 5: get_temperature() SUCCESS: Raw=%04X Temp=%.1f C", raw, tempC);
  206. return tempC;
  207. }
  208. bool RAK12035_SoilMoisture::sensor_sleep() //Command 06 - (w) 1 byte
  209. {
  210. uint8_t tmp = 0;
  211. if (!write_rak12035(SOILMOISTURESENSOR_SET_SLEEP, &tmp, 1)) {
  212. MESH_DEBUG_PRINTLN("Function 6: sensor_sleep() FAIL: Could not send sleep command");
  213. return false;
  214. }
  215. MESH_DEBUG_PRINTLN("Function 6: sensor_sleep() SUCCESS: Sensor acknowledged sleep command");
  216. // Optional: turn off sensor power AFTER successful sleep command
  217. // This has been commented out due to a pin name conflict with the Heltec v3
  218. // This will need to be resolved if this function is to be utilized in the future
  219. /*
  220. digitalWrite(WB_IO2, LOW);
  221. */
  222. return true;
  223. }
  224. bool RAK12035_SoilMoisture::set_humidity_full(uint16_t full) //Command 07 - (w) 2 bytes
  225. {
  226. uint8_t buf[2];
  227. buf[0] = (full >> 8) & 0xFF; // High byte
  228. buf[1] = full & 0xFF; // Low byte
  229. if (!write_rak12035(SOILMOISTURESENSOR_SET_WET_CAL, buf, 2)) {
  230. MESH_DEBUG_PRINTLN("Function 7: set_humidity_full() FAIL: Could not set wet calibration value"
  231. );
  232. return false;
  233. }
  234. MESH_DEBUG_PRINTLN("Function 7: set_humidity_full() SUCCESS: New Full = %04X", full);
  235. return true;
  236. }
  237. bool RAK12035_SoilMoisture::set_humidity_zero(uint16_t zero) //Command 08 - (w) 2 bytes
  238. {
  239. uint8_t buf[2];
  240. buf[0] = (zero >> 8) & 0xFF; // High byte
  241. buf[1] = zero & 0xFF; // Low byte
  242. if (!write_rak12035(SOILMOISTURESENSOR_SET_DRY_CAL, buf, 2)) {
  243. MESH_DEBUG_PRINTLN("Function 8: set_humidity_zero() FAIL: Could not set dry calibration value");
  244. return false;
  245. }
  246. MESH_DEBUG_PRINTLN("Function 8: set_humidity_zero() SUCCESS: New Zero = %04X", zero);
  247. return true;
  248. }
  249. uint8_t RAK12035_SoilMoisture::get_sensor_moisture() //Command 09 - (r) 1 byte
  250. {
  251. // Load calibration values from sensor
  252. _wet_cal = get_humidity_full();
  253. _dry_cal = get_humidity_zero();
  254. MESH_DEBUG_PRINTLN("Function 9: get_moisture() - Read from sensor or calculate from capacitance");
  255. // Read sensor version
  256. uint8_t v = get_sensor_version();
  257. // If version > 2, read moisture directly from the sensor
  258. if (v > 2) {
  259. MESH_DEBUG_PRINTLN("Version > 02 - Reading moisture directly from sensor");
  260. uint8_t moisture = get_sensor_humid();
  261. MESH_DEBUG_PRINTLN("get_moisture() Direct Read = %d%%", moisture);
  262. return moisture;
  263. }
  264. // Otherwise calculate moisture from capacitance
  265. MESH_DEBUG_PRINTLN("Calculating moisture from capacitance");
  266. uint16_t cap = get_sensor_capacitance();
  267. // Clamp capacitance between calibration points
  268. if (_dry_cal < _wet_cal) {
  269. if (cap <= _dry_cal) cap = _dry_cal;
  270. if (cap >= _wet_cal) cap = _wet_cal;
  271. float pct = (_wet_cal - cap) * 100.0f / (_wet_cal - _dry_cal);
  272. if (pct > 100.0f) pct = 100.0f;
  273. MESH_DEBUG_PRINTLN("get_moisture Case 1() Calculated = %d%%", (uint8_t)pct);
  274. return (uint8_t)pct;
  275. } else {
  276. if (cap >= _dry_cal) cap = _dry_cal;
  277. if (cap <= _wet_cal) cap = _wet_cal;
  278. float pct = (_dry_cal - cap) * 100.0f / (_dry_cal - _wet_cal);
  279. if (pct > 100.0f) pct = 100.0f;
  280. MESH_DEBUG_PRINTLN("get_moisture Case 2() Calculated = %d%%", (uint8_t)pct);
  281. return (uint8_t)pct;
  282. }
  283. }
  284. uint8_t RAK12035_SoilMoisture::get_sensor_humid() //Command 09 - (r) 1 byte
  285. {
  286. uint8_t moisture = 0;
  287. if (!read_rak12035(SOILMOISTURESENSOR_GET_MOISTURE, &moisture, 1)) {
  288. MESH_DEBUG_PRINTLN("Function 9: get_sensor_humid() FAIL: Bad data returned = %02X", moisture);
  289. return moisture; // raw fallback
  290. }
  291. MESH_DEBUG_PRINTLN("Function 9: get_sensor_humid() SUCCESS: Moisture = %d%%",moisture);
  292. return moisture;
  293. }
  294. uint16_t RAK12035_SoilMoisture::get_humidity_full() //Command 0A - (r) 2 bytes
  295. {
  296. uint8_t buf[2] = {0};
  297. if (!read_rak12035(SOILMOISTURESENSOR_GET_WET_CAL, buf, 2)) {
  298. MESH_DEBUG_PRINTLN("Function A: get_humidity_full() FAIL: Bad data returned = %02X%02X", buf[0], buf[1]);
  299. return 0xFFFF; // error indicator
  300. }
  301. uint16_t full = (buf[0] << 8) | buf[1];
  302. MESH_DEBUG_PRINTLN("Function A: get_humidity_full() SUCCESS: Full = %04X = %d", full, full);
  303. return full;
  304. }
  305. uint16_t RAK12035_SoilMoisture::get_humidity_zero() //Command 0B - 2 bytes
  306. {
  307. uint8_t buf[2] = {0};
  308. if (!read_rak12035(SOILMOISTURESENSOR_GET_DRY_CAL, buf, 2)) {
  309. MESH_DEBUG_PRINTLN("Function B: get_humidity_zero() FAIL: Bad data returned = %02X%02X", buf[0], buf[1]);
  310. return 0xFFFF; // error indicator
  311. }
  312. uint16_t zero = (buf[0] << 8) | buf[1];
  313. MESH_DEBUG_PRINTLN("Function B: get_humidity_zero() SUCCESS: Zero = %04X = %d", zero, zero);
  314. return zero;
  315. }
  316. /*------------------------------------------------------------------------------------------*
  317. * getEvent() - High-level function to read both moisture and temperature in one call. *
  318. *------------------------------------------------------------------------------------------*
  319. * This function reads the moisture percentage and temperature from the sensor and returns *
  320. * them via output parameters. This may be used for the telemetry delivery in the MeshCore *
  321. * firmware, with a single function to get all sensor data. *
  322. * *
  323. * The function returns true if both readings were successfully obtained, or false if any *
  324. * error occurred during I2C communication. *
  325. * *
  326. * This function is currently not used *
  327. *------------------------------------------------------------------------------------------*/
  328. bool RAK12035_SoilMoisture::getEvent(uint8_t *humidity, uint16_t *temp)
  329. {
  330. // Read moisture (0-100%)
  331. uint8_t moist = get_sensor_moisture();
  332. if (moist == 0xFF) //error indicator
  333. return false;
  334. MESH_DEBUG_PRINTLN("getEvent() - Humidity = %d", moist);
  335. *humidity = moist;
  336. //Read temperature (degrees C)
  337. uint16_t t = get_sensor_temperature();
  338. if (t == 0XFFFF) // error indicator
  339. return false;
  340. *temp = t;
  341. MESH_DEBUG_PRINTLN("getEvent() - Temperature = %d", t);
  342. return true;
  343. }
  344. /*------------------------------------------------------------------------------------------*
  345. * Sensor Power Management and Reset Routines
  346. *
  347. * These routines manage the power and reset state of the sensor. The sensor_on() routine is
  348. * designed to power on the sensor and wait for it to become responsive, while the reset()
  349. * routine toggles the reset pin and waits for the sensor to respond with a valid version.
  350. *
  351. * They are for a future sensor power management function.
  352. *------------------------------------------------------------------------------------------*/
  353. bool RAK12035_SoilMoisture::sensor_on()
  354. {
  355. uint8_t data;
  356. // This has been commented out due to a pin name conflict with the Heltec v3
  357. // This will need to be resolved if this function is to be utilized in the future
  358. /*
  359. pinMode(WB_IO2, OUTPUT);
  360. digitalWrite(WB_IO2, HIGH); //Turn on Sensor Power
  361. pinMode(WB_IO4, OUTPUT); //Set IO4 Pin to Output (connected to *reset on sensor)
  362. digitalWrite(WB_IO4, LOW); //*reset - Reset the Sensor
  363. delay(1); //Wait for the minimum *reset, 1mS is longer than required minimum
  364. digitalWrite(WB_IO4, HIGH); //Deassert Reset
  365. delay(10); // Wait for the sensor code to complete initialization
  366. */
  367. uint8_t v = 0;
  368. uint32_t timeout = millis();
  369. while ((!query_sensor())) //Wait for sensor to respond to I2C commands,
  370. { //indicating it is ready
  371. if ((millis() - timeout) > 50){ //0.5 second timeout for sensor to respond
  372. MESH_DEBUG_PRINTLN("reset() - Timeout, no response from I2C commands");
  373. return false;
  374. }
  375. else {
  376. delay(10); //delay 10mS
  377. }
  378. }
  379. }
  380. bool RAK12035_SoilMoisture::reset()
  381. {
  382. // This function is for a future Sensor Power Management function.
  383. // When power is reapplied this will reset the sensor and wait for it to respond
  384. // with a valid version.
  385. //
  386. // The Atmel 8495 Microcoltroller: Reset input. A low level on this pin for longer than
  387. // the minimum pulse length will generate a reset, even if the clock is not
  388. // running and provided the reset pin has not been disabled. The minimum pulse length is
  389. // given in Table 25-5 on page 240. 2000ns = .002mS
  390. // Shorter pulses are not guaranteed to generate a reset.
  391. //
  392. // Power is never removed so the Sensor reset was removed and is not needed,
  393. // But might be needed if power is ever switched off. Here is tested code.
  394. // This has been commented out due to a pin name conflict with the Heltec v3
  395. // This will need to be resolved if this function is to be utilized in the future
  396. /*
  397. pinMode(WB_IO4, OUTPUT); //Set IO4 Pin to Output (connected to *reset on sensor)
  398. MESH_DEBUG_PRINTLN("Assert *reset (Low) for 1 mS");
  399. digitalWrite(WB_IO4, LOW); //Reset the Sensor
  400. delay(1); //Wait for the minimum *reset, 1mS is longer than required minimum
  401. MESH_DEBUG_PRINTLN("reset() - De-assert *reset (High)");
  402. digitalWrite(WB_IO4, HIGH); // Deassert Reset
  403. */
  404. MESH_DEBUG_PRINTLN("reset() - Begin poling in 100mS intervals for a non-zero version");
  405. uint32_t start_time = millis();
  406. MESH_DEBUG_PRINTLN("reset() - Timeout, Start Time: %d milliseconds", start_time);
  407. const uint32_t timeout_ms = 500; // Wait for 0.5 seconds
  408. uint32_t start = millis();
  409. while (true) {
  410. if (query_sensor()) {
  411. MESH_DEBUG_PRINTLN("reset() - First Pass, Sensor responded with valid version");
  412. uint32_t stop_time = millis();
  413. MESH_DEBUG_PRINTLN("reset() - Timeout, Stop Time: %d mS", stop_time);
  414. MESH_DEBUG_PRINTLN("reset() - Timeout, Duration: %d mS", (stop_time - start_time));
  415. return true;
  416. }
  417. if (millis() - start > timeout_ms) {
  418. MESH_DEBUG_PRINTLN("reset() - Timeout waiting for valid sensor version");
  419. uint32_t stop_time = millis();
  420. MESH_DEBUG_PRINTLN("reset() - Timeout, Stop Time: %d mS", stop_time);
  421. MESH_DEBUG_PRINTLN("reset() - Timeout, Duration: %d mS", (stop_time - start_time));
  422. return false;
  423. }
  424. delay(100);
  425. }
  426. }
  427. bool RAK12035_SoilMoisture::query_sensor()
  428. {
  429. uint8_t v = 0;
  430. v = get_sensor_version();
  431. // Treat 0x00 and 0xFF as invalid / bootloader / garbage
  432. if (v == 0x00 || v == 0xFF) {
  433. MESH_DEBUG_PRINTLN("query_sensor() FAIL: Version value invalid: %02X", v);
  434. return false;
  435. }
  436. MESH_DEBUG_PRINTLN("query_sensor() SUCCESS: Sensor Present, Version = %02X", v);
  437. return true;
  438. }
  439. /*------------------------------------------------------------------------------------------*
  440. * Below are the low-level I2C read and write functions. These handle the actual
  441. * communication with the sensor registers. The higher-level functions call these
  442. * to perform specific tasks.
  443. *------------------------------------------------------------------------------------------*/
  444. bool RAK12035_SoilMoisture::read_rak12035(uint8_t cmd, uint8_t *data, uint8_t length)
  445. {
  446. _i2c->beginTransmission(_addr);
  447. _i2c->write(cmd); // <-- COMMAND, not register index
  448. if (_i2c->endTransmission() != 0)
  449. return false;
  450. delay(20);
  451. int received = _i2c->requestFrom(_addr, length);
  452. if (received != length)
  453. return false;
  454. for (int i = 0; i < length; i++)
  455. data[i] = _i2c->read();
  456. return true;
  457. }
  458. bool RAK12035_SoilMoisture::write_rak12035(uint8_t cmd, uint8_t *data, uint8_t length)
  459. {
  460. _i2c->beginTransmission(_addr);
  461. _i2c->write(cmd); // <-- COMMAND, not register index
  462. for (uint8_t i = 0; i < length; i++)
  463. _i2c->write(data[i]);
  464. if (_i2c->endTransmission() != 0)
  465. return false;
  466. delay(20);
  467. return true;
  468. }