Skip to content

Commit a41286a

Browse files
added local code from app lab
1 parent d25ec34 commit a41286a

23 files changed

Lines changed: 1375 additions & 0 deletions

‎app_lab/tecovolt-rebrand/README.md‎

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
# Linux Blink with UI (JavaScript)
2+
3+
The **Linux Blink** example shows a simple Linux application that changes the LED state on the board. It showcases basic event handling and UI updates through a web-based interface.
4+
5+
![Linux Blink App](assets/docs_assets/linux-blink-banner.png)
6+
7+
## Description
8+
9+
This example toggles an LED on the board using a simple web user interface. The application listens for user input through a web browser and updates the LED state accordingly. It shows how to interact with hardware from a Linux environment and provides a basis for building more complex hardware-interfacing applications.
10+
11+
The `assets` folder contains the **frontend** components of the application. Inside, you'll find the JavaScript source files along with the HTML and CSS files that make up the web user interface. The `python` folder instead includes the application **backend**.
12+
13+
The interactive toggle switch UI is generated with JavaScript, while the Arduino sketch manages the LED hardware control. The Router Bridge enables communication between the web interface and the microcontroller.
14+
15+
## Bricks Used
16+
17+
The Linux blink example uses the following Bricks:
18+
19+
- `web_ui`: Brick to create a web interface to display the LED control toggle switch.
20+
21+
## Hardware and Software Requirements
22+
23+
### Hardware
24+
25+
- Arduino UNO Q (x1)
26+
- USB-C® cable (for power and programming) (x1)
27+
28+
### Software
29+
30+
- Arduino App Lab
31+
32+
**Note:** You can run this example using your Arduino UNO Q as a Single Board Computer (SBC) using a [USB-C® hub](https://store.arduino.cc/products/usb-c-to-hdmi-multiport-adapter-with-ethernet-and-usb-hub) with a mouse, keyboard and display attached.
33+
34+
## How to Use the Example
35+
36+
1. Run the App
37+
![Arduino App Lab - Run App](assets/docs_assets/app-lab-run-app.png)
38+
2. Open the App in your browser at `<UNO-Q-IP-ADDRESS>:7000`
39+
3. Click on the circular switch to change the state of the LED
40+
41+
## How it Works
42+
43+
Once the application is running, the device performs the following operations:
44+
45+
- **Serving the web interface and handling WebSocket communication.**
46+
47+
The `web_ui` Brick provides the web server and WebSocket communication:
48+
49+
```python
50+
from arduino.app_bricks.web_ui import WebUI
51+
52+
ui = WebUI()
53+
ui.on_message('toggle_led', toggle_led_state)
54+
ui.on_message('get_initial_state', on_get_initial_state)
55+
```
56+
57+
- **Communicating LED state to the Arduino.**
58+
59+
The Router Bridge sends LED commands to the microcontroller:
60+
61+
```python
62+
Bridge.call("set_led_state", led_is_on)
63+
```
64+
65+
- **Controlling the hardware LED.**
66+
67+
The Arduino sketch handles the LED hardware control:
68+
69+
```cpp
70+
void set_led_state(bool state) {
71+
digitalWrite(LED_BUILTIN, state ? LOW : HIGH);
72+
}
73+
```
74+
75+
The high-level data flow looks like this:
76+
77+
```
78+
Web Browser Toggle → WebSocket → Python Backend → Router Bridge → Arduino LED Control
79+
```
80+
81+
## Understanding the Code
82+
83+
Here is a brief explanation of the application components:
84+
85+
### 🔧 Backend (`main.py`)
86+
87+
The Python code manages the web interface, handles user interactions, and communicates with the Arduino.
88+
89+
- **`ui = WebUI()`:** Initializes the web server that serves the HTML interface and handles WebSocket communication.
90+
91+
- **`ui.on_message('toggle_led', toggle_led_state)`:** Registers a WebSocket message handler that responds when the user clicks the toggle button in the web interface.
92+
93+
- **`ui.send_message('led_status_update', get_led_status())`:** Sends LED status updates to all connected web clients in real-time.
94+
95+
- **`Bridge.call("set_led_state", led_is_on)`:** Calls the Arduino function to physically control the LED hardware.
96+
97+
- **`get_led_status()`:** Returns the current LED state as a dictionary for the web interface.
98+
99+
### 🔧 Frontend (`index.html` + `app.js`)
100+
101+
The web interface provides a simple toggle button for LED control.
102+
103+
- **Socket.IO connection:** Establishes WebSocket communication with the Python backend through the `web_ui` Brick.
104+
105+
- **`socket.emit('toggle_led', {})`:** Sends a toggle message to the backend when the user clicks the button.
106+
107+
- **`socket.on('led_status_update', updateLedStatus)`:** Receives LED status updates and updates the button appearance accordingly.
108+
109+
- **`updateLedStatus(status)`:** Changes the button's visual state (LED IS ON/OFF) based on the received status.
110+
111+
### 🔧 Hardware (`sketch.ino`)
112+
113+
The Arduino code handles LED hardware control and sets up Bridge communication.
114+
115+
- **`pinMode(LED_BUILTIN, OUTPUT)`:** Configures the built-in LED pin as an output for controlling the LED state.
116+
117+
- **`Bridge.begin()`:** Initializes the Router Bridge communication system for receiving commands from Python.
118+
119+
- **`Bridge.provide(...)`:** Registers the `set_led_state` function to be callable from the Python web interface.
120+
121+
- **`set_led_state(bool state)`:** Controls the LED hardware by setting the pin HIGH or LOW based on the received state parameter.
122+
123+
- **Empty `loop()`:** The main loop remains empty since all LED control is event-driven through Bridge function calls.
124+

‎app_lab/tecovolt-rebrand/app.yaml‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
name: Tecovolt rebrand
2+
description: Blink an LED via microcontroller using RPC calls
3+
ports: []
4+
bricks:
5+
- arduino:web_ui: {}
6+
icon: 💡
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
const POLL_INTERVAL = 1000;
2+
const MAX_LOG_LINES = 50;
3+
let lastState = null;
4+
5+
const els = {
6+
connectionDot: document.getElementById("connectionDot"),
7+
connectionText: document.getElementById("connectionText"),
8+
statusCard: document.getElementById("statusCard"),
9+
statusIcon: document.getElementById("statusIcon"),
10+
statusName: document.getElementById("statusName"),
11+
statusConfidence: document.getElementById("statusConfidence"),
12+
voltageValue: document.getElementById("voltageValue"),
13+
voltageBar: document.getElementById("voltageBar"),
14+
currentValue: document.getElementById("currentValue"),
15+
currentBar: document.getElementById("currentBar"),
16+
tempValue: document.getElementById("tempValue"),
17+
tempBar: document.getElementById("tempBar"),
18+
voltageAnomaly: document.getElementById("voltageAnomaly"),
19+
voltageAnomalyConf: document.getElementById("voltageAnomalyConf"),
20+
tempRisk: document.getElementById("tempRisk"),
21+
tempRiskConf: document.getElementById("tempRiskConf"),
22+
terminalBody: document.getElementById("terminalBody"),
23+
};
24+
25+
const STATE_ICONS = { alta: "🔴", media: "🟡", baja: "🟢" };
26+
27+
function clamp(v, min, max) { return Math.min(Math.max(v, min), max); }
28+
29+
function timeNow() {
30+
return new Date().toLocaleTimeString("es-MX", { hour12: false });
31+
}
32+
33+
function addLog(msg, cls) {
34+
const line = document.createElement("div");
35+
line.className = "log-line" + (cls ? " " + cls : "");
36+
line.innerHTML = '<span class="log-time">' + timeNow() + "</span> " + msg;
37+
els.terminalBody.appendChild(line);
38+
while (els.terminalBody.children.length > MAX_LOG_LINES)
39+
els.terminalBody.removeChild(els.terminalBody.firstChild);
40+
els.terminalBody.scrollTop = els.terminalBody.scrollHeight;
41+
}
42+
43+
function clearLog() {
44+
els.terminalBody.innerHTML = "";
45+
addLog("Log limpiado");
46+
}
47+
window.clearLog = clearLog;
48+
49+
function updateUI(data) {
50+
els.connectionDot.className = "status-dot connected";
51+
els.connectionText.textContent = "Conectado";
52+
53+
var voltage = data.voltage || 0;
54+
var current = data.current || 0;
55+
var temp = data.temperature || 0;
56+
var state = data.demand_state || "---";
57+
var conf = data.demand_confidence || 0;
58+
59+
els.voltageValue.textContent = voltage.toFixed(1);
60+
els.voltageBar.style.width = clamp(voltage / 140 * 100, 0, 100) + "%";
61+
els.currentValue.textContent = current.toFixed(2);
62+
els.currentBar.style.width = clamp(current / 30 * 100, 0, 100) + "%";
63+
els.tempValue.textContent = temp.toFixed(1);
64+
els.tempBar.style.width = clamp(temp / 80 * 100, 0, 100) + "%";
65+
66+
els.statusIcon.textContent = STATE_ICONS[state] || "⚪";
67+
els.statusName.textContent = state.toUpperCase();
68+
els.statusConfidence.textContent = (conf * 100).toFixed(0) + "% confianza";
69+
70+
els.statusCard.className = "card card-status";
71+
if (state === "baja" || state === "media" || state === "alta") {
72+
els.statusCard.classList.add(state);
73+
}
74+
75+
// Voltage anomaly model
76+
var vAnomaly = data.voltage_anomaly || "normal";
77+
var vAnomalyConf = data.voltage_anomaly_confidence || 0;
78+
els.voltageAnomaly.textContent = vAnomaly;
79+
els.voltageAnomaly.className = "model-result " + vAnomaly;
80+
els.voltageAnomalyConf.textContent = (vAnomalyConf * 100).toFixed(0) + "%";
81+
82+
// Temp risk model
83+
var tRisk = data.temp_risk || "bajo";
84+
var tRiskConf = data.temp_risk_confidence || 0;
85+
els.tempRisk.textContent = tRisk;
86+
els.tempRisk.className = "model-result " + tRisk;
87+
els.tempRiskConf.textContent = (tRiskConf * 100).toFixed(0) + "%";
88+
89+
// Log state changes
90+
if (state !== lastState && state !== "---") {
91+
var icon = STATE_ICONS[state] || "⚪";
92+
addLog(
93+
icon + " Demanda: <strong>" + state.toUpperCase() + "</strong> (" +
94+
(conf * 100).toFixed(0) + "%) " + current.toFixed(2) + "A",
95+
state
96+
);
97+
lastState = state;
98+
}
99+
100+
// Log anomalies
101+
if (vAnomaly !== "normal") {
102+
addLog("⚠ Voltaje: " + vAnomaly + " (" + (vAnomalyConf * 100).toFixed(0) + "%)", "warning");
103+
}
104+
if (tRisk === "alto") {
105+
addLog("🔥 Riesgo térmico ALTO (" + (tRiskConf * 100).toFixed(0) + "%)", "error");
106+
}
107+
108+
var relayLatched = data.relay_latched || false;
109+
var relayStatus = document.getElementById("relayStatus");
110+
if (relayStatus) {
111+
relayStatus.textContent = relayLatched ? "LATCHED" : "OFF";
112+
relayStatus.className = "relay-status" + (relayLatched ? " active" : "");
113+
}
114+
}
115+
116+
async function poll() {
117+
try {
118+
var res = await fetch("/status");
119+
if (!res.ok) throw new Error("HTTP " + res.status);
120+
var data = await res.json();
121+
updateUI(data);
122+
} catch (err) {
123+
els.connectionDot.className = "status-dot error";
124+
els.connectionText.textContent = "Sin conexión";
125+
}
126+
}
127+
async function resetRelay() {
128+
try {
129+
await fetch("/reset_relay", { method: "POST" });
130+
addLog("🟢 Relay reset manual", "baja");
131+
} catch (err) {
132+
addLog("⚠ Error reseteando relay", "error");
133+
}
134+
}
135+
window.resetRelay = resetRelay;
136+
addLog("Tecovolt Dashboard iniciado");
137+
addLog("Esperando conexión con MPU...");
138+
setInterval(poll, POLL_INTERVAL);
139+
poll();
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
const POLL_INTERVAL = 1000;
2+
const MAX_LOG_LINES = 50;
3+
let lastState = null;
4+
5+
const els = {
6+
connectionDot: document.getElementById("connectionDot"),
7+
connectionText: document.getElementById("connectionText"),
8+
statusCard: document.getElementById("statusCard"),
9+
statusIcon: document.getElementById("statusIcon"),
10+
statusName: document.getElementById("statusName"),
11+
statusConfidence: document.getElementById("statusConfidence"),
12+
voltageValue: document.getElementById("voltageValue"),
13+
voltageBar: document.getElementById("voltageBar"),
14+
currentValue: document.getElementById("currentValue"),
15+
currentBar: document.getElementById("currentBar"),
16+
tempValue: document.getElementById("tempValue"),
17+
tempBar: document.getElementById("tempBar"),
18+
voltageAnomaly: document.getElementById("voltageAnomaly"),
19+
voltageAnomalyConf:document.getElementById("voltageAnomalyConf"),
20+
tempRisk: document.getElementById("tempRisk"),
21+
tempRiskConf: document.getElementById("tempRiskConf"),
22+
terminalBody: document.getElementById("terminalBody"),
23+
};
24+
25+
const STATE_ICONS = { alta: "🔴", media: "🟡", baja: "🟢" };
26+
27+
function clamp(v, min, max) { return Math.min(Math.max(v, min), max); }
28+
29+
function timeNow() {
30+
return new Date().toLocaleTimeString("es-MX", { hour12: false });
31+
}
32+
33+
function addLog(msg, cls) {
34+
const line = document.createElement("div");
35+
line.className = "log-line" + (cls ? " " + cls : "");
36+
line.innerHTML = `<span class="log-time">${timeNow()}</span> ${msg}`;
37+
els.terminalBody.appendChild(line);
38+
while (els.terminalBody.children.length > MAX_LOG_LINES)
39+
els.terminalBody.removeChild(els.terminalBody.firstChild);
40+
els.terminalBody.scrollTop = els.terminalBody.scrollHeight;
41+
}
42+
43+
function clearLog() {
44+
els.terminalBody.innerHTML = "";
45+
addLog("Log limpiado");
46+
}
47+
window.clearLog = clearLog;
48+
49+
function updateUI(data) {
50+
els.connectionDot.className = "status-dot connected";
51+
els.connectionText.textContent = "Conectado";
52+
53+
const voltage = data.voltage ?? 0;
54+
const current = data.current ?? 0;
55+
const temp = data.temperature ?? 0;
56+
const state = data.demand_state ?? "---";
57+
const conf = data.demand_confidence ?? 0;
58+
59+
els.voltageValue.textContent = voltage.toFixed(1);
60+
els.voltageBar.style.width = clamp(voltage / 140 * 100, 0, 100) + "%";
61+
els.currentValue.textContent = current.toFixed(2);
62+
els.currentBar.style.width = clamp(current / 30 * 100, 0, 100) + "%";
63+
els.tempValue.textContent = temp.toFixed(1);
64+
els.tempBar.style.width = clamp(temp / 80 * 100, 0, 100) + "%";
65+
66+
els.statusIcon.textContent = STATE_ICONS[state] ?? "⚪";
67+
els.statusName.textContent = state.toUpperCase();
68+
els.statusConfidence.textContent = (conf * 100).toFixed(0) + "% confianza";
69+
70+
els.statusCard.className = "card card-status";
71+
if (["baja", "media", "alta"].includes(state))
72+
els.statusCard.classList.add(state);
73+
74+
if (state !== lastState && state !== "---") {
75+
addLog(
76+
`${STATE_ICONS[state] ?? "⚪"} Demanda: <strong>${state.toUpperCase()}</strong> (${(conf * 100).toFixed(0)}%) ${current.toFixed(2)}A`,
77+
state
78+
);
79+
lastState = state;
80+
}
81+
}
82+
83+
async function poll() {
84+
try {
85+
const res = await fetch("/status");
86+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
87+
const data = await res.json();
88+
updateUI

0 commit comments

Comments
 (0)