Hardware components | ||||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 2 | ||||
| × | 1 | ||||
Software apps and online services | ||||||
| ||||||
|
I got two stepper motors from two old DVD drives using the procedure shown in the Arduino Based Mini CNC 2D Plotter project. I used these two motors to control de X-axis and Y-axis of the plotter. I also added a servo motor and a hand-made pen holder to control de Z-axis.
Versions1.x.x
First of all, I've tried a prototype and the idea turned out to be very promising, so I went to a more definitive version.
2.x.x
The final result was really good and it was possible to plot with the hardware controlled by the computer through a USB port.
Take a look at the Mini Plotter version 2 in action in the following video.
3.x.x
In this version I added some improvements:
- New mechanical: now the paper moves (X and Y direction) instead of the pen. This strategy brought much more quality to the plot.
- Android app: now the plotter is controlled by an Android app through Bluetooth instead of by the computer.
Take a look at the Mini Plotter version 3 in action in the following video.
Have fun...
//----------------------------------------------------------------------------//
// Filename : MiniPlotter.ino //
// Description : Mini CNC plotter //
// Version : 2.0.0 //
// Author : Marcelo Avila de Oliveira <marceloavilaoliveira@gmail.com> //
//----------------------------------------------------------------------------//
//----------------------------------------------------------------------------//
// DEFINITIONS //
//----------------------------------------------------------------------------//
// TURN ON DEBUG MODE
// #define DEBUG
//----------------------------------------------------------------------------//
// LIBRARIES //
//----------------------------------------------------------------------------//
// SERVO LIBRARY
#include <Servo.h>
// ADAFRUIT MOTOR SHIELD LIBRARY
#include <AFMotor.h>
//----------------------------------------------------------------------------//
// CONSTANTS //
//----------------------------------------------------------------------------//
// PINS
const int z_pin = 9;
// POSITIONS
const int z_up = 0;
const int z_dn = 35;
// DRAWING SETTINGS
const char STEP = INTERLEAVE;
const float x_steps_per_mm = 13.4;
const float y_steps_per_mm = 13.4;
// DELAYS
const int step_delay_up = 2;
const int step_delay_dn = 5;
const int move_delay = 100;
const int pen_delay = 250;
// LIMITS [mm]
const float x_min = 0;
const float x_max = 40;
const float y_min = 0;
const float y_max = 40;
// READ
const int line_size = 80;
// STEP MOTORS (STEPS/REV, NUMBER)
AF_Stepper x_motor(48, 1);
AF_Stepper y_motor(48, 2);
Servo z_motor;
//----------------------------------------------------------------------------//
// VARIABLES //
//----------------------------------------------------------------------------//
// POSITION
float x = x_min;
float y = y_min;
int z = 0;
// READ
int line_index = 0;
char line[line_size];
boolean ignore = false;
//----------------------------------------------------------------------------//
// FUNCTIONS (SETUP) //
//----------------------------------------------------------------------------//
void setup() {
// INITIATE SERIAL COMMUNICATION
Serial.begin(38400);
// RESET POSITIONS
reset();
#ifdef DEBUG
Serial.println();
Serial.println("MiniPlotter is alive!");
Serial.println();
Serial.print("X from ");
Serial.print(x_min);
Serial.print(" to ");
Serial.print(x_max);
Serial.println(" mm");
Serial.print("Y from ");
Serial.print(y_min);
Serial.print(" to ");
Serial.print(y_max);
Serial.println(" mm");
Serial.println();
#endif
}
void reset() {
move_z(0);
move_z(1);
move_z(0);
move_xy(x_min, y_min);
move_xy(x_max, y_max);
move_z(1);
move_z(0);
move_xy(x_min, y_min);
}
//----------------------------------------------------------------------------//
// FUNCTIONS (READ) //
//----------------------------------------------------------------------------//
void read_serial() {
char c;
while (Serial.available()) {
c = Serial.read();
#ifdef DEBUG
Serial.print("Read character: ");
Serial.println(c);
#endif
if (c == '\n' || c == '\r') {
// END OF LINE REACHED
if (line_index > 0) {
line[line_index] = '\0';
#ifdef DEBUG
Serial.print("Read line: ");
Serial.println(line);
Serial.println();
#endif
process_line(line, line_index);
line_index = 0;
} else {
#ifdef DEBUG
Serial.println("Empty line");
Serial.println();
#endif
}
ignore = false;
} else {
if (ignore) {
if (c == ')') {
// STOP IGNORING LINE
ignore = false;
}
} else {
if (c <= ' ') {
// THROW AWAY WHITESPACE AND CONTROL CHARACTERS
} else if (c == '/') {
// BLOCK DELETE NOT SUPPORTED, IGNORE CHARACTER
} else if (c == '(') {
// START IGNORING LINE
ignore = true;
} else if (c == ';') {
// SEMICOLON NOT SUPPORTED, IGNORE LINE
ignore = true;
} else if (line_index >= line_size - 1) {
Serial.println("ERROR: Line size exceded");
ignore = false;
} else if (c >= 'a' && c <= 'z') {
// TO UPCASE
line[line_index++] = c-'a'+'A';
} else {
line[line_index++] = c;
}
}
}
}
}
void process_line(char* line, int line_size) {
int line_index = 0;
char buffer[50];
#ifdef DEBUG
Serial.print("Process line: ");
Serial.println(line);
Serial.println("");
#endif
while (line_index < line_size) {
switch (line[line_index++]) {
case 'S':
{
buffer[0] = line[line_index++];
buffer[1] = line[line_index++];
buffer[2] = '\0';
float size = atof(buffer);
while (line_index < line_size) {
char c = line[line_index++];
plot_char(c, size);
}
}
case 'G':
{
buffer[0] = line[line_index++];
buffer[1] = '\0';
switch (atoi(buffer)) {
case 0:
{
char* z_index = strchr(line+line_index, 'Z');
int z_pos;
if (z_index <= 0) {
z_pos = z;
} else {
z_pos = atoi(z_index + 1);
z_index = '\0';
}
move_z(z_pos);
break;
}
case 1:
{
char* x_index = strchr(line+line_index, 'X');
char* y_index = strchr(line+line_index, 'Y');
float x_pos, y_pos;
if (y_index <= 0) {
x_pos = atof(x_index + 1);
y_pos = y;
} else if (x_index <= 0) {
y_pos = atof(y_index + 1);
x_pos = x;
} else {
y_pos = atof(y_index + 1);
y_index = '\0';
x_pos = atof(x_index + 1);
}
move_xy(x_pos, y_pos);
break;
}
}
break;
}
case 'M':
{
buffer[0] = line[line_index++];
buffer[1] = line[line_index++];
buffer[2] = line[line_index++];
buffer[3] = '\0';
switch (atoi(buffer)) {
case 300:
{
char* s_index = strchr(line+line_index, 'S');
float s_pos = atof(s_index + 1);
if (s_pos == 50) {
// PEN UP
move_z(0);
}
if (s_pos == 30) {
// PEN DOWN
move_z(1);
}
break;
}
}
break;
}
}
}
}
//----------------------------------------------------------------------------//
// FUNCTIONS (MOVE) //
//----------------------------------------------------------------------------//
void motors_attach_detach(int mode) {
// MODE:
// 0 = ATTACH
// 1 = DETACH
if (mode == 0) {
z_motor.attach(z_pin);
} else {
z_motor.detach();
}
}
void move_z(int z_pos) {
// Z_POS: Z POSITION
// 0 = DOWN
// 1 = UP
#ifdef DEBUG
Serial.print("Move: Z");
Serial.print(z);
Serial.print(" => Z");
Serial.println(z_pos);
Serial.println("");
#endif
// MOVE
if (z_pos != z) {
motors_attach_detach(0);
if (z_pos == 0) {
z_motor.write(z_up);
} else {
z_motor.write(z_dn);
}
delay(pen_delay);
motors_attach_detach(1);
}
// UPDATE THE POSITION
z = z_pos;
}
void move_xy(float x_pos, float y_pos) {
// X_POS, Y_POS: X AND Y POSITIONS
#ifdef DEBUG
Serial.print("Move: X");
Serial.print(x);
Serial.print(" Y");
Serial.print(y);
Serial.print(" => X");
Serial.print(x_pos);
Serial.print(" Y");
Serial.println(y_pos);
Serial.println("");
#endif
// ADJUST THE POSITIONS UP TO THE LIMITS
if (x_pos >= x_max) {
x_pos = x_max;
}
if (x_pos <= x_min) {
x_pos = x_min;
}
if (y_pos >= y_max) {
y_pos = y_max;
}
if (y_pos <= y_min) {
y_pos = y_min;
}
// CONVERT COORDINATES TO STEPS
float x_step = (int)(x * x_steps_per_mm);
float y_step = (int)(y * y_steps_per_mm);
float x_pos_step = (int)(x_pos * x_steps_per_mm);
float y_pos_step = (int)(y_pos * y_steps_per_mm);
// CALCULATE THE CHANGE
long dx = abs(x_pos_step - x_step);
long dy = abs(y_pos_step - y_step);
int sx = x_step < x_pos_step ? FORWARD : BACKWARD;
int sy = y_step < y_pos_step ? FORWARD : BACKWARD;
// MOVE
long over = 0;
if (dx > dy) {
for (int i = 0; i < dx; ++i) {
x_motor.onestep(sx, STEP);
over += dy;
if (over >= dx) {
over -= dx;
y_motor.onestep(sy, STEP);
}
if (z == 0) {
delay(step_delay_up);
} else {
delay(step_delay_dn);
}
}
} else {
for (int i = 0; i < dy; ++i) {
y_motor.onestep(sy, STEP);
over += dx;
if (over >= dy) {
over -= dy;
x_motor.onestep(sx, STEP);
}
if (z == 0) {
delay(step_delay_up);
} else {
delay(step_delay_dn);
}
}
}
delay(move_delay);
// UPDATE THE POSITIONS
x = x_pos;
y = y_pos;
}
//----------------------------------------------------------------------------//
// FUNCTIONS (PLOT) //
//----------------------------------------------------------------------------//
void plot_char(char c, float height) {
// C: CHARACTER TO BE PLOTTED [A-Z0-9]
//
// HEIGHT: IN MM (WIDTH = HEIGHT / 2)
#ifdef DEBUG
Serial.print("Character = ");
Serial.println(c);
Serial.println();
#endif
float scale = height / 12;
float x0 = x;
float y0 = y;
switch (c) {
case 'A':
move_z(1);
move_xy(x0+0*scale, y0+10*scale);
move_xy(x0+2*scale, y0+12*scale);
move_xy(x0+4*scale, y0+12*scale);
move_xy(x0+6*scale, y0+10*scale);
move_xy(x0+6*scale, y0+0*scale);
move_z(0);
move_xy(x0+0*scale, y0+6*scale);
move_z(1);
move_xy(x0+6*scale, y0+6*scale);
move_z(0);
move_xy(x0+8*scale, y0+0*scale);
break;
case 'B':
// TBD
break;
case 'C':
move_xy(x0+6*scale, y0+12*scale);
move_z(1);
move_xy(x0+2*scale, y0+12*scale);
move_xy(x0+0*scale, y0+10*scale);
move_xy(x0+0*scale, y0+2*scale);
move_xy(x0+2*scale, y0+0*scale);
move_xy(x0+6*scale, y0+0*scale);
move_z(0);
move_xy(x0+8*scale, y0+0*scale);
break;
case 'D':
// TBD
break;
case 'E':
move_xy(x0+6*scale, y0+6*scale);
move_z(1);
move_xy(x0+0*scale, y0+6*scale);
move_z(0);
move_xy(x0+6*scale, y0+12*scale);
move_z(1);
move_xy(x0+0*scale, y0+12*scale);
move_xy(x0+0*scale, y0+0*scale);
move_xy(x0+6*scale, y0+0*scale);
move_z(0);
move_xy(x0+8*scale, y0+0*scale);
break;
case 'F':
// TBD
break;
case 'G':
// TBD
break;
case 'H':
// TBD
break;
case 'I':
// TBD
break;
case 'J':
// TBD
break;
case 'K':
// TBD
break;
case 'L':
move_xy(x0+0*scale, y0+12*scale);
move_z(1);
move_xy(x0+0*scale, y0+0*scale);
move_xy(x0+6*scale, y0+0*scale);
move_z(0);
move_xy(x0+8*scale, y0+0*scale);
break;
case 'M':
move_z(1);
move_xy(x0+0*scale, y0+12*scale);
move_xy(x0+3*scale, y0+6*scale);
move_xy(x0+6*scale, y0+12*scale);
move_xy(x0+6*scale, y0+0*scale);
move_z(0);
move_xy(x0+8*scale, y0+0*scale);
break;
case 'N':
// TBD
break;
case 'O':
move_xy(x0+0*scale, y0+2*scale);
move_z(1);
move_xy(x0+0*scale, y0+10*scale);
move_xy(x0+2*scale, y0+12*scale);
move_xy(x0+4*scale, y0+12*scale);
move_xy(x0+6*scale, y0+10*scale);
move_xy(x0+6*scale, y0+2*scale);
move_xy(x0+4*scale, y0+0*scale);
move_xy(x0+2*scale, y0+0*scale);
move_xy(x0+0*scale, y0+2*scale);
move_z(0);
move_xy(x0+8*scale, y0+0*scale);
break;
case 'P':
// TBD
break;
case 'Q':
// TBD
break;
case 'R':
move_z(1);
move_xy(x0, y0+12*scale);
move_xy(x0+4*scale, y0+12*scale);
move_xy(x0+6*scale, y0+10*scale);
move_xy(x0+6*scale, y0+8*scale);
move_xy(x0+4*scale, y0+6*scale);
move_xy(x0+0*scale, y0+6*scale);
move_xy(x0+6*scale, y0+0*scale);
move_z(0);
move_xy(x0+8*scale, y0+0*scale);
break;
case 'S':
// TBD
break;
case 'T':
// TBD
break;
case 'U':
// TBD
break;
case 'V':
// TBD
break;
case 'X':
// TBD
break;
case 'Y':
// TBD
break;
case 'Z':
// TBD
break;
}
}
//----------------------------------------------------------------------------//
// MAIN //
//----------------------------------------------------------------------------//
void loop()
{
read_serial();
}
//----------------------------------------------------------------------------//
// Description : Mini CNC plotter //
// Author : Marcelo Avila de Oliveira <marceloavilaoliveira@gmail.com> //
//----------------------------------------------------------------------------//
//----------------------------------------------------------------------------//
// DEFINITIONS //
//----------------------------------------------------------------------------//
// TURN ON DEBUG MODE
// #define DEBUG
//----------------------------------------------------------------------------//
// LIBRARIES //
//----------------------------------------------------------------------------//
// SERVO LIBRARY
#include <Servo.h>
// ADAFRUIT MOTOR SHIELD LIBRARY
#include <AFMotor.h>
//----------------------------------------------------------------------------//
// CONSTANTS //
//----------------------------------------------------------------------------//
// PINS
const int z_pin = 9;
// POSITIONS
const int z_up = 0;
const int z_dn = 35;
// DRAWING SETTINGS
const char STEP = INTERLEAVE;
const float x_steps_per_mm = 13.4;
const float y_steps_per_mm = 13.4;
// DELAYS
const int step_delay_up = 2;
const int step_delay_dn = 4;
const int move_delay =100;
const int pen_delay = 200;
// LIMITS [mm]
const float x_min = 0;
const float x_max = 40;
const float y_min = 0;
const float y_max = 40;
// SERIAL AND BLUETOOTH COMMUNICATION
const int line_size = 80;
// STEP MOTORS (STEPS/REV, NUMBER)
AF_Stepper x_motor(48, 1);
AF_Stepper y_motor(48, 2);
Servo z_motor;
//----------------------------------------------------------------------------//
// VARIABLES //
//----------------------------------------------------------------------------//
// POSITION
float x = x_min;
float y = y_min;
int z = 0;
// SERIAL COMMUNICATION
char line_se[line_size];
int line_index_se = 0;
boolean string_se = false;
boolean ignore_se = false;
// BLUETOOTH COMMUNICATION
char line_bt[line_size];
int line_index_bt = 0;
boolean string_bt = false;
boolean ignore_bt = false;
//----------------------------------------------------------------------------//
// FUNCTIONS (SETTINGS) //
//----------------------------------------------------------------------------//
void setup() {
// INITIATE SERIAL COMMUNICATION
Serial.begin(38400);
// INITIATE BLUETOOTH COMMUNICATION
setup_bluetooth();
// RESET POSITIONS
reset();
#ifdef DEBUG
Serial.println("MiniPlotter is alive!");
Serial.println();
#endif
}
void setup_bluetooth() {
#ifdef DEBUG
Serial.println("Setting Bluetooth");
Serial.println();
#endif
Serial1.begin(38400); // Set baud rate
Serial1.print("\r\n+STWMOD=0\r\n"); // Set to work in slave mode
Serial1.print("\r\n+STNA=Arduino\r\n"); // Set name
Serial1.print("\r\n+STOAUT=1\r\n"); // Permit Paired device to connect me
Serial1.print("\r\n+STAUTO=0\r\n"); // Auto-connection should be forbidden here
delay(2000); // This delay is required.
Serial1.print("\r\n+INQ=1\r\n"); // Make the slave inquirable
delay(2000); // This delay is required.
while (Serial1.available()) { // Clear data
delay(50);
Serial1.read();
}
}
void reset() {
move_xy(x_min, y_min);
move_xy(x_max, y_max);
move_xy(x_min, y_min);
move_z(0);
move_z(1);
move_z(0);
}
//----------------------------------------------------------------------------//
// FUNCTIONS (READ) //
//----------------------------------------------------------------------------//
void check_bluetooth() {
char c;
boolean ok = false;
while (Serial1.available() && !ok) {
c = Serial1.read();
delay(50);
if (c == '#') {
while (Serial1.available()) {
c = Serial1.read();
process_character(c, line_bt, line_index_bt, string_bt, ignore_bt);
}
ok = true;
}
}
}
void check_serial() {
char c;
while (Serial.available()) {
c = Serial.read();
process_character(c, line_se, line_index_se, string_se, ignore_se);
}
}
void process_character(char c, char* line, int& line_index, boolean& string, boolean& ignore) {
#ifdef DEBUG
Serial.print("Process character: ");
Serial.println(c);
#endif
if (c == '\n' || c == '\r') {
// END OF LINE REACHED
if (line_index > 0) {
// PROCESS THE LINE
line[line_index] = '\0';
#ifdef DEBUG
Serial.print("Read line: ");
Serial.println(line);
Serial.println();
#endif
process_line(line, line_index);
} else {
// EMPTY LINE, IGNORE
}
line_index = 0;
string = false;
ignore = false;
} else {
if (line_index == 0 && (c == 's' || c == 'S')) {
// IT'S A PLOT STRING COMMAND
string = true;
}
if (ignore) {
if (c == ')' && ! string) {
// STOP IGNORING LINE
ignore = false;
}
} else {
if (c <= ' ' && ! string) {
// THROW AWAY WHITESPACE AND CONTROL CHARACTERS
} else if (c == '/' && ! string) {
// BLOCK DELETE NOT SUPPORTED, IGNORE CHARACTER
} else if (c == '(' && ! string) {
// START IGNORING LINE
ignore = true;
} else if (c == ';' && ! string) {
// SEMICOLON NOT SUPPORTED, IGNORE LINE
ignore = true;
} else if (line_index >= line_size - 1) {
Serial.println("ERROR: Line size exceded");
ignore = false;
} else if (c >= 'a' && c <= 'z') {
// TO UPCASE
line[line_index++] = c-'a'+'A';
} else {
line[line_index++] = c;
}
}
}
}
void process_line(char* line, int line_size) {
int line_index = 0;
char buffer[50];
#ifdef DEBUG
Serial.print("Process line: ");
Serial.println(line);
Serial.println("");
#endif
while (line_index < line_size) {
switch (line[line_index++]) {
case 'G':
{
// MOVE PEN
// FORMAT: G0 Zx
// x = 1 (PEN DOWN)
// x = 0 (PEN UP)
// OR: G1 Xx.xx Yy.yy
// x.xx/y.yy = POSITION IN MM
buffer[0] = line[line_index++];
buffer[1] = '\0';
switch (atoi(buffer)) {
case 0:
{
// MOVE Z
char* z_index = strchr(line+line_index, 'Z');
int z_pos;
if (z_index <= 0) {
z_pos = z;
} else {
z_pos = atoi(z_index + 1);
z_index = '\0';
}
move_z(z_pos);
break;
}
case 1:
{
// MOVE X,Y
char* x_index = strchr(line+line_index, 'X');
char* y_index = strchr(line+line_index, 'Y');
float x_pos, y_pos;
if (y_index <= 0) {
x_pos = atof(x_index + 1);
y_pos = y;
} else if (x_index <= 0) {
y_pos = atof(y_index + 1);
x_pos = x;
} else {
y_pos = atof(y_index + 1);
y_index = '\0';
x_pos = atof(x_index + 1);
}
move_xy(x_pos, y_pos);
break;
}
}
break;
}
case 'M':
{
// MOVE PEN
// FORMAT: M300 Sx
// x = 30 (PEN DOWN)
// x = 50 (PEN UP)
buffer[0] = line[line_index++];
buffer[1] = line[line_index++];
buffer[2] = line[line_index++];
buffer[3] = '\0';
switch (atoi(buffer)) {
case 300:
{
char* s_index = strchr(line+line_index, 'S');
float s_pos = atof(s_index + 1);
if (s_pos == 50) {
// PEN UP
move_z(0);
}
if (s_pos == 30) {
// PEN DOWN
move_z(1);
}
break;
}
}
break;
}
case 'R':
{
// RESET POSITIONS
reset();
break;
}
case 'S':
{
// PLOT STRING
// FORMAT: Sxx yyyyy
// xx = HEIGHT IN MM (WIDTH = HEIGHT / 2)
// yyyyy = STRING
buffer[0] = line[line_index++];
buffer[1] = line[line_index++];
buffer[2] = '\0';
float size = atof(buffer);
while (line_index < line_size) {
char c = line[line_index++];
plot_char(c, size);
}
break;
}
}
}
}
//----------------------------------------------------------------------------//
// FUNCTIONS (MOVE) //
//----------------------------------------------------------------------------//
void motors_attach_detach(int mode) {
// MODE:
// 0 = ATTACH
// 1 = DETACH
if (mode == 0) {
z_motor.attach(z_pin);
} else {
z_motor.detach();
}
}
void move_z(int z_pos) {
// Z_POS: Z POSITION
// 0 = DOWN
// 1 = UP
#ifdef DEBUG
Serial.print("Move: Z");
Serial.print(z);
Serial.print(" => Z");
Serial.println(z_pos);
Serial.println("");
#endif
// MOVE
if (z_pos != z) {
motors_attach_detach(0);
if (z_pos == 0) {
z_motor.write(z_up);
} else {
z_motor.write(z_dn);
}
delay(pen_delay);
motors_attach_detach(1);
}
// UPDATE THE POSITION
z = z_pos;
}
void move_xy(float x_pos, float y_pos) {
// X_POS, Y_POS: X AND Y POSITIONS
#ifdef DEBUG
Serial.print("Move: X");
Serial.print(x);
Serial.print(" Y");
Serial.print(y);
Serial.print(" => X");
Serial.print(x_pos);
Serial.print(" Y");
Serial.println(y_pos);
Serial.println("");
#endif
// ADJUST THE POSITIONS UP TO THE LIMITS
if (x_pos >= x_max) {
x_pos = x_max;
}
if (x_pos <= x_min) {
x_pos = x_min;
}
if (y_pos >= y_max) {
y_pos = y_max;
}
if (y_pos <= y_min) {
y_pos = y_min;
}
// CONVERT COORDINATES TO STEPS
float x_step = (int)(x * x_steps_per_mm);
float y_step = (int)(y * y_steps_per_mm);
float x_pos_step = (int)(x_pos * x_steps_per_mm);
float y_pos_step = (int)(y_pos * y_steps_per_mm);
// CALCULATE THE CHANGE
long dx = abs(x_pos_step - x_step);
long dy = abs(y_pos_step - y_step);
int sx = x_step > x_pos_step ? FORWARD : BACKWARD;
int sy = y_step > y_pos_step ? FORWARD : BACKWARD;
// MOVE
long over = 0;
if (dx > dy) {
for (int i = 0; i < dx; ++i) {
x_motor.onestep(sx, STEP);
over += dy;
if (over >= dx) {
over -= dx;
y_motor.onestep(sy, STEP);
}
if (z == 0) {
delay(step_delay_up);
} else {
delay(step_delay_dn);
}
}
} else {
for (int i = 0; i < dy; ++i) {
y_motor.onestep(sy, STEP);
over += dx;
if (over >= dy) {
over -= dy;
x_motor.onestep(sx, STEP);
}
if (z == 0) {
delay(step_delay_up);
} else {
delay(step_delay_dn);
}
}
}
delay(move_delay);
// UPDATE THE POSITIONS
x = x_pos;
y = y_pos;
}
//----------------------------------------------------------------------------//
// FUNCTIONS (PLOT) //
//----------------------------------------------------------------------------//
void plot_char(char c, float height) {
// C: CHARACTER TO BE PLOTTED = [A-Z0-9 +-_/.:%#()><]
// OR A COMMAND TO BE EXECUTED:
// = = MOVE TO THE BEGINNING OF THE LINE
// ^ = MOVE TO THE PREVIOUS LINE
// | = MOVE TO THE NEXT LINE
//
// HEIGHT: IN MM (WIDTH = HEIGHT / 2)
#ifdef DEBUG
Serial.print("Character = ");
Serial.println(c);
Serial.println();
#endif
// VERIFY IF THERE'S ENOUGHT SPACE TO PLOT
if (c == '=') {
// MOVE TO THE BEGINNING OF THE LINE
} else if (c == '^') {
// MOVE TO THE PREVIOUS LINE
if ((y + 7 / 6 * height > y_max)) {
return;
}
} else if (c == '|') {
// MOVE TO THE NEXT LINE
if ((y - 7 / 6 * height < y_min)) {
return;
}
} else {
// CHARACTER
if ((x + height / 2 > x_max) || (y + height > y_max)) {
return;
}
}
float scale = height / 12;
float x0 = x;
float y0 = y;
switch (c) {
case 'A':
move_z(1);
move_xy(x0+0*scale, y0+10*scale);
move_xy(x0+2*scale, y0+12*scale);
move_xy(x0+4*scale, y0+12*scale);
move_xy(x0+6*scale, y0+10*scale);
move_xy(x0+6*scale, y0+0*scale);
move_z(0);
move_xy(x0+0*scale, y0+6*scale);
move_z(1);
move_xy(x0+6*scale, y0+6*scale);
break;
case 'B':
move_z(1);
move_xy(x0+0*scale, y0+12*scale);
move_xy(x0+4*scale, y0+12*scale);
move_xy(x0+6*scale, y0+10*scale);
move_xy(x0+6*scale, y0+8*scale);
move_xy(x0+4*scale, y0+6*scale);
move_xy(x0+6*scale, y0+4*scale);
move_xy(x0+6*scale, y0+2*scale);
move_xy(x0+4*scale, y0+0*scale);
move_xy(x0+0*scale, y0+0*scale);
move_z(0);
move_xy(x0+0*scale, y0+6*scale);
move_z(1);
move_xy(x0+4*scale, y0+6*scale);
break;
case 'C':
move_xy(x0+6*scale, y0+12*scale);
move_z(1);
move_xy(x0+2*scale, y0+12*scale);
move_xy(x0+0*scale, y0+10*scale);
move_xy(x0+0*scale, y0+2*scale);
move_xy(x0+2*scale, y0+0*scale);
move_xy(x0+6*scale, y0+0*scale);
break;
case 'D':
move_z(1);
move_xy(x0+0*scale, y0+12*scale);
move_xy(x0+4*scale, y0+12*scale);
move_xy(x0+6*scale, y0+10*scale);
move_xy(x0+6*scale, y0+2*scale);
move_xy(x0+4*scale, y0+0*scale);
move_xy(x0+0*scale, y0+0*scale);
break;
case 'E':
move_xy(x0+0*scale, y0+6*scale);
move_z(1);
move_xy(x0+6*scale, y0+6*scale);
move_z(0);
move_xy(x0+6*scale, y0+12*scale);
move_z(1);
move_xy(x0+0*scale, y0+12*scale);
move_xy(x0+0*scale, y0+0*scale);
move_xy(x0+6*scale, y0+0*scale);
break;
case 'F':
move_z(1);
move_xy(x0+0*scale, y0+12*scale);
move_xy(x0+6*scale, y0+12*scale);
move_z(0);
move_xy(x0+0*scale, y0+6*scale);
move_z(1);
move_xy(x0+6*scale, y0+6*scale);
break;
case 'G':
move_xy(x0+4*scale, y0+6*scale);
move_z(1);
move_xy(x0+6*scale, y0+6*scale);
move_xy(x0+6*scale, y0+2*scale);
move_xy(x0+4*scale, y0+0*scale);
move_xy(x0+2*scale, y0+0*scale);
move_xy(x0+0*scale, y0+2*scale);
move_xy(x0+0*scale, y0+10*scale);
move_xy(x0+2*scale, y0+12*scale);
move_xy(x0+4*scale, y0+12*scale);
move_xy(x0+6*scale, y0+10*scale);
break;
case 'H':
move_z(1);
move_xy(x0+0*scale, y0+12*scale);
move_z(0);
move_xy(x0+0*scale, y0+6*scale);
move_z(1);
move_xy(x0+6*scale, y0+6*scale);
move_z(0);
move_xy(x0+6*scale, y0+12*scale);
move_z(1);
move_xy(x0+6*scale, y0+0*scale);
break;
case 'I':
move_z(1);
move_xy(x0+6*scale, y0+0*scale);
move_z(0);
move_xy(x0+0*scale, y0+12*scale);
move_z(1);
move_xy(x0+6*scale, y0+12*scale);
move_z(0);
move_xy(x0+3*scale, y0+12*scale);
move_z(1);
move_xy(x0+3*scale, y0+0*scale);
break;
case 'J':
move_xy(x0+0*scale, y0+12*scale);
move_z(1);
move_xy(x0+6*scale, y0+12*scale);
move_xy(x0+6*scale, y0+2*scale);
move_xy(x0+4*scale, y0+0*scale);
move_xy(x0+2*scale, y0+0*scale);
move_xy(x0+0*scale, y0+2*scale);
move_xy(x0+0*scale, y0+4*scale);
break;
case 'K':
move_z(1);
move_xy(x0+0*scale, y0+12*scale);
move_z(0);
move_xy(x0+6*scale, y0+12*scale);
move_z(1);
move_xy(x0+0*scale, y0+6*scale);
move_xy(x0+6*scale, y0+0*scale);
break;
case 'L':
move_xy(x0+0*scale, y0+12*scale);
move_z(1);
move_xy(x0+0*scale, y0+0*scale);
move_xy(x0+6*scale, y0+0*scale);
break;
case 'M':
move_z(1);
move_xy(x0+0*scale, y0+12*scale);
move_xy(x0+3*scale, y0+6*scale);
move_xy(x0+6*scale, y0+12*scale);
move_xy(x0+6*scale, y0+0*scale);
break;
case 'N':
move_z(1);
move_xy(x0+0*scale, y0+12*scale);
move_xy(x0+6*scale, y0+0*scale);
move_xy(x0+6*scale, y0+12*scale);
break;
case 'O':
move_xy(x0+2*scale, y0+0*scale);
move_z(1);
move_xy(x0+0*scale, y0+2*scale);
move_xy(x0+0*scale, y0+10*scale);
move_xy(x0+2*scale, y0+12*scale);
move_xy(x0+4*scale, y0+12*scale);
move_xy(x0+6*scale, y0+10*scale);
move_xy(x0+6*scale, y0+2*scale);
move_xy(x0+4*scale, y0+0*scale);
move_xy(x0+2*scale, y0+0*scale);
break;
case 'P':
move_z(1);
move_xy(x0+0*scale, y0+12*scale);
move_xy(x0+4*scale, y0+12*scale);
move_xy(x0+6*scale, y0+10*scale);
move_xy(x0+6*scale, y0+8*scale);
move_xy(x0+4*scale, y0+6*scale);
move_xy(x0+0*scale, y0+6*scale);
break;
case 'Q':
move_xy(x0+2*scale, y0+0*scale);
move_z(1);
move_xy(x0+0*scale, y0+2*scale);
move_xy(x0+0*scale, y0+10*scale);
move_xy(x0+2*scale, y0+12*scale);
move_xy(x0+4*scale, y0+12*scale);
move_xy(x0+6*scale, y0+10*scale);
move_xy(x0+6*scale, y0+2*scale);
move_xy(x0+4*scale, y0+0*scale);
move_xy(x0+2*scale, y0+0*scale);
move_z(0);
move_xy(x0+3*scale, y0+3*scale);
move_z(1);
move_xy(x0+6*scale, y0+0*scale);
break;
case 'R':
move_z(1);
move_xy(x0+0*scale, y0+12*scale);
move_xy(x0+4*scale, y0+12*scale);
move_xy(x0+6*scale, y0+10*scale);
move_xy(x0+6*scale, y0+8*scale);
move_xy(x0+4*scale, y0+6*scale);
move_xy(x0+0*scale, y0+6*scale);
move_xy(x0+6*scale, y0+0*scale);
break;
case 'S':
move_xy(x0+0*scale, y0+2*scale);
move_z(1);
move_xy(x0+2*scale, y0+0*scale);
move_xy(x0+4*scale, y0+0*scale);
move_xy(x0+6*scale, y0+2*scale);
move_xy(x0+6*scale, y0+4*scale);
move_xy(x0+4*scale, y0+6*scale);
move_xy(x0+2*scale, y0+6*scale);
move_xy(x0+0*scale, y0+8*scale);
move_xy(x0+0*scale, y0+10*scale);
move_xy(x0+2*scale, y0+12*scale);
move_xy(x0+4*scale, y0+12*scale);
move_xy(x0+6*scale, y0+10*scale);
break;
case 'T':
move_xy(x0+0*scale, y0+12*scale);
move_z(1);
move_xy(x0+6*scale, y0+12*scale);
move_z(0);
move_xy(x0+3*scale, y0+12*scale);
move_z(1);
move_xy(x0+3*scale, y0+0*scale);
break;
case 'U':
move_xy(x0+0*scale, y0+12*scale);
move_z(1);
move_xy(x0+0*scale, y0+2*scale);
move_xy(x0+2*scale, y0+0*scale);
move_xy(x0+4*scale, y0+0*scale);
move_xy(x0+6*scale, y0+2*scale);
move_xy(x0+6*scale, y0+12*scale);
break;
case 'V':
move_xy(x0+0*scale, y0+12*scale);
move_z(1);
move_xy(x0+3*scale, y0+0*scale);
move_xy(x0+6*scale, y0+12*scale);
break;
case 'X':
move_z(1);
move_xy(x0+6*scale, y0+12*scale);
move_z(0);
move_xy(x0+0*scale, y0+12*scale);
move_z(1);
move_xy(x0+6*scale, y0+0*scale);
break;
case 'W':
move_xy(x0+0*scale, y0+12*scale);
move_z(1);
move_xy(x0+0*scale, y0+0*scale);
move_xy(x0+3*scale, y0+6*scale);
move_xy(x0+6*scale, y0+0*scale);
move_xy(x0+6*scale, y0+12*scale);
break;
case 'Y':
move_xy(x0+0*scale, y0+12*scale);
move_z(1);
move_xy(x0+3*scale, y0+6*scale);
move_xy(x0+6*scale, y0+12*scale);
move_z(0);
move_xy(x0+3*scale, y0+6*scale);
move_z(1);
move_xy(x0+3*scale, y0+0*scale);
break;
case 'Z':
move_xy(x0+0*scale, y0+12*scale);
move_z(1);
move_xy(x0+6*scale, y0+12*scale);
move_xy(x0+0*scale, y0+0*scale);
move_xy(x0+6*scale, y0+0*scale);
break;
case '0':
move_xy(x0+0*scale, y0+2*scale);
move_z(1);
move_xy(x0+0*scale, y0+10*scale);
move_xy(x0+2*scale, y0+12*scale);
move_xy(x0+4*scale, y0+12*scale);
move_xy(x0+6*scale, y0+10*scale);
move_xy(x0+6*scale, y0+2*scale);
move_xy(x0+4*scale, y0+0*scale);
move_xy(x0+2*scale, y0+0*scale);
move_xy(x0+0*scale, y0+2*scale);
move_z(0);
move_xy(x0+1*scale, y0+1*scale);
move_z(1);
move_xy(x0+5*scale, y0+11*scale);
break;
case '1':
move_z(1);
move_xy(x0+6*scale, y0+0*scale);
move_z(0);
move_xy(x0+0*scale, y0+6*scale);
move_z(1);
move_xy(x0+3*scale, y0+12*scale);
move_xy(x0+3*scale, y0+0*scale);
break;
case '2':
move_xy(x0+0*scale, y0+10*scale);
move_z(1);
move_xy(x0+2*scale, y0+12*scale);
move_xy(x0+4*scale, y0+12*scale);
move_xy(x0+6*scale, y0+10*scale);
move_xy(x0+6*scale, y0+8*scale);
move_xy(x0+4*scale, y0+6*scale);
move_xy(x0+2*scale, y0+6*scale);
move_xy(x0+0*scale, y0+4*scale);
move_xy(x0+0*scale, y0+0*scale);
move_xy(x0+6*scale, y0+0*scale);
break;
case '3':
move_xy(x0+0*scale, y0+10*scale);
move_z(1);
move_xy(x0+2*scale, y0+12*scale);
move_xy(x0+4*scale, y0+12*scale);
move_xy(x0+6*scale, y0+10*scale);
move_xy(x0+6*scale, y0+8*scale);
move_xy(x0+4*scale, y0+6*scale);
move_xy(x0+2*scale, y0+6*scale);
move_z(0);
move_xy(x0+4*scale, y0+6*scale);
move_z(1);
move_xy(x0+6*scale, y0+4*scale);
move_xy(x0+6*scale, y0+2*scale);
move_xy(x0+4*scale, y0+0*scale);
move_xy(x0+2*scale, y0+0*scale);
move_xy(x0+0*scale, y0+2*scale);
break;
case '4':
move_xy(x0+6*scale, y0+6*scale);
move_z(1);
move_xy(x0+0*scale, y0+6*scale);
move_xy(x0+6*scale, y0+12*scale);
move_xy(x0+6*scale, y0+0*scale);
break;
case '5':
move_xy(x0+0*scale, y0+2*scale);
move_z(1);
move_xy(x0+2*scale, y0+0*scale);
move_xy(x0+4*scale, y0+0*scale);
move_xy(x0+6*scale, y0+2*scale);
move_xy(x0+6*scale, y0+4*scale);
move_xy(x0+4*scale, y0+6*scale);
move_xy(x0+0*scale, y0+6*scale);
move_xy(x0+0*scale, y0+12*scale);
move_xy(x0+6*scale, y0+12*scale);
break;
case '6':
move_xy(x0+0*scale, y0+4*scale);
move_z(1);
move_xy(x0+2*scale, y0+6*scale);
move_xy(x0+4*scale, y0+6*scale);
move_xy(x0+6*scale, y0+4*scale);
move_xy(x0+6*scale, y0+2*scale);
move_xy(x0+4*scale, y0+0*scale);
move_xy(x0+2*scale, y0+0*scale);
move_xy(x0+0*scale, y0+2*scale);
move_xy(x0+0*scale, y0+10*scale);
move_xy(x0+2*scale, y0+12*scale);
move_xy(x0+4*scale, y0+12*scale);
move_xy(x0+6*scale, y0+10*scale);
break;
case '7':
move_z(1);
move_xy(x0+6*scale, y0+12*scale);
move_xy(x0+0*scale, y0+12*scale);
break;
case '8':
move_xy(x0+2*scale, y0+0*scale);
move_z(1);
move_xy(x0+0*scale, y0+2*scale);
move_xy(x0+0*scale, y0+4*scale);
move_xy(x0+2*scale, y0+6*scale);
move_xy(x0+0*scale, y0+8*scale);
move_xy(x0+0*scale, y0+10*scale);
move_xy(x0+2*scale, y0+12*scale);
move_xy(x0+4*scale, y0+12*scale);
move_xy(x0+6*scale, y0+10*scale);
move_xy(x0+6*scale, y0+8*scale);
move_xy(x0+4*scale, y0+6*scale);
move_xy(x0+2*scale, y0+6*scale);
move_z(0);
move_xy(x0+4*scale, y0+6*scale);
move_z(1);
move_xy(x0+6*scale, y0+4*scale);
move_xy(x0+6*scale, y0+2*scale);
move_xy(x0+4*scale, y0+0*scale);
move_xy(x0+2*scale, y0+0*scale);
break;
case '9':
move_xy(x0+0*scale, y0+2*scale);
move_z(1);
move_xy(x0+2*scale, y0+0*scale);
move_xy(x0+4*scale, y0+0*scale);
move_xy(x0+6*scale, y0+2*scale);
move_xy(x0+6*scale, y0+10*scale);
move_xy(x0+4*scale, y0+12*scale);
move_xy(x0+2*scale, y0+12*scale);
move_xy(x0+0*scale, y0+10*scale);
move_xy(x0+0*scale, y0+8*scale);
move_xy(x0+2*scale, y0+6*scale);
move_xy(x0+4*scale, y0+6*scale);
move_xy(x0+6*scale, y0+8*scale);
break;
case '+':
move_xy(x0+1*scale, y0+6*scale);
move_z(1);
move_xy(x0+5*scale, y0+6*scale);
move_z(0);
move_xy(x0+3*scale, y0+8*scale);
move_z(1);
move_xy(x0+3*scale, y0+4*scale);
break;
case '-':
move_xy(x0+1*scale, y0+6*scale);
move_z(1);
move_xy(x0+5*scale, y0+6*scale);
break;
case '_':
move_z(1);
move_xy(x0+6*scale, y0+0*scale);
break;
case '/':
move_z(1);
move_xy(x0+6*scale, y0+12*scale);
break;
case '.':
move_xy(x0+2*scale, y0+0*scale);
move_z(1);
move_xy(x0+2*scale, y0+2*scale);
move_xy(x0+4*scale, y0+2*scale);
move_xy(x0+4*scale, y0+0*scale);
move_xy(x0+2*scale, y0+0*scale);
break;
case ':':
move_xy(x0+2*scale, y0+0*scale);
move_z(1);
move_xy(x0+2*scale, y0+2*scale);
move_xy(x0+4*scale, y0+2*scale);
move_xy(x0+4*scale, y0+0*scale);
move_xy(x0+2*scale, y0+0*scale);
move_z(0);
move_xy(x0+2*scale, y0+4*scale);
move_z(1);
move_xy(x0+2*scale, y0+6*scale);
move_xy(x0+4*scale, y0+6*scale);
move_xy(x0+4*scale, y0+4*scale);
move_xy(x0+2*scale, y0+4*scale);
break;
case '%':
move_z(1);
move_xy(x0+6*scale, y0+12*scale);
move_z(0);
move_xy(x0+3*scale, y0+11*scale);
move_z(1);
move_xy(x0+3*scale, y0+9*scale);
move_xy(x0+1*scale, y0+9*scale);
move_xy(x0+1*scale, y0+11*scale);
move_xy(x0+3*scale, y0+11*scale);
move_z(0);
move_xy(x0+3*scale, y0+3*scale);
move_z(1);
move_xy(x0+5*scale, y0+3*scale);
move_xy(x0+5*scale, y0+1*scale);
move_xy(x0+3*scale, y0+1*scale);
move_xy(x0+3*scale, y0+3*scale);
break;
case '#':
move_xy(x0+1*scale, y0+0*scale);
move_z(1);
move_xy(x0+3*scale, y0+12*scale);
move_z(0);
move_xy(x0+5*scale, y0+12*scale);
move_z(1);
move_xy(x0+3*scale, y0+0*scale);
move_z(0);
move_xy(x0+5.33*scale, y0+4*scale);
move_z(1);
move_xy(x0+0*scale, y0+4*scale);
move_z(0);
move_xy(x0+0.67*scale, y0+8*scale);
move_z(1);
move_xy(x0+6*scale, y0+8*scale);
break;
case '(':
...
This file has been truncated, please download it to see its full contents.
Comments