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

Good evening everyone,

I have been trying for days to use SetMessage(const char* message) & SetScreenNow(Screen screen) but I can never get anything that compiles.

I got close at some point with some variation of this:

#if defined(INCLUDE_SSD1306) || defined(ENABLE_SSD1306)
namespace display {
    namespace ssd1306 {
/* the enum was around at some point too but I was getting double definition error for it.
enum Screen {
  SCREEN_UNSET,
  SCREEN_STARTUP,
  SCREEN_MESSAGE,
  SCREEN_ERROR_MESSAGE,
  SCREEN_PLI,
  SCREEN_IMAGE,  // also for animations
  SCREEN_OFF,
  SCREEN_DEFAULT
};
*/
        void SetMessage(const char* message);
        void SetScreenNow(Screen screen);
    }
}

void SetMessageOLED(const char* message) {
    display::ssd1306::SetMessage(message);
    display::ssd1306::SetScreenNow(display::ssd1306::SCREEN_MESSAGE);
}
#endif

This was the error I got:

In file included from C:\Users\Olivier\Desktop\LightSabers\ProffieOS\ProffieOS.ino:1724:
C:\Users\Olivier\Desktop\LightSabers\ProffieOS\config\olicomplex1.6.03.0_prop_presets.h:2588:42: error: 'SSD1306Template<128, long unsigned int> display' redeclared as different kind of entity
 2588 |     SSD1306Template<128,uint32_t> display(&display_controller);
      |                                          ^
In file included from C:\Users\Olivier\Desktop\LightSabers\ProffieOS\config\olicomplex1.6.03.0_prop_presets.h:426,
                 from C:\Users\Olivier\Desktop\LightSabers\ProffieOS\ProffieOS.ino:701:
C:\Users\Olivier\Desktop\LightSabers\ProffieOS\props\multi_prop.h:192:11: note: previous declaration 'namespace display { }'
  192 | namespace display {
      |           ^~~~~~~

exit status 1

Compilation error: 'SSD1306Template<128, long unsigned int> display' redeclared as different kind of entity

Since that was a new error, I thought: there are two “display”, a folder and a variable. I am sure there must be a better way but since i don’t any better, I choose to re-name the variable “dislpay” to “oled_display” in ssd1306.h and in my config for the bullet count. This compiles if I do not use multi_prop.h

Arduino seemed like it was going to compile since the progress bar was going much further than usual but before the end again:

undefined reference to display::ssd1306::SetMessage(char const*)'& undefined reference to display::ssd1306::SetScreenNow(display::ssd1306::Screen)' are back just the same when I started back in October.

I would highly appreciate some help or explanation about what am I doing wrong or if this can even be possible.

My goals/hopes are:

  • with multi_prop.h, to be able to send “prop_mode” message changes to the OLED.
  • with jetpack_prop.h to be able to send “jetpack_status” messages to the OLED & eventually to be able to send animations to the OLED for the jetpack/missile function.
  • with morsecode_prop.h, to have the typed morse code on the top line and the decoded character on the bottom line ideally in both Starjedi and Aurebesh at the same time.

Is any of this possible/impossible ?

Thank you for reading this far, thank you for your time.

All of this is possible.
Some of it is easy, but require some minor workarounds.
Doing it “the right way” is a little harder.

First of all, let me explain why what you have doesn’t work.

The problem

  1. The “folder” doesn’t matter. Folders don’t show up in any meaningful way inside the code, so the “display” folder is not a problem.
  2. There are two things called “display” though, one is a namespace, and you do use “display::” to access things that live inside that namespace. The other is an object/instance of the SSD1306Template templated class. Instances are different from namespaces in that they allocate memory in RAM, and exists as an entity at runtime. (namespaces are purely compile time entities.) Accessing member methods/variables of an instance is done like this: “display.SetMessage”.
  3. It turns out though, that the display instance (which is the one we want) may potentially be called something other than “display”, and there may be more than one of them. (Although that is unusual.) Also, for various reasons, the display instance is declared after the prop in the ProffieOS.ino file, so the prop doesn’t actually know about the display instance.

The workaround

We can put a simple function like this somewhere at global level, before the prop:

void display_SetMessage(const char* message);

Now, the in the prop code, we can use this function, even though it’s not defined yet. (It’s declared, but not defined.)

Finally, at some point after the display instance has been insantiated (in ProffieOS.ino) we can define the function:

void display_SetMessage(const char* message) {
   display.SetMessage(message);
}

And then everything will work.

The second problem

While the above works, it probably won’t do what you want if you have a bigger display, two displays, or some other weird configuration. For that reason, I prefer to set it up so that the display choses what messages to display based on the messages it receives rather than having the prop decide.

The right way (maybe)

It’s possible to write your own “display controller” which controls what is shown on the display in much more detail. That is where I imagine stuff like this belongs. Unfortunately there aren’t a lot of examples that shows how to do this, which probably makes it more difficult than the workaround outlined above.

This is called forward declaration, yes?

Correct

[more characters]

Thank you for that detailed answer. I will keep on trying until I get it working.

  1. But doesn’t the namespace “display” created a reference to the folder display ?
  1. I changed that to oled_display in ssd1306.h, should I change it back to just display ?
  2. Would the name oled_display take more space in RAM because it is 5 characters longer ?
  1. Where is global level ? Is that in the prop file after
#ifndef PROPS_MULTI_PROP_H
#define PROPS_MULTI_PROP_H

but before

template<class Saber, class Blaster, class Detonator, class Jetpack, class MorseCode>
class MultiProp : public virtual Saber, public virtual Blaster, ...

Is that correct ?

  1. I will try to get to that if I get a different display or if there is some interest in my props by someone with different displays. However I feel that if someone has a display, the OLED screen is pretty standard?

Again, thank you for your time. MTFBWY

Yes. Outside of any classes or structs. Like the root level of the file.

1 Like

No.
They just happen to have the same name.

That seems unlikely, because the standard “display” object is not in ssd1306.h, it is here:

No, names of functions and variables don’t survive compilation, and thus are not transferred to the Proffieboard when you upload.

Global level is in every file outside of all class scopes. (So, you are correct.)
The code I posted before should probably go in ProffieOS.ino though.

That might be true for now, but I think color displays will become more common in the future. As of right now, color displays have no support for arbitrary text messages, everything has to be done with animations and images.

Still getting an error:

c:/.../local/arduino15/packages/proffieboard/tools/arm-none-eabi-gcc/9-2020-q2-update/bin/../lib/gcc/arm-none-eabi/9.3.1/../../../../arm-none-eabi/bin/ld.exe:C:\...\Local\Arduino15\packages\proffieboard\hardware\stm32l4\3.6\variants\STM32L452RE-ProffieboardV3/linker_scripts/STM32L452RE_FLASH.ld:224: warning: memory region `SRAM2' not declared
c:/.../local/arduino15/packages/proffieboard/tools/arm-none-eabi-gcc/9-2020-q2-update/bin/../lib/gcc/arm-none-eabi/9.3.1/../../../../arm-none-eabi/bin/ld.exe: warning: changing start of section .bss by 16 bytes "(this line is repeated 5 times)"
c:/.../local/arduino15/packages/proffieboard/tools/arm-none-eabi-gcc/9-2020-q2-update/bin/../lib/gcc/arm-none-eabi/9.3.1/../../../../arm-none-eabi/bin/ld.exe: C:\...\Local\Temp\ProffieOS.ino.elf.kUn1bQ.ltrans11.ltrans.o: in function `void swapProp<MultiProp<SaberFett263Buttons, BlasterBCButtons, Detonator, Jetpack, MorseCode> >(MultiProp<SaberFett263Buttons, BlasterBCButtons, Detonator, Jetpack, MorseCode>*, Prop_Mode, Effect*, char const*, char const*, int)':
C:\...\Desktop\LightSabers\ProffieOS\props/multi_prop.h:131: undefined reference to `display::ssd1306::display_SetMessage(char const*)'
c:/.../local/arduino15/packages/proffieboard/tools/arm-none-eabi-gcc/9-2020-q2-update/bin/../lib/gcc/arm-none-eabi/9.3.1/../../../../arm-none-eabi/bin/ld.exe: C:\...\Local\Temp\ProffieOS.ino.elf.kUn1bQ.ltrans11.ltrans.o: in function `MultiProp<SaberFett263Buttons, BlasterBCButtons, Detonator, Jetpack, MorseCode>::Event(BUTTON, EVENT) [clone .lto_priv.0]':
C:\...\Desktop\LightSabers\ProffieOS\props/multi_prop.h:131: undefined reference to `display::ssd1306::display_SetMessage(char const*)'
collect2.exe: error: ld returned 1 exit status

exit status 1

Compilation error: exit status 1

This is the offending part of the code:

#if defined(INCLUDE_SSD1306) || defined(ENABLE_SSD1306)
// Forward declarations for OLED functionality
namespace display {
    namespace ssd1306 {
void display_SetMessage(const char* message);
    }
}
#endif

namespace MultiPropHelpers { // namespace used to have "setModeMessage" & "announcemode"
                             // available in jetpack_prop.h & morsecode_prop.h
    // Display a mode message on the OLED screen
    void setModeMessage(const char* message1, const char* message2) {
        Serial.println(message1);
        // to check if OLED is present & display message2 on it
        #if defined(INCLUDE_SSD1306) || defined(ENABLE_SSD1306)
            display::ssd1306::display_SetMessage(message2);
        #endif
    }
    // Play a sound for a mode or beep if sound is unavailable
    void announcemode(Effect* sound_name) {
        if (!hybrid_font.PlayPolyphonic(sound_name)) {
            // Use beeper for fallback sounds
            beeper.Beep(0.05, 2000);
            beeper.Silence(0.05);
            beeper.Beep(0.05, 2000);
        };
    }
}

And if you are curious, this is the latest version of multi_prop. I worked on reducing the repetition. I managed for the top part, but for the bottom part, my code ended up using a lot more memory so I abandoned it, but you can still find it in the comments at the bottom of the file.
multi_prop.h (23.6 KB)

Once again, thank you for reading and any help solving the error above will be greatly appreciated.

Remove display::ssd1306::, the function is just called display_SetMessage.

I hadn’t registered this information till now. I understand why you call it a workaround.

But I guess I am still not doing it right as I am now getting this error:

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

exit status 1

Compilation error: 'display' was not declared in this scope; did you mean 'Display'?

Of course I tried with Display but that gives error:

C:\...\Desktop\LightSabers\ProffieOS\ProffieOS.ino: In function 'void display_SetMessage(const char*)':
C:\...\Desktop\LightSabers\ProffieOS\ProffieOS.ino:1569:11: error: missing template arguments before '.' token
 1569 |    Display.SetMessage(message);
      |           ^

exit status 1

Compilation error: missing template arguments before '.' token

And here is my code:

  • In ProffieOS.ino, I have:
#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

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

// added for messages from multi_prop.h
#if defined(INCLUDE_SSD1306) || defined(ENABLE_SSD1306)
void display_SetMessage(const char* message) {
   display.SetMessage(message);
}
#endif
  • In multi_prop.h, I have:
#if defined(INCLUDE_SSD1306) || defined(ENABLE_SSD1306)
void display_SetMessage(const char* message);
#endif

namespace MultiPropHelpers { // namespace used to have "setModeMessage" & "announcemode"
                             // available in jetpack_prop.h & morsecode_prop.h
    // Display a mode message on the OLED screen
    void setModeMessage(const char* message1, const char* message2) {
        Serial.println(message1);
        // to check if OLED is present & display message2 on it
        #if defined(INCLUDE_SSD1306) || defined(ENABLE_SSD1306)
            display_SetMessage(message2);
        #endif
    }
    // Play a sound for a mode or beep if sound is unavailable
    void announcemode(Effect* sound_name) {
        // more code here
        };
    }
}

What am I doing wrong ? Thank you.

Try display_controller

I forgot that this method is in the controller, not in the display object.

1 Like

Unfortunately, it is still not it:

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

exit status 1

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

& DisplayControllerBase gives another:
error: missing template arguments before '.' token

I have to say, I totally fail to see the logic behind how would the compiler know that it would need to look into “C:…\Desktop\LightSabers\ProffieOS\display\ssd1306.h” for SetMessage(message) with just:

void display_SetMessage(const char* message) {
   display.SetMessage(message);            //or with display_controller.SetMessage(message);
}
#endif

For me, it seems that there is some “path” information missing ? Yes I understand that subfolders means nothing but should’nt some reference to “where is” SetMessage “located” be added. Should I maybe add a namespace in ssd1306.h around SetMessage to let the compiler know “this is where to look” ?

Again, thank you for reading. Please accept my apologies for waiting your time on my crazy idea. And as always, any input is greatly appreciated.

Do you have ENABLE_SSD1306 in your config file?

The compiler just looks for “display_controller”, which is defined here:

As you can see the type of the display_controller variable is StandardDisplayConroller<128, uint32_t>. That type is defined here:

That is in a different file, but the compiler doesn’t have to go “find” the file, because the full text of this file is included into ProffieOS.ino here:

1 Like

I have #define INCLUDE_SSD1306 because you told me to use that instead of #define ENABLE_SSD1306 since I am using the bullet count function (I must have bullet count or blaster_BC_buttons.h will not compile without it). I would love to have something that can work for both “include” & “enable” ssd1306, if possible ?

But if it is not possible @NoSloppy, is there a way to make bullet count not mandatory in your prop ? (However, I would hate to loose that functionality)

Bullet count is mandatory?
I’ve never even played with that for OLED before so that can’t be right. Doesn’t compile why?
Which version blaster_BC_buttons? Did you grab the version that’s in an open PR?

That’s fine, it just means that display_SetMessage() has to move further down in ProffieOS.ino, to here:

1 Like

I have two versions:

  • one that was on your github dated:
    Copyright (c) 2016-2019 Fredrik Hubinette
    Copyright (c) 2023 Brian Conner
  • the other is the version you had pm’d me, dated:
    Copyright (c) 2016-2023 Fredrik Hubinette
    Copyright (c) 2023 Brian Conner

When I was testing dual_prop.h and started doing multi_prop.h, I tried to see which POS props could be compiled with dual/multi_prop. I don’t remember the exact error message but your older blaster was “complaining” if I didn’t have bullet count activated in my config. To be honest, I didn’t test the newer one of the two without bullet count.

If you want, I can retry tonight and send you the exact error messages ?

sure. Do you mean BLASTER_SHOTS_UNTIL_EMPTY?

I don’t remember, sorry. I will re-run it and let you know.

I can’t get it to compile. If I put

// added for messages from multi_prop.h
#if defined(INCLUDE_SSD1306) || defined(ENABLE_SSD1306)
void display_SetMessage(const char* message) {
   display.SetMessage(message);
}
#endif

Above:

I get the same error as before:

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

exit status 1

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

And if I put it below:

I get a new error:

C:\...\ProffieOS\ProffieOS.ino: In function 'void display_SetMessage(const char*)':
C:\...\ProffieOS\ProffieOS.ino:1730:23: error: 'class DisplayHelper<128, long unsigned int, BaseLayerOp<StandardDisplayController, ByteArray<> >, ClearRectangleOp<10, 80, 8, 24>, WriteBulletCountOp<10, 20, 5> >' has no member named 'SetMessage'
 1730 |    display_controller.SetMessage(message);
      |                       ^~~~~~~~~~

exit status 1

Compilation error: 'class DisplayHelper<128, long unsigned int, BaseLayerOp<StandardDisplayController, ByteArray<> >, ClearRectangleOp<10, 80, 8, 24>, WriteBulletCountOp<10, 20, 5> >' has no member named 'SetMessage'

How can I resolve the above error ?