Hello,
I am trying to use the CONTROL_SURFACE library to make a MIDI control surface for use with the Reaper DAW along with the CSI (Control Surface Integrator) plugin.
I am using an ESP32S3 board with two SH1106G displays connected on SPI bus. I would like to get to having a surface with 8 channels (8 faders, 8 displays, etc.) but for now I am doing my tests with only two displays.
I started from the MCU-OLED SSD1306-x2 example sketch and tried to adapt it to my needs.
I can see all the information correctly on the two displays but the strange thing is that the track names after number 8 discover and are not displayed. The other values on the other hand (VU, mute, solo, pan, etc.) are updated correctly according to the track selected on the DAW.
I try to explain myself better: if on the DAW I select (with the mouse) track 1, I correctly display the values of tracks 1 to 8; if I move to track 9 the names of tracks 9 to 16 disappear, while the rest are displayed correctly.
I would like to understand if the problem is in the sketch code, in the integration of the controller in the DAW, or in the fact that I have not understood something ![]()
I am not a programmer and I am trying to learn from the example sketches and documentation - could you point me in the right direction?
Thank you very much!
Here is my sketch:
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Control_Surface.h> // Include the Control Surface library
#include <Display/DisplayInterfaces/DisplayInterfaceSH1106.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 SH11O6G OLED display
*/
constexpr int8_t OLED_MOSI = 11; // Data/Command pin of the display
constexpr int8_t OLED_MISO = 12; // Data/Command pin of the display
constexpr int8_t OLED_CLK = 13; // Data/Command pin of the display
constexpr int8_t OLED_CS_L = 3; // Chip Select pin of the L display
constexpr int8_t OLED_CS_M = 9; // Chip Select pin of the L display
//constexpr int8_t OLED_CS_R = 10; // Chip Select pin of the R display
//constexpr int8_t OLED_CS_RR = 4; // Chip Select pin of the R display
constexpr int8_t OLED_DC = 14; // Data/Command pin of the display
constexpr int8_t OLED_RST_L = -1; // Use the external RC circuit for reset
constexpr int8_t OLED_RST_M = -1; // Use the external RC circuit for rese
//constexpr int8_t OLED_RST_R = -1; // Use the external RC circuit for reset
//constexpr int8_t OLED_RST_RR = -1; // Use the external RC circuit for reset
// Instantiate the L displays
Adafruit_SH1106G sh1106Display_L {128, 64, OLED_MOSI, OLED_CLK, OLED_DC, OLED_RST_L, OLED_CS_L};
// Instantiate the M displays
Adafruit_SH1106G sh1106Display_M {128, 64, OLED_MOSI, OLED_CLK, OLED_DC, OLED_RST_M, OLED_CS_M};
// Instantiate the R displays
//Adafruit_SH1106G sh1106Display_R {128, 64, OLED_MOSI, OLED_CLK, OLED_DC, OLED_RST_R, OLED_CS_R};
// Instantiate the RR displays
//Adafruit_SH1106G sh1106Display_RR {128, 64, OLED_MOSI, OLED_CLK, OLED_DC, OLED_RST_RR, OLED_CS_RR};
// --------------------------- Display interface ---------------------------- //
// ========================================================================== //
// Implement the display interface, specifically, the begin and drawBackground
// methods.
class MySH1106_DisplayInterface : public SH1106_DisplayInterface {
public:
MySH1106_DisplayInterface(Adafruit_SH1106G &display)
: SH1106_DisplayInterface(display) {}
void begin() override {
// Initialize the Adafruit_SSD1306 display
if (!disp.begin())
FATAL_ERROR(F("SH1106G allocation failed."), 0x1106);
// If you override the begin method, remember to call the super class method
SH1106_DisplayInterface::begin();
}
void drawBackground() override { disp.drawLine(60, 0, 60, 6, SH110X_WHITE); }
} display_L = sh1106Display_L, display_M = sh1106Display_M;// display_R = sh1106Display_R, display_RR = sh1106Display_RR;
// ------------------------------- Bank setup ------------------------------- //
// ========================================================================== //
/*
Create a bank and a bank selector to change its setting.
*/
Bank<2> bank(4); // Create a new bank with 6 tracks per bank
// Create a new bank selector to control the bank using a push button
//IncrementSelector<2> bankselector {bank, 4};
// Create a new bank selector to control the bank using two push buttons
IncrementDecrementSelector<2> bankselector {bank, {5, 6}, Wrap::Wrap};
// -------------------------- MIDI Input Elements --------------------------- //
// ========================================================================== //
/*
* Define all elements that listen for MIDI messages.
*/
// Main MCU LCD screen, used to get track names
MCU::LCD<> lcd {};
// Time display_L keeps track of the bar counter
//MCU::TimeDisplay timedisplay {};
// Play / Record
//NoteValue play {MCU::PLAY};
//NoteValue record {MCU::RECORD};
// Mute
Bankable::NoteValue<2> mute[] {
{bank, MCU::MUTE_1},
{bank, MCU::MUTE_2},
{bank, MCU::MUTE_3},
{bank, MCU::MUTE_4},
/* {bank, MCU::MUTE_5},
{bank, MCU::MUTE_6},
{bank, MCU::MUTE_7},
{bank, MCU::MUTE_8},
*/
};
// Solo
Bankable::NoteValue<2> solo[] {
{bank, MCU::SOLO_1},
{bank, MCU::SOLO_2},
{bank, MCU::SOLO_3},
{bank, MCU::SOLO_4},
/* {bank, MCU::SOLO_5},
{bank, MCU::SOLO_6},
{bank, MCU::SOLO_7},
{bank, MCU::SOLO_8},
*/
};
NoteValue rudeSolo {MCU::RUDE_SOLO};
// Record arm / ready
Bankable::NoteValue<2> recrdy[] {
{bank, MCU::REC_RDY_1},
{bank, MCU::REC_RDY_2},
{bank, MCU::REC_RDY_3},
{bank, MCU::REC_RDY_4},
/* {bank, MCU::REC_RDY_5},
{bank, MCU::REC_RDY_6},
{bank, MCU::REC_RDY_7},
{bank, MCU::REC_RDY_8},
*/
};
// VU meters
MCU::Bankable::VU<2> vu[] {
{bank, 1, MCU::VUDecay::Default},
{bank, 2, MCU::VUDecay::Default},
{bank, 3, MCU::VUDecay::Default},
{bank, 4, MCU::VUDecay::Default},
/* {bank, 5, MCU::VUDecay::Default},
{bank, 6, MCU::VUDecay::Default},
{bank, 7, MCU::VUDecay::Default},
{bank, 8, MCU::VUDecay::Default},
*/
};
// VPot rings
MCU::Bankable::VPotRing<2> vpot[] {
{bank, 1},
{bank, 2},
{bank, 3},
{bank, 4},
/* {bank, 5},
{bank, 6},
{bank, 7},
{bank, 8},
*/
};
// ---------------------------- Display Elements ---------------------------- //
// ========================================================================== //
/*
* Define all display_L elements that display_L the state of the input elements.
*/
// Track names
MCU::LCDDisplay lcddisps[] {
// track (1), position (0, 40), font size (1)
{display_L, lcd, bank, 1, 1, {0, 0}, 1, SH110X_WHITE},
{display_L, lcd, bank, 2, 1, {66, 0}, 1, SH110X_WHITE},
{display_M, lcd, bank, 3, 1, {0, 0}, 1, SH110X_WHITE},
{display_M, lcd, bank, 4, 1, {66, 0}, 1, SH110X_WHITE},
// {display_R, lcd, bank, 5, 1, {0, 0}, 1, SH110X_WHITE},
// {display_R, lcd, bank, 6, 1, {66, 0}, 1, SH110X_WHITE},
// {display_RR, lcd, bank, 7, 1, {0, 0}, 1, SH110X_WHITE},
// {display_RR, lcd, bank, 8, 1, {66, 0}, 1, SH110X_WHITE},
{display_L, lcd, bank, 1, 2, {0, 7}, 1, SH110X_WHITE},
{display_L, lcd, bank, 2, 2, {66, 7}, 1, SH110X_WHITE},
{display_M, lcd, bank, 3, 2, {0, 7}, 1, SH110X_WHITE},
{display_M, lcd, bank, 4, 2, {66, 7}, 1, SH110X_WHITE},
// {display_R, lcd, bank, 5, 2, {0, 7}, 1, SH110X_WHITE},
// {display_R, lcd, bank, 6, 2, {66, 7}, 1, SH110X_WHITE},
// {display_RR, lcd, bank, 7, 2, {0, 7}, 1, SH110X_WHITE},
// {display_RR, lcd, bank, 8, 2, {66, 7}, 1, SH110X_WHITE},
};
/*
// Time display
MCU::TimeDisplayDisplay timedisplaydisplay {
// position (0, 0), font size (1)
display_L, timedisplay, {0, 0}, 1, SH110X_WHITE,
};
// Play / Record
BitmapDisplay<> playDisp {
display_L, play, XBM::play_7, {16 + 64, 0}, SH110X_WHITE,
};
BitmapDisplay<> recordDisp {
display_L, record, XBM::record_7, {26 + 64, 0}, SH110X_WHITE,
};
*/
// Mute
BitmapDisplay<> muteDisp[] {
{display_L, mute[0], XBM::mute_10B, {14, 50}, SH110X_WHITE},
{display_L, mute[1], XBM::mute_10B, {14 + 64, 50}, SH110X_WHITE},
{display_M, mute[2], XBM::mute_10B, {14, 50}, SH110X_WHITE},
{display_M, mute[3], XBM::mute_10B, {14 + 64, 50}, SH110X_WHITE},
// {display_R, mute[4], XBM::mute_10B, {14, 50}, SH110X_WHITE},
// {display_R, mute[5], XBM::mute_10B, {14 + 64, 50}, SH110X_WHITE},
// {display_RR, mute[6], XBM::mute_10B, {14, 50}, SH110X_WHITE},
// {display_RR, mute[7], XBM::mute_10B, {14 + 64, 50}, SH110X_WHITE},
};
// Solo
BitmapDisplay<> soloDisp[] {
{display_L, solo[0], XBM::solo_10B, {14, 50}, SH110X_WHITE},
{display_L, solo[1], XBM::solo_10B, {14 + 64, 50}, SH110X_WHITE},
{display_M, solo[2], XBM::solo_10B, {14, 50}, SH110X_WHITE},
{display_M, solo[3], XBM::solo_10B, {14 + 64, 50}, SH110X_WHITE},
// {display_R, solo[4], XBM::solo_10B, {14, 50}, SH110X_WHITE},
// {display_R, solo[5], XBM::solo_10B, {14 + 64, 50}, SH110X_WHITE},
// {display_RR, solo[6], XBM::solo_10B, {14, 50}, SH110X_WHITE},
// {display_RR, solo[7], XBM::solo_10B, {14 + 64, 50}, SH110X_WHITE},
};
BitmapDisplay<> rudeSoloDisp {
display_L, rudeSolo, XBM::solo_7, {56 + 64, 0}, SH110X_WHITE};
// Record arm / ready
BitmapDisplay<> recrdyDisp[] {
{display_L, recrdy[0], XBM::rec_rdy_10B, {14 + 14, 50}, SH110X_WHITE},
{display_L, recrdy[1], XBM::rec_rdy_10B, {14 + 14 + 64, 50}, SH110X_WHITE},
{display_M, recrdy[2], XBM::rec_rdy_10B, {14 + 14, 50}, SH110X_WHITE},
{display_M, recrdy[3], XBM::rec_rdy_10B, {14 + 14 + 64, 50}, SH110X_WHITE},
// {display_R, recrdy[4], XBM::rec_rdy_10B, {14 + 14, 50}, SH110X_WHITE},
// {display_R, recrdy[5], XBM::rec_rdy_10B, {14 + 14 + 64, 50}, SH110X_WHITE},
// {display_RR, recrdy[6], XBM::rec_rdy_10B, {14 + 14, 50}, SH110X_WHITE},
// {display_RR, recrdy[7], XBM::rec_rdy_10B, {14 + 14 + 64, 50}, SH110X_WHITE},
};
// VU meters
MCU::VUDisplay<> vuDisp[] {
// position (32+11, 60), width (16), bar height (3) px, bar spacing (1) px
{display_L, vu[0], {32 + 11, 63}, 16, 3, 1, SH110X_WHITE},
{display_L, vu[1], {32 + 11 + 64, 63}, 16, 3, 1, SH110X_WHITE},
{display_M, vu[2], {32 + 11, 63}, 16, 3, 1, SH110X_WHITE},
{display_M, vu[3], {32 + 11 + 64, 63}, 16, 3, 1, SH110X_WHITE},
// {display_R, vu[4], {32 + 11, 63}, 16, 3, 1, SH110X_WHITE},
// {display_R, vu[5], {32 + 11 + 64, 63}, 16, 3, 1, SH110X_WHITE},
// {display_RR, vu[6], {32 + 11, 63}, 16, 3, 1, SH110X_WHITE},
// {display_RR, vu[7], {32 + 11 + 64, 63}, 16, 3, 1, SH110X_WHITE},
};
// VPot rings
MCU::VPotDisplay<> vpotDisp[] {
// position (0, 10), outer radius (14) px, inner radius (12) px
{display_L, vpot[0], {0, 15}, 14, 12, SH110X_WHITE},
{display_L, vpot[1], {64, 15}, 14, 12, SH110X_WHITE},
{display_M, vpot[2], {0, 15}, 14, 12, SH110X_WHITE},
{display_M, vpot[3], {64, 15}, 14, 12, SH110X_WHITE},
// {display_R, vpot[4], {0, 15}, 14, 12, SH110X_WHITE},
// {display_R, vpot[5], {64, 15}, 14, 12, SH110X_WHITE},
// {display_RR, vpot[6], {0, 15}, 14, 12, SH110X_WHITE},
// {display_RR, vpot[7], {64, 15}, 14, 12, SH110X_WHITE},
};
// Bank setting
BankDisplay bankDisp[] {
// first track of the bank (1), position (0, 50), font size (2)
{display_L, bank, 1, {0, 50}, 2, SH110X_WHITE},
{display_L, bank, 2, {64, 50}, 2, SH110X_WHITE},
{display_M, bank, 3, {0, 50}, 2, SH110X_WHITE},
{display_M, bank, 4, {64, 50}, 2, SH110X_WHITE},
// {display_R, bank, 5, {0, 50}, 2, SH110X_WHITE},
// {display_R, bank, 6, {64, 50}, 2, SH110X_WHITE},
// {display_RR, bank, 6, {0, 50}, 2, SH110X_WHITE},
// {display_RR, bank, 7, {64, 50}, 2, SH110X_WHITE},
};
// --------------------------------- Setup ---------------------------------- //
// ========================================================================== //
void setup() {
/*#ifdef DEBUG_OUT
DEBUG_OUT.begin(115200); // Initialize the debug output
while (!DEBUG_OUT); // and wait for it to become available
#endif
*/
// 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
}