Escher — The world’s first cloud-controlled Etch-a-Sketch

The Escher web interface.

Design considerations

Hardware design

The Adafruit HUZZAH32 — Image from https://www.adafruit.com/product/3405
The custom power distribution board for Escher. Totally not necessary if you want to use two power supplies instead.

3D printed base

Designing the Escher base in FreeCAD.
Rear view, showing electronics.

The software

The web client and the HUZZAH32 communicate via Firebase.
Adafruit_MotorShield AFMS = Adafruit_MotorShield();
Adafruit_StepperMotor *myStepper1 = AFMS.getStepper(200, 1);
Adafruit_StepperMotor *myStepper2 = AFMS.getStepper(200, 2);
void forwardstep1() {
myStepper1->onestep(BACKWARD, SINGLE);
}
void backwardstep1() {
myStepper1->onestep(FORWARD, SINGLE);
}
void forwardstep2() {
myStepper2->onestep(BACKWARD, DOUBLE);
}
void backwardstep2() {
myStepper2->onestep(FORWARD, DOUBLE);
}
AccelStepper stepper1(forwardstep1, backwardstep1);
AccelStepper stepper2(forwardstep2, backwardstep2);
stepper1.setMaxSpeed(50);
stepper2.setMaxSpeed(50);
MultiStepper mstepper;
mstepper.addStepper(stepper1);
mstepper.addStepper(stepper2);
AFMS.begin();
#define BACKLASH_X 10
#define BACKLASH_Y 15
// Move to the given position, specified in stepper units.
void EscherStepper::moveTo(long x, long y) {
// Figure out if we're reversing direction in x or y axes.
long dirx = x - last_x_;
if (dirx > 0) {
dirx = 1;
} else if (dirx < 0) {
dirx = -1;
}
long diry = y - last_y_;
if (diry > 0) {
diry = 1;
} else if (diry < 0) {
diry = -1;
}
if (dirx != dir_x_) {
cur_backlash_x_ += (dirx * BACKLASH_X);
}
if (diry != dir_y_) {
cur_backlash_y_ += (diry * BACKLASH_Y);
}
long target[2] = {x + cur_backlash_x_, y + cur_backlash_y_};
last_x_ = x;
last_y_ = y;
// Don't want to add backlash when transitioning from x -> 0 -> x
if (dirx != 0) {
dir_x_ = dirx;
}
if (diry != 0) {
dir_y_ = diry;
}
mstepper_.moveTo(target);
}
// Download the given URL and save it to flash storage.
// Returns true if successful, false otherwise.
bool downloadGcode(const char* url) {
http.setTimeout(10000);
http.begin(url);
int responseCode = http.GET();
if (responseCode <= 0) {
Serial.printf("[downloadGcode] failed, error: %s\n",
http.errorToString(responseCode).c_str());
return false;
}
File outFile = SPIFFS.open("/data.gcd", FILE_WRITE);
int bytesRead = http.writeToStream(&outFile);
http.end();
outFile.close();
if (bytesRead < 0) {
return false;
} else {
return true;
}
}

Stuff that worked, stuff that didn’t

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store