Arduino Touchscreen X-Y MIDI Controller – Part 2

Whilst being able to use a cheap touchscreen with a DIY shield gets me so far, there are a number of cheap touchscreen shields around and I’ve had one for a while, but not managed to really get it going.  I thought it was perhaps about time to see what I could do, so I set about reimplementing my Arduino Touchscreen X-Y MIDI Controller on this touchscreen shield.

Warning! I strongly recommend using old or second hand equipment for your experiments.  I am not responsible for any damage to expensive instruments!

These are the key Arduino tutorials for the main concepts used in this project:

If you are new to Arduino, see the Getting Started pages.

Parts list

  • Arduino Uno
  • “MCU Friend” style 3.5″ touchscreen shield for an Arduino Uno
  • MIDI out interface (e.g. Ready-Made MIDI Modules)

The Circuit

This is the shield I’m using.

There are many different variants out there, but mine is one that is compatible with those designed by MCU Friend and available, well all over the place it would seem!

In principle I guess anything that is compatible with the MCUFriend Library here could work: https://github.com/prenticedavid/MCUFRIEND_kbv.

A MIDI OUT circuit is required connected to the Arduino’s TX.  Once again I’m using one of my Uno clones that has additional header pin breakouts for the UART as shown below.

2020-11-28 09.35.14

The Code

The basic operation is that the code is the same as the Arduino Touchscreen X-Y MIDI Controller but a number of the graphics and touch elements need updating.

These are the libraries required (all installed via the Arduino Library Manager):

  • Adafruit GFX Library
  • Adafruit TouchScreen Library
  • MCUFRIEND_kbv Library
  • Arduino MIDI Library

One of the advantages of using the MCUFRIEND_kbv library is that is quite capable of working with a whole range of display drivers and touchscreens.  Part of that can be seen by how the touch screen is set up and initialised.

First: auto-detect which pins are in use by the TouchScreen.

  1. Download and run the diagnose_Touchpins.ino Example.
  2. This outputs to the serial console the most likely pins to be used by your specific touchscreen.  Here is the output for me.
Making all control and bus pins INPUT_PULLUP
Typical 30k Analog pullup with corresponding pin
would read low when digital is written LOW
e.g. reads ~25 for 300R X direction
e.g. reads ~30 for 500R Y direction

Testing : (A1, D7) = 35
Testing : (A2, D6) = 26
Diagnosing as:-
YP,YM: (A1, D7) = 35
XM,XP: (A2, D6) = 26

This means that the Y positive (YP) and negative (YM) are A1 and D7 respectively; and the X positive (XP) and negative (XM) are D6 and A2 respectively.  These values can be used in the definitions in other sketches when it comes to initialising the TouchScreen as follows.

int XP = 6, YP = A1, XM = A2, YM = 7;
TouchScreen ts(XP, YP, XM, YM, 300);

That last value, 300, is meant to tell the code what the typical resistance is between XM and XP (the X negative and positive) pins.  If you have a multimeter you can measure this and then use the actual figure for your screen.  Mine was around 380Ω.

Second: Calibrate the touch screen.

  1. Edit the TouchScreen_calib_native.ino Example to initialise the touchscreen with the correct pins as described above then run the sketch.
  2. This will prompt you to click on some crosses on your screen.
  3. Then it outputs to the serial console a set of values that tell you both which pins are used with your specific touchscreen and some calibration parameters to give you an accurate output.  Here is part of the output for me.
*** COPY-PASTE from Serial Terminal:
const int XP=6,XM=A2,YP=A1,YM=7; //320x480 ID=0x1581
const int TS_LEFT=920,TS_RT=297,TS_TOP=959,TS_BOT=202;

PORTRAIT CALIBRATION 320 x 480
x = map(p.x, LEFT=920, RT=297, 0, 320)
y = map(p.y, TOP=959, BOT=202, 0, 480)

LANDSCAPE CALIBRATION 480 x 320
x = map(p.y, LEFT=959, RT=202, 0, 480)
y = map(p.x, TOP=297, BOT=920, 0, 320)

The critical line here is the values for TS_LEFT, etc which are then used in a map function to turn touch point coordinates into display coordinates.

Third: Using the MCUFRIEND_kbv library

All of this makes it a lot easier to just pick up and use the library and calibration values in your own code.  The main detection and handling routine I took from the button_simple.ino Example as follows.

int pixel_x, pixel_y; //Touch_getXY() updates global vars
bool Touch_getXY(void)
{
TSPoint p = ts.getPoint();
pinMode(YP, OUTPUT); //restore shared pins
pinMode(XM, OUTPUT);
digitalWrite(YP, HIGH); //because TFT control pins
digitalWrite(XM, HIGH);
bool pressed = (p.z > MINPRESSURE && p.z < MAXPRESSURE);
if (pressed) {
/* // Portrait mode
pixel_x = map(p.x, TS_LEFT, TS_RT, 0, tft.width());
pixel_y = map(p.y, TS_TOP, TS_BOT, 0, tft.height());
*/
// Landscape mode
pixel_x = map(p.y, TS_TOP, TS_BOT, 0, tft.width());
pixel_y = map(p.x, TS_RT, TS_LEFT, 0, tft.height());
}
return pressed;
}

As you can see in the code, how those map functions are used depends on if you’re using the screen in portrait or landscape mode.  As I use a ROTATION=1 with the main graphics library (which means landscape, USB port on the right) I’m using the landscape versions of the map function here.

One other oddity to note here – those pinMode and digitalWrite calls.  This is because these pins are shared by the TFT display and the touchscreen.  The Adafruit TouchScreen library configures them as it requires in order to do the touchscreen sensing, so this code has to restore them to what the TFT library is expecting them to be.  This isn’t very nice tbh, but it is in the example code and I’m not sure there is a cleaner way to do it really anyway.

I was having problems with spurious non-touches when I was still touching the screen, so wondered if the pressure values seen here were too high.  In the end I think it must have been something else, but I widened the range recognised as “touch” anyway as follows:

#define MINPRESSURE 5
#define MAXPRESSURE 2000

You might not need to worry about that.

Most of the logic for the rest of the code is the same as for the Arduino Touchscreen X-Y MIDI Controller but the scaling, calibration and general handling of the touchscreen is a lot easier with much simpler code now.

One thing I did find though – as I say I was getting spurious retriggering of new notes, when I thought I was still pitch-bending them.  I don’t know what caused this but I could see the occasional “no touch” being registered.  In the end I added a “non-touch debouncing” count so that the code only recognised a non-touch after a couple of passes through.  There are two places where this needs to be taken into account: in the pitch bend handling (x2pb and x2pb_reset) and in the “else” from the “IF touched” test in the main gfxLoop.

Whatever the original issue, this seems to allow for it and gives me a nice smooth pitch bend.

Find it on GitHub here.

Closing Thoughts

I can’t remember why I didn’t use the MCUFRIEND_kbv library for my original project – I definitely took a look at it. Maybe I hadn’t properly appreciated the calibration and configuration possibilities.  I also thought I’d tried it with my shield, but for some reason had it in my mind that the touch screen wasn’t working.

So given all that, I was pleasantly surprised how well the MCUFRIEND_kbv code manages with all this and makes it a lot easier to work with.

I’d really like to go back and create MCUFRIEND and “shield” versions of my previous touchscreen projects, and I’d also like to see how the MCUFRIEND_kbv library copes with my other touchscreen and DIY adaptor.

Kevin

 

Leave a comment