Connecting and Reading BLE Device Characteristics using BleuIO and JavaScript

Bluetooth Low Energy (BLE) technology has revolutionized the way we interact with devices wirelessly, enabling a wide range of applications such as fitness trackers, smartwatches, and IoT devices. To demonstrate the seamless process of connecting to a BLE device and reading its characteristics, this tutorial will guide you through the steps of using the BleuIO JavaScript library. BleuIO simplifies the process of communicating with BLE devices and extracting valuable information from them.

Prerequisites

Before diving into the tutorial, make sure you have the following prerequisites:

  1. Basic understanding of JavaScript.
  2. Node.js installed on your system.
  3. BleuIO Bluetooth Low Energy USB dongle.
  4. Air quality monitoring BLE device HibouAir (for demonstration purposes).

Step 1: Set Up the Project

  • Create a new directory for your project and navigate to it using the terminal.
  • Install BleuIO javascript library using
    npm i bleuio
  • Create an index.html page that contains buttons to connect to the dongle, connecting and reading characteristics value. We will also have a js script linked at the bottom of the page. Here is the full page content
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>Connect and Service data from BLE devices</title>
    <link
      href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.1/dist/css/bootstrap.min.css"
      rel="stylesheet"
      integrity="sha384-4bw+/aepP/YC94hEpVNVgiZdgIC5+VKNBQNGCHeKRQN+PtmoHDEXuppvnDJzQIu9"
      crossorigin="anonymous"
    />
    <style>
      #terminal {
        background-color: black;
        color: white;
        font-size: medium;
        font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
        padding: 20px;
        margin: 20px 0;
      }
    </style>
  </head>
  <body>
    <div class="container my-3">
      <img src="https://www.bleuio.com/images/logo.png" alt="" />
      <h1 class="my-4">Example script to connect and read Characteristics</h1>

      <button class="btn btn-warning" id="connect">Connect</button>
      <button class="btn btn-primary" id="info">Connection Information</button>
      <button class="btn btn-success" id="ReadService">
        Connect & Read Service Data
      </button>

      <h3><div id="readResponse"></div></h3>
      <div id="terminal"></div>
    </div>
    <script type="module" src="./script.js"></script>
  </body>
</html>
  • Create a js page that contains the logic for connecting to dongle and then connect to Air quality monitoring BLE device HibouAir. After connecting to the device we try to read device manufacturing company name by reading characteristics.
    following is the script file.
import * as my_dongle from 'bleuio';
document.getElementById('connect').addEventListener('click', function () {
  my_dongle.at_connect();
});
document.getElementById('info').addEventListener('click', function () {
  my_dongle.ati().then((x) => {
    document.getElementById('terminal').innerHTML += x.join('<br>');
  });
});

let deviceToConnect = '[1]D1:53:C9:A9:8C:D2';
document.getElementById('ReadService').addEventListener('click', function () {
  my_dongle.at_dual().then(() => {
    my_dongle.at_gapconnect(deviceToConnect).then((x) => {
      setTimeout(() => {
        document.getElementById('terminal').innerHTML += x.join('<br>');
        my_dongle.at_gattcread('0011').then((x) => {
          document.getElementById('terminal').innerHTML += x.join('<br>');
          document.getElementById('readResponse').innerHTML = x[x.length - 1];
          my_dongle.at_gapdisconnectall();
        });
      }, 500);
    });
  });
});

As you can notice on the script we have a variable called device to connect. This is the mac address of the HibouAir BLE device that we are trying to connect. You can replace this mac address to your own device. After the connection we print out the response in a terminal.
on the terminal we can see available service and characteristics. In this script we are trying to read device manufacturer name which is stored at 0011 handle. Therefore we pass the value as at_gattcread function. You can read more about the AT commands at BleuIO getting started guide.

To run this script, we need a web bundler like parcel js.

Run the script with

npx parcel index.html

Example output


In this tutorial, we’ve successfully demonstrated how to connect to a BLE device using the BleuIO dongle and JavaScript. By leveraging the BleuIO’s Javascript library, we streamlined the process of establishing a connection, reading device characteristics, and displaying the information on a web page. BLE technology has vast potential for various applications, and BleuIO makes it easier than ever to interact with BLE devices programmatically. With this tutorial as a foundation, you can explore further and develop your own BLE-powered projects with confidence.

Share this post on :

BleuIO Python Library Upgraded to support firmware v2.3.0: Simplifying Custom Service Management

BleuIO, a leading provider of Bluetooth Low Energy (BLE) solutions, has recently released an exciting new Python library update that supports its latest firmware version 2.3.0. This Python library is a powerful tool that allows developers to manage BLE custom services effortlessly. In this article, we will explore the key features of BleuIO’s Python library, its benefits, and how it simplifies the process of creating custom BLE services.

BleuIO’s Python Library Update

BleuIO’s Python library is a comprehensive set of tools and functions that interface with the BleuIO Bluetooth Low Energy USB dongle. The latest update, which supports firmware version 2.3.0, introduces enhanced capabilities for managing custom BLE services, making it easier for developers to create and deploy custom services for their applications.

The Python library comes with a range of convenient AT commands that can be used to control various aspects of the BleuIO dongle, including scanning, advertising, connecting, and, most importantly, setting up custom services. With just a few lines of Python code, developers can now configure their own custom BLE services, tailoring them to meet the specific requirements of their projects.

Benefits of the Python Library for Custom Services

The new Python library from BleuIO offers several benefits that simplify the process of working with custom BLE services:

  1. Ease of Use: The library abstracts the complexities of interacting with the BLE dongle through simple and easy-to-understand Python functions. This allows even those new to BLE development to get started quickly.
  2. Time Efficiency: By providing high-level functions for custom service setup, the library saves developers valuable time. No need to write low-level code for every aspect of the BLE service creation; instead, developers can focus on implementing the unique features of their applications.
  3. Flexible Customization: With the library, developers have complete control over the configuration of custom services. They can define custom UUIDs, set permissions, properties, and values for each characteristic, tailoring the service to their specific use case.
  4. Real-time Updates: The example script demonstrates how to continuously update the values of characteristics and notify/indicate subscribers. This feature is invaluable for applications that require real-time data exchange.

Creating a Custom Service with BleuIO’s Python Library

Let’s take a look at a sample Python script that showcases how to create a custom BLE service using BleuIO’s Python library:

# (C) 2023 Smart Sensor Devices AB

import time
from datetime import datetime

from bleuio_lib.bleuio_funcs import BleuIO

# This example will show how to setup your own service
# Press Ctrl-C to exit the script.


# Scan result Callback function
def my_scan_callback(scan_input):
    print("\n\nmy_scan_callback: " + str(scan_input))


# Event Callback function
def my_evt_callback(scan_input):
    print("\n\nmy_evt_callback: " + str(scan_input))


# Start BleuIO
my_dongle = BleuIO()
# Register callback functions for scan results and events
my_dongle.register_evt_cb(my_evt_callback)
my_dongle.register_scan_cb(my_scan_callback)

# Run ATI Command
resp = my_dongle.ati()
print(resp.Cmd)
print(resp.Ack["errMsg"])
print(resp.Rsp)

print("My Role: " + my_dongle.status.role)
print("Is Adv: " + str(my_dongle.status.isAdvertising))
print("Is Connected: " + str(my_dongle.status.isConnected))

# Set role to dual if it isn't set already
if not my_dongle.status.role == my_dongle.gaproles.DUAL:
    resp = my_dongle.at_dual()
    print(resp.Cmd)
    print(resp.Ack)

# Disconnect if we're connected
if my_dongle.status.isConnected:
    resp = my_dongle.at_gapdisconnectall()
    print(resp.Cmd)
    print(resp.Ack)

# Stop Advertising if we're advertising
if my_dongle.status.isAdvertising:
    resp = my_dongle.at_advstop()
    print(resp.Cmd)
    print(resp.Ack)

# Stop custom service just in case it had been started previously
resp = my_dongle.at_customservice_stop()
print(resp.Cmd)
print(resp.Ack)

# Service

# Set service UUID
resp = my_dongle.at_customservice(
    0, my_dongle.UUID, "ee6ec068-7447-4045-9fd0-593f3ba3c2ee"
)
print(resp.Cmd)
print(resp.Ack)

# Characteristic 1

# Set characteristic 1 UUID
resp = my_dongle.at_customservice(
    2, my_dongle.UUID, "018f55d9-d747-4c4e-a87b-e9b074ffd2b6"
)
print(resp.Cmd)
print(resp.Ack)

# Set characteristic 1 value length
resp = my_dongle.at_customservice(1, my_dongle.CHAR_LENGTH, "100")
print(resp.Cmd)
print(resp.Ack)

# Set characteristic 1 value
# Here we set it to the current time in hours, minutes and seconds format.
cbTime = datetime.now()
char1_data = cbTime.strftime("%H:%M:%S")
resp = my_dongle.at_customservice(1, my_dongle.CHAR_VALUE, char1_data)
print(resp.Cmd)
print(resp.Ack)

# Set characteristic 1 permissions
resp = my_dongle.at_customservice(1, my_dongle.CHAR_PERMISSION, "R")
print(resp.Cmd)
print(resp.Ack)

# Set characteristic 1 properties
# We set it to Read and Notify
resp = my_dongle.at_customservice(1, my_dongle.CHAR_PROPERTY, "NR")
print(resp.Cmd)
print(resp.Ack)

# Characteristic 2

# Set characteristic 2 UUID
resp = my_dongle.at_customservice(
    2, my_dongle.UUID, "a693ba2c-f0df-40cb-aea7-8ae47281d997"
)
print(resp.Cmd)
print(resp.Ack)

# Set characteristic 2 value length
resp = my_dongle.at_customservice(2, my_dongle.CHAR_LENGTH, "100")
print(resp.Cmd)
print(resp.Ack)

# Set characteristic 2 value
# Here we set it to the current time in month, days and years format.
cbTime = datetime.now()
char2_data = cbTime.strftime("%m/%d/%Y")
resp = my_dongle.at_customservice(2, my_dongle.CHAR_VALUE, char2_data)
print(resp.Cmd)
print(resp.Ack)

# Set characteristic 2 permissions
resp = my_dongle.at_customservice(2, my_dongle.CHAR_PERMISSION, "R")
print(resp.Cmd)
print(resp.Ack)

# Set characteristic 2 properties
# We set it to Read and Indicate
resp = my_dongle.at_customservice(2, my_dongle.CHAR_PROPERTY, "IR")
print(resp.Cmd)
print(resp.Ack)

# Here we check that the service data we set has gone in
resp = my_dongle.at_customservice()
print(resp.Cmd)
print(resp.Ack)
print(resp.Rsp)
print(resp.End)

# Here we start the custom service so next time we advertise and someone connects they will see the new service
resp = my_dongle.at_customservice_start()
print(resp.Ack)

# Here we start advertising so other devices can detect us and connect
resp = my_dongle.at_advstart()
print(resp.Ack)

# Going in a never ending loop that will just update the values of Characteristic 1 and 2 every second.
# If anyone is subscribed we will notify/indicate them.
while True:
    cbTime = datetime.now()
    notiCharTime = cbTime.strftime("%H:%M:%S")
    resp = my_dongle.at_customservice(1, my_dongle.CHAR_VALUE, notiCharTime)
    print(resp.Cmd)
    print(resp.Ack)

    indiCharTime = cbTime.strftime("%m/%d/%Y")
    resp = my_dongle.at_customservice(2, my_dongle.CHAR_VALUE, indiCharTime)
    print(resp.Cmd)
    print(resp.Ack)
    time.sleep(1)

The example script demonstrates the process of setting up a custom service with two characteristics. Characteristic 1 holds the current time in hours, minutes, and seconds format, while Characteristic 2 contains the date in month, day, and year format. The script registers callbacks for scan results and events, sets up the service UUID, characteristic UUIDs, permissions, properties, and starts advertising the custom service.

With BleuIO’s Python library and the user-friendly AT commands, BLE application development becomes accessible to a wider audience, fostering a new era of creative and groundbreaking IoT solutions. Developers can explore the BleuIO Python library further by visiting the official PyPI page (https://pypi.org/project/bleuio/) and start harnessing the power of BLE for their projects.

Share this post on :

Testing Bluetooth Low Energy Application with BleuIO and Jest

Bluetooth Low Energy (BLE) has become a popular technology for creating wireless communication between devices with low power consumption. When developing BLE applications, it’s essential to thoroughly test them to ensure they work correctly. This tutorial shows a basic test script that determines the role of a Bluetooth Low Energy (BLE) device and passes the test based on the device role. The test script utilizes the BleuIO Bluetooth Low Energy USB dongle along with a JavaScript Testing Framework. You can modify this script according to your specific needs for testing BLE applications.. We’ll communicate with the dongle using Node SerialPort, write AT commands, and read back responses from the dongle.

Prerequisites

Before we start, make sure you have the following:

  1. BleuIO Bluetooth Low Energy USB dongle.
  2. Node.js and npm installed on your computer.
  3. Basic knowledge of JavaScript and testing concepts.

Setting Up the Environment

First, connect the BleuIO dongle to your computer. To find the path of the dongle, open your terminal and run the following command:

ls /dev/cu.*

Note down the path of the dongle (e.g., /dev/cu.usbmodem4048FDE6EBCB1).

Next, create a new directory for your project and initialize a Node.js project by running:

npm init -y

Now, install the required packages: SerialPort and Jest.

npm install serialport
npm install --save-dev jest

Writing the Role Check Function

Create a new file rolecheck.js and paste the following code:

import { SerialPort } from 'serialport';

const dongleReadWrite = () => {
  return new Promise((resolve) => {
    let readDataArray = [];
    const port = new SerialPort({
      path: '/dev/cu.usbmodem4048FDE6EBCB1', // Replace this with your dongle's path
      baudRate: 115200,
      dataBits: 8,
      parity: 'none',
      stopBits: 1,
    });

    // Function to write data to the dongle
    const writeData = async (cmd) => {
      port.on('open', () => {
        port.write(cmd + '\r\n', (err) => {
          if (err) {
            return console.log('Error writing data: ', err.message);
          }
        });
      });
    };

    // Function to read data from the dongle
    const readData = () => {
      return new Promise(function (resolve, reject) {
        port.on('readable', () => {
          let data = port.read();
          let enc = new TextDecoder();
          let arr = new Uint8Array(data);
          arr = enc.decode(arr);
          let removeRn = arr.replace(/\r?\n|\r/gm, '');
          if (removeRn != null) readDataArray.push(removeRn);
          return resolve(readDataArray);
        });
      });
    };

    // Write the command 'AT+GAPSTATUS' to the dongle
    writeData('AT+GAPSTATUS');

    // Read the response from the dongle after a delay of 1 second
    readData().then((data) => {
      setTimeout(() => {
        port.close();
        return resolve(data);
      }, 1000);
    });
  });
};

// Function to get the role of the dongle
export const getRole = () => {
  return new Promise((resolve) => {
    // Call dongleReadWrite() to fetch data from the dongle
    dongleReadWrite().then((data) => {
      const regex = /(Peripheral|Central)/i;
      // Find the role from the response data using regular expression
      const roleMatch = data.find((element) => regex.test(element));
      // Extract the role ('Peripheral' or 'Central') from the match
      const role = roleMatch ? roleMatch.match(regex)[0] : null;
      // Return the role to the caller
      return resolve(role);
    });
  });
};

The getRole() function connects to the dongle and writes the command AT+GAPSTATUS to find out the role. The response we get is an array, which looks like this
[ ‘AT+GAPSTATUS’, ‘Peripheral roleNot ConnectedNot Advertising’ ]
we extract the role information from it.

Writing the Test Script

Create another file rolecheck.test.js and paste the following code:

import { getRole } from './rolecheck';
import { jest } from '@jest/globals';
jest.useFakeTimers();

test('Get peripheral role', async () => {
  const dataPromise = getRole(); // Start the data fetching process

  jest.advanceTimersByTime(1000); // Advance the timer by 1 second

  // At this point, the timer has advanced by 1 second, but the data is not resolved yet
  // We can check that the dataPromise is still pending
  expect(dataPromise).toBeInstanceOf(Promise);

  jest.advanceTimersByTime(2000); // Advance the timer by another 2 seconds to complete the 3 seconds

  // Now, the data should be resolved, and the test should pass if the role is 'Peripheral'
  expect(dataPromise).resolves.toBe('Peripheral');
});

In the test script, we import the getRole() function from rolecheck.js. We use Jest’s fake timers to control the asynchronous flow and mimic the behavior of asynchronous code.

The test checks whether the role obtained from the getRole() function is 'Peripheral'. If it is, the test passes; otherwise, it fails.

Running the Test

To execute the test, run the following command in the terminal:

npm test

Jest will run the test, and you should see the test results on your terminal.

The response will look like this

Conclusion

In this tutorial, we explored how to communicate with the BleuIO dongle, write AT commands, and read back responses using a JavaScript Testing Framework (Jest). With the right testing approach, you can ensure the stability and correctness of your BLE application before deploying it to production. Happy testing!

Share this post on :