Introducing WIF: the WyoLum Image Format.

I love the new e-paper offered by Pervasive Displays and sold by AdaFruit.  They are a bit tricky to get started with however.  By default, they only display images, no text or standard graphics calls like “line” and “circle” and each image takes a lot of memory.  Too much for a stand alone Arduino UNO.

Even if you have the memory, another tricky thing about them is getting an image to look good in pure black and white (no greys).

At WyoLum, we have tackled all of these problems and figured out how to display UNICODE chars, and draw graphics (lines and circles).  We’ve chosen to use an SD card as both a file source and a screen buffer.  It is working well.  Most of this is just in test code state, but we figure there are others out there struggling with the same issues and might find our solutions handy.

Quick Start:

  1. Crop images with Inkscape and export as png
  2. Convert png to WIF in IMAGES directory on SD card
  3. Load AlaModeEPD on your SD extended Arduino (we use AlaMode of coarse)

Slow Start:

The subject of this post is the WyoLum Image Format, as well as how to create and display images on  repaper displays.  This binary format is nearly a direct translation of the XBM format except rather than C-code written in ASCII, it is binary.

The first two bytes are the image pixel height as an unsigned integer in “little endian” format (least significant byte first).  The next two bytes are the image pixel width as an unsigned integer again little endian.  The remaining bytes contain the pixel data, row by row.

I’ve written a little python program called greyscale.py to convert standard image files into WIF.  It is a bit of a misnomer to call it “grey scale” as each pixel is either black or white.  Greys are created with varying densities of dark pixels.  This is a non-trivial task.  I love how the Python Image Library makes this so easy.  To use it, first resize  your image with your favorite image editor to the correct size for your e-paper display:

1.44" --> W=128 and H=96 (pixels)
2.00" --> W=200 and H=96
2.70" --> W=264 and H=176

Inkscape, or Gimp work great for this. I’ll use Inkscape for this example.

Screenshot from 2013-04-29 22:00:27After starting Inkscape, open your document properties found under the file menu.  Set the width and height to the pixel size for your display in the “Custom size” box.  Now draw a hollow box the fills the entire page.  This box is used to center the area of interest in the display area.  (Note that when the box is selected you can use the text entry fields on the top labeled “W” and “H” to get the exact size on the box in pixels.  Set x and y both to 0).  If your box is filled in, you can hollow it out by clicking the “X” above “Fill” in the lower left hand corner of the window.Screenshot from 2013-04-29 22:07:40

Now find an image you’d like to convert.  I will use Lena, an image processing classic for it’s rich mixture of high and low frequency content, lights, darks and greys.  Use “File–>Import” to load your image.  Select “embed” when the dialog pops up and click “ok”.Screenshot from 2013-04-29 22:15:08

There are two things wrong here: we’ve hidden the page size and the image starts off too big.  To push the image to the bottom of the object stack, press the “end” button on your keyboard.  Then, with the aspect ratio lock closed (top center of GUI) re size the image for the display.  Make sure you move the image and not the box.Screenshot from 2013-04-29 22:20:50

Now export the page using “File–>Export Bitmap”.  Click “Page” to export only the page area, make sure the Width and Height are correct, type a filename and click “Export”.  If you use the “Browse” button, you still have to click “Export” after you have selected your filename.Screenshot from 2013-04-29 22:24:25

Now start greyscale.py using the command “python greyscale.py” on the command line or double clicking the filename.  You will need to have the Python Image Library installed in your python distribution  (try Enthought Canopy distro if you are starting from scratch for one click install).  An empty canvas should pop up.Screenshot from 2013-04-29 22:28:44

Use the “File->Open” menu command to find the file you just created.  LENA3.png in my case.  Adjust the target size from the “Size” menu.  Adjust the Contrast and Brightness sliders until you are happy with the result.Screenshot from 2013-04-29 22:32:41

Save the image in WIF using the “File–>Save” menu item.  Your files should conform to the old 8.3 dos filename format (HAPPYDAY.WIF for instance) to avoid FAT hassles.  The greyscale.py program will do its best to help you on this.  Repeat the process with as many pictures as you want.

Display the Images

Aside from the repaper breakout board, you will need an Arduino set up according to the display Pin Assignment and an SD shield (AlaMode with onboard uSD slot works great for this ;-)).  Copy all of your newly created “WIF” files into a directory called “IMAGES/” on your SD card.  And load this demo program from the Arduino environment.

T-Slot Boxmaker

t-slot-box

Laser cut boxes are pretty cool.  They can be tricky to design however.  “Maker” has taken a lot of the guesswork out of making boxes with a cool Inkscape extenstion.  Its written in Python, so Anool challenged me to add t-slots so that the resulting boxes can be held together with screws instead of glue.

It took me a while to wrap my head around the whole extension business.  It is not pretty, but I finally managed to cobble together the necessary parts to get this working.

USE IT:

  1. download code and unzip it into your Inkscape extensions directory.  On my Ubuntu laptop that is /usr/share/inkscape/extensions/.
  2. Start or Restart Inkscape.
  3. Select T-Slot Box Maker from the Extensions->Laser Tools menuScreenshot from 2013-03-27 19:35:30
  4. Insert your box measurements and screw measurements (so far only tested with 16mm M3 screws.Screenshot from 2013-03-27 19:38:27
  5. Click “apply”
  6. Remove unwanted or conflicting t-slots with the associated holeScreenshot from 2013-03-27 19:42:30
  7. Done!  Good luck!Screenshot from 2013-03-27 19:43:57

The intelligent Matrix and RowGB

Anool, Kevin, and I have been working on a cool new project based on 5050-2811 RGB pixels.  We just got the first boards back and they are amazing.

  • RowGB has 16 individualally addressable 24-bit RGB LEDs and can be chained together either horizontally or vertically to make a 2D array.
  • TiM (the intellegent matrix) is an array of 8×16 individually addressable pixels of the same ilk as RowGB.  It is essentially 8 pre-stacked RowGBs but with a very flexible control circuit that allows you to control the whole array with a single pin or up to 10 boards (1280 pixels) chained together using 8 input pins.

Now that the tests are complete, we are ready to go into production.  Please send us an email at info@wyolum.com you’d like to be updated on availability.

 

Re-Paper Experiments

I was lucky enough to get a preview of repaper.org‘s new Electronic Paper Display (also available here) with a breakout board.  It is pretty cool that e-paper has finally made its way to makers with a few other products coming out too, like the E-ink shield from Seeed Studio.

In case you don’t know, e-ink technology is enables a low-power display that can maintain its image without power indefinitely.  My Dad described it well as “like an electronic etch-a-sketch?”  If your project needs a fast refresh rate, e-ink will not do what you want.  The setup below refreshes once every couple of seconds.

The hardware is specifically designed for the TI 430 Launchpad project board, but it turned out to be easy enough wire it up to our own AlaMode with the pin assignment table provided.  It even worked the first try (a rare event for me).

The open source software works with Launchpad and Arduino.  It is in its early stages so programming is a bit of a challenge at this point.  The example code includes a demo for displaying images from EEPROM (external chip) or PROGMEM (limted space).  But I think SD really seems like the way to go here and AlaMode already has an SD slot on board so I wanted to see if I could get them working together.  It turned out to be more of a challenge than I was expecting (story of my life).

Displaying on an E-ink requires four steps: two to erase the old image and two two draw the new image. The extra steps are designed to avoid and eliminate image ghosting. A ghost image is the remnants of an old image after a new image is displayed.  This might be made easier in the future with a nice library that hides all of the details.  In the mean time, I just wanted to get the details out there.

I started poking around the header file EPD.h and found a generic reader template which turned out to be the key.  The comment is my own.

/*
* A generic reader template
*
* buffer -- write buffer for outgoing bytes
* address -- byte index, 0 to display height
* length -- number of bytes to read
*/
typedef void EPD_reader(void *buffer, uint32_t address, uint16_t length);

I wrote a function new_SD_reader() which complied with the EPD_reader template.

/*
 * SD card reader Comply with EPD_reader template
 *
 * buffer -- write buffer for outgoing bytes
 * address -- byte index, 0 to display height
 * length -- number of bytes to read
 */
void new_SD_reader(void *buffer, uint32_t address, uint16_t length){
  byte *my_buffer = (byte *)buffer;
  for(int i=0; i < length; i++){
    newFile.seek(address + i + 4);
    *(my_buffer + i) = newFile.read();
  }
}

The variable newFile is a File object opened elsewhere.  What would be handy here is an extra argument in the EPD_reader template to specify a filename.  Maybe in the next release.  As it is, I just made two similar functions for reading the new and old images.

Now can swap newFile and oldFile around in loop to change the image.

File newFile, oldFile;
char *datfilenames[2] = {"IMAGES/VENUS2.dat", "IMAGES/CAT2.dat"};
void next_image(char* fn){
  oldFile.close();
  oldFile = newFile;
  newFile = SD.open(fn);
  if(!newFile){
    Serial.println("new image not found");
    while(1){
      delay(1000);
    }
  }
}

// main loop
unsigned long int loop_count = 0;
void loop(){
...
	case 6:        // swap old image for new image
	  EPD.frame_cb_repeat(0, old_SD_reader, EPD_compensate);
	  EPD.frame_cb_repeat(0, old_SD_reader, EPD_white);
	  EPD.frame_cb_repeat(0, new_SD_reader, EPD_inverse);
	  EPD.frame_cb_repeat(0, new_SD_reader, EPD_normal);
	  // swap files
	  next_image(datfilenames[loop_count++ % 2]);
		state = 6;  // backe to picture nex time
		break;
...

The complete code can be found here.  Before you can run it, you will need to covert the xbm format to a simple byte format.  I’ve written this quick and dirty python code to do the trick.