thanks for going through the steps and summarising them for others! You may want to do the final stretch and make a pull request on my repository including these changes (for the very first change: add the line commented-out, same as SSD1306_128_32
and SSD1306_96_16
currently are).
display simple text from PureData?
sure, would love to. but does that require a Github account? (which i don't have..)
still learning to navigate github speak, had to google 'pull request'. hah. nooby me.
yes you'd need to:
- register for a GitHub account
- go the repo URL
- click "fork"
- you now have a "fork" of the repo that you have write permission to
- change the files there (ideally on your computer via a client, but you can also edit directly on github)
- commit the changes (push if you are doing this on your computer and not through GitHub's web interface)
- go to the home page of your repo and you will see a button to click to create a pull request
if that sounds like too much, I can try and apply those changes myself and then you can download the updated repo and test it.
giuliomoro if that sounds like too much
well, honestly.. for now, yes. :/
but definitely an option for the future, seems interesting enough.
so i don't mind having a go at it once i get everything up and running with this display/ OSC/ Pd.
it's just that learning to navigate Github while navigating this project (and its dreaded C++ code) at the same time is a bit much for my poor noggin i'm afraid.
- Edited
but! took the next step, and have Pd on the laptop sending OSC messages to BelaMini.
loverly.
only thing i'm having trouble with is getting 'more than one word with spaces in between words' to display.
it looks like [oscformat] is ok with lists (at least reassembled lists through [oscparse]>[print] seem fine) , so maybe the C++ code doesn't know how to handle lists (it would seems the 'string' is more akin to [symbol]?).
all i know is if i send a list, nothing happens - if i send a symbol, it's displayed.
only way i can display spaces and line breaks for now is if i do all my text processing in ASCII where i can add them, and then run [ list tosymbol ] to turn the whole shebang into what is processed as a single symbol, spaces and all.
while that works, i'm just wondering if there's an easier way to generate strings containing spaces?
- Edited
nevermind.
[cyclone/tosymbol] to the rescue!
.. and for a Vanilla solution:
https://forum.pdpatchrepo.info/topic/13372/vanilla-mergefilename/6
used this patch, but added spaces between elements of incoming list:
[ pd list-drip ]
|
[ list fromsymbol ]
|
[ list append 32 ] << add spaces
|
[ list prepend append ]
|
[ list trim ]
etc
seems to work.. ironically, it still converts everything to ASCII. hah.
EDIT: it works, but it ignores any number you might want to display. so not yet ideal.
ok. switched displays, am now using a SDD1309-based one.
had to do a small rewrite of the SDD1306 code, but not too much difference between the two apart from the charge pump that is not available in the 1309. so it's still mostly https://github.com/giuliomoro/OSC2OLED4Bela.
i have Pd on the host sending OSC to Bela, displaying all kinds of lines and circles and text on the OLED over I2C, so all that seems to be going well.
now, the display i bought (which uses a SDD1309) has breakout pins for the usual - GND, 3v3, Clock and Data.
but it also has the reset pin broken out, which needs to be set correctly at startup or the whole thing refuses to work.
datasheet recommended startup procedure:
after 3v3 stabilizes, pull reset pin high.
after reset is pulled high, apply external 12v to power display (no charge pump included..)
right now i'm doing this manually - plug Bela in USB, touch display reset wire to ground and then connect to 3v3. then plug in 12v supply. wait for Bela to say 'connected to SDD1309 over I2C', and for initial message to appear on display.
obviously, this should be automated.
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.
only thing is, i can't seem get the code right. because i don't quite know what i'm doing.
i tried copying stuff out of the digital output example.
i tried adding
int gResetPin = 5;
bool setup(BelaContext **context, void *userData)
{
pinMode(context, 0, gResetPin, OUTPUT);
return true;
}
at the top of render.cpp
and
digitalWrite(context, 0, gResetPin, 0);
usleep(1000);
digitalWrite(context, 0, gResetPin, 1);
in the render.cpp before the initialization of the display.
i get a 'use of undeclared identifier "context" ' error.
i was hoping bela would know what the context was
i tried adding everything in a separate BelaPinSetup.c file, but that didn't work either.
any help would be wildly appreciated.
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.
- Edited
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.
- Edited
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.