Mehmet Emin UğurHarun Yenisan
Published © MIT

Sustainable Energy Management for Off-Grid Sites

Smart off-grid monitoring: solar and battery data sent via LoRaWAN for real-time energy insights.

IntermediateWork in progress10 hours46
Sustainable Energy Management for Off-Grid Sites

Things used in this project

Hardware components

RAKwireless RAK7268V2
×1
RAKwireless RAK11310
×1
RAKwireless RAK19003
×1
RAKwireless RAK12500
×1

Software apps and online services

Arduino IDE
Arduino IDE
The Things Stack
The Things Industries The Things Stack
Emoncms

Hand tools and fabrication machines

Multitool, Screwdriver
Multitool, Screwdriver
Wire Stripper & Cutter, 18-10 AWG / 0.75-4mm² Capacity Wires
Wire Stripper & Cutter, 18-10 AWG / 0.75-4mm² Capacity Wires
Soldering iron (generic)
Soldering iron (generic)

Story

Read more

Code

Code

Arduino
#include <LoRaWan-RAK4631.h> // RAK LoRaWAN library (compatibility with RAK11310 should be verified)
                              // A specific LoRaWAN library for RAK11310 might be needed or this library adapted.
                              // Generally, RAK's RP2040 (RAK11310) LoRaWAN support might differ from RAK4631.
                              // This example is based on the RAK4631 LoRaWAN library; you might need to adapt it for RAK11310.

#include <Wire.h>          // For I2C communication (for RTC)
#include <RTClib.h>        // For RAK12500 RTC module (uses DS3231 chip)

// Current sensor pins
// If RAK11310(11300) is a current sensor module, it will typically have analog or I2C output.
// In this example, we assume an analog current sensor (e.g., ACS712) or similar sensor's analog output is being read.
#define SOLAR_CURRENT_PIN WB_A0    // Analog pin connected to solar panel current sensor
#define BATTERY_CURRENT_PIN WB_A1  // Analog pin connected to battery current sensor

// LoRaWAN Join Settings
// You must obtain these values from your TheThingsNetwork (TTN) console.
String node_device_eui = "AC1F09FFFE0xxxx"; // <<< CHANGE THIS
String node_app_eui = "70B3D57ED000xxxx";   // <<< CHANGE THIS
String node_app_key = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"; // <<< CHANGE THIS

// LoRaWAN Application Port
#define LORAWAN_APP_PORT 1

// Data Transmission Interval (in seconds)
#define SEND_INTERVAL_SEC 300 // Interval for sending sensor data (5 minutes)

// Sensor Objects
RTC_DS3231 rtc; // RAK12500 (DS3231-based) RTC object

// Timer Variable
time_t lastSendTime = 0; // Last data transmission time (millis())

void setup() {
  // Set internal LED as OUTPUT and turn it off initially
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, LOW); 
  
  // Start serial communication (for debug messages)
  Serial.begin(115200);
  while (!Serial); // Wait for serial port to be ready
  Serial.println("Sustainable Energy Management Starting...");

  // Set current sensor pins as input
  pinMode(SOLAR_CURRENT_PIN, INPUT);
  pinMode(BATTERY_CURRENT_PIN, INPUT);

  // Start I2C communication (for RTC)
  Wire.begin();

  // Initialize RAK12500 RTC
  if (!rtc.begin()) {
    Serial.println("RAK12500 RTC not found, check connection!");
    while (1); // Stay in infinite loop if initialization fails
  }
  // If RTC lost power (during initial setup or if battery runs out), set the time
  if (rtc.lostPower()) {
    Serial.println("RTC lost power, setting time to compile time!");
    // Set RTC to Arduino's compile time. In production systems, a more reliable time source should be used (e.g., GPS, NTP).
    rtc.adjust(DateTime(F(__DATE__), F(__TIME__))); 
  }
  Serial.println("RAK12500 RTC successfully initialized.");

  // Initialize and configure LoRaWAN module
  // For RAK11310 (RP2040), a different LoRaWAN library or configuration might be needed than for RAK4631.
  // Check RAKwireless's specific LoRaWAN libraries for RP2040.
  api.lorawan.setMcuStatus(true);         // Set MCU status
  api.lorawan.setDeviceClass(CLASS_A);    // Set device class
  api.lorawan.setRegion(EU868);           // Set LoRaWAN region
  api.lorawan.setDevEui(node_device_eui); // Set Device EUI
  api.lorawan.setAppEui(node_app_eui);   // Set Application EUI
  api.lorawan.setAppKey(node_app_key);   // Set Application Key
  api.lorawan.setConfirm(true);          // Use confirmed messages?
  api.lorawan.setAdaptiveDr(true);       // Use Adaptive Data Rate (ADR)?
  
  Serial.println("Attempting to join LoRaWAN network...");
  api.lorawan.join(); // Start LoRaWAN network join process

  // Initialize timer
  lastSendTime = millis();
}

void loop() {
  // Check LoRaWAN network connection status
  if (api.lorawan.queryNetworkStatus() == LORAWAN_JOINED) {
    // Check if it's time to send data
    if (millis() - lastSendTime > (SEND_INTERVAL_SEC * 1000)) {
      sendEnergyData();       // Send energy data
      lastSendTime = millis(); // Reset timer
    }
  } else {
    // If not connected to LoRaWAN network, try to join again...
    Serial.println("Not connected to LoRaWAN network, trying to join again...");
    api.lorawan.join(); 
    delay(10000); // Wait 10 seconds before retrying
  }
}

// Function to read energy data and send it via LoRaWAN
void sendEnergyData() {
  Serial.println("Reading energy data...");
  
  // Read solar panel current (example analog reading and conversion)
  // Adjust this conversion based on your current sensor's sensitivity and range.
  // For example, for an ACS712 5A model: 0-4095 (ADC) -> 0-5000 mV -> Convert to Amps (based on mV/A sensitivity)
  int rawSolarCurrent = analogRead(SOLAR_CURRENT_PIN);
  float solarCurrentA = map(rawSolarCurrent, 0, 4095, 0, 5000) / 1000.0; // Simple example conversion

  // Read battery current (example analog reading and conversion)
  // Offset and calibration might be needed to account for charging (+) and discharging (-) directions.
  int rawBatteryCurrent = analogRead(BATTERY_CURRENT_PIN);
  float batteryCurrentA = map(rawBatteryCurrent, 0, 4095, 0, 5000) / 1000.0; // Simple example conversion

  // Get current time from RTC
  DateTime now = rtc.now();
  // Convert timestamp to a readable String format
  String timestamp = String(now.year()) + "-" + 
                     String(now.month() < 10 ? "0" : "") + String(now.month()) + "-" + 
                     String(now.day() < 10 ? "0" : "") + String(now.day()) + " " +
                     String(now.hour() < 10 ? "0" : "") + String(now.hour()) + ":" + 
                     String(now.minute() < 10 ? "0" : "") + String(now.minute()) + ":" + 
                     String(now.second() < 10 ? "0" : "") + String(now.second());

  // Print read data to Serial Monitor
  Serial.printf("Time: %s, Solar Current: %.2f A, Battery Current: %.2f A\n", timestamp.c_str(), solarCurrentA, batteryCurrentA);

  // Package data into LoRaWAN payload
  // Payload structure: | Solar Current (float) | Battery Current (float) |
  // Directly adding RTC time as a string to the payload is not efficient for LoRaWAN.
  // Smaller, numerical formats like epoch time (long) are usually preferred.
  uint8_t payload[8]; // 2 floats * 4 bytes = 8 bytes
  memcpy(&payload[0], &solarCurrentA, sizeof(float)); // Solar Current
  memcpy(&payload[4], &batteryCurrentA, sizeof(float)); // Battery Current

  Serial.println("Sending LoRaWAN data...");
  // Send payload to LoRaWAN network. Use LORAWAN_APP_PORT and send as confirmed message (true).
  api.lorawan.send(sizeof(payload), payload, LORAWAN_APP_PORT, true); 
}

// LoRaWAN callback functions
// Downlink messages are not processed in this project, but empty functions are defined for library requirements.
void lorawan_rx_handler(lorawan_AppData_t* appData) {
  Serial.printf("Downlink received: Port %d, Length %d\n", appData->Port, appData->BuffSize);
}

void lorawan_has_joined_handler(void) {
  Serial.println("Successfully joined LoRaWAN network!");
  digitalWrite(LED_BUILTIN, HIGH); // Turn on internal LED after joining network
}

void lorawan_join_failed_handler(void) {
  Serial.println("LoRaWAN network join failed. Retrying...");
  digitalWrite(LED_BUILTIN, LOW); // Turn off internal LED if join fails
}

void lorawan_tx_handler(void) {
  Serial.println("LoRaWAN Tx Done!"); // Inform when data transmission is complete
}

Credits

Mehmet Emin Uğur
9 projects • 9 followers
Computer engineer, working as an IT teacher at the Ministry of National Education.
Harun Yenisan
8 projects • 7 followers
Electronic engineer, working as an IT teacher at the Ministry of National Education.

Comments