The Sketchduino Project
An Automated Etch-A-Sketch

Questions or comments, please email joetcochran AT gmail DOT com.

I was on my way to Las Vegas to attend CES 2009, and while on the plane, I was reading an electronic version of MAKE magazine. An article on a project called the drawbot caught my attention. The drawbot is a robot that takes a bitmap image as input, and plots out the image on a piece of paper. Earlier in the week I had read an article about a project called the Electr-O-Sketch. It was done by a couple of Cornell students, who created a way to control an Etch-A-Sketch with a serial mouse.

It occurred to me that some concepts from both of these projects could be combined. Due to the sheer volume of fun-looking arduino-based projects on instructables.com, I had recently purchased an Arduino Duemilanove and was looking for my first project, and this seemed like a great candidate.

After returning home, I started investigating how to pull this project off. As a software developer with extremely little mechanical and electrical engineering background, I was a little apprehensive about those aspects of the project, but there seemed to be a wealth of information available on the tubes.

PC Software

The first challenge that I felt comfortable tackling was the software which would accept a black-and-white bitmap image and convert it into a continuous line drawing. Initially, it seemed to me that there ought to be a graph algorithm out there that would do what I needed, and I could just copy the codebase and tweak it to my needs.

It turns out that the graph algorithm best suited for this problem is the Traveling Salesman Problem. The downside is that the Traveling Saleman Problem is an NP-Complete algorithm, which Wikipedia will tell you:

"...the time required to solve the problem ... increases very quickly as the size of the problem grows. As a result, the time required to solve even moderately large versions of many of these problems easily reaches into the billions or trillions of years, using any amount of computing power available today."

Bummer. Since I would have potentially tens of thousands of nodes (pixels) that I would need to visit (paint), and slightly less then trillions of years to sit and wait for my laptop to determine the optimal path, I'd have to come up with something else.

I coded a Windows Forms application in VB.NET which would load a bitmap into memory and create a 2-dimensional array to track which pixels need to be painted. Then I made a virtual Etch-A-Sketch in the app, which would track the "pen" position and respond to basic movement commands (up/down/left/right) by drawing a line on the screen, and in turn marking the coordinate in the 2-dimenstional array as having been painted.

I wrote my own logic to find the nearest unpainted pixel to the current pen position. But, since this was going to be used on an Etch-A-Sketch, I didn't have the luxury of issuing "pen up"/"pen down" commands a la LOGO. Remember LOGO? Ah, the good old days. Where was I? Oh yeah....So, I couldn't do "pen up"/"pen down" which meant I had to also find a way to minimize the interference, or the number of pixels painted that shouldn't be painted. See below for an illustration of this.

Consider this example:

In a sense, we're kind of screwed with an image like this because we have 3 seperate islands of pixels we need to paint. Some interference here is unavoidable. After the dog is completely filled in, we have no choice but to send the pen over top of some pixels that shouldn't be painted, simply to get to the other parts of the drawing.

Let's say for example, we draw the dog first, and our pen ends up on the tip of the dog's nose. To keep the image interference to a minimum, we need to move the pen to the leftmost point of the star, not to the leaf. See below.

Good Dog!                            Bad Dog!

Arduino Software

The arduino accepts data over a serial port (vis USB) and the program on the arduino (called a "sketch") can respond to these bytes of data by turning on and off different ports on the arduino itself.

The sketch I made was very simple. It constantly listens on the serial port waiting for incoming data, and when it receives an "A", "B", "C", or "D" on the serial port, it sends a HIGH signal to an output port (8,9,10, or 11, depending on what letter was received) immediately followed by a LOW signal.

I thought this sketch would be a good proof-of-concept to make sure that the arduino was passing the signals through correctly.


Putting the Software Components Together

Once I felt comfortable with the software components, I put them together as seen in the ultra-high quality video demonstration below. At the time, I did not yet have stepper motors so I used 4 different color LED's to represent the 4 different signals that the PC Software could send to the Arduino.




Steppers

After getting the software working, I was pretty excited about the idea coming to fruition. I ordered 2 stepper motors from eBay, some couplers from Jameco, and got an Etch-A-Sketch from Target. I felt like stepper motors would be a better solution than servo motors because of the way stepper motors have discrete and precise ranges of movement. In my mind, one step could be made equivalent to one pixel. And since my software was measuring movement in pixels, it seemed like a natural fit.

The stepper motors arrived and somehow I was able to make sense of the data sheet. The stepper motors I ordered had 6 wires/pins. 4 of the wires control the actual movement of the motor (red, blue, green, black) and 2 of the wires are related to powering the motor (white, yellow). So each motor actually needed 4 output pins. No problem, the arduino supported it, I would just have to cut some more jumper wire for my breadboard.

I learned that in order to move the stepper motor, you have to send voltage to the red/blue/green/black wires in a specific order (as found on the data sheet), and to reverse directions, you simply reverse that order.

To verify that the stepper motors were working, and that I understood the stepping sequence correctly, I wrote an arduino sketch to roll the motors around and around:



/*
* MotorKnobJC
*/

int redX = 8;
int blueX = 9;
int blackX = 10;
int greenX = 11;

int redY = 4;
int blueY = 5;
int blackY = 6;
int greenY = 7;
int DELAY_INT = 10;

void setup()
{

pinMode(redX, OUTPUT);
 pinMode(blueX, OUTPUT); 
pinMode(blackX, OUTPUT);
pinMode(greenX, OUTPUT);
pinMode(redY, OUTPUT);
pinMode(blueY, OUTPUT);
pinMode(blackY, OUTPUT);
pinMode(greenY, OUTPUT);
}

void loop()
{
digitalWrite(redX, HIGH);
digitalWrite(blueX, LOW);
digitalWrite(blackX, LOW);
digitalWrite(greenX, HIGH);
digitalWrite(redY, HIGH);
digitalWrite(blueY, LOW);
digitalWrite(blackY, LO
W);
digitalWrite(greenY, HIGH);

delay(DELAY_INT);

digitalWrite(redX, LOW);
digitalWrite(blueX, HIGH);
digitalWrite(blackX, LOW);
digitalWrite(greenX, HIGH);
digitalWrite(redY, LOW);
digitalWrite(blueY, HIGH);
digitalWrite(blackY, LOW);
digitalWrite(greenY, HIGH);
delay(DELAY_INT);

digitalWrite(redX, LOW);
 digitalWrite(blueX, HIGH);
digitalWrite(blackX, HIGH);
digitalWrite(greenX, LOW);
digitalWrite(redY, LOW);
digitalWrite(blueY, HIGH);
digitalWrite(blackY, HIGH);
digitalWrite(greenY, LOW);
delay(DELAY_INT);

digitalWrite(redX, HIGH);
digitalWrite(blueX, LOW);
digitalWrite(blackX, HIGH);
digitalWrite(greenX, LOW);
digitalWrite(redY, HIGH);
digitalWrite(blueY, LOW);
digitalWrite(blackY, HIGH);
digitalWrite(greenY, LOW);
delay(DELAY_INT);
}

Once that worked, my motors spun right round, baby, right round, like a record baby. So the next step was to make the arduino aware of how to step in a specific direction when a signal (A/B/C/D) was received over the serial connection. In order to do this, I had to map each of the 4 steps in the stepper motors to "step numbers" (0,1,2,3) . Then I wrote logic to move from one step to an adjacent one (0 to 3 or 1; 1 to 0 or 2; etc). I also had to add variables to keep track of the current position (step number) of each motor.

If I told the motor to step in a particular direction, that would do no good unless I knew where I'm stepping from.

 The result can be seen below.

Mounting and Tweaks

Once I felt confident that the stepper motors were turning as the software directed them, the next step was to get the stepper motors mounted to the Etch-A-Sketch knobs in some way. Luckily I had some scrap wood lying around. I cut it to resemble the models below:

The Etch-A-Sketch slides into the C-Shaped cavity with the knobs oriented below the stepper motors. The stepper motors (and the piece of wood they're mounted on) are colored red to show that this is a seperate piece of wood that can be dropped into place and detached as needed. It is held in place by 2 small blocks of wood behind it, and one triangular piece of wood in front of it. Hey, I said it was scrap.

I used Jameco aluminum couplers and rubber spiders (#161998 and #162000) to attach the stepper motors to the Etch-A-Sketch shafts (knobs removed). By the way, I must give thanks to the folks who did the writeup on the Electr-O-Sketch. Without their description of the exact coupler parts needed, I wouldn't have had a clue on how to hook up the motors to the Etch-A-Sketch knobs.

Power and Torque

After I created the mounting, wired up the stepper motors to the arduino, and put everything in place I was ready to run my software test with some basic shapes. The stepper motors were receiving the signals, but there was not enough torque in the motors to turn the Etch-A-Sketch knobs. Talk about a let-down.

Through some more research I found out that, when powered through the USB as I had done, the arduino only supplies 25mA of current. I also found out that the only way to draw more current through the arduino is to use an external power supply. When doing this, you apparently have to use a transistor array. I'm sill not 100% sure of the reason, except that it removes the risk of turning your board into a doorstop.

At the suggestion of a more knowledgable person in a google group, I used a "darlington array", which sounds like something you'd hear in the movie 2001:A Space Odyssey.

"HAL, disable the darlington array."

"I'm sorry Dave, I'm afraid I can't do that."

I ordered the darlington arrays and made a trip to RadioShack to pick up a DC Power Adapter at 4.5V and 1 Amp.

Success (mostly)

I hooked up the darlington arrays following the schematic below:



Then, I put my stepper motors in place and voila! There was enough torque to turn the Etch-A-Sketch Knobs.

To test the software, I created a bitmap with some basic shapes, and here's what was drawn...

Hmm, that circle looks a bit more elliptical than it should. I messed around with the stepper motors and discovered that both the X and Y knobs have a certain amount of "play". If the steppers are changing direction, it takes a number of steps until the actual drawing in that direction gets engaged. What this means is that there is a difference between where the PC Software thinks the pen is, and where the pen actually is.

With some trial and error, I figured out that my Etch-A-Sketch has about 5.4 degrees (3 x 1.8 degree steps) of play on the X-Knob and about 12.6 degrees (7 x 1.8 degree steps) on the Y-Knob.

I added some logic to the PC Program to be aware of what the currently-engaged-drawing-direction was, and it if was changing, to account for the knob-play before performing any of the actual drawing steps.

After finishing those tweaks, I ran the Etch-A-Sketch with a slightly more complicated image, and I'm pretty pleased with the results:

Further enhancements

The PC program could stand to be enhanced quite a bit. I added some logic to "Prompt for User Input" when it detects that the nearest unpainted neighbor will cause a high amount of interference. This lets the user control the pen manually and position it in a better place before letting the software continue. It also lets the user skip a pixel if there's just a small collection of inconsequential pixels that nobody would miss. For example, see the right eyebrow in the image above? That small handful of pixels caused a lot of interference. Yeah, yeah, I know it's a bit of a Turk solution to the challenging problem of "find the nearest unpainted neighbor while minimizing image interference", but it seems to work pretty well for a first pass.

Update (2/11/2009)

I ordered a Wii nunchuck adapter from FunGizmos.comand hooked it up to the Sketchduino, with much success. See below.


Update (2/12/2009)

Got an Atari Joystick working with it:

Update (3/15/2009)

One thing I've notice about running the Sketchduino is that one of the chips gets really hot. I posted some of the information regarding this problem, including a rough schematic and a note about which chip is burning up out on the arduino forums at www.arduino.cc

A helpful responder (cliffdweler) advised me to run the external power supply directly into the Darlington array, rather than running it through the arduino. The chip that was getting overheated was a voltage regulator, and the arduino is apparently not able to handle the kind of current draw (1000 mA) that I was trying to send through it. I made the change that cliffdweler suggested and the overheating problem went away.

Update (3/20/2009)



After giving an impromptu demo to the Cincinnati 2600 group (www.cinci2600.org) and seeing the level of enthusiasm for the final result, I decided to take the Sketchduino and make a commercial offer out of it: www.etchapic.com - Just Hoping to cover my costs for material on the project, which are about $125.

Update (3/25/2009)
I modified the .NET software to allow the user to select a starting position from nine options (Upper Left, Upper Center, Upper Right, etc). Also, I changed the way the nearest-neighbor algorithm works. If it finds an immediately adjacent unpainted pixel (which needs to be painted), it will move to that position. If it finds multiple adjacent pixels, one will be selected at random. This gives the final picture good texture, rather than a bunch of straight lines.

Also, every 100 iterations, I added logic to skip unpainted pixels that are surrounded on top/bottom/left/right by four painted pixels. I've discovered that single unpainted pixels like this typically cause a lot of interference and tend to add some very visible straight lines to the aforementioned "textured" picture we get from the SARAUPF ("select-a-random-adjacent-unpainted-pixel-first") method. Hey I can coin acronyms!

Updated the sketch for the arduino to condense it a bit. Reduced the arduino delay to 5ms. It runs pretty well with 32-byte bursts over the serial connection, with about 200ms between bursts.

int XStep = 0;
int YStep = 0;

int redX = 6;
int blueX = 7;
int blackX = 8;
int greenX = 9;

int redY = 10;
int blueY = 11;
int blackY = 12;
int greenY = 13;

void setup()                    // run once, when the sketch starts
{
  pinMode(redX, OUTPUT);      // sets the digital pin as output
  pinMode(blueX, OUTPUT);      // sets the digital pin as output
  pinMode(blackX, OUTPUT);      // sets the digital pin as output
  pinMode(greenX, OUTPUT);      // sets the digital pin as output
  pinMode(redY, OUTPUT);      // sets the digital pin as output
  pinMode(blueY, OUTPUT);      // sets the digital pin as output
  pinMode(blackY, OUTPUT);      // sets the digital pin as output
  pinMode(greenY, OUTPUT);      // sets the digital pin as output

//intialize pins to step 1

  Serial.begin(9600);
}


void InitializeSteppers()
{
  GotoStep(0, 'X', redX, blueX, blackX, greenX);
  GotoStep(0, 'Y', redY, blueY, blackY, greenY);
}

void MoveMotor(int increment, char axis)
{
  if (axis == 'Y')
  {
    YStep = YStep + increment;
    if (YStep == -1) { YStep = 3; }
    if (YStep == 4)  { YStep = 0; }
    GotoStep(YStep, 'Y', redY, blueY, blackY, greenY);
  }
  if (axis == 'X')
  {
    XStep = XStep + increment;
    if (XStep == -1) { XStep = 3; }
    if (XStep == 4)  { XStep = 0; }
    GotoStep(XStep, 'X', redX, blueX, blackX, greenX);
  } 
}

void GotoStep(int stepnbr, char axis, int redpin, int bluepin, int blackpin, int greenpin)
{

   if (stepnbr == 0)
  {
    digitalWrite(redpin, HIGH);
    digitalWrite(bluepin, LOW);
    digitalWrite(blackpin, HIGH);
    digitalWrite(greenpin, LOW);
  }
   if (stepnbr == 1)
  {
    digitalWrite(redpin, LOW);
    digitalWrite(bluepin, HIGH);
    digitalWrite(blackpin, HIGH);
    digitalWrite(greenpin, LOW);
  }
  if (stepnbr == 2)
  {
    digitalWrite(redpin, LOW);
    digitalWrite(bluepin, HIGH);
    digitalWrite(blackpin, LOW);
    digitalWrite(greenpin, HIGH);
  }
  if (stepnbr == 3)
  {
    digitalWrite(redpin, HIGH);
    digitalWrite(bluepin, LOW);
    digitalWrite(blackpin, LOW);
    digitalWrite(greenpin, HIGH);
  }
  if (axis == 'X') {  XStep = stepnbr; }
  if (axis == 'Y') {  YStep = stepnbr; }
}

void loop()                     // run over and over again
{
  delay(5);
  int hit;
  // read the serial input
  if (Serial.available() > 0) {
    hit = Serial.read();
    if (hit == 65)
    {
        MoveMotor(-1, 'Y');     
    }
    if (hit == 66)
    {
        MoveMotor(1, 'Y');
    }
    if (hit == 68)
    {
        MoveMotor(-1, 'X');
    }
    if (hit == 67)
    {
        MoveMotor(1, 'X');
    }
  }     
}

Update (6/5/2009)
I found out that the Sketchduino won 2nd place in Libelium's Arduino Contest. Woohoo!

http://www.libelium.com/tienda/catalog/contest.php

Update (6/14/2009)
An In-Action video of the Sketchduino rendering the logo of my favorite beverage vendor.

Update 8/18/2009

Met some of the folks at hive13 and showed off the Sketchduino:

 

Update 11/11/2009

RACO Industries asked me to help them with a marketing campaign:

Update 3/16/2014
Lots of people have asked for the source code on this and I'm feeling less territorial than I used to, so if you're interested in the source, here it is. I only ask if you use the source, please let me know so I can enjoy the derivative projects that build off of this one.

Questions or comments, please direct them to joetcochranATgmailDOTcom