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);
}