Remork bool setup(BelaContext **context, void *userData)

one * too many. PS: use ``` around your code.

There are two fundamental issues here:

Remork i was hoping bela would know what the context was 🙂

There is nothing special about a variable of type BelaContext* named context. It is not defined "somewhere" and you cannot access it globally. You can operate on a it it gets passed to the function you are in. It is passed to setup(), render(), cleanup(). From one of those functions, you can pass it to other functions if needed, but you are not allowed to cache it for longer than the lifetime of the function. In other words, with the exception of setup() and cleanup(), you cannot use the BelaContext outside the audio thread.

Remork digitalWrite(context, 0, gResetPin, 0);
usleep(1000);
digitalWrite(context, 0, gResetPin, 1);

Had you written these lines within the audio thread (e.g.: within render(), or a function called by it(, what you'd have as a result is probably:
- a mode switch (usleep() is not a Xenomai-safe function)
- several dropped blocks (you are telling your thread to do nothing for 1ms, which is 44 samples, but it will actually sleep for a bit longer)
- the first digitalWrite() line would have no effect

The last statement may be the confusing one. Unlike other physical computing platforms (e.g.: Arduino), on Bela a call to digitalWrite() (or any of the other read/write functions, for what matters), does not actually go and write the value. Rather, it sets its value in memory, so that after the execution of render() is completed, the value you set for each specific frame is written to the digital output. Your first call sets the memory buffer to 0 for all frames (for a block size of 16, that'd be frames 0 to 15). Your second call sets the memory buffer to 1 for all frames, effectively overwriting all the others. The value for that channel in the buffer at the end of the render() function would therefore be 1 and that's the value that will be written to the output.

In order to obtain a delay of 1 ms between writing a 0 and writing a 1 to a Bela digital channel from within the audio thread, you should "count samples", i.e.: write a 0 to each frame for 44 samples (997us) (possibly across multiple calls to render()), then write a 1. This example and this video should help clarify the issue.

HOWEVER

IIUC, you are doing this as a modification to OSC2OLED4Bela. As you will have noticed, that one is not a "Bela program" as such (i.e.: it contains a main() function, which doesn't call the Bela API, so it doesn't have setup(), render() and cleanup(). This is in fact just a program that uses some of the Bela libraries, but does not actually run a Bela audio thread, but is conveniently built and run through the Bela IDE. The fact that it doesn't call the Bela API and doesn't run a Bela audio thread is actually a requirement for it to be able to run at the same time. All of this means that you should not actually call any of the digitalWrite() or pinMode() functions, because those operate on Bela's digital channels and can only work as part of the Bela audio thread. What you need to do instead is to pick a digital pin not used by Bela (or by anything else) and use the Gpio class (#include <Gpio.h>) . See this page (https://learn.bela.io/using-bela/technical-explainers/other-uses-of-gpio-pins/) for more information.

Remork i'm thinking i can use a digital pin to toggle the reset before (or at the beginning of) display_Init_seq(); , or even before connecting the I2C. and i could use a digital pin to control the 12v line with a mosfet or something.

this should work, with the above adjustments.

    giuliomoro As you will have noticed, that one is not a "Bela program" as such.

    giuliomoro so it doesn't have setup(), render() and cleanup()

    that threw me off a little, yes. was looking for those 🙂

    giuliomoro The fact that it doesn't call the Bela API and doesn't run a Bela audio thread is actually a requirement for it to be able to run at the same time.

    aha. makes sense.

    giuliomoro What you need to do instead is to pick a digital pin not used by Bela (or by anything else) and use the Gpio class.

    ok.. got some reading to do!
    and there was me thinking this would be relatively straightforward 0_0

    thanks for spending your sundays helping strangers, by the way.

      Remork and there was me thinking this would be relatively straightforward 0_0

      it's not too hard, really ... this should be all you need

      #include <Gpio.h>
      
      // globals
      int gResetPin = ...; // TODO: find a free pin
      Gpio gResetGpio;
      
      ...
      
      // inside some init function:
      gResetGpio.open(gResetPin, Gpio::OUTPUT);
      
      // when needed:
      gResetGpio.write(false);
      usleep(1000);
      gResetGpio.write(true);

        i tried something very much along those lines yesterday night using P2-7 and it looks promising.
        (copied and adapted from the synchronous-gpio example.. so i'm glad i got that right, hah)

        didn't have a mosfet on hand to try the extra 12v switching, so for now i had to run the sketch manually and print a message-to-self to plug in the adapter, giving myself 5 seconds to do so 🙂
        bit stupid, but it seemed to work - now need to implement a switching system and try running at boot..

        giuliomoro it's not too hard, really

        true. just another hurdle to take. just meant to say that i did come across a lot of new info for this project, what with the OLED, combining lists to symbols and using arrays in Pd, and now this.. but all very useful for the future!
        and it demonstrates yet again how helpful this forum is.
        still, i'm quite glad i'm not pushing up to the deadline yet 🙂 all of this learning took me longer than anticipated, shall we say.

          Remork giuliomoro it's not too hard, really

          didn't want that to sound bad ... I just wanted to follow up from the previous post, which was pointing to a lengthy explanation and an API documentation, with a barebones practical example

            giuliomoro with a barebones practical example

            and a very useful one, indeed. 🙂 copy/paste FTW.
            used one pin for reset, and another to switch the 12v line (high side switching w/ a 2n3904 - BD140 combo, as i didn't have any P-channel Mosfets around.)
            set project to run on boot, and now i get my startup message to greet me after plugging in! huzzah!

            phfew. that is beautiful.

            so now i'd run this sketch in the background like before? cool.
            ALMOST THERE.

            in terms of timing - does this sort of 'background' thread start up first, compared to Audio/Pd?
            or do they run in parallel?
            not that it matters much, just wondering what the mechanics are.

            i have loaded a welcome message at the end of the SDD1309 driver sketch, to have some kind of visual on the state of the driver and the I2C connection. i'm thinking of leaving that in there, and loading all the arrays in Pd after a long button press, trying to spread some of the heavier lifting needed at startup. it also would prevent me from sending OSC messages from Pd too quickly - seeing that startup screen should also mean OSC is running in the background and is ready to receive.

              Remork phfew. that is beautiful.

              indeed!

              Remork in terms of timing - does this sort of 'background' thread start up first, compared to Audio/Pd?

              probably a bit later. I'd recommend that you don't rely on any particular startup order. Just have one ping the other until the other responds, then you'll know they are both alive.

              Remork spread some of the heavier lifting needed at startup

              For most application, "startup" heavy lifting is "free": you'll get mode switches, dropouts, whatever, doesn't matter: you haven't started yet! If anything, it's better to avoid spreading it out so that it completes as fast as possible ... if - on the other hand - you are trying to do the "startup heavy lifting" while at the same time playing some audio (e.g.: a power on tone), then the "spreading" can definitely help!

                giuliomoro probably a bit later. I'd recommend that you don't rely on any particular startup order.

                later? hmm, i expected it the other way around for some reason. good to know.

                giuliomoro Just have one ping the other until the other responds, then you'll know they are both alive.

                since - as you may have noticed - my coding skills are rather limited, i guess that would be banging

                [connect bela.local 7562(
                I
                [netsend]

                until [netsend] returns a 1? will try.

                not too worried about the startup procedure, actually, apart from getting the order and the timing right - speed doesn't really matter. this is for an art exhibition, so the startup is non-public.. the only audience interaction is the pressing of a button to get a soundfile to play, but it should be up and running when the doors open.

                  I think this should work.

                  Remork but it should be up and running when the doors open.

                  then you could even wait 10 seconds after the patch starts and most likely everything will be online.

                  Remork , i expected it the other way around for some reason. good to know.

                  The line

                  After=network-online.target

                  in the .service file is what tells it in what order the script should be started. As it needs network, I set it to be online after network-online. Bela programs - generally speaking - do not need to wait for the network, so it starts as soon as possible after the kernel modules have been loaded:

                  Requires=systemd-modules-load.service

                  which is a bit earlier than the network target. These - however - are just the times when the processes are started. Whether one or the other comes online first depends on too many things I don't know ... anyhow, most likely the Bela program will get to render() before the other one.

                  Remork [connect bela.local 7562(
                  I
                  [netsend]

                  until [netsend] returns a 1? will try.

                  no. You will need to use [netsend -u], because OscReceiver on OSC2OLED4Bela uses UDP. When you try to [connect( to a UDP port, no connection is actually established (it only sets some internal parameters0 and [netsend]'s output will send out a 1 even though the destination is not online yet.

                  I thought more of something like sending an arbitrary "test" OSC message and modifying OSC2OLED4Bela so that it responds to it. This is actually easier said than done, as I see now that there are no OSC-sending capabilities in there at the moment, so you'd need to add a OscSender object and also UdpServer should be modified to use recvfrom() in order to be able to retrieve the address from which a message was received and use that for a response. Not impossible, but maybe for now you are better off just waiting 10 seconds before expecting the OSC2OLED4Bela to be online.

                    this is clearly over-engineering, but you could have a Bela digital in connected to the pin you are toggling from OSC2OLED4Bela and monitor that pin from within the Pd patch 🙂

                      giuliomoro this is clearly over-engineering, but you could have a Bela digital in connected to the pin you are toggling from OSC2OLED4Bela and monitor that pin from within the Pd patch

                      hahaa
                      like it!

                      i guess the only real issue is indeed when to bang the [netsend -u -b] object (which is was using, actually).
                      have to make sure it doesn't get banged before the custom render is online, and then fails to connect.

                      now over-engineering is my middle name. 🙂
                      but in this case, maybe i'll just wait a couple of seconds..
                      or i could manually bang the connection with the button? once i see my startup message, that should have given Pd plenty of time - and that message only appears after the render is running.
                      will run soms tests asap.

                      thanks for all the help so far!

                      giuliomoro This is actually easier said than done, as I see now that there are no OSC-sending capabilities in there at the moment, so you'd need to add a OscSender object and also UdpServer should be modified to use recvfrom() in order to be able to retrieve the address from which a message was received and use that for a response.

                      Actually, without any changes to the libraries, you could just hardcode a port number to send messages to in OSC2OLED4Bela and have a OscSender object send to it in response to a /ping or something like that ...

                      hahaaa
                      it feels like we're actually creating problems now. 🙂

                      didn't have the time today, will probably be thursday before i manage to come back to this.
                      keep u posted.

                      Remork used one pin for reset, and another to switch the 12v line (high side switching w/ a 2n3904 - BD140 combo, as i didn't have any P-channel Mosfets around.)
                      set project to run on boot, and now i get my startup message to greet me after plugging in! huzzah!

                      huzzah'd too soon.

                      don't know how or why, but it doesn't want to play anymore.
                      both pins just stay high continuously.
                      when you say

                      giuliomoro // inside some init function:
                      gResetGpio.open(gResetPin, Gpio:😮UTPUT);

                      could you elaborate on 'some init function' here?
                      i'm thinking that's (one of the places) where it may be going wrong.

                      i have the write commands in the beginning of the main() now, where i think they belong.
                      but i'm unsure as to where to put the setup lines.

                        Remork could you elaborate on 'some init function' here?

                        Inside main(), at the top, would be fine.

                        Remork both pins just stay high continuously.

                        Both pins ... which ones?

                        What happens in the destructor of Gpio is dependent on whether the pin was already "exported" before the program started. Also, whether the destructor is executed at all depends on whether the program exits cleanly. You could try adding gResetGpio.write(0) just before returning from main() to ensure the pin goes back to 0. Feel free to post your code here if that helps.

                          i was trying to use Gpio pins 30 and 31 (P2_5 and P2_7).

                          this is what would need to happen:

                          "Power ON sequence:
                          1. Power ON VDD (= 3.3v)
                          2. After 3.3v become stable, set RES# pin LOW (logic low) for at least 3us and then HIGH (logic
                          high).
                          3. After set RES# pin LOW (logic low), wait for at least 3us. Then Power ON VCC.(= 12v)
                          4. After 12V become stable, send command AFh for display ON.
                          SEG/COM will be ON after 100ms."

                          so ideally, i'd start off with ResetPin high and 12vPin low.
                          pull reset pin Low, then High again.
                          pull 12vPin high.
                          the 'Display On' command is in the display_Init_seq()., so i need to toggle those pins before that happens.

                          i just tried only writing both of them LOW, but they both remain at 3.3V.
                          so i'm not controlling them at all, as it is.

                          i added #include <Gpio.h> in render.cpp.
                          i addedGpio gResetGpio;
                          int gResetPin = 31;
                          Gpio g12VGpio;
                          int g12VPin = 30;

                          underneath the extern "C" at line 45

                          and i have this: // hopefully set up pin P2_7 for reset of display
                          gResetGpio.open(gResetPin, Gpio::OUTPUT);
                          g12VGpio.open(g12VPin, Gpio::OUTPUT);
                          gResetGpio.write(0);
                          g12VGpio.write(0);
                          usleep(1000000);
                          gResetGpio.write(0);
                          printf("try turning on 12v now?\n");
                          usleep(10000000);
                          g12VGpio.write(0);

                          there for now, i was trying to use the repeat lines of write(0) to toggle them HIGH, obviously.

                          giuliomoro What happens in the destructor of Gpio is dependent on whether the pin was already "exported" before the program started.

                          no clue what that means, or how to make sure it gets exported.?

                            I guess it's because those pins by default are not set to GPIO but to UART. The document I linked earlier https://learn.bela.io/using-bela/technical-explainers/other-uses-of-gpio-pins/ mentions the issue of pinmuxing and goes through how to test pins from the command line before moving to C++.

                            Remork no clue what that means, or how to make sure it gets exported.?

                            I thought you had your pins staying high when the program ended, which could have been explained by the above, but actually it seems irrelevant now that I understood your problem better. "exporting" is sysfs GPIO jargon for "telling the kernel the pin should be available for user space to manipulate". "Unexporting" it would mean telling the kernel you don't need it anymore. On BelaMini all pins are exported at all times, so ... it doesn't really make a difference either. Just remember that if you want the pin to go low when the program exits gracefully, add a yourPin.write(0) just before returning from main().

                            i added
                            Gpio gResetGpio;
                            int gResetPin = 31;
                            Gpio g12VGpio;
                            int g12VPin = 30;

                            switched the order around of these, my colleague noticed that they were the other way around.
                            seems better.
                            so changed to

                            int gResetPin = 31;
                            Gpio gResetGpio;
                            int g12VPin = 30;
                            Gpio g12VGpio;

                            i tried it with the config-pin utility in Terminal, that worked fine..
                            weird thing was that using those pins as outputs is not listed as an option in config -l
                            but i could set them to output/hi/lo just fine.

                            right now setting the 1309 test project to run on boot seems to work.
                            i have no real idea why it works now, but stopped working before- i can only hope it stays this way.

                            now need to set it so it runs in the background. deja vu 🙂