Pd/hvcc noob question: detecting a logical switch position at load

I’m fairly new to Pd, so I am assuming this is one of those “it’s easy if you know” sorta things: how to read the position of a “logical” switch at patch load.

The switch here is a 3-way toggle using two pins on the Daisy Seed: one for UP and the other for DOWN. When I say “logical”, I mean the MIDDLE switch position must be deduced: if neither of the two physical switches results in a bang at start-up, then, logically, the switch can only be in the MIDDLE position.

The board.json looks like this:

// stuff omitted
"components": {
    "sw1_up": {
        "component": "Switch",
        "pin": 9
    },
    "sw1_down": {
        "component": "Switch",
        "pin": 10
    },
}, // more stuff omitted

So, as an example, let’s set an LED to 100%, 66%, or 0% brightness depending on the toggle switch position when the patch loads. This is one approach I’ve come up with:

Using the *_fall variants at runtime is simple enough. However, using a flag and delay to handle patch start-up feels dirty and over-complicated. Is there something more elegant in Pd?

There are smarter and more Pd-experienced folks than me on these forums: am I missing something obvious?

I can barely do anything in Pd, so I’ll come back with a dumb question.

Is all that an effort to not poll the state of the two pins?

1 Like

This came up in a discord server earlier this week and I don’t really remember what solution we came up with then, but was also using the rising and falling edge behavior.

However you could also “poll” the current state of the switch as @tele_player suggested.

Perhaps something like this:

The logic here being:

  • only detect changes in the switch state
  • when either switch is on/1 select and send its associated message
  • when the switches are the same, they must both be closed (0) and then send this message

I haven’t tested this patch, but logically it should work. Downside with control logic is that the order of operations is very important (cold inlet on the == for instance). There might be some edge-case bugs here, but since these switches are handled by a single mechanical input that’s hopefully less likely.

1 Like

Oh I think for detecting on startup this is probably better:

And you may need to do this to force that control logic:

One of the first things I tried was:

The bang was needed to make the DOWN-to-MIDDLE case work. This worked in Plugdata, but when flashed to the Daisy Seed (after swapping in the @hv_params), I got a constant 66% brightness on the LED and I gave up on debugging it.

This also works in Plugdata at design time:

But when flashing to the hardware, I got nothing at all and moved on.

The first approach I posted is the only one I could get to work on the hardware, for reasons I haven’t dug into.

Thanks @tele_player and @dreamer for the suggestions; I’ll play with this more but I have a (janky) working solution for now.

BTW, the only “edge case” bugs I could find in the [==] solution were around both switches being ON at the same time. Which is impossible with a 3-way toggle in the real world.

Indeed that’s the case that you should never be able to physically see. Although there could be glitches in the gpio pins of course.

I do feel that the last case should work. Need to find a three-way-switch and some proto-board so I can test it myself …

Edit: Oh I now see you didn’t use the _press variant of the switches, so you are only receiving bangs and not a stream of number messages.

So in your case that would be sw1_up_press and sw1_down_press.

1 Like

Aaah, of course! Brilliant. Thanks for catching that.

Both of these work as intended:

And the [t b f] version:

I really should keep this bookmarked while learning this stuff.

1 Like

With trigger you get the proper directed control flow of “right-to-left”.

Still not 100% sure if it is needed, but in general it’s good practice to make sure you deal with this in the control flow logic or you might get unintended consequences.

1 Like

Practically, I don’t believe it is needed here (because of the constant stream of polling messages). But I’ve watched enough cheetomoskeeto videos to prefer it. “Belt and bracers”, as they say.

As an aside, I think omitting the [t b f] while using a Pd [tgl] would not work for the DOWN-to-MIDDLE case while testing. Learning the differences between vanilla Pd objects at design time vs. how the hardware and [r foo @hv_param] behave at runtime has been a big part of the learning curve.

Yeah it’s a bit tricky to simulate this constant stream of numbers every block.

Something like this for the 3-position-switch case:

1 Like

Was just thinking that if you have a project with a lot of these 3-way switches an abstraction like this would be handy:

Just pass the names of the two pins as arguments and it sets everything up for you :+1:

2 Likes

Exactly. The Hothouse code does the same thing.

In your effect code, something like:

// Arrays map to toggle switch positions like this:
//   k*Vals[] = {UP, MIDDLE, DOWN}
const float kFreqVals[] = {0.05f, 0.02f, 0.01f};
const float kDepthVals[] = {0.2f, 0.1f, 0.05f};
const float kDelayVals[] = {0.2f, 0.1f, 0.05f};

// [...]

  float freq_vary =
      kFreqVals[hw.GetToggleswitchPosition(Hothouse::TOGGLESWITCH_1)];
  float depth_vary =
      kDepthVals[hw.GetToggleswitchPosition(Hothouse::TOGGLESWITCH_2)];
  float delay_vary =
      kDelayVals[hw.GetToggleswitchPosition(Hothouse::TOGGLESWITCH_3)];

And already in hothouse.cpp & hothouse.h:

  enum ToggleswitchPosition {
    TOGGLESWITCH_UP,
    TOGGLESWITCH_MIDDLE,
    TOGGLESWITCH_DOWN,
    TOGGLESWITCH_UNKNOWN,
  };

  enum Toggleswitch {
    TOGGLESWITCH_1,
    TOGGLESWITCH_2,
    TOGGLESWITCH_3,
  };

Hothouse::ToggleswitchPosition Hothouse::GetToggleswitchPosition(
    Toggleswitch tsw) {
  switch (tsw) {
    case (TOGGLESWITCH_1):
      return GetLogicalSwitchPosition(switches[SWITCH_1_UP],
                                      switches[SWITCH_1_DOWN]);
    case (TOGGLESWITCH_2):
      return GetLogicalSwitchPosition(switches[SWITCH_2_UP],
                                      switches[SWITCH_2_DOWN]);
    case (TOGGLESWITCH_3):
      return GetLogicalSwitchPosition(switches[SWITCH_3_UP],
                                      switches[SWITCH_3_DOWN]);
    default:
      // [...]
      return TOGGLESWITCH_UNKNOWN;
  }
}

Hothouse::ToggleswitchPosition Hothouse::GetLogicalSwitchPosition(Switch up,
                                                                  Switch down) {
  return up.Pressed()
             ? TOGGLESWITCH_UP
             : (down.Pressed() ? TOGGLESWITCH_DOWN : TOGGLESWITCH_MIDDLE);
}