I was wondering if there is a way to disable certain pins on the esp32 such as my i2c and uart pins as during deep sleep, they are still pulling current which is not ideal. I have tried using rtc_gpio_isolate and. disabling the pullup and pulldown pins and I am just getting really confused. Any advice will be appreciated, I have attached my code below:
#include <Arduino.h>
#include <U8x8lib.h>
#include <Wire.h>
#include <string.h>
#include <cstring>
#include "GPSManager.h"
#include "SDManager.h"
#include "SensorReader.h"
#include "SensorStruct.h"
// Minimum GPS accuracy required for data recording
const int GPS_ACCURACY = 6;
// Set to true to enable GPSECHO for debugging
#define GPSECHO false
// Pin definitions
const uint8_t BUTTON_PIN = 2;
const int SD_CARD_PIN = 3;
const int SD_CARD_CS_PIN = D2;
const int TEMP_WIRE_BUS = 1;
// Constants for LoRaWAN configuration
static char recv_buf[512];
static bool is_exist = false;
static bool is_join = false;
// Initialize manager objects
GPSManager gpsManager;
SDManager sdManager(SD_CARD_PIN, SD_CARD_CS_PIN);
SensorData sensorData;
SensorReader sensorReader(TEMP_WIRE_BUS);
// RTC-backed variables for persistent storage
RTC_DATA_ATTR unsigned int bootCount = 0;
RTC_DATA_ATTR char dataBuffer[1000];
RTC_DATA_ATTR unsigned int bufferIndex = 0;
RTC_DATA_ATTR unsigned int bufferWriteCount = 0;
// Conversion factors for sleep timing, GPS Timeout and Buffer for SD Card
#define CONVERT_S_TO_US 1000000ULL
#define CONVERT_M_TO_S 60
#define CONVERT_H_TO_S 3600
#define CONVERT_D_TO_S 86400
#define GPS_TIME_LOCK 120000
#define BUFFER_THRESHOLD 0.7
#define TIME_TO_SLEEP 30
RTC_DATA_ATTR unsigned long long sleepDuration = TIME_TO_SLEEP * CONVERT_S_TO_US;
// Function to send AT commands to the LoRaWAN module and check the response
static int at_send_check_response(const char* expected_ack, int timeout_ms, const char* command_format, ...)
{
memset(recv_buf, 0, sizeof(recv_buf));
// Print the command to both Serial1 and Serial
va_list args;
va_start(args, command_format);
Serial1.printf(command_format, args);
Serial.printf(command_format, args);
va_end(args);
// Wait a short delay for the command to be sent
delay(200);
// If no expected_ack, return immediately
if (expected_ack == nullptr)
{
return 0;
}
// Read the response from Serial1 and check for the expected_ack
unsigned long start_time = millis();
while (millis() - start_time < timeout_ms)
{
while (Serial1.available() > 0)
{
recv_buf[strlen(recv_buf)] = static_cast<char>(Serial1.read());
// Serial.print(recv_buf[strlen(recv_buf) - 1]);
delay(2);
}
if (strstr(recv_buf, expected_ack) != nullptr)
{
return 1;
}
}
// Timeout reached without finding the expected_ack
return 0;
}
// Function to parse the received data from the LoRaWAN module
static void recv_parse(char *message, SensorData& sensorData)
{
if (message == NULL)
{
return;
}
int port = 0;
unsigned long timing = 0;
int rssi = 0;
int snr = 0;
// Parse the port number from the received message
char *start = strstr(message, "PORT:");
if (start && (1 == sscanf(start, "PORT: %d;", &port)))
{
// Parse the timing value from the received message
start = strstr(message, "RX:");
if (start && (1 == sscanf(start, "RX: \"%lx\"", &timing)))
{
// Go through the different ports which indicate a different timing slot
switch (port)
{
case 1:
// Convert to minutes
sleepDuration = timing * CONVERT_M_TO_S * CONVERT_S_TO_US;
break;
case 2:
// Convert to hours
sleepDuration = timing * CONVERT_H_TO_S * CONVERT_S_TO_US;
break;
case 3:
sleepDuration = timing * CONVERT_D_TO_S * CONVERT_S_TO_US;
break;
}
}
}
// Parse the RSSI value from the received message
start = strstr(message, "RSSI");
if (start && (1 == sscanf(start, "RSSI %d,", &rssi)))
{
sensorData.rssi = rssi;
}
// Parse the SNR value from the received message
start = strstr(message, "SNR");
if (start && (1 == sscanf(start, "SNR %d", &snr)))
{
sensorData.snr = snr;
}
}
// Method to initialize LoRaWAN module and configure parameters
bool initaliseLoRaWAN()
{
// Check if E5 Module exists
if(at_send_check_response("+AT: OK", 100, "AT\r\n"))
{
// Configure LoRaWAN parameters
const int TIMEOUT_SHORT = 1000;
const int TIMEOUT_LONG = 12000;
// Configure parameters
at_send_check_response("+ID:", TIMEOUT_SHORT, "AT+ID\r\n"); // Get device ID
at_send_check_response("+MODE: LWOTAA", TIMEOUT_SHORT, "AT+MODE=LWOTAA\r\n"); // Select LoRaWAN mode
at_send_check_response("+DR: EU868", TIMEOUT_SHORT, "AT+DR=EU868\r\n"); // Select frequency band
at_send_check_response("+CH: NUM", TIMEOUT_SHORT, "AT+CH=NUM,0-2\r\n"); // Select sub band
at_send_check_response("+ID: DevEui", TIMEOUT_SHORT, "AT+ID=DevEui,\"70B3D57ED00655FB\"\r\n"); // Write device EUI
at_send_check_response("+ID: AppEui", TIMEOUT_SHORT, "AT+ID=AppEui,\"0128944F83E7AF55\"\r\n"); // Write application EUI
at_send_check_response("+KEY: APPKEY", TIMEOUT_SHORT, "AT+KEY=APPKEY,\"7A48DD0E7F43A8FC53A5F018087C9508\"\r\n"); // Write application key
at_send_check_response("+CLASS: C", TIMEOUT_SHORT, "AT+CLASS=A\r\n"); // Write device class
at_send_check_response("+PORT: 8", TIMEOUT_SHORT, "AT+PORT=8\r\n"); // Set port number
at_send_check_response("+ADR: ON", TIMEOUT_SHORT, "AT+ADR=ON\r\n"); // Enable Adaptive Data Rate
// Delay for stability
delay(200);
// Attempt to join the LoRaWAN network
return at_send_check_response("+JOIN: Network joined", TIMEOUT_LONG, "AT+JOIN\r\n");
}
// If the LoRa Module cannot be seen, return false
return false;
}
bool sendDataOverLoRaWAN(SensorData& sensorData)
{
// Convert sensor values to integers for transmission
int temp_int = static_cast<uint32_t>(sensorData.temp * 10);
int ph_int = static_cast<uint8_t>(sensorData.ph * 10);
int tds_int = static_cast<uint16_t>(sensorData.tds * 100);
int turbidity_int = static_cast<uint16_t>(sensorData.turbidity * 100);
int ir_int = static_cast<uint16_t>(sensorData.ir);
int visible_int = static_cast<uint16_t>(sensorData.visible);
int latitude_int = static_cast<uint64_t>(sensorData.latitude * 1000000);
int longitude_int = static_cast<uint64_t>(sensorData.longitude * 1000000);
int speed_int = static_cast<uint16_t>(sensorData.speed * 100);
int angle_int = static_cast<uint16_t>(sensorData.angle * 100);
int altitude_int = static_cast<uint16_t>(sensorData.altitude * 100);
int satellites_int = sensorData.satellites;
int fix_int = sensorData.fix;
int quality_int = sensorData.quality;
// Construct the uplink message in hexadecimal format
char uplink[128];
sprintf(uplink, "AT+CMSGHEX=\"%08X%02X%04X%04X%04X%04X%016X%016X%04X%04X%04X%02X%02X%02X\"\r\n",
temp_int, ph_int, tds_int, turbidity_int, ir_int, visible_int,
latitude_int, longitude_int, speed_int, angle_int, altitude_int, satellites_int, fix_int, quality_int);
// Check if the uplink has gone through. Will always come back with a fail
at_send_check_response("Done", 5000, uplink);
// Send a zero-length payload to allow the downlink to be read
char downlink[128];
sprintf(downlink, "AT+CMSGHEX");
if (at_send_check_response("Done", 12000, downlink))
{
recv_parse(recv_buf, sensorData);
return true; // If the Data has been sent return true
}
return false; // If the Data has not been sent return false
}
// Function to add data to the buffer and write to the SD card if necessary
void addToBufferAndWriteToFile(const String& dataString)
{
size_t availableSpace = sizeof(dataBuffer) - bufferIndex - 1;
// If buffer is full or dataString cannot fit in the remaining space
if (bufferWriteCount >= BUFFER_THRESHOLD * 100 || dataString.length() > availableSpace)
{
resetBuffer(); // Write buffer to SD card and reset
}
// Determine how much of dataString can fit in the remaining space
size_t copyLength = min(dataString.length(), availableSpace);
// Copy dataString to buffer
strncpy(dataBuffer + bufferIndex, dataString.c_str(), copyLength);
bufferIndex += copyLength;
dataBuffer[bufferIndex++] = '\n'; // Add newline
dataBuffer[bufferIndex] = '\0'; // Null terminate the string
// Increment buffer write count
bufferWriteCount++;
}
// Function to write the buffer to the SD card and reset the buffer
void resetBuffer()
{
const char* DATA_FILE_PATH = "/SensorData.txt";
sdManager.initialise();
bool written = sdManager.writeToFile(DATA_FILE_PATH, dataBuffer);
if (written)
{
memset(dataBuffer, 0, sizeof(dataBuffer));
bufferIndex = 0;
bufferWriteCount = 0;
} else
{
size_t copyLength = bufferIndex; // Keep track of current data length
// Check if there's enough space to accommodate both existing and new data
if (copyLength + bufferIndex < sizeof(dataBuffer))
{
// Shift existing data to make room for new data
memmove(dataBuffer, dataBuffer + copyLength, sizeof(dataBuffer) - copyLength);
// Reset buffer index and write count
bufferIndex = copyLength;
bufferWriteCount++;
} else
{
// Handle case where buffer is not large enough to accommodate both data
// For simplicity the buffer is cleared
bufferIndex = 0;
bufferWriteCount = 0;
}
}
}
void setup() {
// Take some time to open up the Serial Monitor
Serial.begin(115200);
delay(1000);
// Begin serial communication to allow for LoRa communication
Serial1.begin(9600, SERIAL_8N1, 44, 43);
//Initialise the sensors
Wire.begin();
sensorReader.initialise();
gpsManager.setup();
++bootCount;
Serial.println("Boot number: " + String(bootCount));
unsigned long startTime = millis(); // Get the start time
// Wait for GPS to acquire satellites
while (sensorData.satellites == 0.0)
{
gpsManager.update(sensorData);
// Check if 2 minutes have elapsed
if (millis() - startTime >= GPS_TIME_LOCK)
{
//Satellite search timed out
break; // Exit the loop if 2 minutes have elapsed
}
}
// Read sensor values
sensorReader.readSensorValues(sensorData);
// Attempt to initialize LoRaWAN module and send data if initialization succeeds
sensorData.loraSent = initaliseLoRaWAN() && sendDataOverLoRaWAN(sensorData);
String dataString = sensorReader.getSensorValuesAsString(sensorData);
addToBufferAndWriteToFile(dataString);
Serial.println(dataBuffer);
// Disable the I2C, UART, and other RTC peripherals before going to sleep
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_OFF);
// Enable deep sleep mode
esp_sleep_enable_timer_wakeup(sleepDuration);
Serial.println("Going to sleep for " + String(sleepDuration / CONVERT_S_TO_US) + " seconds");
Serial.flush();
esp_deep_sleep_start();
}
void loop()
{
// This loop is empty as the device will be in deep sleep mode
}