Is there a way to send messages to OLED from a prop?

Hmm, it would seem that when using bullet counts, the SetMessage function gets hidden away inside the display controller class.

It should still be possible to call it like this:

void display_SetMessage(const char* message) {
   display_controller.ops_.op_.SetMessage(message);
}

If we want it to work both with and without bullet counts, then we have to add a pass-through function for SetMessage in the DisplayHelper class.

Yikes, another day, another error:

C:\Users\Olivier\Desktop\LightSabers\ProffieOS_for_Oli-experiments\ProffieOS_for_Oli-experiments.ino: In function 'void display_SetMessage(const char*)':
C:\Users\Olivier\Desktop\LightSabers\ProffieOS_for_Oli-experiments\ProffieOS_for_Oli-experiments.ino:1734:28: error: 'BaseLayerOp<StandardDisplayController>::Controller<128, long unsigned int> DisplayHelper2<128, long unsigned int, BaseLayerOp<StandardDisplayController, ByteArray<> >, ClearRectangleOp<10, 80, 8, 24>, WriteBulletCountOp<10, 20, 5> >::op_' is private within this context
 1734 |    display_controller.ops_.op_.SetMessage(message);                               // *** added by Oli ***
      |                            ^~~
In file included from C:\Users\Olivier\Desktop\LightSabers\ProffieOS_for_Oli-experiments\ProffieOS_for_Oli-experiments.ino:1563:
C:\Users\Olivier\Desktop\LightSabers\ProffieOS_for_Oli-experiments\display\ssd1306.h:199:50: note: declared private here
  199 |   typename OP::template Controller<Width, col_t> op_;
      |                                                  ^~~

exit status 1

Compilation error: 'BaseLayerOp<StandardDisplayController>::Controller<128, long unsigned int> DisplayHelper2<128, long unsigned int, BaseLayerOp<StandardDisplayController, ByteArray<> >, ClearRectangleOp<10, 80, 8, 24>, WriteBulletCountOp<10, 20, 5> >::op_' is private within this context

op_’ is private, obviously I had to try:

template<int Width, class col_t, class OP, class... OPS>
class DisplayHelper2<Width, col_t, OP, OPS...> {
 public:
  void set(DisplayHelper1<Width, col_t>* out) {
    out->a = &op_;
    rest_.set(out + 1);
  }
 //private:
 public:
  typename OP::template Controller<Width, col_t> op_;
  DisplayHelper2<Width, col_t, OPS...> rest_;
};

to make it public (in ssd1306.h) and it does compile (finally), but is it alright to do that or will I break something ?

Making it public won’t break anything, it’s just another “workaround”. It might break in the future, because ops_ is an implementation detail, which may change name or disappear completely in the future. But for this particular version of the code, it will work just fine.

Doing things “the right way” means planning ahead so that things work for all configurations and future versions of ProffieOS, but that’s not what we’re doing right now anyways…

1 Like

Thank you for all all your help @profezzorn . I would like to learn “the right way” eventually. For now, I am very happy to have “something” that finally compiles after having “no compile” (with regards to SetMessage) since the 25th of September, when started tinkering with SetMessage!

I have been reading about “private”, “public”, “protected”, “friend”, …
and trying to understand every bit of code from ssd1306.h (still in progress) to hopefully one day be able to do it right.

This sentence has been running wild in my head for the last few days: how does the display “receive” messages ?

I did find this in prop_base.h:

  const char* current_preset_name() {
    return current_preset_.name.get();
  }

This how prop_base.h “send” preset name to OLED, except prop_base.h doesn’t “send” it, it only “sets” it’s value & ssd1306.h uses the “value/content” of current_preset_name() to display the preset name if it is appropriate. Is this an accurate description?

I also found in ssd1306.h:

struct BlasterDisplayConfigFile : public ConfigFile {
  BlasterDisplayConfigFile() { link(&font_config); }
...

#define ONCE_PER_BLASTER_EFFECT(X)  \
...

template<typename PREFIX = ByteArray<>>
struct BlasterDisplayEffects  {
  BlasterDisplayEffects() : dummy_(0) ONCE_PER_BLASTER_EFFECT(INIT_IMG) {}
  int dummy_;
  ONCE_PER_BLASTER_EFFECT(DEF_IMG)
};
...

template<int Width, class col_t, typename PREFIX = ByteArray<>>
class BlasterDisplayController : public StandardDisplayController<Width, col_t, PREFIX> {
public:
  BlasterDisplayEffects<PREFIX> img_;
  BlasterDisplayConfigFile &blaster_font_config;
  BlasterDisplayController() :
    img_(*getPtr<BlasterDisplayEffects<PREFIX>>()),
    blaster_font_config(*getPtr<BlasterDisplayConfigFile>()) {
  }
...

So would I need to create a “MultiPropDisplayController”, a “JetPackDisplayController” & a “MorseCodeDisplayController” similar to BlasterDisplayController to do things “the right way” which seems “easy enough” at least for images &/or animations ? But I still have no clue about “the right way” for text messages.

A “message” is just a function call really. The difference is that it goes through SaberBase. Classes that inherit from SaberBase has a number of virtual functions which gets called from the prop class. The difference between what you’re doing and “sending a message” is that SaberBase has code for calling the virtual functions in all instances of SaberBase. This includes (but is not limited to) all blades, the sound system, displays, and the prop class itself. Here is an example:

The prop class has this code:

Which in SaberBase just calls DoEffectR(EFFECT_CLASH);

DoEffect(R) will in turn call SB_Effect() and SB_Effect2() on all saber bases, which ends up here in the display code:

The difference here is that the prop doesn’t know anything about displays, it just sends the message “a clash has occurred”, and each SaberBase gets notified, and then it can do what it needs to do to react to that message.

In most cases, messages are conveyed by the “Effect” enum, and the values for that Enum are defined here:

Adding more values to this enum is “free”, meaning that it generally doesn’t use any extra memory to do so. Code that sends and receives messages use memory though, but that code can be optional, either because it’s in a prop file, or it may be controlled by a define.

This also goes to your question about display controllers. Code which lives in a custom display controller doesn’t use any flash memory unless you actually use that controller, so yes, you may need to create specific controllers for each prop, unless we can come up with simple, low-cost (in terms of memory) methods to do the same thing generically for all props in one go.

1 Like

So, I created the pass-through function and I have again op set to private, I was not happy to have to change it to public.

I have run a few compile tests (without bullet count & with enable_ssd1306 instead of include ssd1306) but it looks promising so far.

I also tried : include ssd1306 but no bullet count in config bottom & I am getting this error:

C:\...\Desktop\LightSabers\ProffieOS\ProffieOS.ino: In function 'void display_SetMessage(const char*)':
C:\...\Desktop\LightSabers\ProffieOS\ProffieOS.ino:1734:4: error: 'display_controller' was not declared in this scope; did you mean 'DisplayControllerBase'?
 1734 |    display_controller.SetMessage(message);
      |    ^~~~~~~~~~~~~~~~~~
      |    DisplayControllerBase

exit status 1

Compilation error: 'display_controller' was not declared in this scope; did you mean 'DisplayControllerBase'?

But I guess this is expected behavior ? Or is there a case where you would use:
#define INCLUDE_SSD1306 without bullet count in config bottom ?

This should only happen if you use INCLUDE_SSD1306, but you don’t have a display. (Or you don’t call the display controller display_controller.) Since including ssd1306 without using it, eats up some extra memory, it is not a recommended configuration…

1 Like

Let me rephrase that, with INCLUDE SSD1306 and no bullet count, is there another “functionality” (similarly to bullet count) that calls the display controller ?

I have a feeling the answer is no ?

When you say “no bullet count”, what do you mean exactly?
Does it mean that you have a regular display in the config file?
Does it mean there is no display in the config file?
Does it mean you define your own display controller, but with two less ops?

The way I see and understand OLED, there are two possibilities:

  1. in ProffieOS.ino
#ifdef ENABLE_SSD1306
#include "display/ssd1306.h"

#ifndef DISPLAY_POWER_PINS
#define DISPLAY_POWER_PINS PowerPINS<>
#endif

StandardDisplayController<128,uint32_t> display_controller;
SSD1306Template<128,uint32_t,DISPLAY_POWER_PINS> display(&display_controller);
#endif

or 2) in ProffieOS.ino

#ifdef INCLUDE_SSD1306
#include "display/ssd1306.h"
#endif

& also 2) in myconfig.h

#ifdef CONFIG_BOTTOM
    DisplayHelper<128,uint32_t,
    BaseLayerOp<StandardDisplayController>,
    ClearRectangleOp<10,80,8,24>,
    WriteBulletCountOp<10,20,5> >
    SSD1306Template<128,uint32_t> display(&display_controller);
#endif // CONFIG_BOTTOM

And my question is:
about 2):

  • in ProffieOS.ino
#ifdef INCLUDE_SSD1306
#include "display/ssd1306.h"
#endif
  • in mycongig.h
#ifdef CONFIG_BOTTOM
   // ... some code here ...
#endif // CONFIG_BOTTOM

but without in … some code here …

    DisplayHelper<128,uint32_t,
    BaseLayerOp<StandardDisplayController>,
    ClearRectangleOp<10,80,8,24>,
    WriteBulletCountOp<10,20,5> >
    SSD1306Template<128,uint32_t> display(&display_controller);

Is there something else (other than “bullet counts”) that already exit in your ProffieOS “official code” & also “initialize the display & can go in CONFIG_BOTTOM or somewhere else” that I can test with my code to see if everything that is working with your code, still works with my code ?

Not “me” but does “your code” has other possibilities.

No.

No. I just want to know if there something else that would use #define INCLUDE_SSD1306 in myconfig.h and some other bits of code “somewhere else” (in myconfig.h or other than myconfig.h) that I can test with my code to see if it still works as you intended despite my workarounds in my ProffieOS version of your code ?

This code is not correct, it’s missing a display_controller; I think.

If you don’t put this code in CONFIG_BOTTOM, then not only do you not have bullet counts, you also do not have any kind of display at all.

The reason I ask, is because you can also put this in your config file:

#ifdef CONFIG_BOTTOM
StandardDisplayController<128,uint32_t> display_controller;
SSD1306Template<128,uint32_t> display(&display_controller);
#endif

Now you have a display, but no bullet counts.
You could also theoretically create your own display controller, which would do something different, but again, have no bullet counts.

If you don’t have a display then obviously no code can use it.

Well now I’m confused, because you said earlier that there wasn’t…

The answer is no. Ideally, INCLUDE_SSD1306 wouldn’t even exist, it would just default to on all the time. All the display classes would be compiled and ready to use in your config file, all you would need to do is use them. If you don’t, no display, if you do you get a display.

Unfortunately, there doesn’t seem to be a way to do that that doesn’t eat up some memory, even if you don’t use it. That’s why INCLUDE_SSD1306 exists. It doesn’t do anything other than include some more classes into the compilation though.

As stated earlier you shouldn’t have to worry about the case where INCLUDE_SSD1306 is defined, but no display, because that’s not a recommended configuration.

Also, you don’t have to worry about it because your current implementation is just a workaround that isn’t really good enough to put into ProffieOS proper, so why does it matter if there are some configurations that don’t work?

this is a copy/paste from POD

except I moved a ">"from the beginning of a line to the end of the previous line because it looked more logical to me for my config code.

TBH I can’t tell you if it works or not because I still don’t have a working physical “prop”. I am happy if stuff compile.

I used to like soldering (I started when I was 12 with leaded solder), but ever since I started this project being forced to use lead free solder because of EU laws about lead, higher temperature for the non leaded solder, my “short distance” eye sight decreasing due to age, the fact that I started the whole process with non “ptfe/silicone” wires that are too stiff and probably too thick and will make huge goose necks on the bottom of my Proffie board, the amount of wires that need to be soldered in my cramfu configuration (I know, I choose it like that, thinking " how hard could it be"), I am now dreading to pick up my soldering iron and finish the job in fear of damaging the board or having a stray wire or excessive goose neck’s.
Every time I look at my chassis, I go "nah, I prefer to ‘play’ with some code rather than ‘play’ with the soldering iron.

If anyone knows someone in Malta who can do the soldering for me, while I watch them doing it (I did find a shop who would do it for me, but I can’t watch because “it would make the technician nervous” - just provide us with a detailed wiring plan), please let me know.

Sorry rant over.

This is what I wanted to know: what other option(s) can I try to make sure my code will “work” (compile for now) in most possible (and already thought of by you @profezzorn and coded for) situations.

I’m confused too :upside_down_face:. No I do not have a “different” (different from the bullet counts - I only know of the bullet counts) display in my config file.

I understand that I am “breaking” your code and that my stuff is not ProffieOS proper.

It might not matter to you, but since I am “breaking” it, would like to break it as little as possible so that there will not be too much to fix later.

Comparing the two, It’s still missing display_controller;

Get some magnifying googles, it makes a HUGE difference.
Also, always order more than you need of everything, that way if something breaks it’s not that big of a deal…

1 Like

Disregard, the code above is missing it. Must have been deleted when I removed my comments before posting, sorry about that. I was comparing directly from myconfig.