Make your Bluetooth Low Energy connection secure using BleuIO

Protection of private information is essential for every wireless low energy device, from fitness band to payment systems. Privacy mechanisms prevent devices from being tracked by untrusted devices.

Secure communications keep data safe while also preventing unauthorized devices from injecting data to trigger the system’s unintended operation.

In Bluetooth Low Energy (BLE), devices connected to a link can pass sensitive data by setting up a secure encrypted connection, which means making the data unreadable to all but the Bluetooth master and slave devices.

BleuIO has introduced security feature into its latest release (firmware v1.3.0 ). User can now use Numeric Comparison, Just Works or Passkey Entry to make data transmission more secure when working with Bluetooth low energy application using BleuIO. 

  • Numeric Comparison: In this scenario, both devices have a display unit capable of displaying a six-digit number. Both displays output the same number, and the user is asked to confirm that these numbers match. 
  • Passkey Entry: The Passkey Entry is primarily intended for the case that one device has a keyboard but no display unit and the other device has at least a display unit, for example, a PC and a BLE keyboard scenario. The user is shown a six-digit number (from “000000” to “999999”) on the device with a display and then is asked to enter the number on the other device. If the value entered on the second device is correct, the pairing is successful.
  • Just Works: This model is primarily intended for the most constrained devices in terms of I/O. The Just Works association model uses the Numeric Comparison protocol, but the user is never shown a number, and the application may simply ask the user to accept the connection. This method doesn’t offer protection against a Man in the Middle (MITM) attack, but it provides the same protection level against passive eavesdropping as the Numeric Comparison.

Use the following AT commands to apply secure connection.

AT Commands :

  • AT+SETPASSKEY for setting or querying set passkey for passkey authentication.
  • AT+SECLVL for setting or querying minimum security level used when connected to other devices.
  • AT+NUMCOMPA for accepting a numeric comparison authentication request or enabling/disabling auto-accepting numeric comparisons.

Following video shows how to pair between two BleuIO devices and apply above mentioned security.

Share this post on :

A new firmware update (v 1.2.0) has been released for BleuIO

Smart Sensor Devices is announcing a firmware update for Bleuio and Smart USB dongle 2.0. We invite all the users to apply the updated firmware. The new firmware will be available to download on 5th February 2021, at https://www.bleuio.com/getting_started/docs/firmware/

Firmware Update Improvements 

  • It is now possible to access protected characteristics that need an increased security level. 
  • Security level can be increased by successfully pairing/bonding.
  • Security level will now be displayed when changed.

Newly Added features 

  • Added AT Command AT+GAPIOCAP for setting or querying dongle input and output capabilities. Important for what type of security responses is available.
  • Added AT Command AT+GAPPAIR for manually starting a pairing or bonding procedure.
  • Added AT Command AT+GAPUNPAIR for unpairing all or selected devices.
  • Added AT Command AT+ENTERPASSKEY for handling passkey requests when pairing/bonding.
  • You are now able to secure the connection between other devices and the dongle or between dongles via pairing/bonding.
  • The dongle is now capable of initialising or handling pairing and/or bonding requests. Depending on what Input/Output capability you’ve set on it.
  • The dongle can now handle numeric comparison authentication or passkey authentication (with the new AT+ENTERPASSKEY command).

To meet the demands of users, the BleuIO will continue to update and add new features.

Share this post on :

Collect Realtime Air Quality Data From Bluetooth Device

This project will show how to collect Realtime Bluetooth low energy data and show it on web browser.

For this project, I am using Bluetooth Low Energy USB dongle called BlueIO, which will act as a central device to retrieve data. Hibou Air Quality Monitor which will serve as a peripheral device to transmit the data. The script is simple to use and can be used for other purposes such as store the data into database or cloud.

Things we need:

Before we start

The article assumes you have some general knowledge of how Bluetooth Low Energy (BLE) work. Since the Chrome Serial specification on Google Chrome is not finalized yet, you will have to go to enable the highlighted flag, and restart Chrome. open chrome://flags/#enable-experimental-web-platform-features in chrome browser. In this example, we are going to use JavaScript + html (and some CSS for styling) to setup the BleuIO and quickly start scanning.

The script

The source code is available on Github.

https://github.com/smart-sensor-devices-ab/get_realtime_data_bleuio

The index.html file contains the layout of the script. There are two main buttons. Connect, scan and stop getting data. The connect button will use web serial to connect to BleuIO dongle. After that the scan BLE devices button will send some AT commands to the dongle and the respnse will be printed on the scree.

Here is the index.html file

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>BleuIO Web Bluetooth Example</title>
    <meta charset="utf-8" />
    <meta name="ssd" content="beaconexample" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <link rel="shortcut icon" type="image/png" href="images/favicon.png" />

    <script>
      // Redirect to HTTPS if HTTP is requested.
      if (window.location.protocol === "http:") {
        window.location.href = "https:" + window.location.href.substring(5);
      }
    </script>

    <link rel="stylesheet" href="style.css" />
    <link
      rel="stylesheet"
      href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css"
      integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z"
      crossorigin="anonymous"
    />
  </head>
  <body>
    <div class="container">
      <div
        id="carouselExampleFade"
        class="carousel slide carousel-fade"
        data-ride="carousel"
      >
        <div class="carousel-inner">
          <div class="carousel-item active">
            <div class="row">
              <div class="col-md-9 caption">
                <img src="images/logo.png" />
                <h1>Bluetooth® low energy adapter</h1>
                <a
                  class="btn btn-info btn-lg"
                  href="https://www.bleuio.com/"
                  target="_blank"
                  >Learn More</a
                >
              </div>
              <div class="col-md-3">
                <img
                  src="images/bleuIO_white_withlogo.png"
                  class="d-block w-100"
                  alt="..."
                />
              </div>
            </div>
          </div>
          <div class="carousel-item">
            <div class="row">
              <div class="col-md-9 caption">
                <img src="images/logo.png" />
                <h1>Create your own BLE applications</h1>
                <a
                  class="btn btn-info btn-lg"
                  href="https://www.bleuio.com/"
                  target="_blank"
                  >Learn More</a
                >
              </div>
              <div class="col-md-3">
                <img
                  src="images/bleuIO_black_withlogo.png"
                  class="d-block w-100"
                  alt="..."
                />
              </div>
            </div>
          </div>
          <div class="carousel-item">
            <div class="row">
              <div class="col-md-9 caption">
                <img src="images/logo.png" />
                <h1>Quick, Innovative, Simple</h1>
                <a
                  class="btn btn-info btn-lg"
                  href="https://www.bleuio.com/"
                  target="_blank"
                  >Learn More</a
                >
              </div>
              <div class="col-md-3">
                <img
                  src="images/bleuIO_black_withlogo.png"
                  class="d-block w-100"
                  alt="..."
                />
              </div>
            </div>
          </div>
        </div>
      </div>
      <!-- end carousel -->
    </div>
    <!-- end container -->
    <div class="codesection">
      <div class="container">
        <h1>Get realtime data from BLE device</h1>
        <main class="main">
          <div id="notSupported" class="hidden alert alert-danger">
            Sorry, <b>Web Serial</b> is not supported on this device, make sure
            you're running Chrome 78 or later and have enabled the
            <code>#enable-experimental-web-platform-features</code> flag in
            <code>chrome://flags</code> <br />
            Open
            <strong>
              chrome://flags/#enable-experimental-web-platform-features</strong
            >
            in Google Chrome browser.
          </div>

          <br />
          <button id="butConnect" type="button" class="btn btn-success">
            Connect
          </button>
          <br /><br />
          <div class="row">
            <div class="col-md-3">
              <button
                id="butScan"
                type="button"
                disabled
                class="btn btn-primary"
              >
                Scan BLE Devices
              </button>
            </div>
            <div class="col-md-3">
              <select
                class="devices form-control"
                id="devices"
                onchange="getSelectedDevice(this)"
              >
                <option value="">Select a Device</option>
              </select>
            </div>
            <div class="col-md-3">
              <button
                id="butGetData"
                type="button"
                disabled
                class="btn btn-primary"
              >
                Get Data
              </button>
            </div>
          </div>

          <br /><br />

          <pre id="log" class="mt-5 d-none"></pre>
          <div id="dataIntoTable" class="mt-5"></div>

          <div style="background: white"></div>
        </main>
      </div>
    </div>
    <div class="footer text-center mt-3">
      Powered by <a href="https://www.bleuio.com/" target="_blank">BleuIO</a> .
      A product of
      <a href="http://smartsensordevices.com/" target="_blank"
        >Smart Sensor Devices</a
      >
    </div>

    <!-- end container -->
    <!-- JS, Popper.js, and jQuery -->
    <script
      src="https://code.jquery.com/jquery-3.5.1.slim.min.js"
      integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj"
      crossorigin="anonymous"
    ></script>
    <script
      src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js"
      integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN"
      crossorigin="anonymous"
    ></script>
    <script
      src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"
      integrity="sha384-B4gt1jrGC7Jh4AgTPSdUtOBvfO8shuf57BaghqFfPlYxofvL8/KUEfYiJOMMV+rV"
      crossorigin="anonymous"
    ></script>

    <script src="script.js" defer></script>
  </body>
</html>

The Script.js file contains all the JavaScript codes to connect to the dongle and get advertised data from the air quality monitoring device. After getting the advertised data we try to decodes it to get a meaningful format.

"use strict";

let port;
let reader;
let inputDone;
let outputDone;
let inputStream;
let outputStream;
let isScanning = false;
let isGettingData = false;
let hibouDevices = [];
let rightDevice = false;
let scannedSensorData = []
const log = document.getElementById("log");
const butConnect = document.getElementById("butConnect");
const butScan = document.getElementById("butScan");
const butGetData = document.getElementById("butGetData");
const outputTable = document.getElementById('dataIntoTable')
let outputData='';
document.addEventListener("DOMContentLoaded", () => {
  butScan.addEventListener("click", clickScan);
  butGetData.addEventListener("click", clickGetData);
  butConnect.addEventListener("click", clickConnect);
  const notSupported = document.getElementById("notSupported");
  notSupported.classList.toggle("hidden", "serial" in navigator);
});



/**
 * @name connect
 * Opens a Web Serial connection to a serial device such as a Smart USB Dongle 2.0 and sets up the input and
 * output stream.
 */
async function connect() {
  // - Request a port and open a connection.
  port = await navigator.serial.requestPort();
  // - Wait for the port to open.
  await port.open({ baudRate: 9600 });

  const encoder = new TextEncoderStream();
  outputDone = encoder.readable.pipeTo(port.writable);
  outputStream = encoder.writable;

  let decoder = new TextDecoderStream();
  inputDone = port.readable.pipeTo(decoder.writable);
  inputStream = decoder.readable.pipeThrough(
    new TransformStream(new LineBreakTransformer())
  );

  reader = inputStream.getReader();
  readLoop().catch((error) => {
    toggleUIConnected(false);
    port = null;
    log.textContent = "Dongle Disconnected!";
  });
}

/**
 * @name disconnect
 * Closes the Web Serial connection.
 */
async function disconnect() {
  // Close the input stream (reader).
  if (reader) {
    await reader.cancel();
    await inputDone.catch(() => {});
    reader = null;
    inputDone = null;
  }
  // Close the output stream.
  if (outputStream) {
    await outputStream.getWriter().close();
    await outputDone;
    outputStream = null;
    outputDone = null;
  }
  // Close the port.
  await port.close();
  port = null;
  log.textContent = "Dongle Disconnected!";
}

/**
 * @name clickConnect
 * Click handler for the connect/disconnect button.
 * Checks if port != null
 * If true: Checks if any beacons is advertising or scans are running and stops the advertsing or scan if so. Then runs disconnect() and set toggleUIConnected to false.
 * if false: Runs connect() then set toggleUIConnected to true.
 */
async function clickConnect() {
  log.textContent = "";
  if (port) {

    // If disconnected while scanning the dongle will restart
    if (isScanning) {
      writeCmd("\x03");
      butScan.textContent = "Scan BLE Devices";
      isScanning = false;
    }
    await disconnect();
    toggleUIConnected(false);
    return;
  }
  await connect();
  toggleUIConnected(true);
}

function getSelectedDevice(selectObject) {
  var selectedDevice = selectObject.value;  
  localStorage.setItem("selectedDevice", selectedDevice);
}

/**
 * @name clickScan
 * Click handler for the Scan button.
 * Checks if a scan is already running by checking the boolean isScanning.
 * If isScanning = true: Stops scanning and goes back to peripheral mode, changes the button text and shows the beacon buttons. Finally sets isScanning = false.
 * If isScanning = false: Goes into Central mode and starts scanning for ble devices. Also changes button text and hides the beacon buttons. Finally sets isScanning = true.
 */
function clickScan() {
  console.log("SCAN BUTTON PRESSED");
  if (isScanning) {
    writeCmd("\x03"); // Ctrl+C to stop the scan
    setTimeout(() => {
      writeCmd("AT+PERIPHERAL"); // Set the dongle in Peripheral mode needed for advertising.
    }, 500); // Waiting half a bit to make sure each command will get through separately.
    isScanning = false;
    butGetData.removeAttribute("disabled");
    butScan.textContent = "Scan BLE Devices";
    
    return;
  }
  hibouDevices = [];
  writeCmd("AT+CENTRAL"); // Set the dongle in Central mode needed for scanning.
  setTimeout(() => {
    writeCmd("AT+GAPSCAN=2");
  }, 500); // Waiting half a bit to make sure each command will get through separately.

  butScan.textContent = "Stop Scanning...";
  butGetData.setAttribute("disabled", "true");
  log.classList.toggle("d-none", false);

  isScanning = true;
}

/**
 * @name clickGetData
 * Click handler for the 'Get Data' button.
 * Checks if a getData scan is already running by checking the boolean isGettingData.
 * If isGettingData = true: Stops scanning and goes back to peripheral mode, changes the button text and shows the scan button. Finally sets isGettingData = false.
 * If isGettingData = false: Goes into Central mode and starts scanning for ble devices data. Also changes button text and hides the scan button. Finally sets isGettingData = true.
 */
function clickGetData() {
  console.log("GET DATA BUTTON PRESSED");
  if (isGettingData) {
    writeCmd("\x03"); // Ctrl+C to stop the scan
    setTimeout(() => {
      writeCmd("AT+PERIPHERAL"); // Set the dongle in Peripheral mode needed for advertising.
    }, 500); // Waiting half a bit to make sure each command will get through separately.
    isGettingData = false;

    butScan.removeAttribute("disabled");
    butGetData.textContent = "Get Data";
    return;
  }
  writeCmd("AT+CENTRAL"); // Set the dongle in Central mode needed for scanning.
  setTimeout(() => {
   writeCmd("AT+FINDSCANDATA=FF5B07"); // Will just scan for adv data that contains 'FF5B07' which is the tag for Manufaturing Specific Data (FF) and our Company ID (5B07).
  }, 500); // Waiting half a bit to make sure each command will get through separately.

  butGetData.textContent = "Stop Getting Data...";
  butScan.setAttribute("disabled", "true");
  log.classList.toggle("d-none", false);

  isGettingData = true;

  
}


/**
 * @name readLoop
 * Reads data from the input stream and displays it on screen.
 */
async function readLoop() {
  let i=0;
  while (true) {
    i++;
    const { value, done } = await reader.read();
    if (value && (!isScanning && !isGettingData)) {
      log.textContent += value + "\n";
    }
    if (value && isScanning) {
      if(value === "SCAN COMPLETE") {
        isScanning = false;
        butScan.textContent = "Scan BLE Devices";
        log.textContent += "\n" +"Scan Done" + "\n";
        butGetData.removeAttribute("disabled");
        log.classList.toggle("d-none", false);
      }
      let lineValueArray = value.split(" ");
      if (lineValueArray[6] === "(HibouAIR)") {
        if(lineValueArray[2]) {
          hibouDevices.push("["+lineValueArray[2].replace("[1]", "") +"]");

        }
        log.textContent = "\n" + "hibouDevices found: " + hibouDevices.length + "\n";
      }
      if(value === "SCAN COMPLETE") {
        var select = document.getElementById("devices");
        hibouDevices.map(function(item){
          var option = document.createElement("option");
          option.value = item;
          option.text  = item;
          select.appendChild(option)
        });
      }

    }
    if (value && isGettingData) {
      if(value === "SCAN COMPLETE") {
        isGettingData = false;
        butGetData.textContent = "Get Data";
        log.textContent += "\n" +"Scan Done" + "\n";
        butScan.removeAttribute("disabled");
        log.classList.toggle("d-none", false);
        
      }
      let lineValueArray = value.split(" ");
      
        if (lineValueArray[0] ===   localStorage.getItem("selectedDevice") && lineValueArray[3] === "[ADV]:") {

          scannedSensorData = parseSensorData(lineValueArray[4]);
          outputData = ''
          if((i%30) === 0) {

            outputData += 'Time: '+new Date().getHours() + ":" + new Date().getMinutes() + ":" + new Date().getSeconds()+' '
            outputData += 'Pressure: '+scannedSensorData.p+' '
            outputData += 'Temperature: '+scannedSensorData.t+' '
            outputData += 'Humidity: '+scannedSensorData.h+' '
            outputData += 'ALS: '+scannedSensorData.als+' '
            outputData += 'PM1.0: '+scannedSensorData.pm1+' '
            outputData += 'PM2.5: '+scannedSensorData.pm25+' '
            outputData += 'PM10: '+scannedSensorData.pm10+' '
            //log.innerHTML  += "\n" + "SensorData= " + JSON.stringify(scannedSensorData) + "\n";
            log.innerHTML  += "\n" +outputData
          }
        
        }


    }
    if (done) {
      console.log("[readLoop] DONE", done);
      reader.releaseLock();
      break;
    }
  }
}

/**
 * @name writeCmd
 * Gets a writer from the output stream and send the command to the Smart USB Dongle 2.0.
 * @param  {string} cmd command to send to the Smart USB Dongle 2.0
 */
function writeCmd(cmd) {
  // Write to output stream
  const writer = outputStream.getWriter();
  console.log("[SEND]", cmd);

  writer.write(cmd);
  // Ignores sending carriage return if sending Ctrl+C
  if (cmd !== "\x03") {
    writer.write("\r"); // Important to send a carriage return after a command
  }
  writer.releaseLock();
}

/**
 * @name LineBreakTransformer
 * TransformStream to parse the stream into lines.
 */
class LineBreakTransformer {
  constructor() {
    // A container for holding stream data until a new line.
    this.container = "";
  }

  transform(chunk, controller) {
    // Handle incoming chunk
    this.container += chunk;
    const lines = this.container.split("\r\n");
    this.container = lines.pop();
    lines.forEach((line) => controller.enqueue(line));
  }

  flush(controller) {
    // Flush the stream.
    controller.enqueue(this.container);
  }
}

/**
 * @name toggleUIConnected
 * Changes the text on butConnect depending on the action it actually will preform in the current state.
 * @param  {boolean} connected true if connected, false if disconnected.
 */
function toggleUIConnected(connected) {
  let lbl = "Connect";
  if (connected) {
    lbl = "Disconnect";
    butGetData.removeAttribute("disabled");
    butScan.removeAttribute("disabled");
  }
  butScan.classList.toggle("disabled", !connected);
  butGetData.classList.toggle("disabled", !connected);
  butConnect.textContent = lbl;
}

/**
 * @name parseSensorData
 * Parse the data from advertising data string.
 * @param  {string} input advertising data string.
 * @returns {object ={sensorid:{string}, p:{int}, t:{int}, h:{int}, als:{int}, pm1:{int}, pm25:{int}, pm10:{int}}} 
 */
function parseSensorData(input) {
  let counter = 13;
  if (input.includes("5B070503")) {
    counter = 17;
  }
  let sensorData = {
    sensorid:
      input[counter + 1] +
      input[counter + 2] +
      input[counter + 3] +
      input[counter + 4] +
      input[counter + 5] +
      input[counter + 6],
    p:
      parseInt(
        input[counter + 13] +
          input[counter + 14] +
          input[counter + 11] +
          input[counter + 12],
        16
      ) / 10,
    t:
      parseInt(
        input[counter + 17] +
          input[counter + 18] +
          input[counter + 15] +
          input[counter + 16],
        16
      ) / 10,
    h:
      parseInt(
        input[counter + 21] +
          input[counter + 22] +
          input[counter + 19] +
          input[counter + 20],
        16
      ) / 10,
      voc:
      parseInt(
        input[counter + 25] +
          input[counter + 26] +
          input[counter + 23] +
          input[counter + 24],
        16
      ) / 10,
    als: parseInt(
      input[counter + 9] +
        input[counter + 10] +
        input[counter + 7] +
        input[counter + 8],
      16
    ),
    pm1:
      parseInt(
        input[counter + 29] +
          input[counter + 30] +
          input[counter + 27] +
          input[counter + 28],
        16
      ) / 10,
    pm25:
      parseInt(
        input[counter + 33] +
          input[counter + 34] +
          input[counter + 31] +
          input[counter + 32],
        16
      ) / 10,
    pm10:
      parseInt(
        input[counter + 37] +
          input[counter + 38] +
          input[counter + 35] +
          input[counter + 36],
        16
      ) / 10}
  return sensorData
}

Steps to run the script

  • Clone the git repository .git clone https://github.com/smart-sensor-devices-ab/get_rea…
  • Connect the Bleuio dongle to your computer.
  • Open index.html file
  • Click connect and wait for the device to load on your com port.
  • Select your com port.
  • Scan for BLE devices. (this script only scan for Hibou Devices . You can change the manufacturer value at script.js file)
  • Select device and start getting data. You will see real time value on the screen.
  • Click on stop getting data to stop the script.

Output

Here is the output of the script.

Follow the video for better understanding

Share this post on :

Bluetooth Low Energy: What this technology has in store for the future

The term IoT or Internet of Things, is bringing new technologies to transform and make the world in the era of connectivity. The IoT says that everything is connected and Bluetooth has made it much easier to work.

There are several names: Bluetooth Smart, Bluetooth 4.0+ and BLE (Bluetooth Low Energy). We can say that BLE is the friendliest version in terms of application and Bluetooth power. which helps mobility industry an easy connection in the world of applications.

Have you noticed that when you start a scan on your cell phone’s Bluetooth connection, you find several devices around, including the name (ID) of these solutions, without using any password, without making any pairing? And these devices can work simply with small batteries and with a long duration.

Unlike traditional Bluetooth that was created to transmit long files, such as music, photos or other media, consuming a lot of energy, on the other hand, BLE transmits little information in a short time, with minimum consumption of energy.

What benefits can this bring to our security world?

Specialists mentioned that, in 2025, everything will be connected; for example, an intrusion sensor will be connected to the camera and door devices. 

And with BLE that future is already knocking on our doors, we already have smart locks that allow opening through cellular devices, we can also control window opening, automate gates, and control other devices, all safely, with protected and extremely friendly way. This technology will replace wires in the future. The information will be transmitted from device to device, forming a large network, with alternative routes and redundancies.

We will increasingly have the use of “do it yourself”, where the user will install everything necessary, configure (the system will be self-configurable), in addition to monitoring everything through tablets and cell phones.

Smart Sensor Devices has developed an amazing device called BleuIO to create BLE application easily. It has an integrated ready to use software that enables the user to easily create new BLE applications with simple AT-Commands scripts. You can update the firmware or flash your own applications with the integrated bootloader of the dongle.

Share this post on :

Collect and export data from bluetooth devices

This project will show how to collect real-time BLE data and export as csv using web browser.

For this project, I am using Bluetooth Low Energy USB dongle called BlueIO, which will act as a central device to retrieve data. Hibou Air Quality Monitor which will serve as a peripheral device to transmit the data. The script is simple to use and can be used for other purposes such as store the data into database or cloud.

Things we need:

Before we start

The article assumes you have some general knowledge of how Bluetooth Low Energy (BLE) work. Since the Chrome Serial specification on Google Chrome is not finalized yet, you will have to go to enable the highlighted flag, and restart Chrome.

open chrome://flags/#enable-experimental-web-platform-features in chrome browser. ​

In this example, we are going to use JavaScript + html (and some CSS for styling) to setup the BleuIO and quickly start scanning.

Steps:

  • Clone the git repository.
git clone https://github.com/smart-sensor-devices-ab/export_ble_data.git
  • Connect the Bleuio dongle to your computer.
  • Open index.html file
  • Click connect and wait for the device to load on your com port.
  • Select your com port.
  • Scan for BLE devices. (this script only scan for Hibou Devices. You can change the manufacturer value at script.js file)
  • Select device and start getting data. You will get real time value on the screen.
  • Once you click on stop getting data. you will see all the data showing on a table between the time period. You can view data or export in csv.

Project Video

Share this post on :

Smart Sensor Devices Announces Global Distribution Agreement With Digi-Key Electronics

Smart Sensor Devices announces that its Bluetooth low energy USB dongle called BleuIO will be available for immediate shipment worldwide through Digi-Key Electronics, a global electronic components distributor, as a result of a new distribution agreement.

The product is listed on Digikey and available for purchase.

“Through our distribution partnership with Digi-Key we are making it easier for the maker community worldwide to quickly and reliably access our solutions and accelerate their BLE application development efforts,”

said Axel G. Hammar Founder & CEO of Smart Sensor Devices.

About BleuIO

This BleuIO is a Bluetooth low energy USB dongle that can be used to create new BLE 5.0 application in the fastest and easiest way. Just use the AT Commands available on the device. Details about the AT commands can be found on getting started guide which will help anyone make a fast peripheral or central application (or both) without having to develop a single line of embedded code. 

It is a fully integrated solution, providing MCU and Bluetooth radio in one chip, based on Dialog Semiconductor latest Bluetooth chip DA14683. The FLASH based device permits field or boot upgradable, while the application is stored on FLASH memory. Custom settings can also be stored on FLASH memory or OTP for higher integrity. It supports Windows 10, Linux and macOS.

About Digi-Key Electronics

Digi-Key Electronics, headquartered in Thief River Falls, Minn., USA, is an authorized global, full-service distributor of electronic components, offering more than five million products, with over 1.3 million in stock and available for immediate shipment, from over 650 quality name-brand manufacturers. Digi-Key also offers a wide variety of online resources such as EDA and design tools, datasheets, reference designs, instructional articles and videos, multimedia libraries, and much more. Technical support is available 24/7 via email, phone and webchat. Additional information and access to Digi-Key’s broad product offering can be found by visiting www.digikey.com.

Share this post on :

BleuIO Javascript library available on npm

Bleuio Javascript library is available on npm(Node Package Manager).

Now you can easily access all the BleuIO functions from web browser.

Before starting to install our library, make sure you have Chrome 78 or later version installed on your system. You will also need to enable the #enable-experimental-web-platform-features flag in chrome://flags.
To do that, Open chrome://flags/#enable-experimental-web-platform-features in Google Chrome browser and enable this feature.

You can use chrome.serial to create web apps that can access the BleuIO AT commands easily using this library.

Now Install the library by running

npm i bleuio

npm automatically downloads and installs the most recent library on your system in the correct directory. To check that the installation went well, follow the simple readme file from

https://www.npmjs.com/package/bleuio

Follow this video if you find any difficulties.

Good luck on creating amazing Bluetooth Low Energy application using BleuIO

Share this post on :

BleuIO library available on PyPI

Bleuio Python library is available on PyPI (the Python Package Index) to simplify the life of Python developers.

Before starting to install our library, make sure your you have the latest python installed on your system.

If you have never installed a library from PyPI, you must install the pip tool enabling you to download and install a PyPI package. There are several methods which are described on this page.

Now Install the library by running

pip install bleuio

Easy, right? pip automatically downloads and installs the most recent library on your system in the correct directory. To check that the installation went well, you can launch a Python interpreter and run the following lines:

from bleuio_lib.bleuio_funcs import BleuIo
my_dongle = BleuIo()
my_dongle.start_daemon()
print(my_dongle.ati())

Good luck on creating amazing Bluetooth Low Energy application using BleuIO

Share this post on :

Sending and receiving data between two Bluetooth dongle

In this project, we will demonstrate how two Bluetooth dongles can be paired together and share data between them. To pair two Bluetooth dongle, one of them needs to be configured as Central and other needs to be configured as Peripheral. We already wrote a Python script that sends data back and forth between dongles.

In short :

  1. One dongle will take on the Central role and the other will take on the Peripheral role.
  2. Then they will connect to each other.
  3. The Central dongle will then start off sending a message; “Echo”.
  4. The Peripheral dongle will then receive the message and send it back to the Central dongle which in turn will receive it and send it back and so forth until the script is stopped.

Device Required:

  • Laptop
  • BleuIO – BLE Dongle

Software Tools Required : 

Step 1 : Install Python on both laptop and connect a USB Dongle on each of them.

Step 2 : For a quick setup, copy the following script and save it on your local directory as sps_example.py . You can also get the source code from our GITHUB PAGE.

Step 3: Change the COM-port in the script on both computers to match the ones your dongles is actually connected to. For the one that will be set up as the Central dongle you will also need to change the target_dongle_mac_address variable to the MAC address of your Peripheral dongle. You can get the MAC address by scanning for the other dongle while it’s advertising. To learn more about how to scan check out our scanning tutorial.

Step 4: Open up the command prompt, on both computers, in the directory where the script is located. Start the script by typing python sps_example.py and press Enter.

Step 5: You should now be prompted to enter 1 or 2 depending on what role the dongle should have. Set one as Peripheral and the other as Central. It is advisable to setup the Peripheral first as the Central will need someone to connect to.

You should now see in the terminal how the dongles send and recieve data to and from each other.

The script will run until you stop it.

And that is an example of how we can send data between two dongles. If you want to stop the script, you can simply press control C.

Full source also available on GitHub.

Project Video

Share this post on :

Plotting real-time graph from Bluetooth 5.0 device to Google Chrome

Hey there,

Here is a tutorial on how to plot a real-time graph of values to Google Chrome from Hibou Air Quality Monitor using BleuIO and javascript library chartjs.

For this project, I am using Bluetooth Low Energy USB dongle called BlueIO, which will act as a central device to retrieve data. Hibou Air Quality Monitor which will serve as a peripheral device to transmit the data. The script is simple to use and can be used for other purposes such as showing real-time air quality data; temperate, humidity, pressure, particle matters etc.

The data could be later stored on a cloud service of choice.

Things we need:

Before we start

The article assumes you have some general knowledge of how Bluetooth Low Energy (BLE) work. Since the Chrome Serial specification on Google Chrome is not finalized yet, you will have to go to enable the highlighted flag, and restart Chrome.

open chrome://flags/#enable-experimental-web-platform-features in chrome browser. ​

In this example, we are going to use JavaScript + html (and some CSS for styling) to setup the BleuIO and quickly start scanning.

Step 1:

Create a file called index.html and paste the following codes. This gives you basic layout of the page with connect, scan, and get data buttons.

<!DOCTYPE html>
<html lang="en">
<head>
<title>Smart Sensor Devices Web Bluetooth Example</title>
<meta charset="utf-8" />
<meta name="ssd" content="beaconexample" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="shortcut icon" type="image/png" href="images/favicon.png" />

<script>
// Redirect to HTTPS if HTTP is requested.
if (window.location.protocol === "http:") {
window.location.href = "https:" + window.location.href.substring(5);
}
</script>
<link rel="stylesheet" href="style.css" />
<link
rel="stylesheet"
href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css"
integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z"
crossorigin="anonymous"
/>


</head>
<body>
<div class="container">
<div
id="carouselExampleFade"
class="carousel slide carousel-fade"
data-ride="carousel"
>
<div class="carousel-inner">
<div class="carousel-item active">
<div class="row">
<div class="col-md-9 caption">
<img src="images/logo.png" />
<h1>Bluetooth® low energy adapter</h1>
<a
class="btn btn-info btn-lg"
href="https://www.bleuio.com/"
target="_blank"
>Learn More</a
>
</div>
<div class="col-md-3">
<img
src="images/bleuIO_white_withlogo.png"
class="d-block w-100"
alt="..."
/>
</div>
</div>
</div>
<div class="carousel-item">
<div class="row">
<div class="col-md-9 caption">
<img src="images/logo.png" />
<h1>Create your own BLE applications</h1>
<a
class="btn btn-info btn-lg"
href="https://www.bleuio.com/"
target="_blank"
>Learn More</a
>
</div>
<div class="col-md-3">
<img
src="images/bleuIO_black_withlogo.png"
class="d-block w-100"
alt="..."
/>
</div>
</div>
</div>
<div class="carousel-item">
<div class="row">
<div class="col-md-9 caption">
<img src="images/logo.png" />
<h1>Quick, Innovative, Simple</h1>
<a
class="btn btn-info btn-lg"
href="https://www.bleuio.com/"
target="_blank"
>Learn More</a
>
</div>
<div class="col-md-3">
<img
src="images/bleuIO_black_withlogo.png"
class="d-block w-100"
alt="..."
/>
</div>
</div>
</div>
</div>
</div>
<!-- end carousel -->
</div>
<!-- end container -->
<div class="codesection">
<div class="container">
<h1>Web Sensor Plotter Example</h1>
<main class="main">
<div id="notSupported" class="hidden alert alert-danger">
Sorry, <b>Web Serial</b> is not supported on this device, make sure
you're running Chrome 78 or later and have enabled the
<code>#enable-experimental-web-platform-features</code> flag in
<code>chrome://flags</code> <br />
Open
<strong>
chrome://flags/#enable-experimental-web-platform-features</strong
>
in Google Chrome browser.
</div>

<br />
<button id="butConnect" type="button" class="btn btn-success">
Connect
</button>
<br /><br />
<div class="row">
<div class="col-md-3"><button id="butScan" type="button" disabled class="btn btn-primary">
Scan BLE Devices
</button></div>
<div class="col-md-3">
<select class="devices form-control" id="devices" onchange="getSelectedDevice(this)">
<option value="">Select a Device</option>
</select></div>
<div class="col-md-3"><button
id="butGetData"
type="button"
disabled
class="btn btn-primary"
>
Get Data
</button></div>
</div>


<br /><br />


<pre id="log" class="mt-5 d-none"></pre>

<div style="background: white;">
<canvas id="myChart"></canvas>
</div>


</main>
</div>
</div>
<div class="footer text-center mt-3">
Powered by <a href="https://www.bleuio.com/" target="_blank">BleuIO</a> .
A product of
<a href="http://smartsensordevices.com/" target="_blank"
>Smart Sensor Devices</a
>
</div>

<!-- end container -->
<!-- JS, Popper.js, and jQuery -->
<script
src="https://code.jquery.com/jquery-3.5.1.slim.min.js"
integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj"
crossorigin="anonymous"
></script>
<script
src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js"
integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN"
crossorigin="anonymous"
></script>
<script
src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"
integrity="sha384-B4gt1jrGC7Jh4AgTPSdUtOBvfO8shuf57BaghqFfPlYxofvL8/KUEfYiJOMMV+rV"
crossorigin="anonymous"
></script>
<script src="script.js" defer></script>

<script src="https://cdn.jsdelivr.net/npm/moment@2.24.0/min/moment.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js@2.8.0"></script>
<script src="https://cdn.jsdelivr.net/npm/hammerjs@2.0.8"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-zoom@0.7.7"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-streaming@1.8.0"></script>

</body>
</html>

Step 2:

Create a file called script.js and paste the following codes.

"use strict";

let port;
let reader;
let inputDone;
let outputDone;
let inputStream;
let outputStream;
let isScanning = false;
let isGettingData = false;
let hibouDevices = [];
let rightDevice = false;
let scannedSensorData = []
var chartColors = {
red: 'rgb(255, 99, 132)',
orange: 'rgb(255, 159, 64)',
yellow: 'rgb(255, 205, 86)',
green: 'rgb(75, 192, 192)',
blue: 'rgb(54, 162, 235)',
purple: 'rgb(153, 102, 255)',
grey: 'rgb(96, 125, 139)'
};
var color = Chart.helpers.color;
const log = document.getElementById("log");
const butConnect = document.getElementById("butConnect");
const butScan = document.getElementById("butScan");
const butGetData = document.getElementById("butGetData");

document.addEventListener("DOMContentLoaded", () => {
butScan.addEventListener("click", clickScan);
butGetData.addEventListener("click", clickGetData);
butConnect.addEventListener("click", clickConnect);
const notSupported = document.getElementById("notSupported");
notSupported.classList.toggle("hidden", "serial" in navigator);
});
function randomScalingFactor() {
return (Math.random() > 0.5 ? 1.0 : -1.0) * Math.round(Math.random() * 100);
}
function onRefresh(chart) {
chart.config.data.datasets[0].data.push({
x: Date.now(),
y: scannedSensorData.p
});
chart.config.data.datasets[1].data.push({
x: Date.now(),
y: scannedSensorData.t
});
chart.config.data.datasets[2].data.push({
x: Date.now(),
y: scannedSensorData.als
});
chart.config.data.datasets[3].data.push({
x: Date.now(),
y: scannedSensorData.voc
});
chart.config.data.datasets[4].data.push({
x: Date.now(),
y: scannedSensorData.h
});
chart.config.data.datasets[5].data.push({
x: Date.now(),
y: scannedSensorData.pm25
});
}
var config = {
type: 'line',
data: {
datasets: [ {
label: 'Pressure',
backgroundColor: color(chartColors.red).alpha(0.5).rgbString(),
borderColor: chartColors.red,
fill: false,
cubicInterpolationMode: 'monotone',
data: []
},
{
label: 'Temperature',
backgroundColor: color(chartColors.blue).alpha(0.5).rgbString(),
borderColor: chartColors.blue,
fill: false,
cubicInterpolationMode: 'monotone',
data: []
},
{
label: 'Light',
backgroundColor: color(chartColors.orange).alpha(0.5).rgbString(),
borderColor: chartColors.orange,
fill: false,
cubicInterpolationMode: 'monotone',
data: []
},
{
label: 'VOC',
backgroundColor: color(chartColors.grey).alpha(0.5).rgbString(),
borderColor: chartColors.grey,
fill: false,
cubicInterpolationMode: 'monotone',
data: []
},
{
label: 'Humidity',
backgroundColor: color(chartColors.green).alpha(0.5).rgbString(),
borderColor: chartColors.green,
fill: false,
cubicInterpolationMode: 'monotone',
data: []
},
{
label: 'PM 2.5',
backgroundColor: color(chartColors.purple).alpha(0.5).rgbString(),
borderColor: chartColors.purple,
fill: false,
cubicInterpolationMode: 'monotone',
data: []
}]
},
options: {
title: {
display: true,
text: 'Hibou - Realtime data'
},

scales: {
xAxes: [{
type: 'realtime',
realtime: {
duration: 20000,
refresh: 2000,
delay: 2000,
ttl:1000000,
onRefresh: onRefresh
},
gridLines: {
display:false
}
}],
yAxes: [{
scaleLabel: {
display: true,
labelString: 'value'
}
}]
},
plugins: {
zoom: {
// Container for zoom options
zoom: {
// Boolean to enable zooming
enabled: true,

// Zooming directions. Remove the appropriate direction to disable
// Eg. 'y' would only allow zooming in the y direction
mode: 'x',
}
}
},
tooltips: {
mode: 'nearest',
intersect: false
},
hover: {
mode: 'nearest',
intersect: false
}
}
};
var colorNames = Object.keys(chartColors);

/**
* @name connect
* Opens a Web Serial connection to a serial device such as a Smart USB Dongle 2.0 and sets up the input and
* output stream.
*/
async function connect() {
// - Request a port and open a connection.
port = await navigator.serial.requestPort();
// - Wait for the port to open.
await port.open({ baudRate: 9600 });

const encoder = new TextEncoderStream();
outputDone = encoder.readable.pipeTo(port.writable);
outputStream = encoder.writable;

let decoder = new TextDecoderStream();
inputDone = port.readable.pipeTo(decoder.writable);
inputStream = decoder.readable.pipeThrough(
new TransformStream(new LineBreakTransformer())
);

reader = inputStream.getReader();
readLoop().catch((error) => {
toggleUIConnected(false);
port = null;
log.textContent = "Dongle Disconnected!";
});
}

/**
* @name disconnect
* Closes the Web Serial connection.
*/
async function disconnect() {
// Close the input stream (reader).
if (reader) {
await reader.cancel();
await inputDone.catch(() => {});
reader = null;
inputDone = null;
}
// Close the output stream.
if (outputStream) {
await outputStream.getWriter().close();
await outputDone;
outputStream = null;
outputDone = null;
}
// Close the port.
await port.close();
port = null;
log.textContent = "Dongle Disconnected!";
}

/**
* @name clickConnect
* Click handler for the connect/disconnect button.
* Checks if port != null
* If true: Checks if any beacons is advertising or scans are running and stops the advertsing or scan if so. Then runs disconnect() and set toggleUIConnected to false.
* if false: Runs connect() then set toggleUIConnected to true.
*/
async function clickConnect() {
log.textContent = "";
if (port) {

// If disconnected while scanning the dongle will restart
if (isScanning) {
writeCmd("\x03");
butScan.textContent = "Scan BLE Devices";
isScanning = false;
}
await disconnect();
toggleUIConnected(false);
return;
}
await connect();
toggleUIConnected(true);
}

function getSelectedDevice(selectObject) {
var selectedDevice = selectObject.value;
localStorage.setItem("selectedDevice", selectedDevice);
}

/**
* @name clickScan
* Click handler for the Scan button.
* Checks if a scan is already running by checking the boolean isScanning.
* If isScanning = true: Stops scanning and goes back to peripheral mode, changes the button text and shows the beacon buttons. Finally sets isScanning = false.
* If isScanning = false: Goes into Central mode and starts scanning for ble devices. Also changes button text and hides the beacon buttons. Finally sets isScanning = true.
*/
function clickScan() {
console.log("SCAN BUTTON PRESSED");
if (isScanning) {
writeCmd("\x03"); // Ctrl+C to stop the scan
setTimeout(() => {
writeCmd("AT+PERIPHERAL"); // Set the dongle in Peripheral mode needed for advertising.
}, 500); // Waiting half a bit to make sure each command will get through separately.
isScanning = false;
butGetData.removeAttribute("disabled");
butScan.textContent = "Scan BLE Devices";

return;
}
hibouDevices = [];
writeCmd("AT+CENTRAL"); // Set the dongle in Central mode needed for scanning.
setTimeout(() => {
writeCmd("AT+GAPSCAN=3");
}, 500); // Waiting half a bit to make sure each command will get through separately.

butScan.textContent = "Stop Scanning...";
butGetData.setAttribute("disabled", "true");
log.classList.toggle("d-none", false);

isScanning = true;
}

/**
* @name clickGetData
* Click handler for the 'Get Data' button.
* Checks if a getData scan is already running by checking the boolean isGettingData.
* If isGettingData = true: Stops scanning and goes back to peripheral mode, changes the button text and shows the scan button. Finally sets isGettingData = false.
* If isGettingData = false: Goes into Central mode and starts scanning for ble devices data. Also changes button text and hides the scan button. Finally sets isGettingData = true.
*/
function clickGetData() {
console.log("GET DATA BUTTON PRESSED");
if (isGettingData) {
writeCmd("\x03"); // Ctrl+C to stop the scan
setTimeout(() => {
writeCmd("AT+PERIPHERAL"); // Set the dongle in Peripheral mode needed for advertising.
}, 500); // Waiting half a bit to make sure each command will get through separately.
isGettingData = false;
if(window.myChart){
window.myChart.destroy();
}
butScan.removeAttribute("disabled");
butGetData.textContent = "Get Data";
return;
}
writeCmd("AT+CENTRAL"); // Set the dongle in Central mode needed for scanning.
setTimeout(() => {
writeCmd("AT+FINDSCANDATA=FF5B07"); // Will just scan for adv data that contains 'FF5B07' which is the tag for Manufaturing Specific Data (FF) and our Company ID (5B07).
}, 500); // Waiting half a bit to make sure each command will get through separately.

butGetData.textContent = "Stop Getting Data...";
butScan.setAttribute("disabled", "true");
log.classList.toggle("d-none", false);

isGettingData = true;
var ctx = document.getElementById('myChart').getContext('2d');
window.myChart = new Chart(ctx, config);

}

/**
* @name readLoop
* Reads data from the input stream and displays it on screen.
*/
async function readLoop() {
while (true) {
const { value, done } = await reader.read();
if (value && (!isScanning && !isGettingData)) {
log.textContent += value + "\n";
}
if (value && isScanning) {
if(value === "SCAN COMPLETE") {
isScanning = false;
butScan.textContent = "Scan BLE Devices";
log.textContent += "\n" +"Scan Done" + "\n";
butGetData.removeAttribute("disabled");
log.classList.toggle("d-none", false);
}
let lineValueArray = value.split(" ");
if (lineValueArray[6] === "(HibouAIR)") {
if(lineValueArray[2]) {
hibouDevices.push("["+lineValueArray[2].replace("[1]", "") +"]");

}
log.textContent = "\n" + "hibouDevices found: " + hibouDevices.length + "\n";
}
if(value === "SCAN COMPLETE") {
var select = document.getElementById("devices");
hibouDevices.map(function(item){
var option = document.createElement("option");
option.value = item;
option.text = item;
select.appendChild(option)
});
}

}
if (value && isGettingData) {
if(value === "SCAN COMPLETE") {
isGettingData = false;
butGetData.textContent = "Get Data";
log.textContent += "\n" +"Scan Done" + "\n";
butScan.removeAttribute("disabled");
log.classList.toggle("d-none", false);
}

let lineValueArray = value.split(" ");
if(!rightDevice) { // The advdata is spread on two lines, the first identifies it,
if (lineValueArray[0] === localStorage.getItem("selectedDevice") && lineValueArray[3] === "[ADV]:") {
rightDevice = true;
//console.log("CONSOLE.LOG= "+value);
}
} else if (rightDevice) { // Second line contains the actual advdata string we need to parse
scannedSensorData = parseSensorData(lineValueArray[1]);
log.textContent = "\n" + "SensorData= " + JSON.stringify(scannedSensorData) + "\n";
//console.log(scannedSensorData.p)
//console.log("CONSOLE.LOG= "+value);
rightDevice = false;
}

}
if (done) {
console.log("[readLoop] DONE", done);
reader.releaseLock();
break;
}
}
}
/**
* @name writeCmd
* Gets a writer from the output stream and send the command to the Smart USB Dongle 2.0.
* @param {string} cmd command to send to the Smart USB Dongle 2.0
*/
function writeCmd(cmd) {
// Write to output stream
const writer = outputStream.getWriter();
console.log("[SEND]", cmd);

writer.write(cmd);
// Ignores sending carriage return if sending Ctrl+C
if (cmd !== "\x03") {
writer.write("\r"); // Important to send a carriage return after a command
}
writer.releaseLock();
}

/**
* @name LineBreakTransformer
* TransformStream to parse the stream into lines.
*/
class LineBreakTransformer {
constructor() {
// A container for holding stream data until a new line.
this.container = "";
}

transform(chunk, controller) {
// Handle incoming chunk
this.container += chunk;
const lines = this.container.split("\r\n");
this.container = lines.pop();
lines.forEach((line) => controller.enqueue(line));
}

flush(controller) {
// Flush the stream.
controller.enqueue(this.container);
}
}

/**
* @name toggleUIConnected
* Changes the text on butConnect depending on the action it actually will preform in the current state.
* @param {boolean} connected true if connected, false if disconnected.
*/
function toggleUIConnected(connected) {
let lbl = "Connect";
if (connected) {
lbl = "Disconnect";
butGetData.removeAttribute("disabled");
butScan.removeAttribute("disabled");
}
butScan.classList.toggle("disabled", !connected);
butGetData.classList.toggle("disabled", !connected);
butConnect.textContent = lbl;
}

/**
* @name parseSensorData
* Parse the data from advertising data string.
* @param {string} input advertising data string.
* @returns {object ={sensorid:{string}, p:{int}, t:{int}, h:{int}, als:{int}, pm1:{int}, pm25:{int}, pm10:{int}}}
*/
function parseSensorData(input) {
let counter = 13;
if (input.includes("5B070503")) {
counter = 17;
}
let sensorData = {
sensorid:
input[counter + 1] +
input[counter + 2] +
input[counter + 3] +
input[counter + 4] +
input[counter + 5] +
input[counter + 6],
p:
parseInt(
input[counter + 13] +
input[counter + 14] +
input[counter + 11] +
input[counter + 12],
16
) / 10,
t:
parseInt(
input[counter + 17] +
input[counter + 18] +
input[counter + 15] +
input[counter + 16],
16
) / 10,
h:
parseInt(
input[counter + 21] +
input[counter + 22] +
input[counter + 19] +
input[counter + 20],
16
) / 10,
voc:
parseInt(
input[counter + 25] +
input[counter + 26] +
input[counter + 23] +
input[counter + 24],
16
) / 10,
als: parseInt(
input[counter + 9] +
input[counter + 10] +
input[counter + 7] +
input[counter + 8],
16
),
pm1:
parseInt(
input[counter + 29] +
input[counter + 30] +
input[counter + 27] +
input[counter + 28],
16
) / 10,
pm25:
parseInt(
input[counter + 33] +
input[counter + 34] +
input[counter + 31] +
input[counter + 32],
16
) / 10,
pm10:
parseInt(
input[counter + 37] +
input[counter + 38] +
input[counter + 35] +
input[counter + 36],
16
) / 10}
return sensorData
}
// readLoop()
// .then((data) => { console.log(data)})
window.onload = function() {
//var ctx = document.getElementById('myChart').getContext('2d');
//window.myChart = new Chart(ctx, config);
};

This js has all the codes and uses Chrome Serial to

· Connect to your BleuIO Bluetooth dongle,

· Scan nearby devices using the AT-commands

· Receive and store the data

· Generate real time charts from the data.

All the functions used in the script are well commented for understanding.

Step 3:

Now create a style.css on the root folder and paste the following codes. They will give your site a clean look.

.hidden {
display: none !important;
}

.mb-button {
align-self: center;
background-color: #666;
border-radius: 100%;
box-shadow: 0 4px 5px 0 rgba(0, 0, 0, 0.14), 0 2px 9px 1px rgba(0, 0, 0, 0.12),
0 4px 2px -2px rgba(0, 0, 0, 0.2);
height: 30px;
width: 30px;
}

.pressed {
background-color: #d81b60;
box-shadow: inset 0px 0px 5px #c1c1c1;
outline: none;
}
.caption {
margin-top: 100px;
}
pre {
padding: 20px;
color: #ffffff !important;
background-color: #222;
white-space: pre;
text-shadow: 0 1px 0 #000;
border-radius: 5px;
border-bottom: 1px solid #555;
box-shadow: 0 1px 5px rgba(0, 0, 0, 0.4) inset,
0 0 20px rgba(0, 0, 0, 0.2) inset;
font: 16px/24px "Courier New", Courier, "Lucida Sans Typewriter",
"Lucida Typewriter", monospace;
}
.codesection {
background: #0d4c8d;
color: white;
padding: 20px 0;
min-height: 500px;
}

You can download the complete script from here.https://github.com/smart-sensor-devices-ab/Web-Hibou-Sensor-Plotter.git

Step 4:

Once you have everything ready, open the index.html file. Your page should look like this

Image for post

Now connect your BleuIO device to your computer and click connect. Wait for your device to show up on the port screen.

Select your device and press connect.

Image for post

Once the device is connected, you can scan for nearby devices. For this project I have added a filter so that it will only look for Hibou BLE devices. You will see a list of devices on the dropdown menu. We have a small console on the page that shows scan status and number of devices found. Now you can select a device from the dropdown and start getting data from the device.

Image for post

Once you click on get data after selecting a device, it will start to generate graphs using chartjs using real-time data from Hibou Air Quality Monitor.

In my script I am showing Pressure, Temperature, ALS, VOC, Humidity, PM2.5

You can customize your own chart by updating values. All the codes for charts are available on script.js

Done

Here is our real time chart showing on web browser.

Image for post

Follow this video if you find any difficulties.

Share this post on :