Skip to content
Snippets Groups Projects
Commit 6f5a6c76 authored by Carlo Guarnieri Calo' Carducci's avatar Carlo Guarnieri Calo' Carducci
Browse files

repo: add power_plant web interface

parent 2c5255ae
Branches
No related tags found
No related merge requests found
Showing
with 534 additions and 24 deletions
components/power_plant/docs/web_page.png

129 KiB

This diff is collapsed.
components/power_plant/html/favicon.ico

4.19 KiB

components/power_plant/html/images/entity.png

131 KiB

components/power_plant/html/images/legos.png

6.3 KiB

components/power_plant/html/images/load_high.png

5.18 KiB

components/power_plant/html/images/load_low.png

5.35 KiB

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
sodipodi:docname="meter.svg"
inkscape:version="1.0 (4035a4fb49, 2020-05-01)"
id="svg1078"
version="1.1"
viewBox="0 0 31.75 21.166668"
height="21.166668mm"
width="31.75mm">
<defs
id="defs1072">
<linearGradient
osb:paint="solid"
id="linearGradient1985">
<stop
id="stop1983"
offset="0"
style="stop-color:#e3dbdb;stop-opacity:1;" />
</linearGradient>
</defs>
<sodipodi:namedview
inkscape:window-maximized="1"
inkscape:window-y="-8"
inkscape:window-x="-8"
inkscape:window-height="1027"
inkscape:window-width="1920"
showgrid="false"
inkscape:document-rotation="0"
inkscape:current-layer="layer1"
inkscape:document-units="mm"
inkscape:cy="5.2082998"
inkscape:cx="46.074782"
inkscape:zoom="3.959798"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
borderopacity="1.0"
bordercolor="#666666"
pagecolor="#ffffff"
id="base" />
<metadata
id="metadata1075">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
transform="translate(15.875001,10.583334)"
id="layer1"
inkscape:groupmode="layer"
inkscape:label="Layer 1">
<rect
ry="3.4395833"
y="-10.630581"
x="-15.875001"
height="21.166666"
width="31.75"
id="rect1647"
style="opacity:0.999;mix-blend-mode:normal;fill:#e3dbdb;fill-opacity:1;stroke:#000000;stroke-width:1.5;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;paint-order:normal" />
</g>
</svg>
components/power_plant/html/images/tower.png

24.5 KiB

<!DOCTYPE html>
<html>
<title>LEGOS</title>
<meta name="viewport" content="width=device-width, initial-scale=1" charset="UTF-8">
<link rel="stylesheet" href="css/w3.css">
<style>
body,
h1,
h2,
h3,
h4,
h5,
h6 {
font-family: Verdana, sans-serif;
}
</style>
<script>
setInterval(get_status, 1000);
function set_max_load(max_load) {
document.getElementById("s1").style.opacity = (max_load >= 60) ? "1" : "0.2";
document.getElementById("s2").style.opacity = (max_load >= 70) ? "1" : "0.2";
document.getElementById("s3").style.opacity = (max_load >= 80) ? "1" : "0.2";
document.getElementById("s4").style.opacity = (max_load >= 90) ? "1" : "0.2";
document.getElementById("s5").style.opacity = (max_load >= 100) ? "1" : "0.2";
var x = new XMLHttpRequest();
x.open("POST", "post", true);
x.send("max_load|" + max_load / 100.0);
}
function get_status() {
var x = new XMLHttpRequest();
x.onreadystatechange = function () {
if (x.readyState == 4 && x.status == 200) {
var resp = x.responseText.split(";");
var powr = resp[0].split("|")[1];
var load = resp[1].split("|")[1];
document.getElementById("meter_out").innerHTML = powr + " W";
}
};
x.open("GET", "status", true);
x.send();
}
</script>
<body>
<!-- HEADER -->
<header class="w3-container w3-border-bottom w3-border-orange">
<div class="w3-row">
<div class="w3-left">
<h3 class="w3-opacity""><b>Power plant</b></h3>
</div>
<div class=" w3-right">
<button class="w3-button w3-text-orange w3-hover-opacity"
onclick="location.href='https://192.168.1.1';">
<h4></h4>
</button>
<button class="w3-button w3-text-orange w3-hover-opacity"
onclick="document.getElementById('help').style.display='block'">
<h4>?</h4>
</button>
</div>
</div>
</header>
<!-- HELP -->
<div id="help" class="w3-panel w3-orange w3-display-container" style="display:none">
<span onclick="this.parentElement.style.display='none'"
class="w3-button w3-orange w3-large w3-display-topright">X</span>
<p class="w3-small">The power plant is the largest power producer, which converts traditional fossil fuel in
electricity. The higher is the electricity demand in the
grid, the higher is the load for the power plant. The maximum load can be varied using the slider <img
src="images/load_high.png" style="max-height:15px"> from 50 % to 100 %.</p>
</div>
<!-- MAIN -->
<div class="w3-display-container w3-animate-opacity w3-center">
<!-- TOP -->
<div class="w3-row w3-margin-bottom w3-padding">
<div class="w3-left">
<svg viewBox="0 0 44.212619 90" height="80" width="44.21262">
<g transform="translate(-102.13915,-106.12564) scale(1)" stroke-width="1.88976378" stroke="black">
<path d="m 104.88033,139.37353 4.77497,-2.74118" />
<path d="m 143.69901,116.91352 -4.86339,2.82961" />
<path d="m 135.38704,108.86682 -2.74118,4.67121" />
<path d="m 138.83562,136.63235 4.77497,2.74118" />
<path d="m 113.10388,108.86682 2.74118,4.72378" />
<path d="m 104.79191,116.91352 4.86339,2.82961" />
<path d="m 141.48838,128.14352 h 4.86339" />
<path d="m 102.13915,128.14352 h 4.86339" />
<path d="m 124.24534,106.12564 v 4.88902" />
<path
d="m 128.66672,146.6244 c 0,1.51862 0.17685,1.30667 0.17685,4.06756 0,3.07193 -2.37561,4.60834 -4.59811,4.59811 -2.2225,0.0103 -4.59811,-1.52618 -4.59811,-4.59811 0,-2.76089 0.17685,-2.54894 0.17685,-4.06756"
style="fill:none" />
<path
d="m 119.8242,146.44743 c -0.12114,-0.91128 -0.29248,-1.81947 -0.47001,-2.71669 -0.79976,-4.64448 -4.08315,-8.28143 -5.59057,-12.66267 -1.18608,-2.98298 -1.02564,-6.51248 0.6081,-9.2978 1.93545,-3.48423 5.87434,-5.65016 9.87348,-5.65269 3.99914,0.003 7.93803,2.1687 9.87348,5.65293 1.63374,2.78532 1.79418,6.31482 0.6081,9.29779 -1.50742,4.38125 -4.79081,8.0182 -5.59057,12.66268 -0.17753,0.89722 -0.34836,1.80541 -0.46949,2.71657 z"
style="fill:rgb(255, 225, 0);fill-opacity:1;" id="bulb" onclick="loop_brightness()" />
</g>
</svg>
</div>
<div class="w3-right w3-margin-top">
<img src="images/load_low.png" style="max-height:35px">
<svg viewBox="0 0 100 20" width="100" height="20">
<rect id="s1" x="0" y="0" width="15" height="20" fill="orange" opacity="1"
onclick="set_max_load(60)" />
<rect id="s2" x="20" y="0" width="15" height="20" fill="orange" opacity="1"
onclick="set_max_load(70)" />
<rect id="s3" x="40" y="0" width="15" height="20" fill="orange" opacity="1"
onclick="set_max_load(80)" />
<rect id="s4" x="60" y="0" width="15" height="20" fill="orange" opacity="0.2"
onclick="set_max_load(90)" />
<rect id="s5" x="80" y="0" width="15" height="20" fill="orange" opacity="0.2"
onclick="set_max_load(100)" />
</svg>
<img src="images/load_high.png" style="max-height:35px">
</div>
</div>
<!-- MIDDLE -->
<div class="w3-middle">
<img src="images/entity.png" alt="Entity"
style="width:75%;min-height:250px;max-height:500px;max-width:490px">
</div>
<!-- BOTTOM -->
<div class="w3-cell-row w3-margin-top">
<div class="w3-cell w3-cell-bottom" style="width:30%">
<img src="images/tower.png" alt="Power grid"
style="width:70%;min-height:50px;max-height:180px;max-width:138px">
</div>
<div class="w3-cell w3-cell-top" style="width:40%">
<svg viewBox="0 0 120 80" height="80" width="120">
<g transform="translate(60,40)" id="layer1" inkscape:groupmode="layer" inkscape:label="Layer 1">
<rect ry="3.4395833" x="-55" y="-35" height="70" width="110"
style="opacity:1;fill:#e2e2e2;fill-opacity:1;stroke:black;stroke-width:4.5" />
<text x="-45" y="-15" fill="black">Power: </text>
<text x="-20" y="10" fill="black" font-weight="bold" id="meter_out">0 W</text>
</g>
</svg>
<svg class="w3-animate-fading" viewBox="0 0 100 50" height="50" width=100% preserveAspectRatio="none">
<style>
line {
stroke: black;
stroke-width: 2.5;
vector-effect: non-scaling-stroke;
}
</style>
<defs>
<marker id="arrowhead" markerWidth="7" markerHeight="9" markerUnits="userSpaceOnUse" refX="7"
refY="4.5" orient="auto">
<polygon points="0 0, 7 4.5, 0 9" />
</marker>
</defs>
<line x1="50" y1="35" x2="50" y2="0" />
<line x1="50" y1="35" x2="0" y2="35" marker-end="url(#arrowhead)" />
</svg>
</div>
<div class="w3-cell w3-cell-bottom" style="width:30%">
</div>
</div>
</div>
<!-- FOOTER -->
<footer class="w3-container w3-border-top w3-border-orange w3-padding">
<div class="w3-row">
<div class="w3-left">
<p class="w3-opacity w3-small">© ACS 2021</p>
</div>
<div class="w3-right">
<img src="images/legos.png" alt="Logo" style="height:25px">
</div>
</div>
</footer>
</body>
</html>
\ No newline at end of file
......@@ -91,17 +91,12 @@ static const char mqtt_topic_pub[] = "/legos/power_plant";
static const char mqtt_topic_sub[] = "/legos/power_plant/cmd";
/**
* @brief Entity HTML webpage
*/
static const char html_webpage[] = "";
#ifdef __cplusplus
extern "C" {
#endif
esp_err_t powerplant_init_entity();
esp_err_t powerplant_http_post(char *str);
esp_err_t powerplant_http_resp(char *str);
esp_err_t powerplant_http_read(char *str);
esp_err_t powerplant_mqtt_init(void *client);
esp_err_t powerplant_mqtt_post(void *client);
......@@ -113,7 +108,7 @@ esp_err_t powerplant_mqtt_read(void *event);
// Callback static linking
#define init_entity() powerplant_init_entity()
#define http_post(x) powerplant_http_post(x)
#define http_resp(x) powerplant_http_resp(x)
#define http_read(x) powerplant_http_read(x)
#define mqtt_init(x) powerplant_mqtt_init(x)
#define mqtt_post(x) powerplant_mqtt_post(x)
......
......@@ -64,6 +64,18 @@ flowchart TD
- Description: Update firmware
## HTTP callbacks
### GET
- **/status**
- Telemetry
- *Format*: `power|<W>;load|<val>`
- *Description*: Log telemetry data, **W** is the input power measured at the meter. Current load **val** is in the range [0.0 to 1.0](*eg.* `power|0.32;load|0.5`)
### POST
- **/post**
- Max load
- Format: `max_load|<val>`
- Description: Set max power load to **val** [0.0 to 1.0]
### WEB PAGE
[<img src="docs/web_page.png" width="121" height="200">](docs/web_page.png)
## Related Links
......
......@@ -528,35 +528,54 @@ esp_err_t powerplant_init_entity()
//
/**
* @brief HTTP post
* Callback for sending web content via HTTP
* @brief HTTP resp
* Callback for responding to HTTP GET requests
*
* @param[in,out] str Message buffer
*
* @return
* - ESP_OK Success
*/
esp_err_t powerplant_http_post(char* str)
esp_err_t powerplant_http_resp(char* str)
{
return ESP_OK;
sprintf(str, "power|%.2f;load|%.2f",
entity.power_out.power*1000,
entity.power_out.current/4000.0);
return ESP_OK;
}
/**
* @brief HTTP read
* Callback for reading web content via HTTP
* Callback for reading to HTTP POST requests
*
* @param[in,out] str Message buffer
*
* @return
* - ESP_OK Success
* - ESP_OK : Request correctly processed
* - ESP_FAIL : Request fail
*/
esp_err_t powerplant_http_read(char *str)
{
return ESP_OK;
// Unmarshal value-data pair
msg_payload_t msg_payload;
if (msg_unmarshall(str, "max_load", &msg_payload)){
float val = atof(msg_payload.val);
if ((val>=0.0) && (val<=1.0)){
entity.setpoint_imax = 2*(val-0.5)*255;
ESP_LOGD(TAG, "MAX LOAD");
}
return ESP_OK;
}
return ESP_FAIL;
}
/**
* @brief MQTT init topics
* Subscribe to entity-specific topics
......@@ -568,10 +587,10 @@ esp_err_t powerplant_mqtt_init(void *client)
{
mqtt_client = (esp_mqtt_client_handle_t)client;
esp_mqtt_client_subscribe(mqtt_client, mqtt_topic_service, MQTT_QOS_LEVEL_0);
esp_mqtt_client_subscribe(mqtt_client, mqtt_topic_service, CONFIG_MQTT_QOS_LEVEL);
ESP_LOGD(TAG, "Subscribing MQTT topic: %s", mqtt_topic_service);
esp_mqtt_client_subscribe(mqtt_client, mqtt_topic_sub, MQTT_QOS_LEVEL_0);
esp_mqtt_client_subscribe(mqtt_client, mqtt_topic_sub, CONFIG_MQTT_QOS_LEVEL);
ESP_LOGD(TAG, "Subscribing MQTT topic: %s", mqtt_topic_sub);
return ESP_OK;
......@@ -592,7 +611,7 @@ esp_err_t powerplant_mqtt_post(void *client)
mqtt_client = (esp_mqtt_client_handle_t)client;
char data[64];
sprintf(data,"%10s: %7.1f mA [%4.2f V]",TAG,entity.power_out.current,entity.power_out.voltage);
esp_mqtt_client_publish(mqtt_client, mqtt_topic_pub, data, 0, 0, 0);
esp_mqtt_client_publish(mqtt_client, mqtt_topic_pub, data, 0, CONFIG_MQTT_QOS_LEVEL, 0);
ESP_LOGD(TAG, "Outgoing MQTT message: %s\n%s", mqtt_topic_pub, data);
memset(data,0,64);
......@@ -628,11 +647,11 @@ esp_err_t powerplant_mqtt_read(void *event)
ESP_LOGD(TAG, "Incoming MQTT message: %s\n%s", topic, data);
// Unmarshal value-data pair
mqtt_payload_t mqtt_payload;
msg_payload_t msg_payload;
if (!strcmp(topic,mqtt_topic_service)){
if (mqtt_unmarshall(data, "update", &mqtt_payload))
if (!strcmp(mqtt_payload.val,TAG)){
if (msg_unmarshall(data, "update", &msg_payload))
if (!strcmp(msg_payload.val,TAG)){
ESP_LOGD(TAG, "UPDATE");
// TODO
return ESP_OK;
......@@ -640,15 +659,15 @@ esp_err_t powerplant_mqtt_read(void *event)
}
if (!strcmp(topic,mqtt_topic_sub)){
if (mqtt_unmarshall(data, "vbus", &mqtt_payload)){
uint8_t val = atoi(mqtt_payload.val);
if (msg_unmarshall(data, "vbus", &msg_payload)){
uint8_t val = atoi(msg_payload.val);
entity.setpoint_vbus = val;
ESP_LOGD(TAG, "BUS VOLTAGE");
return ESP_OK;
}
if (mqtt_unmarshall(data, "imax", &mqtt_payload)){
uint8_t val = atoi(mqtt_payload.val);
if (msg_unmarshall(data, "imax", &msg_payload)){
uint8_t val = atoi(msg_payload.val);
entity.setpoint_imax = val;
ESP_LOGD(TAG, "BUS CURRENT");
return ESP_OK;
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment