BLE Connection Issue with Multiple Simultaneous Peripherals

I have a BLE system where multiple sensors operate as peripherals and send data to a BLE central. These sensors enter sleep mode after transmitting data and later wake up to send new information.

I have observed an issue when increasing the number of sensors connected to the same central. Specifically, when two sensors become available for connection simultaneously, communication starts to fail. This affects the system's stability and impacts the reliability of data transmission.

Currently, I establish connections using the UUID of the NUS service, while other BLE parameters, such as the device name, remain identical among the sensors.

Communication Flow:

  1. The central identifies a peripheral through advertising.

  2. The central establishes a connection with the peripheral.

  3. The central stops the scanning process.

  4. The peripheral and central exchange information.

  5. The central disconnects from the peripheral.

  6. The central restarts scanning while two peripherals are advertising simultaneously.

Observed Behavior:

  • If only one sensor is available for connection, the process works correctly.

  • If two sensors are available simultaneously, communication fails, and the central cannot manage connections properly.

  • When this issue occurs, communication starts timing out, there are unexpected disconnections, and intermittent failures appear, such as the following:

00> [01:58:53.731,842] <inf> BLE: scan_filter_match: Filters matched. Address: DE:62:6A:79:C5:A5 (random) connectable: 1
00> [01:58:54.357,177] <inf> BLE: connected: Connected: DE:62:6A:79:C5:A5 (random)
00> [01:58:54.461,456] <inf> BLE: exchange_func: MTU exchange done
00> [01:58:58.293,762] <inf> MAIN: ble_connected_state: Connection state timeout
00> [01:58:58.293,792] <inf> PROTO: log_protocol_infos: Num: 0 - Extra: 0 - Off: 0
00> [01:58:58.293,792] <inf> PROTO: log_protocol_infos: data: 00 00 00 00
00> [01:58:58.293,823] <inf> MAIN: set_state: [STATE_BLE_CONNECTED] -> [STATE_BLE_DISCONNECTION]
00> [01:58:58.393,920] <inf> MAIN: ble_disconnection_state: BLE Disconnection State
00> [01:58:58.393,951] <inf> MAIN: set_state: [STATE_BLE_DISCONNECTION] -> [STATE_BLE_DISCOVERY]
00> [01:58:58.494,049] <inf> MAIN: ble_discovery_state: BLE Discovery State
00> [01:58:58.610,198] <err> nus_c: bt_nus_handles_assign: Missing NUS TX characteristic.
00> [01:58:58.610,198] <err> nus_c: bt_nus_subscribe_receive: Subscribe failed (err -128)
00> [01:58:58.610,351] <inf> BLE: disconnected: Disconnected: DE:62:6A:79:C5:A5 (random) (reason 8)
00> [01:58:58.611,053] <inf> MAIN: ble_discovery_state: BLE Connected
00> [01:58:58.626,312] <inf> MAIN: set_state: [STATE_BLE_DISCOVERY] -> [STATE_BLE_CONNECTED]
00> [01:58:58.726,379] <inf> MAIN: ble_connected_state: START FLAG sent
00> [01:58:58.726,409] <err> BLE: bluetooth_central_send_data: Cannot send data, no BLE connection

Questions:

  1. Are there any known limitations in managing multiple sequential connections with peripherals that have similar parameters?

  2. Are there any recommendations to handle this scenario better? Any specific configurations in Zephyr or SoftDevice that could help?

  3. Any best practices to differentiate peripherals and avoid connection conflicts?

I appreciate any guidance that can help resolve this issue.

Best regards,

Parents
  • Hi Arthur, 

    We will need to take a look at how you handle multiple connections in your central code. 
    Please show your code and your project configuration. 

    Have you looked at this issue: 
    00> [01:58:58.610,198] <err> nus_c: bt_nus_handles_assign: Missing NUS TX characteristic.
    00> [01:58:58.610,198] <err> nus_c: bt_nus_subscribe_receive: Subscribe failed (err -128)


    Have you made sure you do service discovery correctly when connect to a new peripheral ? 

    I would suggest to get familiar with the nRF Sniffer. It's very useful for debugging.  

  • Thanks for your response.

    Currently, our central does not support multiple connections. It simply connects to the first peripheral that appears during the scanning process and performs the entire data exchange procedure with that device.

    We’ve only observed the issue when we have too many sensors operating at the same time — specifically when two or more peripherals are advertising simultaneously at the exact moment the central starts scanning. I’ve attached the central_ble file so you can take a look.

    /* main.c - Application main entry point */
    #include <errno.h>
    #include <zephyr/kernel.h>
    #include <zephyr/device.h>
    #include <zephyr/devicetree.h>
    #include <zephyr/sys/byteorder.h>
    #include <zephyr/bluetooth/bluetooth.h>
    #include <zephyr/bluetooth/hci.h>
    #include <zephyr/bluetooth/conn.h>
    #include <zephyr/bluetooth/uuid.h>
    #include <zephyr/bluetooth/gatt.h>
    #include <bluetooth/services/nus.h>
    #include <bluetooth/services/nus_client.h>
    #include <bluetooth/gatt_dm.h>
    #include <bluetooth/scan.h>
    #include <zephyr/settings/settings.h>
    #include <zephyr/logging/log.h>
    #include <zephyr/random/rand32.h>
    #include "bluetooth_central_app.h"
    #include "data_structures.h"
    #include "protocol.h"
    #include <zephyr/drivers/gpio.h>
    
    LOG_MODULE_REGISTER(BLE, CONFIG_LOG_DEFAULT_LEVEL);
    
    #ifdef CONFIG_USE_CODED_PHY
    #pragma message "Build with LongRange"
    #else
    #pragma message "Build WITHOUT LongRange"
    #endif //CONFIG_USE_CODED_PHY
    
    /* Connection handle */
    static struct bt_conn *default_conn;
    
    /* NUS client handle */
    static struct bt_nus_client nus_client;
    
    /* Buffer to receive data - 492 bytes */
    static uint8_t packet_buffer[BLUETOOTH_PACKET_SIZE] = {0};
    
    /* Buffer size */
    static uint16_t packet_buffer_len = 0;
    
    /* Services discovery flag*/
    static bool services_discovery_completed = false;
    
    K_SEM_DEFINE(ble_connection_discovery_sem, 0, 1);
    
    static void ble_data_sent(struct bt_nus_client *nus, uint8_t err,
    						  const uint8_t *const data, uint16_t len)
    {
    	ARG_UNUSED(nus);
    	//LOG_INF("NUS sent data callback");
    }
    
    void bluetooth_start_scan (void)
    {
    	int ret = bt_scan_start(BT_SCAN_TYPE_SCAN_ACTIVE);
    	if (ret != SUCCESS) {
    		LOG_ERR("Scanning failed to start (ret %d)", ret);
    	}
    
    	LOG_INF("Start bt_scan");
    }
    
    static uint8_t ble_data_received(struct bt_nus_client *nus, const uint8_t *data, uint16_t len)
    {
    	ARG_UNUSED(nus);
    
    	// LOG_INF("Received data len: %d", len);
    
    	memset((void *)&packet_buffer, (int)0x00, (size_t)BLUETOOTH_PACKET_SIZE);
    	memcpy((void *)&packet_buffer, (const void *)data, (size_t)len);
    	packet_buffer_len = len;
    	if (protocol_parse_packet((const uint8_t *)&packet_buffer, packet_buffer_len) == SUCCESS) {
    		return BT_GATT_ITER_CONTINUE;
    	}
    	return BT_GATT_ITER_STOP;
    }
    
    static void discovery_complete(struct bt_gatt_dm *dm,
    							   void *context)
    {
    	struct bt_nus_client *nus = context;
    	//LOG_INF("Service discovery completed");
    	bt_gatt_dm_data_print(dm);
    	bt_nus_handles_assign(dm, nus);
    	bt_nus_subscribe_receive(nus);
    	bt_gatt_dm_data_release(dm);
    	k_sem_give(&ble_connection_discovery_sem);
    	services_discovery_completed = true;
    }
    
    static void discovery_service_not_found(struct bt_conn *conn, void *context)
    {
    	LOG_INF("Service not found");
    }
    
    static void discovery_error(struct bt_conn *conn, int err, void *context)
    {
    	LOG_ERR("Error while discovering GATT database: (%d)", err);
    }
    
    struct bt_gatt_dm_cb discovery_cb = {
    	.completed = discovery_complete,
    	.service_not_found = discovery_service_not_found,
    	.error_found = discovery_error,
    };
    
    static void gatt_discover(struct bt_conn *conn)
    {
    	int ret = SUCCESS;
    
    	if (conn != default_conn) {
    		return;
    	}
    
    	ret = bt_gatt_dm_start(conn, BT_UUID_NUS_SERVICE, &discovery_cb, &nus_client);
    	if (ret != SUCCESS) {
    		LOG_ERR("could not start the discovery procedure, error code: %d", ret);
    	}
    }
    
    static void exchange_func(struct bt_conn *conn, uint8_t err, struct bt_gatt_exchange_params *params)
    {
    	if (!err) {
    		LOG_INF("MTU exchange done");
    	} else {
    		LOG_ERR("MTU exchange failed (err %" PRIu8 ")", err);
    	}
    }
    
    static void connected(struct bt_conn *conn, uint8_t conn_err)
    {
    	char addr[BT_ADDR_LE_STR_LEN] = {0};
    	int ret = SUCCESS;
    
    	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
    
    	if (conn_err) {
    		LOG_ERR("Failed to connect to %s (%d)", addr, conn_err);
    
    		if (default_conn == conn) {
    			bt_conn_unref(default_conn);
    			default_conn = NULL;
    
    			bluetooth_start_scan();
    		}
    
    		return;
    	}
    
    	LOG_INF("Connected: %s", addr);
    
    	// Verificar MTU atual
    	uint16_t mtu = bt_gatt_get_mtu(conn);
    	//LOG_INF("Current MTU size: %u", mtu);
    
    	// Negociar MTU apenas se necessário
    	if (mtu == 23) { // Tamanho padrão significa que ainda não foi negociado
    		static struct bt_gatt_exchange_params exchange_params;
    		exchange_params.func = exchange_func;
    
    		ret = bt_gatt_exchange_mtu(conn, &exchange_params);
    		if (ret == 0) {
    			//LOG_INF("MTU exchange initiated");
    		} else if (ret == -EALREADY) {
    			LOG_WRN("MTU already exchanged");
    		} else {
    			LOG_ERR("MTU exchange failed (ret %d)", ret);
    		}
    	} else {
    		LOG_INF("MTU already negotiated, no need to exchange");
    	}
    
    	gatt_discover(conn);
    	// ret = bt_conn_set_security(conn, BT_SECURITY_L2);
    	// if (ret)
    	// {
    	// 	LOG_WRN("Failed to set security: %d", ret);
    	// 	gatt_discover(conn);
    	// }
    
    	ret = bt_scan_stop();
    	if ((!ret) && (ret != -EALREADY)) {
    		LOG_INF("Stop LE scan failed (ret %d)", ret);
    	}
    }
    
    static void disconnected(struct bt_conn *conn, uint8_t reason)
    {
    	char addr[BT_ADDR_LE_STR_LEN] = {0};
    
    	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
    
    	LOG_INF("Disconnected: %s (reason %u)", addr, reason);
    
    	if (default_conn != conn) {
    		return;
    	}
    
    	bt_conn_unref(default_conn);
    	default_conn = NULL;
    }
    
    int bluetooth_is_connected(void)
    {
    	if (default_conn == NULL){
    		return ERROR;
    	}
    
    	return SUCCESS;
    }
    
    void bluetooth_disconnect_ble_nus(void)
    {
    	if (default_conn) {
    		int ret = bt_conn_disconnect(default_conn, BT_HCI_ERR_LOCALHOST_TERM_CONN);
    		if (ret) {
    			LOG_ERR("Disconnection failed (err %d)", ret);
    		} else {
    			LOG_INF("Disconnection initiated");
    		}
    	} else {
    		LOG_INF("No active connection to disconnect");
    	}
    }
    
    static void security_changed(struct bt_conn *conn, bt_security_t level,
    							 enum bt_security_err err)
    {
    	char addr[BT_ADDR_LE_STR_LEN] = {0};
    
    	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
    
    	if (!err) {
    		LOG_INF("Security changed: %s level %u", addr, level);
    	}
    	else {
    		LOG_ERR("Security failed: %s level %u err %d", addr, level, err);
    	}
    
    	gatt_discover(conn);
    }
    
    BT_CONN_CB_DEFINE(conn_callbacks) = {
    	.connected = connected,
    	.disconnected = disconnected,
    	.security_changed = security_changed
    };
    
    static void scan_filter_match(struct bt_scan_device_info *device_info,
    							  struct bt_scan_filter_match *filter_match,
    							  bool connectable)
    {
    	char addr[BT_ADDR_LE_STR_LEN] = {0};
    	bt_addr_le_to_str(device_info->recv_info->addr, addr, sizeof(addr));
    	LOG_INF("Filters matched. Address: %s connectable: %d", addr, connectable);
    
    	// Se já houver uma conexão ativa, ignora novos anúncios
    	if (default_conn) {
    		LOG_INF("Já existe conexão ativa. Ignorando novo anúncio.");
    		return;
    	}
    
        #ifdef CONFIG_USE_CODED_PHY
    	int ret;
    	struct bt_conn_le_create_param *conn_params;
    
    	ret = bt_scan_stop();
    	if (ret) {
    		LOG_ERR("Stop LE scan failed (ret %d)", ret);
    	}
    	conn_params = BT_CONN_LE_CREATE_PARAM(
    		BT_CONN_LE_OPT_CODED | BT_CONN_LE_OPT_NO_1M,
    		BT_GAP_SCAN_FAST_INTERVAL,
    		BT_GAP_SCAN_FAST_INTERVAL);
    	ret = bt_conn_le_create(device_info->recv_info->addr, conn_params,
    							BT_LE_CONN_PARAM_DEFAULT,
    							&default_conn);
    	if (ret) {
    		LOG_ERR("Create conn failed (ret %d)", ret);
    
    		bluetooth_start_scan();
    	}
    	//LOG_INF("Connection pending");
    
        #endif //CONFIG_USE_CODED_PHY
    }
    
    static void scan_connecting_error(struct bt_scan_device_info *device_info)
    {
    	LOG_ERR("Connecting failed");
    }
    
    static void scan_connecting(struct bt_scan_device_info *device_info,
    							struct bt_conn *conn)
    {
    	default_conn = bt_conn_ref(conn);
    }
    
    static int nus_client_init(void)
    {
    	int ret = SUCCESS;
    	struct bt_nus_client_init_param init = {
    		.cb = {
    			.received = ble_data_received,
    			.sent = ble_data_sent,
    		}
    	};
    
    	ret = bt_nus_client_init(&nus_client, &init);
    	if (ret != SUCCESS) {
    		LOG_ERR("NUS Client initialization failed (ret %d)", ret);
    		return ret;
    	}
    
    	LOG_INF("NUS Client module initialized");
    	return ret;
    }
    
    BT_SCAN_CB_INIT(scan_cb, scan_filter_match, NULL,
    				scan_connecting_error, scan_connecting);
    
    static int scan_init(void)
    {
    	int ret = SUCCESS;
    
        #ifdef CONFIG_USE_CODED_PHY
    	/* Use active scanning and disable duplicate filtering to handle any
    	 * devices that might update their advertising data at runtime. */
    	struct bt_le_scan_param scan_param = {
    		.type     = BT_LE_SCAN_TYPE_ACTIVE,
    		.interval = BT_GAP_SCAN_FAST_INTERVAL,
    		.window   = BT_GAP_SCAN_FAST_WINDOW,
    		.options  = BT_LE_SCAN_OPT_CODED | BT_LE_SCAN_OPT_NO_1M
    	};
    
    	struct bt_scan_init_param scan_init = {
    		.connect_if_match = 0,
    		.scan_param = &scan_param,
    		.conn_param = NULL
    	};
    	#else
    	struct bt_scan_init_param scan_init = {
    		.connect_if_match = 1,
    	};
        #endif //CONFIG_USE_CODED_PHY
    
    	bt_scan_init(&scan_init);
    	bt_scan_cb_register(&scan_cb);
    
    	ret = bt_scan_filter_add(BT_SCAN_FILTER_TYPE_UUID, BT_UUID_NUS_SERVICE);
    	if (ret != SUCCESS) {
    		LOG_ERR("Scanning filters cannot be set (ret %d)", ret);
    		return ret;
    	}
    
    	ret = bt_scan_filter_enable(BT_SCAN_UUID_FILTER, false);
    	if (ret != SUCCESS) {
    		LOG_ERR("Filters cannot be turned on (ret %d)", ret);
    		return ret;
    	}
    
    	LOG_INF("Scan module initialized");
    	return ret;
    }
    
    static void auth_cancel(struct bt_conn *conn)
    {
    	char addr[BT_ADDR_LE_STR_LEN] = {0};
    	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
    	LOG_INF("Pairing cancelled: %s", addr);
    }
    
    static void pairing_complete(struct bt_conn *conn, bool bonded)
    {
    	char addr[BT_ADDR_LE_STR_LEN] = {0};
    	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
    	LOG_INF("Pairing completed: %s, bonded: %d", addr, bonded);
    }
    
    static void pairing_failed(struct bt_conn *conn, enum bt_security_err reason)
    {
    	char addr[BT_ADDR_LE_STR_LEN] = {0};
    	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
    	LOG_WRN("Pairing failed conn: %s, reason %d", addr, reason);
    }
    
    static struct bt_conn_auth_cb conn_auth_callbacks = {
    	.cancel = auth_cancel,
    };
    
    static struct bt_conn_auth_info_cb conn_auth_info_callbacks = {
    	.pairing_complete = pairing_complete,
    	.pairing_failed = pairing_failed
    };
    
    int bluetooth_central_init(void)
    {
    	int ret = SUCCESS;
    
    	// Enable pins of SKY66112-11
    	const struct device* gpio0_dev = device_get_binding("gpio@50000000"); // gpio 0
    
    	//	Table 6. SKY66112-11 Mode Control Logic1
    	//	Mode	Description 		CPS	CHL
    	//	1 	Receive LNA 		0 	X
    	//	2 	Transmit high-power 	0 	1
    
    	gpio_pin_configure(gpio0_dev, 6, GPIO_OUTPUT); // gpio 0.06 - CPS
    	gpio_pin_set(gpio0_dev, 6, 0);
    	gpio_pin_configure(gpio0_dev, 8, GPIO_OUTPUT); // gpio 0.08 - CHL
    	gpio_pin_set(gpio0_dev, 8, 1);
    
    	ret = bt_conn_auth_cb_register(&conn_auth_callbacks);
    	if (ret != SUCCESS) {
    		LOG_ERR("Failed to register authorization callbacks.");
    		return ERROR;
    	}
    
    	ret = bt_conn_auth_info_cb_register(&conn_auth_info_callbacks);
    	if (ret != SUCCESS) {
    		LOG_ERR("Failed to register authorization info callbacks.");
    		return ERROR;
    	}
    
    	ret = bt_enable(NULL);
    	if (ret != SUCCESS) {
    		LOG_ERR("Bluetooth init failed (ret %d)", ret);
    		return ERROR;
    	}
    	LOG_INF("Bluetooth initialized");
    
    	if (IS_ENABLED(CONFIG_SETTINGS)) {
    		settings_load();
    	}
    
    	ret = scan_init();
    	if (ret != SUCCESS) {
    		LOG_ERR("scan_init failed (ret %d)", ret);
    		return ERROR;
    	}
    
    	ret = nus_client_init();
    	if (ret != SUCCESS) {
    		LOG_ERR("nus_client_init failed (ret %d)", ret);
    		return ERROR;
    	}
    
    	ret = bt_scan_start(BT_SCAN_TYPE_SCAN_ACTIVE);
    	if (ret != SUCCESS) {
    		LOG_ERR("Scanning failed to start (ret %d)", ret);
    		return ERROR;
    	}
    
    	LOG_INF("Scanning successfully started");
    
    	return SUCCESS;
    }
    
    int bluetooth_central_send_data(const uint8_t *data, uint16_t len)
    {
    	int ret = SUCCESS;
    	if (!default_conn) {
    		LOG_ERR("Cannot send data, no BLE connection");
    		return -ENOTCONN;
    	}
    
    	ret = bt_nus_client_send(&nus_client, data, len);
    	if (ret != SUCCESS) {
    		LOG_ERR("BT NUS send ERROR: %d", ret);
    		return ERROR;
    	}
    	return ret;
    }
    
    bool bluetooth_service_discovery_completed(void)
    {
    	return services_discovery_completed;
    }
    

    In this case, is implementing multiple simultaneous connections strictly necessary to avoid this failure? If so, would you happen to have an example suited to this use case?

    Our idea is to keep the current approach: connect to the first sensor that appears, perform the data exchange, and disconnect. The other peripherals would only send a command or flag indicating they should wait or disconnect if not selected.

    Let me know your thoughts. Thanks again for the support!

    Best regards,

    Arthur

  • Hi Arthur, 

    Arthur J Sary said:
    At the moment, I don’t have access to a sniffer to capture and analyze the BLE packets, but I’ll work on setting that up in order to better investigate the disconnection behavior.

    You just need to have a NRF52 DK to use at the sniffer backend. Having the ability to sniffer the connection will help a lot on debugging BLE issue. Strongly recommend, it will save you a lot of time. 

    I had to ask about the discovery state because in the log I can see :

    00> [01:58:58.293,823] <inf> MAIN: set_state: [STATE_BLE_CONNECTED] -> [STATE_BLE_DISCONNECTION]
    00> [01:58:58.393,920] <inf> MAIN: ble_disconnection_state: BLE Disconnection State
    00> [01:58:58.393,951] <inf> MAIN: set_state: [STATE_BLE_DISCONNECTION] -> [STATE_BLE_DISCOVERY]
    00> [01:58:58.494,049] <inf> MAIN: ble_discovery_state: BLE Discovery State
    00> [01:58:58.610,198] <err> nus_c: bt_nus_handles_assign: Missing NUS TX characteristic.
    00> [01:58:58.610,198] <err> nus_c: bt_nus_subscribe_receive: Subscribe failed (err -128)

    So "Missing NUS TX characteristic" and "Subscribe failed (err -128)" come after the disconnection. If you just started the discovery and subscription right before the disconnection then it's normal. If you start it after the disconnection then something need to be changed. 

    But we would also need to investigate if these error also come when the connection has not been terminated. Then there must be something wrong with the peer device or the discovery process. 

    We need to break the issues down, separate the disconnection issue and the service discovery issue. 

    Are you saying that if you only do 1 peripheral you never seen a disconnection but if you  have 2 peripherals you start seeing disconnection ? 
    Please also show the connection parameter, for example the timeout and the connection interval. 

    Please be aware that the CODED PHY when it has longer range it also has longer packet size (x8) so there will be more chance of collision if you have a lot of interference. Please do the test with 1Mbps for comparison. 

  • Hi Hung,

    Thanks again for the detailed explanation and suggestions.

    I understand the concern regarding whether the discovery and subscription happen after the disconnection, and I agree that this would be a problem. However, based on the logs I sent earlier, it seems clear that the disconnection happens after the discovery/subscription attempt fails — not before.

    For example, in the snippet below, the timeline is quite clear:

    00> [01:43:24.476,470] <inf> MAIN: ble_disconnection_state: BLE Disconnection State
    00> [01:43:24.477,111] <inf> BLE: bluetooth_start_scan: Start bt_scan
    00> [01:43:24.477,142] <inf> MAIN: set_state: [STATE_BLE_DISCONNECTION] -> [STATE_BLE_DISCOVERY]
    00> [01:43:24.577,209] <inf> MAIN: ble_discovery_state: BLE Discovery State
    00> [01:43:24.627,105] <inf> BLE: scan_filter_match: Filters matched. Address: D3:D4:C0:F4:AC:94 (random) connectable: 1
    00> [01:43:24.735,504] <inf> BLE: connected: Connected: D3:D4:C0:F4:AC:94 (random)
    00> [01:43:24.839,813] <inf> BLE: exchange_func: MTU exchange done
    00> [01:43:28.988,525] <dbg> nus_c: bt_nus_handles_assign: Getting handles from NUS service.
    00> [01:43:28.988,555] <err> nus_c: bt_nus_handles_assign: Missing NUS TX characteristic.
    00> [01:43:28.988,555] <err> nus_c: bt_nus_subscribe_receive: Subscribe failed (err -128)
    00> [01:43:28.988,708] <inf> BLE: disconnected: Disconnected: D3:D4:C0:F4:AC:94 (random) (reason 8)
    

    We can see that the Missing NUS TX characteristic and the subscribe failed errors happen before the disconnection event is logged. So, the state transitions and discovery logic seem to be happening in the correct order.

    Regarding your other questions:

    Are you saying that if you only do 1 peripheral you never see a disconnection but if you have 2 peripherals you start seeing disconnection?

    No, the system works perfectly fine with up to 3 peripherals. The problem only starts to appear when I introduce a 4th peripheral, and all of them are communicating within the same 60-second window. This increases the central's BLE activity to almost 100% utilization, with very little idle scanning time between connections.

    As mentioned before, the peripherals advertise for 45 seconds, and if not connected, they go into deep sleep for 60 seconds. This pattern keeps the advertising period tight and overlapping when multiple sensors are active.

    If I switch to 1 Mbps PHY instead of CODED PHY, I need to increase the number of peripherals proportionally to reproduce the same failure — presumably because communication becomes faster and less congested, allowing more idle time between sessions.

    Regarding the connection parameters — I’m not entirely sure of the exact values being used, but they should be defined within the bluetooth.c files I previously attached.

    From what I found:

    For the peripheral, advertising is configured using:

    struct bt_le_adv_param param = 
        BT_LE_ADV_PARAM_INIT(
            BT_LE_ADV_OPT_CONNECTABLE |
            BT_LE_ADV_OPT_EXT_ADV |
            BT_LE_ADV_OPT_CODED,
            BT_GAP_ADV_FAST_INT_MIN_2,
            BT_GAP_ADV_FAST_INT_MAX_2,
            NULL);
    

    For the central, the scan and connection parameters are:

    struct bt_le_scan_param scan_param = {
        .type     = BT_LE_SCAN_TYPE_ACTIVE,
        .interval = BT_GAP_SCAN_FAST_INTERVAL,
        .window   = BT_GAP_SCAN_FAST_WINDOW,
        .options  = BT_LE_SCAN_OPT_CODED | BT_LE_SCAN_OPT_NO_1M
    };
    
    conn_params = BT_CONN_LE_CREATE_PARAM(
        BT_CONN_LE_OPT_CODED | BT_CONN_LE_OPT_NO_1M,
        BT_GAP_SCAN_FAST_INTERVAL,
        BT_GAP_SCAN_FAST_INTERVAL);
    

    So it looks like I'm using the default Zephyr macros for fast scanning and advertising intervals, but I haven’t explicitly overridden connection interval, latency, or supervision timeout. If needed, I can extract the resolved values during runtime using logging or update the config with fixed parameters for more control.

    Let me know if you'd recommend using specific values here to help avoid collisions or improve handling under high peripheral load.

    Let me know if you’d like me to extract a few more specific log points, or if you think it would be useful to experiment with slightly staggered advertising windows between the sensors.

    Thanks again for all the support!

    Best regards,

  • Hi Arthur, 
    Could you show your central's code ? 
    I'm interested to see the create_param you feeding to bt_conn_le_create() function if you do call that function. 
    My suggestion is to increase the connection interval (in the bt_conn_le_create_param create_param ).

    Unless your application has a requirement on the latency, the interval of around 100ms and above should be OK. 

    Regarding the problem with discovery the NUS service. The disconnection and the discovery failed came at almost the same time, so it's hard to tell which come first without the sniffer trace. 

    You can see the time the log printed out is only a few ns apart. So it's not certain which event come first. And which event is the result of the other event. But they seem to be related. 

  • Hi Hung,

    Thanks again for your help and suggestions.

    I'm attaching both the central and peripheral source files so you can review the actual implementation and parameter configurations.

    Regarding your question, yes — the central does use bt_conn_le_create(), and you'll find the bt_conn_le_create_param setup inside the central code.

    As for the NUS discovery failure, I understand your point about the log timestamps being too close to definitively determine the event order. Hopefully, once I set up the sniffer as discussed earlier, I’ll be able to trace that interaction more precisely.


    Central Bluetooth.c

    /* main.c - Application main entry point */
    #include <errno.h>
    #include <zephyr/kernel.h>
    #include <zephyr/device.h>
    #include <zephyr/devicetree.h>
    #include <zephyr/sys/byteorder.h>
    #include <zephyr/bluetooth/bluetooth.h>
    #include <zephyr/bluetooth/hci.h>
    #include <zephyr/bluetooth/conn.h>
    #include <zephyr/bluetooth/uuid.h>
    #include <zephyr/bluetooth/gatt.h>
    #include <bluetooth/services/nus.h>
    #include <bluetooth/services/nus_client.h>
    #include <bluetooth/gatt_dm.h>
    #include <bluetooth/scan.h>
    #include <zephyr/settings/settings.h>
    #include <zephyr/logging/log.h>
    #include <zephyr/random/rand32.h>
    #include "bluetooth_central_app.h"
    #include "data_structures.h"
    #include "protocol.h"
    #include <zephyr/drivers/gpio.h>
    
    LOG_MODULE_REGISTER(BLE, CONFIG_LOG_DEFAULT_LEVEL);
    
    #ifdef CONFIG_USE_CODED_PHY
    #pragma message "Build with LongRange"
    #else
    #pragma message "Build WITHOUT LongRange"
    #endif //CONFIG_USE_CODED_PHY
    
    /* Connection handle */
    static struct bt_conn *default_conn;
    
    /* NUS client handle */
    static struct bt_nus_client nus_client;
    
    /* Buffer to receive data - 492 bytes */
    static uint8_t packet_buffer[BLUETOOTH_PACKET_SIZE] = {0};
    
    /* Buffer size */
    static uint16_t packet_buffer_len = 0;
    
    /* Services discovery flag*/
    static bool services_discovery_completed = false;
    
    K_SEM_DEFINE(ble_connection_discovery_sem, 0, 1);
    
    static void ble_data_sent(struct bt_nus_client *nus, uint8_t err,
    						  const uint8_t *const data, uint16_t len)
    {
    	ARG_UNUSED(nus);
    	//LOG_INF("NUS sent data callback");
    }
    
    void bluetooth_start_scan (void)
    {
    	int ret = bt_scan_start(BT_SCAN_TYPE_SCAN_ACTIVE);
    	if (ret != SUCCESS) {
    		LOG_ERR("Scanning failed to start (ret %d)", ret);
    	}
    
    	LOG_INF("Start bt_scan");
    }
    
    static uint8_t ble_data_received(struct bt_nus_client *nus, const uint8_t *data, uint16_t len)
    {
    	ARG_UNUSED(nus);
    
    	// LOG_INF("Received data len: %d", len);
    
    	memset((void *)&packet_buffer, (int)0x00, (size_t)BLUETOOTH_PACKET_SIZE);
    	memcpy((void *)&packet_buffer, (const void *)data, (size_t)len);
    	packet_buffer_len = len;
    	if (protocol_parse_packet((const uint8_t *)&packet_buffer, packet_buffer_len) == SUCCESS) {
    		return BT_GATT_ITER_CONTINUE;
    	}
    	return BT_GATT_ITER_STOP;
    }
    
    static void discovery_complete(struct bt_gatt_dm *dm,
    							   void *context)
    {
    	struct bt_nus_client *nus = context;
    	//LOG_INF("Service discovery completed");
    	bt_gatt_dm_data_print(dm);
    	bt_nus_handles_assign(dm, nus);
    	bt_nus_subscribe_receive(nus);
    	bt_gatt_dm_data_release(dm);
    	k_sem_give(&ble_connection_discovery_sem);
    	services_discovery_completed = true;
    }
    
    static void discovery_service_not_found(struct bt_conn *conn, void *context)
    {
    	LOG_INF("Service not found");
    }
    
    static void discovery_error(struct bt_conn *conn, int err, void *context)
    {
    	LOG_ERR("Error while discovering GATT database: (%d)", err);
    }
    
    struct bt_gatt_dm_cb discovery_cb = {
    	.completed = discovery_complete,
    	.service_not_found = discovery_service_not_found,
    	.error_found = discovery_error,
    };
    
    static void gatt_discover(struct bt_conn *conn)
    {
    	int ret = SUCCESS;
    
    	if (conn != default_conn) {
    		return;
    	}
    
    	ret = bt_gatt_dm_start(conn, BT_UUID_NUS_SERVICE, &discovery_cb, &nus_client);
    	if (ret != SUCCESS) {
    		LOG_ERR("could not start the discovery procedure, error code: %d", ret);
    	}
    }
    
    static void exchange_func(struct bt_conn *conn, uint8_t err, struct bt_gatt_exchange_params *params)
    {
    	if (!err) {
    		LOG_INF("MTU exchange done");
    	} else {
    		LOG_ERR("MTU exchange failed (err %" PRIu8 ")", err);
    	}
    }
    
    static void connected(struct bt_conn *conn, uint8_t conn_err)
    {
    	char addr[BT_ADDR_LE_STR_LEN] = {0};
    	int ret = SUCCESS;
    
    	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
    
    	if (conn_err) {
    		LOG_ERR("Failed to connect to %s (%d)", addr, conn_err);
    
    		if (default_conn == conn) {
    			bt_conn_unref(default_conn);
    			default_conn = NULL;
    
    			bluetooth_start_scan();
    		}
    
    		return;
    	}
    
    	LOG_INF("Connected: %s", addr);
    
    	// Verificar MTU atual
    	uint16_t mtu = bt_gatt_get_mtu(conn);
    	//LOG_INF("Current MTU size: %u", mtu);
    
    	// Negociar MTU apenas se necessário
    	if (mtu == 23) { // Tamanho padrão significa que ainda não foi negociado
    		static struct bt_gatt_exchange_params exchange_params;
    		exchange_params.func = exchange_func;
    
    		ret = bt_gatt_exchange_mtu(conn, &exchange_params);
    		if (ret == 0) {
    			//LOG_INF("MTU exchange initiated");
    		} else if (ret == -EALREADY) {
    			LOG_WRN("MTU already exchanged");
    		} else {
    			LOG_ERR("MTU exchange failed (ret %d)", ret);
    		}
    	} else {
    		LOG_INF("MTU already negotiated, no need to exchange");
    	}
    
    	gatt_discover(conn);
    	// ret = bt_conn_set_security(conn, BT_SECURITY_L2);
    	// if (ret)
    	// {
    	// 	LOG_WRN("Failed to set security: %d", ret);
    	// 	gatt_discover(conn);
    	// }
    
    	ret = bt_scan_stop();
    	if ((!ret) && (ret != -EALREADY)) {
    		LOG_INF("Stop LE scan failed (ret %d)", ret);
    	}
    }
    
    static void disconnected(struct bt_conn *conn, uint8_t reason)
    {
    	char addr[BT_ADDR_LE_STR_LEN] = {0};
    
    	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
    
    	LOG_INF("Disconnected: %s (reason %u)", addr, reason);
    
    	if (default_conn != conn) {
    		return;
    	}
    
    	bt_conn_unref(default_conn);
    	default_conn = NULL;
    }
    
    int bluetooth_is_connected(void)
    {
    	if (default_conn == NULL){
    		return ERROR;
    	}
    
    	return SUCCESS;
    }
    
    void bluetooth_disconnect_ble_nus(void)
    {
    	if (default_conn) {
    		int ret = bt_conn_disconnect(default_conn, BT_HCI_ERR_LOCALHOST_TERM_CONN);
    		if (ret) {
    			LOG_ERR("Disconnection failed (err %d)", ret);
    		} else {
    			LOG_INF("Disconnection initiated");
    		}
    	} else {
    		LOG_INF("No active connection to disconnect");
    	}
    }
    
    static void security_changed(struct bt_conn *conn, bt_security_t level,
    							 enum bt_security_err err)
    {
    	char addr[BT_ADDR_LE_STR_LEN] = {0};
    
    	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
    
    	if (!err) {
    		LOG_INF("Security changed: %s level %u", addr, level);
    	}
    	else {
    		LOG_ERR("Security failed: %s level %u err %d", addr, level, err);
    	}
    
    	gatt_discover(conn);
    }
    
    BT_CONN_CB_DEFINE(conn_callbacks) = {
    	.connected = connected,
    	.disconnected = disconnected,
    	.security_changed = security_changed
    };
    
    static void scan_filter_match(struct bt_scan_device_info *device_info,
    							  struct bt_scan_filter_match *filter_match,
    							  bool connectable)
    {
    	char addr[BT_ADDR_LE_STR_LEN] = {0};
    	bt_addr_le_to_str(device_info->recv_info->addr, addr, sizeof(addr));
    	LOG_INF("Filters matched. Address: %s connectable: %d", addr, connectable);
    
    	// Se já houver uma conexão ativa, ignora novos anúncios
    	if (default_conn) {
    		LOG_INF("Já existe conexão ativa. Ignorando novo anúncio.");
    		return;
    	}
    
        #ifdef CONFIG_USE_CODED_PHY
    	int ret;
    	struct bt_conn_le_create_param *conn_params;
    
    	ret = bt_scan_stop();
    	if (ret) {
    		LOG_ERR("Stop LE scan failed (ret %d)", ret);
    	}
    	conn_params = BT_CONN_LE_CREATE_PARAM(
    		BT_CONN_LE_OPT_CODED | BT_CONN_LE_OPT_NO_1M,
    		BT_GAP_SCAN_FAST_INTERVAL,
    		BT_GAP_SCAN_FAST_INTERVAL);
    	ret = bt_conn_le_create(device_info->recv_info->addr, conn_params,
    							BT_LE_CONN_PARAM_DEFAULT,
    							&default_conn);
    	if (ret) {
    		LOG_ERR("Create conn failed (ret %d)", ret);
    
    		bluetooth_start_scan();
    	}
    	//LOG_INF("Connection pending");
    
        #endif //CONFIG_USE_CODED_PHY
    }
    
    static void scan_connecting_error(struct bt_scan_device_info *device_info)
    {
    	LOG_ERR("Connecting failed");
    }
    
    static void scan_connecting(struct bt_scan_device_info *device_info,
    							struct bt_conn *conn)
    {
    	default_conn = bt_conn_ref(conn);
    }
    
    static int nus_client_init(void)
    {
    	int ret = SUCCESS;
    	struct bt_nus_client_init_param init = {
    		.cb = {
    			.received = ble_data_received,
    			.sent = ble_data_sent,
    		}
    	};
    
    	ret = bt_nus_client_init(&nus_client, &init);
    	if (ret != SUCCESS) {
    		LOG_ERR("NUS Client initialization failed (ret %d)", ret);
    		return ret;
    	}
    
    	LOG_INF("NUS Client module initialized");
    	return ret;
    }
    
    BT_SCAN_CB_INIT(scan_cb, scan_filter_match, NULL,
    				scan_connecting_error, scan_connecting);
    
    static int scan_init(void)
    {
    	int ret = SUCCESS;
    
        #ifdef CONFIG_USE_CODED_PHY
    	/* Use active scanning and disable duplicate filtering to handle any
    	 * devices that might update their advertising data at runtime. */
    	struct bt_le_scan_param scan_param = {
    		.type     = BT_LE_SCAN_TYPE_ACTIVE,
    		.interval = BT_GAP_SCAN_FAST_INTERVAL,
    		.window   = BT_GAP_SCAN_FAST_WINDOW,
    		.options  = BT_LE_SCAN_OPT_CODED | BT_LE_SCAN_OPT_NO_1M
    	};
    
    	struct bt_scan_init_param scan_init = {
    		.connect_if_match = 0,
    		.scan_param = &scan_param,
    		.conn_param = NULL
    	};
    	#else
    	struct bt_scan_init_param scan_init = {
    		.connect_if_match = 1,
    	};
        #endif //CONFIG_USE_CODED_PHY
    
    	bt_scan_init(&scan_init);
    	bt_scan_cb_register(&scan_cb);
    
    	ret = bt_scan_filter_add(BT_SCAN_FILTER_TYPE_UUID, BT_UUID_NUS_SERVICE);
    	if (ret != SUCCESS) {
    		LOG_ERR("Scanning filters cannot be set (ret %d)", ret);
    		return ret;
    	}
    
    	ret = bt_scan_filter_enable(BT_SCAN_UUID_FILTER, false);
    	if (ret != SUCCESS) {
    		LOG_ERR("Filters cannot be turned on (ret %d)", ret);
    		return ret;
    	}
    
    	LOG_INF("Scan module initialized");
    	return ret;
    }
    
    static void auth_cancel(struct bt_conn *conn)
    {
    	char addr[BT_ADDR_LE_STR_LEN] = {0};
    	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
    	LOG_INF("Pairing cancelled: %s", addr);
    }
    
    static void pairing_complete(struct bt_conn *conn, bool bonded)
    {
    	char addr[BT_ADDR_LE_STR_LEN] = {0};
    	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
    	LOG_INF("Pairing completed: %s, bonded: %d", addr, bonded);
    }
    
    static void pairing_failed(struct bt_conn *conn, enum bt_security_err reason)
    {
    	char addr[BT_ADDR_LE_STR_LEN] = {0};
    	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
    	LOG_WRN("Pairing failed conn: %s, reason %d", addr, reason);
    }
    
    static struct bt_conn_auth_cb conn_auth_callbacks = {
    	.cancel = auth_cancel,
    };
    
    static struct bt_conn_auth_info_cb conn_auth_info_callbacks = {
    	.pairing_complete = pairing_complete,
    	.pairing_failed = pairing_failed
    };
    
    int bluetooth_central_init(void)
    {
    	int ret = SUCCESS;
    
    	// Enable pins of SKY66112-11
    	const struct device* gpio0_dev = device_get_binding("gpio@50000000"); // gpio 0
    
    	//	Table 6. SKY66112-11 Mode Control Logic1
    	//	Mode	Description 		CPS	CHL
    	//	1 	Receive LNA 		0 	X
    	//	2 	Transmit high-power 	0 	1
    
    	gpio_pin_configure(gpio0_dev, 6, GPIO_OUTPUT); // gpio 0.06 - CPS
    	gpio_pin_set(gpio0_dev, 6, 0);
    	gpio_pin_configure(gpio0_dev, 8, GPIO_OUTPUT); // gpio 0.08 - CHL
    	gpio_pin_set(gpio0_dev, 8, 1);
    
    	ret = bt_conn_auth_cb_register(&conn_auth_callbacks);
    	if (ret != SUCCESS) {
    		LOG_ERR("Failed to register authorization callbacks.");
    		return ERROR;
    	}
    
    	ret = bt_conn_auth_info_cb_register(&conn_auth_info_callbacks);
    	if (ret != SUCCESS) {
    		LOG_ERR("Failed to register authorization info callbacks.");
    		return ERROR;
    	}
    
    	ret = bt_enable(NULL);
    	if (ret != SUCCESS) {
    		LOG_ERR("Bluetooth init failed (ret %d)", ret);
    		return ERROR;
    	}
    	LOG_INF("Bluetooth initialized");
    
    	if (IS_ENABLED(CONFIG_SETTINGS)) {
    		settings_load();
    	}
    
    	ret = scan_init();
    	if (ret != SUCCESS) {
    		LOG_ERR("scan_init failed (ret %d)", ret);
    		return ERROR;
    	}
    
    	ret = nus_client_init();
    	if (ret != SUCCESS) {
    		LOG_ERR("nus_client_init failed (ret %d)", ret);
    		return ERROR;
    	}
    
    	ret = bt_scan_start(BT_SCAN_TYPE_SCAN_ACTIVE);
    	if (ret != SUCCESS) {
    		LOG_ERR("Scanning failed to start (ret %d)", ret);
    		return ERROR;
    	}
    
    	LOG_INF("Scanning successfully started");
    
    	return SUCCESS;
    }
    
    int bluetooth_central_send_data(const uint8_t *data, uint16_t len)
    {
    	int ret = SUCCESS;
    	if (!default_conn) {
    		LOG_ERR("Cannot send data, no BLE connection");
    		return -ENOTCONN;
    	}
    
    	ret = bt_nus_client_send(&nus_client, data, len);
    	if (ret != SUCCESS) {
    		LOG_ERR("BT NUS send ERROR: %d", ret);
    		return ERROR;
    	}
    	return ret;
    }
    
    bool bluetooth_service_discovery_completed(void)
    {
    	return services_discovery_completed;
    }


    Central main.c
    #include <zephyr/kernel.h>
    #include <zephyr/logging/log.h>
    #include <zephyr/sys/reboot.h>
    
    #include "imachine_central.h"
    #include "bluetooth_central_app.h"
    #include "flash.h"
    #include "protocol.h"
    #include "comm_uart.h"
    #include "watchdog.h"
    
    LOG_MODULE_REGISTER(MAIN, CONFIG_LOG_DEFAULT_LEVEL);
    
    static bool ack_sent = false;
    static bool start_sent = false;
    static protocol_ack_st ack_packet = {0};
    static int timeout_count;
    
    typedef enum {
    	STATE_INITIAL,
    	STATE_WATCHDOG_CONFIG,
    	STATE_WATCHDOG_ERROR,
    	STATE_FLASH_CONFIG,
    	STATE_FLASH_ERROR,
    	STATE_BLE_CONFIG,
    	STATE_BLE_DISCOVERY,
    	STATE_BLE_CONNECTED,
    	STATE_BLE_WAIT_DISCONNECTION,
    	STATE_BLE_DISCONNECTION,
    	STATE_BLE_DISCOVERY_TIMEOUT,
    	STATE_BLE_ERROR,
    	STATE_FATAL_ERROR,
    	STATE_COUNT,
    } machine_state_t;
    
    static machine_state_t current_state;
    
    const char* state_names[STATE_COUNT] = {
    	"STATE_INITIAL",
    	"STATE_WATCHDOG_CONFIG",
    	"STATE_WATCHDOG_ERROR",
    	"STATE_FLASH_CONFIG",
    	"STATE_FLASH_ERROR",
    	"STATE_BLE_CONFIG",
    	"STATE_BLE_DISCOVERY",
    	"STATE_BLE_CONNECTED",
    	"STATE_BLE_WAIT_DISCONNECTION",
    	"STATE_BLE_DISCONNECTION",
    	"STATE_BLE_DISCOVERY_TIMEOUT",
    	"STATE_BLE_ERROR",
    	"STATE_FATAL_ERROR",
    };
    
    #include <hal/nrf_gpio.h>
    #include <zephyr/devicetree.h>
    #include <zephyr/drivers/gpio.h>
    
    
    static const struct gpio_dt_spec led1_g = GPIO_DT_SPEC_GET(DT_ALIAS(led1_g), gpios);
    static const struct gpio_dt_spec led2_b = GPIO_DT_SPEC_GET(DT_ALIAS(led2_b), gpios);
    
    static struct k_work_delayable led_toggle_worker;
    
    static void led_toggle_work_handler(struct k_work *work)
    {
    	gpio_pin_toggle_dt(&led1_g);
    
    	/* Re-agenda para rodar novamente após 1 minuto */
    	k_work_schedule(&led_toggle_worker, K_SECONDS(1));
    }
    
    void main_worker_init()
    {
    	gpio_pin_configure_dt(&led1_g, GPIO_OUTPUT_INACTIVE);
    	gpio_pin_configure_dt(&led2_b, GPIO_OUTPUT_INACTIVE);
    	k_work_init_delayable(&led_toggle_worker, led_toggle_work_handler);
    	k_work_schedule(&led_toggle_worker, K_SECONDS(1));
    }
    
    void set_state(machine_state_t new_state)
    {
    	LOG_INF("[%s] -> [%s]", state_names[current_state], state_names[new_state]);
    	current_state = new_state;
    }
    
    void initial_state()
    {
    	LOG_INF("Initial State");
    	init_uart();
    	main_worker_init();
    	set_state(STATE_WATCHDOG_CONFIG);
    }
    
    void watchdog_config_state() {
    	LOG_INF("Watchdog Config State");
    
    	if( watchdog_init() == SUCCESS ) {
    		set_state(STATE_FLASH_CONFIG);
    	}
    	else {
    		set_state(STATE_WATCHDOG_ERROR);
    	}
    }
    
    void watchdog_error_state() {
    	LOG_ERR("Watchdog Error State");
    	LOG_ERR("Handle Watchdog Error");
    
    	set_state(STATE_FATAL_ERROR);
    }
    
    void flash_config_state()
    {
    	LOG_INF("Flash Config State");
    
    	if( storage_config() == SUCCESS ) {
    		set_state(STATE_BLE_CONFIG);
    	} else {
    		set_state(STATE_FLASH_ERROR);
    	}
    }
    
    void flash_error_state()
    {
    	LOG_ERR("Flash Error State");
    	LOG_ERR("Handle Flash Error");
    	set_state(STATE_FATAL_ERROR);
    }
    
    void ble_config_state()
    {
    	LOG_INF("BLE Configuration State");
    
    	if( bluetooth_central_init() == SUCCESS ) {
    		LOG_INF("BLE Successfully Configured");
    		set_state(STATE_BLE_DISCOVERY);
    
    	} else {
    		LOG_WRN("BLE init FAILED");
    		//k_sleep(K_MSEC(3000));
    		set_state(STATE_BLE_ERROR);
    	}
    }
    
    void ble_discovery_state()
    {
    	LOG_INF("BLE Discovery State");
    
    	k_sem_reset(&ble_connection_discovery_sem);
    
    	int ret = k_sem_take(&ble_connection_discovery_sem, K_MINUTES(30));
    
    	if (ret == 0) {
    		LOG_INF("BLE Connected");
    
    		ack_sent = false;
    		start_sent = false;
    		memset(&ack_packet, 0, sizeof(protocol_ack_st));
    		clear_state_flags();
    
    		// create a timeout of connected state
    		timeout_count = 60000/SM_DELTA_TIME;
    
    		set_state(STATE_BLE_CONNECTED);
    	} else {
    		LOG_WRN("BLE Connection timeout");
    		k_sleep(K_MSEC(3000));
    		set_state(STATE_BLE_DISCOVERY_TIMEOUT);
    	}
    }
    
    void ble_connected_state()
    {
    	gpio_pin_set_dt(&led2_b, 1);
    
    	timeout_count--;
    	if (timeout_count <= 0) {
    		LOG_INF("Connection state timeout");
    
    		log_protocol_infos();
    
    		bluetooth_disconnect_ble_nus();
    
    		timeout_count = 10000/SM_DELTA_TIME;
    		set_state(STATE_BLE_WAIT_DISCONNECTION);
    		return;
    	}
    
    
    	if (start_sent == false) {
    		uint8_t data = START_FLAG;
    		LOG_INF("START FLAG sent");
    		bluetooth_central_send_data((const uint8_t *)&data, sizeof(data));
    		start_sent = true;
    	}
    
    	/* Wait for receiving all packets */
    	if (protocol_received_all_packets() != true) {
    		return;
    	}
    
    	if (ack_sent == false) {
    		LOG_INF("Sending ACK packet");
    		protocol_build_ack(&ack_packet);
    		int ret = bluetooth_central_send_data((const uint8_t *)&ack_packet, sizeof(protocol_ack_st));
    		if (ret != SUCCESS) {
    			LOG_ERR("Failed to send data over BLE connection (err %d)", ret);
    		}
    		ack_sent = true;
    	}
    
    	/* If ACK was succesfully sent... save data into flash memory */
    	if (ack_sent != true) {
    		return;
    	}
    
    	if (bluetooth_is_connected() == SUCCESS) {
    		return;
    	}
    
    	timeout_count = 10000/SM_DELTA_TIME;
    	set_state(STATE_BLE_WAIT_DISCONNECTION);
    }
    
    void ble_wait_disconnection_state()
    {
    	gpio_pin_set_dt(&led2_b, 0);
    	timeout_count--;
    
    	// Wait for timeout finish
    	if (timeout_count <= 0) {
    		set_state(STATE_BLE_DISCONNECTION);
    	}
    }
    
    void ble_disconnection_state()
    {
    	LOG_INF("BLE Disconnection State");
    	bluetooth_start_scan();
    	set_state(STATE_BLE_DISCOVERY);
    }
    
    void ble_discovery_timeout_state()
    {
    	LOG_WRN("BLE Discovery Timeout State");
    	set_state(STATE_BLE_ERROR);
    }
    
    void ble_error_state()
    {
    	LOG_ERR("BLE Error State");
    	set_state(STATE_FATAL_ERROR);
    }
    
    void fatal_error_state()
    {
    	LOG_ERR("Fatal Error State");
    	LOG_ERR("Rebooting");
    	k_sleep(K_MSEC(3000));
    	sys_reboot(SYS_REBOOT_COLD);
    }
    
    int state_machine_run(void)
    {
    	switch (current_state) {
    		case STATE_INITIAL:
    			initial_state();
    			break;
    		case STATE_WATCHDOG_CONFIG:
    			watchdog_config_state();
    			break;
    		case STATE_FLASH_CONFIG:
    			flash_config_state();
    			break;
    		case STATE_FLASH_ERROR:
    			flash_error_state();
    			break;
    		case STATE_BLE_CONFIG:
    			ble_config_state();
    			break;
    		case STATE_BLE_DISCOVERY:
    			ble_discovery_state();
    			break;
    		case STATE_BLE_CONNECTED:
    			ble_connected_state();
    			break;
    		case STATE_BLE_WAIT_DISCONNECTION:
    			ble_wait_disconnection_state();
    			break;
    		case STATE_BLE_DISCONNECTION:
    			ble_disconnection_state();
    			break;
    		case STATE_BLE_DISCOVERY_TIMEOUT:
    			ble_discovery_timeout_state();
    			break;
    		case STATE_BLE_ERROR:
    			ble_error_state();
    			break;
    		case STATE_FATAL_ERROR:
    			fatal_error_state();
    			break;
    		case STATE_COUNT:
    			// Only here to clear the warning:
    			// warning: enumeration value 'STATE_COUNT' not handled in switch [-Wswitch]
    			// 'STATE_COUNT' is used to keep track of the enum size.
    		default:
    			LOG_ERR("Unknown state");
    			set_state(STATE_FATAL_ERROR);
    			break;
    	}
    
    	watchdog_feed();
    
    	return 0;
    }
    
    
    int main(void)
    {
    	LOG_INF("-----------------------------");
    	LOG_INF("-- iMachine Central BLE V3 --");
    	LOG_INF("-----------------------------");
    
    	current_state = STATE_INITIAL;
    	int k = 0;
    	while (true) {
    		state_machine_run();
    		k_sleep(K_MSEC(SM_DELTA_TIME));
    
    		if(k % 100 == 0 ) {
    			printk(".");
    		}
    
    		k++;
    	}
    
    	return 0;
    }

    Peripheral Bluetooth.c

    #include "bluetooth.h"
    #include <zephyr/types.h>
    #include <zephyr/kernel.h>
    #include <zephyr/device.h>
    #include <zephyr/devicetree.h>
    #include <bluetooth/services/nus.h>
    #include <zephyr/settings/settings.h>
    #include <zephyr/logging/log.h>
    #include "flash.h"
    #include "protocol.h"
    #include "imachine.h"
    #include "configuration.h"
    
    #include <stddef.h>
    #include <zephyr/sys/util.h>
    #include <zephyr/sys/byteorder.h>
    
    #include <zephyr/bluetooth/bluetooth.h>
    #include <zephyr/bluetooth/hci.h>
    #include <zephyr/bluetooth/hci_vs.h>
    
    LOG_MODULE_REGISTER(BLE, CONFIG_LOG_DEFAULT_LEVEL);
    
    #define DEVICE_NAME CONFIG_BT_DEVICE_NAME
    #define DEVICE_NAME_LEN (sizeof(DEVICE_NAME) - 1)
    
    /* Set when is connected to a Gateway */
    static bool is_connected = false;
    
    /* Set when Bluetooth hardware is running */
    static bool is_running = false;
    
    /* Buffer to receive data */
    static uint8_t buffer[BLUETOOTH_PACKET_SIZE] = {0};
    static uint16_t buffer_len = 0;
    
    static bool receive_data = false;
    
    static bool bluetooth_services_discovered = false;
    
    K_SEM_DEFINE(ble_connection_sem, 0, 1);
    K_SEM_DEFINE(ble_disconnection_sem, 0, 1);
    static struct bt_conn *current_conn = NULL;
    
    #ifdef CONFIG_USE_CODED_PHY
    #pragma message "Build with LongRange"
    
    static struct bt_le_ext_adv *adv;
    
    static const struct bt_data ad[] = {
    	BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
    	BT_DATA_BYTES(BT_DATA_UUID128_ALL, BT_UUID_NUS_VAL),
    	BT_DATA(BT_DATA_NAME_COMPLETE, DEVICE_NAME, DEVICE_NAME_LEN)
    };
    #else
    #pragma message "Build WITHOUT LongRange"
    
    static const struct bt_data adverstising[] = {
    	BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
    	BT_DATA(BT_DATA_NAME_COMPLETE, DEVICE_NAME, DEVICE_NAME_LEN),
    };
    
    static const struct bt_data service_data[] = {
    	BT_DATA_BYTES(BT_DATA_UUID128_ALL, BT_UUID_NUS_VAL),
    };
    #endif //CONFIG_USE_CODED_PHY
    
    static void print_conn_addr(struct bt_conn *conn)
    {
    	char addr_str[BT_ADDR_LE_STR_LEN];
    	struct bt_conn_info info;
    
    	if (bt_conn_get_info(conn, &info) == 0) {
    		bt_addr_le_to_str(info.le.dst, addr_str, sizeof(addr_str));
    		LOG_INF("Connected to device: %s", addr_str);
    	} else {
    		LOG_ERR("Unable to get connection info");
    	}
    }
    
    static void connected(struct bt_conn *conn, uint8_t ret)
    {
    	if (ret) {
    		LOG_INF("Connection failed (ret 0x%02x)", ret);
    		is_connected = false;
    	} else {
    		LOG_INF("Connected");
    		is_connected = true;
    		current_conn = bt_conn_ref(conn);
    
    		print_conn_addr(conn);
    
    		k_sem_give(&ble_connection_sem);
    	}
    }
    
    static void disconnected(struct bt_conn *conn, uint8_t reason)
    {
    	LOG_INF("Disconnected (reason 0x%02x)", reason);
    	is_connected = false;  // Update connection state
    	k_sem_give(&ble_disconnection_sem);
    	//set_is_it_ok_to_deep_sleep(true);
    
    	if (current_conn) {
    		bt_conn_unref(current_conn);
    		current_conn = NULL;
    	}
    }
    
    BT_CONN_CB_DEFINE(conn_callbacks) = {
    	.connected = connected,
    	.disconnected = disconnected,
    };
    
    void parse_command(const uint8_t *const data, uint16_t len)
    {
    	if (len == 1) {
    		uint8_t msg[30];
    		int ret;
    
    		switch (data[0]) {
    			case START_FLAG:
    				LOG_INF("START FLAG received");
    				bluetooth_services_discovered = true;
    				imachine_bluetooth_send_data();
    				return;
    
    			case SANITY_CHECK:
    				LOG_INF("SANITY_CHECK received");
    				snprintf((char *)msg, sizeof(msg), "#SANITY_CHECK received");
    				break;
    
    			case CONFIG_RTC_PERIOD:
    				LOG_INF("CONFIG_RTC_PERIOD received");
    				snprintf((char *)msg, sizeof(msg), "#CONFIG_RTC_PERIOD received");
    				break;
    
    			case SLEEP_CMD:
    				LOG_INF("SLEEP_CMD received");
    				snprintf((char *)msg, sizeof(msg), "#SLEEP_CMD received");
    				k_sem_give(&ble_disconnection_sem);
    				break;
    
    			default:
    				LOG_WRN("Unknown command received: 0x%.2x", data[0]);
    				return;
    		}
    
    		ret = bluetooth_send_data((uint8_t *)msg, strlen((char *)msg));
    		if (ret != SUCCESS) {
    			LOG_ERR("BT NUS send ERROR: %d", ret);
    		}
    	} else if (len == sizeof(protocol_ack_st)) {
    		// TODO: define better ACK packet size
    		LOG_INF("ACK received");
    		memcpy((void *)buffer, (const void *)data, (size_t)len);
    		buffer_len = len;
    		set_received_data_flag(true);
    	} else {
    		LOG_ERR("Packet len invalid");
    		LOG_ERR("Expected: %d, received: %d", sizeof(protocol_ack_st), len);
    	}
    }
    
    static void bt_receive_cb(struct bt_conn *conn, const uint8_t *const data, uint16_t len)
    {
    	char addr[BT_ADDR_LE_STR_LEN] = {0};
    	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, ARRAY_SIZE(addr));
    
    	LOG_INF("Received data from: %s", addr);
    
    	for(int i = 0; i < len; i++) {
    		printk("%x", data[i]);
    	}
    
    	printk("\n");
    
    	for(int i = 0; i < len; i++) {
    		printk("%x", data[i]);
    	}
    
    	printk("\n");
    
    	parse_command(data, len);
    }
    
    static struct bt_nus_cb nus_cb = {
    	.received = bt_receive_cb,
    };
    
    static int bt_ready(void)
    {
    	int ret = SUCCESS;
    
    	LOG_INF("Bluetooth initialized");
    
    	if (IS_ENABLED(CONFIG_SETTINGS)) {
    		settings_load();
    	}
    
    	#ifdef CONFIG_USE_CODED_PHY
    		struct bt_le_adv_param param =
    		BT_LE_ADV_PARAM_INIT(BT_LE_ADV_OPT_CONNECTABLE |
    				BT_LE_ADV_OPT_EXT_ADV |
    				BT_LE_ADV_OPT_CODED,
    				BT_GAP_ADV_FAST_INT_MIN_2,
    				BT_GAP_ADV_FAST_INT_MAX_2,
    				NULL);
    
    		ret = bt_le_ext_adv_create(&param, NULL, &adv);
    		if (ret) {
    			LOG_INF("Failed to create advertiser set (ret %d)", ret);
    			return ret;
    		}
    
    		LOG_INF("Created adv:");
    
    		ret = bt_le_ext_adv_set_data(adv, ad, ARRAY_SIZE(ad), NULL, 0);
    		if (ret) {
    			LOG_INF("Failed to set advertising data (ret %d)", ret);
    			return ret;
    		}
    
    
    		ret = bt_le_ext_adv_start(adv, NULL);
    		if (ret) {
    			LOG_INF("Failed to start advertising set (ret %d)", ret);
    			return ret;
    		}
    
    		LOG_INF("Advertiser set started");
    	#else
    		ret = bt_le_adv_start(BT_LE_ADV_CONN, adverstising, ARRAY_SIZE(adverstising),
    				service_data, ARRAY_SIZE(service_data));
    
    		if (ret != SUCCESS) {
    			LOG_ERR("Advertising failed to start (ret %d)", ret);
    			return ERROR;
    		}
    
    		LOG_INF("Advertising successfully started");
    	#endif //CONFIG_USE_CODED_PHY
    
    	return ret;
    }
    
    int bluetooth_send_data(uint8_t *data, uint16_t len)
    {
    	int ret = SUCCESS;
    
    	if (data == NULL) {
    		LOG_ERR("Packet data cannot be NULL");
    		return ERROR;
    	}
    
    	if (len > BLUETOOTH_PACKET_SIZE) {
    		LOG_ERR("Packet size must be less or equal to %d", BLUETOOTH_PACKET_SIZE);
    		return ERROR;
    	}
    
    	ret = bt_nus_send(NULL, data, len);
    	if (ret != SUCCESS) {
    		LOG_ERR("BT NUS send ERROR: %d", ret);
    		return ERROR;
    	}
    
    	return ret;
    }
    
    int bluetooth_init(void)
    {
    	int ret = SUCCESS;
    
    	/* Enable Bluetooth hardware */
    	ret = bt_enable(NULL);
    	if (ret != SUCCESS) {
    		LOG_ERR("Bluetooth init failed (ret %d)", ret);
    		return ERROR;
    	}
    
    	/* Enable NUS */
    	ret = bt_nus_init(&nus_cb);
    	if (ret != SUCCESS) {
    		LOG_ERR("Failed to initialize UART service (ret: %d)", ret);
    		return ERROR;
    	}
    
    	ret = bt_ready();
    	if (ret != SUCCESS) {
    		return ERROR;
    	}
    
    	/* Set Bluetooth running flag */
    	is_running = true;
    
    	/* Wait for service discovery */
    	// Check if necessary
    	//k_sleep(K_MSEC(2000));
    
    	return ret;
    }
    
    bool is_bluetooth_connected(void)
    {
    	return is_connected;
    }
    
    bool is_bluetooth_running(void)
    {
    	return is_running;
    }
    
    void bluetooth_get_received_buffer(uint8_t *data, uint16_t *len)
    {
    	memcpy((void *)data, (const void *)buffer, (size_t)buffer_len);
    	*len = buffer_len;
    }
    
    bool get_received_data_flag(void)
    {
    	return receive_data;
    }
    
    void set_received_data_flag(bool flag)
    {
    	receive_data = flag;
    }
    
    bool get_bluetooth_services_discovered(void)
    {
    	return bluetooth_services_discovered;
    }
    
    void disconnect_ble_nus(void)
    {
    	if (current_conn) {
    		int ret = bt_conn_disconnect(current_conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN);
    		if (ret) {
    			LOG_ERR("Disconnection failed (err %d)", ret);
    		} else {
    			LOG_INF("Disconnection initiated");
    		}
    	} else {
    		LOG_INF("No active connection to disconnect");
    	}
    }
    
    void stop_advertising(void)
    {
    	#ifdef CONFIG_USE_CODED_PHY
    		bt_le_ext_adv_stop(adv);
    	#else
    		bt_le_adv_stop();
    	#endif //CONFIG_USE_CODED_PHY
    }
    
    void bluetooth_disable(void)
    {
    	disconnect_ble_nus();
    	stop_advertising();
    	bt_disable();
    }

    Peripheral main.c

    #include <zephyr/kernel.h>
    #include <zephyr/logging/log.h>
    #include <zephyr/sys/reboot.h>
    
    #include "imachine_central.h"
    #include "bluetooth_central_app.h"
    #include "flash.h"
    #include "protocol.h"
    #include "comm_uart.h"
    #include "watchdog.h"
    
    LOG_MODULE_REGISTER(MAIN, CONFIG_LOG_DEFAULT_LEVEL);
    
    static bool ack_sent = false;
    static bool start_sent = false;
    static protocol_ack_st ack_packet = {0};
    static int timeout_count;
    
    typedef enum {
    	STATE_INITIAL,
    	STATE_WATCHDOG_CONFIG,
    	STATE_WATCHDOG_ERROR,
    	STATE_FLASH_CONFIG,
    	STATE_FLASH_ERROR,
    	STATE_BLE_CONFIG,
    	STATE_BLE_DISCOVERY,
    	STATE_BLE_CONNECTED,
    	STATE_BLE_WAIT_DISCONNECTION,
    	STATE_BLE_DISCONNECTION,
    	STATE_BLE_DISCOVERY_TIMEOUT,
    	STATE_BLE_ERROR,
    	STATE_FATAL_ERROR,
    	STATE_COUNT,
    } machine_state_t;
    
    static machine_state_t current_state;
    
    const char* state_names[STATE_COUNT] = {
    	"STATE_INITIAL",
    	"STATE_WATCHDOG_CONFIG",
    	"STATE_WATCHDOG_ERROR",
    	"STATE_FLASH_CONFIG",
    	"STATE_FLASH_ERROR",
    	"STATE_BLE_CONFIG",
    	"STATE_BLE_DISCOVERY",
    	"STATE_BLE_CONNECTED",
    	"STATE_BLE_WAIT_DISCONNECTION",
    	"STATE_BLE_DISCONNECTION",
    	"STATE_BLE_DISCOVERY_TIMEOUT",
    	"STATE_BLE_ERROR",
    	"STATE_FATAL_ERROR",
    };
    
    #include <hal/nrf_gpio.h>
    #include <zephyr/devicetree.h>
    #include <zephyr/drivers/gpio.h>
    
    
    static const struct gpio_dt_spec led1_g = GPIO_DT_SPEC_GET(DT_ALIAS(led1_g), gpios);
    static const struct gpio_dt_spec led2_b = GPIO_DT_SPEC_GET(DT_ALIAS(led2_b), gpios);
    
    static struct k_work_delayable led_toggle_worker;
    
    static void led_toggle_work_handler(struct k_work *work)
    {
    	gpio_pin_toggle_dt(&led1_g);
    
    	/* Re-agenda para rodar novamente após 1 minuto */
    	k_work_schedule(&led_toggle_worker, K_SECONDS(1));
    }
    
    void main_worker_init()
    {
    	gpio_pin_configure_dt(&led1_g, GPIO_OUTPUT_INACTIVE);
    	gpio_pin_configure_dt(&led2_b, GPIO_OUTPUT_INACTIVE);
    	k_work_init_delayable(&led_toggle_worker, led_toggle_work_handler);
    	k_work_schedule(&led_toggle_worker, K_SECONDS(1));
    }
    
    void set_state(machine_state_t new_state)
    {
    	LOG_INF("[%s] -> [%s]", state_names[current_state], state_names[new_state]);
    	current_state = new_state;
    }
    
    void initial_state()
    {
    	LOG_INF("Initial State");
    	init_uart();
    	main_worker_init();
    	set_state(STATE_WATCHDOG_CONFIG);
    }
    
    void watchdog_config_state() {
    	LOG_INF("Watchdog Config State");
    
    	if( watchdog_init() == SUCCESS ) {
    		set_state(STATE_FLASH_CONFIG);
    	}
    	else {
    		set_state(STATE_WATCHDOG_ERROR);
    	}
    }
    
    void watchdog_error_state() {
    	LOG_ERR("Watchdog Error State");
    	LOG_ERR("Handle Watchdog Error");
    
    	set_state(STATE_FATAL_ERROR);
    }
    
    void flash_config_state()
    {
    	LOG_INF("Flash Config State");
    
    	if( storage_config() == SUCCESS ) {
    		set_state(STATE_BLE_CONFIG);
    	} else {
    		set_state(STATE_FLASH_ERROR);
    	}
    }
    
    void flash_error_state()
    {
    	LOG_ERR("Flash Error State");
    	LOG_ERR("Handle Flash Error");
    	set_state(STATE_FATAL_ERROR);
    }
    
    void ble_config_state()
    {
    	LOG_INF("BLE Configuration State");
    
    	if( bluetooth_central_init() == SUCCESS ) {
    		LOG_INF("BLE Successfully Configured");
    		set_state(STATE_BLE_DISCOVERY);
    
    	} else {
    		LOG_WRN("BLE init FAILED");
    		//k_sleep(K_MSEC(3000));
    		set_state(STATE_BLE_ERROR);
    	}
    }
    
    void ble_discovery_state()
    {
    	LOG_INF("BLE Discovery State");
    
    	k_sem_reset(&ble_connection_discovery_sem);
    
    	int ret = k_sem_take(&ble_connection_discovery_sem, K_MINUTES(30));
    
    	if (ret == 0) {
    		LOG_INF("BLE Connected");
    
    		ack_sent = false;
    		start_sent = false;
    		memset(&ack_packet, 0, sizeof(protocol_ack_st));
    		clear_state_flags();
    
    		// create a timeout of connected state
    		timeout_count = 60000/SM_DELTA_TIME;
    
    		set_state(STATE_BLE_CONNECTED);
    	} else {
    		LOG_WRN("BLE Connection timeout");
    		k_sleep(K_MSEC(3000));
    		set_state(STATE_BLE_DISCOVERY_TIMEOUT);
    	}
    }
    
    void ble_connected_state()
    {
    	gpio_pin_set_dt(&led2_b, 1);
    
    	timeout_count--;
    	if (timeout_count <= 0) {
    		LOG_INF("Connection state timeout");
    
    		log_protocol_infos();
    
    		bluetooth_disconnect_ble_nus();
    
    		timeout_count = 10000/SM_DELTA_TIME;
    		set_state(STATE_BLE_WAIT_DISCONNECTION);
    		return;
    	}
    
    
    	if (start_sent == false) {
    		uint8_t data = START_FLAG;
    		LOG_INF("START FLAG sent");
    		bluetooth_central_send_data((const uint8_t *)&data, sizeof(data));
    		start_sent = true;
    	}
    
    	/* Wait for receiving all packets */
    	if (protocol_received_all_packets() != true) {
    		return;
    	}
    
    	if (ack_sent == false) {
    		LOG_INF("Sending ACK packet");
    		protocol_build_ack(&ack_packet);
    		int ret = bluetooth_central_send_data((const uint8_t *)&ack_packet, sizeof(protocol_ack_st));
    		if (ret != SUCCESS) {
    			LOG_ERR("Failed to send data over BLE connection (err %d)", ret);
    		}
    		ack_sent = true;
    	}
    
    	/* If ACK was succesfully sent... save data into flash memory */
    	if (ack_sent != true) {
    		return;
    	}
    
    	if (bluetooth_is_connected() == SUCCESS) {
    		return;
    	}
    
    	timeout_count = 10000/SM_DELTA_TIME;
    	set_state(STATE_BLE_WAIT_DISCONNECTION);
    }
    
    void ble_wait_disconnection_state()
    {
    	gpio_pin_set_dt(&led2_b, 0);
    	timeout_count--;
    
    	// Wait for timeout finish
    	if (timeout_count <= 0) {
    		set_state(STATE_BLE_DISCONNECTION);
    	}
    }
    
    void ble_disconnection_state()
    {
    	LOG_INF("BLE Disconnection State");
    	bluetooth_start_scan();
    	set_state(STATE_BLE_DISCOVERY);
    }
    
    void ble_discovery_timeout_state()
    {
    	LOG_WRN("BLE Discovery Timeout State");
    	set_state(STATE_BLE_ERROR);
    }
    
    void ble_error_state()
    {
    	LOG_ERR("BLE Error State");
    	set_state(STATE_FATAL_ERROR);
    }
    
    void fatal_error_state()
    {
    	LOG_ERR("Fatal Error State");
    	LOG_ERR("Rebooting");
    	k_sleep(K_MSEC(3000));
    	sys_reboot(SYS_REBOOT_COLD);
    }
    
    int state_machine_run(void)
    {
    	switch (current_state) {
    		case STATE_INITIAL:
    			initial_state();
    			break;
    		case STATE_WATCHDOG_CONFIG:
    			watchdog_config_state();
    			break;
    		case STATE_FLASH_CONFIG:
    			flash_config_state();
    			break;
    		case STATE_FLASH_ERROR:
    			flash_error_state();
    			break;
    		case STATE_BLE_CONFIG:
    			ble_config_state();
    			break;
    		case STATE_BLE_DISCOVERY:
    			ble_discovery_state();
    			break;
    		case STATE_BLE_CONNECTED:
    			ble_connected_state();
    			break;
    		case STATE_BLE_WAIT_DISCONNECTION:
    			ble_wait_disconnection_state();
    			break;
    		case STATE_BLE_DISCONNECTION:
    			ble_disconnection_state();
    			break;
    		case STATE_BLE_DISCOVERY_TIMEOUT:
    			ble_discovery_timeout_state();
    			break;
    		case STATE_BLE_ERROR:
    			ble_error_state();
    			break;
    		case STATE_FATAL_ERROR:
    			fatal_error_state();
    			break;
    		case STATE_COUNT:
    			// Only here to clear the warning:
    			// warning: enumeration value 'STATE_COUNT' not handled in switch [-Wswitch]
    			// 'STATE_COUNT' is used to keep track of the enum size.
    		default:
    			LOG_ERR("Unknown state");
    			set_state(STATE_FATAL_ERROR);
    			break;
    	}
    
    	watchdog_feed();
    
    	return 0;
    }
    
    
    int main(void)
    {
    	LOG_INF("-----------------------------");
    	LOG_INF("-- iMachine Central BLE V3 --");
    	LOG_INF("-----------------------------");
    
    	current_state = STATE_INITIAL;
    	int k = 0;
    	while (true) {
    		state_machine_run();
    		k_sleep(K_MSEC(SM_DELTA_TIME));
    
    		if(k % 100 == 0 ) {
    			printk(".");
    		}
    
    		k++;
    	}
    
    	return 0;
    }

    Thanks again, and I appreciate your continued support.

  • Hi Arthur, 
    The BT_GAP_SCAN_FAST_INTERVAL is 60ms which is quite OK. You can try to increase the interval you put in conn_params in scan_filter_match(). 
    A sniffer trace can reveal more info. 
    You may want to check the memory buffer in your central's prj.conf to see if you are hitting any limit 

Reply Children
No Data
Related