{"id":26447,"date":"2026-09-06T13:59:07","date_gmt":"2026-09-06T13:59:07","guid":{"rendered":"https:\/\/wolles-elektronikkiste.de\/?p=26447"},"modified":"2026-09-06T13:59:17","modified_gmt":"2026-09-06T13:59:17","slug":"using-arduinoble","status":"publish","type":"post","link":"https:\/\/wolles-elektronikkiste.de\/en\/using-arduinoble","title":{"rendered":"Using ArduinoBLE"},"content":{"rendered":"\n<h2 class=\"wp-block-heading\">About this Post<\/h2>\n<p>After discussing Bluetooth Low Energy (BLE) on the ESP32 in <a href=\"https:\/\/wolles-elektronikkiste.de\/en\/ble-with-the-esp32-an-introduction\" target=\"_blank\" rel=\"noopener\">my last post<\/a>, I would now like to explain how you can use BLE on Arduino boards using the <a href=\"https:\/\/github.com\/arduino-libraries\/Arduinoble\" target=\"_blank\" rel=\"noopener\">ArduinoBLE<\/a> library. Of course, there are many similarities between using BLE on ESP32 and Arduino boards, but the libraries differ so much that I thought it made sense to split this into two separate posts. The structure of this post, however, is essentially the same.  <\/p>\n<p>I won&#8217;t go over the basic BLE concepts\u2014such as &#8220;service,&#8221; &#8220;characteristic,&#8221; &#8220;peripheral,&#8221; and &#8220;central&#8221;\u2014again here. If you need more information, check <a href=\"https:\/\/wolles-elektronikkiste.de\/en\/ble-with-the-esp32-an-introduction#overview\" target=\"_blank\" rel=\"noopener\">here<\/a>. <\/p>\n\n<p class=\"wp-block-paragraph\">What to expect in this article:<\/p>\n\n<ul class=\"wp-block-list\">\n<li><a href=\"#minimal_sketch\">ArduinoBLE Minimal Sketch<\/a><\/li>\n\n\n\n<li><a href=\"#event_handler\">Event Handlers (Callbacks)<\/a><\/li>\n\n\n\n<li><a href=\"#notify_descriptors\">Notify and Descriptors<\/a><\/li>\n\n\n\n<li><a href=\"#two_arduinos\">Connecting two Arduinos via BLE<\/a>\n<ul class=\"wp-block-list\">\n<li><a href=\"#server\">The Server (Peripheral)<\/a><\/li>\n\n\n\n<li><a href=\"#client\">The Client (Cental)<\/a>\n<ul class=\"wp-block-list\">\n<li><a href=\"#client_scans\">Intermediate Step: The Client scans<\/a><\/li>\n\n\n\n<li><a href=\"#client_complete\">The Complete Client Sketch<\/a><\/li>\n\n\n\n<li><a href=\"#alternative_client_sketch\">Alternative Client Sketch, non-blocking<\/a><\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><a href=\"#appendix\">Appendix \u2013 Sending Structures<\/a><\/li>\n<\/ul>\n\n<h2 class=\"wp-block-heading\">Preparation<\/h2>\n\n<p class=\"wp-block-paragraph\">To follow along with the examples in this post, you will first need two BLE-capable boards that are supported by the ArduinoBLE library. As of today, and to the best of my knowledge, these are the following Arduino boards:  <\/p>\n\n<ul class=\"wp-block-list\">\n<li>UNO WiFi Rev2, UNO R4 WiFi<\/li>\n\n\n\n<li>MKR WiFi 1010<\/li>\n\n\n\n<li>Nano 33 IoT, Nano 33 BLE (including Sense and Sense Rev2), Nano RP2040<\/li>\n\n\n\n<li>Nicla Sense ME, Nicla Voice, Nicla Vision<\/li>\n\n\n\n<li>Opta<\/li>\n\n\n\n<li>Portenta H7, Portenta C33<\/li>\n<\/ul>\n\n<p class=\"wp-block-paragraph\">BLE communication also works between different boards. So you do not need two identical models. But of course, I have not tested all the models and combinations myself.   <\/p>\n<p>For some boards, you may need to update the NINA-W102 firmware. You can identify the problem by the corresponding error messages that appear during compilation. But the update is no big deal: select the correct board and port in the Arduino IDE. Then go to Tools \u2192 Firmware Updater \u2192 Select Board \u2192 Check for Updates \u2192 Select Version (&gt;3.0.0) \u2192 Install.    <\/p>\n\n<p class=\"wp-block-paragraph\">Then you will need the ArduinoBLE library, which you can easily install using the Arduino IDE&#8217;s library manager. <\/p>\n\n<p class=\"wp-block-paragraph\">And finally, you will need a smartphone with a compatible BLE app, such as <a href=\"https:\/\/punchthrough.com\/lightblue\/\" data-type=\"link\" data-id=\"https:\/\/punchthrough.com\/lightblue\/\" target=\"_blank\" rel=\"noreferrer noopener\">LightBlue<\/a> or nRF Connect (<a href=\"https:\/\/play.google.com\/store\/apps\/details?id=no.nordicsemi.android.mcp&amp;hl=de\" data-type=\"link\" data-id=\"https:\/\/play.google.com\/store\/apps\/details?id=no.nordicsemi.android.mcp&amp;hl=de\" target=\"_blank\" rel=\"noreferrer noopener\">for Android<\/a> \/ <a href=\"https:\/\/apps.apple.com\/de\/app\/nrf-connect-for-mobile\/id1054362403\" data-type=\"link\" data-id=\"https:\/\/apps.apple.com\/de\/app\/nrf-connect-for-mobile\/id1054362403\" target=\"_blank\" rel=\"noreferrer noopener\">for Apple<\/a>). Alternatively, you can use a PC or laptop with a Bluetooth adapter. In that case, you will also need suitable apps, such as Bluetooth LE Explorer. I discussed some smartphone and PC apps in more detail in my last post.   <\/p>\n\n<h2 id=\"minimal_sketch\" class=\"wp-block-heading\">ArduinoBLE Minimal Sketch<\/h2>\n<p>We will start off simple and turn our Arduino into a peripheral, assigning it a service and a characteristic. The characteristic should be writable by the client. We will use a suitable app on a PC or smartphone as the client. Here&#8217;s the sketch:   <\/p>\n<\/p>\n<div class=\"scroll-paragraph\">\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-group=\"ble_minimal.ino\" data-enlighter-title=\"ble_minimal.ino\">#include &lt;ArduinoBLE.h&gt;\n\nBLEService myService(\"19B10000-E8F2-537E-4F6C-D104768A1214\"); \nBLEStringCharacteristic myCharacteristic(\"19B10001-E8F2-537E-4F6C-D104768A1214\", BLERead | BLEWrite, 40);\n\nvoid setup() {\n    Serial.begin(115200);\n    while (!Serial);\n\n    if (!BLE.begin()) {\n        Serial.println(\"Starting BLE module failed!\");\n        while (1);\n    }\n\n    BLE.setLocalName(\"BLE Minimal\"); \/\/ \"Advertising Name\"\n    \/\/ BLE.setDeviceName(\"BLE Minimal\"); \/\/ Device Name\n    BLE.setAdvertisedService(myService);\n\n    myService.addCharacteristic(myCharacteristic);\n    BLE.addService(myService);\n\n    myCharacteristic.writeValue(\"Hello Smartphone!\");\n\n    BLE.advertise();\n\n    Serial.println(\"BLE Minimal Sketch\");\n}\n\nvoid loop() {\n    BLEDevice central = BLE.central();\n\n    if(central) {\n        Serial.print(\"Connected to central: \");\n        Serial.println(central.address());\n\n        while (central.connected()) {\n            if (myCharacteristic.written()) {\n                if (myCharacteristic.value()) {   \n                    Serial.println(myCharacteristic.value());\n                }\n            }\n        }\n    \n        Serial.println(\"Disconnected from central.\");\n    }\n}<\/pre>\n<p>\u00a0<\/p>\n<\/div>\n<p>\n<p>Upload the sketch, open your BLE app (such as LightBlue), and search for the &#8220;BLE Minimal&#8221; peripheral. Connect to it, then go to the corresponding characteristic. If you tap or click \u201cRead,\u201d you should see the value of the characteristic, which is \u201cHello Smartphone.\u201d If you only see numbers, you will need to change the format to UTF-8. In nRF Connect, you will need to tap the service before the characteristic, and the down arrow corresponds to \u201cread.\u201d     <\/p>\n<p>Then click &#8220;Write New Value&#8221; (nRF Connect: up arrow) and enter a new value, such as &#8220;Hello Arduino.&#8221; If necessary, you may also need to change the format to UTF-8 here. You will see the new value immediately in the serial monitor, and on the client (central), you will see the value the next time it reads.   <\/p>\n<p>This is what the output looked like on my smartphone using LightBlue (before changing the characteristic):<\/p>\n\n<div class=\"wp-block-columns is-layout-flex wp-container-core-columns-is-layout-8f761849 wp-block-columns-is-layout-flex\">\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\">\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2026\/08\/screen_lightblue_start-473x1024.webp\"><img loading=\"lazy\" decoding=\"async\" width=\"473\" height=\"1024\" src=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2026\/08\/screen_lightblue_start-473x1024.webp\" alt=\"\" class=\"wp-image-26418\" srcset=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2026\/08\/screen_lightblue_start-473x1024.webp 473w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2026\/08\/screen_lightblue_start-139x300.webp 139w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2026\/08\/screen_lightblue_start.webp 500w\" sizes=\"auto, (max-width: 473px) 100vw, 473px\" \/><\/a><figcaption class=\"wp-element-caption\">LightBlue &#8211; Home Screen<\/figcaption><\/figure>\n<\/div>\n\n\n\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\">\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2026\/08\/screen_lightblue_peripheral-473x1024.webp\"><img loading=\"lazy\" decoding=\"async\" width=\"473\" height=\"1024\" src=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2026\/08\/screen_lightblue_peripheral-473x1024.webp\" alt=\"\" class=\"wp-image-26419\" srcset=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2026\/08\/screen_lightblue_peripheral-473x1024.webp 473w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2026\/08\/screen_lightblue_peripheral-139x300.webp 139w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2026\/08\/screen_lightblue_peripheral.webp 500w\" sizes=\"auto, (max-width: 473px) 100vw, 473px\" \/><\/a><figcaption class=\"wp-element-caption\">Peripheral Screen<\/figcaption><\/figure>\n<\/div>\n\n\n\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\">\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2026\/08\/screen_lightblue_characteristic-473x1024.webp\"><img loading=\"lazy\" decoding=\"async\" width=\"473\" height=\"1024\" src=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2026\/08\/screen_lightblue_characteristic-473x1024.webp\" alt=\"\" class=\"wp-image-26420\" srcset=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2026\/08\/screen_lightblue_characteristic-473x1024.webp 473w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2026\/08\/screen_lightblue_characteristic-139x300.webp 139w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2026\/08\/screen_lightblue_characteristic.webp 500w\" sizes=\"auto, (max-width: 473px) 100vw, 473px\" \/><\/a><figcaption class=\"wp-element-caption\">Characteristics Screen<\/figcaption><\/figure>\n<\/div>\n<\/div>\n\n<p class=\"wp-block-paragraph\">And this was the output on the serial monitor after writing the characteristic and the peripheral logging out of the central unit:<\/p>\n\n<figure class=\"wp-block-image size-full\"><a href=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2026\/08\/output_arduinoble_minimal.png\"><img loading=\"lazy\" decoding=\"async\" width=\"549\" height=\"76\" src=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2026\/08\/output_arduinoble_minimal.png\" alt=\"\" class=\"wp-image-26365\" srcset=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2026\/08\/output_arduinoble_minimal.png 549w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2026\/08\/output_arduinoble_minimal-300x42.png 300w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2026\/08\/output_arduinoble_minimal-542x76.png 542w\" sizes=\"auto, (max-width: 549px) 100vw, 549px\" \/><\/a><figcaption class=\"wp-element-caption\">Output: ble_minimal.ino<\/figcaption><\/figure>\n\n<h3 class=\"wp-block-heading\">Explanations of the sketch<\/h3>\n<p>After including the ArduinoBLE library, use <code>BLEService myService(uuid);<\/code> to create the &#8220;myService&#8221; object and pass in the UUID (which you can choose freely). <code>BLEStringCharacteristic myCharacteristic(uuid, permissions);<\/code> creates the &#8220;myCharacteristic&#8221; characteristic object. Its UUID can also be chosen freely (provided it has not already been assigned elsewhere).  <\/p>\n<p>An object of the <strong>BLEStringCharacteristic <\/strong>class expects a string as its value. There is a separate class for all major data types, such as <strong>BLEFloatCharacteristic <\/strong>or <strong>BLEBooleanCharacteristic<\/strong>. You can find these classes (except for the String class) in the ArduinoBLE file <a href=\"https:\/\/github.com\/arduino-libraries\/ArduinoBLE\/blob\/master\/src\/BLETypedCharacteristics.h\" target=\"_blank\" rel=\"noopener\">BLETypedCharacteristics.h<\/a>.  <\/p>\n<p>In addition to the UUID, you pass your characteristics object its properties (permissions). The most important ones are:  <\/p>\n<ul>\n<li>BLERead: The characteristic value can be read.<\/li>\n<li>BLEWrite: The characteristic value may be written.<\/li>\n<li>BLEWriteWithoutResponse: Write without confirmation (required, for example, for Bluetooth LE Lab)<\/li>\n<li>BLENotify: The peripheral may notify the client device of changes to characteristic values. <\/li>\n<\/ul>\n<p>You can find a list of all properties in the &#8220;BLEproperty&#8221; enum in <a href=\"https:\/\/github.com\/arduino-libraries\/ArduinoBLE\/blob\/master\/src\/BLEProperty.h\" target=\"_blank\" rel=\"noopener\">BLEProperty.h<\/a>.  <\/p>\n<p>Use <code>BLE.begin()<\/code> to initialize your Arduino&#8217;s BLE functions. <\/p>\n<p>You can set the name used for advertising with <code>setLocalName()<\/code>; in other words, you will see this name in the list of available peripherals. It is worth noting that after disconnecting from the client, your peripheral may appear in the list under a different name. In my case, it was &#8220;Arduino.&#8221; &#8220;Arduino&#8221; was the default device name. If you want to change this behavior, change the device name using <code>setDeviceName()<\/code>. <\/p>\n\n<h5 class=\"wp-block-heading\">Further explanations in short:<\/h5>\n<ul>\n<li>Using <code data-start=\"431\" data-end=\"469\">BLE.setAdvertisedService(myService);<\/code>, the UUID from <code data-start=\"488\" data-end=\"499\">myService<\/code> is included in the advertising data.<\/li>\n<li>Using <code>myService.addCharacteristic(myCharacteristic);<\/code>, we add the characteristic <code>myCharacteristic<\/code> to the service <code>myService<\/code>.<\/li>\n<li><code>BLE.addService(myService);<\/code> adds the service to the local GATT server.<\/li>\n<li><code>myCharacteristic.writeValue()<\/code> updates the value of the characteristic.<\/li>\n<li><code>BLEDevice central = BLE.central();<\/code> creates a BLEDevice object named &#8220;central.&#8221; If a central is connected, this object represents the connected central. Otherwise, when queried with <code>if (central)<\/code>, it evaluates to false.  <\/li>\n<li><code>central.address();<\/code> returns the MAC address of the central. <\/li>\n<li>The function <code>central.connected()<\/code> checks whether the central is still connected. If not, we exit the while loop. Because <code>central<\/code> is recreated, the check <code>if (central)<\/code> returns &#8220;false&#8221; until a new connection is established.   <\/li>\n<li>Using <code>myCharacteristic.written();<\/code>, we check whether the central has assigned a new value to the characteristic.<\/li>\n<\/ul>\n\n<h2 id=\"event_handler\" class=\"wp-block-heading\">Event Handlers (Callbacks)<\/h2>\n<p>In the next example, we will use the write function to switch the Arduino board&#8217;s LED on and off. It is actually a simple task: a specific value of the characteristic means \u201cturn the LED on,\u201d while another means \u201cturn the LED off.\u201d However, constantly checking the value to see if it has changed is a bit impractical. It would be more elegant if these actions were performed automatically. And that is exactly what the event handler functions in the ArduinoBLE library are for. In principle, they correspond to the callback functions I introduced in my last post about BLE with the ESP32.     <\/p>\n<p>And since we are on the topic of event handler functions, let us use them to be notified when a central connects to our Arduino or when the connection is lost.<\/p>\n<p>Here is the sketch:<\/p>\n<\/p>\n<div class=\"scroll-paragraph\">\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-group=\"ble_switch_led.ino\" data-enlighter-title=\"ble_switch_led.ino\">#include &lt;ArduinoBLE.h&gt;\n\nBLEService ledService(\"19B10000-E8F2-537E-4F6C-D104768A1214\");\nBLEByteCharacteristic switchCharacteristic(\"19B10001-E8F2-537E-4F6C-D104768A1214\", BLERead | BLEWrite);\n\nconst int ledPin = LED_BUILTIN;\n\nvoid setup() {\n    Serial.begin(115200);\n    while (!Serial);\n\n    pinMode(ledPin, OUTPUT);\n\n    if (!BLE.begin()) {\n        Serial.println(\"Starting BLE module failed!\");\n\n        while (1);\n    }\n\n    BLE.setLocalName(\"LED_Switch\");\n    BLE.setDeviceName(\"LED_Switch\");\n    BLE.setAdvertisedService(ledService);\n\n    ledService.addCharacteristic(switchCharacteristic);\n\n    BLE.addService(ledService);\n\n    BLE.setEventHandler(BLEConnected, blePeripheralConnectHandler);\n    BLE.setEventHandler(BLEDisconnected, blePeripheralDisconnectHandler);\n\n    switchCharacteristic.setEventHandler(BLEWritten, switchCharacteristicWritten);\n\n    switchCharacteristic.setValue(0);\n\n    BLE.advertise();\n\n    Serial.println((\"BLE device active, waiting for connections...\"));\n}\n\nvoid loop() {\n    BLE.poll();\n}\n\nvoid blePeripheralConnectHandler(BLEDevice central) {\n    Serial.print(\"Connected event, central: \");\n    Serial.println(central.address());\n}\n\nvoid blePeripheralDisconnectHandler(BLEDevice central) {\n    Serial.print(\"Disconnected event, central: \");\n    Serial.println(central.address());\n}\n\nvoid switchCharacteristicWritten(BLEDevice central, BLECharacteristic characteristic) {\n    \/\/ unused parameters\n    (void)central;\n    (void)characteristic;\n\n    Serial.print(\"Characteristic event, written: \");\n\n    if (switchCharacteristic.value()) {\n        Serial.println(\"LED on\");\n        digitalWrite(ledPin, HIGH);\n    } else {\n        Serial.println(\"LED off\");\n        digitalWrite(ledPin, LOW);\n   }\n}\n<\/pre>\n<p>\u00a0<\/p>\n<\/div>\n<p>\n<p>If you connect your Arduino board to the central, set the characteristic to 1 (hexadecimal format) once, then to 0, and then disconnect, you should see output like the one shown below on the serial monitor. On the board, you will see the board LED turn on and then off again.  <\/p>\n\n<figure class=\"wp-block-image size-full\"><a href=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2026\/08\/output_arduinoble_led_switch.png\"><img loading=\"lazy\" decoding=\"async\" width=\"544\" height=\"96\" src=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2026\/08\/output_arduinoble_led_switch.png\" alt=\"\" class=\"wp-image-26370\" srcset=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2026\/08\/output_arduinoble_led_switch.png 544w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2026\/08\/output_arduinoble_led_switch-300x53.png 300w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2026\/08\/output_arduinoble_led_switch-542x96.png 542w\" sizes=\"auto, (max-width: 544px) 100vw, 544px\" \/><\/a><figcaption class=\"wp-element-caption\">Output of ble_switch_led.ino<\/figcaption><\/figure>\n\n<h3 class=\"wp-block-heading\">Explanations to the sketch<\/h3>\n<p>Much of the sketch is already familiar. To control the LED, we define the service <code>ledService<\/code> with the characteristic <code>switchCharacteristic<\/code>. What is new is that we are using the data type <code>byte<\/code> for the characteristic (<code>BLEByteCharacteristic<\/code>).  <\/p>\n<p>We define the event handlers using the function <code>setEventHandler()<\/code>. There is one version for the BLE object (<code>BLE.setEventHandler()<\/code>) and one for the characteristics (<code>switchCharacteristic.setEventHandler()<\/code>). You pass the event and the freely chosen name of the event handler function to the function. You are already familiar with this principle from interrupts.   <\/p>\n<p>The following events are defined for the characteristics:<\/p>\n<ul>\n<li><strong>BLESubscribed \/ BLEUnsubscribed<\/strong>: A central has subscribed to the characteristic or canceled the subscription.<\/li>\n<li><strong>BLERead, BLEWritten<\/strong> (same as: <strong>BLEUpdated<\/strong>): A central has read or written to the characteristic.<\/li>\n<\/ul>\n<p>The following events are defined for the BLE object:<\/p>\n<ul>\n<li><strong>BLEConnected \/ BLEDisconnected<\/strong>: A central unit or a peripheral unit has been connected or disconnected. <\/li>\n<li><strong>BLEDiscovered<\/strong>: The Arduino, acting as the central unit, detected a peripheral during scanning.<\/li>\n<\/ul>\n<p>The connected central is passed to the BLE event handlers. The event handler for the characteristic receives not only the central but also the triggering characteristic. Nevertheless, in the event handler function, we use <code>switchCharacteristic<\/code> and not the passed parameter <code>characteristic<\/code>. This is because the data type of the passed parameter is the BLECharacteristic base class and not <strong>BLEByteCharacteristic<\/strong>. The function <code>value()<\/code> of the base class (i.e., <code>BLECharacteristic::value()<\/code>) is defined as <code>const uint8_t* value() const;<\/code>. Its return value is therefore a pointer, which would complicate matters (at least for C++ beginners). On the central side, we will have to address this challenge later.       <\/p>\n<p>You could also omit the expressions <code>(void)characteristics;<\/code> and <code>(void)central;<\/code>. However, you might see a warning about &#8220;unused parameters.&#8221; Here, we are performing what is known as a &#8220;cast to void,&#8221; which essentially tells the compiler: I am intentionally not using these parameters.  <\/p>\n<p><code>BLE.poll()<\/code> checks and processes pending BLE events and, if necessary, calls the registered event handlers for them.<\/p>\n\n<h4 class=\"wp-block-heading\">Why do I have to poll here, but not with the ESP32?<\/h4>\n<p>So it seems the callbacks do not run quite as automatically as I had promised; otherwise, we would not have to poll. And anyone who has read my last post about BLE with the ESP32 might wonder why there was no need to use a counterpart to <code>BLE.poll()<\/code> there. The answer is: the ESP32 also \u201cpolls,\u201d but it does so automatically in the background using FreeRTOS.  <\/p>\n<p>The next question is: What happens if the Arduino is busy with other time-consuming tasks in <code>loop()<\/code> meanwhile? Does it miss something because of a delayed <code>BLE.poll()<\/code>? To test this, I inserted various delays into loop(). As a result, establishing the connection in LightBlue took approximately 14 times the delay time. It appears that there are multiple rounds negotiation, and each negotiation step is delayed by the delay time. Toggling the LED had a delay of up to a maximum of one delay time.     <\/p>\n<p>In my tests, switching commands sent in quick succession (period = &lt; delay time) were not lost but were processed individually at intervals of DelayInSeconds. So it is best to keep <code>loop()<\/code> as lean as possible.  <\/p>\n\n<h2 id=\"notify_descriptors\" class=\"wp-block-heading\">Notify and Descriptors<\/h2>\n<p>In the previous example, the central changed the characteristic value, and we used a callback function on the peripheral to be automatically notified of this change. In the following example, we will reverse the process. The peripheral changes the characteristic, and we want to be automatically notified of this on the central device. To achieve this, we use the &#8220;notify&#8221; characteristic property.   <\/p>\n<p>The event that triggers the update (write process) of the characteristic is pressing or releasing a button. And to make things a little more interesting, we are using two buttons. We will set up a characteristic for each button\u2014not because we have to or because it is practical, but simply to try working with two characteristics.  <\/p>\n<p>Since it is a bit inconvenient to pick the characteristics on the central device by their UUID, we will give them names: &#8220;Button 1 state&#8221; and &#8220;Button 2 state.&#8221; To set up these names, we use a descriptor. Just as there are characteristics predefined by the Bluetooth SIG, there are also predefined descriptors. The descriptor for naming characteristics is called \u201cCharacteristic User Description\u201d and has the UUID 0x2901.   <\/p>\n<p>To try out the following sketch, connect two pushbuttons with one side each to pins 6 and 7 on your Arduino board. Connect the other sides of both pushbuttons to GND. <\/p>\n<p>Here is the sketch:<\/p>\n<\/p>\n<div class=\"scroll-paragraph\">\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-group=\"ble_two_buttons.ino\" data-enlighter-title=\"ble_two_buttons.ino\">#include &lt;ArduinoBLE.h&gt;\n\n#define SERVICE_UUID \"7a3e0001-9b52-4b24-91c9-123456789abc\"\n#define BUTTON1_UUID \"7a3e0002-9b52-4b24-91c9-123456789abc\"\n#define BUTTON2_UUID \"7a3e0003-9b52-4b24-91c9-123456789abc\"\n\nconst int button1Pin = 6;\nconst int button2Pin = 7;\n\nBLEService buttonService(SERVICE_UUID);\n\nBLEStringCharacteristic button1Char(\n    BUTTON1_UUID, BLERead | BLENotify, 25);\n\nBLEStringCharacteristic button2Char(\n    BUTTON2_UUID, BLERead | BLENotify, 25);\n\nBLEDescriptor button1Desc(\"2901\", \"Button 1 state\");\nBLEDescriptor button2Desc(\"2901\", \"Button 2 state\");\n\nbool lastButton1 = HIGH;\nbool lastButton2 = HIGH;\n\nvoid setup() {\n    Serial.begin(115200);\n    while (!Serial);\n\n    pinMode(button1Pin, INPUT_PULLUP);\n    pinMode(button2Pin, INPUT_PULLUP);\n\n    if (!BLE.begin()) {\n        Serial.println(\"Starting BLE module failed!\");\n        while (1);\n    }\n\n    BLE.setLocalName(\"Arduino Two Buttons\");\n    BLE.setDeviceName(\"Arduino Two Buttons\");\n\n    BLE.setAdvertisedService(buttonService);\n\n    buttonService.addCharacteristic(button1Char);\n    buttonService.addCharacteristic(button2Char);\n\n    button1Char.addDescriptor(button1Desc);\n    button2Char.addDescriptor(button2Desc);\n\n    BLE.addService(buttonService);\n\n    button1Char.writeValue(\"Button 1 not yet pressed\");\n    button2Char.writeValue(\"Button 2 not yet pressed\");\n\n    BLE.advertise();\n\n    Serial.println(\"BLE device active, waiting for connections...\");\n}\n\nvoid loop() {\n    BLE.poll();\n\n    bool button1 = digitalRead(button1Pin);\n    bool button2 = digitalRead(button2Pin);\n\n    if (lastButton1 == HIGH &amp;&amp; button1 == LOW) {\n        button1Char.writeValue(\"Button 1 pressed\");\n\n        Serial.println(\"Button 1 pressed\");\n        delay(200); \/\/ debouncing\n    }\n    else if (lastButton1 == LOW &amp;&amp; button1 == HIGH) {\n        button1Char.writeValue(\"Button 1 released\");\n\n        Serial.println(\"Button 1 released\");\n        delay(200);\n    }\n\n    if (lastButton2 == HIGH &amp;&amp; button2 == LOW) {\n        button2Char.writeValue(\"Button 2 pressed\");\n\n        Serial.println(\"Button 2 pressed\");\n        delay(200);\n    }\n    else if (lastButton2 == LOW &amp;&amp; button2 == HIGH) {\n        button2Char.writeValue(\"Button 2 released\");\n\n        Serial.println(\"Button 2 released\");\n        delay(200);\n    }\n\n    lastButton1 = button1;\n    lastButton2 = button2;\n}<\/pre>\n<p>\u00a0<\/p>\n<\/div>\n<p>\n<p>If you open LightBlue or a similar app, you will see that the characteristics now have names (middle image below). Select one of the characteristics and change the format from HEX to UTF-8. Then tap &#8220;Subscribe&#8221;. When you press the corresponding button now, you will see an output like the one shown in the lower right. This means you no longer need to tap \u201cRead\u201d. <\/p>\n\n<div class=\"wp-block-columns are-vertically-aligned-bottom is-layout-flex wp-container-core-columns-is-layout-8f761849 wp-block-columns-is-layout-flex\">\n<div class=\"wp-block-column is-vertically-aligned-bottom is-layout-flow wp-block-column-is-layout-flow\">\n<figure class=\"wp-block-image size-full\"><a href=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2026\/08\/output_ble_two_buttons.png\"><img loading=\"lazy\" decoding=\"async\" width=\"206\" height=\"97\" src=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2026\/08\/output_ble_two_buttons.png\" alt=\"\" class=\"wp-image-26381\"\/><\/a><figcaption class=\"wp-element-caption\">Serial Monitor Output<\/figcaption><\/figure>\n<\/div>\n\n\n\n<div class=\"wp-block-column is-vertically-aligned-bottom is-layout-flow wp-block-column-is-layout-flow\">\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2026\/08\/ble_two_buttons_light_blue_I-1024x558.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"558\" src=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2026\/08\/ble_two_buttons_light_blue_I-1024x558.png\" alt=\"\" class=\"wp-image-26382\" srcset=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2026\/08\/ble_two_buttons_light_blue_I-1024x558.png 1024w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2026\/08\/ble_two_buttons_light_blue_I-300x163.png 300w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2026\/08\/ble_two_buttons_light_blue_I-768x418.png 768w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2026\/08\/ble_two_buttons_light_blue_I.png 1080w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><figcaption class=\"wp-element-caption\">LightBlue Edition<\/figcaption><\/figure>\n<\/div>\n\n\n\n<div class=\"wp-block-column is-vertically-aligned-bottom is-layout-flow wp-block-column-is-layout-flow\">\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2026\/08\/ble_two_buttons_light_blue_II-1024x768.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"768\" src=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2026\/08\/ble_two_buttons_light_blue_II-1024x768.png\" alt=\"\" class=\"wp-image-26383\" srcset=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2026\/08\/ble_two_buttons_light_blue_II-1024x768.png 1024w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2026\/08\/ble_two_buttons_light_blue_II-300x225.png 300w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2026\/08\/ble_two_buttons_light_blue_II-768x576.png 768w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2026\/08\/ble_two_buttons_light_blue_II.png 1080w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><figcaption class=\"wp-element-caption\">LightBlue Edition<\/figcaption><\/figure>\n<\/div>\n<\/div>\n\n<h3 class=\"wp-block-heading\">Explanations to the sketch<\/h3>\n<p>I will just explain the new aspects:<\/p>\n<ul>\n<li>Using <code>BLERead | BLENotify<\/code>, we assign the &#8220;Read&#8221; and &#8220;Notify&#8221; properties to the characteristics. <\/li>\n<li><code>BLEDescriptor button1Desc(\"2901\", \"Button 1 state\");<\/code> creates the descriptor object <code>button1Desc<\/code>.\n<ul>\n<li>The parameter &#8220;2901&#8221; specifies that this is the &#8220;Characteristic User Description&#8221; descriptor.<\/li>\n<li>The second parameter specifies the name of the characteristic. <\/li>\n<\/ul>\n<\/li>\n<li>The descriptor is assigned to the characteristic <code>button1Char<\/code> by <code>button1Char.addDescriptor(button1Desc);<\/code><\/li>\n<\/ul>\n<p>And that&#8217;s essentially it. In <code>loop()<\/code>, the button states are continuously checked and compared with the states from the previous iteration. If anything has changed, the value of the corresponding characteristic is updated.   <\/p>\n<p>Anyone who has read my last post about BLE with the ESP32 might wonder why we do not also need to assign the CCCD (Client Characteristic Configuration Descriptor) with the UUID 0x2902 to the characteristics in this sketch so that the client (central) can subscribe to the characteristics. With the ESP32-BLE library, &#8220;notify&#8221; alone was not enough. The simple answer is: The ArduinoBLE library is more convenient in this regard and handles this automatically.   <\/p>\n\n<h4 class=\"wp-block-heading\">Additional Notes on the Sketch<\/h4>\n<p>This sketch is for illustrative purposes only and is not optimized. To ensure that button presses in rapid succession are not lost, I would use interrupts. In addition, we could easily get by with a single characteristic covering buttons 1 and 2. However, I wanted to show how to set up two characteristics.   <\/p>\n\n<h2 id=\"two_arduinos\" class=\"wp-block-heading\">Connecting two Arduinos via BLE<\/h2>\n<p>For educational purposes, the client side has so far been a smartphone, PC, or laptop. Now we are taking the next step by having two Arduino boards communicate via BLE. A message entered into the serial monitor of the server Arduino (peripheral) should be displayed on the serial monitor of the client Arduino, and vice versa. Furthermore, the two Arduinos should automatically reconnect.   <\/p>\n\n<h3 id=\"server\" class=\"wp-block-heading\">The Server (Peripheral)<\/h3>\n<p>On the server side, there is nothing really new to discuss regarding BLE. However, we are combining some elements from previous examples into a single sketch. When the message is sent to the client (more specifically, when we change the value of the characteristic), we use &#8220;notify&#8221; so that the client does not have to constantly check for the value in <code>loop()<\/code>. To be notified of a message\u2019s arrival (= Write event) on the client side without having to constantly check \u201cmanually,\u201d we use the characteristic function <code>setEventHandler()<\/code> with the event <code>BLEWritten<\/code>.   <\/p>\n<p>We also use the function of our peripheral, <code>setEventHandler()<\/code>, with the parameters <code>BLEConnected<\/code> or <code>BLEDisconnected<\/code>, to keep track of the connection status. <\/p>\n<p>We use two characteristics for the messages we send and receive. RX_UUID is the UUID for receiving (R = receive), and TX_UUID is the UUID for sending (T = transmit).  <\/p>\n<\/p>\n<div class=\"scroll-paragraph\">\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-group=\"two_arduinos_peripheral.ino\" data-enlighter-title=\"two_arduinos_peripheral.ino\">#include &lt;ArduinoBLE.h&gt;\n\n#define SERVICE_UUID \"11111111-1111-1111-1111-111111111111\"\n#define RX_UUID      \"22222222-2222-2222-2222-222222222222\" \/\/ Central -&gt; Peripheral\n#define TX_UUID      \"33333333-3333-3333-3333-333333333333\" \/\/ Peripheral -&gt; Central\n\nBLEService textService(SERVICE_UUID);\n\nBLEStringCharacteristic rxCharacteristic(\n    RX_UUID, BLEWrite, 100);\n\nBLEStringCharacteristic txCharacteristic(\n    TX_UUID, BLERead | BLENotify, 100);\n\nvoid setup() {\n    Serial.begin(115200);\n    while (!Serial);\n\n    if (!BLE.begin()) {\n        Serial.println(\"Starting BLE failed!\");\n        while (1);\n    }\n\n    BLE.setLocalName(\"Arduino BLE Peripheral\");\n    BLE.setDeviceName(\"Arduino BLE Peripheral\");\n\n    BLE.setAdvertisedService(textService);\n\n    textService.addCharacteristic(rxCharacteristic);\n    textService.addCharacteristic(txCharacteristic);\n\n    BLE.addService(textService);\n\n    BLE.setEventHandler(BLEConnected, connectHandler);\n    BLE.setEventHandler(BLEDisconnected, disconnectHandler);\n\n    rxCharacteristic.setEventHandler(BLEWritten, rxWrittenHandler);\n\n    BLE.advertise();\n\n    Serial.println(\"Peripheral started\");\n}\n\nvoid loop() {\n    BLE.poll();\n\n    if (BLE.connected() &amp;&amp; Serial.available()) {\n        String text = Serial.readStringUntil('\\n');\n        \n        if (text.length() &gt; 0) {\n            txCharacteristic.writeValue(text);\n\n            Serial.print(\"Sent: \");\n            Serial.println(text);\n        }\n    }\n}\n\nvoid connectHandler(BLEDevice central) {\n    Serial.print(\"Connected to central: \");\n    Serial.println(central.address());\n\n    Serial.println(\"Ready. Type in some text and press enter.\");\n}\n\nvoid disconnectHandler(BLEDevice central) {\n    Serial.print(\"Disconnected from central: \");\n    Serial.println(central.address());\n}\n\nvoid rxWrittenHandler(BLEDevice central, BLECharacteristic characteristic) {\n    (void)central;\n    (void)characteristic;\n\n    Serial.print(\"Received: \");\n    Serial.println(rxCharacteristic.value());\n}<\/pre>\n<p>\u00a0<\/p>\n<\/div>\n<p>\n\n<h3 id=\"client\" class=\"wp-block-heading\">The Client (Central)<\/h3>\n<p>The client side is new and includes a whole range of new features. Until now, the software (like LightBlue) has handled scanning for available BLE devices. We had then selected the device from the list and established a connection. Now, we first need to &#8220;teach&#8221; the central Arduino how to do all of this. Since this involves quite a few new things all at once, I have added an intermediate step: a central sketch that, for now, simply scans and displays the scan results.     <\/p>\n\n<h4 id=\"client_scans\" class=\"wp-block-heading\">Intermediate Step: The Client scans<\/h4>\n<p>The following sketch is essentially &#8220;Scan.ino&#8221; from the ArduinoBLE library examples. I have just expanded it a little.  <\/p>\n<\/p>\n<div class=\"scroll-paragraph\">\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-group=\"arduino_ble_scan.ino\" data-enlighter-title=\"arduino_ble_scan.ino\">#include &lt;ArduinoBLE.h&gt;\n\n\/* scan for specific devices or UUIDs - adjust and uncomment *\/\n\/\/#define SPECIFIC_UUID \"11111111-1111-1111-1111-111111111111\"\n\/\/#define SPECIFIC_NAME \"Arduino BLE Peripheral\"\n\/\/#define SPECIFIC_ADDRESS \"3c:71:bf:95:da:ba\"\n\nvoid setup() {\n  Serial.begin(9600);\n  while (!Serial);\n\n  \/\/ begin initialization\n  if (!BLE.begin()) {\n    Serial.println(\"starting Bluetooth\u00ae Low Energy module failed!\");\n\n    while (1);\n  }\n\n  Serial.println(\"Bluetooth\u00ae Low Energy Central scan\");\n\n  \/\/ start scanning for peripheral\n  BLE.scan();\n  \/* scan for specific devices or UUIDs - adjust and uncomment*\/\n  \/\/BLE.scanForUuid(SPECIFIC_UUID);\n  \/\/BLE.scanForName(SPECIFIC_NAME);\n  \/\/BLE.scanForAddress(SPECIFIC_ADDRESS);\n}\n\nvoid loop() {\n  \/\/ check if a peripheral has been discovered\n  BLEDevice peripheral = BLE.available();\n\n  if (peripheral) {\n    \/\/ discovered a peripheral\n    Serial.println(\"Discovered a peripheral\");\n    Serial.println(\"-----------------------\");\n\n    \/\/ print address\n    Serial.print(\"Address: \");\n    Serial.println(peripheral.address());\n\n    \/\/ print the local name, if present\n    if (peripheral.hasLocalName()) {\n      Serial.print(\"Local Name: \");\n      Serial.println(peripheral.localName());\n    }\n\n    \/\/ print the advertised service UUIDs, if present\n    if (peripheral.hasAdvertisedServiceUuid()) {\n      Serial.print(\"Service UUIDs: \");\n      for (int i = 0; i &lt; peripheral.advertisedServiceUuidCount(); i++) {\n        Serial.print(peripheral.advertisedServiceUuid(i));\n        Serial.print(\" \");\n      }\n      Serial.println();\n    }\n\n    \/\/ print the RSSI\n    Serial.print(\"RSSI: \");\n    Serial.println(peripheral.rssi());\n\n    Serial.println();\n  }\n}<\/pre>\n<p>\u00a0<\/p>\n<\/div>\n<p>\n<p>Upload this sketch and the peripheral sketch to two Arduino boards. To make the scanner detect more than just the Arduino board, I also turned on my Bluetooth headphones. This was the output: <\/p>\n\n<figure class=\"wp-block-image size-full\"><a href=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2026\/08\/output_arduino_ble_scan.png\"><img loading=\"lazy\" decoding=\"async\" width=\"892\" height=\"239\" src=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2026\/08\/output_arduino_ble_scan.png\" alt=\"\" class=\"wp-image-26392\" srcset=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2026\/08\/output_arduino_ble_scan.png 892w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2026\/08\/output_arduino_ble_scan-300x80.png 300w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2026\/08\/output_arduino_ble_scan-768x206.png 768w\" sizes=\"auto, (max-width: 892px) 100vw, 892px\" \/><\/a><figcaption class=\"wp-element-caption\">Output from arduino_ble_scan.ino<\/figcaption><\/figure>\n<p>The scan is started using <code>BLE.scan()<\/code> in the setup. It is important to note the following: <\/p>\n<ul>\n<li>The scan continues to run in the background until you actively stop it with <code>BLE.stopScan()<\/code>. That is why you can also use this sketch to find BLE devices that will not be available until later. <\/li>\n<li>Before connecting a peripheral, you must stop the scan. Since we are not connecting any devices in this sketch, we will not stop the scan. <\/li>\n<\/ul>\n<p>I find the function name <code>available()<\/code> in <code>BLEDevice peripheral = BLE.available();<\/code> to be poorly chosen, as it does not immediately reveal how the function works. A name like &#8220;nextDiscoveredDevice()&#8221; would be more appropriate, in my opinion. Essentially, <code>BLE.available()<\/code> means: &#8220;Return the next BLE device discovered during the scan that has not yet been returned by <code>available()<\/code>&#8220;.  <\/p>\n<p>If another BLE device is found, the expression <code>if (peripheral)<\/code> is interpreted as <code>true<\/code>, and the loop is executed. The remaining lines are actually self-explanatory: <\/p>\n<div>\n<ul>\n<li><code>peripheral.address()<\/code> returns the MAC address.<\/li>\n<li><code>peripheral.hasLocalName()<\/code> checks whether the BLE device has a name.<\/li>\n<li><code>peripheral.localName()<\/code> returns the name of the device.<\/li>\n<li><code>peripheral.hasAdvertisedServiceUuid())<\/code> checks whether the device offers &#8220;advertised&#8221; services.<\/li>\n<li><code>peripheral.advertisedServiceUuidCount()<\/code> will tell you the number of &#8220;advertised&#8221; services.<\/li>\n<li><code>peripheral.advertisedServiceUuid(i)<\/code> returns the UUID of the i-th service.<\/li>\n<li><code>peripheral.rssi()<\/code> returns the signal strength (Received Signal Strength Indicator) of the received BLE signal.<\/li>\n<\/ul>\n<div>If you are looking for a specific device, you can set a filter for the scan:<\/div>\n<\/div>\n<div>\n<div>\n<ul>\n<li><code>BLE.scanForUuid();<\/code> filters by UUID.<\/li>\n<li><code>BLE.scanForName();<\/code> searches for the device name (local name).<\/li>\n<li><code>BLE.scanForAddress();<\/code> scans for the MAC address.<\/li>\n<\/ul>\n<p>Comment out or uncomment the relevant lines and adjust them as needed to test the functions. <\/p>\n<\/div>\n<\/div>\n\n<h4 id=\"client_complete\" class=\"wp-block-heading\">The Complete Client Sketch<\/h4>\n<\/p>\n<div class=\"scroll-paragraph\">\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-group=\"two_arduinos_central.ino\" data-enlighter-title=\"two_arduinos_central.ino\">#include &lt;ArduinoBLE.h&gt;\n\n#define SERVICE_UUID \"11111111-1111-1111-1111-111111111111\"\n#define RX_UUID      \"22222222-2222-2222-2222-222222222222\" \/\/ Central -&gt; Peripheral\n#define TX_UUID      \"33333333-3333-3333-3333-333333333333\" \/\/ Peripheral -&gt; Central\n\nvoid setup() {\n    Serial.begin(115200);\n    while (!Serial);\n\n    if (!BLE.begin()) {\n        Serial.println(\"Starting BLE failed!\");\n        while (1);\n    }\n\n    Serial.println(\"Arduino BLE Central\");\n    Serial.println(\"Searching peripheral...\");\n\n    BLE.scanForUuid(SERVICE_UUID);\n}\n\nvoid loop() {\n    BLEDevice peripheral = BLE.available();\n\n    if (peripheral) {\n        Serial.print(\"Found peripheral: \");\n        Serial.println(peripheral.address());\n\n        BLE.stopScan();\n\n        communicateWithPeripheral(peripheral);\n\n        \/\/ communicateWithPeripheral() returns after disconnect\n        Serial.println(\"Searching peripheral again...\");\n        BLE.scanForUuid(SERVICE_UUID);\n    }\n}\n\nvoid communicateWithPeripheral(BLEDevice peripheral) {\n    Serial.println(\"Connecting...\");\n\n    if (!peripheral.connect()) {\n        Serial.println(\"Connection failed\");\n        return;\n    }\n\n    Serial.println(\"Connected to peripheral\");\n\n    if (!peripheral.discoverService(SERVICE_UUID)) {\n        Serial.println(\"Service discovery failed\");\n        peripheral.disconnect();\n        return;\n    }\n\n    BLECharacteristic rxCharacteristic =\n        peripheral.characteristic(RX_UUID);\n\n    BLECharacteristic txCharacteristic =\n        peripheral.characteristic(TX_UUID);\n\n    if (!rxCharacteristic || !txCharacteristic) {\n        Serial.println(\"Characteristic not found\");\n        peripheral.disconnect();\n        return;\n    }\n\n    if (!rxCharacteristic.canWrite()) {\n        Serial.println(\"RX characteristic is not writable\");\n        peripheral.disconnect();\n        return;\n    }\n\n    if (!txCharacteristic.canSubscribe()) {\n        Serial.println(\"TX characteristic is not subscribable\");\n        peripheral.disconnect();\n        return;\n    }\n\n    if (!txCharacteristic.subscribe()) {\n        Serial.println(\"Subscription failed\");\n        peripheral.disconnect();\n        return;\n    }\n\n    Serial.println(\"Subscribed to TX characteristic\");\n    Serial.println(\"Ready. Type in some text and press enter.\");\n\n    while (peripheral.connected()) {\n        BLE.poll();\n\n        \/\/ Text from peripheral received?\n        if (txCharacteristic.valueUpdated()) {\n            printReceivedText(txCharacteristic);\n        }\n\n        \/\/ Text entered in Serial Monitor?\n        if (Serial.available()) {\n            String text = Serial.readStringUntil('\\n');\n\n            if (text.length() &gt; 0) {\n                sendText(rxCharacteristic, text);\n\n                Serial.print(\"Sent: \");\n                Serial.println(text);\n            }\n        }\n    }\n\n    Serial.print(\"Disconnected from peripheral: \");\n    Serial.println(peripheral.address());\n}\n\n\nvoid printReceivedText(BLECharacteristic characteristic) {\n    char text[101];\n\n    int length = characteristic.readValue(text, 100);\n\n    text[length] = '\\0'; \/\/ add null terminator\n\n    Serial.print(\"Received: \");\n    Serial.println(text);\n}\n\n\nvoid sendText(BLECharacteristic characteristic, const String &amp;text) {\n    characteristic.writeValue(\n        text.c_str(),\n        text.length()\n    );\n}<\/pre>\n<p>\u00a0<\/p>\n<\/div>\n<p>\n<p>Upload the sketches to two BLE-enabled Arduinos and open the corresponding serial monitors. The Arduinos should connect automatically. Now you can enter messages on either side, and they will be displayed on the other side. Here is an example of the output:   <\/p>\n\n<div class=\"wp-block-columns are-vertically-aligned-bottom is-layout-flex wp-container-core-columns-is-layout-8f761849 wp-block-columns-is-layout-flex\">\n<div class=\"wp-block-column is-vertically-aligned-bottom is-layout-flow wp-block-column-is-layout-flow\">\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"349\" height=\"152\" src=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2026\/08\/output_two_arduinos_peripheral.png\" alt=\"\" class=\"wp-image-26395\" srcset=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2026\/08\/output_two_arduinos_peripheral.png 349w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2026\/08\/output_two_arduinos_peripheral-300x131.png 300w\" sizes=\"auto, (max-width: 349px) 100vw, 349px\" \/><figcaption class=\"wp-element-caption\">Output Peripheral<\/figcaption><\/figure>\n<\/div>\n\n\n\n<div class=\"wp-block-column is-vertically-aligned-bottom is-layout-flow wp-block-column-is-layout-flow\">\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"349\" height=\"168\" src=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2026\/08\/output_two_arduinos_central.png\" alt=\"\" class=\"wp-image-26396\" srcset=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2026\/08\/output_two_arduinos_central.png 349w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2026\/08\/output_two_arduinos_central-300x144.png 300w\" sizes=\"auto, (max-width: 349px) 100vw, 349px\" \/><figcaption class=\"wp-element-caption\">Central Edition<\/figcaption><\/figure>\n<\/div>\n<\/div>\n\n<h4 class=\"wp-block-heading\">Explanations to the sketch<\/h4>\n<p>First, just as we did on the peripheral side, we define a service UUID and the characteristic UUIDs for receiving and sending messages (from the peripheral&#8217;s perspective!).<\/p>\n<p>In <code>setup()<\/code>, we initialize the BLE function and start the scan, specifically searching for the service UUID. In <code>loop()<\/code>, we check whether the peripheral with the desired service UUID was found. If so, the scan is stopped, and we initiate communication with the peripheral in <code>communicateWithPeripheral()<\/code>, passing the found peripheral to the function. If the connection is lost, the scan restarts.   <\/p>\n<p>In <code>communicateWithPeripheral()<\/code>, the connection is first established using <code>peripheral.connect()<\/code>. If that fails, the sketch goes back with <code>return<\/code>. <\/p>\n<div>\n<div><code>peripheral.discoverService(SERVICE_UUID)<\/code> is used to &#8220;discover&#8221; the characteristics of the service. This is essential. With \u2062<code data-start=\"36\" data-end=\"72\">peripheral.characteristic(RX_UUID)<\/code>, we access the characteristic with the specified UUID that was previously discovered on the peripheral. The returned object <code data-start=\"188\" data-end=\"207\">BLECharacteristic<\/code> serves as a local representation of this remote characteristic. The same process applies to the TX characteristic.    <\/div>\n<\/div>\n<div> <\/div>\n<div>Here are a few non-essential security prompts that you could skip:<\/div>\n<div>\n<ul>\n<li><code>if (!rxCharacteristic || !txCharacteristic)<\/code> checks whether the characteristics actually exist.<\/li>\n<li><code>rxCharacteristic.canWrite()<\/code> determines whether the RX characteristic can be written to.<\/li>\n<li><code>txCharacteristic.canSubscribe()<\/code> tells you whether the TX characteristic is available for subscription.<\/li>\n<\/ul>\n<p>Then we subscribe to the TX-Characteristic using <code>txCharacteristic.subscribe()<\/code>. <\/p>\n<p>As long as the central is connected to the peripheral (<code>while (peripheral.connected())<\/code>), the following occurs in each loop iteration:<\/p>\n<ul>\n<li>&#8220;polling&#8221;: <code>BLE.poll()<\/code>,<\/li>\n<li>check for an updated TX characteristic: <code>txCharacteristic.valueUpdated()<\/code><\/li>\n<li>and, if necessary, any input to be sent to the serial monitor is processed: (<code>if (Serial.available())<\/code>).<\/li>\n<\/ul>\n<\/div>\n\n<h5 class=\"wp-block-heading\">Processing the Characteristics <\/h5>\n<p data-start=\"653\" data-end=\"982\">I would like to draw your attention once again to the functions <code>printReceivedText()<\/code> and <code>sendText()<\/code>. <\/p>\n<p class=\"PDq2pG_selectionAnchorContainer\" data-start=\"653\" data-end=\"982\">When processing the characteristics, it is important to know that on the central side it is the data type <code data-start=\"788\" data-end=\"807\">BLECharacteristic<\/code> and not <code data-start=\"834\" data-end=\"859\">BLEStringCharacteristic<\/code>. As explained above, <code data-start=\"904\" data-end=\"913\">value()<\/code> returns or a <code data-start=\"924\" data-end=\"943\">BLECharacteristic<\/code> a pointer of type <code data-start=\"965\" data-end=\"981\">const uint8_t*<\/code>. <\/p>\n<p data-start=\"987\" data-end=\"1108\">The function <code data-start=\"1000\" data-end=\"1013\">readValue()<\/code> is overloaded, meaning there are several versions with different arguments, for example:<\/p>\n<ul data-start=\"1113\" data-end=\"1328\">\n<li data-section-id=\"n7zhcz\" data-start=\"1113\" data-end=\"1160\"><code data-start=\"1115\" data-end=\"1160\">int readValue(uint8_t value[], int length);<\/code><\/li>\n<li data-section-id=\"1c2m9ny\" data-start=\"1163\" data-end=\"1206\"><code data-start=\"1165\" data-end=\"1206\">int readValue(void* value, int length);<\/code><\/li>\n<li data-section-id=\"1g9ubz3\" data-start=\"1209\" data-end=\"1326\"><code data-start=\"1211\" data-end=\"1241\">int readValue(xxxxx& value);<\/code> with <code data-start=\"1246\" data-end=\"1253\">xxxxx<\/code> = <code data-start=\"1256\" data-end=\"1265\">uint8_t<\/code>,  <code data-start=\"1267\" data-end=\"1275\">int8_t<\/code>,  <code data-start=\"1277\" data-end=\"1287\">uint16_t<\/code>,  <code data-start=\"1289\" data-end=\"1298\" data-is-only-node=\"\">int16_t<\/code>,  <code data-start=\"1300\" data-end=\"1310\">uint32_t<\/code> or <code data-start=\"1316\" data-end=\"1325\">int32_t<\/code>.<\/li>\n<\/ul>\n<p data-start=\"1331\" data-end=\"1591\">The second variant is particularly useful. It is what makes <code>readValue(text, 100)<\/code> work in the first place. A <code data-start=\"1380\" data-end=\"1387\">void*<\/code> is a generic pointer of no defined data type and can therefore point to memory locations of different data types. For example, you can pass the address of an <code data-start=\"1533\" data-end=\"1540\">float<\/code> variable directly to readValue(): <code data-start=\"1560\" data-end=\"1573\">readValue(&floatVariable, sizeof(floatVariable))<\/code>. In <a href=\"#appendix\">the appendix<\/a>, I will show you how to handle structures in this context.    <\/p>\n<p data-start=\"1596\" data-end=\"1980\">There are also various options for <code data-start=\"1605\" data-end=\"1619\">writeValue()<\/code>, including <code data-start=\"1661\" data-end=\"1735\">int writeValue(const void* value, int length, bool withResponse = true);<\/code>. Since <code data-start=\"1740\" data-end=\"1746\">text<\/code> was previously read as a string object and <code>writeValue()<\/code> does not accept these, we use <code data-start=\"1805\" data-end=\"1819\">text.c_str()<\/code>. This function returns a <code data-start=\"1850\" data-end=\"1863\">const char*<\/code> pointing to the null-terminated string of the object <code data-start=\"1905\" data-end=\"1913\">String<\/code>, which can then be passed to <code data-start=\"1943\" data-end=\"1957\">writeValue()<\/code>. All clear? For less experienced Arduino users, this might all be a bit confusing. If you would like to learn more about strings and character arrays, you can read <a href=\"https:\/\/wolles-elektronikkiste.de\/en\/character-arrays-vs-strings\" target=\"_blank\" rel=\"noopener\">my article on this topic<\/a>.     <\/p>\n\n<h4 id=\"alternative_client_sketch\" class=\"wp-block-heading\">Alternative client sketch, non-blocking<\/h4>\n<p>The client sketch has a certain drawback. If the client has other tasks to perform\u2014such as making an LED blink or continuously checking a button\u2019s state (on the client)\u2014where do I incorporate those? The sketch only returns to <code>loop()<\/code> if the connection is lost. On the other hand, if the connection is successful, the sketch remains at <code>while(peripheral.connected())<\/code>, but <em>only in that case<\/em>. If you look at the example sketches in the ArduinoBLE library\u2014<a href=\"https:\/\/github.com\/arduino-libraries\/ArduinoBLE\/blob\/master\/examples\/Central\/LedControl\/LedControl.ino\" target=\"_blank\" rel=\"noopener\">LEDControl.ino<\/a> or <a href=\"https:\/\/github.com\/arduino-libraries\/ArduinoBLE\/blob\/master\/examples\/Central\/SensorTagButton\/SensorTagButton.ino\" target=\"_blank\" rel=\"noopener\">SensorTagButton.ino<\/a>\u2014you will see that they are structured similarly and therefore have the same (potential!) problem.    <\/p>\n<p>The following sketch solves the problem by repeatedly returning to <code>loop()<\/code>, regardless of whether a connection exists or not. Since the sketch does not include any new functions, I will skip further explanations. <\/p>\n<\/p>\n<div class=\"scroll-paragraph\">\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-group=\"non_blocking_client_sketch.ino\" data-enlighter-title=\"non_blocking_client_sketch.ino\">#include &lt;ArduinoBLE.h&gt;\n\n#define SERVICE_UUID \"11111111-1111-1111-1111-111111111111\"\n#define RX_UUID      \"22222222-2222-2222-2222-222222222222\"\n#define TX_UUID      \"33333333-3333-3333-3333-333333333333\"\n\nBLEDevice peripheral;\nBLECharacteristic rxCharacteristic;\nBLECharacteristic txCharacteristic;\n\nbool connected = false;\nbool scanning = false;\n\nvoid setup() {\n    Serial.begin(115200);\n    while (!Serial);\n\n    if (!BLE.begin()) {\n        Serial.println(\"Starting BLE failed!\");\n        while (1);\n    }\n\n    startScan();\n}\n\nvoid loop() {\n    BLE.poll();\n\n    if (!connected) {\n        handleConnection();\n    }\n    else {\n        handleCommunication();\n    }\n    \n    \/\/ doSomethingElse();\n}\n\nvoid startScan() {\n    BLE.scanForUuid(SERVICE_UUID);\n    scanning = true;\n    Serial.println(\"Searching peripheral...\");\n}\n\nvoid handleConnection() {\n    if (!scanning) {\n        startScan();\n    }\n\n    peripheral = BLE.available();\n\n    if (!peripheral) {\n        return;\n    }\n\n    BLE.stopScan();\n    scanning = false;\n\n    Serial.println(\"Peripheral found\");\n    Serial.println(\"Connecting...\");\n\n    if (!peripheral.connect()) {\n        Serial.println(\"Connection failed\");\n        return;\n    }\n\n    if (!peripheral.discoverService(SERVICE_UUID)) {\n        Serial.println(\"Service discovery failed\");\n        peripheral.disconnect();\n        return;\n    }\n\n    rxCharacteristic = peripheral.characteristic(RX_UUID);\n    txCharacteristic = peripheral.characteristic(TX_UUID);\n\n    if (!rxCharacteristic || !txCharacteristic) {\n        Serial.println(\"Characteristic not found\");\n        peripheral.disconnect();\n        return;\n    }\n\n    if (!txCharacteristic.subscribe()) {\n        Serial.println(\"Subscription failed\");\n        peripheral.disconnect();\n        return;\n    }\n\n    connected = true;\n\n    Serial.println(\"Connected\");\n    Serial.println(\"Ready. Type in some text and press enter.\");\n}\n\nvoid handleCommunication() {\n    if (!peripheral.connected()) {\n        connected = false;\n\n        Serial.println(\"Disconnected from peripheral\");\n\n        startScan();\n        return;\n    }\n\n    if (txCharacteristic.valueUpdated()) {\n        printReceivedText(txCharacteristic);\n    }\n\n    if (Serial.available()) {\n        String text = Serial.readStringUntil('\\n');\n        text.trim();\n\n        if (text.length() &gt; 0) {\n            sendText(rxCharacteristic, text);\n\n            Serial.print(\"Sent: \");\n            Serial.println(text);\n        }\n    }\n}\n\nvoid printReceivedText(BLECharacteristic characteristic) {\n    char text[101];\n\n    int length = characteristic.readValue(text, 100);\n\n    text[length] = '\\0';\n\n    Serial.print(\"Received: \");\n    Serial.println(text);\n}\n\nvoid sendText(BLECharacteristic characteristic, String text) {\n    characteristic.writeValue(\n        text.c_str(),\n        text.length()\n    );\n}<\/pre>\n<p>\u00a0<\/p>\n<\/div>\n<p>\n\n<h2 id=\"appendix\" class=\"wp-block-heading\">Appendix \u2013 Sending Structures<\/h2>\n\n<p class=\"wp-block-paragraph\">To go into more detail, I would like to show you how to send structures\u2014or, more precisely, how to use structures as values for a characteristic. As an example, on the peripheral side, we have a weather station that measures temperature (float) and humidity (int) and reports whether it is currently raining (bool). The data is updated every 10 seconds and made available to the client via BLE. Since we are only focusing on the principle here and do not actually have a weather station on the peripheral, we will work with fixed weather data.   <\/p>\n\n<h3 class=\"wp-block-heading\">Weather Station &#8211; Peripheral Sketch<\/h3>\n\n<p class=\"wp-block-paragraph\">First, here is the peripheral sketch:<\/p>\n<\/p>\n<div class=\"scroll-paragraph\">\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-group=\"weather_peripheral.ino\" data-enlighter-title=\"weather_peripheral.ino\">#include &lt;ArduinoBLE.h&gt;\n\n#define SERVICE_UUID \"11111111-1111-1111-1111-111111111111\"\n#define WEATHER_UUID \"11111112-1111-1111-1111-111111111111\"\n\nstruct WeatherData {\n    float temperature;\n    int humidity;\n    bool raining;\n};\n\nWeatherData weatherData;\n\nBLEService weatherService(SERVICE_UUID);\n\nBLECharacteristic weatherCharacteristic(\n    WEATHER_UUID,\n    BLERead | BLENotify,\n    sizeof(WeatherData)\n);\n\nconst unsigned long updateInterval = 10000;\nunsigned long lastUpdate = 0;\n\n\nvoid setup() {\n    Serial.begin(115200);\n    while (!Serial);\n\n    if (!BLE.begin()) {\n        Serial.println(\"Starting BLE failed!\");\n        while (1);\n    }\n\n    BLE.setLocalName(\"Weather Station\");\n    BLE.setDeviceName(\"Weather Station\");\n\n    BLE.setAdvertisedService(weatherService);\n\n    weatherService.addCharacteristic(weatherCharacteristic);\n    BLE.addService(weatherService);\n\n    BLE.setEventHandler(BLEConnected, connectHandler);\n    BLE.setEventHandler(BLEDisconnected, disconnectHandler);\n\n    updateWeatherData();\n\n    BLE.advertise();\n\n    Serial.println(\"Weather Station Peripheral\");\n    Serial.println(\"Advertising...\");\n}\n\nvoid loop() {\n    BLE.poll();\n\n    if (millis() - lastUpdate &gt;= updateInterval) {\n        lastUpdate = millis();\n\n        updateWeatherData();\n\n        weatherCharacteristic.writeValue(\n            &amp;weatherData,\n            sizeof(weatherData)\n        );\n\n        Serial.println(\"Weather data updated\");\n    }\n}\n\nvoid updateWeatherData() {\n    weatherData.temperature = 21.7;\n    weatherData.humidity = 63;\n    weatherData.raining = false;\n}\n\nvoid connectHandler(BLEDevice central) {\n    Serial.print(\"Connected to central: \");\n    Serial.println(central.address());\n}\n\nvoid disconnectHandler(BLEDevice central) {\n    Serial.print(\"Disconnected from central: \");\n    Serial.println(central.address());\n}<\/pre>\n<p>\u00a0<\/p>\n<\/div>\n<p>\n\n<p class=\"wp-block-paragraph\">Since there is no BLEStructCharacteristic or similar, we use the general <code>BLECharacteristic<\/code> as the data type for our <code>weatherCharacteristic<\/code>. It is passed <code>WEATHER_UUID<\/code>, the properties <code>Read<\/code> and <code>Notify<\/code>, and the length. The weather data is combined in the structure <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">weatherData<\/code>. Otherwise, the sketch has no new elements that I need to discuss in detail.    <\/p>\n\n<h3 class=\"wp-block-heading\">Weather Station: Central-Sketch<\/h3>\n\n<p class=\"wp-block-paragraph\">And here is the sketch for the central:<\/p>\n<\/p>\n<div class=\"scroll-paragraph\">\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-group=\"weather_central.ino\" data-enlighter-title=\"weather_central.ino\">#include &lt;ArduinoBLE.h&gt;\n\n#define SERVICE_UUID \"11111111-1111-1111-1111-111111111111\"\n#define WEATHER_UUID \"11111112-1111-1111-1111-111111111111\"\n\nstruct WeatherData {\n    float temperature;\n    int humidity;\n    bool raining;\n};\n\nBLEDevice peripheral;\nBLECharacteristic weatherCharacteristic;\n\nbool connected = false;\nbool scanning = false;\n\nvoid setup() {\n    Serial.begin(115200);\n    while (!Serial);\n\n    if (!BLE.begin()) {\n        Serial.println(\"Starting BLE failed!\");\n        while (1);\n    }\n\n    Serial.println(\"Weather Station Central\");\n\n    startScan();\n}\n\nvoid loop() {\n    BLE.poll();\n\n    if (!connected) {\n        handleConnection();\n    }\n    else {\n        handleCommunication();\n    }\n\n    \/\/ add additional code here\n}\n\n\nvoid startScan() {\n    BLE.scanForUuid(SERVICE_UUID);\n    scanning = true;\n\n    Serial.println(\"Searching for Weather Station...\");\n}\n\nvoid handleConnection() {\n    if (!scanning) {\n        startScan();\n    }\n\n    peripheral = BLE.available();\n\n    if (!peripheral) {\n        return;\n    }\n\n    BLE.stopScan();\n    scanning = false;\n\n    Serial.print(\"Peripheral found: \");\n    Serial.println(peripheral.address());\n\n    if (!peripheral.connect()) {\n        Serial.println(\"Connection failed\");\n        return;\n    }\n\n    Serial.println(\"Connected\");\n\n    if (!peripheral.discoverService(SERVICE_UUID)) {\n        Serial.println(\"Service discovery failed\");\n        peripheral.disconnect();\n        return;\n    }\n\n    weatherCharacteristic =\n        peripheral.characteristic(WEATHER_UUID);\n\n    if (!weatherCharacteristic) {\n        Serial.println(\"Weather characteristic not found\");\n        peripheral.disconnect();\n        return;\n    }\n\n    if (!weatherCharacteristic.canSubscribe()) {\n        Serial.println(\"Weather characteristic cannot be subscribed\");\n        peripheral.disconnect();\n        return;\n    }\n\n    if (!weatherCharacteristic.subscribe()) {\n        Serial.println(\"Subscription failed\");\n        peripheral.disconnect();\n        return;\n    }\n\n    connected = true;\n\n    Serial.println(\"Subscribed to weather data\");\n\n    printWeatherData();\n}\n\nvoid handleCommunication() {\n    if (!peripheral.connected()) {\n        connected = false;\n\n        Serial.println(\"Disconnected from peripheral\");\n\n        startScan();\n        return;\n    }\n\n    if (weatherCharacteristic.valueUpdated()) {\n        printWeatherData();\n    }\n}\n\nvoid printWeatherData() {\n    WeatherData weatherData;\n\n    int length = weatherCharacteristic.readValue(\n        &amp;weatherData,\n        sizeof(weatherData)\n    );\n\n    if (length != sizeof(weatherData)) {\n        Serial.println(\"Invalid weather data\");\n        return;\n    }\n\n    Serial.print(\"Temperature: \");\n    Serial.print(weatherData.temperature, 1);\n    Serial.println(\" \u00b0C\");\n\n    Serial.print(\"Humidity: \");\n    Serial.print(weatherData.humidity);\n    Serial.println(\" %\");\n\n    Serial.print(\"Raining: \");\n    Serial.println(weatherData.raining ? \"yes\" : \"no\");\n\n    Serial.println();\n}<\/pre>\n<p>\u00a0<\/p>\n<\/div>\n<p>\n\n<p class=\"wp-block-paragraph\">There is not much new here either. The key point is how the weather data is read\u2014namely, via <code>readValue(<br\/>&amp;weatherData, sizeof(weatherData));<\/code>. Surprisingly simple, isn&#8217;t it?  <\/p>\n\n<p class=\"wp-block-paragraph\">Here are the outputs of the sketches:<\/p>\n\n<div class=\"wp-block-columns is-layout-flex wp-container-core-columns-is-layout-8f761849 wp-block-columns-is-layout-flex\">\n<div class=\"wp-block-column is-vertically-aligned-bottom is-layout-flow wp-block-column-is-layout-flow\">\n<figure class=\"wp-block-image size-full\"><a href=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2026\/08\/output_weather_peripheral.png\"><img loading=\"lazy\" decoding=\"async\" width=\"385\" height=\"209\" src=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2026\/08\/output_weather_peripheral.png\" alt=\"\" class=\"wp-image-26408\" srcset=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2026\/08\/output_weather_peripheral.png 385w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2026\/08\/output_weather_peripheral-300x163.png 300w\" sizes=\"auto, (max-width: 385px) 100vw, 385px\" \/><\/a><figcaption class=\"wp-element-caption\">Output Peripheral<\/figcaption><\/figure>\n<\/div>\n\n\n\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\">\n<figure class=\"wp-block-image size-full\"><a href=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2026\/08\/output_weather_central.png\"><img loading=\"lazy\" decoding=\"async\" width=\"323\" height=\"236\" src=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2026\/08\/output_weather_central.png\" alt=\"\" class=\"wp-image-26409\" srcset=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2026\/08\/output_weather_central.png 323w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2026\/08\/output_weather_central-300x219.png 300w\" sizes=\"auto, (max-width: 323px) 100vw, 323px\" \/><\/a><figcaption class=\"wp-element-caption\">Central Edition<\/figcaption><\/figure>\n<\/div>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>I will show you, step by step, how to use Bluetooth Low Energy on BLE-capable Arduino boards using the ArduinoBLE library. <\/p>\n","protected":false},"author":1,"featured_media":26446,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[568,548],"tags":[556,2956,2935,614,2936,2939,2937,1626,2958,2957,2943,2945,2949,2940,1627,2938,2946],"class_list":["post-26447","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-bluetooth-en","category-wireless","tag-arduino-en-2","tag-arduinoble","tag-ble","tag-bluetooth-en","tag-bluetooth-low-energy","tag-central","tag-characteristic","tag-client-en","tag-descriptors","tag-event-handler","tag-lightblue","tag-notify","tag-nrf-connect","tag-peripheral","tag-server-en","tag-service","tag-write-2"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v28.4 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Using ArduinoBLE &#8226; Wolles Elektronikkiste<\/title>\n<meta name=\"description\" content=\"I&#039;ll show you, step by step, how to use Bluetooth Low Energy on BLE-capable Arduino boards with the ArduinoBLE library.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/wolles-elektronikkiste.de\/en\/using-arduinoble\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Using ArduinoBLE &#8226; Wolles Elektronikkiste\" \/>\n<meta property=\"og:description\" content=\"I&#039;ll show you, step by step, how to use Bluetooth Low Energy on BLE-capable Arduino boards with the ArduinoBLE library.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/wolles-elektronikkiste.de\/en\/using-arduinoble\" \/>\n<meta property=\"og:site_name\" content=\"Wolles Elektronikkiste\" \/>\n<meta property=\"article:published_time\" content=\"2026-09-06T13:59:07+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2026-09-06T13:59:17+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2026\/08\/ardble_post_image.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"950\" \/>\n\t<meta property=\"og:image:height\" content=\"950\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Wolfgang Ewald\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Wolfgang Ewald\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"29 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en\\\/using-arduinoble#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en\\\/using-arduinoble\"},\"author\":{\"name\":\"Wolfgang Ewald\",\"@id\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en#\\\/schema\\\/person\\\/b774e4d64b4766889a2f7c6e5ec85b46\"},\"headline\":\"Using ArduinoBLE\",\"datePublished\":\"2026-09-06T13:59:07+00:00\",\"dateModified\":\"2026-09-06T13:59:17+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en\\\/using-arduinoble\"},\"wordCount\":3626,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en#\\\/schema\\\/person\\\/b774e4d64b4766889a2f7c6e5ec85b46\"},\"image\":{\"@id\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en\\\/using-arduinoble#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/wp-content\\\/uploads\\\/2026\\\/08\\\/ardble_post_image.jpg\",\"keywords\":[\"Arduino\",\"ArduinoBLE\",\"BLE\",\"Bluetooth\",\"Bluetooth Low Energy\",\"central\",\"characteristic\",\"client\",\"Descriptors\",\"event handler\",\"LightBlue\",\"notify\",\"nRF Connect\",\"peripheral\",\"server\",\"service\",\"write\"],\"articleSection\":[\"Bluetooth\",\"Wireless\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en\\\/using-arduinoble#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en\\\/using-arduinoble\",\"url\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en\\\/using-arduinoble\",\"name\":\"Using ArduinoBLE &#8226; Wolles Elektronikkiste\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en\\\/using-arduinoble#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en\\\/using-arduinoble#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/wp-content\\\/uploads\\\/2026\\\/08\\\/ardble_post_image.jpg\",\"datePublished\":\"2026-09-06T13:59:07+00:00\",\"dateModified\":\"2026-09-06T13:59:17+00:00\",\"description\":\"I'll show you, step by step, how to use Bluetooth Low Energy on BLE-capable Arduino boards with the ArduinoBLE library.\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en\\\/using-arduinoble#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en\\\/using-arduinoble\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en\\\/using-arduinoble#primaryimage\",\"url\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/wp-content\\\/uploads\\\/2026\\\/08\\\/ardble_post_image.jpg\",\"contentUrl\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/wp-content\\\/uploads\\\/2026\\\/08\\\/ardble_post_image.jpg\",\"width\":950,\"height\":950},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en\\\/using-arduinoble#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Startseite\",\"item\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Using ArduinoBLE\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en#website\",\"url\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en\",\"name\":\"Wolles Elektronikkiste\",\"description\":\"Die wunderbare Welt der Elektronik\",\"publisher\":{\"@id\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en#\\\/schema\\\/person\\\/b774e4d64b4766889a2f7c6e5ec85b46\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en#\\\/schema\\\/person\\\/b774e4d64b4766889a2f7c6e5ec85b46\",\"name\":\"Wolfgang Ewald\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/wp-content\\\/uploads\\\/2019\\\/03\\\/cropped-Logo-1.png\",\"url\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/wp-content\\\/uploads\\\/2019\\\/03\\\/cropped-Logo-1.png\",\"contentUrl\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/wp-content\\\/uploads\\\/2019\\\/03\\\/cropped-Logo-1.png\",\"width\":512,\"height\":512,\"caption\":\"Wolfgang Ewald\"},\"logo\":{\"@id\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/wp-content\\\/uploads\\\/2019\\\/03\\\/cropped-Logo-1.png\"}}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Using ArduinoBLE &#8226; Wolles Elektronikkiste","description":"I'll show you, step by step, how to use Bluetooth Low Energy on BLE-capable Arduino boards with the ArduinoBLE library.","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:\/\/wolles-elektronikkiste.de\/en\/using-arduinoble","og_locale":"en_US","og_type":"article","og_title":"Using ArduinoBLE &#8226; Wolles Elektronikkiste","og_description":"I'll show you, step by step, how to use Bluetooth Low Energy on BLE-capable Arduino boards with the ArduinoBLE library.","og_url":"https:\/\/wolles-elektronikkiste.de\/en\/using-arduinoble","og_site_name":"Wolles Elektronikkiste","article_published_time":"2026-09-06T13:59:07+00:00","article_modified_time":"2026-09-06T13:59:17+00:00","og_image":[{"width":950,"height":950,"url":"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2026\/08\/ardble_post_image.jpg","type":"image\/jpeg"}],"author":"Wolfgang Ewald","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Wolfgang Ewald","Est. reading time":"29 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/wolles-elektronikkiste.de\/en\/using-arduinoble#article","isPartOf":{"@id":"https:\/\/wolles-elektronikkiste.de\/en\/using-arduinoble"},"author":{"name":"Wolfgang Ewald","@id":"https:\/\/wolles-elektronikkiste.de\/en#\/schema\/person\/b774e4d64b4766889a2f7c6e5ec85b46"},"headline":"Using ArduinoBLE","datePublished":"2026-09-06T13:59:07+00:00","dateModified":"2026-09-06T13:59:17+00:00","mainEntityOfPage":{"@id":"https:\/\/wolles-elektronikkiste.de\/en\/using-arduinoble"},"wordCount":3626,"commentCount":0,"publisher":{"@id":"https:\/\/wolles-elektronikkiste.de\/en#\/schema\/person\/b774e4d64b4766889a2f7c6e5ec85b46"},"image":{"@id":"https:\/\/wolles-elektronikkiste.de\/en\/using-arduinoble#primaryimage"},"thumbnailUrl":"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2026\/08\/ardble_post_image.jpg","keywords":["Arduino","ArduinoBLE","BLE","Bluetooth","Bluetooth Low Energy","central","characteristic","client","Descriptors","event handler","LightBlue","notify","nRF Connect","peripheral","server","service","write"],"articleSection":["Bluetooth","Wireless"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/wolles-elektronikkiste.de\/en\/using-arduinoble#respond"]}]},{"@type":"WebPage","@id":"https:\/\/wolles-elektronikkiste.de\/en\/using-arduinoble","url":"https:\/\/wolles-elektronikkiste.de\/en\/using-arduinoble","name":"Using ArduinoBLE &#8226; Wolles Elektronikkiste","isPartOf":{"@id":"https:\/\/wolles-elektronikkiste.de\/en#website"},"primaryImageOfPage":{"@id":"https:\/\/wolles-elektronikkiste.de\/en\/using-arduinoble#primaryimage"},"image":{"@id":"https:\/\/wolles-elektronikkiste.de\/en\/using-arduinoble#primaryimage"},"thumbnailUrl":"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2026\/08\/ardble_post_image.jpg","datePublished":"2026-09-06T13:59:07+00:00","dateModified":"2026-09-06T13:59:17+00:00","description":"I'll show you, step by step, how to use Bluetooth Low Energy on BLE-capable Arduino boards with the ArduinoBLE library.","breadcrumb":{"@id":"https:\/\/wolles-elektronikkiste.de\/en\/using-arduinoble#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/wolles-elektronikkiste.de\/en\/using-arduinoble"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/wolles-elektronikkiste.de\/en\/using-arduinoble#primaryimage","url":"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2026\/08\/ardble_post_image.jpg","contentUrl":"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2026\/08\/ardble_post_image.jpg","width":950,"height":950},{"@type":"BreadcrumbList","@id":"https:\/\/wolles-elektronikkiste.de\/en\/using-arduinoble#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Startseite","item":"https:\/\/wolles-elektronikkiste.de\/en"},{"@type":"ListItem","position":2,"name":"Using ArduinoBLE"}]},{"@type":"WebSite","@id":"https:\/\/wolles-elektronikkiste.de\/en#website","url":"https:\/\/wolles-elektronikkiste.de\/en","name":"Wolles Elektronikkiste","description":"Die wunderbare Welt der Elektronik","publisher":{"@id":"https:\/\/wolles-elektronikkiste.de\/en#\/schema\/person\/b774e4d64b4766889a2f7c6e5ec85b46"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/wolles-elektronikkiste.de\/en?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":["Person","Organization"],"@id":"https:\/\/wolles-elektronikkiste.de\/en#\/schema\/person\/b774e4d64b4766889a2f7c6e5ec85b46","name":"Wolfgang Ewald","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2019\/03\/cropped-Logo-1.png","url":"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2019\/03\/cropped-Logo-1.png","contentUrl":"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2019\/03\/cropped-Logo-1.png","width":512,"height":512,"caption":"Wolfgang Ewald"},"logo":{"@id":"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2019\/03\/cropped-Logo-1.png"}}]}},"_links":{"self":[{"href":"https:\/\/wolles-elektronikkiste.de\/en\/wp-json\/wp\/v2\/posts\/26447","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/wolles-elektronikkiste.de\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/wolles-elektronikkiste.de\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/wolles-elektronikkiste.de\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/wolles-elektronikkiste.de\/en\/wp-json\/wp\/v2\/comments?post=26447"}],"version-history":[{"count":3,"href":"https:\/\/wolles-elektronikkiste.de\/en\/wp-json\/wp\/v2\/posts\/26447\/revisions"}],"predecessor-version":[{"id":26450,"href":"https:\/\/wolles-elektronikkiste.de\/en\/wp-json\/wp\/v2\/posts\/26447\/revisions\/26450"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/wolles-elektronikkiste.de\/en\/wp-json\/wp\/v2\/media\/26446"}],"wp:attachment":[{"href":"https:\/\/wolles-elektronikkiste.de\/en\/wp-json\/wp\/v2\/media?parent=26447"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/wolles-elektronikkiste.de\/en\/wp-json\/wp\/v2\/categories?post=26447"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/wolles-elektronikkiste.de\/en\/wp-json\/wp\/v2\/tags?post=26447"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}