Firmware for the Hat Labs HALMET board that reads a legacy Raymarine ST60+ wind transducer — the vane and cups at the top of the mast, whose display head died but whose sensor is still healthy — and republishes apparent wind onto the boat's NMEA 2000 backbone.
The story: the ST60+ display head failed; the wind vane 15 m up the mast was fine. Rather than climb the mast or replace the instrument, HALMET taps the old sensor's raw sin/cos and cup-pulse wires directly and puts wind back on the bus.
📖 Project page: https://den200.github.io/halmet-wind-vane/ · 🔌 Wiring & power guide
- V1 (primary): transmits PGN 130306 (Wind Data) every 100 ms (10 Hz) with
WindReference = Apparent. This is the deliverable — the boat (Orca Core 2 + N2K displays) consume wind over the wired backbone. - V2 (secondary): publishes the same calibrated values to SignalK over WiFi
(
environment.wind.angleApparent/speedApparent), so any SignalK plugin or web app (KIP, Freeboard, instrument panels, loggers) can display them once installed.
| Pipeline | Chain |
|---|---|
| Angle | A1/A2 (sin/cos) → ADS1115 16-bit → per-channel centring → moving average of the sin/cos vector (5) → atan2(sin,cos) → raw-voltage validity gate → AWA |
| Speed | D1 pulse → DigitalInputCounter (RISING, 500 ms) → Frequency → × K (m/s per Hz) → AWS |
| Output | latched values → NMEA 2000 PGN 130306 @ 10 Hz and SignalK environment.wind.* |
The vane encodes its angle as two ratiometric voltages (sine and cosine);
recovering the angle is atan2(sin, cos). Since atan2 is scale-invariant,
calibration only needs to centre each channel — see src/sin_cos_angle_transform.h.
Two details there are deliberate. Smoothing averages the sin/cos vector, not the emitted angle: averaging the angle breaks at the ±π wrap, where samples alternate between ≈ +179° and ≈ −179° and mean to 0°, so dead astern would read as dead ahead. And the no-data check watches the raw terminal voltage (Blue/Green must be inside 2–6 V), not the computed magnitude: an unpowered transducer puts both channels at 0 V, which centres to a large, perfectly steady vector and yields a convincing, unchanging −135°.
| Wire | Signal | To | Mode |
|---|---|---|---|
| Red | +8.0 V | external buck OUT+ | supply (set buck to 8.0 V first!) |
| Screen | 0 V / GND | shared sensor ground | — |
| Blue | sine | A1 | passive, CCS off |
| Green | cosine | A2 | passive, CCS off |
| Yellow | speed pulse | D1 / GPIO23 | passive, RISING |
Most of the build is reused — the wind vane, its cable, and the boat's NMEA 2000 network are already there. You really only buy two things:
| Part | For | Link |
|---|---|---|
| Hat Labs HALMET | the bridge board — reads the vane/cups, speaks N2K, hosts the web UI | shop.hatlabs.fi |
| Adjustable buck converter (with voltage display) | steps boat 12 V down to the vane's 8.0 V — set it before wiring | amazon.de |
| Raymarine ST60+ wind vane | the existing masthead sensor — reused, not bought | — |
| Inline ~1 A fuse + wire | fuse the buck's 12 V input; three signal taps off the existing mast cable | — |
Plus the open-source firmware in this repo.
All tunables persist to flash and are editable at http://halmet-wind.local/:
- Angle offset (
offset_rad) — align vane zero to the bow. - Angle direction (
sin_sign) —+1normal,−1if wind reads backward. - Per-channel centring — set each sin/cos midpoint (≈ Vmid).
- Smoothing (
samples) — sin/cos samples averaged before the angle is computed; 5 ≈ a 2.5 s window at the ~2 Hz sample rate. - Speed multiplier K —
0.5144m/s·Hz⁻¹ for egg-cup ST60+ (~1 kn/Hz), ~0.36square-cup.
With the mast up, derive the sin/cos centres by ellipse-fit on logged free-rotation
data (tools/ellipse_fit.py); trim K against GPS in calm air.
Function 130 (Atmospheric), Class 85 (External Environment), Manufacturer 2046 (unregistered), preferred source address 35.
pio run -e halmet # build
pio run -e halmet -t upload # flash over USB
pio device monitor -b 115200 # serial
tools/run_host_tests.sh # angle-transform regression tests, on the laptopsrc/sin_cos_angle_transform.h is pure math over a thin SensESP base, so it is
compiled against minimal stubs in test/host/ and exercised on a
laptop — full circle accuracy, behaviour through the ±π wrap, NaN recovery,
sign/offset handling and config clamping. No board required.
SensESP v3 on PlatformIO (pioarduino platform), NMEA 2000 via the ESP32 TWAI
driver, ADS1115 via Adafruit ADS1X15. Board target esp32dev.
- On the wire:
candump can0,09FD0200:1FFFFF00on the RPi/MacArthur HAT → a PGN 130306 frame from source 35 every 100 ms. - SignalK / Orca: apparent wind angle & speed populate and read correctly.
Licensed under the Apache License 2.0 — see LICENSE. This is the same license as the Hat Labs HALMET example firmware and the SensESP framework it builds on, keeping everything license-compatible.
Forked from the structure of the Hat Labs HALMET example firmware; built on SensESP. Not affiliated with Raymarine or Orca. Built with Claude Code.