I have an Ubuntu PC that I want to connect to an advertising microcontroller (nrf52840) using python.
They connect just fine when the PC has already discovered the microcontroller using a command line tool like bluetoothctl. The issue is when the devices are interacting for the first time.
According to the Bluezero docs it seems like nearby_discovery() is just the function for the job. The issue is unless I set the timeout to be absurdly long, like ~45 seconds, the Central cannot be created because it hasn't been discovered.
The steps I'm taking now:
from bluezero import adapter, central
dongles = adapter.list_adapters()
dongle = adapter.Adapter(dongles[0])
dongle.nearby_discovery(timeout=45)
myCentral = central.Central(adapter_addr=dongle.address, device_addr="AA:BB:CC:DD:EE:FF")
Is there a more reliable way to discover/connect to new devices?
For what it's worth, bluetoothctl scan on seems to discover devices almost immediately.
The solution turned out to be unrelated to bluezero. I'm using JTAG to grab the MAC address and that function resets the CPU on the board, meaning that it took around 30 seconds to reboot and start advertising again. Thank you for the responses
There shouldn't be any significant time difference between Bluezero and bluetoothctl to discover a device as they are making the same API calls to bluetoothd.
Once a device is in the known list in BlueZ, then because BlueZ ignores "duplicates", it doesn't report a device very frequently in the scan results.
This is what leads to most scripts I've seen not using the nearby_discovery() because once your device is in the known list, you can connect to it without discovery.
In bluetoothctl you can type devices to get the list. In Bluezero there is not a tidy option to do this (Just not something I have thought of providing previously). If you wanted to do a test to see if the device you want to connect to is already in the list then below is my initial thought:
from bluezero import dbus_tools
def known_devices():
"""Get a list of devices Bluez already knows about"""
device_list = []
mng_objs = dbus_tools.get_managed_objects()
for path in mng_objs:
address = mng_objs[path].get('org.bluez.Device1', {}).get('Address')
if address:
device_list.append(str(address))
return device_list
def is_known_device(mac_addr):
"""Is the given Bluetooth address already a known device"""
found_devices = known_devices()
return mac_addr in found_devices
print(f'Device list: {known_devices()}')
look_list = ['DC:DB:16:6B:8C:5F', 'DC:DB:16:6B:8C:xx']
for look_up in look_list:
print(f'{look_up} is known? {is_known_device(look_up)}')
In my testing I got the following output:
Device list: ['DC:DB:16:6B:8C:5F', 'DE:82:35:E7:43:BE']
DC:DB:16:6B:8C:5F is known? True
DC:DB:16:6B:8C:xx is known? False
Related
Using BlueZ, which
is the official Linux Bluetooth stack
I'd like to know which of the below two methods are better suited for detecting a device's presence in the nearby.
To be more exact, I want to periodically scan for a Bluetooth device (not BLE => no advertisement packets are sent).
I found two ways of detecting it:
1.) Using l2ping
# l2ping BTMAC
2.) Using hcitool
# hcitool name BTMAC
Both approaches working.
I'd like to know, which approach would drain more battery of the scanned device?
Looking at solution #1 (l2ping's source):
It uses a standard socket connect call to connect to the remote device, then uses the send command to send data to it:
send(sk, send_buf, L2CAP_CMD_HDR_SIZE + size, 0)
Now, L2CAP_CMD_HDR_SIZE is 4, and default size is 44, so altogether 48 bytes are sent, and received back with L2CAP_ECHO_REQ.
For hcitool I just have found the entrypoint:
int hci_read_remote_name(int dd, const bdaddr_t *bdaddr, int len, char *name, int to);
My questions:
which of these approaches are better (less power-consuming) for the remote device? If there is any difference at all.
shall I reduce the l2ping's size? What shall be the minimum?
is my assumption correct that hci_read_remote_name also connects to the remote device and sends some kind of request to it for getting back its name?
To answer your questions:-
which of these approaches are better (less power-consuming) for the remote device? If there is any difference at all.
l2ping BTMAC is the more suitable command purely because this is what it is meant to do. While "hcitool name BTMAC" is used to get the remote device's name, "l2ping" is used to detect its presence which is what you want to achieve. The difference in power consumption is really minimal, but if there is any then l2ping should be less power consuming.
shall I reduce the l2ping's size? What shall be the minimum?
If changing the l2ping size requires modifying the source code then I recommend leaving it the same. By leaving it the same you are using the same command that has been used countless times and the same command that was used to qualify the BlueZ stack. This way there's less chance for error and any change would not result in noticeable performance or power improvements.
is my assumption correct that hci_read_remote_name also connects to the remote device and sends some kind of request to it for getting back its name?
Yes your assumption is correct. According the Bluetooth Specification v5.2, Vol 4, Part E, Section 7.1.19 Remote Name Request Command:
If no connection exists between the local device and the device
corresponding to the BD_ADDR, a temporary Link Layer connection will
be established to obtain the LMP features and name of the remote
device.
I hope this helps.
I am using the instructions of https://pimylifeup.com/raspberry-pi-rfid-rc522/ to learn how to read my rfid-rc522.
I installed all the things needed, and cloned from git all the files. I connected accordingly and double checked the wire.
The code in Write.py is
#!/usr/bin/env python
import RPi.GPIO as GPIO
import SimpleMFRC522
reader = SimpleMFRC522.SimpleMFRC522()
try:
text = raw_input('New data:')
print("Now place your tag to write")
reader.write(text)
print("Written")
finally:
GPIO.cleanup()
When I run it- The result stops after "Now place your tag to write" and nothing happens when I place my tag. Any help please? What's wrong here? The module gives red light meaning it is connected. Is it really connected? How do I know? Please help.
Edit: My title and tags were wrong in this question due to already saved data. I edited. :)
It can be multiple reasons why your reader is not retrieving any data:
The RFID cards that you are providing to the reader are not supported by the ISO 14443: Mifare Classic, 4K. (Some DesFire, etc...) are the cards you want to try. Source: https://en.wikipedia.org/wiki/ISO/IEC_14443
The MFRC522 is not receiving enough input voltage. Try to increase from 3.3V to 4V. I have tried with 5V months and the reader is still running perfectly, but be cautious. The safe range in NXP is from 2.5V to 3.6V. Source: https://www.nxp.com/docs/en/data-sheet/MFRC522.pdf
The library you are using does not support IRQ (Interruption Request). This means the process of reading UIDs is high CPU consuming and low in performance. Try to use this popular library which supports interruptions: https://github.com/ondryaso/pi-rc522 . You will need to connect another wire from the IRQ pin in the MFRC522 to one GPIO pin in the Raspberry that is free and supports reading/writing operations.
Check if SPI interfaces are enable in Raspbian. Open a terminal and run:
ls -l /dev/spi
Lastly, it could be that your reader is broken. Some chinese versions do not work as they should do. Maybe you should buy another one and try more luck.
I suppose you have connected all cables in the correct way between the MFRC522 and the Raspberry Pi. Check that again.
I want to use a SensorTag 2 so that it is sitting there broadcasting it's data (and, critically connectable over bluetooth) from when it's turned on to when\if I ever turn it off. Out of the box, the tag is set to only advertise over Bluetooth for a few mins which means that when the connection eventually fails or when the device connected to the tag boots etc etc you can't connect again without physically visiting the tag and resetting it.
I see there is firmware that the myWeatherCenter people have created that lets the tag work as a weather station by basically setting it to advertise indefinitely... frustratingly though their firmware is only the sensortag version 1 :-(
I've researched on and off for months and months now and nothing coming back.. suggesting either nobody else wants this, there is a really simple solution everyone one else knows about or ...? I can't even see anyone else asking this question really... which is worrying.
Does anyone have a firmware file that sets this setting for the sensortag 2 or know how to modify the firmware to set this setting?
It is definitely possible to change the SensorTag 2/CC2650 platform so that its behavior suits your use case. I - for example - currently use a custom firmware doing pre-processing of sensor readings on the SensorTag and sending data directly in the advertisement message indefinitely (of course you have to tweak intervals and payload to get a decent battery life).
I assume you have CCS and the sources (SensorTagApp and SensorTagStack) at hand?
I recommend flashing the current SensorTagStack first, if not done yet (though I'm not yet on 2.2 myself).
In the SensorTagApp project under Application you find SensorTag.c. In there you should change:
#define DEFAULT_DISCOVERABLE_MODE GAP_ADTYPE_FLAGS_LIMITED
to
#define DEFAULT_DISCOVERABLE_MODE GAP_ADTYPE_FLAGS_GENERAL
This is the usual advise and might already do the trick.
In my firmware I also changed (in SensorTag.c, in the SensorTag_init function):
uint16_t advertOffTime = 0;
to
uint16_t advertOffTime = 1;
Please try these changes and get back to me. If that wasn't enough I will do a more thorough comparison of the stock firmware and my custom firmware. I also recommend the more specific ti Bluetooth low energy forum.
You could find the workflow for a complete solution and the firmware for continuous advertising which I created recently, at Sensortag CC2650STK Custom Firmware Modification&Download Workflow (Continuous Advertising) using Code Composer Studio, Debugger DevPack and Flash Programmer 2 software
I want to initiate pairing on Bluez with a Bluetooth Low Energy device.
While there are some posts on how to trigger the SMP procedures using GATT, there is not much available if you do not want to use GATT.
My use case is that I want to use an encrypted link for bluetooth-6lowpan which exchanges data over L2CAP credit based mode and not ATT/GATT.
Further, I would like to use the OOB mode for SMP pairing.
Pointers on how I could trigger SMP pairing either using command line or writing a C program is appreciated.
Thank you!
I don't think it's possible to perform BLE pairing without the use of GATT commands (from the command line only). The reason for this is that security in LE is GATT-action-based. In other words, the characteristic/service permissions dictate whether you need to pair with the device or not (i.e. to read the heart rate characteristic, the device might dictate that you need to be paired first). For this, the operation would be something like:
gatttool --sec-level=high --device=00:11:22:33:44:55:66 --char-read --uuid=0x2A37
This command will establish pairing first before reading the characteristic.
As for how to perform this using a C program, You can download the BlueZ source code and have a look at what passing this "sec-level" option does. I've quickly browsed through the code and found this in utils.c:-
chan = bt_io_connect(connect_cb, NULL, NULL, &tmp_err,
BT_IO_OPT_SOURCE_BDADDR, &sba,
BT_IO_OPT_SOURCE_TYPE, BDADDR_LE_PUBLIC,
BT_IO_OPT_DEST_BDADDR, &dba,
BT_IO_OPT_DEST_TYPE, dest_type,
BT_IO_OPT_CID, ATT_CID,
BT_IO_OPT_SEC_LEVEL, sec,
BT_IO_OPT_INVALID);
where sec is set with sec = BT_IO_SEC_HIGH;
I hope this helps.
I am trying to make a BLED112 behave like an iBEacon and also advertise few more GATT services. While advertising user data for iBeacon as in Bluegiga examples works fine, I do not know how to also advertise the list of available GATT services. Any ideas are highly appreciated!
Take a look at my website for some potential help regarding using the BLED112 and Bluegiga tools: http://www.sureshjoshi.com/tag/bluegiga/
Otherwise, you shouldn't really be explicitly advertising anything. If you've set up your gatt.xml correctly, the GATT characteristics are advertised inherently (it's a BLE thing, not an explicit thing).
Are you sure you're setting them up correctly? Take a look at my BLE113 examples, specifically dealing with gatt.xml and see if there is anything helpful there: https://github.com/sureshjoshi/ble113-firmware-examples
One approach would be to use the Bluegiga dual-mode advertising as a guide and instead of the Physical Web Beacon, advertise your GATT service there. Assuming you have a 128 bit service UUID of 112233-4455-6677-8899-00AABBCCDDEEFF your advertising data would look like this:
procedure gatt_service_advertisement()
# Beacon mode
beaconMode = 1
#Stop advertisement
call gap_set_mode(0,0)
# Length
service_adv(0:1) = $11
# Incomplete list of 128 bit UUIDs (use $07 if the list is complete)
service_adv(1:1) = $06
# GATT Service UUID - should be little endian I think
service_adv(2:1) = $FF
service_adv(3:1) = $EE
...
service_adv(16:1) = $11
service_adv(17:1) = $00
# Set advertisement interval to 100ms.
# Use all three advertisement channels
call gap_set_adv_parameters(160, 160, 7)
# Set advertisement data
call gap_set_adv_data(0, 18, service_adv(0:18))
#set to advertising mode - with user data
call gap_set_mode(4, gap_undirected_connectable)
end
You can use that procedure to alternate advertisements between iBeacon and your GATT service by calling it in a repeating timer like in the linked dual-mode example.
Another approach would be to advertise the GATT service in the scan response, but without knowing more about your particular use case, it's hard to say if that's an option for you.