Add moving average for battery values in OBPSensorTask

This commit is contained in:
norbert-walter 2022-03-25 18:19:14 +01:00
parent 05f08e1699
commit 7958810e7d
7 changed files with 284 additions and 34 deletions

View File

@ -13,6 +13,7 @@
#include "NMEA0183.h"
#include "ObpNmea0183.h"
#include "OBP60ExtensionPort.h"
#include "movingAvg.h" // Lib for moving average building
// Timer Interrupts for hardware functions
void underVoltageDetection();
@ -79,6 +80,15 @@ void sensorTask(void *param){
bool AS5600_ready = false; // AS5600 initialized and ready to use
bool INA226_1_ready = false; // INA226_1 initialized and ready to use
// Create integer arrays for average building
int avgsize = 300;
constexpr int arrayBatV{300};
constexpr int arrayBatC{300};
movingAvg batV(arrayBatV);
movingAvg batC(arrayBatC);
batV.begin();
batC.begin();
// Start timer interrupts
bool uvoltage = api->getConfig()->getConfigItem(api->getConfig()->underVoltage,true)->asBoolean();
if(uvoltage == true){
@ -210,7 +220,7 @@ void sensorTask(void *param){
String shunt1 = api->getConfig()->getConfigItem(api->getConfig()->shunt1, true)->asString();
float shuntResistor = 1.0; // Default value for shunt resistor
float current = 10.0; // Default value for max. current
float maxCurrent = 10.0; // Default value for max. current
float corrFactor = 1; // Correction factor for fix calibration
if(String(powsensor1) == "INA226"){
@ -220,14 +230,19 @@ void sensorTask(void *param){
else{
api->getLogger()->logDebug(GwLog::LOG,"Modul 1 INA226 found");
shuntResistor = SHUNT_VOLTAGE / float(shunt1.toInt()); // Calculate shunt resisitor for max. shunt voltage 75mV
current = float(shunt1.toInt());
api->getLogger()->logDebug(GwLog::LOG,"Calibation INA226, Imax:%3.0fA Rs:%7.5fOhm Us:%5.3f", current, shuntResistor, SHUNT_VOLTAGE);
// ina226_1.setMaxCurrentShunt(current, shuntResistor);
maxCurrent = float(shunt1.toInt());
api->getLogger()->logDebug(GwLog::LOG,"Calibation INA226, Imax:%3.0fA Rs:%7.5fOhm Us:%5.3f", maxCurrent, shuntResistor, SHUNT_VOLTAGE);
// ina226_1.setMaxCurrentShunt(maxCurrent, shuntResistor);
ina226_1.setMaxCurrentShunt(10, 0.01); // Calibration with fix values (because the original values outer range)
corrFactor = (current / 10) * (0.001 / shuntResistor) / (current / 100); // correction factor for fix calibration
corrFactor = (maxCurrent / 10) * (0.001 / shuntResistor) / (maxCurrent / 100); // Correction factor for fix calibration
sensors.batteryVoltage = ina226_1.getBusVoltage();
sensors.batteryCurrent = ina226_1.getCurrent() * corrFactor;
sensors.batteryPower = ina226_1.getPower() * corrFactor;
// Fill average arrays with start values
for (int i=1; i<=avgsize+1; ++i) {
batV.reading(int(sensors.batteryVoltage * 100));
batC.reading(int(sensors.batteryCurrent * 10));
}
INA226_1_ready = true;
}
}
@ -394,12 +409,30 @@ void sensorTask(void *param){
if(String(powsensor1) == "INA226" && INA226_1_ready == true){
sensors.batteryVoltage = ina226_1.getBusVoltage();
sensors.batteryCurrent = ina226_1.getCurrent() * corrFactor;
sensors.batteryPower = ina226_1.getPower() * corrFactor;
// Eliminates bit jitter by zero current values
float factor = maxCurrent / 100;
if(sensors.batteryCurrent >= (-0.015 * factor) && sensors.batteryCurrent <= (0.015 * factor)){
sensors.batteryCurrent = 0;
}
// Save actual values in average arrays as integer values
batV.reading(int(sensors.batteryVoltage * 100));
batC.reading(int(sensors.batteryCurrent * 10));
// Calculate the average values for different time lines from integer values
sensors.batteryVoltage10 = batV.getAvg(10) / 100.0;
sensors.batteryVoltage60 = batV.getAvg(60) / 100.0;
sensors.batteryVoltage300 = batV.getAvg(300) / 100.0;
sensors.batteryCurrent10 = batC.getAvg(10) / 10.0;
sensors.batteryCurrent60 = batC.getAvg(60) / 10.0;
sensors.batteryCurrent300 = batC.getAvg(300) / 10.0;
sensors.batteryPower10 = sensors.batteryVoltage10 * sensors.batteryCurrent10;
sensors.batteryPower60 = sensors.batteryVoltage60 * sensors.batteryCurrent60;
sensors.batteryPower300 = sensors.batteryVoltage300 * sensors.batteryCurrent300;
// sensors.batteryPower = ina226_1.getPower() * corrFactor; // Real value
sensors.batteryPower = sensors.batteryVoltage * sensors.batteryCurrent; // Calculated power value (more stable)
}
// Send battery data to NMEA200 bus
// Send battery live data to NMEA200 bus
if(!isnan(sensors.batteryVoltage) && !isnan(sensors.batteryCurrent)){
SetN2kDCBatStatus(N2kMsg, 0, sensors.batteryVoltage, sensors.batteryCurrent, N2kDoubleNA, 1);
// SetN2kDCBatStatus(N2kMsg, 0, sensors.batteryVoltage, sensors.batteryCurrent, sensors.batteryPower, 1);
api->sendN2kMessage(N2kMsg);
}
}

View File

@ -6,6 +6,7 @@
class PageBattery : public Page
{
bool keylock = false; // Keylock
int average = 0; // Average type [0...3], 0=off, 1=10s, 2=60s, 3=300s
public:
PageBattery(CommonData &comon){
@ -13,7 +14,15 @@ class PageBattery : public Page
}
virtual int handleKey(int key){
if(key == 11){ // Code for keylock
// Change average
if(key == 1){
average ++;
average = average % 4; // Modulo 4
return 0; // Commit the key
}
// Code for keylock
if(key == 11){
keylock = !keylock; // Toggle keylock
return 0; // Commit the key
}
@ -47,13 +56,27 @@ class PageBattery : public Page
// Get voltage value
String name1 = "BatVolt"; // Value name
if(String(powsensor1) == "INA219" || String(powsensor1) == "INA226"){
value1 = commonData.data.batteryVoltage; // Value as double in SI unit
// Switch average values
switch (average) {
case 0:
value1 = commonData.data.batteryVoltage; // Real data
break;
case 1:
value1 = commonData.data.batteryVoltage10; // Average 10s
break;
case 2:
value1 = commonData.data.batteryVoltage60; // Average 60s
break;
case 3:
value1 = commonData.data.batteryVoltage300; // Average 300s
break;
default:
value1 = commonData.data.batteryVoltage; // Default
break;
}
}
else{
if(simulation == false){
value1 = 0; // No sensor data
}
else{
if(simulation == true){
value1 = 12 + float(random(0, 5)) / 10; // Simulation data
}
}
@ -63,13 +86,26 @@ class PageBattery : public Page
// Get current value
String name2 = "BatCurr"; // Value name
if(String(powsensor1) == "INA219" || String(powsensor1) == "INA226"){
value2 = commonData.data.batteryCurrent; // Value as double in SI unit
switch (average) {
case 0:
value2 = commonData.data.batteryCurrent; // Real data
break;
case 1:
value2 = commonData.data.batteryCurrent10; // Average 10s
break;
case 2:
value2 = commonData.data.batteryCurrent60; // Average 60s
break;
case 3:
value2 = commonData.data.batteryCurrent300; // Average 300s
break;
default:
value2 = commonData.data.batteryCurrent; // Default
break;
}
}
else{
if(simulation == false){
value2 = 0; // No sensor data
}
else{
if(simulation == true){
value2 = 8 + float(random(0, 10)) / 10; // Simulation data
}
}
@ -79,13 +115,26 @@ class PageBattery : public Page
// Get power value
String name3 = "BatPow"; // Value name
if(String(powsensor1) == "INA219" || String(powsensor1) == "INA226"){
value3 = commonData.data.batteryPower; // Value as double in SI unit
switch (average) {
case 0:
value3 = commonData.data.batteryPower; // Real data
break;
case 1:
value3 = commonData.data.batteryPower10; // Average 10s
break;
case 2:
value3 = commonData.data.batteryPower60; // Average 60s
break;
case 3:
value3 = commonData.data.batteryPower300; // Average 300s
break;
default:
value3 = commonData.data.batteryPower; // Default
break;
}
}
else{
if(simulation == false){
value3 = 0; // No sensor data
}
else{
if(simulation == true){
value3 = value1 * value2; // Simulation data
}
}
@ -99,7 +148,7 @@ class PageBattery : public Page
}
// Logging boat values
LOG_DEBUG(GwLog::LOG,"Drawing at PageBattery, %s: %f, %s: %f, %s: %f", name1, value1, name2, value2, name3, value3);
LOG_DEBUG(GwLog::LOG,"Drawing at PageBattery, %s: %f, %s: %f, %s: %f, Avg: %d", name1, value1, name2, value2, name3, value3, average);
// Draw page
//***********************************************************
@ -120,6 +169,52 @@ class PageBattery : public Page
}
// Clear display by call in obp60task.cpp in main loop
// Show average settings
display.setTextColor(textcolor);
display.setFont(&Ubuntu_Bold8pt7b);
switch (average) {
case 0:
display.setCursor(60, 90);
display.print("Avg: 1s");
display.setCursor(60, 180);
display.print("Avg: 1s");
display.setCursor(60, 270);
display.print("Avg: 1s");
break;
case 1:
display.setCursor(60, 90);
display.print("Avg: 10s");
display.setCursor(60, 180);
display.print("Avg: 10s");
display.setCursor(60, 270);
display.print("Avg: 10s");
break;
case 2:
display.setCursor(60, 90);
display.print("Avg: 60s");
display.setCursor(60, 180);
display.print("Avg: 60s");
display.setCursor(60, 270);
display.print("Avg: 60s");
break;
case 3:
display.setCursor(60, 90);
display.print("Avg: 300s");
display.setCursor(60, 180);
display.print("Avg: 300s");
display.setCursor(60, 270);
display.print("Avg: 300s");
break;
default:
display.setCursor(60, 90);
display.print("Avg: 1s");
display.setCursor(60, 180);
display.print("Avg: 1s");
display.setCursor(60, 270);
display.print("Avg: 1s");
break;
}
// ############### Value 1 ################
// Show name
@ -134,13 +229,17 @@ class PageBattery : public Page
display.setCursor(20, 90);
display.print(unit1); // Unit
// Show value
display.setFont(&DSEG7Classic_BoldItalic30pt7b);
display.setCursor(180, 90);
// Show bus data
display.print(value1,2); // Real value as formated string
if(String(powsensor1) != "off"){
display.print(value1,2); // Real value as formated string
}
else{
display.print("---"); // No sensor data (sensor is off)
}
// ############### Horizontal Line ################
@ -166,7 +265,12 @@ class PageBattery : public Page
display.setCursor(180, 180);
// Show bus data
display.print(value2,1); // Real value as formated string
if(String(powsensor1) != "off"){
display.print(value2,1); // Real value as formated string
}
else{
display.print("---"); // No sensor data (sensor is off)
}
// ############### Horizontal Line ################
@ -192,7 +296,12 @@ class PageBattery : public Page
display.setCursor(180, 270);
// Show bus data
display.print(value3,1); // Real value as formated string
if(String(powsensor1) != "off"){
display.print(value3,1); // Real value as formated string
}
else{
display.print("---"); // No sensor data (sensor is off)
}
// ############### Key Layout ################
@ -200,8 +309,10 @@ class PageBattery : public Page
// Key Layout
display.setTextColor(textcolor);
display.setFont(&Ubuntu_Bold8pt7b);
display.setCursor(130, 290);
if(keylock == false){
display.setCursor(10, 290);
display.print("[AVG]");
display.setCursor(130, 290);
display.print("[ <<<< " + String(commonData.data.actpage) + "/" + String(commonData.data.maxpage) + " >>>> ]");
if(String(backlightMode) == "Control by Key"){ // Key for illumination
display.setCursor(343, 290);
@ -209,6 +320,7 @@ class PageBattery : public Page
}
}
else{
display.setCursor(130, 290);
display.print(" [ Keylock active ]");
}

View File

@ -17,6 +17,15 @@ typedef struct{
double batteryVoltage = 0;
double batteryCurrent = 0;
double batteryPower = 0;
double batteryVoltage10 = 0;
double batteryCurrent10 = 0;
double batteryPower10 = 0;
double batteryVoltage60 = 0;
double batteryCurrent60 = 0;
double batteryPower60 = 0;
double batteryVoltage300 = 0;
double batteryCurrent300 = 0;
double batteryPower300 = 0;
double solarVoltage = 0;
double solarCurrent = 0;
double solarPower = 0;

View File

@ -351,7 +351,7 @@
},
{
"name": "shunt3",
"label": "Gen. Sensor",
"label": "Gen. Shunt",
"type": "list",
"default": "10",
"description": "Shunt current value [10A|50A|100A|200A|300A|400A|500A]",

View File

@ -0,0 +1,67 @@
// Arduino Moving Average Library
// https://github.com/JChristensen/movingAvg
// Copyright (C) 2018 by Jack Christensen and licensed under
// GNU GPL v3.0, https://www.gnu.org/licenses/gpl.html
#include <movingAvg.h>
// initialize - allocate the interval array
void movingAvg::begin()
{
m_readings = new int[m_interval];
}
// add a new reading and return the new moving average
int movingAvg::reading(int newReading)
{
// add each new data point to the sum until the m_readings array is filled
if (m_nbrReadings < m_interval) {
++m_nbrReadings;
m_sum += newReading;
}
// once the array is filled, subtract the oldest data point and add the new one
else {
m_sum = m_sum - m_readings[m_next] + newReading;
}
m_readings[m_next] = newReading;
if (++m_next >= m_interval) m_next = 0;
return (m_sum + m_nbrReadings / 2) / m_nbrReadings;
}
// just return the current moving average
int movingAvg::getAvg()
{
return (m_sum + m_nbrReadings / 2) / m_nbrReadings;
}
// return the average for a subset of the data, the most recent nPoints readings.
// for invalid values of nPoints, return zero.
int movingAvg::getAvg(int nPoints)
{
if (nPoints < 1 || nPoints > m_interval || nPoints > m_nbrReadings) {
return 0;
}
else {
long sum{0};
int i = m_next;
for (int n=0; n<nPoints; ++n) {
if (i == 0) {
i = m_interval - 1;
}
else {
--i;
}
sum += m_readings[i];
}
return (sum + nPoints / 2) / nPoints;
}
}
// start the moving average over again
void movingAvg::reset()
{
m_nbrReadings = 0;
m_sum = 0;
m_next = 0;
}

29
lib/obp60task/movingAvg.h Normal file
View File

@ -0,0 +1,29 @@
// Arduino Moving Average Library
// https://github.com/JChristensen/movingAvg
// Copyright (C) 2018 by Jack Christensen and licensed under
// GNU GPL v3.0, https://www.gnu.org/licenses/gpl.html
#ifndef MOVINGAVG_H_INCLUDED
#define MOVINGAVG_H_INCLUDED
class movingAvg
{
public:
movingAvg(int interval)
: m_interval{interval}, m_nbrReadings{0}, m_sum{0}, m_next{0} {}
void begin();
int reading(int newReading);
int getAvg();
int getAvg(int nPoints);
int getCount() {return m_nbrReadings;}
void reset();
int* getReadings() {return m_readings;}
private:
int m_interval; // number of data points for the moving average
int m_nbrReadings; // number of readings
long m_sum; // sum of the m_readings array
int m_next; // index to the next reading
int* m_readings; // pointer to the dynamically allocated interval array
};
#endif

View File

@ -20,7 +20,7 @@ DECLARE_INITFUNCTION(OBP60Init);
// OBP60 Task
void OBP60Task(GwApi *param);
DECLARE_USERTASK_PARAM(OBP60Task, 20000) // Need 20k RAM as stack size
DECLARE_USERTASK_PARAM(OBP60Task, 10000) // Need 25k RAM as stack size
DECLARE_CAPABILITY(obp60,true);
#endif