Introduction: LCD Magic

About: Happiness is invisible? Then you cannot sell it? You cannot copy it nor like it:-) You have to arrive there yourself??? But i am just a consumer! See Baudrillard, La societe de consommation, p60....
Liquid Crystal Displays... very useful no doubt, but for also a little bit boring. One font, one format for the 5 x 8 pixel characters.

I have made some objects with more and different LCD's but was not really satisfied with the effect:
http://myfablab.wordpress.com/2012/06/10/prepairin...

I came across a cheap 4 x 16 display and started playing with it.
This instructable is sharing these coding experiments. Most of the content will be programming visual effects for small screens.

In the end I wrapped the ideas inside a cloth and send this omiyage to some friends in Berlin, see step "results".

Some of the coding is rather specific for the 4 x 16 display, but most ideas like the random waves and the buffer can also be used with other LCD's.

I started with the special characters, the Arduino Liquid Crystal library script "CustomCharacters". It is funny to see a moving character but this single character (5 by 8 pixels) remains so small. Making more of this jumping figures? But you have only 8 special characters to play with.

By experimenting I developed 5 ideas which might inspire others to experiment and invent:
  • random waves of special characters
  • a "buffer"
  • "bigger fonts"
  • crude geometrical shapes like an ellips, or a sine wave
  • falling snow

Random Waves
you can change the 8 special characters on the fly inside a script. Redefining the character makes all the positions with this character change at the same time, this creates wave like effects over the screen.

Buffer
With the buffer you can make more effects than just the shifting (scrolling) left or right. In fact because you have the text inside an array you can choose which row to put on which line (scrolling up and down) or "random".
Also, you can make a nice transition by randomly replacing the characters of the screen by the characters of the buffer.

Bigger fonts
Making letters from bigger blocks of pixels creates a word the size of the screen. Alternating this with "normal" texts makes the LCD much more interesting.

Geometrical shapes
By first calculating a shape, then rounding off and putting a character on that spot, you can create crude "bigger" shapes. It "just" works on this lcd with a letter resolution of 4 by 16: a sort of ellipse as a frame around a text for example.

Snow
Using a sequence (horizontal or vertical) of special characters and using the sequence (say 4) as a continuous array, you can "drop" a simple small shape, square of circle, continuously.

These are some ideas that are still possible with this simple LCD. Of course you can go on to the graphical screens where all the pixels can be used and the lines between the character spots doesn't exist. But sometimes it is fun to explore what you can still do in more restricted situations...
Explore!

Step 1: Making Your Own Characters

Making your own characters is fun and the custom character example in the Arduino LCD library explains it all.

There is also this instructable about the LCD explaining more about the special characters:
https://www.instructables.com/id/Controlling-a-char...

Issue
In the current Liquid Crystal library (2014, Arduino 1.0.5)
lcd.write(0); //writing the first special character
gives an error:
CustomCharacter.ino: In function 'void setup()':
CustomCharacter:115: error: call of overloaded 'write(int)' is ambiguous /Applications/Arduino 3.app/Contents/Resources/Java/libraries/LiquidCrystal/LiquidCrystal.h:82: note: candidates are: virtual size_t LiquidCrystal::write(uint8_t) /Applications/Arduino 3.app/Contents/Resources/Java/hardware/arduino/cores/arduino/Print.h:49: note: size_t Print::write(const char*)
You can make the custom character but not show it?
It turns out that you can print this first special characters also like:
lcd.print( char(0) );
Then you can get your special first character without an error.

Inspecting
If you look at the special characters using a loop:

  for (int i = 0; i< 16; i++)
   lcd.print (char(i));
You see that the special characters are two times displayed (in the first 16 memory slots)...I am still wondering if there are ways to get more out of this....


I wanted more visual effects, transitions and started making random characters on the fly like this:

Random
Two variations:
  • one random pixel at each of the 8 lines (less dense),
  • totally random numbers on each line (density 50%)
for ( int q = 0; q < 8; q++)    
{
	if( g%2 == 0 )
      		t6[q] = random(8)<<2; //variation 1
    	else
      		t6[q] = random(64);//variation 2
  	lcd.createChar(g%7 + 1, t6);
  g++;
}
t6[] is an 5 x 8 array, like this one, which is made explicitly further in the script "by code".

byte t6[8] = {//all white
    0b00000,
    0b00000,
    0b00000,
    0b00000,
    0b00000,
    0b00000,
    0b00000,
    0b00000
  };

There is a third variation tempting: inserting bigger "blocks" inside these arrays to obtain the random effect on a more "bigger" format.

Bigger Special Characters

By making a block of say 2 x 4 special characters (this one gives 16 x 20 pixels), you can make a bigger "drawing". You have to brake down the drawing to fit in the different single special characters and this can be tedious.
I prepare this "image" in Photoshop using an image of 16 x 20 pixels. Either you can recode this to 8 special characters by hand or use a Processing sketch for this which scans an image and has as output the bytes in the format of the arrays. You can copy paste this code into the Arduino code directly.



Step 2: Using a Screen Buffer

The standard effects are limited. Of course you can expand the library code. Using the buffer inside your script is most clear and invites for more experiments.

Just initialize a 4 by 17 array:

char screen1[4][17] = {
  "                ", "                ", "                ", "                " };

Why 17 instead of 16? Because the char aray needs a so called terminator.

Be careful to change one char inside the screen like this:
screen1[0][c2] = '?';//this between ''is a char
//and not like this:
screen1[0][c2] = "?";//wrong: this between "" is a string, not a char
Because this is a char array. The last line tries to fit a string inside this char, resulting in an error. (A string is a char with a terminator char, so at least 2 c hars long.)

Filling the buffer is done with a customized stringCopy function:
We need to fill the buffer lines at specific positions, so we could not use strCopy.

unsigned char myStrCopy( unsigned char startAddress,  char *arraySource,  char *arrayDest, unsigned char arrayLength){
  unsigned char counter = 0;
  while ( counter < arrayLength){
    arrayDest[ startAddress + counter ] = arraySource [ counter ];
    counter++;
  }
  return startAddress + counter;
}
This "copy code" can be made more C looking using pointers, but than you have to understand more about pointers and chars. You have to use the pointer in the parameters for the arraySource, and arrayDest. This then works without making copies of the arays and having to return the changed arrays. It is a bit like working with globals.

To have a nice transition effect the content of the buffer is transferred to the screen in a random way, creating a bit of curiosity for the public. The message is revealed, rather than thrown on the screen.

void bufferToScreen(){
  int cc = 0;
  //255 times random writing of the characters of the buffer
  while ( cc< 255 ){
    int hx = random(4);
    int hy = random (16);
    int hpx = 0;//there is a function for this later on
    int hpy = 0;
    if ( hx == 1 || hx == 3)
      hpx = 1; 
    if ( hx > 1 )
      hpy = 16;
    lcd.setCursor(hy+hpy, hpx);
    lcd.print( screen1[hx][hy] );
    delay(10);
    cc++;
  }
  delay(100);
  //copy all to be sure all is shown
  lcd.setCursor(0, 0); 
  lcd.print( screen1[0] );
  lcd.setCursor(0, 1); 
  lcd.print( screen1[1] );
  lcd.setCursor(16, 0); 
  lcd.print( screen1[2] );
  lcd.setCursor(16, 1); 
  lcd.print( screen1[3] );
}

With your text in the buffer, you can invent many more effects.
At the moment, the buffer does not contain the special characters.

Step 3: Bigger Fonts

By using the special characters you can make bigger fonts on the screen.
Bigger is using the slots as a 5 x 8 pixel.
The basic building block is just a special character that is all black and one that is all white.

In Photoshop the chars are prepared. See pictures.

(Suggestion for exploring: can we use the " " or space for the all white? This would save us defining a special character. How can we call a normal character like a special character? Answer: lcd.print(char(32)); )

Further building blocks are necessary because we have only 4 lines on the LCD. So a "H" or an "S" need a special "half" building block, upper and lower.

The script is big, but very clear, you start with defing the buidling blocks.
all like:
byte b2[8] = {lower half
    0b00000,
    0b00000,
    0b00000,
    0b00000,
    0b11111,
    0b11111,
    0b11111,
    0b11111
  };
  lcd.createChar(1, b1);

After that you make an array ( 4 by 16 ) and define in this array the numbers for your building blocks.
example:
char beam[4][17] = { // word "BEAM" which should be changed: first screen shown once (fits on one screen)
    "1140111011101461", 
    "1210120010101751", 
    "1310130011101001", 
    "1150111010101001"            };
Then you sample this array and write the special characters to the screen.

Creativity is needed to have enough building blocks inside your possible 8 special characters!
So at one time, you don't have a fancy all 26 letters of the alphabet available.

void patches(){
  lcd.setCursor(0,0);
  //
  // creating the parts of the "big" letters
  //
  byte b1[8] = { //all black ----->> can we save this slot? I don't think so....
    0b11111,
    0b11111,
    0b11111,
    0b11111,
    0b11111,
    0b11111,
    0b11111,
    0b11111
  };
  byte b0[8] = {//all white ----->> save a custom char by using char(32) white space
    0b00000,
    0b00000,
    0b00000,
    0b00000,
    0b00000,
    0b00000,
    0b00000,
    0b00000
  };
  byte b3[8] = {upper half
    0b11111,
    0b11111,
    0b11111,
    0b11111,
    0b00000,
    0b00000,
    0b00000,
    0b00000
  };
  byte b2[8] = {lower half
    0b00000,
    0b00000,
    0b00000,
    0b00000,
    0b11111,
    0b11111,
    0b11111,
    0b11111
  };
  //
  // this is all we need for the "PATCHES" text
  //
  lcd.createChar(1, b1);
  lcd.createChar(2, b0);
  lcd.createChar(3, b3);
  lcd.createChar(4, b2); 
  //
  // for the BEAM text we need some more special characters
  //
  byte b4[8] = {
    0b11000,
    0b11000,
    0b11000,
    0b11000,
    0b11111,
    0b11111,
    0b11111,
    0b11111
  };
  byte b5[8] = {
    0b11111,
    0b11111,
    0b11111,
    0b11111,
    0b11100,
    0b11100,
    0b11100,
    0b11100
  };
  byte b6[8] = {
    0b00011,
    0b00011,
    0b00011,
    0b00011,
    0b11111,
    0b11111,
    0b11111,
    0b11111
  };
  byte b7[8] = {
    0b11111,
    0b11111,
    0b11111,
    0b11111,
    0b00111,
    0b00111,
    0b00111,
    0b00111
  };
  lcd.createChar(5, b4);
  lcd.createChar(6, b5);
  lcd.createChar(7, b6);
  lcd.createChar(8, b7);
  //
  // coding of the first title
  //
  char beam[4][17] = { // word "BEAM" which should be changed: first screen shown once (fits on one screen)
    "1140111011101461", 
    "1210120010101751", 
    "1310130011101001", 
    "1150111010101001"            };
  //
  //write word "beam"
  //
  for ( int j = 0; j < 4; j++ )
  {
    for ( int i = 0; i < 17; i++ )
    {
      setCursorFor16_4(j,i);
      writeCharForBigWords(i, beam[j] );
    }
  }
  delay(2000);
  //the word patches in code
  unsigned char nextAddress = 0;
  nextAddress = myStrCopy( 2, "proudly        ", screen1[1], 14 );
  nextAddress = myStrCopy( 2, "presents       ", screen1[2], 14 );
  bufferToScreen();
  delay(2000);
  //
  // coding of the second title
  //
  char patches[4][28] = {  // word "PATCHES" which should be changed: in first screen shown once - in two halves
    "111011101110111010101110111", 
    "101010100100100012101200120", 
    "111011100100100013101300031", 
    "100010100100111010101110111"                  };
  //
  //the word "patcees" does not fit on this screen, so:
  //
  //write first half of word "patches"
  int cc = 0;
  while ( cc++ <  4 ){
    for ( int j = 0; j < 4; j++ )
    {
      for ( int i = 0; i < 16; i++ )
      {
        setCursorFor16_4(j,i);
        writeCharForBigWords(i, patches[j] );
      }
    }
    delay(2000 -cc*400 );
    //write second half of word "patches"
    for ( int j = 0; j < 4; j++ )
    {
      for ( int i = 0; i < 16; i++ )
      {
        setCursorFor16_4(j,i);
        writeCharForBigWords(i+11, patches[j] );
      }
    }
    delay(2000 -cc*400);
  }
}


Step 4: Crude Geometrical Shapes

With a sin and a cosine you can make a circle or an ellipse in the usual way:
    int xp = 16*sin(counter);
    int yp = 4*cosine(counter);
For a chip like this a sin lookup table is used. Finding the sine is just getting a value from a table:
(where the cosine is just a sine with a phase shift.)
    int xp = 16*wavetable[i2]/256;<br>    int yp = 4*wavetable[(i2 + 64)%255]/256;
Of course on a small screen like this there is not much of an ellipse. It is more to have a convenient way of making a frame around some text in the middle.
You can generate your own sine lookup table, or search for one on the internet, I borrowed:
http://interface.khm.de/index.php/lab/experiments/...




Step 5: Animated Snow Flakes

By using a sequence of 4 special characters and changing the content of the characters within the code in a continous way, yu can make some small drawing (snow flake) fall down:

building blocks of the code:
creating an array of 32 bytes,
making a flake of 4 bytes
cleaning the 32 array
making a loop of falling
inserting the flake into the 32 array
breaking up of this 32 array for the 4 special characters
writing the characters to the screen

it really falls down!
void snow(){
  byte snow[32];
  byte flake[4] = {
    0b01010,
    0b11111,
    0b11111,
    0b01010
  };
  for ( int falling = 0 ; falling < 32; falling++){ //insert the flake, make it start at -4 
    for (int i = -4 ; i<36; i++) //not 0 because of the  lcd.createChar(0, ) issue
      snow[i] = 0b00000;
    for (int j = 0 ; j<4 ; j++)
      if( ((falling + j) > -1) && ((falling + j)< 32))  
        snow[falling+j] = flake[j];
    //making four special characters
    for (int i = 1 ; i<5 ; i++) //not 0 because of the  lcd.createChar(0, ) issue
    {
      byte s1[8];
      for (int j = 0 ; j<8 ; j++)
        s1[j] = snow[(i-1)*8+j];
      lcd.createChar(i, s1);
      //make snowflakes appear on the screen
      setCursorFor16_4(i-1, 0);
      lcd.write(i);
    }
    delay(100);
  }
}
Only the SetCursor function is specific to this 16 x 4 LCD: (can be changed for other screens)
void setCursorFor16_4(int j, int i){
  if ( (j>-1)&&(j<4)&&(i>-1)&&(i<16)){
    int hpx = 0;
    int hpy = 0;
    if ( j == 1 || j == 3)
      hpx = 1; 
    if ( j > 1 )
      hpy = 16;
    lcd.setCursor(i+hpy, hpx);
  }
}
If you want more flakes, just copy the
//using the trick of rotating the chars to get another position<br>      setCursorFor16_4(i-1, 0) ; 
      lcd.write(i);
      setCursorFor16_4((i-1 +4 -3 )%4, 3) ; 
      lcd.write(i);
      setCursorFor16_4((i-1 +4 +2 )%4, 5) ; 
      lcd.write(i);
      setCursorFor16_4((i-1 +4 +1 )%4, 7) ; 
      lcd.write(i);
      setCursorFor16_4((i-1 +4 -1 )%4, 8) ; 
      lcd.write(i);
      setCursorFor16_4((i-1 +4 -4 )%4, 12) ; 
      lcd.write(i);
for other locations.
I am using the modulo to be sure that it will end up in position 0,1,2,3. The +4 is making sure the result is positive.

The flake is inserted from the top, so for the flakes having another position, this creates a small jump in the animation.


Step 6: Result and Download

I did the testing with an Arduino, breadboard and the LCD, see picture. Later on I copied the script to an Arduino Pro Mini (see next step).

A Movie of the "Patches" idea can be found here:

An Arduino script to scan all the characters inside the HD44780 with a potentiometer:
The scripts can be found here:
https://github.com/contrechoc/LCD_magic

Important is the Liquid Crystal Library I used, because this library, as the Arduino software environment will be updated. The library I used (with the issue of lcd.createChar(0, ) ) is included in the GITHUB repository.

Step 7: Example Application

I used these experiments for making a "Happy New Year Object". This object should have a "soft" wearable look, so a piece of cloth is used to wrap it into. On the outside there is a button (of metal). Attaching the two parts is the switch.

I use an arduino pro mini:
http://dx.com/p/arduino-pro-mini-microcontroller-c...
actually cheaper than buying an ATmega328 chip (bare bones).
just a small potentiometer for the contrast of the lcd
and a lipo for the energy.

Change the text, make a fancy object of it and you can send it to a friend too!

My idea is that they can recycle the parts and make something different from it. That is meant with the screen "Recycle me!", in step "Bigger Fonts"

Step 8: Alternatives

LCD screens come in various sizes. Colors can be green, blue, black. Some have backlights, which can be used in some unexpected way (or just by night).

If you are bored with these screens you can also use the graphical LCD screens.

Another kind of "crude" display is the LED matrix. The 8 x 8, but also the bigger ones, like 16 x 16, or even 32 x 32 are inspiring, because you really have to search what is still possible for fonts, or drawings in these width and height - very little resolution situation.

One example is the 32 x 32 LED matrix display, which I programmed, containing a primitive "movie". I used a Processing sketch to preview the effect in pixels, and which images are still recognizable in such a low resolution situation.

The Nokia display on a shield for Arduino
http://imall.iteadstudio.com/display/graphic-lcm/i...
you can push the buttons and letters appear...over the whole screen...only 16 of the whole alphabet? We have to explore hacking this display.

Graphical displays.
With these displays you have full control over the pixels. It is slower than the LCD's. There are two colored displays and RGB displays, of course some even with touch.
The 2 colored graphical board from Jeelabs.
http://www.digitalsmarties.net/products/graphics-b...

The RGB display from ITEAD:
http://imall.iteadstudio.com/display/tft-lcm.html
This has all colors. It comes at a shield, which makes it "thick" for inserting in wearables.
It comes at 240 x 300 pixels, which gives really sharp small text.
With this resolution and colors, the magic of the LCD seems primitive but in fact, you are entering another domain of challenges, other laws of design.

Knitting and resolution.
Even more bold a jump, but comparable in the sense of resolution is making images or text on a knitter, like the Brother. Here too, you have the crude image screen of needles and tours, being your width and height.
The pixel isn't even a square, but a V like shape.
http://myfablab.wordpress.com/2013/02/04/design-pr...
This knitted example is an e-scarf with a pollution sensor, made together with http://by-wire.net/. In this medium you have the problem of the loose thread at the back, so we introduced diagonals to avoid wires at the back over the whole width. Every medium has its peculiarities!

Step 9: Links and References