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

repo: add substation web interface

parent c4d927ee
No related branches found
No related tags found
No related merge requests found
components/substation/docs/web_page.png

101 KiB

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

4.19 KiB

components/substation/html/images/control.png

2.83 KiB

components/substation/html/images/entity.png

217 KiB

components/substation/html/images/legos.png

6.3 KiB

components/substation/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_fault(branch) {
var x = new XMLHttpRequest();
x.open("POST", "post", true);
x.send("fault|" + branch);
}
function get_status() {
var x = new XMLHttpRequest();
x.onreadystatechange = function () {
if (x.readyState == 4 && x.status == 200) {
for (let i=0; i<5; i++){
document.getElementById("sw"+(i+1).toString()).style.visibility = (status[i].split("|")[0] == 1) ? "visible" : "hidden";
document.getElementById("sc"+(i+1).toString()).style.visibility = (status[i].split("|")[0] == 1) ? "visible" : "hidden";
document.getElementById("sc"+(i+1).toString()).style.fill = (status[i].split("|")[1] == 1) ? "#ff0000" : "#008000";
}
}
};
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>Substation</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 substation is an essential element for the automation of an electrical grid, which
provides monitoring and protection functions. In case of fault, the instruments in the substation first
identify which feeders are affected and then trip the respective switches to isolate the short-circuit.
Manual control is possible using the switch controls <img src="images/control.png"
style="max-height:15px">.
</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:0.4;" id="bulb" onclick="loop_brightness()" />
</g>
</svg>
</div>
<div class="w3-right w3-margin-top">
<svg viewBox="0 0 120 100" width="120" height="100">
<style>
line, circle {
stroke: black;
stroke-width: 2.5;
vector-effect: non-scaling-stroke;
}
</style>
<line id="sc1" x1="60" y1="50" x2="82" y2="11" />
<line id="sc2" x1="60" y1="50" x2="105"y2="50" />
<line id="sc3" x1="60" y1="50" x2="82" y2="89" />
<line id="sc4" x1="60" y1="50" x2="37" y2="89" />
<line id="sc5" x1="60" y1="50" x2="15" y2="50" />
<line id="sc6" x1="60" y1="50" x2="37" y2="11" />
<circle id="sc1" cx="82" cy="11" r="10" fill="#ffffff" onclick="set_fault(0)" />
<circle id="sc2" cx="105"cy="50" r="10" fill="#ffffff" onclick="set_fault(1)" />
<circle id="sc3" cx="82" cy="89" r="10" fill="#ffffff" onclick="set_fault(2)" />
<circle id="sc4" cx="37" cy="89" r="10" fill="#ffffff" onclick="set_fault(3)" />
<circle id="sc5" cx="15" cy="50" r="10" fill="#ffffff" onclick="set_fault(4)" />
<circle id="sc6" cx="37" cy="11" r="10" fill="#ffffff" onclick="set_fault(5)" />
</svg>
</div>
</div>
<!-- MIDDLE -->
<div class="w3-middle">
<img src="images/entity.png" alt="Entity"
style="width:65%;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">
<text x="-50" y="10" fill="black" font-weight="bold" id="meter_in">Monitoring</text>
</g>
</svg>
<svg class="w3-animate-fading" viewBox="0 0 100 50" height="50" width=100% preserveAspectRatio="none">
<style>
line, path {
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="0" y1="35" x2="35" y2="35" />
<line x1="35" y1="35" x2="65" y2="15" />
<line x1="65" y1="35" x2="100" y2="35" />
<path d="M 40 10 q 10 5 15 30" fill="none" marker-end="url(#arrowhead)"/>
</svg>
</div>
<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>
</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
......@@ -95,7 +95,7 @@ extern "C" {
#endif
esp_err_t substation_init_entity();
esp_err_t substation_http_post(char *str);
esp_err_t substation_http_resp(char *str);
esp_err_t substation_http_read(char *str);
esp_err_t substation_mqtt_init(void *client);
esp_err_t substation_mqtt_post(void *client);
......@@ -107,7 +107,7 @@ esp_err_t substation_mqtt_read(void *event);
// Callback static linking
#define init_entity() substation_init_entity()
#define http_post(x) substation_http_post(x)
#define http_resp(x) substation_http_resp(x)
#define http_read(x) substation_http_read(x)
#define mqtt_init(x) substation_mqtt_init(x)
#define mqtt_post(x) substation_mqtt_post(x)
......
......@@ -20,7 +20,7 @@ static timer_config_t cfg = { TIMER_ALARM_EN, // Timer configuration
static esp_mqtt_client_handle_t mqtt_client; // MQTT client handle
static esp_mqtt_event_handle_t mqtt_event; // MQTT event handle
static uint8_t remote_override = 0;
uint8_t PIN_EN[6] = {BUS_EN_1,BUS_EN_2,BUS_EN_3,BUS_EN_4,BUS_EN_5,BUS_EN_6};
uint8_t PIN_IRQ[6] = {BUS_IRQ_1,BUS_IRQ_2,BUS_IRQ_3,BUS_IRQ_4,BUS_IRQ_5,BUS_IRQ_6};
......@@ -177,35 +177,37 @@ void entity_main_tasks(void* arg)
// Detect bus status
bool fault = false;
for (int i=0; i<6; i++){
if (!remote_override){
for (int i=0; i<6; i++){
entity.bus_status[i] = !gpio_get_level(PIN_IRQ[i]);
fault |= entity.bus_status[i];
}
} else {
remote_override--;
}
// FLISR
if (!fault){ // Restore service
// Restore service
for (int i=0; i<6; i++)
if (entity.bus_linked[i])
gpio_set_level(PIN_EN[i],0);
fault_cnt = -1;
} else { // Trip reclosers
if (fault_cnt < 0){
fault_cnt = 0;
// Start overload sequence
for (int i=0; i<6; i++)
if (entity.bus_status[i])
pwm_fade(i, LED_INTENSITY_100, 10000);
pwm_fade(i, LED_INTENSITY_100, 3000);
} else {
fault_cnt++;
// Open reclosers with delay
if (fault_cnt == 100)
if (fault_cnt == 40)
for (int i=0; i<6; i++)
if (entity.bus_status[i])
gpio_set_level(PIN_EN[i],1);
}
fault_cnt++;
}
}
vTaskDelete(NULL);
......@@ -348,35 +350,59 @@ esp_err_t substation_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 substation_http_post(char* str)
esp_err_t substation_http_resp(char* str)
{
sprintf(str,"%d|%d;%d|%d;%d|%d;%d|%d;%d|%d;%d|%d",
entity.bus_linked[0], entity.bus_status[0],
entity.bus_linked[1], entity.bus_status[1],
entity.bus_linked[2], entity.bus_status[2],
entity.bus_linked[3], entity.bus_status[3],
entity.bus_linked[4], entity.bus_status[4],
entity.bus_linked[5], entity.bus_status[5]);
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 substation_http_read(char *str)
{
return ESP_OK;
// Unmarshal value-data pair
msg_payload_t msg_payload;
if (msg_unmarshall(str, "fault", &msg_payload)){
uint8_t val = atoi(msg_payload.val);
if ((val>=0) && (val<=5)){
remote_override = 100;
entity.bus_status[val] = 1;
ESP_LOGD(TAG, "DELTA");
}
return ESP_OK;
}
return ESP_FAIL;
}
/**
* @brief MQTT init topics
* Subscribe to entity-specific topics
......@@ -388,10 +414,10 @@ esp_err_t substation_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;
......@@ -418,7 +444,7 @@ esp_err_t substation_mqtt_post(void *client)
entity.bus_linked[3], entity.bus_status[3],
entity.bus_linked[4], entity.bus_status[4],
entity.bus_linked[5], entity.bus_status[5]);
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);
return ESP_OK;
}
......@@ -448,11 +474,11 @@ esp_err_t substation_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;
......
......@@ -53,6 +53,18 @@ flowchart TD
- Description: Update firmware
## HTTP callbacks
### GET
- **/status**
- Telemetry
- *Format*: `L1|F1;L2|F2...`
- *Description*: Log telemetry data **L|F**, where **L** is the link status and **F** is the fault status.(*eg.* `1|0;0|0;1|1;0|1;1|1;0|0`)
### POST
- **/post**
- Delta
- Format: `fault|<val>`
- Description: Set fault to branch **val** [0 to 6]
### WEB PAGE
[<img src="docs/web_page.png" width="119" height="200">](docs/web_page.png)
## Related Links
......
......@@ -67,7 +67,7 @@ flowchart TD
- **/status**
- Telemetry
- *Format*: `power|<W>;chp|<Wext>`
- *Description*: Log telemetry data, **W** is the input power measured at the meter. Power generated from gas **Wext** is in the range [0.0 to 1.0](*eg.* `power|0.32;chp|0.5`)
- *Description*: Log telemetry data, **W** is the input power measured at the meter. Power generated from gas is **Wext**.(*eg.* `power|0.32;chp|0.5`)
### POST
- **/post**
- Light
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment