Metadata-Version: 2.4
Name: erdilrf-lrf
Version: 0.2.0
Summary: Host-side UART driver for ERDI 905 nm laser rangefinder modules (LR1000E2 protocol family)
License-Expression: MIT
Project-URL: Homepage, https://erdilrf.com
Project-URL: Repository, https://github.com/erdilrf/erdilrf-drivers
Project-URL: Documentation, https://github.com/erdilrf/erdilrf-drivers/blob/main/protocol/PROTOCOL.md
Keywords: laser-rangefinder,lidar,dtof,905nm,uart,embedded,robotics,drone,industrial-automation
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Manufacturing
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Scientific/Engineering :: Interface Engine/Protocol Translator
Classifier: Topic :: System :: Hardware :: Hardware Drivers
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: serial
Requires-Dist: pyserial>=3.5; extra == "serial"
Provides-Extra: dev
Requires-Dist: pytest>=7; extra == "dev"
Dynamic: license-file

# Python host driver

Host-side UART driver for **ERDI 905 nm laser rangefinder modules** — the LR1000E2 protocol family.

**Zero core dependencies** — framing and decoding are pure standard library, so the whole driver
runs and is testable on any machine, including one with no hardware attached. `pyserial` is optional
and only needed to actually open a port.

```bash
pip install -e .            # from this directory (python/)
pip install -e python/      # from the repository root — same thing
pip install pyserial        # only needed to open a real port
```

## Command line

Every command below runs with **no hardware and no serial port** except the last three:

```bash
erdilrf frames                             # print the whole command table
erdilrf decode "55 AA 88 01 FF 27 10 BE"   # decode a captured frame
erdilrf ports
erdilrf measure --port COM3 --count 5
erdilrf stream  --port COM3
erdilrf selftest --port COM3               # run immediately after power-on
```

## Library

```python
from erdilrf_lrf import Rangefinder, SerialTransport

with Rangefinder(SerialTransport("COM3")) as rf:
    print(rf.single_shot())                 # 123.4 metres, or None if the shot failed
    for distance in rf.stream(count=10):    # 3-10 Hz adaptive continuous ranging
        print(distance)
```

## Protocol in one table

Fixed 8-byte frames. The checksum is the low byte of a sum — and **the two directions use different
ranges**, which is the single most common integration mistake.

```text
offset   0     1     2      3    4    5     6      7
        0x55  0xAA  FUNC   D1   D2   D3    D4     SUM

host  -> module   SUM = (FUNC + D1 + D2 + D3 + D4) & 0xFF
module -> host    SUM = (0x55 + 0xAA + FUNC + D1 + D2 + D3 + D4) & 0xFF
```

| Command | Transmit | Response |
|---|---|---|
| Single-shot ranging | `55 AA 88 FF FF FF FF 84` | `55 AA 88 STA FF DIS_H DIS_L SUM` |
| Continuous ranging | `55 AA 89 FF FF FF FF 85` | `55 AA 89 STA FF DIS_H DIS_L SUM` |
| Stop ranging | `55 AA 8E FF FF FF FF 8A` | `55 AA 8E STA FF FF FF SUM` |
| Angle measurement † | `55 AA 8A FF FF FF FF 86` | `55 AA 8A STA FF ANG_H ANG_L SUM` |
| LD constant-on on / off | `55 AA 86 FF FF FF 01 84` / `… 00 83` | see known gap below |
| Set baud rate | `55 AA TYPE FF FF FF FF SUM` | `55 AA TYPE STA FF FF FF SUM` |

Distance and angle are both scaled by 10 — the 16-bit register holds tenths, so
`((DIS_H << 8) | DIS_L) / 10` gives metres.

† Angle measurement is available **only on modules fitted with an angle sensor**.

Default link: **115200 bps, 8 data bits, 8-byte frame**. The manufacturer documents 115200 and 8 data
bits; it is **silent on parity and stop bits**, so this driver assumes 8N1 and says so. If a module
stays silent, try `--parity E` or `--parity O` before assuming a wiring fault.

## Tests

50 tests, no hardware and no serial port required — serial I/O is injected as a two-method
transport, so the whole driver runs against a scripted fake link.

```bash
python -m pytest -q
```

The suite has already caught real defects in this repository: a `FrameReader` that validated
*transmitted* frames with the *response* checksum rule, and a pre-command flush that silently ate
the answer to the command it had just sent (a wrong distance with no error — the worst failure this
driver can produce).

Golden vectors assert that all six transmit frames printed in the manufacturer's manual are
reproduced byte for byte, and that each printed checksum is reproducible from the stated rule.

## What the tests do not cover

No test in this repository has ever run against a real module. The framing is verified against the
manual and against itself, **not against silicon**. A capture from real hardware outranks every
table above — if your module disagrees, open an issue with the raw bytes.

## Known gaps we did not paper over

* **The LD constant-on response frame is incomplete in the published manual** — section 8 prints
  only `55` for that row. This driver sends the command and does not validate a reply. It does not
  guess the missing bytes.
* **Cable wire colours are not documented.** The manual gives pin numbers only:
  1 GND, 2 VCC, 3 I/O (reserved), 4 TXD, 5 RXD, 6 SW-SHOT.
* **SW-SHOT polarity is configuration-dependent.** The manual says "function enable (active high)"
  and in the same row "compatible with active-low control requirements". Confirm per unit.
* **UART logic-level tolerance is not specified.** Only the 3.3–5 V supply figure is given.

---

## About this file

This is the **PyPI long description** for the `erdilrf-lrf` distribution. The packaging toolchain
cannot read a file outside the project directory, so this file lives here rather than reusing the
repository root `README.md`. It mirrors the Python-relevant sections of that root README; the root
README remains authoritative for the repository as a whole (protocol reference, Arduino driver,
wiring notes).

Installable package name: **`erdilrf-lrf`** · import name: **`erdilrf_lrf`** · CLI: **`erdilrf`**.
Note that the package name differs from the import name and from the CLI name — see
[`pyproject.toml`](pyproject.toml) for the single source of truth.

Part of [erdilrf/erdilrf-drivers](https://github.com/erdilrf/erdilrf-drivers) · MIT ·
modules documented at [erdilrf.com](https://erdilrf.com)
