Skip to content

Instantly share code, notes, and snippets.

@pteacher
Created August 23, 2022 10:40
Show Gist options
  • Save pteacher/7f36bd2debbcfbbc93867b0a47d4597f to your computer and use it in GitHub Desktop.
Save pteacher/7f36bd2debbcfbbc93867b0a47d4597f to your computer and use it in GitHub Desktop.
Makelangelo for 28byj-48 stepper motor Arduino
//------------------------------------------------------------------------------
// Draw robot
// dan@marginallycelver.com 2012 feb 11
//------------------------------------------------------------------------------
// Copyright at end of file. Please see
// http://www.github.com/MarginallyClever/Makelangelo for more information.
#define FORWARD 1
#define BACKWARD -1
//------------------------------------------------------------------------------
// CONSTANTS
//------------------------------------------------------------------------------
// Comment out this line to silence most serial output.
//#define VERBOSE (1)
// Comment out this line to disable SD cards.
//#define USE_SD_CARD (1)
// Comment out this line to disable findHome and limit switches
//#define USE_LIMIT_SWITCH (1)
// which motor is on which pin?
//#define M1_PIN (1)
//#define M2_PIN (2)
// which limit switch is on which pin?
#define L_PIN (A4)
#define R_PIN (A5)
// NEMA17 are 200 steps (1.8 degrees) per turn. If a spool is 0.8 diameter
// then it is 2.5132741228718345 circumference, and
// 2.5132741228718345 / 200 = 0.0125663706 thread moved each step.
// NEMA17 are rated up to 3000RPM. Adafruit can handle >1000RPM.
// These numbers directly affect the maximum velocity.
#define STEPS_PER_TURN (2800.0)
#define MAX_RPM (7.5)
// delay between steps, in microseconds.
#define STEP_DELAY (3500) // = 3.5ms
// *****************************************************************************
// *** Don't change the constants below unless you know what you're doing. ***
// *****************************************************************************
// switch sensitivity
#define SWITCH_HALF (512)
// servo angles for pen control
#define PEN_UP_ANGLE (170)
#define PEN_DOWN_ANGLE (10) // Some steppers don't like 0 degrees
#define PEN_DELAY (250) // in ms
#define MAX_STEPS_S (STEPS_PER_TURN*MAX_RPM/60.0) // steps/s
#define MIN_VEL (0.001) // cm/s
// for arc directions
#define ARC_CW (1)
#define ARC_CCW (-1)
// Arcs are split into many line segments. How long are the segments?
#define CM_PER_SEGMENT (0.2)
// Serial communication bitrate
#define BAUD (57600)
// Maximum length of serial input message.
#define MAX_BUF (64)
// servo pin differs based on device
#define SERVO_PIN 10
#ifndef USE_SD_CARD
#define File int
#endif
//------------------------------------------------------------------------------
// EEPROM MEMORY MAP
//------------------------------------------------------------------------------
#define EEPROM_VERSION 4 // Increment EEPROM_VERSION when adding new variables
#define ADDR_VERSION 0 // address of the version number (one byte)
#define ADDR_UUID 1 // address of the UUID (long - 4 bytes)
#define ADDR_SPOOL_DIA1 5 // address of the spool diameter (float - 4 bytes)
#define ADDR_SPOOL_DIA2 9 // address of the spool diameter (float - 4 bytes)
//------------------------------------------------------------------------------
// INCLUDES
//------------------------------------------------------------------------------
// Adafruit motor driver library
//#include <AFMotorDrawbot.h>
#include <Stepper.h>
// Default servo library
#include <Servo.h>
// SD card library
#ifdef USE_SD_CARD
#include <SD.h>
#endif
// Saving config
#include <EEPROM.h>
#include <Arduino.h> // for type definitions
//------------------------------------------------------------------------------
// VARIABLES
//------------------------------------------------------------------------------
// Initialize Adafruit stepper controller
//static AF_Stepper m1((int)STEPS_PER_TURN, M2_PIN);
//static AF_Stepper m2((int)STEPS_PER_TURN, M1_PIN);
static Stepper m2(64, 2,4,3,5);
static Stepper m1(64, 6,8,7,9);
static Servo s1;
// robot UID
int robot_uid=0;
// plotter limits
// all distances are relative to the calibration point of the plotter.
// (normally this is the center of the drawing area)
static float limit_top = 0; // distance to top of drawing area.
static float limit_bottom = 0; // Distance to bottom of drawing area.
static float limit_right = 0; // Distance to right of drawing area.
static float limit_left = 0; // Distance to left of drawing area.
// what are the motors called?
char m1d='L';
char m2d='R';
// which way are the spools wound, relative to motor movement?
int M1_REEL_IN = FORWARD;
int M1_REEL_OUT = BACKWARD;
int M2_REEL_IN = FORWARD;
int M2_REEL_OUT = BACKWARD;
// calculate some numbers to help us find feed_rate
float SPOOL_DIAMETER1 = 0.950;
float THREADPERSTEP1; // thread per step
float SPOOL_DIAMETER2 = 0.950;
float THREADPERSTEP2; // thread per step
float MAX_VEL = MAX_STEPS_S * THREADPERSTEP1; // cm/s
// plotter position.
static float posx, velx;
static float posy, vely;
static float posz; // pen state
static float feed_rate=0;
static long step_delay;
// motor position
static long laststep1, laststep2;
static char absolute_mode=1; // absolute or incremental programming mode?
static float mode_scale; // mm or inches?
static char mode_name[3];
// time values
static long t_millis;
static float t; // since board power on
static float dt; // since last tick
// Serial comm reception
static char buffer[MAX_BUF]; // Serial buffer
static int sofar; // Serial buffer progress
//------------------------------------------------------------------------------
// METHODS
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
// calculate max velocity, threadperstep.
static void adjustSpoolDiameter(float diameter1,float diameter2) {
SPOOL_DIAMETER1 = diameter1;
float SPOOL_CIRC = SPOOL_DIAMETER1*PI; // circumference
THREADPERSTEP1 = SPOOL_CIRC/STEPS_PER_TURN; // thread per step
SPOOL_DIAMETER2 = diameter2;
SPOOL_CIRC = SPOOL_DIAMETER2*PI; // circumference
THREADPERSTEP2 = SPOOL_CIRC/STEPS_PER_TURN; // thread per step
float MAX_VEL1 = MAX_STEPS_S * THREADPERSTEP1; // cm/s
float MAX_VEL2 = MAX_STEPS_S * THREADPERSTEP2; // cm/s
MAX_VEL = MAX_VEL1 > MAX_VEL2 ? MAX_VEL1 : MAX_VEL2;
// Serial.print(F("SpoolDiameter1 = "); Serial.println(F(SPOOL_DIAMETER1,3));
// Serial.print(F("SpoolDiameter2 = "); Serial.println(F(SPOOL_DIAMETER2,3));
}
//------------------------------------------------------------------------------
// increment internal clock
static void tick() {
long nt_millis=millis();
long dt_millis=nt_millis-t_millis;
t_millis=nt_millis;
dt=(float)dt_millis*0.001; // time since last tick, in seconds
t=(float)nt_millis*0.001;
}
//------------------------------------------------------------------------------
// returns angle of dy/dx as a value from 0...2PI
static float atan3(float dy,float dx) {
float a=atan2(dy,dx);
if(a<0) a=(PI*2.0)+a;
return a;
}
//------------------------------------------------------------------------------
static char readSwitches() {
#ifdef USE_LIMIT_SWITCH
// get the current switch state
return ( (analogRead(L_PIN) < SWITCH_HALF) | (analogRead(R_PIN) < SWITCH_HALF) );
#else
return 0;
#endif // USE_LIMIT_SWITCH
}
//------------------------------------------------------------------------------
// feed rate is given in units/min and converted to cm/s
static void setFeedRate(float v) {
float v1 = v * mode_scale/60.0;
if( feed_rate != v1 ) {
feed_rate = v1;
if(feed_rate > MAX_VEL) feed_rate=MAX_VEL;
if(feed_rate < MIN_VEL) feed_rate=MIN_VEL;
}
long step_delay1 = 1000000.0 / (feed_rate/THREADPERSTEP1);
long step_delay2 = 1000000.0 / (feed_rate/THREADPERSTEP2);
step_delay = step_delay1 > step_delay2 ? step_delay1 : step_delay2;
Serial.print(F("step_delay="));
Serial.println(step_delay);
}
//------------------------------------------------------------------------------
static void printFeedRate() {
Serial.print(F("f1="));
Serial.print(feed_rate*60.0/mode_scale);
Serial.print(mode_name);
Serial.print(F("/min"));
}
//------------------------------------------------------------------------------
// Change pen state.
static void setPenAngle(int pen_angle) {
if(posz!=pen_angle) {
posz=pen_angle;
if(posz<PEN_DOWN_ANGLE) posz=PEN_DOWN_ANGLE;
if(posz>PEN_UP_ANGLE ) posz=PEN_UP_ANGLE;
s1.write( (int)posz );
delay(PEN_DELAY);
}
}
//------------------------------------------------------------------------------
// Inverse Kinematics - turns XY coordinates into lengths L1,L2
static void IK(float x, float y, long &l1, long &l2) {
// find length to M1
float dy = y - limit_top;
float dx = x - limit_left;
l1 = floor( sqrt(dx*dx+dy*dy) / THREADPERSTEP1 );
// find length to M2
dx = limit_right - x;
l2 = floor( sqrt(dx*dx+dy*dy) / THREADPERSTEP2 );
}
//------------------------------------------------------------------------------
// Forward Kinematics - turns L1,L2 lengths into XY coordinates
// use law of cosines: theta = acos((a*a+b*b-c*c)/(2*a*b));
// to find angle between M1M2 and M1P where P is the plotter position.
static void FK(float l1, float l2,float &x,float &y) {
float a = l1 * THREADPERSTEP1;
float b = (limit_right-limit_left);
float c = l2 * THREADPERSTEP2;
// slow, uses trig
//float theta = acos((a*a+b*b-c*c)/(2.0*a*b));
//x = cos(theta)*l1 + limit_left;
//y = sin(theta)*l1 + limit_top;
// but we know that cos(acos(i)) = i
// and we know that sin(acos(i)) = sqrt(1-i*i)
float i=(a*a+b*b-c*c)/(2.0*a*b);
x = i * l1 + limit_left;
y = sqrt(1.0 - i*i)*l1 + limit_top;
}
//------------------------------------------------------------------------------
static void line_safe(float x,float y,float z) {
// split up long lines to make them straighter?
float dx=x-posx;
float dy=y-posy;
float len=sqrt(dx*dx+dy*dy);
if(len<=CM_PER_SEGMENT) {
line(x,y,z);
return;
}
// too long!
long pieces=floor(len/CM_PER_SEGMENT);
float x0=posx;
float y0=posy;
float z0=posz;
float a;
for(long j=0;j<=pieces;++j) {
a=(float)j/(float)pieces;
line((x-x0)*a+x0,
(y-y0)*a+y0,
(z-z0)*a+z0);
}
line(x,y,z);
}
//------------------------------------------------------------------------------
void pause(long ms) {
delay(ms/1000);
delayMicroseconds(ms%1000);
}
//------------------------------------------------------------------------------
static void line(float x,float y,float z) {
long l1,l2;
IK(x,y,l1,l2);
long d1 = l1 - laststep1;
long d2 = l2 - laststep2;
long ad1=abs(d1);
long ad2=abs(d2);
int dir1=d1<0?M1_REEL_IN:M1_REEL_OUT;
int dir2=d2<0?M2_REEL_IN:M2_REEL_OUT;
long over=0;
long i;
setPenAngle((int)z);
// bresenham's line algorithm.
if(ad1>ad2) {
for(i=0;i<ad1;++i) {
m1.step(dir1);
over+=ad2;
if(over>=ad1) {
over-=ad1;
m2.step(dir2);
}
pause(step_delay);
if(readSwitches()) return;
}
} else {
for(i=0;i<ad2;++i) {
m2.step(dir2);
over+=ad1;
if(over>=ad2) {
over-=ad2;
m1.step(dir1);
}
pause(step_delay);
if(readSwitches()) return;
}
}
laststep1=l1;
laststep2=l2;
posx=x;
posy=y;
}
//------------------------------------------------------------------------------
// This method assumes the limits have already been checked.
// This method assumes the start and end radius match.
// This method assumes arcs are not >180 degrees (PI radians)
// cx/cy - center of circle
// x/y - end position
// dir - ARC_CW or ARC_CCW to control direction of arc
static void arc(float cx,float cy,float x,float y,float z,float dir) {
// get radius
float dx = posx - cx;
float dy = posy - cy;
float radius=sqrt(dx*dx+dy*dy);
// find angle of arc (sweep)
float angle1=atan3(dy,dx);
float angle2=atan3(y-cy,x-cx);
float theta=angle2-angle1;
if(dir>0 && theta<0) angle2+=2*PI;
else if(dir<0 && theta>0) angle1+=2*PI;
theta=angle2-angle1;
// get length of arc
// float circ=PI*2.0*radius;
// float len=theta*circ/(PI*2.0);
// simplifies to
float len = abs(theta) * radius;
int i, segments = floor( len / CM_PER_SEGMENT );
float nx, ny, nz, angle3, scale;
for(i=0;i<segments;++i) {
// interpolate around the arc
scale = ((float)i)/((float)segments);
angle3 = ( theta * scale ) + angle1;
nx = cx + cos(angle3) * radius;
ny = cy + sin(angle3) * radius;
nz = ( z - posz ) * scale + posz;
// send it to the planner
line(nx,ny,nz);
}
line(x,y,z);
}
//------------------------------------------------------------------------------
// instantly move the virtual plotter position
// does not validate if the move is valid
static void teleport(float x,float y) {
posx=x;
posy=y;
// @TODO: posz?
long L1,L2;
IK(posx,posy,L1,L2);
laststep1=L1;
laststep2=L2;
}
//------------------------------------------------------------------------------
static void help() {
Serial.println(F("== DRAWBOT - http://github.com/i-make-robots/Drawbot/ =="));
Serial.println(F("All commands end with a semi-colon."));
Serial.println(F("HELP; - display this message"));
Serial.println(F("CONFIG [Tx.xx] [Bx.xx] [Rx.xx] [Lx.xx];"));
Serial.println(F(" - display/update this robot's configuration."));
Serial.println(F("TELEPORT [Xx.xx] [Yx.xx]; - move the virtual plotter."));
Serial.println(F("As well as the following G-codes (http://en.wikipedia.org/wiki/G-code):"));
Serial.println(F("G00,G01,G02,G03,G04,G20,G21,G28,G90,G91,M18,M114"));
}
//------------------------------------------------------------------------------
// find the current robot position and
static void FindHome() {
#ifdef USE_LIMIT_SWITCH
Serial.println(F("Homing..."));
if(readSwitches()) {
Serial.println(F("** ERROR **"));
Serial.println(F("Problem: Plotter is already touching switches."));
Serial.println(F("Solution: Please unwind the strings a bit and try again."));
return;
}
int step_delay=3;
int safe_out=50;
// reel in the left motor until contact is made.
Serial.println(F("Find left..."));
do {
m1.step(M1_REEL_IN );
m2.step(M2_REEL_OUT);
delay(step_delay);
} while(!readSwitches());
laststep1=0;
// back off so we don't get a false positive on the next motor
int i;
for(i=0;i<safe_out;++i) {
m1.step(M1_REEL_OUT);
delay(step_delay);
}
laststep1=safe_out;
// reel in the right motor until contact is made
Serial.println(F("Find right..."));
do {
m1.step(M1_REEL_OUT);
m2.step(M2_REEL_IN );
delay(step_delay);
laststep1++;
} while(!readSwitches());
laststep2=0;
// back off so we don't get a false positive that kills line()
for(i=0;i<safe_out;++i) {
m2.step(M2_REEL_OUT);
delay(step_delay);
}
laststep2=safe_out;
Serial.println(F("Centering..."));
line(0,0,posz);
#endif // USE_LIMIT_SWITCH
}
//------------------------------------------------------------------------------
static void where() {
Serial.print(F("X"));
Serial.print(posx);
Serial.print(F(" Y"));
Serial.print(posy);
Serial.print(F(" Z"));
Serial.print(posz);
Serial.print(F(" F"));
printFeedRate();
Serial.print(F("\n"));
}
//------------------------------------------------------------------------------
static void printConfig() {
Serial.print(m1d); Serial.print(F("="));
Serial.print(limit_top); Serial.print(F(","));
Serial.print(limit_left); Serial.print(F("\n"));
Serial.print(m2d); Serial.print(F("="));
Serial.print(limit_top); Serial.print(F(","));
Serial.print(limit_right); Serial.print(F("\n"));
Serial.print(F("Bottom=")); Serial.println(limit_bottom);
Serial.print(F("Feed rate=")); printFeedRate();
}
//------------------------------------------------------------------------------
// from http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1234477290/3
void EEPROM_writeLong(int ee, long value) {
byte* p = (byte*)(void*)&value;
for (int i = 0; i < sizeof(value); i++)
EEPROM.write(ee++, *p++);
}
//------------------------------------------------------------------------------
// from http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1234477290/3
float EEPROM_readLong(int ee) {
long value = 0;
byte* p = (byte*)(void*)&value;
for (int i = 0; i < sizeof(value); i++)
*p++ = EEPROM.read(ee++);
return value;
}
//------------------------------------------------------------------------------
static void LoadConfig() {
char version_number=EEPROM.read(ADDR_VERSION);
if(version_number<3 || version_number>EEPROM_VERSION) {
// If not the current EEPROM_VERSION or the EEPROM_VERSION is sullied (i.e. unknown data)
// Update the version number
EEPROM.write(ADDR_VERSION,EEPROM_VERSION);
// Update robot uuid
robot_uid=0;
SaveUID();
// Update spool diameter variables
SaveSpoolDiameter();
}
if(version_number==3) {
// Retrieve Stored Configuration
robot_uid=EEPROM_readLong(ADDR_UUID);
adjustSpoolDiameter((float)EEPROM_readLong(ADDR_SPOOL_DIA1)/10000.0f,
(float)EEPROM_readLong(ADDR_SPOOL_DIA1)/10000.0f); //3 decimal places of percision is enough
// save the new data so the next load doesn't screw up one bobbin size
SaveSpoolDiameter();
// update the EEPROM version
EEPROM.write(ADDR_VERSION,EEPROM_VERSION);
} else if(version_number==EEPROM_VERSION) {
// Retrieve Stored Configuration
robot_uid=EEPROM_readLong(ADDR_UUID);
adjustSpoolDiameter((float)EEPROM_readLong(ADDR_SPOOL_DIA1)/10000.0f,
(float)EEPROM_readLong(ADDR_SPOOL_DIA2)/10000.0f); //3 decimal places of percision is enough
} else {
// Code should not get here if it does we should display some meaningful error message
Serial.println(F("An Error Occurred during LoadConfig"));
}
}
//------------------------------------------------------------------------------
static void SaveUID() {
EEPROM_writeLong(ADDR_UUID,(long)robot_uid);
}
//------------------------------------------------------------------------------
static void SaveSpoolDiameter() {
EEPROM_writeLong(ADDR_SPOOL_DIA1,SPOOL_DIAMETER1*10000);
EEPROM_writeLong(ADDR_SPOOL_DIA2,SPOOL_DIAMETER2*10000);
}
#ifdef USE_SD_CARD
//------------------------------------------------------------------------------
void SD_PrintDirectory(File dir, int numTabs) {
while(true) {
File entry = dir.openNextFile();
if (! entry) {
// no more files
Serial.println(F("**nomorefiles**"));
}
for (uint8_t i=0; i<numTabs; i++) {
Serial.print('\t');
}
Serial.print(entry.name());
if (entry.isDirectory()) {
Serial.println(F("/"));
SD_PrintDirectory(entry, numTabs+1);
} else {
// files have sizes, directories do not
Serial.print(F("\t\t"));
Serial.println(entry.size(), DEC);
}
}
}
#endif // USE_SD_CARD
//------------------------------------------------------------------------------
static void SD_ListFiles() {
#ifdef USE_SD_CARD
File f = SD.open("/");
SD_PrintDirectory(f,0);
#endif // USE_SD_CARD
}
//------------------------------------------------------------------------------
static void SD_ProcessFile(char *filename) {
#ifdef USE_SD_CARD
File f=SD.open(filename);
if(!f) {
Serial.print(F("File "));
Serial.print(filename);
Serial.println(F(" not found."));
return;
}
int c;
while(f.peek() != -1) {
c=f.read();
if(c=='\n' || c=='\r') continue;
buffer[sofar++]=c;
if(buffer[sofar]==';') {
// end string
buffer[sofar]=0;
// print for our benefit
Serial.println(buffer);
// process command
processCommand();
// reset buffer for next line
sofar=0;
}
}
f.close();
#endif // USE_SD_CARD
}
//------------------------------------------------------------------------------
static int processSubcommand() {
int found=0;
char *ptr=buffer;
while(ptr && ptr<buffer+sofar && strlen(ptr)) {
if(!strncmp(ptr,"G20",3)) {
mode_scale=2.54f; // inches -> cm
strcpy(mode_name,"in");
printFeedRate();
found=1;
} else if(!strncmp(ptr,"G21",3)) {
mode_scale=0.1; // mm -> cm
strcpy(mode_name,"mm");
printFeedRate();
found=1;
} else if(!strncmp(ptr,"G90",3)) {
// absolute mode
absolute_mode=1;
found=1;
} else if(!strncmp(ptr,"G91",3)) {
// relative mode
absolute_mode=0;
found=1;
}
ptr=strchr(ptr,' ')+1;
}
return found;
}
//------------------------------------------------------------------------------
static void processCommand() {
// blank lines
if(buffer[0]==';') return;
if(!strncmp(buffer,"HELP",4)) {
help();
} else if(!strncmp(buffer,"UID",3)) {
robot_uid=atoi(strchr(buffer,' ')+1);
SaveUID();
} else if(!strncmp(buffer,"G28",3)) {
FindHome();
} else if(!strncmp(buffer,"TELEPORT",8)) {
float xx=posx;
float yy=posy;
char *ptr=buffer;
while(ptr && ptr<buffer+sofar) {
ptr=strchr(ptr,' ')+1;
switch(*ptr) {
case 'X': xx=atof(ptr+1)*mode_scale; break;
case 'Y': yy=atof(ptr+1)*mode_scale; break;
default: ptr=0; break;
}
}
teleport(xx,yy);
} else
if(!strncmp(buffer,"M114",4)) {
where();
} else if(!strncmp(buffer,"M18",3)) {
// disable motors
//m1.release();
//m2.release();
} else if(!strncmp(buffer,"CONFIG",6)) {
float tt=limit_top;
float bb=limit_bottom;
float rr=limit_right;
float ll=limit_left;
char gg=m1d;
char hh=m2d;
char *ptr=buffer;
while(ptr && ptr<buffer+sofar && strlen(ptr)) {
ptr=strchr(ptr,' ')+1;
switch(*ptr) {
case 'T': tt=atof(ptr+1); break;
case 'B': bb=atof(ptr+1); break;
case 'R': rr=atof(ptr+1); break;
case 'L': ll=atof(ptr+1); break;
case 'G': gg=*(ptr+1); break;
case 'H': hh=*(ptr+1); break;
case 'I':
if(atoi(ptr+1)>0) {
M1_REEL_IN=FORWARD;
M1_REEL_OUT=BACKWARD;
} else {
M1_REEL_IN=BACKWARD;
M1_REEL_OUT=FORWARD;
}
break;
case 'J':
if(atoi(ptr+1)>0) {
M2_REEL_IN=FORWARD;
M2_REEL_OUT=BACKWARD;
} else {
M2_REEL_IN=BACKWARD;
M2_REEL_OUT=FORWARD;
}
break;
}
}
// @TODO: check t>b, r>l ?
limit_top=tt;
limit_bottom=bb;
limit_right=rr;
limit_left=ll;
m1d=gg;
m2d=hh;
teleport(0,0);
printConfig();
} else if(!strncmp(buffer,"G00 ",4) || !strncmp(buffer,"G01 ",4)
|| !strncmp(buffer,"G0 " ,3) || !strncmp(buffer,"G1 " ,3) ) {
// line
processSubcommand();
float xx, yy, zz;
if(absolute_mode==1) {
xx=posx;
yy=posy;
zz=posz;
} else {
xx=0;
yy=0;
zz=0;
}
char *ptr=buffer;
while(ptr && ptr<buffer+sofar && strlen(ptr)) {
ptr=strchr(ptr,' ')+1;
switch(*ptr) {
case 'X': xx=atof(ptr+1)*mode_scale; break;
case 'Y': yy=atof(ptr+1)*mode_scale; break;
case 'Z': zz=atof(ptr+1); break;
case 'F': setFeedRate(atof(ptr+1)); break;
}
}
if(absolute_mode==0) {
xx+=posx;
yy+=posy;
zz+=posz;
}
line_safe(xx,yy,zz);
} else if(!strncmp(buffer,"G02 ",4) || !strncmp(buffer,"G2 " ,3)
|| !strncmp(buffer,"G03 ",4) || !strncmp(buffer,"G3 " ,3)) {
// arc
processSubcommand();
float xx, yy, zz;
float dd = (!strncmp(buffer,"G02",3) || !strncmp(buffer,"G2",2)) ? -1 : 1;
float ii = 0;
float jj = 0;
if(absolute_mode==1) {
xx=posx;
yy=posy;
zz=posz;
} else {
xx=0;
yy=0;
zz=0;
}
char *ptr=buffer;
while(ptr && ptr<buffer+sofar && strlen(ptr)) {
ptr=strchr(ptr,' ')+1;
switch(*ptr) {
case 'I': ii=atof(ptr+1)*mode_scale; break;
case 'J': jj=atof(ptr+1)*mode_scale; break;
case 'X': xx=atof(ptr+1)*mode_scale; break;
case 'Y': yy=atof(ptr+1)*mode_scale; break;
case 'Z': zz=atof(ptr+1); break;
case 'F': setFeedRate(atof(ptr+1)); break;
}
}
if(absolute_mode==0) {
xx+=posx;
yy+=posy;
zz+=posz;
}
arc(posx+ii,posy+jj,xx,yy,zz,dd);
} else if(!strncmp(buffer,"G04 ",4) || !strncmp(buffer,"G4 ",3)) {
// dwell
long xx=0;
char *ptr=buffer;
while(ptr && ptr<buffer+sofar && strlen(ptr)) {
ptr=strchr(ptr,' ')+1;
switch(*ptr) {
case 'X':
case 'U':
case 'P': xx=atoi(ptr+1); break;
}
}
delay(xx);
} else if(!strncmp(buffer,"D00 ",4)) {
// move one motor
char *ptr=strchr(buffer,' ')+1;
int amount = atoi(ptr+1);
int i, dir;
if(*ptr == m1d) {
dir = amount < 0 ? M1_REEL_IN : M1_REEL_OUT;
amount=abs(amount);
for(i=0;i<amount;++i) { m1.step(dir); delay(2); }
} else if(*ptr == m2d) {
dir = amount < 0 ? M2_REEL_IN : M2_REEL_OUT;
amount = abs(amount);
for(i=0;i<amount;++i) { m2.step(dir); delay(2); }
}
} else if(!strncmp(buffer,"D01 ",4)) {
// adjust spool diameters
float amountL=SPOOL_DIAMETER1;
float amountR=SPOOL_DIAMETER2;
char *ptr=buffer;
while(ptr && ptr<buffer+sofar && strlen(ptr)) {
ptr=strchr(ptr,' ')+1;
switch(*ptr) {
case 'L': amountL=atof(ptr+1); break;
case 'R': amountR=atof(ptr+1); break;
}
}
float tps1=THREADPERSTEP1;
float tps2=THREADPERSTEP2;
adjustSpoolDiameter(amountL,amountR);
if(THREADPERSTEP1 != tps1 || THREADPERSTEP2 != tps2) {
// Update EEPROM
SaveSpoolDiameter();
}
} else if(!strncmp(buffer,"D02 ",4)) {
Serial.print('L');
Serial.print(SPOOL_DIAMETER1);
Serial.print(F(" R"));
Serial.println(SPOOL_DIAMETER2);
} else if(!strncmp(buffer,"D03 ",4)) {
// read directory
SD_ListFiles();
} else if(!strncmp(buffer,"D04 ",4)) {
// read file
SD_ProcessFile(strchr(buffer,' ')+1);
} else {
if(processSubcommand()==0) {
Serial.print(F("Invalid command '"));
Serial.print(buffer);
Serial.println(F("'"));
}
}
}
//------------------------------------------------------------------------------
void setup() {
LoadConfig();
// initialize the read buffer
sofar=0;
// start communications
Serial.begin(BAUD);
Serial.print(F("\n\nHELLO WORLD! I AM DRAWBOT #"));
Serial.println(robot_uid);
#ifdef USE_SD_CARD
SD.begin();
SD_ListFiles();
#endif
// initialize the scale
strcpy(mode_name,"mm");
mode_scale=0.1;
setFeedRate(MAX_VEL*30/mode_scale); // *30 because i also /2
// servo should be on SER1, pin 10.
s1.attach(SERVO_PIN);
// turn on the pull up resistor
digitalWrite(L_PIN,HIGH);
digitalWrite(R_PIN,HIGH);
// display the help at startup.
help();
// initialize the plotter position.
teleport(0,0);
velx=0;
velx=0;
setPenAngle(PEN_UP_ANGLE);
Serial.print(F("> "));
}
//------------------------------------------------------------------------------
void loop() {
// See: http://www.marginallyclever.com/2011/10/controlling-your-arduino-through-the-serial-monitor/
// listen for serial commands
while(Serial.available() > 0) {
buffer[sofar++]=Serial.read();
if(buffer[sofar-1]==';') break; // in case there are multiple instructions
}
// if we hit a semi-colon, assume end of instruction.
if(sofar>0 && buffer[sofar-1]==';') {
buffer[sofar]=0;
// echo confirmation
// Serial.println(F(buffer));
// do something with the command
processCommand();
// reset the buffer
sofar=0;
// echo completion
Serial.print(F("> "));
}
}
/**
* This file is part of DrawbotGUI.
*
* DrawbotGUI is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* DrawbotGUI is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Foobar. If not, see <http://www.gnu.org/licenses/>.
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment