Added gauge, fixed shit
Some checks failed
Test compiling project / test (push) Failing after 1m30s

This commit is contained in:
2024-10-24 22:54:54 +02:00
parent 57b1ce8db7
commit db567eeaa5
14 changed files with 413 additions and 223 deletions

27
data/gauge.js Normal file

File diff suppressed because one or more lines are too long

View File

@@ -82,10 +82,59 @@ window.addEventListener('DOMContentLoaded', (event) => {
});
}
fetchData(); // fetch immediately on page load
setInterval(fetchData, 5000); // fetch every 5 seconds
});
function roundToTwo(num) {
return Math.round(num*100)/100;
}
}
window.addEventListener('load', function () {
var gauge = new RadialGauge({
renderTo: 'main_gauge',
width: 300,
height: 300,
units: "l",
minValue: 0,
maxValue: 220,
majorTicks: [
"0",
"20",
"40",
"60",
"80",
"100",
"120",
"140",
"160",
"180",
"200",
"220"
],
minorTicks: 2,
strokeTicks: true,
highlights: [
{
"from": 160,
"to": 220,
"color": "rgba(200, 50, 50, .75)"
}
],
colorPlate: "#fff",
borderShadowWidth: 0,
borders: false,
needleType: "arrow",
needleWidth: 2,
needleCircleSize: 7,
needleCircleOuter: true,
needleCircleInner: false,
animationDuration: 1500,
animationRule: "linear"
}).draw();
})
// document.body.appendChild(gauge.options.renderTo);

View File

@@ -25,22 +25,22 @@
<legend>Settings</legend>
<p>
<label for="range">Sensor Range in cm</label>
<input id="range" name="range" type="number" placeholder="200">
<input id="range" name="sensor_range" type="number" placeholder="200" value="%sensor_range%">
</p>
<p>
<label for="level_max">Water max height in cm</label>
<input id="level_max" name="level_max" type="number">
<input id="level_max" name="water_level_max" type="number" value="%water_level_min%">
</p>
<p>
<label for="level_min">Water min height in cm</label>
<input id="level_min" name="level_min" type="number">
<input id="level_min" name="water_level_min" type="number" value="%water_level_max%">
</p>
<p>
<label for="liters">Volume in liters</label>
<input id="liters" name="liters" type="number">
<input id="liters" name="water_volume" type="number" value="%water_volume%">
</p>
<p>
<button class="button primary" style="width: 100%;">Submit</button>
<button class="button primary" style="width: 100%%;">Submit</button>
</p>
</fieldset>
</form>
@@ -55,15 +55,15 @@
<p>
<label for="ssid">SSID</label>
<input id="ssid" name="ssid" type="text">
<input id="ssid" name="ssid" type="text" value="%sensor_range%">
</p>
<p>
<label for="wifi_password">Password</label>
<input id="wifi_password" name="wifi_password" type="password">
<input id="wifi_password" name="wifi_password" type="password" value="%sensor_range%">
</p>
<p>
<button class="button primary" style="width: 100%;">Submit</button>
<button class="button primary" style="width: 100%%;">Submit</button>
</p>
</fieldset>
</form>

View File

@@ -2,7 +2,10 @@
<head>
<link rel="stylesheet" href="/chota.css">
<script src="/gauge.js"></script>
<script src="/logic.js"></script>
</head>
<body>
@@ -70,13 +73,7 @@
<div class="col-12 col-6-lg">
<div class="outer-wrapper">
<div class="column-wrapper">
<div class="column"></div>
</div>
<div class="percentage">25%</div>
<div class="value">2500 / 10000</div>
</div>
<canvas data-type="radial-gauge" id="main_gauge"></canvas>
</div>

View File

@@ -0,0 +1,18 @@
meta {
name: Update Sensor Settings
type: http
seq: 2
}
post {
url: http://192.168.5.181/update_sensor_settings
body: multipartForm
auth: none
}
body:multipart-form {
sensor_range: 200
water_level_max: 190
water_level_min: 10
water_volume: 1000
}

View File

@@ -0,0 +1,9 @@
{
"version": "1",
"name": "Waterlevel",
"type": "collection",
"ignore": [
"node_modules",
".git"
]
}

View File

@@ -0,0 +1,9 @@
// Define keys to prevent typos
#define ssid_key "ssid"
#define wifi_password_key "wifi_password"
#define level_sensor_range_key "sensor_range"
#define water_level_min_key "water_level_min"
#define water_level_max_key "water_level_max"
#define water_volume_key "water_volume"
#define RESISTOR_VALUE 4

View File

@@ -0,0 +1,10 @@
#include "global_data.h"
NetworkData wifi_data;
NetworkData ethernet_data;
DeviceTelemetry telemetry;
SensorData shunt_data;
WaterData water_data;
ActiveErrors active_errors = { false, false, false, false, false, false };

View File

@@ -0,0 +1,38 @@
#include <Arduino.h>
struct SensorData {
float bus_voltage;
float shunt_voltage;
float shunt_current;
};
struct WaterData{
// Water level in cm
float level;
// Water volume in liters
float liters;
// Percentage
float percentage;
};
struct NetworkData {
String ip_address;
bool link;
float rssi;
String network_name;
};
struct DeviceTelemetry {
float heap_used_percent;
int uptime_seconds;
};
struct ActiveErrors {
bool voltage_low;
bool voltage_high;
bool current_low;
bool current_high;
bool level_low;
bool level_high;
};

View File

@@ -12,11 +12,16 @@
#include <ArduinoOTA.h>
#include "INA226.h"
#include "Wire.h"
#include "INA233.h"
#include "global_data/defines.h"
#include "networking/networking.h"
#include <Preferences.h>
#include "global_data/global_data.h"
#include "sensor/sensor.h"
#define LED_1 4
#define LED_2 2
@@ -25,73 +30,17 @@
#define LED_5 12
#define LED_RED 14
// Define keys to prevent typos
#define ssid_key "ssid"
#define wifi_password_key "wifi_password"
#define level_sensor_range_key "range"
#define water_level_min_key "level_min"
#define water_level_max_key "level_max"
#define liters_key "liters"
#define RESISTOR_VALUE 4
extern "C" int rom_phy_get_vdd33();
// Calibration variables
float zero_value = 0.03; // Measured shunt voltage with nothing connected, used to fix measuring offset
bool voltage_high = false;
bool voltage_low = false;
bool current_high = false;
bool current_low = false;
extern NetworkData wifi_data;
extern NetworkData ethernet_data;
extern DeviceTelemetry telemetry;
uint8_t failed_connection_attempts = 0;
extern SensorData shunt_data;
extern WaterData water_data;
struct SensorData {
float bus_voltage;
float shunt_voltage;
float shunt_current;
};
struct WaterData{
// Water level in cm
float level;
// Water volume in liters
float liters;
// Percentage
float percentage;
};
struct NetworkData {
String ip_address;
bool link;
float rssi;
String network_name;
};
struct DeviceTelemetry {
float heap_used_percent;
int uptime_seconds;
};
NetworkData wifi_data;
NetworkData ethernet_data;
DeviceTelemetry telemetry;
SensorData shunt_data;
WaterData water_data;
int64_t mac_address = ESP.getEfuseMac();
//#define USE_INA226
#ifdef USE_INA226
INA226 ina_sensor(0x40);
#else
INA233 ina_sensor(0x40);
#endif
extern ActiveErrors active_errors;
Preferences prefs;
@@ -159,7 +108,7 @@ void display_error_code(byte err_code)
bool is_error()
{
return voltage_high || voltage_low || current_high || current_low;
return active_errors.voltage_high || active_errors.voltage_low || active_errors.current_high || active_errors.current_low;
}
void printSuffix(Print* _logOutput, int logLevel)
@@ -186,16 +135,16 @@ void display_task(void* parameter)
Log.verbose("Error detected");
// We have an error, display error code for 3 seconds and then water level for 3 seconds
if (voltage_low) {
if (active_errors.voltage_low) {
display_error_code(1);
delay(3000);
} else if (voltage_high) {
} else if (active_errors.voltage_high) {
display_error_code(2);
delay(3000);
} else if (current_low) {
} else if (active_errors.current_low) {
display_error_code(3);
delay(3000);
} else if (current_high) {
} else if (active_errors.current_high) {
display_error_code(4);
delay(3000);
} else {
@@ -206,63 +155,6 @@ void display_task(void* parameter)
}
}
void wifi_task(void* parameter)
{
Log.verbose("Starting WiFi Task");
while (true) {
if (prefs.getString(ssid_key, "") == "" || failed_connection_attempts > 5) {
wifi_data.link = false;
if (failed_connection_attempts > 5) {
Log.verbose("Failed to connecto to currently saved SSID, starting SoftAP");
} else {
Log.verbose("No SSID saved, starting SoftAP");
}
String ap_ssid = "Watermeter-" + String(mac_address);
WiFi.softAP(ap_ssid, "");
Log.verbose("[WIFI_TASK] Waiting for SSID now...");
String old_ssid = prefs.getString(ssid_key, "xxx");
while (prefs.getString(ssid_key, "") == "" || prefs.getString(ssid_key, "") == old_ssid) {
delay(5000);
}
failed_connection_attempts = 0;
} else {
if (WiFi.isConnected() && WiFi.SSID() == prefs.getString(ssid_key, "")) {
failed_connection_attempts = 0;
wifi_data.rssi = WiFi.RSSI();
wifi_data.link = true;
wifi_data.network_name = WiFi.SSID();
wifi_data.ip_address = WiFi.localIP().toString();
Log.verbose("RSSI: %F, IP Address, %p, SSID: %s", float(WiFi.RSSI()), WiFi.localIP(), prefs.getString(ssid_key, "NOSSID"));
delay(5000);
} else {
Log.verbose("Connecting to %s using password %s", prefs.getString(ssid_key, ""), prefs.getString(wifi_password_key, ""));
WiFi.mode(WIFI_STA);
WiFi.begin(prefs.getString(ssid_key, ""), prefs.getString(wifi_password_key, ""));
failed_connection_attempts++;
delay(5000);
}
}
}
}
void ethernet_task(void* parameter)
{
Log.verbose("Connecting Ethernet");
ETH.begin(0, 17, 23, 18);
ETH.setHostname("watermeter");
while (true) {
ethernet_data.link = ETH.linkUp();
ethernet_data.rssi = ETH.linkSpeed();
ethernet_data.ip_address = ETH.localIP().toString();
delay(60 * 1000);
}
}
void collect_internal_telemetry_task(void* parameter)
{
while (true) {
@@ -275,70 +167,24 @@ void collect_internal_telemetry_task(void* parameter)
}
}
void read_sensor_task(void* parameter)
String processor(const String& var)
{
while (true) {
// Get Values from sensor
float bus_voltage = ina_sensor.getBusVoltage();
float shunt_voltage = ina_sensor.getShuntVoltage_mV() - zero_value;
float shunt_current = shunt_voltage / RESISTOR_VALUE;
// Get values from storage
float sensor_range = prefs.getFloat(level_sensor_range_key, 200);
float max_water_level = prefs.getFloat(water_level_max_key, sensor_range);
float min_water_level = prefs.getFloat(water_level_min_key, 0);
float max_liters = prefs.getFloat(liters_key, 10000.);
float mA_per_cm = (20. - 4.) / (sensor_range);
// Get mA over 0cm/4mA for max/min water level
float min_water_level_mA_over_zero = (min_water_level * mA_per_cm);
float max_water_level_mA_over_zero = (max_water_level * mA_per_cm);
// Levels which represent raw sensor value, with 4mA added
float min_water_level_mA = 4 + min_water_level_mA_over_zero;
float max_water_level_mA = 4 + max_water_level_mA_over_zero;
Log.verbose("max_water_level_mA: %F", max_water_level_mA);
Log.verbose("min_water_level_mA_over_zero: %F", min_water_level_mA_over_zero);
// Current over the 0 level of the water
float shunt_current_over_zero = shunt_current - min_water_level_mA;
// cm over zero water level
float cm_over_zero = shunt_current_over_zero / mA_per_cm;
// Raw unrounded percentage in decimal
float percentage_raw = (shunt_current_over_zero / (max_water_level_mA_over_zero - min_water_level_mA_over_zero));
float percentage_rounded = round(percentage_raw*100);
// Tank volume in liters
float liters_raw = max_liters * percentage_raw;
int liters = round(liters_raw);
current_low = shunt_current < 3.8;
current_high = shunt_current > 20.2;
voltage_low = bus_voltage < 23;
voltage_high = bus_voltage > 25;
Log.verbose("Shunt current: %F", shunt_current);
Log.verbose("Shunt voltage: %F", shunt_voltage);
Log.verbose("Bus voltage: %F", bus_voltage);
Log.verbose("cm_over_zero: %F", cm_over_zero);
shunt_data.bus_voltage = bus_voltage;
shunt_data.shunt_voltage = shunt_voltage;
shunt_data.shunt_current = shunt_current;
water_data.level = cm_over_zero;
water_data.liters = liters;
water_data.percentage = percentage_rounded;
delay(20000);
}
if (var == level_sensor_range_key) {
return String(prefs.getFloat(level_sensor_range_key, -1));
} else if (var == water_level_min_key) {
return String(prefs.getFloat(water_level_min_key, -1));
} else if (var == water_level_max_key) {
return String(prefs.getFloat(water_level_max_key, -1));
} else if (var == water_volume_key) {
return String(prefs.getFloat(water_volume_key, -1));
}
return String("Test");
}
void setup()
{
prefs.begin("waterlevel", false);
@@ -358,6 +204,7 @@ void setup()
display_error_code(31);
delay(500);
display_error_code(17);
init_sensor();
Log.verbose("Beginning SPIFFS");
SPIFFS.begin(true);
@@ -365,26 +212,15 @@ void setup()
display_error_code(19);
Log.verbose("Begin INA");
ina_sensor.begin(33, 32);
display_error_code(20);
#ifdef USE_INA226
ina_sensor.setMaxCurrentShunt(0.02, 4, false);
ina_sensor.setBusVoltageConversionTime(7);
ina_sensor.setShuntVoltageConversionTime(7);
ina_sensor.setAverage(4);
#else
ina_sensor.setShuntVoltageConversionTime(conversion_time_8244uS);
ina_sensor.setBusVoltageConversionTime(conversion_time_8244uS);
ina_sensor.setAveragingMode(averages_128);
#endif
display_error_code(22);
/////////////////////////////// ROUTES ///////////////////////////////
Log.verbose("Route Setup");
server.on("/", HTTP_GET, [](AsyncWebServerRequest* request) { request->send(SPIFFS, "/status.html", "text/html", false); });
server.on("/", HTTP_GET, [](AsyncWebServerRequest* request) { request->send(SPIFFS, "/status.html", "text/html", false, processor); });
server.on("/settings", HTTP_GET, [](AsyncWebServerRequest* request) { request->send(SPIFFS, "/settings.html", "text/html", false); });
server.on("/settings", HTTP_GET, [](AsyncWebServerRequest* request) { request->send(SPIFFS, "/settings.html", "text/html", false, processor); });
server.on("/export", HTTP_GET, [](AsyncWebServerRequest* request) { request->send(SPIFFS, "/data_export.html", "text/html", false); });
@@ -393,13 +229,15 @@ void setup()
server.on("/update_wifi_credentials", HTTP_POST, [](AsyncWebServerRequest* request) {
int params = request->params();
// For settings SSID
if (request->hasParam(ssid_key, true) && request->hasParam(wifi_password_key, true)) {
Log.verbose("Updating SSID config");
AsyncWebParameter* ssid_param = request->getParam(ssid_key, true);
AsyncWebParameter* password_param = request->getParam(wifi_password_key, true);
prefs.putString(ssid_key, ssid_param->value().c_str());
prefs.putString(wifi_password_key, password_param->value().c_str());
} else {
}else {
for (int i = 0; i < params; i++) {
AsyncWebParameter* p = request->getParam(i);
if (p->isFile()) { // p->isPost() is also true
@@ -418,17 +256,37 @@ void setup()
server.on("/update_sensor_settings", HTTP_POST, [](AsyncWebServerRequest* request) {
int params = request->params();
if (request->hasParam(level_sensor_range_key, true) && request->hasParam(water_level_min_key, true) && request->hasParam(water_level_max_key, true) && request->hasParam(liters_key, true)) {
if (request->hasParam(level_sensor_range_key, true) && request->hasParam(water_level_min_key, true) && request->hasParam(water_level_max_key, true) && request->hasParam(water_volume_key, true)) {
Log.verbose("Updating Sensor config");
AsyncWebParameter* range_param = request->getParam(level_sensor_range_key, true);
AsyncWebParameter* level_min_param = request->getParam(water_level_min_key, true);
AsyncWebParameter* level_max_param = request->getParam(water_level_max_key, true);
AsyncWebParameter* liters_param = request->getParam(liters_key, true);
AsyncWebParameter* liters_param = request->getParam(water_volume_key, true);
prefs.putFloat(level_sensor_range_key, range_param->value().toFloat());
prefs.putFloat(water_level_min_key, level_min_param->value().toFloat());
prefs.putFloat(water_level_max_key, level_max_param->value().toFloat());
prefs.putFloat(liters_key, liters_param->value().toFloat());
String range_str = range_param->value();
String level_min_str = level_min_param->value();
String level_max_str = level_max_param->value();
String liters_str = liters_param->value();
float range_float = range_str.toFloat();
float level_min_float = level_min_str.toFloat();
float level_max_float = level_max_str.toFloat();
float liters_float = liters_str.toFloat();
const char* paramCStr = range_str.c_str();
char* endPtr;
// Convert the C string to a float using strtod
float value = strtod(paramCStr, &endPtr);
Log.verbose("range_float:%D:", range_float);
prefs.putFloat(level_sensor_range_key, range_float);
prefs.putFloat(water_level_min_key, level_min_float);
prefs.putFloat(water_level_max_key, level_max_float);
prefs.putFloat(water_volume_key, liters_float);
Log.verbose("range_float_after:%D:", prefs.getFloat(level_sensor_range_key, -1.0));
} else {
Log.verbose("!!!! FAIL lo");
for (int i = 0; i < params; i++) {
@@ -492,6 +350,8 @@ void setup()
});
server.on("/chota.css", HTTP_GET, [](AsyncWebServerRequest* request) { request->send(SPIFFS, "/chota.css", "text/css", false); });
server.on("/gauge.js", HTTP_GET, [](AsyncWebServerRequest* request) { request->send(SPIFFS, "/gauge.js", "application/javascript", false); });
display_error_code(23);

View File

@@ -0,0 +1,70 @@
#include <ArduinoLog.h>
#include <ETH.h>
#include <WiFi.h>
#include "../global_data/defines.h"
#include "../global_data/global_data.h"
#include <Preferences.h>
int64_t mac_address = ESP.getEfuseMac();
uint8_t failed_connection_attempts = 0;
extern NetworkData wifi_data;
extern NetworkData ethernet_data;
extern Preferences prefs;
void wifi_task(void* parameter)
{
Log.verbose("Starting WiFi Task");
while (true) {
if (prefs.getString(ssid_key, "") == "" || failed_connection_attempts > 5) {
wifi_data.link = false;
if (failed_connection_attempts > 5) {
Log.verbose("Failed to connecto to currently saved SSID, starting SoftAP");
} else {
Log.verbose("No SSID saved, starting SoftAP");
}
String ap_ssid = "Watermeter-" + String(mac_address);
WiFi.softAP(ap_ssid, "");
Log.verbose("[WIFI_TASK] Waiting for SSID now...");
String old_ssid = prefs.getString(ssid_key, "xxx");
while (prefs.getString(ssid_key, "") == "" || prefs.getString(ssid_key, "") == old_ssid) {
delay(5000);
}
failed_connection_attempts = 0;
} else {
if (WiFi.isConnected() && WiFi.SSID() == prefs.getString(ssid_key, "")) {
failed_connection_attempts = 0;
wifi_data.rssi = WiFi.RSSI();
wifi_data.link = true;
wifi_data.network_name = WiFi.SSID();
wifi_data.ip_address = WiFi.localIP().toString();
Log.verbose("RSSI: %F, IP Address, %p, SSID: %s", float(WiFi.RSSI()), WiFi.localIP(), prefs.getString(ssid_key, "NOSSID"));
delay(5000);
} else {
Log.verbose("Connecting to %s using password %s", prefs.getString(ssid_key, ""), prefs.getString(wifi_password_key, ""));
WiFi.mode(WIFI_STA);
WiFi.begin(prefs.getString(ssid_key, ""), prefs.getString(wifi_password_key, ""));
failed_connection_attempts++;
delay(5000);
}
}
}
}
void ethernet_task(void* parameter)
{
Log.verbose("Connecting Ethernet");
ETH.begin(0, 17, 23, 18);
ETH.setHostname("watermeter");
while (true) {
ethernet_data.link = ETH.linkUp();
ethernet_data.rssi = ETH.linkSpeed();
ethernet_data.ip_address = ETH.localIP().toString();
delay(60 * 1000);
}
}

View File

@@ -0,0 +1,2 @@
void wifi_task(void*);
void ethernet_task(void*);

99
src/sensor/sensor.cpp Normal file
View File

@@ -0,0 +1,99 @@
#include "../global_data/defines.h"
#include <INA233.h>
#include <Preferences.h>
#include <ArduinoLog.h>
#include <global_data/global_data.h>
#include "Wire.h"
#ifdef USE_INA226s
INA226 ina_sensor(0x40);
#else
INA233 ina_sensor(0x40);
#endif
extern Preferences prefs;
extern WaterData water_data;
extern ActiveErrors active_errors;
extern SensorData shunt_data;
// Calibration variables
float zero_value = 0.03; // Measured shunt voltage with nothing connected, used to fix measuring offset
void init_sensor(){
ina_sensor.begin(33, 32);
#ifdef USE_INA226
ina_sensor.setMaxCurrentShunt(0.02, 4, false);
ina_sensor.setBusVoltageConversionTime(7);
ina_sensor.setShuntVoltageConversionTime(7);
ina_sensor.setAverage(4);
#else
ina_sensor.setShuntVoltageConversionTime(conversion_time_8244uS);
ina_sensor.setBusVoltageConversionTime(conversion_time_8244uS);
ina_sensor.setAveragingMode(averages_128);
#endif
}
void read_sensor_task(void* parameter)
{
while (true) {
// Get Values from sensor
float bus_voltage = ina_sensor.getBusVoltage();
float shunt_voltage = ina_sensor.getShuntVoltage_mV() - zero_value;
float shunt_current = shunt_voltage / RESISTOR_VALUE;
// Get values from storage
float sensor_range = prefs.getFloat(level_sensor_range_key, 200);
float max_water_level = prefs.getFloat(water_level_max_key, sensor_range);
float min_water_level = prefs.getFloat(water_level_min_key, 0);
float max_liters = prefs.getFloat(water_volume_key, 10000.);
float mA_per_cm = (20. - 4.) / (sensor_range);
// Get mA over 0cm/4mA for max/min water level
float min_water_level_mA_over_zero = (min_water_level * mA_per_cm);
float max_water_level_mA_over_zero = (max_water_level * mA_per_cm);
// Levels which represent raw sensor value, with 4mA added
float min_water_level_mA = 4 + min_water_level_mA_over_zero;
float max_water_level_mA = 4 + max_water_level_mA_over_zero;
Log.verbose("max_water_level_mA: %F", max_water_level_mA);
Log.verbose("min_water_level_mA_over_zero: %F", min_water_level_mA_over_zero);
// Current over the 0 level of the water
float shunt_current_over_zero = shunt_current - min_water_level_mA;
// cm over zero water level
float cm_over_zero = shunt_current_over_zero / mA_per_cm;
// Raw unrounded percentage in decimal
float percentage_raw = (shunt_current_over_zero / (max_water_level_mA_over_zero - min_water_level_mA_over_zero));
float percentage_rounded = round(percentage_raw*100);
// Tank volume in liters
float liters_raw = max_liters * percentage_raw;
int liters = round(liters_raw);
active_errors.current_low = shunt_current < 3.8;
active_errors.current_high = shunt_current > 20.2;
active_errors.voltage_low = bus_voltage < 23;
active_errors.voltage_high = bus_voltage > 25;
Log.verbose("Shunt current: %F", shunt_current);
Log.verbose("Shunt voltage: %F", shunt_voltage);
Log.verbose("Bus voltage: %F", bus_voltage);
Log.verbose("cm_over_zero: %F", cm_over_zero);
shunt_data.bus_voltage = bus_voltage;
shunt_data.shunt_voltage = shunt_voltage;
shunt_data.shunt_current = shunt_current;
water_data.level = cm_over_zero;
water_data.liters = liters;
water_data.percentage = percentage_rounded;
delay(20000);
}
}

2
src/sensor/sensor.h Normal file
View File

@@ -0,0 +1,2 @@
void read_sensor_task(void*);
void init_sensor();