Skip to content

Latest commit

 

History

History
134 lines (109 loc) · 6.43 KB

File metadata and controls

134 lines (109 loc) · 6.43 KB

DialServer - WebSocket interface for Surface Dial

This is a Web Socket server that provides a continous stream of decoded events from a Microsoft Surface Dial. It's intended to be used to feed a volume controller for network streamers or other network connected audio devices. In particular it works with my BluView app to control volume and playback on a BlueOS device like a BlueSound NODE.

It's designed to run on a Raspberry Pi Zero 2W and the installation instructions are for that device but it should also be easily adapted to other devices running Linux.

Example web socket messages

See index.html for an example of connecting and viewing the web socket messages. All messages are in JSON format. Examples:

{ "button" : "down" }
{ "button" : "up" }
{ "degrees" : 3.1 }
{ "degrees" : -5.3 }

Operating system choices

I've done my latest testing on a Raspberry Pi Zero 2W running Raspberry Pi OS Lite (64-bit) based on Trixie released 2025-12-04 and recommend using this OS as described in the installation steps below.

Usage

The preferred method is to install it as a service so it's always running. See the installation section. However, for testing and development you can run it directly:

$ sudo node main.mjs [options]

Options:

-p,--portWeb sockets port (ws://)default: 3000
-w,--webWeb server port (http://)default: 3080
-b,--buzzEnable haptic feedback on wake-up (useful if there's a noticeable delay)
-d,--debugEnable debug logging
--verboseEnable verbose logging
-v--versionShow version number
-h--helpShow help

It creates both a web server and a web-socket server. The web server is just used for spying on the output primarily for debugging. Connect to it with your browser and it serves a single page that displays data coming from the web-socket. See index.html for an example. You can disable the web server with --web=0

The Surface Dial's haptic feedback is optional to let you know when it wakes up and is ready to handle gestures. You can enable this with --buzz. This is useful if there is a noticeable delay when the device wakes up and reconnects. This was the case previously running on Bookworm but no longer seems to be so significant on Trixie. Try it without the haptic feedback but enable it if the delay is too long.

When running as a server edit the comand line parameters in the dialserver.service file like this:

ExecStart=/usr/bin/node /opt/dialserver/main.mjs --web=0 --buzz

Running as root

By default DialServer needs to be run as root which the install does for you. But if you want to run from non-root then you must create a udev rule based on the vendorId and productId. I had trouble getting this to work but you might give it try. The Microsoft Surface Dial vendorId is 0x045e and the productId is 0x091b. See https://github.com/node-hid/node-hid#udev-device-permissions for an example. I don't view running as root a big issue since the Rpi is dedicated to the Surface Dial and doesn't do anything else.

Installation

  1. Create an image on an SD card using the Raspberry Pi Imager. You can download the official imager from: https://www.raspberrypi.com/software/

    Select the Raspberry Pi OS (other) option and then Raspberry Pi OS Lite (64-bit) for a headless installation.

    IMPORTANT: This is the lite image with no GUI so set the WiFi and SSH parameters in the Raspberry Pi Imager before you create the image or you won't be able to connect or login.

  2. Unmount the SD card and put it in your Rpi. Power it up and wait for it to initialize and connect to your WiFi network.

  3. ssh to the Rpi and update the package list and all packages (the upgrade takes a few minutes):

    $ sudo apt update
    $ sudo apt upgrade -y
    
  4. Install the latest NodeJS version:

    $ apt install nodejs npm
    
  5. Install additional development tools to support NPM modules that require compilation:

    $ sudo apt install -y build-essential libusb-1.0-0 libusb-1.0-0-dev libudev-dev git curl
    
  6. Reboot to get everything up-to-date:

    $ sudo reboot
    

Pairing the Microsoft Surface Dial

You only need to do this once to pair your RPi with the Surface Dial. After it has been paired it will stay paired across reboots of the RPi, etc.

  1. For all of these commands use bluetoothctl:

    $ sudo bluetoothctl
    [bluetooth]#
    
  2. Register the agent:

    [bluetooth]# agent on
    Agent registered
    
    [bluetooth]# default-agent
    Default agent request successful
    
  3. Push the pairing button on the bottom if the Surface Dial (under the rubber base) and hold until the tiny white LED starts blinking. Then scan for nearby Bluetooth devices and wait for a few seconds for the Surface Dial to be found:

    [bluetooth]# scan on
    Discovery started
    [NEW] Device XX:XX:XX:XX:XX:XX Surface Dial
    
  4. Pair to the Surface Dial using its address:

    [bluetooth]# pair XX:XX:XX:XX:XX:XX
    ...
    [bluetooth]# connect XX:XX:XX:XX:XX:XX
    Attempting to connect to XX:XX:XX:XX:XX:XX
    [CHG] Device XX:XX:XX:XX:XX:XX Connected: yes
    
  5. Set the Surface Dial to trusted so it can reconnect:

    [bluetooth]# trust XX:XX:XX:XX:XX:XX
    

Install DialServer

  1. Download the contents of this repository to a directory on the Rpi (eg. ~/dialserver):

    $ git clone https://github.com/jaymarnz/dialserver.git
    $ cd dialserver
    
  2. Install required Node packages (only necessary if you want to run it directly rather than installing it as a service in the next step):

    $ npm install
    
  3. To keep it running all the time you can run it as a service. The install script copies the files to /opt/dialserver, installs dependencies, and uses systemctl to create, enable and start the dialserver service:

    $ sudo bash ~/dialserver/install.sh
    
  4. You can also run it directly using the options shown above. If you've already started it as a service then you'll need to stop it first:

    $ sudo systemctl stop dialserver
    $ sudo node main.mjs [options]