{"id":4777,"date":"2026-10-02T08:07:17","date_gmt":"2026-10-02T08:07:17","guid":{"rendered":"https:\/\/blog.embeddedexpert.io\/?p=4777"},"modified":"2026-10-02T08:07:19","modified_gmt":"2026-10-02T08:07:19","slug":"getting-started-with-ble-on-the-stm32wb55-part-3","status":"publish","type":"post","link":"https:\/\/blog.embeddedexpert.io\/?p=4777","title":{"rendered":"Getting Started with BLE on the STM32WB55: Part 3"},"content":{"rendered":"\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"559\" src=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/10\/image-1-1024x559.png\" alt=\"\" class=\"wp-image-4778\" srcset=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/10\/image-1-1024x559.png 1024w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/10\/image-1-300x164.png 300w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/10\/image-1-768x419.png 768w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/10\/image-1-1150x627.png 1150w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/10\/image-1-750x409.png 750w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/10\/image-1-400x218.png 400w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/10\/image-1-250x136.png 250w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/10\/image-1.png 1408w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>In Part 3 of our series, we will configure GATT Services and Characteristics on the STM32WB55 to establish two-way data communication with your smartphone. You\u2019ll learn how to handle read, write, and notification operations to seamlessly transmit real-time telemetry and process incoming mobile commands.<\/p>\n\n\n\n<p>In this guide, we shall cover the following:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Firmware development.<\/li>\n\n\n\n<li>Results.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">1. Firmware Development:<\/h2>\n\n\n\n<p><\/p>\n\n\n\n<p>In order to develop the BLE firmware, you need two source files, custom_stm.c and custom_app.c.<\/p>\n\n\n\n<p>Open custom_stm.c source file.<\/p>\n\n\n\n<p>In line 193 which states:<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">  else if (attribute_modified-&gt;Attr_Handle == (CustomContext.CustomMy_Rx_CharHdle + CHARACTERISTIC_VALUE_ATTRIBUTE_OFFSET))<\/pre><\/div>\n\n\n\n<p>In <\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\"> \/* USER CODE BEGIN CUSTOM_STM_Service_1_Char_2_ACI_GATT_ATTRIBUTE_MODIFIED_VSEVT_CODE *\/<\/pre><\/div>\n\n\n\n<p>Add the following:<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">Notification.Custom_Evt_Opcode = CUSTOM_STM_MY_RX_CHAR_WRITE_NO_RESP_EVT;\nNotification.DataTransfered.pPayload = attribute_modified-&gt;Attr_Data;\nNotification.DataTransfered.Length = attribute_modified-&gt;Attr_Data_Length;\nCustom_STM_App_Notification(&amp;Notification);<\/pre><\/div>\n\n\n\n<p>This code packages the data that the phone just wrote into a notification structure: it sets the event type to <code>CUSTOM_STM_MY_RX_CHAR_WRITE_NO_RESP_EVT<\/code>, stores a pointer to the received bytes in <code>pPayload<\/code>, and stores the byte count in <code>Length<\/code>. It then calls <code>Custom_STM_App_Notification()<\/code> to hand this structure to <code>custom_app.c<\/code>, where your RX handler (<code>CUSTOM_STM_MY_RX_CHAR_WRITE_NO_RESP_EVT<\/code> case) can copy the data into <code>rxBuffer<\/code> for your application to use. In short, these 4 lines are the bridge between the BLE stack&#8217;s low-level event and your high-level application code.<\/p>\n\n\n\n<p>Next, in custom_app.c source file:<\/p>\n\n\n\n<p>In user code begin PV, declare the following:<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">#define MAX_DATA_LENGTH 20\nstatic uint8_t  rxBuffer[MAX_DATA_LENGTH];\nstatic uint16_t rxDataLength = 0;\nstatic uint8_t  newDataReceived = 0;\nstatic uint8_t  notificationsEnabled = 0;<\/pre><\/div>\n\n\n\n<p>Next, in <\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">\/* USER CODE BEGIN PFP *\/<\/pre><\/div>\n\n\n\n<p>Declare the following:<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">uint8_t Send_Data_To_Phone(uint8_t *data, uint16_t length);<\/pre><\/div>\n\n\n\n<p>This function shall send the data to the phone.<\/p>\n\n\n\n<p>Next, in:<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">case CUSTOM_STM_MY_TX_CHAR_NOTIFY_ENABLED_EVT:<\/pre><\/div>\n\n\n\n<p>Set the notification enabled to 1:<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">notificationsEnabled = 1;<\/pre><\/div>\n\n\n\n<p>In<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">case CUSTOM_STM_MY_TX_CHAR_NOTIFY_DISABLED_EVT:<\/pre><\/div>\n\n\n\n<p>Set notification enabled to 0.<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">notificationsEnabled = 0;<\/pre><\/div>\n\n\n\n<p>Next, in <\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">case CUSTOM_STM_MY_RX_CHAR_WRITE_NO_RESP_EVT:<\/pre><\/div>\n\n\n\n<p>Add the following:<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">uint16_t receivedLength = pNotification-&gt;DataTransfered.Length;\nuint8_t *receivedData = pNotification-&gt;DataTransfered.pPayload;\n\n\/\/ Copy received data to our safe buffer\nmemcpy(rxBuffer, receivedData, receivedLength);\nrxDataLength = receivedLength;\n\n\/\/ Echo back the received data to confirm (optional debug print)\nAPP_DBG_MSG(&quot;&gt;&gt; Data received from phone! Length: %d bytes\\n&quot;, rxDataLength);\n\n\/\/ Example processing:\nif (rxDataLength &gt;= 1) {\n  switch (rxBuffer[0]) {\n    case 0x01:\n      APP_DBG_MSG(&quot;&gt;&gt; Command 01 received\\n&quot;);\n      \/\/ Do something\n      break;\n    case 0x02:\n      APP_DBG_MSG(&quot;&gt;&gt; Command 02 received\\n&quot;);\n      \/\/ Do something else\n      break;\n    default:\n      APP_DBG_MSG(&quot;&gt;&gt; Unknown command: 0x%02X\\n&quot;, rxBuffer[0]);\n      break;\n  }\n}<\/pre><\/div>\n\n\n\n<p><\/p>\n\n\n\n<p>Next, in user code begin FD:<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">uint8_t Send_Data_To_Phone(uint8_t *data, uint16_t length)\n{\n    if (!notificationsEnabled) return 0;\n    if (length &gt; MAX_DATA_LENGTH) length = MAX_DATA_LENGTH;\n    memcpy(UpdateCharData, data, length);\n    Custom_STM_App_Update_Char(CUSTOM_STM_MY_TX_CHAR, UpdateCharData);\n    return 1;\n}<\/pre><\/div>\n\n\n\n<p>Here&#8217;s what this function does, in three sentences:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>It first checks that the phone has actually enabled notifications on the TX characteristic (<code>notificationsEnabled<\/code>) and clamps the requested length to <code>MAX_DATA_LENGTH<\/code>, returning <code>0<\/code> early if notifications are off.<\/li>\n\n\n\n<li>It then copies your data into the shared buffer <code>UpdateCharData<\/code> \u2014 the same buffer the ST framework&#8217;s TX characteristic uses internally.<\/li>\n\n\n\n<li>Finally, it calls <code>Custom_STM_App_Update_Char()<\/code> with <code>CUSTOM_STM_MY_TX_CHAR<\/code>, which triggers the BLE stack to push that buffer to the phone as a Notification, and returns <code>1<\/code> to signal success.<\/li>\n<\/ol>\n\n\n\n<p>Next, declare the following function:<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">uint16_t Get_Data_From_Phone(uint8_t *buffer, uint16_t maxLength) {\n    uint16_t copyLength = (rxDataLength &lt; maxLength) ? rxDataLength : maxLength;\n    memcpy(buffer, rxBuffer, copyLength);\n    return copyLength;\n}<\/pre><\/div>\n\n\n\n<p>Here&#8217;s what this function does, in three sentences:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>It calculates how many bytes to copy by taking the smaller of two values \u2014 <code>rxDataLength<\/code> (how many bytes the phone last sent) and <code>maxLength<\/code> (the caller&#8217;s buffer size) \u2014 to prevent a buffer overflow.<\/li>\n\n\n\n<li>It then copies that many bytes from the internal <code>rxBuffer<\/code> (filled earlier by the RX event handler) into the caller-supplied <code>buffer<\/code>.<\/li>\n\n\n\n<li>Finally, it returns <code>copyLength<\/code>, telling the caller exactly how many valid bytes were written so the application knows how much data to process.<\/li>\n<\/ol>\n\n\n\n<p>Finally, simple test to be sent to the phone:<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">void Send_Test_Data(void) {\n    static uint8_t counter = 0;\n    uint8_t testData[4];\n\n    testData[0] = 0xAA;  \/\/ Header\n    testData[1] = counter++;\n    testData[2] = ~counter;\n    testData[3] = 0x55;  \/\/ Footer\n\n    Send_Data_To_Phone(testData, 4);\n    APP_DBG_MSG(&quot;&gt;&gt; Sent test data: counter = %d\\n&quot;, testData[1]);\n}<\/pre><\/div>\n\n\n\n<p>Next, open main.c source file:<\/p>\n\n\n\n<p>In user code begin code 0, declare the following:<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">extern uint8_t Send_Data_To_Phone(uint8_t *data, uint16_t length);\nextern void Send_Test_Data(void);\n\nuint32_t lastTestSendTime;<\/pre><\/div>\n\n\n\n<p>Next, in user  code begin 3 in while 1 loop:<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">if (HAL_GetTick() - lastTestSendTime &gt; 1000) {\n  \/\/ Check if we're connected (notificationsEnabled is in custom_app.c)\n  \/\/ We'll check by trying to send, or use a global flag\n  Send_Test_Data();\n  lastTestSendTime = HAL_GetTick();\n}<\/pre><\/div>\n\n\n\n<p>We shall send data periodically each 1 second.<\/p>\n\n\n\n<p>Thats all for the firmware.<\/p>\n\n\n\n<p>Save, build the project and run it as follows:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"34\" src=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/image-1-1024x34.png\" alt=\"\" class=\"wp-image-4349\" srcset=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/image-1-1024x34.png 1024w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/image-1-300x10.png 300w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/image-1-768x26.png 768w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/image-1-1536x51.png 1536w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/image-1-1150x38.png 1150w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/image-1-750x25.png 750w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/image-1-400x13.png 400w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/image-1-250x8.png 250w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/image-1.png 1986w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>You may download the project from our github repository from&nbsp;<a href=\"https:\/\/github.com\/hussamaldean\/Embedded-Expert-Post-Projects\/tree\/main\/Projects\/STM32_WB55\" target=\"_blank\" rel=\"noreferrer noopener\">here<\/a>.<\/p>\n\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">2. Results:<\/h2>\n\n\n\n<p>Start a debugging session.<\/p>\n\n\n\n<p>Add rxBuffer from custom_app.c source file to live expression.<\/p>\n\n\n\n<p>Using LightBlue, open the services that starts with 0x1235, send some string like Embedded:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"471\" height=\"1024\" src=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/10\/IMG_1120-471x1024.png\" alt=\"\" class=\"wp-image-4780\" srcset=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/10\/IMG_1120-471x1024.png 471w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/10\/IMG_1120-138x300.png 138w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/10\/IMG_1120-768x1669.png 768w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/10\/IMG_1120-707x1536.png 707w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/10\/IMG_1120-943x2048.png 943w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/10\/IMG_1120-1150x2499.png 1150w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/10\/IMG_1120-750x1630.png 750w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/10\/IMG_1120-400x869.png 400w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/10\/IMG_1120-250x543.png 250w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/10\/IMG_1120.png 1320w\" sizes=\"(max-width: 471px) 100vw, 471px\" \/><\/figure>\n\n\n\n<p>You will notice that rxBuffer is filled with &#8220;Embedded&#8221;<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"334\" src=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/10\/2026-10-02_10-26-49-1024x334.png\" alt=\"\" class=\"wp-image-4779\" srcset=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/10\/2026-10-02_10-26-49-1024x334.png 1024w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/10\/2026-10-02_10-26-49-300x98.png 300w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/10\/2026-10-02_10-26-49-768x250.png 768w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/10\/2026-10-02_10-26-49-1150x375.png 1150w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/10\/2026-10-02_10-26-49-750x244.png 750w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/10\/2026-10-02_10-26-49-400x130.png 400w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/10\/2026-10-02_10-26-49-250x81.png 250w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/10\/2026-10-02_10-26-49.png 1314w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>Next, open a service thats starts with 0x1234:<\/p>\n\n\n\n<p>Note that the value is being updated each 1 second as we set it in the while loop.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"471\" height=\"1024\" src=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/10\/IMG_1118-471x1024.png\" alt=\"\" class=\"wp-image-4781\" srcset=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/10\/IMG_1118-471x1024.png 471w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/10\/IMG_1118-138x300.png 138w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/10\/IMG_1118-768x1669.png 768w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/10\/IMG_1118-707x1536.png 707w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/10\/IMG_1118-943x2048.png 943w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/10\/IMG_1118-1150x2499.png 1150w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/10\/IMG_1118-750x1630.png 750w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/10\/IMG_1118-400x869.png 400w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/10\/IMG_1118-250x543.png 250w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/10\/IMG_1118.png 1320w\" sizes=\"(max-width: 471px) 100vw, 471px\" \/><\/figure>\n\n\n\n<p>Now, you have full functioning BLE firmware that will have the ability to send and receive data up to 20 bytes.<\/p>\n\n\n\n<p><\/p>\n\n\n\n<p>Happy coding \ud83d\ude09<\/p>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>In Part 3 of our series, we will configure GATT Services and Characteristics on the STM32WB55 to establish two-way data communication with your smartphone. You\u2019ll learn how to handle read, write, and notification operations to seamlessly transmit real-time telemetry and process incoming mobile commands. In this guide, we shall cover the following: 1. Firmware Development: [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[2,11,12],"tags":[],"class_list":["post-4777","post","type-post","status-publish","format-standard","hentry","category-embedded-systems","category-peripheral-drivers","category-stm32"],"_links":{"self":[{"href":"https:\/\/blog.embeddedexpert.io\/index.php?rest_route=\/wp\/v2\/posts\/4777"}],"collection":[{"href":"https:\/\/blog.embeddedexpert.io\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.embeddedexpert.io\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.embeddedexpert.io\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.embeddedexpert.io\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=4777"}],"version-history":[{"count":1,"href":"https:\/\/blog.embeddedexpert.io\/index.php?rest_route=\/wp\/v2\/posts\/4777\/revisions"}],"predecessor-version":[{"id":4782,"href":"https:\/\/blog.embeddedexpert.io\/index.php?rest_route=\/wp\/v2\/posts\/4777\/revisions\/4782"}],"wp:attachment":[{"href":"https:\/\/blog.embeddedexpert.io\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=4777"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.embeddedexpert.io\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=4777"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.embeddedexpert.io\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=4777"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}