Skip to content

Commit

Permalink
Added PowerFeather specific features (#91)
Browse files Browse the repository at this point in the history
* Added PowerFeather specific status information to LoRaWAN node status message
* Added PowerFeather specific configuration parameters to BresserWeatherSensorLWCfg.h and LoadNodeCfg.h/.cpp
  • Loading branch information
matthias-bs committed Jul 29, 2024
1 parent 57f1ff8 commit 63a2d32
Show file tree
Hide file tree
Showing 8 changed files with 235 additions and 32 deletions.
35 changes: 26 additions & 9 deletions BresserWeatherSensorLW.ino
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@
// Moved decodeDownlink() & sendCfgUplink() to BresserWeatherSensorLWCmd.cpp/.h
// 20240725 Added reading of hardware/deployment specific configuration node_config.json
// from LittleFS (optional)
// 20240729 PowerFeather: Enabled battery temperature measurement, added specific configuration
//
// ToDo:
// -
Expand Down Expand Up @@ -225,6 +226,17 @@ ESP32Time rtc;
/// Application layer
AppLayer appLayer(&rtc, &rtcLastClockSync);

#if defined(ARDUINO_ESP32S3_POWERFEATHER)
struct sPowerFeatherCfg PowerFeatherCfg = {
.battery_capacity = BATTERY_CAPACITY_MAH,
.supply_maintain_voltage = PF_SUPPLY_MAINTAIN_VOLTAGE,
.temperature_measurement = PF_TEMPERATURE_MEASUREMENT,
.battery_fuel_gauge = PF_BATTERY_FUEL_GAUGE
};
#else
struct sPowerFeatherCfg PowerFeatherCfg = {0};
#endif

#if defined(ESP32)
/*!
* \brief Print wakeup reason (ESP32 only)
Expand Down Expand Up @@ -479,25 +491,30 @@ void setup()
uint16_t battery_low = BATTERY_LOW;
uint16_t battery_discharge_lim = BATTERY_DISCHARGE_LIM;
uint16_t battery_charge_lim = BATTERY_CHARGE_LIM;
#if defined(BATTERY_CAPACITY_MAH)
uint16_t battery_capacity_mah = BATTERY_CAPACITY_MAH;
#else
uint16_t battery_capacity_mah = 0;
#endif
// #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_lim,
battery_charge_lim,
battery_capacity_mah);
PowerFeatherCfg);

#if defined(ARDUINO_ESP32S3_POWERFEATHER)
delay(2000);
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)
// Note: Battery capacity / type has to be set for voltage measurement
Board.init(PowerFeatherCfg.battery_capacity);
Board.enable3V3(true); // Power supply for FeatherWing
Board.enableVSQT(true); // Power supply for battery management chip (voltage measurement)
Board.enableBatteryTempSense(PowerFeatherCfg.temperature_measurement); // Enable battery temperature measurement
Board.enableBatteryFuelGauge(PowerFeatherCfg.battery_fuel_gauge); // Enable battery fuel gauge
Board.setSupplyMaintainVoltage(PowerFeatherCfg.supply_maintain_voltage); // Set supply maintain voltage
Board.enableBatteryCharging(true); // Enable battery charging
#endif

#if defined(ARDUINO_ARCH_RP2040)
Expand Down
18 changes: 16 additions & 2 deletions BresserWeatherSensorLWCfg.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
// 20240722 Added LW_STATUS_INTERVAL,
// renamed STATUS_INTERVAL to APP_STATUS_INTERVAL
// 20240726 Renamed BATTERY_DISCHARGE_LIMIT/BATTERY_CHARGE_LIMIT
// 20240729 Added PowerFeather specific configuration
//
// Note:
// Depending on board package file date, some defines are written either
Expand Down Expand Up @@ -96,6 +97,16 @@
#define DFROBOT_COVER_LORA
#endif

// PowerFeather specific configuration
struct sPowerFeatherCfg {

uint16_t battery_capacity; /// Battery capacity in mAh
uint16_t supply_maintain_voltage; /// Supply voltage to maintain in mV
bool temperature_measurement; /// Enable temperature measurement
bool battery_fuel_gauge; /// Enable battery fuel gauge
};


// Uplink message payload size
// The maximum allowed for all data rates is 51 bytes.
const uint8_t PAYLOAD_SIZE = 51;
Expand Down Expand Up @@ -205,8 +216,11 @@ const uint8_t MAX_DOWNLINK_SIZE = 51;
// On-board VB
#define PIN_ADC_IN A0
#elif defined(ARDUINO_ESP32S3_POWERFEATHER)
// Set battery capacity in mAh
#define BATTERY_CAPACITY_MAH 0
// See https://docs.powerfeather.dev
#define BATTERY_CAPACITY_MAH 2200 // battery capacity in mAh
#define PF_TEMPERATURE_MEASUREMENT true // enable/diable temperature measurement
#define PF_SUPPLY_MAINTAIN_VOLTAGE 5500 // ~maximum power point (MPP) voltage if using a solar panel
#define PF_BATTERY_FUEL_GAUGE true // enable/disable battery fuel gauge
#if BATTERY_CAPACITY_MAH == 0
#pragma message("Battery capacity set to 0 - battery voltage measurement disabled.")
#endif
Expand Down
95 changes: 95 additions & 0 deletions BresserWeatherSensorLWCmd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
// History:
//
// 20240723 Extracted from BresserWeatherSensorLW.ino
// 20240729 Added PowerFeather specific status information
//
// ToDo:
// -
Expand All @@ -48,6 +49,10 @@
#include <ESP32Time.h>
#include "src/AppLayer.h"

#if defined(ARDUINO_ESP32S3_POWERFEATHER)
#include <PowerFeather.h>
using namespace PowerFeather;
#endif

/*
* From config.h
Expand Down Expand Up @@ -186,6 +191,96 @@ void sendCfgUplink(uint8_t uplinkReq, uint32_t uplinkInterval)
log_d("Device Status: U_batt=%u mV, longSleep=%u", getBatteryVoltage(), status);
encoder.writeUint16(getBatteryVoltage());
encoder.writeUint8(status);
#if defined(ARDUINO_ESP32S3_POWERFEATHER)
Result res;
uint16_t voltage;
int16_t current;
uint8_t battery_soc;
uint8_t battery_soh;
uint16_t battery_cycles;
float battery_temp;
int time_left;

res = Board.getSupplyVoltage(voltage);
if (res == Result::Ok)
{
encoder.writeUint16(voltage);
}
else
{
encoder.writeUint16(INV_UINT16);
}

res = Board.getSupplyCurrent(current);
if (res == Result::Ok)
{
encoder.writeUint16(current + 0x8000);
}
else
{
encoder.writeUint16(INV_UINT16);
}

res = Board.getBatteryCurrent(current);
if (res == Result::Ok)
{
encoder.writeUint16(current + 0x8000);
}
else
{
encoder.writeUint16(INV_UINT16);
}

res = Board.getBatteryCharge(battery_soc);
if (res == Result::Ok)
{
encoder.writeUint8(battery_soc);
}
else
{
encoder.writeUint8(INV_UINT8);
}

res = Board.getBatteryHealth(battery_soh);
if (res == Result::Ok)
{
encoder.writeUint8(battery_soh);
}
else
{
encoder.writeUint8(INV_UINT8);
}

res = Board.getBatteryCycles(battery_cycles);
if (res == Result::Ok)
{
encoder.writeUint16(battery_cycles);
}
else
{
encoder.writeUint16(INV_UINT16);
}

res = Board.getBatteryTimeLeft(time_left);
if (res == Result::Ok)
{
encoder.writeUint32(time_left + 0x80000000);
}
else
{
encoder.writeUint32(INV_UINT32);
}

res = Board.getBatteryTemperature(battery_temp);
if (res == Result::Ok)
{
encoder.writeTemperature(battery_temp);
}
else
{
encoder.writeTemperature(INV_TEMP);
}
#endif
}
else
{
Expand Down
13 changes: 10 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -313,9 +313,12 @@ File
| `SLEEP_INTERVAL`<br>`SLEEP_INTERVAL_LONG`<br>`LW_STATUS_INTERVAL`<br>`APP_STATUS_INTERVAL`<br>`WEATHERSENSOR_TIMEOUT` | Timing parameters | X | X | |
| `en_decoders` | Enabled sensor decoders (saves CPU cycles / energy) | | X | |
| `BATTERY_WEAK`<br>`BATTERY_LOW`<br>`BATTERY_DISCHARGE_LIM`<br>`BATTERY_CHARGE_LIM` | Battery voltage levels in mV | X | | X |
| `BATTERY_CAPACITY_MAH` /<br>`battery_capacity` | Battery capacity | X | | X |
| see header file | ADC's input pins, dividers and oversampling | X | | |

| &mdash; | PowerFeather specific configuration | | | |
| `BATTERY_CAPACITY_MAH` /<br>`powerfeather/battery_capacity` | see [https://docs.powerfeather.dev](https://docs.powerfeather.dev) | X | | X |
| `PF_TEMPERATURE_MEASUREMENT` / <br>`powerfeather/temperature_measurement` | see [https://docs.powerfeather.dev](https://docs.powerfeather.dev) | X | | X |
| `PF_BATTERY_FUEL_GAUGE` / <br>`powerfeather/battery_fuel_gauge` | see [https://docs.powerfeather.dev](https://docs.powerfeather.dev) | X | | X |
| `PF_SUPPLY_MAINTAIN_VOLTAGE` / <br>`powerfeather/supply_maintain_voltage` | see [https://docs.powerfeather.dev](https://docs.powerfeather.dev) | X | | X |


### Enabling Debug Output
Expand Down Expand Up @@ -682,7 +685,11 @@ The following parameters are available:
| battery_low | Voltage threshold in mV for deep-discharge protection<br>(power off) | `3200` |
| battery_discharge_lim | Discharging voltage limit in mV<br>for battery level estimation | `3200` |
| battery_charge_lim | Charging voltage limit in mV<br>for battery level estimation | `4200` |
| battery_capacity | Battery capacity in mAh<br>(curently only used by ESP32S3 PowerFeather) | `0` |
| powerfeather/ | PowerFeather specific (see [https://docs.powerfeather.dev](https://docs.powerfeather.dev)) | |
| &nbsp;&nbsp;battery_capacity | Battery capacity in mAh<br>(`0`: no battery connected) | `0` |
| &nbsp;&nbsp;supply_maintain_voltage | see [PowerFeather Docs: `setSupplyMaintainVoltage()`](https://docs.powerfeather.dev/sdk/api/mainboard#result-setsupplymaintainvoltageuint16_t-voltage) | `5500` |
| &nbsp;&nbsp;temperature_measurement | see [PowerFeather Docs: `setSupplyMaintainVoltage(enableBatteryTempSense)`](https://docs.powerfeather.dev/sdk/api/mainboard#result-enablebatterytempsensebool-enable) | `true` |
| &nbsp;&nbsp;battery_fuel_gauge | see [PowerFeather Docs: `enableBatteryFuelGauge()`](https://docs.powerfeather.dev/sdk/api/mainboard#result-enablebatteryfuelgaugebool-enable) | `true` |


Modify the example [data/node_config.json](data/node_config.json) as required and install it to the board's Flash memory using [earlephilhower/arduino-littlefs-upload](https://github.com/earlephilhower/arduino-littlefs-upload).
Expand Down
8 changes: 6 additions & 2 deletions data/node_config.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,9 @@
"battery_low": 3201,
"battery_discharge_lim": 3201,
"battery_charge_lim": 4201,
"battery_capacity": 2201
}
"powerfeather": {
"battery_capacity": 2201,
"temperature_measurement": true,
"battery_fuel_gauge": true
}
}
59 changes: 50 additions & 9 deletions scripts/uplink_formatter.js
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@
// 20240716 Added CMD_SCAN_SENSORS
// 20240722 Added CMD_SET_LW_STATUS_INTERVAL, modified CMD_GET_LW_CONFIG,
// renamed CMD_SET_STATUS_INTERVAL to CMD_SET_APP_STATUS_INTERVAL
// 20240729 Added PowerFeather specific status information
//
// ToDo:
// -
Expand All @@ -159,6 +160,9 @@ function decoder(bytes, port) {
// Compatibility mode: create "status" as in BresserweatherSensorTTN
const COMPATIBILITY_MODE = false;

// Enable PowerFeather specific information in LoRaWAN Node Status message
const POWERFEATHER = false;

const CMD_GET_DATETIME = 0x20;
const CMD_GET_LW_CONFIG = 0x36;
const CMD_GET_LW_STATUS = 0x38;
Expand Down Expand Up @@ -335,6 +339,30 @@ function decoder(bytes, port) {
};
uint32BE.BYTES = 4;

var int16 = function (bytes) {
if (bytes.length !== int16.BYTES) {
throw new Error('int must have exactly 2 bytes');
}
let res = bytesToInt(bytes);
if (SKIP_INVALID_SIGNALS && res === 0xFFFF) {
return NaN;
}
return res - 0x8000;
};
int16.BYTES = 2;

var int32 = function (bytes) {
if (bytes.length !== int32.BYTES) {
throw new Error('int must have exactly 4 bytes');
}
let res = bytesToInt(bytes);
if (SKIP_INVALID_SIGNALS && res === 0xFFFF) {
return NaN;
}
return res - 0x80000000;
};
int32.BYTES = 4;

function byte2hex(byte) {
return ('0' + byte.toString(16)).slice(-2);
}
Expand Down Expand Up @@ -603,6 +631,8 @@ function decoder(bytes, port) {
uint8: uint8,
uint16: uint16,
uint32: uint32,
int16: int16,
int32: int32,
uint16BE: uint16BE,
uint32BE: uint32BE,
mac48: mac48,
Expand Down Expand Up @@ -732,14 +762,25 @@ function decoder(bytes, port) {
]
);
} else if (port === CMD_GET_LW_STATUS) {
return decode(
port,
bytes,
[uint16, uint8
],
['ubatt_mv', 'long_sleep'
]
);
if (POWERFEATHER) {
return decode(
port,
bytes,
[uint16, uint8, uint16, int16, int16, uint8, uint8, uint16, int32, temperature
],
['ubatt_mv', 'long_sleep', 'usupply_mv', 'isupply_ma', 'ibatt_ma', 'soc', 'soh', 'batt_cycles', 'Tbatt_min', 'batt_temp_c'
]
);
} else {
return decode(
port,
bytes,
[uint16, uint8
],
['ubatt_mv', 'long_sleep'
]
);
}
} else if (port === CMD_GET_WS_TIMEOUT) {
return decode(
port,
Expand Down Expand Up @@ -811,7 +852,7 @@ function decoder(bytes, port) {
]
);
} else if (port === CMD_SCAN_SENSORS) {
return {'found_sensors': found_sensors(bytes)};
return { 'found_sensors': found_sensors(bytes) };
}
else if (port === CMD_GET_SENSORS_STAT) {
return decode(
Expand Down
Loading

0 comments on commit 63a2d32

Please sign in to comment.