Thursday, 5 May 2016

Scrolling text on guitar fingerboard

This was always going to be an experiment. And one that we're not entirely sure would work. But, in true Nerd Club fashion, it wouldn't be the first time we'd invested a lot of time and effort in trying something out without being sure of success from  the outset. That's probably how all the best stuff gets invented anyway. Probably.

Our light-up guitar firmware is pretty much finished. But we've still got nearly 20% programming space left on the AVR. Which means there's still time to shoe-horn in some unnecessary functionality.



So what about.. scrolling text? The idea with this was to have a scrolling message running from the head, towards the body. The text would be made up of simple bitmap glyphs 6x4 pixels wide (6x5 in the case of the letters M and W).


Each character could be described by 5 bytes but since our fingerboard is 6, not 8, LEDs high, we're only interested in the lower 6 bits of each byte.

The letter A, for example, would be described as (binary)

00011110
00101000
00101000
00011110
00000000

(if you look at this sideways, you should "see" the letter A in the positive bits of the binary values) which in turn is represented by the decimal values 30, 62, 62, 30, 0.

Then a quick bit of VB6 code "read" our font bitmap and produced array values for each letter. The final Arduino code looked a little bit like this:


const unsigned char characters[] PROGMEM =
{
   30,40,40,30,0,
   62,42,42,20,0,
   28,34,34,34,0,
   62,34,34,28,0,
   62,42,42,34,0,
   62,40,40,32,0,
   28,34,42,46,0,
   62,8,8,62,0,
   34,62,34,0,0,
   4,34,34,60,0,
   62,8,20,34,0,
   62,2,2,0,0,
   62,16,12,16,62,
   62,16,8,62,0,
   28,34,34,28,0,
   62,40,40,16,0,
   28,42,38,30,0,
   62,40,40,22,0,
   18,42,42,36,0,
   32,62,32,0,0,
   60,2,2,62,0,
   56,6,4,56,0,
   62,4,24,4,62,
   54,8,8,54,0,
   50,10,10,60,0,
   38,42,42,50,0,


   4,42,42,30,0,
   62,18,18,12,0,
   12,18,18,18,0,
   12,18,18,62,0,
   28,42,42,18,0,
   31,40,40,0,0,
   9,21,21,30,0,
   62,16,16,14,0,
   2,46,2,0,0,
   1,9,46,0,0,
   62,8,8,22,0,
   60,2,2,0,0,
   14,16,12,16,14,
   14,16,16,14,0,
   12,18,18,12,0,
   31,18,18,12,0,
   12,18,18,15,0,
   14,16,16,0,0,
   18,42,42,36,0,
   60,18,18,0,0,
   28,2,2,30,0,
   24,6,4,24,0,
   28,2,12,2,28,
   18,12,12,18,0,
   25,5,5,30,0,
   18,22,26,18,0,
   

   28,34,34,28,0,
   18,62,2,0,0,
   38,42,42,18,0,
   34,42,42,20,0,
   56,8,30,8,0,
   58,42,42,36,0,
   28,42,42,4,0,
   32,38,40,48,0,
   20,42,42,20,0,
   18,42,42,28,0,   
   6,42,46,34,28,
   0,20,0,0,0,
   2,28,32,0,0,
   0,58,58,0,0,
   8,28,28,8,0,
   2,0,0,0,0,
   0,0,0,0,0,
   0,0,0,0,0,
   0,0,0,0,0,
   0,0,0,0,0,
   0,0,0,0,0,
   0,0,0,0,0,
   0,0,0,0,0,
   0,0,0,0,0,
   0,0,0,0,0,
   0,0,0,0,0
};


Which allowed us to recall and display the appropriate pixels in a recurring function call:

// move all previous "pixels" along to the left
for (int i = 0; i < NUM_LEDS-6; i++) {
     leds[i] = leds[i+6];
}

// now decide which pattern to display on the right-most six pixels
int k=scrollText.length();
if(curr_character < k){
currLetter = scrollText.charAt(curr_character);
}else{
currLetter = " ";
}

// unless we decide otherwise, assume a blank/space character
byte b[2];
currLetter.getBytes(b,2);
int p=b[0];
k=78;

if(isUpperCase(p)){ k=p-65; }
if(isLowerCase(p)){ k=(p-97)+26; }
if(isDigit(p)){ k=(p-48)+52; }

// get the pattern for the current letter
k=(k*5) + col_count;
curr_col_pattern = pgm_read_byte_near(characters + k);

if(curr_col_pattern==0){
     curr_character++;
     col_count=0;
}else{
     col_count++;
     if(col_count>=5){
          curr_character++;
          col_count=0;
     }
}

// now put the current column pattern onto the right-most pixels
k=curr_col_pattern<<1;
k=k<<1;

for(int i=5; i>=0; i--){
     if((k & 128) > 0){
          leds[(NUM_LEDS-(5-i))-1] = scroll_text_colour;
     }else{
          leds[(NUM_LEDS-(5-i))-1] = CRGB::Black;
     }
     k=k<<1;
}

// update the LEDs
FastLED.show();


After a bit of tweaking and debugging, we finally got our text scrolling along the neck.
Here's a (very) quick demo:


it's quite difficult to read, but if you focus on the left-hand edge of the display, you might just make out the shape of the letters  as they whizz by


The only thing is.... it doesn't actually look very good.
Sure, it might look fine on a small, closely or more densely populated LED matrix. But on a guitar fingerboard, where the LEDs are spaced so far apart (particularly higher up the neck) it just looks like a jumbled mess of flashing lights.

So there we go.
Another Nerd Club fail.
Another load of wasted hours on  something that doesn't work.

In fact, that's not strictly true. It does work. Or, at least, it works - functionally - how we expected it to. It's just not really suitable for this particular task. But that doesn't make it a failure. And, indeed, while quite a few hours went into developing, debugging and making it work, they were hardly wasted. We came up with an idea, executed it, and got the LEDs to flash in the sequence we determined at the start. To come up with an idea and bring it to life is never a waste of time. In this case, it wasn't the execution, but the idea that wasn't suitable.

And at the very start, we didn't know if the idea was going to work. So we built something to test the idea. And the thing worked, and proved that the idea wasn't a very good one. So in just about every respect, this one was a resounding success. Yet, somehow, it doesn't quite feel like a success....

Then again, if you're guaranteed success before you start, you're not actually creating anything; you may as well buy and build Ikea furniture and say you made it yourself!