summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorkintel <kintel@cb376a5e-1013-0410-a455-b6b1f9ac8223>2009-02-04 23:49:50 +0000
committerkintel <kintel@cb376a5e-1013-0410-a455-b6b1f9ac8223>2009-02-04 23:49:50 +0000
commit77550c4ebf630846443376ff27ad4d012bfc852f (patch)
treec97b81768b85f47ab21628dd0d5160cd4613bb69
parent63d346df3db80c1e99124ce0f9933499ccd9a891 (diff)
downloadreprap-backup-77550c4ebf630846443376ff27ad4d012bfc852f.tar.gz
reprap-backup-77550c4ebf630846443376ff27ad4d012bfc852f.zip
Started on a stand-alone Gen3 exerciser. Don't know if it makes sense, but easier than dragging in the entire ReplicatorG at the moment
git-svn-id: https://reprap.svn.sourceforge.net/svnroot/reprap@2495 cb376a5e-1013-0410-a455-b6b1f9ac8223
-rw-r--r--trunk/users/metalab/processing/Gen3_Exerciser/Gen3_Exerciser.pde238
-rw-r--r--trunk/users/metalab/processing/Gen3_Exerciser/Sanguino3GTools.pde1310
2 files changed, 1548 insertions, 0 deletions
diff --git a/trunk/users/metalab/processing/Gen3_Exerciser/Gen3_Exerciser.pde b/trunk/users/metalab/processing/Gen3_Exerciser/Gen3_Exerciser.pde
new file mode 100644
index 00000000..68210b5e
--- /dev/null
+++ b/trunk/users/metalab/processing/Gen3_Exerciser/Gen3_Exerciser.pde
@@ -0,0 +1,238 @@
+// Yep, this is actually -*- Java -*-
+
+import processing.serial.*;
+
+
+// The serial port
+Serial myPort;
+String serialLine = "";
+
+boolean started = true;
+boolean finished = false;
+boolean commandComplete = true;
+int commandCount = 0;
+
+String temperature = "?";
+String debug = "";
+float feedrate = 200;
+
+String gcode[] = {
+};
+int gcodeIndex = 0;
+
+public Sanguino3GDriver driver;
+
+void setup()
+{
+ //init stuff
+ size(1024, 600);
+
+ driver = new Sanguino3GDriver();
+ driver.setInitialized(false);
+ driver.initialize(this);
+}
+
+void draw()
+{
+ String cmd;
+
+ background(20, 20, 20);
+
+ while (driver.serial.available() > 0) {
+ int inByte = driver.serial.read();
+
+ if (inByte == '\n') {
+ println("Got: " + serialLine);
+ serialLine = "";
+ }
+ else if (inByte > 0) {
+ serialLine += (char)inByte;
+ }
+ }
+
+ getNextCommand();
+
+ PFont font;
+ font = loadFont("ArialMT-48.vlw");
+ textFont(font, 16);
+ String temp = "Temperature: " + temperature + "C";
+ temp += "\nTarget: " + targetTemp + "C";
+ temp += "\nDC Speed: " + motorSpeed + "C";
+ temp += "\nFeedrate: " + feedrate + " mm/min. < >: dec/inc feedrate";
+ text(temp, 10, 20);
+ temp = "Status: " + debug + "\n";
+ text(temp, 300, 20);
+
+ temp =
+ "Keys:\n";
+ text(temp, 10, 100);
+
+ temp =
+ "a: -X\n" +
+ "d: +X\n" +
+ "w: +Y\n" +
+ "s: -Y\n" +
+ "q: +Z\n" +
+ "z: -Z\n";
+ text(temp, 10, 120);
+
+ temp =
+ "u: Set temp to 225\n" +
+ "p: Increase temp\n" +
+ "l: Decrease temp\n";
+ text(temp, 100, 100);
+
+ temp =
+ "i: Increase motor speed\n" +
+ "j: Decrease motor speed\n";
+ text(temp, 100, 160);
+
+ temp =
+ "m: Extruder on\n" +
+ "n: Extruder off\n" +
+ "b: Extruder reverse\n";
+ text(temp, 100,200);
+}
+
+int targetTemp = 20;
+int motorSpeed = 130;
+
+String getNextCommand()
+{
+ String c = null;
+ if (keyPressed) {
+ switch (key) {
+ case '>':
+ feedrate+=10;
+ break;
+ case '<':
+ feedrate-=10;
+ break;
+
+ case 'e':
+ c = "G21 (mm)";
+ break;
+
+ case 'r':
+ c = "G90 (abs)";
+ break;
+
+ //case 'y':
+ // c = "G92 (set home)";
+ // break;
+
+ // X axis
+ case 'a':
+ c = "G1 X-10 Y0 Z0 F" + int(feedrate);
+ break;
+ case 'd':
+ Point3d delta = new Point3d(10, 0, 0);
+ driver.queueDelta(delta);
+ break;
+ case 'A':
+ c = "G1 X-1 Y0 Z0 F150";
+ break;
+ case 'D':
+ c = "G1 X1 Y0 Z0 F150";
+ break;
+ // Y axis
+ case 'w':
+ c = "G1 X0 Y10 Z0 F" + int(feedrate);
+ break;
+ case 's':
+ c = "G1 X0 Y-10 Z0 F" + int(feedrate);
+ break;
+ case 'W':
+ c = "G1 X0 Y1 Z0 F150";
+ break;
+ case 'S':
+ c = "G1 X0 Y-1 Z0 F150";
+ break;
+ // Z axis
+ case 'q':
+ c = "G1 X0 Y0 Z2 F150";
+ break;
+ case 'Q':
+ c = "G1 X0 Y0 Z0.05 F30";
+ break;
+ case 'z':
+ c = "G1 X0 Y0 Z-2 F150";
+ break;
+ case 'Z':
+ c = "G1 X0 Y0 Z-0.05 F30";
+ break;
+
+ // Temp
+ case 'u':
+ targetTemp = 210;
+ c = "M104 S" + targetTemp;
+ break;
+ case 'p':
+ targetTemp += 5;
+ c = "M104 S" + targetTemp;
+ break;
+ case 'l':
+ targetTemp -= 10;
+ c = "M104 S" + targetTemp;
+ break;
+ case 't':
+ case 'T':
+ c = "M105 (get current temp)";
+ break;
+
+ // Motor
+ case 'i':
+ motorSpeed += 1;
+ c = "M108 S" + motorSpeed;
+ break;
+ case 'j':
+ motorSpeed -= 1;
+ c = "M108 S" + motorSpeed;
+ break;
+
+ // Extruder
+ case 'm':
+ c = "M101 (extruder on)";
+ break;
+ case 'n':
+ case ' ':
+ c = "M103 (extruder off)";
+ break;
+ case 'b':
+ c = "M102 (extruder reverse)";
+ break;
+
+ // Cooling
+ case 'f':
+ c = "M106 (cooling on)";
+ break;
+
+ case 'F':
+ c = "M107 (cooling off)";
+ break;
+
+ // Home
+ case 'h':
+ c = "G30 X0 Y0 Z0 F240";
+ break;
+
+ // Set zero
+ case 'o':
+ c = "G92";
+ break;
+
+ // Goto zero
+ case '0':
+ c = "G28 F" + int(feedrate);
+ break;
+
+ case 'y':
+ case 'Y':
+ driver.getVersion(2);
+ break;
+
+ }
+ }
+
+ return c;
+}
diff --git a/trunk/users/metalab/processing/Gen3_Exerciser/Sanguino3GTools.pde b/trunk/users/metalab/processing/Gen3_Exerciser/Sanguino3GTools.pde
new file mode 100644
index 00000000..22ff0827
--- /dev/null
+++ b/trunk/users/metalab/processing/Gen3_Exerciser/Sanguino3GTools.pde
@@ -0,0 +1,1310 @@
+/*
+ Sanguino3GDriver.java
+
+ This is a driver to control a machine that uses the Sanguino with 3rd Generation Electronics.
+
+ Part of the ReplicatorG project - http://www.replicat.org
+ Copyright (c) 2008 Zach Smith
+
+ This program 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 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 this program; if not, write to the Free Software Foundation,
+ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+import java.io.IOException;
+import java.text.DecimalFormat;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.Queue;
+import java.util.Vector;
+
+import javax.vecmath.Point3d;
+
+import processing.serial.*;
+
+public class Sanguino3GDriver
+{
+ /**
+ * An enumeration of the available command codes for the three-axis
+ * CNC stage.
+ */
+ class CommandCodesMaster {
+ public final static int GET_VERSION = 0;
+ public final static int INIT = 1;
+ public final static int GET_AVAIL_BUF = 2;
+ public final static int CLEAR_BUF = 3;
+ public final static int GET_POS = 4;
+ public final static int GET_RANGE = 5;
+ public final static int SET_RANGE = 6;
+ public final static int ABORT = 7;
+ public final static int PAUSE = 8;
+ public final static int PROBE = 9;
+ public final static int TOOL_QUERY = 10;
+
+ public final static int QUEUE_POINT_INC = 128;
+ public final static int QUEUE_POINT_ABS = 129;
+ public final static int SET_POS = 130;
+ public final static int FIND_MINS = 131;
+ public final static int FIND_MAXS = 132;
+ public final static int DELAY = 133;
+ public final static int CHANGE_TOOL = 134;
+ public final static int WAIT_FOR_TOOL = 135;
+ public final static int TOOL_COMMAND = 136;
+ };
+
+ /**
+ * An enumeration of the available command codes for a tool.
+ */
+ class CommandCodesSlave {
+ public final static int GET_VERSION = 0;
+ public final static int INIT = 1;
+ public final static int GET_TEMPERATURE = 2;
+ public final static int SET_TEMPERATURE = 3;
+ public final static int SET_MOTOR_1_PWM = 4;
+ public final static int SET_MOTOR_2_PWM = 5;
+ public final static int SET_MOTOR_1_RPM = 6;
+ public final static int SET_MOTOR_2_RPM = 7;
+ public final static int SET_MOTOR_1_DIR = 8;
+ public final static int SET_MOTOR_2_DIR = 9;
+ public final static int TOGGLE_MOTOR_1 = 10;
+ public final static int TOGGLE_MOTOR_2 = 11;
+ public final static int TOGGLE_FAN = 12;
+ public final static int TOGGLE_VALVE = 13;
+ public final static int SET_SERVO_1_POS = 14;
+ public final static int SET_SERVO_2_POS = 15;
+ public final static int FILAMENT_STATUS = 16;
+ public final static int GET_MOTOR_1_RPM = 17;
+ public final static int GET_MOTOR_2_RPM = 18;
+ public final static int GET_MOTOR_1_PWM = 19;
+ public final static int GET_MOTOR_2_PWM = 20;
+ public final static int ABORT = 21;
+ public final static int PAUSE = 22;
+ };
+
+
+ /** The start byte that opens every packet. */
+ private final byte START_BYTE = (byte)0xD5;
+
+ /** The response codes at the start of every response packet. */
+ class ResponseCode {
+ final static int GENERIC_ERROR = 0;
+ final static int OK = 1;
+ final static int BUFFER_OVERFLOW = 2;
+ final static int CRC_MISMATCH = 3;
+ final static int QUERY_OVERFLOW = 4;
+ final static int UNSUPPORTED = 5;
+ };
+
+ /**
+ * An object representing the serial connection.
+ */
+ public Serial serial;
+
+ /**
+ * Serial connection parameters
+ **/
+ String name;
+ int rate;
+ char parity;
+ int databits;
+ float stopbits;
+
+ private int debugLevel = 2;
+
+ private boolean isInitialized = false;
+
+ /**
+ * This is a Java implementation of the IButton/Maxim 8-bit CRC.
+ * Code ported from the AVR-libc implementation, which is used
+ * on the RR3G end.
+ */
+ protected class IButtonCrc {
+
+ private int crc = 0;
+
+ /**
+ * Construct a new, initialized object for keeping track of a CRC.
+ */
+ public IButtonCrc() {
+ crc = 0;
+ }
+
+ /**
+ * Update the CRC with a new byte of sequential data.
+ * See include/util/crc16.h in the avr-libc project for a
+ * full explanation of the algorithm.
+ * @param data a byte of new data to be added to the crc.
+ */
+ public void update(byte data) {
+ crc = (crc ^ data)&0xff; // i loathe java's promotion rules
+ for (int i=0; i<8; i++) {
+ if ((crc & 0x01) != 0) {
+ crc = ((crc >>> 1) ^ 0x8c)&0xff;
+ } else {
+ crc = (crc >>> 1)&0xff;
+ }
+ }
+ }
+
+ /**
+ * Get the 8-bit crc value.
+ */
+ public byte getCrc() {
+ return (byte)crc;
+ }
+
+ /**
+ * Reset the crc.
+ */
+ public void reset() {
+ crc = 0;
+ }
+ }
+
+ /**
+ * A class for building a new packet to send down the wire to the
+ * Sanguino3G.
+ */
+ class PacketBuilder {
+ // yay magic numbers.
+ byte[] data = new byte[256];
+ // current end of packet. Bytes 0 and 1 are reserved for start byte
+ // and packet payload length.
+ int idx = 2;
+ IButtonCrc crc = new IButtonCrc();
+
+ /**
+ * Start building a new command packet.
+ * @param target the target identifier for this packet.
+ * @param command the command identifier for this packet.
+ */
+ PacketBuilder(int command)
+ {
+ idx = 2;
+ data[0] = START_BYTE;
+ //data[1] = length; // just to avoid confusion
+ add8((byte)command);
+ }
+
+ /**
+ * Add an 8-bit value to the end of the packet payload.
+ * @param v the value to append.
+ */
+ void add8( int v ) {
+ data[idx++] = (byte)v;
+ crc.update((byte)v);
+ }
+
+ /**
+ * Add a 16-bit value to the end of the packet payload.
+ * @param v the value to append.
+ */
+ void add16( int v ) {
+ add8((byte)(v & 0xff));
+ add8((byte)((v >> 8) & 0xff));
+ }
+
+ /**
+ * Add a 32-bit value to the end of the packet payload.
+ * @param v the value to append. Must be long to support unsigned ints.
+ */
+ void add32( long v ) {
+ add16((int)(v & 0xffff));
+ add16((int)((v >> 16) & 0xffff));
+ }
+
+ /**
+ * Complete the packet.
+ * @return a byte array representing the completed packet.
+ */
+ byte[] getPacket() {
+ data[idx] = crc.getCrc();
+ data[1] = (byte)(idx-2); // len does not count packet header
+ byte[] rv = new byte[idx+1];
+ System.arraycopy(data,0,rv,0,idx+1);
+ return rv;
+ }
+ };
+
+ /**
+ * A class for keeping track of the state of an incoming packet and
+ * storing its payload.
+ */
+ class PacketProcessor {
+ final static byte PS_START = 0;
+ final static byte PS_LEN = 1;
+ final static byte PS_PAYLOAD = 2;
+ final static byte PS_CRC = 3;
+ final static byte PS_LAST = 4;
+
+ byte packetState = PS_START;
+ int payloadLength = -1;
+ int payloadIdx = 0;
+ byte[] payload;
+ byte targetCrc = 0;
+ IButtonCrc crc;
+
+ /**
+ * Reset the packet's state. (The crc is (re-)generated on the length byte
+ * and thus doesn't need to be reset.(
+ */
+ public void reset() {
+ packetState = PS_START;
+ }
+
+ /**
+ * Create a PacketResponse object that contains this packet's payload.
+ * @return A valid PacketResponse object
+ */
+ public PacketResponse getResponse()
+ {
+ PacketResponse pr = new PacketResponse(payload);
+
+ if (debugLevel >= 2)
+ pr.printDebug();
+
+ return pr;
+ }
+
+ /**
+ * Process the next byte in an incoming packet.
+ * @return true if the packet is complete and valid; false otherwise.
+ */
+ public boolean processByte(byte b)
+ {
+
+ if (debugLevel >= 2) {
+ System.out.print(Integer.toHexString((int)b&0xff) + " ");
+ }
+
+
+ switch (packetState) {
+ case PS_START:
+ if (debugLevel >= 3)
+ System.out.println("Start byte?");
+
+ if (b == START_BYTE) {
+ packetState = PS_LEN;
+ } else {
+ // throw exception?
+ }
+ break;
+
+ case PS_LEN:
+ if (debugLevel >= 3)
+ System.out.println("Length: " + (int)b);
+
+ payloadLength = ((int)b) & 0xFF;
+ payload = new byte[payloadLength];
+ crc = new IButtonCrc();
+ packetState = PS_PAYLOAD;
+ break;
+
+ case PS_PAYLOAD:
+ if (debugLevel >= 3)
+ System.out.println("payload.");
+
+ // sanity check
+ if (payloadIdx < payloadLength) {
+ payload[payloadIdx++] = b;
+ crc.update(b);
+ }
+ if (payloadIdx >= payloadLength) {
+ packetState = PS_CRC;
+ }
+ break;
+
+ case PS_CRC:
+ targetCrc = b;
+
+ if (debugLevel >= 3) {
+ System.out.println("Target CRC: " + Integer.toHexString( (int)targetCrc&0xff ) +
+ " - expected CRC: " + Integer.toHexString( (int)crc.getCrc()&0xff ));
+ }
+
+ if (crc.getCrc() != targetCrc) {
+ throw new java.lang.RuntimeException("CRC mismatch on reply");
+ }
+ return true;
+ }
+ return false;
+ }
+ }
+
+ /**
+ * Packet response wrapper, with convenience functions for
+ * reading off values in sequence and retrieving the response
+ * code.
+ */
+ class PacketResponse {
+
+ byte[] payload;
+ int readPoint = 1;
+
+ public PacketResponse(byte[] p) {
+ payload = p;
+ }
+
+ /**
+ * Prints a debug message with the packet response code decoded, along wiith the
+ * packet's contents in hex.
+ */
+ public void printDebug() {
+ String msg = "Unknown";
+ switch(payload[0]) {
+ case ResponseCode.GENERIC_ERROR:
+ msg = "Generic Error";
+ break;
+
+ case ResponseCode.OK:
+ msg = "OK";
+ break;
+
+ case ResponseCode.BUFFER_OVERFLOW:
+ msg = "Buffer overflow";
+ break;
+
+ case ResponseCode.CRC_MISMATCH:
+ msg = "CRC mismatch";
+ break;
+
+ case ResponseCode.QUERY_OVERFLOW:
+ msg = "Query overflow";
+ break;
+
+ case ResponseCode.UNSUPPORTED:
+ msg = "Unsupported command";
+ break;
+ }
+ System.out.println("Packet response code: " + msg);
+ System.out.print("Packet payload: ");
+ for (int i = 1; i < payload.length; i++) {
+ System.out.print(Integer.toHexString(payload[i]&0xff) + " ");
+ }
+ System.out.print("\n");
+ }
+
+ /**
+ * Retrieve the packet payload.
+ * @return an array of bytes representing the payload.
+ */
+ public byte[] getPayload() {
+ return payload;
+ }
+
+ /**
+ * Get the next 8-bit value from the packet payload.
+ */
+ int get8() {
+ if (payload.length > readPoint)
+ return ((int)payload[readPoint++])&0xff;
+ else
+ {
+ System.out.println("Error: payload not big enough.");
+ return 0;
+ }
+ }
+ /**
+ * Get the next 16-bit value from the packet payload.
+ */
+ int get16() {
+ return get8() + (get8()<<8);
+ }
+ /**
+ * Get the next 32-bit value from the packet payload.
+ */
+ int get32() {
+ return get16() + (get16()<<16);
+ }
+
+ /**
+ * Does the response code indicate that the command was successful?
+ */
+ public boolean isOK() {
+ return payload[0] == ResponseCode.OK;
+ }
+ };
+
+ public Sanguino3GDriver()
+ {
+ }
+
+ public void setInitialized(boolean status)
+ {
+ isInitialized = status;
+ }
+
+ public boolean isInitialized()
+ {
+ return isInitialized;
+ }
+
+ public void initialize(PApplet parent)
+ {
+ // List all the available serial ports
+ println(Serial.list());
+ //open the first port...
+ this.serial = new Serial(parent, Serial.list()[0], 38400);
+
+ //wait till we're initialized
+ if (!isInitialized()) {
+ // attempt to send version command and retrieve reply.
+ try {
+ //read our string that means we're started up.
+ waitForStartup();
+
+ //okay, take care of version info /etc.
+ getVersion(2);
+ sendInit();
+ setInitialized(true);
+ System.out.println("Ready to rock.");
+ } catch (Exception e) {
+ //todo: handle init exceptions here
+ System.out.println("yarg! " + e.getMessage());
+ }
+ }
+ }
+
+ protected void waitForStartup()
+ {
+ assert (serial != null);
+ synchronized(serial) {
+ String cmd = "";
+ byte[] responsebuffer = new byte[512];
+ String result = "";
+
+ while (!isInitialized()) {
+ try {
+ int numread = serial.input.read(responsebuffer);
+ assert (numread != 0); // This should never happen since we know we have a buffer
+ if (numread < 0) {
+ // This signifies EOF. FIXME: How do we handle this?
+ System.out.println("RepRap3GDriver.readResponse(): EOF occured");
+ return;
+ }
+ else {
+ result += new String(responsebuffer , 0, numread, "US-ASCII");
+
+ int index;
+ while ((index = result.indexOf('\n')) >= 0) {
+ String line = result.substring(0, index).trim(); // trim to remove any trailing \r
+ result = result.substring(index+1);
+ if (line.length() == 0) continue;
+
+ //old arduino firmware sends "start"
+ if (line.startsWith("R3G Master v")) {
+ //todo: set version
+ setInitialized(true);
+ System.out.println(line);
+ }
+ }
+ }
+ }
+ catch (IOException e) {
+ System.out.println("inputstream.read() failed: " + e.toString());
+ // FIXME: Shut down communication somehow.
+ }
+ }
+ }
+ }
+
+ /**
+ * Sends the command over the serial connection and retrieves a result.
+ */
+ protected PacketResponse runCommand(byte[] packet)
+ {
+ assert (serial != null);
+
+ if (packet == null || packet.length < 4)
+ return null; // skip empty commands or broken commands
+
+ boolean checkQueue = false;
+ if (packet[2] == 0x0 && (packet[3]&0x80) != 0x0) {
+ checkQueue = true;
+ }
+
+ synchronized(serial) {
+ //do the actual send.
+ serial.write(packet);
+ }
+
+ if (debugLevel >= 2) {
+ System.out.print("OUT: ");
+ for (int i =0; i<packet.length;i++)
+ {
+ System.out.print(Integer.toHexString((int)packet[i] & 0xff ));
+ System.out.print(" ");
+ }
+ System.out.print("\n");
+ }
+
+ PacketProcessor pp = new PacketProcessor();
+ try {
+ boolean c = false;
+ System.out.print("IN: ");
+ while (!c) {
+ int b = serial.input.read();
+ c = pp.processByte((byte)b);
+ System.out.flush();
+ }
+ System.out.println();
+ } catch (java.io.IOException ioe) {
+ System.out.println(ioe.toString());
+ }
+ return pp.getResponse();
+ }
+
+
+ public boolean isFinished()
+ {
+ // todo agm
+ return true;
+ }
+
+ /****************************************************
+ * commands used internally to driver
+ ****************************************************/
+ public int getVersion(int ourVersion)
+ {
+ PacketBuilder pb = new PacketBuilder(CommandCodesMaster.GET_VERSION);
+ pb.add16(ourVersion);
+
+ PacketResponse pr = runCommand(pb.getPacket());
+ int version = pr.get16();
+
+ System.out.println("Reported version: " + Integer.toHexString(version));
+
+ return version;
+ }
+
+ public void sendInit()
+ {
+ PacketBuilder pb = new PacketBuilder(CommandCodesMaster.INIT);
+ PacketResponse pr = runCommand(pb.getPacket());
+ }
+
+ /****************************************************
+ * commands for interfacing with the driver directly
+ ****************************************************/
+
+ public void queueDelta(Point3d delta)
+ {
+ if (debugLevel >= 1)
+ System.out.println("Queued point " + delta);
+
+ //sendCommand(cmd);
+ PacketBuilder pb = new PacketBuilder(CommandCodesMaster.QUEUE_POINT_INC);
+
+ //figure out our feedrate variables.
+ long ticks = convertDeltaToTicks(delta, feedrate);
+ Point3d steps = getDeltaSteps(delta);
+
+ //figure out the axis with the most steps.
+ int max = Math.max((int)steps.x, (int)steps.y);
+ max = Math.max(max, (int)steps.z);
+
+ //get the ratio of steps to take each segment
+ double xRatio = steps.x / max;
+ double yRatio = steps.y / max;
+ double zRatio = steps.z / max;
+
+ //how many segments will there be?
+ int segmentCount = (int)Math.ceil(max / 32767.0);
+
+ //within our range? just do it.
+ if (segmentCount == 1) {
+ queueIncrementalPoint(pb, steps, ticks);
+ }
+ else {
+ for (int i=0; i<segmentCount; i++) {
+ Point3d segmentSteps = new Point3d();
+
+ //TODO: is this accurate?
+ //calculate our line segments
+ segmentSteps.x = Math.round(32767 * xRatio);
+ segmentSteps.y = Math.round(32767 * yRatio);
+ segmentSteps.z = Math.round(32767 * zRatio);
+
+ //keep track of them.
+ steps.x -= segmentSteps.x;
+ steps.y -= segmentSteps.y;
+ steps.z -= segmentSteps.z;
+
+ //send this segment
+ queueIncrementalPoint(pb, segmentSteps, ticks);
+ }
+ }
+ }
+
+ private void queueIncrementalPoint(PacketBuilder pb, Point3d steps, long ticks)
+ {
+ //figure out our timer values.
+ byte prescaler = convertTicksToPrescaler(ticks);
+ int counter = convertTicksToCounter(ticks);
+
+ if (debugLevel >= 1)
+ System.out.println("Queued incremental point " + steps + " at " + counter + "/" + prescaler + " (" + ticks + ")");
+
+ //just add them in now.
+ pb.add16((int)steps.x);
+ pb.add16((int)steps.y);
+ pb.add16((int)steps.z);
+ pb.add8(prescaler);
+ pb.add16(counter);
+
+ PacketResponse pr = runCommand(pb.getPacket());
+ }
+
+// public void setCurrentPosition(Point3d p)
+// {
+// PacketBuilder pb = new PacketBuilder(CommandCodesMaster.SET_POS);
+
+// Point3d steps = machine.mmToSteps(p);
+// pb.add32((long)steps.x);
+// pb.add32((long)steps.y);
+// pb.add32((long)steps.z);
+
+// if (debugLevel >= 1)
+// System.out.println("Set current position to " + p + " (" + steps + ")");
+
+// PacketResponse pr = runCommand(pb.getPacket());
+
+// }
+
+ public void homeXYZ()
+ {
+ if (debugLevel >= 1)
+ System.out.println("Home XYZ");
+
+ homeAxes(true, true, true);
+ }
+
+ public void homeXY()
+ {
+ if (debugLevel >= 1)
+ System.out.println("Home XY");
+
+ homeAxes(true, true, false);
+ }
+
+ public void homeX()
+ {
+ if (debugLevel >= 1)
+ System.out.println("Home X");
+
+ homeAxes(true, false, false);
+ }
+
+ public void homeY()
+ {
+ if (debugLevel >= 1)
+ System.out.println("Home Y");
+
+ homeAxes(false, true, false);
+ }
+
+ public void homeZ()
+ {
+ if (debugLevel >= 1)
+ System.out.println("Home Z");
+
+ homeAxes(false, false, false);
+ }
+
+ private void homeAxes(boolean x, boolean y, boolean z)
+ {
+ byte flags = 0x00;
+
+ //figure out our fastest feedrate.
+ Point3d maxFeedrates = new Point3d(200,200,200); //FIXME: real feedrate
+ double feedrate = Math.max(maxFeedrates.x, maxFeedrates.y);
+ feedrate = Math.max(maxFeedrates.z, feedrate);
+
+ Point3d target = new Point3d();
+
+ if (x)
+ {
+ flags += 1;
+ feedrate = Math.min(feedrate, maxFeedrates.x);
+ target.x = 1; //just to give us feedrate info.
+ }
+ if (y)
+ {
+ flags += 2;
+ feedrate = Math.min(feedrate, maxFeedrates.y);
+ target.y = 1; //just to give us feedrate info.
+ }
+ if (z)
+ {
+ flags += 4;
+ feedrate = Math.min(feedrate, maxFeedrates.z);
+ target.z = 1; //just to give us feedrate info.
+ }
+
+ //calculate ticks
+ long ticks = convertDeltaToTicks(target, feedrate);
+
+ //calculate prescaler/counter values
+ byte prescaler = convertTicksToPrescaler(ticks);
+ int counter = convertTicksToCounter(ticks);
+
+ if (debugLevel >= 2)
+ System.out.println("Homing w/ flags " + Integer.toBinaryString(flags) + " at " + counter + "/" + prescaler);
+
+ //send it!
+ PacketBuilder pb = new PacketBuilder(CommandCodesMaster.FIND_MINS);
+ pb.add8(prescaler);
+ pb.add16(counter);
+ pb.add16(300); //default to 5 minutes
+ pb.add8(flags);
+
+ PacketResponse pr = runCommand(pb.getPacket());
+ }
+
+ public void delay(long millis)
+ {
+ if (debugLevel >= 1)
+ System.out.println("Delaying " + millis + " millis.");
+
+ //send it!
+ PacketBuilder pb = new PacketBuilder(CommandCodesMaster.DELAY);
+ pb.add32(millis);
+ PacketResponse pr = runCommand(pb.getPacket());
+ }
+
+ public void openClamp(int clampIndex)
+ {
+ //TODO: throw some sort of unsupported exception.
+ }
+
+ public void closeClamp(int clampIndex)
+ {
+ //TODO: throw some sort of unsupported exception.
+ }
+
+ public void enableDrives()
+ {
+ //TODO: throw some sort of unsupported exception.
+ }
+
+ public void disableDrives()
+ {
+ //TODO: throw some sort of unsupported exception.
+ }
+
+ public void changeGearRatio(int ratioIndex)
+ {
+ //TODO: throw some sort of unsupported exception.
+ }
+
+ public void selectTool(int toolIndex)
+ {
+ if (debugLevel >= 1)
+ System.out.println("Selecting tool #" + toolIndex);
+
+ //send it!
+ PacketBuilder pb = new PacketBuilder(CommandCodesMaster.CHANGE_TOOL);
+ pb.add8((byte)toolIndex);
+ PacketResponse pr = runCommand(pb.getPacket());
+
+ }
+
+ /*************************************
+ * Motor interface functions
+ *************************************/
+ public void setMotorRPM(double rpm)
+ {
+ //convert RPM into microseconds and then send.
+ long microseconds = (int)Math.round(60 * 1000000 / rpm); //no unsigned ints?!?
+ microseconds = Math.min(microseconds, 2^32-1); // limit to uint32.
+
+ if (debugLevel >= 1)
+ System.out.println("Setting motor 1 speed to " + rpm + " RPM (" + microseconds + " microseconds)");
+
+ //send it!
+ PacketBuilder pb = new PacketBuilder(CommandCodesMaster.TOOL_COMMAND);
+ pb.add8((byte)1);
+ pb.add8(CommandCodesSlave.SET_MOTOR_1_RPM);
+ pb.add8((byte)4); //length of payload.
+ pb.add32(microseconds);
+ PacketResponse pr = runCommand(pb.getPacket());
+
+ }
+
+ public void setMotorSpeedPWM(int pwm)
+ {
+ if (debugLevel >= 1)
+ System.out.println("Setting motor 1 speed to " + pwm + " PWM");
+
+ //send it!
+ PacketBuilder pb = new PacketBuilder(CommandCodesMaster.TOOL_COMMAND);
+ pb.add8((byte)1);
+ pb.add8(CommandCodesSlave.SET_MOTOR_1_PWM);
+ pb.add8((byte)1); //length of payload.
+ pb.add8((byte)pwm);
+ PacketResponse pr = runCommand(pb.getPacket());
+
+ }
+
+// public void enableMotor()
+// {
+// //our flag variable starts with motors enabled.
+// byte flags = 1;
+
+// //bit 1 determines direction...
+// if (machine.currentTool().getMotorDirection() == ToolModel.MOTOR_CLOCKWISE)
+// flags += 2;
+
+// if (debugLevel >= 1)
+// System.out.println("Toggling motor 1 w/ flags: " + Integer.toBinaryString(flags));
+
+// //send it!
+// PacketBuilder pb = new PacketBuilder(CommandCodesMaster.TOOL_COMMAND);
+// pb.add8((byte)machine.currentTool().getIndex());
+// pb.add8(CommandCodesSlave.TOGGLE_MOTOR_1);
+// pb.add8((byte)1); //payload length
+// pb.add8(flags);
+// PacketResponse pr = runCommand(pb.getPacket());
+
+// }
+
+// public void disableMotor()
+// {
+// //bit 1 determines direction...
+// byte flags = 0;
+// if (machine.currentTool().getSpindleDirection() == ToolModel.MOTOR_CLOCKWISE)
+// flags += 2;
+
+// if (debugLevel >= 1)
+// System.out.println("Disabling motor 1");
+
+// PacketBuilder pb = new PacketBuilder(CommandCodesMaster.TOOL_COMMAND);
+// pb.add8((byte)machine.currentTool().getIndex());
+// pb.add8(CommandCodesSlave.TOGGLE_MOTOR_1);
+// pb.add8((byte)1); //payload length
+// pb.add8(flags);
+// PacketResponse pr = runCommand(pb.getPacket());
+
+// }
+
+// public int getMotorSpeedPWM()
+// {
+// PacketBuilder pb = new PacketBuilder(CommandCodesMaster.TOOL_QUERY);
+// pb.add8((byte)machine.currentTool().getIndex());
+// pb.add8(CommandCodesSlave.GET_MOTOR_1_PWM);
+// PacketResponse pr = runCommand(pb.getPacket());
+
+// //get it
+// int pwm = pr.get8();
+
+// if (debugLevel >= 1)
+// System.out.println("Current motor 1 PWM: " + pwm);
+
+// //set it.
+// machine.currentTool().setMotorSpeedReadingPWM(pwm);
+
+// return pwm;
+// }
+
+// public double getMotorSpeedRPM()
+// {
+// PacketBuilder pb = new PacketBuilder(CommandCodesMaster.TOOL_QUERY);
+// pb.add8((byte)machine.currentTool().getIndex());
+// pb.add8(CommandCodesSlave.GET_MOTOR_1_RPM);
+// PacketResponse pr = runCommand(pb.getPacket());
+
+// //convert back to RPM
+// long micros = pr.get32();
+// double rpm = (60.0 * 1000000.0 / micros);
+
+// if (debugLevel >= 1)
+// System.out.println("Current motor 1 RPM: " + rpm + " (" + micros + ")");
+
+// //set it.
+// machine.currentTool().setMotorSpeedReadingRPM(rpm);
+
+// return rpm;
+// }
+
+// /*************************************
+// * Spindle interface functions
+// *************************************/
+// public void setSpindleRPM(double rpm)
+// {
+// //convert RPM into microseconds and then send.
+// long microseconds = (int)Math.round(60 * 1000000 / rpm); //no unsigned ints?!?
+// microseconds = Math.min(microseconds, 2^32-1); // limit to uint32.
+
+// if (debugLevel >= 1)
+// System.out.println("Setting motor 2 speed to " + rpm + " RPM (" + microseconds + " microseconds)");
+
+// //send it!
+// PacketBuilder pb = new PacketBuilder(CommandCodesMaster.TOOL_COMMAND);
+// pb.add8((byte)machine.currentTool().getIndex());
+// pb.add8(CommandCodesSlave.SET_MOTOR_2_RPM);
+// pb.add8((byte)4); //payload length
+// pb.add32(microseconds);
+// PacketResponse pr = runCommand(pb.getPacket());
+
+// }
+
+// public void setSpindleSpeedPWM(int pwm)
+// {
+// if (debugLevel >= 1)
+// System.out.println("Setting motor 2 speed to " + pwm + " PWM");
+
+// //send it!
+// PacketBuilder pb = new PacketBuilder(CommandCodesMaster.TOOL_COMMAND);
+// pb.add8((byte)machine.currentTool().getIndex());
+// pb.add8(CommandCodesSlave.SET_MOTOR_2_PWM);
+// pb.add8((byte)1); //length of payload.
+// pb.add8((byte)pwm);
+// PacketResponse pr = runCommand(pb.getPacket());
+
+// }
+
+// public void enableSpindle()
+// {
+// //our flag variable starts with spindles enabled.
+// byte flags = 1;
+
+// //bit 1 determines direction...
+// if (machine.currentTool().getSpindleDirection() == ToolModel.MOTOR_CLOCKWISE)
+// flags += 2;
+
+// if (debugLevel >= 1)
+// System.out.println("Toggling motor 2 w/ flags: " + Integer.toBinaryString(flags));
+
+// //send it!
+// PacketBuilder pb = new PacketBuilder(CommandCodesMaster.TOOL_COMMAND);
+// pb.add8((byte)machine.currentTool().getIndex());
+// pb.add8(CommandCodesSlave.TOGGLE_MOTOR_2);
+// pb.add8((byte)1); //payload length
+// pb.add8(flags);
+// PacketResponse pr = runCommand(pb.getPacket());
+
+// }
+
+// public void disableSpindle()
+// {
+// //bit 1 determines direction...
+// byte flags = 0;
+// if (machine.currentTool().getSpindleDirection() == ToolModel.MOTOR_CLOCKWISE)
+// flags += 2;
+
+// if (debugLevel >= 1)
+// System.out.println("Disabling motor 2");
+
+// PacketBuilder pb = new PacketBuilder(CommandCodesMaster.TOOL_COMMAND);
+// pb.add8((byte)machine.currentTool().getIndex());
+// pb.add8(CommandCodesSlave.TOGGLE_MOTOR_1);
+// pb.add8((byte)1); //payload length
+// pb.add8(flags);
+// PacketResponse pr = runCommand(pb.getPacket());
+
+// }
+
+// public double getSpindleSpeedRPM()
+// {
+// PacketBuilder pb = new PacketBuilder(CommandCodesMaster.TOOL_QUERY);
+// pb.add8((byte)machine.currentTool().getIndex());
+// pb.add8(CommandCodesSlave.GET_MOTOR_2_RPM);
+// PacketResponse pr = runCommand(pb.getPacket());
+
+// //convert back to RPM
+// long micros = pr.get32();
+// double rpm = (60.0 * 1000000.0 / micros);
+
+// if (debugLevel >= 1)
+// System.out.println("Current motor 2 RPM: " + rpm + " (" + micros + ")");
+
+// //set it.
+// machine.currentTool().setSpindleSpeedReadingRPM(rpm);
+
+// return rpm;
+// }
+
+// public int getSpindleSpeedPWM()
+// {
+// PacketBuilder pb = new PacketBuilder(CommandCodesMaster.TOOL_QUERY);
+// pb.add8((byte)machine.currentTool().getIndex());
+// pb.add8(CommandCodesSlave.GET_MOTOR_2_PWM);
+// PacketResponse pr = runCommand(pb.getPacket());
+
+// //get it
+// int pwm = pr.get8();
+
+// if (debugLevel >= 1)
+// System.out.println("Current motor 1 PWM: " + pwm);
+
+// //set it.
+// machine.currentTool().setSpindleSpeedReadingPWM(pwm);
+
+// return pwm;
+// }
+
+
+// /*************************************
+// * Temperature interface functions
+// *************************************/
+// public void setTemperature(double temperature)
+// {
+// //constrain our temperature.
+// int temp = (int)Math.round(temperature);
+// temp = Math.min(temp, 65535);
+
+// if (debugLevel >= 1)
+// System.out.println("Setting temperature to " + temp + "C");
+
+// PacketBuilder pb = new PacketBuilder(CommandCodesMaster.TOOL_COMMAND);
+// pb.add8((byte)machine.currentTool().getIndex());
+// pb.add8(CommandCodesSlave.SET_TEMPERATURE);
+// pb.add8((byte)2); //payload length
+// pb.add16(temp);
+// PacketResponse pr = runCommand(pb.getPacket());
+
+// }
+
+// public void readTemperature()
+// {
+// PacketBuilder pb = new PacketBuilder(CommandCodesMaster.TOOL_QUERY);
+// pb.add8((byte)machine.currentTool().getIndex());
+// pb.add8(CommandCodesSlave.GET_TEMPERATURE);
+// PacketResponse pr = runCommand(pb.getPacket());
+
+// int temp = pr.get16();
+// machine.currentTool().setCurrentTemperature(temp);
+
+// if (debugLevel >= 1)
+// System.out.println("Current temperature: " + temp + "C");
+// }
+
+// /*************************************
+// * Flood Coolant interface functions
+// *************************************/
+// public void enableFloodCoolant()
+// {
+// //TODO: throw unsupported exception
+
+// }
+
+// public void disableFloodCoolant()
+// {
+// //TODO: throw unsupported exception
+
+// }
+
+// /*************************************
+// * Mist Coolant interface functions
+// *************************************/
+// public void enableMistCoolant()
+// {
+// //TODO: throw unsupported exception
+
+// }
+
+// public void disableMistCoolant()
+// {
+// //TODO: throw unsupported exception
+
+// }
+
+// /*************************************
+// * Fan interface functions
+// *************************************/
+// public void enableFan()
+// {
+// if (debugLevel >= 1)
+// System.out.println("Enabling fan");
+
+// PacketBuilder pb = new PacketBuilder(CommandCodesMaster.TOOL_COMMAND);
+// pb.add8((byte)machine.currentTool().getIndex());
+// pb.add8(CommandCodesSlave.TOGGLE_FAN);
+// pb.add8((byte)1); //payload length
+// pb.add8((byte)1); //enable
+// PacketResponse pr = runCommand(pb.getPacket());
+
+// }
+
+// public void disableFan()
+// {
+// if (debugLevel >= 1)
+// System.out.println("Disabling fan");
+
+// PacketBuilder pb = new PacketBuilder(CommandCodesMaster.TOOL_COMMAND);
+// pb.add8((byte)machine.currentTool().getIndex());
+// pb.add8(CommandCodesSlave.TOGGLE_FAN);
+// pb.add8((byte)1); //payload length
+// pb.add8((byte)0); //disable
+// PacketResponse pr = runCommand(pb.getPacket());
+
+// }
+
+// /*************************************
+// * Valve interface functions
+// *************************************/
+// public void openValve()
+// {
+// if (debugLevel >= 1)
+// System.out.println("Opening valve");
+
+// PacketBuilder pb = new PacketBuilder(CommandCodesMaster.TOOL_COMMAND);
+// pb.add8((byte)machine.currentTool().getIndex());
+// pb.add8(CommandCodesSlave.TOGGLE_VALVE);
+// pb.add8((byte)1); //payload length
+// pb.add8((byte)1); //enable
+// PacketResponse pr = runCommand(pb.getPacket());
+
+// }
+
+// public void closeValve()
+// {
+// if (debugLevel >= 1)
+// System.out.println("Closing valve");
+
+// PacketBuilder pb = new PacketBuilder(CommandCodesMaster.TOOL_COMMAND);
+// pb.add8((byte)machine.currentTool().getIndex());
+// pb.add8(CommandCodesSlave.TOGGLE_VALVE);
+// pb.add8((byte)1); //payload length
+// pb.add8((byte)0); //disable
+// PacketResponse pr = runCommand(pb.getPacket());
+
+// }
+
+// /*************************************
+// * Collet interface functions
+// *************************************/
+// public void openCollet()
+// {
+// //TODO: throw unsupported exception.
+
+// }
+
+// public void closeCollet()
+// {
+// //TODO: throw unsupported exception.
+
+// }
+
+ /*************************************
+ * Various timer and math functions.
+ *************************************/
+
+ private Point3d getDeltaSteps(Point3d delta)
+ {
+ //calculate our deltas.
+ delta.x = Math.abs(delta.x);
+ delta.y = Math.abs(delta.y);
+ delta.z = Math.abs(delta.z);
+
+ return mmToSteps(delta);
+ }
+
+ private Point3d stepsPerMM = new Point3d(200,200,200);
+ public Point3d mmToSteps(Point3d mm)
+ {
+ Point3d temp = new Point3d();
+
+ temp.x = Math.round(mm.x * stepsPerMM.x);
+ temp.y = Math.round(mm.y * stepsPerMM.y);
+ temp.z = Math.round(mm.z * stepsPerMM.z);
+
+ return temp;
+ }
+
+ private long convertDeltaToTicks(Point3d delta, double feedrate)
+ {
+ Point3d deltaSteps = getDeltaSteps(delta);
+
+ //how long is our line length?
+ double distance = Math.sqrt(deltaSteps.x * deltaSteps.x +
+ deltaSteps.y * deltaSteps.y +
+ deltaSteps.z * deltaSteps.z);
+
+ long master_steps = 0;
+
+ //find the dominant axis.
+ if (deltaSteps.x > deltaSteps.y) {
+ if (deltaSteps.z > deltaSteps.x)
+ master_steps = (long)deltaSteps.z;
+ else
+ master_steps = (long)deltaSteps.x;
+ }
+ else {
+ if (deltaSteps.z > deltaSteps.y)
+ master_steps = (long)deltaSteps.z;
+ else
+ master_steps = (long)deltaSteps.y;
+ }
+
+ //calculate delay between steps in ticks. this is sort of tricky, but not too bad.
+ // 960,000,000 = number of ticks in a second at 16Mhz, the speed of an Arduino/Sanguino.
+ //the formula has been condensed to save space. here it is in english:
+ // (feedrate is in mm/minute)
+ // distance / feedrate * 960,000,000.0 = move duration in ticks
+ // move duration / master_steps = time between steps for master axis.
+
+ return (long)Math.floor(((distance * 960000000.0) / feedrate) / master_steps);
+ }
+
+ private byte convertTicksToPrescaler(long ticks)
+ {
+ // these also represent frequency: 1000000 / ticks / 2 = frequency in hz.
+
+ // our slowest speed at our highest resolution ( (2^16-1) * 0.0625 usecs = 4095 usecs (4 millisecond max))
+ // range: 8Mhz max - 122hz min
+ if (ticks <= 65535L)
+ return 1;
+ // our slowest speed at our next highest resolution ( (2^16-1) * 0.5 usecs = 32767 usecs (32 millisecond max))
+ // range:1Mhz max - 15.26hz min
+ else if (ticks <= 524280L)
+ return 2;
+ // our slowest speed at our medium resolution ( (2^16-1) * 4 usecs = 262140 usecs (0.26 seconds max))
+ // range: 125Khz max - 1.9hz min
+ else if (ticks <= 4194240L)
+ return 3;
+ // our slowest speed at our medium-low resolution ( (2^16-1) * 16 usecs = 1048560 usecs (1.04 seconds max))
+ // range: 31.25Khz max - 0.475hz min
+ else if (ticks <= 16776960L)
+ return 4;
+ // our slowest speed at our lowest resolution ((2^16-1) * 64 usecs = 4194240 usecs (4.19 seconds max))
+ // range: 7.812Khz max - 0.119hz min
+ else if (ticks <= 67107840L)
+ return 5;
+ //its really slow... hopefully we can just get by with super slow.
+ else
+ return 5;
+ }
+
+ private int convertTicksToCounter(long ticks)
+ {
+ // our slowest speed at our highest resolution ( (2^16-1) * 0.0625 usecs = 4095 usecs)
+ if (ticks <= 65535)
+ return ((int)(ticks & 0xffff));
+ // our slowest speed at our next highest resolution ( (2^16-1) * 0.5 usecs = 32767 usecs)
+ else if (ticks <= 524280)
+ return ((int)((ticks / 8) & 0xffff));
+ // our slowest speed at our medium resolution ( (2^16-1) * 4 usecs = 262140 usecs)
+ else if (ticks <= 4194240)
+ return ((int)((ticks / 64) & 0xffff));
+ // our slowest speed at our medium-low resolution ( (2^16-1) * 16 usecs = 1048560 usecs)
+ else if (ticks <= 16776960)
+ return ((int)(ticks / 256));
+ // our slowest speed at our lowest resolution ((2^16-1) * 64 usecs = 4194240 usecs)
+ else if (ticks <= 67107840)
+ return ((int)(ticks / 1024));
+ //its really slow... hopefully we can just get by with super slow.
+ else
+ return 65535;
+ }
+}