{"id":1801,"date":"2026-10-16T14:58:39","date_gmt":"2026-10-16T14:58:39","guid":{"rendered":"https:\/\/www.bleuio.com\/blog\/?p=1801"},"modified":"2026-10-16T15:01:47","modified_gmt":"2026-10-16T15:01:47","slug":"scanning-hibouair-sensors-with-circuitpython-bleuio-and-the-cytron-motion-2350-pro","status":"publish","type":"post","link":"https:\/\/www.bleuio.com\/blog\/scanning-hibouair-sensors-with-circuitpython-bleuio-and-the-cytron-motion-2350-pro\/","title":{"rendered":"Scanning HibouAir Sensors with CircuitPython, BleuIO and the Cytron MOTION 2350 Pro"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">In this tutorial we will use <strong>CircuitPython<\/strong> to turn a Cytron MOTION 2350 Pro into a HibouAir scanner. The board&#8217;s RP2350 has no Bluetooth radio of its own. A <strong>BleuIO dongle<\/strong> in the board&#8217;s USB-A host port provides Bluetooth Low Energy, and a short CircuitPython program talks to it with ordinary AT commands.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The program scans for <strong><a href=\"https:\/\/www.hibouair.com\/\" target=\"_blank\" rel=\"noreferrer noopener\">air quality monitoring device<\/a><\/strong> HibouAir advertisements, decodes them, and keeps the latest readings in global variables. One of them, the PM10 value from a <strong><a href=\"https:\/\/www.hibouair.com\/specifications-air-quality-monitor-pm-sensor.php\" target=\"_blank\" rel=\"noreferrer noopener\">HibouAir PM sensor<\/a><\/strong>, 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&#8217;s servo ports into an analog PM10 gauge.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">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.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Requirements<\/h2>\n\n\n\n<h4 class=\"wp-block-heading\">Hardware<\/h4>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/www.digikey.com\/en\/products\/detail\/cytron-technologies-sdn-bhd\/MOTION-2350-PRO\/25587464?s=N4IgTCBcDaILYHsAuBLBA7ABABwE4MzAGYBWABhAF0BfIA\" target=\"_blank\" rel=\"noreferrer noopener\">Cytron MOTION 2350 Pro<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/www.bleuio.com\/bluetooth-low-energy-usb-ssd025.php\" target=\"_blank\" rel=\"noreferrer noopener\">BleuIO USB dongle<\/a>. This tutorial was tested with a BleuIO Pro, firmware 1.0.7.1.<\/li>\n\n\n\n<li>One or more <a href=\"https:\/\/www.hibouair.com\/air-quality-monitor-hibouair-duo.php\" target=\"_blank\" rel=\"noreferrer noopener\">HibouAir air quality sensors<\/a>. A <a href=\"https:\/\/www.hibouair.com\/specifications-air-quality-monitor-pm-sensor.php\" target=\"_blank\" rel=\"noreferrer noopener\">HibouAir PM sensor<\/a> is needed for the PM10 value.<\/li>\n\n\n\n<li>A <a href=\"https:\/\/www.bleuio.com\/accessories.php\" target=\"_blank\" rel=\"noreferrer noopener\">USB-C data cable<\/a> between the board and your computer<\/li>\n\n\n\n<li>Optional: a hobby servo for the PM10 gauge<\/li>\n<\/ul>\n\n\n\n<h4 class=\"wp-block-heading\">Software<\/h4>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>CircuitPython 10.x<\/strong> for the Cytron MOTION 2350 Pro, from <a href=\"https:\/\/circuitpython.org\/board\/cytron_motion_2350_pro\/\" target=\"_blank\" rel=\"noreferrer noopener\">circuitpython.org<\/a>. The tutorial uses 10.3.1.<\/li>\n\n\n\n<li>Two libraries from the <a href=\"https:\/\/circuitpython.org\/libraries\" target=\"_blank\" rel=\"noreferrer noopener\">Adafruit CircuitPython Library Bundle<\/a> for version 10.x, copied into the <code>lib<\/code> folder on the <code>CIRCUITPY<\/code> drive:\n<ul class=\"wp-block-list\">\n<li><code>asyncio<\/code> (the whole folder)<\/li>\n\n\n\n<li><code>adafruit_ticks.mpy<\/code>, which <code>asyncio<\/code> needs<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">With <a href=\"https:\/\/github.com\/adafruit\/circup\" target=\"_blank\" rel=\"noreferrer noopener\">circup<\/a> installed on your computer, one command copies both:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>circup install asyncio<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Everything else the program uses is built into CircuitPython: <code>usb_host<\/code>, <code>usb.core<\/code>, <code>json<\/code>, <code>binascii<\/code>, <code>microcontroller<\/code>, <code>pwmio<\/code> and <code>time<\/code>. The servo is driven with <code>pwmio<\/code> directly, so it needs no extra library. No <code>boot.py<\/code> is needed.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">What Does This Project Do?<\/h2>\n\n\n\n<h4 class=\"wp-block-heading\">1. The board becomes a USB host<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">The MOTION 2350 Pro&#8217;s USB-A port is driven by the RP2350&#8217;s PIO. CircuitPython starts it with <code>usb_host.Port<\/code> on GPIO24 and GPIO25. The board definition has no named pins for this port, so the program uses the raw pins.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">2. CircuitPython finds BleuIO<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">The program waits until a device with vendor ID <code>0x2DCF<\/code> and product ID <code>0x6002<\/code> appears, which is BleuIO. It selects the dongle&#8217;s configuration and sets DTR and RTS, so that BleuIO starts sending its output.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">3. The dongle is brought into a known state<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">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:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>ATE0<\/code>, which turns echo off<\/li>\n\n\n\n<li><code>ATV1<\/code>, which turns verbose mode on, so every reply is one line of JSON<\/li>\n<\/ul>\n\n\n\n<h4 class=\"wp-block-heading\">4. BleuIO scans for HibouAir only<\/h4>\n\n\n\n<pre class=\"wp-block-code\"><code>AT+FINDSCANDATA=FF5B07<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This reports only advertisements that contain <code>FF5B07<\/code>. <code>FF<\/code> is the AD type for manufacturer-specific data, and <code>5B 07<\/code> is HibouAir&#8217;s company ID, 0x075B, as it appears in the packet. Each result arrives as a line such as:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>{\"SF\":127,\"addr\":\"D9:63:8F:8E:01:0D\",\"type\":0,\"data\":\"0201061BFF5B070504220095AE009627CF00AC014200000000000000019902\"}<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">5. The board decodes the advertisement<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">The program walks the advertisement&#8217;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:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Offset<\/th><th>Field<\/th><th>Format<\/th><\/tr><\/thead><tbody><tr><td>2<\/td><td>Beacon type<\/td><td>0x05 for sensor readings<\/td><\/tr><tr><td>3<\/td><td>Board type<\/td><td>0x03 is the PM sensor<\/td><\/tr><tr><td>4\u20136<\/td><td>Sensor ID<\/td><td>as shown in the HibouAir app, e.g. 22026A<\/td><\/tr><tr><td>9<\/td><td>Pressure<\/td><td>16-bit little-endian, tenths of hPa<\/td><\/tr><tr><td>11<\/td><td>Temperature<\/td><td>16-bit little-endian, signed, tenths of \u00b0C<\/td><\/tr><tr><td>13<\/td><td>Humidity<\/td><td>16-bit little-endian, tenths of %rh<\/td><\/tr><tr><td>17<\/td><td>PM1.0<\/td><td>16-bit little-endian, tenths of \u00b5g\/m\u00b3<\/td><\/tr><tr><td>19<\/td><td>PM2.5<\/td><td>16-bit little-endian, tenths of \u00b5g\/m\u00b3<\/td><\/tr><tr><td>21<\/td><td>PM10<\/td><td>16-bit little-endian, tenths of \u00b5g\/m\u00b3<\/td><\/tr><tr><td>23<\/td><td>CO2<\/td><td>16-bit <strong>big<\/strong>-endian, ppm<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h4 class=\"wp-block-heading\">6. The readings are kept in global variables<\/h4>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>pm10<\/code>: the latest PM10 value in \u00b5g\/m\u00b3, or <code>None<\/code> until a PM sensor has been heard<\/li>\n\n\n\n<li><code>pm10_sensor<\/code>: the ID of the sensor it came from<\/li>\n\n\n\n<li><code>pm10_time<\/code>: <code>time.monotonic()<\/code> when it arrived, so that a stale value can be recognised<\/li>\n\n\n\n<li><code>sensors<\/code>: a dictionary with everything the last beacon of every HibouAir sensor said, keyed by sensor ID<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">If you have more than one PM sensor, set <code>PM_SENSOR_ID<\/code> to the one <code>pm10<\/code> should follow. If nothing has been heard for 15 seconds, the program starts the scan again.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Showing PM10 on a Servo Gauge<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">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 <strong>GP0<\/strong> as a needle: 0 \u00b5g\/m\u00b3 at 0 degrees, and 100 \u00b5g\/m\u00b3 or more at 180 degrees.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">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:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>PM10<\/th><th>Level<\/th><th>Needle<\/th><\/tr><\/thead><tbody><tr><td>0\u201320 \u00b5g\/m\u00b3<\/td><td>Good<\/td><td>0\u201336\u00b0<\/td><\/tr><tr><td>20\u201350 \u00b5g\/m\u00b3<\/td><td>Fair<\/td><td>36\u201390\u00b0<\/td><\/tr><tr><td>50\u2013100 \u00b5g\/m\u00b3<\/td><td>Poor<\/td><td>90\u2013180\u00b0<\/td><\/tr><tr><td>100 \u00b5g\/m\u00b3 and above<\/td><td>Bad<\/td><td>180\u00b0<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">The gauge is its own asyncio task, beside the reader and the command logic:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>It sends a 50 Hz servo signal, from 500 \u00b5s at 0 degrees to 2500 \u00b5s at 180.<\/li>\n\n\n\n<li>It moves the needle 2 degrees every 20 ms, so the arm swings to a new reading instead of jumping.<\/li>\n\n\n\n<li>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.<\/li>\n\n\n\n<li>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.<\/li>\n\n\n\n<li>The pulses start again when a new reading moves the needle by at least one degree, so readings of 0.0 and 0.1 \u00b5g\/m\u00b3 taking turns do not wake it.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Connect the servo to the GP0 servo header, matching the servo cable to the markings next to the header. Check Cytron&#8217;s datasheet for how the servo ports are supplied before connecting a larger servo.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The settings are at the top of <code>code.py<\/code>:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>SERVO_PIN<\/code>: the port, <code>board.GP0<\/code> to <code>board.GP7<\/code><\/li>\n\n\n\n<li><code>PM10_FULL_SCALE<\/code>: the PM10 value at 180 degrees<\/li>\n\n\n\n<li><code>SERVO_MIN_US<\/code> and <code>SERVO_MAX_US<\/code>: the pulse widths at the ends. Narrow them if your servo strains at its end stops.<\/li>\n\n\n\n<li><code>SERVO_STEP_DEG<\/code>: how fast the needle moves<\/li>\n\n\n\n<li><code>SERVO_REST_S<\/code> and <code>SERVO_DEADBAND_DEG<\/code>: how long the needle is still before it rests, and how far a reading must move it to wake it<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">The board&#8217;s four DC motor drivers, on GP8 to GP15 (<code>board.M1A<\/code> to <code>board.M4B<\/code> in CircuitPython), would suit a different use of the same value, such as running a fan faster as PM10 rises.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Why the Flush After Ctrl-C Matters<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">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.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">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.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The program now does two things:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>It removes each line from the buffer before decoding it, so a broken line costs that line only.<\/li>\n\n\n\n<li>It discards everything until the dongle has been quiet before sending the next command.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">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.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">How to Run the Project<\/h2>\n\n\n\n<h4 class=\"wp-block-heading\">Step 1: Install CircuitPython<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">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 <code>RP2350<\/code> drive that appears. The board restarts as a drive named <code>CIRCUITPY<\/code>.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Step 2: Add the libraries<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">Run <code>circup install asyncio<\/code>, or copy the <code>asyncio<\/code> folder and <code>adafruit_ticks.mpy<\/code> from the library bundle into <code>CIRCUITPY\/lib<\/code>.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Step 3: Copy the program<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">Save the code at the end of this post as <code>code.py<\/code> on the <code>CIRCUITPY<\/code> drive. CircuitPython starts it as soon as the file is saved.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Step 4: Connect BleuIO<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">Insert the BleuIO dongle into the board&#8217;s USB-A port. Do not connect BleuIO to the computer for this setup.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Step 5: Connect the servo (optional)<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">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.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Step 6: Watch the serial console<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">Open the board&#8217;s serial console at 115200 baud. Use Mu, Thonny or the CircuitPython web workflow, or on macOS and Linux:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>screen \/dev\/cu.usbmodem2101 115200<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The device name differs from computer to computer. If the console shows <code>raw REPL; CTRL-B to exit<\/code>, an editor has left it in raw REPL mode: press Ctrl-B, then Ctrl-D to start <code>code.py<\/code>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Expected Output<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>BleuIO dongle found! (0x2dcf:0x6002)\nCDC Control Lines (DTR\/RTS) enabled.\nStarting background USB reader task...\nStarting the HibouAir scan...\n&#91;Sending]: ATE0\n&#91;Sending]: ATV1\n&#91;Sending]: AT+FINDSCANDATA=FF5B07\n&#91;HibouAir 22026A] PM10 0.1 ug\/m3  PM2.5 0.1  PM1 0.0  21.8 C\n&#91;HibouAir 22008C] board 0x04, 21.5 C, 39.8 %rh, CO2 459 ppm\n&#91;HibouAir 220095] board 0x04, 20.8 C, 42.9 %rh, CO2 411 ppm\n&#91;HibouAir 22005A] board 0x04, 19.6 C, 44.2 %rh, CO2 721 ppm\n&#91;HibouAir 220265] board 0x09, 19.8 C, 45.5 %rh, CO2 533 ppm<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">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 <code>pm10<\/code>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">The Code<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Source code is available to download from<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/github.com\/smart-sensor-devices-ab\/hibouair-with-circuitpython\">https:\/\/github.com\/smart-sensor-devices-ab\/hibouair-with-circuitpython<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">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.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The complete source code is available for developers to explore, modify, and integrate into their own projects. Whether you&#8217;re building sensor monitoring systems, automation solutions, or custom IoT applications, this example provides a starting point for using BleuIO with compatible development boards.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In this tutorial we will use CircuitPython to turn a Cytron MOTION 2350 Pro into a HibouAir scanner. The board&#8217;s RP2350 has no Bluetooth radio of its own. A BleuIO dongle in the board&#8217;s USB-A host port provides Bluetooth Low Energy, and a short CircuitPython program talks to it with ordinary AT commands. The program [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":1803,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1,2],"tags":[],"class_list":["post-1801","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-bleuio","category-bleuio-tutorial"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v28.7 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Scanning HibouAir Sensors with CircuitPython, BleuIO and the Cytron MOTION 2350 Pro - BleuIO - Create Bluetooth Low Energy application<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.bleuio.com\/blog\/scanning-hibouair-sensors-with-circuitpython-bleuio-and-the-cytron-motion-2350-pro\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Scanning HibouAir Sensors with CircuitPython, BleuIO and the Cytron MOTION 2350 Pro - BleuIO - Create Bluetooth Low Energy application\" \/>\n<meta property=\"og:description\" content=\"In this tutorial we will use CircuitPython to turn a Cytron MOTION 2350 Pro into a HibouAir scanner. The board&#8217;s RP2350 has no Bluetooth radio of its own. A BleuIO dongle in the board&#8217;s USB-A host port provides Bluetooth Low Energy, and a short CircuitPython program talks to it with ordinary AT commands. The program [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.bleuio.com\/blog\/scanning-hibouair-sensors-with-circuitpython-bleuio-and-the-cytron-motion-2350-pro\/\" \/>\n<meta property=\"og:site_name\" content=\"BleuIO - Create Bluetooth Low Energy application\" \/>\n<meta property=\"article:published_time\" content=\"2026-10-16T14:58:39+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2026-10-16T15:01:47+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.bleuio.com\/blog\/wp-content\/uploads\/2026\/10\/bleuio-circuitpython.webp\" \/>\n\t<meta property=\"og:image:width\" content=\"1200\" \/>\n\t<meta property=\"og:image:height\" content=\"670\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/webp\" \/>\n<meta name=\"author\" content=\"BleuIO\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/www.bleuio.com\\\/blog\\\/scanning-hibouair-sensors-with-circuitpython-bleuio-and-the-cytron-motion-2350-pro\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.bleuio.com\\\/blog\\\/scanning-hibouair-sensors-with-circuitpython-bleuio-and-the-cytron-motion-2350-pro\\\/\"},\"author\":{\"name\":\"BleuIO\",\"@id\":\"https:\\\/\\\/www.bleuio.com\\\/blog\\\/#\\\/schema\\\/person\\\/89bc581382d5964043f96efc54b75b80\"},\"headline\":\"Scanning HibouAir Sensors with CircuitPython, BleuIO and the Cytron MOTION 2350 Pro\",\"datePublished\":\"2026-10-16T14:58:39+00:00\",\"dateModified\":\"2026-10-16T15:01:47+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/www.bleuio.com\\\/blog\\\/scanning-hibouair-sensors-with-circuitpython-bleuio-and-the-cytron-motion-2350-pro\\\/\"},\"wordCount\":1549,\"commentCount\":0,\"image\":{\"@id\":\"https:\\\/\\\/www.bleuio.com\\\/blog\\\/scanning-hibouair-sensors-with-circuitpython-bleuio-and-the-cytron-motion-2350-pro\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.bleuio.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/10\\\/bleuio-circuitpython.webp\",\"articleSection\":[\"BleuIO\",\"BleuIO tutorial\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/www.bleuio.com\\\/blog\\\/scanning-hibouair-sensors-with-circuitpython-bleuio-and-the-cytron-motion-2350-pro\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/www.bleuio.com\\\/blog\\\/scanning-hibouair-sensors-with-circuitpython-bleuio-and-the-cytron-motion-2350-pro\\\/\",\"url\":\"https:\\\/\\\/www.bleuio.com\\\/blog\\\/scanning-hibouair-sensors-with-circuitpython-bleuio-and-the-cytron-motion-2350-pro\\\/\",\"name\":\"Scanning HibouAir Sensors with CircuitPython, BleuIO and the Cytron MOTION 2350 Pro - BleuIO - Create Bluetooth Low Energy application\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.bleuio.com\\\/blog\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/www.bleuio.com\\\/blog\\\/scanning-hibouair-sensors-with-circuitpython-bleuio-and-the-cytron-motion-2350-pro\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/www.bleuio.com\\\/blog\\\/scanning-hibouair-sensors-with-circuitpython-bleuio-and-the-cytron-motion-2350-pro\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.bleuio.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/10\\\/bleuio-circuitpython.webp\",\"datePublished\":\"2026-10-16T14:58:39+00:00\",\"dateModified\":\"2026-10-16T15:01:47+00:00\",\"author\":{\"@id\":\"https:\\\/\\\/www.bleuio.com\\\/blog\\\/#\\\/schema\\\/person\\\/89bc581382d5964043f96efc54b75b80\"},\"breadcrumb\":{\"@id\":\"https:\\\/\\\/www.bleuio.com\\\/blog\\\/scanning-hibouair-sensors-with-circuitpython-bleuio-and-the-cytron-motion-2350-pro\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/www.bleuio.com\\\/blog\\\/scanning-hibouair-sensors-with-circuitpython-bleuio-and-the-cytron-motion-2350-pro\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/www.bleuio.com\\\/blog\\\/scanning-hibouair-sensors-with-circuitpython-bleuio-and-the-cytron-motion-2350-pro\\\/#primaryimage\",\"url\":\"https:\\\/\\\/www.bleuio.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/10\\\/bleuio-circuitpython.webp\",\"contentUrl\":\"https:\\\/\\\/www.bleuio.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/10\\\/bleuio-circuitpython.webp\",\"width\":1200,\"height\":670,\"caption\":\"bleuio-circuitpython\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/www.bleuio.com\\\/blog\\\/scanning-hibouair-sensors-with-circuitpython-bleuio-and-the-cytron-motion-2350-pro\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/www.bleuio.com\\\/blog\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Scanning HibouAir Sensors with CircuitPython, BleuIO and the Cytron MOTION 2350 Pro\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/www.bleuio.com\\\/blog\\\/#website\",\"url\":\"https:\\\/\\\/www.bleuio.com\\\/blog\\\/\",\"name\":\"BleuIO - Create Bluetooth Low Energy application\",\"description\":\"Learn Bluetooth Low Energy programming and build Bluetooth Low Energy Application\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/www.bleuio.com\\\/blog\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/www.bleuio.com\\\/blog\\\/#\\\/schema\\\/person\\\/89bc581382d5964043f96efc54b75b80\",\"name\":\"BleuIO\",\"sameAs\":[\"https:\\\/\\\/www.bleuio.com\\\/blog\"],\"url\":\"https:\\\/\\\/www.bleuio.com\\\/blog\\\/author\\\/biadmin\\\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Scanning HibouAir Sensors with CircuitPython, BleuIO and the Cytron MOTION 2350 Pro - BleuIO - Create Bluetooth Low Energy application","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.bleuio.com\/blog\/scanning-hibouair-sensors-with-circuitpython-bleuio-and-the-cytron-motion-2350-pro\/","og_locale":"en_US","og_type":"article","og_title":"Scanning HibouAir Sensors with CircuitPython, BleuIO and the Cytron MOTION 2350 Pro - BleuIO - Create Bluetooth Low Energy application","og_description":"In this tutorial we will use CircuitPython to turn a Cytron MOTION 2350 Pro into a HibouAir scanner. The board&#8217;s RP2350 has no Bluetooth radio of its own. A BleuIO dongle in the board&#8217;s USB-A host port provides Bluetooth Low Energy, and a short CircuitPython program talks to it with ordinary AT commands. The program [&hellip;]","og_url":"https:\/\/www.bleuio.com\/blog\/scanning-hibouair-sensors-with-circuitpython-bleuio-and-the-cytron-motion-2350-pro\/","og_site_name":"BleuIO - Create Bluetooth Low Energy application","article_published_time":"2026-10-16T14:58:39+00:00","article_modified_time":"2026-10-16T15:01:47+00:00","og_image":[{"width":1200,"height":670,"url":"https:\/\/www.bleuio.com\/blog\/wp-content\/uploads\/2026\/10\/bleuio-circuitpython.webp","type":"image\/webp"}],"author":"BleuIO","twitter_card":"summary_large_image","schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.bleuio.com\/blog\/scanning-hibouair-sensors-with-circuitpython-bleuio-and-the-cytron-motion-2350-pro\/#article","isPartOf":{"@id":"https:\/\/www.bleuio.com\/blog\/scanning-hibouair-sensors-with-circuitpython-bleuio-and-the-cytron-motion-2350-pro\/"},"author":{"name":"BleuIO","@id":"https:\/\/www.bleuio.com\/blog\/#\/schema\/person\/89bc581382d5964043f96efc54b75b80"},"headline":"Scanning HibouAir Sensors with CircuitPython, BleuIO and the Cytron MOTION 2350 Pro","datePublished":"2026-10-16T14:58:39+00:00","dateModified":"2026-10-16T15:01:47+00:00","mainEntityOfPage":{"@id":"https:\/\/www.bleuio.com\/blog\/scanning-hibouair-sensors-with-circuitpython-bleuio-and-the-cytron-motion-2350-pro\/"},"wordCount":1549,"commentCount":0,"image":{"@id":"https:\/\/www.bleuio.com\/blog\/scanning-hibouair-sensors-with-circuitpython-bleuio-and-the-cytron-motion-2350-pro\/#primaryimage"},"thumbnailUrl":"https:\/\/www.bleuio.com\/blog\/wp-content\/uploads\/2026\/10\/bleuio-circuitpython.webp","articleSection":["BleuIO","BleuIO tutorial"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.bleuio.com\/blog\/scanning-hibouair-sensors-with-circuitpython-bleuio-and-the-cytron-motion-2350-pro\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.bleuio.com\/blog\/scanning-hibouair-sensors-with-circuitpython-bleuio-and-the-cytron-motion-2350-pro\/","url":"https:\/\/www.bleuio.com\/blog\/scanning-hibouair-sensors-with-circuitpython-bleuio-and-the-cytron-motion-2350-pro\/","name":"Scanning HibouAir Sensors with CircuitPython, BleuIO and the Cytron MOTION 2350 Pro - BleuIO - Create Bluetooth Low Energy application","isPartOf":{"@id":"https:\/\/www.bleuio.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.bleuio.com\/blog\/scanning-hibouair-sensors-with-circuitpython-bleuio-and-the-cytron-motion-2350-pro\/#primaryimage"},"image":{"@id":"https:\/\/www.bleuio.com\/blog\/scanning-hibouair-sensors-with-circuitpython-bleuio-and-the-cytron-motion-2350-pro\/#primaryimage"},"thumbnailUrl":"https:\/\/www.bleuio.com\/blog\/wp-content\/uploads\/2026\/10\/bleuio-circuitpython.webp","datePublished":"2026-10-16T14:58:39+00:00","dateModified":"2026-10-16T15:01:47+00:00","author":{"@id":"https:\/\/www.bleuio.com\/blog\/#\/schema\/person\/89bc581382d5964043f96efc54b75b80"},"breadcrumb":{"@id":"https:\/\/www.bleuio.com\/blog\/scanning-hibouair-sensors-with-circuitpython-bleuio-and-the-cytron-motion-2350-pro\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.bleuio.com\/blog\/scanning-hibouair-sensors-with-circuitpython-bleuio-and-the-cytron-motion-2350-pro\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.bleuio.com\/blog\/scanning-hibouair-sensors-with-circuitpython-bleuio-and-the-cytron-motion-2350-pro\/#primaryimage","url":"https:\/\/www.bleuio.com\/blog\/wp-content\/uploads\/2026\/10\/bleuio-circuitpython.webp","contentUrl":"https:\/\/www.bleuio.com\/blog\/wp-content\/uploads\/2026\/10\/bleuio-circuitpython.webp","width":1200,"height":670,"caption":"bleuio-circuitpython"},{"@type":"BreadcrumbList","@id":"https:\/\/www.bleuio.com\/blog\/scanning-hibouair-sensors-with-circuitpython-bleuio-and-the-cytron-motion-2350-pro\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.bleuio.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Scanning HibouAir Sensors with CircuitPython, BleuIO and the Cytron MOTION 2350 Pro"}]},{"@type":"WebSite","@id":"https:\/\/www.bleuio.com\/blog\/#website","url":"https:\/\/www.bleuio.com\/blog\/","name":"BleuIO - Create Bluetooth Low Energy application","description":"Learn Bluetooth Low Energy programming and build Bluetooth Low Energy Application","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.bleuio.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Person","@id":"https:\/\/www.bleuio.com\/blog\/#\/schema\/person\/89bc581382d5964043f96efc54b75b80","name":"BleuIO","sameAs":["https:\/\/www.bleuio.com\/blog"],"url":"https:\/\/www.bleuio.com\/blog\/author\/biadmin\/"}]}},"_links":{"self":[{"href":"https:\/\/www.bleuio.com\/blog\/wp-json\/wp\/v2\/posts\/1801","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.bleuio.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.bleuio.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.bleuio.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.bleuio.com\/blog\/wp-json\/wp\/v2\/comments?post=1801"}],"version-history":[{"count":4,"href":"https:\/\/www.bleuio.com\/blog\/wp-json\/wp\/v2\/posts\/1801\/revisions"}],"predecessor-version":[{"id":1806,"href":"https:\/\/www.bleuio.com\/blog\/wp-json\/wp\/v2\/posts\/1801\/revisions\/1806"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.bleuio.com\/blog\/wp-json\/wp\/v2\/media\/1803"}],"wp:attachment":[{"href":"https:\/\/www.bleuio.com\/blog\/wp-json\/wp\/v2\/media?parent=1801"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.bleuio.com\/blog\/wp-json\/wp\/v2\/categories?post=1801"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.bleuio.com\/blog\/wp-json\/wp\/v2\/tags?post=1801"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}