Current section
Files
Jump to
Current section
Files
lib/meshtastic_client.ex
defmodule MeshtasticClient do
@moduledoc """
A client for interacting with Meshtastic devices using Protocol Buffers.
This client provides functionality to:
- Connect to Meshtastic devices via TCP, Serial, or BLE
- Encode and decode Meshtastic protobuf messages
- Send and receive mesh packets
- Manage device configuration
"""
alias MeshtasticClient.Connection
alias MeshtasticClient.Message
@type connection :: Connection.t()
@type error :: {:error, term()}
@doc """
Connects to a Meshtastic device.
## Options
* `:type` - Connection type: `:tcp`, `:serial`, or `:ble` (default: `:tcp`)
* `:host` - Host address for TCP connections (default: `"meshtastic.local"`)
* `:port` - Port for TCP connections (default: `4403`)
* `:device` - Device path for serial connections (e.g., `"/dev/ttyUSB0"`)
* `:baud_rate` - Baud rate for serial connections (default: `115200`)
## Examples
# Connect via TCP
{:ok, conn} = MeshtasticClient.connect(type: :tcp, host: "192.168.1.100", port: 4403)
# Connect via Serial
{:ok, conn} = MeshtasticClient.connect(type: :serial, device: "/dev/ttyUSB0")
"""
@spec connect(keyword()) :: {:ok, connection()} | error()
def connect(opts \\ []) do
Connection.connect(opts)
end
@doc """
Disconnects from a Meshtastic device.
"""
@spec disconnect(connection()) :: :ok | error()
def disconnect(conn) do
Connection.disconnect(conn)
end
@doc """
Sends a text message to a specific node or broadcast.
## Options
* `:to` - Destination node ID (default: `0xFFFFFFFF` for broadcast)
* `:channel` - Channel index (default: `0`)
* `:want_ack` - Request acknowledgment (default: `false`)
## Examples
MeshtasticClient.send_text(conn, "Hello, mesh!", to: 0x12345678)
MeshtasticClient.send_text(conn, "Broadcast message")
"""
@spec send_text(connection(), String.t(), keyword()) :: :ok | error()
def send_text(conn, text, opts \\ []) do
Message.send_text(conn, text, opts)
end
@doc """
Sends a position update.
## Options
* `:latitude` - Latitude in degrees
* `:longitude` - Longitude in degrees
* `:altitude` - Altitude in meters
* `:to` - Destination node ID (default: broadcast)
* `:channel` - Channel index (default: `0`)
"""
@spec send_position(connection(), keyword()) :: :ok | error()
def send_position(conn, opts \\ []) do
Message.send_position(conn, opts)
end
@doc """
Requests the device configuration.
"""
@spec get_config(connection()) :: {:ok, map()} | error()
def get_config(conn) do
Message.get_config(conn)
end
@doc """
Subscribes to incoming messages from the device.
Returns a stream of messages that can be consumed.
"""
@spec subscribe(connection()) :: Enumerable.t()
def subscribe(conn) do
Connection.subscribe(conn)
end
@doc """
Gets information about the local node.
This function requests configuration and then listens for responses.
You should subscribe first to receive the responses asynchronously.
## Example
MeshtasticClient.subscribe(conn)
# Then receive messages
receive do
{:meshtastic_message, msg} -> IO.inspect(msg)
after
5000 -> :timeout
end
"""
@spec get_node_info(connection()) :: {:ok, map()} | error()
def get_node_info(conn) do
Message.get_node_info(conn)
end
@doc """
Waits for and collects incoming messages for a specified timeout.
## Options
* `:timeout` - Timeout in milliseconds (default: 5000)
* `:count` - Number of messages to collect (default: 10)
## Example
MeshtasticClient.subscribe(conn)
messages = MeshtasticClient.collect_messages(timeout: 3000)
"""
@spec collect_messages(keyword()) :: [term()]
def collect_messages(opts \\ []) do
timeout = Keyword.get(opts, :timeout, 5000)
count = Keyword.get(opts, :count, 10)
collect_messages_loop([], count, timeout)
end
defp collect_messages_loop(acc, 0, _timeout), do: Enum.reverse(acc)
defp collect_messages_loop(acc, remaining, timeout) do
receive do
{:meshtastic_message, msg} ->
collect_messages_loop([msg | acc], remaining - 1, timeout)
after
timeout -> Enum.reverse(acc)
end
end
end