Skip to content

Commit

Permalink
Added reading of hardware/deployment specific configuration node_conf…
Browse files Browse the repository at this point in the history
…ig.json from LittleFS
  • Loading branch information
matthias-bs committed Jul 25, 2024
1 parent e59c7fd commit ff42d2d
Show file tree
Hide file tree
Showing 4 changed files with 225 additions and 18 deletions.
54 changes: 36 additions & 18 deletions BresserWeatherSensorLW.ino
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@
// 20240722 Added periodic uplink of LoRaWAN node status messages
// 20240723 Moved loadSecrets() to LoadSecrets.cpp/.h
// Moved decodeDownlink() & sendCfgUplink() to BresserWeatherSensorLWCmd.cpp/.h
// 20240725 Added reading of hardware/deployment specific configuration node_config.json
// from LittleFS (optional)
//
// ToDo:
// -
Expand Down Expand Up @@ -172,13 +174,13 @@ using namespace PowerFeather;
#include "BresserWeatherSensorLWCfg.h"
#include "BresserWeatherSensorLWCmd.h"
#include "src/LoadSecrets.h"
#include "src/LoadNodeCfg.h"
#include "src/AppLayer.h"
#include "src/adc/adc.h"

// Time zone info
const char *TZ_INFO = TZINFO_STR;


// Variables which must retain their values after deep sleep
#if defined(ESP32)
// Stored in RTC RAM
Expand Down Expand Up @@ -245,7 +247,6 @@ void print_wakeup_reason()
}
#endif


/*!
* \brief Compute sleep duration
*
Expand All @@ -261,14 +262,14 @@ void print_wakeup_reason()
*
* \returns sleep duration in seconds
*/
uint32_t sleepDuration(void)
uint32_t sleepDuration(uint16_t battery_weak)
{
uint32_t sleep_interval = prefs.sleep_interval;
longSleep = false;

uint16_t voltage = getBatteryVoltage();
// Long sleep interval if battery is weak
if (voltage && voltage <= BATTERY_WEAK)
if (voltage && voltage <= battery_weak)
{
sleep_interval = prefs.sleep_interval_long;
longSleep = true;
Expand All @@ -289,7 +290,6 @@ uint32_t sleepDuration(void)
return sleep_interval;
}


#if defined(ESP32)
/*!
* \brief Enter sleep mode (ESP32 variant)
Expand Down Expand Up @@ -351,7 +351,6 @@ void gotoSleep(uint32_t seconds)
}
#endif


/// Print date and time (i.e. local time)
void printDateTime(void)
{
Expand All @@ -364,7 +363,6 @@ void printDateTime(void)
log_i("%s", tbuf);
}


/*!
* \brief Activate node by restoring session or otherwise joining the network
*
Expand Down Expand Up @@ -472,17 +470,36 @@ void setup()
M5.begin(cfg);
#endif

Serial.begin(115200);
delay(2000); // give time to switch to the serial monitor
log_i("Setup");

String timeZoneInfo(TZ_INFO);
uint16_t battery_weak = BATTERY_WEAK;
uint16_t battery_low = BATTERY_LOW;
uint16_t battery_discharge_limit = BATTERY_DISCHARGE_LIMIT;
uint16_t battery_charge_limit = BATTERY_CHARGE_LIMIT;
#if defined(BATTERY_CAPACITY_MAH)
uint16_t battery_capacity_mah = BATTERY_CAPACITY_MAH;
#else
uint16_t battery_capacity_mah = 0;
#endif

loadNodeCfg(
timeZoneInfo,
battery_weak,
battery_low,
battery_discharge_limit,
battery_charge_limit,
battery_capacity_mah);

#if defined(ARDUINO_ESP32S3_POWERFEATHER)
delay(2000);
Board.init(BATTERY_CAPACITY_MAH); // Note: Battery capacity / type has to be set for voltage measurement
Board.init(battery_capacity_mah); // Note: Battery capacity / type has to be set for voltage measurement
Board.enable3V3(true); // Power supply for FeatherWing
Board.enableVSQT(true); // Power supply for battery management chip (voltage measurement)
#endif

Serial.begin(115200);
delay(2000); // give time to switch to the serial monitor
log_i("Setup");

#if defined(ARDUINO_ARCH_RP2040)
// see pico-sdk/src/rp2_common/hardware_rtc/rtc.c
rtc_init();
Expand Down Expand Up @@ -519,7 +536,7 @@ void setup()
}

// Set time zone
setenv("TZ", TZ_INFO, 1);
setenv("TZ", timeZoneInfo.c_str(), 1);
printDateTime();

// Try to load LoRaWAN secrets from LittleFS file, if available
Expand All @@ -538,10 +555,10 @@ void setup()
preferences.end();

uint16_t voltage = getBatteryVoltage();
if (voltage && voltage <= BATTERY_LOW)
if (voltage && voltage <= battery_low)
{
log_i("Battery low!");
gotoSleep(sleepDuration());
gotoSleep(sleepDuration(battery_weak));
}

// build payload byte array (+ reserve to prevent overflow with configuration at run-time)
Expand Down Expand Up @@ -575,14 +592,14 @@ void setup()
{
battLevel = 255;
}
else if (voltage > BATTERY_CHARGE_LIMIT)
else if (voltage > battery_charge_limit)
{
battLevel = 0;
}
else
{
battLevel = static_cast<uint8_t>(
static_cast<float>(voltage - BATTERY_DISCHARGE_LIMIT) / static_cast<float>(BATTERY_CHARGE_LIMIT - BATTERY_DISCHARGE_LIMIT) * 255);
static_cast<float>(voltage - battery_discharge_limit) / static_cast<float>(battery_charge_limit - battery_discharge_limit) * 255);
battLevel = (battLevel == 0) ? 1 : battLevel;
battLevel = (battLevel == 255) ? 254 : battLevel;
}
Expand Down Expand Up @@ -614,6 +631,7 @@ void setup()
appStatusUplinkPending = true;
}

// Set lwStatusUplink flag if required
if (prefs.lw_stat_interval && (fCntUp % prefs.lw_stat_interval == 0))
{
lwStatusUplinkPending = true;
Expand Down Expand Up @@ -763,7 +781,7 @@ void setup()
memcpy(LWsession, persist, RADIOLIB_LORAWAN_SESSION_BUF_SIZE);

// wait until next uplink - observing legal & TTN Fair Use Policy constraints
gotoSleep(sleepDuration());
gotoSleep(sleepDuration(battery_weak));
}

// The ESP32 wakes from deep-sleep and starts from the very beginning.
Expand Down
8 changes: 8 additions & 0 deletions data/node_config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"timezone": "CET-1CEST,M3.5.0,M10.5.0/3",
"battery_weak": 3301,
"battery_low": 3401,
"battery_discharge_lim": 3201,
"battery_charge_lim": 4201,
"battery_capacity": 2201
}
110 changes: 110 additions & 0 deletions src/LoadNodeCfg.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
///////////////////////////////////////////////////////////////////////////////
// LoadNodeCfg.cpp
//
// Load LoRaWAN node configuration 'node_config.json' from LittleFS, if available
//
// This configuration file is intended for hardware/deployment environment
// specific settings (e.g. battery voltage levels, timezone)
//
// created: 07/2024
//
//
// MIT License
//
// Copyright (c) 2024 Matthias Prinke
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
//
// History:
//
// 20240725 Created
//
// ToDo:
// -
//
///////////////////////////////////////////////////////////////////////////////

#include "LoadNodeCfg.h"

// Load LoRaWAN node configuration 'node_config.json' from LittleFS, if available
void loadNodeCfg(
String &tzinfo,
uint16_t &batt_weak,
uint16_t &batt_low,
uint16_t &batt_discharge_lim,
uint16_t &batt_charge_lim,
uint16_t &batt_capacity)
{
uint16_t foo = 42;

if (!LittleFS.begin(
#if defined(ESP32)
// Format the LittleFS partition on error; parameter only available for ESP32
true
#endif
))
{
log_d("Could not initialize LittleFS.");
}
else
{
File file = LittleFS.open("/node_config.json", "r");

if (!file)
{
log_i("File 'node_config.json' not found.");
}
else
{
log_d("Reading 'node_config.json'");
JsonDocument doc;

// Deserialize the JSON document
DeserializationError error = deserializeJson(doc, file);
if (error)
{
log_d("Failed to read JSON file, using defaults.");
}
else
{
if (doc.containsKey("timezone"))
tzinfo = doc["timezone"].as<String>();
if (doc.containsKey("battery_weak"))
batt_weak = doc["battery_weak"];
if (doc.containsKey("battery_low"))
batt_low = doc["battery_low"];
if (doc.containsKey("battery_discharge_lim"))
batt_discharge_lim = doc["battery_discharge_lim"];
if (doc.containsKey("battery_charge_lim"))
batt_charge_lim = doc["battery_charge_lim"];
if (doc.containsKey("battery_capacity"))
batt_capacity = doc["battery_capacity"];
} // deserializeJson o.k.
} // file read o.k.
file.close();
} // LittleFS o.k.

log_d("Timezone: %s", tzinfo.c_str());
log_d("Battery weak: %4d mV", batt_weak);
log_d("Battery low: %4d mV", batt_low);
log_d("Battery discharge limit: %4d mV", batt_discharge_lim);
log_d("Battery charge limit: %4d mV", batt_charge_lim);
log_d("Battery capacity: %4d mAh", batt_capacity);
} // loadNodeCfg()
71 changes: 71 additions & 0 deletions src/LoadNodeCfg.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
///////////////////////////////////////////////////////////////////////////////
// LoadNodeCfg.cpp
//
// Load LoRaWAN node configuration 'node_config.json' from LittleFS, if available
//
// This configuration file is intended for hardware/deployment environment
// specific settings (e.g. battery voltage levels, timezone)
//
// created: 07/2024
//
//
// MIT License
//
// Copyright (c) 2024 Matthias Prinke
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
//
// History:
//
// 20240725 Created
//
// ToDo:
// -
//
///////////////////////////////////////////////////////////////////////////////

#include <Arduino.h>
#include <LittleFS.h>
#include <ArduinoJson.h>
#include "logging.h"

/*!
* \brief Load LoRaWAN node configuration 'node_config.json' from LittleFS, if available
*
* Returns all values by reference. Keeps the original value(s) if file not found,
* cannot be parsed or any value is missing.
*
* JSON file format:
* {
* "timezone": "CET-1CEST,M3.5.0,M10.5.0/3",
* "battery_weak": 3300,
* "battery_low": 3400,
* "battery_discharge_lim": 3200,
* "battery_charge_lim": 4200,
* "battery_capacity": 2200
* }
*/
void loadNodeCfg(
String &tzinfo,
uint16_t &batt_weak,
uint16_t &batt_low,
uint16_t &batt_discharge_lim,
uint16_t &batt_charge_lim,
uint16_t &batt_capacity);

0 comments on commit ff42d2d

Please sign in to comment.