Skip to content

KLINI 400 Posthole Tiltmeter

This documentation covers the KLINI 400 Posthole Tiltmeter, a KLINI 400 (1 µrad-and-finer resolution class) two-axis instrument built for posthole/borehole deployment. The case currently shipping is 2" OD, approximately 34" long; additional case variants are planned for the future, so treat "the case" throughout this manual as describing the current one unless a later revision says otherwise.

About This Manual

Firmware Versions Covered

This manual covers instrument firmware 1.0.0 — the first version installed on shipped units. The KLINI 400 Posthole Tiltmeter runs its own firmware, developed and versioned independently of the KLINI Surface Inclinometer's 1.1/1.2/1.3 line — the two share a family name and some sensor hardware, but not a codebase, a version number, or an enclosure. Nothing in the Surface Inclinometer manual should be assumed to apply here unless this manual says so.

Finding Your Firmware Version

  • The power-up banner prints a line such as # Firmware: 1.0.0 (built Sep 29 2026 15:47:33).
  • The VERSION serial command reports the same line on demand.

Don't confuse this with the Version: line in SHOW's EEPROM dump — that number (e.g. 20) is the internal settings-storage layout revision, not the firmware version. It changes far more often than FW_VERSION does and isn't meaningful on its own; use VERSION for the real firmware identity.

Overview

The KLINI 400 Posthole Tiltmeter measures tilt on two perpendicular axes (X and Y) using two independent sensing technologies per axis: an electrolytic (fluid-based) tilt sensor and a MEMS inclinometer, both read through the same command interface and both independently calibrated to nanoradians. Each reading is reported alongside the on-board temperature. The instrument also carries a 3-axis magnetometer (for a gross orientation/heading check after installation), an environmental sensor (temperature, humidity, pressure), and two built-in leveling motors that can drive the instrument back toward level either on command or fully automatically.

Everything — configuration, data retrieval, leveling, and calibration — goes through a single serial command interface (§"Operation" below); there is no local display, SD card, or Wi-Fi interface on this instrument (unlike the KLINI Surface Inclinometer — do not assume those apply here).

The current case is a cylindrical, 6061 aluminum tube, 2" OD and approximately 34" long, designed for shallow deployments (100m or less). Weight is TBD.

Specifications

Parameter Value Source / Notes
Tilt Measurement
Axes 2 (X and Y) -
Sensing technologies Electrolytic tilt sensor + MEMS inclinometer, independent, both per axis -
Tilt output resolution Sub-microradian (factory-calibrated slope on shipped units: 0.61-0.76 nrad/count) Measured across 3 fielded units' factory calibration reports; see "Calibration" below
Tilt ADC 24-bit (±8,388,608 counts full scale) Kept in its highest-resolution power mode at all times by design
Other Sensors
Magnetometer 3-axis, µT output, used for a gross orientation/heading check (`ORIENT`) Not a precision heading reference - see "Checking Orientation" below
Environmental Temperature, humidity, pressure -
Board temperature Reported with every electrolytic reading -
Electrical / Communication
Serial interface RS232 (9600 baud) or RS485 (1200 baud), both 8N1 - set at the factory, not user-selectable -
Motor current limit (default) 200 mA Adjustable, `SCURLIM`
Measured operating current (bench) ~54 mA idle, ~138-145 mA during a leveling motor pulse, at 12V supply -
Connector Blue Trail Cobalt 4-pin bulkhead Posthole case only - the deep-hole variant uses a different connector
Mechanical
Case (current) Cylindrical, 6061 aluminum, 2" OD × ~34" long Additional case variants are planned; this describes the current shipping case only
Depth rating Shallow deployments, 100m or less -
Weight TBD -

Installation

Electrical Connection

The communication mode (RS232 or RS485) is set at the factory by an internal strap and is not customer-adjustable; confirm which mode your unit is set to with the factory or by checking SHOW's Comm mode line after power-up.

Mode Baud Format
RS232 9600 8 data bits, no parity, 1 stop bit (8N1)
RS485 1200 8 data bits, no parity, 1 stop bit (8N1)

Any standard serial terminal works (PuTTY, screen, CoolTerm, etc.). Both bare LF and CRLF line endings are accepted when sending commands.

Connector (Posthole Case)

The posthole case uses a Blue Trail Cobalt 4-pin bulkhead connector. The deep-hole case variant uses a different connector — this pinout applies to the posthole case only.

Pin Wire Function
A Black GND
B Red VDC
C White Instrument RX (from logger)
D Yellow Instrument TX (to logger)

"Instrument RX"/"Instrument TX" are named from the instrument's point of view — wire the logger's TX to pin C and its RX to pin D.

Powering Up

At power-up, the instrument prints a startup banner, for example:

# Tiltmeter starting up...
# Firmware: 1.0.0 (built Sep 29 2026 15:47:33)
# Comm mode: RS232
# Register Commands... DONE
# Reading EEPROM settings... DONE
# Serial number: SN001
# Packet format: WIDE_CSV
# Columns: t_ms,type,mems_x,mems_y,elec_status,elec_ch0,elec_ch1,pcb_temp,env_temp,env_humidity,env_pressure,mag_x,mag_y,mag_z,mems_x_nrad,mems_y_nrad,elec_x_nrad,elec_y_nrad

Lines starting with # are informational text, never data to parse. For about the first 10 seconds after this banner, the instrument holds off automatic telemetry so you have a clear window to send setup commands (like checking SHOW) before data starts flowing. After that window, telemetry begins per whatever intervals are configured, and commands can still be sent at any time — replies interleave with the telemetry stream.

Checking a Unit Before Deployment

Before lowering the instrument into the hole, confirm it's the unit you think it is and that its calibration is loaded:

SHOW

Check the reported Serial number against the unit's physical marking, and confirm Elec angle cal points (X,Y) reads 2,2 or higher (not 0,0 — an uncalibrated axis reports elec_x_nrad/elec_y_nrad as a flat 0, which looks deceptively like "perfectly level" rather than "not calibrated"). See "Calibration" below for how to verify the calibration itself, not just that one is present.

Checking Orientation After Installation

Once the instrument is set in the hole, ORIENT gives a one-shot "gross orientation" report — not part of the regular telemetry, run it whenever you want a check:

ORIENT
# ==================== ORIENTATION ====================
# MEMS tilt X: 1234 bits ( 2.21 deg )
# MEMS tilt Y: -567 bits ( -1.01 deg )
# Magnetometer raw (uT): X=48.32  Y=3.11  Z=-12.04
# Magnetometer, tool axes (uT):  X=-3.11  Y=-12.04  Z(up)=-48.32
# Tilt from vertical: 2.44 deg
# Estimated tilt-corrected magnetic azimuth of tool +Y: 121.37 deg
# NOTE: sign convention field-verified to ~3-25 deg - see sensors.cpp
# ======================================================
OK

Finding +X on the case: a small dimple on the instrument's top cap marks the +X direction — use it to know which way the instrument is facing before it goes in the hole, and to set a known rotational reference as you lower and backfill/grout it.

Keep three things in mind, all of which depend on how the instrument is physically oriented in the hole:

  • Tool axes are right-handed with Z up (not down-hole), and the heading reported is the azimuth of the tool's +Y axis, not +X — think of it as a map grid where Y points toward the reference direction. It's magnetic, not true, north — apply your site's declination if you need true north.
  • The sign convention was checked against a real 4-point compass calibration (+Y held pointed N, E, S, W in turn); residual error was roughly 3-25 degrees after that check — small compared to the gross ~180-degree error the same check caught and fixed, but real. Treat ORIENT as a gross orientation check, not a precision heading, until that residual is characterized further — the original check was run indoors, where nearby steel structure is expected to add magnetic distortion beyond the sign convention itself.
  • The MEMS tilt shown by ORIENT uses a fixed, generic conversion, not the per-unit factory calibration table that the data stream's mems_x_nrad/mems_y_nrad columns and the RTILT command use. It's a quick gross check, not a precision reading — for the calibrated value, read the telemetry stream or RTILT instead.

The dimple tells you where +X is on the instrument; it doesn't by itself tell you what compass direction +X ends up facing once the instrument is down the hole — that depends on how it was lowered and oriented going in. Record the installation orientation (e.g. with a known alignment relative to the dimple as you lower it, and/or an ORIENT reading immediately after setting the instrument, before backfilling or grouting) if you need to relate the X/Y data to a real-world direction later.

Operation

Serial Command Basics

  • Commands are case-sensitive and must be typed in UPPERCASE exactly as documented.
  • Arguments are separated by single spaces.
  • A successful command replies OK. An invalid one (wrong argument count, an out-of-range value, or a prerequisite sensor that isn't available) replies !.
  • A command the instrument doesn't recognize gets no reply at all — if you send something and nothing comes back, suspect a typo or wrong firmware version before suspecting a wiring problem.
  • Telemetry rows are not prefixed with #; every other line the instrument sends is. That's the cheapest way to separate the two programmatically.

Telemetry Format

Every reading is one CSV row:

t_ms,type,mems_x,mems_y,elec_status,elec_ch0,elec_ch1,pcb_temp,env_temp,env_humidity,env_pressure,mag_x,mag_y,mag_z,mems_x_nrad,mems_y_nrad,elec_x_nrad,elec_y_nrad
Column Meaning Units
t_ms Milliseconds since the instrument booted (not wall-clock time — there is no real-time clock) ms
type Record type — see table below -
mems_x, mems_y Raw MEMS tilt sensor counts counts
elec_status Electrolytic ADC status word -
elec_ch0, elec_ch1 Raw electrolytic tilt counts (X, Y) counts
pcb_temp Board temperature °C
env_temp, env_humidity, env_pressure Environmental sensor readings °C, %RH, hPa
mag_x, mag_y, mag_z Magnetometer readings µT
mems_x_nrad, mems_y_nrad Calibrated MEMS tilt nanoradians
elec_x_nrad, elec_y_nrad Calibrated electrolytic tilt nanoradians

Only the columns relevant to a given record's type are filled in; the rest are blank. A calibrated (_nrad) column reads a flat 0 until its calibration table is loaded — that's expected on an uncalibrated axis, not a fault; see "Checking a Unit Before Deployment" above.

type Populated columns Sent
MEMS mems_x, mems_y, mems_x_nrad, mems_y_nrad On RMEMS, or automatically on the configured MEMS interval (off by default)
ELEC elec_status, elec_ch0, elec_ch1, elec_x_nrad, elec_y_nrad On RELEC, or automatically every 1 second by default
TEMP pcb_temp Alongside every automatic ELEC record
ENV env_temp, env_humidity, env_pressure On RENV, or automatically every 60 seconds by default
MAG mag_x, mag_y, mag_z On RMAG, or automatically on the configured magnetometer interval (off by default)

MEMS and magnetometer automatic streaming ship disabled. Turn them on with SMEMSINT/SMAGINT if you want them in the regular data stream, or just use RMEMS/RMAG/ORIENT to read those two sensors on demand without changing anything.

Example Data Row

A single ELEC record, with the TEMP record always sent alongside it:

25137,ELEC,,,1283,-8388608,-748487,,,,,,,,,,0,-458768
25138,TEMP,,,,,,25.41,,,,,,,,,,

Reading this: at t_ms=25137, the electrolytic sensor's status word was 1283, X read -8388608 raw counts (the ADC's negative full-scale value — this particular bench unit was sitting pinned at an extreme tilt when this row was captured), Y read -748487 raw counts, X's calibrated reading was 0 (no calibration table loaded for X at that moment), and Y's calibrated reading was -458768 nrad. The following row, 1ms later, reports the board at 25.41°C. All other columns are blank because neither record type populates them.

Self-Leveling

The instrument can drive its own leveling motors to correct tilt, either on command or fully automatically.

  • LEVEL (optionally LEVEL X or LEVEL Y) runs the self-leveling motors on one or both axes right now, and reports DONE or TIMEOUT per axis when finished. LEVELSTOP stops it immediately (takes effect within about 100ms, even mid-pulse).
  • Full-auto leveling (SLEVELAUTO 1) makes the instrument level itself whenever tilt drifts outside a configured bound, with no command needed — it's off by default. The trigger bound is set deliberately wide (83% of the sensor's full counting range on shipped units) — it's meant as a rare "something has moved a lot, go fix it" safety net, not a routine fine-leveling trigger. After a successful auto-correction it waits at least a minute before checking again; after one that times out without converging, it waits much longer (30 minutes by default) rather than repeatedly cycling the motors on a problem a quick retry won't fix.
  • A leveling pulse is current-limited (SCURLIM, 200mA by default) — if a pulse draws more current than that, it stops immediately rather than continuing to drive against whatever's causing the extra load. Repeated current-limit trips are worth investigating mechanically before assuming it's a settings problem (see "Troubleshooting").
  • Leveling bounds and timing are normally set once at the factory and shouldn't need routine adjustment. The full list of leveling settings is in the command reference below if you do need to check or change one.

Full Command Reference

Leveling

Command Args Description
LEVEL [X\|Y] Runs the self-leveling motors. No argument levels X then Y; X or Y runs just that axis. Fails (!) if already leveling (send LEVELSTOP first), or if a required sensor/motor driver isn't available.
LEVELSTOP - Immediately stops any in-progress leveling and brakes the motors.
SLEVELAUTO <0\|1> Enables/disables fully-automatic leveling.
SLEVELUB / SLEVELLB <X\|Y> <value> Upper/lower auto-trigger bound (raw counts) for an axis.
SLEVELDB <X\|Y> <value> Deadband (± counts considered "level") for an axis.
SLEVELTARGET <X\|Y> <value> Target band: landing within this on one reading is accepted immediately. Must be less than the deadband.
SLEVELCYCLES <X\|Y> <1-255> How many times the reading must cross zero (inside the deadband, outside the target band) before an axis is accepted, instead of waiting for a lucky sample inside the target band.
SLEVELDIR <X\|Y> <0\|1> Reverses the leveling motor's pulse direction for an axis (set at the factory).
SLEVELSPD <X\|Y> <0-255> Leveling motor pulse strength (PWM), fixed for every pulse.
SLEVELMINPULSE / SLEVELMAXPULSE <X\|Y> <1-5000> Floor/ceiling (ms) for the adaptive pulse duration — it shrinks or grows automatically pulse to pulse rather than using one fixed strength.
SLEVELSETTLE <X\|Y> <0-65535> How long (ms) to wait after a full-strength pulse before trusting the next reading.
SLEVELMAXMS <X\|Y> <0-4294967295> Safety timeout (ms) for one axis's leveling attempt.
SLEVELCOOLDOWN <0-65535> Minimum time (ms) between full-auto attempts, after a successful one.
SLEVELFAILCOOLDOWN <0-4294967295> Minimum time (ms) before full-auto retries after a TIMEOUT.
SLEVELCHKMS <0-65535> How often (ms) full-auto checks whether leveling is needed.

Reading Sensors On Demand

Command Description
RMEMS Sends one MEMS telemetry record right now.
RELEC Sends one ELEC telemetry record right now.
RENV Sends one ENV telemetry record right now.
RMAG Sends one MAG telemetry record right now.
ORIENT One-shot orientation report — see "Checking Orientation" above.
RTILT Calibrated tilt (nrad) for MEMS X/Y and electrolytic X/Y, printed as text, not a telemetry row.
RCUR Raw motor current-sense count and calibrated current (mA).

Status and Diagnostics

Command Description
SHOW The full status report: serial number, detected hardware, current settings, and what's actually stored on flash (with a CRC check).
HELP Condensed on-instrument reference for every command, for use without this manual handy.
VERSION Firmware version and build date/time — see "Finding Your Firmware Version" above.
SETSN <value> — sets the instrument's serial number (metadata only, up to 19 printable characters, no spaces). Normally set once, at commissioning.
I2CSCAN Scans the internal I2C bus and lists every address that responds — useful if a sensor is reported "NOT DETECTED" at boot.

Sensor Configuration

Command Args Description
SMEMSEN / SELECEN <0\|1> Enables/disables the MEMS or electrolytic tilt sensor. Disabling is immediate; re-enabling needs a RESET to actually resume reading (see note below).
SELECAVG / SMEMSAVG / SMAGAVG <1-255> Samples averaged per reading, per sensor.
SELECINT / SMEMSINT / SMAGINT / SSOHINT <0-65535> Automatic telemetry interval (ms) for electrolytic/MEMS/magnetometer/environmental data. 0 disables automatic sending for that sensor.
SPKTFMT <format id> Telemetry packet format. Only 0 (WIDE_CSV, the format this manual documents) exists today.

Note: disabling a sensor takes effect immediately, but re-enabling it only updates the stored setting — the sensor doesn't actually resume reading until the next RESET or power cycle.

Firmware Update and Reset

Command Description
BOOTLOAD Restarts into the serial bootloader for a firmware update. Refused while leveling — send LEVELSTOP first. Resumes normal operation on its own if no update follows within about 2 seconds.
RESET Restarts the instrument (like a power cycle). Replies OK first, then resets. Refused while leveling.
FACTORYRESET CONFIRM Erases all settings and calibration data and restores factory defaults — except the serial number, which is preserved. Irreversible short of reloading your calibration data; the literal second argument CONFIRM is required as a typo safeguard. Refused while leveling.

Calibration

How Readings Are Calculated

Each axis's raw sensor counts are converted to a calibrated angle in nanoradians using a table of (raw count, angle) points, loaded at the factory. A table needs at least two points to represent a real slope; with none loaded, that axis's _nrad output is a flat 0 (see "Checking a Unit Before Deployment" above). The electrolytic sensor additionally supports an optional temperature-compensation model (a table correcting the sensor's zero point for temperature, plus a single sensitivity-vs-temperature coefficient) — the MEMS sensor has no temperature compensation modeled at all.

What's Loaded on Your Unit

Each instrument ships with a single-slope, zero-offset electrolytic calibration per axis — the slope from that unit's own factory calibration report at its reference temperature (~20°C), with no temperature compensation currently loaded. This is a deliberate choice to keep the on-instrument conversion simple; it is not the only thing the factory calibration process measures.

Your factory calibration report, if you have one, characterizes real temperature-dependent drift beyond what's loaded into the instrument today — in factory calibration testing, the zero-point (intercept) alone was seen to drift by as much as several hundred thousand nanoradians across a -20°C to 40°C sweep, axis- and unit-dependent. If your deployment sees meaningful temperature swings and that level of residual matters for your application, two options:

  • Ask the factory to load the full temperature-compensated model (the SNULLPT/SSCALETC commands in the reference above exist for this).
  • Every telemetry row already reports raw counts and board temperature together, so the full correction from your calibration report can always be applied afterward in your own post-processing, even if the instrument itself isn't doing it in real time.

Viewing the Calibration

SHOW

reports how many calibration points are loaded per axis/sensor (Elec angle cal points (X,Y), MEMS angle cal points (X,Y), Elec null cal points (X,Y)) and the scale temperature coefficient, if any. To see the actual loaded points:

RANGTBL ELEC X
RANGTBL ELEC Y

(substitute MEMS for the MEMS table, or RNULLTBL <X|Y> for the temperature-compensation table).

Changing the Calibration

Loading a new table: SANGPT loads one point at a time (in increasing raw-count order), then SANGCNT sets how many of the loaded points are valid. See the command reference above for exact syntax. FACTORYRESET clears all calibration data along with every other setting (serial number excepted) — reload calibration afterward before trusting the data.

Data Interpretation

Raw Counts vs. Calibrated Tilt

Use the _nrad columns for anything quantitative — they're the factory-calibrated values. The raw mems_x/mems_y/elec_ch0/elec_ch1 columns are there for diagnostics and for redoing the calibration yourself if you ever need to (see "Calibration" above); they are not directly comparable between units or even between the electrolytic and MEMS sensors on the same unit, since each is calibrated independently.

What a Positive Reading Means

The factory calibration makes increasing raw counts correspond to increasing (more positive) nanoradians, consistently for both axes. What that positive direction corresponds to physically — which way the instrument tips for a positive X or Y reading — depends entirely on how the instrument is mounted and oriented in the hole. The dimple on the top cap marks +X on the instrument itself, but there is no fixed "positive = this compass direction" rule beyond that; record the installation orientation (see "Checking Orientation After Installation" above) if you need to relate sign to a real-world direction.

Temperature and Environmental Columns

pcb_temp is the board's own temperature, reported with every automatic electrolytic reading — useful both as a health check and, per "Calibration" above, as an input if you want to apply temperature compensation yourself in post-processing. env_temp/env_humidity/env_pressure come from a separate environmental sensor and are reported on their own slower interval.

Troubleshooting

Symptom Likely cause / what to check
No reply to a command at all The instrument doesn't recognize it — check spelling and that it's in UPPERCASE. Confirm you're talking to the right port/baud (see "Electrical Connection").
Command replies ! Rejected: wrong argument count, an out-of-range value, or a sensor/motor it needs isn't available right now (e.g. sent while already leveling).
A sensor reports "NOT DETECTED" at boot Run I2CSCAN to see what actually responds on the internal bus, to help narrow down a wiring or sensor fault.
_nrad column reads a flat 0 That axis/sensor has no calibration table loaded — see "Checking a Unit Before Deployment." Not a sensor fault.
LEVEL reports TIMEOUT The axis didn't converge within its safety timeout. Worth a bench check of mechanical freedom of motion for that axis before assuming it's a settings problem.
Leveling stops immediately / current-limit related messages The motor pulse drew more current than SCURLIM allows and was cut off — check for a mechanical obstruction before raising the limit.
Communication is unreliable or garbled Confirm the comm mode (RS232 vs RS485) and matching baud rate (SHOW reports which mode the instrument detected) and check cabling against the connector pinout above.
Need to start over completely FACTORYRESET CONFIRM restores factory defaults (serial number preserved) — reload your calibration afterward; see "Calibration."

Firmware Change Log

Only firmware actually installed on a customer unit is listed here.

Version Status Notes
1.0.0 Released First released version. Dual-sensor (electrolytic + MEMS) tilt sensing on two axes, magnetometer-based gross orientation check, environmental monitoring, adaptive self-leveling (manual and full-auto) with current-limited motor protection, per-axis raw-to-angle calibration with optional electrolytic temperature compensation, and the full serial command interface documented in this manual.

Revision History

Date Changes
September 2026 Initial release, covering firmware 1.0.0.