Full-colour RGB 4x4 button pad (monome-esque)

Over at my blog I've posted a write-up of my first Arduino project. It's a 4x4 button pad, inspired by the monome using the silicon rubber part from Sparkfun, which can display fully-blended RGB colours (not just R,G,B,C,M,Y) in each button independently.

The blog entry has plenty of description and a few videos of it in action, but no code, because I'm new to Blogger and haven't figured out how to get it to display code nicely yet. So, for those who are interested, here's the generic code framework, with some Serial debugging stuff in there too. (I'm always self-conscious about showing my code, because I'm sure it could be much better, but here it is anyways):

// This is generic code for the 4x4 button pad, with separate routines for every possible event

#define DATAOUT 11//MOSI (pin 7 of AD5206)
#define DATAIN 12//MISO - not used, but part of builtin SPI
#define SPICLOCK  13//sck (pin 8 of AD5206)
#define ROWS 4
#define COLS 4

#define slavesel(x) ((x<6) ? 0 : 1)
const byte rowpin[ROWS] = {
  14,15,16,17}; // Using the analog inputs as digital pins
const byte slaveselect[2] = {
  10, 18};

// The pot register numbers for each of the red, green, and blue channels
const byte red[4] = {
  8, 7, 2, 5};
const byte green[4] = {
  10, 6, 0 , 3};
const byte blue[4] = {
  11, 9, 4, 1};
  
// Main data for the drawing routine
byte rGrid[ROWS][COLS] = {
  0};
byte gGrid[ROWS][COLS] = {
  0};
byte bGrid[ROWS][COLS] = {
  0};

const byte buttonRead[4] = {
  5, 2, 3, 4}; //Pins for the Vin of the buttons
const byte buttonWrite[4] = {
  6, 7, 8, 9}; //Pins for reading the state of the buttons
boolean pressed[ROWS][COLS] = {
  0};
  
// END DEFINITIONS, BEGIN PROGRAM

char spi_transfer(volatile char data)
{
  SPDR = data;                    // Start the transmission
  while (!(SPSR & (1<<SPIF)))     // Wait the end of the transmission
  {
  };
  return SPDR;                    // return the received byte
}

byte write_pot(byte address, byte value)
{
  digitalWrite(slaveselect[slavesel(address)], LOW);
  //2 byte opcode
  spi_transfer(address % 6);
  spi_transfer(constrain(255-value,0,255));
  digitalWrite(slaveselect[slavesel(address)], HIGH); //release chip, signal end transfer
}

void setup(){
  Serial.begin(19200);
  
  byte i;
  byte clr;
  pinMode(DATAOUT, OUTPUT);
  pinMode(DATAIN, INPUT);
  pinMode(SPICLOCK,OUTPUT);
  pinMode(slaveselect[0],OUTPUT);
  pinMode(slaveselect[1],OUTPUT);

  for(byte r = 0; r < ROWS; ++r){
    pinMode(rowpin[r], OUTPUT); // Initialize rows
    digitalWrite(rowpin[r], LOW); // Turn all rows off
  }

  digitalWrite(slaveselect[0],HIGH); //disable device
  digitalWrite(slaveselect[1],HIGH);
  // SPCR = 01010000
  //interrupt disabled,spi enabled,msb 1st,master,clk low when idle,
  //sample on leading edge of clk,system clock/4 (fastest)
  SPCR = (1<<SPE)|(1<<MSTR);
  clr=SPSR;
  clr=SPDR;
  delay(10);
  // clear all of the pot registers
  for (i=0;i<12;i++)
  {
    write_pot(i,0);
  }

  //setup the button inputs and outputs
  for(int i = 0; i < ROWS; ++i){ // ???? Is ROWS the right quanitity here?
    pinMode(buttonWrite[i], OUTPUT);
    digitalWrite(buttonWrite[i],LOW);
    pinMode(buttonRead[i], INPUT);
  }

  grid_init();

  delay(10);
}

void loop(){
  always();
  
  for(byte r = 0; r < 4; ++r){

    digitalWrite(buttonWrite[r], HIGH);
    for(byte c = 0; c < COLS; ++c){
      if(pressed[r][c] != digitalRead(buttonRead[c])){
        pressed[r][c] = digitalRead(buttonRead[c]);
        if(pressed[r][c]){
          on_press(r, c);
        } 
        else {
          on_release(r, c);
        }
      } 
      else {
        if(pressed[r][c]){
          while_pressed(r, c);
        } 
        else {
          while_released(r,c);
        }
      }

      write_pot(red[c],rGrid[c][r]);
      write_pot(green[c],gGrid[c][r]);
      write_pot(blue[c],bGrid[c][r]);

    }
    digitalWrite(buttonWrite[r], LOW);

    digitalWrite(rowpin[r], HIGH); //turn one row on
    delayMicroseconds(750); // display
    digitalWrite(rowpin[r], LOW); //turn the row back off  
  }
}

void grid_init(){
  //initialize the button grids with blank data
  for(byte x = 0; x < ROWS; ++x){
    for(byte y = 0; y < COLS; ++y){
      rGrid[x][y] = 0;
      gGrid[x][y] = 0;
      bGrid[x][y] = 0;
    } 
  } 
}

void always(){

}

void on_press(byte r, byte c){
  rgb(r, c, 0, 255, 0);
  Serial.print(r, DEC);
  Serial.print(", ");
  Serial.println(c, DEC);
}

void on_release(byte r, byte c){
  rgb(r, c, 0, 0, 0);
}

void while_pressed(byte r, byte c){

}

void while_released(byte r, byte c){

}

void rgb(byte x, byte y, byte r, byte g, byte b){
  rGrid[x][y] = r;
  gGrid[x][y] = g;
  bGrid[x][y] = b;
}

Feedback is appreciated, and for those of you who are into Processing, check out the "challenge" (which is really just me trying to get a free ride from a better Processing programmer :wink: ).

the colors are very cool, they just amaze me how many colors can come from ONE rgb led!

hows the rubber buttons from spark by the way? i was thinking about gettin em and the breakout, but i wasent sure if it was quality, or just junk

Sparkfun doesn't sell junk... IMHO

I think the buttons are very nice (although I have no basis for comparison). They're definitely not junk, though. Very good feel, and very sturdy.

:slight_smile: i like sparkfun, dont hate me

That looks great. Nice job!
:smiley:

well, it hurts me to say, i ordered all the parts to make this, and i cost me almost 100 bucks...

lol but i cant wait to get this working, it looks awsome-rific!

hopefully if i come up with problems, you can assist me in solving them...
nice project, and i cant wait to be able to make stuff!

I've designed my own board for the second version of this. It has LED drivers on-board instead of the potentiometer, which allows the LEDs to be much brighter and have a wider range of colours. It'll still be driven by the Arduino, at least for now. I'm hoping to find a few like-minded tinkerers in order to split the costs of ordering boards. I've posted more details here. I'd confirm that the board works electrically first, and then send copies out to a group of people who can all play around with it, sharing Arduino code and interfacing software.

If you're interested, please send me a message (an email to the address on that page, or a PM on this board) and I'll be in touch.

square, i am a little confused on how to get the first revision working.

ok, for the buttons, i connect the button grounds straight to the arduino, while connecting the power pins through pull down resistors? or the other way around? And laos, how do i check for button presses that way through the code?

and as for the led's;
why are they all the transistors connected to the analog pins? what is supposed to be sensed?

And finally;
how do i hook up the second digital potentiometer ic? it was VERY briefly explained, but it would be easier for me to receive a pin from the digital pot, to connect to the arduino. The other pins i got, just that one single pin that is different then the first digital pot.

thanks in advance!
good luck with the kit!

ok, for the buttons, i connect the button grounds straight to the arduino, while connecting the power pins through pull down resistors? or the other way around? And laos, how do i check for button presses that way through the code?

The button grounds go straight to the Arduino (to input pins), with a pull-down resistor. The button power pins are connected to output pins, which you bring high one at a time in sequence. You check for button presses by reading the input pins. You know which row is being brought high, and you read input off of four columns, so you know all the button presses. The sequence is: set all rows low, bring row 1 high, read all four columns; set all rows low, bring row 2 high, read all four columns; etc.

and as for the led's;
why are they all the transistors connected to the analog pins? what is supposed to be sensed?

The analog pins are being used as digital pins here. You can address them like any other digital pin, numbers 14-19. The transistors are there so that the Arduino pins don't need to source too much current.

And finally;
how do i hook up the second digital potentiometer ic? it was VERY briefly explained, but it would be easier for me to receive a pin from the digital pot, to connect to the arduino. The other pins i got, just that one single pin that is different then the first digital pot.

I'm not sure I understand the question, but you connect all of the digital pot pins on the second pot to the exact same wires as the first one, except for the slave select pin. Then you send data to one pot by setting the slave select high on one and low on the other, and vice versa to send data to the other pot. If you get the AD5204 instead of the AD5206 there's a serial out and you can daisy chain the chips, but the 5204 is only a quad potentiometer, so you'd need 3 chips instead of 2.

I hope that helps!

ok, this cleared alot up, but i have a few more questions;

resistor values seem to be different all over the board, what values did you use for the led's to light up, and what values did you use for the pull down resistors?

Now the digital potentiometers make more sense, but what pin did you connect the second slave pin?
and if i understand, your explination means that both digital pots cant be on at the same time?
So they are switch to and from very quickly im guessing.

And the code posted up top, Is it the code for the first video on the page? where the one color gets sent to all the other colors when pressed?
Also, how do i work with the code, meaning how do i make random colors appear? and how do i change individual buttons colors? The code is interesting, but very confusing, which means you did a good job shortening it! If i cant read the code, it means it's good, so don't worry, im just noobish at c... but i'm learning.

Thanks alot for the help already, and i cant wait to get this up and running hopefully tomorrow when my rgb led's get here. Stupid mail took almost 2 weeks to get them here...

Good luck with the future of this project!
-big93

hmmm, i'm also having trouble hooking up the pull down resistors, can you tell me how you hooked them up?

i connected the brown black orange resistors together, and grounded them, and i connected the red red brown resistors to arduino pins, and i connected both of the other ends together, and connected the switch pins to with the resistors:

so it was like this

Wire to arduino -----> connected to red red brown resistor-----> connected to both button ground, and the brown black orange resistor -----> connected to ground.

with the sketch above, it seems only 1 row of buttons lights up, and the last 2 rows of buttons light up when i press the buttons, and the row in between them doesent do anything...

thanks in advance

What does one do with this project besides blink the leds? I've been reading about monomes and clones for ages but everything assumes you already know what to do with it. Just to blink LEDs, wouldn't sparkfun's 8x8 RGB matrix be better?

Also, does the sparkfun bezel break into 2x2 units like the buttons and PCB do?

well, to answer your first question, i believe monomes are just a huge pad of buttons, in this case only a 4x4 that when pressed producde sounds, from that, the sounds can be manipulated, like for instance you can set different sounds to different buttons and make your own types of songs, or remixes, or any other type of thing, try youtubing "monomes" and see why the buttons are important. Thats is why simple led's don't help, you need a buttons surrounding the led's to show an indication of a change. But then again, i'm just trying to repeat this dudes first experiment, and ill go from there.

and as for the bezel, i do not believe that it breaks up into 2x2, but with a saw im sure you could work something out... or just buy your own case, and drill holes, which is what ill probably do...

hope that helped u

well, to answer your first question, i believe monomes are just a huge pad of buttons, in this case only a 4x4 that when pressed producde sounds, from that, the sounds can be manipulated, like for instance you can set different sounds to different buttons and make your own types of songs, or remixes, or any other type of thing, try youtubing "monomes" and see why the buttons are important. Thats is why simple led's don't help, you need a buttons surrounding the led's to show an indication of a change. But then again, i'm just trying to repeat this dudes first experiment, and ill go from there.

and as for the bezel, i do not believe that it breaks up into 2x2, but with a saw im sure you could work something out... or just buy your own case, and drill holes, which is what ill probably do...

hope that helped u

From your description, I still don't really understand, maybe because I'm not into music. You have samples on each button, but what do the lights do? BTW, I do understand it's a generalized keyboard, it does nothing until programmed, but I mean in use how is it used.

I know I couldn't hack the bezel without it looking like crap, same for cutting the square holes out of an enclosure. At least now I know not to order a bezel if I want 2x2 panels.

From your description, I still don't really understand, maybe because I'm not into music. You have samples on each button, but what do the lights do?

I can think of a couple of variations:

With just a single colour LED you could use it to represent a drum loop, with a lit LED under a button indicating it was "active". Press the button to turn the "beat" on/off and the light would do the same.

With a multi-colour LED you could have each colour represent a different drum sample for that beat and cycle through them with repeated button presses--with the current colour giving feedback on the current sample.

The same approach could be used for cool/warm/hot control for a set of fans or something...

--Phil.

The point of the monome is that it is a really simple, versatile interface that can be made to do whatever you like. The buttons provide input, the lights provide feedback. Musicians like it for all sorts of reasons, but of course it can be used in countless applications: to be very cheesy, your imagination is the limit.

To be honest, I have no use for it in mind personally. It's just an interesting project to work on, that combines interesting hardware and software problems.

To be honest, I have no use for it in mind personally. It's just an interesting project to work on, that combines interesting hardware and software problems.

That's pretty much where I am. It looks like an amazing and versitile idea, especially the RGB clone. Yet I can't think of a thing to do with it.

That's pretty much where I am. It looks like an amazing and versitile idea, especially the RGB clone. Yet I can't think of a thing to do with it.

Well, the PCBs for the revised RGB just arrived, so hopefully I'll come up with something soon. :slight_smile: I think the first thing I'll do is set up a monome-compatible device where it is monochrome, but the user can select the global color. That, and maybe a "monochrome" monome-compatible where the global color is constantly cycling, just because it'd look cool. After that, I need to start thinking of ways to use the color on a button-by-button basis.

im still trying to set up your first version of the project, and i cant get it working, if you could look at the posts on the last page and help me figure out whats not good, i'd be very gratful :slight_smile:

it's just that i have this huge buttons pad sitting on my desk, and it doesent seem to be working, and wires are running everywhere, and there bread boards to the waazoo, as soon as i get this running i can stick it in a nice case, and furthur program from there.

the code you gave me is so far just making rows of the same color light up except for one row which isint lighting up, and 2 rows of which only work when i press a button, and the last row, is on, but when i disconnect the button ground, then the buttons freak out for bout a second, then they all turn on... wierd, if you could put up a schematic just for how you hooked up the buttons, i think i could get this working.

thnks
-big93