Open JSON serial protocol for integrating MeshVani encrypted mesh communicators with host systems.
MeshVani communicators expose a JSON-based serial protocol over USB (CDC-ACM) for integration with laptops, single-board computers (Raspberry Pi, Jetson Nano), or custom embedded systems. This protocol allows external applications to send and receive encrypted mesh messages, query network status, configure device parameters, and monitor link quality — all through a simple serial interface.
Product page: autoabode.com/meshvani
- Simple JSON format — Human-readable, easy to parse in any language
- Full mesh access — Send messages, receive messages, query topology
- Encryption is transparent — All messages are AES-256 encrypted on the device; the serial protocol handles plaintext
- Async event model — Device pushes incoming messages and status events without polling
- Cross-platform — Works on Linux, macOS, Windows (any USB CDC-ACM compatible OS)
Connect MeshVani to your computer via USB-C. The device appears as a serial port:
- Linux:
/dev/ttyACM0(or/dev/ttyUSB0) - macOS:
/dev/cu.usbmodem* - Windows:
COM3(check Device Manager)
| Parameter | Value |
|---|---|
| Baud Rate | 115200 |
| Data Bits | 8 |
| Parity | None |
| Stop Bits | 1 |
| Flow Control | None |
| Line Ending | \n (newline) |
{"cmd": "send", "to": 5, "msg": "Hello from serial!"}All communication is newline-delimited JSON. Each line is a complete JSON object terminated by \n.
Host to Device (Commands):
{"cmd": "<command_name>", ...parameters}Device to Host (Responses & Events):
{"evt": "<event_type>", ...data}{"res": "<command_name>", "ok": true|false, ...data}Send an encrypted message to a specific node or broadcast to all nodes.
{"cmd": "send", "to": 5, "msg": "Patrol check-in: all clear"}Parameters:
| Field | Type | Required | Description |
|---|---|---|---|
cmd |
string | Yes | "send" |
to |
integer | Yes | Destination node ID (0 = broadcast) |
msg |
string | Yes | Message text (max 200 bytes UTF-8) |
ack |
boolean | No | Request delivery acknowledgment (default: true) |
Response:
{"res": "send", "ok": true, "msg_id": 1042, "hops": 2}Error response:
{"res": "send", "ok": false, "error": "node_unreachable", "to": 5}{"cmd": "status"}Response:
{
"res": "status",
"ok": true,
"node_id": 3,
"firmware": "2.4.1",
"battery_pct": 78,
"battery_mv": 3842,
"uptime_s": 84200,
"mesh_nodes": 12,
"freq_band": "865-867 MHz",
"tx_power_dbm": 20,
"encryption": "AES-256-CTR",
"fhss_enabled": true,
"hop_channels": 20
}{"cmd": "nodes"}Response:
{
"res": "nodes",
"ok": true,
"count": 4,
"nodes": [
{"id": 1, "name": "Base", "hops": 1, "rssi": -62, "snr": 9.5, "last_seen_s": 12},
{"id": 2, "name": "Relay-North", "hops": 1, "rssi": -88, "snr": 4.2, "last_seen_s": 28},
{"id": 5, "name": "Unit-Alpha", "hops": 2, "rssi": -95, "snr": 2.1, "last_seen_s": 45},
{"id": 7, "name": "Unit-Bravo", "hops": 3, "rssi": -108, "snr": -1.5, "last_seen_s": 60}
]
}Get all configuration:
{"cmd": "config"}Set a parameter:
{"cmd": "config", "set": {"node_name": "Relay-South", "tx_power": 14}}Configurable parameters:
| Parameter | Type | Range | Description |
|---|---|---|---|
node_name |
string | 1-16 chars | Human-readable node name |
tx_power |
integer | 2-22 | Transmit power in dBm |
hop_rate |
integer | 1-10 | Hops per second |
beacon_interval |
integer | 10-120 | Beacon broadcast interval in seconds |
sleep_mode |
string | "off", "light", "deep" |
Power management mode |
{"cmd": "ping", "to": 5}Response:
{"res": "ping", "ok": true, "to": 5, "rtt_ms": 342, "hops": 2, "rssi": -94}{"cmd": "gps"}Response:
{"res": "gps", "ok": true, "lat": 28.6139, "lon": 77.2090, "alt_m": 216, "fix": "3D", "sats": 8}{"cmd": "topology"}Response:
{
"res": "topology",
"ok": true,
"edges": [
{"from": 1, "to": 3, "rssi": -62, "snr": 9.5},
{"from": 3, "to": 5, "rssi": -88, "snr": 4.2},
{"from": 1, "to": 2, "rssi": -71, "snr": 7.1},
{"from": 2, "to": 5, "rssi": -95, "snr": 2.1}
]
}Events are pushed by the device asynchronously. Your application should continuously read the serial port and handle these.
{
"evt": "msg",
"from": 5,
"from_name": "Unit-Alpha",
"msg": "Checkpoint reached. All good.",
"msg_id": 2087,
"hops": 2,
"rssi": -94,
"snr": 2.1,
"timestamp": 1714200000
}{"evt": "node_join", "id": 9, "name": "Unit-Charlie"}{"evt": "node_lost", "id": 7, "name": "Unit-Bravo", "last_seen_s": 120}{"evt": "ack", "msg_id": 1042, "from": 5, "rtt_ms": 680}{"evt": "error", "code": "buffer_full", "detail": "TX queue full, message dropped"}pip install pyserial#!/usr/bin/env python3
"""MeshVani serial integration example."""
import json
import serial
import threading
import time
class MeshVani:
"""Interface to a MeshVani device over serial."""
def __init__(self, port="/dev/ttyACM0", baudrate=115200):
self.ser = serial.Serial(port, baudrate, timeout=1)
self.running = True
self._callbacks = []
self._reader_thread = threading.Thread(target=self._read_loop, daemon=True)
self._reader_thread.start()
def send_command(self, cmd: dict) -> None:
"""Send a JSON command to the device."""
line = json.dumps(cmd) + "\n"
self.ser.write(line.encode("utf-8"))
def send_message(self, to: int, msg: str) -> None:
"""Send an encrypted message to a node."""
self.send_command({"cmd": "send", "to": to, "msg": msg})
def broadcast(self, msg: str) -> None:
"""Broadcast a message to all nodes."""
self.send_message(to=0, msg=msg)
def get_status(self) -> None:
"""Request device status."""
self.send_command({"cmd": "status"})
def get_nodes(self) -> None:
"""Request list of known mesh nodes."""
self.send_command({"cmd": "nodes"})
def ping(self, node_id: int) -> None:
"""Ping a specific node."""
self.send_command({"cmd": "ping", "to": node_id})
def on_event(self, callback):
"""Register a callback for incoming events."""
self._callbacks.append(callback)
def _read_loop(self):
"""Continuously read and parse serial data."""
while self.running:
try:
line = self.ser.readline().decode("utf-8").strip()
if line:
data = json.loads(line)
for cb in self._callbacks:
cb(data)
except json.JSONDecodeError:
pass # Skip malformed lines
except Exception as e:
print(f"Serial read error: {e}")
time.sleep(1)
def close(self):
"""Close the serial connection."""
self.running = False
self.ser.close()
def handle_event(data):
"""Example event handler."""
if "evt" in data:
if data["evt"] == "msg":
print(f"[{data['from_name']}] {data['msg']}")
elif data["evt"] == "node_join":
print(f"+ Node joined: {data['name']} (ID: {data['id']})")
elif data["evt"] == "node_lost":
print(f"- Node lost: {data['name']} (ID: {data['id']})")
elif data["evt"] == "ack":
print(f" Delivered msg #{data['msg_id']} in {data['rtt_ms']}ms")
elif "res" in data:
print(f"Response: {json.dumps(data, indent=2)}")
if __name__ == "__main__":
mv = MeshVani(port="/dev/ttyACM0")
mv.on_event(handle_event)
print("MeshVani connected. Type messages to broadcast, or 'quit' to exit.")
mv.get_status()
try:
while True:
user_input = input("> ")
if user_input.lower() == "quit":
break
mv.broadcast(user_input)
except KeyboardInterrupt:
pass
finally:
mv.close()
print("Disconnected.")| Code | Description |
|---|---|
node_unreachable |
Destination node not found in routing table |
msg_too_long |
Message exceeds 200-byte limit |
buffer_full |
Transmit queue is full; try again later |
invalid_cmd |
Unrecognized command name |
invalid_param |
Missing or invalid parameter value |
not_provisioned |
Device has not been provisioned with network keys |
radio_busy |
Radio transceiver is busy (retry after short delay) |
| Firmware Version | Protocol Version | Notes |
|---|---|---|
| 2.0.x | 1.0 | Initial serial protocol release |
| 2.2.x | 1.1 | Added topology command, GPS events |
| 2.4.x | 1.2 | Added config set, FHSS status in status response |
- MeshVani Product Page: autoabode.com/meshvani
- MeshVani Relay: autoabode.com/meshrelay
- AutoAbode Website: autoabode.com
This protocol documentation is released under the MIT License.
The MeshVani hardware and firmware are proprietary products of AutoAbode. This repository documents the serial interface for third-party integration.
We welcome contributions! If you've built an integration library in another language (Node.js, Go, Rust, etc.), we'd love to include it.
- Fork this repository
- Add your integration under
examples/<language>/ - Include a README with setup instructions
- Submit a pull request
Built by AutoAbode — New Delhi, India