Obtaining GpioPin/AnyPin from pin number integer

I'm working on building a few chemical dispensers on some ESP32-C6 devices. Each device has a different configuration (different number of pumps, with different chemicals, dispense rates, etc). However I want to be able to reuse the code for each so that the only thing that differs is an array of config structs.
For example:

struct DispenserConfig {
  name: &'static str,
  ratio: f32,
  pump_pin: u8,
}
static DISPENSER_CONFIGS: [DispenserConfig; 2] = [
  DispenserConfig {
    name: "citric acid",
    ratio: 0.1,
    pump_pin: 22,
  },
  DispenserConfig {
    name: "chlorine",
    ratio: 0.05
    pump_pin: 23,
  }
]

Where I'd like to then iterate through DISPENSER_CONFIGS to get everything going.

The problem is that ESP stores each pin as a separate field on a Pins struct.
Plus ESP uses a generic for each pin, rather than a single type which stores the pin as struct field, but that can be solved by using degrade() to convert to an AnyPin type.

I thought about trying to just build a mapping of all possible pin integers, such as:

for dispenser_config in DISPENSER_CONFIGS.iter() {
  let pump_pin = match dispenser_config.pump_pin {
    22 => io.pins.gpio22.into_push_pull_output().degrade(),
    23 => io.pins.gpio23.into_push_pull_output().degrade(),
    _ => panic!("invalid pin"),
  }

  ...
}

However this errors because of:

error[E0382]: use of moved value: `io.pins.gpio22`
    --> src/main.rs:190:19
     |
190  |             22 => io.pins.gpio22.into_push_pull_output().degrade(),
     |                   ^^^^^^^^^^^^^^ ----------------------- `io.pins.gpio22` moved due to this method call, in previous iteration of loop
     |
note: `GpioPin::<MODE, GPIONUM>::into_push_pull_output` takes ownership of the receiver `self`, which moves `io.pins.gpio22`
    --> /home/phemmer/.cargo/registry/src/index.crates.io-6f17d22bba15001f/esp-hal-0.16.1/src/gpio.rs:1170:34
     |
1170 |     pub fn into_push_pull_output(self) -> GpioPin<Output<PushPull>, GPIONUM> {
     |                                  ^^^^
     = note: move occurs because `io.pins.gpio22` has type `GpioPin<esp_hal::gpio::Unknown, 22>`, which does not implement the `Copy` trait

I'm guessing this is because even though I know that no 2 dispenser configs will reuse the same pin, rust doesn't.

Is there any way I can get this working?

1 Like

This feels like a use case where a macro might be easier than trying to fight the borrow checker. The macro would expand to effectively an unrolled loop so the borrow checker can do partial moves and see for itself that each pin is only used once.

1 Like

If you don't need other pins after your for loop has run you could move all of them to a Vec<Option<AnyPin>> and then .take() only those you need.

I ended up going with the macro route. Though instead of configuring which pin each dispenser was on, I just built an array of the maximum number of pins used by any device, witch each position in the array corresponding to a pin. And instead of brewing my own macro, I just went with seq-macro for simplicity. But your response got me thinking in that direction. Thanks

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.