Thing-a-day, Day 19: Whiteboard Bot

While napping, I had this dream about a crazy way to make a clock face (I often think of these things). It reminded me of this drawing bot ‘hektor’ that I had read about, and I wondered how hard it would be to reproduce. With some encouragement from my friend, an Arduino clone, pieces from a VEX robotics kit and some Python, I have some sort of start. Sample code after the break. Note: I plan to fix up the code for this one.

Left side driver Building the platform First image: Tetris dreams img_6122.jpg First image: Tetris dreams (closeup)

This part goes on the Arduino. It requires the SimpleMessageSystem library.

#include <SimpleMessageSystem.h>
 *  Window washer drawing bot
 * by Matt Mets
 * Created 19 Feb. 2008
 * This is a little driver routine to move around two continuous rotation servo motors,
 * for the purpose of moving a pen platform to draw on a whiteboard
 * Servo control code thanks to:
 *    Servo control from an analog input
 *    by Tom Igoe
 *    additions by Carlyn Maw
 *    Created 28 Jan. 2006
 *    Updated 7 Jun. 2006
 *  The minimum (minPulse) and maxiumum (maxPuluse) values
 *  will be different depending on your specific servo motor.
 *  Ideally, it should be between 1 and 2 milliseconds, but in practice,
 *  0.5 - 2.5 milliseconds works well for me.
 *  Try different values to see what numbers are best for you.
 *  This program uses the millis() function to keep track of when the servo was 
 *  last pulsed.  millis() produces an overflow error (i.e. generates a number
 *  that's too big to fit in a long variable) after about 5 days. if you're
 *  making a program that has to run for more than 5 days, you may need to 
 *  account for this.
/***** Variable Definitions ***************************************************************************/
int lMotorPin =    2;      // Motor on left side
int rMotorPin =    3;      // Motor on right side
int statusPin =    13;     // Status LED
// Adjust these to fit your servo
int minPulse = 500;        // Minimum servo position (us)
int maxPulse = 2200;       // Maximum servo position (us)
int refreshTime = 20;      // the time needed in between pulses (ms)
/***** Functions ************************************************************************************/
void setup()
  pinMode(lMotorPin, OUTPUT);
  pinMode(rMotorPin, OUTPUT);
  pinMode(statusPin, OUTPUT);
/* Move the motors at given speed for a duration of time
 * lMotorVal - speed of left motor (128 = stop)
 * rMotorVal - speed of right motor (128 = stop)
 * time - time to move for (100 ms intervals)
void doMoveCommand(int lMotorVal, int rMotorVal, int time)
  int position;
  long startTime;
  long lastPulse = 0;    // the time in milliseconds of the last pulse
  long lMotorDelay, rMotorDelay;
  lMotorDelay = (long)lMotorVal*4*(maxPulse - minPulse)/1024 + minPulse;
  rMotorDelay = (long)rMotorVal*4*(maxPulse - minPulse)/1024 + minPulse;
  startTime = millis();
  while(millis() - startTime < time*100)
    // pulse the servo again if rhe refresh time (20 ms) have passed:
    if (millis() - lastPulse >= refreshTime)
      digitalWrite(lMotorPin, HIGH);   // Turn the motor on
      delayMicroseconds(lMotorDelay);       // Length of the pulse sets the motor position
      digitalWrite(lMotorPin, LOW);    // Turn the motor off
      digitalWrite(rMotorPin, HIGH);   // Turn the motor on
      delayMicroseconds(rMotorDelay);       // Length of the pulse sets the motor position
      digitalWrite(rMotorPin, LOW);    // Turn the motor off
      lastPulse = millis();           // save the time of the last pulse
void loop()
  char firstChar;
  digitalWrite(statusPin, HIGH);
  while(1) {
    if (messageBuild())
    { // Checks to see if the message is complete
      firstChar = messageGetChar(); // Gets the first word as a character
      if (firstChar = 'm')
        int lMotorVal = messageGetInt(); // Gets the next word an integer
        int rMotorVal = messageGetInt(); // Gets the next word an integer
        int time = messageGetInt();
        doMoveCommand(lMotorVal, rMotorVal, time);
    digitalWrite(statusPin, LOW);
    digitalWrite(statusPin, HIGH);

This is the Python demo driver, to be run on the PC. Warning: The whole thing is a hack…

#!/usr/bin/env python
import serial
from time import sleep
lL = 205    # move left motor counterclockwise
lH = 180    # hold left motor position
lR = 105    # move left motor clockwise
rL = 180    # move right motor counterclockwise
rH = 150    # hold right motor position
rR = 90     # move right motor clockwise
class whiteBoardBot():
    def __init__(self, port, baud=9600):
        self.serial = serial.Serial(port,baud)
    def makeMove(self, left, right, time):
        message = 'm %(left)d %(right)d %(time)d\r' % locals()
    def moveDirection(self, direction):
        if direction == 'l':
            bot.makeMove(lL, rL, 8)
        elif direction == 'r':
            bot.makeMove(lR, rR, 6)
        elif direction == 'u':
            bot.makeMove(lL, rR, 7)
        elif direction == 'd':
            bot.makeMove(lR, rL, 9)
bot = whiteBoardBot("/dev/ttyUSB0")
moves = 'r','r','r','u','l','u','l','d','l','d'
for i in range (1,4):
    for move in moves:
This entry was posted in tech, thingaday. Bookmark the permalink.

12 Responses to Thing-a-day, Day 19: Whiteboard Bot

  1. Ric says:

    You know what’d be really cool? If you were controlling magnets behind the board and the marker was floating mysteriously in front of the board. It’d be a cool seance trick!

    I love what you’ve done on this site. The things you are making are incredible. I wish I had time. My Arduino is still blinking 12:00… ;)

  2. nickjohnson says:

    I love the way this device does positioning. It places the pen at the intersection of two circles whose radii are decided by the length of the cable.

    This is a breath of fresh air when compared to the ubiquitous orthogonal lead-screw design.

    Thanks for posting this.

  3. Andrew says:

    How accurate are the servos? they seem handy for home robotics but it looks like this project relies on the speed of the servos being stable and similar. Have you considered shaft encoders or stepper motors?

  4. mahto says:


    @Ric- have you seen the SanDraw bot?

    @Andrew- They are probably not very accurate, especially the continuous rotation ones. I was planning to add some shaft encoders, however my friend gave me a couple of steppers today so I might try those instead.

  5. Johntron says:

    Have you tried using a counterbalance similar to the kind used with cameras? Basically, just hang a washer from the bottom of the stylus component. It may help smooth out the drawing.

  6. mahto says:

    Hi Johntron- I’ve got a somewhat heavy flywheel attached to the bottom, is that the sort of thing you are referring to?
    Building the platform

  7. David says:

    The counter weight is not the problem (keep the one you have). It’s your platform under the stylus. It’s too wide. You need the connection of the string to be closer to the pen from both sides. For down and dirty testing, pull out the duct tape, tape your weight to the pen, tape the 4 connection points of string to the pen (2 near tip, 2 far from tip). That will steady it out quite a bit.

  8. mahto says:

    Hi David,

    Thanks for the suggestion! I will keep that in mind for the next version. I more or less gave up on this design because the servo motors weren’t able to respond quickly enough to make drawing repeatable. I got some steppers and drivers, but haven’t gotten back to it :-/.

  9. Pingback: links-- sketchbook

  10. Pingback: DrawBot Resources and Links | MakerBlock

  11. Pingback: DrawBot – The Assembly, Part III | MakerBlock

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>