Bluetooth Low Energy based RGB LED Strip color control from a web browser and STM32

1. Introduction

This project is about Bluetooth Low Energy based RGB LED Strip Color Control from a web browser and STM 32. The LEDs light strip can also be controlled wirelessly via a BLE scanning App using IOS or Android. With the received data, we decide which color of the RGB strip to activate.

You will need two dongles, one connected to the Nucleo board and one connected to a computer to control from web browser. The web script is also available on GitHub.

When the BleuIO Dongle is connected to the Nucleo boards USB port the STM32 will recognize it and directly start advertising. This allows the other Dongle to connect to it.

It will also accept 3 different inputs from the UART:

inputresult
0Send ATI (Request device information) command to BlueIO Dongle.
1Manually turn the LED on
2Manually turn the LED off

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) and the WS2812, a intelligent control LED light source, for this example.

Connect the LED to the Nucleo Board by connecting:

4-7 VDC to 5V

GND to any GND

DIN to PE9

On the Nucleo NUCLEO-H743ZI2:

Note : 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.

2. Project requirments

  • Two BleuIO dongles (https://www.bleuio.com/)
  • A board with a STM32 Microcontroller with a USB port. (A Nucleo-144 development board: NUCLEO-H743ZI2, is used for this project. (https://www.st.com/en/evaluation-tools/nucleo-h743zi.html)
  • To connect the dongle to the Nucleo board a “USB A to Micro USB B”-cable with a USB A female-to-female adapter can be used.)
  • STM32CubeIDE (https://www.st.com/en/development-tools/stm32cubeide.html)
  • An WS2812 RGB LED

3. About the Code

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

This project is based on another STM32 project (https://github.com/smart-sensor-devices-ab/stm32_bleuio_example) with the interface to the WS2812 Interface WS2812 with STM32 by Controllers Tech

The DIN pin will be connected to TIM1 which need to be enabled in to the .ioc file:

Parameter Settings:

DMA for TIM1 will also be enabled:

In main.c we will need to add a callback for TIM PWM Pulse Finished:

void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim)
{
    HAL_TIM_PWM_Stop_DMA(&htim1, TIM_CHANNEL_1);
    datasentflag=1;
}



In main.c there is also a functions for controlling the LED.

Set_LED() for changing the color of the individual LEDs.

Set_Brightness() for setting the brightness. A brightness value of 0 means the LED is turned off.

WS2812_Send() execute the changes we made to the LED.

void Set_LED (int LEDnum, int Red, int Green, int Blue)
{
    LED_Data[LEDnum][0] = LEDnum;
    LED_Data[LEDnum][1] = Green;
    LED_Data[LEDnum][2] = Red;
    LED_Data[LEDnum][3] = Blue;
}

#define PI 3.14159265

void Set_Brightness (int brightness)  // 0-45
{
#if USE_BRIGHTNESS

    if (brightness > 45) brightness = 45;
    for (int i=0; i<MAX_LED; i++)
    {
        LED_Mod[i][0] = LED_Data[i][0];
        for (int j=1; j<4; j++)
        {
            float angle = 90-brightness;  // in degrees
            angle = angle*PI / 180;  // in rad
            LED_Mod[i][j] = (LED_Data[i][j])/(tan(angle));
        }
    }

#endif

}

void WS2812_Send (void)
{
    uint32_t indx=0;
    uint32_t color;


    for (int i= 0; i<MAX_LED; i++)
    {
#if USE_BRIGHTNESS
        color = ((LED_Mod[i][1]<<16) | (LED_Mod[i][2]<<8) | (LED_Mod[i][3]));
#else
        color = ((LED_Data[i][1]<<16) | (LED_Data[i][2]<<8) | (LED_Data[i][3]));
#endif

        for (int i=23; i>=0; i--)
        {
            if (color&(1<<i))
            {
                pwmData[indx] = 60;  // 2/3 of 90
            }

            else pwmData[indx] = 30;  // 1/3 of 90

            indx++;
        }

    }

    for (int i=0; i<50; i++)
    {
        pwmData[indx] = 0;
        indx++;
    }

    HAL_TIM_PWM_Start_DMA(&htim1, TIM_CHANNEL_1, (uint32_t *)pwmData, indx);
    while (!datasentflag){};
    datasentflag = 0;
}



We also update the handleUartInput function so we can have manual control over the LED via the UART.

/**
  * @brief Simple uart input handler
  * @retval None
  */
void handleUartInput(UARTCommandTypeDef cmd)
{
    switch(cmd)
    {
        case UART_RX_0:
        {
            // 0
            uart_buf_len = sprintf(uart_tx_buf, "\r\n(0 pressed)\r\n");
            HAL_UART_Transmit(&huart3, (uint8_t *)uart_tx_buf, uart_buf_len, HAL_MAX_DELAY);
            if(isBleuIOReady)
            {
                writeToDongle((uint8_t*)DONGLE_CMD_ATI);
            } else
            {
                uart_buf_len = sprintf(uart_tx_buf, BLEUIO_NOT_READY_MSG);
                HAL_UART_Transmit(&huart3, (uint8_t *)uart_tx_buf, uart_buf_len, HAL_MAX_DELAY);
            }
            uartStatus = UART_RX_NONE;
            break;
        }
        case UART_RX_1:
        {
            // 1
            uart_buf_len = sprintf(uart_tx_buf, "\r\n(1 pressed LED on!)\r\n");
            HAL_UART_Transmit(&huart3, (uint8_t *)uart_tx_buf, uart_buf_len, HAL_MAX_DELAY);
              Set_Brightness(40);
              WS2812_Send();
            uartStatus = UART_RX_NONE;
            break;
        }
        case UART_RX_2:
        {
            // 2
            uart_buf_len = sprintf(uart_tx_buf, "\r\n(2 pressed LED off!)\r\n");
            HAL_UART_Transmit(&huart3, (uint8_t *)uart_tx_buf, uart_buf_len, HAL_MAX_DELAY);
              Set_Brightness(0);
              WS2812_Send();

            uartStatus = UART_RX_NONE;
            break;
        }
        case UART_RX_NONE:
        {
            break;
        }
        default:
        {
            uartStatus = UART_RX_NONE;
            break;
        }
    }
}

4. How to setup project

4.1 Downloading the project from GitHub

Get project HERE

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


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

4.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_rgb_led_example”, check it and click ‘Finish’.

5. 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.

Serial port Setup:

Baudrate: 115200

Data Bits: 8

Parity: None

Stop Bits: 1

Flow Control: None

  • Connect the BleuIO Dongle before running the example
  • 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.
  • You should be greeted by this welcome message:
Welcome to STM32 BleuIO RGB LED Example!
Press 0 to run the ATI command
Press 1 to manually turn on LED
Press 2 to manually turn off LED

The LED will turn on briefly when starting up.

  • Wait until the message: “[BleuIO Dongle Ready]” is shown.

  • The LEDs should now turn off and you can now connect with the other dongle using the script.

You can also use the uart commands (0, 1 or 2):

  • Press 0 to get device information.
  • 1 to turn on LED.
  • 2 to turn off LED.

Dongle response will be printed to UART.

Control the colors 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. The web script is available inside the source file. Now we can control the colors wirelessly.

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>
      Bluetooth controlled RGB LED Strip - Control color from web browser
    </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 controlled RGB LED Strip - Control color from web browser
      </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="sendMsgForm" name="sendMsgForm">
            <div class="mb-3">
              <label for="msgToSend" class="form-label"
                >Select color option</label
              >
              <select
                class="form-select"
                aria-label="Default select example"
                name="msgToSend"
                id="msgToSend"
                required
              >
                <option selected>Open this select menu</option>
                <option value="L=0">LED Off</option>
                <option value="L=1">LED On</option>
                <option value="L=RED">Set LED lights red</option>
                <option value="L=GREEN">Set LED lights green</option>
                <option value="L=BLUE">Set LED lights blue</option>
                <option value="L=RAINBOW">
                  Set LEDs lights to different colors
                </option>
              </select>
            </div>

            <button type="submit" class="btn btn-primary">Submit</button>
          </form>
        </div>
        <div class="col-md-8">
          <img
            src="https://www.bleuio.com/blog/wp-content/uploads/2022/09/bluetooth-controlled-rgb-led-strip-collor-control.jpg"
          />
        </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'
const dongleToConnect='[0]40:48:FD:E5:2F:17'
document.getElementById('connect').addEventListener('click', function(){
  my_dongle.at_connect()
  document.getElementById("connect").disabled=true;
  document.getElementById("sendMsgForm").hidden=false;
})

document.getElementById("sendMsgForm").addEventListener("submit", function(event){
    event.preventDefault()

    
    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 = document.getElementById('msgToSend').value;
            console.log('Message Send  '+theVal)
            // send command to show data
            my_dongle.at_spssend(theVal).then(()=>{
                console.log('Message Send '+theVal)
            })
        })
        
    })
  });

The script has a button to connect to COM port on the computer. After connecting to the dongle , we should be able to control the colors of the LED strip.

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 update the LED strip.

The web script looks like this

Share this post on :

Benefits and use cases of Bluetooth Low Energy technology

We live in a day where wireless data transfer is a rather common phenomenon. From offering location services or audio streaming to maintaining device networks and data transfers, all is made possible due to wireless communication technologies and Bluetooth is one of them. Bluetooth is a short-range wireless technology which helps exchange data between connected devices over short radiuses. 

Bluetooth Low Energy is essential for IoT networks due to its ability to preserve battery power on devices. Major use cases of Bluetooth Low Energy technology include:

Use Cases

  • Mobile payments at retail outlets.
  • Wireless charging for multiple devices.
  • Keep track of traveller luggage at the airport by using smart tags.
  • Monitor air quality of the indoor environment by using BLE-powered sensors.
  • Remote monitoring of patient’s health conditions
  • Autonomous vehicles with intelligent seats and steering wheels, intelligent proximity reporting etc
  • Smart Home automation systems with sensors for tracking fire breakouts, water leakages, or motion in case of robbery.

Advantages of Bluetooth Low Energy technology

  • Sends an ID number via BLE advertising channels
  • The 2.4 GHz frequency controls interference in signals.
  • Determines the location of users within its range
  • Longer battery life by keeping the radio off when not required
  • Uninterrupted broadcast using Bluetooth beacon 
  • The devices are able to establish connections and faster data transfer.
  • Small size (maximum up to 255 bytes) data transfer which is helpful for IoT-based applications
  • The 128-bit AES algorithm ensures the safety of data transfer.
  • Low power consumption compared to other wireless technologies.
  • Comparatively lower cost of deployment and maintenance of chipsets and modules.

The BleuIO is Bluetooth low energy USB dongle that can be used to create Bluetooth low energy application. Just use the AT Commands available on the device for faster development.
The BleuIO is a fully integrated solution, providing MCU and Bluetooth® radio in one chip, based on Dialog Semiconductor latest Bluetooth chip DA14683. The FLASH based device permits field or boot upgradable, while application is stored on FLASH memory.
Projects and tutorials with source code can be found at https://www.bleuio.com/blog/?bleprojects

Share this post on :

Integration of Bluetooth technology within Embedded Systems

Bluetooth Low Energy (BLE) is one of the most widely applicable low-power connectivity standards. Its purpose is to connect devices over a fairly short range. BLE was developed with IoT applications in mind, which has special implications for its design.

Integration of Bluetooth technology within Embedded Systems facilitates an expansive range of Application Domains to support devices at various design levels. Engaging Bluetooth into an embedded system creates value in device capability for specific needs, via the implementation of Bluetooth Profiles that can help the data transfer between compatible devices. Recent introductions to Bluetooth LE Audio in the SMART phone market deliver revolutions in Bluetooth audio capability, enabling users to share audio from a single device to multiple wireless headsets.

Bluetooth Low Energy is used almost everywhere, which is one of its core strengths compared to other low-power networks. This is one of the reasons why everyone looking into BLE when designing their solutions. Because of the well-established name of Bluetooth Classic, BLE had an easier time gaining trust among developers. All these factors combined made it the obvious choice for many consumer IoT applications and much more.

On the other hand, Bluetooth Low Energy is constantly being developed and perfected to meet the most current demands of the market, therefore, it is one of the top choices for embedded system developers to integrate BLE in their solutions.

The BleuIO is Bluetooth low energy USB dongle that can be used in embedded system solutuions. Just use the AT Commands available on the device for faster development.
The BleuIO is a fully integrated solution, providing MCU and Bluetooth® radio in one chip, based on Dialog Semiconductor latest Bluetooth chip DA14683. The FLASH based device permits field or boot upgradable, while application is stored on FLASH memory.
Projects and tutorials with source code can be found at https://www.bleuio.com/blog/?bleprojects

Share this post on :

Python GUI Bluetooth Programming With Tkinter

Python has a lot of GUI frameworks, but Tkinter is the only framework that’s built into the Python standard library. Tkinter has several strengths. It’s cross-platform, so the same code works on Windows, macOS, and Linux. Visual elements are rendered using native operating system elements, so applications built with Tkinter look like they belong on the platform where they’re run.

Tkinter is lightweight and relatively painless to use compared to other frameworks. This makes it a compelling choice for building GUI applications in Python, especially for applications where a modern sheen is unnecessary, and the top priority is to quickly build something functional and cross-platform.

In this article, we will try to create a simple Python GUI application that can scan for nearby Bluetooth devices using Pyserial and shows the list on the screen.

Requirments 

Instructions

  • Connect the BleuIO to your computer. The script uses pyserial to connect to the Bluetooth USB dongle BleuIO.
  • Update the script and write the correct COM port (line 25), where the dongle is connected. 
  • After connecting to the dongle, we put the dongle into the central role using AT+CENTRAL so that it can scan for nearby Bluetooth devices. 
  • Then we do a simple Gap scan using AT+GAPSCAN=3 command to scan for nearby Bluetooth devices for 3 seconds.
  • After that, we read the output from the serial port and filter the device to get the unique number of devices.
  • Then we add a timestamp when the scan was completed. 
  • Finally, we sort the result by RSSI value before printing it out on screen.
  • ‘Scan again’ button will do the whole process again.

Here is the final script file. 

# Gjort av William 
# 2022-06-16 
# Smart Sensors Devices AB
# 
# libraries that is necessary for tkinter to work
import tkinter as tk
from tkinter import  ttk

# this is imported for the dongle and also for the "time.sleep()" commands
import serial
import time

# this is the library that is uesd to check the current time 
import datetime
now = datetime.datetime.now()

# this is what creates the main window
main_window = tk.Tk()

#changes the titel of the window
main_window.title('Scan for nearby Bluetooth devices')


# sets your port for the dongle
your_com_port = "COM18"  
connecting_to_dongle = True

#changes the size of the screens window
window_width = 900
window_height = 500

# get the screen dimension
screen_width = main_window.winfo_screenwidth()
screen_height = main_window.winfo_screenheight()
# find the center point
center_x = int(screen_width/2 - window_width / 2)
center_y = int(screen_height/2 - window_height / 2)
# set the position of the window to the center of the screen
main_window.geometry(f'{window_width}x{window_height}+{center_x}+{center_y}')

# apply the grid layout
main_window.grid_columnconfigure(1, weight=1)
main_window.grid_rowconfigure(1, weight=1)


# create the text widget
text = tk.Text(main_window, height=30, width=30)
text.grid(row=1, column=1, sticky=tk.EW)


# this is the part of the code that communicates whit the dongle
print("Connecting to dongle...")
while connecting_to_dongle:
    try:
        console = serial.Serial(
            port=your_com_port,
            baudrate=57600,
            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+CENTRAL"))
console.write("\r".encode())
print("Putting dongle in Central role.")
time.sleep(0.1)

console.write(str.encode("AT+GAPSCAN=3"))
console.write("\r".encode())
time.sleep(0.1)
print("Looking for nearby Bluetooth devices ...")
dongle_output2 = console.read(console.in_waiting)
time.sleep(3)
print("Scan Complete!")
filtered = []

for dev in dongle_output2.decode().splitlines():
    if len(dev)>20:
        filtered.append(dev.split(maxsplit=1)[1])

seen = set()
out = []
for elem in filtered:
    prefix = elem.split(' ')[1]
    if prefix not in seen:
        seen.add(prefix)
        out.append(elem)

# sort list 
out.sort(key=lambda x:int(x.split()[3]), reverse=True)

# writes out the amount of bluetooth devices found on the main screen
text.insert('0.5', 'Amount of devices found: ' + str(len(out)) + '\n\n')

# funktion to get the current time
def get_time():
    return now.strftime('%H:%M:%S')

# prints out the time of the scan on the main screen
text.insert('1.0','The time of the scan: ' + str(get_time()) + '\n\n')

# writes out the results on the main screen
for i in range(0,len(out)):
    position = f'{i+5}.{len(out[i])}'
    tempStr = out[i] + "\n"
    text.insert(position,f' {tempStr}')

# is supposed to delet everyting on the list
out.clear()

#the funktion for the scan button
def button_clicked():
    # enables the programe to change the results on the main screen to the new ones after the user presses the scan button
    text['state'] = 'normal'
    # update the current time.
    now = datetime.datetime.now()
    # funktion to get the current time
    def get_time():
        return now.strftime('%H:%M:%S')
    # this simply puts a emty row betwen the results and the rest of the output on kommandotolken
    print()
    # this delets the previous output that is on the main screen
    text.delete('0.0', tk.END)

    # this is the part of the code that communicates whit the dongle 
    console.write(str.encode("AT+GAPSCAN=3"))
    console.write("\r".encode())
    time.sleep(0.1)
    
    dongle_output2 = console.read(console.in_waiting)
    time.sleep(3)
    filtered = []

    for dev in dongle_output2.decode().splitlines():
        if len(dev)>20:
            filtered.append(dev.split(maxsplit=1)[1])

    seen = set()
    out = []
    for elem in filtered:
        prefix = elem.split(' ')[1]
        if prefix not in seen:
            seen.add(prefix)
            out.append(elem)

    # sort list 
    out.sort(key=lambda x:int(x.split()[3]), reverse=True)

    
    #writes out the time of the scan on the main screen
    text.insert('1.0','The time of the scan: ' + str(get_time()) + '\n\n')

     # writes out the amount of bluetooth devices found on the main screen
    text.insert('0.0', 'Amount of devices found: ' + str(len(out)) + '\n\n')

    # writes out the results on the main screen
    for i in range(0,len(out)):
        position = f'{i+5}.{len(out[i])}'
        tempStr = out[i] + "\n" 
        text.insert(position,f' {tempStr}')

    # makes it so that you cant edite the results on the main screen
    text['state'] = 'disabled'

#what calls the function for the scan button. also fixes what the user will see as the buttons name.
main_button = ttk.Button(
    main_window,
    text='Scan again',
    command=lambda: button_clicked()
)
# creat the scan button 
main_button.grid(row=0, column=1, sticky=tk.EW)

# just an exit button.
exit_button = ttk.Button(
    main_window,
    text='Exit',
    command=lambda: main_window.quit()
)
# this determens were the exit button is located 
exit_button.grid(row=2, column=1, sticky=tk.EW)

# makes it so that you cant edite the results on the main screen
text['state'] = 'disabled'

# keeps the main window open
main_window.mainloop()

time.sleep(1)
console.close()

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

Run the script

To run the script we use start

pythonw Scan_ble.pyw

Note : Scan_ble.pyw is the file name

Output

After running the script, we see a total 25 devices found nearby. We can scan again using the ‘Scan again’ button

Share this post on :

C# desktop application to Scan for nearby Bluetooth devices

Bluetooth Low Energy (BLE) is a low-power wireless technology used for connecting devices with each other. It is a popular communication method, especially in the era of the Internet of Things. Several devices around the house have a built-in Bluetooth transceiver and most of them provide useful capabilities to automate jobs. For that reason, it is really interesting to create a desktop application using C# that connects to the devices around the house and manages them.

In this example, we are going to create a simple C# windows form application that scans and shows a list of nearby Bluetooth devices. 

Let’s start

As a first step 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 through the serial port.

We will have buttons that connect and disconnects from the dongle. We create a button called Scan. And a text area will display a list of Bluetooth devices.
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/c-sharp-scan-bluetooth-device.git

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

namespace Scan_BLE_devices
{
    public partial class Form1 : Form
    {
        SerialPort mySerialPort = new SerialPort("COM18", 57600, Parity.None, 8, StopBits.One);
        public Form1()
        {
            InitializeComponent();
            mySerialPort.DataReceived += new SerialDataReceivedEventHandler(mySerialPort_DataReceived);
            mySerialPort.Open();

        }

        //print response from the dongle
        private void mySerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            SerialPort sp = (SerialPort)sender;
            string s = sp.ReadExisting();
            output_data.Invoke(new EventHandler(delegate { output_data.Text += s + "\r\n"; }));
            //lbl_output.Invoke(this.myDelegate, new Object[] { s });
        }





   

        private void Form1_Load(object sender, EventArgs e)
        {

        }

        private void btn_disconnect_Click_1(object sender, EventArgs e)
        {
            mySerialPort.Close();
            Environment.Exit(0);
        }

        private void button1_Click_1(object sender, EventArgs e)
        {
            lbl_test.Text = "Connected";
        }

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

            System.Threading.Thread.Sleep(1000);
            output_data.Text = "";
            byte[] bytes2 = Encoding.UTF8.GetBytes("AT+GAPSCAN=3");
            var inputByte2 = new byte[] { 13 };
            bytes2 = bytes2.Concat(inputByte2).ToArray();
            mySerialPort.Write(bytes2, 0, bytes2.Length);
            //System.Threading.Thread.Sleep(3000);
        }

        private void output_data_TextChanged(object sender, EventArgs e)
        {

        }
    }
}

As you can notice I wrote COM18 to connect to serial port because BleuIO device on my computer is connected to COM18.
You can check your COM port from device manager.
Lets run the project and click on connect button.

Once we are connected to BlueIO dongle through serial port, we will be able to scan for nearby Bluetooth device. Clicking on Scan button will show a list of nearby Bluetooth device on the screen.

Output

Share this post on :

Get company identifier information from Bluetooth advertising packets using Python

Bluetooth Advertisements are crucial for any BLE device since they are utilized for all types of applications, whether that’s a device that allows connections or one that simply advertises its presence and includes data for others to discover.

The most important goal of advertising packets is to convey information to other BLE devices via the advertising packet type, and the advertising data types included in the packet.

Bluetooth beacons are the most prominent devices that take full advantage of Bluetooth advertising packets. This is due to the reason that most beacons stay in the advertising state throughout their lifetime (do not allow connections), so they rely completely on advertising for relaying the relevant information to the scanning devices.

I tried to create a simple python example script that scans for nearby Bluetooth devices and returns the manufacturer company information by reading the advertising packet.

Requirments 

Steps

  • Get the script from GitHub at https://github.com/smart-sensor-devices-ab/python_bluetooth_device_info.git
  • Connect the BleuIO to your computer. The script uses pyserial to connect to the Bluetooth USB dongle BleuIO.
  • Update the script and write the correct COM port, where the dongle is connected. (main.py line 6)
  • After connecting to the dongle, we put the dongle into the central role so that it can scan for nearby Bluetooth devices. 
  • Then we do a simple Gap scan using AT+GAPSCAN=3 command to scan for nearby Bluetooth devices for 3 seconds.
  • After that, we read the output from the serial port and print out the list of devices with MAC addresses.
  • User selects a device to get manufacturer company information
  • We read the advertising packet for this specific device. 
  • Pass the response to a function which separates the manufacturer id. 
  • Then we try to find a match from an object which I have downloaded recently from https://www.bluetooth.com/specifications/assigned-numbers/company-identifiers/
  • The company name shows on the screen.

Here is the final script file. 

import serial
import time
from companydata import companyData
       

your_com_port = "COM18"  # Change this to the com port your dongle is connected to.
connecting_to_dongle = True

print("Connecting to dongle...")
# Trying to connect to dongle until connected. Make sure the port and baudrate is the same as your dongle.
# You can check in the device manager to see what port then right-click and choose properties then the Port Settings
# tab to see the other settings

# function to get company name from advertised id
def getCompany(adv):
  # example advertised package would look like this 
  # 0201061BFF5B070504220118A3003127ED006901090100000000000001BD03
  # explains here 
  # https://twitter.com/josryke/status/763006284052463617/photo/1
  indentifierReversed=''
  # first part 02 is the length
  length = int(adv[0:2],16)  
  pl =int(adv[length*2+2:length*2+4 ], 16) 
  # this gives us 1B which is 27 in decimal. that is our length
  startsFrom = length*2+4
  # this gives us 8, from where it starts
  # now get the packet
  fd=adv[startsFrom:pl]
  # look for the position of flag FF
  flagPosition = fd.find("FF")
  if flagPosition!=-1:    
    identifier = fd[flagPosition+2:flagPosition+6]
    # get 5B07
    indentifierReversed = identifier[2]+identifier[3]+identifier[0]+identifier[1]
    # get 075B
    # now look for the company name on the list
    for attr in companyData:
        if attr['Hexadecimal']=='0x'+indentifierReversed:
            theName=attr['Company']
  else:
    indentifierReversed='-'
    theName='Unknown'
  
  return theName

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.")

#put the dongle in dual role, so we can scan for nearby device
console.write(str.encode("AT+CENTRAL"))
console.write("\r".encode())
print("Putting dongle in Central role.")
time.sleep(0.1)
# Scan for nearby devices for 3 seconds
console.write(str.encode("AT+GAPSCAN=3"))
console.write("\r".encode())
time.sleep(0.1)
print("Looking for nearby Bluetooth devices ...")
dongle_output2 = console.read(console.in_waiting)
time.sleep(3)
filtered = []
# Filter out unncecssary outputs and keep only the list of devices (also remove index)
for dev in dongle_output2.decode().splitlines():
    if len(dev)>20:
        filtered.append(dev)

# Get unique device by device id and add distance to each raw        
seen = set()
out = []
for elem in filtered:
    prefix = elem.split(' ')[2]
    if prefix not in seen:
        seen.add(prefix)
        out.append(elem) 

# sort list by closest device
# out.sort(key=lambda x:int(x.split()[3]),reverse=True)
print("Scan Completed! "+ str(len(out)) +" devices found.")
# print(out)
for i in range(0, len(out)):
    print (out[i]) 

getInput = input("Select device from the list to get company identifier information (ex.1): ")
deviceToScan = out[int(getInput)-1].split(" ")[2]
# clear output
console.flushInput()
console.flushOutput()
time.sleep(0.1)
# Scan for advertised data of the selected device for 4 seconds
console.write(str.encode("AT+SCANTARGET="+deviceToScan+"=4"))
console.write("\r".encode())
time.sleep(0.1)
print("Getting company identifier information ...")
dongle_output3 = console.read(console.in_waiting)
time.sleep(5)
resp = dongle_output3.decode().splitlines()
# get the adv data only 
for d in resp:
    if('[ADV]' in d):
        companyName=getCompany(d.split(" ")[-1])
        break;
print(companyName)               
time.sleep(0.1)
console.close()

Output

After running the script, we see a total of 20 devices found nearby. The script prints out the manufacturer company information of the device [05] Device: [1]C1:BF:22:71:81:36

Share this post on :

Scan for nearby Bluetooth device using JAVA

This article is a guide for creating Java applications that can scan for nearby Bluetooth devices. This example project will be helpful to create BLE application easily. Source file is available.

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.

By clicking on the SCAN button , it will put the dongle into CENTRAL mode, wait for one second and write another AT command (AT+GAPSCAN=3) to scan for nearby Bluetooth devices for three seconds.

The response will be available on the output panel.

The script can be updated as required. For example. If you want to scan for more than three seconds, just update the MainJFrame.java file 271 line.

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. After successful connection, we should be able to click on scan button. The response will be available on output screen.

If we click on scan button again , it will look for nearby devices for three seconds and show the output.

Share this post on :

Measuring distance with Bluetooth in indoor environment using Python

Bluetooth ranging technology is very popular. There are many localization systems that exist based on beacons. Beacon technology usually estimates the distance between devices using the received signal strength (RSSI). 

Bluetooth can be an excellent way to narrow down a search area at close distances when tracking something. This feature can be used in several fields. such as Secure Locks for Buildings and Automotive, Asset localization & tracking, Indoor navigation etc

GPS tracking isn’t excellent at giving accurate measurements of the close distance, especially in the indoor environment. On the other hand, Bluetooth is excellent in short ranges because the waves can go through walls. This might fill the gap that GPS tracking has when tracking devices in indoor spaces.

However, most calculations of the distance between two Bluetooth devices are estimates. It’s hard to determine the exact distance between two Bluetooth devices because many factors affect the calculations. Despite the challenges, there are methods to determine the distance between two Bluetooth devices with an accuracy of at least 80%.

The ranging method is simple to implement, and it has the formula to calculate the distance between two Bluetooth devices. As the name suggests, both devices need to be within Bluetooth range to estimate the distance. 

This article will share a simple python script to determine nearby Bluetooth devices and their distance in meters.

This script scans for nearby Bluetooth devices and gets an approximation of the distance by using the well-known RSSI to distance formula.

Read more about how to calculate the distance

Requirments 

Instructions

  • Get the script from GitHub at https://github.com/smart-sensor-devices-ab/python_bluetooth_device_distance_meter.git
  • Connect the BleuIO to your computer. The script uses pyserial to connect to the Bluetooth USB dongle BleuIO.
  • Update the script and write the correct COM port, where the dongle is connected. 
  • After connecting to the dongle, we put the dongle into the central role so that it can scan for nearby Bluetooth devices. 
  • Then we do a simple Gap scan using AT+GAPSCAN=3 command to scan for nearby Bluetooth devices for 3 seconds.
  • After that, we read the output from the serial port and use our RSSI to distance formula to get the distance in meters. 
  • Finally, we sort the result by distance before printing it out on screen.

Here is the final script file. 

import serial
import time

your_com_port = "COM18"  # Change this to the com port your dongle is connected to.
connecting_to_dongle = True

print("Connecting to dongle...")
# Trying to connect to dongle until connected. Make sure the port and baudrate is the same as your dongle.
# You can check in the device manager to see what port then right-click and choose properties then the Port Settings
# tab to see the other settings

while connecting_to_dongle:
    try:
        console = serial.Serial(
            port=your_com_port,
            baudrate=57600,
            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.")

# function to convert rssi to distance in meter
def rssiToDistance(rssi):    
  n=2
  mp=-69
  return round(10 ** ((mp - (int(rssi)))/(10 * n)),2)    

#put the dongle in dual role, so we can scan for nearby device
console.write(str.encode("AT+CENTRAL"))
console.write("\r".encode())
print("Putting dongle in Central role.")
time.sleep(0.1)
# Scan for nearby devices for 3 seconds
console.write(str.encode("AT+GAPSCAN=3"))
console.write("\r".encode())
time.sleep(0.1)
print("Looking for nearby Bluetooth devices ...")
dongle_output2 = console.read(console.in_waiting)
time.sleep(3)
print("Scan Complete!")
filtered = []
# Filter out unncecssary outputs and keep only the list of devices (also remove index)
for dev in dongle_output2.decode().splitlines():
    if len(dev)>20:
        filtered.append(dev.split(maxsplit=1)[1])
# Get unique device by device id and add distance to each raw        
seen = set()
out = []
for elem in filtered:
    prefix = elem.split(' ')[1]
    if prefix not in seen:
        seen.add(prefix)
        out.append(elem + " Distance: "+str(rssiToDistance(elem.split()[3]))+" meter") 

# sort list by closest device
out.sort(key=lambda x:int(x.split()[3]),reverse=True)

# print(out)
for i in range(0, len(out)):
    print (out[i]) 

time.sleep(0.1)
console.close()

Output

After running the script, we see a total 20 devices found nearby. The list shows their distance in meter from the central device.

Share this post on :

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 :

Build a Bluetooth Low energy application with the Chrome. Web Bluetooth vs Web Serial (Bluetooth Dongle)

In general, most of us think of Bluetooth as a simple device-to-device connection used to do things like play music or other audio (speakers/headsets), offer quick access (smartwatches), or perform other tasks. But there is a new Bluetooth standard and it allows the browser to control Bluetooth devices nearby.

These features can be accessed using Web Bluetooth or Web serial API. What is promising is that both features are already available in Chromium-based browsers (Google Chrome, Edge, Opera). It makes it easy for web developers to interact with users’ peripherals in their homes – if the user would of course allow them.

Web applications are adapting to face the challenges of a competitive native environment. There is a concern that the browser may connect to nearby Bluetooth devices – wondering what kind of information the site can access is a question that needs to be asked. The good news is that, as with all other APIs built into browsers like Chrome, every website has to request access. The browser shows a pop-up asking for permission to access the site in question, just as it does for messaging, site access or the webcam. If the user does not respond, the request will be denied automatically. Users can also change this permit decision at any time. 

What is Web Bluetooth?

Web Bluetooth allows websites to communicate with nearby Bluetooth devices. That means no need to install any dedicated native app to connect to a beacon, heart rate monitor, smart light bulb, or any other Bluetooth Low Energy device. Using Web Bluetooth, developers can easily connect to nearby BLE devices and read/write Bluetooth characteristics. However, web Bluetooth has certain limitations when it comes to developing a complex Bluetooth application.

What is Web Serial?

A serial port is a bidirectional communication interface that allows sending and receiving data byte by byte. The Web Serial API provides a way for websites to read from and write to serial devices with JavaScript. These devices may be connected via a serial port, or by USB or Bluetooth devices that emulate a serial port.

Developing a complex Bluetooth Low Energy application for a web browser is relatively easy with Web Serial and a Bluetooth USB dongle BleuIO. The AT commands available on Bluetooth Low Energy USB dongle called Bleuio helps to do BLE operations easily. List of AT commands can be found here, https://www.bleuio.com/getting_started/docs/commands/

Another advantage of using Web Serial; there are good web serial libraries available along with BleuIO’s own JS library , which makes it easier to develop Bluetooth Low Energy applications

We think both the API has the potential to revolutionize how companies will create the BLE devices of the future, allowing users to manage and configure these devices by simply using a browser. We also hope this becomes officially supported by more browsers.

Share this post on :