• Hardware
  • is anyone successfully using i2c OLED / LCD?

okay nevermind, just found another thread where i'm asking the same questions.
still need to check those solutions.
focus much?

hmm. so i hooked the OLED up to pepper.
it's faster than the other board i tried, but still too slow to succesfully keep up with the 100ms bangs Pd is sending out. it seems to be updating at something between half and 1/3 that speed. i.e. if i run LFO'sfor four seconds, then turn it off in Pd, it takes another four or five seconds to finish the animation.

it could be workable, but it's not really ideal.

first, check the i2c bus speed by running:

dmesg | grep omap_i2c.*bus

Remork i've a feeling the old OSC2OLED4BELA was faster, but maybe that's just me thinking everything was better when i was younger.

I don't think that would have been the case. It is really down to how much data you send, how fast the I2c bus is and how busy the CPU is doing other things. It seems like it would be good to ignore all past OSC messages and only take into consideration the most recent one available in the queue, but this is a workaround. Let's first find out what is limiting the refresh rate for you now.

[ 0.702571] omap_i2c 44e0b000.i2c: bus 0 rev0.11 at 400 kHz
[ 0.704215] omap_i2c 4802a000.i2c: bus 1 rev0.11 at 400 kHz
[ 0.707380] omap_i2c 4819c000.i2c: bus 2 rev0.11 at 400 kHz

OK that looks good. Let's see about this performance issue, then: do you get this "delay" even when running O2O alone, without any Bela program?

not sure if that's what you mean, but this is with just O2O running on Bela/Pepper, and the pd example patch running on my laptop.
i don't have O2O running in the background, with another project running on Pepper, if that's the question.

yes that's the question. I guess this is unexpected, then. Can you disable all metros from the Pd patch and just use the following (with the sliders set to a range of 0:1 ) and see if you still notice the latency when adjusting one of the sliders manually:

alt text

will try.
but i found this on an arduino forum:

'The SSD1327 indeed is a graylevel controller (4 bit per pixel). There is no monochrome mode and no graphics acceleration: So, u8g2 has to transfer 4 bit for one pixel.
Also remember, that u8g2 is a monochrome library. U8g2 can not use all the different shades of gray: 0 is mapped to 0000 and 1 to 1111.

Maybe the full page mode constructor will be a little bit faster (if you have something bigger than an Uno), but in general, there is nothing much what you could do.'

so sounds like SSD1327 is just a bad match for the U8g2 lib..
will look into other screens then - probably easier than reinventing the wheel on the lib front.

Let's try to live with the low refresh rate but avoid messages piling up which cause the noticeable delay after a while. I will send you a patch later today hopefully.

thinking of migrating this Oled to the Principium keyboard - should be plenty fast to just display a patch name and a large number. don't really need to see live control changes..

still interested in the workings of such a patch, though, but it just seems to be a bit of an ill fit - seeing how the unused functionality of something supposedly more powerful actually causes a slowdown. 🙂

tried it with a 128x64 SSD1306 Oled i had lying around.
can confirm this one does work beautifully.

conclusion: SSD1327, not an ideal combo w/ U8g2.

    well, after some fiddling with the setup function
    (my display needs {U8G2LinuxI2C(U8G2_R0, gI2cBus, 0x3c, u8g2_Setup_ssd1327_i2c_midas_128x128_f), -1} to work, apparently)

    • it still works, as in: it's responsive to osc messages.
    • i guess it avoids the pileup, as in: response time when switching between functions is much better.

    however, the LFO's and array waveform only get displayed partially, like top quarter of the screen. rest of the screen remains black. when you stop the LFO's or waveform, the whole display is filled with what it should display. same goes for parameters: a bang there causes only parameter 1 to be displayed, with varying length of its block underneath, and at what i assume is the end of the last [line], all three get displayed. same behaviour as the lfo's, really.

    feels like it doesn't get the time to fully write the contents of the buffer to the screen before refreshing.
    i totally appreciate the effort, but i'm not sure if it's worth it. not for my use case, anyway..

    Try removing the if(!sent) line from the bottom

    hmm. same.
    alt text

    this is the LFO example.

    And increase the usleep in the next line to 100000 ?

    yes! definitely the best one yet.
    don't know what you did exactly, but it seems to work. 🙂

    i mean, you can still see it struggling.. parameter blocks are jumpy and especially on the sine waveform you can see the screen rolling. However! timing is as accurate as it will be and the buffer buildup is gone. yay!

    i wouldn't use it for waveform displays as they are nowhere near smooth enough, but for most other functions this is beautiful. hats off to you sir.
    mind explaining your process? am i correct in thinking this is at 1/4 refresh rate now?

      Remork mind explaining your process? am i correct in thinking this is at 1/4 refresh rate now?

      see the diff here: https://github.com/giuliomoro/O2O/compare/only-latest

      Sending data to the screen has been moved out of the receiving thread (parseMessage()) and moved to the main thread (main()). parseMessage() parses all incoming messages and updates the in-memory buffer with the bitmap representation that has to be sent out to the display. When this buffer has been modified, it notifies the main thread (by setting the global gShouldSend per each display whose buffer has been updated), which proceeds to send the buffer to the display(s), ultimately causing the output to be visualised. Access to the in-memory buffers and the gShouldSend flags are protected by the mutex mtx, so that at any given time only one of parseMessage() or main() can read from or write to the buffer. The rate limiting is done by the fact that it doesn't matter how many times parseMessage() has updated the buffer while main() was sleeping: only the latest version of it will be written to the output. As parseMessage() will in principle run much faster than main()'s sleep, main() will be dropping frames, thus avoiding the backlog.

      The max frame rate is sort of limited by the time it takes to send data to the screen plus any sleep in main(). There is usleep(50000) in main(), but actually this sleep is only observed if no data has just been sent out, so that this should only affect the jitter of isolated messages (delaying their visualisation by up to 50ms), so if you are sending a steady stream of messages, there will be no sleep and it will immediately try to lock the mtx again.

      Now that I think of it again, we are sort of over-relying on the Linux scheduler to ensure we have enough CPU time to process incoming messages and you could find yourselves having both threads either busy or waiting on a lock and so little progress may be made on the parseMessage() side, still potentially leading to a backlog. This is because the "critical section" (i.e.: the section where the mutex is held) in main() is rather long (i.e.: it takes a lot of time because it's sending a lot of bits over I2C, whereas best practice suggests to keep critical sections as short as possible and without I/O. It is possible that a better approach would be to make a deep copy of the U8G2 object that we want to send out to, but this requires changing things deep inside it.
      I understand you are not seeing such issue right now, but I am thinking it could happen if the CPU gets busy with something else (e.g.: running Bela audio). One option here would be to change the priority of parseMessage() thread so that it is higher than main() (they are both at priority 0 right now. Alternatively, more simply, you could just force a sleep even in case something has just been sent.
      Back of the envelope (optimistic) frame rate calculation:

      payload (bits): 128*128*4 = 65536
      transfer rate: 400000 bits/s
      min transfer time (optimistic, assumes bus fully occupied and no transaction overheads): 65536/400000 = 0.16s
      max frame rate = 1/0.16s = 6.25Hz

      So even adding 1ms of sleep after just sending some data will not significantly slow down the frame rate, but may give parseMessage() more CPU time to process incoming messages. Something like this would be nice:

      		if(sent)
      			usleep(1000); //short sleep
      		else
      			usleep(50000); //long sleep

      Further note:

      I scoped the I2C bus on Bela and it looks like instead of running at the nominal 400kHz it is doing 333kHz. This means we are actually below that, but I see no straightforward way of increasing that (the datasheet lists 100kHz or 400kHz as the only values) and it's probably not worth the effort here as the display's sampling rate wouldn't actually improve much here.
      Table 13-6 of the SSD1327 datasheet shows that min "Clock Cycle Time" is 2.5us, i.e.: 400kHz seems to be the highest clock you could use here and we are already close to that.

      alt text