<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Bleuio tech</title>
    <description>The latest articles on DEV Community by Bleuio tech (@bleuiot).</description>
    <link>https://dev.to/bleuiot</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F750450%2Ff1213bd0-3cf0-4212-915a-ee90ecdf41ea.png</url>
      <title>DEV Community: Bleuio tech</title>
      <link>https://dev.to/bleuiot</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/bleuiot"/>
    <language>en</language>
    <item>
      <title>Less Setup, More Development: Simplify Bluetooth Low Energy Testing with BleuIO</title>
      <dc:creator>Bleuio tech</dc:creator>
      <pubDate>Mon, 26 Oct 2026 19:41:46 +0000</pubDate>
      <link>https://dev.to/bleuiot/less-setup-more-development-simplify-bluetooth-low-energy-testing-with-bleuio-2g22</link>
      <guid>https://dev.to/bleuiot/less-setup-more-development-simplify-bluetooth-low-energy-testing-with-bleuio-2g22</guid>
      <description>&lt;h1&gt;
  
  
  Less Setup, More Development: Simplify Bluetooth Low Energy Testing with BleuIO
&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;What if you could start testing Bluetooth Low Energy devices with a simple command instead of setting up an entire SDK?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Bluetooth Low Energy (BLE) development often involves installing SDKs, configuring libraries, and understanding platform-specific Bluetooth APIs before you can even begin testing your idea.&lt;/p&gt;

&lt;p&gt;But what if you could skip much of that setup and focus on what really matters — &lt;strong&gt;building, testing, and improving your application logic?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That's where &lt;a href="https://www.bleuio.com/" rel="noopener noreferrer"&gt;BleuIO&lt;/a&gt; comes in.&lt;/p&gt;

&lt;h2&gt;
  
  
  BLE Development Without the SDK Complexity
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;BleuIO is a Bluetooth Low Energy USB dongle that lets developers interact with BLE devices using simple AT commands over a serial connection.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Plug it into a compatible host, open a serial terminal, and start working with BLE immediately.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AT+GAPSCAN
AT+SCANTARGET
AT+GAPCONNECT
AT+FINDSCANDATA
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhagj2clhfa2wdxbsic26.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhagj2clhfa2wdxbsic26.webp" alt="BleuIO AT Commands in Terminal" width="500" height="300"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With these commands, you can discover nearby BLE devices, target specific devices, establish connections, and inspect advertising data.&lt;/p&gt;

&lt;p&gt;No dedicated BLE SDK is required to access the dongle's built-in Bluetooth functions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Spend Less Time Setting Up, More Time Building
&lt;/h2&gt;

&lt;p&gt;Whether you're prototyping an IoT solution, debugging a BLE sensor, or testing communication between devices, BleuIO helps simplify your development workflow.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Faster prototyping:&lt;/strong&gt; Test BLE functionality without building a complete application first.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Easier debugging:&lt;/strong&gt; Send commands and inspect responses directly from your terminal.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Language flexibility:&lt;/strong&gt; Integrate with Python, JavaScript, C/C++, .NET, or any language that supports serial communication.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cross-platform development:&lt;/strong&gt; Work with Windows, macOS, Linux, Raspberry Pi, and other compatible hosts.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead of spending time navigating Bluetooth APIs, you can concentrate on &lt;strong&gt;application logic, device behavior, and the features you're building.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep Bluetooth Simple
&lt;/h2&gt;

&lt;p&gt;BLE development doesn't always need to begin with a complex software setup.&lt;/p&gt;

&lt;p&gt;Sometimes, a serial connection and a few AT commands are all you need to move from an idea to a working BLE test.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Less setup. Faster testing. More time for development.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Explore &lt;a href="https://www.bleuio.com/" rel="noopener noreferrer"&gt;BleuIO&lt;/a&gt; or check out the &lt;a href="https://www.bleuio.com/getting_started/docs/commands/" rel="noopener noreferrer"&gt;AT Command Reference&lt;/a&gt; to get started.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Tags:&lt;/strong&gt; Bluetooth Low Energy, BLE, IoT, Embedded Systems, Software Development&lt;/p&gt;

</description>
      <category>bluetooth</category>
      <category>bleuio</category>
      <category>iot</category>
      <category>embeddedsystem</category>
    </item>
    <item>
      <title>Turn your Cytron MOTION 2350 Pro into a standalone air quality monitor with BleuIO and CircuitPython!</title>
      <dc:creator>Bleuio tech</dc:creator>
      <pubDate>Wed, 21 Oct 2026 11:07:29 +0000</pubDate>
      <link>https://dev.to/bleuiot/turn-your-cytron-motion-2350-pro-into-a-standalone-air-quality-monitor-with-bleuio-and-30bf</link>
      <guid>https://dev.to/bleuiot/turn-your-cytron-motion-2350-pro-into-a-standalone-air-quality-monitor-with-bleuio-and-30bf</guid>
      <description>&lt;p&gt;In this tutorial we will use &lt;strong&gt;CircuitPython&lt;/strong&gt; to turn a Cytron MOTION 2350 Pro into a HibouAir scanner. The board’s RP2350 has no Bluetooth radio of its own. A &lt;strong&gt;BleuIO dongle&lt;/strong&gt; in the board’s USB-A host port provides Bluetooth Low Energy, and a short CircuitPython program talks to it with ordinary AT commands.&lt;/p&gt;

&lt;p&gt;The program scans for &lt;strong&gt;&lt;a href="https://www.hibouair.com/" rel="noopener noreferrer"&gt;air quality monitoring device&lt;/a&gt;&lt;/strong&gt; HibouAir advertisements, decodes them, and keeps the latest readings in global variables. One of them, the PM10 value from a &lt;strong&gt;&lt;a href="https://www.hibouair.com/specifications-air-quality-monitor-pm-sensor.php" rel="noopener noreferrer"&gt;HibouAir PM sensor&lt;/a&gt;&lt;/strong&gt;, is ready to be used by the rest of your program. As an example, the program turns a hobby servo on one of the board’s servo ports into an analog PM10 gauge.&lt;/p&gt;

&lt;p&gt;Everything runs on the board. No computer is needed once the code is in place, and the HibouAir sensors do not need to be paired.&lt;/p&gt;

&lt;h2&gt;
  
  
  Requirements
&lt;/h2&gt;

&lt;h4&gt;
  
  
  Hardware
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;a href="https://www.digikey.com/en/products/detail/cytron-technologies-sdn-bhd/MOTION-2350-PRO/25587464?s=N4IgTCBcDaILYHsAuBLBA7ABABwE4MzAGYBWABhAF0BfIA" rel="noopener noreferrer"&gt;Cytron MOTION 2350 Pro&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://www.bleuio.com/bluetooth-low-energy-usb-ssd025.php" rel="noopener noreferrer"&gt;BleuIO USB dongle&lt;/a&gt;. This tutorial was tested with a BleuIO Pro, firmware 1.0.7.1.&lt;/li&gt;
&lt;li&gt;  One or more &lt;a href="https://www.hibouair.com/air-quality-monitor-hibouair-duo.php" rel="noopener noreferrer"&gt;HibouAir air quality sensors&lt;/a&gt;. A &lt;a href="https://www.hibouair.com/specifications-air-quality-monitor-pm-sensor.php" rel="noopener noreferrer"&gt;HibouAir PM sensor&lt;/a&gt; is needed for the PM10 value.&lt;/li&gt;
&lt;li&gt;  A &lt;a href="https://www.bleuio.com/accessories.php" rel="noopener noreferrer"&gt;USB-C data cable&lt;/a&gt; between the board and your computer&lt;/li&gt;
&lt;li&gt;  Optional: a hobby servo for the PM10 gauge&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Software
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;CircuitPython 10.x&lt;/strong&gt; for the Cytron MOTION 2350 Pro, from &lt;a href="https://circuitpython.org/board/cytron_motion_2350_pro/" rel="noopener noreferrer"&gt;circuitpython.org&lt;/a&gt;. The tutorial uses 10.3.1.&lt;/li&gt;
&lt;li&gt;  Two libraries from the &lt;a href="https://circuitpython.org/libraries" rel="noopener noreferrer"&gt;Adafruit CircuitPython Library Bundle&lt;/a&gt; for version 10.x, copied into the &lt;code&gt;lib&lt;/code&gt; folder on the &lt;code&gt;CIRCUITPY&lt;/code&gt; drive:

&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;asyncio&lt;/code&gt; (the whole folder)&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;adafruit_ticks.mpy&lt;/code&gt;, which &lt;code&gt;asyncio&lt;/code&gt; needs&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With &lt;a href="https://github.com/adafruit/circup" rel="noopener noreferrer"&gt;circup&lt;/a&gt; installed on your computer, one command copies both:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;circup install asyncio
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Everything else the program uses is built into CircuitPython: &lt;code&gt;usb_host&lt;/code&gt;, &lt;code&gt;usb.core&lt;/code&gt;, &lt;code&gt;json&lt;/code&gt;, &lt;code&gt;binascii&lt;/code&gt;, &lt;code&gt;microcontroller&lt;/code&gt;, &lt;code&gt;pwmio&lt;/code&gt; and &lt;code&gt;time&lt;/code&gt;. The servo is driven with &lt;code&gt;pwmio&lt;/code&gt; directly, so it needs no extra library. No &lt;code&gt;boot.py&lt;/code&gt; is needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Does This Project Do?
&lt;/h2&gt;

&lt;h4&gt;
  
  
  1. The board becomes a USB host
&lt;/h4&gt;

&lt;p&gt;The MOTION 2350 Pro’s USB-A port is driven by the RP2350’s PIO. CircuitPython starts it with &lt;code&gt;usb_host.Port&lt;/code&gt; on GPIO24 and GPIO25. The board definition has no named pins for this port, so the program uses the raw pins.&lt;/p&gt;

&lt;h4&gt;
  
  
  2. CircuitPython finds BleuIO
&lt;/h4&gt;

&lt;p&gt;The program waits until a device with vendor ID &lt;code&gt;0x2DCF&lt;/code&gt; and product ID &lt;code&gt;0x6002&lt;/code&gt; appears, which is BleuIO. It selects the dongle’s configuration and sets DTR and RTS, so that BleuIO starts sending its output.&lt;/p&gt;

&lt;h4&gt;
  
  
  3. The dongle is brought into a known state
&lt;/h4&gt;

&lt;p&gt;BleuIO remembers what it was doing, so a scan from an earlier run may still be going. The program first sends Ctrl-C three times to stop it. It then throws away everything the dongle sends until it has been quiet for 0.3 seconds. After that it sends:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;ATE0&lt;/code&gt;, which turns echo off&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;ATV1&lt;/code&gt;, which turns verbose mode on, so every reply is one line of JSON&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  4. BleuIO scans for HibouAir only
&lt;/h4&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AT+FINDSCANDATA=FF5B07
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This reports only advertisements that contain &lt;code&gt;FF5B07&lt;/code&gt;. &lt;code&gt;FF&lt;/code&gt; is the AD type for manufacturer-specific data, and &lt;code&gt;5B 07&lt;/code&gt; is HibouAir’s company ID, 0x075B, as it appears in the packet. Each result arrives as a line such as:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{"SF":127,"addr":"D9:63:8F:8E:01:0D","type":0,"data":"0201061BFF5B070504220095AE009627CF00AC014200000000000000019902"}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h4&gt;
  
  
  5. The board decodes the advertisement
&lt;/h4&gt;

&lt;p&gt;The program walks the advertisement’s AD structures until it finds the HibouAir manufacturer data. This works whether or not the flags field comes first. Counting from the company ID, the sensor beacon (beacon type 0x05) holds:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Offset&lt;/th&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;Format&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Beacon type&lt;/td&gt;
&lt;td&gt;0x05 for sensor readings&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Board type&lt;/td&gt;
&lt;td&gt;0x03 is the PM sensor&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4–6&lt;/td&gt;
&lt;td&gt;Sensor ID&lt;/td&gt;
&lt;td&gt;as shown in the HibouAir app, e.g. 22026A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;Pressure&lt;/td&gt;
&lt;td&gt;16-bit little-endian, tenths of hPa&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;td&gt;Temperature&lt;/td&gt;
&lt;td&gt;16-bit little-endian, signed, tenths of °C&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;13&lt;/td&gt;
&lt;td&gt;Humidity&lt;/td&gt;
&lt;td&gt;16-bit little-endian, tenths of %rh&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;17&lt;/td&gt;
&lt;td&gt;PM1.0&lt;/td&gt;
&lt;td&gt;16-bit little-endian, tenths of µg/m³&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;19&lt;/td&gt;
&lt;td&gt;PM2.5&lt;/td&gt;
&lt;td&gt;16-bit little-endian, tenths of µg/m³&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;21&lt;/td&gt;
&lt;td&gt;PM10&lt;/td&gt;
&lt;td&gt;16-bit little-endian, tenths of µg/m³&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;23&lt;/td&gt;
&lt;td&gt;CO2&lt;/td&gt;
&lt;td&gt;16-bit &lt;strong&gt;big&lt;/strong&gt;-endian, ppm&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h4&gt;
  
  
  6. The readings are kept in global variables
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;pm10&lt;/code&gt;: the latest PM10 value in µg/m³, or &lt;code&gt;None&lt;/code&gt; until a PM sensor has been heard&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;pm10_sensor&lt;/code&gt;: the ID of the sensor it came from&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;pm10_time&lt;/code&gt;: &lt;code&gt;time.monotonic()&lt;/code&gt; when it arrived, so that a stale value can be recognised&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;sensors&lt;/code&gt;: a dictionary with everything the last beacon of every HibouAir sensor said, keyed by sensor ID&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you have more than one PM sensor, set &lt;code&gt;PM_SENSOR_ID&lt;/code&gt; to the one &lt;code&gt;pm10&lt;/code&gt; should follow. If nothing has been heard for 15 seconds, the program starts the scan again.&lt;/p&gt;
&lt;h2&gt;
  
  
  Showing PM10 on a Servo Gauge
&lt;/h2&gt;

&lt;p&gt;The MOTION 2350 Pro has eight servo ports, on GP0 to GP7, each with a PWM channel of its own. The program drives a servo on &lt;strong&gt;GP0&lt;/strong&gt; as a needle: 0 µg/m³ at 0 degrees, and 100 µg/m³ or more at 180 degrees.&lt;/p&gt;

&lt;p&gt;With that full scale, the PM10 limits the HibouAir app uses fall at simple angles. That is where to draw the bands on a paper scale behind the servo arm:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;PM10&lt;/th&gt;
&lt;th&gt;Level&lt;/th&gt;
&lt;th&gt;Needle&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0–20 µg/m³&lt;/td&gt;
&lt;td&gt;Good&lt;/td&gt;
&lt;td&gt;0–36°&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;20–50 µg/m³&lt;/td&gt;
&lt;td&gt;Fair&lt;/td&gt;
&lt;td&gt;36–90°&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;50–100 µg/m³&lt;/td&gt;
&lt;td&gt;Poor&lt;/td&gt;
&lt;td&gt;90–180°&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;100 µg/m³ and above&lt;/td&gt;
&lt;td&gt;Bad&lt;/td&gt;
&lt;td&gt;180°&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The gauge is its own asyncio task, beside the reader and the command logic:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  It sends a 50 Hz servo signal, from 500 µs at 0 degrees to 2500 µs at 180.&lt;/li&gt;
&lt;li&gt;  It moves the needle 2 degrees every 20 ms, so the arm swings to a new reading instead of jumping.&lt;/li&gt;
&lt;li&gt;  At start it sweeps once across the whole scale. A servo that is not connected is then noticed at once, instead of being taken for clean air.&lt;/li&gt;
&lt;li&gt;  Once the needle has been still for half a second, the pulses stop. A servo told to hold a position holds it actively, and at 0 degrees, the commonest reading of all, it pushes against its end stop and hums. Without pulses an ordinary hobby servo goes quiet and stays where it is.&lt;/li&gt;
&lt;li&gt;  The pulses start again when a new reading moves the needle by at least one degree, so readings of 0.0 and 0.1 µg/m³ taking turns do not wake it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Connect the servo to the GP0 servo header, matching the servo cable to the markings next to the header. Check Cytron’s datasheet for how the servo ports are supplied before connecting a larger servo.&lt;/p&gt;

&lt;p&gt;The settings are at the top of &lt;code&gt;code.py&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;SERVO_PIN&lt;/code&gt;: the port, &lt;code&gt;board.GP0&lt;/code&gt; to &lt;code&gt;board.GP7&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;PM10_FULL_SCALE&lt;/code&gt;: the PM10 value at 180 degrees&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;SERVO_MIN_US&lt;/code&gt; and &lt;code&gt;SERVO_MAX_US&lt;/code&gt;: the pulse widths at the ends. Narrow them if your servo strains at its end stops.&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;SERVO_STEP_DEG&lt;/code&gt;: how fast the needle moves&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;SERVO_REST_S&lt;/code&gt; and &lt;code&gt;SERVO_DEADBAND_DEG&lt;/code&gt;: how long the needle is still before it rests, and how far a reading must move it to wake it&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The board’s four DC motor drivers, on GP8 to GP15 (&lt;code&gt;board.M1A&lt;/code&gt; to &lt;code&gt;board.M4B&lt;/code&gt; in CircuitPython), would suit a different use of the same value, such as running a fan faster as PM10 rises.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why the Flush After Ctrl-C Matters
&lt;/h2&gt;

&lt;p&gt;Ctrl-C stops the scan, but whatever BleuIO had already queued still arrives afterwards. Often the last piece is a line cut off in the middle, sometimes with a byte that is not valid UTF-8.&lt;/p&gt;

&lt;p&gt;In early versions of this program, a reader that tried to decode that line before removing it from its buffer stuck on it. Every later piece of data was appended behind the same broken line, and the program heard nothing more, in about half of all restarts.&lt;/p&gt;

&lt;p&gt;The program now does two things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  It removes each line from the buffer before decoding it, so a broken line costs that line only.&lt;/li&gt;
&lt;li&gt;  It discards everything until the dongle has been quiet before sending the next command.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With both in place, the start-up worked every time in repeated tests, whether the dongle was fresh (echo on, verbose off) or left in verbose mode by an earlier run.&lt;/p&gt;
&lt;h2&gt;
  
  
  How to Run the Project
&lt;/h2&gt;
&lt;h4&gt;
  
  
  Step 1: Install CircuitPython
&lt;/h4&gt;

&lt;p&gt;Download the CircuitPython UF2 for the Cytron MOTION 2350 Pro and put the board into its UF2 bootloader mode. On RP2350 boards this is done by holding BOOT while pressing RESET, or while connecting the USB cable. Copy the UF2 to the &lt;code&gt;RP2350&lt;/code&gt; drive that appears. The board restarts as a drive named &lt;code&gt;CIRCUITPY&lt;/code&gt;.&lt;/p&gt;
&lt;h4&gt;
  
  
  Step 2: Add the libraries
&lt;/h4&gt;

&lt;p&gt;Run &lt;code&gt;circup install asyncio&lt;/code&gt;, or copy the &lt;code&gt;asyncio&lt;/code&gt; folder and &lt;code&gt;adafruit_ticks.mpy&lt;/code&gt; from the library bundle into &lt;code&gt;CIRCUITPY/lib&lt;/code&gt;.&lt;/p&gt;
&lt;h4&gt;
  
  
  Step 3: Copy the program
&lt;/h4&gt;

&lt;p&gt;Save the code at the end of this post as &lt;code&gt;code.py&lt;/code&gt; on the &lt;code&gt;CIRCUITPY&lt;/code&gt; drive. CircuitPython starts it as soon as the file is saved.&lt;/p&gt;
&lt;h4&gt;
  
  
  Step 4: Connect BleuIO
&lt;/h4&gt;

&lt;p&gt;Insert the BleuIO dongle into the board’s USB-A port. Do not connect BleuIO to the computer for this setup.&lt;/p&gt;
&lt;h4&gt;
  
  
  Step 5: Connect the servo (optional)
&lt;/h4&gt;

&lt;p&gt;Plug the servo into the GP0 servo header. It sweeps once from 0 to 180 degrees and back when the program starts, then follows PM10, and goes quiet whenever the needle is still.&lt;/p&gt;
&lt;h4&gt;
  
  
  Step 6: Watch the serial console
&lt;/h4&gt;

&lt;p&gt;Open the board’s serial console at 115200 baud. Use Mu, Thonny or the CircuitPython web workflow, or on macOS and Linux:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;screen /dev/cu.usbmodem2101 115200
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The device name differs from computer to computer. If the console shows &lt;code&gt;raw REPL; CTRL-B to exit&lt;/code&gt;, an editor has left it in raw REPL mode: press Ctrl-B, then Ctrl-D to start &lt;code&gt;code.py&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Expected Output
&lt;/h2&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;BleuIO dongle found! (0x2dcf:0x6002)
CDC Control Lines (DTR/RTS) enabled.
Starting background USB reader task...
Starting the HibouAir scan...



[HibouAir 22026A] PM10 0.1 ug/m3  PM2.5 0.1  PM1 0.0  21.8 C
[HibouAir 22008C] board 0x04, 21.5 C, 39.8 %rh, CO2 459 ppm
[HibouAir 220095] board 0x04, 20.8 C, 42.9 %rh, CO2 411 ppm
[HibouAir 22005A] board 0x04, 19.6 C, 44.2 %rh, CO2 721 ppm
[HibouAir 220265] board 0x09, 19.8 C, 45.5 %rh, CO2 533 ppm
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Every HibouAir sensor in range appears with its ID. Lines from the PM sensor show the particle readings; the others show temperature, humidity and CO2. Each new PM line also updates &lt;code&gt;pm10&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Code
&lt;/h2&gt;

&lt;p&gt;Source code is available to download from&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/smart-sensor-devices-ab/hibouair-with-circuitpython" rel="noopener noreferrer"&gt;https://github.com/smart-sensor-devices-ab/hibouair-with-circuitpython&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This project is a practical example of how BleuIO can bring Bluetooth Low Energy connectivity to development boards without built-in Bluetooth support. Using the Cytron MOTION 2350 Pro and CircuitPython, we demonstrated how to scan for HibouAir sensors, read real-time data, and use that data to control a servo motor.&lt;/p&gt;

&lt;p&gt;The complete source code is available for developers to explore, modify, and integrate into their own projects. Whether you’re building sensor monitoring systems, automation solutions, or custom IoT applications, this example provides a starting point for using BleuIO with compatible development boards.&lt;/p&gt;

</description>
      <category>bluetooth</category>
      <category>bleuio</category>
      <category>hibouair</category>
      <category>circuitpython</category>
    </item>
    <item>
      <title>Send BLE Air Quality Data to Cloudflare D1 with Python and BleuIO</title>
      <dc:creator>Bleuio tech</dc:creator>
      <pubDate>Thu, 15 Oct 2026 12:56:05 +0000</pubDate>
      <link>https://dev.to/bleuiot/send-ble-air-quality-data-to-cloudflare-d1-with-python-and-bleuio-41fj</link>
      <guid>https://dev.to/bleuiot/send-ble-air-quality-data-to-cloudflare-d1-with-python-and-bleuio-41fj</guid>
      <description>&lt;p&gt;In this tutorial, we will build a simple BLE-to-cloud air quality gateway using &lt;strong&gt;HibouAir, BleuIO, Python, Cloudflare Workers, and Cloudflare D1&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A HibouAir sensor will advertise its air quality measurements over Bluetooth Low Energy. BleuIO, connected to a computer through USB, will scan for a specific HibouAir device and provide the BLE advertising data to a Python application through the serial port. The Python application will decode &lt;strong&gt;CO2, temperature, and humidity&lt;/strong&gt;, display the measurements in the terminal, and upload them to a Cloudflare Worker. The Worker will then store the measurements in a Cloudflare D1 database.&lt;/p&gt;

&lt;p&gt;This project is intentionally kept simple. It demonstrates how BleuIO can act as the Bluetooth interface for a desktop or gateway application without requiring the application itself to manage a Bluetooth stack.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Cloudflare D1?
&lt;/h2&gt;

&lt;p&gt;Cloudflare D1 is a serverless SQL database built on SQLite. It works particularly well with Cloudflare Workers, allowing us to create a small HTTP API without running our own server.&lt;/p&gt;

&lt;p&gt;For a project like this, there are several advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  No database server to install or maintain&lt;/li&gt;
&lt;li&gt;  No local web server required&lt;/li&gt;
&lt;li&gt;  HTTPS is handled automatically&lt;/li&gt;
&lt;li&gt;  Cloudflare Workers can communicate directly with D1&lt;/li&gt;
&lt;li&gt;  D1 supports regular SQL&lt;/li&gt;
&lt;li&gt;  A useful &lt;strong&gt;free tier&lt;/strong&gt; is available&lt;/li&gt;
&lt;li&gt;  The database remains accessible even when the Computer running the BLE gateway is offline&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This makes it convenient for prototypes, BLE gateways, environmental monitoring projects, demonstrations, and small IoT applications.&lt;/p&gt;

&lt;p&gt;For this example, the Computer only needs to run the Python gateway application. Everything after the HTTP request is handled in the cloud.&lt;/p&gt;

&lt;h1&gt;
  
  
  Requirements
&lt;/h1&gt;

&lt;p&gt;For this project, you will need:&lt;/p&gt;

&lt;h3&gt;
  
  
  Hardware
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;&lt;a href="https://www.bleuio.com/bluetooth-low-energy-usb-ssd025.php" rel="noopener noreferrer"&gt;BleuIO USB Bluetooth Low Energy dongle&lt;/a&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;&lt;a href="https://www.hibouair.com/specifications-air-quality-monitor-co2-sensor.php" rel="noopener noreferrer"&gt;HibouAir CO2 air quality sensor&lt;/a&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Software and services
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;  Python 3&lt;/li&gt;
&lt;li&gt;  A free Cloudflare account&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;pyserial&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;requests&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For this example, the HibouAir sensor has the board ID:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;220069
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;and BleuIO appears on the Mac at:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/dev/cu.usbmodem4048FDEBA6D01
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Your serial port depends on operating system and HibouAir board ID may be different.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Create a Cloudflare D1 Database
&lt;/h2&gt;

&lt;p&gt;First, log in to your &lt;strong&gt;&lt;a href="https://dash.cloudflare.com/" rel="noopener noreferrer"&gt;Cloudflare account&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;From the Cloudflare dashboard, go to the D1 database section and create a new database.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4y1tnbxpttlr2f8kvf0b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4y1tnbxpttlr2f8kvf0b.png" width="799" height="402"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For this tutorial, we will call it:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;hibouair-db
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Once the database has been created, open its SQL Console.&lt;/p&gt;

&lt;p&gt;Run the following SQL:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CREATE TABLE IF NOT EXISTS readings (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    board_id TEXT NOT NULL,
    measured_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
    co2 INTEGER NOT NULL,
    temperature REAL NOT NULL,
    humidity REAL NOT NULL
);

CREATE INDEX IF NOT EXISTS idx_readings_board_time
ON readings(board_id, measured_at);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Faid7juqwscqlpx7k2aio.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Faid7juqwscqlpx7k2aio.png" width="800" height="441"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This creates a simple table containing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Database ID&lt;/li&gt;
&lt;li&gt;  HibouAir board ID&lt;/li&gt;
&lt;li&gt;  Timestamp&lt;/li&gt;
&lt;li&gt;  CO₂&lt;/li&gt;
&lt;li&gt;  Temperature&lt;/li&gt;
&lt;li&gt;  Humidity&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;measured_at&lt;/code&gt; field is generated automatically by D1 when the reading is inserted.&lt;/p&gt;

&lt;p&gt;A stored record will look conceptually like this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;id          1
board_id    220069
measured_at 2026-10-05 14:42:30
co2         623
temperature 24.1
humidity    43.7
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Step 2: Create the Cloudflare Worker
&lt;/h2&gt;

&lt;p&gt;Next, create a Cloudflare Worker.&lt;/p&gt;

&lt;p&gt;Now go to:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Workers &amp;amp; Pages → Create application&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Create a basic Worker. ( you can choose “Start with Hello world”)&lt;/p&gt;

&lt;p&gt;The Worker will sit between our Python application and the D1 database.&lt;/p&gt;

&lt;p&gt;For this project, we can name it:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;hibouair-api
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;and then press deploy.&lt;/p&gt;

&lt;p&gt;After creating the Worker, open the code editor and replace the default code with the following :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffknhhhil6te66dl8xd5a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffknhhhil6te66dl8xd5a.png" width="800" height="323"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export default {
  async fetch(request, env) {
    const url = new URL(request.url);

    // API status
    if (url.pathname === "/" &amp;amp;&amp;amp; request.method === "GET") {
      return Response.json({
        service: "HibouAir Cloud API",
        status: "online"
      });
    }

    // Store a new sensor reading
    if (url.pathname === "/api/readings" &amp;amp;&amp;amp; request.method === "POST") {
      const suppliedKey = request.headers.get("X-API-Key");

      if (!suppliedKey || suppliedKey !== env.API_KEY) {
        return Response.json(
          {
            success: false,
            error: "Unauthorized"
          },
          {
            status: 401
          }
        );
      }

      let data;

      try {
        data = await request.json();
      } catch {
        return Response.json(
          {
            success: false,
            error: "Invalid JSON"
          },
          {
            status: 400
          }
        );
      }

      const boardId = String(data.board_id || "").trim();
      const co2 = Number(data.co2);
      const temperature = Number(data.temperature);
      const humidity = Number(data.humidity);

      if (!boardId) {
        return Response.json(
          {
            success: false,
            error: "board_id is required"
          },
          {
            status: 400
          }
        );
      }

      if (
        !Number.isFinite(co2) ||
        !Number.isFinite(temperature) ||
        !Number.isFinite(humidity)
      ) {
        return Response.json(
          {
            success: false,
            error: "Invalid sensor values"
          },
          {
            status: 400
          }
        );
      }

      const result = await env.DB
        .prepare(`
          INSERT INTO readings (
            board_id,
            co2,
            temperature,
            humidity
          )
          VALUES (?, ?, ?, ?)
        `)
        .bind(
          boardId,
          co2,
          temperature,
          humidity
        )
        .run();

      return Response.json({
        success: true,
        message: "Reading stored",
        board_id: boardId,
        co2: co2,
        temperature: temperature,
        humidity: humidity,
        meta: result.meta
      });
    }

    // Return the latest reading
    if (
      url.pathname === "/api/readings/latest" &amp;amp;&amp;amp;
      request.method === "GET"
    ) {
      const boardId =
        url.searchParams.get("board_id") || "220069";

      const reading = await env.DB
        .prepare(`
          SELECT
            id,
            board_id,
            measured_at,
            co2,
            temperature,
            humidity
          FROM readings
          WHERE board_id = ?
          ORDER BY id DESC
          LIMIT 1
        `)
        .bind(boardId)
        .first();

      if (!reading) {
        return Response.json(
          {
            success: false,
            error: "No readings found"
          },
          {
            status: 404
          }
        );
      }

      return Response.json({
        success: true,
        reading: reading
      });
    }

    return Response.json(
      {
        success: false,
        error: "Not found"
      },
      {
        status: 404
      }
    );
  }
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Deploy the Worker.&lt;/p&gt;

&lt;p&gt;At this stage, the Worker exists, but it is not yet connected to the D1 database.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Bind the D1 Database to the Worker
&lt;/h2&gt;

&lt;p&gt;Open the settings for the &lt;code&gt;hibouair-api&lt;/code&gt; Worker.&lt;/p&gt;

&lt;p&gt;Add a &lt;strong&gt;D1 database binding&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Use:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Variable name: DB
Database: hibouair-db
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The variable name must be:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;because the Worker accesses the database through:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;env.DB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;await env.DB
    .prepare("SELECT * FROM readings")
    .all();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Once the binding has been added, deploy the Worker again if necessary.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fa5vl56rxsxrdzxr84uge.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fa5vl56rxsxrdzxr84uge.png" width="800" height="404"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Step 4: Add an API Key
&lt;/h2&gt;

&lt;p&gt;Our Python application will be publicly connected to the Cloudflare Worker over HTTPS.&lt;/p&gt;

&lt;p&gt;We therefore do not want anybody who discovers the Worker URL to be able to insert arbitrary sensor measurements.&lt;/p&gt;

&lt;p&gt;We can protect the write endpoint using a simple API key.&lt;/p&gt;

&lt;p&gt;On the Computer, generate a random key:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;python3 -c "import secrets; print(secrets.token_hex(32))"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This will generate a long random value.&lt;/p&gt;

&lt;p&gt;Copy it.&lt;/p&gt;

&lt;p&gt;Now create a Worker secret environment variable named:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;API_KEY
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;and paste the generated value as its value.&lt;/p&gt;

&lt;p&gt;The Python application will include this key in every request using:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;X-API-Key
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The Worker checks:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const suppliedKey =
    request.headers.get("X-API-Key");
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;and compares it against:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;env.API_KEY
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fm9n936d4kenmcskhj89p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fm9n936d4kenmcskhj89p.png" width="800" height="404"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: Test the Worker
&lt;/h2&gt;

&lt;p&gt;Cloudflare will provide a Worker URL similar to:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://hibouair-api.your-account.workers.dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Open the base Worker URL in a browser.&lt;/p&gt;

&lt;p&gt;You should receive something similar to:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "service": "HibouAir Cloud API",
  "status": "online"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;If you see this response, the Worker is running.&lt;/p&gt;

&lt;p&gt;Our Python application will send measurements to:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://hibouair-api.your-account.workers.dev/api/readings
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The latest stored reading can later be retrieved from:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://hibouair-api.your-account.workers.dev/api/readings/latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Step 6: Download the Python Project
&lt;/h2&gt;

&lt;p&gt;The source code for the BleuIO Python gateway is available on GitHub:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/smart-sensor-devices-ab/bleuio-cloudflare-d1-database" rel="noopener noreferrer"&gt;https://github.com/smart-sensor-devices-ab/bleuio-cloudflare-d1-database&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Clone the repository:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git clone https://github.com/smart-sensor-devices-ab/bleuio-cloudflare-d1-database
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Then enter the project directory:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd bleuio-cloudflare-d1-database
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The project structure is intentionally small:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bleuio-cloudflare-d1/
│
├── main.py
├── info.example.txt
├── requirements.txt
└── .gitignore
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The Python application handles:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Connecting to BleuIO&lt;/li&gt;
&lt;li&gt; Sending BleuIO AT commands&lt;/li&gt;
&lt;li&gt; Searching for the HibouAir board ID&lt;/li&gt;
&lt;li&gt; Reading BLE advertising data&lt;/li&gt;
&lt;li&gt; Decoding CO2, temperature, and humidity&lt;/li&gt;
&lt;li&gt; Displaying the measurement in the terminal&lt;/li&gt;
&lt;li&gt; Sending the measurement to Cloudflare&lt;/li&gt;
&lt;li&gt; Repeating the process at a configurable interval&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Step 7: Install the Python Dependencies
&lt;/h2&gt;

&lt;p&gt;It is recommended to create a Python virtual environment.&lt;/p&gt;

&lt;p&gt;Run:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;python3 -m venv .venv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Activate it:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;source .venv/bin/activate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Then install the required libraries:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install -r requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The application only requires:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pyserial
requests
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;code&gt;pyserial&lt;/code&gt; is used to communicate with BleuIO through the USB serial port.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;requests&lt;/code&gt; is used to send the decoded measurements to the Cloudflare Worker over HTTPS.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 8: Configure the Project
&lt;/h2&gt;

&lt;p&gt;The repository includes:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;info.example.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Copy it:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cp info.example.txt info.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Then open:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;info.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The configuration looks like this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;BLEUIO_PORT=/dev/cu.usbmodem4048FDEBA6D01
HIBOUAIR_SENSOR_ID=220069

SCAN_INTERVAL_SECONDS=30

CLOUDFLARE_API_URL=https://hibouair-api.YOUR-SUBDOMAIN.workers.dev/api/readings
CLOUDFLARE_API_KEY=YOUR_REAL_API_KEY
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;You need to update the values for your own environment.&lt;/p&gt;

&lt;h4&gt;
  
  
  BLEUIO_PORT
&lt;/h4&gt;

&lt;p&gt;On our Mac, BleuIO is available at:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/dev/cu.usbmodem4048FDEBA6D01
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Your port may be different.&lt;/p&gt;

&lt;p&gt;You can normally find USB serial devices with:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ls /dev/cu.*
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Look for the device corresponding to BleuIO.&lt;/p&gt;

&lt;p&gt;On windows , look for COM port on device manager.&lt;/p&gt;

&lt;h4&gt;
  
  
  HIBOUAIR_SENSOR_ID
&lt;/h4&gt;

&lt;p&gt;For this project, our HibouAir board ID is:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;220069
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Replace this with the board ID of the HibouAir device you want to monitor.&lt;/p&gt;

&lt;h4&gt;
  
  
  SCAN_INTERVAL_SECONDS
&lt;/h4&gt;

&lt;p&gt;This determines how often the application starts a new HibouAir scan.&lt;/p&gt;

&lt;p&gt;For this tutorial:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SCAN_INTERVAL_SECONDS=30
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;means approximately one scan every 30 seconds.&lt;/p&gt;

&lt;p&gt;You can later change this without modifying the Python source code.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SCAN_INTERVAL_SECONDS=60
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;for approximately one reading per minute.&lt;/p&gt;

&lt;p&gt;Or:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SCAN_INTERVAL_SECONDS=300
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;for approximately one reading every five minutes.&lt;/p&gt;

&lt;h4&gt;
  
  
  CLOUDFLARE_API_URL
&lt;/h4&gt;

&lt;p&gt;Replace this with the URL of the Worker created earlier:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CLOUDFLARE_API_URL=https://hibouair-api.YOUR-SUBDOMAIN.workers.dev/api/readings
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h4&gt;
  
  
  CLOUDFLARE_API_KEY
&lt;/h4&gt;

&lt;p&gt;Enter the same secret that was configured in the Cloudflare Worker:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CLOUDFLARE_API_KEY=YOUR_REAL_API_KEY
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;info.txt&lt;/code&gt; file should not be committed to GitHub because it contains the API key.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 9: How the HibouAir Data Is Read
&lt;/h2&gt;

&lt;p&gt;BleuIO communicates with our Python application using simple AT commands over the serial port.&lt;/p&gt;

&lt;p&gt;The application first connects to BleuIO and places it in central mode.&lt;/p&gt;

&lt;p&gt;It then searches for our HibouAir board ID using:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AT+FINDSCANDATA=220069=5
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Here:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;220069
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;is the HibouAir board ID, and:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;5
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;defines the scan duration.&lt;/p&gt;

&lt;p&gt;BleuIO handles the Bluetooth Low Energy scanning and returns the matching BLE advertising information over serial.&lt;/p&gt;

&lt;p&gt;This means the Python application does not need to directly communicate with the operating system’s native Bluetooth API.&lt;/p&gt;

&lt;p&gt;BleuIO then returns the advertising data to Python.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 10: Decode the HibouAir Advertisement
&lt;/h2&gt;

&lt;p&gt;The Python application searches the advertising payload for the HibouAir data section:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;5B070
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;For the HibouAir advertisement used in this example, the application extracts the three measurements needed for this project. Temperature, Humidity and CO2. The result becomes a normal Python structure similar to:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    "co2": 623,
    "temperature": 24.1,
    "humidity": 43.7
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The application also performs basic validation before uploading the measurements.&lt;/p&gt;

&lt;p&gt;For example, obviously invalid temperature, humidity, or CO2 values are rejected instead of being stored in the database.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 11: Sending the Measurement to Cloudflare
&lt;/h2&gt;

&lt;p&gt;Once a valid advertisement has been decoded, Python prepares a JSON request.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "board_id": "220069",
  "co2": 623,
  "temperature": 24.1,
  "humidity": 43.7
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This is sent using an HTTP &lt;code&gt;POST&lt;/code&gt; request to:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/api/readings
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The API key is included in the request header:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;X-API-Key
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The Cloudflare Worker validates the key and inserts the measurement into D1.&lt;/p&gt;

&lt;p&gt;The Python application therefore never connects directly to the database.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 12: Run the Application
&lt;/h2&gt;

&lt;p&gt;Before running the project, make sure no other program is currently using the BleuIO serial port.&lt;/p&gt;

&lt;p&gt;For example, close any active BleuIO connection in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  BleuIO application&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;screen&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  serial monitor&lt;/li&gt;
&lt;li&gt;  terminal program&lt;/li&gt;
&lt;li&gt;  other Python application&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then run:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;python3 main.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The application should connect to BleuIO.&lt;/p&gt;

&lt;p&gt;You should see output similar to:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;BleuIO + HibouAir + Cloudflare D1
---------------------------------

BleuIO port : /dev/cu.usbmodem4048FDEBA6D01
HibouAir ID : 220069
Scan interval: 30 seconds

Trying BleuIO on /dev/cu.usbmodem4048FDEBA6D01 at 115200 baud...

BleuIO connected successfully.

Setting BleuIO to central mode...
Ready.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The application will then start scanning:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Scanning for HibouAir 220069...

Command: AT+FINDSCANDATA=220069=5
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;When a valid HibouAir advertisement is received and decoded, the terminal will display something similar to:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;============================================
          HIBOUAIR AIR QUALITY
============================================

  CO2                623 ppm
  Temperature       24.1 °C
  Humidity          43.7 %RH

============================================

Uploading to Cloudflare D1...
✓ Reading stored successfully.

Next scan in 23 seconds...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The application continues running automatically.&lt;/p&gt;

&lt;p&gt;There is no need to restart it for each measurement.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6st5uvg382869rwov2xl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6st5uvg382869rwov2xl.png" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To stop the application, press:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Ctrl + C
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Step 13: Check the Data in Cloudflare D1
&lt;/h2&gt;

&lt;p&gt;Now return to the Cloudflare D1 database console.&lt;/p&gt;

&lt;p&gt;Run:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SELECT *
FROM readings
ORDER BY id DESC
LIMIT 10;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;You should see the measurements coming from HibouAir.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;id  board_id  measured_at          co2  temperature  humidity

12  220069    2026-10-05 14:58:20  618  24.1         43.8
11  220069    2026-10-05 14:57:50  621  24.1         43.8
10  220069    2026-10-05 14:57:20  623  24.1         43.7
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;You can also explore the data from your database.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjvk24sxdocrlu8h9bgtp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjvk24sxdocrlu8h9bgtp.png" width="800" height="212"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzgia16h6zu3el76o3r30.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzgia16h6zu3el76o3r30.png" width="799" height="409"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 14: Check the Latest Reading Through the API
&lt;/h2&gt;

&lt;p&gt;We also created a simple read endpoint.&lt;/p&gt;

&lt;p&gt;Open:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://YOUR-WORKER.workers.dev/api/readings/latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The Worker will query D1 and return the latest measurement.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "success": true,
  "reading": {
    "id": 12,
    "board_id": "220069",
    "measured_at": "2026-10-05 14:58:20",
    "co2": 618,
    "temperature": 24.1,
    "humidity": 43.8
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Building a Dashboard Later
&lt;/h2&gt;

&lt;p&gt;This tutorial intentionally stops at the database and API level.&lt;/p&gt;

&lt;p&gt;The objective is to demonstrate the complete BLE-to-cloud data path while keeping the example easy to understand.&lt;/p&gt;

&lt;p&gt;At this point, the data is already available in the cloud.&lt;/p&gt;

&lt;p&gt;A developer could later build a dashboard showing:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Current CO₂
Current Temperature
Current Humidity

CO₂ history
Temperature history
Humidity history
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;A dashboard could request historical readings through additional Worker API endpoints.&lt;/p&gt;

&lt;p&gt;It could also display thresholds, warnings, averages, minimum/maximum measurements, or other analytics.&lt;/p&gt;

&lt;p&gt;Those features are deliberately outside the scope of this example because the main goal is to demonstrate how easily BLE data can be brought into a serverless cloud architecture using BleuIO.&lt;/p&gt;

&lt;p&gt;We kept the project intentionally simple so that the individual parts are easy to understand and modify.&lt;/p&gt;

&lt;p&gt;From here, the project can be expanded in many directions. Multiple HibouAir sensors could be monitored, additional air quality parameters could be stored, historical API endpoints could be created, and a complete cloud dashboard could be built on top of the existing data.&lt;/p&gt;

</description>
      <category>bleuio</category>
      <category>python</category>
      <category>bluetooth</category>
      <category>cloudflare</category>
    </item>
    <item>
      <title>Scanning BLE Devices with C++ and Boost Using the BleuIO Dongle</title>
      <dc:creator>Bleuio tech</dc:creator>
      <pubDate>Mon, 23 Mar 2026 10:00:03 +0000</pubDate>
      <link>https://dev.to/bleuiot/scanning-ble-devices-with-c-and-boost-using-the-bleuio-dongle-32ai</link>
      <guid>https://dev.to/bleuiot/scanning-ble-devices-with-c-and-boost-using-the-bleuio-dongle-32ai</guid>
      <description>&lt;p&gt;Bluetooth Low Energy (BLE) has become one of the most widely used wireless technologies for IoT devices, sensors, wearables, and industrial monitoring systems. Developers working with embedded systems, automation platforms, and hardware integration often rely on &lt;strong&gt;C++&lt;/strong&gt; because of its performance, low-level hardware access, and portability.&lt;/p&gt;

&lt;p&gt;In this tutorial, we will create a simple command-line BLE scanning application using C++. The program connects to the BleuIO USB dongle through a serial port and sends AT commands to control Bluetooth operations. After starting the program, the user enters the number of seconds to scan, and the application instructs the BleuIO dongle to perform a BLE scan and print the detected devices directly in the terminal. This example demonstrates the basic workflow of communicating with BleuIO from a C++ application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why C++ and Boost Are Commonly Used for Bluetooth Development
&lt;/h2&gt;

&lt;p&gt;C++ is widely used in Bluetooth and embedded development because it provides high performance and direct access to hardware interfaces such as serial communication. Many IoT gateways, embedded systems, and industrial applications rely on C++ to interact with sensors and wireless devices. To simplify development, developers often use the Boost libraries, which extend the C++ standard library with reliable cross-platform tools. In this tutorial we use &lt;strong&gt;Boost.Asio&lt;/strong&gt;, which provides a portable and efficient way to handle serial communication and asynchronous input/output across different operating systems.&lt;/p&gt;

&lt;h2&gt;
  
  
  Requirements
&lt;/h2&gt;

&lt;p&gt;Before starting this project, you should have the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  A computer running &lt;strong&gt;macOS, Windows, or Linux&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;  A &lt;strong&gt;&lt;a href="https://www.bleuio.com/bluetooth-low-energy-usb-ssd025.php" rel="noopener noreferrer"&gt;BleuIO USB dongle&lt;/a&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;  A &lt;strong&gt;C++ compiler&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;&lt;a href="https://www.boost.org/doc/user-guide/getting-started.html" rel="noopener noreferrer"&gt;Boost libraries installed&lt;/a&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;  Basic knowledge of &lt;strong&gt;C++ and terminal commands&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Installing the Required Tools
&lt;/h2&gt;

&lt;h4&gt;
  
  
  macOS Setup
&lt;/h4&gt;

&lt;p&gt;First install &lt;strong&gt;Xcode Command Line Tools&lt;/strong&gt;, which provide the C++ compiler.&lt;/p&gt;

&lt;p&gt;xcode-select --install&lt;/p&gt;

&lt;p&gt;Next install &lt;strong&gt;Homebrew&lt;/strong&gt; if it is not already installed.&lt;/p&gt;

&lt;p&gt;/bin/bash -c "$(curl -fsSL &lt;a href="https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" rel="noopener noreferrer"&gt;https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)&lt;/a&gt;"&lt;/p&gt;

&lt;p&gt;Then install Boost:&lt;/p&gt;

&lt;p&gt;brew install boost&lt;/p&gt;

&lt;p&gt;You can verify the installation using:&lt;/p&gt;

&lt;p&gt;brew --prefix boost&lt;/p&gt;

&lt;h4&gt;
  
  
  Windows Setup
&lt;/h4&gt;

&lt;p&gt;On Windows you will need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Visual Studio or MSVC compiler&lt;/li&gt;
&lt;li&gt;  Boost libraries&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Install &lt;strong&gt;Visual Studio Community Edition&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt; Enable &lt;strong&gt;Desktop development with C++&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt; Download Boost from &lt;a href="https://www.boost.org" rel="noopener noreferrer"&gt;https://www.boost.org&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt; Extract Boost and configure it for your project.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Alternatively, Boost can be installed using &lt;strong&gt;vcpkg&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Have a look into the getting started guide on boost office page&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.boost.org/doc/user-guide/getting-started.html" rel="noopener noreferrer"&gt;https://www.boost.org/doc/user-guide/getting-started.html&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding How the Script Works
&lt;/h2&gt;

&lt;p&gt;The example script uses &lt;strong&gt;Boost.Asio serial communication&lt;/strong&gt; to interact with the BleuIO dongle.&lt;/p&gt;

&lt;p&gt;The application works in several stages.&lt;/p&gt;

&lt;h4&gt;
  
  
  Serial Connection
&lt;/h4&gt;

&lt;p&gt;The program opens a serial port connected to the BleuIO dongle.&lt;/p&gt;

&lt;p&gt;serial_.open(port_name);&lt;/p&gt;

&lt;p&gt;The serial port parameters are configured to match BleuIO’s default UART settings.&lt;/p&gt;

&lt;p&gt;serial_.set_option(serial_port_base::baud_rate(57600));&lt;br&gt;&lt;br&gt;
serial_.set_option(serial_port_base::character_size(8));&lt;br&gt;&lt;br&gt;
serial_.set_option(serial_port_base::parity(serial_port_base::parity::none));&lt;br&gt;&lt;br&gt;
serial_.set_option(serial_port_base::stop_bits(serial_port_base::stop_bits::one));&lt;br&gt;&lt;br&gt;
serial_.set_option(serial_port_base::flow_control(serial_port_base::flow_control::none));&lt;/p&gt;

&lt;h4&gt;
  
  
  Asynchronous Serial Reader
&lt;/h4&gt;

&lt;p&gt;The script uses an asynchronous reader to continuously listen for responses from the BleuIO dongle.&lt;/p&gt;

&lt;p&gt;serial_.async_read_some(...)&lt;/p&gt;

&lt;p&gt;Whenever the dongle sends data, the program prints the received information to the terminal.&lt;/p&gt;

&lt;p&gt;This allows us to see scan results in real time.&lt;/p&gt;

&lt;h4&gt;
  
  
  Sending AT Commands
&lt;/h4&gt;

&lt;p&gt;Commands are sent to BleuIO using the &lt;code&gt;sendCommand()&lt;/code&gt; function.&lt;/p&gt;

&lt;p&gt;bleuio.sendCommand("AT+CENTRAL");&lt;/p&gt;

&lt;p&gt;The command is written to the serial port followed by a carriage return and newline.&lt;/p&gt;

&lt;h4&gt;
  
  
  Setting Central Role
&lt;/h4&gt;

&lt;p&gt;BLE devices can operate in different roles.&lt;br&gt;&lt;br&gt;
Before scanning, the BleuIO dongle must be set to &lt;strong&gt;central mode&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;bleuio.sendCommand("AT+CENTRAL");&lt;/p&gt;

&lt;h4&gt;
  
  
  Starting a BLE Scan
&lt;/h4&gt;

&lt;p&gt;The scan command is then issued.&lt;/p&gt;

&lt;p&gt;AT+GAPSCAN=&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;p&gt;AT+GAPSCAN=5&lt;/p&gt;

&lt;p&gt;This instructs the BleuIO dongle to scan for nearby BLE devices for five seconds.&lt;/p&gt;

&lt;p&gt;The dongle returns advertising data for detected devices during the scan.&lt;/p&gt;

&lt;h2&gt;
  
  
  Full Source Code
&lt;/h2&gt;

&lt;p&gt;You can find the full source code on GitHub.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitHub repository&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/smart-sensor-devices-ab/bleuio-cpp-boost" rel="noopener noreferrer"&gt;https://github.com/smart-sensor-devices-ab/bleuio-cpp-boost&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The repository contains the complete C++ script used in this tutorial.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Run the Script
&lt;/h2&gt;

&lt;p&gt;First compile the program.&lt;/p&gt;

&lt;p&gt;clang++ -std=c++17 main.cpp -I$(brew --prefix boost)/include -o bleuio_scan&lt;/p&gt;

&lt;p&gt;After compilation, run the program:&lt;/p&gt;

&lt;p&gt;./bleuio_scan&lt;/p&gt;

&lt;p&gt;The program will ask for the scan duration.&lt;/p&gt;

&lt;p&gt;Example:&lt;/p&gt;

&lt;p&gt;Enter scan duration in seconds: 5&lt;/p&gt;

&lt;p&gt;The script will then:&lt;/p&gt;

&lt;p&gt;Connect to the BleuIO serial port,Put the dongle into central mode,Start scanning for BLE devices,Print scan results in the terminal&lt;/p&gt;

&lt;p&gt;Example output may look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwtty8oc74ikb4gsrnv3i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwtty8oc74ikb4gsrnv3i.png" width="744" height="1024"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Locating the BleuIO Serial Port
&lt;/h2&gt;

&lt;p&gt;Before running the program, you need to identify the serial port where the BleuIO dongle is connected.&lt;/p&gt;

&lt;p&gt;On &lt;strong&gt;macOS&lt;/strong&gt;, you can list available serial devices using the terminal command:&lt;/p&gt;

&lt;p&gt;ls /dev/cu.*&lt;/p&gt;

&lt;p&gt;The BleuIO device will typically appear with a name similar to:&lt;/p&gt;

&lt;p&gt;/dev/cu.usbmodemXXXXXXXX&lt;/p&gt;

&lt;p&gt;This value can then be used in the script as the serial port path.&lt;/p&gt;

&lt;p&gt;On &lt;strong&gt;Windows&lt;/strong&gt;, the serial port can be identified through &lt;strong&gt;Device Manager&lt;/strong&gt;. After plugging in the BleuIO dongle, open Device Manager and expand the &lt;strong&gt;Ports (COM &amp;amp; LPT)&lt;/strong&gt; section. The device will appear as a USB serial device with a COM port number, such as &lt;strong&gt;COM17&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Expanding This Example
&lt;/h2&gt;

&lt;p&gt;The script in this tutorial is a basic example showing how to communicate with the BleuIO dongle using C++ and Boost.Asio. Although it only performs BLE scanning, the same approach can be used to send any AT command supported by BleuIO. Developers can extend this example to connect to devices, read GATT characteristics, parse advertisement data, or integrate BLE functionality into larger applications such as IoT gateways, monitoring tools, or automation systems.&lt;/p&gt;

</description>
      <category>cpp</category>
      <category>bleuio</category>
      <category>bluetoothlowenergy</category>
    </item>
    <item>
      <title>Integrating BleuIO with Teensy 4.1 – Scanning and Decoding HibouAir Sensor Data (Part 2)</title>
      <dc:creator>Bleuio tech</dc:creator>
      <pubDate>Mon, 23 Mar 2026 09:58:29 +0000</pubDate>
      <link>https://dev.to/bleuiot/integrating-bleuio-with-teensy-41-scanning-and-decoding-hibouair-sensor-data-part-2-3907</link>
      <guid>https://dev.to/bleuiot/integrating-bleuio-with-teensy-41-scanning-and-decoding-hibouair-sensor-data-part-2-3907</guid>
      <description>&lt;p&gt;In the &lt;strong&gt;&lt;a href="https://www.bleuio.com/blog/integrating-bleuio-with-teensy-4-1-for-seamless-ble-applications/" rel="noopener noreferrer"&gt;previous project&lt;/a&gt;&lt;/strong&gt;, we focused on getting &lt;strong&gt;Teensy 4.1&lt;/strong&gt; working as a USB Host for the &lt;strong&gt;BleuIO&lt;/strong&gt;. The goal was simple: remove the PC from the equation and prove that a microcontroller could directly control BleuIO and communicate over BLE using AT commands.&lt;/p&gt;

&lt;p&gt;This project builds on that foundation and does something practical with it. Instead of manually sending commands and observing responses, we now create a complete scanner that automatically detects nearby HibouAir sensors, reads their BLE advertisement data, decodes it, and prints meaningful environmental values in real time.&lt;/p&gt;

&lt;p&gt;At this point, the system stops being a connectivity demo and becomes an actual application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hardware Requirements
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;a href="https://www.digikey.pt/pt/products/detail/sparkfun-electronics/20359/16688101" rel="noopener noreferrer"&gt;Teensy 4.1&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://bleuio.com/bluetooth-low-energy-usb-ssd025.php" rel="noopener noreferrer"&gt;BleuIO&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://www.hibouair.com/specifications-air-quality-monitor-co2-sensor.php" rel="noopener noreferrer"&gt;HibouAir Air quality monitoring sensor&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  USB cable&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://www.bleuio.com/accessories.php#usbTypeAConnectorBoardv" rel="noopener noreferrer"&gt;USB TYPE A pcb adaptor&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://www.arduino.cc/en/software/" rel="noopener noreferrer"&gt;Arduino IDE 2.x&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  ArduinoJson library (v7.x or compatible)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Software Requirements
&lt;/h2&gt;

&lt;h5&gt;
  
  
  Install ArduinoJson
&lt;/h5&gt;

&lt;p&gt;This project uses &lt;strong&gt;ArduinoJson&lt;/strong&gt; to parse scan results from BleuIO.&lt;/p&gt;

&lt;p&gt;In Arduino IDE:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Open &lt;strong&gt;Library Manager&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt; Search for &lt;code&gt;arduinojson&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt; Install version 7.x or compatible&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;ArduinoJson is required to deserialize the JSON scan data received from BleuIO.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4bct5mc8a7rdpbzu8pp7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4bct5mc8a7rdpbzu8pp7.png" width="614" height="808"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How it Works
&lt;/h2&gt;

&lt;p&gt;The architecture remains the same as in Part 1, but now it is used with purpose.&lt;/p&gt;

&lt;p&gt;Teensy operates in USB Host mode and communicates directly with BleuIO. BleuIO handles all Bluetooth Low Energy scanning internally and outputs scan results as structured JSON strings over USB serial. Teensy receives those strings, parses the JSON content, extracts the manufacturer-specific payload, and decodes it into usable values.&lt;/p&gt;

&lt;p&gt;Conceptually, the flow looks like this:&lt;/p&gt;

&lt;p&gt;Teensy 4.1 (USB Host + Application Logic)&lt;br&gt;&lt;br&gt;
        ↓&lt;br&gt;&lt;br&gt;
BleuIO (BLE Scanning Engine)&lt;br&gt;&lt;br&gt;
        ↓&lt;br&gt;&lt;br&gt;
BLE Advertisement Data (JSON)&lt;br&gt;&lt;br&gt;
        ↓&lt;br&gt;&lt;br&gt;
HibouAir Decoder&lt;br&gt;&lt;br&gt;
        ↓&lt;br&gt;&lt;br&gt;
Readable Environmental Measurements&lt;/p&gt;

&lt;p&gt;The important thing to notice here is that Teensy never deals with BLE packets directly. There is no radio handling, no GAP or GATT management, and no BLE stack integration. Everything related to Bluetooth stays inside BleuIO. The microcontroller simply receives structured scan results and processes them like any other data stream.&lt;/p&gt;

&lt;h2&gt;
  
  
  Automatic Startup and Scanning
&lt;/h2&gt;

&lt;p&gt;When the firmware starts, it configures BleuIO automatically. It disables command echo, enables verbose mode, and then sends a filtered scan command:&lt;/p&gt;

&lt;p&gt;AT+FINDSCANDATA=FF5B07&lt;/p&gt;

&lt;p&gt;This tells BleuIO to report only devices containing the HibouAir manufacturer identifier. From that moment, scan results begin arriving continuously as JSON lines.&lt;/p&gt;

&lt;p&gt;Each line contains fields such as the device address and a &lt;code&gt;data&lt;/code&gt; field containing the manufacturer payload in hex format. That hex string is where the sensor readings are encoded.&lt;/p&gt;

&lt;h2&gt;
  
  
  Parsing the JSON Data
&lt;/h2&gt;

&lt;p&gt;Since scan data arrives asynchronously, the project includes a small USB serial line reader. It buffers incoming characters until a newline is detected, ensuring that we always attempt to parse complete JSON messages.&lt;/p&gt;

&lt;p&gt;The ArduinoJson library is used to deserialize each line into a &lt;code&gt;JsonDocument&lt;/code&gt;. Once deserialized, we check that the expected scan fields are present. If so, we extract the hex-encoded manufacturer payload and pass it to the &lt;code&gt;HibouAir&lt;/code&gt; decoder.&lt;/p&gt;

&lt;p&gt;At this stage, the data is still raw — just a long hex string representing packed bytes from the BLE advertisement.&lt;/p&gt;

&lt;h2&gt;
  
  
  Decoding the HibouAir Advertisement Payload
&lt;/h2&gt;

&lt;p&gt;The core of this project is the &lt;code&gt;HibouAir&lt;/code&gt; structure. Instead of manually extracting bytes in the main loop, the decoding logic is encapsulated in a dedicated class.&lt;/p&gt;

&lt;p&gt;The constructor receives the JSON document, extracts the &lt;code&gt;data&lt;/code&gt; field, and interprets the hex string as a packed binary structure. Using &lt;code&gt;offsetof()&lt;/code&gt; ensures that the correct byte offsets are used, and helper functions convert the hex pairs into integers. Because the BLE advertisement uses little-endian ordering, some fields require byte swapping before they become meaningful.&lt;/p&gt;

&lt;p&gt;Once decoded, the class provides clean accessor functions such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;getTemp()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;getHum()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;getBar()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;getCo2()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;getPM2_5()&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These functions already return properly scaled values. For example, temperature is divided by 10 to convert from raw integer format to degrees Celsius.&lt;/p&gt;

&lt;p&gt;This separation keeps the application logic simple. The main loop only needs to create a &lt;code&gt;HibouAir&lt;/code&gt; object and call &lt;code&gt;show_sensor()&lt;/code&gt; to print the values.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example Output
&lt;/h2&gt;

&lt;p&gt;When running the project with a nearby HibouAir sensor, the Serial Monitor shows structured environmental readings like this:&lt;/p&gt;

&lt;p&gt;Sensor ID: 22008C&lt;br&gt;&lt;br&gt;
Light: 14 Lux&lt;br&gt;&lt;br&gt;
Pressure: 1007.3 hPA&lt;br&gt;&lt;br&gt;
Temperature: 22.9 C&lt;br&gt;&lt;br&gt;
Humidity: 14.1 %rh&lt;br&gt;&lt;br&gt;
CO2: 508 ppm&lt;/p&gt;

&lt;p&gt;For particulate matter devices, additional values appear:&lt;/p&gt;

&lt;p&gt;PM 1.0: 0.0 ug/m3&lt;br&gt;&lt;br&gt;
PM 2.5: 1.2 ug/m3&lt;br&gt;&lt;br&gt;
PM 10: 2.5 ug/m3&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjm4iagdfruca2ugyfvqk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjm4iagdfruca2ugyfvqk.png" width="800" height="948"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This output is generated directly from BLE advertisements without establishing a connection to the sensor. The sensors simply broadcast their measurements, and the system passively collects and decodes them.&lt;/p&gt;

&lt;h2&gt;
  
  
  GitHub Repository
&lt;/h2&gt;

&lt;p&gt;The complete source code for this project is available here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/smart-sensor-devices-ab/bleuio-teensy-hibouair-scanner" rel="noopener noreferrer"&gt;https://github.com/smart-sensor-devices-ab/bleuio-teensy-hibouair-scanner&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can clone the repository, install the ArduinoJson library through the Arduino IDE Library Manager, upload the sketch to Teensy 4.1, and run it immediately. The code is modular and organized so you can reuse the USB line reader, the HibouAir decoder, or the scanning logic in your own applications.&lt;/p&gt;

&lt;p&gt;With this foundation in place, several natural extensions become possible. You could store measurements on an SD card, publish them via MQTT, expose them through a REST interface, or even build a complete air-quality gateway. The BLE side does not need to change.&lt;/p&gt;

</description>
      <category>bleuio</category>
      <category>hibouair</category>
      <category>airqualitymonitor</category>
      <category>teensy</category>
    </item>
    <item>
      <title>Integrating BleuIO with Teensy 4.1 for Seamless BLE Applications</title>
      <dc:creator>Bleuio tech</dc:creator>
      <pubDate>Mon, 16 Feb 2026 12:57:50 +0000</pubDate>
      <link>https://dev.to/bleuiot/integrating-bleuio-with-teensy-41-for-seamless-ble-applications-5b9</link>
      <guid>https://dev.to/bleuiot/integrating-bleuio-with-teensy-41-for-seamless-ble-applications-5b9</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbjwzjxfohmg2ful1fpjh.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbjwzjxfohmg2ful1fpjh.jpg" alt="Integrating BleuIO with Teensy 4.1 for Seamless BLE Applications" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this project, we will build a high-performance embedded Bluetooth Low Energy (BLE) host system using the &lt;strong&gt;Teensy 4.1 microcontroller&lt;/strong&gt; and the &lt;strong&gt;&lt;a href="https://bleuio.com/bluetooth-low-energy-usb-ssd025.php" rel="noopener noreferrer"&gt;BleuIO USB BLE dongle&lt;/a&gt;&lt;/strong&gt;. The goal is to turn the Teensy into a USB Host controller that communicates directly with BleuIO through a simple Arduino sketch, allowing us to create BLE applications without implementing a full BLE stack on the microcontroller.&lt;/p&gt;

&lt;p&gt;By the end of this project, you will have a fully functional embedded BLE platform where Teensy 4.1 acts as the application controller and BleuIO handles all Bluetooth Low Energy communication internally. You will be able to send AT commands from Teensy to scan for BLE devices, connect to them, read characteristics, and build your own BLE-based solutions. More importantly, you will gain a reusable architecture that can serve as the foundation for industrial gateways, IoT devices, monitoring systems, or custom embedded products.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why We Chose Teensy 4.1
&lt;/h2&gt;

&lt;p&gt;The Teensy 4.1 is built around the powerful NXP i.MX RT1062 ARM Cortex-M7 processor running at 600 MHz. This makes it one of the fastest microcontrollers compatible with the Arduino ecosystem. Its high clock speed, large memory capacity, and hardware floating point support allow it to handle complex logic, real-time data processing, and communication tasks with ease.&lt;/p&gt;

&lt;p&gt;What makes Teensy 4.1 particularly ideal for this project is its USB Host capability. Since BleuIO is a USB device, it requires a host controller to operate independently of a PC. Teensy 4.1 provides exactly that. It allows us to connect BleuIO directly to the microcontroller and build a fully standalone BLE system. The board’s performance headroom ensures stable communication, fast response handling, and scalability for advanced applications.&lt;/p&gt;

&lt;p&gt;Rather than choosing a minimal low-power MCU, we selected Teensy 4.1 because it bridges the gap between traditional microcontrollers and more complex application processors. It gives developers flexibility, speed, and reliability in embedded BLE projects.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbfhae2a3tsgfz659qse0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbfhae2a3tsgfz659qse0.png" width="604" height="261"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Project Requirements
&lt;/h2&gt;

&lt;p&gt;To build this project, you will need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;&lt;a href="https://www.digikey.pt/pt/products/detail/sparkfun-electronics/20359/16688101" rel="noopener noreferrer"&gt;Teensy 4.1 microcontroller board&lt;/a&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;&lt;a href="https://bleuio.com/bluetooth-low-energy-usb-ssd025.php" rel="noopener noreferrer"&gt;BleuIO – Bluetooth Low Energy USB Dongle&lt;/a&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://www.arduino.cc/en/software/" rel="noopener noreferrer"&gt;Arduino IDE 2.x&lt;/a&gt; (2.0.4 or later recommended)&lt;/li&gt;
&lt;li&gt;  Teensy board support package (Teensyduino)&lt;/li&gt;
&lt;li&gt;  USB cable for programming&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Project Architecture Overview
&lt;/h2&gt;

&lt;p&gt;The system architecture is straightforward. Teensy 4.1 operates as a USB Host and communicates directly with the BleuIO dongle over serial. BleuIO then manages all Bluetooth Low Energy communication with nearby BLE devices. This separation of responsibilities simplifies development significantly. Teensy focuses on application logic, while BleuIO handles the BLE stack internally.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Teensy 4.1 (USB Host)
        ↓
BleuIO USB Dongle
        ↓
BLE Devices
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How the Project Works – Step-by-Step
&lt;/h2&gt;

&lt;h4&gt;
  
  
  Step 1: Install Arduino IDE and Teensy Support
&lt;/h4&gt;

&lt;p&gt;First &lt;a href="https://www.arduino.cc/en/software" rel="noopener noreferrer"&gt;download the Arduino 2.x.x IDE&lt;/a&gt; from Arduino’s website. All versions 2.0.4 and later are supported. Versions 2.3.0 or later are recommended, due to improvements in Boards Manager.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;To install Teensy on Arduino IDE 2.x, click File &amp;gt; Preferences (on MacOS, click Arduino IDE &amp;gt; Settings).&amp;nbsp;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F55r55qpfdzvtpfg6ybcy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F55r55qpfdzvtpfg6ybcy.png" width="800" height="203"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the main Arduino window, open Boards Manager by clicking the left-side board icon, search for “teensy”, and click “Install”.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fajpkce1bllq7jt29nx6s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fajpkce1bllq7jt29nx6s.png" width="228" height="345"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Configure USB Type
&lt;/h3&gt;

&lt;p&gt;Teensy supports multiple USB configurations. Under &lt;strong&gt;Tools → USB Type&lt;/strong&gt;, select the appropriate mode so the board can manage serial communication while operating with USB Host functionality. Teensyduino is also &lt;a href="https://www.pjrc.com/teensy/td_libs.html" rel="noopener noreferrer"&gt;compatible with many Arduino libraries&lt;/a&gt;.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faa4xilf4tz5uredaa75m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faa4xilf4tz5uredaa75m.png" width="672" height="393"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Upload the Sketch
&lt;/h3&gt;

&lt;p&gt;The source code for this project, &lt;code&gt;USBtoUSBHostSerial.ino&lt;/code&gt; sketch, is publicly available on GitHub: &lt;a href="https://github.com/smart-sensor-devices-ab/bleuio_Teensy_host" rel="noopener noreferrer"&gt;https://github.com/smart-sensor-devices-ab/bleuio_Teensy_host&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Upload the provided &lt;code&gt;USBtoUSBHostSerial.ino&lt;/code&gt; sketch to the Teensy 4.1 using the Arduino IDE. This sketch initializes the USB Host interface and establishes a communication bridge between the Teensy and the BleuIO dongle. Once programmed, the Teensy essentially becomes a serial terminal for BleuIO, allowing you to type AT commands through the Arduino Serial Monitor and receive responses in real time.&lt;/p&gt;

&lt;p&gt;Instead of embedding complex BLE logic in the microcontroller firmware, the sketch focuses on maintaining stable USB communication and forwarding user commands to the dongle. BleuIO processes these commands internally and returns structured responses. This design keeps the firmware clean, modular, and easy to expand.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh8riyttxi1t27xx7snpb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh8riyttxi1t27xx7snpb.png" width="800" height="774"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Approach Is Powerful
&lt;/h2&gt;

&lt;p&gt;Developing BLE applications directly on microcontrollers traditionally requires integrating a BLE stack, managing connection states, handling security layers, parsing protocol events, and debugging complex timing issues. This process can be time-consuming and hardware-dependent. Each microcontroller family often requires a different SDK, stack configuration, and maintenance strategy.&lt;/p&gt;

&lt;p&gt;By using &lt;strong&gt;BleuIO&lt;/strong&gt;, this complexity is dramatically reduced. BLE functionality is abstracted behind a simple AT command interface. The microcontroller does not need to manage low-level BLE operations. Instead, it communicates using straightforward serial commands while BleuIO takes care of scanning, connecting, reading characteristics, and maintaining protocol compliance internally. This modular architecture makes the system portable across hardware platforms and reduces firmware complexity, development time, and maintenance effort.&lt;/p&gt;

&lt;h2&gt;
  
  
  GitHub Repository
&lt;/h2&gt;

&lt;p&gt;The source code for this project, &lt;code&gt;USBtoUSBHostSerial.ino&lt;/code&gt; sketch, is publicly available on GitHub:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/smart-sensor-devices-ab/bleuio_Teensy_host" rel="noopener noreferrer"&gt;https://github.com/smart-sensor-devices-ab/bleuio_Teensy_host&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This project is shared as a public example to demonstrate how BleuIO can be integrated into high-performance embedded systems. It is intentionally simple in structure so that developers can clearly understand the architecture and reuse it in their own applications.&lt;/p&gt;

&lt;p&gt;By combining the computational power of Teensy 4.1 with the modular BLE capabilities of BleuIO, we created a clean and scalable embedded architecture. This project highlights how BLE integration does not need to be complicated. With the right approach, developers can focus on innovation and application logic rather than low-level protocol management.&lt;/p&gt;

</description>
      <category>bluetoothlowenergy</category>
      <category>bleuio</category>
      <category>teensy</category>
    </item>
    <item>
      <title>Developing a Desktop BLE Air-Quality Application with Rust, Dioxus, and BleuIO</title>
      <dc:creator>Bleuio tech</dc:creator>
      <pubDate>Mon, 02 Feb 2026 13:40:14 +0000</pubDate>
      <link>https://dev.to/bleuiot/developing-a-desktop-ble-air-quality-application-with-rust-dioxus-and-bleuio-2d2o</link>
      <guid>https://dev.to/bleuiot/developing-a-desktop-ble-air-quality-application-with-rust-dioxus-and-bleuio-2d2o</guid>
      <description>&lt;p&gt;Bluetooth Low Energy is often associated with mobile apps, embedded firmware, or cloud gateways. In practice, however, BLE is equally powerful on the desktop. With the right tools, a desktop application can scan, decode, and visualize BLE sensor data in real time—without relying on browsers or mobile platforms.&lt;/p&gt;

&lt;p&gt;In this tutorial, we demonstrate how &lt;strong&gt;BleuIO&lt;/strong&gt; can be used as a flexible BLE interface for desktop applications written in &lt;strong&gt;Rust&lt;/strong&gt;, using the &lt;strong&gt;Dioxus&lt;/strong&gt; framework. The result is a native desktop application that scans nearby &lt;strong&gt;HibouAir&lt;/strong&gt; sensors, decodes their BLE advertisement data, and presents air-quality metrics in a clean, responsive user interface.&lt;/p&gt;

&lt;p&gt;This project is intended as a practical example that shows how BleuIO fits naturally into modern desktop development workflows—regardless of programming language.&lt;/p&gt;

&lt;h2&gt;
  
  
  What We Are Building
&lt;/h2&gt;

&lt;p&gt;The application is a native desktop air-quality dashboard. It connects to a BleuIO USB dongle over a serial port, puts the dongle into BLE scanning mode, and continuously listens for BLE advertisements from nearby HibouAir sensors. These advertisements contain manufacturer-specific data with environmental measurements such as CO2 concentration, particulate matter, temperature, humidity, pressure, VOC levels, and ambient light.&lt;/p&gt;

&lt;p&gt;The desktop application decodes this data locally and displays it in real time. Each detected sensor is shown as its own panel, with a clear header identifying the device type and a structured content area showing the latest measurements.&lt;/p&gt;

&lt;p&gt;The entire solution runs locally on the user’s computer. There is no cloud dependency, no browser runtime, and no mobile device involved.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Rust and Dioxus?
&lt;/h2&gt;

&lt;p&gt;Rust has become increasingly popular for system-level and desktop applications because it combines performance, memory safety, and strong tooling. For BLE applications that involve continuous serial communication and real-time data processing, these properties are particularly valuable.&lt;/p&gt;

&lt;p&gt;Dioxus is a Rust UI framework inspired by modern component-based design. It allows developers to build native desktop interfaces using a declarative approach while still compiling to a true desktop binary. In this project, Dioxus is used to render the dashboard, manage state updates as sensor data arrives, and keep the UI responsive even during continuous BLE scanning.&lt;/p&gt;

&lt;p&gt;The combination of Rust, Dioxus, and BleuIO demonstrates that desktop BLE applications do not need to rely on platform-specific SDKs or heavyweight frameworks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Requirements
&lt;/h2&gt;

&lt;p&gt;To run this project, the following hardware and software are required:&lt;/p&gt;

&lt;h3&gt;
  
  
  Hardware
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;&lt;a href="https://www.bleuio.com/" rel="noopener noreferrer"&gt;BleuIO USB BLE dongle&lt;/a&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;&lt;a href="https://www.hibouair.com/" rel="noopener noreferrer"&gt;HibouAir air-quality sensor&lt;/a&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Software
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Rust toolchain (stable)&lt;/strong&gt;
&lt;a href="https://www.rust-lang.org/tools/install" rel="noopener noreferrer"&gt;https://www.rust-lang.org/tools/install&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Dioxus CLI&lt;/strong&gt;
&lt;a href="https://dioxuslabs.com/learn/0.5/getting_started" rel="noopener noreferrer"&gt;https://dioxuslabs.com/learn/0.5/getting_started&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  A supported desktop operating system (macOS, Linux, or Windows)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No proprietary SDKs or BLE drivers are required. BleuIO communicates using standard AT commands over a serial interface, making the setup lightweight and portable.&lt;/p&gt;

&lt;h2&gt;
  
  
  How the Project Works Internally
&lt;/h2&gt;

&lt;p&gt;When the application starts, it first searches for a connected BleuIO dongle by scanning available serial ports and matching the dongle’s USB vendor and product IDs. Once the correct device is found, the application opens the serial port and initializes the dongle by disabling command echo and enabling verbose mode.&lt;/p&gt;

&lt;p&gt;After initialization, the application instructs BleuIO to start scanning for BLE advertisements that match HibouAir’s manufacturer identifier. BleuIO then streams scan results back to the application as JSON-formatted lines over the serial connection.&lt;/p&gt;

&lt;p&gt;Each incoming scan packet is parsed and inspected. The application locates the BLE manufacturer-specific data, verifies that it belongs to HibouAir, and decodes the payload into a structured Rust data type. To ensure stable and predictable readings, only the full advertisement format used by HibouAir beacon type &lt;code&gt;0x05&lt;/code&gt; is processed. Partial or auxiliary beacon formats are ignored in this example project.&lt;/p&gt;

&lt;p&gt;Decoded sensor data is stored in memory and immediately reflected in the user interface. As new advertisements arrive, the corresponding sensor panel updates automatically.&lt;/p&gt;

&lt;h2&gt;
  
  
  Project Structure
&lt;/h2&gt;

&lt;p&gt;The source code is organized into clear, functional modules. UI components are separated from BLE logic and data models, making the project easy to understand and extend. The main application entry point configures the desktop window and mounts the dashboard component. BLE communication is encapsulated in a dedicated hook that runs asynchronously and feeds decoded sensor data into the UI layer.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;src/
├── components/
│   ├── dashboard.rs
│   ├── sensor_panel.rs
│   └── mod.rs
├── hooks/
│   ├── use_bleuio.rs
│   └── mod.rs
├── models/
│   ├── bleuio.rs
│   ├── hibouair.rs
│   ├── sensor_data.rs
│   └── mod.rs
├── main.rs
assets/
├── main.css
├── tailwind.css
└── favicon.ico
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This structure mirrors how larger Rust desktop applications are typically organized, and it provides a solid foundation for adding features such as data logging, historical charts, filtering, or export functionality.&lt;/p&gt;

&lt;h2&gt;
  
  
  Source Code and How to Run the Project
&lt;/h2&gt;

&lt;p&gt;The complete source code for this project is publicly available on GitHub:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitHub repository:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://github.com/smart-sensor-devices-ab/hibouair-bleuio-rust-readltime-desktop" rel="noopener noreferrer"&gt;https://github.com/smart-sensor-devices-ab/hibouair-bleuio-rust-readltime-desktop&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After cloning the repository, the application can be run in development mode using the Dioxus CLI. This launches the desktop window and enables hot reloading, which is useful when experimenting with UI changes or decoding logic. The project can also be built and run using standard Cargo commands, producing a native desktop binary.&lt;/p&gt;

&lt;p&gt;Because the code is open and self-contained, developers are free to study it, modify it, or reuse parts of it in their own BLE-based desktop applications.&lt;/p&gt;

&lt;p&gt;The complete instruction on how to run this project is available on the &lt;strong&gt;&lt;a href="https://github.com/smart-sensor-devices-ab/hibouair-bleuio-rust-readltime-desktop/blob/master/README.md" rel="noopener noreferrer"&gt;Readme file&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Application Output
&lt;/h2&gt;

&lt;p&gt;When running, the application displays a dashboard with one panel per detected HibouAir sensor. Each panel includes a colored header identifying the sensor type, followed by a white content area showing live air-quality measurements. Values update continuously as new BLE advertisements are received, providing an immediate view of the surrounding environment.&lt;/p&gt;

&lt;p&gt;A screenshot of the running application is shown below to illustrate the final result.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8iui38k7ipk7ka2wikub.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8iui38k7ipk7ka2wikub.png" width="800" height="464"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This project is intentionally kept simple. It is not a finished product, but rather an educational example that demonstrates how &lt;strong&gt;BleuIO can be used with Rust and Dioxus to build a native desktop BLE application&lt;/strong&gt;. The source code is public, easy to follow, and designed to be extended.&lt;/p&gt;

</description>
      <category>bleuio</category>
      <category>bluetooth</category>
      <category>rust</category>
      <category>airquality</category>
    </item>
    <item>
      <title>Building a BLE Device Detection Web App with Phoenix and BleuIO</title>
      <dc:creator>Bleuio tech</dc:creator>
      <pubDate>Wed, 31 Dec 2025 11:14:12 +0000</pubDate>
      <link>https://dev.to/bleuiot/building-a-ble-device-detection-web-app-with-phoenix-and-bleuio-4bpm</link>
      <guid>https://dev.to/bleuiot/building-a-ble-device-detection-web-app-with-phoenix-and-bleuio-4bpm</guid>
      <description>&lt;p&gt;In this tutorial, we build a simple but practical web application that demonstrates how &lt;strong&gt;&lt;a href="https://www.bleuio.com/index.php" rel="noopener noreferrer"&gt;BleuIO&lt;/a&gt;&lt;/strong&gt; can be integrated with a modern web framework to interact with Bluetooth Low Energy (BLE) devices in real time. The goal of this project is not to create a full-featured product, but to provide a &lt;strong&gt;clear example&lt;/strong&gt; that developers can learn from and extend for their own use cases.&lt;/p&gt;

&lt;p&gt;The application connects to a BleuIO USB dongle via a serial port, enables verbose mode, performs a BLE scan, and detects whether a specific nearby BLE device is present. If the target device is found, the application displays its MAC address. This approach is particularly useful for scenarios such as &lt;strong&gt;proximity detection&lt;/strong&gt;, presence monitoring, or gateway-style BLE integrations.&lt;/p&gt;

&lt;p&gt;The complete source code for this project is provided separately so developers can easily clone, modify, and build upon it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Phoenix Framework?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.phoenixframework.org/" rel="noopener noreferrer"&gt;Phoenix is a web framework&lt;/a&gt;&lt;/strong&gt; written in &lt;strong&gt;Elixir&lt;/strong&gt; and built on top of the &lt;strong&gt;Erlang/OTP&lt;/strong&gt; ecosystem. It is designed for applications that require high concurrency, real-time updates, and long-running background processes. These characteristics make Phoenix particularly suitable for hardware-integrated applications where continuous communication with external devices is required.&lt;/p&gt;

&lt;p&gt;In this project, Phoenix allows the BleuIO serial connection to remain open while BLE scan results are streamed to the web interface. The framework handles process supervision, message passing, and real-time UI updates in a clean and reliable way, without the need for complex front-end JavaScript logic.&lt;/p&gt;

&lt;h2&gt;
  
  
  How This Differs from a JavaScript-Based Web App
&lt;/h2&gt;

&lt;p&gt;This project does not use the Web Serial API. Web Serial allows browser-based JavaScript applications to access serial devices, but it is primarily intended for interactive, user-driven scenarios. It requires explicit user permission, depends on browser support, and is not suitable for unattended or always-on systems.&lt;/p&gt;

&lt;p&gt;By contrast, this Phoenix-based approach keeps all BLE logic on the backend. The web interface simply reflects the current state of the system and allows the user to trigger actions such as connecting or rescanning. This separation makes the application easier to extend, easier to deploy, and more suitable for real-world integrations where reliability and continuous operation are important.&lt;/p&gt;

&lt;h2&gt;
  
  
  Requirements
&lt;/h2&gt;

&lt;p&gt;To follow this tutorial, you will need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  A computer&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://www.bleuio.com/bluetooth-low-energy-usb-ssd025.php" rel="noopener noreferrer"&gt;A BleuIO USB dongle&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://www.closebeacon.com/" rel="noopener noreferrer"&gt;A Close beacon&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  Elixir and Phoenix installed on your system&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How the Application Works
&lt;/h2&gt;

&lt;p&gt;The application follows a straightforward process. The user enters the serial port name where the BleuIO dongle is connected and initiates the connection from the web interface. Once connected, the application sends an AT command to enable verbose mode, making the responses easier to read and parse.&lt;/p&gt;

&lt;p&gt;After verbose mode is enabled, the application starts a timed BLE scan. As scan results arrive from BleuIO, they are analyzed in real time. If a BLE device advertising the configured target name is detected, the application extracts its MAC address and updates the interface accordingly. The user can repeat the scan at any time using the rescan option.&lt;/p&gt;

&lt;p&gt;All serial communication and BLE processing run in the background, while the web interface updates automatically based on events generated by the backend.&lt;/p&gt;

&lt;h2&gt;
  
  
  Running the Application
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Installing Phoenix
&lt;/h3&gt;

&lt;p&gt;Phoenix can be installed on macOS using standard tooling for Elixir. Once Elixir is installed, Phoenix can be added using the official project generator. Detailed installation steps are included in the project documentation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Running the App
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Download the source code&lt;/strong&gt; from &lt;a href="https://github.com/smart-sensor-devices-ab/bleuio-phoenix-erlang" rel="noopener noreferrer"&gt;https://github.com/smart-sensor-devices-ab/bleuio-phoenix-erlang&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After downloading the source code:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Install dependencies&lt;/li&gt;
&lt;li&gt; Start the Phoenix server&lt;/li&gt;
&lt;li&gt; Open the application in a browser&lt;/li&gt;
&lt;li&gt; Enter the BleuIO serial port&lt;/li&gt;
&lt;li&gt; Click &lt;strong&gt;Connect&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt; View scan results and detected devices&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Follow &lt;strong&gt;&lt;a href="https://github.com/smart-sensor-devices-ab/bleuio-phoenix-erlang/blob/master/README.md" rel="noopener noreferrer"&gt;README.md&lt;/a&gt;&lt;/strong&gt; for more details&lt;/p&gt;

&lt;p&gt;The application runs locally and does not require any cloud services.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuring the Serial Port and Target Device
&lt;/h2&gt;

&lt;p&gt;Two key parameters are intentionally easy to modify:&lt;/p&gt;

&lt;h3&gt;
  
  
  Serial Port
&lt;/h3&gt;

&lt;p&gt;The serial port used by BleuIO can be updated either through the web interface or in the configuration file (config&amp;gt;runtime.exs). This allows the project to run on different machines without code changes.&lt;/p&gt;

&lt;p&gt;The serial port name depends on the operating system being used. On macOS, BleuIO typically appears as a device starting with &lt;code&gt;/dev/cu.usbmodem&lt;/code&gt;. On Linux systems, the dongle is commonly available as &lt;code&gt;/dev/ttyUSB0&lt;/code&gt; or &lt;code&gt;/dev/ttyACM0&lt;/code&gt;, depending on the system configuration. On Windows, BleuIO appears as a COM port, such as &lt;code&gt;COM3&lt;/code&gt; or &lt;code&gt;COM5&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Target BLE Device
&lt;/h3&gt;

&lt;p&gt;The application looks for a specific BLE device name during scanning. This name is defined as a constant in the backend code on lib&amp;gt;bleuio_phx&amp;gt;bleuio_worker.ex and is matched &lt;strong&gt;case-insensitively&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Proximity Detection and CloseBeacon Use Case
&lt;/h2&gt;

&lt;p&gt;In the example implementation, the application detects a device advertising the name &lt;code&gt;closebeacon.com&lt;/code&gt;. This makes the project suitable for proximity detection use cases, such as presence awareness, zone monitoring, or asset tracking.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flyn3macne4dhpteyuitz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flyn3macne4dhpteyuitz.png" width="800" height="435"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;BLE beacons like CloseBeacon are commonly used in proximity-based systems, and this project demonstrates how BleuIO can serve as a reliable BLE scanning interface for such applications. More information about CloseBeacon can be found at &lt;a href="https://www.closebeacon.com/" rel="noopener noreferrer"&gt;https://www.closebeacon.com/&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How This Project Helps Developers
&lt;/h2&gt;

&lt;p&gt;This tutorial is intended as an example project rather than a finished solution. It shows how BleuIO can be integrated with a backend web framework to handle BLE scanning in a clean and scalable way. Developers can use this project as a reference to understand the overall architecture and then build their own custom logic on top of it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/smart-sensor-devices-ab/bleuio-phoenix-erlang" rel="noopener noreferrer"&gt;The complete source code&lt;/a&gt; is provided so that anyone interested can explore the implementation details, experiment with different configurations, and adapt the project for their own BLE-enabled applications.&lt;/p&gt;

&lt;p&gt;By combining BleuIO with Phoenix, developers can build BLE-enabled web applications that are not limited by browser APIs or client-side constraints. This example demonstrates a backend-first approach to BLE scanning that is well suited for gateways, monitoring tools, and proximity detection systems.&lt;/p&gt;

</description>
      <category>bluetoothlowenergy</category>
      <category>bleuio</category>
      <category>elixir</category>
    </item>
    <item>
      <title>Sending BLE Air Quality Data to Arduino Cloud Using BleuIO</title>
      <dc:creator>Bleuio tech</dc:creator>
      <pubDate>Wed, 17 Dec 2025 10:03:34 +0000</pubDate>
      <link>https://dev.to/bleuiot/sending-ble-air-quality-data-to-arduino-cloud-using-bleuio-f3</link>
      <guid>https://dev.to/bleuiot/sending-ble-air-quality-data-to-arduino-cloud-using-bleuio-f3</guid>
      <description>&lt;p&gt;Bluetooth Low Energy (BLE) devices are widely used for environmental monitoring, but getting their data into the cloud often requires complex SDKs, gateways, or proprietary platforms. In this tutorial, we demonstrate a simple and flexible alternative: sending &lt;strong&gt;BLE advertisement data directly to Arduino Cloud&lt;/strong&gt; using &lt;strong&gt;BleuIO&lt;/strong&gt; as a USB BLE gateway.&lt;/p&gt;

&lt;p&gt;In this project, we build a lightweight data pipeline where a HibouAir air quality sensor broadcasts environmental data over BLE advertisements, BleuIO scans and captures that data, a Python script decodes the values, and the results are sent straight to &lt;strong&gt;Arduino Cloud&lt;/strong&gt; for storage and visualization — all using free tools.&lt;/p&gt;

&lt;p&gt;This project is designed as a &lt;strong&gt;showcase example&lt;/strong&gt; to illustrate how BLE development and cloud integration can be done quickly and transparently, without BLE SDKs or embedded firmware development.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Arduino Cloud?
&lt;/h2&gt;

&lt;p&gt;Arduino Cloud offers a convenient and reliable platform for storing and visualizing IoT data without the need to build and maintain a custom backend. Although it is often associated with Arduino hardware, the platform supports Manual Devices, which makes it suitable for gateway-based solutions where data originates from external devices such as BLE sensors. In this project, Arduino Cloud serves as a secure endpoint where decoded air quality data can be published using standard MQTT communication. Its integrated dashboards allow developers to quickly visualize sensor data, making it especially useful for prototyping, demonstrations, and proof-of-concept projects that require minimal setup.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hardware and Software Requirements
&lt;/h2&gt;

&lt;h4&gt;
  
  
  Hardware
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;a href="https://www.hibouair.com/specifications-air-quality-monitor-co2-sensor.php" rel="noopener noreferrer"&gt;&lt;strong&gt;HibouAir&lt;/strong&gt; CO2 air quality monitor&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://www.bleuio.com/" rel="noopener noreferrer"&gt;&lt;strong&gt;BleuIO&lt;/strong&gt; Bluetooth Low Energy USB dongle&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Software
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;  Python 3.9 or later&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;pyserial&lt;/code&gt; Python library&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;arduino-iot-cloud&lt;/code&gt; Python library&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://app.arduino.cc/dashboards" rel="noopener noreferrer"&gt;Arduino Cloud&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No embedded programming or BLE SDKs are required.&lt;/p&gt;

&lt;h2&gt;
  
  
  How the System Works
&lt;/h2&gt;

&lt;p&gt;The HibouAir device periodically broadcasts air quality data within its BLE advertisement payload. BleuIO continuously scans for nearby BLE advertisements and filters packets that match a specific device identifier. When a matching advertisement is detected, the Python gateway script extracts the raw data and applies decoding logic to convert the hexadecimal values into human-readable measurements. These decoded values are then published to Arduino Cloud using authenticated MQTT communication. The entire process runs continuously, enabling real-time data updates without establishing a persistent BLE connection to the sensor.&lt;/p&gt;

&lt;h2&gt;
  
  
  Arduino Cloud Setup (Step by Step)
&lt;/h2&gt;

&lt;h4&gt;
  
  
  Step 1: Create or Log In to Arduino Cloud
&lt;/h4&gt;

&lt;p&gt;Go to:&lt;br&gt;&lt;br&gt;
&lt;strong&gt;&lt;a href="https://app.arduino.cc/dashboards" rel="noopener noreferrer"&gt;https://app.arduino.cc/dashboards&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Create a free account or log in to your existing one. After login Arduino Cloud will generate&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Device ID&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Secret Key&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Save these securely — they will be used in the Python script.&lt;/p&gt;
&lt;h4&gt;
  
  
  Step 2: Create a Device
&lt;/h4&gt;

&lt;p&gt;Navigate to:&lt;br&gt;&lt;br&gt;
&lt;strong&gt;&lt;a href="https://app.arduino.cc/devices" rel="noopener noreferrer"&gt;https://app.arduino.cc/devices&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Click &lt;strong&gt;Add Device&lt;/strong&gt; from left menu&lt;/li&gt;
&lt;li&gt;  Choose &lt;strong&gt;Manual Device&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;  Name the device &lt;strong&gt;HibouAir&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  Step 3: Create a Thing
&lt;/h4&gt;

&lt;p&gt;When prompted after creating device, create a new Thing and name it &lt;strong&gt;HibouAir Thing&lt;/strong&gt;.&lt;/p&gt;
&lt;h4&gt;
  
  
  Step 4: Add Cloud Variables
&lt;/h4&gt;

&lt;p&gt;Add the following variables to the Thing:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Variable Name&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;co2_ppm&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;CO₂ concentration (ppm)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;temperature_c&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;float&lt;/td&gt;
&lt;td&gt;Temperature in °C&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;humidity_rh&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;float&lt;/td&gt;
&lt;td&gt;Relative humidity (%)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2cakm27yhcn36bg6lwzf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2cakm27yhcn36bg6lwzf.png" width="758" height="578"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  Step 5: Create a Dashboard
&lt;/h4&gt;

&lt;p&gt;Go back to &lt;strong&gt;Dashboards&lt;/strong&gt; and create a new dashboard.&lt;/p&gt;

&lt;p&gt;Add widgets such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Value widget for CO2&lt;/li&gt;
&lt;li&gt;  Gauge widget for temperature&lt;/li&gt;
&lt;li&gt;  Chart widget for humidity over time&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3j7ezlzg3goey9bmiw59.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3j7ezlzg3goey9bmiw59.png" width="800" height="407"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Your cloud setup is now complete.&lt;/p&gt;
&lt;h2&gt;
  
  
  Project Source Code
&lt;/h2&gt;

&lt;p&gt;Clone or download the project from GitHub:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/smart-sensor-devices-ab/bleuio-to-arduino-cloud" rel="noopener noreferrer"&gt;https://github.com/smart-sensor-devices-ab/bleuio-to-arduino-cloud&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  Configure &lt;code&gt;secrets.py&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;Update the following values:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DEVICE_ID = "YOUR_DEVICE_ID"
SECRET_KEY = "YOUR_SECRET_KEY"
SERIAL_PORT = "/dev/tty.usbmodemXXXX"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make sure the serial port matches where BleuIO is connected.&lt;/p&gt;

&lt;h4&gt;
  
  
  Configure &lt;code&gt;gateway.py&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;In &lt;code&gt;gateway.py&lt;/code&gt;, update the scan command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SCAN_CMD = "AT+FINDSCANDATA=220069=3"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, &lt;code&gt;220069&lt;/code&gt; is the &lt;strong&gt;HibouAir board ID&lt;/strong&gt; used in the BLE advertisement.&lt;br&gt;&lt;br&gt;
If your HibouAir device uses a different ID, update this value accordingly.&lt;/p&gt;
&lt;h2&gt;
  
  
  Running the Project
&lt;/h2&gt;

&lt;p&gt;Once the Arduino Cloud configuration and local script setup are complete, running the project requires only a single command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;python3 gateway.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the gateway script is executed, BleuIO is placed into dual-role mode and begins scanning for BLE advertisements that match the specified HibouAir board identifier. As advertisement packets are received, the script decodes the sensor values and immediately publishes them to Arduino Cloud. Within moments, the dashboard begins displaying live air quality data. This continuous loop allows the system to operate as a real-time BLE-to-cloud gateway with minimal overhead.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsim3kcu5tpxwg5kfp8gk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsim3kcu5tpxwg5kfp8gk.png" width="800" height="706"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Customizing the Dashboard
&lt;/h2&gt;

&lt;p&gt;Arduino Cloud dashboards can be customized to present air quality data in a way that best fits the user’s needs. Live values can be displayed using numeric widgets, gauges can be used to visualize ranges such as CO2 concentration or temperature, and chart widgets can be added to show trends over time. By arranging and configuring these widgets, users can create a clear and informative interface for monitoring indoor air quality. This flexibility makes the dashboard suitable not only for development and testing, but also for presentations and live demonstrations.&lt;/p&gt;

&lt;p&gt;This project demonstrates how BLE advertisement data can be efficiently captured and delivered to the cloud using a minimal and transparent approach. By combining HibouAir sensors, BleuIO, a simple Python gateway, and Arduino Cloud, it is possible to create a complete end-to-end monitoring solution without relying on complex SDKs or embedded firmware development. While this tutorial focuses on air quality data, the same method can be extended to other BLE-based sensors and cloud platforms. As a showcase example, it highlights the flexibility of BleuIO as a BLE development tool and provides a solid foundation for developers who want to build and expand their own BLE-enabled cloud solutions.&lt;/p&gt;

</description>
      <category>bluetoothlowenergy</category>
      <category>bleuio</category>
      <category>hibouair</category>
      <category>arduino</category>
    </item>
    <item>
      <title>Integrating BleuIO with Adafruit Feather RP2040 for Seamless BLE Applications – Part 5 (Two-Way Communication)</title>
      <dc:creator>Bleuio tech</dc:creator>
      <pubDate>Fri, 05 Dec 2025 15:13:58 +0000</pubDate>
      <link>https://dev.to/bleuiot/integrating-bleuio-with-adafruit-feather-rp2040-for-seamless-ble-applications-part-5-two-way-5nf</link>
      <guid>https://dev.to/bleuiot/integrating-bleuio-with-adafruit-feather-rp2040-for-seamless-ble-applications-part-5-two-way-5nf</guid>
      <description>&lt;p&gt;In the earlier parts of this series, we combined the Adafruit Feather RP2040 with the BleuIO USB dongle to build different BLE applications: setting up the RP2040 as a USB host, reading sensor data, advertising measurements and handling secure connections.&lt;/p&gt;

&lt;p&gt;In this &lt;strong&gt;Part 5&lt;/strong&gt;, we take the next step and create a simple two-way communication setup. Instead of only broadcasting data, we let a Python script running on your computer talk to the BleuIO dongle connected to the Feather RP2040 and control its LED in real time. At the same time, the Feather responds over the Serial Port Service (SPS), echoing messages back so you can see exactly what was sent on both sides.&lt;/p&gt;

&lt;p&gt;This project is a good starting point if you want to remotely control devices, test custom BLE command protocols or build interactive demos using BleuIO and RP2040.&lt;/p&gt;

&lt;h2&gt;
  
  
  What This Project Does
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Arduino project on Adafruit Feather RP2040
&lt;/h3&gt;

&lt;p&gt;On the hardware side, the Adafruit Feather RP2040 is configured as a USB host for the BleuIO dongle, using the same TinyUSB and Pico PIO USB approach as in &lt;a href="https://www.bleuio.com/blog/integrating-bleuio-with-adafruit-feather-rp2040-for-seamless-ble-applications/" rel="noopener noreferrer"&gt;&lt;strong&gt;Part 1 of the series&lt;/strong&gt;&lt;/a&gt;. When the board starts, it initializes the USB host stack, detects the BleuIO dongle and sends a short sequence of AT commands. These commands disable echo, ask the dongle for its own MAC address, set a friendly advertising name (&lt;code&gt;BleuIO Arduino Example&lt;/code&gt;) and start BLE advertising. After that, the sketch simply listens for BLE connection events and SPS messages. Depending on what text message it receives over SPS, it either echoes the message back or sends a command to change the LED behaviour on the dongle.&lt;/p&gt;

&lt;h3&gt;
  
  
  Python script on the computer
&lt;/h3&gt;

&lt;p&gt;On the computer, a Python script acts as the BLE central. It uses the MAC address printed by the Feather’s serial output to connect to the advertising BleuIO dongle. Once connected, it sends text commands over SPS such as &lt;code&gt;**ALERT**&lt;/code&gt;, &lt;code&gt;**NORMAL**&lt;/code&gt; or &lt;code&gt;**OFF**&lt;/code&gt;, and reads back whatever the Feather sends in response. When the Python script sends one of these special words, the Feather generates BLEU AT commands to control the dongle’s LED; for any other text, it just echoes the message. This creates a complete round-trip: you type in Python, the message travels over BLE to the RP2040 and BleuIO, and a response comes back the same way.&lt;/p&gt;

&lt;h2&gt;
  
  
  Requirements
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Hardware&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;a href="https://www.adafruit.com/product/5723" rel="noopener noreferrer"&gt;Adafruit Feather RP2040 board&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="http://bleuio.com/bluetooth-low-energy-usb-ssd025.php" rel="noopener noreferrer"&gt;BleuIO Pro – Bluetooth Low Energy USB dongle&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  USB cable for the Feather&lt;/li&gt;
&lt;li&gt;  USB-A port or hub for the BleuIO dongle&lt;/li&gt;
&lt;li&gt;  Computer running Windows, macOS or Linux&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Software&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;a href="https://www.arduino.cc/en/software" rel="noopener noreferrer"&gt;Arduino IDE with RP2040 board support installed&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  Adafruit TinyUSB Library&lt;/li&gt;
&lt;li&gt;  Pico PIO USB Library&lt;/li&gt;
&lt;li&gt;  ArduinoJson library&lt;/li&gt;
&lt;li&gt;  Python 3.x and the Python project that connects to BleuIO over SPS&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you already followed &lt;a href="https://www.bleuio.com/blog/integrating-bleuio-with-adafruit-feather-rp2040-for-seamless-ble-applications/" rel="noopener noreferrer"&gt;Part 1,&lt;/a&gt; your RP2040 USB host environment and board configuration should be ready to use.&lt;/p&gt;

&lt;h2&gt;
  
  
  Source Code on GitHub
&lt;/h2&gt;

&lt;p&gt;You can find the complete source code for this project — both the Arduino sketch and the Python script — in our public GitHub repository: &lt;strong&gt;bleuio_arduino_message_transfer_example&lt;/strong&gt;. Visit the repository at:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/smart-sensor-devices-ab/bleuio_arduino_message_transfer_example" rel="noopener noreferrer"&gt;https://github.com/smart-sensor-devices-ab/bleuio_arduino_message_transfer_example&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Feel free to clone or download the repo to get started quickly. All necessary files — including the &lt;code&gt;.ino&lt;/code&gt;, helper headers, and the Python script — are included, so you can replicate the example or adapt it for your own project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Recap: Preparing the Feather RP2040 as a USB Host
&lt;/h2&gt;

&lt;p&gt;To quickly recap the setup from the earlier article: you install the Raspberry Pi RP2040 board package in the Arduino IDE, select the Feather RP2040 board, and install the Adafruit TinyUSB and Pico PIO USB libraries. You then make sure the CPU speed is set to 120 MHz or 240 MHz, since Pico PIO USB requires a clock that is a multiple of 120 MHz.&lt;/p&gt;

&lt;h2&gt;
  
  
  Uploading the Arduino Sketch
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt; Open the &lt;code&gt;bleuio_arduino_connect_example.ino&lt;/code&gt; and &lt;code&gt;usbh_helper.h&lt;/code&gt; in the same Arduino sketch folder.&lt;/li&gt;
&lt;li&gt; Select &lt;strong&gt;Adafruit Feather RP2040&lt;/strong&gt; (or your RP2040 board) under &lt;em&gt;Tools → Board&lt;/em&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkx3w98gcnsj4euvqc430.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkx3w98gcnsj4euvqc430.png" width="800" height="718"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Choose the correct COM port for the Feather.&lt;/li&gt;
&lt;li&gt; Click &lt;strong&gt;Upload&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmqzfbcmldeunuyykxepz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmqzfbcmldeunuyykxepz.png" width="396" height="49"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After upload:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Open &lt;strong&gt;Serial Monitor&lt;/strong&gt; at &lt;strong&gt;9600 baud&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt; You should see something like:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Connect test v1.0
Core1 setup to run TinyUSB host with pio-usb
SerialHost is connected to a new CDC device. Idx: 0

BleuIO response:
{"own_mac_addr":"xx:xx:xx:xx:xx:xx"}
----
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt; Every 10 seconds (based on &lt;code&gt;ALIVE_TIME&lt;/code&gt;) you’ll see an update:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;H:M:S - 0:0:10
own_mac_addr: xx:xx:xx:xx:xx:xx
Not connected!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Initially it will say &lt;strong&gt;Not connected!&lt;/strong&gt; because no BLE central is connected yet.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Python Script (BLE Central)
&lt;/h2&gt;

&lt;p&gt;The Python script acts as a &lt;strong&gt;BLE central&lt;/strong&gt; that connects to the advertising BleuIO dongle and uses the &lt;strong&gt;Serial Port Service (SPS)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A typical flow in the Python script is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Read the &lt;strong&gt;MAC address&lt;/strong&gt; printed by the Arduino Serial Monitor (&lt;code&gt;own_mac_addr&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt; Use the BleuIO Python library (or BLE stack) to connect to that address.&lt;/li&gt;
&lt;li&gt; Once connected, send plain text messages over SPS:

&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;"ALERT"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;"NORMAL"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;"OFF"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  Or any other text.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;On the Python side you’ll see:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Connection success message.&lt;/li&gt;
&lt;li&gt;  Any SPS response sent from the RP2040 (e.g. &lt;code&gt;[RP2040] Alert command Received: [...]&lt;/code&gt; or &lt;code&gt;[RP2040] Echo: ...&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On the Arduino Serial Monitor you’ll see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Connected!
SPS Received!
BleuIO response:
{"type":"SPS","evt":{"len":5,"ascii":"ALERT"}}
----
Sending command: AT+SPSSEND=[RP2040] Alert command Received: [ALERT]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the LED on the BleuIO dongle will react according to the command:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;ALERT&lt;/code&gt; → Blink pattern (&lt;code&gt;AT+LED=T=100=100&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;NORMAL&lt;/code&gt; → Toggle LED (&lt;code&gt;AT+LED=T&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;OFF&lt;/code&gt; → Turn LED off (&lt;code&gt;AT+LED=0&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;  Any other message → Just an echo, no LED change.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Where to Go Next
&lt;/h2&gt;

&lt;p&gt;This example completes the journey from simple advertising to full two-way communication between a computer application and a BleuIO dongle hosted by an Adafruit Feather RP2040. With this pattern in place, you can replace the LED commands with your own device protocol, combine it with the sensor examples from Part 2 and Part 4, or feed the exchanged messages into larger systems for logging, dashboards or control logic. Because the communication relies on the standard Serial Port Service and BleuIO AT commands, the same structure can be reused for many other projects where a PC, an embedded board and a BLE device need to work together.&lt;/p&gt;

</description>
      <category>arduino</category>
      <category>bluetooth</category>
      <category>bleuio</category>
      <category>adafruit</category>
    </item>
    <item>
      <title>How AI Makes BLE Development Even Easier with BleuIO</title>
      <dc:creator>Bleuio tech</dc:creator>
      <pubDate>Tue, 02 Dec 2025 10:04:11 +0000</pubDate>
      <link>https://dev.to/bleuiot/how-ai-makes-ble-development-even-easier-with-bleuio-40pa</link>
      <guid>https://dev.to/bleuiot/how-ai-makes-ble-development-even-easier-with-bleuio-40pa</guid>
      <description>&lt;p&gt;Bluetooth Low Energy (BLE) has become a key technology in modern wireless applications—from IoT devices and sensors to wearables, smart tools, and more. While BLE development can traditionally require time, experience, and familiarity with complex protocols, &lt;strong&gt;&lt;a href="https://www.bleuio.com/" rel="noopener noreferrer"&gt;BleuIO&lt;/a&gt;&lt;/strong&gt; dramatically simplifies the process.&lt;/p&gt;

&lt;p&gt;BleuIO is a powerful USB BLE dongle designed to help developers of all levels build BLE applications quickly and efficiently. With straightforward AT commands, intuitive documentation, and cross-platform flexibility, it allows users to prototype and develop BLE solutions without the usual learning curve.&lt;/p&gt;

&lt;p&gt;But now, with the rapid growth of AI tools such as &lt;strong&gt;ChatGPT&lt;/strong&gt; and &lt;strong&gt;Gemini&lt;/strong&gt;, the development workflow becomes even smoother. AI can help generate ready-to-run scripts, automate coding tasks, and speed up BLE experiments—making the combination of &lt;strong&gt;BleuIO + AI&lt;/strong&gt; incredibly valuable for developers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common Challenges in BLE Development
&lt;/h2&gt;

&lt;p&gt;Developing Bluetooth Low Energy applications often requires a solid understanding of BLE protocols and command structures, which can be intimidating for beginners. Developers must also write code that interfaces correctly with hardware such as dongles or embedded devices, and this process can involve repetitive boilerplate code—especially when handling tasks like scanning, connecting, and transferring data. Another common challenge is ensuring that code works consistently across different languages and platforms. These factors can slow down development and create barriers for those who simply want to prototype or test BLE functionality quickly.&lt;/p&gt;

&lt;h2&gt;
  
  
  How BleuIO and AI Solve These Problems
&lt;/h2&gt;

&lt;p&gt;BleuIO addresses many of these challenges by offering straightforward AT commands that simplify common BLE operations. When paired with modern AI tools, the development process becomes even more efficient. AI systems can read the &lt;a href="https://www.bleuio.com/getting_started/docs/commands/" rel="noopener noreferrer"&gt;BleuIO AT Command List&lt;/a&gt; and instantly generate complete scripts that integrate these commands correctly, significantly speeding up prototyping. This eliminates the need for manually writing repetitive code, allowing developers to focus on their application rather than the setup. Because BleuIO works seamlessly with Python, JavaScript, C#, Node.js, and many other environments, developers can choose the language they prefer. Even newcomers can get started easily, as AI-generated scripts help bridge any knowledge gaps and provide a smooth entry point into BLE development.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example: Using ChatGPT and Gemini to Generate a BLE Scan Script
&lt;/h2&gt;

&lt;p&gt;To demonstrate how effectively BleuIO and AI work together, we created a simple test scenario. We began by downloading the BleuIO AT Command List PDF from the Getting Started guide and then asked both ChatGPT and Gemini to generate a Python script that communicates with the &lt;a href="https://www.bleuio.com/bluetooth-low-energy-usb-ssd025.php" rel="noopener noreferrer"&gt;BleuIO BLE USB dongle&lt;/a&gt;. The script needed to use the correct AT commands, include the appropriate COM port, and perform a scan for nearby BLE devices lasting five seconds. After generating the scripts, we ran them to compare the output produced by the two AI tools.&lt;/p&gt;

&lt;h2&gt;
  
  
  Video Demonstration
&lt;/h2&gt;

&lt;p&gt;You can watch the full demonstration below, where we walk through the entire process—from downloading the command list to generating and testing the scripts:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=8wegP7O5Xls" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=8wegP7O5Xls&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This example demonstrates just how powerful the combination of &lt;strong&gt;BleuIO&lt;/strong&gt; and modern &lt;strong&gt;AI tools&lt;/strong&gt; can be. By letting AI generate boilerplate code and BLE scripts, you can focus on building features, testing ideas, or integrating wireless communication into your products.&lt;/p&gt;

&lt;p&gt;BleuIO already makes BLE development easy—but with AI, it becomes even more efficient, accessible, and developer-friendly.&lt;/p&gt;

</description>
      <category>bleuio</category>
      <category>bluetoothlowenergy</category>
      <category>ai</category>
      <category>chatgpt</category>
    </item>
    <item>
      <title>Ambient-Adaptive Particulate Monitor (PM1.0 / PM2.5 / PM10) with BleuIO &amp; HibouAir</title>
      <dc:creator>Bleuio tech</dc:creator>
      <pubDate>Fri, 31 Oct 2025 10:31:40 +0000</pubDate>
      <link>https://dev.to/bleuiot/ambient-adaptive-particulate-monitor-pm10-pm25-pm10-with-bleuio-hibouair-lji</link>
      <guid>https://dev.to/bleuiot/ambient-adaptive-particulate-monitor-pm10-pm25-pm10-with-bleuio-hibouair-lji</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyl008ly385lb76exh3za.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyl008ly385lb76exh3za.webp" alt="Ambient-Adaptive Particulate Monitor (PM1.0 / PM2.5 / PM10) with BleuIO &amp;amp; HibouAir" width="800" height="448"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Outdoor air quality is a major focus in Europe in 2025, with tighter standards placing greater emphasis on fine particulate matter—especially PM2.5. Elevated PM levels are linked to asthma, reduced cognitive performance, and increased cardiovascular risk, making reliable monitoring essential. This project demonstrates a simple, browser-based way to visualize PM1.0, PM2.5, and PM10 in real time—supporting better ventilation decisions and aligning with &lt;a href="https://smartsensordevices.com/outdoor-air-quality-in-2025-navigating-new-eu-regulations-and-monitoring-solutions/" rel="noopener noreferrer"&gt;&lt;strong&gt;evolving EU air-quality&lt;/strong&gt;&lt;/a&gt; expectations.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you’ll build
&lt;/h2&gt;

&lt;p&gt;A single &lt;strong&gt;HTML file&lt;/strong&gt; styled with &lt;strong&gt;Tailwind CSS&lt;/strong&gt; that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Puts &lt;strong&gt;BleuIO&lt;/strong&gt; in a central scanning role&lt;/li&gt;
&lt;li&gt;  Periodically runs a targeted scan for your HibouAir &lt;strong&gt;Board ID&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;  Decodes &lt;strong&gt;PM1.0 / PM2.5 / PM10&lt;/strong&gt; from the manufacturer data inside BLE advertisements&lt;/li&gt;
&lt;li&gt;  Maps the values to three horizontal bars (default display windows: PM1.0/PM2.5 → &lt;strong&gt;0–150 µg/m³&lt;/strong&gt;, PM10 → &lt;strong&gt;0–200 µg/m³&lt;/strong&gt;)&lt;/li&gt;
&lt;li&gt;  Shows a &lt;strong&gt;high particulate&lt;/strong&gt; banner when any value exceeds your thresholds&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Source code:&lt;/strong&gt; &lt;code&gt;[https://github.com/smart-sensor-devices-ab/pm-monitor-bleuio](https://github.com/smart-sensor-devices-ab/pm-monitor-bleuio)&lt;/code&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Live demo:&lt;/strong&gt; &lt;code&gt;[https://smart-sensor-devices-ab.github.io/pm-monitor-bleuio/](https://smart-sensor-devices-ab.github.io/pm-monitor-bleuio/)&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Hardware &amp;amp; software
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;a href="https://www.bleuio.com/bluetooth-low-energy-usb-ssd025.php" rel="noopener noreferrer"&gt;&lt;strong&gt;BleuIO&lt;/strong&gt; USB BLE dongle&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://www.hibouair.com/specifications-air-quality-monitor-pm-sensor.php" rel="noopener noreferrer"&gt;&lt;strong&gt;HibouAir&lt;/strong&gt; air quality monitor with particle sensor&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;p&gt;HibouAir broadcast short advertisement packets that includes real-time air quality data. We can read them without pairing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scan cadence.&lt;/strong&gt; The dongle sends:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;AT+CENTRAL&lt;/code&gt; &lt;strong&gt;once&lt;/strong&gt; to enter scanning mode&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;AT+FINDSCANDATA=&amp;lt;BOARD_ID&amp;gt;=3&lt;/code&gt; every cycle to run a &lt;strong&gt;3-second targeted scan&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;  It reads lines until BleuIO prints &lt;strong&gt;&lt;code&gt;SCAN COMPLETE&lt;/code&gt;&lt;/strong&gt;, then waits and repeats&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Decoding.&lt;/strong&gt; HibouAir advertises a compact environmental frame beginning with the marker &lt;code&gt;5B 07 05&lt;/code&gt;. PM values are &lt;strong&gt;16-bit little-endian&lt;/strong&gt; fields. In this build we anchor to the marker and read:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;PM1.0&lt;/strong&gt; (raw ÷ 10 → µg/m³)&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;PM2.5&lt;/strong&gt; (raw ÷ 10 → µg/m³)&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;PM10&lt;/strong&gt; (raw ÷ 10 → µg/m³)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;UI behavior.&lt;/strong&gt; Each metric drives a bar that fills left-to-right as the value rises within its display window. Thresholds are configurable (defaults: PM1.0 &lt;strong&gt;1&lt;/strong&gt;, PM2.5 &lt;strong&gt;2&lt;/strong&gt;, PM10 &lt;strong&gt;5&lt;/strong&gt; µg/m³). If any metric is at or above its threshold, the page shows &lt;strong&gt;“High particulate levels detected.”&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Customize &amp;amp; extend
&lt;/h2&gt;

&lt;p&gt;You can tailor this monitor to your space and workflow in several practical ways. If you anticipate larger spikes, widen the display windows—for example, expand PM2.5 to 0–200 µg/m³—to keep the bar responsive at higher ranges. For lightweight analytics, append readings to a CSV file or store them in IndexedDB to explore trends over hours or days. If you’re tracking multiple HibouAir units, build a wallboard that scans a list of Board IDs and renders compact tiles for each sensor in a single view. To act on thresholds, add automation hooks that trigger a webhook or drive a fan/relay from a companion script when levels rise. Finally, pair this particulate display with your existing CO₂ or Noise monitors to create a more complete picture of indoor conditions and ventilation effectiveness.&lt;/p&gt;

&lt;h2&gt;
  
  
  Output
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fufsqvpt8ou89vinhlw3m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fufsqvpt8ou89vinhlw3m.png" width="800" height="370"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foe4jjaeuwj0cdx6ynlmj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foe4jjaeuwj0cdx6ynlmj.png" width="800" height="370"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=WUZL5VnDtfE" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=WUZL5VnDtfE&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the video , the session starts at &lt;strong&gt;0.0 µg/m³&lt;/strong&gt; across PM1.0/PM2.5/PM10. To demonstrate responsiveness, we briefly spray &lt;strong&gt;aerosol&lt;/strong&gt; near the HibouAir device. Within seconds, the bars respond and the page displays &lt;strong&gt;“High particulate levels detected.”&lt;/strong&gt; After stopping the aerosol and allowing air to clear, values &lt;strong&gt;decay back down&lt;/strong&gt;, the bars recede, and the banner disappears. This sequence illustrates typical behavior you’ll see during quick particulate events (e.g., cleaning sprays, dust disturbances, smoke from cooking) and their recovery.&lt;/p&gt;

&lt;p&gt;This project turns HibouAir’s BLE adverts into a clear view of PM1.0, PM2.5, and PM10 using a BleuIO dongle. In minutes, you get live bars, thresholds, and a simple alert that makes particulate spikes obvious. It’s easy to tune—adjust display windows, tweak thresholds, and adapt the layout for different rooms. As EU air-quality expectations tighten, this lightweight monitor helps you spot issues and validate ventilation quickly. From here, you can add data export, multi-device dashboards, or pair it with your CO2 monitor for a fuller picture.&lt;/p&gt;

</description>
      <category>bluetoothlowenergy</category>
      <category>bleuio</category>
      <category>hibouair</category>
      <category>airqualitymonitor</category>
    </item>
  </channel>
</rss>
