tttapa / Control-Surface

Arduino library for creating MIDI controllers and other MIDI devices.
GNU General Public License v3.0
1.28k stars 141 forks source link

ssd1306 and teensy 4.0 #286

Open benwadub opened 4 years ago

benwadub commented 4 years ago

hi, again me, I d like to add the ssd 1306 display I just received to my controller but can t find how to adapt the exemple code to my teensy 4.0, no problem to wire and and vcc but how to wire the scl sda res and dc pins of the screen and how to write it in the code, I can t find in your exemple code the screen pins, I just see dc pin reset and cs that I don t have on my screen

/**
 /**
 * An example demonstrating the use of DisplayElement%s to display information
 * from the DAW on a small OLED display.
 *
 * @boards  Teensy 3.x
 * 
 * Connections
 * -----------
 * 
 * - 5:  Push button (to ground)
 * - 6:  Push button (to ground)
 * - 7:  OLED Data/D1 (SPI MOSI)
 * - 13: OLED Clock/D0 (SPI SCK)
 * - 17: OLED Data/Command
 * - 10: OLED Cable Select
 * 
 * Add a capacitor between the reset pin of the display and ground, and a 
 * resistor from reset to 3.3V. The values are not critical, 0.1µF and 10kΩ 
 * work fine.  
 * You do need some way to reset the display, without it, it won't work.  
 * Alternatively, you could use an IO pin from the Teensy to reset the 
 * display, but this just "wastes" a pin.
 * 
 * Behavior
 * --------
 * 
 * - The time (bars, beats, fraction), play and record status are shown at the 
 *   top of the display.
 * - For each of the 8 first tracks, a VU level meter with peak indicator and
 *   a V-Pot ring showing the pan are displayed, as well as the the mute, solo 
 *   and record arm status.
 * - Two tracks are displayed at once. By pressing the push buttons connected
 *   to pins 5 and 6, you can cycle through four banks to display all 8 tracks.
 * 
 * Mapping
 * -------
 * 
 * Map "Control Surface" as a Mackie Control Universal unit in your DAW.
 * 
 * @note    There seem to be some differences in the way some applications 
 *          handle VU meters: some expect the hardware to decay automatically,
 *          some don't.  
 *          If you notice that the meters behave strangely, try both 
 *          MCU::VUDecay::Hold and MCU::VUDecay::Default, or try a different 
 *          decay time.
 * 
 * Written by PieterP, 2019-11-12  
 * https://github.com/tttapa/Control-Surface
 */

#include <Encoder.h> // Include the Encoder library.
// This must be done before the Control Surface library.
#include <Control_Surface.h> // Include the Control Surface library
// Include the display interface you'd like to use
#include <Display/DisplayInterfaces/DisplayInterfaceSSD1306.hpp>

// ----------------------------- MIDI Interface ----------------------------- //
// ========================================================================== //

/*
   Instantiate a MIDI interface to use for the Control Surface.
*/

USBMIDI_Interface midi;
// USBDebugMIDI_Interface midi(115200);

// ----------------------------- Display setup ------------------------------ //
// ========================================================================== //

/*
   Instantiate and initialize the SSD1306 OLED display
*/

constexpr uint8_t SCREEN_WIDTH = 128;
constexpr uint8_t SCREEN_HEIGHT = 64;

constexpr int8_t OLED_DC = 17;    // Data/Command pin of the display
constexpr int8_t OLED_reset = -1; // Use the external RC circuit for reset
constexpr int8_t OLED_CS = 10;    // Chip Select pin of the display

constexpr uint32_t SPI_Frequency = SPI_MAX_SPEED;

// Instantiate the displays
Adafruit_SSD1306 ssd1306Display = {
  SCREEN_WIDTH, SCREEN_HEIGHT, &SPI,          OLED_DC,
  OLED_reset,   OLED_CS,       SPI_Frequency,
};

// --------------------------- Display interface ---------------------------- //
// ========================================================================== //

// 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())
      FATAL_ERROR(F("SSD1306 allocation failed."), 0x1306);

    // 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); }

} display = ssd1306Display;

// ------------------------------- Bank setup ------------------------------- //
// ========================================================================== //

/*
   Create a bank and a bank selector to change its setting.
*/

Bank<4> bank(2); // Create a new bank with two tracks per bank

// Create a new bank selector to control the bank using two push buttons
IncrementDecrementSelector<4> bankselector = {bank, {5, 6}, Wrap::Wrap};

// -------------------------- MIDI Input Elements --------------------------- //
// ========================================================================== //

/*
   Define all elements that listen for MIDI messages.
*/

// Time display keeps track of the bar counter
MCU::TimeDisplay timedisplay = {};

// Play / Record
NoteValue play = {MCU::PLAY};
NoteValue record = {MCU::RECORD};

// Mute
Bankable::NoteValue<4> mute[] = {
  {bank, MCU::MUTE_1},
  {bank, MCU::MUTE_2},
};

// Solo
Bankable::NoteValue<4> solo[] = {
  {bank, MCU::SOLO_1},
  {bank, MCU::SOLO_2},
};

NoteValue rudeSolo = {MCU::RUDE_SOLO};

// Record arm / ready
Bankable::NoteValue<4> recrdy[] = {
  {bank, MCU::REC_RDY_1},
  {bank, MCU::REC_RDY_2},
};

// VU meters
MCU::Bankable::VU<4> vu[] = {
  {bank, 1, MCU::VUDecay::Hold},
  {bank, 2, MCU::VUDecay::Hold},
};

// VPot rings
MCU::Bankable::VPotRing<4> vpot[] = {
  {bank, 1},
  {bank, 2},
};

// ---------------------------- Display Elements ---------------------------- //
// ========================================================================== //

/*
   Define all display elements that display the state of the input elements.
*/

// Time display
MCU::TimeDisplayDisplay timedisplaydisplay = {
  // position (0, 0), font size (1)
  display, timedisplay, {0, 0}, 1, WHITE,
};

// Play / Record
NoteBitmapDisplay playDisp = {
  display, play, XBM::play_7, {16 + 64, 0}, WHITE,
};
NoteBitmapDisplay recordDisp = {
  display, record, XBM::record_7, {26 + 64, 0}, WHITE,
};

// Mute
NoteBitmapDisplay muteDisp[] = {
  {display, mute[0], XBM::mute_10B, {14, 50}, WHITE},
  {display, mute[1], XBM::mute_10B, {14 + 64, 50}, WHITE},
};

// Solo
NoteBitmapDisplay soloDisp[] = {
  {display, solo[0], XBM::solo_10B, {14, 50}, WHITE},
  {display, solo[1], XBM::solo_10B, {14 + 64, 50}, WHITE},
};

NoteBitmapDisplay rudeSoloDisp = {
  display, rudeSolo, XBM::solo_7, {36 + 64, 0}, WHITE};

// Record arm / ready
NoteBitmapDisplay recrdyDisp[] = {
  {display, recrdy[0], XBM::rec_rdy_10B, {14 + 14, 50}, WHITE},
  {display, recrdy[1], XBM::rec_rdy_10B, {14 + 14 + 64, 50}, WHITE},
};

// VU meters
MCU::VUDisplay vuDisp[] = {
  // position (32+11, 60), width (16), bar height (3) px, bar spacing (1) px
  {display, vu[0], {32 + 11, 60}, 16, 3, 1, WHITE},
  {display, vu[1], {32 + 11 + 64, 60}, 16, 3, 1, WHITE},
};

// VPot rings
MCU::VPotDisplay vpotDisp[] = {
  // position (0, 10), outer radius (16) px, inner radius (13) px
  {display, vpot[0], {0, 10}, 16, 13, WHITE},
  {display, vpot[1], {64, 10}, 16, 13, WHITE},
};

// Bank seting
BankDisplay bankDisp[] = {
  // first track of the bank (1), position (0, 50), font size (2)
  {display, bank, 1, {0, 50}, 2, WHITE},
  {display, bank, 2, {64, 50}, 2, WHITE},
};

// --------------------------------- Setup ---------------------------------- //
// ========================================================================== //

void setup() {
  // The default SPI MOSI pin (11) is used for I²S, so we need to use the
  // alternative MOSI pin (7)
  SPI.setMOSI(7);
  // Correct relative mode for MCU rotary encoders
  RelativeCCSender::setMode(MACKIE_CONTROL_RELATIVE);
  Control_Surface.begin(); // Initialize Control Surface
}

// ---------------------------------- Loop ---------------------------------- //
// ========================================================================== //

void loop() {
  Control_Surface.loop(); // Refresh all elements
}
benwadub commented 4 years ago

oh I found the problem! I have a2 button to manage my banks and added the same button to the mcu bank change but In wraps/wraps mode :-( so my banks displayed was never the same as the bank that was modified by the mute in Ableton! now all is working fine! and you know that push button can manage control surface bank at the same time as mcu bank but the have to be in wrap/clamp mode! thanks again!

benwadub commented 4 years ago

hi Pieter, hope you are fine! I made working my screen nicely now I try to understand how to adapt it to my needs, I set Ableton to receive and send cc94 for mute, how can I write the code for my display get state of cc number 94 instead of


NoteBitmapDisplay muteDisp[] = {
  {display, mute[0], XBM::mute_10B, {10, 13}, WHITE},```
like in your exemple
I thought writing it like this could work?
 {display, {94, CHANNEL_1}, XBM::mute_10B, {10, 13}, WHITE},
tttapa commented 4 years ago

Please see my previous reply:

The NoteBitmapDisplay doesn't do any MIDI related things, so 94, CHANNEL_1 is meaningless in that context.

You need two things: 1. a NoteValue object that listens for MIDI messages on the given address, and 2. a NoteBitmapDisplay to display the values that are stored by the NoteValue object.

// Mute
Bankable::NoteValue<2> mute[] = {
  {bank, {94, CHANNEL_1}},
  {bank, {94, CHANNEL_2}},
  // ...
};
NoteBitmapDisplay muteDisp[] = {
  {display, mute[0], XBM::mute_10B, {10, 13}, WHITE},
  {display, mute[1], XBM::mute_10B, {10, 13}, WHITE},
  // ...
};

If you want to use MIDI control change instead of notes, use CCValue instead of NoteValue.

benwadub commented 4 years ago

sorry I didn't t understood all, English is not my native language so sometimes I don t understand all very well! so I send my mute like this:


Bankable::CCButtonLatched<2> mute[] = { 
  {{bank,BankType::CHANGE_ADDRESS}, mux5.pin(0), {94, CHANNEL_1}},//numéro de bank correspondant/indique que le changement de bank change le canal midi (+8 car on a 8 adresses par bank)/pin sur laquelle le controleur est branché/numéro de cc/numéro de canal midi
  {{bank,BankType::CHANGE_ADDRESS},mux5.pin(1), {94, CHANNEL_2}},
  {{bank,BankType::CHANGE_ADDRESS},mux5.pin(2), {94, CHANNEL_3}},
  {{bank,BankType::CHANGE_ADDRESS},mux5.pin(3), {94, CHANNEL_4}},
  {{bank,BankType::CHANGE_ADDRESS},mux5.pin(4), {94, CHANNEL_5}},
  {{bank,BankType::CHANGE_ADDRESS},mux5.pin(5), {94, CHANNEL_6}},
  {{bank,BankType::CHANGE_ADDRESS},mux5.pin(6), {94, CHANNEL_7}},
  {{bank,BankType::CHANGE_ADDRESS},mux5.pin(7), {94, CHANNEL_8}},
};```
how does the bitmap display should be written please.
tttapa commented 4 years ago

First you need a CCValue object to receive the MIDI data from Ableton:

Bankable::CCValue<2> muteAbltn[] = {
  {{bank, BankType::CHANGE_ADDRESS}, {94, CHANNEL_1}},
  {{bank, BankType::CHANGE_ADDRESS}, {94, CHANNEL_2}},
  // ...
};

Then you need a NoteBitmapDisplay object to display the value you received:

NoteBitmapDisplay muteDisp[] = {
  {display, muteAbltn[0], XBM::mute_10B, {10, 13}, WHITE},
  {display, muteAbltn[1], XBM::mute_10B, {26, 13}, WHITE},
  // ...
};
benwadub commented 4 years ago

ohhhh yeahhhh thanks!! I finally understand! I was misunderstanding the cc value receiver and the cc I sent from my controller! all is clear now and that compile! now I ll be able to get away from the MCU and be able to use the Ableton script I made without using the MCU script except to receive theme display

benwadub commented 4 years ago

a last question for tonight :-) why does this don t work for play and rec button with ccvalue?


CCValue play = {103, CHANNEL_1};
CCValue record = {104, CHANNEL_1};```
tttapa commented 4 years ago

The MIDI address is a single argument, so you have to group it using braces:

CCValue play = {{103, CHANNEL_1}};
benwadub commented 4 years ago

thanks!! a lot! for the time display in MCU do you know how that work? MCU receive midi time code and show it on display? do you know if that s possible to receive it without using MCU?

tttapa commented 4 years ago

No, it doesn't use MIDI Time Code, the time is sent as text using Control Change messages. The implementation is as follows:

https://github.com/tttapa/Control-Surface/blob/7f0ce0f6f1122fd2b2fe140064268db349be8468/src/MIDI_Inputs/MIDIInputElementMatchers.hpp#L79-L103

https://github.com/tttapa/Control-Surface/blob/7f0ce0f6f1122fd2b2fe140064268db349be8468/src/MIDI_Inputs/MCU/SevenSegmentDisplay.hpp#L43-L55

Also see page 116 of https://images.thomann.de/pics/prod/151261_manual.pdf.

benwadub commented 4 years ago

wow thanks I ll look at it tomorrow at first read it s like Chinese for me!! don t know how I ll take theses informations from the daw!

benwadub commented 4 years ago

hi pieter do you know why i need every time I start my midi controller everything works but not my displays, I always need to reboot the teensy to get the display working, at first boot the screen stay black but as soon as I reboot all is running fine?

// ----------------------------- Display setup ------------------------------ //
// ========================================================================== //

/*
   Instantiate and initialize the SSD1306 OLED display
*/

constexpr uint8_t SCREEN_WIDTH = 128;
constexpr uint8_t SCREEN_HEIGHT = 64;

constexpr int8_t OLED_DC = 22;    // Data/Command pin of the display
constexpr int8_t OLED_reset = -1; // Use the external RC circuit for reset
constexpr int8_t OLED_CS = 10;   //wired on 13

constexpr uint32_t SPI_Frequency = SPI_MAX_SPEED;

// Instantiate the displays
Adafruit_SSD1306 ssd1306Display = {
  SCREEN_WIDTH, SCREEN_HEIGHT, &SPI,          OLED_DC,
  OLED_reset,   OLED_CS,       SPI_Frequency,
};

// --------------------------- Display interface ---------------------------- //
// ========================================================================== //

// 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 {
    delay(200);
    // Initialize the Adafruit_SSD1306 display
    if (!disp.begin())

      FATAL_ERROR(F("SSD1306 allocation failed."), 0x1306);

    // 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); }

} display = ssd1306Display;
CD74HC4067 mux1 = {
  14,       // numéro de broche de l'arduino
  {9, 3, 4, 5} // numéro de pins de l'arduino sur lesquels sont branchés tous les multiplexeurs apellés mux S0, S1, S2
};
benwadub commented 4 years ago

@tttapa i think you didn’t see this question, I didn’t wanted to open a new issue for this problem