tttapa / Control-Surface

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

anyone as already managed to use ili9341 for timedisplay? #307

Open benwadub opened 3 years ago

benwadub commented 3 years ago

hi everyone, does someone as already wrote a code to use the mcu of the control surface to print on an ili9341?

tttapa commented 3 years ago

What are you having trouble with? The TimeDisplay class inherits from the Arduino Printable class, so you can just do something like

Adafruit_ILI9341 tft = (...);
MCU::TimeDisplay timedisplay = {};

...

  tft.print(timedisplay);

You can also use the getText() and getCharacterAt() methods, see the documentation for more details.

benwadub commented 3 years ago

As you know I started arduino with your library so when I have to use something not implemented by you all become difficult :-) I didn’t know at all where to start and didn’t began to writ anything, I ll try to make a first test this evening

benwadub commented 3 years ago
// Time display
MCU::TimeDisplayDisplay timedisplaydisplay = {};
  // position (0, 0), font size (1)
  tft.print (timedisplay), {0, 0}, 1, ILI9341_WHITE,
};

when I try it I get: controleur_BEN_and_ED_ableton1_2_touch_avec_display_et_boutons:315: error: could not convert '()' from '' to 'CS::MCU::TimeDisplayDisplay' MCU::TimeDisplayDisplay timedisplaydisplay = {}; ^

benwadub commented 3 years ago

so my code compile now but time display doesn't show on screen, do you know what I missed?


#include <Control_Surface.h>
#include <Encoder.h> // Include the Encoder library.
//#include <name.c>
#include <SPI.h>
#include <ILI9341_t3.h>

 //USBDebugMIDI_Interface midi = 115200; // enlever les // en début de ligne pour entrer en mode debug usb et voir dans le panneau de control si vos controler envoient bien les infos

//auto &serial = Serial1;// Selectionne le port série à utiliser remplacer par serial pour une arduino
//SerialMIDI_Interface<decltype(serial)> midi = {serial, MIDI_BAUD};// démarre une interface midi serial au midi baud rate par defaut
USBMIDI_Interface usbmidi;// enlever les / en debut de ligne pour activer l'interface usb, penser à désactiver l'interface série(din)
//HardwareSerialMIDI_Interface midiser = Serial1;

#include "TouchScreen.h"

// These are the four touchscreen analog pins
#define YP 23  // must be an analog pin, use "An" notation!
#define XM 22  // must be an analog pin, use "An" notation!
#define YM 4   // can be a digital pin
#define XP 5   // can be a digital pin

// This is calibration data for the raw touch data to the screen coordinates
#define TS_MINX 150
#define TS_MINY 120
#define TS_MAXX 920
#define TS_MAXY 940

#define MINPRESSURE 10
#define MAXPRESSURE 1000

// The display uses hardware SPI, plus #9 & #10
#define TFT_CS 10
#define TFT_DC  9
#define rst -1
ILI9341_t3 tft = ILI9341_t3(TFT_CS, TFT_DC);

// For better pressure precision, we need to know the resistance
// between X+ and X- Use any multimeter to read it
// For the one we're using, its 300 ohms across the X plate
TouchScreen ts = TouchScreen(XP, YP, XM, YM, 360);

// Size of the color selection boxes and the paintbrush size
#define BOXSIZE 40
#define PENRADIUS 3
int oldcolor, currentcolor;

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

/*
   Define all elements that listen for MIDI messages.
*/
MCU::TimeDisplay timedisplay = {};

// Play / Record
CCValue play = {{103, CHANNEL_1}};
CCValue record = {{104, CHANNEL_1}};

void setup() {
  tft.begin();

  // Correct relative mode for MCU rotary encoders
  RelativeCCSender::setMode(MACKIE_CONTROL_RELATIVE);

  Control_Surface.begin(); // initialise la library surface de control

  // Display a splash screen for five seconds
  //tft.fillScreen(ILI9341_BLACK),
  tft.print (timedisplay),timedisplay, (0, 0, 1, ILI9341_WHITE);

 /* tft.drawXBitmap(0, 0, 
                      XBM::logo.bits, 
                      XBM::logo.width,
                      XBM::logo.height, 
                      ILI9341_WHITE);
  display.display();
  delay(5000);
*/

}                           
void loop() {

  Control_Surface.loop(); // Update the Control Surface
usbmidi.update();

}```
tttapa commented 3 years ago
timedisplay, (0, 0, 1, ILI9341_WHITE);

This expression has no effect. Please enable all compiler warnings in the IDE, it should warn you about things like that.

You really need to get out of the habbit of randomly adding or removing brackets and other punctuation until your code compiles. I highly recommend following a C++ tutorial/book, because I can fix individual problems with your code, but I cannot teach you how to program.

Please see the examples that come with the ILI9341 library to learn how to print text to the display, e.g. https://github.com/PaulStoffregen/ILI9341_t3/blob/effb156ef9bf94d11ec12c4b8f122f3b35d0e1e7/examples/onoffbutton/onoffbutton.ino#L50-L53:

#include <Control_Surface.h>
#include <SPI.h>
#include <ILI9341_t3.h>

USBMIDI_Interface usbmidi;

// The display uses hardware SPI, plus #9 & #10
#define TFT_CS 10
#define TFT_DC  9
#define rst -1
ILI9341_t3 tft(TFT_CS, TFT_DC);

// This timedisplay just listens to incoming MIDI messages,
// it has nothing to do with any physical displays connected to
// the Teensy.
MCU::TimeDisplay timedisplay = {};

void setup() {
  tft.begin();
  tft.fillScreen(ILI9341_BLACK),

  // Correct relative mode for MCU rotary encoders
  RelativeCCSender::setMode(MACKIE_CONTROL_RELATIVE);
  Control_Surface.begin();
}                           
void loop() {
  tft.setCursor(0, 0);
  tft.setTextSize(1);
  tft.setTextColor(ILI9341_WHITE);
  tft.print(timedisplay);

  Control_Surface.loop();
}

The code above is untested, because I don't have the necessary hardware or libraries installed, but it should given you a rough idea of what you need to do to get it working. If you're using the new-input branch, you can detect if the content of the time display changed using the MCU::TimeDisplay::getDirty() and clearDirty() methods, otherwise, it'll re-draw the display on each iteration of the loop(), which is probably not what you want.

benwadub commented 3 years ago

thanks that nearly work! I begin understanding MCU! all is clear now with things that listen midi!! i ll check if I find the documentation for get dirty and clean dirty, I think it s something to clear the screen to avoid the character becoming unreadable after few loops!

tttapa commented 3 years ago

getDirty() has nothing to do with the displays per se, it's just a way of detecting changes. See https://tttapa.github.io/Control-Surface-doc/new-input/Doxygen/df/d69/classMCU_1_1TimeDisplay.html#a67459e7b1758df918954df57cfec7384.

benwadub commented 3 years ago

ok so I just have to write cpp MCU::TimeDisplay timedisplay = {}; MCU::TimeDisplay::clearDirty(), and it will get change and clean them before redraw that s the way I understand it

tttapa commented 3 years ago

MCU::TimeDisplay::clearDirty()

You can't write that, clearDirty() is a non-static member function, you have to invoke it against an instance of the MCU::TimeDisplay class. Again, there's no point in trying to use these functions without first understanding the basics of C++ functions, namespaces and classes.

When the timedisplay object receives a MIDI message that changes the text of the display, it sets the “dirty” flag. You can query that flag using the MCU::TimeDisplay::clearDirty() method, calling timedisplay.getDirty() if that method returns true, you know that you have to re-draw the text to the display. Finally, you clear the “dirty” flag again to acknowledge the change, and so you can detect the next change.

benwadub commented 3 years ago

Thanks! I ll try to find a good book to learn basics to understand better how the library and c++ works

tttapa commented 3 years ago

Getting a book is a good idea, but if that's not an investment you want to make, you could try https://www.learncpp.com. I haven't followed it myself, but I've heard good things about it, and it seems to covers pretty much everything you need to know.

You don't need to read and understand all of it, some parts are pretty specific or don't really apply to microcontrollers, but you do need a firm understanding of things like statements, expressions, variables, scope, lifetime, functions, classes, initialization, etc. Otherwise programming will feel more like a guessing game, which is not an efficient strategy.

benwadub commented 3 years ago

yes thanks!! I really want to learn and understand what id do and now that I really need specific things to ad to my controller I have to learn the basics!

benwadub commented 3 years ago

i ll wait that you put exemple with the dirty things, Doxygen files are a big mystery for me!! I began reading the site you gave me and already learnt few things! thanks!

tttapa commented 3 years ago

Here's an example: https://tttapa.github.io/Control-Surface-doc/new-input/Doxygen/d0/d6c/Pitch-Bend-Value_8ino-example.html

It uses PBValue instead of TimeDisplay, but the only real difference is that the former listens for MIDI Pitch Bend messages, and the latter listens for MIDI MCU Time Display messages. It also prints to the serial port instead of a display, but once you figure out how the Adafruit ILI9341 examples work, that shouldn't be an issue either.

benwadub commented 3 years ago

hi Pieter, i finaly find time to come back to the ili9341 I can now see the time display on the ili9341 using this code

#include <Control_Surface.h>
#include <SPI.h>
#include <ILI9341_t3.h>

USBMIDI_Interface usbmidi;
Bank<4> bank(2); // Create a new bank with two tracks per bank
// The display uses hardware SPI, plus #9 & #10
#define TFT_CS 10
#define TFT_DC  9
#define rst -1
ILI9341_t3 tft(TFT_CS, TFT_DC);

// This timedisplay just listens to incoming MIDI messages,
// it has nothing to do with any physical displays connected to
// the Teensy.
MCU::TimeDisplay timedisplay = {};

void setup() {
  tft.begin();
  tft.fillScreen(ILI9341_BLACK),

  // Correct relative mode for MCU rotary encoders
  RelativeCCSender::setMode(MACKIE_CONTROL_RELATIVE);
  Control_Surface.begin();
}                           
void loop() {
  tft.setCursor(0, 0);
  tft.setTextSize(4);
  tft.setTextColor(ILI9341_WHITE);
  //tft.print(timedisplay);
  if (timedisplay.getDirty()) {
    // Print it
    tft.print(timedisplay);
    // Clear the dirty flag to acknowledge the change
    timedisplay.clearDirty();
    tft.fillScreen(ILI9341_BLACK);

    tft.print(timedisplay);
  }

  Control_Surface.loop();
}

i had to filscreen in the loop to avoid the problem of my number on the screen that continuously overwrite on the precedent it s not really nice to see because the numbers seems to scroll on the screen but for a beginner like me it s a cool thing for the moment just to see it working! i ll now try to investigate how to show mute state on the ili9341 for the moment I get an error 👍

Arduino : 1.8.13 (Mac OS X), TD: 1.53, Carte : "Teensy 4.0, Serial + MIDI, 600 MHz, Faster, US English"

In file included from /Users/benoitparisot/Documents/Arduino/libraries/Control-Surface-new-input/src/Control_Surface.h:91:0,
                 from /Users/benoitparisot/Documents/Arduino/test_timedisplay/test_timedisplay.ino:1:
/Users/benoitparisot/Documents/Arduino/libraries/Control-Surface-new-input/src/MIDI_Inputs/MCU/VU.hpp:188:7: warning: 'CS::MCU::VU' has a field 'CS::MCU::VU::decayTimer' whose type uses the anonymous namespace
 class VU : public MatchingMIDIInputElement<MIDIMessageType::CHANNEL_PRESSURE,
       ^
test_timedisplay:18: error: 'mute' was not declared in this scope
   {tft.print, mute[0], XBM::mute_10B, {14, 50}, ILI9341_WHITE},
               ^
test_timedisplay:19: error: 'mute' was not declared in this scope
   {tft.print, mute[1], XBM::mute_10B, {14 + 64, 50}, ILI9341_WHITE},
               ^
test_timedisplay:20: error: could not convert '{tft.Print::print, <expression error>, CS::XBM::mute_10B, {14, 50}, 65535}' from '<brace-enclosed initializer list>' to 'CS::BitmapDisplay<>'
 };
 ^
test_timedisplay:20: error: could not convert '{tft.Print::print, <expression error>, CS::XBM::mute_10B, {78, 50}, 65535}' from '<brace-enclosed initializer list>' to 'CS::BitmapDisplay<>'
'mute' was not declared in this scope

Ce rapport pourrait être plus détaillé avec
l'option "Afficher les résultats détaillés de la compilation"
activée dans Fichier -> Préférences.

with the code

#include <Control_Surface.h>
#include <SPI.h>
#include <ILI9341_t3.h>

USBMIDI_Interface usbmidi;
Bank<4> bank(2); // Create a new bank with two tracks per bank
// The display uses hardware SPI, plus #9 & #10
#define TFT_CS 10
#define TFT_DC  9
#define rst -1
ILI9341_t3 tft(TFT_CS, TFT_DC);

// This timedisplay just listens to incoming MIDI messages,
// it has nothing to do with any physical displays connected to
// the Teensy.
MCU::TimeDisplay timedisplay = {};
BitmapDisplay<> muteDisp[] = {
  {tft.print, mute[0], XBM::mute_10B, {14, 50}, ILI9341_WHITE},
  {tft.print, mute[1], XBM::mute_10B, {14 + 64, 50}, ILI9341_WHITE},
};
void setup() {
  tft.begin();
  tft.fillScreen(ILI9341_BLACK),

  // Correct relative mode for MCU rotary encoders
  RelativeCCSender::setMode(MACKIE_CONTROL_RELATIVE);
  Control_Surface.begin();
}                           
void loop() {
  tft.setCursor(0, 0);
  tft.setTextSize(4);
  tft.setTextColor(ILI9341_WHITE);
  //tft.print(timedisplay);
  if (timedisplay.getDirty()) {
    // Print it
    tft.print(timedisplay);
    // Clear the dirty flag to acknowledge the change
    timedisplay.clearDirty();
    tft.fillScreen(ILI9341_BLACK);

    tft.print(timedisplay);
  }

  Control_Surface.loop();
}

i ll try to understand what it mean!

tttapa commented 3 years ago
test_timedisplay:18: error: 'mute' was not declared in this scope
   {tft.print, mute[0], XBM::mute_10B, {14, 50}, ILI9341_WHITE},
               ^

You're trying to use a variable named mute, but you never declared a variable mute. Have a look at the OLED examples, they contain a declaration (and definition) of mute.

benwadub commented 3 years ago

#include <Control_Surface.h>
#include <SPI.h>
#include <ILI9341_t3.h>

USBMIDI_Interface usbmidi;

// The display uses hardware SPI, plus #9 & #10
#define TFT_CS 10
#define TFT_DC  9
#define rst -1
ILI9341_t3 tft(TFT_CS, TFT_DC);

// This timedisplay just listens to incoming MIDI messages,
// it has nothing to do with any physical displays connected to
// the Teensy.
MCU::TimeDisplay timedisplay = {};
Bank<4> bank(2); // Create a new bank with two tracks per bank
NoteValue play = {MCU::PLAY};
NoteValue record = {MCU::RECORD};
// Mute
Bankable::NoteValue<4> mute[] = {
  {bank, MCU::MUTE_1},
  {bank, MCU::MUTE_2},
};
BitmapDisplay<> muteDisp[] = {
  {tft.print, mute[0], XBM::mute_10B, {14, 50}, ILI9341_WHITE},
  {tft.print, mute[1], XBM::mute_10B, {14 + 64, 50}, ILI9341_WHITE},

};
void setup() {
  tft.begin();
  tft.fillScreen(ILI9341_BLACK),

  // Correct relative mode for MCU rotary encoders
  RelativeCCSender::setMode(MACKIE_CONTROL_RELATIVE);
  Control_Surface.begin();
}                           
void loop() {
  tft.setCursor(0, 0);
  tft.setTextSize(4);
  tft.setTextColor(ILI9341_WHITE, ILI9341_BLACK);
  //tft.setTextColor(ILI9341_WHITE);
  //tft.print(timedisplay);
  if (timedisplay.getDirty()) {
    // Print it
    tft.print(timedisplay);
    // Clear the dirty flag to acknowledge the change
    timedisplay.clearDirty();

  }
BitmapDisplay<> muteDisp[] = {
  {tft.print, mute[0], XBM::mute_10B, {14, 50}, ILI9341_WHITE},
  {tft.print, mute[1], XBM::mute_10B, {14 + 64, 50}, ILI9341_WHITE},
};
  Control_Surface.loop();
}```
I got the same problem with the mute declared like this, maybe those print you wrote are related to your display interface that I don t use cause I use the ili9341?
may I have to try to make a display interface for the ili9341 to make it work?
benwadub commented 2 years ago

hi @tttapa I try to make the adapter for the ili9341 here is how I made it, I use the Paul 's library for ili9341 found here https://github.com/PaulStoffregen/ILI9341_t3 here is my adapter

#pragma once

#include <ILI9341_t3.h>
#include <Display/DisplayInterface.hpp>

BEGIN_CS_NAMESPACE

/**
 * @brief   This class creates a mapping between the Adafruit_SSD1306 display
 *          driver and the general display interface used by the Control Surface
 *          library.
 */
class ILI9341_DisplayInterface : public DisplayInterface {
  protected:
    ILI9341_DisplayInterface(ILI9341_t3 &display) : tft(display) {}

  public:
    /// Clear the frame buffer or clear the display.
    void clear() override { tft.fillScreen(ILI9341_BLACK)(); }
    /// Draw a custom background.
    void drawBackground() override = 0;
    /// Write the frame buffer to the display. If your display library writes to
    /// the display directly, without a display buffer in RAM, you can leave
    /// this function empty.
    void display() override { tft.begin(); }

    /// Paint a single pixel with the given color.
    void drawPixel(int16_t x, int16_t y, uint16_t color);

    /// Set the text color.
    void setTextColor(uint16_t c);
    /// Set the text size.
    void setTextSize(uint8_t s);
    /// Set the cursor position.
    void setCursor(int16_t x, int16_t y);

    /**
     * @brief   Write a character to the display.
     *
     * @see     setCursor
     * @see     setTextSize
     * @see     setTextColor
     */
    virtual size_t write(uint8_t);

    /// Draw a line between two points.
    void drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint16_t color);

    /// Draw a vertical line.
    void drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color);

    /// Draw a horizontal line.
    void drawFastHLine(int16_t x, int16_t y, int16_t h, uint16_t color);

    /// Draw a bitmap to the display.
    void drawBitmap(int16_t x, int16_t y, const uint8_t *bitmap, int16_t w, int16_t h, uint16_t color);

  protected:
    ILI93341_t3 &display;
};

END_CS_NAMESPACE

but when I use it in my code I got the error aft was not declared I find it strange as I include the library in the adapter do I have to modify an other file in the display interfaces? here is the code



#include <Control_Surface.h>

USBMIDI_Interface usbmidi;
#include <SPI.h>
#include <Display/DisplayInterfaces/DisplayInterfaceSILI9341.hpp>

#include <ILI9341_t3.h>

// The display uses hardware SPI, plus #9 & #10
#define TFT_CS 10
#define TFT_DC  9
#define rst -1
ILI9341_t3 tft = ILI9341_t3(TFT_CS, TFT_DC);
//ILI9341_t3 tft(TFT_CS, TFT_DC);
//extern uint8_t play_bitmap[];
// This timedisplay just listens to incoming MIDI messages,
// it has nothing to do with any physical displays connected to
// the Teensy.
MCU::TimeDisplay timedisplay = {};
NoteValue play {MCU::PLAY};

void setup() {
  Serial.begin(9600);
  tft.begin();
  tft.fillScreen(ILI9341_BLACK),
Serial.begin(9600);
  // Correct relative mode for MCU rotary encoders
  RelativeCCSender::setMode(MACKIE_CONTROL_RELATIVE);
  Control_Surface.begin();
}                           
void loop() {
  tft.setRotation(1);
  tft.setCursor(0, 0);
  tft.setTextSize(3);
  tft.setTextColor(ILI9341_WHITE, ILI9341_BLACK); 

  if (timedisplay.getDirty()) {
    // Print it
    tft.print(timedisplay);
    // Clear the dirty flag to acknowledge the change
    timedisplay.clearDirty();
    //tft.fillScreen(ILI9341_BLACK);

    //tft.print(timedisplay);
  }
  tft.setCursor(0, 60);
  tft.setTextSize(3);
  tft.setTextColor(ILI9341_WHITE, ILI9341_BLACK); 
  if (play.getDirty()) {

 /*tft.drawBitmap(65, 70, play_bitmap, 195, 146,ILI9341_RED);
 void drawBitmap(int16_t x, int16_t y,
 const uint8_t play_bitmap, int16_t w, int16_t h, uint16_t color) {

  int16_t i, j, byteWidth = (w + 7) / 8;
  uint8_t byte;

  for(j=0; j<h; j++) {
    for(i=0; i<w; i++) {
      if(i & 7) byte <<= 1;
      else      byte   = pgm_read_byte(bitmap + j * byteWidth + i / 8);
      if(byte & 0x80) tft.drawPixel(x+i, y+j, color);
    }*/
    // Print it
    //tft.println("play");
    // Clear the dirty flag to acknowledge the change
   play.clearDirty();
  }
  Control_Surface.loop();
}```
tttapa commented 2 years ago

I got the error tft was not declared

That's because you didn't declare it. You only declared display, not tft:

  protected:
    ILI93341_t3 &display;
};

Either rename that member variable to tft, or replace tft by display in the rest of the code.