Difficulty Level = 5 [What’s this?]
Here is a device I call the Hack-a-Sketch. The screen is a normal laptop (an old one), but it has real knobs which control the stylus on the screen.
An Arduino board reads the inputs from two potentiometers (the knobs), and sends the information via USB to a Processing sketch which displays the path of the stylus on the screen. This was extremely easy to build because the Arduino is just running the StandardFirmata firmware. No custom code on the board. The Processing sketch was surprisingly easy to write. Using this really did feel like using an Etch-a-Sketch.Here’s the Hack-a-Sketch in action. Wait for the big finish where I erase the image…
How did I erase the drawing by shaking the computer? There’s a mercury switch hidden behind the panel holding the knobs. When the code senses shaking, the image is slowly erased. More shaking = more erasure.Finally, here’s the Processing code that does all the work:
import processing.serial.*; import processing.core.*; import cc.arduino.Arduino; PImage backgroundImage; Arduino arduino; int STYLUS_X_PIN = 0; int STYLUS_Y_PIN = 1; int MERCURY_SWITCH_LEFT_PIN = 8; int MERCURY_SWITCH_RIGHT_PIN = 9; // Origin is lower left corner of screen. int originX = 146; int originY = 673; int width = 740; int height = 530; int PEN_COLOR = 50; int GRAY = 217; int GRAY_ALPHA = 80; int lastX, lastY; int SMOOTH_BUFFER_SIZE = 2; int[] smoothBufferX = new int[SMOOTH_BUFFER_SIZE]; int[] smoothBufferY = new int[SMOOTH_BUFFER_SIZE]; int smoothBufferXSum = 0; int smoothBufferYSum = 0; int LEFT = 0; int RIGHT = 1; int SHAKE_SPEED_THRESHOLD = 300; int lastSwitchPosition; int lastSwitchPositionChange; void setup() { size(1024, 768); frameRate(24); backgroundImage = loadImage("hackasketch-cropped.jpg"); background(backgroundImage); String[] ports = Arduino.list(); println(ports[0]); println(ports[1]); String port = ports[1]; try { arduino = new Arduino(this, port, 115200); arduino.pinMode(MERCURY_SWITCH_LEFT_PIN, Arduino.INPUT); arduino.pinMode(MERCURY_SWITCH_RIGHT_PIN, Arduino.INPUT); } catch (Exception e) { println("Failed to find Arduino board"); } stroke(PEN_COLOR); } void draw() { smooth(); int x, y; if (frameCount == 1) { // Give the Arduino some time to initialize so we can get a good first // reading from the potentiometers. delay(2000); lastX = readStylusX(); lastY = readStylusY(); for(int i=0;i < SMOOTH_BUFFER_SIZE;i++) { smoothBufferX[i] = lastX; smoothBufferXSum += lastX; smoothBufferY[i] = lastY; smoothBufferYSum += lastY; } point(originX+lastX, originY-lastY); } x = smoothX(readStylusX()); y = smoothY(readStylusY()); if ((x != lastX) || (y != lastY)) { line(originX+lastX, originY-lastY, originX+x, originY-y); } lastX = x; lastY = y; if (shake()) { erase(); } } boolean shake() { boolean left = (arduino.digitalRead(MERCURY_SWITCH_LEFT_PIN) == Arduino.HIGH); if (left && (lastSwitchPosition != LEFT)) { lastSwitchPosition = LEFT; if ((millis() - lastSwitchPositionChange) < SHAKE_SPEED_THRESHOLD) { lastSwitchPositionChange = millis(); return true; } lastSwitchPositionChange = millis(); } boolean right = (arduino.digitalRead(MERCURY_SWITCH_RIGHT_PIN) == Arduino.HIGH); if (right && (lastSwitchPosition != RIGHT)) { lastSwitchPosition = RIGHT; if ((millis() - lastSwitchPositionChange) < SHAKE_SPEED_THRESHOLD) { lastSwitchPositionChange = millis(); return true; } lastSwitchPositionChange = millis(); } return false; } void keyPressed() { if (key == ' ') { erase(); } } void erase() { fill(GRAY, GRAY_ALPHA); noStroke(); rect(originX, originY-height, width+1, height+1); stroke(PEN_COLOR); } // Read current stylus X position relative to the origin. int readStylusX() { int reading = arduino.analogRead(STYLUS_X_PIN); return (int)map((float)reading, 0f, 1023f, 0f, (float)width); } int smoothX(int v) { int index = frameCount % SMOOTH_BUFFER_SIZE; smoothBufferXSum -= smoothBufferX[index]; smoothBufferXSum += v; smoothBufferX[index] = v; return (int)(smoothBufferXSum / SMOOTH_BUFFER_SIZE); } // Read current stylus Y position relative to the origin. int readStylusY() { int reading = arduino.analogRead(STYLUS_Y_PIN); return (int)map((float)reading, 0f, 1023f, 0f, (float)height); } int smoothY(int v) { int index = frameCount % SMOOTH_BUFFER_SIZE; smoothBufferYSum -= smoothBufferY[index]; smoothBufferYSum += v; smoothBufferY[index] = v; return (int)(smoothBufferYSum / SMOOTH_BUFFER_SIZE); }
To quote Weird Al, “Got a flat-screen monitor forty inches wide wide / I believe that your says ‘Etch-A-Sketch’ on the side.”
that’d be kinda neat if you integrated it into a thinkpad’s active hard drive protection, that way you wouldn’t need the mercury switch AND it’d stop the hard drive when you started to shake it, which would negate all those complaining about destroying it.
Listen guys over at the Project Lab have made a cool Computer based Etch a Sketch project called Hack a Sketch. Looks like it works just like a real one.This project lets us recreate this interface on our computer. The Hack a Sketch is a combination of an Etch a Sketch style input
I came across this blog a few days back but completely did not remember to bookmark it – I wont make the same mistake just as before – it goes right in my favourites.
thats very cool:D
I love those :=) You need to teach me. Thank you for the info anyway.