Blurz Rust Library on Raspberry PI 4 Bluetooth connection with specific Device - rust

I'm making a project on raspberry pi 4 model b based on Rust, using the Blurz Rust Library.
So this is my code:
let sessionBLE = BluetoothSession::create_session(Option::None).unwrap();
let adapter: BluetoothAdapter = BluetoothAdapter::init(&sessionBLE).unwrap();
let discoverySessionBLE : BluetoothDiscoverySession = BluetoothDiscoverySession::create_session(&sessionBLE, adapter.get_id()).unwrap();
println!("IS DISCOVERABLE {}", adapter.is_discoverable().unwrap());
println!("IS POWERED {}", adapter.is_powered().unwrap());
println!("IS DISCOVERING {}", adapter.is_discovering().unwrap());
adapter.set_discoverable(true);
println!("start discovery");
discoverySessionBLE.start_discovery().unwrap();
thread::sleep(Duration::from_secs(2));
println!("<------------------------------------------>");
println!("IS DISCOVERABLE {}", adapter.is_discoverable().unwrap());
println!("IS POWERED {}", adapter.is_powered().unwrap());
println!("IS DISCOVERING {}", adapter.is_discovering().unwrap());
println!("device list number (count) {}", adapter.get_device_list().unwrap().len());
println!("device list {:?}", adapter.get_device_list().unwrap());
let devices = adapter.get_device_list().unwrap();
println!("Test array.: {}", adapter.get_device_list().unwrap()[1]);
let uuid = adapter.get_uuids().unwrap()[1].to_string();
println!("Connecting..");
let deviceBle : BluetoothDevice = adapter.get_first_device().unwrap();
deviceBle.connect(10000);
thread::sleep(Duration::from_secs(2));
println!("device BluetoothDevice {}", deviceBle.get_address().unwrap());
println!("device RSSI {}", deviceBle.get_rssi().unwrap());
println!("device CONNECETED {}", deviceBle.is_connected().unwrap());
println!("device GATT {:?}", deviceBle.get_gatt_services().unwrap());
Everything works but there's a problem, so I don't understand how to connect to specific device. This is the list found after scan:
["/org/bluez/hci0/dev_D4_CA_6E_F0_9E_21", "/org/bluez/hci0/dev_00_A0_50_FC_85_B4", "/org/bluez/hci0/dev_7D_A6_E2_28_E2_21", "/org/bluez/hci0/dev_7D_26_EF_98_8A_F4", "/org/bluez/hci0/dev_42_14_C3_01_25_25", "/org/bluez/hci0/dev_75_0C_80_CD_04_1E", "/org/bluez/hci0/dev_52_71_26_A1_52_AA", "/org/bluez/hci0/dev_40_C3_D7_65_CC_BF", "/org/bluez/hci0/dev_72_95_B8_34_82_A3", "/org/bluez/hci0/dev_6B_99_43_81_D0_31", "/org/bluez/hci0/dev_45_66_4A_20_7C_0B", "/org/bluez/hci0/dev_46_99_9C_AA_BD_36", "/org/bluez/hci0/dev_CC_52_AF_CB_49_12", "/org/bluez/hci0/dev_C4_22_C6_95_9C_1A", "/org/bluez/hci0/dev_00_A0_50_FC_18_46", "/org/bluez/hci0/dev_00_A0_50_FD_15_F4"]
I can't connect to a specific device, I need to connect with the third device: "7D_A6_E2_28_E2_21" but from the documentation I don't understand how to, I see there's a single method:
let deviceBle : BluetoothDevice = adapter.get_first_device().unwrap();
But it gets only the first device, what can I do to get the third device?

It is pretty straightforward with BluetoothDevice::new:
let device_3 = BluetoothDevice::new(&sessionBLE, devices[2].clone());

Related

Dynamically add rtmp source to running pipeline using compositor

The bounty expires in 5 days. Answers to this question are eligible for a +50 reputation bounty.
Adam Burke wants to draw more attention to this question.
I have a very simple Gstreamer pipeline looking like this
uridecodebin -> compositor -> videoconvert -> autovideosink
I want to be able at any time to add a new uridecodebin on the compositor.
If I add the second source before the pipeline is running, it works fine, but if I delay the addition of the second source, pipeline gets stuck and I have tons of QoS events telling me frames are being dropped.
This issue does not occur if I only read non-live sources, but it happens with my RTMP streams, or if I mix live and non-live sources.
When sync=false is set on the sync, RTMP streams are played, but it does not work with non-live sources.
My assumption is that I am missing a step with time/clock/latency, but I don't know what.
Here is the code (in rust) used to add a new source :
fn connect_pad_added(src_pad: &gst::Pad, src: &gst::Element, compositor: &gst::Element) {
println!("Received new pad {} from {}", src_pad.name(), src.name());
let new_pad_caps = src_pad
.current_caps()
.expect("Failed to get caps of new pad.");
let new_pad_struct = new_pad_caps
.structure(0)
.expect("Failed to get first structure of caps.");
let new_pad_type = new_pad_struct.name();
let is_video = new_pad_type.starts_with("video/x-raw");
if !is_video {
println!(
"It has type {} which is not raw video. Ignoring.",
new_pad_type
);
return;
}
println!("Created template");
let sink_pad = compositor
.request_pad_simple("sink_%u")
.expect("Could not get sink pad from compositor");
println!("Got pad");
if sink_pad.is_linked() {
println!("We are already linked. Ignoring.");
return;
}
if sink_pad.name() == "sink_0" {
sink_pad.set_property("width", 1920i32);
sink_pad.set_property("height", 1080i32);
} else {
sink_pad.set_property("alpha", 0.8f64);
}
let res = src_pad.link(&sink_pad);
if res.is_err() {
println!("Type is {} but link failed.", new_pad_type);
} else {
println!("Link succeeded (type {}).", new_pad_type);
}
}
fn add_new_element(pipeline: &gst::Pipeline, uri: &str) {
println!("Adding new element");
let source = gst::ElementFactory::make("uridecodebin")
.property("uri", uri)
.build()
.unwrap();
let compositor = pipeline.by_name("compositor").unwrap();
pipeline.add(&source).unwrap();
source.connect_pad_added(move |src, src_pad| {
println!("Received new pad {} from {}", src_pad.name(), src.name());
connect_pad_added(src_pad, src, &compositor);
});
source
.set_state(gst::State::Paused)
.expect("Unable to set the uridecodebin to the `Paused` state");
println!("Added new element");
}

Winapi connect to bluethooth failed,error OS code 10060 or 10049

I'm programing a rust project to connecting bluetooth device and communicating with it on Windows, and use a lib named "IO-BLUETOOTH-MASTER", the only dependency is WinAPI.
Now discovey device is working well. But connecting to device is a big problem. As usual, connecting t o Iphone and Android are OK, but when I connected to an embedded ble chip, it will working wrong.
The main code is :
fn main() -> io::Result<()> {
let devices = bt::discover_devices()?;
println!("Devices:");
for (idx, device) in devices.iter().enumerate() {
println!("{}: {}", idx, *device);
}
if devices.len() == 0 {
return Err(io::Error::new(
io::ErrorKind::NotFound,
"No Bluetooth devices found.",
));
}
let device_idx = request_device_idx(devices.len())?;
let socket = BtStream::connect(iter::once(&devices[device_idx]),
bt::BtProtocol::RFCOMM)?;
}
You can see this demo is using Sock to listening to message and bluetooth stream, and address is bond to 127.0.0.1, I can sure that I didn't make this mistake, but the error had occured.
Error: Os { code: 10060, kind: TimedOut, message: "A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond." }
Sometime the error is : Code 10049, but when I change a chip, the error is always 10060
Error: Os { code: 10049, kind: AddrNotAvailable, message: "The requested address is not valid in its context." }
The connect method code is :
pub struct BtStream {
inner: Socket,
protocol: BtProtocol,
}
impl BtStream {
pub fn connect(addr: &BtAddr, protocol: BtProtocol) -> io::Result<Self> {
let (addr, len) = addr.into();
let socket = Socket::new(protocol)?;
cvt_r(|| unsafe { c::connect(*socket.as_inner(), &addr as *const _ as *const _, len) })?;
Ok(Self {
inner: socket,
protocol,
})
}
}
Also I can sure that the chip's fuction is OK, I can use the ble tool to communicate with it on Android, and using the BlueTooth privide by Windows is also can't connect to the device, but they can paired.
I'm just a beginner for rust, please be tolerant if I made some ridiculous mistake

Fail to pass raw pointer to ioctl in Rust 1.56.1 using Nix 0.23.1

I'm trying to pass a file descriptor to the ioctl system call.
I'm trying to link a loop device to a file so I can then set an offset so I can mount it correctly.
I have the following code snippet:
use std::fs::{File, OpenOptions};
use std::os::unix::io::{AsRawFd, RawFd};
use nix::{ioctl_none, ioctl_write_ptr};
use crate::consts::consts::{MAGIC_NUMBER_SIZE, SIGNATURE_SIZE};
const LOOP_MAGIC_BIT: u8 = 0x4C;
const LOOP_SET_FD: u32 = 0x4C00;
const LOOP_SET_STATUS64: u32 = 0x4C04;
const LOOP_CTL_GET_FREE: u32 = 0x4C82;
ioctl_none!(loopback_read_free_device, LOOP_MAGIC_BIT, LOOP_CTL_GET_FREE);
ioctl_write_ptr!(loopback_set_device_fd, LOOP_MAGIC_BIT, LOOP_SET_FD, RawFd);
ioctl_write_ptr!(loopback_set_device_info, LOOP_MAGIC_BIT, LOOP_SET_STATUS64, LoopbackInfo);
pub struct Loopback {}
pub struct LoopbackInfo {
pub io_offset: u64
}
impl Loopback {
pub fn mount_loopback_device(file_path: &str) -> String {
// Open the loopback control device
let loopback_control = File::open("/dev/loop-control");
// Check if is open correctly
match loopback_control {
Ok(control_fd) => {
unsafe {
// Format the device path
let result = format!("/dev/loop{}", loopback_read_free_device(control_fd.as_raw_fd()).unwrap());
// Open the device and the container
let container_file = OpenOptions::new().read(true).write(false).open(file_path);
let device_file = OpenOptions::new().read(true).write(true).open(result.clone());
// Check if is open correctly both files
if container_file.is_err() {
panic!("[Error]: Failed to open the application file");
}
if device_file.is_err() {
panic!("[Error]: Failed to open the device file");
}
// Get the raw pointer from the files
let raw_device_fd = device_file.unwrap().as_raw_fd();
let raw_container_fd = container_file.unwrap().as_raw_fd();
// Match file and device
match loopback_set_device_fd(raw_device_fd, raw_container_fd as *const RawFd) {
Ok(_) => {
// Prepare new offset
let loop_info = LoopbackInfo {
io_offset : (MAGIC_NUMBER_SIZE + SIGNATURE_SIZE) as u64 // Magic Number + Signature Offset
};
// Set the device information
loopback_set_device_info(raw_device_fd, &loop_info as *const LoopbackInfo).unwrap();
// Return the path of the prepared device
return result.clone();
}
Err(code) => panic!("[Error]: Failed to associate the file with the device, error code: {}", code)
}
}
}
Err(_) => panic!("[Error]: It is impossible to get control over the loopback device")
}
}
}
When I try to execute these lines, I get the following panic:
[Error]: Failed to associate the file with the device, error code: EBADF: Bad file number
It seems that it is a bug in the rust nix library, I will try to report it shortly. I changed ioctl_write_ptr to ioctl_write_ptr_bad and it worked as it should.

TcpListener stuck in accept() even when client thinks the connection is already established

let addr: SocketAddr = self.listen_bind.parse().unwrap();
let mut listener = TcpListener::bind(&addr).await?;
info!("Nightfort listening on {}", addr);
loop {
info!("debug1");
match listener.accept().await {
Ok((stream, addr)) => {
info!("debug2");
let watcher = self.watcher.clone();
info!("debug3");
tokio::spawn(async move {
info!("debug4");
if let Err(e) = Nightfort::process(watcher, stream, addr).await {
error!("Error on this ranger: {}, error: {:?}", addr, e);
}
});
}
Err(e) => error!("Socket conn error {}", e),
}
// let (stream, addr) = listener.accept().await?;
}
I spent two days on troubleshooting this weird issue. The process in rust can run very well on my local macos, linux, docker on linux, but can not run on aws linux or k8s on aws. The main issues I found is: the process hang on accept() even a client thinks it established a connection to the server and started sending messages to it. ps show the the server process is in S status. The code was written in nightly rust with alpha libs, and I thought there might be a bug in the dependency, then I updated my code and switch it to stable rust with the latest release of dependencies, but the issue is still there.

Reading Serial Data Stream from Bluetooth 5 LE DevBoard via Rumble

I am trying to read a serial data stream coming from a bluetooth low energy devboard. The firmware registers as UART emulation service ( custom UUID ), and sends data via Receive_Characteristic ( custom UUID ). The serial data being send is just an incrementing numbers.
Using rumble, I am able to form a connection to the device, and read something, but not the stream. What follows is a minimal working code example:
let manager = Manager::new().unwrap();
let mut adapter = manager
.adapters()
.expect("could not list adapters")
.into_iter()
.find(|a| a.name == self.adapter_name)
.expect("could not find adapter by name");
println!("power cycle adapter");
adapter = manager.down(&adapter).unwrap();
adapter = manager.up(&adapter).unwrap();
println!("connect adapter");
let central = adapter.connect().unwrap();
central.start_scan().unwrap();
println!(
"find desired {:?} peripheral...",
&self.device_name
);
// keep scanning for 10 s
std::thread::sleep(std::time::Duration::from_secs(1));
central.stop_scan().unwrap();
let peripherals = central.peripherals();
let mdevice = central
.peripherals()
.into_iter()
.find(|perf| {
perf.properties()
.local_name
.iter()
.any(|name| name.contains(&self.device_name))
})
.expect("could not find peripheral by name");
std::thread::sleep(std::time::Duration::from_secs(1));
match mdevice.connect() {
Ok(d) => {
println!("mdevice connected");
d
}
Err(err) => {
eprintln!("error connecting to mdevice: {:?}", err);
panic!()
}
};
std::thread::sleep(std::time::Duration::from_secs(1));
println!("discovering characteristics");
for ch in mdevice.discover_characteristics().unwrap().into_iter() {
println!("found characteristic: {:?}", ch);
}
std::thread::sleep(std::time::Duration::from_secs(1));
println!("get desired characteristic");
let receive_characteristic = mdevice
.discover_characteristics()
.unwrap()
.into_iter()
.find(|c| {
RECEIVE_CHARACTERISTIC == c.uuid
})
.expect("could not find given characteristic");
// this is some testing code to print out received data
let (tx, rx) = std::sync::mpsc::channel();
std::thread::spawn(move || loop {
let data = match mdevice.read(&receive_characteristic) {
Ok(d) => d,
Err(err) => { println!("received an error {:?}", err);
Vec::new()}
};
println!("send : {:02?}", data);
match tx.send(data) {
Ok(d) => d,
Err(e) => println!("error {:?}", e)
};
});
loop {
let dd = rx.recv();
println!("received : {:02?}", dd.unwrap());
}
Ok(())
Using rumble, I am able to connect to the device, but getting a stream is weird. I keep getting the same number in a vec, but get sometimes a number being in range of the increment. Is reading the serial stream being done correctly?
EDIT: I am currently using the nRF52840-DK development board. The firmware sends out incrementing numbers from 0 to 255, and then repeats the sequence.
Solved it.
The main problem was, that I didn't fully understand the GATT profile and thus the Bluetooth LE protocol. This resource gives a good introduction into this topic.
The solution is to subscribe to data (event) updates, after the device has been connected and register an event handler, that reacts to incoming data. It was that simple.
// ... same code as before, but only the relevant pieces are shown.
mdevice.connect().expect("Could not connect to device");
std::thread::sleep(std::time::Duration::from_secs(1));
let chars = mdevice.discover_characteristics()
.expect("Discovering characteristics failed");
std::thread::sleep(std::time::Duration::from_secs(1));
let receive_characteristic = chars.clone().into_iter()
.find(|c|
{
// The constant is just a fixed array
RECEIVE_CHARACTERISTIC == c.uuid
}).expect("Could not find given characteristic");
// subscribe to the event
mdevice.subscribe(&receive_characteristic)
mdevice.on_notification(Box::from(move |v : rumble::api::ValueNotification|
{
// do something with the received data
}));

Resources