halmet-lower-helm
Fuel tank levels, engine RPM, bilge alarms and galley temperature — all published to Signal K and NMEA 2000 from a single HALMET board at the lower helm.
Project links
What it does
This is the most capable of the HALMET devices on the boat. It sits at the lower helm and monitors the engine bay. Two fuel tanks (port and starboard) are read via resistive senders on the ADS1115 ADC. Engine RPM is read from a tacho sender on digital input D1 — the signal is a frequency (Hz) that the SensESP framework converts to RPM. Two bilge alarms on digital inputs D3 and D4 publish boolean alarm states. A DHT11 temperature and humidity sensor in the galley undersink cupboard monitors that space. All data goes to Signal K, and engine RPM goes to NMEA 2000 as well.
Hardware
| Component | Purpose | Connection |
|---|---|---|
| HALMET board (ESP32) | Main controller, WiFi, SensESP, NMEA 2000 CAN | — |
| ADS1115 16-bit ADC | Reads resistive fuel tank senders | I2C (built into HALMET) |
| Fuel sender — Port | Resistive float sender | ADS1115 channel A2 |
| Fuel sender — Starboard | Resistive float sender | ADS1115 channel A3 |
| Tacho sender (engine) | Frequency pulse from engine alternator W terminal or dedicated sender | Digital input D1 |
| Bilge alarm — front bilge | Float switch, active when bilge full | Digital input D3 |
| Bilge alarm — rear bilge | Float switch, active when bilge full | Digital input D4 |
| DHT11 | Galley undersink temperature & humidity | GPIO 5, 2m Cat5e cable |
| SSD1306 OLED (optional) | Local status display showing IP, RPM, alarm states | I2C |
Code structure
| File | Purpose |
|---|---|
src/main.cpp | All application logic. Substantially more involved than the tank monitor — fuel tanks, tacho, bilge alarms, DHT11, NMEA 2000 output, and optional OLED display all wired up here. |
src/halmet_analog.cpp/.h | ConnectTankSender() — same as tank monitor |
src/halmet_digital.cpp/.h | ConnectAlarmSender() for bilge alarms and ConnectTachoSender() for engine RPM. Both handle debounce, rate limiting, and SK output. |
src/halmet_display.cpp/.h | OLED display helpers. The display shows IP address, RPM and alarm states when a display is connected. |
src/n2k_senders.h | NMEA 2000 output — N2kEngineParameterRapidSender sends engine RPM onto the NMEA 2000 bus. |
src/halmet_const.h | HALMET pin constants |
src/rate_limiter.h | Prevents flooding the Signal K bus with updates — rate-limits outputs to a configurable interval |
src/expiring_value.h | Marks a value as stale if not updated within a timeout — used to detect lost sensor signals |
Signal K paths published
| Signal K path | Unit | Description |
|---|---|---|
tanks.fuel.port.currentLevel | ratio (0–1) | Port fuel tank level |
tanks.fuel.starboard.currentLevel | ratio (0–1) | Starboard fuel tank level |
alarm.bilge.front | boolean | Front bilge alarm state (true = alarm triggered) |
alarm.bilge.rear | boolean | Rear bilge alarm state |
propulsion.main.revolutions | Hz (rev/s) | Engine RPM as revolutions per second — multiply by 60 for RPM |
environment.inside.galley.undersink.temperature | K | Galley undersink temperature (Kelvin — subtract 273.15 for °C) |
environment.inside.galley.undersink.relativeHumidity | ratio (0–1) | Galley undersink humidity |
sensors.a2.voltage | V | Raw voltage on ADS1115 channel A1 (spare diagnostic input) |
Signal K standard paths use SI units throughout: ratios (not percentages), Kelvin (not Celsius), Hz (not RPM). Consumer code — the dashboard, CYD display — handles the display conversion.
NMEA 2000 output
Engine RPM is published to NMEA 2000 as a PGN 127488 Engine Parameters (Rapid Update) message. This means a chartplotter or multifunction display connected to the NMEA 2000 bus will show engine RPM alongside its other engine data. The HALMET CAN bus transceiver handles the bus-level signalling; the tNMEA2000_esp32 library handles the protocol.
NMEA 2000 output for tank levels is available but disabled by default in this build (#define ENABLE_NMEA2000_OUTPUT is commented out). When enabled, fuel tanks are sent as PGN 127505 Fluid Level messages.
Key things to customise
| What to change | Where | Notes |
|---|---|---|
| WiFi / Signal K server | set_hostname(), set_wifi(), set_sk_server() in setup() |
Credentials are hard-coded or use SensESP AP mode for first boot |
| OTA password and upload IP | enable_ota("password") in code; upload_port in platformio.ini |
Set the IP to your device's address. Default OTA port is 3232. |
| Fuel tank sender channels | ConnectTankSender(ads1115, channel, ...) |
Channels 2 and 3 (A2, A3) are used here. A0 and A1 are available if you have more tanks. |
| Tacho pulses per revolution | Via the SensESP web UI under the tacho input config | Default is 100 pulses/rev. Set this to match your engine's tacho sender specification. |
| DHT sensor pin or type | #define GALLEY_DHT_PIN 5 and GALLEY_DHT_TYPE DHT11 |
Change to DHT22 for higher accuracy. The 60-second read interval can be adjusted in onRepeat(60000, ...). |
| Enable NMEA 2000 tanks | Uncomment #define ENABLE_NMEA2000_OUTPUT |
Also check N2k instance numbers match your chartplotter configuration |
How the tacho input works
Most marine diesel engines have a W terminal on the alternator that outputs an AC signal whose frequency is proportional to engine speed. Some engines use a dedicated magnetic pickup or Hall effect sender. Either way, the signal is conditioned to a 3.3V square wave and fed to a digital input pin on the HALMET board.
The ConnectTachoSender() function creates a DigitalInputFrequencyCounter and applies a configurable multiplier (pulses per revolution) to convert the frequency to rev/s. The standard Signal K path for engine RPM is propulsion.main.revolutions in Hz (rev/s). To display as RPM, multiply by 60.
RPM = frequency (Hz) × 60 ÷ pulses_per_revolution