蓝牙编程在树莓派4使用Bluez和dbus.每件事都很好,只是RPI需要90多毫秒才能收到8个浮子



我正在进行一个项目,其中Rpi将从Arduino nano-ble接收8个浮点值。Arduino作为外围设备,通过蓝牙发送8个浮点值。Arduino代码如下所示。

#include <Arduino_LSM9DS1.h>  // C:Users16312DocumentsArduinolibrariesArduino_LSM9DS1src
#include <ArduinoBLE.h>
String localName = "testPeripheral";
#define BLE_UUID_SENSOR_DATA_SERVICE              "2BEEF31A-B10D-271C-C9EA-35D865C1F48A"
#define BLE_UUID_MULTI_SENSOR_DATA                "4664E7A1-5A13-BFFF-4636-7D0A4B16496C"
#define NUMBER_OF_SENSORS 8
union multi_sensor_data
{
struct __attribute__( ( packed ) )
{
float values[NUMBER_OF_SENSORS];
};
uint8_t bytes[ NUMBER_OF_SENSORS * sizeof( float ) ];
};
union multi_sensor_data multiSensorData;
BLEService sensorDataService( BLE_UUID_SENSOR_DATA_SERVICE );
BLECharacteristic multiSensorDataCharacteristic( BLE_UUID_MULTI_SENSOR_DATA, BLERead | BLENotify, sizeof multiSensorData.bytes );
static long previousMillis = 0;
#define UPDATE_INTERVALL 50
float iter;
float dt;
void setup() {
Serial.begin(9600);
//while (!Serial);
initBLE();
if (!IMU.begin()) {
Serial.println("Failed to initialize IMU!");
while (1);
}
iter = 0;
}

void serial_print_xyz(float x, float y, float z) {
int num_digits = 6;
Serial.print(x, num_digits);  Serial.print('t');
Serial.print(y, num_digits);  Serial.print('t');
Serial.print(z, num_digits);  Serial.print('t');
}
void serial_print_1(float val) {
int num_digits = 6;
Serial.print(val, num_digits);  Serial.print('t');
}
void serial_print_1(int val) {
Serial.print(val);  Serial.print('t');
}
void serial_print_endln() {
Serial.print("end");   Serial.println();
}

void loop() {
//comment below line to remove serial print of master address
dt = millis()-previousMillis;
float acc_x, acc_y, acc_z;
float mag_x, mag_y, mag_z;
// Magnetic field seems to read at a slower rate than acceleration?
if (IMU.accelerationAvailable() && IMU.magneticFieldAvailable()) {
if ( millis() - previousMillis > UPDATE_INTERVALL )
{
previousMillis =  millis();
// Read measurements
IMU.readAcceleration(acc_x, acc_y, acc_z);
IMU.readMagneticField(mag_x, mag_y, mag_z);
iter++;
// Export measurements
checkCentralConnect();  //check if slave is connected to Master

updateIMU(acc_x, acc_y, acc_z, mag_x, mag_y, mag_z);

Serial.print("startt");
serial_print_1(iter);
serial_print_1(dt);
serial_print_xyz(acc_x, acc_y, acc_z);
serial_print_xyz(mag_x, mag_y, mag_z);
serial_print_endln();
}
}
}
void initBLE()
{
// begin initialization
if (!BLE.begin()) {
Serial.println("starting BLE failed!");
}
// set advertised local name and service UUID:
BLE.setDeviceName( "Arduino Nano 33 BLE" );
BLE.setLocalName( "Arduino Nano 33 BLE" );
BLE.setAdvertisedService( sensorDataService );
// BLE add characteristics
sensorDataService.addCharacteristic( multiSensorDataCharacteristic );
// add service
BLE.addService( sensorDataService );
// set the initial value for the characeristic:
multiSensorDataCharacteristic.writeValue( multiSensorData.bytes, sizeof multiSensorData.bytes );
// start advertising
BLE.advertise();
String address = BLE.address();
Serial.print("Local mac address is: ");
Serial.println(address);
Serial.println("LocalName: Arduino Nano 33 BLE");
}
void checkCentralConnect()
{
// listen for BLE centrals to connect:
BLEDevice central = BLE.central();
// if a central is connected to peripheral print mac address
if (central) {
//Serial.print("Connected to central: ");
// print the central's MAC address:
//Serial.print(central.address());
}
}
void updateIMU(float accX, float accY, float accZ, float magX, float magY, float magZ)
{
multiSensorData.values[0]  = accX;
multiSensorData.values[1]  = accY;
multiSensorData.values[2]  = accZ;
multiSensorData.values[3]  = magX;
multiSensorData.values[4]  = magY;
multiSensorData.values[5]  = magZ;
multiSensorData.values[6]  = dt;
multiSensorData.values[7]  = iter;
multiSensorDataCharacteristic.writeValue( multiSensorData.bytes, sizeof multiSensorData.bytes );
}

在这段代码中,你可以看到Arduino也在串行端口上发送数据,在这8个浮点中,有2个浮点是一个循环的迭代和执行时间,所以通过检查Arduino的串行输出,我可以知道Arduino通过蓝牙发送数据需要多少时间。我发现Arduino循环大约需要50毫秒,但在这50毫秒中,IMU传感器在每个循环中需要48到49毫秒(如果我删除了用于从IMU获取值的代码,那么循环的执行时间仅为1毫秒(。Arduino通过蓝牙发送数据只需要1毫秒。但另一方面,RPI需要90多秒才能从Arduino接收到这8辆花车。我在Linux中使用BlueZ堆栈进行蓝牙通信,并使用Python Dbus模块使用Python与BlueZ进行通信。我在下面附上了蓝牙扫描仪和数据读取器代码。初始化类时,它会扫描设备,如果找到Arduino,它会与之连接。初始化完成后,代码开始读取特征值。我使用while循环来连续读取值,但我不明白为什么RPI只读取8个浮点值就需要90毫秒以上(请查看python代码底部的主循环代码(。我对python和Dbus编程很陌生,请帮我弄清楚如何在RPI中提高蓝牙数据传输速度。如果有另一种方法,而我使用的是错误的或旧的方法,请提供一个文档链接,我可以通过它了解问题所在。

#!/usr/bin/python3
from gi.repository import GLib
import bluetooth_utils
import bluetooth_constants
import dbus
import dbus.mainloop.glib
import sys
#import pydbus
import struct
from allDefData import defData
sys.path.insert(0, '.')
adapter_interface = None
mainloop = None
timer_id = None
devices = {}
class bleScan():
def __init__(self, defD):
# dbus initialisation steps
self.defD = defD
self.adapter_path = bluetooth_constants.BLUEZ_NAMESPACE + bluetooth_constants.ADAPTER_NAME
self.ArdunoBleD = lambda: None
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
self.bus = dbus.SystemBus()
self.get_through_list_connected_devices()
print(hasattr(self.ArdunoBleD, 'Name'))
while(not hasattr(self.ArdunoBleD, 'Name')):
print("Scanning")
self.discover_devices(self.bus, 2000)
if hasattr(self.ArdunoBleD, 'Name') and defD.ArdBleName in self.ArdunoBleD.Name:
print("Scanning Stoped")
break
else:
print("Not found Rescanning")
char_proxy = self.bus.get_object(bluetooth_constants.BLUEZ_SERVICE_NAME, '/org/bluez/hci0/dev_1B_4E_C8_D6_CC_06/service000a/char000b')
self.char_interface = dbus.Interface(char_proxy, bluetooth_constants.GATT_CHARACTERISTIC_INTERFACE)

def Som(self, interfaces, path):
if bluetooth_constants.GATT_SERVICE_INTERFACE in interfaces:
properties = interfaces[bluetooth_constants.GATT_SERVICE_INTERFACE]
print("-------------------------------------------------------------- ------------------")
print("SVC path :", path)
if 'UUID' in properties:
uuid = properties['UUID']
if uuid == bluetooth_constants.TEMPERATURE_SVC_UUID:
found_ts = True
ts_path = path
print("SVC UUID : ", bluetooth_utils.dbus_to_python(uuid))
print("SVC name : ", bluetooth_utils.get_name_from_uuid(uuid))
return
if bluetooth_constants.GATT_CHARACTERISTIC_INTERFACE in interfaces:
properties = interfaces[bluetooth_constants.GATT_CHARACTERISTIC_INTERFACE]
print(" CHR path :", path)
if 'UUID' in properties:
uuid = properties['UUID']
if uuid == bluetooth_constants.TEMPERATURE_CHR_UUID:
found_tc = True
tc_path = path
print(" CHR UUID : ", bluetooth_utils.dbus_to_python(uuid))
print(" CHR name : ", bluetooth_utils.get_name_from_uuid(uuid))
flags = ""
for flag in properties['Flags']:
flags = flags + flag + ","
print(" CHR flags : ", flags)
return
if bluetooth_constants.GATT_DESCRIPTOR_INTERFACE in interfaces:
properties =interfaces[bluetooth_constants.GATT_DESCRIPTOR_INTERFACE]
print(" DSC path :", path)
if 'UUID' in properties:
uuid = properties['UUID']
print(" DSC UUID : ", bluetooth_utils.dbus_to_python(uuid))
print(" DSC name : ", bluetooth_utils.get_name_from_uuid(uuid))
return
def get_through_list_connected_devices(self):
bus = self.bus
proxy = bus.get_object(bluetooth_constants.BLUEZ_SERVICE_NAME, '/')
interface = dbus.Interface(proxy, bluetooth_constants.DBUS_OM_IFACE)
mngd_objs = interface.GetManagedObjects()
for path, interfaces in mngd_objs.items():
print(path)
for ainterface in interfaces:
print(ainterface)
if ainterface == bluetooth_constants.DEVICE_INTERFACE:
device_properties = interfaces[bluetooth_constants.DEVICE_INTERFACE]
#if path not in devices:
devices[path] = device_properties
dev = devices[path]
try:
adr_name_Conn_inDev = ('Address' and 'Name' and 'Connected' in dev)
arduinoName_isCorrect = self.defD.ArdBleName in bluetooth_utils.dbus_to_python(dev['Name'])
isConnected = bluetooth_utils.dbus_to_python(device_properties['Connected'])
except:
adr_name_Conn_inDev = False
arduinoName_isCorrect = False
isConnected = False
if adr_name_Conn_inDev and arduinoName_isCorrect and isConnected:
self.ArdunoBleD.Name = bluetooth_utils.dbus_to_python(dev['Name'])
self.ArdunoBleD.Address = bluetooth_utils.dbus_to_python(dev['Address'])
#self.ArdunoBleD.RSSI = bluetooth_utils.dbus_to_python(dev['RSSI'])
self.ArdunoBleD.Path = path
self.Som(interfaces , path)
print("Found: Arduino Nano Ble in Connected Dev")
elif adr_name_Conn_inDev and arduinoName_isCorrect and not isConnected:
print("Not found in connected Devices")
print("Connecting...")
self.connect(path)
if self.isConnected(path):
self.ArdunoBleD.Name = bluetooth_utils.dbus_to_python(dev['Name'])
self.ArdunoBleD.Address = bluetooth_utils.dbus_to_python(dev['Address'])
# self.ArdunoBleD.RSSI = bluetooth_utils.dbus_to_python(dev['RSSI'])
self.ArdunoBleD.Path = path
self.Som(interfaces, path)

# if 'Address' in dev:
#     print("EXI bdaddr: ", bluetooth_utils.dbus_to_python(device_properties['Address']))
# if 'Name' in device_properties:
#     print("Name: ", bluetooth_utils.dbus_to_python(device_properties['Name']))
# if 'Connected' in device_properties:
#     print("Con: ", bluetooth_utils.dbus_to_python(device_properties['Connected']))


def isConnected(self, device_path):
bus = self.bus
proxy = bus.get_object(bluetooth_constants.BLUEZ_SERVICE_NAME, device_path)
interface = dbus.Interface(proxy, 'org.freedesktop.DBus.Properties')
print("----------------")
return interface.Get(bluetooth_constants.DEVICE_INTERFACE, 'Connected')

def connect(self, device_path):
bus = self.bus
device_proxy = bus.get_object(bluetooth_constants.BLUEZ_SERVICE_NAME, device_path)
device_interface = dbus.Interface(device_proxy, bluetooth_constants.DEVICE_INTERFACE)
#p_interface = dbus.Interface(device_proxy, bluetooth_constants.DEVICE_INTERFACE)
try:
device_interface.Connect()
except Exception as e:
print("Failed to connect")
print(e.get_dbus_name())
print(e.get_dbus_message())
if ("UnknownObject" in e.get_dbus_name()):
print("Try scanning first to resolve this problem")
return bluetooth_constants.RESULT_EXCEPTION
else:
print("Connected OK")
return bluetooth_constants.RESULT_OK
def interfaces_added(self, path, interfaces):
# interfaces is an array of dictionary entries
if not bluetooth_constants.DEVICE_INTERFACE in interfaces:
return
device_properties = interfaces[bluetooth_constants.DEVICE_INTERFACE]
print(path)
if path not in devices:
print("NEW path :", path)
devices[path] = device_properties
dev = devices[path]
if ('Address' and 'Name' and 'RSSI' in dev) and self.defD.ArdBleName in bluetooth_utils.dbus_to_python(
dev['Name']):
self.connect(path)
if self.isConnected(path):
self.ArdunoBleD.Name = bluetooth_utils.dbus_to_python(dev['Name'])
self.ArdunoBleD.Address = bluetooth_utils.dbus_to_python(dev['Address'])
self.ArdunoBleD.RSSI = bluetooth_utils.dbus_to_python(dev['RSSI'])
self.ArdunoBleD.Path = path
self.Som(interfaces, path)
# self.ArdunoBleD.Name = bluetooth_utils.dbus_to_python(dev['Name'])
# self.ArdunoBleD.Address = bluetooth_utils.dbus_to_python(dev['Address'])
# self.ArdunoBleD.RSSI = bluetooth_utils.dbus_to_python(dev['RSSI'])
# self.ArdunoBleD.Path = path
print("Found: Arduino Nano Ble")
self.discovery_timeout()
print("------------------------------")
def interfaces_removed(self, path, interfaces):
# interfaces is an array of dictionary strings in this signal
if not bluetooth_constants.DEVICE_INTERFACE in interfaces:
return
if path in devices:
dev = devices[path]
if 'Address' in dev:
print("DEL bdaddr: ",
bluetooth_utils.dbus_to_python(dev['Address']))
else:
print("DEL path : ", path)
print("Rem",path)
del devices[path]
def discovery_timeout(self):
global adapter_interface
global mainloop
global timer_id
GLib.source_remove(timer_id)
mainloop.quit()
adapter_interface.StopDiscovery()
bus = dbus.SystemBus()
bus.remove_signal_receiver(self.interfaces_added, "InterfacesAdded")
bus.remove_signal_receiver(self.interfaces_removed, "InterfacesRemoved")
return True
def read_temperature(self):

char_interface = self.char_interface
try:
self.value = char_interface.ReadValue({})
except Exception as e:
print("Failed to read temperature")
print(e.get_dbus_name())
print(e.get_dbus_message())
return bluetooth_constants.RESULT_EXCEPTION
# else:
#     self.temperature = bluetooth_utils.dbus_to_python(self.value[0])
#     #print("Temperature=" + str(self.temperature ) + "C")
#     return bluetooth_constants.RESULT_OK

def discover_devices(self, bus, timeout):
global adapter_interface
global mainloop
global timer_id

# acquire an adapter proxy object and its Adapter1 interface so we can
# call its methods
adapter_object = bus.get_object(bluetooth_constants.BLUEZ_SERVICE_NAME, self.adapter_path)
adapter_interface = dbus.Interface(adapter_object, bluetooth_constants.ADAPTER_INTERFACE)
# register signal handler functions so we can asynchronously report
# discovered devices
# InterfacesAdded signal is emitted by BlueZ when an advertising packet
# from a device it doesn't
# already know about is received
bus.add_signal_receiver(self.interfaces_added, dbus_interface=bluetooth_constants.DBUS_OM_IFACE,
signal_name="InterfacesAdded")
bus.add_signal_receiver(self.interfaces_removed,
dbus_interface=bluetooth_constants.DBUS_OM_IFACE,
signal_name="InterfacesRemoved")
mainloop = GLib.MainLoop()
timer_id = GLib.timeout_add(timeout, self.discovery_timeout)
adapter_interface.StartDiscovery(byte_arrays=True)
mainloop.run()
import time
if __name__ == '__main__':
defD = defData()
Dev = bleScan(defD)
while(True):
if hasattr(Dev.ArdunoBleD, 'Name') and Dev.isConnected(Dev.ArdunoBleD.Path):
time1 = time.time()
Dev.read_temperature()
print("hmm", Dev.value)
s = struct.unpack('8f', bytearray(Dev.value))
print(s)
dt2 = time.time() - time1
print(dt2)
else:
print("Er")

Arduino的输出为波纹管

start   309.000000  52.000000   0.033325    0.993530    -0.032715   0.329590    -1.257324   -1.013184   end
start   310.000000  50.000000   0.033569    0.993408    -0.034668   0.305176    -1.354980   -2.331543   end

开始和结束之间的8个数字是Arduino通过蓝牙发送的8个浮点,第一个浮点是迭代,第二个是一个循环的执行时间,单位为ms。

Python代码的输出如下所示。

hmm dbus.Array([dbus.Byte(0), dbus.Byte(0), dbus.Byte(234), dbus.Byte(60), dbus.Byte(0), dbus.Byte(56), dbus.Byte(126), dbus.Byte(63), dbus.Byte(0), dbus.Byte(128), dbus.Byte(101), dbus.Byte(189), dbus.Byte(0), dbus.Byte(128), dbus.Byte(59), dbus.Byte(62), dbus.Byte(0), dbus.Byte(32), dbus.Byte(25), dbus.Byte(191), dbus.Byte(0), dbus.Byte(224), dbus.Byte(43), dbus.Byte(191), dbus.Byte(0), dbus.Byte(0), dbus.Byte(72), dbus.Byte(66), dbus.Byte(0), dbus.Byte(104), dbus.Byte(178), dbus.Byte(69)], signature=dbus.Signature('y'))
(0.028564453125, 0.9930419921875, -0.0560302734375, 0.18310546875, -0.59814453125, -0.67138671875, 50.0, 5709.0)
0.09242081642150879
----------------

hmm dbus.Array([dbus.Byte(0), dbus.Byte(0), dbus.Byte(231), dbus.Byte(60), dbus.Byte(0), dbus.Byte(144), dbus.Byte(126), dbus.Byte(63), dbus.Byte(0), dbus.Byte(128), dbus.Byte(99), dbus.Byte(189), dbus.Byte(0), dbus.Byte(0), dbus.Byte(97), dbus.Byte(190), dbus.Byte(0), dbus.Byte(128), dbus.Byte(9), dbus.Byte(191), dbus.Byte(0), dbus.Byte(160), dbus.Byte(215), dbus.Byte(191), dbus.Byte(0), dbus.Byte(0), dbus.Byte(68), dbus.Byte(66), dbus.Byte(0), dbus.Byte(120), dbus.Byte(178), dbus.Byte(69)], signature=dbus.Signature('y'))
(0.0281982421875, 0.994384765625, -0.0555419921875, -0.2197265625, -0.537109375, -1.6845703125, 49.0, 5711.0)
0.092437744140625
----------------

要加快速度,请在RPi端使用指示或通知。这些是GATT客户订阅GATT服务器提供的数据的一种方式。通知是未确认的消息或更新,而指示是已确认的消息和更新。由于您将频繁更新,因此使用通知似乎是明智的。这将在MainLoop而不是While循环中完成。这包含在BlueZ GATT API中https://git.kernel.org/pub/scm/bluetooth/bluez.git/tree/doc/gatt-api.txt#n181

这应该足以让你达到你想要的速度。如果您仍然需要更多信息,则有AcquireNotify。

BlueZ D-Bus在进行通知时会经过各种操作系统层,这会降低数据更新的速度。为了克服这一点,在后来的版本中,BlueZ引入了AcquireNotify,它打开了一个套接字来绕过一些层来加快速度。

相关内容

最新更新