Go Down

Topic: Touchpad Midi (Read 14812 times) previous topic - next topic

ludo

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.

bds

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

ludo

#2
Dec 10, 2007, 07:48 pm Last Edit: Dec 10, 2007, 07:50 pm by ludo Reason: 1
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 ?

Code: [Select]
#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);
}      


ludo

Second and last part :
Code: [Select]
/**********************************************************************
*
* 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);
   }
 
 
}

Severino

That is an awesome project!  What do you planning to do with this setup?
Mike
www.liquidware.com

big93

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!

danreetz

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. ;)

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:




Sasha

#7
Feb 19, 2008, 11:26 am Last Edit: Feb 19, 2008, 11:28 am by Sasha Reason: 1
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. :)

nomuse

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

marcel82

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

Stigsi

Wow! This is amazing! ;D

joerick

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?

bsom

Quote
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...)

joerick

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.

joerick

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!

Go Up