-
Hi there, I'm struggling with the displays on my controller. This is my sketch (edited 10/26): #include <Wire.h>
#include <Control_Surface.h> // Include the Control Surface library
#include <Display/DisplayInterfaces/DisplayInterfaceSSD1306.hpp>
// ----------------------------- MIDI Interface ----------------------------- //
// ========================================================================== //
USBMIDI_Interface midi;
HardwareSerialMIDI_Interface midiser = Serial1;
MIDI_PipeFactory<3> pipes;
// ----------------------------- Display setup ------------------------------ //
// ========================================================================== //
constexpr uint8_t SCREEN_WIDTH = 128;
constexpr uint8_t SCREEN_HEIGHT = 64;
constexpr int8_t OLED_reset = -1; // Use the external RC circuit for reset
//constexpr int8_t OLED_reset = 17;
// Instantiate the displays
Adafruit_SSD1306 ssd1306Display1 = {SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_reset,};
Adafruit_SSD1306 ssd1306Display2 = {SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_reset,};
Adafruit_SSD1306 ssd1306Display3 = {SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_reset,};
Adafruit_SSD1306 ssd1306Display4 = {SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_reset,};
Adafruit_SSD1306 ssd1306Display5 = {SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_reset,};
Adafruit_SSD1306 ssd1306Display6 = {SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_reset,};
Adafruit_SSD1306 ssd1306Display7 = {SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_reset,};
Adafruit_SSD1306 ssd1306Display8 = {SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_reset,};
constexpr uint8_t muxCh1 = 0; // multiplexer channel for Screen1
constexpr uint8_t muxCh2 = 1; // multiplexer channel for Screen2
constexpr uint8_t muxCh3 = 2; // multiplexer channel for Screen3
constexpr uint8_t muxCh4 = 3; // multiplexer channel for Screen4
constexpr uint8_t muxCh5 = 4; // multiplexer channel for Screen5
constexpr uint8_t muxCh6 = 5; // multiplexer channel for Screen6
constexpr uint8_t muxCh7 = 6; // multiplexer channel for Screen7
constexpr uint8_t muxCh8 = 7; // multiplexer channel for Screen8
// --------------------------- Display interface ---------------------------- //
// ========================================================================== //
class MySSD1306_DisplayInterface : public SSD1306_DisplayInterface {
public:
MySSD1306_DisplayInterface(Adafruit_SSD1306 &display, uint8_t muxCh)
: SSD1306_DisplayInterface(display), muxCh(muxCh) {}
void begin() override {
selectMux();
// Initialize the Adafruit_SSD1306 display
if (!disp.begin(SSD1306_SWITCHCAPVCC, 0x3C))
FATAL_ERROR(F("SSD1306 allocation failed."), 0x1306);
SSD1306_DisplayInterface::begin();
disp.setTextColor(WHITE);
disp.setTextSize(2);
}
void display() override {
selectMux();
SSD1306_DisplayInterface::display();
// delay(2000); // slow down, easier to debug
}
void drawBackground() override {}
private:
uint8_t muxCh;
void selectMux() {
Wire.beginTransmission(0x70);
Wire.write(1 << muxCh);
Wire.endTransmission();
Serial.print("Selected mux:");
Serial.println(muxCh);
}
} display1 = {ssd1306Display1, muxCh1},
display2 = {ssd1306Display2, muxCh2},
display3 = {ssd1306Display3, muxCh3},
display4 = {ssd1306Display4, muxCh4},
display5 = {ssd1306Display5, muxCh5},
display6 = {ssd1306Display6, muxCh6},
display7 = {ssd1306Display7, muxCh7},
display8 = {ssd1306Display8, muxCh8};
// ------------------------------- CCRotaryEncoders ------------------------------- //
// ========================================================================== //
// Instantiate a CCRotaryEncoder object
CCRotaryEncoder enc1 {{2, 4}, MCU::V_POT_1, 5,};
CCRotaryEncoder enc2 {{3, 5}, MCU::V_POT_2, 5,};
CCRotaryEncoder enc3 {{9, 6}, MCU::V_POT_3, 5,};
CCRotaryEncoder enc4 {{10, 7}, MCU::V_POT_4, 5,};
CCRotaryEncoder enc5 {{11, 8}, MCU::V_POT_5, 5,};
CCRotaryEncoder enc6 {{13, 12}, MCU::V_POT_6, 5,};
CCRotaryEncoder enc7 {{15, 14}, MCU::V_POT_7, 5,};
CCRotaryEncoder enc8 {{21, 16}, MCU::V_POT_8, 5,};
// -------------------------- MIDI Input Elements --------------------------- //
// ========================================================================== //
// Define all elements that listen for MIDI messages.
// Main MCU LCD screen, used to get track names
MCU::LCD<> lcd {};
// VPot rings
MCU::VPotRing vpot[8] {
{1},
{2},
{3},
{4},
{5},
{6},
{7},
{8},
};
// ---------------------------- Display Elements ---------------------------- //
// ========================================================================== //
// Define all display elements that display the state of the input elements.
MCU::LCDDisplay lcddisps[] {
// track (1), position (0, 40), font size (1)
{display1, lcd, 1, 1, {0, 2}, 2, WHITE},
{display1, lcd, 1, 2, {0, 50}, 2, WHITE},
{display2, lcd, 2, 1, {0, 2}, 2, WHITE},
{display2, lcd, 2, 2, {0, 50}, 2, WHITE},
{display3, lcd, 3, 1, {0, 2}, 2, WHITE},
{display3, lcd, 3, 2, {0, 50}, 2, WHITE},
{display4, lcd, 4, 1, {0, 2}, 2, WHITE},
{display4, lcd, 4, 2, {0, 50}, 2, WHITE},
{display5, lcd, 5, 1, {0, 2}, 2, WHITE},
{display5, lcd, 5, 2, {0, 50}, 2, WHITE},
{display6, lcd, 6, 1, {0, 2}, 2, WHITE},
{display6, lcd, 6, 2, {0, 50}, 2, WHITE},
{display7, lcd, 7, 1, {0, 2}, 2, WHITE},
{display7, lcd, 7, 2, {0, 50}, 2, WHITE},
{display8, lcd, 8, 1, {0, 2}, 2, WHITE},
{display8, lcd, 8, 2, {0, 50}, 2, WHITE},
};
// VPot rings
MCU::VPotDisplay<> vpotDisp[8] {
// position (0, 10), outer radius (14) px, inner radius (12) px
{display1, vpot[0], {87, 16}, 20, 15, WHITE},
{display2, vpot[1], {87, 16}, 20, 15, WHITE},
{display3, vpot[2], {87, 16}, 20, 15, WHITE},
{display4, vpot[3], {87, 16}, 20, 15, WHITE},
{display5, vpot[4], {87, 16}, 20, 15, WHITE},
{display6, vpot[5], {87, 16}, 20, 15, WHITE},
{display7, vpot[6], {87, 16}, 20, 15, WHITE},
{display8, vpot[7], {87, 16}, 20, 15, WHITE},
};
//// ---------------------------MCURevEng-------------------------------- //
//// ========================================================================== //
//
//bool channelMessageCallback(ChannelMessage cm) {
// MIDIMessageType type = cm.getMessageType();
// if (type == MIDIMessageType::NoteOn || type == MIDIMessageType::NoteOff) {
// Serial << hex << cm.header << ' ' << cm.data1 << ' ' << cm.data2 << dec
// << "\t(" << MCU::getMCUNameFromNoteNumber(cm.data1) << ")"
// << F(" on cable ") << cm.cable.getOneBased() << endl;
// } else {
// Serial << hex << cm.header << ' ' << cm.data1 << ' ' << cm.data2 << dec
// << F(" on cable ") << cm.cable.getOneBased() << endl;
// }
// return false;
//}
//
//bool sysExMessageCallback(SysExMessage se) {
// Serial << F("System Exclusive message: [") << se.length << "] " //
// << AH::HexDump(se.data, se.length) //
// << F(" on cable ") << se.cable.getOneBased() << endl;
// return false;
//}
//
//bool sysCommonMessageCallback(SysCommonMessage sc) {
// Serial << F("System Common message: ") << hex << sc.getMessageType();
// if (sc.getNumberOfDataBytes() >= 1)
// Serial << sc.getData1();
// if (sc.getNumberOfDataBytes() >= 2)
// Serial << sc.getData2();
// Serial << dec << F(" on cable ") << sc.cable.getOneBased() << endl;
// return false;
//}
//
//bool realTimeMessageCallback(RealTimeMessage rt) {
// Serial << F("Real-Time: ") << hex << rt.message << dec << F(" on cable ")
// << rt.cable.getOneBased() << endl;
// return false;
//}
// --------------------------------- Setup ---------------------------------- //
// ========================================================================== //
void setup() {
RelativeCCSender::setMode(relativeCCmode::MACKIE_CONTROL_RELATIVE);
midi << pipes << midiser; // all incoming midi from Serial is sent to USB
Control_Surface >> pipes >> midi; // Output to usbmidi
Control_Surface << pipes << midi; // Input from usbmidi
midi.begin();
midiser.begin();
Serial.begin(1000000);
Wire.begin(); // This is important! Otherwise, you can't select the mux channel
// Wire.setClock(400000);
Control_Surface.begin(); // Initialize Control Surface
// Control_Surface.setMIDIInputCallbacks(channelMessageCallback, //
// sysExMessageCallback, //
// sysCommonMessageCallback, //
// realTimeMessageCallback); //
}
// ---------------------------------- Loop ---------------------------------- //
// ========================================================================== //
void loop() {
midi.update();
midiser.update();
Control_Surface.loop(); // Refresh all elements
// if (lcd.getDirty()) {
// display1 << "LCD text updated: " << lcd.getText() << endl;
// display2 << "LCD text updated: " << lcd.getText() << endl;
// display3 << "LCD text updated: " << lcd.getText() << endl;
// display4 << "LCD text updated: " << lcd.getText() << endl;
// display5 << "LCD text updated: " << lcd.getText() << endl;
// display6 << "LCD text updated: " << lcd.getText() << endl;
// display7 << "LCD text updated: " << lcd.getText() << endl;
// display8 << "LCD text updated: " << lcd.getText() << endl;
// lcd.clearDirty();
// }
} And this is the "AlwaysDirty" bit: // -------------------------- ALWAYS_DIRTY --------------------------- //
// ========================================================================== //
struct AlwaysDirty : public DisplayElement {
AlwaysDirty(DisplayInterface &display) : DisplayElement(display) {}
void draw() override {}
bool getDirty() const override { return true; }
} redraw_display1 {display1},
redraw_display2 {display2},
redraw_display3 {display3},
redraw_display4 {display4},
redraw_display5 {display5},
redraw_display6 {display6},
redraw_display7 {display7},
redraw_display8 {display8}; Do you have any suggestion how to make the displays update correctly while keeping the controller responsive? Thanks very much in advance. |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 8 replies
-
Edited with new info and a more readable sketch (I hope). Edit2 (sorry) - I just realized I have to also add the MCU reverse engineering code to compliment "AlwaysDirty", or the controller is so slow it hardly works at all. So this is also added to "AlwaysDirty" for my initial post to make sense. // ---------------------------MCURevEng-------------------------------- //
// ========================================================================== //
bool channelMessageCallback(ChannelMessage cm) {
MIDIMessageType type = cm.getMessageType();
if (type == MIDIMessageType::NoteOn || type == MIDIMessageType::NoteOff) {
Serial << hex << cm.header << ' ' << cm.data1 << ' ' << cm.data2 << dec
<< "\t(" << MCU::getMCUNameFromNoteNumber(cm.data1) << ")"
<< F(" on cable ") << cm.cable.getOneBased() << endl;
} else {
Serial << hex << cm.header << ' ' << cm.data1 << ' ' << cm.data2 << dec
<< F(" on cable ") << cm.cable.getOneBased() << endl;
}
return false;
}
bool sysExMessageCallback(SysExMessage se) {
Serial << F("System Exclusive message: [") << se.length << "] " //
<< AH::HexDump(se.data, se.length) //
<< F(" on cable ") << se.cable.getOneBased() << endl;
return false;
}
bool sysCommonMessageCallback(SysCommonMessage sc) {
Serial << F("System Common message: ") << hex << sc.getMessageType();
if (sc.getNumberOfDataBytes() >= 1)
Serial << sc.getData1();
if (sc.getNumberOfDataBytes() >= 2)
Serial << sc.getData2();
Serial << dec << F(" on cable ") << sc.cable.getOneBased() << endl;
return false;
}
bool realTimeMessageCallback(RealTimeMessage rt) {
Serial << F("Real-Time: ") << hex << rt.message << dec << F(" on cable ")
<< rt.cable.getOneBased() << endl;
return false;
}
// ---------------------------Setup-------------------------------- //
// ========================================================================== //
Control_Surface.setMIDIInputCallbacks(channelMessageCallback, //
sysExMessageCallback, //
sysCommonMessageCallback, //
realTimeMessageCallback); // So I guess it has something to do with the callbacks. And I also should mention that I edited the LCDDisplay.hpp like you mentioned in your comments |
Beta Was this translation helpful? Give feedback.
-
Using a single LCD object that is shared across different physical displays is currently not supported, because the LCD object only has a single I've pushed a possible fix to the lcd-multi-dirty branch. |
Beta Was this translation helpful? Give feedback.
Using a single LCD object that is shared across different physical displays is currently not supported, because the LCD object only has a single
dirty
flag, which is cleared the first time any display is redrawn.I've pushed a possible fix to the lcd-multi-dirty branch.