Get air quality Bluetooth LE sensor data from multiple devices

There are so many Bluetooth-enabled smart devices that it can be confusing how the technology connects the devices. Often we want to connect to multiple peripheral devices simultaneously to get advertised packets or do other operations. In this article, we will see how we can get advertised packets from two different Air quality monitoring sensor devices. 

For this project, we will use Chrome Web serial API to connect to a Bluetooth USB dongle. Using the serial port read/write operation we will scan for specific device advertised data and filter out what we need. After that, we decode the advertised packet to meaningful air quality data using the device documentation.

Requirments

  1. BleuIO Bluetooth USB dongle x1
  2. HibouAir Air quality monitor x2

Steps

At first, we will get the bleuIO javascript library from NPM. This library will help us easily connect to the serial port,wtire AT commands and read responses in real-time.

Type npm i bleuio on the command prompt of your project root folder.

After that, we create two files. index.html and script.js

Index.html will be responsible for the output and layouts of the project. 

Script.js will have the programming and logic to connect to the dongle and read/write data.

There will be two buttons connect and get data.

The connect button will connect to the bleuIO dongle using the serial port. 

The get data button will do several tasks.

At first, we put the dongle in a dual role (central mode) so that it can scan for peripheral devices. Then we will look for advertised data with their sensor ID one after another. 

Using the documentation from the air quality device, we decode the advertised data.

Finally, we print it out on the screen.

Here is the index.html file code

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>Bootstrap demo</title>
    <link
      href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0-beta1/dist/css/bootstrap.min.css"
      rel="stylesheet"
      integrity="sha384-0evHe/X+R7YkIZDRvuzKMRqM+OrBnVFBL6DOitfPri4tjfHxaWutUpFmBp4vmVor"
      crossorigin="anonymous"
    />
  </head>
  <body>
    <div class="container mt-5">
      <h1>Get air quality Bluetooth LE sensor data from multiple devices</h1>
      <br /><br /><br /><button
        id="connect"
        class="btn btn-success btn-lg me-5"
      >
        Connect
      </button>
      <button id="getData" class="btn btn-warning btn-lg ms-5">Get Data</button>
      <br />
      <br />
      <div id="loading" style="display: none">Fetching Data ...</div>
      <br />
      <div id="airData"></div>
    </div>

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

Here is script.js file code

import * as my_dongle from 'bleuio'
document.getElementById('connect').addEventListener('click', function(){
  my_dongle.at_connect()
  document.getElementById("connect").classList.add('disabled');
})
let dev1BoardID='45840D'
let dev2BoardID='60FDED'
document.getElementById('getData').addEventListener('click', function(){
    
    //show loading
    document.getElementById("loading").style.display = "block";
    //make the dongle in dual role , so it can scan for peripheral devices advertised data
    my_dongle.at_dual().then(()=>{
        //scan for a devices advertised data, a PM sensor
        my_dongle.at_findscandata(dev1BoardID,8).then((x)=>{
            //it returns an array of advertised data
            //from the array, we take the last one
            //it looks like this "[F9:0D:35:E7:72:65] Device Data [ADV]: 0201061BFF5B07050345840DB1031527FB002A010402040004000400000001"
            //then we split it by space
            //so we can get the advertised data only (the last part)
            return x[x.length-1].split(" ").pop()
        }).then((adv1)=>{ 
            //now lets decode the advertised data for this device using the device documentaion
            let pmEnvData=advDataDecode(adv1)
            //we do the same process to get advertised data of another device
            //after waiting 1 seconds
            setTimeout(()=>{
                my_dongle.at_findscandata(dev2BoardID,8).then((y)=>{
                    let adv2= y[y.length-1].split(" ").pop()
                    //again we decode the advertised data for this device using the device documentaion
                    let co2EnvData=advDataDecode(adv2)
                    //now merge pm data to this array
                    co2EnvData.pm1=pmEnvData.pm1
                    co2EnvData.pm25=pmEnvData.pm25
                    co2EnvData.pm10=pmEnvData.pm10
                    document.getElementById('airData').innerHTML=`
                    Air Quality data from PM and CO2 sensor devices<br/><br/>
                    CO2 : ${co2EnvData.co2} ppm<br/>
                    PM 1.0 : ${co2EnvData.pm1} µg/m³<br/>
                    PM 2.5 : ${co2EnvData.pm25} µg/m³<br/>
                    PM 10 : ${co2EnvData.pm10} µg/m³<br/>
                    Temperature : ${co2EnvData.temp} °C<br/>
                    Humidity : ${co2EnvData.hum} %rH<br/>
                    Pressure : ${co2EnvData.pressure} mbar<br/>
                    Light : ${co2EnvData.light} Lux<br/>

                    `
                    //hide loading
                    document.getElementById("loading").style.display = "none"; 
                })
            },1000)
            
        })
    })
    

  })

  const advDataDecode =((data)=>{
    let pos = data.indexOf("5B0705")
    let dt = new Date();
    let currentTs = dt.getFullYear() 
    + '/' 
    + (dt.getMonth() + 1).toString().padStart(2, "0") 
    + '/' 
    + dt.getDate().toString().padStart(2, "0")
    +' '
    +
    dt.getHours().toString().padStart(2, "0")
    +
    ':'
    +
    dt.getMinutes().toString().padStart(2, "0")
    +
    ':'
    +dt.getSeconds().toString().padStart(2, "0")
    let tempHex=parseInt('0x'+data.substr(pos+22,4).match(/../g).reverse().join(''))
    if(tempHex>1000)
        tempHex = (tempHex - (65535 + 1) )/10
    else
        tempHex = tempHex/10
    return {
      "boardID":data.substr(pos+8,6),
      "type":data.substr(pos+6,2),
      "light":parseInt('0x'+data.substr(pos+14,4).match(/../g).reverse().join('')),
      "pressure":parseInt('0x'+data.substr(pos+18,4).match(/../g).reverse().join(''))/10,
      "temp":tempHex,
      "hum":parseInt('0x'+data.substr(pos+26,4).match(/../g).reverse().join(''))/10,
      "pm1":parseInt('0x'+data.substr(pos+34,4).match(/../g).reverse().join(''))/10,
      "pm25":parseInt('0x'+data.substr(pos+38,4).match(/../g).reverse().join(''))/10,
      "pm10":parseInt('0x'+data.substr(pos+42,4).match(/../g).reverse().join(''))/10,
      "co2":parseInt('0x'+data.substr(pos+46,4)),
      "ts":currentTs
    }
})

Source code also available on github

https://github.com/shuhad/bluetooth_mutiple_device_read

Run the script

You can either clone the script from github or follow the instructions and make a new project.

For this project to run we may need a web build tool. I prefer using parcel.

https://parceljs.org/

install parcel if you don’t have

npm install --save-dev parcel

Now run the script using

parcel index.html

Output

The script can be modified to read more than two devices as required.

Share this post on :

Smart Phone Controlled Home Automation using Raspberry Pi and BleuIO

Home automation involves automating household environment equipment. To achieve that, we have created a smart bulb that can be controlled remotely using smart phone app. The aim of this project is to control different home appliances using smartphone at your home.

Introduction

This example is showing how to control a GPIO pin on a RaspberryPi remotely from a smart phone (or another BleuIO Dongle).

For this example we will need:

  • A RaspberryPi
  • A BleuIO Dongle (https://www.bleuio.com/)
  • Our example python script (https://github.com/smart-sensor-devices-ab/bleuio_rpi_switch_example)
  • A way to connect to the GPIO Pin (Like a 5V Relay and a Lightbulb)

WARNING – THIS PROJECT INVOLVES HIGH VOLTAGES THAT CAN CAUSE SERIOUS INJURY OR DEATH. PLEASE TAKE ALL NECESSARY PRECAUTIONS, AND TURN OFF ALL POWER TO A CIRCUIT BEFORE WORKING ON IT.

Connecting the relay

Beware:

Always be very careful when experimenting with AC, electrical shock can result in serious injuries! NOTICE OF RISK; DISCLAIMER OF LIABILITY

alt text

Instructions for bleuio_rpi_switch_example.py

  • Connect the BleuIO Dongle to your RaspberryPi.
  • Edit the variable ‘switch’ in the script to the GPIO pin you want to use. (You can use the command pinout to get a graphical view showing you the GPIO pins for the board)
  • Finally just run python script and and use your phone to connect to the BleuIO Dongle and send on/off messages to controll the GPIO!

Instructions for connecting to the BleuIO from mobile

  • Download a BLE scanning App that can connect and read/write to a device. (Like nRFConnect or BLEScanner)
    AndroidIOS
  • Look for the dongle, it will be advertising as ‘BleuIO’.
  • Connect to the BleuIO Dongle.
  • To enable BleuIO to recieve commands you must first write 0x01 to the Flow Control characteristic (UUID: 0783b03e-8535-b5a0-7140-a304d2495cb9)
  • Now you can write to the Server RX Data characteristic (UUID: 0783b03e-8535-b5a0-7140-a304d2495cba) to control the GPIO.
    |CMD|Effect|
    |–|–|
    |“SW=1”| ON|
    |“SW=0”| OFF|

The script

Here is the python script that receives the messages from smart phone app and helps control the light.

#!/usr/bin/python3
# Copyright 2022 Smart Sensor Devices in Sweden AB
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
# The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

import time
import serial.tools.list_ports
import serial
import RPi.GPIO as io


switch = 7  # Edit this to suit your setup! (7 = GPIO 04), use command pinout to graphically show you the GPIO pins for the board
io.setmode(io.BOARD)
io.setup(switch, io.OUT)

master_array = []
index = 1
dongle_port = ""

print("\nWelcome to BleuIO RaspberryPi Switch Example!\n")

print("\nPlease insert dongle...")
try:
    while len(master_array) == 0:
        m_ports = serial.tools.list_ports.comports(include_links=False)
        for port in m_ports:
            if str(port.hwid).__contains__("VID:PID=2DCF"):
                master = port.device + " " + port.hwid
                if master.__contains__("VID:PID=2DCF:6002"):
                    print("Found dongle in port: %s" % port.device)
                    master_array.append(master)
                    dongle_port = port
                    break

    for dongle in master_array:
        print("\nConnecting to BleuIO @ %s\n" % dongle)

    time.sleep(0.5)
    dongle_conn = serial.Serial(
        dongle_port.device,
        115200,
        timeout=1,
    )

    if not dongle_conn.is_open:
        dongle_conn.open()

    print("Starting Advertising...")
    dongle_conn.write("AT+GAPDISCONNECTALL\rAT+DUAL\rAT+ADVSTART\rATI\r".encode())
    read_tries = 0
    dongle_resp = ""
    while read_tries < 20:
        dongle_resp = dongle_conn.readline().decode()
        if "Not Advertising" in dongle_resp:
            dongle_conn.write("AT+ADVSTART\r")
        if b"Advertising\r\n" in dongle_resp.encode():
            break
        read_tries += 1
        time.sleep(0.01)

    if dongle_resp:
        print("BleuIO is %s" % dongle_resp)
    else:
        print("ERROR! No response...")
        exit()

    print(
        "Going into loop, waiting for signal to turn switch on/off...\n(Press Ctrl+C to abort)"
    )
    while True:
        try:
            dongle_resp = dongle_conn.readline().decode()
            if "SW=0" in dongle_resp:
                print("Turn Switch off!")
                io.output(switch, io.LOW)
            if "SW=1" in dongle_resp:
                print("Turn Switch on!")
                io.output(switch, io.HIGH)
        except KeyboardInterrupt:
            if dongle_conn.is_open:
                dongle_conn.write("AT+GAPDISCONNECTALL\rAT+ADVSTOP\r".encode())
                dongle_conn.close()
                io.cleanup()
            print("\nBye!")
            exit()

except Exception as e:
    print("(ERROR: %s)" % (e))

Output

We have tested the script using nRFConnect app from both IOS and Android phone to turn on/off the light bulb. Here is the output of this project.

Share this post on :

BLE USB dongle throughput measurement

Introduction

Here we will describe two quick ways of measuring the data throughput of the BleuIO Dongle.
For both examples we are going to need a BleuIO Dongle, another Bluetooth device (like another Bleuio Dongle) and a computer with Python (minimum version: 3.6) installed.

For the first measurement example, measuring the BLE data throughput, you will need one of the following supported development kits from Nordic Semiconductor:

  • nRF52840 DK (PCA10056)
  • nRF52840 Dongle (PCA10059)
  • nRF52833 DK (PCA10100)
  • nRF52 DK (PCA10040)
  • nRF51 DK (PCA10028)
  • nRF51 Dongle (PCA10031)

The first measurement example is the actual BLE data throughput. For this we will use a BleuIO Dongle and Wireshark. (For help on how to setup Wireshark and requirements go to this link: https://infocenter.nordicsemi.com/topic/ug_sniffer_ble/UG/sniffer_ble/intro.html ).
We will also utilize a simple python script that sends a set amount of data. For this measurement you can ignore the throughput print at the end of the script.

The second measurement example is for measuring the actual data being transferred over the USB as a Virtual COM port (via the CDC protocol).
We will be using the same simple script that will send a set amount of data and time when the transfer starts and then stops. Then divide the amount of data with the time the transfer took to get the throughput.

Notice : Interference can be caused by other wireless networks, other 2.4 GHz frequency devices, and high voltage devices that generate electromagnetic interference. This have impact on the measurement of throughput. To avoid interference, select wireless free space or use a shield box.

Instructions for BLE data throughput

  • For best result place the nRF Dev Kit between the BleuIO Dongle and your target device.
  • Open Wireshark and double-click the ‘nRF Sniffer for Bluetooth LE’.
  • Make sure the target Bluetooth device is advertising and find in the the scroll-down list.
  • Choose ‘IO/Data’ under the ‘Analysis’ menu tab.
  • Click the ‘+’ button to add new graphs. Add ‘bytes per seconds’ and/or ‘bit per seconds’.
  • Modify the script by filling in the relevant information into the variables ‘your_com_port’‘target_mac_addr’ and ‘write_handle’.
  • Run the python script.
  • You can now observe the graph showing the BLE Data throughput!

Instructions for USB port data throughput

This is the second measurement example for measuring the actual point to point data transfer between the two USB ports.

  • Connect the dongle to your computer. (Look up the COM port your dongle uses and paste it in the script in the variable ‘your_com_port’)
  • Scan (Using AT+GAPSCAN) after the device you wish to send the data to. Copy the mac address of the device into the script in the variable ‘target_mac_addr’.
  • Connect to the device and look up the handle of the characteristic you want to write to and paste into the script in the variable ‘write_handle’.
  • Finally just run python script and the throughput will be displayed at the end!

The script

import datetime
import serial
import time
import string
import random

connecting_to_dongle = True
trying_to_connect = False

# Change this to the com port your dongle is connected to.
your_com_port = "COM20"
# Change this to the mac address of your target device.
target_mac_addr = "[0]40:48:FD:E5:2C:F2"
# Change this to the handle of the characteristic on your target device.
write_handle = "0011"

# You can experiment with the packet length, increasing or decreasing it and see how that effect the throughput
packet_length = 150
# 1 Megabytes = 1000000 Bytes
file_size = 0.5 * 1000000
end_when = file_size / packet_length
send_counter = 0

# Random data string generator
def random_data_generator(size=packet_length, chars=string.digits + string.digits):
    return "".join(random.choice(chars) for _ in range(size))


print("Connecting to dongle...")
while connecting_to_dongle:
    try:
        console = serial.Serial(
            port=your_com_port,
            baudrate=115200,
            parity="N",
            stopbits=1,
            bytesize=8,
            timeout=0,
        )
        if console.is_open.__bool__():
            connecting_to_dongle = False
    except:
        print("Dongle not connected. Please reconnect Dongle.")
        time.sleep(5)

print("Connected to Dongle.")
console.write(str.encode("AT+GAPDISCONNECT\r"))
start = input("Press Enter to start.\n\r>> ")

console.write(str.encode("ATE0\r"))
console.write(str.encode("AT+DUAL\r"))
connected = "0"
while connected == "0":
    time.sleep(0.5)
    if not trying_to_connect:
        # change to Mac address of the device you want to connect to
        console.write(str.encode("AT+GAPCONNECT=" + target_mac_addr + "\r"))
        trying_to_connect = True
    dongle_output2 = console.read(console.in_waiting)
    time.sleep(2)
    print("Trying to connect to Peripheral...")
    if not dongle_output2.isspace():
        if dongle_output2.decode().__contains__("\r\nCONNECTED."):
            connected = "1"
            print("Connected!")
            time.sleep(8)
        if dongle_output2.decode().__contains__("\r\nDISCONNECTED."):
            connected = "0"
            print("Disconnected!")
            trying_to_connect = False
        dongle_output2 = " "

start2 = input("Press Enter to sending.\n\r>> ")
start_time = time.mktime(datetime.datetime.today().timetuple())
console.write(
    str.encode(
        "AT+GATTCWRITEWRB=" + write_handle + " " + random_data_generator() + "\r"
    )
)
while 1:
    dongle_output = console.read(console.in_waiting)
    if send_counter > end_when:
        end_time = time.mktime(datetime.datetime.today().timetuple())
        break
    # Change to the handle of the characteristic you want to write to
    if "handle_evt_gattc_write_completed" in str(dongle_output):
        console.write(
            str.encode(
                "AT+GATTCWRITEWR=" + write_handle + " " + random_data_generator() + "\r"
            )
        )
        send_counter = send_counter + 1
    try:
        if not dongle_output.decode() == "":
            print(dongle_output.decode())
    except:
        print(dongle_output)

time_elapsed = end_time - start_time
time.sleep(0.1)

print("*" * 25)
print("Transfer Complete in: " + str(time_elapsed) + " seconds")
print(str(packet_length * send_counter) + "bytes sent.")
print("*" * 25)
print(
    "Throughput via USB (Virtual COM port): "
    + str((packet_length * send_counter) / time_elapsed)
    + " Bytes per seconds"
)
print("*" * 25)
Share this post on :

Show Bluetooth LE Sensor readings on LCD screen connected to STM32

The aim of this Bluetooth LE project is to read air quality sensor data and show it on an LCD display which is connected to STM32 board. A web browser will read the sensor data and pass it to STM32 board using BleuIO.

1. Introduction

The project is based on STM32 Nucleo-144 which controls LCD display using BleuIO.

For this project, we will need two BleuIO USB dongles, one connected to the Nucleo board and the other to a computer running the web script and a HibouAir – Air quality monitoring device .
When the BleuIO Dongle is connected to the Nucleo boards USB port the STM32 will recognize it and directly start advertising. This allows the Dongle on the computer port connect with the web script.

With the web script on the computer, we can scan and get air quality sensor data from HibouAir. Then we send these data to LCD screen connected to STM32 using Bluetooth.

We have used a STM32 Nucleo-144 development board with STM32H743ZI MCU (STM32H743ZI micro mbed-Enabled Development Nucleo-144 series ARM® Cortex®-M7 MCU 32-Bit Embedded Evaluation Board) for this example. This development board has a USB host where we connect the BleuIO dongle.

If you want to use another setup you will have to make sure it support USB Host and beware that the GPIO setup might be different and may need to be reconfigured in the .ioc file.

About the Code

The project source code is available at Github.

https://github.com/smart-sensor-devices-ab/stm32_ble_sensor_lcd.git

Either clone the project, or download it as a zip file and unzip it, into your STM32CubeIDE workspace.

If you download the project as a zip file you will need to rename the project folder from ‘stm32_bleuio_lcd-master’ to ‘stm32_bleuio_lcd’

Connect the SDA to PF0 on the Nucleo board and SCL to PF1.

Then setup I2C2 in the STM32Cube ioc file as follows. (Make sure to change the I2C speed frequency to 50 KHz as per LCD display requirements.)

In the USBH_CDC_ReceiveCallback function in USB_HOST\usb_host.c we copy the CDC_RX_Buffer into a external variable called dongle_response that is accessable from the main.c file.

void USBH_CDC_ReceiveCallback(USBH_HandleTypeDef *phost)
{
  	if(phost == &hUsbHostFS)
  	{
  		// Handles the data recived from the USB CDC host, here just printing it out to UART
  		rx_size = USBH_CDC_GetLastReceivedDataSize(phost);
		HAL_UART_Transmit(&huart3, CDC_RX_Buffer, rx_size, HAL_MAX_DELAY);

		// Copy buffer to external dongle_response buffer
		strcpy((char *)dongle_response, (char *)CDC_RX_Buffer);

		// Reset buffer and restart the callback function to receive more data
		memset(CDC_RX_Buffer,0,RX_BUFF_SIZE);
		USBH_CDC_Receive(phost, CDC_RX_Buffer, RX_BUFF_SIZE);
  	}

  	return;
}

In main.c we create a simple intepreter so we can react to the data we are recieving from the dongle.

void dongle_interpreter(uint8_t * input)
{

	if(strlen((char *)input) != 0)
	{
		if(strstr((char *)input, "\r\nADVERTISING...") != NULL)
		{
			isAdvertising = true;
		}
		if(strstr((char *)input, "\r\nADVERTISING STOPPED") != NULL)
		{
			isAdvertising = false;
		}
		if(strstr((char *)input, "\r\nCONNECTED") != NULL)
		{
			isConnected = true;
			HAL_GPIO_WritePin(GPIOE, GPIO_PIN_1, GPIO_PIN_SET);
		}
		if(strstr((char *)input, "\r\nDISCONNECTED") != NULL)
		{
			isConnected = false;
			HAL_GPIO_WritePin(GPIOE, GPIO_PIN_1, GPIO_PIN_RESET);
		}


		if(strstr((char *)input, "L=0") != NULL)
		{

			isLightBulbOn = false;
			//HAL_GPIO_WritePin(Lightbulb_GPIO_Port, Lightbulb_Pin, GPIO_PIN_RESET);
			lcd_clear();

			writeToDongle((uint8_t*)DONGLE_SEND_LIGHT_OFF);

			uart_buf_len = sprintf(uart_tx_buf, "\r\nClear screen\r\n");
			HAL_UART_Transmit(&huart3, (uint8_t *)uart_tx_buf, uart_buf_len, HAL_MAX_DELAY);
		}

		if(strstr((char *)input, "L=1") != NULL)
		{
				isLightBulbOn = true;
				writeToDongle((uint8_t*)DONGLE_SEND_LIGHT_ON);


				lcd_clear();

				lcd_write(input);

		}

	}
	memset(&dongle_response, 0, RSP_SIZE);
}

We put the intepreter function inside the main loop.

/* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */
    MX_USB_HOST_Process();

    /* USER CODE BEGIN 3 */
    // Simple handler for uart input
    handleUartInput(uartStatus);

    // Inteprets the dongle data
    dongle_interpreter(dongle_response);

	// Starts advertising as soon as the Dongle is ready.
	if(!isAdvertising && !isConnected && isBleuIOReady)
	{
		HAL_Delay(200);
		writeToDongle((uint8_t*)DONGLE_CMD_AT_ADVSTART);
		isAdvertising = true;
	}
  }
  /* USER CODE END 3 */

Using the example project

What we will need

Importing as an Existing Project

From STM32CubeIDE choose File>Import…

Then choose General>Existing Projects into Workspace then click ‘Next >’

Make sure you’ve choosen your workspace in ‘Select root directory:’

You should see the project “stm32_bleuio_SHT85_example”, check it and click ‘Finish’.

Running the example

Upload the the code to STM32 and run the example. The USB dongle connect to STM32 will start advertising automatically.

Send Sensor data to LCD screen from a web browser

Connect the BleuIO dongle to the computer. Run the web script to connect to the other BleuIO dongle on the STM32. Now you can send sensor data to the LCD screen.

For this script to work, we need

Create a simple Html file called index.html which will serve as the frontend of the script. This Html file contains some buttons that help connect, read advertised data from the HibouAir to get air quality sensor data, and send this data to the LCD screen which is connected to stm32.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link
      href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css"
      rel="stylesheet"
      integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3"
      crossorigin="anonymous"
    />
    <title>Bluetooth LE Air quality sensor data to LCD screen</title>
  </head>
  <body class="mt-5">
    <div class="container mt-5">
      <img
        src="https://www.bleuio.com/blog/wp-content/themes/bleuio/images/logo.png"
      />
      <h1 class="mb-5">Bluetooth LE Air quality sensor data to LCD screen</h1>

      <div class="row">
        <div class="col-md-4 pt-5">
          <button class="btn btn-success mb-2" id="connect">Connect</button>
          <form method="post" id="sendDataForm" name="sendMsgForm" hidden>
            <div class="mb-3">
              <label for="sensorID" class="form-label">Sensor ID</label>
              <input
                type="text"
                class="form-control"
                name="sensorID"
                id="sensorID"
                required
                maxlength="60"
                value="0578E0"
              />
            </div>

            <button type="submit" class="btn btn-primary">Get Data</button>
          </form>
          <br />
          <button class="btn btn-danger" id="clearScreen" disabled>
            Clear screen
          </button>
        </div>
        <div class="col-md-8">
          <img src="air_quality_lcd.jpg" class="img-fluid" />
        </div>
      </div>
    </div>

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

Create a js file called script.js and include it at the bottom of the Html file. This js file uses the BleuIO js library to write AT commands and communicate with the other dongle.

import * as my_dongle from 'bleuio'
import 'regenerator-runtime/runtime'

const dongleToConnect='[0]40:48:FD:E5:2F:17'
//const sensorID = '0578E0'
document.getElementById('connect').addEventListener('click', function(){
  my_dongle.at_connect()
  document.getElementById("clearScreen").disabled=false;
  document.getElementById("connect").disabled=true;
  document.getElementById("sendDataForm").hidden=false;
})

document.getElementById("sendDataForm").addEventListener("submit", function(event){
    event.preventDefault()
    const sensorID = document.getElementById('sensorID').value
    getSensorData(sensorID)
    setInterval(function () {getSensorData(sensorID)}, 10000);

   
  });

  const getSensorData =((sensorID)=>{
    my_dongle.ati().then((data)=>{
        //make central if not
        if(JSON.stringify(data).includes("Peripheral")){
            console.log('peripheral')
            my_dongle.at_dual().then((x)=>{
                console.log('central now')
            })
        }        
    })
    .then(()=>{
        // connect to dongle
        my_dongle.at_getconn().then((y)=>{
            if(JSON.stringify(y).includes(dongleToConnect)){
                console.log('already connected')
            }else{
                my_dongle.at_gapconnect(dongleToConnect).then(()=>{
                    console.log('connected successfully')
                })
            }
        })
        .then(async()=>{
           return my_dongle.at_findscandata(sensorID,6).then((sd)=>{
                console.log('scandata',sd)
                let advData = sd[sd.length - 1].split(" ").pop()
                let positionOfID= advData.indexOf(sensorID);
                let tempHex = advData.substring(positionOfID+14, positionOfID+18)
                let temp = parseInt('0x'+tempHex.match(/../g).reverse().join(''))/10;

                let co2Hex = advData.substring(positionOfID+38, positionOfID+42)
                let co2 = parseInt('0x'+co2Hex);
                //console.log(temp,co2)
                return {
                    'CO2' :co2,
                    'Temp' :temp,                       
                  }
            })
        })
        .then((x)=>{
            console.log(x.CO2)
            console.log(x.Temp)
            var theVal = "L=1 SENSOR ID "+sensorID+"    TEMPERATURE " + x.Temp + ' °c    CO2 '+ x.CO2+' ppm';
            console.log('Message Send 1 ')
            // send command to show data
            my_dongle.at_spssend(theVal).then(()=>{
                console.log('Message Send '+theVal)
            })
        })
        
    })
})

document.getElementById('clearScreen').addEventListener('click', function(){
    my_dongle.ati().then((data)=>{
        //make central if not
        if(JSON.stringify(data).includes("Peripheral")){
            console.log('peripheral')
            my_dongle.at_central().then((x)=>{
                console.log('central now')
            })
        }
    })
    .then(()=>{
        // connect to dongle
        my_dongle.at_getconn().then((y)=>{
            if(JSON.stringify(y).includes(dongleToConnect)){
                console.log('already connected')
            }else{
                my_dongle.at_gapconnect(dongleToConnect).then(()=>{
                    console.log('connected successfully')
                })
            }
        })
        .then(()=>{
            // send command to clear the screen
            my_dongle.at_spssend('L=0').then(()=>{
                console.log('Screen Cleared')
            })
        })
        
    })
})

The script has a button to connect to COM port on the computer. There is a text field where you can write sensor ID of the air quality monitor device. Once connected, the script will try to get advertised data from the sensor and convert it to a meaningful data. After that it will send this data to the STM32 board which then display on the LCD screen.

To connect to the BleuIO dongle on the STM32, make sure the STM32 is powered up and a BleuIO dongle is connected to it.

Get the MAC address

Follow the steps to get the MAC address of the dongle that is connected to STM32

- Open this site https://bleuio.com/web_terminal.html and click connect to dongle.
- Select the appropriate port to connect.
- Once it says connected, type ATI. This will show dongle information and current status.
- If the dongle is on peripheral role, set it to central by typing AT+CENTRAL
- Now do a gap scan by typing AT+GAPSCAN
- Once you see your dongle on the list ,stop the scan by pressing control+c
- Copy the ID and paste it into the script (script.js) line #4

Run the web script

You will need a web bundler. You can use parcel.js

Once parcel js installed, go to the root directory of web script and type “parcel index.html”. This will start your development environment.

Open the script on a browser. For this example we opened http://localhost:1234

You can easily connect to the dongle and see air quality data on the LCD screen. The response will show on browser console screen.

The web script looks like this

Output

The message will show on the LCD screen.

Share this post on :

Develop Bluetooth LE application easily with JAVA and BleuIO

This article is a guide for creating Java applications that can write AT commands to BleuIO and access nearby Bluetooth Low Energy devices. This example project will be helpful to create BLE application easily.

Requirments

  1. BleuIO. (Bluetooth Low Energy USB dongle)
  2. NetBean

About the Project

The script has a COM port settings section. This section shows connected devices to the COM port. Using jSerialComm we get the list of COM ports and show it on a dropdown menu to select.

The connect and disconnect buttons manages the connection of the dongle.

Once we are connected to the BleuIO dongle, we will be able to write AT commands to the dongle using serial port write command.

We have a button at the bottom right to stop on going process. This button is effective when we write AT+GAPSCAN. The BleuIO dongle will keep scanning for nearby BLE devices unless we stop the process.

The response will be available on the output panel.

List of AT commands are available at https://www.bleuio.com/getting_started/docs/commands/

Step 1: Clone the project

Step 2 : Run the project

Connect BleuIO dongle into the computer.

Run the project using NetBean play button.

Alternatively we can open the project using command line interface by going to the root folder of the project and type

java -jar "dist/JavaBleuIO.jar"

The output will look like this.

Lets select a COM port where the BleuIO dongle is connected and click connect.

Now we will be able to write AT commands and see the response from the dongle on output screen.

Share this post on :

Bluetooth LE based LCD messenger using STM32 and BleuIO

The aim of this project is to send message via Bluetooth using a web browser or smartphone to a LCD display which is connected to STM32 board.

1. Introduction

The project is based on STM32 Nucleo-144 which controls LCD display using BleuIO.

For this project, we will need two BleuIO USB dongles, one connected to the Nucleo board and the other to a computer, running the web script.
When the BleuIO Dongle is connected to the Nucleo boards USB port the STM32 will recognize it and directly start advertising. This allows the Dongle on the computer port connect with the web script.

With the web script on the computer, we can send message to LCD screen connected to STM32 using BleuIO.

We have used a STM32 Nucleo-144 development board with STM32H743ZI MCU (STM32H743ZI micro mbed-Enabled Development Nucleo-144 series ARM® Cortex®-M7 MCU 32-Bit Embedded Evaluation Board) for this example. This development board has a USB host where we connect the BleuIO dongle.

If you want to use another setup you will have to make sure it support USB Host and beware that the GPIO setup might be different and may need to be reconfigured in the .ioc file.

About the Code

The project source code is available at Github.

https://github.com/smart-sensor-devices-ab/stm32_bleuio_lcd.git

Either clone the project, or download it as a zip file and unzip it, into your STM32CubeIDE workspace.

If you download the project as a zip file you will need to rename the project folder from ‘stm32_bleuio_lcd-master’ to ‘stm32_bleuio_lcd’

Connect the SDA to PF0 on the Nucleo board and SCL to PF1.

Then setup I2C2 in the STM32Cube ioc file as follows. (Make sure to change the I2C speed frequency to 50 KHz as per LCD display requirements.)

In the USBH_CDC_ReceiveCallback function in USB_HOST\usb_host.c we copy the CDC_RX_Buffer into a external variable called dongle_response that is accessable from the main.c file.

void USBH_CDC_ReceiveCallback(USBH_HandleTypeDef *phost)
{
  	if(phost == &hUsbHostFS)
  	{
  		// Handles the data recived from the USB CDC host, here just printing it out to UART
  		rx_size = USBH_CDC_GetLastReceivedDataSize(phost);
		HAL_UART_Transmit(&huart3, CDC_RX_Buffer, rx_size, HAL_MAX_DELAY);

		// Copy buffer to external dongle_response buffer
		strcpy((char *)dongle_response, (char *)CDC_RX_Buffer);

		// Reset buffer and restart the callback function to receive more data
		memset(CDC_RX_Buffer,0,RX_BUFF_SIZE);
		USBH_CDC_Receive(phost, CDC_RX_Buffer, RX_BUFF_SIZE);
  	}

  	return;
}

In main.c we create a simple intepreter so we can react to the data we are recieving from the dongle.

void dongle_interpreter(uint8_t * input)
{

	if(strlen((char *)input) != 0)
	{
		if(strstr((char *)input, "\r\nADVERTISING...") != NULL)
		{
			isAdvertising = true;
		}
		if(strstr((char *)input, "\r\nADVERTISING STOPPED") != NULL)
		{
			isAdvertising = false;
		}
		if(strstr((char *)input, "\r\nCONNECTED") != NULL)
		{
			isConnected = true;
			HAL_GPIO_WritePin(GPIOE, GPIO_PIN_1, GPIO_PIN_SET);
		}
		if(strstr((char *)input, "\r\nDISCONNECTED") != NULL)
		{
			isConnected = false;
			HAL_GPIO_WritePin(GPIOE, GPIO_PIN_1, GPIO_PIN_RESET);
		}


		if(strstr((char *)input, "L=0") != NULL)
		{

			isLightBulbOn = false;
			//HAL_GPIO_WritePin(Lightbulb_GPIO_Port, Lightbulb_Pin, GPIO_PIN_RESET);
			lcd_clear();

			writeToDongle((uint8_t*)DONGLE_SEND_LIGHT_OFF);

			uart_buf_len = sprintf(uart_tx_buf, "\r\nClear screen\r\n");
			HAL_UART_Transmit(&huart3, (uint8_t *)uart_tx_buf, uart_buf_len, HAL_MAX_DELAY);
		}

		if(strstr((char *)input, "L=1") != NULL)
		{
				isLightBulbOn = true;
				writeToDongle((uint8_t*)DONGLE_SEND_LIGHT_ON);


				lcd_clear();

				lcd_write(input);

		}

	}
	memset(&dongle_response, 0, RSP_SIZE);
}

We put the intepreter function inside the main loop.

/* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */
    MX_USB_HOST_Process();

    /* USER CODE BEGIN 3 */
    // Simple handler for uart input
    handleUartInput(uartStatus);

    // Inteprets the dongle data
    dongle_interpreter(dongle_response);

	// Starts advertising as soon as the Dongle is ready.
	if(!isAdvertising && !isConnected && isBleuIOReady)
	{
		HAL_Delay(200);
		writeToDongle((uint8_t*)DONGLE_CMD_AT_ADVSTART);
		isAdvertising = true;
	}
  }
  /* USER CODE END 3 */

Using the example project

What we will need

Importing as an Existing Project

From STM32CubeIDE choose File>Import…

Then choose General>Existing Projects into Workspace then click ‘Next >’

Make sure you’ve choosen your workspace in ‘Select root directory:’

You should see the project “stm32_bleuio_SHT85_example”, check it and click ‘Finish’.

Running the example

Upload the the code to STM32 and run the example. The USB dongle connect to STM32 will start advertising automatically.

Send Messages to LCD screen from a web browser

Connect the BleuIO dongle to the computer. Run the web script to connect to the other BleuIO dongle on the STM32. Now you can send messages to the LCD screen.

For this script to work, we need

Create a simple Html file called index.html which will serve as the frontend of the script. This Html file contains some buttons that help connect and read advertised data from the remote dongle, which is connected to stm32.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link
      href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css"
      rel="stylesheet"
      integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3"
      crossorigin="anonymous"
    />
    <title>Send message to STM32 LCD display using Bluetooth LE</title>
  </head>
  <body class="mt-5">
    <div class="container mt-5">
      <h1 class="mb-5">Send message to STM32 LCD display using Bluetooth LE</h1>
      <button class="btn btn-success" id="connect">Connect</button>
      <div class="row">
        <div class="col-md-4">
          <form method="post" id="sendMsgForm" name="sendMsgForm">
            <div class="mb-3">
              <label for="msgToSend" class="form-label">Your Message</label>
              <input
                type="text"
                class="form-control"
                name="msgToSend"
                id="msgToSend"
                required
                maxlength="60"
              />
            </div>

            <button type="submit" class="btn btn-primary">Submit</button>
          </form>
        </div>
      </div>

      <br />
      <button class="btn btn-danger" id="clearScreen" disabled>
        Clear screen
      </button>
    </div>

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

Create a js file called script.js and include it at the bottom of the Html file. This js file uses the BleuIO js library to write AT commands and communicate with the other dongle.

import * as my_dongle from 'bleuio'
const dongleToConnect='[0]40:48:FD:E5:2F:17'
document.getElementById('connect').addEventListener('click', function(){
  my_dongle.at_connect()
  document.getElementById("clearScreen").disabled=false;
  document.getElementById("connect").disabled=true;
  document.getElementById("sendMsgForm").hidden=false;
})

document.getElementById("sendMsgForm").addEventListener("submit", function(event){
    event.preventDefault()
    console.log('here')

    
    my_dongle.ati().then((data)=>{
        //make central if not
        if(JSON.stringify(data).includes("Peripheral")){
            console.log('peripheral')
            my_dongle.at_central().then((x)=>{
                console.log('central now')
            })
        }        
    })
    .then(()=>{
        // connect to dongle
        my_dongle.at_getconn().then((y)=>{
            if(JSON.stringify(y).includes(dongleToConnect)){
                console.log('already connected')
            }else{
                my_dongle.at_gapconnect(dongleToConnect).then(()=>{
                    console.log('connected successfully')
                })
            }
        })
        .then(()=>{
            var theVal = "L=1 " + document.getElementById('msgToSend').value;
            console.log('Message Send 1 '+theVal)
            // send command to show data
            my_dongle.at_spssend(theVal).then(()=>{
                console.log('Message Send '+theVal)
            })
        })
        
    })
  });



document.getElementById('clearScreen').addEventListener('click', function(){
    my_dongle.ati().then((data)=>{
        //make central if not
        if(JSON.stringify(data).includes("Peripheral")){
            console.log('peripheral')
            my_dongle.at_central().then((x)=>{
                console.log('central now')
            })
        }
    })
    .then(()=>{
        // connect to dongle
        my_dongle.at_getconn().then((y)=>{
            if(JSON.stringify(y).includes(dongleToConnect)){
                console.log('already connected')
            }else{
                my_dongle.at_gapconnect(dongleToConnect).then(()=>{
                    console.log('connected successfully')
                })
            }
        })
        .then(()=>{
            // send command to clear the screen
            my_dongle.at_spssend('L=0').then(()=>{
                console.log('Screen Cleared')
            })
        })
        
    })
})

The script has a button to connect to COM port on the computer. There is a text field where you can write your message. Your messages will be displayed on LCD screen connected to STM32 board.

To connect to the BleuIO dongle on the STM32, make sure the STM32 is powered up and a BleuIO dongle is connected to it.

Get the MAC address

Follow the steps to get the MAC address of the dongle that is connected to STM32

- Open this site https://bleuio.com/web_terminal.html and click connect to dongle.
- Select the appropriate port to connect.
- Once it says connected, type ATI. This will show dongle information and current status.
- If the dongle is on peripheral role, set it to central by typing AT+CENTRAL
- Now do a gap scan by typing AT+GAPSCAN
- Once you see your dongle on the list ,stop the scan by pressing control+c
- Copy the ID and paste it into the script (script.js) line #2

Run the web script

You will need a web bundler. You can use parcel.js

Once parcel js installed, go to the root directory of web script and type “parcel index.html”. This will start your development environment.

Open the script on a browser. For this example we opened http://localhost:1234

You can easily connect to the dongle and send your message to the LCD screen. The response will show on browser console screen.

The web script looks like this

Output

The message will show on the LCD screen.

Video of working project

Share this post on :

Send BLE sensor data over MQTT using BleuIO

We’re living in the world of connected devices. The internet of things helps us live and work smarter, as well as gain complete control over our lives. One of the latest technological advancements in IoT is the MQTT gateway, which acts as a mediator between the cloud and IoT platforms.

MQTT stands for Message Queuing Telemetry Transport. It’s among the key communication protocols for the internet of things devices and local networks. It’s an ideal protocol for communication between smart devices or machine-to-machine communication.

What Is MQTT Gateway?

Generally, the MQTT gateway can be defined as an intermediary between any internet of things platform and sensors. It works by getting data from these sensors or smart devices and translating it into MQTT. It then transmits that data to either the internet of things platform or to the MQTT broker.

The publish/subscribe pattern

The publish/subscribe pattern (also known as pub/sub) provides an alternative to a traditional client-server architecture. In the client-server model, a client communicates directly with an endpoint. The pub/sub model decouples the client that sends a message (the publisher) from the client or clients that receive the messages (the subscribers). The publishers and subscribers never contact each other directly. In fact, they are not even aware that the other exists. The connection between them is handled by a third component (the broker). The job of the broker is to filter all incoming messages and distribute them correctly to subscribers.

MQTT Broker

A broker helps in handling clients in MQTT technology. It can manage hundreds, thousands, or millions of connected MQTT clients at once, depending on the implementation. Its main functions are;

  • Receiving information
  • Decoding and filtering the messages received
  • Determining which client will be interested in which message
  • Transmitting these messages to clients depending on their interests

The project

Let’s make a simple BLE 2 MQTT project that collects sensor data from a BLE Air quality monitor device called HibouAir and sends it to a free public MQTT broker.

For this project, we will use Flespi. You can choose any public or private MQTT broker as you like.

Steps

Requirments 

  • BleuIO dongle.
  • air quality monitor device HibouAir.
  • A public MQTT broker (Flepsi token) https://flespi.com/mqtt-broker
  • BleuIO Javascript library. https://www.npmjs.com/package/bleuio
  • A build tool for Javascript (parcel) https://parceljs.org/docs/

Get the Flespi token

  • Create an account at Flespi.
  • Log into the Flespi dashboard.
  • Copy the token

Download source file

Get the source file from https://github.com/smart-sensor-devices-ab/ble2mqtt_bleuio.git

And run npm install

In the root folder, we will see two Html files called index.html and subscribe.html and two js files called pub.js and sub.js

Index.html file collects sensor data from a BLE Air quality monitor device called HibouAir with the help of BleuIO. It has three buttons. connect, device info and Scan and Send BLE Data.

First we need to connect a BleuIO dongle into the computer and connect to it using connect button. The device info button will show BleuIO dongle status on console log. And the Scan and Send BLE data will scan for Air quality data and send it to the cloud. For this script I am scanning and collecting a fixed device with the board id of 0578E0. You can change the value in pub.js file line number 4

Here is the index.html file

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link
      href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css"
      rel="stylesheet"
      integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3"
      crossorigin="anonymous"
    />
    <title>Publish</title>
  </head>
  <body>
    <div class="container mt-5">
      <button id="connect" class="btn btn-secondary">Connect</button>
      <button id="deviceinfo" class="btn btn-success d-none">
        Device Info
      </button>
      <button id="sendData" class="btn btn-primary">
        Scan and Send BLE Data
      </button>

      <h3 class="mt-5">Message Sent</h3>
      <div id="output"></div>
    </div>
  </body>
  <script src="./pub.js"></script>
</html>

After collecting advertised data, we try to decode it and get meaningful air quality data with co2, pressure, temperature, humidity, light values. Then we publish the data to Flepsi broker using topic name HibouAirTopic

The process is done in pub.js file.

Here is the pub.js file

const clientId = 'hibouair_' + Math.random().toString(16).substr(2, 8)
import * as my_dongle from 'bleuio'
import 'regenerator-runtime/runtime'
const sensorID = '0578E0'
import * as mqtt from "mqtt" 
document.getElementById('connect').addEventListener('click', function(){
  my_dongle.at_connect()
})
document.getElementById('deviceinfo').addEventListener('click', function(){
  my_dongle.ati().then((data)=>console.log(data))
})

const getTheBLEData = ( async()=>{
  return my_dongle.at_dual().then(()=>{
    return my_dongle.at_findscandata(sensorID,6).then((x)=>{
      let advData = x[x.length - 1].split(" ").pop()
      let positionOfID= advData.indexOf(sensorID);
      let tempHex = advData.substring(positionOfID+14, positionOfID+18)
      let temp = parseInt('0x'+tempHex.match(/../g).reverse().join(''))/10;

      let pressHex = advData.substring(positionOfID+10, positionOfID+14)
      let press = parseInt('0x'+pressHex.match(/../g).reverse().join(''))/10;

      let humHex = advData.substring(positionOfID+18, positionOfID+22)
      let hum = parseInt('0x'+humHex.match(/../g).reverse().join(''))/10;

      let lightHex = advData.substring(positionOfID+6, positionOfID+10)
      let light = parseInt('0x'+lightHex.match(/../g).reverse().join(''));

      let co2Hex = advData.substring(positionOfID+38, positionOfID+42)
      let co2 = parseInt('0x'+co2Hex);
      

      return {
        'CO2' :co2,
        'Temp' :temp,
        'Pressure':press,
        'Humidity':hum,        
        'Light':light,        
      }
    })
  })
})



//pass data
document.getElementById('sendData').addEventListener('click', function(){
    const host = 'wss://mqtt.flespi.io:443'

    const options = {
      keepalive: 60,
      clientId: clientId,
      protocolId: 'MQTT',
      username:'SET_YOUR_USERNAME',
      password:'SET_YOUR_PASSWORD',
      protocolVersion: 4,
      clean: true,
      reconnectPeriod: 1000,
      connectTimeout: 30 * 1000,
      will: {
        topic: 'HibouAirMsg',
        payload: 'Connection Closed abnormally..!',
        qos: 0,
        retain: false
      },
    }
    const client  = mqtt.connect(host, options)
    client.on('connect',()=>{
        console.log('connected mqtt client',clientId)

    })

    client.on('error', (err) => {
      console.log('Connection error: ', err)
      client.end()
    })

    client.on('reconnect', () => {
      console.log('Reconnecting...')
    })

    var topic = 'HibouAirTopic'

    // Publish
    setInterval(() => {

        getTheBLEData().then((x)=>{
          client.publish(topic, JSON.stringify(x), { qos: 0, retain: false })
        console.log("Message sent!", JSON.stringify(x));
        document.getElementById('output').innerHTML += JSON.stringify(x)+"<br>";
        })
      }, 5000);

    })

Make sure to set your username and password and change the Board ID of your device on pub.js file.

Subscribe.html file works as a subscriber which reads sensor data from the broker and shows it on the screen.

here is the subscribe.html file

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Subscribe</title>
  </head>
  <body>
    <h3>Message Received</h3>
    <div id="output"></div>
  </body>
  <script src="https://unpkg.com/mqtt/dist/mqtt.min.js"></script>

  <script src="sub.js"></script>
</html>

Again we need a js script that fetch data from cloud and show it on html file. Here is the sub.js file

const clientId = 'hibouair_' + Math.random().toString(16).substr(2, 8)

//const host = 'ws://broker.emqx.io:8083/mqtt'
const host = 'wss://mqtt.flespi.io:443'

const options = {
  keepalive: 60,
  clientId: clientId,
  protocolId: 'MQTT',
  username:'YOUR_USERNAME',
  password:'YOUR_PASSWORD',
  protocolVersion: 4,
  clean: true,
  reconnectPeriod: 1000,
  connectTimeout: 30 * 1000,
  will: {
    topic: 'HibouAirMsg',
    payload: 'Connection Closed abnormally..!',
    qos: 0,
    retain: false
  },
}

const client = mqtt.connect(host, options)
client.on('connect',()=>{
  console.log('connected mqtt client',clientId)
  client.subscribe('HibouAirTopic', { qos: 0 })
})
client.on('error', (err) => {
  console.log('Connection error: ', err)
  client.end()
})

client.on('reconnect', () => {
  console.log('Reconnecting...')
})


  
  // Received
client.on('message', (topic, message, packet) => {
  console.log(JSON.parse(message))
  //console.log('Received Message: ' + message + '\nOn topic: ' + topic)
  document.getElementById('output').innerHTML += message.toString()+"<br>";

})

To run the index.html we can just type

parcel index.html

Parcel JS can be installed from https://parceljs.org/

Project Video

Share this post on :

Sensor data collection from STM32 and SHT85 using Bluetooth Low Energy

The project is showcasing a simple way of using the the BleuIO Dongle to advertise data that the STM32 reads from a sensor which is connected to the STM32 Nucleo-144.

Requirments :

  • A BleuIO dongle (https://www.bleuio.com/)
  • A SHT85 sensor (https://sensirion.com/products/catalog/SHT85/)
  • A board with a STM32 Microcontroller with a USB port. (A Nucleo-144 development board: NUCLEO-H743ZI2, was used developing this example. (https://www.st.com/en/evaluation-tools/nucleo-h743zi.html)
  • To connect the dongle to the Nucleo board we used a “USB A to Micro USB B”-cable with a USB A female-to-female adapter.)
  • STM32CubeIDE (https://www.st.com/en/development-tools/stm32cubeide.html)

When the BleuIO Dongle is connected to the Nucleo boards USB port, the STM32 will recognize it and start advertising the sensor values that it reads from the SHT85 along with the sensor serial number. It will update these values every 10 seconds.

Setup the project

Part 1 : Download the project

Get project HERE

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

Either clone the project, or download it as a zip file and unzip it, into your STM32CubeIDE workspace.

Part 2 : Importing as an Existing Project

From STM32CubeIDE choose File>Import…

Then choose General>Existing Projects into Workspace then click ‘Next >’

Make sure you’ve choosen your workspace in ‘Select root directory:’

You should see the project “stm32_bleuio_SHT85_example”, check it and click ‘Finish’.

If you download the project as a zip file you will need to rename the project folder from ‘stm32_bleuio_SHT85_example-master’ to ‘stm32_bleuio_SHT85_example’

Connect the SDA to PF0 on the Nucleo board and SCL to PF1.

Then setup I2C2 in the STM32Cube ioc file like this:

Running the example

In STMCubeIDE click the hammer icon to build the project.

  • Open up the ‘STMicroelectronics STLink Viritual COM Port’ with a serial terminal emulation program like TeraTerm, Putty or CoolTerm.

Baudrate: 115200

Data Bits: 8

Parity: None

Stop Bits: 1

Flow Control: None

  • In STMCubeIDE click the green play button to flash and run it on your board. The first time you click it the ‘Run Configuration’ window will appear. You can just leave it as is and click run.
  • Connect the BleuIO Dongle.

Access sensor data from a web browser

We wrote a simple script that connects to the BleuIO dongle and reads advertised data from STM32.

For this script to work, we need

Steps

Create a simple Html file called index.html which will serve as the frontend of the script. This Html file contains some buttons that help connect and read advertised data from the remote dongle, which is connected to stm32.

<!DOCTYPE html>
<html lang="en">
  <head>
    <!-- Required meta tags -->
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />

    <!-- Bootstrap CSS -->
    <link
      href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css"
      rel="stylesheet"
      integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3"
      crossorigin="anonymous"
    />

    <title>STM32 Read sensor value</title>
  </head>
  <body>
    <div class="container mt-5">
      <h1>Sensor data collection from stm32 using Bluetooth Low Energy</h1>
      <button id="connect" class="btn btn-primary">Connect</button>
      <button id="getdata" class="btn btn-success">Get device data</button>
      <div id="loader"></div>
      <br />
      <div id="response" class="fw-bold"></div>

      <script src="./index.js"></script>
    </div>
  </body>
</html>

Create a js file called script.js and include it at the bottom of the Html file. This js file uses the BleuIO js library to write AT commands and communicate with the other dongle.

import * as my_dongle from 'bleuio'

//connect to BleuIO
document.getElementById('connect').addEventListener('click', function(){
  my_dongle.at_connect()
})
//get sensor data
document.getElementById('getdata').addEventListener('click', function(){
  document.getElementById('loader').innerHTML = 'Loading'
  //set the BleuIO dongle into dual role
    my_dongle.at_dual().then(()=>{
      // sensor id of the device that we are trying to get data from
      let sensorID='05084FA3'

      //look for advertised data of with the sensor id
        my_dongle.at_findscandata(sensorID,4).then(x=>{        

          //split the advertised data from the respnse
          let advdata= x[x.length-1].split(" ").pop()

          //trim the advertised string to only get sensor response
          const result = advdata.split(sensorID).slice(1).join(sensorID) 

          //get temperature and humidity value
          let temp = result.substring(0, 4);
          let hum = result.substring(4, 8);

          //convert from hex to decimal and device by 100
          temp = parseInt(temp, 16)/100
          hum = (parseInt(hum, 16)/100).toFixed(1)  

          document.getElementById('loader').innerHTML = ''
          document.getElementById('response').innerHTML = `Sensor ID : 05084FA3 <br/>
          Temperature : ${temp} °C<br/>
          Humidity : ${hum} %rH<br/>`              
        })
    })
    
  })

The script js file has two button actions; connect and read advertised data.

We also need to update the Sensor ID on line 13 of script js. The Sensor ID of this example project is 05084FA3, which we got from SHT85.

Therefore this script looks for advertised data that contains sensor ID 05084FA3. After getting advertised data , we split the temperature and humidity information and show it on our index.html page.

Now we need a web bundler. We can use parcel.js

Once parcel js installed, lets go to the root directory and type “parcel index.html”. This will start our development environment.

Lets open the script on a browser and select the right port where the dongle is connected.

The web script is available on web script folder of the GitHub repository.

Share this post on :

Plotting real-time graph from Bluetooth device using C#

Bluetooth Low Energy (BLE) is a low power wireless technology used to connect devices. It is a popular communication method, especially in the Internet of Things era. Several devices around the house have a built-in Bluetooth transceiver, and most of them provide useful capabilities to automate jobs. This technology is widely used in the healthcare, fitness, beacons, security, and home entertainment industries. For that reason, it is really interesting to create a desktop application using C# that plot a real-time graph of values from HibouAir – Air Quality Monitor using BleuIO.

For this project, Bluetooth Low Energy USB dongle called BlueIO is used, which will act as a central device to retrieve data. HibouAir will serve as a peripheral device to transmit the data. The 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.

Let’s start

First, let’s create a new project in visual studio and select C# windows form application from the list.

Choose a suitable name for your project.

Once the project is created, we will see a blank form screen where we will add buttons and labels to communicate with BleuIO graphically and show plot real-time values from HibouAir.

The application will connect to the BleuIO dongle to its given COM port from the script. You can change the port easily by going to line number 19.

We will have a disconnect button to disconnect BleuIO from the COM port.

By clicking on the Get data button, the script will connect to The BleuIO dongle and put it on DUAL mode. Then it will look for scanned data and filter out the device advertised information that we are looking for. You can change the scanForDevice value on line number 24.

Once it starts fetching data, it will go through a parser that decodes the advertised data and returns a meaningful number. 

In this script, we are only showing how to plot Ambient Light Sensor (ALS) value in lux and plot it on the chart.

The form will look like this.

The .cs file associated with this will have the following code.
Source code is available at https://github.com/smart-sensor-devices-ab/bluetooth_realtimedata_csharp

using System;
using System.Windows.Forms;
using System.IO.Ports;
using LiveCharts;
using LiveCharts.Configurations;
using LiveCharts.Wpf;
using System.Threading;
using System.Collections;
using System.Linq;
using System.Text;
using Timer = System.Windows.Forms.Timer;

namespace ConstantChanges
{
    public partial class ConstantChanges : Form
    {
        //Connect to Serial port
        //Replace port number to your comport
        SerialPort mySerialPort = new SerialPort("COM7", 57600, Parity.None, 8, StopBits.One);
        //string ScannedData = "";
        string ScannedData = "";
        bool clicked = false;

        public string scanForDevice = "5B07050345840D";
        int chartYval ;

        public String ParseSensorData(string input)
        {
            int counter = 17;
            int pressureData = Convert.ToInt32(input[counter + 9].ToString() + input[counter + 10].ToString() + input[counter + 7].ToString() + input[counter + 8].ToString(), 16);
            chartYval = pressureData;

            return pressureData.ToString();

        }
        public ConstantChanges()
        {
            InitializeComponent();
            mySerialPort.DataReceived += new SerialDataReceivedEventHandler(mySerialPort_DataReceived);
            mySerialPort.Open();
            ArrayList device = new ArrayList();
            //To handle live data easily, in this case we built a specialized type
            //the MeasureModel class, it only contains 2 properties
            //DateTime and Value
            //We need to configure LiveCharts to handle MeasureModel class
            //The next code configures MEasureModel  globally, this means
            //that livecharts learns to plot MeasureModel and will use this config every time
            //a ChartValues instance uses this type.
            //this code ideally should only run once, when application starts is reccomended.
            //you can configure series in many ways, learn more at http://lvcharts.net/App/examples/v1/wpf/Types%20and%20Configuration

            var mapper = Mappers.Xy<MeasureModel>()
                .X(model => model.DateTime.Ticks)   //use DateTime.Ticks as X
                .Y(model => model.Value);           //use the value property as Y

            //lets save the mapper globally.
            Charting.For<MeasureModel>(mapper);

            //the ChartValues property will store our values array
            ChartValues = new ChartValues<MeasureModel>();
            cartesianChart1.Series = new SeriesCollection
            {
                new LineSeries
                {
                    Values = ChartValues,
                    PointGeometrySize = 18,
                    StrokeThickness = 4
                }
            };
            cartesianChart1.AxisX.Add(new Axis
            {
                DisableAnimations = true,
                LabelFormatter = value => new System.DateTime((long)value).ToString("mm:ss"),
                Separator = new Separator
                {
                    Step = TimeSpan.FromSeconds(1).Ticks
                }
            });

            SetAxisLimits(System.DateTime.Now);

            //The next code simulates data changes every 500 ms
            Timer = new Timer
            {
                Interval = 3000
            };
            Timer.Tick += TimerOnTick;
            R = new Random();
            Timer.Start();
        }

        //Store response from the dongle
        private void mySerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {

            SerialPort sp = (SerialPort)sender;
            string s = sp.ReadExisting();

            //get advertised data
            if (s.Contains("[ADV]"))
            {
                ScannedData = s;
            }

        }

        public ChartValues<MeasureModel> ChartValues { get; set; }
        public Timer Timer { get; set; }
        public Random R { get; set; }

        private void SetAxisLimits(System.DateTime now)
        {
            cartesianChart1.AxisX[0].MaxValue = now.Ticks + TimeSpan.FromSeconds(1).Ticks; // lets force the axis to be 100ms ahead
            cartesianChart1.AxisX[0].MinValue = now.Ticks - TimeSpan.FromSeconds(8).Ticks; //we only care about the last 8 seconds
        }

        private void TimerOnTick(object sender, EventArgs eventArgs)
        {
            var now = System.DateTime.Now;
            if(clicked == true) {
                getBleData();
            }
            
            ChartValues.Add(new MeasureModel
            {
                DateTime = now,
                //Value = R.Next(0, 10)
                Value = chartYval
            });

            SetAxisLimits(now);            
            //lets only use the last 30 values
            if (ChartValues.Count > 30) ChartValues.RemoveAt(0);
        }

        private void label1_Click(object sender, EventArgs e)
        {

        }

        private void cartesianChart1_ChildChanged(object sender, System.Windows.Forms.Integration.ChildChangedEventArgs e)
        {

        }

        //Get data every 3 seconds
        private void getBleData()
        {
            var inputByte = new byte[] { 13 };
            byte[] dualCmd = Encoding.UTF8.GetBytes("AT+DUAL");
            dualCmd = dualCmd.Concat(inputByte).ToArray();
            mySerialPort.Write(dualCmd, 0, dualCmd.Length);
            Thread.Sleep(500);
            byte[] gapScanCmd = Encoding.UTF8.GetBytes("AT+FINDSCANDATA="+ scanForDevice);
            gapScanCmd = gapScanCmd.Concat(inputByte).ToArray();
            mySerialPort.Write(gapScanCmd, 0, gapScanCmd.Length);
            Thread.Sleep(1200);
            byte[] bytes = Encoding.UTF8.GetBytes("\u0003");
            bytes = bytes.Concat(inputByte).ToArray();
            mySerialPort.Write(bytes, 0, bytes.Length);

            if (ScannedData != null)
            {
                sensor_op.Text = ScannedData;
                string lastWord = ScannedData.Split(' ').Last();
                if(lastWord !=null) {
                    var toPrint = ParseSensorData(lastWord);
                    sensor_op.Text = "Current Value :" + toPrint;
                }
                
                
            }
        }


        private void btnGetData_Click(object sender, EventArgs e)
        {
            var inputByte = new byte[] { 13 };
            byte[] dualCmd = Encoding.UTF8.GetBytes("AT+DUAL");
            dualCmd = dualCmd.Concat(inputByte).ToArray();
            mySerialPort.Write(dualCmd, 0, dualCmd.Length);
            Thread.Sleep(500);
            byte[] gapScanCmd = Encoding.UTF8.GetBytes("AT+FINDSCANDATA="+ scanForDevice);
            gapScanCmd = gapScanCmd.Concat(inputByte).ToArray();
            mySerialPort.Write(gapScanCmd, 0, gapScanCmd.Length);
            Thread.Sleep(1200);
            byte[] bytes = Encoding.UTF8.GetBytes("\u0003");
            bytes = bytes.Concat(inputByte).ToArray();
            mySerialPort.Write(bytes, 0, bytes.Length);

            if (ScannedData!=null) {
                sensor_op.Text = ScannedData;
                string lastWord = ScannedData.Split(' ').Last();
                if (lastWord != null)
                {
                    var toPrint = ParseSensorData(lastWord);
                    sensor_op.Text = "Current Value :"+toPrint;
                }
                clicked = true;
            }  
            //Show Chart
            cartesianChart1.Visible = true;
        }
    }
}

As you can notice, I wrote COM7 to connect to the serial port because the BleuIO device on my computer is connected to COM7.

You can check your COM port from the device manager. 

Also, scanForDevice value is 5B07050345840D where 45840D is the device id. 

Let’s run the project and click on Get Data. You will notice a new light value is plotting in every three seconds.

Here is a demo of this sample application. 

Share this post on :

C# Desktop application to get real-time BLE data from Air Quality Sensor

Bluetooth Low Energy (BLE) is a low power wireless technology used to connect devices. It is a popular communication method, especially in the Internet of Things era. Several devices around the house have a built-in Bluetooth transceiver, and most of them provide useful capabilities to automate jobs. This technology is widely used in the healthcare, fitness, beacons, security, and home entertainment industries. For that reason, it is really interesting to create a desktop application using C# that access BLE device data around the house.

In this example, we will create a simple C# windows form application to get real-time HibouAir environmental data using BleuIO. 

This script can be used to connect and view other BLE devices data.

Let’s start

First, let’s create a new project in visual studio and select C# windows form application from the list.

Choose a suitable name for your project.

Once the project is created, we will see a blank form screen where we will add buttons and labels to communicate with BleuIO graphically.


The application will connect to the BleuIO dongle to its given COM port from the script. You can change the port easily by going to line number 18.
We will have a disconnect button to disconnect BleuIO from the COM port.

The button Scan for HibouAir devices will look for nearby HibouAir devices and store them in an Array. The Combobox (dropdown item) next to it will load the stored device from where we can select a device to get its data.


The Get data button will show the real-time environmental data of this device.

The form will look like this.

The .cs file associated to this will have the following code.

Source code is available at https://github.com/smart-sensor-devices-ab/bluetooth_realtimedata_csharp

using System;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Collections;
using System.Threading;


namespace WindowsFormsApp1
{
    

    public partial class Form1 : Form
    {
        //Connect to Serial port
        //Replace port number to your comport
        SerialPort mySerialPort = new SerialPort("COM18", 57600, Parity.None, 8, StopBits.One);
        ArrayList device = new ArrayList();
        string ScannedData = "";
        public string selectedDevices = "";
        string advData = "";

        // HibouAir Advertise data decrypt and show output
        public String ParseSensorData(string input)
        {
            int counter = 17;
            string theData = " SensorID : " + input[counter + 1] +
              input[counter + 2] +
              input[counter + 3] +
              input[counter + 4] +
              input[counter + 5] +
              input[counter + 6] + "\n\n " +
              "Pressure : " + Convert.ToInt32(input[counter + 13].ToString() + input[counter + 14].ToString() + input[counter + 11].ToString() + input[counter + 12].ToString(), 16) / 10 + " mbar\n\n " +
              "Temperature : " + Convert.ToInt32(input[counter + 17].ToString() + input[counter + 18].ToString() + input[counter + 15].ToString() + input[counter + 16].ToString(), 16) / 10 + " °C\n\n " +
              "Humidity : " + Convert.ToInt32(input[counter + 21].ToString() + input[counter + 22].ToString() + input[counter + 19].ToString() + input[counter + 20].ToString(), 16) / 10 + " %rH\n\n " +
              "VOC : " + Convert.ToInt32(input[counter + 25].ToString() + input[counter + 26].ToString() + input[counter + 23].ToString() + input[counter + 24].ToString(), 16) / 10 + "\n\n " +
              "ALS : " + Convert.ToInt32(input[counter + 9].ToString() + input[counter + 10].ToString() + input[counter + 7].ToString() + input[counter + 8].ToString(), 16) + " Lux\n\n " +
              "PM 1.0 : " + Convert.ToInt32(input[counter + 29].ToString() + input[counter + 30].ToString() + input[counter + 27].ToString() + input[counter + 28].ToString(), 16) / 10 + " µg/m³\n\n " +
              "PM 2.5 : " + Convert.ToInt32(input[counter + 33].ToString() + input[counter + 34].ToString() + input[counter + 31].ToString() + input[counter + 32].ToString(), 16) / 10 + " µg/m³\n\n " +
              "PM 10 : " + Convert.ToInt32(input[counter + 37].ToString() + input[counter + 38].ToString() + input[counter + 35].ToString() + input[counter + 36].ToString(), 16) / 10 + " µg/m³"
              ;

            sensor_op.Text = theData.ToString();
            return theData;
        }


        public Form1()
        {
            InitializeComponent();    
            mySerialPort.DataReceived += new SerialDataReceivedEventHandler(mySerialPort_DataReceived);
            mySerialPort.Open();
            ArrayList device = new ArrayList();

        }

        
        //Store response from the dongle
        private void mySerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            
            SerialPort sp = (SerialPort)sender;
            string s = sp.ReadExisting();
            if (s.Contains("RSSI") && s.Contains("(HibouAIR)") )
            {
                device.Add(s);
                
            }
            if (selectedDevices != "")
            {
                label1.Invoke(new EventHandler(delegate { label1.Text = selectedDevices.Remove(0, 3); })); 
           
                if (s.Contains("[ADV]"))
                {
                    ScannedData=s;
                    
                   // output_data.Invoke(new EventHandler(delegate { output_data.Text += "TT: " + s + "\r\n"; }));
                }           
             }
            //output_data.Invoke(new EventHandler(delegate { output_data.Text += s + "\r\n"; }));


            //lbl_output.Invoke(this.myDelegate, new Object[] { s });
        }


       
        // Disconnect from COM port
        private void btn_disconnect_Click(object sender, EventArgs e)
        {
            mySerialPort.Close();
            Environment.Exit(0);
        }

        private void Form1_Load(object sender, EventArgs e)
        {

        }



        private void pictureBox1_Click(object sender, EventArgs e)
        {

        }

        private void btn_deviceList_Click(object sender, EventArgs e)
        {
            var inputByte = new byte[] { 13 };
            byte[] dualCmd = Encoding.UTF8.GetBytes("AT+DUAL");
            dualCmd = dualCmd.Concat(inputByte).ToArray();
            mySerialPort.Write(dualCmd, 0, dualCmd.Length);

            byte[] gapScanCmd = Encoding.UTF8.GetBytes("AT+GAPSCAN=2");
            gapScanCmd = gapScanCmd.Concat(inputByte).ToArray();
            mySerialPort.Write(gapScanCmd, 0, gapScanCmd.Length);
            

        }

        private void label1_Click(object sender, EventArgs e)
        {

        }

        // Load devices on dropdown
        private void dropdown_device_SelectedIndexChanged(object sender, EventArgs e)
        {
            if(dropdown_device.Text == "Load Devices")
            {
                
                foreach (var a in device)
                {
                    dropdown_device.Items.Add(a);
                }
            }
            else
            {
                selectedDevices = dropdown_device.Text;
                string[] selectedDeviceID = selectedDevices.Split(' ');
                selectedDevices = selectedDeviceID[2];
                btnGetData.Visible = true;
            }
            
        }

        //get advertised data of selected device
        private void btnGetData_Click(object sender, EventArgs e)
        {


            var inputByte = new byte[] { 13 };
            byte[] gapScanCmd = Encoding.UTF8.GetBytes("AT+FINDSCANDATA=FF5B07");
            gapScanCmd = gapScanCmd.Concat(inputByte).ToArray();
            mySerialPort.Write(gapScanCmd, 0, gapScanCmd.Length);
            Thread.Sleep(500);
            byte[] bytes = Encoding.UTF8.GetBytes("\u0003");
            bytes = bytes.Concat(inputByte).ToArray();
            mySerialPort.Write(bytes, 0, bytes.Length);
            var array = ScannedData.Split('\n');
            int i = 0;
            //filter selected device data
            foreach (var a in array)
            {
                string kk = (string)a;
                if (kk.Contains(selectedDevices.Remove(0, 3)))
                {
                    var bleData = array[i].Split(' ');
                    advData = bleData[4];

                    var toPrint = ParseSensorData(advData);

                    break;
                }

                i++;

            }
            
        }
    }

    
}

As you can notice, I wrote COM18 to connect to the serial port because the BleuIO device on my computer is connected to COM18.
You can check your COM port from the device manager.
Let’s run the project and click on scan for HibouAir devices.

Here is a demo of this sample application.

Share this post on :