LCD Bitmap - Updated v1.6

Arduino library that allows you to create a tiny 20x16 pixel bitmap (raster) display on a normally character-only LCD display. Typical drawing functions like line, rectangle, invert, etc. Control right down to the pixel level. Works with both the standard LiquidCrystal library as well as Francisco's New LiquidCrystal library.

Example sketchs are available on the project website and as part of the library download. Try it out and let me know what you think. I plan on adding additional features in the near-term.

LCDBitmap Library for Arduino

Here's a sample of it in action:

New in v1.6
BITMAP_RANGE_CHK bug fix.

New in v1.5
4bit method now works without the New LiquidCrystal library (was always supposed to work this way, oversight on my part). Update switch now optional on all functions, defaults to NO_UPDATE.

New in v1.4
Fixed a few problems with the barGraph function. Further optimization with the range checking. Compatible with Arduino 0023.

LCDBitmap Library v1.6 Download

Tim Eckel

I need to check this out!

Cheers

fm:
I need to check this out!

And thanks for the New LiquidCrystal library. Love it, and a shift register daughterboard for my LCD was my first Arduino hardware project. The New LiquidCrystal library makes it a piece of cake.

Tim

Glad you liked the library. I need to check out the one you've posted, I liked the video you've posted. I have a project that will surely make good use of it!
Thanks for sharing.

wel done!!

does it also work on a 4x20 display?

just waiting until someone makes (sort of) tetris with this ... (vertical way)

Hi, just wanted to make a few comments on the library and the way you use the LCD library.

I've seen that you have multiple constructors for the various LCD, however, if you change:

#ifdef _LIQUIDCRYSTAL_SR3W_H_
	LCDBitmap(LiquidCrystal_SR3W& lcd, byte bitmap_x, byte bitmap_y);
#else
	#ifdef _LIQUIDCRYSTAL_SR2W_
		LCDBitmap(LiquidCrystal_SR2W& lcd, byte bitmap_x, byte bitmap_y);
	#else
		#ifdef _LIQUIDCRYSTAL_SR_
			LCDBitmap(LiquidCrystal_SR& lcd, byte bitmap_x, byte bitmap_y);
		#else
			#ifdef LiquidCrystal_I2C_h
				LCDBitmap(LiquidCrystal_I2C& lcd, byte bitmap_x, byte bitmap_y);
			#else
				LCDBitmap(LiquidCrystal& lcd, byte bitmap_x, byte bitmap_y);
			#endif
		#endif
	#endif
#endif

and use:

LCDBitmap ( LCD *lcd, byte bitmap_x, byte bitmap_y);

and in the private section of the class definition you declare:

#include "LCD.h"

LCD *_lcd;

Modify the constructor with this:

LCDBitmap::LCDBitmap(LCD *lcd, byte x, byte y) 
{
   bitmap_x = x; 
   bitmap_y = y;
   _lcd = lcd;
}

as opposed to use each individual supported library. Your entire code could use the abstract class all the time and would be completely independent of the driver. Additionally you would have to change in your code the "lcd." for the pointer to "_lcd->" but a simple find and replace will do the trick nicely.

This is the nice thing about this library, if you use the LCD abstract class, you don't need to change the code at all to support any of its drivers, just the LCD object creation in your main program.

You would have to use some conditional compilation flags to determine if you are using the stock LiquidCrystal library or the LCD library though.

Your main program and examples would look as follows to support the I2C variant:

LiquidCrystal_I2C lcd(0x38); 
LCDBitmap bitmap(&lcd, 12, 0);

To support the SR3W variant.

LiquidCrystal_SR3W lcd(2, 3, 4); 
LCDBitmap bitmap(&lcd, 12, 0);

Tim,
I really like the library but could you check the code into the project repository so
it can be properly tracked and viewed using the SCM tools?

One thing that probably should be high on the list of future improvements
is to use a bit array or maybe even a bitmap buffer vs an array of booleans which take up a byte each.
As is, the library uses up 320 bytes of RAM for the bitmap[][] array
which when combined with the 64 bytes for chr[][] ends up being a large
percentage of the overall RAM on some AVRs.

--- bill

robtillaart:
wel done!!

does it also work on a 4x20 display?

just waiting until someone makes (sort of) tetris with this ... (vertical way)

You tell me :wink: Seriously, I only have a 2x16 display, so I can only say with authority that it works with 2x16. With that said, it's not hard-coded to work on a particular display size and should work on all sizes as long as they use the Hitachi HD44780 and each character is 5x8 pixels. Is that all of them? I would seriously be surprised if it didn't work on every display with a HD44780 controller.

Tim Eckel

Thanks fm! I wasn't aware of using the LCD abstract class which made the code a mess (but functional). I believe I've implimented your changes and will test them tonight to make sure I got everything correct. I figured there was a cleaner way of doing it, but lacked the experience to even begin to figure it out.

Thanks again!

You are very welcome, that is one of the nice things about OO programing, not too common around here though. You are doing a very nice job with the library. Very cool.

bperrybap:
I really like the library but could you check the code into the project repository so
it can be properly tracked and viewed using the SCM tools?

Maybe a little outside my experience level. I believe I have everything turned on in Google Project Hosting. I'm a software developer, but have never used an online code repository, or even C for at least 25 years. So, if you'd like me to do something specific, I may need a little hand-holding because this is all new to me.

I just got an Arduino a week or so ago with the purpose of getting my 16 year old son interested in something other than video games (his birthday is today actually).

bperrybap:
One thing that probably should be high on the list of future improvements
is to use a bit array or maybe even a bitmap buffer vs an array of booleans which take up a byte each.
As is, the library uses up 320 bytes of RAM for the bitmap[][] array
which when combined with the 64 bytes for chr[][] ends up being a large
percentage of the overall RAM on some AVRs.

For sure, that is high on my list. I actually started the library that way, but figured I'd go the easy route to test the concept first. Optimization of memory and speed have not been considered at all at this point. I wanted to get it out there to see if anyone else was interested in something like this first. If so, then I'd deal with those issues. I haven't completed the functions either. Missing are circle, oval, floodfill, etc.

Tim

I just got an Arduino a week or so ago with the purpose of getting my 16 year old son interested in something other than video games (his birthday is today actually).

Shouldn't he be playing with the board instead of you? :wink:

Let me know if it works, my kids seam to have the same video game habit.

Version 1.1 released

With the help of Francisco Malpartida, the library was cleaned up and now more easily supports either the standard LiquidCrystal library or the New LiquidCrystal library.

The only change to sketches would be to change one line, from:

LCDBitmap bitmap(lcd, 12, 0);

to this:

LCDBitmap bitmap(&lcd, 12, 0);

Basically, just add "&" to the front of the lcd class. Let me know if there's any problem, suggestions, etc.

LCDBitmap Library v1.1

Tim

Just downloaded the library and it looks great. Nice job there and thanks for the comments. I have taken it for a spin on my shift register backpack and it is very speedy.

I would do a little bit of range checking to avoid divisions by zero (in the line method) and modify the magic numbers that you have about the code by defines, #define BITMAP_X_SZ  20. Some may have to go into the header file others can remain private in the .cpp.

Being picky and to make the library available to the public, it would be good to comment the code a bit. It would also help people that wish to increase the functionality or improve it.

The last comment is similar to that from bill, but I guess that is already on your radar right now.

It would be good not only to publish the released versions in a zip file in google code but also the code its self. The option you have chosen to manage the source code in the repo is mercurial. If you are interested in seeing what it does and how to work with it, go to this online tutorial http://mercurial.selenic.com/wiki/UnderstandingMercurial, http://mercurial.selenic.com/wiki/Tutorial and if you want to dig in a bit further: Mercurial: The Definitive Guide. All of them are very easy to read.

With this, you will be able to synchronize your local repository with that in google code. You will have a repo version on your local machine and another one on google code. If you want me to give you a hand on setting it up, send me an email.

For UI if you are on Windows I recommend tortoisehg, if you are on a Mac: MacHg (very easy to use and intuitive) or SourceTree (ditto). If you are from the old school, you can always use the command line interface.

In a nutshell, I like it very much an excellent compliment for the LCD.

Range checking was not done to make it faster and smaller. What I figured is with only 20x16 pixels, it should be kind of hard to get too crazy anyway. But, if range checking doesn't add much to the code, it would be a simple process to add.

fm:
modify the magic numbers that you have about the code by defines, #define BITMAP_X_SZ 20

I don't exactly follow what you mean by this. Do you mean don't use 20, 16 and 8, etc. as they repeat in many places but instead use a define instead? I assume the reason is for memory or speed for micro controllers? I've spent almost 100% of my programming life on fast servers and mainframes with plenty of memory, so saving a byte or cycle here or there was never a concern. But, it's obviously very different on this platform. Tips on saving memory and processing cycles is welcomely received.

Years of being the only programmer on projects had resulted in poor comment skills. I typically don't add comments unless it's for me to remember how I did something. It's hard for me to even read code with comments in it. I typically remove comments from other people's code so I can see what is going on. With that said, yes, I probably need some comments here and there.

I downloaded and installed tortoisehg last night but haven't had a chance yet to look at it. I sure hope it doesn't update the code on the site till I tell it to because I'm one to change source 500 times a day, delete entire pieces of code, rewrite it a different way, then erase everything and go back the way it was.

teckel:
Range checking was not done to make it faster and smaller. What I figured is with only 20x16 pixels, it should be kind of hard to get too crazy anyway. But, if range checking doesn't add much to the code, it would be a simple process to add.

That shouldn't add to much code, the only place that is needed is to avoid a division by zero which would cause the AVR to crash.

This is kind off in the line method, a simple if ( ( x2 - x1 ) == 0 ) to avoid the division by 0. One thing that strikes me is why do you use a float in the line method? You should be able to get away with just using a uint8_t or byte and would avoid having to pull in the floating point library.

fm:
modify the magic numbers that you have about the code by defines, #define BITMAP_X_SZ 20

I don't exactly follow what you mean by this. Do you mean don't use 20, 16 and 8, etc. as they repeat in many places but instead use a define instead? I assume the reason is for memory or speed for micro controllers?

This is a bit more for clarity, readability and maintainability. It will not add any speed improvements, nor additional code but it will make things a bit easier to follow. For example:

byte chr[8][8];

by

#define USER_DEF_CHARS    8   // Number of LCD user defined characters
#define USER_CHAR_LEN     8   // Bitmap length for each LCD user defined character

byte chr[USER_DEF_CHARS][USER_CHAR_LEN];
...

for (byte c=0; c<USER_DEF_CHARS; c++)
{ 
   for (byte a=0; a<USER_CHAR_LEN; a++) 
   {
      chr[c][a]=OFF;
   }
}

As I said, just being picky here.

Years of being the only programmer on projects had resulted in poor comment skills. I typically don't add comments unless it's for me to remember how I did something. It's hard for me to even read code with comments in it. I typically remove comments from other people's code so I can see what is going on. With that said, yes, I probably need some comments here and there.

I guess it is to help future users more than anything.

I downloaded and installed tortoisehg last night but haven't had a chance yet to look at it. I sure hope it doesn't update the code on the site till I tell it to because I'm one to change source 500 times a day, delete entire pieces of code, rewrite it a different way, then erase everything and go back the way it was.

Nothing gets pushed to the repo unless you tell it to. The nice thing is that you can work on a file or two and if you don't want to commit the changes you can alway go back to the previous version. I think is a nice thing to use even for home hobby project.

The only optimization right now, is to save a bit on memory by having a true byte bitmap, nothing much.

I would remove from the examples, or from the library begin method, the duplicate, lcd.clear, ... In any case, when the LCD is initialized the screen is cleared, with no cursor, and with no blinking either. But that is not any performance improvement in the sense that the it is only done during initialization.

As an update, I have tried it on my LCD I2C backpack, it is not as speedy but the animations work great.

I'll try range checking to see what kind of hit it takes.

fm:
One thing that strikes me is why do you use a float in the line method?

Float is used to make a diagnal line. If you dont use a float, the line won't be drawn on the correct path. Because the pixels coordinates are so small I could use a different technique while still staying with a single byte variable. I'll try it out and see how it works.

While adding comments I'll change the "magic numbers" to named constants for clarification. Never heard the term "magic numbers" before, but I now get your meaning.

Your other suggestions are also being addressed. Thanks!

Tim

You could try the DrawLIne() function from the glcd library.
It is in the glcd.cpp module.
http://code.google.com/p/glcd-arduino/source/browse/trunk/glcd/glcd.cpp

Just modify the code to use your bitmap[][] array instead of calling SetDot()

--- bill

Update:

Curiosity got the best of me.
The glcd library DrawLine() function drops in and seems to work ok (at least for the demo code)
to replace the line() function.
It saves around 1600 bytes as it does not use floating point.
Tim, if you want it, PM me your email address and I'll email you code.

--- bill

Float is used to make a diagnal line. If you dont use a float, the line won't be drawn on the correct path. Because the pixels coordinates are so small I could use a different technique while still staying with a single byte variable. I'll try it out and see how it works.

check - Bresenham's line algorithm - Wikipedia

robtillaart:

Float is used to make a diagnal line. If you dont use a float, the line won't be drawn on the correct path. Because the pixels coordinates are so small I could use a different technique while still staying with a single byte variable. I'll try it out and see how it works.

check - Bresenham's line algorithm - Wikipedia

That is the algorithm the glcd library uses for its DrawLine() function, which is what I pulled into
the bitmap library. It saves 1600 bytes of code since it does not use floats.

--- bill