I'm using the btleplug crate on Windows 11 to scan for peripherals and print out a list of devices' MAC address and names. For some reason, all names are missing. The code I'm using is:
use btleplug::api::{Central, Manager as _, Peripheral as _, ScanFilter};
use btleplug::platform::{Manager};
use std::error::Error;
use std::time::Duration;
use tokio::time;
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
let manager = Manager::new().await?;
// get adapter
let adapters = manager.adapters().await?;
let central = adapters.into_iter().nth(0).unwrap();
// start scanning for devices
central.start_scan(ScanFilter::default()).await?;
time::sleep(Duration::from_secs(2)).await;
// get a list of devices that have been discovered
let devices = central.peripherals().await?;
for device in devices {
// print the address and name of each device
let properties= device.properties()
.await
.unwrap()
.unwrap();
let names: Vec<String> = properties
.local_name
.iter()
.map(|name| name.to_string())
.collect();
println!("{}: {:?}", device.address(), names);
}
Ok(())
}
And the output I get is
6E:A4:49:71:1C:01: []
D3:81:93:EA:E4:B9: []
2C:76:79:7E:4B:72: []
E9:84:B3:82:CF:5A: []
25:DB:2D:F0:7B:5F: []
17:EB:FE:FD:1C:1B: []
...
The example usage in the docs use the local_name attribute assuming it exists, and I've followed that. How do I get the names to appear?
Most Bluetooth LE devices that "advertise" their presence, however, do not transmit their "full local name" or "shortened local name". You can check this with various apps on your mobile phone, for example nRF Connect
There are several reasons for this. On the one hand, the amount of information to be transmitted in the "advertisements" is severely limited and other data may be more important. On the other hand, such a unique name can pose a major privacy problem.
Related
I am trying to process live system audio using cpal in Rust. This is a simplified version for now and part of a bigger project. In theory, the build_input_stream_raw() data_callback would return a vector of f32 amplitudes from eg. a song/video/stream. However the data (&cpal::Data) returned is always 0.0.
use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
pub fn main() {
let host = cpal::default_host();
let device = host.default_input_device().unwrap();
let config = device
.default_input_config()
.expect("Failed to get default input config");
let sample_format = config.sample_format();
let err_fn = move |err| {
eprintln!("an error occurred on stream: {}", err);
};
let stream = device
.build_input_stream_raw(
&config.into(),
sample_format,
move |data: &cpal::Data, cb: &cpal::InputCallbackInfo| {
// amplitudes as f32 vec
},
err_fn,
None,
)
.unwrap();
stream.play().unwrap();
std::thread::sleep(std::time::Duration::from_secs(10));
drop(stream);
}
I already read the documentation and multiple universal cpal examples online, without any significant progress. My current solution does not rely on null sinks, as I want it to be as universal as possible.
If there is a better tool for that purpose, I am open for suggestions.
cpal version: 0.15.0
audio system: ALSA
I'm trying to learn Rust, but have hit a wall, tying to do something I expected to be relatively straightforward. I'm trying to write a simple blink example for the ESP32c3 MCU. I got a basic example working, but started running into compilation errors when trying to expand/generalize the example.
My project consists of a cargo workspace with two crates - entrypoint and blink.
I was able to get the following basic version working without issues:
// entrypoint/src/main.rs
use esp_idf_sys as _; // If using the `binstart` feature of `esp-idf-sys`, always keep this module imported
use blink::blink;
fn main() {
// Temporary. Will disappear once ESP-IDF 4.4 is released, but for now it is necessary to call this function once,
// or else some patches to the runtime implemented by esp-idf-sys might not link properly.
esp_idf_sys::link_patches();
println!("Hello, world!");
blink();
}
// blink/src/lib.rs
use std::thread;
use std::time::Duration;
use esp_idf_hal::prelude::Peripherals;
use esp_idf_hal::gpio;
pub fn blink() {
let peripherals = Peripherals::take().unwrap();
let mut led = gpio::PinDriver::output(peripherals.pins.gpio8).unwrap();
for _ in 0..20 {
led.set_high().unwrap();
thread::sleep(Duration::from_secs(1));
led.set_low().unwrap();
thread::sleep(Duration::from_secs(1));
}
}
Then I wanted to improve error handling (stop using unwrap() inside the blink crate) and make the blink() function reusable (the Peripherals::take() call panics, if it's executed more than once).
I came up with the following changes to improve error handling. This version also worked fine, I'm only including it to get feedback on how idiomatic my approach is / what would you do differently? I'm guessing it would be better practice to make a custom error type or is it acceptable/common place to return a string slice as an error even in production code?
pub fn blink(count: i32) -> Result<(), &'static str> {
let peripherals = Peripherals::take().ok_or("Failed to take peripherals")?;
let mut led = gpio::PinDriver::output(peripherals.pins.gpio8).map_err(|_: EspError| "Failed to set pin to output")?;
for _ in 0..count {
led.set_high().map_err(|_: EspError| "Failed to set pin high")?;
thread::sleep(Duration::from_secs(1));
led.set_low().map_err(|_: EspError| "Failed to set pin low")?;
thread::sleep(Duration::from_secs(1));
}
Ok(())
}
Next, I attempted to make the blink() function reusable by separating the Peripherals::take() call from the rest of the blink() function, so it could be called only once at boot. I know I could make the call in my entrypoint and pass the peripherals as an argument to blink(), but I wanted to keep the blink crate responsible for making the Peripherals::take() call. This is where I started running into issues.
Attempt nr. 1: My first approach was trying to use a global Peripherals variable. I quickly found out that won't work unless I wrap the global variable with the thread_local macro or wrap operations on the global variable into an unsafe block which I wanted to avoid. I tried a number of things, but couldn't get my code to compile when using thread_local.
Both with and without RefCell (I found articles suggesting to use RefCell, but after trying it and reading the docs, I didn't see a good reason to use it for my use-case), thread_local seems to wrap my global variable into a LocalKey. I'm not sure how to use the LocalKey, besides the with() function - I'd like to avoid using with(), if possible, since I need to move my code into a closure, making it harder to read. I'm also not sure how to keep the for loop outside of the closure and only initialize the led variable from inside the closure - usually I'd move the variable declaration out of the closure, initialized to null, but null doesn't seem to be a concept which exists within Rust as far as I can tell.
thread_local! {
static PERIPHERALS: Option<Peripherals> = Peripherals::take();
}
pub fn blink(count: i32) -> Result<(), &'static str> {
PERIPHERALS.with(| p | {
let peripherals = match p {
Some(peripherals) => peripherals,
None => return Err("Failed to take peripherals")
};
let mut led = gpio::PinDriver::output(peripherals.pins.gpio8).map_err(|_: EspError| "Failed to set pin to output")?;
for _ in 0..count {
led.set_high().map_err(|_: EspError| "Failed to set pin high")?;
thread::sleep(Duration::from_secs(1));
led.set_low().map_err(|_: EspError| "Failed to set pin low")?;
thread::sleep(Duration::from_secs(1));
}
Ok(())
})
}
The above code resulted in the following compiler error:
error[E0507]: cannot move out of `peripherals.pins.gpio8` which is behind a shared reference
--> blink/src/lib.rs:19:47
|
19 | let mut led = gpio::PinDriver::output(peripherals.pins.gpio8).map_err(|_: EspError| "Failed to set pin to output")?;
| ^^^^^^^^^^^^^^^^^^^^^^ move occurs because `peripherals.pins.gpio8` has type `Gpio8`, which does not implement the `Copy` trait
For more information about this error, try `rustc --explain E0507`.
error: could not compile `blink` due to previous error
The same error occurs, if I try to dereference peripherals variable first:
...
let mut led = gpio::PinDriver::output((*peripherals).pins.gpio8).map_err(|_: EspError| "Failed to set pin to output")?;
...
Attempt nr. 2: As my next approach, I tried to write a struct with a couple functions which would act as a class. Unfortunately I ran into the exact same compiler error.
// blink/src/lib.rs
use std::thread;
use std::time::Duration;
use anyhow::Result;
use esp_idf_hal::prelude::Peripherals;
use esp_idf_hal::gpio;
use esp_idf_sys::EspError;
pub struct Blink {
peripherals: Peripherals,
}
impl Blink {
pub fn new() -> Result<Blink, &'static str> {
match Peripherals::take() {
Some(peripherals) => Ok(Blink{ peripherals }),
None => return Err("Failed to take peripherals")
}
}
pub fn blink(&self, count: i32) -> Result<(), &'static str> {
let mut led = gpio::PinDriver::output(self.peripherals.pins.gpio8).map_err(|_: EspError| "Failed to set pin to output")?;
for _ in 0..count {
led.set_high().map_err(|_: EspError| "Failed to set pin high")?;
thread::sleep(Duration::from_secs(1));
led.set_low().map_err(|_: EspError| "Failed to set pin low")?;
thread::sleep(Duration::from_secs(1));
}
Ok(())
}
}
// entrypoint/src/main.rs
use std::thread;
use std::time::Duration;
use esp_idf_sys as _; // If using the `binstart` feature of `esp-idf-sys`, always keep this module imported
use blink::Blink;
fn main() {
// Temporary. Will disappear once ESP-IDF 4.4 is released, but for now it is necessary to call this function once,
// or else some patches to the runtime implemented by esp-idf-sys might not link properly.
esp_idf_sys::link_patches();
println!("Hello, world!");
let blink = Blink::new()?;
loop {
blink.blink(2).unwrap();
thread::sleep(Duration::from_secs(5));
}
}
error[E0507]: cannot move out of `self.peripherals.pins.gpio8` which is behind a shared reference
--> blink/src/lib.rs:23:47
|
23 | let mut led = gpio::PinDriver::output(self.peripherals.pins.gpio8).map_err(|_: EspError| "Failed to set pin to output")?;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ move occurs because `self.peripherals.pins.gpio8` has type `Gpio8`, which does not implement the `Copy` trait
For more information about this error, try `rustc --explain E0507`.
error: could not compile `blink` due to previous error
I don't have a good enough understanding of how borrowing, references, and/or variable moving/copying works in Rust just yet to be able to solve this. It seems to be drastically different from other (more traditional) languages I'm familiar with (C, C++, Java, JS/TS, Python, Dart).
Once again, I'd also really appreciate any best practice recommendations/corrections, if you find anything out of the ordinary in my code above.
The basic gist of the error can be reproduced with a simpler example:
struct Foo {
bar: String
}
impl Foo {
fn baz(&self) {
let s = self.bar;
}
}
What's happening is:
self has type &Foo, because the parameter is declared with &self, which is a shorthand for self: &Self
self.bar has type String, because bar is declared to be a String
this leads to an issue, we're trying to *move s out of self, but we only have a reference to self, not owned access to self
You'll need to find a way to make that work without owned access. Disclaimer, I've not used this crate before (and the setup instructions are a bit crazy), but here's what I could piece together from the docs for PinDriver::output
PinDriver::output takes an impl Peripheral, i.e. any type that implements the trait Peripheral
looking at the docs for Peripheral, we can see a list of structs that implement it.
Gpio8 does implement it, but this is no good, since we'd run into the "cannot move out of shared reference" issue again
in most Rust code, you might want to .clone() the gpio pin, however, there's no clone method (because it represents a unique handle to a physical resource, unless you've got some board that can grow new pins :p)
however, right at the bottom, there's this impl:
impl<T: DerefMut> Peripheral for T
where
T::Target: Peripheral { ... }
i.e. any type which implements DerefMut<Target = T> implements Periperal, as long as T also implements Peripheral
this means you can use &mut Gpio8. This makes sense, since you're mutating the state of the light, so you need a mutable handle to it. If you change blink() to take a &mut self, you should be able to write PinDriver::output(&mut self.peripherals.pins.gpio8)
FWIW, embedded Rust often uses clever type system tricks to verify more behavior at compile time (either to save precious CPU cycles or for better reliability). If you're new to Rust, it can be quite confusing, since it's arguably more "advanced" than most CLI apps, for example
my goal/use case:
Subscribe to a datafeed
Publish to internal subscribers
Example use case: subscribe to stock prices, consume from multiple different bots running on different threads within the same app.
In other languages, I'd be using an RX Subject and simply subscribe to that from anywhere else and I can choose which thread to observe the values on (threadpool or same thread, etc).
Here is my attempt using a simulated data feed:
Code:
async fn test_observable() -> Receiver<Decimal> {
let (x, response) = mpsc::channel::<Decimal>(100);
tokio::spawn(async move {
for i in 0..10 {
sleep(Duration::from_secs(1)).await;
x.send(Decimal::from(i)).await;
}
});
response
}
#[tokio::main]
async fn main() {
let mut o = test_observable().await;
while let Some(x) = o.recv().await {
println!("{}", x);
}
}
Questions:
Is this the right approach? I normally use RX in other languages but it is too complicated in Rust so I resorted to using Rust channels. RX for Rust
I think this approach won't work if I have multiple receivers. How do I work around that? I just want something like an RX observable, it should not be difficult to achieve that.
Is this creating any threads?
I am creating a custom plugin with application/x-rtp as sync and src. I want to add custom payload to the extension. My plugin definition is :
glib::wrapper! {
pub struct CustomPlugin(ObjectSubclass<imp::CustomPlugin>) #extends gst_base::BaseTransform, gst::Element, gst::Object;
}
In transform_ip, I just want to add custom data to the extension.
As of current I have
fn transform_ip(&self, element: &Self::Type, buf: &mut gst::BufferRef) -> Result<gst::FlowSuccess, gst::FlowError> {
let mut out_frame: BufferMap<Writable> = buf.map_writable().map_err(|_| {
gst::element_error!(
element,
gst::CoreError::Failed,
["Failed to map input buffer readable"]
);
gst::FlowError::Error
})?;
let mut_buf: &mut Buffer;// = out_frame...
let mut rtp_buffer = RTPBuffer::from_buffer_writable(mut_buf).unwrap();
rtp_buffer.add_extension_onebyte_header(1u8, &[1u8]).unwrap();
Ok(gst::FlowSuccess::Ok)
}
Line containing let mut_buf: &mut Buffer;// = out_frame... needs to be fixed.
My questions are :
Am I going in the right direction. Do I need to use a different Base class. The current base class is gst_base::BaseTransform.
I am new to multimedia and Rust. Is there a good resource apart from gstreamer. It is a good source but I find it difficult to follow. Though tutorial with Rust is fantastic with detailed explanations.
GStreamer Change to fix above issue
Gstreamer team made a change to the interface and now RTPBuffer::from_buffer_writable accepts &mut gst::BufferRef, which we already have.
Final code looks like
fn transform_ip(&self, element: &Self::Type, buf: &mut gst::BufferRef) -> Result<gst::FlowSuccess, gst::FlowError> {
let mut rtp_buffer = RTPBuffer::from_buffer_writable(&mut buf).unwrap();
rtp_buffer.add_extension_onebyte_header(1u8, &[1u8]).unwrap();
Ok(gst::FlowSuccess::Ok)
}
And we have fix. Thanks, GStreamer team to resolving this fast.
I'm trying to work on a nrf52-dk board and trying to blink a light and get SPI working at the same time.
I could have one or the other working at a time, but not both at once.
I'm pretty sure that nrf52810_hal::pac::Peripherals::take() shouldn't be called more than once, as its data would change all references, but it gets moved when I specify pins.
I'm unsure how I'd be able to make it work without passing in without using up the variable.
In the following example, the "PERIPHERALS are null" is always written out and the code panics because of the statements that are following it.
I do need to have the PERIPHERALS being "static mut", as I need them in another "thread", because it's an interrupt-called function, to which I'm unable to pass data.
#![no_main]
#![no_std]
#[allow(unused_extern_crates)]
use panic_halt as _;
use asm_delay::AsmDelay;
use cortex_m_rt::entry;
use cortex_m_semihosting::hprint;
use hal::gpio::Level;
use hal::pac::interrupt;
use nrf52810_hal as hal;
use nrf52810_hal::prelude::*;
use nrf52810_pac as nrf;
use nrf52810_pac::{Interrupt, NVIC};
static mut PERIPHERALS: Option<nrf::Peripherals> = None;
#[entry]
unsafe fn main() -> ! {
let p = hal::pac::Peripherals::take().unwrap();
let port0 = hal::gpio::p0::Parts::new(p.P0);
let spiclk = port0.p0_25.into_push_pull_output(Level::Low).degrade();
let spimosi = port0.p0_24.into_push_pull_output(Level::Low).degrade();
let spimiso = port0.p0_23.into_floating_input().degrade();
let pins = hal::spim::Pins {
sck: spiclk,
miso: Some(spimiso),
mosi: Some(spimosi),
};
let spi = hal::Spim::new(
p.SPIM0,
pins,
hal::spim::Frequency::K500,
hal::spim::MODE_0,
0,
);
let reference_data = "Hello World!".as_bytes();
let mut eh_spi = embedded_hal_spy::new(spi, |_| {});
use embedded_hal::blocking::spi::Write;
match eh_spi.write(reference_data) {
Ok(_) => {}
Err(_) => {}
}
PERIPHERALS = nrf::Peripherals::take();
if PERIPHERALS.is_none() {
hprint!("PERIPHERALS are null!").unwrap();
}
NVIC::unmask(Interrupt::SWI0_EGU0);
let mut d = AsmDelay::new(asm_delay::bitrate::U32BitrateExt::mhz(74));
PERIPHERALS
.as_ref()
.unwrap()
.P0
.dir
.write(|w| w.pin20().output());
PERIPHERALS
.as_ref()
.unwrap()
.P0
.out
.write(|w| w.pin20().low());
loop {
NVIC::pend(Interrupt::SWI0_EGU0);
d.delay_ms(100u32);
}
}
#[interrupt]
fn SWI0_EGU0() {
static mut LED_STATE: bool = false;
flip_led(LED_STATE);
*LED_STATE = !*LED_STATE;
}
fn flip_led(led_state: &mut bool) {
match led_state {
true => unsafe {
PERIPHERALS
.as_ref()
.unwrap()
.P0
.out
.write(|w| w.pin20().low());
},
false => unsafe {
PERIPHERALS
.as_ref()
.unwrap()
.P0
.out
.write(|w| w.pin20().high());
},
}
}
Do you actually need to access all of Peripherals from your interrupt context? Probably not, you probably need to access only specific peripherals there. You can move those out of the Peripherals struct and into a static. Then you'll have the peripherals you need in your main as local variables there, and everything else in a static.
But there's an even better solution, in my opinion: Use RTIC. It's designed to handle that exact use case. It allows you to specify exactly which resources you need in which context, and will make those resources available there. You can even safely share resources between different contexts. It will automatically protect them with mutexes, as required.
I can't recommend RTIC highly enough. For me, the only reason not to use it would be, if my program doesn't have any interrupt handlers.