This commit is contained in:
1 parent
57b1ce8db7
commit
db567eeaa5
14 files changed
+411
-221
No files matched your search
@@ -0,0 +1,27 @@
|
|||||||
|
/*!
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2016 Mykhailo Stadnyk <mikhus@gmail.com>
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* @version 2.1.7
|
||||||
|
*/
|
||||||
|
!function(e){"use strict";function t(e){if(Array.isArray(e)){for(var t=0,i=Array(e.length);t<e.length;t++)i[t]=e[t];return i}return Array.from(e)}function i(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function r(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}function o(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function n(e,t){if(t||(t="undefined"==typeof window?global:window),void 0!==t[e])return t[e];for(var i=["webkit","moz","ms","o"],r=0,o=i.length,n=e.charAt(0).toUpperCase()+e.substr(1);r<o;r++){var a=t[i[r]+n];if(void 0!==a)return a}return null}function a(e,t,i,r,o,n,l){if("function"!=typeof r)throw new TypeError("Invalid animation rule:",r);var s=e-i,d=s/o,c=0;d>1&&(d=1),1!==d&&(c=r(d),isFinite(c)&&!isNaN(c)&&(d=c)),t&&t(d),s<o?l.frame=me(function(e){return a(e,t,i,r,o,n,l)}):(n&&n(),l.inProgress=!1)}function l(){Array.prototype.constructor.apply(this,arguments)}function s(e){if(!(e instanceof DOMException&&2152923147===e.result))throw e}function d(e,t){return t.replace(Ve,function(t,i){var r=e[i];return void 0!==r?r:t})}function c(e){return e.majorTicks instanceof Array||(e.majorTicks=e.majorTicks?[e.majorTicks]:[]),e.majorTicks.length||(e.majorTicks.push(We.formatMajorTickNumber(e.minValue,e)),e.majorTicks.push(We.formatMajorTickNumber(e.maxValue,e))),["right"!==e.tickSide,"left"!==e.tickSide]}function h(e,t,i,r,o,n){e.beginPath(),e.moveTo(t+n,i),e.lineTo(t+r-n,i),e.quadraticCurveTo(t+r,i,t+r,i+n),e.lineTo(t+r,i+o-n),e.quadraticCurveTo(t+r,i+o,t+r-n,i+o),e.lineTo(t+n,i+o),e.quadraticCurveTo(t,i+o,t,i+o-n),e.lineTo(t,i+n),e.quadraticCurveTo(t,i,t+n,i),e.closePath()}function u(e,t){var i=t.valueDec,r=t.valueInt,o=0,n=void 0,a=void 0,l=void 0;if(e=parseFloat(e),l=e<0,e=Math.abs(e),i>0){for(a=e.toFixed(i).toString().split("."),n=r-a[0].length;o<n;++o)a[0]="0"+a[0];a=(l?"-":"")+a[0]+"."+a[1]}else{for(a=Math.round(e).toString(),n=r-a.length;o<n;++o)a="0"+a;a=(l?"-":"")+a}return a}function f(e,t){var i=void 0,r=!1;return i=0===t.majorTicksDec?Math.round(e).toString():e.toFixed(t.majorTicksDec),t.majorTicksInt>1?(r=~i.indexOf("."),~i.indexOf("-")?"-"+[t.majorTicksInt+t.majorTicksDec+2+(r?1:0)-i.length].join("0")+i.replace("-",""):[t.majorTicksInt+t.majorTicksDec+1+(r?1:0)-i.length].join("0")+i):i}function m(e){return e*Math.PI/180}function v(e,t){return{x:-e*Math.sin(t),y:e*Math.cos(t)}}function g(e,t,i,r){var o=!(arguments.length>4&&void 0!==arguments[4])||arguments[4],n=arguments.length>5&&void 0!==arguments[5]?arguments[5]:0,a=e.createLinearGradient(o?0:n,o?n:0,o?0:r,o?r:0);return a.addColorStop(0,t),a.addColorStop(1,i),a}function b(e,t){if(arguments.length>2&&void 0!==arguments[2]&&arguments[2])return e.restore(),!0;e.save();var i=t.borderShadowWidth;return i&&(e.shadowBlur=i,e.shadowColor=t.colorBorderShadow),!0}function p(e,t){t.needleShadow&&(e.shadowOffsetX=2,e.shadowOffsetY=2,e.shadowBlur=10,e.shadowColor=t.colorNeedleShadowDown)}function w(e,t,i){return e["font"+t+"Style"]+" "+e["font"+t+"Weight"]+" "+e["font"+t+"Size"]*i+"px "+e["font"+t]}function k(e){e.shadowOffsetX=null,e.shadowOffsetY=null,e.shadowBlur=null,e.shadowColor="",e.strokeStyle=null,e.lineWidth=0,e.save()}function y(e,t,i,r){t.valueTextShadow&&(e.shadowOffsetX=i,e.shadowOffsetY=i,e.shadowBlur=r,e.shadowColor=t.colorValueTextShadow)}function x(e,t,i,r,o,n){if(t.valueBox){k(e);var a=t.valueDec?1+t.valueDec:0,l="9".repeat(Math.max.apply(null,[String(parseInt(i)).length+a].concat(t.majorTicks.map(function(e){return String(parseInt(e,10)).length+a})))),s=t.valueText||u(i,t),d=n/200,c=n/100,f=.4*c,m=1.2*c;e.font=w(t,"Value",d),y(e,t,f,m);var v=e.measureText(t.valueText?s:"-"+u(Number(l),t)).width;k(e);var g=parseFloat(t.fontValueSize)*d+f+m,b=c*parseFloat(t.valueBoxStroke),p=2*n-2*b,x=v+10*c,T=1.1*g+f+m,S=c*t.valueBoxBorderRadius,V=(parseFloat(t.valueBoxWidth)||0)/100*p;V>x&&(x=V),x>p&&(x=p);var W=r-x/2,O=o-T/2,A=o-5.75*c;if(e.beginPath(),S?h(e,W,O,x,T,S):e.rect(W,O,x,T),b){var P=e.createRadialGradient(r,A,10*c,r,A,20*c);P.addColorStop(0,t.colorValueBoxRect),P.addColorStop(1,t.colorValueBoxRectEnd),e.strokeStyle=P,e.lineWidth=b,e.stroke()}t.colorValueBoxShadow&&(e.shadowBlur=1.2*c,e.shadowColor=t.colorValueBoxShadow),t.colorValueBoxBackground&&(e.fillStyle=t.colorValueBoxBackground,e.fill()),e.closePath(),e.restore(),y(e,t,f,m),e.fillStyle=t.colorValueText,e.textAlign="center",e.textBaseline="alphabetic",e.fillText(s,W+x/2,o+T/2-g/3),e.restore()}}function T(e){var t=e.value,i=e.minValue,r=e.maxValue,o=.01*(r-i);return{normal:t<i?i:t>r?r:t,indented:t<i?i-o:t>r?r+o:t}}function S(e,t,i,r,o){i.beginPath(),i.arc(0,0,xe(e),0,2*Oe,!0),i.lineWidthLine truncated
|
||||||
|
}}],[{key:"redraw",value:function(){for(var t=0,i=e.collection.length;t<i;t++)e.collection[t].redraw()}},{key:"pixelRatio",get:function(){return window.devicePixelRatio||1}}]),e}();pe.collection=[],window.matchMedia&&window.matchMedia("screen and (min-resolution: 2dppx)").addListener(pe.redraw);var we={renderTo:null,width:0,height:0,minValue:0,maxValue:100,value:0,units:!1,exactTicks:!1,majorTicks:[0,20,40,60,80,100],minorTicks:10,strokeTicks:!0,animatedValue:!1,animateOnInit:!1,title:!1,borders:!0,numbersMargin:1,listeners:null,valueInt:3,valueDec:2,majorTicksInt:1,majorTicksDec:0,animation:!0,animationDuration:500,animationRule:"cycle",colorPlate:"#fff",colorPlateEnd:"",colorMajorTicks:"#444",colorMinorTicks:"#666",colorStrokeTicks:"",colorTitle:"#888",colorUnits:"#888",colorNumbers:"#444",colorNeedle:"rgba(240,128,128,1)",colorNeedleEnd:"rgba(255,160,122,.9)",colorValueText:"#444",colorValueTextShadow:"rgba(0,0,0,0.3)",colorBorderShadow:"rgba(0,0,0,0.5)",colorBorderOuter:"#ddd",colorBorderOuterEnd:"#aaa",colorBorderMiddle:"#eee",colorBorderMiddleEnd:"#f0f0f0",colorBorderInner:"#fafafa",colorBorderInnerEnd:"#ccc",colorValueBoxRect:"#888",colorValueBoxRectEnd:"#666",colorValueBoxBackground:"#babab2",colorValueBoxShadow:"rgba(0,0,0,1)",colorNeedleShadowUp:"rgba(2,255,255,0.2)",colorNeedleShadowDown:"rgba(188,143,143,0.45)",colorBarStroke:"#222",colorBar:"#ccc",colorBarProgress:"#888",colorBarShadow:"#000",fontNumbers:"Arial",fontTitle:"Arial",fontUnits:"Arial",fontValue:"Arial",fontNumbersSize:20,fontTitleSize:24,fontUnitsSize:22,fontValueSize:26,fontNumbersStyle:"normal",fontTitleStyle:"normal",fontUnitsStyle:"normal",fontValueStyle:"normal",fontNumbersWeight:"normal",fontTitleWeight:"normal",fontUnitsWeight:"normal",fontValueWeight:"normal",needle:!0,needleShadow:!0,needleType:"arrow",needleStart:5,needleEnd:85,needleWidth:4,borderOuterWidth:3,borderMiddleWidth:3,borderInnerWidth:3,borderShadowWidth:3,valueBox:!0,valueBoxStroke:5,valueBoxWidth:0,valueText:"",valueTextShadow:!0,valueBoxBorderRadius:2.5,highlights:[{from:20,to:60,color:"#eee"},{from:60,to:80,color:"#ccc"},{from:80,to:100,color:"#999"}],highlightsWidth:15,highlightsLineCap:"butt",barWidth:20,barStrokeWidth:0,barProgress:!0,barShadow:0};l.prototype=Object.create(Array.prototype),l.prototype.constructor=l,l.prototype.get=function(e){if("string"==typeof e)for(var t=0,i=this.length;t<i;t++){var r=this[t].options.renderTo.tagName?this[t].options.renderTo:document.getElementById(this[t].options.renderTo||"");if(r.getAttribute("id")===e)return this[t]}else if("number"==typeof e)return this[e];return null};var ke="2.1.7",ye=Math.round,xe=Math.abs,Te=new l;Te.version=ke;var Se=function(t){function n(t){o(this,n);var r=i(this,(n.__proto__||Object.getPrototypeOf(n)).call(this)),a=r.constructor.name;if("BaseGauge"===a)throw new TypeError("Attempt to instantiate abstract class!");if(Te.push(r),t.listeners&&Object.keys(t.listeners).forEach(function(e){(t.listeners[e]instanceof Array?t.listeners[e]:[t.listeners[e]]).forEach(function(t){r.on(e,t)})}),r.version=ke,r.type=e[a]||n,r.initialized=!1,t.minValue=parseFloat(t.minValue),t.maxValue=parseFloat(t.maxValue),t.value=parseFloat(t.value)||0,t.borders||(t.borderInnerWidth=t.borderMiddleWidth=t.borderOuterWidth=0),!t.renderTo)throw TypeError("Canvas element was not specified when creating the Gauge object!");var l=t.renderTo.tagName?t.renderTo:document.getElementById(t.renderTo);if(!(l instanceof HTMLCanvasElement))throw TypeError("Given gauge canvas element is invalid!");return t.width=parseFloat(t.width)||0,t.height=parseFloat(t.height)||0,t.width&&t.height||(t.width||(t.width=l.parentNode?l.parentNode.offsetWidth:l.offsetWidth),t.height||(t.height=l.parentNode?l.parentNode.offsetHeight:l.offsetHeight)),r.options=t||{},r.options.animateOnInit&&(r._value=r.options.value,r.options.value=r.options.minValue),r.canvas=new pe(l,t.width,t.height),r.canvas.onRedraw=r.draw.bind(r),r.animation=new ge(t.animationRule,t.animationDuration),r}return r(n,t),ue(n,[{key:"update",value:function(e){return Object.assign(this.options,this.type.configure(e||{})),this.canvas.width=this.options.width,this.canvas.height=this.options.height,this.animation.rule=this.options.animationRule,this.animation.duration=this.options.animationDuration,this.canvas.redraw(),this}},{key:"destroy",value:function(){var e=Te.indexOf(this);~e&&Te.splice(e,1),this.canvas.destroy(),this.canvas=null,this.animation.destroy(),this.animation=null,this.emit("destroy")}},{key:"draw",value:function(){return this.options.animateOnInit&&!this.initialized&&(this.value=this._value,this.initialized=!0,this.emit("init")),this.emit("render"),this}},{key:"value",set:function(e){var t=this;e=n.ensureValue(e,this.options.minValue);var i=this.options.value;if(e!==i)if(this.options.animation){if(this.animation.frame&&(this.options.value=this._value,this._value===e))return this.animation.cancel(),void delete this._value;void 0===this._value&&(this._value=e),this.emit("aLine truncated
|
||||||
@@ -82,6 +82,8 @@ window.addEventListener('DOMContentLoaded', (event) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
fetchData(); // fetch immediately on page load
|
fetchData(); // fetch immediately on page load
|
||||||
setInterval(fetchData, 5000); // fetch every 5 seconds
|
setInterval(fetchData, 5000); // fetch every 5 seconds
|
||||||
});
|
});
|
||||||
@@ -89,3 +91,50 @@ window.addEventListener('DOMContentLoaded', (event) => {
|
|||||||
function roundToTwo(num) {
|
function roundToTwo(num) {
|
||||||
return Math.round(num*100)/100;
|
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);
|
||||||
+8
-8
@@ -25,22 +25,22 @@
|
|||||||
<legend>Settings</legend>
|
<legend>Settings</legend>
|
||||||
<p>
|
<p>
|
||||||
<label for="range">Sensor Range in cm</label>
|
<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>
|
||||||
<p>
|
<p>
|
||||||
<label for="level_max">Water max height in cm</label>
|
<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>
|
||||||
<p>
|
<p>
|
||||||
<label for="level_min">Water min height in cm</label>
|
<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>
|
||||||
<p>
|
<p>
|
||||||
<label for="liters">Volume in liters</label>
|
<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>
|
||||||
<p>
|
<p>
|
||||||
<button class="button primary" style="width: 100%;">Submit</button>
|
<button class="button primary" style="width: 100%%;">Submit</button>
|
||||||
</p>
|
</p>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</form>
|
</form>
|
||||||
@@ -55,15 +55,15 @@
|
|||||||
|
|
||||||
<p>
|
<p>
|
||||||
<label for="ssid">SSID</label>
|
<label for="ssid">SSID</label>
|
||||||
<input id="ssid" name="ssid" type="text">
|
<input id="ssid" name="ssid" type="text" value="%sensor_range%">
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<label for="wifi_password">Password</label>
|
<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>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<button class="button primary" style="width: 100%;">Submit</button>
|
<button class="button primary" style="width: 100%%;">Submit</button>
|
||||||
</p>
|
</p>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
+4
-7
@@ -2,7 +2,10 @@
|
|||||||
|
|
||||||
<head>
|
<head>
|
||||||
<link rel="stylesheet" href="/chota.css">
|
<link rel="stylesheet" href="/chota.css">
|
||||||
|
|
||||||
|
<script src="/gauge.js"></script>
|
||||||
<script src="/logic.js"></script>
|
<script src="/logic.js"></script>
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
@@ -70,13 +73,7 @@
|
|||||||
|
|
||||||
<div class="col-12 col-6-lg">
|
<div class="col-12 col-6-lg">
|
||||||
|
|
||||||
<div class="outer-wrapper">
|
<canvas data-type="radial-gauge" id="main_gauge"></canvas>
|
||||||
<div class="column-wrapper">
|
|
||||||
<div class="column"></div>
|
|
||||||
</div>
|
|
||||||
<div class="percentage">25%</div>
|
|
||||||
<div class="value">2500 / 10000</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"version": "1",
|
||||||
|
"name": "Waterlevel",
|
||||||
|
"type": "collection",
|
||||||
|
"ignore": [
|
||||||
|
"node_modules",
|
||||||
|
".git"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -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
|
||||||
@@ -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 };
|
||||||
@@ -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;
|
||||||
|
};
|
||||||
+66
-206
@@ -12,11 +12,16 @@
|
|||||||
#include <ArduinoOTA.h>
|
#include <ArduinoOTA.h>
|
||||||
|
|
||||||
#include "INA226.h"
|
#include "INA226.h"
|
||||||
#include "Wire.h"
|
|
||||||
#include "INA233.h"
|
#include "INA233.h"
|
||||||
|
|
||||||
|
#include "global_data/defines.h"
|
||||||
|
|
||||||
|
#include "networking/networking.h"
|
||||||
|
|
||||||
|
|
||||||
#include <Preferences.h>
|
#include <Preferences.h>
|
||||||
|
#include "global_data/global_data.h"
|
||||||
|
#include "sensor/sensor.h"
|
||||||
|
|
||||||
#define LED_1 4
|
#define LED_1 4
|
||||||
#define LED_2 2
|
#define LED_2 2
|
||||||
@@ -25,73 +30,17 @@
|
|||||||
#define LED_5 12
|
#define LED_5 12
|
||||||
#define LED_RED 14
|
#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();
|
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;
|
extern NetworkData wifi_data;
|
||||||
bool voltage_low = false;
|
extern NetworkData ethernet_data;
|
||||||
bool current_high = false;
|
extern DeviceTelemetry telemetry;
|
||||||
bool current_low = false;
|
|
||||||
|
|
||||||
uint8_t failed_connection_attempts = 0;
|
extern SensorData shunt_data;
|
||||||
|
extern WaterData water_data;
|
||||||
|
|
||||||
struct SensorData {
|
extern ActiveErrors active_errors;
|
||||||
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
|
|
||||||
|
|
||||||
Preferences prefs;
|
Preferences prefs;
|
||||||
|
|
||||||
@@ -159,7 +108,7 @@ void display_error_code(byte err_code)
|
|||||||
|
|
||||||
bool is_error()
|
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)
|
void printSuffix(Print* _logOutput, int logLevel)
|
||||||
@@ -186,16 +135,16 @@ void display_task(void* parameter)
|
|||||||
Log.verbose("Error detected");
|
Log.verbose("Error detected");
|
||||||
|
|
||||||
// We have an error, display error code for 3 seconds and then water level for 3 seconds
|
// 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);
|
display_error_code(1);
|
||||||
delay(3000);
|
delay(3000);
|
||||||
} else if (voltage_high) {
|
} else if (active_errors.voltage_high) {
|
||||||
display_error_code(2);
|
display_error_code(2);
|
||||||
delay(3000);
|
delay(3000);
|
||||||
} else if (current_low) {
|
} else if (active_errors.current_low) {
|
||||||
display_error_code(3);
|
display_error_code(3);
|
||||||
delay(3000);
|
delay(3000);
|
||||||
} else if (current_high) {
|
} else if (active_errors.current_high) {
|
||||||
display_error_code(4);
|
display_error_code(4);
|
||||||
delay(3000);
|
delay(3000);
|
||||||
} else {
|
} 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)
|
void collect_internal_telemetry_task(void* parameter)
|
||||||
{
|
{
|
||||||
while (true) {
|
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) {
|
if (var == level_sensor_range_key) {
|
||||||
// Get Values from sensor
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
float bus_voltage = ina_sensor.getBusVoltage();
|
return String("Test");
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void setup()
|
void setup()
|
||||||
{
|
{
|
||||||
prefs.begin("waterlevel", false);
|
prefs.begin("waterlevel", false);
|
||||||
@@ -358,6 +204,7 @@ void setup()
|
|||||||
display_error_code(31);
|
display_error_code(31);
|
||||||
delay(500);
|
delay(500);
|
||||||
display_error_code(17);
|
display_error_code(17);
|
||||||
|
init_sensor();
|
||||||
|
|
||||||
Log.verbose("Beginning SPIFFS");
|
Log.verbose("Beginning SPIFFS");
|
||||||
SPIFFS.begin(true);
|
SPIFFS.begin(true);
|
||||||
@@ -365,26 +212,15 @@ void setup()
|
|||||||
display_error_code(19);
|
display_error_code(19);
|
||||||
|
|
||||||
Log.verbose("Begin INA");
|
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);
|
display_error_code(22);
|
||||||
|
|
||||||
/////////////////////////////// ROUTES ///////////////////////////////
|
/////////////////////////////// ROUTES ///////////////////////////////
|
||||||
Log.verbose("Route Setup");
|
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); });
|
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) {
|
server.on("/update_wifi_credentials", HTTP_POST, [](AsyncWebServerRequest* request) {
|
||||||
int params = request->params();
|
int params = request->params();
|
||||||
|
|
||||||
|
// For settings SSID
|
||||||
if (request->hasParam(ssid_key, true) && request->hasParam(wifi_password_key, true)) {
|
if (request->hasParam(ssid_key, true) && request->hasParam(wifi_password_key, true)) {
|
||||||
Log.verbose("Updating SSID config");
|
Log.verbose("Updating SSID config");
|
||||||
AsyncWebParameter* ssid_param = request->getParam(ssid_key, true);
|
AsyncWebParameter* ssid_param = request->getParam(ssid_key, true);
|
||||||
AsyncWebParameter* password_param = request->getParam(wifi_password_key, true);
|
AsyncWebParameter* password_param = request->getParam(wifi_password_key, true);
|
||||||
prefs.putString(ssid_key, ssid_param->value().c_str());
|
prefs.putString(ssid_key, ssid_param->value().c_str());
|
||||||
prefs.putString(wifi_password_key, password_param->value().c_str());
|
prefs.putString(wifi_password_key, password_param->value().c_str());
|
||||||
} else {
|
|
||||||
|
}else {
|
||||||
for (int i = 0; i < params; i++) {
|
for (int i = 0; i < params; i++) {
|
||||||
AsyncWebParameter* p = request->getParam(i);
|
AsyncWebParameter* p = request->getParam(i);
|
||||||
if (p->isFile()) { // p->isPost() is also true
|
if (p->isFile()) { // p->isPost() is also true
|
||||||
@@ -418,17 +256,37 @@ void setup()
|
|||||||
server.on("/update_sensor_settings", HTTP_POST, [](AsyncWebServerRequest* request) {
|
server.on("/update_sensor_settings", HTTP_POST, [](AsyncWebServerRequest* request) {
|
||||||
int params = request->params();
|
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");
|
Log.verbose("Updating Sensor config");
|
||||||
AsyncWebParameter* range_param = request->getParam(level_sensor_range_key, true);
|
AsyncWebParameter* range_param = request->getParam(level_sensor_range_key, true);
|
||||||
AsyncWebParameter* level_min_param = request->getParam(water_level_min_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* 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());
|
String range_str = range_param->value();
|
||||||
prefs.putFloat(water_level_min_key, level_min_param->value().toFloat());
|
String level_min_str = level_min_param->value();
|
||||||
prefs.putFloat(water_level_max_key, level_max_param->value().toFloat());
|
String level_max_str = level_max_param->value();
|
||||||
prefs.putFloat(liters_key, liters_param->value().toFloat());
|
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 {
|
} else {
|
||||||
Log.verbose("!!!! FAIL lo");
|
Log.verbose("!!!! FAIL lo");
|
||||||
for (int i = 0; i < params; i++) {
|
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("/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);
|
display_error_code(23);
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
void wifi_task(void*);
|
||||||
|
void ethernet_task(void*);
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
void read_sensor_task(void*);
|
||||||
|
void init_sensor();
|
||||||
Reference in new issue
Block a user