Skip to content

uBridge Server

uBridge server

uBridge runs as a daemon (systemd service), which constantly monitors the machine's USB ports for new enumerated devices.

If the detected device is a uThing™, the server notifies the connected plugins, optionally configures the new device, and listens for incoming data from the uThing™'s.

Each new sensor message is then streamed to the subscribed plugins.

uBridge Diagram

The messaging model is built on top of the socket library NNG (Nanomessage New Generation). NNG is an evolution of Nanomessage and ZeroMQ.

Quick start (systemd)

sudo make install            # from the uBridge build directory
sudo systemctl enable ubridge ubridge-server
sudo systemctl start ubridge

Check status and logs:

systemctl status ubridge-server.service
tail -f /tmp/ubridge.log

Transports

The uBridge can be configured to use two different transports:

  • IPC: Uses Linux Message Queues for efficient communication when the uBridge and plugin applications run on the same machine.
  • TCP: Uses TCP sockets, in case that the client (plugin) applications run on different machines.

All messages between Server and Clients are encapsulated into JSON objects.

The uBridge server communicates with the clients (plugins) via two sockets:

Control socket (REQ/REP)

The Config socket uses the Request / Response model. The clients send Requests to uBridge (i.e. setConfig, getConfig, getDevices, queryDevice, sendCommand, getStatistics) and get the corresponding responses with status or information.

Examples using nngcat:

# List devices
nngcat --req --dial ipc:///tmp/ubridgeReqResp --data '{"request":"getDevices"}'

# Get statistics
nngcat --req --dial ipc:///tmp/ubridgeReqResp --data '{"request":"getStatistics"}'

# Query a specific device by channel ID
nngcat --req --dial ipc:///tmp/ubridgeReqResp --data '{"request":"queryDevice","channelID":"uThing::VOC_1234","query":{"status":true}}'

# Send a command to a device
nngcat --req --dial ipc:///tmp/ubridgeReqResp --data '{"request":"sendCommand","channelID":"uThing::VOC_1234","command":{"led":true}}'

Here is an example of a response message to a getDevices request:

{
    "channelID": "uThing::MNL_D657",
    "fwVersion": "1.0.3",
    "name": "uThing::MNL rev.A",
    "serialNumber": "28AE9B9745B5D657"
},
{
    "channelID": "uThing::VOC_F8B6",
    "fwVersion": "1.3.2",
    "name": "uThing::VOC rev.2",
    "serialNumber": "7F8D2D3FEF89F8B6"
}
And an example response to a getStatistics request:
{
  "bridgeUpTime": 1764846,
  "devices": {
    "uThing::MNL_D657": {
      "msgReceived": 1771,
      "msgSent": 4,
      "upTime": 262823477
    },
    "uThing::VOC_F8B6": {
      "msgReceived": 592,
      "msgSent": 4,
      "upTime": 443589765
    }
  },
  "numConnectedDevices": 2,
  "receivedMsgPerSec": 1.34,
  "sentMsgPerSec": 0.01
}

Stream socket (PUB)

The Stream socket is used for the uBridge to stream sensor data to the plugins. The plugins can subscribe to specific "topics" (for example a single sensor), or to all the incoming sensor data:

    uBridgeClient.subscribe("/sensors", subsMessageHandler); //subscribe to all sensors
    uBridgeClient.subscribe("/sensors/uThing::VOC_9142", subsMessageHandler); //specific unit

The assigned names ("channelID") can be obtained from the config socket with a getDevices request.

The sensors are identified by their type and the 4 last characters of their serial number in order to differentiate multiple sensors of the same type connected.

Source code & Installation

Using with the InfluxDB plugin in a RaspberryPi

At the moment of writing, InfluxDB 2.0 hasn't been released for the armhf port yet, so check the InfluxDB plugin documentation for instructions of installing the arm64 Raspbian version before starting. NOTE: This is only required if InfluxDB will be installed locally.

The source code and installation instructions are located here: https://github.com/ohmtech-io/uBridge

Configuration

All the configuration files are installed under /etc/ubridge

Here is the default configuration file for the uBridge component:

Note that the format is JSON-like, with added comments enclosed in "/* */" blocks

{
    "devNameBase":"",/* If this field is left empty, the bridge will try to find devices
                    in '/dev/ttyACM*' and '/dev/ttyUSB*' - For MacOS, use '/dev/cu'*/
    "configSockUrl":"ipc:///tmp/ubridgeReqResp",/* The sockets for the Request Response Server and data Streamer*/
    "streamSockUrl":"ipc:///tmp/ubridgeStream",/* If the client app runs in the same machine, IPC is 
                    more efficient. If a different host is used (as with a client running in a Docker instance)
                    use TCP host:port ("tcp://localhost:8001")*/
    "maxDevices":10/* Maximum number of devices supported */
}
  • "devNameBase": each Linux distribution assigns different base names for the Virtual Serial Ports.

    For example, Debian assigns /dev/ACM0 to the first VCP device enumerated. By default (leave this field empty), uBridge will look into /dev/ttyACM* and /dev/ttyUSB* devices.

  • "configSockUrl": Socket name for the configuration Request/Respond messages.

    Using ipc:///tmp/*, NNG will use Linux Message Queues when the client/s are running on the same machine, this is more efficient than TCP sockets.

    For TCP transport, use tcp://server:port (i.e. tcp://localhost:8001). This can be handy if the plugins are running inside a Docker container, or in a remote server.

  • "streamSockUrl": Socket name for the message streaming (Publish / Subscribe model).

  • "maxDevices": This will limit the maximum number of devices supported if needed (extra enumerated uThing's will be ignored).

NOTE: Remember to restart the server after changing the configuration file (sudo systemctl restart ubridge-server.service).

Service files

The installation provides two service units:

  • ubridge.service: uBridge main service.
  • ubridge-server.service: convenience wrapper to ensure the bridge is started and monitored.

You can override unit configuration via drop-in files under /etc/systemd/system/ if needed.

Monitoring

Check running status of the service

 systemctl status ubridge-server.service

Log file

The log files for the uBridge and plugins are located in the /tmp directory:

tail /tmp/ubridge.log

Troubleshooting

  • Missing NNG or LibSerial: ensure libserial-dev and nng are installed and visible to the linker (often /usr/local/lib); run sudo ldconfig after installing NNG.
  • Permission errors on serial ports: add your user to the appropriate group (e.g. dialout) or run the service as root.
  • No devices found: verify device nodes under /dev/ttyACM*//dev/ttyUSB* (or set devNameBase). On macOS, set devNameBase to /dev/cu for development.
  • No PUB messages: confirm streamSockUrl matches between server and client and that the subscriber topic prefix is correct.

IPC permissions (PermissionDenied)

If a client gets PermissionDenied when connecting to ipc:///tmp/ubridgeReqResp or ipc:///tmp/ubridgeStream, it’s due to filesystem permissions on the created IPC endpoints.

  • Quick check:
    • ls -l /tmp/ubridge*
  • Options:
    • For development, switch to TCP in ubridgeConfig.json and in clients
    • Run the client with elevated permissions: sudo ...
    • Run the service as your user so the sockets are owned by you (edit service and set User=<youruser>; restart)
    • Relax the file creation mask in the service: add UMask=0002 (or 000) to the unit and restart
    • Use a dedicated runtime dir and group permissions:
      • In the service: RuntimeDirectory=ubridge, RuntimeDirectoryMode=0775, User=ubridge, Group=ubridge, UMask=0002
      • Update config to ipc:///run/ubridge/ubridgeReqResp and ipc:///run/ubridge/ubridgeStream