High deepsleep consumption SDI-12 RAK13010

Hi all,

@a.occ We are using the SDI-12 interface with the RAK4631 Arduino and we noticed high overall quiescent current during sleep. With the PPK2 we measure 1.47mA.
With a 900mA lipo battery and 1/4W solar panel we often run out of power. We use the same battery and solar panel on many other products without troubles.

  • Is there a way to reduce the sleep current during sleep via firmware?
  • Is the quiescent current of the SGM6607A & SGM66051-ADJ responsible for the issue?
  • Would it help to interpose an IO-controlled p-MOSFET between VBAT and VIN/VCC pins of the two boosters?

Sleep after data sending:

There is actually a moment when the consumption falls down to around 60uA before the 1.47mA sleep phase. Any Idea what is that?

Thanks for your help.

Here the code:

#include <Arduino.h>
#include <SPI.h>

#include <LoRaWan-RAK4630.h>

#include "RAK13010_SDI12.h" // Click to install library: http://librarymanager/All#RAK13010

#define TX_PIN WB_IO6 // The pin of the SDI-12 data bus.
#define RX_PIN WB_IO5 // The pin of the SDI-12 data bus.
#define OE WB_IO4     // Output enable pin, active low.

#define SENSOR_ADDRESS 'a'

RAK_SDI12 mySDI12(RX_PIN, TX_PIN, OE);

String sdiResponse = "";
int sdiCommandIndex = 1;
String sdiCommand = "M1!";

float waterContent, ecPore, temperature, batteryVoltage, ecBulk, permittivity;

#define PIN_VBAT WB_A0

uint32_t vbat_pin = PIN_VBAT;

#define VBAT_MV_PER_LSB (0.73242188F) // 3.0V ADC range and 12 - bit ADC resolution = 3000mV / 4096
#define VBAT_DIVIDER_COMP (1.73)      // Compensation factor for the VBAT divider, depend on the board

#define REAL_VBAT_MV_PER_LSB (VBAT_DIVIDER_COMP * VBAT_MV_PER_LSB)

// Comment the next line if you want DEBUG output. But the power savings are not as good then!!!!!!!
#define MAX_SAVE

/* Time the device is sleeping in milliseconds = 2 minutes * 60 seconds * 1000 milliseconds */
#define SLEEP_TIME 2 * 60 * 1000

/** DIO1 GPIO pin for RAK4631 */
#define PIN_LORA_DIO_1 47

/** Max size of the data to be transmitted. */
#define LORAWAN_APP_DATA_BUFF_SIZE 64
/** Number of trials for the join request. */
#define JOINREQ_NBTRIALS 8

/** Lora application data buffer. */
static uint8_t m_lora_app_data_buffer[LORAWAN_APP_DATA_BUFF_SIZE];
/** Lora application data structure. */
static lmh_app_data_t m_lora_app_data = {m_lora_app_data_buffer, 0, 0, 0, 0};

// LoRaWan event handlers
/** LoRaWan callback when join network finished */
static void lorawan_has_joined_handler(void);
/** LoRaWan callback when join failed */
static void lorawan_join_failed_handler(void);
/** LoRaWan callback when data arrived */
static void lorawan_rx_handler(lmh_app_data_t *app_data);
/** LoRaWan callback after class change request finished */
static void lorawan_confirm_class_handler(DeviceClass_t Class);
/** LoRaWan Function to send a package */
bool sendLoRaFrame(void);

/**@brief Structure containing LoRaWan parameters, needed for lmh_init()

   Set structure members to
   LORAWAN_ADR_ON or LORAWAN_ADR_OFF to enable or disable adaptive data rate
   LORAWAN_DEFAULT_DATARATE OR DR_0 ... DR_5 for default data rate or specific data rate selection
   LORAWAN_PUBLIC_NETWORK or LORAWAN_PRIVATE_NETWORK to select the use of a public or private network
   JOINREQ_NBTRIALS or a specific number to set the number of trials to join the network
   LORAWAN_DEFAULT_TX_POWER or a specific number to set the TX power used
   LORAWAN_DUTYCYCLE_ON or LORAWAN_DUTYCYCLE_OFF to enable or disable duty cycles
                     Please note that ETSI mandates duty cycled transmissions.
*/
static lmh_param_t lora_param_init = {LORAWAN_ADR_ON, DR_3, LORAWAN_PUBLIC_NETWORK, JOINREQ_NBTRIALS, LORAWAN_DEFAULT_TX_POWER, LORAWAN_DUTYCYCLE_ON};

/** Structure containing LoRaWan callback functions, needed for lmh_init() */
static lmh_callback_t lora_callbacks = {BoardGetBatteryLevel, BoardGetUniqueId, BoardGetRandomSeed, lorawan_rx_handler, lorawan_has_joined_handler, lorawan_confirm_class_handler, lorawan_join_failed_handler};

//  !!!! KEYS ARE MSB !!!!
/** Device EUI required for OTAA network join */
uint8_t nodeDeviceEUI[8] = {};
/** Application EUI required for network join */
uint8_t nodeAppEUI[8] = {};
/** Application key required for network join */
uint8_t nodeAppKey[16] = {};

DeviceClass_t gCurrentClass = CLASS_A;
LoRaMacRegion_t gCurrentRegion = LORAMAC_REGION_EU868;

/** Semaphore used by events to wake up loop task */
SemaphoreHandle_t taskEvent = NULL;

/** Timer to wakeup task frequently and send message */
SoftwareTimer taskWakeupTimer;

/** Buffer for received LoRaWan data */
uint8_t rcvdLoRaData[256];
/** Length of received data */
uint8_t rcvdDataLen = 0;

> Alessio Occhipinti:


/**
 * @brief Flag for the event type
 * -1 => no event
 * 0 => LoRaWan data received
 * 1 => Timer wakeup
 * 2 => tbd
 * ...
 */
uint8_t eventType = -1;

/**
 * @brief Timer event that wakes up the loop task frequently
 *
 * @param unused
 */
void periodicWakeup(TimerHandle_t unused)
{
  // Switch on blue LED to show we are awake
  digitalWrite(LED_BUILTIN, HIGH);
  eventType = 1;
  // Give the semaphore, so the loop task will wake up
  xSemaphoreGiveFromISR(taskEvent, pdFALSE);
}

float readVBAT(void)
{
  // Set the analog reference to 3.0V (default = 3.6V)
  analogReference(AR_INTERNAL_3_0);

  // Set the resolution to 12-bit (0..4095)
  analogReadResolution(12); // Can be 8, 10, 12 or 14

  // Let the ADC settle
  delay(1);

  float raw;

  // Get the raw 12-bit, 0..3000mV ADC value
  raw = analogRead(vbat_pin);

  return raw * REAL_VBAT_MV_PER_LSB;
}

// Get the response as string from the SDI-12, split by "+" sign and assign the values to the variables waterContent, ecPore and temperature.
void parseResponse(String response)
{
  int index = 0;
  int nextIndex = 0;
  int counter = 0;
  String value = "";
  while (index < response.length())
  {
    nextIndex = response.indexOf('+', index);

    // If no other "+" sign is found, get the rest of the string.
    if (nextIndex == -1)
    {
      nextIndex = response.length();
    }

    value = response.substring(index, nextIndex);
    switch (counter)
    {
    case 0:
      // Address of the sensor
      break;
    case 1:
      waterContent = value.toFloat();
      break;
    case 2:
      ecPore = value.toFloat();
      break;
    case 3:
      temperature = value.toFloat();
      break;
    case 4:
      ecBulk = value.toFloat();
      break;
    case 5:
      permittivity = value.toFloat();
      break;
    default:
      break;
    }

    index = nextIndex + 1;
    counter = counter + 1;
  }
}

void setAddress()
{
  // Power the sensor.
  pinMode(WB_IO2, OUTPUT);
  digitalWrite(WB_IO2, HIGH);
  delay(3000);
  mySDI12.begin();
  delay(500);
  mySDI12.clearBuffer();
  delay(3000);
  mySDI12.sendCommand("0Aa!");
  delay(30);

  mySDI12.end();
  digitalWrite(WB_IO2, LOW);
}

void readFromSensor()
{
  // Power the sensor.
  pinMode(WB_IO2, OUTPUT);
  digitalWrite(WB_IO2, HIGH);
  delay(1500);
  mySDI12.begin();
  delay(500);
  mySDI12.clearBuffer();
  delay(500);
  mySDI12.sendCommand(String(SENSOR_ADDRESS) + sdiCommand);
  delay(30);

  // sdiResponse = mySDI12.readStringUntil('\n');
#ifndef MAX_SAVE
  Serial.println("Response TIME: " + sdiResponse);
#endif

  // Should be gather from the response
  delay(2000);

  mySDI12.clearBuffer();
  mySDI12.sendCommand(String(SENSOR_ADDRESS) + "D0!");
  sdiResponse = mySDI12.readStringUntil('\n');
#ifndef MAX_SAVE
  Serial.println("Response: " + sdiResponse);
#endif
  parseResponse(sdiResponse);

  mySDI12.end();
  digitalWrite(WB_IO2, LOW);
  digitalWrite(OE, LOW);
}

/**
   @brief Initialize LoRa HW and LoRaWan MAC layer

   @return int8_t result
    0 => OK
   -1 => SX126x HW init failure
   -2 => LoRaWan MAC initialization failure
   -3 => Subband selection failure
*/
int8_t initLoRaWan(void)
{
  // Initialize LoRa chip.
  if (lora_rak4630_init() != 0)
  {
    return -1;
  }

  // Setup the EUIs and Keys
  lmh_setDevEui(nodeDeviceEUI);
  lmh_setAppEui(nodeAppEUI);
  lmh_setAppKey(nodeAppKey);

  // Initialize LoRaWan
  if (lmh_init(&lora_callbacks, lora_param_init, true, gCurrentClass, gCurrentRegion) != 0)
  {
    return -2;
  }

  // For some regions we might need to define the sub band the gateway is listening to
  // This must be called AFTER lmh_init()
  if (!lmh_setSubBandChannels(1))
  {
    return -3;
  }

  // Start Join procedure
#ifndef MAX_SAVE
  Serial.println("Start network join request");
#endif
  lmh_join();

  return 0;
}

/**
   @brief LoRa function for handling HasJoined event.
*/
static void lorawan_has_joined_handler(void)
{
  uint32_t otaaDevAddr = lmh_getDevAddr();
#ifndef MAX_SAVE
  Serial.printf("OTAA joined and got dev address %08X\n", otaaDevAddr);
#endif

> Alessio Occhipinti:


  // Default is Class A, where the SX1262 transceiver is in sleep mode unless a package is sent
  // If switched to Class C the power consumption is higher because the SX1262 chip remains in RX mode
  // lmh_class_request(CLASS_C);

  digitalWrite(LED_CONN, LOW);

  // Now we are connected, start the timer that will wakeup the loop frequently
  taskWakeupTimer.begin(SLEEP_TIME, periodicWakeup);
  taskWakeupTimer.start();
}
/**@brief LoRa function for handling OTAA join failed
 */
static void lorawan_join_failed_handler(void)
{
  Serial.println("OVER_THE_AIR_ACTIVATION failed!");
  Serial.println("Check your EUI's and Keys's!");
  Serial.println("Check if a Gateway is in range!");
}
/**
   @brief Function for handling LoRaWan received data from Gateway

   @param app_data  Pointer to rx data
*/
static void lorawan_rx_handler(lmh_app_data_t *app_data)
{
#ifndef MAX_SAVE
  Serial.printf("LoRa Packet received on port %d, size:%d, rssi:%d, snr:%d\n",
                app_data->port, app_data->buffsize, app_data->rssi, app_data->snr);
#endif
  switch (app_data->port)
  {
  case 3:
    // Port 3 changes the command for the sensor
    if (app_data->buffsize == 1)
    {
      sdiCommandIndex = app_data->buffer[0];
      sdiCommand = "M" + String(sdiCommandIndex) + "!";
    }
    break;

  case LORAWAN_APP_PORT:
    // Copy the data into loop data buffer
    memcpy(rcvdLoRaData, app_data->buffer, app_data->buffsize);
    rcvdDataLen = app_data->buffsize;
    eventType = 0;
    // Notify task about the event
    if (taskEvent != NULL)
    {
#ifndef MAX_SAVE
      Serial.println("Waking up loop task");
#endif
      xSemaphoreGive(taskEvent);
    }
  }
}

/**
   @brief Callback for class switch confirmation

   @param Class The new class
*/
static void lorawan_confirm_class_handler(DeviceClass_t Class)
{
#ifndef MAX_SAVE
  Serial.printf("switch to class %c done\n", "ABC"[Class]);
#endif

  // Informs the server that switch has occurred ASAP
  m_lora_app_data.buffsize = 0;
  m_lora_app_data.port = LORAWAN_APP_PORT;
  lmh_send(&m_lora_app_data, LMH_UNCONFIRMED_MSG);
}

/**
   @brief Send a LoRaWan package

   @return result of send request
*/
bool sendLoRaFrame(void)
{
  if (lmh_join_status_get() != LMH_SET)
  {
    // Not joined, try again later
#ifndef MAX_SAVE
    Serial.println("Did not join network, skip sending frame");
#endif
    return false;
  }

  // KIND: ecp
  m_lora_app_data.port = 7;
  batteryVoltage = readVBAT();

  //******************************************************************
  /// \todo here some more usefull data should be put into the package
  //******************************************************************

  uint8_t buffSize = 0;
  // First byte is here for future use
  m_lora_app_data_buffer[buffSize++] = highByte(00);
  m_lora_app_data_buffer[buffSize++] = lowByte(00);
  // TODO battery voltage
  m_lora_app_data_buffer[buffSize++] = highByte((int)batteryVoltage);
  m_lora_app_data_buffer[buffSize++] = lowByte((int)batteryVoltage);
  m_lora_app_data_buffer[buffSize++] = highByte((int)(waterContent * 100));
  m_lora_app_data_buffer[buffSize++] = lowByte((int)(waterContent * 100));
  m_lora_app_data_buffer[buffSize++] = highByte((int)(temperature * 100));
  m_lora_app_data_buffer[buffSize++] = lowByte((int)(temperature * 100));
  m_lora_app_data_buffer[buffSize++] = highByte((int)(ecPore * 10));
  m_lora_app_data_buffer[buffSize++] = lowByte((int)(ecPore * 10));
  m_lora_app_data_buffer[buffSize++] = highByte((int)(SLEEP_TIME / 1000));
  m_lora_app_data_buffer[buffSize++] = lowByte((int)(SLEEP_TIME / 1000));
  m_lora_app_data_buffer[buffSize++] = highByte((int)(sdiCommandIndex));
  m_lora_app_data_buffer[buffSize++] = lowByte((int)(sdiCommandIndex));

  m_lora_app_data.buffsize = buffSize;

  lmh_error_status error = lmh_send(&m_lora_app_data, LMH_UNCONFIRMED_MSG);

  return (error == 0);
}

Hi @pico ,

It can be the quiescent current. However, you shouldn’t have this if the converter chips are disabled via EN pin using WB_IO2. When EN pins are properly configured to sleep, it should have the sleep current (both converters at 1uA).

The code you share is the .h file. Maybe you can recheck the main code and see the sleep implementation and figure out where the current might be coming from.

Hi @carlrowan, thanks for your reply.
Here is the complete version of the sketch (I’ve used the deep sleep example from the RAK4631 to implement this one):

#include <Arduino.h>
#include <SPI.h>

#include <LoRaWan-RAK4630.h>

#include "RAK13010_SDI12.h"

#define TX_PIN WB_IO6 // The pin of the SDI-12 data bus.
#define RX_PIN WB_IO5 // The pin of the SDI-12 data bus.
#define OE WB_IO4     // Output enable pin, active low.

#define SENSOR_ADDRESS 'a'

RAK_SDI12 mySDI12(RX_PIN, TX_PIN, OE);

String sdiResponse = "";
int sdiCommandIndex = 1;
String sdiCommand = "M1!";

float waterContent, ecPore, temperature, batteryVoltage, ecBulk, permittivity;

#define PIN_VBAT WB_A0
uint32_t vbat_pin = PIN_VBAT;
#define VBAT_MV_PER_LSB (0.73242188F) // 3.0V ADC range and 12 - bit ADC resolution = 3000mV / 4096
#define VBAT_DIVIDER_COMP (1.73)      // Compensation factor for the VBAT divider, depend on the board
#define PIN_LORA_DIO_1 47             // DIO1 GPIO pin for RAK4631
#define REAL_VBAT_MV_PER_LSB (VBAT_DIVIDER_COMP * VBAT_MV_PER_LSB)
#define LORAWAN_APP_DATA_BUFF_SIZE 64                                         // Max size of the data to be transmitted.
#define JOINREQ_NBTRIALS 8                                                    // Number of trials for the join request.
static uint8_t m_lora_app_data_buffer[LORAWAN_APP_DATA_BUFF_SIZE];            // Lora application data buffer.
static lmh_app_data_t m_lora_app_data = {m_lora_app_data_buffer, 0, 0, 0, 0}; // Lora application data structure.
// LoRaWan event handlers
static void lorawan_has_joined_handler(void);
static void lorawan_join_failed_handler(void);
static void lorawan_rx_handler(lmh_app_data_t *app_data);
static void lorawan_confirm_class_handler(DeviceClass_t Class);
bool sendLoRaFrame(void);

/**
 * @brief Structure containing LoRaWan parameters, needed for lmh_init()
   Set structure members to
   LORAWAN_ADR_ON or LORAWAN_ADR_OFF to enable or disable adaptive data rate
   LORAWAN_DEFAULT_DATARATE OR DR_0 ... DR_5 for default data rate or specific data rate selection
   LORAWAN_PUBLIC_NETWORK or LORAWAN_PRIVATE_NETWORK to select the use of a public or private network
   JOINREQ_NBTRIALS or a specific number to set the number of trials to join the network
   LORAWAN_DEFAULT_TX_POWER or a specific number to set the TX power used
   LORAWAN_DUTYCYCLE_ON or LORAWAN_DUTYCYCLE_OFF to enable or disable duty cycles
                     Please note that ETSI mandates duty cycled transmissions.
*/
static lmh_param_t lora_param_init = {LORAWAN_ADR_ON, DR_3, LORAWAN_PUBLIC_NETWORK, JOINREQ_NBTRIALS, LORAWAN_DEFAULT_TX_POWER, LORAWAN_DUTYCYCLE_ON};
/** Structure containing LoRaWan callback functions, needed for lmh_init() */
static lmh_callback_t lora_callbacks = {BoardGetBatteryLevel, BoardGetUniqueId, BoardGetRandomSeed, lorawan_rx_handler, lorawan_has_joined_handler, lorawan_confirm_class_handler, lorawan_join_failed_handler};

// Comment the next line if you want DEBUG output. But the power savings are not as good then!!!!!!!
#define MAX_SAVE
/* Time the device is sleeping in milliseconds = 2 minutes * 60 seconds * 1000 milliseconds */
#define SLEEP_TIME 20 * 60 * 1000

uint8_t nodeDeviceEUI[8] = {0xAC, 0x1F, 0x09, 0xFF, 0xFE, 0x08, 0xF5, 0x20};
uint8_t nodeAppEUI[8] = {0x45, 0x34, 0x52, 0x34, 0x23, 0x49, 0x20, 0x34};
uint8_t nodeAppKey[16] = {0x3A, 0xCC, 0x06, 0xB0, 0xE9, 0x13, 0xAB, 0x7C, 0x99, 0x94, 0xB6, 0x89, 0x50, 0x0C, 0xDF, 0x5B};
DeviceClass_t gCurrentClass = CLASS_A;
LoRaMacRegion_t gCurrentRegion = LORAMAC_REGION_EU868;

/** Semaphore used by events to wake up loop task */
SemaphoreHandle_t taskEvent = NULL;
/** Timer to wakeup task frequently and send message */
SoftwareTimer taskWakeupTimer;

/** Buffer for received LoRaWan data */
uint8_t rcvdLoRaData[256];
/** Length of received data */
uint8_t rcvdDataLen = 0;

/**
 * @brief Flag for the event type
 * -1 => no event
 * 0 => LoRaWan data received
 * 1 => Timer wakeup
 * 2 => tbd
 * ...
 */
uint8_t eventType = -1;

/**
 * @brief Timer event that wakes up the loop task frequently
 *
 * @param unused
 */
void periodicWakeup(TimerHandle_t unused)
{
  eventType = 1;
  // Give the semaphore, so the loop task will wake up
  xSemaphoreGiveFromISR(taskEvent, pdFALSE);
}

float readVBAT(void)
{
  // Set the analog reference to 3.0V (default = 3.6V)
  analogReference(AR_INTERNAL_3_0);
  // Set the resolution to 12-bit (0..4095)
  analogReadResolution(12); // Can be 8, 10, 12 or 14
  // Let the ADC settle
  delay(1);
  float raw;
  // Get the raw 12-bit, 0..3000mV ADC value
  raw = analogRead(vbat_pin);
  return raw * REAL_VBAT_MV_PER_LSB;
}

/*
 * Get the response as string from the SDI-12, split by "+" sign and
 * assign the values to the variables waterContent, ecPore and temperature.
 */
void parseResponse(String response)
{
  int index = 0;
  int nextIndex = 0;
  int counter = 0;
  String value = "";
  while (index < response.length())
  {
    nextIndex = response.indexOf('+', index);

    // If no other "+" sign is found, get the rest of the string.
    if (nextIndex == -1)
    {
      nextIndex = response.length();
    }

    value = response.substring(index, nextIndex);
    switch (counter)
    {
    case 0:
      // Address of the sensor
      break;
    case 1:
      waterContent = value.toFloat();
      break;
    case 2:
      ecPore = value.toFloat();
      break;
    case 3:
      temperature = value.toFloat();
      break;
    case 4:
      ecBulk = value.toFloat();
      break;
    case 5:
      permittivity = value.toFloat();
      break;
    default:
      break;
    }

    index = nextIndex + 1;
    counter = counter + 1;
  }
}

void setAddress()
{
  // Power the sensor.
  pinMode(WB_IO2, OUTPUT);
  digitalWrite(WB_IO2, HIGH);
  delay(3000);
  mySDI12.begin();
  delay(500);
  mySDI12.clearBuffer();
  delay(3000);
  mySDI12.sendCommand("0Aa!");
  delay(30);

  mySDI12.end();
  digitalWrite(WB_IO2, LOW);
}

void readFromSensor()
{
  // Power the sensor.
  pinMode(WB_IO2, OUTPUT);
  digitalWrite(WB_IO2, HIGH);
  delay(1500);
  mySDI12.begin();
  delay(500);
  mySDI12.clearBuffer();
  delay(500);
  mySDI12.sendCommand(String(SENSOR_ADDRESS) + sdiCommand);
  delay(30);

  sdiResponse = mySDI12.readStringUntil('\n');
#ifndef MAX_SAVE
  Serial.println("Response TIME: " + sdiResponse);
#endif

  // Should be gather from the response
  delay(2000);

  mySDI12.clearBuffer();
  mySDI12.sendCommand(String(SENSOR_ADDRESS) + "D0!");
  sdiResponse = mySDI12.readStringUntil('\n');
#ifndef MAX_SAVE
  Serial.println("Response: " + sdiResponse);
#endif
  parseResponse(sdiResponse);

  mySDI12.end();
  digitalWrite(WB_IO2, LOW);
}

/**
   @brief Initialize LoRa HW and LoRaWan MAC layer

   @return int8_t result
    0 => OK
   -1 => SX126x HW init failure
   -2 => LoRaWan MAC initialization failure
   -3 => Subband selection failure
*/
int8_t initLoRaWan(void)
{
  // Initialize LoRa chip.
  if (lora_rak4630_init() != 0)
  {
    return -1;
  }

  // Setup the EUIs and Keys
  lmh_setDevEui(nodeDeviceEUI);
  lmh_setAppEui(nodeAppEUI);
  lmh_setAppKey(nodeAppKey);

  // Initialize LoRaWan
  if (lmh_init(&lora_callbacks, lora_param_init, true, gCurrentClass, gCurrentRegion) != 0)
  {
    return -2;
  }

  // For some regions we might need to define the sub band the gateway is listening to
  // This must be called AFTER lmh_init()
  if (!lmh_setSubBandChannels(1))
  {
    return -3;
  }

  // Start Join procedure
#ifndef MAX_SAVE
  Serial.println("Start network join request");
#endif
  lmh_join();

  return 0;
}

/**
   @brief LoRa function for handling HasJoined event.
*/
static void lorawan_has_joined_handler(void)
{
  uint32_t otaaDevAddr = lmh_getDevAddr();
#ifndef MAX_SAVE
  Serial.printf("OTAA joined and got dev address %08X\n", otaaDevAddr);
#endif

  // Default is Class A, where the SX1262 transceiver is in sleep mode unless a package is sent
  // If switched to Class C the power consumption is higher because the SX1262 chip remains in RX mode
  // lmh_class_request(CLASS_C);

  // Now we are connected, start the timer that will wakeup the loop frequently
  taskWakeupTimer.begin(SLEEP_TIME, periodicWakeup);
  taskWakeupTimer.start();
}
/**@brief LoRa function for handling OTAA join failed
 */
static void lorawan_join_failed_handler(void)
{
  Serial.println("OVER_THE_AIR_ACTIVATION failed!");
  Serial.println("Check your EUI's and Keys's!");
  Serial.println("Check if a Gateway is in range!");
}
/**
   @brief Function for handling LoRaWan received data from Gateway

   @param app_data  Pointer to rx data
*/
static void lorawan_rx_handler(lmh_app_data_t *app_data)
{
#ifndef MAX_SAVE
  Serial.printf("LoRa Packet received on port %d, size:%d, rssi:%d, snr:%d\n",
                app_data->port, app_data->buffsize, app_data->rssi, app_data->snr);
#endif
  switch (app_data->port)
  {
  case 3:
    // Port 3 changes the command for the sensor
    if (app_data->buffsize == 1)
    {
      sdiCommandIndex = app_data->buffer[0];
      sdiCommand = "M" + String(sdiCommandIndex) + "!";
    }
    break;

  case LORAWAN_APP_PORT:
    // Copy the data into loop data buffer
    memcpy(rcvdLoRaData, app_data->buffer, app_data->buffsize);
    rcvdDataLen = app_data->buffsize;
    eventType = 0;
    // Notify task about the event
    if (taskEvent != NULL)
    {
#ifndef MAX_SAVE
      Serial.println("Waking up loop task");
#endif
      xSemaphoreGive(taskEvent);
    }
  }
}

/**
   @brief Callback for class switch confirmation

   @param Class The new class
*/
static void lorawan_confirm_class_handler(DeviceClass_t Class)
{
#ifndef MAX_SAVE
  Serial.printf("switch to class %c done\n", "ABC"[Class]);
#endif

  // Informs the server that switch has occurred ASAP
  m_lora_app_data.buffsize = 0;
  m_lora_app_data.port = LORAWAN_APP_PORT;
  lmh_send(&m_lora_app_data, LMH_UNCONFIRMED_MSG);
}

/**
   @brief Send a LoRaWan package

   @return result of send request
*/
bool sendLoRaFrame(void)
{
  if (lmh_join_status_get() != LMH_SET)
  {
    // Not joined, try again later
#ifndef MAX_SAVE
    Serial.println("Did not join network, skip sending frame");
#endif
    return false;
  }

  // KIND: ecp
  m_lora_app_data.port = 7;
  batteryVoltage = readVBAT();

  //******************************************************************
  /// \todo here some more usefull data should be put into the package
  //******************************************************************

  uint8_t buffSize = 0;
  // First byte is here for future use
  m_lora_app_data_buffer[buffSize++] = highByte(00);
  m_lora_app_data_buffer[buffSize++] = lowByte(00);
  // TODO battery voltage
  m_lora_app_data_buffer[buffSize++] = highByte((int)batteryVoltage);
  m_lora_app_data_buffer[buffSize++] = lowByte((int)batteryVoltage);
  m_lora_app_data_buffer[buffSize++] = highByte((int)(waterContent * 100));
  m_lora_app_data_buffer[buffSize++] = lowByte((int)(waterContent * 100));
  m_lora_app_data_buffer[buffSize++] = highByte((int)(temperature * 100));
  m_lora_app_data_buffer[buffSize++] = lowByte((int)(temperature * 100));
  m_lora_app_data_buffer[buffSize++] = highByte((int)(ecPore * 10));
  m_lora_app_data_buffer[buffSize++] = lowByte((int)(ecPore * 10));
  m_lora_app_data_buffer[buffSize++] = highByte((int)(SLEEP_TIME / 1000));
  m_lora_app_data_buffer[buffSize++] = lowByte((int)(SLEEP_TIME / 1000));
  m_lora_app_data_buffer[buffSize++] = highByte((int)(sdiCommandIndex));
  m_lora_app_data_buffer[buffSize++] = lowByte((int)(sdiCommandIndex));

  m_lora_app_data.buffsize = buffSize;

  lmh_error_status error = lmh_send(&m_lora_app_data, LMH_UNCONFIRMED_MSG);

  return (error == 0);
}

/**
 * @brief Arduino setup function. Called once after power-up or reset
 *
 */
void setup(void)
{
  setAddress();
  // Create the LoRaWan event semaphore
  taskEvent = xSemaphoreCreateBinary();
  // Initialize semaphore
  xSemaphoreGive(taskEvent);

  // Initialize LoRaWan and start join request
  int8_t loraInitResult = initLoRaWan();

#ifndef MAX_SAVE
  if (loraInitResult != 0)
  {
    switch (loraInitResult)
    {
    case -1:
      Serial.println("SX126x init failed");
      break;
    case -2:
      Serial.println("LoRaWan init failed");
      break;
    case -3:
      Serial.println("Subband init error");
      break;
    case -4:
      Serial.println("LoRa Task init error");
      break;
    default:
      Serial.println("LoRa init unknown error");
      break;
    }

    // Without working LoRa we just stop here
    while (1)
    {
      Serial.println("Nothing I can do, just loving you");
      delay(5000);
    }
  }
  Serial.println("LoRaWan init success");
#endif

  // // Take the semaphore so the loop will go to sleep until an event happens
  // xSemaphoreTake(taskEvent, 10);
  eventType = 1;
}

/**
 * @brief Arduino loop task. Called in a loop from the FreeRTOS task handler
 *
 */
void loop(void)
{
  // Sleep until we are woken up by an event
  if (xSemaphoreTake(taskEvent, portMAX_DELAY) == pdTRUE)
  {
    // Check the wake up reason
    switch (eventType)
    {
    case 0: // Wakeup reason is package downlink arrived
#ifndef MAX_SAVE
      Serial.println("Received package over LoRaWan");
#endif
      if (rcvdLoRaData[0] > 0x1F)
      {
#ifndef MAX_SAVE
        Serial.printf("%s\n", (char *)rcvdLoRaData);
#endif
      }
      else
      {
#ifndef MAX_SAVE
        for (int idx = 0; idx < rcvdDataLen; idx++)
        {
          Serial.printf("%X ", rcvdLoRaData[idx]);
        }
        Serial.println("");
#endif
      }

      break;
    case 1: // Wakeup reason is timer
#ifndef MAX_SAVE
      Serial.println("Timer wakeup");
#endif
      readFromSensor();
      // Send the data package
      if (sendLoRaFrame())
      {
#ifndef MAX_SAVE
        Serial.println("LoRaWan package sent successfully");
#endif
      }
      else
      {
#ifndef MAX_SAVE
        Serial.println("LoRaWan package send failed");
        /// \todo maybe you need to retry here?
#endif
      }

      break;
    default:
#ifndef MAX_SAVE
      Serial.println("This should never happen ;-)");
#endif
      break;
    }
    // Go back to sleep
    xSemaphoreTake(taskEvent, 10);
  }
}

Hi @carlrowan do you have any hint about this?

Just to test I’ve tried to compile the SDI12 sketch for the RAK3172 (v4.0.6) but it still seems not supported.

Any clue on what’s happening here? @carlrowan @beegee
I’m sorry if I’m being spammy, but I have 5 devices on the field and they are coming back one by one without battery.

Just from code side (without RAK13010, I have none at hand), I get a sleep power consumption of 23uA.

Are you using external 12V supply or generate the 12V on the RAK13010?
How did you set the jumper J12? I guess to position 1-2, as your sensors are working.

Can you measure the current when using external 12V (just for testing).

Hi @beegee, thanks for your reply.

Here is what I’ve tested:

  1. removed completely the RAK13010 and got the measurement down to 17uA

  2. added the external 12V and switched the jumper as you suggested, that lowered down the consumption to 60uA-ish with the sensor attached

Is there something broken with the internal booster?

Thanks for testing.
I reported this to our R&D for investigation.

I don’t have the module at hand, @carlrowan can you give it a try?

Hi Alessio,

We found the problem. There is a current flowing through the inductivity even if the booster is disabled.

We will redesign the module, but this will take some time. Your idea with an IO controlled p-Mosfet between VBAT and VIN of the booster is the only temporary solution.

1 Like

Thanks @beegee, we are going to try the solution with the p-Mosfet and re-test again to check if we are good to go, waiting for the fix.

We solved the issue temporarily with this approach.
The components are DTC043ZEBTL and AO3401, incapsulated withing a drop of bicompinent resin.
Base of DTC043ZEBTL is connected to IO2.


2 Likes