Making a Drawing Machine: Processing/Arduino

Hello,

I am making a drawing machine like this–http://www.cameronzotter.com/mica/work_arduino_drawing.html using Processing to control servos connected to an Arduino. I have set up a sketch in Processing that will set up parameters for the servos to rotate within ( so that the pen will draw within a defined area–the paper) I have established a serial connection but I don't know how to make Processing control the servos through the Arduino software. Please help with the Arduino code!

Here is the Processing code:

import processing.serial.*;
Serial port;
/Drawing Servo Controller/
//90 is stopping

// 0 means stop
// 1 means rotate left
// 2 means rotate right

//first value for left servo
//second value for right servo
//00
//10

boolean topRecorded = false;
boolean bottomRecorded = false;
int currentAngleLeft = 0;
int currentAngleRight = 0;
int maxAngleLeft;
int maxAngleRight;

void setup () {
size(300, 400);
println("Available serial ports:");
println(Serial.list());
// Uses the first port in this list (number 0). Change this to
// select the port corresponding to your Arduino board. The last
// parameter (e.g. 9600) is the speed of the communication. It
// has to correspond to the value passed to Serial.begin() in your
// Arduino sketch.
port = new Serial(this, Serial.list()[0], 9600);
}

void draw () {
//drawing left servo draw left and right controls
background(125);
pushMatrix();
translate(50, height/2);
fill(255);
rect(0, 0, 30, 30);
text("l up", 0, 0);
fill(0);
rect(0+30, 0, 30, 30);
text("l down", 30, 0);
popMatrix();

//drawing right controls
pushMatrix();
translate(200, height/2);
text("l down", 0, 0);
fill(0);
rect(0, 0, 30, 30);
text("l up", 30, 0);
fill(255);
rect(0+30, 0, 30, 30);
popMatrix();

//draw record burrons
pushMatrix();
translate(width/2, 50);
fill(255, 0, 0);
text("rec top", 0, 0);
rect(0, 0, 30, 30);
fill(0, 255, 0);
text("rec bot", 40, 0);
rect(0+30, 0, 30, 30);
popMatrix();

//draw left servo visual
pushMatrix();
ellipseMode(CENTER);
translate(80, height/2-50);
rotate(radians(currentAngleLeft));
fill(255, 0, 0);
arc(0, 0, 30, 30, 0, PI+HALF_PI, PIE);
popMatrix();

//draw right servo visual
pushMatrix();
ellipseMode(CENTER);
translate(200, height/2-50);
rotate(radians(currentAngleRight));
fill(255, 0, 0);
arc(0, 0, 30, 30, 0, PI+HALF_PI, PIE);
popMatrix();

if (mousePressed) {
if (mouseX>50 && mouseX<80 && mouseY>height/2 && mouseY<height/2+30) {
println("left servo rotate left");
//if we are currently testing for top and bottom can move anyway
if (!topRecorded) {
currentAngleLeft -=1;
} else {
//if we are drawing make sure to trap it
if ( currentAngleLeft > 0) {
currentAngleLeft -=1;
}
}
}

if (mouseX>80 && mouseX<110 && mouseY>height/2 && mouseY<height/2+30) {
println("left servo rotate right");
if (!bottomRecorded) {
currentAngleLeft +=1;
} else {
//if we are drawing cannot go over the maxiumum position
if (currentAngleLeft< maxAngleLeft) {
currentAngleLeft +=1;
}
}
}

if (mouseX>200 && mouseX<230 && mouseY>height/2 && mouseY<height/2+30) {
println("right servo rotate left");
currentAngleRight -=1;
}

if (mouseX>230 && mouseX<260 && mouseY>height/2 && mouseY<height/2+30) {
println("right servo rotate right");
currentAngleRight +=1;
}
}
}

void mousePressed () {
if (mouseX>width/2 && mouseX<width/2+30 && mouseY>50 && mouseY<80) {
println("at top");
topRecorded=true;
currentAngleLeft = 0;
currentAngleRight = 0;
}

if (mouseX>width/2+30 && mouseX<width/2+60 && mouseY>50 && mouseY<80) {
println("at bottom");
if (topRecorded) {
bottomRecorded = true;
maxAngleLeft = currentAngleLeft;
maxAngleRight = currentAngleRight;
println("Max Angle Left: " + maxAngleLeft + ", Max Angle Right: " + maxAngleRight);
}
}
}

You need to define and establish a message protocol between the Arduino and the PC. The PC would be waiting for the Arduino to get ready and then start sending messages to the Arduino. The Arduino would report after each message that it is ready. As you are making a drawing machine with X and Y coordinates and Pen-up and Pen-down, you might want to consider to use GCODEs or Gerber files. This is the industry standard and a lot of cad packages support this.
Here is an example of processing messages from another project (http://projectsentrygun.rudolphlabs.com/) which does what you want. I have changed the source code to use Stepper motors instead of servo's. An alternative is to look at the source code from 3D printers as they process GCodes (search words: RepRap, Marlin)

Here is the message processing part:

/**
 * @name loop()
 * Main loop for program. loops indefinitely
 */
void loop() {
	//
	// check if enoug bytes are loaded in the serial buffer received
	// from the computer
	//
	if (Serial.available() >= 10) {		// At least 10 bytes for a command
		watchdog 	= 0;				// active so no need for watchdog
		indicator 	= Serial.read();    // read first byte in buffer
										// and process dependent of type
		switch (indicator){
			case 'a':
				//
				// message =  'a'
				// indicator  	1 byte	  if 'a' coordinate + activity
				// X coord	  	3 bytes   hundreds-tens-decimals
				// Y coord	  	3 bytes   hundreds-tens-decimals
				// fireByte	  	1 byte
				// scanningByte 1 byte	  1 = true, 0 = false		 *
				//
				idle 		= false; 	// were are not in idle mode
				idleCounter = 0;		// reset the counter
				digitalWrite(USBIndicatorLEDPin, HIGH); // light up the USB
														//indicator LED
				x100byte 			= Serial.read();    // X coordinate
				x010byte 			= Serial.read();
				x001byte 			= Serial.read();
				y100byte 			= Serial.read();    // Y coordinate
				y010byte 			= Serial.read();
				y001byte 			= Serial.read();
				fireByte 			= Serial.read();    // Fire?
				fireSelectorByte 	= Serial.read(); 	// How to fire
				//
				// convert the ASCII character to an integer by subtracting
				// hex 30 (=dec 48) from it.
				//
				fireSelector 		= int(fireSelectorByte) - 48;
				scanningByte 		= Serial.read();
				if((int(scanningByte) - 48) == 1) {
					scanning = true;
				} else {
					scanning = false;
				}
				break;

			case 'z':
				//
				// message =  'z'
				// Go into idle mode
				//
				idle = true;
				break;

			case 'b':
				//
				// message = 'b'
				// make backup of parameters into EEPROM
				//
				backup(configuration1);
				break;

			case 'r':
				//
				// message =  'r'
				// retrieve parameters from backup
				//
				restore(configuration1);
				break;

			default:
				//
				// no valid message code so increase the
				// watchdog timer. Apparently we did receive data
				// but we can't make any sense out of it
				//
				watchdog++;
				//
				// if we have reached the timeout situation
				// go into idle mode
				//
				if(watchdog > watchdogTimeout) {
					idle = true;
				}
				break;
		}
	}
	//
	// now see if there is anything useful to do
	//
	if(idle) {                  // when Arduino is not getting commands from
	  	  	  	  	  	  	  	// computer...
		Serial.write('T');    	// tell the computer that Arduino is here
		idleCounter++;          //  periodically blink the USB indicator LED
		if(idleCounter > 1000) {
			//
			// Blink every count of 1000 counts that we are in
			// idle mode
			//
			sequenceLEDs(1, 100);
			delay(10);
			//      digitalWrite(USBIndicatorLEDPin, HIGH);
			//      delay(250);
			//      digitalWrite(USBIndicatorLEDPin, LOW);
			idleCounter = 0;
		} else {
			//
			// just turn the USB indicator off
			//
			digitalWrite(USBIndicatorLEDPin, LOW);
		}
		//
		// Since nothing is happening keep both servo's in their
		// home position and don't fire
		//
		xPosition 	= panServo_HomePosition;
		yPosition 	= tiltServo_HomePosition;
		fire 		= 0;
	} else {
		//
		// We are not in idle mode so let's process the command
		// we received.
		//
		// first convert the ASCII characters we received into
		// an integer number
		//
		xPosition 	= (100 * (int(x100byte)-48)) 	+
			  	  	  (10  * (int(x010byte)-48)) 	+
			  	             (int(x001byte)-48);
		yPosition 	= (100 * (int(y100byte)-48)) 	+
			  	      (10  * (int(y010byte)-48)) 	+
			  	  	         (int(y001byte)-48);
		fire 		= 		  int(fireByte)-48;   // convert byte to integer
	}
	//
	// see if we told to go in scanning mode
	//
  	if(scanning) {
  		//
  		// show we are scanning by lightning the led
  		//
  		digitalWrite(modeIndicatorLEDPin, HIGH);
  		//
  		// determine the scan direction
  		//
  		if(scanDirection) {
  			scanXPosition += 1;
  			//
  			// avoid maximum scan direction
  			//
  			if(scanXPosition > panServo_scanningMax) {
  				scanDirection = false;
  				scanXPosition = panServo_scanningMax;
  			}
  		} else {
  			scanXPosition -= 1;
  			//
  			// avoid going past minimum scan direction
  			//
  			if(scanXPosition < panServo_scanningMin) {
  				scanDirection = true;
  				scanXPosition = panServo_scanningMin;
  			}
  		}
  		//
  		// in scanmode move to the required or home x postion
  		// move to the Y home position
  		// Don't fire
  		// and give the servo's time to reach their position
  		//
  		xPosition 	= scanXPosition;
  		yPosition 	= tiltServo_HomePosition;
  		fire 		= 0;
  		//
  		// beetje een zinloos volgend statement omdat er nog niets is gebeurd
  		//
  		delay(scanningSpeed/abs(panServo_scanningMax-panServo_scanningMin));
  		//
  		//
  		//
  	} else {
  		//
  		// we are not scanning so switch the SCAN led off
  		//
  		digitalWrite(modeIndicatorLEDPin, LOW);
  	}

  	//
  	// Check the disable plate if it is pressed.
  	//
  	if( (digitalRead(disablePlatePin) == HIGH && !invertInputs) ||
  		(digitalRead(disablePlatePin) == LOW && invertInputs)) {
  		//
  		// set that we are disabled and wait the time we defined
  		//
  		disabled 		= true;
  		disableEndTime 	= millis() + disablePlateDelay;
  	}
  	//
  	// if we are passed the disablePlate time enable everything
  	// again
  	//
  	if(millis() > disableEndTime) {
  		disabled = false;
  	}

  	//
  	// test the reload switch if it is flipped
  	// if so, override any computer commands,
  	// move the servo's to their load position and
  	// don't fire and
  	// flash the mode indicator
  	//
  	if((digitalRead(reloadSwitchPin) == HIGH && !invertInputs) ||
  	   (digitalRead(reloadSwitchPin) == LOW && invertInputs)) {
  		shotCounter = 0;
  		xPosition 	= panServo_ReloadPosition;
  		yPosition 	= tiltServo_ReloadPosition;
  		fire 		= 0;
  		digitalWrite(modeIndicatorLEDPin, HIGH);
  		delay(100);
  		digitalWrite(modeIndicatorLEDPin, LOW);
  		delay(100);
  	}
  	//
  	// if the gun is disabled move to the reload position
  	// don't fire
  	// and give a shore flash
  	//
  	if(disabled) {
  		xPosition 	= panServo_ReloadPosition;
  		yPosition 	= tiltServo_ReloadPosition;
  		fire 		= 0;
  		digitalWrite(modeIndicatorLEDPin, HIGH);
  		delay(50);
  		digitalWrite(modeIndicatorLEDPin, LOW);
  		delay(50);
  	}
  	//
  	// Finally we get to actually move the servo's
  	//
#ifdef STEPPERS
  	//
  	// move both steppers to the new position
  	//
  	moveXY(xPosition, yPosition);
#else
  	//
  	// move the servo's to their new position
  	//
  	pan.write(xPosition);
  	tilt.write(yPosition);
#endif

  	//
  	// check if we have any ammo left
  	//
  	if(useAmmoCounter && shotCounter >= clipSize) {
  		clipEmpty = true;
  	} else {
  		clipEmpty = false;
  	}
  	//
  	// check if we want to fire AND we have ammo left to fire
  	// is yes fire in the firing selected mode
  	// if no (not firing or clip empty), cease fire
  	//
  	if(fire == 1 && !clipEmpty) {
  		Fire(fireSelector);
  	} else{
  		ceaseFire(fireSelector);
  	}
}

How to use this forum

Code tags.

Read this before posting a programming question

Please do not cross-post. This wastes time and resources as people attempt to answer your question on multiple threads.

Other thread deleted.

  • Moderator