Touchpad Midi

I've built a MIDI controller around the Arduino.

Started from a scavenged Cirque Glidepoint resistive touchpad (PS/2 compatible), 2x8 LCD (in 4 bit mode), two buttons and a serial Arduino board.

All that take place into a simple enclosure.

The buttons when pressed allow to set the Continuous Controller number by gliding the finger on the touchpad. Left for CC# linked to X and right for Y.

The LCD displays the value sent in MIDI. Some custom characters where coded into the CGRAM in order to have a sort of bar graph. The first line is for X and the second for Y.

I'will post the code after some clean up and referencing all the source. I will also code a MIDI Channel setting and maybe a Note On message.

Beautiful work!! I look forward to more details..

Here is the complete code (first part) . It will be enhanced soon. I've tried to credit all sources. Thanks for usefull code and inspiration. Shall I write this in the playground ?

#define LCD_RS      8      // Register select
#define LCD_EN      9      // Enable
#define LCD_D4 10      // Data bits
#define LCD_D5 11      // Data bits
#define LCD_D6 12      // Data bits
#define LCD_D7 13      // Data bits

#define MDATA 2         // Mouse Data
#define MCLK 3          // Mouse Clock
 
int midivalueX;
int midivalueY;

int XCC = 19;          // Midi Continous Controler assigned by default to X
int YCC = 21;          // Midi Continous Controler assigned by default to Y

/**********************************************************************
 *
 * Mouse
 * Source : Arduino Playground
 * URL : http://www.arduino.cc/playground/ComponentLib/Ps2mouse
 * Author : Unknown
 **********************************************************************/


void gohi(int pin)
{
  pinMode(pin, INPUT);
  digitalWrite(pin, HIGH);
}

void golo(int pin)
{
  pinMode(pin, OUTPUT);
  digitalWrite(pin, LOW);
}

void mouse_write(char data)
{
  char i;
  char parity = 1;

  /* put pins in output mode */
  gohi(MDATA);
  gohi(MCLK);
  delayMicroseconds(300);
  golo(MCLK);
  delayMicroseconds(300);
  golo(MDATA);
  delayMicroseconds(10);
  /* start bit */
  gohi(MCLK);
  /* wait for mouse to take control of clock); */
  while (digitalRead(MCLK) == HIGH);
  /* clock is low, and we are clear to send data */
  for (i=0; i < 8; i++) {
    if (data & 0x01) {
      gohi(MDATA);
    } 
    else {
      golo(MDATA);
    }
    /* wait for clock cycle */
    while (digitalRead(MCLK) == LOW)
      ;
    while (digitalRead(MCLK) == HIGH)
      ;
    parity = parity ^ (data & 0x01);
    data = data >> 1;
  }  
  /* parity */
  if (parity) {
    gohi(MDATA);
  } 
  else {
    golo(MDATA);
  }
  while (digitalRead(MCLK) == LOW)
    ;
  while (digitalRead(MCLK) == HIGH)
    ;
  /* stop bit */
  gohi(MDATA);
  delayMicroseconds(50);
  while (digitalRead(MCLK) == HIGH)
    ;
  /* wait for mouse to switch modes */
  while ((digitalRead(MCLK) == LOW) || (digitalRead(MDATA) == LOW))
    ;
  /* put a hold on the incoming data. */
  golo(MCLK);
  //  Serial.print("done.\n");
}

/*
 * Get a byte of data from the mouse
 */
char mouse_read(void)
{
  char data = 0x00;
  int i;
  char bit = 0x01;

  //  Serial.print("reading byte from mouse\n");
  /* start the clock */
  gohi(MCLK);
  gohi(MDATA);
  delayMicroseconds(50);
  while (digitalRead(MCLK) == HIGH)
    ;
  delayMicroseconds(5);  /* not sure why */
  while (digitalRead(MCLK) == LOW) /* eat start bit */
    ;
  for (i=0; i < 8; i++) {
    while (digitalRead(MCLK) == HIGH)
      ;
    if (digitalRead(MDATA) == HIGH) {
      data = data | bit;
    }
    while (digitalRead(MCLK) == LOW)
      ;
    bit = bit << 1;
  }
  /* eat parity bit, which we ignore */
  while (digitalRead(MCLK) == HIGH)
    ;
  while (digitalRead(MCLK) == LOW)
    ;
  /* eat stop bit */
  while (digitalRead(MCLK) == HIGH)
    ;
  while (digitalRead(MCLK) == LOW)
    ;

  /* put a hold on the incoming data. */
  golo(MCLK);
  //  Serial.print("Recvd data ");
  //  Serial.print(data, HEX);
  //  Serial.print(" from mouse\n");
  return data;
}

void mouse_init()
{
  gohi(MCLK);
  gohi(MDATA);
  //  Serial.print("Sending reset to mouse\n");
  mouse_write(0xff);
  mouse_read();  /* ack byte */
  //  Serial.print("Read ack byte1\n");
  mouse_read();  /* blank */
  mouse_read();  /* blank */
  //  Serial.print("Sending remote mode code\n");
  mouse_write(0xf0);  /* remote mode */
  mouse_read();  /* ack */
  //  Serial.print("Read ack byte2\n");
  delayMicroseconds(100);
}

/**********************************************************************
 *
 * MIDI
 *
 **********************************************************************/
 
// Send a MIDI note-on message.  Like pressing a piano key
void noteOn(byte channel, byte note, byte velocity) {
  midiMsg( (0x80 | (channel<<4)), note, velocity);
}
 
// Send a MIDI note-off message.  Like releasing a piano key
void noteOff(byte channel, byte note, byte velocity) {
  midiMsg( (0x80 | (channel<<4)), note, velocity);
}
 
// Send a general MIDI message
void midiMsg(byte cmd, byte data1, byte data2) {
  //digitalWrite(ledPin,HIGH);  // indicate we're sending MIDI data
  Serial.print(cmd, BYTE);
  Serial.print(data1, BYTE);
  Serial.print(data2, BYTE);
 // digitalWrite(ledPin,LOW);
}

Second and last part :

/**********************************************************************
 *
 * LCD
 * Based on 4bit interface example
 * 2006 Massimo Banzi
 * Based on code written by Craig Lee 1998
 * 2007 Ludovic Drochon : LCDbar127
 *
 **********************************************************************/
 
void lcd_strobe()
{
      digitalWrite(LCD_EN,HIGH);
      digitalWrite(LCD_EN,LOW);
}

/* write a byte to the LCD in 4 bit mode */

void lcd_write(byte c) 
{
      if(c & 0x80) digitalWrite(LCD_D7,HIGH); else  digitalWrite(LCD_D7,LOW);
      if(c & 0x40) digitalWrite(LCD_D6,HIGH); else  digitalWrite(LCD_D6,LOW);  
      if(c & 0x20) digitalWrite(LCD_D5,HIGH); else  digitalWrite(LCD_D5,LOW);  
      if(c & 0x10) digitalWrite(LCD_D4,HIGH); else  digitalWrite(LCD_D4,LOW);  
      lcd_strobe();
      if(c & 0x08) digitalWrite(LCD_D7,HIGH); else  digitalWrite(LCD_D7,LOW);
      if(c & 0x04) digitalWrite(LCD_D6,HIGH); else  digitalWrite(LCD_D6,LOW);  
      if(c & 0x02) digitalWrite(LCD_D5,HIGH); else  digitalWrite(LCD_D5,LOW);  
      if(c & 0x01) digitalWrite(LCD_D4,HIGH); else  digitalWrite(LCD_D4,LOW);  
      lcd_strobe(); 
      delayMicroseconds(40);
}

/*
 *       Clear and home the LCD
 */

void lcd_clear(void)
{
      digitalWrite(LCD_RS,LOW);

      lcd_write(0x1);
      delay(2);
}

/* write a string of chars to the LCD */

void lcd_puts(const char * s)
{
    digitalWrite(LCD_RS,HIGH);      // write characters

      while(*s) lcd_write(*s++);
}

/* write one character to the LCD */

void lcd_putch(byte c)
{
    digitalWrite(LCD_RS,HIGH); // write characters

      lcd_write(c);
}


/*
 * Go to the specified position
 */

void
lcd_goto(byte pos)
{
    digitalWrite(LCD_RS,0); 

      lcd_write(0x80 + pos);
}
      
/* initialise the LCD - put into 4 bit mode */

void
lcd_init(void)
{               
  
  
  pinMode(LCD_D7,OUTPUT);            
      pinMode(LCD_D6,OUTPUT);
      pinMode(LCD_D5,OUTPUT);
      pinMode(LCD_D4,OUTPUT);
      pinMode(LCD_EN,OUTPUT);        
      pinMode(LCD_RS,OUTPUT);        
      
      digitalWrite(LCD_RS, LOW);      // write control bytes

      delay(15);// power on delay

      digitalWrite(LCD_D4, HIGH);      // init!      
      digitalWrite(LCD_D5, HIGH); //
      lcd_strobe();
      delay(5);

      lcd_strobe();// init!      
      delayMicroseconds(100);

      lcd_strobe();// init!      
      delay(5);

      digitalWrite(LCD_D4, LOW);      // set 4 bit mode
      lcd_strobe();
      delayMicroseconds(40);
      
      lcd_write(0x28);// 4 bit mode, 1/16 duty, 5x8 font, 2lines
      lcd_write(0x0C);// display on
      lcd_write(0x06);// entry mode advance cursor
      lcd_write(0x01);// clear display and reset cursor
}

void downloadcustomcharacter(int CGRAMaddress, byte character[])
{
  for (int i = 0; i<8; i++) //load character bytes in to CGRAM
  {
    lcd_putch(character[i]);  
  }
  delay(20);
}

void LCDbar127(int midiValue, short charBlock, short line)
{
  char  SmidiValue[3];
  lcd_goto(0x40*line);
   
  midiValue = constrain (midiValue, 0, 127); // limit input into 0 and 127
  int pixelPos = (midiValue  * charBlock * 5) / 127;
  int blockPos = (midiValue  * charBlock) / 127;
   
  for (short idx = 1; idx <= blockPos; idx++)
  {
    lcd_putch(5);
  }

  if (pixelPos % 5) lcd_putch(pixelPos % 5);  // display the not entire block
  blockPos++;
  
  for (short idx = blockPos ; idx < charBlock; idx++)
  {
    lcd_putch(0);
  }
 
  lcd_goto(0x40*line + charBlock);
  itoa(midiValue, SmidiValue, 10);
  if (midiValue<10) lcd_puts(" ");
  if (midiValue<100) lcd_puts(" ");
   
  lcd_puts(SmidiValue);
}

/**********************************************************************
 *
 * Main
 *
 **********************************************************************/
 
void setup() {
  delay (20);
  lcd_init();
  delay (200);
  lcd_write(0x40);
  //delay(40);
  byte barre1[] = {0x00,0x1F,0x00,0x00,0x00,0x00,0x1F,0x00}; //progress bar characters
  byte barre2[] = {0x00,0x1F,0x10,0x10,0x10,0x10,0x1F,0x00};
  byte barre3[] = {0x00,0x1F,0x18,0x18,0x18,0x18,0x1F,0x00};
  byte barre4[] = {0x00,0x1F,0x1C,0x1C,0x1C,0x1C,0x1F,0x00};
  byte barre5[] = {0x00,0x1F,0x1E,0x1E,0x1E,0x1E,0x1F,0x00};
  byte barre6[] = {0x00,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x00};
    
  downloadcustomcharacter (0 , barre1);
  downloadcustomcharacter (1 , barre2);
  downloadcustomcharacter (2 , barre3);
  downloadcustomcharacter (3 , barre4);
  downloadcustomcharacter (4 , barre5);
  downloadcustomcharacter (5 , barre6);

  Serial.begin(31250);// midi
  //Serial.begin(9600); //debug
  pinMode(2, OUTPUT);
  pinMode(3, OUTPUT);
  pinMode(4, INPUT);
  pinMode(5, INPUT);
  pinMode(6, OUTPUT);
  
  mouse_init();
  
  lcd_clear();
  lcd_goto(0);
  lcd_puts("TouchPad");
  lcd_goto(0x40);
  lcd_puts("  MIDI");
}

void loop() {
  char mstat;
  char mx;
  char my;
 
  /* get a reading from the mouse */
  mouse_write(0xeb);  /* give me data! */
  mouse_read();      /* ignore ack */

  mstat = mouse_read();
  mx = mouse_read();
  my = mouse_read();
  
  if (digitalRead(4)==HIGH)
    {
      XCC += mx;
      XCC = constrain (XCC, 0 , 127);
      lcd_goto(0);
      lcd_puts("CC# on X");
      LCDbar127(XCC, 5, 1);
    }
    else if (digitalRead(5)==HIGH)
    {
      YCC += mx;
      YCC = constrain (YCC, 0 , 127);
      lcd_goto(0);
      lcd_puts("CC# on Y");
      LCDbar127(YCC, 5, 1);
    }
  else if (mx!=0 || my!=0 )
  {
     
      midivalueX += mx;
      midivalueY += my;
    
      midivalueX = constrain (midivalueX, 0 , 127);
      midivalueY = constrain (midivalueY, 0 , 127);

      midiMsg(0xb0, XCC, midivalueX);
      midiMsg(0xb0, YCC, midivalueY);

      LCDbar127(midivalueX, 5, 0);
      LCDbar127(midivalueY, 5, 1);
    }
  
  
}

That is an awesome project! What do you planning to do with this setup?

wow, very nicely put together, looks very "CLEAN". My kind of "clean" is oversize drill holes, with a case way too big to be needed with 15 different battery packs... I gotta work on my enclosure skills...

either way, nice project, i hope you can develop this into a type of computer mouse, or music player.
Maybe it can have ipod touch like capabilities lol! good luck!

This is a beautifully executed project. Congratulations.

I'm trying to build a very similar device, though it is hard for me to understand all the code you've written. Hopefully with time, I'll understand it all. I would love to see any enhancements that you've made, as well as a few more comments. :wink:

On the hardware side, I've been working on a couple different things that might be of interest to the community. One, I've found that Microsoft Optical mice are natively capable of PS2 -- it's only necessary to wire them differently to get them to turn on in PS2 mode. This opens up the possibility of using cheap and readily available optical mice with the Arduino. Below are two crude images describing the mouse and pinout. I've tried it on several generations of MS optical mice and it worked on all of them. I've never seen this pinout anywhere else.



Additionally, I had a VERY old laptop which had a touchpad in it. The touchpad pinout took some time to discover. Perhaps it will be useful for others, so here it is:

Hi everybody, I`ve seen this project on flickr (thanks ludo for directing me here) and thought it is very well done, looks great and it seams worth building it. Congratulations!
I got no programming skills, but as code is there I think I might give it try. As your touchpad is PS2, could this touchpad from Voti can be used?
It is very cheap surplus pad costs just 4.37EUR
http://www.voti.nl/shop/p/S-Touchpad.html

Are there any wiring details for this project?
Some youtube in action would be great to see. :slight_smile:

VERRY nice! I also got into Arduino for the MIDI thing (but my aspirations were quite a bit lower).

Hi ludo

I'm trying to read out data from a Cirque 9920 touchpad.
I just saw on one of your photos, that you were using a similar type of touchpad...

What I am trying to do is to catch data from the finger position BEFORE I start moving it.
The regular PS/2 protocol gives you only delta values of the position. When I configure the pad to send automatically (stream mode) it only sends data, when a finger movement occurs.
In the (small) specification PDF for the touchpad, it sais something about an extended packet mode where I can get even the Z value (finger press). You have to change a hardware configurable option for that. On the back of the pad you find some connections you have to bridge to enable this options.
...I'm really hoping you (or someone else) know(s) what I am talking about and can give me some advice how to read out this data.

Thanks!
Marcel

Wow! This is amazing! ;D

Nice.
Am working on a similar project myself, however am running a synaptic trackpad in absolute mode, and then I can get XYZ (z is pressure) for 3 MIDI CCs. Will probably create a library for my own use (I like to keep messy hardware code separate), would anybody be interested in it?

Nice.
Am working on a similar project myself, however am running a synaptic trackpad in absolute mode, and then I can get XYZ (z is pressure) for 3 MIDI CCs. Will probably create a library for my own use (I like to keep messy hardware code separate), would anybody be interested in it?

I am absolutely interested in your code, and which trackpad part number you used. I'm also working on an enhanced version of this project, but absolute mode would be best for what I am doing (and Z could add an interesting wrinkle...)

might see if i can get it packaged up today... i'm using a PS/2 synaptic trackpad, according to the synaptic interfacing guide i found on the internet, it's an 'Ultra-thin' TouchPad TM41Pxx220 (don't have it right here, not sure what the x's are!)

I think any PS/2 synaptic will work though.

Ok, have got the code all wrapped up, would love if somebody could test? Don't know if it'll work with anything but the synaptic touchpads, unless it's all fairly standard protocol-wise...

http://www.arduino.cc/playground/Main/Trackpad

You need the PS2 library too.
http://www.arduino.cc/playground/ComponentLib/Ps2mouse

Let me know how you get on!

Great project, tried it and it work right away.
Thanks man. :wink:

Hello World!
Hello Ludo!

I'm in to build a controler like this with a x/y touchpad, a poti, two buttons but without the lcd display. :slight_smile:

My problem is i got no experience in coding for the arduino and need help to change your code to my conditions! :-/

The Poti should be connected to Analog in 1 and the Buttons to 2/3.

The Midi routing should be locked to channel1 and the following notes:

X: CC01
Y: CC03
Poti: CC05
Button 1: CC07
Button 2: CC09

I will be so thankfull if anybody can help me!

greetz chris tall

sry for my bad english i'm german :slight_smile:

Hi ChrisTall,

Would you like to send me your email address in a private message. I'll send you the modified code.
However it's pretty simple. All you have to do is to remove all LCD code and to add a POTCC in the spirit of XCC. Don't hesitate to understand how this work by navigating in this website.

Here some Pictures of my project!

The case is an old C64 Joystick!

The Synaptics Touchpad and the 10k Poti.
The blue border will be changed against a alu plate.

The inside without the taster, power led, wiring and the arduino duemilanove.

Lobo u got post!

COOOL :slight_smile: