Control-Surface icon indicating copy to clipboard operation
Control-Surface copied to clipboard

8 SSD1306 0.66 64X48 OLEDS

Open henkmeid opened this issue 4 years ago • 89 comments

Hello,

I ran into this project and i might be suitable for my needs. I have basically 2 questions:

  1. Is it possible to use a smaller size SPI 7pin OLED? I have 64x48.
  2. Is it possible to use 8 OLEDS by using extra digital pins for the CS lines?

Great project!

henkmeid avatar Apr 09 '20 05:04 henkmeid

Is it possible to use a smaller size SPI 7pin OLED? I have 64x48.

Yes, this shouldn't be an issue. The OLED examples all use the Adafruit_SSD1306 library, so if that library supports your display, it should work. You can simply change the resolution and the pin configuration. I've successfully used it with both I²C and SPI variants of the SSD1306.

Is it possible to use 8 OLEDS by using extra digital pins for the CS lines?

Yes, but there's a caveat: the Adafruit_SSD1306 library allocates a separate buffer for each display you add. This requires huge amounts of RAM if you have many displays.
The Control Surface library keeps all displays and display elements in order, so it only draws to one display buffer at a time, which means that you can reuse one buffer for all displays.
The standard Adafruit_SSD1306 doesn't support this, but you can use my fork, which does support it: https://github.com/adafruit/Adafruit_SSD1306/pull/149

tttapa avatar Apr 09 '20 09:04 tttapa

Cool!

Yes, but there's a caveat: the Adafruit_SSD1306 library allocates a separate buffer for each display you add. This requires huge amounts of RAM if you have many displays.

I would like to keep the code as standard as possible. Would board with larger RAM, such as an Arduino Mega, work?

henkmeid avatar Apr 09 '20 10:04 henkmeid

It depends on what else you have going on in your sketch. You need 48×64×8 bits of RAM for the display buffers, which is 3 KiB. With the improved SSD1306 library, you only need 384 bytes.

I wouldn't recommend an Arduino Mega for MIDI controllers, you can find more info here: https://tttapa.github.io/Control-Surface-doc/Doxygen/d8/d4a/md_pages_MIDI-over-USB.html

tttapa avatar Apr 09 '20 10:04 tttapa

It depends on what else you have going on in your sketch.

Basically all i want to see are tracknames, maybe the VU. Pan, Solo, Mute, etc i have already on my existing control surface.

henkmeid avatar Apr 09 '20 12:04 henkmeid

You can try compiling the code for an Arduino Mega, add 3 KiB, and make sure you have at least a couple of hundreds of bytes left for the stack.

If you just want some displays, I don't think an Arduino Mega is the best board choice, especially if you want MIDI over USB.
Personally, I'd probably use an Arduino Micro/Leonardo with the improved SSD1306 library, or a Teensy LC (has 8K of RAM and native USB support).

tttapa avatar Apr 09 '20 13:04 tttapa

I think I will go with a Teensy.

Out or curiosity, a teensy 3.2 supports 4 usb-to-midi devices. Would I be able to make this 4x with one controler. So 32 display over 4 mackie contollers?

I will take it step by step though, first I want to get this working with 1 midi device and 8 screens. But I have 40 faders on my current control surface.

henkmeid avatar Apr 09 '20 13:04 henkmeid

Or would a better approach be a seperate controller per 8 oleds and rename the controlers?

henkmeid avatar Apr 09 '20 13:04 henkmeid

Teensy 3.2 even supports 16 USB MIDI cables. You can map them as Mackie Control Extenders in your DAW, for instance (keep in mind that your DAW may limit the number of extenders).

The Control Surface library supports up to 16 different USB MIDI cables as well. You can use the third optional cable field of the MIDIAddress class.

Or would a better approach be a seperate controller per 8 oleds and rename the controlers?

I don't think that has any advantage over using a single controller, it's probably more complicated.

tttapa avatar Apr 09 '20 13:04 tttapa

Wow, you are fast in your replies!

I don't think that has any advantage over using a single controller, it's probably more complicated.

The reason i'm asking this, is the choice of controller, if i would use a single controller, i would need more digital pins of all the cs lines. Do you think a teensy 3.5 or 3.6 would be good enough to handle 40 screens?

henkmeid avatar Apr 09 '20 13:04 henkmeid

40 screens really is a lot, and it would probably require a lot of bandwidth to refresh all of them. For static things like track names, this is not an issue, but for VU meters it could be.

You can use shift registers or demultiplexers to handle the cs lines, this would probably require just a small tweak to the SSD1306 library, and it might be worth the $10 you save by using a Teensy 3.2 over a Teensy 3.6.
Even the Teensy 3.5/6 only have 40 breadboard-accessible IO in total.

tttapa avatar Apr 09 '20 14:04 tttapa

Alright, I will start with an Teensy 3.2 and 8 OLEDs and keep you up to date.

henkmeid avatar Apr 09 '20 15:04 henkmeid

Update. I have now got 2 screens hooked up according to the example. First, only the second screen worked with a shared reset pin. But when i uses 2 seperate reset pins it works. Is this normal behaviour? Or should this be working with 1 shared reset pin? Or could I be using wrong capacitor type and values? Does it need to be ceramic or elco?

Also i cannot get tracknames and time to show on the screens. VU, Pan, Rec, Mute and Solo works. I am using Cubase 9.5 pro. Any suggestions?

henkmeid avatar Apr 11 '20 09:04 henkmeid

Just tried with Reaper and tracknames now showing. Any thoughts on how to get this working with cubase?

henkmeid avatar Apr 11 '20 10:04 henkmeid

I got 4 Oleds working now. Although its with 4 seperate reset pin. When I try one reset pin and connect them all together only the last screen works, the rest remains black. Its a waste of digital IO pins as i am looking to connect 8.

henkmeid avatar Apr 11 '20 13:04 henkmeid

If you use an RC circuit for the reset pin you don't have to connect it to an IO pin.

If you do want to use an IO pin for the reset line, you can share a single IO pin for all displays, but you have to pass reset = false to the Adafruit_SSD1306::begin method for all displays except for the first one:

https://github.com/adafruit/Adafruit_SSD1306/blob/66cba544151eef5f39a6d9689a773bb2f04fbd5e/Adafruit_SSD1306.cpp#L432-L439

I've never used Cubase, so I'm afraid I can't help you with that. If Cubase fully supports the MCU protocol, it should work, but if Cubase doesn't send the track names, there's no way to display them on the Arduino. You can't really "request" them from the DAW, communication is mostly one way.

You could try the Mackie-Control-Universal-Reverse-Engineering.ino to see what Cubase is sending, or you could try a MIDI monitor on your computer.

If you figure out how Cubase sends the track names, I can tell you how to receive them with the library, but I can't test it myself, because I don't have Cubase.

The text is most likely sent as a SysEx message, you can look for 20 in the data (0x20 is the ASCII code for a space, most text fields will contain some spaces).

tttapa avatar Apr 11 '20 14:04 tttapa

That’s some very useful information! I will give it a try. I assume Cubase is fully MCU compatible. But I will use the monitor to see what type of messages it is sending.

henkmeid avatar Apr 11 '20 16:04 henkmeid

If you use an RC circuit for the reset pin you don't have to connect it to an IO pin.

Works! Bit dodgy though, probably need to change the capacitor with a different value. Sometimes one of the screens doesn’t light up, but resetting everything again does the trick.

henkmeid avatar Apr 12 '20 10:04 henkmeid

You could try the Mackie-Control-Universal-Reverse-Engineering.ino to see what Cubase is sending, or you could try a MIDI monitor on your computer.

By running this code, when I set channnel 1 to trackname "Henk", i get the following result:

SysEx: f0 00 00 66 14 12 39 20 48 65 6e 6b f7 on cable 0

henkmeid avatar Apr 12 '20 14:04 henkmeid

The format is correct. The problem lies in how the message is displayed by the MCU::LCDDisplay class: On the original Mackie Control Universal, you had one large LCD display, so you could also display messages across the different channel strips. If you split up the channels like on an OLED display, you need a way do determine if track names are displayed, or if the display is used for one long message across all channels.
The Control Surface library currently checks the following:

https://github.com/tttapa/Control-Surface/blob/ac372918071bd18f243ff77dd180a30d5a105fd2/src/Display/MCU/LCDDisplay.hpp#L78-L102

This worked for Tracktion and Reaper, but apparently, other DAWs don't always write spaces between channels.

For now, you can just remove the check here: https://github.com/tttapa/Control-Surface/blob/ac372918071bd18f243ff77dd180a30d5a105fd2/src/Display/MCU/LCDDisplay.hpp#L58-L60

Long-term, I don't know a single solution that would work on all DAWs, if you have any ideas, feel free to let me know!

tttapa avatar Apr 12 '20 17:04 tttapa

Thanks! That makes sense. I noticed a difference with Cubase and Reaper with the output on the monitor, Reaper sends out 3 extra spaces.

So I commented out the suggested piece of code and now info is showing up on my screens, but not the correct one yet. Cubase sends out 2 lines per channel, so now i'm seeing at the first screen "Pan", on the 3rd "Left". How can i get the second line to show with MCU::LCDDisplay?

Example on how Cubase outputs on Mackie Control.

[]https://dt7v1i9vyp3mf.cloudfront.net/styles/header/s3/imagelibrary/m/mackiecontrol1-RiCgQ9ziij_q4fn2FGQdMTCCjm6WkUsh.jpg

henkmeid avatar Apr 12 '20 17:04 henkmeid

There's a constructor that takes an extra line argument:

https://github.com/tttapa/Control-Surface/blob/ac372918071bd18f243ff77dd180a30d5a105fd2/src/Display/MCU/LCDDisplay.hpp#L38-L55

If you pass 0, it'll display the first line, if you pass 1 it'll display the second line.

tttapa avatar Apr 12 '20 18:04 tttapa

Succes!! Will post findings tomorrow.

IMG_7268

henkmeid avatar Apr 12 '20 19:04 henkmeid

I'm still having some issues with some of the oled not starting every time, its random which ones (can be multiple) they are. Does each OLED needs its own reset RC circuit (capacitor, resistor), or can the share one circuit with all 8 of them?

Alternatively:

If you do want to use an IO pin for the reset line, you can share a single IO pin for all displays, but you have to pass reset = false to the Adafruit_SSD1306::begin method for all displays except for the first one: https://github.com/adafruit/Adafruit_SSD1306/blob/66cba544151eef5f39a6d9689a773bb2f04fbd5e/Adafruit_SSD1306.cpp#L432-L439

How do I achieve this? Is there a place in my sketch where I can put this per display or do I have to amend something in another file?

henkmeid avatar Apr 13 '20 20:04 henkmeid

I'm still having some issues with some of the oled not starting every time, its random which ones (can be multiple) they are. Does each OLED needs its own reset RC circuit (capacitor, resistor), or can the share one circuit with all 8 of them?

Does this happen when powering/plugging in the Arduino?
You could try increasing the capacitance or lowering the resistance.
Sharing them shouldn't be an issue.

How do I achieve this? Is there a place in my sketch where I can put this per display or do I have to amend something in another file?

You could do something like this (in your sketch):

// Implement the display interface, specifically, the begin and drawBackground
// methods.
class MySSD1306_DisplayInterface : public SSD1306_DisplayInterface {
 public:
  MySSD1306_DisplayInterface(Adafruit_SSD1306 &display)
    : SSD1306_DisplayInterface(display) {}

  void begin() override {
    // Initialize the Adafruit_SSD1306 display
    if (!disp.begin(SSD1306_SWITCHCAPVCC, 0, first))
      FATAL_ERROR(F("SSD1306 allocation failed."), 0x1306);

    first = false;

    // If you override the begin method, remember to call the super class method
    SSD1306_DisplayInterface::begin();
  }

  void drawBackground() override { disp.drawLine(1, 8, 126, 8, WHITE); }

 private:
  static bool first;
};
bool MySSD1306_DisplayInterface::first = true;

tttapa avatar Apr 13 '20 21:04 tttapa

Does this happen when powering/plugging in the Arduino?

Both on power-up and reset.

You could do something like this (in your sketch):

Thanks, that increased the stability a little bit. Though sometimes I have to reset the controller a few times to light them up all at the same time. Could it be that signals are a bit noisy and interfered due to my setup (lots of wires and breadboard)? Or could I have a faulty oled among them which causes the whole lot to have this behaviour?

henkmeid avatar Apr 14 '20 06:04 henkmeid

I have designed the following PCB:

Oled_PCB

  • Hopefully this will reduce noise and interferance.
  • This design allows for external reset RC circuit by adding capacitor and resistor and not connecting reset to an IO pin.
  • This design also allows for reset by IO pin by not adding a capacitor and resistor and connection and IO pin to reset.
  • PCB can be daisychained. GND, VCC, SCL, SDA, RES and DC are shared on H1, H2 and H3. Each PCB has 4 seperate CS connection.

Any thoughts?

henkmeid avatar Apr 14 '20 07:04 henkmeid

lowering the resistance

Sorry, that should be increasing as well, of course. You want to increase the RC time.

Thanks, that increased the stability a little bit. Though sometimes I have to reset the controller a few times to light them up all at the same time. Could it be that signals are a bit noisy and interfered due to my setup (lots of wires and breadboard)? Or could I have a faulty oled among them which causes the whole lot to have this behaviour?

Is this using a GPIO pin for the reset line? Did you pass the right pins to the constructors for your displays? When using this approach, you have to leave out the capacitor and the resistor.

You could also try adding some delay at the start of your setup, to make sure the displays are out of reset when you start sending data to them.

I've never tried more than two displays, so I've never had this problem. You might have more luck asking this question on the Arduino forum, for example?

The PCB looks nice, though!

tttapa avatar Apr 14 '20 09:04 tttapa

Is this using a GPIO pin for the reset line? When using this approach, you have to leave out the capacitor and the resistor.

Yes, while using the GPIO pin, and i have removed the capacitor and resistor. I think such a lot of jumperwires on a breadboard are not the best connections, also when i touch them i see some strange jumping pixels on some of the screens. I think i'll order some PCB's online and see if that makes any difference.

Did you pass the right pins to the constructors for your displays?

  • 7: OLED Data/D1 (SPI MOSI)
  • 13: OLED Clock/D0 (SPI SCK)
  • 17: OLED Data/Command
  • 14: OLED Reset
  • 19: OLED_CS_1
  • 18: OLED_CS_2
  • 10: OLED_CS_3
  • 20: OLED_CS_4
  • 21: OLED_CS_5
  • 22: OLED_CS_6
  • 23: OLED_CS_7
  • 15: OLED_CS_8

henkmeid avatar Apr 14 '20 09:04 henkmeid

@tttapa are you from the Netherlands?

henkmeid avatar Apr 14 '20 11:04 henkmeid

I'm from Belgium.
Are you from the Netherlands by any chance?

tttapa avatar Apr 14 '20 12:04 tttapa