Documentation
ELM327 Device
Source:
hardware_io/automotive/elm327/README.md
The Elm327Device provides low-level serial communication with an
ELM327-compatible automotive diagnostic device. It sends raw ELM327 commands
and returns Elm327Response objects.
OBD-II request modeling and PID decoding belong in protocols.obd2. An adapter
that translates between those protocol models and this device belongs in
controllers.automotive.
Connection
The default connection is /dev/rfcomm0 at 38400 baud. Both values can be
configured when creating the device:
from hardware_io.automotive.elm327 import Elm327Device
device = Elm327Device(port="/dev/rfcomm0", baud=38400)
try:
device.connect()
response = device.send_command("ATI")
print(response.raw)
finally:
device.disconnect()
Initialization
On connection, the device sends ATZ, ATE0, ATL0, ATS0, ATH1, and
ATSP0 to reset and configure the ELM327.
Initialization commands use a settling delay. Normal commands do not use a
fixed delay; the device starts reading immediately and completes the command
when it receives the ELM327 > prompt.
Dependency
The device requires pyserial:
python3 -m pip install pyserial
Bluetooth RFCOMM
A Bluetooth Classic ELM327 uses the Serial Port Profile (SPP). BlueZ exposes
that connection as an RFCOMM serial device such as /dev/rfcomm0, which
Elm327Device opens through pyserial.
Use the setup helper with the address reported by the Bluetooth scanner:
scripts/installers/setup_rfcomm0.sh \
--address 12:34:5A:05:9C:54
The helper:
- Checks whether the device is already paired and pairs it when needed.
- Marks the device as trusted.
- Disconnects any connection held by
bluetoothctl. - Uses SDP to discover the Serial Port Profile channel.
- Installs and starts a systemd service that actively maintains
/dev/rfcomm0now and on future boots, restarting the RFCOMM connection if it drops. - Warns if the current user does not belong to
dialout.
Specify the channel only when SDP discovery fails:
scripts/installers/setup_rfcomm0.sh \
--address 12:34:5A:05:9C:54 \
--channel 1
The host installer can install the feature and configure RFCOMM together:
scripts/install_arm64.sh \
--feature elm327 \
--elm327-address 12:34:5A:05:9C:54
Pairing and RFCOMM configuration are host responsibilities. Elm327Device
intentionally begins at the serial-device boundary and does not invoke
bluetoothctl, sdptool, or rfcomm itself.
Component Test
The component test opens a real serial connection, initializes the ELM327, and lets you send raw adapter or OBD-II commands. It does not emulate an adapter and does not decode OBD-II PID values.
Use this raw test first to separate serial, RFCOMM, and ELM327 problems from
OBD-II parsing problems. Once commands such as ATI and 0100 work, use the
decoded automotive component test
to exercise Elm327ObdAdapter and the PID decoders.
Prerequisites
Before running the test:
- Power the ELM327 and, for a Bluetooth model, pair it with the host.
- Establish its RFCOMM serial device, such as
/dev/rfcomm0. - Confirm that the device exists and that your user can access it:
ls -l /dev/rfcomm0
Serial devices are commonly assigned to the dialout group. If necessary, add
your user to that group, then log out and back in:
sudo usermod -aG dialout "$USER"
Activate the project virtual environment so that pyserial and the project
packages are available:
source venv/bin/activate
Interactive Mode
From the project root, run:
python3 -m hardware_io.automotive.elm327.component_test.elm327_cli
The test connects to /dev/rfcomm0 at 38400 baud by default. After the ELM327
initialization commands succeed, enter commands at the prompt:
Connecting to ELM327 on /dev/rfcomm0 at 38400 baud...
Connected. Enter an ELM327 command, or 'quit' to stop.
elm327> ATI
Command: ATI
Response:
ELM327 v1.5
Raw response:
'ELM327 v1.5\r>'
elm327> quit
Useful initial commands include:
ATI— report the adapter identification string.ATDP— report the selected vehicle protocol.0100— request the vehicle’s supported Mode 01 PIDs.
Enter quit or exit, press Ctrl+D, or press Ctrl+C to disconnect and
stop the test.
Single-Command Mode
Use --command to connect, send one command, print its response, and exit:
python3 -m hardware_io.automotive.elm327.component_test.elm327_cli \
--command ATI
Use --port and --baud for an adapter with different serial settings:
python3 -m hardware_io.automotive.elm327.component_test.elm327_cli \
--port /dev/ttyUSB0 \
--baud 9600 \
--command ATI
Display all command-line options with:
python3 -m hardware_io.automotive.elm327.component_test.elm327_cli --help
Troubleshooting
Unable to connectusually means the serial path is wrong, RFCOMM is not connected, the user lacks device permission, or another process owns the port.- An empty response means the serial connection opened but the adapter did not return data before the configured timeout.
NO DATAis an ELM327 response, not a connection failure. It can mean the vehicle is off, no ECU answered, or the requested PID is unsupported.- Some inexpensive adapters advertise an inaccurate ELM327 version string, so
ATIidentifies what the adapter claims rather than verifying its firmware.