ChanServ changed the topic of #rust-embedded to: Welcome to the Rust Embedded IRC channel! Bridged to and logged at, code of conduct at
<re_irc> <riskable> firefrommoonlight: _Digital filtersโ€ฝ_ OMG! I need to look at this!
<re_irc> <riskable> I type on an analog hall effect keyboard I designed myself every day and it has ZERO digital or circuit-based filters. I always figured that I could make the signals so much smoother if I just understood how TF to implement a digital filter (the math is beyond me).
<re_irc> <firefrommoonlight> CMSIS-DSP is pretty nice once you have the filter designed. The bindgen port works well in Rust, although I wrapped it so the API uses references instead of pointers, and checks that your state buffers are the right size etc
<re_irc> <firefrommoonlight> -pretty
<re_irc> <firefrommoonlight> This book ( avail for free online is pretty good if you want details
<re_irc> <firefrommoonlight> How the flow works is you set up the filter coefficients using Matlab, Scipy etc, then pass them to CMSIS-DSP, which is designed to run on cortex-M
<re_irc> <firefrommoonlight> Of note, you should use statics for the state and buffers, to make sure they live long enough. (It might be worth wrapping the API further so the "FirInstance" etc structs own their state and coefficient buffers...)
<re_irc> <9names (> > For reference, I need to read two ADC pins 16 times every millisecond.
<re_irc> Riskable: I've been meaning to ask - what determines this requirement for your keyboard?
<re_irc> <riskable> 9names: Just regular 1ms polling. Kinda like Pokรฉmon... Gotta read em all ๐Ÿ‘
<re_irc> <riskable> firefrommoonlight: Yeah that sounds waaaaay beyond me. I've never used Matlab in my life and I've read some of the scipy before and got lost real fast. It's like they just expect you know what all the math symbols represent intuitively.
<re_irc> <riskable> +docs
<re_irc> <firefrommoonlight> It depends
<re_irc> <firefrommoonlight> You need to identify _why_ you're using a filter. The tricky parts might be choosing the correct type of filter (FIR vs various varieties of IIR). Once you have that done, you can use Scipy. (Eg the example function call in the comment 9names mentioned shows you how; it's a one-liner, plus a few more to convert it to CMSIS's format)
<re_irc> <firefrommoonlight> Then copy+paste the cofficients it gives you into the code I posted
<re_irc> <firefrommoonlight> Part of the reason I posted the example was to demonstrate _it's not that bad_
<re_irc> <firefrommoonlight> Scipy is a collection of unrelated tools, and its docs are...not great. In this case, the relevant onces are all in "scipy.signal":
<re_irc> <firefrommoonlight> I made this tool to plot the response, wrapping scipy:
<re_irc> <firefrommoonlight> I'll eventually get around to adding filter design to it (currently just plotting)
<re_irc> <firefrommoonlight> For example, I chose a bessel type IIR filter with 1 stage in the example, since I need minimal latency (so IIR as default, low stage count) don't need sharp rolloff (1 stage), want to preserve signal shape (Bessel)
<re_irc> <firefrommoonlight> If you need 0 phase distortion or sharp cutoffs, and/or don't care about latency, you might choose FIR
<re_irc> <firefrommoonlight> You can understand what's in the toolbox without grokking the math behind it
<re_irc> <riskable> Latency is the one thing I cannot allow
<re_irc> <riskable> ... Unless it's the type that just builds over time. Like, if it takes a dozen samples before it starts working hard fine
<re_irc> <riskable> * that's
<re_irc> <firefrommoonlight> IIR will be a good baseline
emerent has quit [Ping timeout: 240 seconds]
emerent has joined #rust-embedded
Rahix has quit [Quit: ZNC -]
Rahix has joined #rust-embedded
explore has quit [Quit: Connection closed for inactivity]
Shell is now known as derg
derg is now known as Shell
starblue has quit [Ping timeout: 256 seconds]
<re_irc> <ub|k> Is anyone using rust-analyzer. "cargo check" seems to take forever
<re_irc> <mriise> Just got done building a project
<re_irc> <mriise> If you are running vs code make sure everything is up to date
<re_irc> <mriise> Often I run into that causing issues
<re_irc> <ub|k> everything is up to date
<re_irc> <ub|k> hm... i suspect it's because it's invoking "cargo check" with "--workspace"
neceve has joined #rust-embedded
starblue has joined #rust-embedded
nohit has quit [Ping timeout: 260 seconds]
nohit has joined #rust-embedded
<XMPPwocky> <re_irc> "<riskable> I type on an analog..." <- honestly just sticking a big honkin' lowpass on there at 20hz or something to kill mains hum and its harmonics can often do a lot more than you'd think
<re_irc> <riskable> From a memory efficiency standpoint, is it better to write a function that takes a mutable variable and modifies it, returning just a "Result" or a function that takes a mutable variable and returns that same variable after it has been modified?
<re_irc> <riskable> Well, which way is better _in general_?
<re_irc> <pwychowaniec> IIRC the mut+result approach is faster in general - e.g. "fmt::Display" is designed this way + I myself saw missed optimization opportunity in cases such as:
<re_irc> fn do_something<T>(x: T) -> T {
<re_irc> }
<re_irc> /* ... */
<re_irc> ... where the compiled function was actually copying all the bytes of "x" into its stack first and only the operating on the variable ๐Ÿ™ƒ
<re_irc> <pwychowaniec> but ofc. it's impossible to predict The Mighty Optimizer in all cases - as always, I'd suggest benchmarking
<re_irc> <riskable> pwychowaniec: It's just a question out of curiosity because I see both ways out in the wild. I was curious why one might be chosen over the other.
<re_irc> <riskable> When I was writing "rp2040-tickv" I noted that the "tickv" API wants you to pass in a mutable buffer when you call "get_key()". That seemed very unintuitive to me since simply requesting a key like "let buf = get_key(b"some_key")" would be more idiomatic/intuitive than "let mut buf: [u8; 8] = [0; 8]; get_key(b"some_key", buf);"
<re_irc> <riskable> +&mut
<re_irc> <pwychowaniec> oh yeah, that's a popular approach - this allows you to re-use one buffer for multiple reads (unless you need to access the read values at the same time, ofc.) with guaranteed no re-allocation in between instead of relying on the optimizer; same stuff with as
<re_irc> <riskable> I mean, either way the buffer needs to get created. It's just a matter of creating it before you call the function or inside of it.
<re_irc> <riskable> pwychowaniec: Oh _so that's why_! I figured there was a good reason. Wow, awesome ๐Ÿ‘
<re_irc> <riskable> Is it possible to make an array of GPIO pins? Have we figured that out yet?
<re_irc> <burrbull> erase/downgrade
<re_irc> <dirbaio> HALs should define a type-erased pin type, many still don't
<re_irc> <dirbaio> "AnyPin" in Embassy HALs, others call it "Pin" or "DynPin"
<re_irc> <riskable> dirbaio: "rp2040-hal" has an "AnyPin" thingamabob I don't see a way to use it in an array. You can use it when defining functions but I just want a simple array!
<re_irc> <riskable> Well, let me take a step back: I want _something_ I can iterate over and pass to "<the pin>)"
<re_irc> <firefrommoonlight> STM32?
<re_irc> <firefrommoonlight> I've been using a "u8" to represent ADC channel
<re_irc> <dirbaio> hm, "read()" requires a "PIN" implementing the "Channel" trait
<re_irc> <dirbaio> so it can check at compile-time that ADC is supported on the pin (not all pins can do ADC)
<re_irc> <dirbaio> it'd need an impl for DynPin that panics at runtime, or a DynAdcChannel, or something along those lines
<re_irc> <dirbaio> so, no, not possible :(
<re_irc> <dirbaio> it'd need an impl Channel for DynPin that panics at runtime, or a DynAdcChannel, or something along those lines
<re_irc> <riskable> dirbaio: ARG! This is _the_ most frustrating thing about embedded Rust
<re_irc> <dirbaio> agreed
<re_irc> <newam> nothing a little "unsafe" can't fix :D
<re_irc> <dirbaio> use the PAC ๐Ÿ”ฅ
<re_irc> <riskable> I _just want to be able to iterate over an arbitrary number of pins_ (based on compile-time configuration) and be able to read their ADC values
<re_irc> <dirbaio> or fix the HAL... a "DynAdcChannel" would work
<re_irc> <riskable> I want the end user to be able to specify something like, "adc_pins: 26, 27" in a config file and then have my code take care of the rest.
<re_irc> <firefrommoonlight> I'm interested in that approach too
<re_irc> <firefrommoonlight> I'd like the flight controller firmware I'm writing to be compatible with diff boards, where you set up a config file that specifies what pin is what
<re_irc> <firefrommoonlight> Haven't attempted that yet, and nothing is popping up as an immediate solution. Currently have feature gates, but that's not idea since it clutters up code, and isn't consolidated
<re_irc> <dirbaio> fun
<re_irc> <dirbaio> I've had to battle with multi-board support, ended up writing "mini-BSPs" in-crate
<re_irc> <dirbaio> "fn init(core_peris: cortex_m::Peripherals, hal_peris: hal::Peripherals) -> Hardware"
<re_irc> <dirbaio> one .rs file with such an "init" fn per board, include the right ".rs" file based on cargo feature
neceve has quit [Ping timeout: 246 seconds]
<re_irc> <dirbaio> has allowed me to keep like ~95% of the board-specific stuff in these "mini-bsp"s
<re_irc> <ub|k> doesn't the compiler optimize it either way?
<re_irc> <dirbaio> thanks to trying to make the "Hardware" struct have the same "shape" for all boards,
<re_irc> <dirbaio> * boards
<re_irc> <riskable> dirbaio: I want to do that too but how do you deal with boards that have multiple input pins and you need to somehow pass those pins to other functions in your code?
<re_irc> <riskable> Board 1 has 3 inputs, board 2 has 5... How do you write code that can iterate over them and read them all as necessary?
<re_irc> <dirbaio> each .rs defines the "Hardware" struct on its own
<re_irc> <dirbaio> so you can have $THING have different types on different boards
<re_irc> <riskable> I don't understand
<re_irc> <dirbaio> I'd link to the code but it's not opensource :'(
<re_irc> <riskable> Do you have an "impl" for "Hardware" that has something like "fn read_it_all_in()" and it has its own internal state structures for everything?
<re_irc> <dirbaio> the gist of it is
<re_irc> <dirbaio> /// Cargo.toml
<re_irc> [features]
<re_irc> board-idlr = []
<re_irc> board-cyl = []
<re_irc> <dirbaio> * board-idl
<re_irc> <dirbaio> having a different _amount_ of channels is trickier, you can do
<re_irc> <dirbaio> * could do something like
<re_irc> <dirbaio> [features]
<re_irc> have-analog-3ch = []
<re_irc> board-cyl = []
<re_irc> board-idl = ["have-analog-3ch]
<re_irc> <dirbaio> * ["have-analog-3ch"]
<re_irc> <dirbaio> or maybe make your "AdcKeyboardReader" struct and stick that in "Hardware", having each board file configure it differently
<re_irc> <dirbaio> I tend to make the stuff in "Hardware" as high-level as possible
<re_irc> <dirbaio> for example I have different boards with NFC and without, and with different chips, with spi/i2c, on different pins
<re_irc> <dirbaio> so each "" intializes the pins, initializes i2c/spi, initializes the nfc driver for the current chip, and returns it in "Hardware"
<re_irc> <dirbaio> so the rest of the code can directly do "#[cfg(feature="have-nfc")] hw.nfc.do_whatever()"
<re_irc> <dirbaio> for example I have different boards with NFC and without, and with different NFC reader chips, with spi/i2c, on different pins
<re_irc> <firefrommoonlight> dirbaio: That sounds straightfoward
<re_irc> <firefrommoonlight> Not an ideal config file, but you could definitely write a bunch of equivalent ".rs" files and do "use boarda as board" etc
<re_irc> <firefrommoonlight> I've been doing that for a few hardware parts that are sort of interchangeable like IMUs (gotta stay flexible these days...)
<re_irc> <firefrommoonlight> Didn't think to apply it to MCUs too
<re_irc> <dirbaio> yeah I wouldn't say it's a "config"
<re_irc> <dirbaio> it's actual code
<re_irc> <firefrommoonlight> workable, even if not ideal
<re_irc> <firefrommoonlight> Eg, I have a line like this:
<re_irc> use icm426xx as imu;
<re_irc> // use ism330 as imu;
<re_irc> <dirbaio> it depends on what you're doing
<re_irc> <firefrommoonlight> Yea completely
<re_irc> <firefrommoonlight> I can see that not being ideal for a lot of cases
<re_irc> <dirbaio> for my usecase the different boards are very different, it's all the products you see in
<re_irc> <dirbaio> (and multuiple revisions of the boards, some with very significant changes due to the chip shortage ๐Ÿ˜ญ)
bpye has quit [Ping timeout: 252 seconds]
<re_irc> <dirbaio> some are battery-powered, some have network, some have nfc...
<re_irc> <firefrommoonlight> Nice!
<re_irc> <dirbaio> I imagine for a really really simpler usecase you could get away with a config with just pin numbers
<re_irc> <firefrommoonlight> Those look cool
<re_irc> <firefrommoonlight> I think something like a YAML file or even a terse custom format would be idea for a lot of these cases
<re_irc> <dirbaio> but you get one chip shortagened and have to swap it and your nice "config" goes out the window because now you have different chips
<re_irc> <firefrommoonlight> I haven't thought about it too much since I'm only alternating between 2 MCUs (G4 for lower price and good enough; H7 for high perf features)
<re_irc> <firefrommoonlight> But have a lot of feature-gated DRY where I assign pins
<re_irc> <dirbaio> and at that point you're reinventing a crappy subset of Rust inside YAML :P
<re_irc> <dirbaio> with undebuggable code generation :D
<re_irc> <firefrommoonlight> Haha yea
<re_irc> <firefrommoonlight> quick+dirty and works, but would get out of hand if adding more options
<re_irc> <dirbaio> or just use Linux's (Zephyr's?) Device Tree โ˜ ๏ธ
<re_irc> <dirbaio> (no)
bpye has joined #rust-embedded
<re_irc> <riskable> dirbaio: It's way worse than that: I need each board to have different _numbers_ of ADC pins as well as different channel/key layouts (so A might be channel 2 on ADC 1 on one board but on another it might be channel 6 on ADC 0)
<re_irc> <riskable> Channels are truly generic though so that's much easier to deal with
<cr1901> >v1.0.0-alpha.8 I feel like that embedded-hal should've started at 1.0.0 instead of 0.1.0 to avoid all these alpha versions and "1.0 fever"
m5zs7k has quit [Quit: m5zs7k]
<re_irc> <firefrommoonlight> V1.0.0 a bit like the speed of light, as described in 1905 by the Special Theory of Relativity
<re_irc> <firefrommoonlight> A short, but careful read through will illuminate why we conceputalize this marvel, but never attain it thus
<re_irc> <firefrommoonlight> Even approaching close will take substantial effort
<re_irc> <firefrommoonlight> For now, 0-based versioning is a safe standard
<cr1901> Zeno's Version
<re_irc> <dirbaio> v0.5, v0.75, v0.875, v0.9375, v0.96875, v0.984375, v0.9921875 ...
<re_irc> <dirbaio> +v0.9999999999
<re_irc> <James Munns> I can't remember if each version number is a u32 or u64
<re_irc> <dirbaio> crate versions are "double"s
<cr1901> Ahhh. Okay.
<re_irc> <dirbaio> (I was joking! they're not doubles!!)
<cr1901> Oops :P
<re_irc> <dirbaio> could've made it more obvious, sorry. ๐Ÿ™ˆ it's a common meme, in semver "0.9 < 0.10", vs with floating point it'd be the other way around ๐Ÿ˜‚
<cr1901> My joke/socializing bandwidth is gone till at least tomorrow. In a worse mood than usual.
<re_irc> <dirbaio> :'(
<cr1901> So yea I would've bought that "cargo uses a double". It would've been weird, but interesting :P
Lumpio- has quit [Ping timeout: 256 seconds]