Chat with HibouAir using BleuIO: Smart Air Quality Analysis with Google Technologies 

Indoor air quality is crucial for maintaining a healthy living and working environment. HibouAir is a powerful air quality monitoring device that provides real-time data on CO2 levels, temperature, humidity, and air pressure.  

This project demonstrates how BleuIO enables communication with HibouAir, allowing real-time environmental data to be retrieved, while Google’s Gemma model processes and analyzes the data to provide meaningful, easy-to-understand responses through a chat interface.

Users can interact with HibouAir’s smart assistant and ask questions such as:

  • “What is my room temperature?”
  • “What is the humidity level?”
  • “How is the air quality in my room?”

The system retrieves real-time sensor data from HibouAir and provides contextual recommendations based on environmental conditions.

By leveraging Google’s lightweight Gemma model, this project ensures efficient and intelligent analysis of air quality data, making it accessible for various applications, from smart homes to research.

Features of This Project

  • Live CO2, Temperature, Humidity, and Pressure Monitoring
  • Google-Powered Analysis for Meaningful Insights
  • Conversational Chat Interface (“Chat with HibouAir”)
  • Completely Local – No Internet Required
  • Lightweight & Efficient Processing with Gemma

Step-by-Step Guide: How It Works

Install Required Software

We need:

  • BleuIO for Bluetooth communication.
  • Ollama to run Google’s Gemma model locally.
  • HibouAir for air quality monitoring
  • Gemma for efficient data analysis and meaningful responses.

Install Python Dependencies

pip install flask bleuio

Install Ollama (For Local Processing)
For Mac/Linux:

curl -fsSL https://ollama.com/install.sh | sh

For Windows, download it from Ollama’s official site.

Install Google’s Gemma Model

ollama pull gemma

Why Gemma? What Are the Alternatives?

For this project, we chose Gemma, a lightweight, open-source model developed by Google, because it aligns with Google’s ecosystem and provides efficient, real-time insights for environmental data.

Why Use Google’s Gemma?

  • Optimized for Efficiency – Runs well on low-power machines without requiring cloud resources.
  • Google-Backed & Open Source – Developed by Google DeepMind, ensuring high-quality performance with full transparency.
  • No API Costs & Fully Local – No need for an internet connection or paid APIs, making it a cost-effective solution.
  • Designed for Meaningful Responses – Processes real-time air quality data and provides insightful, structured feedback.

Other Model Alternatives

  • Phi-2 – Even lighter but lacks detailed contextual understanding.
  • Llama3 – More powerful but requires more computational resources.
  • Mistral – Previously used, efficient, but not part of the Google ecosystem.

Connecting HibouAir via Bluetooth (BleuIO)

HibouAir continuously broadcasts CO2, temperature, humidity, and pressure via Bluetooth. We use BleuIO to scan and retrieve these values in real-time.

Setting Up the Chat Interface

Users can type questions like:

  • “What is the temperature?”
  • “What is my CO2 level?”
  • “How is the air quality?”

The system fetches real-time sensor data from HibouAir and provides Google-powered analysis and recommendations.

app.py (Backend)

  • This script:
  • Scans for HibouAir data
  • Extracts CO2, temperature, humidity, and pressure
  • Uses Google’s Gemma model for intelligent responses
  • Serves the chat interface via Flask
def chat():
    """Handles user input, fetches air quality data if needed, and returns response."""
    user_input = request.json.get("message", "").lower()

    with Manager() as manager:
        air_data = manager.dict({"co2": 0, "pressure": 0, "temperature": 0, "humidity": 0})
        process = Process(target=scan_for_air_quality_process, args=(air_data,))
        process.start()
        process.join()

        # Check for specific sensor queries
        if "temperature" in user_input:
            if air_data["temperature"] > 0:
                response = f"The current temperature in your room is {air_data['temperature']}°C."
            else:
                response = "⚠️ Unable to retrieve temperature data. Ensure HibouAir is in range."
            return jsonify({"response": response})

        elif "humidity" in user_input:
            if air_data["humidity"] > 0:
                response = f"The current humidity level in your room is {air_data['humidity']}%."
            else:
                response = "⚠️ Unable to retrieve humidity data. Ensure HibouAir is in range."
            return jsonify({"response": response})

        elif "pressure" in user_input:
            if air_data["pressure"] > 0:
                response = f"The current air pressure in your room is {air_data['pressure']} hPa."
            else:
                response = "⚠️ Unable to retrieve air pressure data. Ensure HibouAir is in range."
            return jsonify({"response": response})
        elif "co2" in user_input:
            if air_data["co2"] > 0:
                response = f"The current CO2 in your room is {air_data['co2']} ppm."
            else:
                response = "⚠️ Unable to retrieve co2 data. Ensure HibouAir is in range."
            return jsonify({"response": response})

        elif "air quality" in user_input :
            if air_data["co2"] > 0:
                prompt = (
                    f"The current air quality readings are:\n"
                    f"- CO2 Level: {air_data['co2']} ppm\n"
                    f"- Temperature: {air_data['temperature']}°C\n"
                    f"- Humidity: {air_data['humidity']}%\n"
                    f"- Pressure: {air_data['pressure']} hPa\n"
                    f"First give all the data. This is my room data. Give me short analysis on this data. and give me short suggestions "
                )
            else:
                return jsonify({"response": "⚠️ Unable to retrieve air quality data. Ensure HibouAir is in range and try again."})
        else:
            # Normal response for non-air quality queries
            prompt = user_input

    ai_response = subprocess.run(
        ["ollama", "run", "gemma", prompt],
        capture_output=True,
        text=True
    ).stdout.strip()

    return jsonify({"response": ai_response})

Get full source code from Github

index.html (Frontend – Chat Interface)

<div class="card">
        <div class="card-header">Chat with HibouAir</div>
        <div class="card-body">
          <div
            id="chatbox"
            class="border rounded p-3"
            style="height: 400px; overflow-y: auto; background: #f8f9fa"
          >
            <div class="alert alert-secondary">
              <b>HibouAir:</b> Ask me about air quality!
            </div>
          </div>
        </div>
      </div>

Expected Responses

You: “What is my CO2 level?”
HibouAir: “The current CO2 level is 850 ppm.”

You: “What is the air quality in my room?”
HibouAir:

CO2 Level: 850 ppm  
Temperature: 24°C  
Humidity: 55%  
Pressure: 1010 hPa  
Based on these readings, the air quality is good.

Using the Chat Interface

The web interface allows users to ask about specific values like temperature, humidity, pressure, CO2, or overall air quality.

Output

Get the Source Code

This project is open-source! You can access the full code and modify it for your own needs.

👉 [GitHub Repository]

This project showcases how HibouAir and BleuIO can be integrated to provide real-time air quality analysis in a way that is easy to understand. Instead of requiring users to interpret raw sensor data, the chat interface translates complex air quality values into clear, meaningful insights. Powered by Google’s Gemma model, it delivers simple and actionable responses—helping users understand their indoor air quality without needing to be experts.  

Share this post on :

Using BleuIO with Angular & TypeScript via Web Serial API

In modern web development, Bluetooth Low Energy (BLE) integration is becoming increasingly important for IoT applications. With the Web Serial API, we can now directly communicate with hardware devices like BleuIO using just a browser, eliminating the need for native drivers or middleware.

In this tutorial, we will demonstrate how to use Angular 19 and TypeScript to communicate with a BleuIO USB Dongle via Web Serial API. We will build a simple web application that connects to BleuIO, sends AT commands, and displays responses in real time.

This approach is beneficial because:

  • No additional backend services are required—everything runs directly in the browser.
  • Simplicity—Web Serial API allows communication with serial devices in JavaScript/TypeScript without native code.
  • Cross-platform compatibility—Works on any OS with Google Chrome or Microsoft Edge.

Why Use Angular & TypeScript?

Angular is a powerful framework for building scalable, maintainable, and modern web applications. TypeScript, which is the foundation of Angular, brings type safety and better tooling, making development smoother and reducing runtime errors.

By using Angular 19, we ensure that our application is built on the latest and most optimized framework version, taking advantage of standalone components and improved performance features.

Setting Up the Angular Project

To get started, ensure you have Node.js and Angular CLI installed.

Create an Angular Project

Run the following command to generate a new Angular 19 project:

new angular-bleuio --strict
cd angular-bleuio

Choose No for routing and CSS as the styling option.

add Bootstrap to styles.css:

@import url('https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css');

This ensures our application has a clean, professional UI.

Building the BleuIO Web Serial Connection

We will now implement the core functionality:

  1. Connect to BleuIO via Web Serial API.
  2. Send AT commands and receive responses.
  3. Display formatted responses using delimiters.

Creating the Angular Component

In src/app/app.component.ts, replace the contents with:

import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';

@Component({
  selector: 'app-root',
  standalone: true, //
  imports: [FormsModule],
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
})
export class AppComponent {
  port: any | undefined;
  writer: WritableStreamDefaultWriter | undefined;
  reader: ReadableStreamDefaultReader | undefined;
  command: string = '';
  response: string = 'Not connected.';

  async connectToBleuIO() {
    try {
      if (!('serial' in navigator)) {
        this.response = 'Web Serial API not supported in this browser.';
        return;
      }

      this.port = await (navigator as any).serial.requestPort();
      await this.port.open({ baudRate: 115200 });

      this.response = 'Connected to BleuIO.';
      this.writer = this.port.writable?.getWriter();
      this.reader = this.port.readable?.getReader();

      this.readData();
    } catch (error) {
      console.error('Connection failed:', error);
      this.response = 'Failed to connect.';
    }
  }

  async sendCommand() {
    if (!this.port || !this.writer || !this.command) {
      this.response = 'Not connected or empty command.';
      return;
    }

    try {
      // Reset the response before sending a new command
      this.response = 'Waiting for response...';

      // Clear any old responses before sending new data
      if (this.reader) {
        await this.reader.cancel();
        this.reader.releaseLock();
        this.reader = this.port.readable?.getReader(); // Reinitialize reader
      }

      // Send the AT command
      const encoder = new TextEncoder();
      await this.writer.write(encoder.encode(this.command + '\r\n'));

      this.response = 'Command sent: ' + this.command;
      this.readData(); // Start reading the new response
    } catch (error) {
      console.error('Send error:', error);
      this.response = 'Error sending command.';
    }
  }

  async readData() {
    if (!this.reader) return;

    const decoder = new TextDecoder();
    let fullResponse = '';

    try {
      while (true) {
        const { value, done } = await this.reader.read();
        if (done) break;

        let textChunk = decoder.decode(value);
        fullResponse += textChunk; // Append new data to the response

        this.response = fullResponse
          .replace(/(\w)\.(\s[A-Z])/g, '$1.\n$2') // Add newline after full stops (but not inside numbers)
          .replace(
            /(BleuIO|Firmware Version|Peripheral role|Central role|Not Connected|Not Advertising)/g,
            '\n$1'
          ); // New line before keywords
      }
    } catch (error) {
      console.error('Read error:', error);
    }
  }
}

Creating the UI

Modify src/app/app.component.html:

<div class="container text-center mt-5">
  <h1 class="mb-4 text-primary">
    Angular & TypeScript: Connect to BleuIO via Serial port
  </h1>

  <!-- Connect Button -->
  <button class="btn btn-primary mb-3" (click)="connectToBleuIO()">
    Connect to BleuIO
  </button>

  <div class="input-group mb-3 w-50 mx-auto">
    <input
      type="text"
      [(ngModel)]="command"
      class="form-control"
      placeholder="Enter AT Command"
    />
    <button class="btn btn-success" (click)="sendCommand()">
      Send Command
    </button>
  </div>

  <h3 class="mt-4">Response:</h3>

  <div
    class="border p-3 w-75 mx-auto bg-light rounded shadow-sm"
    style="white-space: pre-line"
  >
    {{ response }}
  </div>
</div>

Update src/main.ts

Replace its contents with:

import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app/app.component';
import { importProvidersFrom } from '@angular/core';
import { FormsModule } from '@angular/forms';

bootstrapApplication(AppComponent, {
  providers: [importProvidersFrom(FormsModule)],
}).catch((err) => console.error(err));

Running the Project

Start the Angular app:

ng serve

Open Google Chrome and go to http://localhost:4200/.
Test by clicking “Connect to BleuIO”, entering an AT command, and clicking “Send Command”.

Complete Source Code

Find the full source code on GitHub:
🔗 GitHub Repository

Output

Use Cases: Why This is Helpful

  • This tutorial is beneficial for developers who:
  • Want a simple, browser-based way to communicate with BleuIO.
  • Need a cross-platform solution that works on Windows, macOS, and Linux.
  • Are building IoT applications that require BLE communication.
  • Want to use Angular & TypeScript for a scalable frontend solution.

This tutorial demonstrates how to use Angular 19, TypeScript, and the Web Serial API to communicate with BleuIO. The project is lightweight, scalable, and works entirely in the browser—perfect for IoT applications!

Share this post on :

Smart CO2-Based Fan Control Using BleuIO and Renesas RA4M2

This project showcases how to integrate the Renesas EK-RA4M2 microcontroller with a BleuIO BLE USB dongle to create a smart air ventilation system. By using HibouAir‘s CO2 parameter, an air quality monitoring device, the system continuously monitors indoor air quality and automatically controls a fan based on CO2 levels.

The BleuIO dongle scans for HibouAir’s BLE advertising data to retrieve real-time CO2 readings. When the CO2 concentration exceeds 600 ppm, the system activates the fan to improve air circulation. Once the CO2 level drops below 550 ppm, the fan is turned off to conserve energy.

This implementation demonstrates a practical IoT-based air quality control solution, making indoor environments healthier and more efficient.

The EK-RA4M2 board prints the CO2 values, as they change, on the RTTViewer.

Requirements

Setup

  • Connect a Micro USB device cable (type-A male to micro-B male) between J10 (Debug1) and a Computer USB port.
  • Plug in a BleuIO Dongle in the USB OTG Cable (type-A female to micro-B male) and connect it to J11 (USB Full Speed).
  • Make sure Jumper J12 is placed on pins 1-2
  • Remove Jumper J15 pins
  • Connect the fan power adapter to 3V3 and GND on the developer kit like this:

  • The fan power adapter will also need to be connected to GPIO pin 505 on the developer kit to turn it on and off:

Importing project

  • Open e² studio IDE
  • Choose a workspace and click ‘Launch’
  • Download or clone the example project. Place the folder ‘bleuio_ra4m2_fan_example’ in workspace.
  • Choose Import Project
  • Select ‘Existing Projects into Workspace’ under the ‘General’ tab:
  • Click the ‘Browse…’ button and open the folder where the ‘bleuio_ra4m2_fan_example’ project folder is located:
  • Finally select the project and click ‘Finish’. You have now imported the the project!

Running the example

  • Go to file ‘usb_hcdc_app.c’ under ‘src/’ and edit line 41 to the board ID of the HibouAir Sensor:
  • #define BOARD_ID_TO_SCAN “2202B3”
  • The board ID is printed on the back of the HibouAir sensor:
  • You can also threshold values to change when the fan should start and stop.

    The defines can be found on row 45 and 47 in ‘usb_hcdc_app.c’ under ‘src/’:
  • /* CO2 threshold value 1. If at this value or above, the fan will start. */
    #define CO2_FAN_ROOF 600
    /* CO2 threshold value 2. If at this value or below, the fan will stop. */
    #define CO2_FAN_FLOOR 550

Build the project by clicking the building icon:

  • Use Debug to download and run the project. The first time you need to configure the debug settings. Click down arrow to the right of the Debug icon and select ‘Debug Configurations…’


Under ‘Renesas GDB Hardware Debugging’ select ‘bleuio_ra4m2_fan_example.elf’ and click ‘Debug’

  • The debug is now configured and the ‘Debug’ icon can be used next time to run the project.
  • Open RTTViewer. Connect and use these settings:

    Connection to J-Link: USB

    Specify Target Device: R7FA4M2AD

    Target Interface & Speed: SWD 4000kHz

    RTT Control Block: Address 0x2000095c


On the debugger screen in e² studio click the ‘Resume’ icon twice to run the project.

  • The application is now running. When starting up you should notice all LEDs lighting up for one second then only the red LED will be on. It will turn off as soon as the BleuIO is configured.
  • You should now see the output on the RTTViewer. 


If CO2 value is 600ppm or above, the fan will turn on.

If CO2 value is 550ppm or below, the fan will turn off.


The LEDs will light up like the previous CO2 Monitor Example:

When the CO2 level is less than 600 ppm only the blue LED will be turned on.


If the CO2 level is over 600 ppm but below 1000 ppm then the green LED will be on.


If the CO2 level is above 1000 ppm then the red LED will be on.

Share this post on :

Building a Secure Proximity-Based Login System with Bluetooth Low Energy (BLE)

Bluetooth Low Energy (BLE) provides a powerful mechanism to enhance security through proximity-based authentication. This tutorial showcases how to create a secure login system using Node.js, Express, and BLE technology. The project demonstrates how BLE scanning can verify the presence of an authorized BLE device (such as Close Beacon) in range before granting access. This offers an extra layer of protection for your applications.

Why This Project?

With the increasing need for secure authentication mechanisms, BLE (Bluetooth Low Energy) offers a lightweight, reliable, and energy-efficient solution. This project demonstrates how you can utilize a BleuIO USB dongle to securely check the proximity of a specific Close Beacon’s mac address before granting access to sensitive resources.

Key Features:

  • Node.js Integration: Simplifies server-side logic using JavaScript.
  • BLE Device Scanning: Checks for the presence of an authorized device based on its MAC address.
  • Flexible Hardware Support: While this project uses Close Beacon, any BLE device can be used for authentication.
  • Enhanced Security: Adds an additional layer of security to the login process.
  • Practical Example: Serves as a foundation for more advanced BLE-based applications.

Practical Use Cases

  • Extra Security Layer: Combine BLE proximity detection with traditional authentication methods for added security.
  • Access Control Systems: Enable access to sensitive areas only when an authorized device is nearby.
  • BLE-Powered IoT Applications: Use BleuIO for real-time device monitoring and communication.

Project Overview

This project uses:

  • Node.js for server-side scripting.
  • Express for handling routes and server logic.
  • BleuIO USB Dongle for BLE scanning.
  • A BLE Device (e.g., Close Beacon): Ensure the device is powered on and within range.
  • Bootstrap for a simple and clean UI.

When the user clicks the “Login Securely” button, the application:

  • Connects to the BleuIO dongle via a serial port (tested on macOS; Windows code is commented in the source).
  • Puts the dongle into central mode with the AT+CENTRAL command.
  • Sets an RSSI filter (AT+FRSSI=-56) to filter out weak signals.
  • Initiates a BLE scan for nearby devices (AT+GAPSCAN=3).
  • Checks if a target device with a specific MAC address is present in the scan results.
  • Logs the user into the dashboard if the device is nearby; otherwise, it denies access.

Setting Up the Project

Prerequisites

  1. BleuIO Dongle: Ensure the BleuIO dongle is plugged into your computer.
  2. Node.js: Install Node.js from nodejs.org.
  3. Dependencies: Install express and serialport using npm:
    npm install express serialport

How It Works

Step 1: Frontend (HTML and JavaScript)

The index page displays a button, “Login to dashboard securely.” When clicked, it sends a request to the server to scan for BLE devices.

index.html:

<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link
      href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css"
      rel="stylesheet"
      integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH"
      crossorigin="anonymous"
    />

    <title>Login Securely</title>
  </head>
  <body>
    <div class="container text-center mt-5">
      <br />
      <div class="row">
        <div class="col-md-6 offset-md-3">
          <p class="lead">
            This login checks if an authorized BLE device is nearby. The
            <strong>BleuIO</strong>
            dongle scans for the authorized device's MAC address. If it is
            nearby, the system will log you into the dashboard.
          </p>
        </div>
      </div>

      <br />
      <button class="btn btn-success btn-lg" id="scanBtn">
        Login to dashboard securely
      </button>
      <div id="listScan"></div>
    </div>

    <script>
      document.getElementById('scanBtn').addEventListener('click', () => {
        fetch('/scanbledevice')
          .then((response) => response.json())
          .then((data) => {
            // Log the response for debugging
            console.log(data.message);

            // Redirect to /dashboard if the device is nearby
            if (data.success) {
              window.location.href = '/dashboard';
            } else {
              alert(data.message); // Show a message if the device is not nearby
            }
          })
          .catch((err) => {
            console.error('Error:', err);
            alert('An error occurred while scanning for devices.');
          });
      });
    </script>
  </body>
</html>

dashboard.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link
      href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css"
      rel="stylesheet"
      integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH"
      crossorigin="anonymous"
    />
    <title>Dashboard</title>
  </head>
  <body>
    <div class="container">
      <h3 class="mt-5">Welcome to Smart Dashboard</h3>
      <br /><br />
      <a href="/" class="btn btn-danger btn-lg" id="logoutBtn">Logout</a>
    </div>
    <script>
      document
        .getElementById('logoutBtn')
        .addEventListener('click', (event) => {
          event.preventDefault(); // Prevent default navigation
          fetch('/disconnect')
            .then((response) => response.json())
            .then((data) => {
              if (data.success) {
                console.log(data.message);
                window.location.href = '/'; // Redirect to the index page
              } else {
                alert('Failed to disconnect: ' + data.message);
              }
            })
            .catch((err) => {
              console.error('Error during disconnect:', err);
              alert('An error occurred during logout.');
            });
        });
    </script>
  </body>
</html>

Step 2: Backend Logic

The server handles requests to scan for BLE devices, manage the serial port connection, and redirect users to the dashboard.

server.js:

// server.js
const express = require('express');
const { scanBLE, disconnectBLE } = require('./script');

const app = express();
const port = 3000;

// Serve the HTML file
app.get('/', (req, res) => {
  res.sendFile(__dirname + '/index.html');
});
app.get('/dashboard', (req, res) => {
  res.sendFile(__dirname + '/dashboard.html');
});
app.get('/disconnect', (req, res) => {
  disconnectBLE()
    .then((message) => {
      res.json({ success: true, message });
    })
    .catch((err) => {
      console.error('Error during disconnect:', err);
      res
        .status(500)
        .json({ success: false, message: 'Failed to disconnect serial port' });
    });
});

// Endpoint to fetch the list of serial ports
app.get('/scanbledevice', (req, res) => {
  scanBLE()
    .then((bleDevice) => {
      const targetMacAddress = '[1]D1:79:29:DB:CB:CC';

      // Check if the target device is nearby
      const isNearby = bleDevice.some((line) =>
        line.includes(targetMacAddress)
      );

      // Respond with a message
      if (isNearby) {
        res.json({
          success: true,
          message: `Device ${targetMacAddress} is nearby.`,
        });
      } else {
        res.json({
          success: false,
          message: `Login Failed ! Device ${targetMacAddress} is not nearby.`,
        });
      }
    })
    .catch((err) => {
      console.error('Error during BLE scan:', err);
      res
        .status(500)
        .json({ success: false, message: 'Error fetching BLE devices.' });
    });
});

// Start the server
app.listen(port, () => {
  console.log(`Server is running on http://localhost:${port}`);
});

Step 3: BLE Scanning Logic

The script connects to the BleuIO dongle, configures it, and scans for nearby devices.

script.js:

// script.js
const { SerialPort } = require('serialport');
var firstPort = '';
var readDataArray = [];
var openPort;
// Function to fetch the ble device list
function scanBLE() {
  return new Promise((resolve, reject) => {
    SerialPort.list()
      .then((ports) => {
        //filter ports to get BleuIO path
        // windows
        /* result = ports.filter(
            (portVal) =>
              portVal.pnpId && portVal.pnpId.includes("VID_2DCF&PID_6002")
          ); */
        //Mac
        result = ports.filter(
          (portVal) =>
            portVal.manufacturer &&
            portVal.manufacturer.includes('Smart Sensor Devices')
        );
        // get the first port path of the BleuIO connected to computer
        firstPort = result[0] && result[0].path;
        openPort = new SerialPort({ path: firstPort, baudRate: 115200 });
        // function to read serial port data
        const readData = (dm) => {
          return new Promise((resolve, reject) => {
            openPort.on('readable', () => {
              let data = openPort.read();
              let enc = new TextDecoder();
              let arr = new Uint8Array(data);
              let rawString = enc.decode(arr);

              // Split response into lines and trim extra spaces or empty lines
              let lines = rawString
                .split(/[\r\n]+/)
                .filter((line) => line.trim() !== '');
              readDataArray.push(...lines);

              // Log each line for better readability
              lines.forEach((line) => console.log(line));

              if (rawString.includes(dm)) {
                return resolve(readDataArray);
              } else {
                return resolve(readDataArray);
              }
            });
          });
        };

        // put the dongle to central role
        openPort.write('AT+CENTRAL\r', (err) => {
          if (err) {
            return reject(
              new Error('Error setting dongle to central role ' + err.message)
            );
          } else {
            // Set the RSSI filter
            openPort.write('AT+FRSSI=-56\r', (err) => {
              if (err) {
                return reject(
                  new Error('Error setting RSSI filter ' + err.message)
                );
              } else {
                // Scan for BLE devices for three seconds
                openPort.write('AT+GAPSCAN=3\r', (err) => {
                  if (err) {
                    return reject(
                      new Error('Error initiating BLE scan ' + err.message)
                    );
                  } else {
                    setTimeout(() => {
                      resolve(readData('SCAN COMPLETE'));
                    }, 3500);
                  }
                });
              }
            });
          }
        });
      })
      .catch((err) => {
        console.error('Error listing serial ports:', err);
        reject(err);
      });
  });
}
function disconnectBLE() {
  return new Promise((resolve, reject) => {
    if (openPort && openPort.isOpen) {
      openPort.close((err) => {
        if (err) {
          console.error('Error closing serial port:', err);
          return reject(err);
        }
        console.log('Serial port disconnected.');
        resolve('Disconnected');
      });
    } else {
      console.log('No serial port to disconnect.');
      resolve('No port to disconnect');
    }
  });
}

module.exports = { scanBLE, disconnectBLE };

Full source code is available at github. Source code

Output

This project is an example of how to integrate BleuIO with Node.js to build a BLE-powered secure login system. The source code is available and can be adapted for your use case. Start experimenting with BleuIO today and unlock the potential of BLE in your applications!

Share this post on :

Real-Time CO₂ Monitoring App with Go and BleuIO

Awareness of Air quality monitoring importance for health and productivity has been increasing lately, especially in indoor environments like offices and homes. In this tutorial, we’ll demonstrate how to create a real-time CO₂ monitoring application using Go, a modern programming language with a vibrant community, alongside the BleuIO BLE USB dongle and HibouAir, a BLE-enabled air quality sensor.

This project showcases how to use Go’s simplicity and performance to build an efficient application that scans for CO₂ data, decodes it, and provides real-time notifications on macOS when the CO₂ level exceeds a critical threshold. By using BleuIO’s integrated AT commands, you can focus on your application logic without worrying about complex embedded BLE programming.

Project Overview

The goal of this project is to:

  1. Use BleuIO to scan for BLE advertisements from HibouAir, which broadcasts real-time CO₂ levels.
  2. Decode the advertised data to extract CO₂ concentration.
  3. Send a real-time macOS notification when CO₂ levels exceed a specified threshold (1000 ppm in this example).

Notifications are implemented using the macOS osascript utility, ensuring you are immediately alerted about high CO₂ levels on your laptop screen.

Why This Project Is Useful

When you’re focused on work, you might not notice subtle changes in your environment. This application ensures you’re notified directly on your laptop screen when CO₂ levels become unsafe. This is especially helpful for:

  • Office Workers: Monitor meeting rooms or shared spaces where ventilation may be insufficient.
  • Remote Workers: Ensure a healthy workspace at home without distractions.
  • Educational Settings: Keep classrooms or labs safe for students and staff.

Technical Details

Tools and Devices

  • Programming Language: Go – Chosen for its simplicity, performance, and active community.
  • BLE USB Dongle: BleuIO – Simplifies BLE communication with built-in AT commands.
  • CO₂ Monitoring Device: HibouAir – Provides real-time air quality metrics over BLE.

How It Works

  1. Initialize the Dongle:
    • Set the BleuIO dongle to the central role to enable scanning for BLE devices.
  2. Scan for Advertised Data:
    • Use the AT+FINDSCANDATA command to scan for HibouAir’s advertisements containing air quality data.
  3. Decode CO₂ Information:
    • Extract and convert the relevant part of the advertisement to get the CO₂ level in ppm.
  4. Send Notifications:
    • Use Go’s exec.Command to invoke macOS osascript and display a desktop notification if the CO₂ level exceeds the threshold.

Implementation

Here is the source code for the project:

package main

import (
"bufio"
"fmt"
"log"
"os/exec"
"strconv"
"strings"
"time"

"go.bug.st/serial"
)

func main() {
// Open the serial port
mode := &serial.Mode{
BaudRate: 9600,
}
port, err := serial.Open("/dev/cu.usbmodem4048FDE52CF21", mode)
if err != nil {
log.Fatalf("Failed to open port: %v", err)
}
defer port.Close()

// Initial setup: Set the dongle to central mode
err = setupDongle(port)
if err != nil {
log.Fatalf("Failed to set up dongle: %v", err)
}

// Repeatedly scan for advertised data and process it
for {
err := scanAndProcessData(port)
if err != nil {
log.Printf("Error during scan and process: %v", err)
}
time.Sleep(10 * time.Second) // Wait before the next scan (interval)
}
}

// setupDongle sets the dongle to central mode
func setupDongle(port serial.Port) error {
_, err := port.Write([]byte("AT+CENTRAL\r"))
if err != nil {
return fmt.Errorf("failed to write AT+CENTRAL: %w", err)
}
time.Sleep(1 * time.Second) // Ensure the command is processed

buf := make([]byte, 100)
_, err = port.Read(buf)
if err != nil {
return fmt.Errorf("failed to read response from AT+CENTRAL: %w", err)
}

fmt.Println("Dongle set to central mode.")
return nil
}

// scanAndProcessData scans for advertised data and processes it
func scanAndProcessData(port serial.Port) error {
_, err := port.Write([]byte("AT+FINDSCANDATA=220069=2\r"))
if err != nil {
return fmt.Errorf("failed to write AT+FINDSCANDATA: %w", err)
}

time.Sleep(3 * time.Second) // Wait for scan to complete

buf := make([]byte, 1000)
n, err := port.Read(buf)
if err != nil {
return fmt.Errorf("failed to read scan response: %w", err)
}

response := string(buf[:n])

// Extract the first advertised data
firstAdvertisedData := extractFirstAdvertisedData(response)
if firstAdvertisedData == "" {
fmt.Println("No advertised data found.")
return nil
}

// Extract the specific part (6th from last to 3rd from last) and convert to decimal
if len(firstAdvertisedData) >= 6 {
extractedHex := firstAdvertisedData[len(firstAdvertisedData)-6 : len(firstAdvertisedData)-2]

decimalValue, err := strconv.ParseInt(extractedHex, 16, 64)
if err != nil {
return fmt.Errorf("failed to convert hex to decimal: %w", err)
}
fmt.Printf("CO₂ Value: %d ppm\n", decimalValue)

// Send notification if CO₂ value exceeds 1000
if decimalValue > 1000 {
sendNotification("CO₂ Alert", fmt.Sprintf("High CO₂ level detected: %d ppm", decimalValue))
}
} else {
fmt.Println("Advertised data is too short to extract the desired part.")
}
return nil
}

// extractFirstAdvertisedData extracts the first advertised data from the response
func extractFirstAdvertisedData(response string) string {
scanner := bufio.NewScanner(strings.NewReader(response))
for scanner.Scan() {
line := scanner.Text()
if strings.Contains(line, "Device Data [ADV]:") {
parts := strings.Split(line, ": ")
if len(parts) > 1 {
return parts[1]
}
}
}
if err := scanner.Err(); err != nil {
log.Printf("Error scanning response: %v", err)
}
return ""
}

// sendNotification sends a macOS notification with the specified title and message
func sendNotification(title, message string) {
script := `display notification "` + message + `" with title "` + title + `"`
cmd := exec.Command("osascript", "-e", script)
err := cmd.Run()
if err != nil {
log.Printf("Error sending notification: %v", err)
}
}

Source code

Source code is available on https://github.com/smart-sensor-devices-ab/monitor-realtime-co2-go

Output

This project demonstrates how to build a real-time CO₂ monitoring application using Go, BleuIO, and HibouAir. By using Go’s capabilities and BleuIO’s ease of use, you can focus on the logic of your application and quickly adapt the solution to your specific needs.

Share this post on :

Building a BLE Real-Time macOS Menu Bar App Using BleuIO

In this tutorial, we will guide you through creating a BLE real-time macOS menu bar application using the BleuIO USB BLE dongle. BleuIO is an incredibly versatile tool that simplifies the development of BLE (Bluetooth Low Energy) applications, making it ideal for developers looking to build innovative projects with ease.

macOS menu bar applications offer a seamless way to monitor and interact with data in real time without requiring a dedicated application window. By leveraging the power of the BleuIO dongle, we can create a menu bar app that provides live updates on environmental metrics like temperature, humidity, and CO2 levels. This project demonstrates how BleuIO can be integrated into real-time applications, showcasing its potential for BLE-based projects.

Why is This Project Useful?

  • Real-Time Updates: The app fetches BLE data at regular intervals and updates the macOS menu bar dynamically.
  • Ease of Access: The macOS menu bar provides a non-intrusive interface, allowing users to access live data at a glance.
  • Extensibility: This tutorial serves as a starting point for developers to explore more advanced BLE applications with BleuIO.

Requirements

To complete this project, you will need:

  1. BleuIO USB BLE Dongle: A powerful and easy-to-use BLE dongle for developing BLE applications.
  2. HibouAir – Air Quality Monitor: A BLE-enabled air quality monitor that broadcasts real-time environmental data such as temperature,pressure,voc,light, humidity, and CO2 levels.
  3. macOS System: A macOS device with Python 3 installed.
  4. Python Libraries:
    • rumps: For creating macOS menu bar applications.
    • bleuio: For communicating with the BleuIO dongle.

How Real-Time Updates Are Handled

The app connects to the BleuIO dongle and scans for BLE advertisements air quality data from HibouAir. Using a timer, the app periodically initiates a scan every 2 minutes. The decoded data is then displayed directly in the macOS menu bar, providing real-time updates without user intervention.

Step-by-Step Guide

Step 1: Set Up the Environment

  1. Ensure you have a macOS system with Python 3 installed.
  2. Install the necessary dependencies using pip:
    pip install rumps bleuio
  3. Plug in your BleuIO USB dongle.

Step 2: Project Overview

Our goal is to:

  • Connect to the BleuIO dongle.
  • Put the dongle in Central Mode to scan for BLE advertisements.
  • Scan for real time air quality data from HibouAir
  • Decode the advertisement data to extract temperature, humidity, pressure, and CO2 levels.
  • Update the macOS menu bar with the decoded data in real time.

Step 3: Writing the Code

Below is the Python script for the macOS menu bar app. This code handles the dongle initialization, data scanning, decoding, and menu updates.

import rumps
import time
import json
from datetime import datetime
from bleuio_lib.bleuio_funcs import BleuIO
boardID="220069"

# Function to decode advertisement data
def adv_data_decode(adv):
    try:
        pos = adv.find("5B0705")
        if pos == -1:
            raise ValueError("Invalid advertisement data: '5B0705' not found.")

        dt = datetime.now()
        current_ts = dt.strftime("%Y/%m/%d %H:%M:%S")

        # Temperature decoding
        temp_hex = int(adv[pos + 22:pos + 26][::-1], 16)  # Reversed bytes
        if temp_hex > 1000:
            temp_hex = (temp_hex - (65535 + 1)) / 10
        else:
            temp_hex = temp_hex / 10

        # Pressure decoding (convert from little-endian)
        pressure_bytes = bytes.fromhex(adv[pos + 18:pos + 22])
        pressure = int.from_bytes(pressure_bytes, byteorder='little') / 10

        # Humidity decoding (convert from little-endian)
        humidity_bytes = bytes.fromhex(adv[pos + 26:pos + 30])
        humidity = int.from_bytes(humidity_bytes, byteorder='little') / 10

        return {
            "boardID": adv[pos + 8:pos + 14],
            "pressure": pressure,
            "temp": temp_hex,
            "hum": humidity,
            "co2": int(adv[pos + 46:pos + 50], 16),
            "ts": current_ts,
        }
    except Exception as e:
        print(f"Error decoding advertisement data: {e}")
        return {}


# Callback function for scan results
def my_scan_callback(scan_input):
    try:
        scan_result = json.loads(scan_input[0])
        data = scan_result.get("data", "")

        decoded_data = adv_data_decode(data)

        # Update menu with decoded data
        app.update_menu(decoded_data)

    except Exception as e:
        print(f"Error parsing scan result: {e}")

# Callback function for events
def my_evt_callback(evt_input):
    cbTime = datetime.now()
    currentTime = cbTime.strftime("%H:%M:%S")
    print(f"\n\n[{currentTime}] Event: {evt_input}")

class AirQualityApp(rumps.App):
    def __init__(self):
        super(AirQualityApp, self).__init__("CO2 : 550")
        self.my_dongle = None  # Placeholder for the dongle object
        self.co2_item = rumps.MenuItem(title="CO2 : 550ppm", callback=lambda _: None)
        self.temp_item = rumps.MenuItem(title="Temperature: 25°C", callback=lambda _: None)
        self.hum_item = rumps.MenuItem(title="Humidity: 65 %rh", callback=lambda _: None)
        self.press_item = rumps.MenuItem(title="Pressure: 1000 mbar", callback=lambda _: None)

        self.menu = [
            self.co2_item,
            self.temp_item,
            self.hum_item,
            self.press_item,
            None  # Separator
        ]

        # Establish connection and start scanning on startup
        self.connect_dongle()
        self.start_periodic_scan()

    def connect_dongle(self):
        try:
            self.my_dongle = BleuIO()  # Initialize the dongle
            self.my_dongle.register_evt_cb(my_evt_callback)  # Register event callback
            self.my_dongle.register_scan_cb(my_scan_callback)  # Register scan callback
            print("Dongle connected successfully.")

            # Set the dongle to central mode
            response = self.my_dongle.at_central()
            print("Dongle is now in central mode.")

        except Exception as e:
            print(f"Error connecting to dongle: {e}")

    def scan(self, _=None):  # Added `_` to accept the timer argument
        try:
            # Start scanning for specific data
            response = self.my_dongle.at_findscandata(boardID, 3)
            print(f"Scan initiated. Response: {response.Rsp}")
        except Exception as e:
            print(f"Error during scan: {e}")

    def start_periodic_scan(self):
        try:
            rumps.timer(120)(self.scan)  # Run the scan method every 30 seconds
        except Exception as e:
            print(f"Error setting up periodic scan: {e}")

    def update_menu(self, decoded_data):
        try:
            self.title = f"CO2 : {decoded_data.get('co2', 'N/A')}ppm"  # Update app title
            self.co2_item.title = f"CO2 : {decoded_data.get('co2', 'N/A')}ppm"
            self.temp_item.title = f"Temperature: {decoded_data.get('temp', 'N/A')}°C"
            self.hum_item.title = f"Humidity: {decoded_data.get('hum', 'N/A')} %rh"
            self.press_item.title = f"Pressure: {decoded_data.get('pressure', 'N/A')} mbar"
        except Exception as e:
            print(f"Error updating menu: {e}")

if __name__ == "__main__":
    app = AirQualityApp()
    app.run()

Note : Make sure to change the BoardID to your HibouAir CO2 device on line 6

Step 4: Run the App

  1. Save the script as bleuio.py.
  2. Run the script using:
    python bleuio.py
  3. The app will appear in the macOS menu bar with latest CO2 value. Click the icon to view the live BLE data updates.

Output

Extending the Project

This project is a foundation for exploring the capabilities of BleuIO. You can extend it to:

  • Monitor additional BLE devices.
  • Implement alert notifications for specific data thresholds.
  • Log the data to a file or send it to a cloud service for further analysis.

This tutorial demonstrates how to create a real-time macOS menu bar application using the BleuIO dongle. By following this guide, you’ll not only learn how to handle BLE data but also understand how to integrate it into user-friendly macOS applications. BleuIO opens up endless possibilities for BLE-based projects, and we’re excited to see what you create next!

Share this post on :

Real-Time BLE Proximity-Based LED Blinking with BleuIO: A Practical Guide

BleuIO, a versatile BLE USB dongle, simplifies BLE application development with its powerful AT commands and cross-platform compatibility. This tutorial demonstrates how to use BleuIO with Python to build a real-time BLE proximity-based LED control system.

In this example, we focus on monitoring the RSSI (Received Signal Strength Indicator) of a specific BLE device and controlling the LED blinking rate of BleuIO based on proximity. The closer the device, the faster the blinking rate. The script also ensures clean termination, turning off the LED when the program exits.

This tutorial uses the BleuIO Python library to showcase a practical use case. However, the concepts can be implemented in any programming language.

Devices and Tools Needed

  1. BleuIO Pro BLE USB Dongle
  2. Python Installed (Python 3.6 or newer recommended)
  3. BleuIO Python Library
    Install the library using
    pip install bleuio
  4. Source Code
    Get the complete source code from GitHub:
    GitHub Repository

Use Case: Real-Time LED Blinking Based on Proximity

Objective:

  1. Continuously monitor the RSSI value of a specific BLE device.
  2. Adjust the LED blinking rate of the BleuIO dongle based on the device’s proximity.
  3. Ensure the LED turns off when the script is terminated.

Workflow:

  1. Scan for BLE devices: Identify the target device using its MAC address.
  2. Read RSSI values: Fetch real-time signal strength data.
  3. Control LED: Adjust the blinking rate based on proximity:
    • Very Close: Fast blinking.
    • Close: Moderate blinking.
    • Far: Slow blinking.
    • Very Far: Very slow blinking.
  4. Graceful Exit: Turn off the LED when the script ends.

The Python Script

Below is the complete script for this project:

import time
import json
import atexit
from bleuio_lib.bleuio_funcs import BleuIO

rssi_value = None
dongle = None  

def scan_callback(scan_input):
    global rssi_value  
    try:
        device_data = json.loads(scan_input[0])  
        if device_data.get("addr") == "[1]6B:C0:5C:BD:CF:14":
            rssi_value = device_data["rssi"]  
            print(f"\nDevice Found! Address: {device_data['addr']}, RSSI: {rssi_value}")
    except json.JSONDecodeError as e:
        print(f"Error decoding JSON: {e}")
    except Exception as e:
        print(f"Unexpected error: {e}")

def send_led_command(dongle, rssi):
    if rssi is not None:  
        if rssi > -40:
            print(f"RSSI: {rssi} | Sending LED Command: 50/50")
            dongle.at_led(toggle="T", on_period="50", off_period="50")
        elif -60 <= rssi <= -40:
            print(f"RSSI: {rssi} | Sending LED Command: 100/100")
            dongle.at_led(toggle="T", on_period="100", off_period="100")
        elif -90 <= rssi < -60:
            print(f"RSSI: {rssi} | Sending LED Command: 200/200")
            dongle.at_led(toggle="T", on_period="200", off_period="200")
        else:
            print(f"RSSI: {rssi} | Sending LED Command: 300/300")
            dongle.at_led(toggle="T", on_period="300", off_period="300")
    else:
        print("No RSSI value available for LED command.")


def cleanup():
    if dongle:
        print("\n--- Turning off LED and cleaning up ---")
        dongle.at_led(0)

# Main logic
def main():
    global rssi_value, dongle  
    dongle = BleuIO()

    atexit.register(cleanup)

    dongle.register_scan_cb(scan_callback)

    print("\n--- Starting BLE Task ---\n")

    print("Setting device role to Central...")
    central_response = dongle.at_central()

    try:
        while True:
            print("\nStarting scan for 2 seconds...")
            dongle.at_gapscan(2)  
            time.sleep(3)  

            send_led_command(dongle, rssi_value)

            print("\nScan cycle completed. Restarting...\n")
            time.sleep(1)
    except KeyboardInterrupt:
        print("\n--- Script Terminated by User ---")

# Run the main function
if __name__ == "__main__":
    main()

How It Works

  1. Initialization: The script initializes the BleuIO dongle and sets it to Central role for scanning.
  2. Scan Callback: The scan_callback function extracts the RSSI value of the target device in real-time.
  3. LED Control: Based on the RSSI value:
    • RSSI > -40: Fast blinking (50ms on/off).
    • -60 <= RSSI <= -40: Moderate blinking (100ms on/off).
    • -90 <= RSSI < -60: Slow blinking (200ms on/off).
    • RSSI < -90: Very slow blinking (300ms on/off).
  4. Graceful Termination: The script turns off the LED when terminated with Ctrl + C.

Output

This example demonstrates how easy it is to use BleuIO for BLE applications. Whether you’re building proximity-based solutions or exploring BLE capabilities, BleuIO’s AT commands and Python library make it simple to get started.

Take this script, adapt it to your needs, and unlock the potential of BLE with BleuIO!

Share this post on :

Creating a BLE Chat Application Using BleuIO USB Dongle

Bluetooth Low Energy (BLE) has become a core technology in the modern world, enabling secure and efficient communication for IoT devices, wearables, healthcare gadgets, and more. One of the fascinating applications of BLE is in creating private communication systems. In this tutorial, we will explore how to create a BLE chat application using the BleuIO USB dongle, a powerful yet simple device for BLE application development.

Why This Project?

With increasing concerns about privacy, BLE chat offers a solution that keeps communication entirely local. Unlike internet-based messaging systems, BLE chat does not rely on servers or cloud storage, ensuring that no data leaves your devices. This project demonstrates a simple prototype of such a BLE-based communication system.

How It Works

The project involves two laptops, each connected to a BleuIO USB dongle. For simplicity, we designate one laptop as User 1 (Central role) and the other as User 2 (Peripheral role). Here’s a high-level breakdown of the workflow:

  1. Setup:
    Each laptop runs a script to initialize its BleuIO dongle. User 1 starts in a dual role and acts as the central device. User 2 also sets its dongle in a dual role but begins advertising itself.
  2. Connection:
    Once User 2 starts advertising, it displays its MAC address. User 1 uses this MAC address to connect to User 2.
  3. Messaging:
    After establishing a connection, the users can send and receive messages in real time. The communication is direct and local, with no reliance on external servers.

Setting Up the Project

The source code for this project is available on GitHub: BLE Chat Source Code. You can explore, experiment, and build on it to fit your needs.

Steps to Set Up the Project:

  1. Clone the Repository
    Open a terminal on both computers and run
    git clone https://github.com/smart-sensor-devices-ab/ble-chat.git
  2. Install Dependencies
    Navigate to the project directory and install the required Node.js dependencies
    cd ble-chat
    npm install
  3. Run the Server
    Start the server by running
    node server.js
    Ensure the server is running on both computers. The terminal should display messages confirming the dongle is detected.

Running the Scripts

Once the servers are running on both computers, follow these steps to use the BLE chat application:

  1. User 1 Setup:
    • Open the browser and navigate to http://localhost:3000.
    • Click “Chat as User 1.” This will initialize the BleuIO dongle in dual role mode.
  2. User 2 Setup:
    • On the second computer, open the browser and navigate to http://localhost:3000.
    • Click “Chat as User 2.” This will initialize the BleuIO dongle in dual role mode and start advertising. You’ll also see the MAC address of the dongle displayed on the screen. ble chat user 2
  3. Connecting the Devices:
    • Copy the MAC address from User 2’s screen and enter it on User 1’s screen in the provided input field.
    • Click “Connect” on User 1. Once the connection is established, you’ll see a confirmation message.
      ble chat user 1
  4. Start Chatting:
    • Use the chat interface on both devices to send messages back and forth. Messages sent from one device will instantly appear on the other device’s screen.
      ble chat screen

Output

In the video below, we demonstrate how this project works with two BleuIO dongles connected to two different computers. To provide a complete view, we are sharing the screen of the second computer to showcase how the chat functions in real-time.

Use Cases for BLE Chat

The BLE chat prototype we’ve created can inspire real-world applications. Here are some potential use cases:

Secure Local Communication:
BLE chat can be used for private messaging within offices, factories, or homes without the need for internet connectivity.

Education Projects:
BLE chat can be a great project for students learning about BLE technology and its applications.

Why Choose BleuIO for Your BLE Projects?

The BleuIO USB dongle makes BLE application development accessible for everyone, from beginners to advanced developers. Its built-in AT commands allow you to quickly prototype applications without diving into complex BLE stacks or SDKs. Whether you’re working on a small hobby project or an enterprise-level IoT solution, BleuIO provides the tools you need.

Here are some standout features of BleuIO:

  • Cross-Platform Compatibility: Works seamlessly on Windows, macOS, and Linux.
  • Simple AT Commands: No need for extensive coding; just use the built-in commands to control the dongle.
  • Lightweight and Portable: Easy to carry and set up for on-the-go development.

This BLE chat application demonstrates the power and simplicity of BLE communication using the BleuIO USB dongle. While it’s a prototype, it showcases how BLE can enable private, secure, and efficient messaging without relying on external networks.

If you’re interested in exploring this further, the source code for the project is available. You can modify and extend it to fit your specific needs. With BleuIO, the possibilities are endless.

Order Your BleuIO USB Dongle Today!
Ready to create your own BLE applications? Visit BleuIO’s to learn more and order your dongle today.

Share this post on :

Building a Remote BLE Interface: Access BLE devices from Any Location

This article will discuss about accessing a Bluetooth Low Energy (BLE) device—specifically the BleuIO BLE USB dongle—from a cloud-based computer. This setup is invaluable for developers, researchers, and organizations that need to control or monitor BLE devices located in remote or hard-to-reach locations. We will explain how to set up both local and cloud servers, and how to establish a secure connection between them, allowing you to send commands and receive data from a remote BLE device with ease.

Why Do We Need Remote Access to BLE Devices?

Remote access to BLE devices opens up new possibilities in IoT, distributed applications, and various remote monitoring and management scenarios. Here are a few key use cases where this approach can be especially beneficial:

Remote Device Monitoring: In scenarios where BLE devices like sensors or trackers are deployed in different physical locations—such as environmental monitoring stations, healthcare devices in hospitals, or industrial sensors in manufacturing facilities—remote access allows centralized monitoring and control. For example, an environmental monitoring company could deploy BLE sensors in different regions and access data from all devices from a single central hub.

Distributed Development and Testing: Developers and QA engineers can use remote access for testing and debugging BLE applications from any location, without needing to be physically near the BLE devices. This is particularly useful for teams working on IoT applications who want to test BLE functionality across different device types or networks. For instance, a developer could work on BLE application features from home, while the test device is connected to a machine in the office.

Centralized Management of BLE Devices: Organizations that manage multiple BLE devices across various locations, such as in retail stores, hospitals, or warehouses, can benefit from a centralized server setup to communicate with and manage all devices remotely. A central server could send updates, retrieve data, or manage configurations for multiple BLE devices, providing an efficient and scalable solution for distributed IoT applications.

Remote Troubleshooting and Maintenance: For businesses that deploy BLE devices at customer sites or in field locations, remote access allows technical support teams to troubleshoot issues without requiring on-site visits. This can reduce downtime and improve customer satisfaction. For example, a support technician could diagnose issues with a BLE-enabled device at a remote client site, identifying and resolving problems directly from the company’s central office.

By using BleuIO with AT commands, we make BLE application development much simpler and more accessible. The BleuIO dongle is compatible with Windows, macOS, and Linux, allowing consistent development across platforms.

Project Structure and Components

In this project, we’ll use the following components to remotely access and control BLE devices:

  • Device with BleuIO Connected (Local Device): This is the device with the BleuIO dongle physically attached, acting as the local interface for sending and receiving BLE commands. It will run a small server to manage communication with the BLE dongle over a serial connection.
  • Remote Access Server (Cloud Server): This is the server that provides remote access to the local device. By connecting to the local server, it enables us to send commands and receive data from the BLE dongle remotely.
  • LocalTunnel: We’ll use LocalTunnel to generate a secure public URL, allowing the cloud server to communicate with the local device without needing complex router configurations. This URL acts as a bridge, making the local device accessible from anywhere with internet access.
  • Node.js: Both the local device and the cloud server will use Node.js to run simple server scripts that facilitate communication between the cloud server and the BLE dongle.

Step-by-Step Guide

Step 1: Setting Up the Local Server with BleuIO dongle connected to it

Local device is where the BleuIO dongle is physically connected. We’ll set up a Node.js server that communicates with the dongle through the serial port. This server will accept commands from the cloud server and send them to the BLE dongle, then return the response.

Install Node.js (if not already installed) on Local device.

Create a project folder and initialize a Node.js project:
mkdir local_serial_server
cd local_serial_server
npm init -y

Install Dependencies:

  • Install serialport to handle serial communication with the BLE dongle.Install socket.io to manage WebSocket connections.
npm install serialport socket.io

Create the Local Serial Server Script:

  • Create a file named local_serial_server.js. This script will:
    • Listen for WebSocket connections from the cloud server.Accept commands, pass them to the BleuIO dongle, and send back responses.

const http = require('http');
const { SerialPort } = require('serialport');
const socketIo = require('socket.io');

const server = http.createServer();
const io = socketIo(server);

// Define the serial port path and baud rate
const portPath = 'COM592'; // Replace 'COM3' with your dongle's port
const serialPort = new SerialPort({ path: portPath, baudRate: 9600 });

// Listen for incoming connections from the cloud server
io.on('connection', (socket) => {
console.log('Connected to cloud server');

// Receive command from cloud and send to serial port
socket.on('sendCommand', (command) => {
const formattedCommand = `${command}\r\n`;
console.log(`Sending command to serial port: ${formattedCommand}`);
serialPort.write(formattedCommand);
});

// Send serial responses to cloud server
serialPort.on('data', (data) => {
console.log(`Data received from serial port: ${data}`);
socket.emit('serialResponse', data.toString());
});
});

// Start the server on a specified port
const LOCAL_PORT = 4000; // Choose any port, ensure firewall allows it
server.listen(LOCAL_PORT, () => {
console.log(`Local Serial Server running on http://localhost:${LOCAL_PORT}`);
});

Find and Set the Serial Port Path:

In your local_serial_server.js, specify the correct serial port path for your BLE dongle (e.g., COM3 on Windows or /dev/ttyUSB0 on Linux).

Run the Local Serial Server:

  • Start the server by running: node local_serial_server.js

At this point, the local server is ready, but it’s only accessible to itself. Next, we’ll use LocalTunnel to make it accessible to the cloud server.

Step 2: Exposing the Local Server to the Internet Using LocalTunnel

Since local device 1 and cloud device are on different networks, we’ll need to create a public URL for the local server. There are several options to make a local server accessible publicly, including tools like ngrok, Pagekite, and LocalTunnel. For this tutorial, we’ll be using LocalTunnel as it’s free and easy to set up, but feel free to explore other solutions if they better meet your needs.

  1. Install LocalTunnel:
    • On local device, install LocalTunnel globally npm install -g localtunnel
  2. Start LocalTunnel:
    • Open a new terminal window on local device and run lt --port 4000
    • LocalTunnel will generate a public URL (e.g., https://five-sides-live.loca.lt). This URL will allow cloud device to access the local server running on port 4000 of Local device.
  3. Save the LocalTunnel URL:
    • Copy the URL provided by LocalTunnel. This URL will be used in the cloud server configuration on Cloud device.

Step 3: Setting Up the Cloud Server on Cloud device

In this setup, Cloud device will act as the cloud server that connects to local device’s LocalTunnel URL, allowing remote access to the BLE dongle. Cloud device can be any machine with Node.js installed—located anywhere, such as a remote computer in New York. You could also deploy this server on a cloud platform like Heroku, Replit, or Vercel for persistent access.

For simplicity, in this example, we’ll demonstrate how to set up the cloud server on another computer running Node.js, not connected to the same network as local device.

Create a Project Folder on cloud device and Initialize Node.js:

Open a terminal or command prompt on cloud device and create a folder for the cloud server mkdir cloud_serial_server
cd cloud_serial_server
npm init -y

Install Dependencies:

You’ll need express to serve the front-end pages and socket.io to manage WebSocket communication.

Install socket.io-client to allow the cloud server to connect to the LocalTunnel URL created on Local device
npm install express socket.io socket.io-client

Create the Cloud Server Script: In the cloud_serial_server folder, create a file named cloud_server.js. This script will Connect to the LocalTunnel URL (generated by Local device) and forward BLE commands to the local server.Serve the front-end pages (index.html and page2.html) for interacting with the BLE device remotely.

const express = require('express');
const http = require('http');
const socketIo = require('socket.io');
const ioClient = require('socket.io-client'); // Client for local serial server

const app = express();
const server = http.createServer(app);
const io = socketIo(server);

// Connect to the local serial server via WebSocket
const LOCAL_SERVER_URL = 'https://real-poets-count.loca.lt';
const localSocket = ioClient(LOCAL_SERVER_URL);

// Serve static files (frontend files for Page 1 and Page 2)
app.use(express.static('public'));

// Handle messages from Page 2 and forward to local serial server
io.on('connection', (socket) => {
  console.log('Client connected to cloud server');

  // Receive command from Page 2 and forward to local serial server
  socket.on('sendCommand', (command) => {
    console.log(`Forwarding command to local server: ${command}`);
    localSocket.emit('sendCommand', command); // Send to local serial server
  });

  socket.on('disconnect', () => {
    console.log('Client disconnected from cloud server');
  });
});

// Receive data from local serial server and forward to clients
localSocket.on('serialResponse', (data) => {
  console.log(`Received data from local serial server: ${data}`);
  io.emit('serialResponse', data); // Broadcast to connected clients
});

const PORT = process.env.PORT || 3000;
server.listen(PORT, () => {
  console.log(`Cloud server is running on http://localhost:${PORT}`);
});

Update the LocalTunnel URL in Cloud Server:

Replace the LOCAL_SERVER_URL in cloud_server.js with the LocalTunnel URL you generated on Local device (e.g., https://five-sides-live.loca.lt).

Run the Cloud Server:

Start the cloud server by running:bashCopy codenode cloud_server.js

This will start a server that listens for connections on Cloud device. You can open a web browser on Cloud device and go to http://localhost:3000 to access the front-end pages.

Access the Front-End Pages:

Open a browser and navigate to http://localhost:3000/index.html (for displaying BLE responses) and http://localhost:3000/page2.html (for sending commands).

Note: If you decide to deploy this server to a cloud platform (e.g., Heroku, Replit, or Vercel), replace localhost with the appropriate URL provided by the platform.

With this setup, Cloud device can be anywhere in the world, allowing you to control and receive data from the BLE dongle on Local device (in Stockholm) remotely.

Source code

The source code is available here at https://github.com/smart-sensor-devices-ab/remote_bleuio.git

Output

This tutorial demonstrates the potential of combining Node.js, BLE technology, and tunneling services for remote BLE access. The BleuIO dongle’s compatibility and simplicity make it an excellent choice for developers interested in building BLE applications across various operating systems.

Share this post on :

BLE Connectivity Test Tool using BleuIO

This article takes a deep dive into the capabilities of BleuIO, exploring how it can simplify BLE application testing, especially in complex environments like IoT, health, automotive, and smart home applications. Whether you are a seasoned developer or a newcomer, BleuIO provides a reliable, versatile tool to jumpstart your BLE projects. We’ll also cover an example project, the BLE Connectivity Test Tool, to illustrate BleuIO’s ease of use and the possibilities it opens up for developers.

Example Project: Building a BLE Connectivity Test Tool

To demonstrate the practicality and ease of using BleuIO, let’s look at a sample project: a BLE Connectivity Test Tool. This tool allows developers to test the connection stability of a BLE device, such as the HibouAir air quality monitor, by performing repeated connection and disconnection attempts. This example showcases how easy it is to set up a real-world testing scenario with BleuIO, perfect for those looking to explore connectivity applications.

This BLE Connectivity Test Tool will:

Scan for the HibouAir Device: Use BleuIO to locate the device based on its BLE address.

Initiate Connections: Repeatedly connect and disconnect from the HibouAir device, tracking the success and failure of each attempt.

Log Results: Display a summary at the end, indicating successful connections and any errors encountered.

Code Overview

The Python script below uses the official BleuIO Python library, which simplifies interaction with BLE devices. It uses AT commands to set the dongle in central mode, initiate connections, and disconnect after a set duration.

import time
from datetime import datetime
from bleuio_lib.bleuio_funcs import BleuIO

# Initialize the BleuIO dongle
dongle = BleuIO()

# Device address of HibouAir (replace with your actual address if different)
device_address = "[1]D1:79:29:DB:CB:CC"

# Number of connection attempts
test_count = 5
# Duration to stay connected in seconds
connect_duration = 2
# Delay between connection attempts
delay_between_tests = 5

# Counters for tracking connection results
successful_connections = 0
successful_disconnections = 0
connection_failures = 0

# Register scan callback to handle scanned device responses
def my_scan_callback(scan_input):
    print("\n\nScan Result: " + str(scan_input))

# Register event callback to handle connection events with timestamps
def my_evt_callback(evt_input):
    cbTime = datetime.now()
    currentTime = cbTime.strftime("%H:%M:%S")
    print("\n\n[" + str(currentTime) + "] Event: " + str(evt_input))

# Set up the callback functions
dongle.register_evt_cb(my_evt_callback)
dongle.register_scan_cb(my_scan_callback)

# Set the BLE role to central to initiate connections
dongle.at_central()

# Start the connection test
print("Starting BLE Connectivity Test with HibouAir device...\n")
for i in range(test_count):
    print(f"\nAttempt {i+1}/{test_count}")
    
    # Connect to the device
    connect_response = dongle.at_gapconnect(device_address)
    if connect_response.Ack.get("err") == 0:  # Check if the connection was successful
        print("Connection successful!")
        successful_connections += 1
        time.sleep(connect_duration)  # Stay connected for the specified duration

        # Disconnect
        disconnect_response = dongle.at_gapdisconnect()
        if disconnect_response.Ack.get("err") == 0:
            print("Disconnected successfully.")
            successful_disconnections += 1
        else:
            print("Error during disconnect:", disconnect_response.Ack.get("errMsg"))
            connection_failures += 1
    else:
        print("Connection failed:", connect_response.Ack.get("errMsg"))
        connection_failures += 1
    
    # Wait before the next attempt
    time.sleep(delay_between_tests)



# Display test results summary
print("\nConnectivity Test Summary:")
print(f"Total Connection Attempts: {test_count}")
print(f"Successful Connections: {successful_connections}")
print(f"Successful Disconnections: {successful_disconnections}")
print(f"Failures: {connection_failures}")

This tool’s simplicity shows how BleuIO can be used in the development and testing process, making it a preferred choice for developers who want to avoid BLE’s traditional complexities.

Output

Real-World Applications of BleuIO

IoT Device Testing: From smart home devices to industrial sensors, BleuIO is invaluable for testing the connectivity and data transmission of IoT devices.

Wearable Tech Development: For developers working on BLE-enabled wearables like fitness trackers or health monitors, BleuIO provides a straightforward way to test device connectivity.

Educational and Prototyping Tool: BleuIO’s ease of use makes it an excellent educational tool for introducing BLE concepts to students and rapid prototyping for startups.

Environmental Monitoring: As demonstrated with the HibouAir air quality monitor, BleuIO facilitates connectivity testing in applications involving environmental data, like CO2 monitoring, temperature, and humidity.

Why BleuIO for BLE Development?

Developing BLE applications often requires specialized hardware and software expertise. Traditional BLE development involves understanding intricate details of Bluetooth stacks, profiles, and data protocols. BleuIO alleviates this by offering a straightforward command structure that abstracts the technical details, allowing developers to quickly build and test applications.

In addition, BleuIO’s compact design makes it suitable for portable applications and rapid prototyping. From sensor connectivity to device tracking and monitoring, BleuIO proves to be a versatile asset for a range of BLE applications.

BleuIO offers an all-in-one solution for BLE development, bridging the gap between beginner-friendly usability and powerful functionality. With support for multiple platforms, easy integration with popular programming languages, and straightforward AT command access, BleuIO empowers developers to quickly and efficiently test, prototype, and deploy BLE applications.

By using tools like the BLE Connectivity Test Tool, developers can focus on creating innovative applications without needing to dive deep into the complexities of BLE. BleuIO enables developers to bring their ideas to life, from IoT to healthcare and beyond. Start your BLE development journey with BleuIO today and see the difference in ease and efficiency it brings to your projects.

Share this post on :