Initial commit with action
Some checks failed
Test compiling project / test (push) Failing after 33s
Some checks failed
Test compiling project / test (push) Failing after 33s
This commit is contained in:
22
.gitea/workflows/on_push.yaml
Normal file
22
.gitea/workflows/on_push.yaml
Normal file
@@ -0,0 +1,22 @@
|
||||
name: Test compiling project
|
||||
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: debian:latest
|
||||
|
||||
steps:
|
||||
- name: Install necessary dependencies
|
||||
run: apt update && apt install nodejs -y
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.11'
|
||||
- name: Install Platformio
|
||||
run: pip install --break-system-packages --upgrade platformio
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v2
|
||||
- name: Run PlatformIO
|
||||
run: pio ci -c platformio.ini ./
|
||||
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
.pio
|
||||
.vscode/.browse.c_cpp.db*
|
||||
.vscode/c_cpp_properties.json
|
||||
.vscode/launch.json
|
||||
.vscode/ipch
|
||||
10
.vscode/extensions.json
vendored
Normal file
10
.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
// See http://go.microsoft.com/fwlink/?LinkId=827846
|
||||
// for the documentation about the extensions.json format
|
||||
"recommendations": [
|
||||
"platformio.platformio-ide"
|
||||
],
|
||||
"unwantedRecommendations": [
|
||||
"ms-vscode.cpptools-extension-pack"
|
||||
]
|
||||
}
|
||||
1
data/chota.css
Normal file
1
data/chota.css
Normal file
File diff suppressed because one or more lines are too long
60
data/data_export.html
Normal file
60
data/data_export.html
Normal file
@@ -0,0 +1,60 @@
|
||||
<!DOCTYPE html>”
|
||||
|
||||
<head>
|
||||
<link rel="stylesheet" href="/chota.css">
|
||||
|
||||
<script>
|
||||
if (window.matchMedia &&
|
||||
window.matchMedia('(prefers-color-scheme: dark)').matches) {
|
||||
document.body.classList.add('dark');
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container">
|
||||
<nav class="nav" style="margin-bottom: 50px;">
|
||||
<div class="nav-left">
|
||||
<a class="brand" href="#">Watermeter</a>
|
||||
<div class="tabs">
|
||||
<a href="/">Status</a>
|
||||
<a href="/settings">Settings</a>
|
||||
<a class="active" href="/export">Data export</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="nav-right">
|
||||
<a class="button outline">Button</a>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="card">
|
||||
<header>
|
||||
<h4>JSON</h4>
|
||||
</header>
|
||||
Hello json
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<header>
|
||||
<h4>MQTT</h4>
|
||||
</header>
|
||||
Hello mqtt
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<header>
|
||||
<h4>Email alert</h4>
|
||||
</header>
|
||||
Hello json
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</body>
|
||||
|
||||
<style>
|
||||
.card {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
</style>
|
||||
0
data/logic.js
Normal file
0
data/logic.js
Normal file
79
data/settings.html
Normal file
79
data/settings.html
Normal file
@@ -0,0 +1,79 @@
|
||||
<!DOCTYPE html>”
|
||||
|
||||
<head>
|
||||
<link rel="stylesheet" href="/chota.css">
|
||||
|
||||
<script>
|
||||
if (window.matchMedia &&
|
||||
window.matchMedia('(prefers-color-scheme: dark)').matches) {
|
||||
document.body.classList.add('dark');
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container">
|
||||
<nav class="nav" style="margin-bottom: 50px;">
|
||||
<div class="nav-left">
|
||||
<a class="brand" href="#">Watermeter</a>
|
||||
<div class="tabs">
|
||||
<a href="/">Status</a>
|
||||
<a class="active" href="/settings">Settings</a>
|
||||
<a href="/export">Data export</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="nav-right">
|
||||
<a class="button outline">Button</a>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
<fieldset id="form-settings">
|
||||
<legend>Settings</legend>
|
||||
<p>
|
||||
<label for="input__text">Sensor max height (e.g. 4m)</label>
|
||||
<input id="input__text" type="number" placeholder="Text Input">
|
||||
</p>
|
||||
<p>
|
||||
<label for="input__password">Water max height</label>
|
||||
<input id="input__password" type="number">
|
||||
</p>
|
||||
<p>
|
||||
<label for="input__webaddress">Water min height</label>
|
||||
<input id="input__webaddress" type="number">
|
||||
</p>
|
||||
<p>
|
||||
<label for="input__emailaddress">Email Address</label>
|
||||
<input id="input__emailaddress" type="number">
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<button class="button primary" style="width: 100%;">Submit</button>
|
||||
</p>
|
||||
</fieldset>
|
||||
|
||||
<form action="/update_wifi_credentials" method="post">
|
||||
<fieldset id="form-settings">
|
||||
<legend>WiFi Settings</legend>
|
||||
<p>
|
||||
<label for="ssid">SSID</label>
|
||||
<input id="ssid" name="ssid" type="text">
|
||||
</p>
|
||||
<p>
|
||||
<label for="wifi_password">Password</label>
|
||||
<input id="wifi_password" name="wifi_password" type="password">
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<button class="button primary" style="width: 100%;">Submit</button>
|
||||
</p>
|
||||
</fieldset>
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</body>
|
||||
178
data/status.html
Normal file
178
data/status.html
Normal file
@@ -0,0 +1,178 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
<head>
|
||||
<link rel="stylesheet" href="/chota.css">
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.js"></script>
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container">
|
||||
<nav class="nav" style="margin-bottom: 50px;">
|
||||
<div class="nav-left">
|
||||
<a class="brand" href="#">Watermeter</a>
|
||||
<div class="tabs">
|
||||
<a class="active" href="/">Status</a>
|
||||
<a href="/settings">Settings</a>
|
||||
<a href="/export">Data export</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="nav-right">
|
||||
<a class="button outline">Button</a>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12 col-3-lg">
|
||||
<!--Sensor card-->
|
||||
<div class="card">
|
||||
<header>
|
||||
<h4>Sensor information</h4>
|
||||
</header>
|
||||
<table class="tg">
|
||||
<thead></thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Voltage: </td>
|
||||
<td>12.12V</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Current: </td>
|
||||
<td>10 mA</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!--ESP Infor card-->
|
||||
<div class="card">
|
||||
<header>
|
||||
<h4>ESP information</h4>
|
||||
</header>
|
||||
<table class="tg">
|
||||
<thead></thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Uptime: </td>
|
||||
<td>132d</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>FW version: </td>
|
||||
<td>0.2</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Time/Date: </td>
|
||||
<td>1.1.202022</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!--WIFI card-->
|
||||
<div class="card">
|
||||
<header>
|
||||
<h4>Connection Info</h4>
|
||||
</header>
|
||||
<table class="tg">
|
||||
<thead></thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Type: </td>
|
||||
<td>WiFi</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>IP: </td>
|
||||
<td>127.0.0.1</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>RSSI: </td>
|
||||
<td>good</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-7-lg">
|
||||
|
||||
<canvas id="myChart" style="width:100%;max-width:700px"></canvas>
|
||||
|
||||
</div>
|
||||
<div class="col-12 col-2-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>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</body>
|
||||
|
||||
<style>
|
||||
.outer-wrapper {
|
||||
display: inline-block;
|
||||
margin: 5px 15px;
|
||||
padding: 25px 15px;
|
||||
background: #eee;
|
||||
min-width: 50px;
|
||||
}
|
||||
|
||||
.column-wrapper {
|
||||
height: 400px;
|
||||
width: 20px;
|
||||
background: #CFD8DC;
|
||||
transform: rotate(180deg);
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.column {
|
||||
width: 20px;
|
||||
height: 25%;
|
||||
background: #1900ff;
|
||||
}
|
||||
|
||||
.percentage,
|
||||
.value {
|
||||
margin-top: 10px;
|
||||
padding: 5px 10px;
|
||||
color: #FFF;
|
||||
background: #263238;
|
||||
position: relative;
|
||||
border-radius: 4px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.value {
|
||||
background: #7986CB;
|
||||
}
|
||||
|
||||
.card {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
var xValues = [1,60,70,80,90,100,110,120,130,140,150];
|
||||
var yValues = [7,8,8,9,9,9,10,11,14,14,15];
|
||||
|
||||
new Chart("myChart", {
|
||||
type: "line",
|
||||
data: {
|
||||
labels: xValues,
|
||||
datasets: [{
|
||||
backgroundColor: "rgba(0,0,0,1.0)",
|
||||
borderColor: "rgba(0,0,0,0.1)",
|
||||
data: yValues
|
||||
}]
|
||||
},
|
||||
options:{}
|
||||
});
|
||||
|
||||
|
||||
</script>
|
||||
11
documentation/api_doc.md
Normal file
11
documentation/api_doc.md
Normal file
@@ -0,0 +1,11 @@
|
||||
## Sensor data API
|
||||
{
|
||||
"voltage": 12.43,
|
||||
"current": 15.2732,
|
||||
"error_code": 2,
|
||||
"level_percent":73.843,
|
||||
"level_cm": 281.3,
|
||||
"top_level_cm": 281.3,
|
||||
"bottom_level_cm": 281.3,
|
||||
"liter": 24000
|
||||
}
|
||||
15
documentation/error_codes.md
Normal file
15
documentation/error_codes.md
Normal file
@@ -0,0 +1,15 @@
|
||||
## Error codes
|
||||
1. Voltage Low
|
||||
2. Voltage High
|
||||
3. Current low
|
||||
4. Current high
|
||||
|
||||
else if (voltage_low) {
|
||||
display_error_code(1);
|
||||
} else if (voltage_high){
|
||||
display_error_code(2);
|
||||
} else if (current_low) {
|
||||
display_error_code(3);
|
||||
} else if (current_high){
|
||||
display_error_code(4);
|
||||
}
|
||||
39
include/README
Normal file
39
include/README
Normal file
@@ -0,0 +1,39 @@
|
||||
|
||||
This directory is intended for project header files.
|
||||
|
||||
A header file is a file containing C declarations and macro definitions
|
||||
to be shared between several project source files. You request the use of a
|
||||
header file in your project source file (C, C++, etc) located in `src` folder
|
||||
by including it, with the C preprocessing directive `#include'.
|
||||
|
||||
```src/main.c
|
||||
|
||||
#include "header.h"
|
||||
|
||||
int main (void)
|
||||
{
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Including a header file produces the same results as copying the header file
|
||||
into each source file that needs it. Such copying would be time-consuming
|
||||
and error-prone. With a header file, the related declarations appear
|
||||
in only one place. If they need to be changed, they can be changed in one
|
||||
place, and programs that include the header file will automatically use the
|
||||
new version when next recompiled. The header file eliminates the labor of
|
||||
finding and changing all the copies as well as the risk that a failure to
|
||||
find one copy will result in inconsistencies within a program.
|
||||
|
||||
In C, the usual convention is to give header files names that end with `.h'.
|
||||
It is most portable to use only letters, digits, dashes, and underscores in
|
||||
header file names, and at most one dot.
|
||||
|
||||
Read more about using header files in official GCC documentation:
|
||||
|
||||
* Include Syntax
|
||||
* Include Operation
|
||||
* Once-Only Headers
|
||||
* Computed Includes
|
||||
|
||||
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html
|
||||
46
lib/README
Normal file
46
lib/README
Normal file
@@ -0,0 +1,46 @@
|
||||
|
||||
This directory is intended for project specific (private) libraries.
|
||||
PlatformIO will compile them to static libraries and link into executable file.
|
||||
|
||||
The source code of each library should be placed in a an own separate directory
|
||||
("lib/your_library_name/[here are source files]").
|
||||
|
||||
For example, see a structure of the following two libraries `Foo` and `Bar`:
|
||||
|
||||
|--lib
|
||||
| |
|
||||
| |--Bar
|
||||
| | |--docs
|
||||
| | |--examples
|
||||
| | |--src
|
||||
| | |- Bar.c
|
||||
| | |- Bar.h
|
||||
| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
|
||||
| |
|
||||
| |--Foo
|
||||
| | |- Foo.c
|
||||
| | |- Foo.h
|
||||
| |
|
||||
| |- README --> THIS FILE
|
||||
|
|
||||
|- platformio.ini
|
||||
|--src
|
||||
|- main.c
|
||||
|
||||
and a contents of `src/main.c`:
|
||||
```
|
||||
#include <Foo.h>
|
||||
#include <Bar.h>
|
||||
|
||||
int main (void)
|
||||
{
|
||||
...
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
PlatformIO Library Dependency Finder will find automatically dependent
|
||||
libraries scanning project source files.
|
||||
|
||||
More information about PlatformIO Library Dependency Finder
|
||||
- https://docs.platformio.org/page/librarymanager/ldf.html
|
||||
23
platformio.ini
Normal file
23
platformio.ini
Normal file
@@ -0,0 +1,23 @@
|
||||
; PlatformIO Project Configuration File
|
||||
;
|
||||
; Build options: build flags, source filter
|
||||
; Upload options: custom upload port, speed and extra flags
|
||||
; Library options: dependencies, extra library storages
|
||||
; Advanced options: extra scripting
|
||||
;
|
||||
; Please visit documentation for the other options and examples
|
||||
; https://docs.platformio.org/page/projectconf.html
|
||||
|
||||
[env:ESP32]
|
||||
platform = espressif32
|
||||
board = wemos_d1_mini32
|
||||
framework = arduino
|
||||
monitor_speed = 115200
|
||||
lib_deps =
|
||||
ottowinter/ESPAsyncWebServer-esphome@^3.1.0
|
||||
robtillaart/INA226@^0.4.4
|
||||
bblanchon/ArduinoJson@^6.21.3
|
||||
ArduinoLog
|
||||
|
||||
upload_protocol = espota
|
||||
upload_port = 192.168.4.18
|
||||
388
src/main.cpp
Normal file
388
src/main.cpp
Normal file
@@ -0,0 +1,388 @@
|
||||
#include <Arduino.h>
|
||||
#include "SPIFFS.h"
|
||||
#include <WiFi.h>
|
||||
#include <AsyncTCP.h>
|
||||
#include <ESPAsyncWebServer.h>
|
||||
#include <ETH.h>
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <ArduinoLog.h>
|
||||
#include "AsyncJson.h"
|
||||
|
||||
#include <ArduinoOTA.h>
|
||||
|
||||
#include "INA226.h"
|
||||
#include "Wire.h"
|
||||
|
||||
#include <Preferences.h>
|
||||
|
||||
#define LED_1 4
|
||||
#define LED_2 2
|
||||
#define LED_3 15
|
||||
#define LED_4 13
|
||||
#define LED_5 12
|
||||
#define LED_RED 14
|
||||
|
||||
// Define keys to prevent typos
|
||||
#define ssid_key "ssid"
|
||||
#define wifi_password_key "wifi_password"
|
||||
|
||||
//Calibration variables
|
||||
float zero_value = 0.03; //Measured shunt voltage with nothing connected, used to fix measuring offset
|
||||
float max_water_level_cm = 200;
|
||||
float min_water_level_cm = 0;
|
||||
float sensor_range = 2.0;
|
||||
|
||||
bool voltage_high = false;
|
||||
bool voltage_low = false;
|
||||
bool current_high = false;
|
||||
bool current_low = false;
|
||||
|
||||
float rssi;
|
||||
uint32_t ip_address;
|
||||
uint8_t failed_connection_attempts = 0;
|
||||
|
||||
struct SensorData {
|
||||
int percentage;
|
||||
float voltage;
|
||||
float current;
|
||||
float water_height;
|
||||
};
|
||||
|
||||
SensorData current_data = SensorData{-1, -1, -1, -1};
|
||||
|
||||
int64_t mac_address = ESP.getEfuseMac();
|
||||
|
||||
INA226 ina_sensor(0x40);
|
||||
|
||||
Preferences prefs;
|
||||
|
||||
AsyncWebServer server(80);
|
||||
#define FORMAT_LITTLEFS_IF_FAILED true
|
||||
|
||||
|
||||
void display_percentage(int percentage){
|
||||
digitalWrite(LED_RED, 0);
|
||||
|
||||
if (percentage > 20){
|
||||
digitalWrite(LED_1, 1);
|
||||
} else {
|
||||
digitalWrite(LED_1, 0);
|
||||
}
|
||||
if (percentage > 40){
|
||||
digitalWrite(LED_2, 1);
|
||||
} else {
|
||||
digitalWrite(LED_2, 0);
|
||||
}
|
||||
if (percentage > 60){
|
||||
digitalWrite(LED_3, 1);
|
||||
} else {
|
||||
digitalWrite(LED_3, 0);
|
||||
}
|
||||
if (percentage > 80){
|
||||
digitalWrite(LED_4, 1);
|
||||
} else {
|
||||
digitalWrite(LED_4, 0);
|
||||
}
|
||||
if (percentage > 95){
|
||||
digitalWrite(LED_5, 1);
|
||||
} else {
|
||||
digitalWrite(LED_5, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void display_error_code(byte err_code) {
|
||||
digitalWrite(LED_RED, 1);
|
||||
digitalWrite(LED_1, bitRead(err_code, 0));
|
||||
digitalWrite(LED_2, bitRead(err_code, 1));
|
||||
digitalWrite(LED_3, bitRead(err_code, 2));
|
||||
digitalWrite(LED_4, bitRead(err_code, 3));
|
||||
digitalWrite(LED_5, bitRead(err_code, 4));
|
||||
|
||||
}
|
||||
|
||||
bool is_error(){
|
||||
return voltage_high || voltage_low || current_high || current_low;
|
||||
}
|
||||
|
||||
void printSuffix(Print* _logOutput, int logLevel) {
|
||||
_logOutput->print(CR);
|
||||
}
|
||||
|
||||
void print_prefix(Print* _logOutput, int logLevel) {
|
||||
_logOutput->print("WATERMETER - C");
|
||||
_logOutput->print(xPortGetCoreID());
|
||||
_logOutput->print(" - ");
|
||||
_logOutput->print(pcTaskGetName(xTaskGetCurrentTaskHandle()));
|
||||
_logOutput->print(" - ");
|
||||
}
|
||||
|
||||
void display_task(void * parameter ){
|
||||
while (true) {
|
||||
if (!is_error()){
|
||||
// We have no error, refresh status display and wait half a second
|
||||
display_percentage(current_data.percentage);
|
||||
delay(1000);
|
||||
} else {
|
||||
Log.verbose("Error detected");
|
||||
// We have an error, display error code for 3 seconds and then water level for 3 seconds
|
||||
if (voltage_low) {
|
||||
display_error_code(1);
|
||||
} else if (voltage_high){
|
||||
display_error_code(2);
|
||||
} else if (current_low) {
|
||||
display_error_code(3);
|
||||
} else if (current_high){
|
||||
display_error_code(4);
|
||||
}
|
||||
delay(3000);
|
||||
display_percentage(current_data.percentage);
|
||||
delay(3000);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void wifi_task(void * parameter ){
|
||||
Log.verbose("Starting WiFi Task");
|
||||
while (true) {
|
||||
if (prefs.getString(ssid_key, "") == "" || failed_connection_attempts > 5 ) {
|
||||
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(1000);
|
||||
}
|
||||
|
||||
|
||||
failed_connection_attempts = 0;
|
||||
} else {
|
||||
if (WiFi.isConnected() && WiFi.SSID() == prefs.getString(ssid_key, "")) {
|
||||
failed_connection_attempts = 0;
|
||||
rssi = WiFi.RSSI();
|
||||
ip_address = WiFi.localIP();
|
||||
|
||||
|
||||
|
||||
Log.verbose("RSSI: %F, IP Address, %p, SSID: %s", 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 ){
|
||||
while (true) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void setup() {
|
||||
prefs.begin("waterlevel", false);
|
||||
Serial.begin(115200);
|
||||
|
||||
Log.begin(LOG_LEVEL_VERBOSE, &Serial);
|
||||
Log.setSuffix(printSuffix);
|
||||
Log.setPrefix(print_prefix);
|
||||
|
||||
Log.verbose("Starting WiFi Task now (external)");
|
||||
xTaskCreate(wifi_task, "WiFiTask", 10000, NULL, 1, NULL);
|
||||
|
||||
Log.verbose("Init LEDs");
|
||||
pinMode(LED_1, OUTPUT);
|
||||
pinMode(LED_2, OUTPUT);
|
||||
pinMode(LED_3, OUTPUT);
|
||||
pinMode(LED_4, OUTPUT);
|
||||
pinMode(LED_5, OUTPUT);
|
||||
pinMode(LED_RED, OUTPUT);
|
||||
display_error_code(17);
|
||||
|
||||
Log.verbose("Beginning SPIFFS");
|
||||
SPIFFS.begin(true);
|
||||
Log.verbose("SPIFFS initialized");
|
||||
display_error_code(19);
|
||||
|
||||
Log.verbose("Begin INA");
|
||||
ina_sensor.begin(33, 32);
|
||||
display_error_code(20);
|
||||
ina_sensor.setMaxCurrentShunt(0.02, 4, false);
|
||||
ina_sensor.setBusVoltageConversionTime(7);
|
||||
ina_sensor.setShuntVoltageConversionTime(7);
|
||||
ina_sensor.setAverage(4);
|
||||
display_error_code(21);
|
||||
|
||||
Log.verbose("Connecting Ethernet");
|
||||
ETH.begin(0, 17, 23, 18);
|
||||
Log.verbose(ETH.getHostname());
|
||||
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("/settings", HTTP_GET, [](AsyncWebServerRequest *request){
|
||||
request->send(SPIFFS, "/settings.html", "text/html", false);
|
||||
});
|
||||
|
||||
server.on("/export", HTTP_GET, [](AsyncWebServerRequest *request){
|
||||
request->send(SPIFFS, "/data_export.html", "text/html", false);
|
||||
});
|
||||
|
||||
server.on("/update_wifi_credentials", HTTP_POST, [](AsyncWebServerRequest *request){
|
||||
int params = request->params();
|
||||
|
||||
// Log.verbose(request->hasParam(ssid_key));
|
||||
// Log.verbose(request->hasParam(wifi_password_key));
|
||||
|
||||
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 {
|
||||
for(int i=0;i<params;i++){
|
||||
AsyncWebParameter* p = request->getParam(i);
|
||||
if(p->isFile()){ //p->isPost() is also true
|
||||
Log.verbose("POST[%s]: %s\n", p->name().c_str(), p->value().c_str());
|
||||
} else if(p->isPost()){
|
||||
Log.verbose("POST[%s]: %s\n", p->name().c_str(), p->value().c_str());
|
||||
} else {
|
||||
Log.verbose("GET[%s]: %s\n", p->name().c_str(), p->value().c_str());
|
||||
}
|
||||
}
|
||||
request->send(400, "text/plain", "Missing parameters"); //TODO add proper error messages
|
||||
}
|
||||
request->send(SPIFFS, "/settings.html", "text/html", false); //TODO add proper return templating
|
||||
});
|
||||
|
||||
server.on("/sensor_data", HTTP_GET, [](AsyncWebServerRequest *request){
|
||||
StaticJsonDocument<128> doc;
|
||||
doc["percentage"] = current_data.percentage;
|
||||
doc["voltage"] = current_data.voltage;
|
||||
doc["current"] = current_data.current;
|
||||
doc["water_height"] = current_data.water_height;
|
||||
|
||||
String output;
|
||||
serializeJson(doc, output);
|
||||
request->send(200, "application/json", output);
|
||||
});
|
||||
|
||||
server.on("/chota.css", HTTP_GET, [](AsyncWebServerRequest *request){
|
||||
request->send(SPIFFS, "/chota.css", "text/css", false);
|
||||
});
|
||||
|
||||
display_error_code(23);
|
||||
|
||||
Log.verbose("Starting webserver");
|
||||
server.begin();
|
||||
display_error_code(24);
|
||||
|
||||
Log.verbose("OTA Setup");
|
||||
ArduinoOTA
|
||||
.onStart([]() {
|
||||
String type;
|
||||
if (ArduinoOTA.getCommand() == U_FLASH)
|
||||
type = "sketch";
|
||||
else // U_SPIFFS
|
||||
type = "filesystem";
|
||||
|
||||
// NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
|
||||
Log.verbose("Start updating %s", type);
|
||||
})
|
||||
.onEnd([]() {
|
||||
Log.verbose("\nEnd");
|
||||
})
|
||||
.onProgress([](unsigned int progress, unsigned int total) {
|
||||
Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
|
||||
})
|
||||
.onError([](ota_error_t error) {
|
||||
Serial.printf("Error[%u]: ", error);
|
||||
if (error == OTA_AUTH_ERROR) Log.verbose("Auth Failed");
|
||||
else if (error == OTA_BEGIN_ERROR) Log.verbose("Begin Failed");
|
||||
else if (error == OTA_CONNECT_ERROR) Log.verbose("Connect Failed");
|
||||
else if (error == OTA_RECEIVE_ERROR) Log.verbose("Receive Failed");
|
||||
else if (error == OTA_END_ERROR) Log.verbose("End Failed");
|
||||
});
|
||||
display_error_code(25);
|
||||
ArduinoOTA.begin();
|
||||
display_error_code(26);
|
||||
digitalWrite(LED_RED, 0);
|
||||
Log.verbose(ETH.localIP());
|
||||
|
||||
xTaskCreate(display_task, "DisplayTask", 10000, NULL, 1, NULL);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Updates the global variables from the current ina sensor data
|
||||
* All other parts just read these variables to prevent incositencies
|
||||
*/
|
||||
void update_sensor_data(){
|
||||
float bus_voltage = ina_sensor.getBusVoltage();
|
||||
float shunt_voltage = ina_sensor.getShuntVoltage_mV() - zero_value;
|
||||
float shunt_current = shunt_voltage / 4 ;
|
||||
|
||||
|
||||
float mA_per_cm = (20 - 4)/ (sensor_range*100);
|
||||
|
||||
|
||||
float min_water_level_mA_over_zero = (min_water_level_cm * mA_per_cm);
|
||||
float max_water_level_mA_over_zero = (max_water_level_cm * mA_per_cm);
|
||||
|
||||
float min_water_level_mA = 4 + min_water_level_mA_over_zero;
|
||||
float max_water_level_mA = 4 + max_water_level_mA_over_zero;
|
||||
|
||||
// Over Zero always revers to zero water level
|
||||
float shunt_current_over_zero = shunt_current - min_water_level_mA;
|
||||
|
||||
int percentage = round(( shunt_current_over_zero / max_water_level_mA_over_zero) * 100);
|
||||
|
||||
|
||||
current_low = shunt_current < 3.5;
|
||||
current_high = shunt_current > 20.5;
|
||||
voltage_low = bus_voltage < 23;
|
||||
voltage_high = bus_voltage > 25;
|
||||
Log.verbose("==========");
|
||||
Log.verbose("Shunt current: %F", shunt_current);
|
||||
Log.verbose("Shunt voltage: %F", shunt_voltage);
|
||||
Log.verbose("Value percentage: %F", percentage);
|
||||
|
||||
|
||||
current_data = SensorData{percentage, bus_voltage, shunt_current, shunt_current_over_zero};
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
void loop() {
|
||||
ArduinoOTA.handle();
|
||||
|
||||
|
||||
update_sensor_data();
|
||||
// Log.verbose(WiFi.softAPIP());
|
||||
// Log.verbose(ETH.localIP());
|
||||
delay(5000);
|
||||
}
|
||||
|
||||
|
||||
11
test/README
Normal file
11
test/README
Normal file
@@ -0,0 +1,11 @@
|
||||
|
||||
This directory is intended for PlatformIO Test Runner and project tests.
|
||||
|
||||
Unit Testing is a software testing method by which individual units of
|
||||
source code, sets of one or more MCU program modules together with associated
|
||||
control data, usage procedures, and operating procedures, are tested to
|
||||
determine whether they are fit for use. Unit testing finds problems early
|
||||
in the development cycle.
|
||||
|
||||
More information about PlatformIO Unit Testing:
|
||||
- https://docs.platformio.org/en/latest/advanced/unit-testing/index.html
|
||||
Reference in New Issue
Block a user