ESP32 Bleutooth master

Post here to discuss any new features, components, chips, etc, that you would like to see in Flowcode.
Post Reply
MJU20
Posts: 341
http://meble-kuchenne.info.pl
Joined: Tue Dec 08, 2020 5:11 pm
Has thanked: 94 times
Been thanked: 68 times

Flowcode v10 ESP32 Bleutooth master

Post by MJU20 »

In https://flowcode.co.uk/forums/viewtopic ... 159#p13159 I've been wondering if there is a way to connect a Bluetooth slave device with an ESP32 in FC10.

Apparently there is only a Bluetooth slave component for the ESP32?
When searching if this is possible in Arduino, I came across this tutorial:
https://deepbluembedded.com/esp32-bluet ... t=cmp-true

So it seems that it is possible.

A Bluetooth master component would be welcome.

MJU20
Posts: 341
Joined: Tue Dec 08, 2020 5:11 pm
Has thanked: 94 times
Been thanked: 68 times

Re: ESP32 Bleutooth master

Post by MJU20 »

230 views en not 1 reply?
:oops:

Not even too tell me that it is a stupid idea??

WingNut
Posts: 266
Joined: Tue Jul 13, 2021 1:53 pm
Has thanked: 37 times
Been thanked: 29 times

Re: ESP32 Bleutooth master

Post by WingNut »

Yeah sorry. I viewed it 229 times but couldn't make up my mind if it was stupid idea or not

chipfryer27
Valued Contributor
Posts: 1574
Joined: Thu Dec 03, 2020 10:57 am
Has thanked: 356 times
Been thanked: 560 times

Re: ESP32 Bleutooth master

Post by chipfryer27 »

Hi

I "liked" your suggestion and see no reason not to ask for the capability.

Here's hoping :)

Regards

Steve-Matrix
Matrix Staff
Posts: 1476
Joined: Sat Dec 05, 2020 10:32 am
Has thanked: 207 times
Been thanked: 349 times

Re: ESP32 Bleutooth master

Post by Steve-Matrix »

MJU20 wrote:
Sat Oct 28, 2023 5:36 pm
230 views en not 1 reply?
:oops:

Not even too tell me that it is a stupid idea??
Sorry for not replying before. Yes - it's a really good suggestion and it is on our list to investigate.

MJU20
Posts: 341
Joined: Tue Dec 08, 2020 5:11 pm
Has thanked: 94 times
Been thanked: 68 times

Re: ESP32 Bleutooth master

Post by MJU20 »

Thanks all of you.

MJU20
Posts: 341
Joined: Tue Dec 08, 2020 5:11 pm
Has thanked: 94 times
Been thanked: 68 times

Re: ESP32 Bleutooth master

Post by MJU20 »

Bump

Steve-Matrix
Matrix Staff
Posts: 1476
Joined: Sat Dec 05, 2020 10:32 am
Has thanked: 207 times
Been thanked: 349 times

Re: ESP32 Bleutooth master

Post by Steve-Matrix »

No progress yet, but it is something we want to add.

MJU20
Posts: 341
Joined: Tue Dec 08, 2020 5:11 pm
Has thanked: 94 times
Been thanked: 68 times

Re: ESP32 Bleutooth master

Post by MJU20 »

Bump???

MJU20
Posts: 341
Joined: Tue Dec 08, 2020 5:11 pm
Has thanked: 94 times
Been thanked: 68 times

Re: ESP32 Bleutooth master

Post by MJU20 »

I've asked an AI agent of this is possible and this is ( a part) of the answer:


### Bluetooth Master on ESP32 in Flowcode (without external components)

The ESP32 indeed has powerful built-in Bluetooth capabilities (both Bluetooth Classic and Bluetooth Low Energy - BLE). However, in the context of **Flowcode's pre-built components**, the focus for Bluetooth Classic (SPP - Serial Port Profile) on the ESP32 is primarily on the **slave mode**.

What does this mean for master mode?

1. **Standard Component:** The built-in Bluetooth SPP component for the ESP32 in Flowcode is, as far as known, primarily designed to operate as a **Bluetooth slave**. This means the device can be discovered and accept connections from an external master device (like your phone or another Bluetooth master).
2. **Master Functionality:** Implementing **Bluetooth master functionality** (where the ESP32 actively scans for and connects to other Bluetooth slave devices) is more complex. This often requires deeper interaction with the ESP32's Bluetooth stack.
3. **Custom C Code:** In many cases, for more advanced or less common Bluetooth roles (like master mode for Bluetooth Classic on the ESP32), you'll need to use **custom C code** within Flowcode. Flowcode provides the ability to add 'C Code' blocks to your flowchart. With these, you can directly call functions from the ESP-IDF (Espressif IoT Development Framework) or other libraries that support Bluetooth master functionality. However, this requires knowledge of C programming and the specific Bluetooth APIs of the ESP32.
4. **BLE (Bluetooth Low Energy):** If you want to work with **Bluetooth Low Energy (BLE)**, the situation is a bit more flexible. Flowcode also has components for BLE, and in BLE, roles like 'Central' (which is comparable to master) and 'Peripheral' (comparable to slave) can be implemented. Depending on what exactly you want to do, BLE might be an option, but its implementation is also more specific than simple serial communication via SPP.

---

**In summary:** A direct "drag-and-drop" Flowcode component that automatically configures the ESP32 as a complete Bluetooth Classic master is **not standardly available** as it is for a slave configuration. For that functionality, you'll likely need to rely on adding **custom C code** to configure and operate the ESP32 as a master.

---

### C Code for ESP32 Bluetooth Classic (SPP) Master

Okay, implementing a Bluetooth Classic (SPP) master on an ESP32 is significantly more complex than a slave, and it requires in-depth interaction with the Bluetooth stack of the ESP-IDF (Espressif IoT Development Framework).

It's important to understand that I'll provide **conceptual C code blocks** here. These are based on the standard ESP-IDF Bluetooth APIs. Seamlessly integrating this code into a Flowcode project requires you to:
1. **Understand how Flowcode compiles and integrates C code blocks.**
2. **Handle the necessary events and callbacks correctly** within the Flowcode structure (e.g., by updating global variables in Flowcode from the C code).
3. **Have knowledge of the ESP-IDF Bluetooth APIs.**

I cannot provide a ready-to-use Flowcode project file, but I can give you the core C code that you could place in Flowcode's 'C Code' blocks.

---

**Important Notes Before We Begin:**

* **ESP-IDF Version:** The exact APIs may vary slightly between different versions of ESP-IDF. Ensure that the Flowcode version you're using is compatible with a reasonably recent ESP-IDF version.
* **Debugging:** Debugging Bluetooth code is challenging. Use many `ESP_LOGI` (or `printf`) statements in your C code to track the status.
* **Flowcode Variables:** If you want to exchange data between your C code and Flowcode's graphical elements, you'll need to define **global variables in Flowcode** and call/manipulate them from your C code (e.g., `FCV_MY_VARIABLE = 123;`).
* **Includes:** You'll need to include the correct header files. Flowcode often handles basic includes, but for Bluetooth, you'll need specific ones.

---

Here is a structured approach, divided into logical blocks. You would place this code in 'C Code' blocks at the appropriate points in your Flowcode flowchart (e.g., during initialization, on button presses for scan, etc.).

### Step 0: Required Includes and Defines (At the top of your project or in an 'Initialisation' C code block)

Code: Select all

```c
#include "esp_bt.h"
#include "esp_bt_main.h"
#include "esp_gap_bt_api.h"
#include "esp_bt_device.h"
#include "esp_spp_api.h" // For SPP (Serial Port Profile)
#include "esp_log.h"
#include "nvs_flash.h" // NVS (Non-Volatile Storage) for Bluetooth

// Define a TAG for logging
static const char *TAG = "BT_MASTER";

// Global variables to track status and found devices
// THESE MUST ALSO BE DEFINED AS GLOBAL VARIABLES IN FLOWCODE!
// Example: Flowcode variable "found_device_mac" of type "byte array" or "string"
// Example: Flowcode variable "connection_established" of type "byte" (boolean)
// Example: Flowcode variable "spp_handle" of type "int"

// MAC address of the device you want to connect to (example)
// Replace this with the MAC address of your target slave device
// uint8_t target_mac[6] = {0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC}; // This can be set via Flowcode
// or read from a Flowcode array/string variable.

// The current Bluetooth connection handle for SPP
static uint32_t spp_connect_handle = 0;
```
### Step 1: Bluetooth Initialization (in your main 'Initialisation' block)

This should happen early in your program.

Code: Select all

```c
// Initialize NVS - needed for Bluetooth
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
    ESP_ERROR_CHECK(nvs_flash_erase());
    ret = nvs_flash_init();
}
ESP_ERROR_CHECK(ret);

ESP_LOGI(TAG, "Initializing Bluedroid...");
ESP_ERROR_CHECK(esp_bt_controller_mem_release(ESP_BT_MODE_BLE)); // No BLE needed for Classic
esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
ret = esp_bt_controller_init(&bt_cfg);
if (ret) {
    ESP_LOGE(TAG, "%s initialize controller failed: %s", __func__, esp_err_to_name(ret));
    return; // Or handle error in Flowcode
}

ret = esp_bt_controller_enable(ESP_BT_MODE_CLASSIC_BT);
if (ret) {
    ESP_LOGE(TAG, "%s enable controller failed: %s", __func__, esp_err_to_name(ret));
    return;
}

ret = esp_bluedroid_init();
if (ret) {
    ESP_LOGE(TAG, "%s initialize bluedroid failed: %s", __func__, esp_err_to_name(ret));
    return;
}

ret = esp_bluedroid_enable();
if (ret) {
    ESP_LOGE(TAG, "%s enable bluedroid failed: %s", __func__, esp_err_to_name(ret));
    return;
}

ESP_LOGI(TAG, "Bluedroid initialized and enabled.");

// Register the GAP event handler (important for scanning)
ret = esp_bt_gap_register_callback(esp_bt_gap_cb); // This function needs to be defined later!
if (ret){
    ESP_LOGE(TAG, "%s gap register failed: %s", __func__, esp_err_to_name(ret));
    return;
}

// Register the SPP event handler
ret = esp_spp_register_callback(esp_spp_cb); // This function needs to be defined later!
if (ret){
    ESP_LOGE(TAG, "%s spp register failed: %s", __func__, esp_err_to_name(ret));
    return;
}

// Initialize SPP
ret = esp_spp_init(ESP_SPP_MODE_CB); // Callback mode
if (ret){
    ESP_LOGE(TAG, "%s spp init failed: %s", __func__, esp_err_to_name(ret));
    return;
}

// Optional: Set the device name
esp_bt_dev_set_device_name("ESP32_Master");
```
### Step 2: GAP Event Handler (Function that handles Bluetooth events)

This is a C function that you define separately and that will be called by the system when Bluetooth events occur (such as finding a device during scanning). Place this in a 'C Code' block with a function definition or at the top.

Code: Select all

```c
static void esp_bt_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *param)
{
    switch (event) {
    case ESP_BT_GAP_DISC_RES_EVT: // Device found during scanning
        ESP_LOGI(TAG, "Device found: %s", (char *)param->disc_res.bda);
        for (int i = 0; i < param->disc_res.num_prop; i++) {
            if (param->disc_res.prop[i].type == ESP_BT_GAP_DISC_PROP_BONDABLE_NAME) {
                // Show the name of the discovered device
                ESP_LOGI(TAG, "  Name: %s", (char *)param->disc_res.prop[i].val);
            }
            if (param->disc_res.prop[i].type == ESP_BT_GAP_DISC_PROP_BD_ADDR) {
                // Compare the found MAC address with your target MAC
                // YOU NEED TO ADD YOUR CONNECTION LOGIC HERE!
                // Example: If MAC address matches, stop scan and start connection
                // For example: if (memcmp(param->disc_res.bda, target_mac, 6) == 0) {
                //      ESP_LOGI(TAG, "Target device found! Stopping scan and connecting...");
                //      esp_bt_gap_cancel_discovery();
                //      esp_spp_connect(ESP_SPP_SEC_NONE, ESP_SPP_ROLE_MASTER, param->disc_res.bda);
                //      break; // Stop loop if you found your target
                // }

                // For demonstration, we just print the MAC address
                ESP_LOGI(TAG, "  MAC: %02X:%02X:%02X:%02X:%02X:%02X",
                         param->disc_res.bda[0], param->disc_res.bda[1],
                         param->disc_res.bda[2], param->disc_res.bda[3],
                         param->disc_res.bda[4], param->disc_res.bda[5]);

                // You can store the MAC of the found device in a Flowcode variable here
                // e.g. FCV_found_device_mac[0] = param->disc_res.bda[0]; etc.
            }
        }
        break;
    case ESP_BT_GAP_DISC_STATE_CHANGED_EVT: // Scan state changed
        if (param->disc_state_changed.state == ESP_BT_GAP_DISCOVERY_STOPPED) {
            ESP_LOGI(TAG, "Device discovery stopped.");
            // You can take actions here when the scan is complete
        } else if (param->disc_state_changed.state == ESP_BT_GAP_DISCOVERY_STARTED) {
            ESP_LOGI(TAG, "Device discovery started.");
        }
        break;
    case ESP_BT_GAP_AUTH_CMPL_EVT: // Authentication complete
        if (param->auth_cmpl.stat == ESP_BT_STATUS_SUCCESS) {
            ESP_LOGI(TAG, "authentication success: %s", param->auth_cmpl.device_name);
        } else {
            ESP_LOGE(TAG, "authentication failed, status:%d", param->auth_cmpl.stat);
        }
        break;
    default:
        ESP_LOGI(TAG, "GAP event: %d", event);
        break;
    }
}
```
### Step 3: SPP Event Handler (For SPP connections and data)

This is the function that handles SPP-specific events.

Code: Select all

```c
static void esp_spp_cb(esp_spp_cb_event_t event, esp_spp_cb_param_t *param)
{
    switch (event) {
    case ESP_SPP_INIT_EVT: // SPP is initialized
        ESP_LOGI(TAG, "ESP_SPP_INIT_EVT");
        break;
    case ESP_SPP_UNINIT_EVT:
        ESP_LOGI(TAG, "ESP_SPP_UNINIT_EVT");
        break;
    case ESP_SPP_OPEN_EVT:
        ESP_LOGI(TAG, "ESP_SPP_OPEN_EVT");
        // This event is triggered when an SPP connection is OPENED (or requested)
        break;
    case ESP_SPP_CLOSE_EVT:
        ESP_LOGI(TAG, "ESP_SPP_CLOSE_EVT");
        spp_connect_handle = 0; // Reset handle
        // Update Flowcode variable: FCV_connection_established = 0;
        break;
    case ESP_SPP_START_EVT: // SPP server started (less relevant for master, unless you also want to be a server)
        ESP_LOGI(TAG, "ESP_SPP_START_EVT");
        break;
    case ESP_SPP_CL_INIT_EVT: // Client initialization (when you try to connect)
        ESP_LOGI(TAG, "ESP_SPP_CL_INIT_EVT");
        break;
    case ESP_SPP_SRV_OPEN_EVT: // Server open (less relevant for master)
        ESP_LOGI(TAG, "ESP_SPP_SRV_OPEN_EVT");
        break;
    case ESP_SPP_DATA_IND_EVT: // Data received
        ESP_LOGI(TAG, "ESP_SPP_DATA_IND_EVT len=%d", param->data_ind.len);
        if (param->data_ind.len > 0) {
            // Process the received data here
            // param->data_ind.data contains the bytes
            // You can copy this to a Flowcode array variable
            // e.g. memcpy(FCV_received_data, param->data_ind.data, param->data_ind.len);
            // and then process the variable further in Flowcode.
            // Make sure FCV_received_data is large enough!
        }
        break;
    case ESP_SPP_CONG_EVT: // Congestion event (buffer full)
        ESP_LOGI(TAG, "ESP_SPP_CONG_EVT cong=%d", param->cong.cong);
        break;
    case ESP_SPP_WRITE_EVT: // Data sent
        ESP_LOGI(TAG, "ESP_SPP_WRITE_EVT status=%d len=%d", param->write.status, param->write.len);
        break;
    case ESP_SPP_DISCOVERY_COMP_EVT: // Service Discovery complete (important for master!)
        ESP_LOGI(TAG, "ESP_SPP_DISCOVERY_COMP_EVT status=%d scn_num=%d", param->disc_comp.status, param->disc_comp.scn_num);
        if (param->disc_comp.status == ESP_SPP_SUCCESS) {
            if (param->disc_comp.scn_num > 0) {
                // If services found, connect to the first SPP service
                ESP_LOGI(TAG, "SPP service found at SCN: %d. Connecting...", param->disc_comp.scn[0]);
                esp_spp_connect(ESP_SPP_SEC_NONE, ESP_SPP_ROLE_MASTER, param->disc_comp.bda);
            } else {
                ESP_LOGW(TAG, "No SPP service found on device.");
            }
        } else {
            ESP_LOGE(TAG, "SPP service discovery failed.");
        }
        break;
    case ESP_SPP_SRV_STOP_EVT:
        ESP_LOGI(TAG, "ESP_SPP_SRV_STOP_EVT");
        break;
    case ESP_SPP_VFS_REGISTER_EVT:
        ESP_LOGI(TAG, "ESP_SPP_VFS_REGISTER_EVT");
        break;
    case ESP_SPP_VFS_UNREGISTER_EVT:
        ESP_LOGI(TAG, "ESP_SPP_VFS_UNREGISTER_EVT");
        break;
    case ESP_SPP_WRITE_FINISHED_EVT: // Write operation finished
        ESP_LOGI(TAG, "ESP_SPP_WRITE_FINISHED_EVT");
        break;
    case ESP_SPP_CONNECT_EVT: // Connection has been established!
        if (param->connect.status == ESP_SPP_SUCCESS) {
            ESP_LOGI(TAG, "ESP_SPP_CONNECT_EVT success handle=%d", param->connect.handle);
            spp_connect_handle = param->connect.handle; // Store the connection handle
            // Update Flowcode variable: FCV_connection_established = 1;
            // You can now send data!
        } else {
            ESP_LOGE(TAG, "ESP_SPP_CONNECT_EVT failed, status=%d", param->connect.status);
            // Update Flowcode variable: FCV_connection_established = 0;
        }
        break;
    default:
        ESP_LOGI(TAG, "SPP event: %d", event);
        break;
    }
}
```
### Step 4: Functions for Scanning and Connecting (Trigger from Flowcode)

You can create Flowcode functions (or button events) that call these C code blocks.

**Function: `start_bluetooth_scan()` (C Code Block)**
This code starts the Bluetooth scanning process.

Code: Select all

```c
// Start a discovery/scan for 10 seconds
ESP_LOGI(TAG, "Starting Bluetooth device discovery...");
esp_bt_gap_start_discovery(ESP_BT_INQ_MODE_GENERAL_UNLIMITED_LIAC, 10); // 10 seconds
```
**Function: `connect_to_target_mac(uint8_t *mac_address)` (C Code Block)**
This function attempts to establish an SPP connection. Call this *after* you have found the MAC address of your target slave device via the scan.

Code: Select all

```c
// Ensure that 'mac_address' is the 6-byte array of the target MAC address.
// This must be passed from Flowcode, for example, via a C-function call with a Flowcode array.
// Or as a global C variable that Flowcode populates (e.g., FCV_target_mac).

ESP_LOGI(TAG, "Attempting to connect to %02X:%02X:%02X:%02X:%02X:%02X",
         mac_address[0], mac_address[1], mac_address[2],
         mac_address[3], mac_address[4], mac_address[5]);

// Step 1: Perform a service discovery on the target device
// This is necessary to find the Serial Port Profile (SPP) service.
// The esp_spp_cb handler will then establish the connection in ESP_SPP_DISCOVERY_COMP_EVT.
esp_spp_start_discovery(mac_address);
```
### Step 5: Sending Data (Trigger from Flowcode)

Use this function to send data once a connection is established (`spp_connect_handle != 0`).

Code: Select all

```c
// Sends a string (or byte array) via SPP
// char *data_to_send (this can be a Flowcode string or byte array)
// int data_len (the length of the data)

if (spp_connect_handle != 0) {
    ESP_LOGI(TAG, "Sending data: %s (len: %d)", data_to_send, data_len);
    esp_spp_write(spp_connect_handle, data_len, (uint8_t *)data_to_send);
} else {
    ESP_LOGW(TAG, "Not connected to SPP device. Cannot send data.");
}
```
---

### How to Integrate this into Flowcode:

1. **Global C Code Blocks:**
* Create a 'C Code' block (e.g., in your initialization loop) for **Step 0** and the **first part of Step 1** (NVS, controller, bluedroid init).
* Create separate 'C Code' blocks (or one large block) for the functions `esp_bt_gap_cb` and `esp_spp_cb`. These must be accessible as callbacks.

2. **Flowchart Logic:**
* Add a 'C Code' block to call `start_bluetooth_scan()`, for example, when you press a 'Scan' button.
* In the `esp_bt_gap_cb` callback, when you find your target device, you can set a Flowcode flag (variable). Your Flowcode program can then check this flag and call the `connect_to_target_mac()` function, passing the MAC address of the found device (stored in a Flowcode array).
* When `FCV_connection_established` is set to 1 in `esp_spp_cb`, your Flowcode knows you can start sending data. You can then call 'C Code' blocks with `esp_spp_write`.
* For received data, when `ESP_SPP_DATA_IND_EVT` is handled, copy the data to a Flowcode variable (`FCV_received_data`), and Flowcode can then continue processing the data.

---

This is a complex project and requires patience with debugging and experimentation. If you have a specific scenario in mind or encounter particular issues when implementing this in Flowcode, feel free to ask for further assistance! Good luck!

Post Reply