Run Scripts on Your Laptop From The Smartphone Using Bluetooth LE

Create a framework to allow commanding the execution of scripts on a BLE central from your smartphone

Lorenzo Felletti
Geek Culture

--

Example of the Android peripheral application in action.

In this article, I will talk about how you can command the execution of scripts on your laptop (acting as a Bluetooth LE Central) from your smartphone (acting as the Peripheral).

If you don’t know what BLE is, or what BLE central and peripheral are, or what GATT is, it is highly recommended that you read something about it first, and then come back to continue with the article.

Before starting to describe the details of the project, let’s start with a brief high-level overview of the goal.

Create a system that, from a smartphone, is able to broadcast-command the execution of scripts to a set of BLE centrals.

The goal is to create a system that, from a smartphone, can broadcast-command the execution of scripts to a set of BLE centrals, possibly supporting many different Operating Systems.
This project started from a project work assignment I did in university, and the “use case scenario” was that of an operator going around a factory and commanding the execution of “something” (a script) to all the machines nearby.

So, more specifically, what you are going to achieve if you follow the article is a “framework” in which an Android application is able to command the execution of scripts in all nearby devices running a client that continuously scan for “certain” peripherals, and connects to one whenever it finds one.

The Project’s Stack

The project consists of two main parts:

  • the Android applications, implementing a GATT server
  • the multi-platform client.

For the development of the Android application, it was used Kotlin as the language, and a permission management library I wrote myself to make it easier to manage permissions in the app.

For the client, on the other side, two different versions were developed:

  • one using Python, and the Bleak library for Bluetooth
  • the other using Rust, and the Btleplug library for Bluetooth.

Both implementations support Linux, Windows, and macOS, but the Python one is far more complete than the Rust one, partly because the Python library is more mature, partly because I’m lazy…

The Messages Exchanged by The Devices

The Peripheral exposes a service with UUID 0000ffe0-0000-1000-8000-00805f9b34fb having two characteristics:

  • one that will be used to command the script execution, and to which centrals subscribes to for notifications
  • one that will be used by the centrals to inform the peripheral about the execution status (whether it has started, and the exit code).

The typical communication flow is shown in Figure 1.

Figure 1 — Typical communication flow: 1 — the client subscribes to notifications for the script characteristic. 2a — the user chooses the script to execute. 2b — the notification is sent to the client. 3 — the client signals the server that it is starting the execution by making a read to the status characteristic. 4 — when the client finishes the execution, it writes the exit code to the status characteristic.

To make it more clear, the script’s characteristic value is a string indicating what script to execute with the relative arguments (e.g. sum_two_numbers.sh 1 3). When the central starts to execute the script, it makes a read to the status characteristic, to signal “hey! I’m about to execute your request” and, when it finishes, writes to the status characteristic the exit code of the script (the exit code, not the script’s output!).

Schema of the service
Figure 2 — Service schema. script_characteristic is the characteristic that contains the script to execute, status_characteristic is used to manage status updates. Both characteristics have a characteristic descriptor, which is something you do not care much about.

The Roles

The Android app takes the role of BLE Peripheral, and is the one that is “producing” information (i.e. the script to run). In the project I created, it works by accepting connections from all the devices that request it, and broadcasts any new script execution request (that is, for example, if you run a script that start a server in localhost, all connected devices will try to start said server).

The “desktop” app takes the role of BLE Central, and is the one “gathering and consuming” information. The app continuously search for new device to connect to (i.e. devices exposing the service), and connect to the first available one. Once connected, it remains connected “forever” or until the connection is lost.

The Android Application (BLE Peripheral)

The application’s designed is very simple:

  • on top, the list of connected devices, and info about their subscription and execution statuses will be shown
  • in the lower part, a field to insert the script you want to execute and its parameters is present, as well as a “send notification” button, to command the script’s execution, and a “start/stop server” button, to start and stop exposing the service.
Screenshot of the Android app UI, showing 3 connected devices, two of which are executing an operation and the other one not yet
Figure 3 — In the image, a screenshot of the application is taken, where three devices are connected to the peripheral, and subscribed to notification (the green dots). The peripheral has commanded the execution of a script, that has already started in two out of three devices.

The link to the application repo is available at the end of the article.

The application uses a permissions' management library that I created to ensure that the necessary permissions are granted by the user, and also it checks that the Bluetooth LE is available on the device and turned on.

The app has various components organized into packages:

  • the ble.gattserver package contains a set of components useful to manage the GATT server and its advertisement, that tries to be as general as possible
  • the blescriptrunner package, on the other hand, contains components that are specific to the actual use of the server that is made
  • the fragments package contains the connected devices fragment.

The two main Android components that are used to manage the Bluetooth LE GATT server are the BluetoothLeGattServer and the BluetoothLeAdvertiser.
In particular, the advertiser is moved to a Service, while the GATT Server is encapsulated in the GattServerManager class, that tries to provide a (as much as possible) general implementation of a GATT server.

Figure 4 — Screenshots taken on the peripheral (i.e. the smartphone) showing an example of execution flow. From left to right, the first screenshot shows the three devices connected to the peripheral, with the notifications enabled; in the second screenshot is noticeable that a notification was sent and that two of the three devices has started the execution, while the third devices start executing the script only in the third screenshot; the fourth screenshot shows that all the three devices has successfully concluded the execution.

The Python Central

The Python application is very simple, and it is developed using the Bleak library to manage the Bluetooth LE.

The application continuously tries to discover and connect to devices exposing the script runner’s service UUID and, Once one of such devices is found, and the application connects to it, it enables the notification for the script characteristic, and waits to receive notifications.

Within the app, it is not possible to command the execution of any script, but each script that you want to be executable from the smartphone must be put in a specific directory.

Once a notification is received, the notified characteristic value is put into an asynchronous queue, that is consumed (also asynchronously) by the coroutine that has the task to try the script’s invocation. This task parses the characteristic value (in a POSIX compliant way), and then tries to execute the script obtained by prepending arg[0] with the path to the script directory. If a script with that name does not exist in the script directory, the error is saved in the log file, but the program continues to run without being blocked.

The application is able to run scripts on Linux, Windows, and macOS and, if you provide different implementations of the same script (one for each platform) it is able to automatically add and/or correct the script’s extension to use the one that is correct for the central’s Operating System.

The Rust Central

The Rust application was created after the Python one, and because of limitations in the used library — Btleplug — and in my knowledge of Rust, it offers fewer features than the Python counterpart.

In particular, the Rust application is not able to:

  • handle disconnections
  • be configured via command line options
  • add or correct the script extension.

Anyway, the core functionalities are still present, and a scenario with some centrals using the Python application and some others using the Rust one is perfectly tolerable.

Performance Assessment

In the tests conducted using the Python central app, the results when having a single laptop (MacBook Air M1, with macOS 12.6) connecting over 14 trials were:

  • 1.28s — mean discovery latency (with a standard deviation of 1.58s)
  • 0.76 — mean connection latency (with a standard deviation of 0.47s).

Another test was conducted, whose results are shown in Figures 5 and 6. The test was carried out using three different laptops trying to connect at the same time or one at a time. The three laptops were a MacBook Air M1, a laptop with Ubuntu installed (Linux1), and a laptop with Manjaro installed (Linux2).

Figure 5 — Analysis of mean discovery latencies measured when connecting the devices one at a time, and when connected simultaneously. The bars of the bar plot shows the average latency measured on each device, while the blue line shows the variation of the mean of the mean latencies of each individual device in the two scenarios.
Figure 6 — Analysis of mean connection latencies measured when connecting the devices one at a time, and when connected simultaneously. The bars of the bar plot shows the average latency measured on each device, while the blue line shows the variation of the mean of the mean latencies of each individual device in the two scenarios.

The results of the performance analysis carried out on different devices, shows that the main factor influencing the latencies is indeed the Operating System mounted by the device (or how good is the Bleak backend for that OS). Indeed, as noticeable in Figures 5 and 6, the two Linux machines showed similar performances (despite being onboarded with different Bluetooth chipset, and different versions of Bluetooth too), while the macOS laptop had totally different performances.
In particular, despite being onboarded with a newer chipset, the macOS laptop had a significantly higher discovery latency, partly compensated on the other hand by a lower connection latency, compared with the two Linux laptops.

Moreover, using having all three laptops trying to connect simultaneously, or using them one at a time, did not show significant performance differences (at least, not that would be noticeable by a human user).

Conclusions

To conclude, by completing this project, I was able to explore more in depth the strengths and weaknesses of Bluetooth LE which is, in my opinion, a huge step forward compared to Bluetooth classic.

One of the biggest obstacles I faced while carrying out this project, was to find resources showing how to implement a GATT server. I have the impression that most resources focuses on how to create a Bluetooth LE Central, while the documentation and resources on how to create a service is much more scarce.

One of the biggest strengths of BLE is that it provides a fairly good communication range (5 to 10 meters in my experience), and a decent transmission speed, with a very low battery consumption.

On the other hand, I feel that more effort should be conveyed toward making Bluetooth LE easier to work with for developers (e.g. more and better resources and documentation), which is exactly what I am trying to achieve (at least a bit) with this article.

Let me know in the comments if you liked it or not, or if you have doubts or questions, I will try my best to answer!

--

--