Packages
mob
0.6.9
0.7.20
0.7.19
0.7.18
0.7.17
0.7.16
0.7.15
0.7.14
0.7.13
0.7.12
0.7.11
0.7.10
0.7.9
0.7.8
0.7.7
0.7.6
0.7.5
0.7.4
0.7.3
0.7.2
0.7.1
0.7.0
0.6.26
0.6.25
0.6.24
0.6.23
0.6.22
0.6.21
0.6.20
0.6.19
0.6.18
0.6.17
0.6.16
0.6.15
0.6.14
0.6.13
0.6.12
0.6.11
0.6.10
0.6.9
0.6.8
0.6.7
0.6.6
0.6.5
0.6.2
0.6.1
0.6.0
0.5.18
0.5.17
0.5.16
0.5.15
0.5.14
0.5.11
0.5.10
0.5.7
0.5.6
0.5.5
0.5.4
0.5.3
0.5.2
0.5.1
0.5.0
0.4.0
0.3.10
0.3.9
0.3.8
0.3.7
0.3.6
0.3.5
0.3.4
0.3.3
0.3.2
0.3.1
0.3.0
0.2.0
0.1.0
BEAM-on-device mobile framework for Elixir
Current section
Files
Jump to
Current section
Files
lib/mob/bt/spp.ex
defmodule Mob.Bt.Spp do
@moduledoc """
Bluetooth Classic Serial Port Profile (SPP) — RFCOMM byte streams.
Use this for legacy serial-over-Bluetooth devices: Arduino HC-05/HC-06
modules, OBD-II ELM327 readers, marine GPS pucks, industrial sensors,
legacy barcode scanners, etc. Anything that exposes itself as a
bidirectional byte pipe over a custom RFCOMM channel UUID.
See `Mob.Bt` for pairing, discovery, and disconnect.
## Typical flow
# 1. Pair (Mob.Bt.pair/2)
# 2. Connect SPP, supplying the RFCOMM service UUID.
# The well-known SPP UUID is "00001101-0000-1000-8000-00805F9B34FB".
socket = Mob.Bt.Spp.connect(socket, device,
uuid: "00001101-0000-1000-8000-00805F9B34FB")
# {:bt, :spp_connected, session_id, device}
# 3. Receive bytes:
# {:bt, :spp_data, session_id, bytes}
# 4. Send bytes:
Mob.Bt.Spp.write(socket, session_id, "ATZ\\r\\n")
# 5. Disconnect (Mob.Bt.disconnect/2)
## UUIDs
Most SPP devices advertise the standard SPP UUID
`00001101-0000-1000-8000-00805F9B34FB`. Some manufacturers use custom
UUIDs to scope to a specific protocol on the same physical device.
Pass via the `:uuid` opt; if omitted, the standard SPP UUID is used.
## Insecure RFCOMM
By default the connection uses the secure RFCOMM channel (encrypted,
requires bond). Some legacy devices (especially HC-06 clones) only
accept insecure RFCOMM. Pass `secure: false` to fall back.
"""
alias Mob.Bt
@standard_spp_uuid "00001101-0000-1000-8000-00805F9B34FB"
@doc """
Open an SPP (RFCOMM) connection to `device`.
## Options
* `:uuid` — RFCOMM service UUID (default: `"#{@standard_spp_uuid}"`)
* `:secure` — `true` (default, encrypted) or `false` (legacy insecure)
Result: `{:bt, :spp_connected, session_id, device}` on success,
`{:bt, :spp_connect_failed, nil, %{device: device, reason: atom()}}`
on failure.
"""
@spec connect(socket :: term(), Bt.device(), keyword()) :: term()
def connect(socket, device, opts \\ []) do
json = encode_connect(device, opts)
:mob_nif.bt_spp_connect(json)
socket
end
@doc false
@spec encode_connect(Bt.device(), keyword()) :: binary()
def encode_connect(device, opts) when is_list(opts) do
uuid = Keyword.get(opts, :uuid, @standard_spp_uuid)
secure = Keyword.get(opts, :secure, true)
device
|> Map.put(:uuid, uuid)
|> Map.put(:secure, secure)
|> Bt.encode_device()
end
@doc """
Write a byte payload to the SPP session.
Returns the socket. Fire-and-forget: bytes are queued in Kotlin's
output stream and flushed asynchronously. No completion event.
Errors during write are surfaced as
`{:bt, :spp_disconnected, session_id, reason}` (Kotlin closes the
socket on write failure).
"""
@spec write(socket :: term(), Bt.session_id(), binary()) :: term()
def write(socket, session_id, bytes)
when is_integer(session_id) and is_binary(bytes) do
:mob_nif.bt_spp_write(session_id, bytes)
socket
end
end