Boat project

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.

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

ComponentPurposeConnection
HALMET board (ESP32)Main controller, WiFi, SensESP, NMEA 2000 CAN
ADS1115 16-bit ADCReads resistive fuel tank sendersI2C (built into HALMET)
Fuel sender — PortResistive float senderADS1115 channel A2
Fuel sender — StarboardResistive float senderADS1115 channel A3
Tacho sender (engine)Frequency pulse from engine alternator W terminal or dedicated senderDigital input D1
Bilge alarm — front bilgeFloat switch, active when bilge fullDigital input D3
Bilge alarm — rear bilgeFloat switch, active when bilge fullDigital input D4
DHT11Galley undersink temperature & humidityGPIO 5, 2m Cat5e cable
SSD1306 OLED (optional)Local status display showing IP, RPM, alarm statesI2C

Code structure

FilePurpose
src/main.cppAll 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/.hConnectTankSender() — same as tank monitor
src/halmet_digital.cpp/.hConnectAlarmSender() for bilge alarms and ConnectTachoSender() for engine RPM. Both handle debounce, rate limiting, and SK output.
src/halmet_display.cpp/.hOLED display helpers. The display shows IP address, RPM and alarm states when a display is connected.
src/n2k_senders.hNMEA 2000 output — N2kEngineParameterRapidSender sends engine RPM onto the NMEA 2000 bus.
src/halmet_const.hHALMET pin constants
src/rate_limiter.hPrevents flooding the Signal K bus with updates — rate-limits outputs to a configurable interval
src/expiring_value.hMarks a value as stale if not updated within a timeout — used to detect lost sensor signals

Signal K paths published

Signal K pathUnitDescription
tanks.fuel.port.currentLevelratio (0–1)Port fuel tank level
tanks.fuel.starboard.currentLevelratio (0–1)Starboard fuel tank level
alarm.bilge.frontbooleanFront bilge alarm state (true = alarm triggered)
alarm.bilge.rearbooleanRear bilge alarm state
propulsion.main.revolutionsHz (rev/s)Engine RPM as revolutions per second — multiply by 60 for RPM
environment.inside.galley.undersink.temperatureKGalley undersink temperature (Kelvin — subtract 273.15 for °C)
environment.inside.galley.undersink.relativeHumidityratio (0–1)Galley undersink humidity
sensors.a2.voltageVRaw 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 changeWhereNotes
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