Switching between many (more than 2) preset lists

Hello The Crucible,

I found the below thread, very interesting. Thank you @Sabersense for asking this question. I find the outcome to a lot of your past & present questions very instructive. :pray:t2:

struct FakeBladeID {
   static int return_value;
   float id() { return return_value; }
   static void toggle() {
     return_value = NO_BLADE - return_value;
   }
};
int FakeBladeID::return_value = 0;
#undef BLADE_ID_CLASS_INTERNAL
#define BLADE_ID_CLASS_INTERNAL FakeBladeID
#undef BLADE_ID_CLASS
#define BLADE_ID_CLASS FakeBladeID

So how can I adapt this FakeBladeID concept to 3, 4, 5 or more presets ?

Preset presets_saber[] = {
saber preset(s) here
};
Preset presets_blaster[] = {
blaster preset(s) here
};
Preset presets_detonator[] = {
detonator preset here
};
...

This is to be applied in my multi_prop.h so each prop can have it’s own preset.

So I guess, I would need to modify my switch to:

void switchModes() {
    switch (currentMode) {
        case Prop_Mode::SABER:
            hybrid_font.PlayPolyphonic(&SFX_blastermode);
            currentMode = Prop_Mode::BLASTER;
            //add code for "set preset to preset_blaster" here;
            break;
        case Prop_Mode::BLASTER:
            !hybrid_font.PlayPolyphonic(&SFX_detmode);
            currentMode = Prop_Mode::DETONATOR;
            //add code for "set preset to preset_detonator" here;
            break;
        case Prop_Mode::DETONATOR:
            !hybrid_font.PlayPolyphonic(&SFX_jetmode);
            currentMode = Prop_Mode::JETPACK;
            //add code for "set preset to preset_jetpack" here;
            break;
...
    }
}

Where //add code for "set preset to preset_xyz" here; would be replaced by:

FakeBladeID::toggle(preset_xyz);
FindBladeAgain();
SaberBase::DoBladeDetect(true);

And would I need to add anything else in my config ?

Thank you.

1 Like

Were you thinking one button per preset, or one button which goes to the next set of presets?

1 Like

I was thinking several preset lists like this:

Preset presets_saber[] = {
{ "crispity;common", "common/tracks/track1.wav", ...blades styles..., "crispity" },
{ "TeensySF;common", "common/tracks/venus.wav", ... },
{ "SmthJedi;common", "common/tracks/mars.wav", ... },
... many more ...
};

Preset presets_blaster[] = {
{ "_BlasterMode/DC-17;common", "common/tracks/track1.wav", ... },
{ "_BlasterMode/DL-44;common", "common/tracks/track1.wav", ...},
... few more (not as many as saber)...
};

Preset presets_detonator[] = {
{ "detonator;common", "", ... }, // just one for now
};

Preset presets_jetpack[] = {
{ "jetpack_red;common", "", ... },
{ "jetpack_blue;common", "", ... },
};

//Maybe more Preset if I manage to complete other props

BladeConfig blades[] = {
 { 0, //SABER
    WS281XBladePtr<144,bladePin,Color8::GRB,PowerPINS<bladePowerPin2,bladePowerPin3>>(),
    SubBladeWithStride(0,15,2,WS281XBladePtr<16,blade2Pin,Color8::GRB,PowerPINS<bladePowerPin2,bladePowerPin3>>()),
    SubBladeWithStride(1,15,2,NULL),
    CONFIGARRAY(presets_blade), "blade_save" },
 { 1, //BLASTER
    WS281XBladePtr<144,bladePin,Color8::GRB,PowerPINS<bladePowerPin2,bladePowerPin3>>(),
    SubBladeWithStride(0,15,2,WS281XBladePtr<16,blade2Pin,Color8::GRB,PowerPINS<bladePowerPin2,bladePowerPin3>>()),
    SubBladeWithStride(1,15,2,NULL),
    CONFIGARRAY(presets_blaster), "blaster_save" },
 { 2, //DETONATOR
    WS281XBladePtr<144,bladePin,Color8::GRB,PowerPINS<bladePowerPin2,bladePowerPin3>>(),
    SubBladeWithStride(0,15,2,WS281XBladePtr<16,blade2Pin,Color8::GRB,PowerPINS<bladePowerPin2,bladePowerPin3>>()),
    SubBladeWithStride(1,15,2,NULL),
    CONFIGARRAY(presets_detonator), "detonator_save" },
...
};

I have this so far in my switch:

void switchModes() {
    switch (currentMode) {
        case Prop_Mode::SABER:
            !hybrid_font.PlayPolyphonic(&SFX_blastermode);
            currentMode = Prop_Mode::BLASTER;
            FakeBladeID::SetFakeBlade(1);
            FindBladeAgain();
            SaberBase::DoBladeDetect(true);  // Re-detect blades
            break;
        case Prop_Mode::BLASTER:
            !hybrid_font.PlayPolyphonic(&SFX_detmode);
            currentMode = Prop_Mode::DETONATOR;
            FakeBladeID::SetFakeBlade(2);
            FindBladeAgain();
            SaberBase::DoBladeDetect(true);
            break;
        case...
    }
}

And for the part to put at the top, I have this:

struct FakeBladeID {
    static int return_value;
    float id() { return return_value; }
    static void SetFakeBlade(int id) {
        return_value = id;
    }
};
int FakeBladeID::return_value = 0;
#undef BLADE_ID_CLASS_INTERNAL
#define BLADE_ID_CLASS_INTERNAL FakeBladeID
#undef BLADE_ID_CLASS
#define BLADE_ID_CLASS FakeBladeID

Unfortunately, it gives me compile error:

In file included from C:\Users\Olivier\Desktop\LightSabers\ProffieOS\config\olicomplex1.6.03_prop_presets.h:408,
                 from C:\Users\Olivier\Desktop\LightSabers\ProffieOS\ProffieOS.ino:690:
C:\Users\Olivier\Desktop\LightSabers\ProffieOS\props\multi_prop.h: In function 'void switchModes()':
C:\Users\Olivier\Desktop\LightSabers\ProffieOS\props\multi_prop.h:196:13: error: 'FindBladeAgain' was not declared in this scope
  196 |             FindBladeAgain();
      |             ^~~~~~~~~~~~~~

exit status 1

Compilation error: 'FindBladeAgain' was not declared in this scope

I am using Github Master.

Sorry I have not answered your question. Which I had missunderstood and gave you the answer above.

I am thinking (hoping) for my “DUAL_BUTTON_XLONG_PUSH” would switch presets at the same time when switching to the next prop.

Most of this looks reasonable.
I think the problem is that switchModes() might be a function outside the prop class, and thus cannot access FindBladeAgain().

Also, you might want to have a helper function which does the repeated part so that you don’t have to… repeat it…

1 Like

Yes, switchModes() is a function outside the prop class.
So I changed from FindBladeAgain(); to PropBase::FindBladeAgain();
but I get these errors:

n file included from C:\Users\Olivier\Desktop\LightSabers\ProffieOS\config\olicomplex1.6.03_prop_presets.h:408,
                 from C:\Users\Olivier\Desktop\LightSabers\ProffieOS\ProffieOS.ino:690:
C:\Users\Olivier\Desktop\LightSabers\ProffieOS\props\multi_prop.h: In function 'void switchModes()':
C:\Users\Olivier\Desktop\LightSabers\ProffieOS\props\multi_prop.h:202:37: error: cannot call member function 'void PropBase::FindBladeAgain()' without object
  202 |             PropBase::FindBladeAgain();
      |             ~~~~~~~~~~~~~~~~~~~~~~~~^~
C:\Users\Olivier\Desktop\LightSabers\ProffieOS\props\multi_prop.h:212:37: error: cannot call member function 'void PropBase::FindBladeAgain()' without object
  212 |             PropBase::FindBladeAgain();
      |             ~~~~~~~~~~~~~~~~~~~~~~~~^~
C:\Users\Olivier\Desktop\LightSabers\ProffieOS\props\multi_prop.h:222:37: error: cannot call member function 'void PropBase::FindBladeAgain()' without object
  222 |             PropBase::FindBladeAgain();
      |             ~~~~~~~~~~~~~~~~~~~~~~~~^~
C:\Users\Olivier\Desktop\LightSabers\ProffieOS\props\multi_prop.h:240:37: error: cannot call member function 'void PropBase::FindBladeAgain()' without object
  240 |             PropBase::FindBladeAgain();
      |             ~~~~~~~~~~~~~~~~~~~~~~~~^~
C:\Users\Olivier\Desktop\LightSabers\ProffieOS\props\multi_prop.h:250:37: error: cannot call member function 'void PropBase::FindBladeAgain()' without object
  250 |             PropBase::FindBladeAgain();
      |             ~~~~~~~~~~~~~~~~~~~~~~~~^~

exit status 1

Compilation error: cannot call member function 'void PropBase::FindBladeAgain()' without object

Yes, I would like to do that but I need help, please.

This is what I got so far for the helper functions. I need two:

  • one for the sound because one of my props needs something different than the others.
  • one for the rest of the code.

This is my pseudo sound function:

// Function to play sound 
void announcemode { "something3 that takes a 'sound_name_'"
    // (like 'blastermode' from bastermode.wav)
    & does: if (!hybrid_font.PlayPolyphonic('&SFX_' + 'sound_name_' = &SFX_sound_name_)) {beeper.Beep(0.05, 2000);beeper.Silence(0.05);beeper.Beep(0.05, 2000);}
}

This is my pseudo function for the rest:

// Function to avoid repetition (I need a better name for it)
void repetition_function {
  ["something1 that takes text1_" (like "Blaster Mode"),
   "something2 that takes text2_" (like "blaster/nmode"),
    id] // id is already defined in "struct FakeBladeID". Can it be re-used here ?
    & does: Serial.println("text1_");
            // to check if OLED is present & display text2_ on it
            #if defined(INCLUDE_SSD1306) || defined(ENABLE_SSD1306)
            "../display/ssd1306"::SetMessage("text2_");  // This doesn't work. How can I use SetMessage and SetScreenNow from a prop ?
            "../display/ssd1306"::SetScreenNow(SCREEN_MESSAGE);
            #endif
            FakeBladeID::SetFakeBlade(id);
            PropBase::FindBladeAgain(); // error: cannot call member function 'void PropBase::FindBladeAgain()' without object ?
            SaberBase::DoBladeDetect(true);
}

something1, 2 are “text string” variables, I guess ?
something3 is the name of a sound.wav, other than that, I don’t know what kind of object it is ?

private: 
    std::basic_string text1_ // something1
    std::basic_string text2_ // something2
    something3 sound_name_

Thank you for your time & consideration.

This is not how you write a function.
I suggest learning some more C++.

FindBladeAgain() is not a static function, you would need prop.FindBladeAgain(). Although that won’t work either because “prop” isn’t defined yet. You are probably better of moving switchMode() into the prop.

1 Like

As I said, I need help if I need a helper function. I am ok with repeating stuff, as long as it works.

Ok thanks, I will do that.

1 Like

Following this with interest as I think the ability to cycle through multiple batches of arrays/presets would be a really nice feature. As mentioned before, I’ve used it with two arrays a few times now and it’s been really helpful, and even as I write this I’m getting ideas for how you could divide up presets/fonts into batches to make saber navigation really slick and incredibly simple for the end user.

For instance:
Array 1 - Light Side Fonts
Array 2 - Dark Side Fonts
Array 3 - Blade Plug Charging Font
Array 4 - Diagnostic fonts (blade length finder etc.)

With a little creative file and folder management, you could have individual bladein.wavs for each array telling you what you’d just selected. :smiley:

As I say, I watch with interest, especially as I’m in the process of tidying up my own little prop file which had become somewhat cluttered. LOL!

1 Like

That is cool too, I am creating a “multi_prop.h” prop. This is the list of presets arrays:

  • One array for saber
  • One array for blaster
  • One array for detonator
  • One array for jetpack (also prop in progress)
  • and one “goofy” for morse code (to send morse code to the speaker or the blade(s) & display code and decode on OLED (if OLED present) with option to display in Aurebesh or “latin”/ StarJedi font. Why morse code, you could ask: because I fly airplanes and it would be a fun way to teach morse code to my kids (and Aurebesh to my kids and myself).

I would like a prop (chassis) that can do everything.

In your case @Sabersense it is easier, I think, because you don’t need to “switch props”, just “switch presets” and that switch code can be inserted in the “prop” section. Mine I am not sure yet if it can go there.

Cheers

1 Like

I think you missed this one:

  • One array to bring them all and in the darkness bind them
3 Likes

That is for sure going in a comment in my config. :grin:

2 Likes

When the nerds are nerding nerdy things arise so we can all Nerd Out. Love it.

I think I’ve made some progress with this! :smiley: Although I’m not sure it will be much use to you @olivierflying747-8 , since, as you say, you’re aiming for something rather more complex than me. But I’ll share my results anyway in case they’re useful…

So basically I’ve made it so that with no define in your config file, BladeID works as normal, but if you add two define lines to the config, you get the Sabersense Array Selector which can cycle through any number of blade arrays manually using a button press, with the actual returned BladeID values being ignored.

Before we get into the nuts and bolts of how it works, what are the pracatical applications for this?

Well, one way you could use it would be to split your fonts/presets into batches, like this:

Array 1 - Light Side Fonts
Array 2 - Dark Side Fonts
Array 3 - Blade Plug Charging Font
rray 4 - Diagnostic fonts (blade length finder etc.)

Or perhaps in installs with a rotating motor in the chassis, you might want to duplicate the preset list and just have one array that spins the motor and the other array that doesn’t. This would effectively make the Array Selector button press a motor on/off toggle switch.

Or how about setting your actual arrays up differently, so that with one array you have a main blade of 132 pixels, but on the other array, the main blade (i.e. the blade connector) just has the number of pixels on the connector, allowing for more elaborate animations.

Or what about if you have multiple blades, none of which have resistors in them between neg and data (which means BladeID can’t differentiate between them)? Well now you can set up three arrays, all with the same presets and fonts, but have one for your 36 inch 132 pixel blade, another for your shorter 118 pixel blade, and a third for your indoor shoto blade which only has 80 pixels.

I dare say there are lots of other possibilities too.
So how does it all work?

Well first you need to add this to the top of your prop file, just below the prop files title define lines, like this:

#ifndef PROPS_SABER_SABERSENSE_BUTTONS_H
#define PROPS_SABER_SABERSENSE_BUTTONS_H


#ifdef SABERSENSE_ARRAY_SELECTOR
  #ifndef SABERSENSE_NUM_ARRAYS    // No default number of arrays - must be defined in config.
    #error "SABERSENSE_NUM_ARRAYS must be defined in the config file."
  #endif
  struct SabersenseArraySelector {
    static int return_value; // Tracks the current array index.
    static const int sabersense_num_arrays = SABERSENSE_NUM_ARRAYS;
    float id() { 
      return return_value; 
    }  // Return the current array index.
    // New method for cycling through the arrays, using the defined num_arrays.
    static void cycle() {
      return_value = (return_value + 1) % sabersense_num_arrays;
    }
  };
  int SabersenseArraySelector::return_value = 0; // Start with the first array (index 0). 
  #undef BLADE_ID_CLASS_INTERNAL
  #define BLADE_ID_CLASS_INTERNAL SabersenseArraySelector
  #undef BLADE_ID_CLASS
  #define BLADE_ID_CLASS SabersenseArraySelector
#endif

Then add these two to the list of defines in the prop:

#ifdef SABERSENSE_ARRAY_SELECTOR
#define SABERSENSE_ARRAY_SELECTOR
#endif

#ifdef SABERSENSE_NUM_ARRAYS
#define SABERSENSE_NUM_ARRAYS
#endif

Next add this section somewhere within all the event handlers of the prop:

// BladeID and manual blade array selector.
case EVENTID(BUTTON_POWER, EVENT_THIRD_SAVED_CLICK_SHORT, MODE_OFF):
#ifdef SABERSENSE_ARRAY_SELECTOR
  // Cycles through blade arrays regardless of BladeID status.
  SabersenseArraySelector::cycle();
  FindBladeAgain();
  SaberBase::DoBladeDetect(true);
  return true;
#else
  // Runs BladeID manually to actually check BladeID status.
  // Won't change arrays unless status tells it to (i.e. Data/Neg resistance changes).
  FindBladeAgain();
  SaberBase::DoBladeDetect(true);
  return true;
#endif

Then add these two defines to the top of your config. The number on the second define needs to be the number of blade arrays in your config:

#define SABERSENSE_ARRAY_SELECTOR	 
#define SABERSENSE_NUM_ARRAYS 4

And finally lay out your actual blade arrays, each with sequential ident numbers starting at zero, like this:

BladeConfig blades[] = {
//  Use sequential numbers starting at 0 if using Sabersense Array Selector.
   { 0,
//  Main Blade:
 	WS281XBladePtr<8, bladePin, Color8::GRB, PowerPINS<bladePowerPin2, bladePowerPin3>>(),
//  Bluetooth Module:   
    SimpleBladePtr<Bluetooth, NoLED, NoLED, NoLED, bladePowerPin6, -1, -1, -1>(),   
   CONFIGARRAY(noblade),  "Savehilt"}

   { 1,
//  Main Blade:
 	WS281XBladePtr<68, bladePin, Color8::GRB, PowerPINS<bladePowerPin2, bladePowerPin3>>(),
//  Bluetooth Module:   
    SimpleBladePtr<Bluetooth, NoLED, NoLED, NoLED, bladePowerPin6, -1, -1, -1>(),   
   CONFIGARRAY(testblde),  "Savetest"}

   { 2,
//  Main Blade:
 	WS281XBladePtr<114, bladePin, Color8::GRB, PowerPINS<bladePowerPin2, bladePowerPin3>>(),
//  Bluetooth Module:   
    SimpleBladePtr<Bluetooth, NoLED, NoLED, NoLED, bladePowerPin6, -1, -1, -1>(),   
   CONFIGARRAY(lgtblade),  "Savelgt"}

   { 3,
//  Main Blade:
 	WS281XBladePtr<10, bladePin, Color8::GRB, PowerPINS<bladePowerPin2, bladePowerPin3>>(),
//  Bluetooth Module:   
    SimpleBladePtr<Bluetooth, NoLED, NoLED, NoLED, bladePowerPin6, -1, -1, -1>(),   
   CONFIGARRAY(charging), "SaveChrg"}

};
#endif

All arrays must have the same number of blades, though you can always specify dummy blades if you have to, and each array needs to have its presets added to the blade style array. The names in inverted commas specify the location of save files. This means when you switch array, it will return to the blade preset you left the saber on when you were last on that array.

I’ve tested it and it all seems to work at my end. So my next challenge is to make it play unique array ident sound effects to tell you which array you’ve switched to. But that’s a work in progress - if I crack it, I’ll report back here. :slight_smile:

1 Like

I’d like to play with this. Seems super useful, especially with all the testing and fooling around I tend to do.

This part seems weird though, no?

That’s like saying “if the word blue is a thing, then the word blue is a thing”

Did you mean #ifndef ?

2 Likes

I’m not actually sure you need the second one as it obviously only appears essentially as part of the first one and is kind of wrapped in that. But I don’t yet understand enough about everything to be able to confidently omit things like this, so I just followed the convention for all other defines as it didn’t seem to do any harm.

Without actually running any of this (just glancing while at work) I think you could likely simplify the defines bits a little.
NUM_BLADES already holds the the number of blade arrays, so you could just use that inplace of using SABERSENSE_NUM_ARRAYS.
Then, if the user chooses to use the feature by adding
#define SABERSENSE_ARRAY_SELECTOR in their config file,
there’s no purpose to re-define it again with

#ifdef SABERSENSE_ARRAY_SELECTOR
#define SABERSENSE_ARRAY_SELECTOR
#endif

Lastly, is this all assuming you are NOT using BLADE_ID_SCAN_MILLIS?
I ask because I wonder about interference if it were used in conjunction (seems like it would), and how that pertains to needing an option to manually trigger FindBladeAgain() to scan (like you have in your EVENT’s #else condition.

So slimmed down, i think this would do the same thing?


#ifndef PROPS_SABER_SABERSENSE_BUTTONS_H
#define PROPS_SABER_SABERSENSE_BUTTONS_H


#ifdef SABERSENSE_ARRAY_SELECTOR
  struct SabersenseArraySelector {
    static int selected_array;  // Tracks the current array index.
    float id() return selected_array;  // Return the current array index.
    // Cycle through the preset arrays.
    static void cycle() {
      selected_array = (selected_array + 1) % NUM_BLADES;
    }
  };
  int SabersenseArraySelector::selected_array = 0; // Start with the first array (index 0). 
  #undef BLADE_ID_CLASS_INTERNAL
  #define BLADE_ID_CLASS_INTERNAL SabersenseArraySelector
  #undef BLADE_ID_CLASS
  #define BLADE_ID_CLASS SabersenseArraySelector
#endif


No, I’m not using BLADE_ID_SCAN_MILLIS. The idea is to take any automatic scanning out of the process and simply have the button cycle to the next array, so that the entire process is completely manual.

Not sure what you mean about NUM_BLADES already holding the number of blade arrays, as arrays and blades are different. In the example above, there are only two blades but four arrays, i.e. each array has two blades. The system needs to know how many arrays there are so that it knows when to cycle back to the first one. Or have I misunderstood what you mean?

That said, I have a hunch you’re right in that the SABERSENSE_NUM_ARRAYS could be dropped from the list of defines, but it still has to be in the main chunk of code for the above reason. I think…

Maybe my first glance is blurry for sure, sorry.

But I’m pretty sure id() returns which blade array in the BladeConfig to use, not which preser array.
The BladeConfig chosen (best_choice) then chooses the preset array to use because of CONFIGARRAY(preset array name), .

I think there should be some way to get NELEM of the CONFIGARRAYs…
Was it working for you as you had everything?
It may have been happenstance, but again, I’d need to focus in on it all.