Grid Studies: Arduino

By design the monome grid does nothing on it's own. You the user assign it purpose and meaning: instrument, experiment, tool, toy... choose your own adventure. This grid is intended to be reimagined. Here we set forth to impart some introductory knowledge: potential energy for radical creative freedom.

Arduino is an open-source electronics platform based on easy-to-use hardware and software. It is intended for anyone making interactive projects.

We'll be using the Ardunio's USB Host capabilities to run grid code without a computer, with easy access to physical hardware: sensors, motors, other actuators, wireless bits, everything.

Prerequisites

If you're very new to Arduino, it will be very beneficial to work through the 'Getting Started' section at Arduino.cc: arduino.cc/en/Guide/HomePage

Hardware

To gain access to the USB Host capabilities, you need a strange USB cable-- micro OTG to Female A. See Digikey 839-1105-ND.

Grids also require a substantial amount of power to light so many LEDs-- more power than an Arduino can supply. The solution to this is to power the grid externally. We've made a small adapter that does this, called ext5v. You can get one at market.monome.org.

The kit includes the needed OTG-A cable and a very short USB cable to make this entire setup very tidy, as seen below.

It is incredibly important to power the small adapter with 5V and nothing else. You can fry all sorts of things otherwise. The 5V DC supply is included in the kit (not shown in the photo).

If this condition is true, we toggle the corresponding position in the step data. Notice the addition of the dirty flag.

Inside loop() we check this flag. If true, it means we need to refresh the grid display.

if(dirty) {
redraw();
monome.refresh();
dirty = false;
}

We will "build" the LED display from scratch each time, inside of redraw(). Below we simply copy the step data to the led array, doing the proper multiplication by 11 in order to get almost-full brightness:

First led_clear() completely clears the grid, and then we iterate through the array, copying step data to the grid.

After leaving redraw() we do monome.refresh() and then unset dirty.

That'll get us started.

3.2 Play

See grid-studies-3-2.ino for this step.

Now we need a clock. We'll make a quick (read: slightly sloppy) clock using millis(). First a few variables:

unsigned long t = millis();
unsigned long interval = 200;

And then inside loop():

if(millis() - t > interval) {
t = millis();
next();
}

Each time this is called, the current time is compared to the last recorded t to see if it has reached interval. If so, we reset t to the current time and call next(), which will process the next sequence step.

You can change the speed by altering the value of interval. The value is roughly the number of milliseconds in between steps-- so smaller values will make the sequencer go faster.

During this loop which copies steps to the grid, we check if we're updating a column that is the play position. If so, we increase the highlight value. By adding this value during the copy we'll get a nice effect of an overlaid translucent bar.

3.3 Triggers

See grid-studies-3-3.ino for this step.

When the playhead advances to a new row we want something to happen which corresponds to the toggled-on rows. We'll do two things: we'll show separate visual feedback on the grid in the second-to-last (trigger) row, and we'll call a trigger() function that will send serial.

First we create a dim row (level 4 is fairly dim). Then we search through the step array at the current play position, showing a bright indicator for each on state. This displays a sort of horizontal correlation of rows (or "channels") 1-6 current state.

For sending serial, we create a function trigger() which gets passed values of activated steps. This is what we do, inside next() right after we change `play_position':

Now, when pressing keys on the bottom row it will cue the next position to be played. Note that we set cutting = false after each cycle so that each press only affects the timer once.

3.5 Loop

Lastly, we'll implement setting the loop start and end points with a two-press gesture: pressing and holding the start point, and pressing an end point while still holding the first key. We'll need to add a variable to count keys held, one to track the last key pressed, and variables to store the loop positions.

byte keys_held, key_last;
byte loop_start, loop_end = 15;

We set loop_end by default to the maximum length (15).

We count keys held on the bottom row thusly:

keys_held = keys_held + (z*2) - 1;

By multiplying z by 2 and then subtracting one, we add one on a key down and subtract one on a key up.