Many-to-one sensor collector topology, which BLE sample is better to modify?

Hi, 

1) I want to collect sensor data using BLE like the following topology, the sensor devices are battery-powered, which sample in the NCS is suitable to modify?

2) Does NUS central/peripheral can do many-to-one topology in this usage? or need to use BLE mesh?

Parents
  • Hello,

    I think the peripheral/central_uart samples is a good starting point for this. I wrote an answer to a similar question just a couple of weeks ago. It was in a private ticket, but I'll paste it here:

    -------------

    I am not aware of any official samples that will connect to multiple peripherals, but it is indeed possible. To get started, I would use the peripheral and central sample that are closest to what you want to use. E.g. the peripheral_uart and central_uart are good starting points. 

    While it is not official, a colleague of mine has a multi-nus sample, which is basically a central that can connect to multiple peripherals. You can have a look at it for inspiration:

    https://github.com/NordicMatt/multi-NUS/tree/master

    You can see that it has some functions for sending messages to one particular peripheral, or multiple. But in theory, all you need to do is to do some changes in prj.conf of the central_uart sample:

    CONFIG_BT_MAX_CONN=20
    CONFIG_BT_MAX_PAIRED=20
    CONFIG_BT_CONN_CTX=y

    This will allow it to connect to up to 20 devices. Then your app needs to keep track of how many connections it has, and resume scanning if the maximum is not reached. You don't need CONFIG_BT_MAX_PAIRED if you are not going to use pairing (encryption). 

    Another thing that you need to do is to look at the way that the central_uart sample keeps track of the connection pointer, bt_conn *default_conn. In the connected() callback, which triggers when the device enters a connection. This particular example sets the default_conn pointer in it's scan_connecting() callback, but you can do it in the connected() callback. And in your case, you need to have an array of bt_conn pointers. As many as the maximum number of simultaneous connections. Then set them using bt_conn_ref() when you enter a new connection, and unset them using bt_conn_unref() when you disconnect. 

    It may also be a good idea to have this conn pointer as an input in the function that you use to send data, like the bt_nus_client_send(). This sample in particular doesn't do that, because it assumes only one connection, and this is set in the nus_client parameter, but you can see how it uses the conn pointer inside bt_nus_client_send():

    	err = bt_gatt_write(nus_c->conn, &nus_c->rx_write_params);

    So that you send the message to the intended peripheral. You can see how it is done in the multi-nus example (link to github above).

    -------------

    So there is a description on how to make the central have multiple concurrent connections. When sending data the other way, you can look at the conn* pointer in the callback to see what device the data is coming from. 

    Also, in general, Bluetooth Mesh is probably not the solution you are looking for. It will work, but the power consumption is a lot higher than in a BLE application. So since you mentioned that they will be battery powered, I recommend using normal BLE.

    Best regards,

    Edvin

  • Hi,

    1) Does the github multi-nus sample base on ncs v2.6.1 central_uart project to modify? There are many differences compared to v2.6.1 sample except multi-nus functions.

    2) I am still failed to port multi-nus to v2.9.0 central_uart project, it can't connect one peripheral for more than 5s, the central will crash. Is there any other Connection Context Library sample for reference?

    add 

        struct bt_nus_client *nus_client = bt_conn_ctx_alloc(&conns_ctx_lib, conn);
    to connected() cause central crash. The RTT log as the following,
    00> [00:00:00.016,021] <inf> fs_nvs: 2 Sectors of 4096 bytes
    00> [00:00:00.016,021] <inf> fs_nvs: alloc wra: 0, fd0
    00> [00:00:00.016,052] <inf> fs_nvs: data wra: 0, 1c
    00> [00:00:00.016,174] <inf> bt_sdc_hci_driver: SoftDevice Controller build revision: 
    00>                                             2d 79 a1 c8 6a 40 b7 3c  f6 74 f9 0b 22 d3 c4 80 |-y..j@.< .t.."...
    00>                                             74 72 82 ba                                      |tr..             
    00> [00:00:00.020,080] <inf> bt_hci_core: HW Platform: Nordic Semiconductor (0x0002)
    00> [00:00:00.020,111] <inf> bt_hci_core: HW Variant: nRF52x (0x0002)
    00> [00:00:00.020,172] <inf> bt_hci_core: Firmware: Standard Bluetooth controller (0x00) Version 45.41337 Build 3074452168
    00> [00:00:00.020,812] <inf> bt_hci_core: No ID address. App must call settings_load()
    00> [00:00:00.020,843] <inf> central_uart: Bluetooth initialized
    00> [00:00:00.021,606] <inf> bt_hci_core: Identity: F2:76:DA:88:7F:67 (random)
    00> [00:00:00.021,636] <inf> bt_hci_core: HCI: version 6.0 (0x0e) revision 0x106b, manufacturer 0x0059
    00> [00:00:00.021,667] <inf> bt_hci_core: LMP: version 6.0 (0x0e) subver 0x106b
    00> [00:00:00.022,888] <inf> central_uart: Scan module initialized
    00> [00:00:00.027,709] <inf> central_uart: Scanning successfully started L913
    00> [00:00:00.178,436] <inf> central_uart: Filters matched. Address: C1:69:8A:90:CB:FC (random) connectable: 0
    00> [00:00:00.282,379] <inf> central_uart: Connected: C1:69:8A:90:CB:FC (random)
    00> [00:00:00.287,261] <err> central_uart: Stop LE scan failed (err 0)
    00> [00:00:00.483,978] <inf> central_uart: MTU exchange done
    00> [00:00:00.583,892] <err> bt_smp: pairing failed (peer reason 0x3)
    00> [00:00:00.584,136] <wrn> central_uart: Security failed: C1:69:8A:90:CB:FC (random) level 1 err 4 
    00> [00:00:00.584,350] <wrn> central_uart: Pairing failed conn: C1:69:8A:90:CB:FC (random), reason 4 
    00> [00:00:01.283,813] <inf> central_uart: Service discovery completed
    00> [00:00:01.285,034] <inf> central_uart: Scanning successfully started L492
    00> [00:00:01.383,819] <err> os: ***** USAGE FAULT *****
    00> [00:00:01.383,819] <err> os:   Illegal use of the EPSR
    00> [00:00:01.383,850] <err> os: r0/a1:  0x20002650  r1/a2:  0x20010450  r2/a3:  0x00000000
    00> [00:00:01.383,850] <err> os: r3/a4:  0x00000000 r12/ip:  0x20010464 r14/lr:  0x0002f739[0m
    00> [00:00:01.383,880] <err> os:  xpsr:  0x60000000
    00> [00:00:01.383,880] <err> os: Faulting instruction address (r15/pc): 0x00000000
    00> [00:00:01.383,911] <err> os: >>> ZEPHYR FATAL ERROR 35: Unknown error on CPU 0
    00> [00:00:01.383,941] <err> os: Current thread: 0x200025b0 (unknown)
    00> [00:00:01.662,384] <err> os: Halting system
  • Hello,

    Can you upload the application that is crashing? 

    I am sorry, but I am not aware of any other samples that automatically connects to several peripherals. 

    I would say that the default central_uart sample is also a good starting point. It is mostly a matter of setting CONFIG_BT_MAX_CONN=2, or to however many you need. In addition, you need to adapt the application to handle multiple connections. The connection handle that is set when connecting needs to be an array of connection handles, so that you can address the different connections, send messages to them, and see what connected device that sent you a message. 

    Also make sure to resume scanning after connecting to a device as long as your number of connections are less than CONFIG_BT_MAX_CONN.

    Best regards,

    Edvin

Reply
  • Hello,

    Can you upload the application that is crashing? 

    I am sorry, but I am not aware of any other samples that automatically connects to several peripherals. 

    I would say that the default central_uart sample is also a good starting point. It is mostly a matter of setting CONFIG_BT_MAX_CONN=2, or to however many you need. In addition, you need to adapt the application to handle multiple connections. The connection handle that is set when connecting needs to be an array of connection handles, so that you can address the different connections, send messages to them, and see what connected device that sent you a message. 

    Also make sure to resume scanning after connecting to a device as long as your number of connections are less than CONFIG_BT_MAX_CONN.

    Best regards,

    Edvin

Children
  • Hi,

    After some modifications based on multi-NUS sample, the two peripheral_uart can connect to central_uart now, but the central_uart is failed to send nus_index in 

    discovery_complete(). I check the ctx-> data is not equal to nus in line 39
    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);
    
    	int err = bt_scan_start(BT_SCAN_TYPE_SCAN_ACTIVE);
    	if (err) {
    		LOG_ERR("Scanning failed to start (err %d)", err);
    	} else {
    		LOG_INF("Scanning started");
    	}
    
    	/*	Send a message to the new NUS server informing it of its ID in this 
    	*	mini-network
    	*	The new NUS will have been added to the connection context library and so
    	* 	will be the highest index as they are added incrementally upwards.
    	*	This is a bit of a workaround because in this function, I don't know
    	*	the ID of this connection which is the piece of info I want to transmit.
    	*/
    	size_t num_nus_conns = bt_conn_ctx_count(&conns_ctx_lib);
    	size_t nus_index = 99;
    
    	/*	This is a little inelegant but we must get the index of the device to
    	* 	convey it
    	*/
    	for (size_t i = 0; i < num_nus_conns; i++) {
    		const struct bt_conn_ctx *ctx =
    			bt_conn_ctx_get_by_id(&conns_ctx_lib, i);
    
    		if (ctx) {
    			if (ctx->data == nus) {
    				nus_index = i;
    				char message[3];
    				sprintf(message, "%d", nus_index);
    				message[2] = '\r';
    				int length = 3;
    
    				err = bt_nus_client_send(nus, message, length);
    				if (err) {
    					LOG_WRN("Failed to send data over BLE connection"
    						"(err %d)",
    						err);
    				} else {
    					LOG_INF("Sent to server %d: %s",
    						nus_index, message);
    				}
    
    				bt_conn_ctx_release(&conns_ctx_lib,
    						    (void *)ctx->data);
    				break;
    			} else {
    				bt_conn_ctx_release(&conns_ctx_lib,
    						    (void *)ctx->data);
    			}
    		}
    	}
    }
    so it won't run bt_nus_client_send().
    The call stack is as the following,
    Where is it passing context parameter to discovery_complete() to trace the value?
  • Can you please zip and send the entire central application that you are testing?

    BR,
    Edvin

  • Hi,

    The key difference is in gatt_discover(), the NCS v2.9.0 is

    	err = bt_gatt_dm_start(conn,
    			       BT_UUID_NUS_SERVICE,
    			       &discovery_cb,
    			       &nus_client);

    the multi-NUS version is,

     
    	err = bt_gatt_dm_start(conn,
    					BT_UUID_NUS_SERVICE,
    					&discovery_cb,
    					nus_client);
    the multi-NUS version does not have the address-of operator before nus_client, 
    so how to fix multi-NUS sample to run in NCS v2.9.0?
    I attached the project on the following,
  • Hello,

    I ran the sample that you attached just now, and this is what it prints over UART:

    *** Booting nRF Connect SDK v2.9.0-7787b2649840 ***
    *** Using Zephyr OS v3.7.99-1f8f3dc29142 ***
    felix multi-nus central
    Starting Bluetooth Central UART example
    central : Connected CE:20:8F:A0:A2:4D (random), number: 0
    num_nus_conns = 3, L687
    NUS Client module initializedService discovery completed
    Scanning successfully started L514
    ctx->data = 0x2000a0e8
    nus=0x2000a0e8
    i = 0
    central : Connected E3:4A:AE:AC:E6:6E (random), number: 1
    num_nus_conns = 3, L687
    NUS Client module initializedService discovery completed
    Scanning successfully started L514
    ctx->data = 0x2000a0e8
    nus=0x2000a12c
    ctx->data != nus
    ctx->data = 0x2000a12c
    nus=0x2000a12c
    i = 1
    

    Then the two devkits programmed with the default peripheral_uart prints this:

    *** Booting My Application v2.9.0-0a85ae2bebaa ***
    *** Using nRF Connect SDK v2.9.0-7787b2649840 ***
    *** Using Zephyr OS v3.7.99-1f8f3dc29142 ***
    Starting Nordic UART service example
    00
    

    *** Booting My Application v2.9.0-0a85ae2bebaa ***
    *** Using nRF Connect SDK v2.9.0-7787b2649840 ***
    *** Using Zephyr OS v3.7.99-1f8f3dc29142 ***
    Starting Nordic UART service example
    00
    

    So apparently, it does pass the check "if (ctx->data == nus)"

    James168 said:
    the multi-NUS version does not have the address-of operator before nus_client, 

    The reason for this is that in the multinus application, the nus_client is declared as a pointer, so it already points to the address. 

    Do you not see the same log?

    Best regards,

    Edvin

  • I don't notice they are declared for different type, one is non-pointer,

    static struct bt_nus_client nus_client;
    the other is pointer,
    struct bt_nus_client *nus_client = bt_conn_ctx_get(&conns_ctx_lib, conn);
    I have no problem for this issue now.
Related