ChanServ changed the topic of #rust-embedded to: Welcome to the Rust Embedded IRC channel! Bridged to #rust-embedded:matrix.org and logged at https://libera.irclog.whitequark.org/rust-embedded, code of conduct at https://www.rust-lang.org/conduct.html
<re_irc> <GrantM11235> "ili9341" used to report a generic error for the reset pin, but now it just uses the non-generic "display-interface::DisplayError" https://github.com/yuri91/ili9341-rs/commit/954d111959d88605cffac1a3eacdf7bede47959a
explore has joined #rust-embedded
fabic_ has joined #rust-embedded
fabic_ has quit [Ping timeout: 256 seconds]
<re_irc> <eldruin> adamgreig: yes, any of my spi drivers do that. some examples are: https://github.com/eldruin/ad983x-rs and https://github.com/eldruin/mcp49xx-rs
emerent has quit [Ping timeout: 240 seconds]
emerent has joined #rust-embedded
fabic_ has joined #rust-embedded
gsalazar has quit [Ping timeout: 246 seconds]
fabic_ has quit [Ping timeout: 272 seconds]
explore has quit [Quit: Connection closed for inactivity]
fabic_ has joined #rust-embedded
Socke has quit [Ping timeout: 256 seconds]
Socke has joined #rust-embedded
Rahix has quit [Quit: ZNC - https://znc.in]
Rahix has joined #rust-embedded
GenTooMan has quit [Read error: Connection reset by peer]
GenTooMan has joined #rust-embedded
<re_irc> <riskable> I have an interesting problem and I'm looking for ideas how to solve it: I have a struct "Config" that has "pub encoders: [EncoderConfig; 2],". The problem is I want this "2" to be dynamic and based on "num_encoders" that gets read earlier in the "Config.toml" (or even better: Detect how many the "[encoder]"s the user put in the config). It's sort of "chicken and egg" problem where I can put "{ config::<whatever> }" in...
<re_irc> ... structs everywhere _else_ but since this is within the same file I can't do that
<re_irc> <riskable> I tried switching it to a "heapless::Vec" instead of just an array of fixed size but it turns out that Serde's "Deserialize" isn't supported by that (everything else in "heapless" seems to be though).
<re_irc> <huw.> Can you use const generic?
<re_irc> <huw.> I think the release notes here explain it well https://blog.rust-lang.org/2021/03/25/Rust-1.51.0.html
<re_irc> <riskable> huw.: Yeah but that would require the end user set the number of encoders somewhere in the .rs files instead of the config
<re_irc> <riskable> Basically, this is the "config_structs.rs" file that contains all structs related to configuration items... "build.rs" reads a "Config.toml" file and populates "config.rs" with the contents. That "config.rs" is sourced by several other files for things like const generics (e.g. "&[config_structs::EncoderConfig; { config::ENCODERS_NUM_ENCODERS }];"
<re_irc> <riskable> Since "config_structs.rs" needs to exist _before_ "config.rs" I can't use a const generic value for "encoders"... Unless there's some other way I'm missing
<re_irc> <riskable> I don't mind pre-allocating some value like, "8"
<re_irc> <riskable> ...which was "the plan" when I tried to use "heapless::Vec" but then it wouldn't serialize/deserialize because that's not implemented for that type
<re_irc> <GrantM11235> You should be able to calculate the size of the array at compile time with a const fn
<re_irc> <riskable> GrantM11235: Interesting. I'll look into that right now. Do you have a decent (applicable) example?
<re_irc> <GrantM11235> I hope that is at least a little applicable for you
<re_irc> <riskable> GrantM11235: Yeah that helps but I can also see that it won't work since I can't know the length of the number of encoders from inside "config_structs.rs" since that gets figured out by "build.rs" (which imports "config_structs.rs"... Unless I can read the "Config.toml" from within a "const fn"? Hmm
<re_irc> <riskable> No, I can't because then "config_struct.rs" would need "std"
<re_irc> <GrantM11235> If you know the length in build.rs, you should be able to define a const that you can read from you main program
<re_irc> <GrantM11235> also, you can use the "include_str" or "include_bytes" macro to include a file in your program as a "&str" or "&[u8]"
<re_irc> <GrantM11235> ie in your build script, write "const MY_SIZE: usize = {calculated_size}" to a file called something like "generated.rs", then use "generated::MY_SIZE" as your array length
<re_irc> <riskable> GrantM11235: I like this idea... I've already got my "build.rs" creating a "userconfig.rs" which gets used via "include!()" inside "config.rs". What I think I could do (based on your suggestion) is have some logic that runs before everything else inside "build.rs" that generates something like "const_lengths.rs" and pre-populates the top of "config_structs.rs" via another "include!()" line. It'll be a bit convoluted...
<re_irc> ... but it already kinda is haha
<re_irc> <huw.> Sounds like you need to use a trait to abstract the actual length away, like this maybe https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=89dbfe263772fa471e3b816699a0feb5
<re_irc> <GrantM11235> huw.: You can do the same thing without the trait: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=c47a2f37806cafba56babaa1982328fa
<re_irc> <GrantM11235> Do you need to use the "include!" macro? Can't you just "use userconfig;" the file as a normal module?
<re_irc> <riskable> GrantM11235: Well this doesn't work because of the same reason I can't use "heapless::Vec" (sigh)... (the trait bound "[EncoderConfig; ENC]: Serialize" is not satisfied)
<re_irc> <riskable> (where ENC is from "pub struct Config<const ENC: usize>")
<re_irc> <riskable> GrantM11235: Well I tried that but the "userconfig.rs" ends up being spat out to "$OUT_DIR/userconfig.rs" which ends up... I don't even know where LOL
<re_irc> <riskable> The way I did it was copied from the example here: https://doc.rust-lang.org/cargo/reference/build-script-examples.html
<re_irc> <dirbaio> if it's all const you can use "&'static [usize]"
<re_irc> <dirbaio> no need for const generics
<re_irc> <dirbaio> also a nice trick I've found is "format!("{:?}")" generates _almost_ valid Rust code, you just have to replace "[" into "&["
<re_irc> <riskable> dirbaio: Hah! I'm actually using that trick in my "build.rs" to output the "EncoderConfig" structs...
<re_irc> for encoder in config.encoders.iter() {
<re_irc> out.push_str(format!("{:?},\n", encoder).as_str());
<re_irc> }
<re_irc> <dirbaio> :D
<re_irc> <riskable> The output ends up looking like this:
<re_irc> pub const ENCODERS: [EncoderConfig; 2] = [
<re_irc> EncoderConfig { mux: 0, resolution: 20, press_threshold: 75, channel1: 4, channel2: 6, press_channel: 15 },
<re_irc> EncoderConfig { mux: 1, resolution: 40, press_threshold: 60, channel1: 9, channel2: 11, press_channel: 10 },
<re_irc> ];
<re_irc> <riskable> dirbaio: What do you mean by this? How would I change "pub encoders: [EncoderConfig; 2]," to use a static usize?
<re_irc> <dirbaio> "pub const ENCODERS: &'static [EncoderConfig] = &[ ... ]"
<re_irc> <riskable> Ah. Let's try that...
<re_irc> <riskable> Well that looks like it'd _kinda_ work but now I need to figure out how to make Serde happy... "the trait bound "&'static [EncoderConfig]: Deserialize<'_>" is not satisfied"
<re_irc> <dirbaio> on build.rs, use Vec, deserialize into that
<re_irc> <dirbaio> then format it with "{:?}"
<re_irc> <dirbaio> and on the firmware side use `*
<re_irc> <dirbaio> * "&'static [..]"
<re_irc> <riskable> dirbaio: Yeah I was trying to avoid pulling the "encoders" out of "pub struct Config {}" but I might have to since I can't know the number of encoders the user put in the "Config.toml" before "build.rs" runs
<re_irc> <dirbaio> yeah..
<re_irc> <riskable> dirbaio: The problem with deserializing into a Vec is that you get "Value()" and "Integer()" structs that don't specify the integer type
<re_irc> <riskable> I suppose I could just make literally everything a "u32" and then I wouldn't have to worry about that
<re_irc> <dirbaio> yeah
<re_irc> <dirbaio> I had to write 2 versions of the structs: one serde-friendly, one no-alloc-friendly
fabic_ has quit [Ping timeout: 256 seconds]
<re_irc> <riskable> dirbaio: GrantM11235 huw. I finally got it working by just telling Serde to skip deserialization on the encoders array:
<re_irc> #[serde(skip_deserializing)]
<re_irc> pub encoders: &'static [EncoderConfig],
<re_irc> <riskable> Then in my "build.rs" I assembled the static struct like so:
<re_irc> // Handle the rotary encoders
<re_irc> if let Some(arr) = encoders_arr {
<re_irc> let encoders_arr = config_table["encoders"].as_array();
<re_irc> <dirbaio> why not use std Vec?
<re_irc> <riskable> config_table is the same "Config.toml" but read in as a Serde "Table" instead of using my "Config" struct. So in essence it converts the .toml file twice
<re_irc> <riskable> dirbaio: ...cuz this is "[no_std]"?
<re_irc> <dirbaio> but you're doing the serde thing in build.rs right? that one has std available
<re_irc> <riskable> Yeah but the output is available and read by all the other code. This is what it looks like:
<re_irc> pub const ENCODERS: [EncoderConfig; 2] = [
<re_irc> EncoderConfig { mux: 0, resolution: 20, press_threshold: 75, channel1: 4, channel2: 6, press_channel: 15 },
<re_irc> EncoderConfig { mux: 1, resolution: 40, press_threshold: 60, channel1: 9, channel2: 11, press_channel: 10 },
<re_irc> ];
<re_irc> <riskable> Can't use a Vec there
<re_irc> <dirbaio> the struct you use in build.rs and the struct you use in the firmware don't have to be the same
<re_irc> <riskable> You mean, "why am I reading it in as a Serde table and not a Vec?"
<re_irc> <dirbaio> yeah
<re_irc> <riskable> "let config_table: Value = toml::from_str(&contents).unwrap();" <--that's how it gets converted inside "build.rs"
<re_irc> <riskable> I could just use "Vec" instead of "Table". Wouldn't make any difference though
<re_irc> <dirbaio> you could do
<re_irc> struct EncoderConfig { ... }
<re_irc> // build.rs
<re_irc> struct Config {
<re_irc> <riskable> "config_table["keyboard"].get(fname);" works whether I use Serde's format or a Vec 🤷
<re_irc> <riskable> dirbaio: That would require maintaining two different copies of "config_structs.rs" though
<re_irc> <dirbaio> in build.rs, deserialize the entire "Config" with "serde", and convert it to Rust source with "format!("{:?}", config)"
<re_irc> <dirbaio> it does the whole array for you
<re_irc> <riskable> BTW: "Value" and "Table" mean the same thing from this perspective
<re_irc> <dirbaio> I have a similar thing here
<re_irc> <riskable> dirbaio: Do you maintain two different versions of all your structs? One for "build.rs" and one for everything else?
<re_irc> <riskable> I mean, I thought about doing that early on but decided it would be "easier" (hah!) to just have one .rs file with all my configuration structs
<re_irc> <dirbaio> build-time structs use "Vec" and "String": https://github.com/embassy-rs/embassy/blob/master/stm32-metapac-gen/src/data.rs
<re_irc> run-time structs use "&'static str" and "&'static [..]": https://github.com/embassy-rs/embassy/blob/master/stm32-metapac/src/metadata.rs
<re_irc> <dirbaio> but the flip side is you can load the thing with a single serde call, and convert it to rust with a single "format!("{:?}", config)" call
<re_irc> <dirbaio> the whole thing, nested arrays and everything
<re_irc> <dirbaio> no need to manually loop and add strings together
<re_irc> <riskable> dirbaio: I see. That's cool 👍
<re_irc> <dirbaio> I wish "{:?}" printed enums correctly, but no :P
<re_irc> <riskable> dirbaio: I'm actually going through now and changing my code to use your way (mostly) 👍
<re_irc> <riskable> Rather than do the whole "Config" as a "format!()" call I'm going to do each individual section that way. Otherwise I don't think I can get the "Encoders" part working haha
<re_irc> <dirbaio> 🤔
<re_irc> <dirbaio> I think it should Just Work
<re_irc> <dirbaio> if you use a slice instead of an array: "&'static [EncoderConfig]"
bjc has joined #rust-embedded
bjc has quit [Ping timeout: 240 seconds]
bjc has joined #rust-embedded
explore has joined #rust-embedded
bjc has quit [Ping timeout: 240 seconds]
bjc has joined #rust-embedded
bjc has quit [Ping timeout: 240 seconds]
bjc has joined #rust-embedded