Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 

Repository files navigation

meshvani-serial-protocol

Open JSON serial protocol for integrating MeshVani encrypted mesh communicators with host systems.

License: MIT Python 3.8+ MeshVani


Overview

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

Key Features

  • 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)

Quick Start

1. Connect

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)

2. Serial Parameters

Parameter Value
Baud Rate 115200
Data Bits 8
Parity None
Stop Bits 1
Flow Control None
Line Ending \n (newline)

3. Send Your First Message

{"cmd": "send", "to": 5, "msg": "Hello from serial!"}

Protocol Reference

Message Format

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}

Commands

send — Send a Message

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}

status — Get Device Status

{"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
}

nodes — List Known Mesh Nodes

{"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}
  ]
}

config — Get or Set Configuration

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

ping — Ping a Node

{"cmd": "ping", "to": 5}

Response:

{"res": "ping", "ok": true, "to": 5, "rtt_ms": 342, "hops": 2, "rssi": -94}

gps — Get or Share GPS Position

{"cmd": "gps"}

Response:

{"res": "gps", "ok": true, "lat": 28.6139, "lon": 77.2090, "alt_m": 216, "fix": "3D", "sats": 8}

topology — Get Mesh Topology Map

{"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 (Device to Host)

Events are pushed by the device asynchronously. Your application should continuously read the serial port and handle these.

msg — Incoming Message

{
  "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
}

node_join — New Node Joined Mesh

{"evt": "node_join", "id": 9, "name": "Unit-Charlie"}

node_lost — Node Lost from Mesh

{"evt": "node_lost", "id": 7, "name": "Unit-Bravo", "last_seen_s": 120}

ack — Delivery Acknowledgment

{"evt": "ack", "msg_id": 1042, "from": 5, "rtt_ms": 680}

error — Device Error

{"evt": "error", "code": "buffer_full", "detail": "TX queue full, message dropped"}

Example: Python Integration

Requirements

pip install pyserial

Basic Send/Receive

#!/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.")

Error Codes

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 Compatibility

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

Related Resources


License

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.


Contributing

We welcome contributions! If you've built an integration library in another language (Node.js, Go, Rust, etc.), we'd love to include it.

  1. Fork this repository
  2. Add your integration under examples/<language>/
  3. Include a README with setup instructions
  4. Submit a pull request

Built by AutoAbode — New Delhi, India

About

Open JSON serial protocol for integrating MeshVani encrypted mesh communicators with host systems. Python SDK included.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors