TinyCNC_Gcode_XY_noZ.ino 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492
  1. /*
  2. Tiny 2-Axis CNC Sketch to draw shapes
  3. derived from the original code for 3-axis PlotterBot:
  4. https://github.com/MakerBlock/TinyCNC-Sketches
  5. The point of modifications is to run it on a 2-axis
  6. using the MakerBot Unicorn Inkscape plugin available here
  7. https://github.com/martymcguire/inkscape-unicorn
  8. */
  9. // Note we commented out all Z related code (lift the pen code)
  10. #include <Servo.h>
  11. #define SERVOPINX 11
  12. #define SERVOPINY 10
  13. //#define SERVOPINZ 9
  14. #define LINE_BUFFER_LENGTH 512
  15. /* Structures, global variables */
  16. struct point {
  17. float x;
  18. float y;
  19. // float z;
  20. };
  21. /* Naming servos */
  22. Servo servoX;
  23. Servo servoY;
  24. //Servo servoZ;
  25. struct point actuatorPos;
  26. // Gear settings
  27. float gearD = 40; //56; // not sure the gear is the same
  28. //float ZgearD = 13.3239;
  29. // Drawing settings
  30. int StepIncPower = 3;
  31. float StepInc = (1/ pow(2,StepIncPower));
  32. int StepDelay = 0;
  33. int LineDelay = 50;
  34. int penDelay = 50;
  35. // Rounding the mm2deg functions corrects for floating point errors
  36. float PowerRound = 2;
  37. // Drawing robot limits, in degrees
  38. float Xdmin = 17; // 8.31mm
  39. float Xdmax = 171; // 83.57mm
  40. float Ydmin = 25; // Functionally 18 degrees // 12.22mm
  41. float Ydmax = 146; // Functionally 146 // 71.35mm
  42. float Zdmin = 18;
  43. float Zdmax = 50;
  44. //////////////////////////////////////////
  45. // Functions for Calculating Useful Stuff
  46. //////////////////////////////////////////
  47. // Converts mm to degrees for the servos
  48. float mm2deg(float mm) {
  49. return mm/PI/gearD*360;
  50. }
  51. // Converts mm to degrees for the servos
  52. float deg2mm(float deg) {
  53. return PI*gearD*deg/360;
  54. }
  55. // Function for getting Y to draw a circle
  56. float returnY(float x, float x0, float rad)
  57. {
  58. return ((int) ((pow( pow(rad, 2) - pow((x-x0), 2) , 0.5))*pow(10,PowerRound)))/pow(10,PowerRound);
  59. }
  60. // Function for determining distance between two points
  61. float lineDist(float x0, float y0, float x1, float y1)
  62. {
  63. // return ((int) (pow(pow(x1-x0, 2) + pow(y1-y0, 2), 0.5) * pow(10,PowerRound)))/pow(10,PowerRound);
  64. float temp = pow(pow(x1-x0, 2) + pow(y1-y0, 2), 0.5);
  65. // Serial.println(temp);
  66. temp = (int) temp * pow(10,PowerRound);
  67. // Serial.println(temp);
  68. temp = temp/pow(10,PowerRound);
  69. // Serial.println(temp);
  70. return temp;
  71. }
  72. // Drawing robot limits, in mm
  73. float Xmin = deg2mm(Xdmin);
  74. float Xmax = deg2mm(Xdmax);
  75. float Ymin = deg2mm(Ydmin);
  76. float Ymax = deg2mm(Ydmax);
  77. float Zmin = Zdmin;
  78. float Zmax = Zdmax;
  79. float Xpos = Xdmin;
  80. float Ypos = Ydmin;
  81. float Zpos = Zmax;
  82. boolean verbose = true; //false;
  83. // Needs to interpret
  84. // G1 for moving
  85. // G4 P300 (wait 150ms)
  86. // M300 S30 (pen down)
  87. // M300 S50 (pen up)
  88. // Discard anything with a (
  89. // Discard any other command!
  90. /**********************
  91. * void setup() - Initialisations
  92. ***********************/
  93. void setup() {
  94. // Setup
  95. Serial.begin( 115200 );
  96. // Attach servos
  97. servoX.attach( SERVOPINX );
  98. servoY.attach( SERVOPINY );
  99. // servoZ.attach( SERVOPINZ );
  100. // Set & move to initial default position
  101. servoX.write(Xpos);
  102. servoY.write(Ypos);
  103. // servoZ.write(Zpos);
  104. // Notifications!!!
  105. Serial.println("Robot Team Go!!!");
  106. Serial.print("Step increment is ");
  107. Serial.print(StepInc);
  108. Serial.println(" degrees");
  109. Serial.print("X range is from ");
  110. Serial.print(Xmin);
  111. Serial.print(" to ");
  112. Serial.print(Xmax);
  113. Serial.println(" mm.");
  114. Serial.print("Y range is from ");
  115. Serial.print(Ymin);
  116. Serial.print(" to ");
  117. Serial.print(Ymax);
  118. Serial.println(" mm.");
  119. }
  120. /**********************
  121. * void loop() - Main loop
  122. ***********************/
  123. void loop()
  124. {
  125. delay(1000);
  126. char line[ LINE_BUFFER_LENGTH ];
  127. char c;
  128. int lineIndex;
  129. bool lineIsComment, lineSemiColon;
  130. lineIndex = 0;
  131. lineSemiColon = false;
  132. lineIsComment = false;
  133. while (1) {
  134. // Serial reception - Mostly from Grbl, added semicolon support
  135. while ( Serial.available()>0 ) {
  136. c = Serial.read();
  137. if (( c == '\n') || (c == '\r') ) { // End of line reached
  138. if ( lineIndex > 0 ) { // Line is complete. Then execute!
  139. line[ lineIndex ] = '\0'; // Terminate string
  140. if (verbose) {
  141. Serial.print( "Received : ");
  142. Serial.println( line );
  143. }
  144. processIncomingLine( line, lineIndex );
  145. lineIndex = 0;
  146. }
  147. else {
  148. // Empty or comment line. Skip block.
  149. }
  150. lineIsComment = false;
  151. lineSemiColon = false;
  152. }
  153. else {
  154. if ( (lineIsComment) || (lineSemiColon) ) { // Throw away all comment characters
  155. if ( c == ')' ) lineIsComment = false; // End of comment. Resume line.
  156. }
  157. else {
  158. if ( c <= ' ' ) { // Throw away whitepace and control characters
  159. }
  160. else if ( c == '/' ) { // Block delete not supported. Ignore character.
  161. }
  162. else if ( c == '(' ) { // Enable comments flag and ignore all characters until ')' or EOL.
  163. lineIsComment = true;
  164. }
  165. else if ( c == ';' ) {
  166. lineSemiColon = true;
  167. }
  168. else if ( lineIndex >= LINE_BUFFER_LENGTH-1 ) {
  169. Serial.println( "ERROR - lineBuffer overflow" );
  170. lineIsComment = false;
  171. lineSemiColon = false;
  172. }
  173. else if ( c >= 'a' && c <= 'z' ) { // Upcase lowercase
  174. line[ lineIndex++ ] = c-'a'+'A';
  175. }
  176. else {
  177. line[ lineIndex++ ] = c;
  178. }
  179. }
  180. }
  181. }
  182. }
  183. }
  184. void processIncomingLine( char* line, int charNB ) {
  185. int currentIndex = 0;
  186. char buffer[ 64 ]; // Hope that 64 is enough for 1 parameter
  187. struct point newPos;
  188. newPos.x = 0.0;
  189. newPos.y = 0.0;
  190. // Needs to interpret
  191. // G1 for moving
  192. // G4 P300 (wait 150ms)
  193. // G1 X60 Y30
  194. // G1 X30 Y50
  195. // M300 S30 (pen down)
  196. // M300 S50 (pen up)
  197. // Discard anything with a (
  198. // Discard any other command!
  199. while( currentIndex < charNB ) {
  200. switch ( line[ currentIndex++ ] ) { // Select command, if any
  201. case 'U':
  202. penUp();
  203. break;
  204. case 'D':
  205. penDown();
  206. break;
  207. case 'G':
  208. buffer[0] = line[ currentIndex++ ]; // /!\ Dirty - Only works with 2 digit commands
  209. // buffer[1] = line[ currentIndex++ ];
  210. // buffer[2] = '\0';
  211. buffer[1] = '\0';
  212. switch ( atoi( buffer ) ){ // Select G command
  213. case 0: // G00 & G01 - Movement or fast movement. Same here
  214. case 1:
  215. // /!\ Dirty - Suppose that X is before Y
  216. char* indexX = strchr( line+currentIndex, 'X' ); // Get X/Y position in the string (if any)
  217. char* indexY = strchr( line+currentIndex, 'Y' );
  218. if ( indexY <= 0 ) {
  219. newPos.x = atof( indexX + 1);
  220. newPos.y = actuatorPos.y;
  221. }
  222. else if ( indexX <= 0 ) {
  223. newPos.y = atof( indexY + 1);
  224. newPos.x = actuatorPos.x;
  225. }
  226. else {
  227. newPos.y = atof( indexY + 1);
  228. indexY = '\0';
  229. newPos.x = atof( indexX + 1);
  230. }
  231. Serial.println("OK");
  232. drawLine((int) newPos.x, (int) newPos.y );
  233. actuatorPos.x = newPos.x;
  234. actuatorPos.y = newPos.y;
  235. break;
  236. }
  237. break;
  238. case 'M':
  239. buffer[0] = line[ currentIndex++ ]; // /!\ Dirty - Only works with 3 digit commands
  240. buffer[1] = line[ currentIndex++ ];
  241. buffer[2] = line[ currentIndex++ ];
  242. buffer[3] = '\0';
  243. switch ( atoi( buffer ) ){
  244. case 300:
  245. {
  246. char* indexS = strchr( line+currentIndex, 'S' );
  247. float Spos = atof( indexS + 1);
  248. Serial.println("OK");
  249. if (Spos == 30) { penDown(); }
  250. if (Spos == 50) { penUp(); }
  251. break;
  252. }
  253. case 114: // M114 - Repport position
  254. Serial.print( "Absolute position : X = " );
  255. Serial.print( actuatorPos.x );
  256. Serial.print( " - Y = " );
  257. Serial.println( actuatorPos.y );
  258. break;
  259. default:
  260. Serial.print( "Command not recognized : M");
  261. Serial.println( buffer );
  262. }
  263. }
  264. }
  265. }
  266. // Draw a circle spiral!
  267. void drawCircleSpiral(float x0, float y0, float rad, float dec)
  268. {
  269. int i = 0;
  270. while (rad-dec*i > StepInc) {
  271. drawCircle(x0, y0, rad-dec*i);
  272. i++;
  273. }
  274. }
  275. // Draw a circle!
  276. void drawCircle(float x0, float y0, float rad)
  277. {
  278. // Lift the pen, go to leftmost point on circle, set the pen down
  279. penUp();
  280. drawLine(x0-rad, y0);
  281. penDown();
  282. float x1, y1, dist;
  283. // This will draw the top half of the circle!
  284. for (int i=0; i*StepInc < rad*2; i++ )
  285. {
  286. x1 = x0+StepInc*i-rad;
  287. y1 = y0 + returnY(x0+StepInc*i-rad, x0, rad);
  288. dist = lineDist(deg2mm(Xpos), deg2mm(Ypos), x1, y1);
  289. while (dist < StepInc)
  290. {
  291. i++;
  292. x1 = x0+StepInc*i-rad;
  293. y1 = y0 + returnY(x0+StepInc*i-rad, x0, rad);
  294. dist = lineDist(deg2mm(Xpos), deg2mm(Ypos), x1, y1);
  295. }
  296. drawLine(x1, y1);
  297. }
  298. // This should draw the bottom half of the circle!
  299. for (int i=0; i*StepInc < rad*2; i++ )
  300. {
  301. x1 = x0+rad - StepInc*i;
  302. y1 = y0 -returnY(x0-StepInc*i+rad, x0, rad);
  303. dist = lineDist(deg2mm(Xpos), deg2mm(Ypos), x1, y1);
  304. while (dist < StepInc)
  305. {
  306. i++;
  307. x1 = x0-StepInc*i+rad;
  308. y1 = y0 - returnY(x0-StepInc*i+rad, x0, rad);
  309. dist = lineDist(deg2mm(Xpos), deg2mm(Ypos), x1, y1);
  310. }
  311. drawLine(x1, y1);
  312. }
  313. // Complete the circle, pen up!
  314. drawLine(x0-rad, y0);
  315. }
  316. // Draw a rectangular spiral using just two points
  317. void drawRectSpiral(float x0, float y0, float x1, float y1, float dec, boolean toggle)
  318. {
  319. drawLine(x0,y0);
  320. for (int i=0; ((x1-x0)/2 > dec*i) && ((y1-y0)/2 > dec*i);i++)
  321. {
  322. drawRect(x0+dec*i,y0+dec*i,x1-dec*i,y1-dec*i, toggle);
  323. }
  324. }
  325. // Draw a rectangle using just two points
  326. void drawRect(float x0, float y0, float x1, float y1, boolean toggle)
  327. {
  328. penUp();
  329. drawLine(x0,y0);
  330. if (toggle) {
  331. penUp();
  332. delay(penDelay);
  333. penDown();
  334. }
  335. else {
  336. penDown();
  337. }
  338. drawLine(x1,y0);
  339. if (toggle) {
  340. penUp();
  341. delay(penDelay);
  342. penDown();
  343. }
  344. drawLine(x1,y1);
  345. if (toggle) {
  346. penUp();
  347. delay(penDelay);
  348. penDown();
  349. }
  350. drawLine(x0,y1);
  351. if (toggle) {
  352. penUp();
  353. delay(penDelay);
  354. penDown();
  355. }
  356. drawLine(x0,y0);
  357. if (toggle) {
  358. penUp();
  359. delay(penDelay);
  360. penDown();
  361. }
  362. }
  363. /*********************************
  364. * Draw a line from (x0;y0) to (x1;y1). Bresenham algorythm from http://rosettacode.org/wiki/Bitmap/Bresenham's_line_algorithm
  365. * int (x1;y1) : Starting coordinates
  366. * int (x2;y2) : Ending coordinates
  367. * Modified only require destination, new step min distance, max/min travel
  368. **********************************/
  369. void drawLine(float x1, float y1) {
  370. // Convert coordinatesz to degrees
  371. x1 = ((int) mm2deg(x1)*pow(10,PowerRound))/pow(10,PowerRound);
  372. y1 = ((int) mm2deg(y1)*pow(10,PowerRound))/pow(10,PowerRound);
  373. float x0 = Xpos;
  374. float y0 = Ypos;
  375. // Bring instructions within limits
  376. if (x1 >= Xdmax) {
  377. x1 = Xdmax;
  378. }
  379. if (x1 <= Xdmin) {
  380. x1 = Xdmin;
  381. }
  382. if (y1 >= Ydmax) {
  383. y1 = Ydmax;
  384. }
  385. if (y1 <= Ydmin) {
  386. y1 = Ydmin;
  387. }
  388. // Let's find out the change for the coordinates
  389. float dx = abs(x1-x0), sx = x0<x1 ? StepInc : -StepInc;
  390. float dy = abs(y1-y0), sy = y0<y1 ? StepInc : -StepInc;
  391. float err = (dx>dy ? dx : -dy)/2, e2;
  392. // Loops servo instructions until destination reached
  393. for(;;){
  394. delay(1); // Without this the program sometimes hangs!
  395. servoX.write( x0 );
  396. servoY.write( y0 );
  397. if (x0==x1 && y0==y1) break;
  398. e2 = err;
  399. if (e2 >-dx) {
  400. err -= dy;
  401. x0 += sx;
  402. }
  403. if (e2 < dy) {
  404. err += dx;
  405. y0 += sy;
  406. }
  407. delay(StepDelay); //delay for settling
  408. }
  409. if (verbose)
  410. {
  411. Serial.print("Going to (");
  412. Serial.print(deg2mm(x0));
  413. Serial.print(",");
  414. Serial.print(deg2mm(y0));
  415. Serial.println(")");
  416. }
  417. // Delay before any next lines are submitted
  418. delay(LineDelay);
  419. // Update the positions
  420. Xpos = x1;
  421. Ypos = y1;
  422. }
  423. // Raises pen
  424. void penUp() {
  425. Serial.println("Pen up!: Sorry Tiny CNC has no Z capability");
  426. }
  427. // Lowers pen
  428. void penDown() {
  429. Serial.println("Pen down!:Sorry Tiny CNC has no Z capability");
  430. }