Sotavento
Telemetry, monitoring and data analysis for an experimental wind farm.
From each turbine's RS-232 serial line to a real-time dashboard and wind-forecasting models: a complete capture, ingest and data-science system built end to end — custom hardware, firmware, protocol, backend and analytics.
Every turbine speaks its own dialect over a serial cable.
The Sotavento Experimental Wind Farm (Xermade, Galicia) hosts turbines from several
manufacturers installed as a test bench: Izar-Bonus 1.3 MW, Gamesa G-47,
MADE, Vestas / Neg Micon and Ecotecnia. Each model exposes its controller over an
RS-232 line speaking a different proprietary SCADA protocol — from SOH/EOT-delimited
ASCII at 2400 baud (Bonus) to IEC 60870-5 FT 1.2 at 9600 baud (Gamesa).
The challenge: capture, centralize and analyze that scattered data without replacing the turbines' original electronics. The answer is a small device — the "Node" — that listens on each turbine's serial bus and uploads it to a shared platform where all the analysis lives.
And it's no ordinary site: Sotavento is Galicia's reference experimental wind farm — Spain's 3rd region by installed wind power, with some 3,874 MW (2025). The province of A Coruña alone contributes nearly 1,240 MW, across more than 35 wind farms and some 1,590 turbines: working here means working on a real test bench for that industry.
Today the farm is operated from a Windows 98/XP PC running the software the manufacturer shipped 20 years ago. The brief was clear: move turbine control —start, stop, errors— onto a modern, reliable system reachable from any device, and along the way optimize operation based on the energy sale price.
From the serial bus to the predictive model
A single signal chain runs through the whole system: the turbine's electrical frame becomes bytes, gets batched, travels over WiFi, is validated against a versioned contract, and ends up feeding the dashboard and the machine-learning models.
Wind turbine
SCADA controller with a proprietary RS-232 output per machine.
RS-232 · 2400–9600 bdNode
Custom PCB: MAX3232 + Raspberry Pi Pico W. Reads TXD and RXD.
UART0 / UART1Capture + batch
MicroPython: frame detection, queue and batched upload.
HTTP/JSON + tokenIngest
FastAPI validates against the contract and persists to MariaDB.
39 tables · partitionsDashboard + ML
Real time, production, OMIE prices and wind forecasting.
SSE · Chart.jsNode and backend are developed separately and only talk through a formal, versioned contract (interface/PROTOCOL.md): 8 endpoints, JSON Schemas and SemVer versioning. Neither side can break the other without bumping the protocol version.
The Node: custom electronics, from trace to 3D case
The bridge between the turbine's RS-232 bus and the IP world is a board designed, fabricated and assembled from scratch in KiCad. It is bidirectional: it captures both the PLC's replies (TXD channel) and the PC's queries (RXD channel) without interfering with the original link.
01 PCB design
A family of KiCad boards, iterated to the bidirectional version:
- Base Board V3 — bidirectional
- MCU V3 — houses the Pico W
- Interposer V6 — bidirectional
- MAX3222E test board — transceiver validation
- placaDualCNC SMD — double-sided variant
02 Serial interface
Level shifting and dual capture of the bus:
- MAX3232 / MAX3222E: RS-232 ↔ 3.3 V TTL
- UART0 (GP0/GP1) → TXD channel (PLC → PC)
- UART1 (GP4/GP5) → RXD channel (PC → PLC)
- Common ground and data-line protection
- SSD1306 OLED + status LED
03 Fabrication
Full lab prototyping:
- Double-sided CNC milling of the board
- Laser-cut stencil for the solder paste
- SMD assembly and reflow soldering
- 3D-printed enclosure (modeled in Cinema 4D)
- Gerbers + BOM ready for a production run
The transceiver must be a MAX3232 (3.3 V compatible), not a classic 5 V MAX232: the latter would damage the Pico. A common ground between the Pico and the RS-232 bus is mandatory.
MicroPython on the Pico W
Every Node runs the same firmware; its identity comes from its MAC + a token persisted to flash after the first registration. The firmware captures frames, ships them in batches and keeps itself running — including its own over-the-air update with automatic rollback.
Frame capture
- Silence detection: a gap >
GAP_MSmarks the end of a frame - Batched upload at 20 frames or every 5 s
- Queue with backpressure (drops oldest when saturated)
- Stamps each frame with monotonic ts_ms + ts_unix (if NTP)
Autonomous operation
- Auto-registration: shows up as "pending" on its first frame
- Heartbeat every 60 s with RSSI, IP and version
- Remote config applied hot (no reboot)
- OTA: atomic rename, trial period and rollback if 3 boots fail
- Remote drop_to_repl command for diagnosis without visiting the site
A versioned contract between two teams
The firmware (Pico team) and the backend (web team) are developed independently. Everything that crosses between them is written down in a contract: endpoints, headers, authentication and error policies, with SemVer versioning and JSON Schema validation.
| Endpoint | Use | Auth |
|---|---|---|
| POST /api/gateways/registro | Initial registration → returns token | — |
| POST /api/gateways/ping | Heartbeat + pending remote commands | Bearer |
| POST /api/ingest | Batched upload of captured frames | Bearer |
| POST /api/gateways/logs | Node log flush (debug→error) | Bearer |
| GET /api/firmware/latest | Manifest of the active OTA version | Bearer |
| GET /api/gateways/config | Runtime parameters (applied hot) | Bearer |
Versioning
X-Sotavento-Protocol on every request. If the MAJOR mismatches → 426 Upgrade Required and the Node forces an OTA.
Guarantees
Idempotent ingest by ts_unix, order preserved within a batch, and at-most-once command delivery.
Security
Revocable 64-hex gateway token; on 401/403 the Node deletes its token and re-registers itself.
The platform that centralizes the farm
A FastAPI backend on a Raspberry Pi at the site itself receives the frames, parses them and serves an operational web app. All with reproducible deployment, two environments (staging / production) and secure remote access.
Real time
- SSE dashboard: power, wind, RPM, voltage per machine
- Power sparklines and the day's OMIE prices
- Start / stop / query commands with full traceability
Data & parsing
- MariaDB 11, 39 tables, monthly partitions of frames
- Bonus parser (ASCII) over the raw frames
- Monthly archival to ZIP with Time-Machine-style retention
Operations
- rsync + systemd deploy; Tailscale / Cloudflare Tunnel
- Installable PWA and ES / GL / EN i18n
- Encrypted backups synced to NAS
What the course taught, applied to the farm
This is where the course becomes tangible. Each week of the syllabus maps directly onto a real feature of the system — the same problem you solve with a toy dataset, solved instead with Sotavento's real frames and production data.
| Course week | Technique | Where it lives in Sotavento |
|---|---|---|
| Fitting | Curve fitting / regression | Power curve (Cp) fitted from real production vs. wind; per-turbine degradation detection over time. |
| Machine Learning | Supervised models, ensemble, stacking | Wind forecasting with 4 providers + 5 models retrained nightly (TensorFlow / LSTM), bias correction, stacking and a weighted ensemble ∝ 1/MAE. |
| Probability | Uncertainty, quantiles | Quantile forecasting, gust estimation and a 0–3 h nowcast against the farm's real measurement. |
| Density Estimation | Error distribution | Error histogram (forecast − real) per provider: 20 bins of 2 km/h, with mean, σ and RMSE. Reveals each model's bias and spread. |
| Transforms | Domain changes, feature engineering | Vectorial wind u/v → magnitude/direction (Windy), adaptive time buckets (averages, not subsampling) and hourly/daily aggregation. |
| Presentation | Communicating results | This document, the operational dashboard and the PDF/Excel production & maintenance reports. |
Evaluation Which model is most accurate?
The system doesn't just predict: it evaluates itself continuously against the real wind measured by the turbines.
- MAE and bias per provider, always shown in the anemometer header
- Rolling 7-day MAE to detect drift (is someone getting worse by season?)
- Adaptive scoreboard: % of wins by intensity (calm <5 · moderate 5–10 · strong >10 m/s)
- Ensemble with weights ∝ 1/MAE that usually beats any single provider
Pipeline From frame to dataset
All the analysis rests on a reproducible data flow that turns heterogeneous sources into clean, queryable tables.
🔧 Predictive maintenance + work orders
With the full work-order history since 2001 —over 13,000 operations and their SCADA code catalogs— the system anticipates failures, prioritizes them and proposes fixes drawing on past cases.
- Work-order manager that replaces the paper form (entry, signature, PDF/Excel reports)
- Per-turbine health and degradation, flagged when efficiency drops
- Per-model / manufacturer comparison to spot systemic problems
💶 Operating by price
Production is crossed with the electricity market (OMIE) to put every kWh in euros and make operational calls on economic grounds.
- Opportunity cost: how much revenue is lost by stopping a machine
- Best window for maintenance — the slots where stopping costs least
- Estimated revenue and average price, at fixed price or at market
The data sources
| Source | Content |
|---|---|
Gamesa_registro | Turbine records: active/reactive power, voltage, wind speed, state and alarms (sot_am); hourly production (sot_ah); stops and losses (sot_epp); billed energy and availability (sot_sh). |
OMIE | Hourly electricity-market price — crossed with production to estimate revenue and opportunity cost. |
Efivento | Maintenance history: 13,495 operations (2000→2026) with SCADA code catalogs. |
Weather | Forecasts from Open-Meteo, AEMET, MeteoGalicia and Windy — the raw material for the models. |
A datum is only worth it if it arrives, gets cleaned and is understood.
Sotavento began as an electronics problem — reading a serial cable — and ended up being, above all, a data problem: capturing it without loss, storing it at scale, aggregating it with judgment and extracting a useful forecast from it.
The Data Science course supplied exactly the last piece: going from "I have the frames saved" to fitting curves, training models, measuring their error and picking the one that's right. The firmware and boards bring the data in; the analysis makes sense of it.