package org.reprap.devices; import java.io.IOException; import org.reprap.Device; import org.reprap.Preferences; import org.reprap.utilities.Debug; import org.reprap.Extruder; import org.reprap.Printer; import org.reprap.devices.pseudo.LinePrinter; import org.reprap.comms.Address; import org.reprap.comms.Communicator; import org.reprap.comms.IncomingMessage; import org.reprap.comms.OutgoingMessage; import org.reprap.comms.IncomingMessage.InvalidPayloadException; import org.reprap.comms.messages.OutgoingBlankMessage; import org.reprap.comms.messages.OutgoingByteMessage; import javax.media.j3d.Appearance; import javax.vecmath.Color3f; import javax.media.j3d.Material; /** * @author jwiel * */ public class GenericExtruder extends Device implements Extruder{ /** * API for firmware * Activate the extruder motor in forward direction */ public static final byte MSG_SetActive = 1; /** * Activate the extruder motor in reverse direction */ public static final byte MSG_SetActiveReverse = 2; /** * There is no material left to extrude */ public static final byte MSG_IsEmpty = 8; /** * Set the temperature of the extruder */ public static final byte MSG_SetHeat = 9; /** * Get the temperature of the extruder */ public static final byte MSG_GetTemp = 10; /** * Turn the cooler/fan on */ public static final byte MSG_SetCooler = 11; /** * Open the valve */ public static final byte MSG_ValveOpen = 12; /** * Close the valve */ public static final byte MSG_ValveClosed = 13; /** * Set Vref */ public static final byte MSG_SetVRef = 52; /** * Set the Tempscaler */ public static final byte MSG_SetTempScaler = 53; /** * Offset of 0 degrees centigrade from absolute zero */ private static final double absZero = 273.15; /** * The temperature to maintain */ private double requestedTemperature = 0; /** * The temperature most recently read from the device */ private double currentTemperature = 0; /** * Temprature history */ private double[] tH; private int tHi; /** * Is a material-out sensor connected to the exteruder or not. * If this is the case, TODO: impact? */ private boolean currentMaterialOutSensor = false; /** * Indicates when polled values are first ready */ private boolean sensorsInitialised = false; /** * */ private Thread pollThread = null; /** * */ private boolean pollThreadExiting = false; /** * */ private int vRefFactor = 7; /** * */ private int tempScaler = 4; /** * Thermistor beta */ private double beta; /** * Thermistor resistance at 0C */ private double rz; /** * Thermistor timing capacitor in farads */ private double cap; /** * Heater power gradient */ private double hm; /** * Heater power intercept * TODO: hb should probably be ambient temperature measured at this point */ private double hb; /** * Maximum motor speed (value between 0-255) */ private int maxExtruderSpeed; /** * The actual extrusion speed */ private int extrusionSpeed; /** * The extrusion temperature */ private double extrusionTemp; /** * The extrusion width in XY */ private double extrusionSize; /** * The extrusion height in Z * TODO: Should this be a machine-wide constant? - AB */ private double extrusionHeight; /** * The step between infill tracks */ private double extrusionInfillWidth; /** * below this infill finely */ private double lowerFineThickness; /** * Above this infill finely */ private double upperFineThickness; /** * Use this for broad infill in the middle; if negative, always * infill fine. */ private double extrusionBroadWidth; /** * The number of seconds to cool between layers */ private int coolingPeriod; /** * The speed of movement in XY when depositing */ private int xySpeed; /** * Zero torque speed */ private int t0; /** * Infill speed [0,1]*maxSpeed */ private double iSpeed; /** * Outline speed [0,1]*maxSpeed */ private double oSpeed; /** * Length (mm) to speed up round corners */ private double asLength; /** * Factor by which to speed up round corners */ private double asFactor; /** * Line length below which to plot faster */ private double shortLength; /** *Factor for short line speeds */ private double shortSpeed; /** * The name of this extruder's material */ private String material; /** * Number of mm to overlap the hatching infill with the outline. 0 gives none; -ve will * leave a gap between the two */ private double infillOverlap = 0; /** * Where to put the nozzle */ private double offsetX, offsetY, offsetZ; /** * */ private long lastTemperatureUpdate = 0; /** * Identifier of and extruder * TODO: which values mean what? */ private int myExtruderID; /** * Start polygons at random perimiter points */ private boolean randSt = false; /** * Start polygons at incremented perimiter points */ private boolean incrementedSt = false; /** * Flag indicating if initialisation succeeded. Usually this * indicates if the extruder is present in the network. */ //private boolean isCommsAvailable = false; /** * The colour black */ protected static final Color3f black = new Color3f(0, 0, 0); /** * The colour of the material to use in the simulation windows */ private Appearance materialColour; /** * @param communicator * @param address * @param prefs * @param extruderId */ /** * Enable wiping procedure for nozzle */ private boolean nozzleWipeEnabled; /** * Co-ordinates for the nozzle wiper */ private double nozzleWipeDatumX; private double nozzleWipeDatumY; /** * X Distance to move nozzle over wiper */ private double nozzleWipeStrokeX; /** * Y Distance to move nozzle over wiper */ private double nozzleWipeStrokeY; /** * Number of wipe cycles per method call */ private int nozzleWipeFreq; /** * Number of seconds to run to re-start the nozzle before a wipe */ private double nozzleClearTime; /** * Number of seconds to wait after restarting the nozzle */ private double nozzleWaitTime; /** * The current coordinate to wipe at */ private double wipeX; /** * The number of ms to pulse the valve to open or close it * -ve to supress */ private double valvePulseTime; /** * The number of milliseconds to wait before starting a border track */ private double extrusionDelayForLayer = 0; /** * The number of milliseconds to wait before starting a hatch track */ private double extrusionDelayForPolygon = 0; /** * The number of milliseconds to wait before starting a border track */ private double valveDelayForLayer = 0; /** * The number of milliseconds to wait before starting a hatch track */ private double valveDelayForPolygon = 0; /** * The number of mm to stop extruding before the end of a track */ private double extrusionOverRun; /** * The number of mm to stop extruding before the end of a track */ private double valveOverRun; /** * The smallest allowable free-movement height above the base */ private double minLiftedZ = 1; /** * The number of outlines to plot */ int shells = 1; public GenericExtruder(Communicator communicator, Address address, Preferences prefs, int extruderId) { super(communicator, address); tH = new double[] {20, 20, 20}; // Bit of a hack - room temp tHi = 0; myExtruderID = extruderId; String prefName = "Extruder" + extruderId + "_"; beta = prefs.loadDouble(prefName + "Beta(K)"); rz = prefs.loadDouble(prefName + "Rz(ohms)"); cap = prefs.loadDouble(prefName + "Capacitor(F)"); hm = prefs.loadDouble(prefName + "hm(C/pwr)"); hb = prefs.loadDouble(prefName + "hb(C)"); maxExtruderSpeed = prefs.loadInt(prefName + "MaxSpeed(0..255)"); extrusionSpeed = prefs.loadInt(prefName + "ExtrusionSpeed(0..255)"); extrusionTemp = prefs.loadDouble(prefName + "ExtrusionTemp(C)"); extrusionSize = prefs.loadDouble(prefName + "ExtrusionSize(mm)"); extrusionHeight = prefs.loadDouble(prefName + "ExtrusionHeight(mm)"); extrusionInfillWidth = prefs.loadDouble(prefName + "ExtrusionInfillWidth(mm)"); lowerFineThickness = prefs.loadDouble(prefName + "LowerFineThickness(mm)"); upperFineThickness = prefs.loadDouble(prefName + "UpperFineThickness(mm)"); extrusionBroadWidth = prefs.loadDouble(prefName + "ExtrusionBroadWidth(mm)"); coolingPeriod = prefs.loadInt(prefName + "CoolingPeriod(s)"); xySpeed = prefs.loadInt(prefName + "XYSpeed(0..255)"); t0 = prefs.loadInt(prefName + "t0(0..255)"); iSpeed = prefs.loadDouble(prefName + "InfillSpeed(0..1)"); oSpeed = prefs.loadDouble(prefName + "OutlineSpeed(0..1)"); asLength = prefs.loadDouble(prefName + "AngleSpeedLength(mm)"); asFactor = prefs.loadDouble(prefName + "AngleSpeedFactor(0..1)"); material = prefs.loadString(prefName + "MaterialType(name)"); offsetX = prefs.loadDouble(prefName + "OffsetX(mm)"); offsetY = prefs.loadDouble(prefName + "OffsetY(mm)"); offsetZ = prefs.loadDouble(prefName + "OffsetZ(mm)"); nozzleWipeEnabled = prefs.loadBool(prefName + "NozzleWipeEnabled"); nozzleWipeDatumX = prefs.loadDouble(prefName + "NozzleWipeDatumX(mm)"); nozzleWipeDatumY = prefs.loadDouble(prefName + "NozzleWipeDatumY(mm)"); nozzleWipeStrokeX = prefs.loadDouble(prefName + "NozzleWipeStrokeX(mm)"); nozzleWipeStrokeY = prefs.loadDouble(prefName + "NozzleWipeStrokeY(mm)"); nozzleWipeFreq = prefs.loadInt(prefName + "NozzleWipeFreq"); nozzleClearTime = prefs.loadDouble(prefName + "NozzleClearTime(s)"); nozzleWaitTime = prefs.loadDouble(prefName + "NozzleWaitTime(s)"); randSt = prefs.loadBool(prefName + "RandomStart"); incrementedSt = prefs.loadBool(prefName + "IncrementedStart"); shortLength = prefs.loadDouble(prefName + "ShortLength(mm)"); shortSpeed = prefs.loadDouble(prefName + "ShortSpeed(0..1)"); infillOverlap = prefs.loadDouble(prefName + "InfillOverlap(mm)"); extrusionDelayForLayer = prefs.loadDouble(prefName + "ExtrusionDelayForLayer(ms)"); extrusionDelayForPolygon = prefs.loadDouble(prefName + "ExtrusionDelayForPolygon(ms)"); extrusionOverRun = prefs.loadDouble(prefName + "ExtrusionOverRun(mm)"); valveDelayForLayer = prefs.loadDouble(prefName + "ValveDelayForLayer(ms)"); valveDelayForPolygon = prefs.loadDouble(prefName + "ValveDelayForPolygon(ms)"); valveOverRun = prefs.loadDouble(prefName + "ValveOverRun(mm)"); minLiftedZ = prefs.loadDouble(prefName + "MinimumZClearance(mm)"); // NB - store as 2ms ticks to allow longer pulses valvePulseTime = 0.5*prefs.loadDouble(prefName + "ValvePulseTime(ms)"); shells = prefs.loadInt(prefName + "NumberOfShells(0..N)"); Color3f col = new Color3f((float)prefs.loadDouble(prefName + "ColourR(0..1)"), (float)prefs.loadDouble(prefName + "ColourG(0..1)"), (float)prefs.loadDouble(prefName + "ColourB(0..1)")); materialColour = new Appearance(); materialColour.setMaterial(new Material(col, black, col, black, 101f)); wipeX = getNozzleWipeDatumX(); //Anyone home? if(!isAvailable()) return; // Set up thermometer try { setTempRange(); } catch (Exception ex) { return; } /*pollThread = new Thread() { public void run() { Thread.currentThread().setName("Extruder poll"); boolean first = true; while(!pollThreadExiting) { try { // Sleep is beforehand to prevent runaway on exception if (!first) Thread.sleep(2000); first = false; RefreshTemperature(); RefreshEmptySensor(); sensorsInitialised = true; } catch (InterruptedException ex) { // This is normal when shutting down, so ignore } catch (Exception ex) { System.out.println("Exception during temperature poll"); ex.printStackTrace(); } } } }; pollThread.start();*/ } /* (non-Javadoc) * @see org.reprap.Extruder#dispose() */ public void dispose() { if (pollThread != null) { pollThreadExiting = true; pollThread.interrupt(); } } /** * Start the extruder motor at a given speed. This ranges from 0 * to 255 but is scaled by maxSpeed and t0, so that 255 corresponds to the * highest permitted speed. It is also scaled so that 0 would correspond * with the lowest extrusion speed. * @param speed The speed to drive the motor at (0-255) * @throws IOException */ public void setExtrusion(int speed) throws IOException { if(!wasAvailable()) { Debug.d("Attempting to control or interrogate non-existent extruder for " + material); return; } setExtrusion(speed, false); } /** * Start the extruder motor at a given speed. This ranges from 0 * to 255 but is scaled by maxSpeed and t0, so that 255 corresponds to the * highest permitted speed. It is also scaled so that 0 would correspond * with the lowest extrusion speed. * @param speed The speed to drive the motor at (0-255) * @param reverse If set, run extruder in reverse * @throws IOException */ public void setExtrusion(int speed, boolean reverse) throws IOException { if(!wasAvailable()) { Debug.d("Attempting to control or interrogate non-existent extruder for " + material); return; } // Assumption: Between t0 and maxSpeed, the speed is fairly linear int scaledSpeed; if (speed > 0) scaledSpeed = (int)Math.round((maxExtruderSpeed - t0) * speed / 255.0 + t0); else scaledSpeed = 0; lock(); try { OutgoingMessage request = new OutgoingByteMessage(reverse ? MSG_SetActiveReverse : MSG_SetActive, (byte)scaledSpeed); sendMessage(request); } finally { unlock(); } } /** * Open or close the valve. pulseTime is the number of ms to zap it. * @param pulseTime * @param valveOpen * @throws IOException */ public void setValve(boolean valveOpen) throws IOException { if(valvePulseTime < 0) return; if(!wasAvailable()) { Debug.d("Attempting to control or interrogate non-existent extruder for " + material); return; } lock(); try { OutgoingMessage request = new OutgoingByteMessage(valveOpen ? MSG_ValveOpen : MSG_ValveClosed, (byte)valvePulseTime); sendMessage(request); } finally { unlock(); } } /** * Turn the extruder on using the extrusionTemp property * @throws Exception */ public void heatOn() throws Exception { if(!wasAvailable()) { Debug.d("Attempting to control or interrogate non-existent extruder for " + material); return; } setTemperature(extrusionTemp); } /* (non-Javadoc) * @see org.reprap.Extruder#setTemperature(double) */ public void setTemperature(double temperature) throws Exception { if(!wasAvailable()) { Debug.d("Attempting to control or interrogate non-existent extruder for " + material); return; } setTemperature(temperature, true); } /** * @param temperature * @param lock * @throws Exception */ private void setTemperature(double temperature, boolean lock) throws Exception { requestedTemperature = temperature; if(Math.abs(requestedTemperature - extrusionTemp) > 5) { Debug.d(material + " extruder temperature set to " + requestedTemperature + "C, which is not the standard temperature (" + extrusionTemp + "C)."); } // Aim for 10% above our target to ensure we reach it. It doesn't matter // if we go over because the power will be adjusted when we get there. At // the same time, if we aim too high, we'll overshoot a bit before we // can react. // Tighter temp constraints under test 10% -> 3% (10-1-8) double temperature0 = temperature * 1.03; // A safety cutoff will be set at 20% above requested setting // Tighter temp constraints added by eD 20% -> 6% (10-1-8) double temperatureSafety = temperature * 1.06; // Calculate power output from hm, hb. In general, the temperature // we achieve is power * hm + hb. So to achieve a given temperature // we need a power of (temperature - hb) / hm // If we reach our temperature, rather than switching completely off // go to a reduced power level. int power0 = (int)Math.round(((0.9 * temperature0) - hb) / hm); if (power0 < 0) power0 = 0; if (power0 > 255) power0 = 255; // Otherwise, this is the normal power level we will maintain int power1 = (int)Math.round((temperature0 - hb) / hm); if (power1 < 0) power1 = 0; if (power1 > 255) power1 = 255; // Now convert temperatures to equivalent raw PIC temperature resistance value // Here we use the original specified temperature, not the slight overshoot double resistance0 = calculateResistanceForTemperature(temperature); double resistanceSafety = calculateResistanceForTemperature(temperatureSafety); // Determine equivalent raw value int t0 = calculatePicTempForResistance(resistance0); if (t0 < 0) t0 = 0; if (t0 > 255) t0 = 255; int t1 = calculatePicTempForResistance(resistanceSafety); if (t1 < 0) t1 = 0; if (t1 > 255) t1 = 255; if (temperature == 0) setHeater(0, 0, lock); else { setHeater(power0, power1, t0, t1, lock); } } /** * Set a heat output power. For normal production use you would * normally call setTemperature, however this method may be useful * for lower temperature profiling, etc. * @param heat Heater power (0-255) * @param maxTemp Cutoff temperature in celcius * @throws IOException */ public void setHeater(int heat, double maxTemp) throws IOException { if(!wasAvailable()) { Debug.d("Attempting to control or interrogate non-existent extruder for " + material); return; } double safetyResistance = calculateResistanceForTemperature(maxTemp); // Determine equivalent raw value int safetyPICTemp = calculatePicTempForResistance(safetyResistance); if (safetyPICTemp < 0) safetyPICTemp = 0; if (safetyPICTemp > 255) safetyPICTemp = 255; if (heat == 0) setHeater(0, 0, true); else setHeater(heat, safetyPICTemp, true); } /** * Set the raw heater output value and safety cutoff. A specific * temperature can be reached by setting a suitable output power. * A limit temperature can also be specified. If reached, the * heater will be automatically turned off until the temperature * drops below the limit. * @param heat A heater power output * @param safetyCutoff A temperature at which to cut off the heater * @throws IOException */ private void setHeater(int heat, int safetyCutoff, boolean lock) throws IOException { //System.out.println(material + " extruder heater set to " + heat + " limit " + safetyCutoff); if (lock) lock(); try { sendMessage(new RequestSetHeat((byte)heat, (byte)safetyCutoff)); } finally { if (lock) unlock(); } } /** * Similar to setHeater(int, int) except this provides two different * heating zones. Below t0, it heats at h1. From t0 to t1 it heats at h0 * and above t1 it shuts off. This has the effect of still providing some * power when the desired temperature is reached so it cools less quickly. * @param heat0 * @param heat1 * @param t0 * @param t1 * @throws IOException */ private void setHeater(int heat0, int heat1, int t0, int t1, boolean lock) throws IOException { Debug.d(material + " extruder heater set to " + heat0 + "/" + heat1 + " limit " + t0 + "/" + t1); if (lock) lock(); try { sendMessage(new RequestSetHeat((byte)heat0, (byte)heat1, (byte)t0, (byte)t1)); } finally { if (lock) unlock(); } } /** * Check if the extruder is out of feedstock * @return true if there is no material remaining */ public boolean isEmpty() { if(!wasAvailable()) { Debug.d("Attempting to control or interrogate non-existent extruder for " + material); return true; } awaitSensorsInitialised(); TEMPpollcheck(); return currentMaterialOutSensor; } /** * */ private void awaitSensorsInitialised() { // Simple minded wait to let sensors become valid //while(!sensorsInitialised) { // try { // Thread.sleep(100); // } catch (InterruptedException e) { // } //} } /** * Send current vRefFactor and a suitable timer scaler * to the device. * */ private void setTempRange() throws Exception { // We will send the vRefFactor to the PIC. At the same // time we will send a suitable temperature scale as well. // To maximize the range, when vRefFactor is high (15) then // the scale is minimum (0). Debug.d(material + " extruder vRefFactor set to " + vRefFactor); tempScaler = 7 - (vRefFactor >> 1); setVref(vRefFactor); setTempScaler(tempScaler); if (requestedTemperature != 0) setTemperature(requestedTemperature, false); } /** * Called internally to refresh the empty sensor. This is * called periodically in the background by another thread. * @throws IOException */ private void RefreshEmptySensor() throws IOException { // TODO in future, this should use the notification mechanism rather than polling (when fully working) lock(); try { //System.out.println(material + " extruder refreshing sensor"); RequestIsEmptyResponse reply = new RequestIsEmptyResponse(this, new OutgoingBlankMessage(MSG_IsEmpty), 500); currentMaterialOutSensor = reply.getValue() == 0 ? false : true; } catch (InvalidPayloadException e) { throw new IOException(); } finally { unlock(); } } /* (non-Javadoc) * @see org.reprap.Extruder#getTemperatureTarget() */ public double getTemperatureTarget() { return requestedTemperature; } /* (non-Javadoc) * @see org.reprap.Extruder#getDefaultTemperature() */ public double getDefaultTemperature() { return extrusionTemp; } /** * TEMPORARY WORKAROUND FUNCTION */ private void TEMPpollcheck() { if (System.currentTimeMillis() - lastTemperatureUpdate > 10000) { // Polled updates are having a hard time getting through with // the temporary comms locking, so we'll get them through here try { RefreshEmptySensor(); RefreshTemperature(); tH[tHi] = currentTemperature; currentTemperature = tempVote(); } catch (Exception ex) { Debug.d(material + " extruder exception during temperature/material update ignored"); ex.printStackTrace(); } } } /** * Take a vote among the last three temperatures * @return */ private double tempVote() { int ip = (tHi + 1)%3; int ipp = (tHi + 2)%3; double dp = Math.abs(tH[tHi] - tH[ip]); double dpp = Math.abs(tH[tHi] - tH[ipp]); double d = Math.abs(tH[ip] - tH[ipp]); if(dp <= d || dpp <= d) d = tH[tHi]; else d = 0.5*(tH[ip] + tH[ipp]); //Debug.d("tempVote() - t0: " + tH[0] + ", t1: " + tH[1] + ", t2: " + tH[2] + //", current: " + tH[tHi] + ", returning: " + d); tHi = (tHi + 1)%3; return d; } /* (non-Javadoc) * @see org.reprap.Extruder#getTemperature() */ public double getTemperature() { if(!wasAvailable()) { Debug.d("Attempting to control or interrogate non-existent extruder for " + material); return Preferences.absoluteZero(); } awaitSensorsInitialised(); TEMPpollcheck(); //return tempVote(); return currentTemperature; } /** * The the outline speed and the infill speed [0,1] */ public double getInfillSpeed() { return iSpeed; } /* (non-Javadoc) * @see org.reprap.Extruder#getOutlineSpeed() */ public double getOutlineSpeed() { return oSpeed; } /** * The length in mm to speed up when going round corners * (non-Javadoc) * @see org.reprap.Extruder#getAngleSpeedUpLength() */ public double getAngleSpeedUpLength() { return asLength; } /** * The factor by which to speed up when going round a corner. * The formula is speed = baseSpeed*[1 - 0.5*(1 + ca)*getAngleSpeedFactor()] * where ca is the cos of the angle between the lines. So it goes fastest when * the line doubles back on itself (returning 1), and slowest when it * continues straight (returning 1 - getAngleSpeedFactor()). * (non-Javadoc) * @see org.reprap.Extruder#getAngleSpeedFactor() */ public double getAngleSpeedFactor() { return asFactor; } /* (non-Javadoc) * @see org.reprap.Extruder#setCooler(boolean) */ public void setCooler(boolean f) throws IOException { if(!wasAvailable()) { Debug.d("Attempting to control or interrogate non-existent extruder for " + material); return; } lock(); try { OutgoingMessage request = new OutgoingByteMessage(MSG_SetCooler, f?(byte)200:(byte)0); // Should set speed properly! sendMessage(request); } finally { unlock(); } } /** * @throws Exception */ private void RefreshTemperature() throws Exception { //System.out.println(material + " extruder refreshing temperature"); getDeviceTemperature(); } /** * * @param rawHeat * @return * @throws Exception */ private boolean rerangeTemperature(int rawHeat) throws Exception { boolean notDone = false; if (rawHeat == 255 && vRefFactor > 0) { vRefFactor--; Debug.d(material + " extruder re-ranging temperature (faster): "); setTempRange(); } else if (rawHeat < 64 && vRefFactor < 15) { vRefFactor++; Debug.d(material + " extruder re-ranging temperature (slower): "); setTempRange(); } else notDone = true; return notDone; } /** * * @throws Exception */ private void getDeviceTemperature() throws Exception { lock(); try { int rawHeat = 0; int calibration = 0; for(;;) { // Don't repeatedly re-range? OutgoingMessage request = new OutgoingBlankMessage(MSG_GetTemp); RequestTemperatureResponse reply = new RequestTemperatureResponse(this, request, 500); rawHeat = reply.getHeat(); //System.out.println(material + " extruder raw temp " + rawHeat); calibration = reply.getCalibration(); if(rerangeTemperature(rawHeat)) break; // All ok else Thread.sleep(500); // Wait for PIC temp routine to settle before going again } double resistance = calculateResistance(rawHeat, calibration); currentTemperature = calculateTemperature(resistance); Debug.d(material + " extruder current temp " + currentTemperature); lastTemperatureUpdate = System.currentTimeMillis(); } finally { unlock(); } } /** * Calculates the actual resistance of the thermistor * from the raw timer values received from the PIC. * @param picTemp * @param calibrationPicTemp * @return */ private double calculateResistance(int picTemp, int calibrationPicTemp) { // TODO remove hard coded constants // TODO should use calibration value instead of first principles //double resistor = 10000; // ohms //double c = 1e-6; // farads - now cap from prefs(AB) double scale = 1 << (tempScaler+1); double clock = 4000000.0 / (4.0 * scale); // hertz double vdd = 5.0; // volts double vRef = 0.25 * vdd + vdd * vRefFactor / 32.0; // volts double T = picTemp / clock; // seconds double resistance = -T / (Math.log(1 - vRef / vdd) * cap); // ohms return resistance; } /** * Calculate temperature in celsius given resistance in ohms * @param resistance * @return */ private double calculateTemperature(double resistance) { return (1.0 / (1.0 / absZero + Math.log(resistance/rz) / beta)) - absZero; } /** * @param temperature * @return */ private double calculateResistanceForTemperature(double temperature) { return rz * Math.exp(beta * (1/(temperature + absZero) - 1/absZero)); } /** * Calculates an expected PIC Temperature expected for a * given resistance * @param resistance * @return */ private int calculatePicTempForResistance(double resistance) { //double c = 1e-6; // farads - now cap from prefs(AB) double scale = 1 << (tempScaler+1); double clock = 4000000.0 / (4.0 * scale); // hertz double vdd = 5.0; // volts double vRef = 0.25 * vdd + vdd * vRefFactor / 32.0; // volts double T = -resistance * (Math.log(1 - vRef / vdd) * cap); double picTemp = T * clock; return (int)Math.round(picTemp); } /** * Set raw voltage reference used for analogue to digital converter * @param ref Set reference voltage (0-15). Actually this is * just directly OR'd into the PIC VRCON register, so it can also * set the High/Low range bit. * @throws IOException */ private void setVref(int ref) throws IOException { sendMessage(new OutgoingByteMessage(MSG_SetVRef, (byte)ref)); vRefFactor = ref; } /** * Set the scale factor used on the temperature timer used * for analogue to digital conversion * @param scale A value from 0..7 * @throws IOException */ private void setTempScaler(int scale) throws IOException { sendMessage(new OutgoingByteMessage(MSG_SetTempScaler, (byte)scale)); tempScaler = scale; } // /* (non-Javadoc) // * @see org.reprap.Extruder#isAvailable() // */ // public boolean isAvailable() { // return isCommsAvailable; // } /** * */ protected class RequestTemperatureResponse extends IncomingMessage { /** * @param device * @param message * @param timeout * @throws IOException */ public RequestTemperatureResponse(Device device, OutgoingMessage message, long timeout) throws IOException { super(device, message, timeout); } /* (non-Javadoc) * @see org.reprap.comms.IncomingMessage#isExpectedPacketType(byte) */ protected boolean isExpectedPacketType(byte packetType) { return packetType == MSG_GetTemp; } /** * @return * @throws InvalidPayloadException */ public int getHeat() throws InvalidPayloadException { byte [] reply = getPayload(); if (reply == null || reply.length != 3) throw new InvalidPayloadException(); return reply[1] < 0 ? reply[1] + 256 : reply[1]; } /** * @return * @throws InvalidPayloadException */ public int getCalibration() throws InvalidPayloadException { byte [] reply = getPayload(); if (reply == null || reply.length != 3) throw new InvalidPayloadException(); return reply[2] < 0 ? reply[2] + 256 : reply[2]; } } /** * */ protected class RequestSetHeat extends OutgoingMessage { /** * */ byte [] message; /** * @param heat * @param cutoff */ RequestSetHeat(byte heat, byte cutoff) { message = new byte [] { MSG_SetHeat, heat, heat, cutoff, cutoff }; } /** * @param heat0 * @param heat1 * @param t0 * @param t1 */ RequestSetHeat(byte heat0, byte heat1, byte t0, byte t1) { message = new byte [] { MSG_SetHeat, heat0, heat1, t0, t1}; } /* (non-Javadoc) * @see org.reprap.comms.OutgoingMessage#getBinary() */ public byte[] getBinary() { return message; } } /** * */ protected class RequestIsEmptyResponse extends IncomingMessage { /** * @param device * @param message * @param timeout * @throws IOException */ public RequestIsEmptyResponse(Device device, OutgoingMessage message, long timeout) throws IOException { super(device, message, timeout); } /** * @return * @throws InvalidPayloadException */ public byte getValue() throws InvalidPayloadException { byte [] reply = getPayload(); if (reply == null || reply.length != 2) throw new InvalidPayloadException(); return reply[1]; } /* (non-Javadoc) * @see org.reprap.comms.IncomingMessage#isExpectedPacketType(byte) */ protected boolean isExpectedPacketType(byte packetType) { return packetType == MSG_IsEmpty; } } /* (non-Javadoc) * @see org.reprap.Extruder#getXYSpeed() */ public int getXYSpeed() { return xySpeed; } /* (non-Javadoc) * @see org.reprap.Extruder#getExtruderSpeed() */ public int getExtruderSpeed() { return extrusionSpeed; } /* (non-Javadoc) * @see org.reprap.Extruder#getExtrusionSize() */ public double getExtrusionSize() { return extrusionSize; } /* (non-Javadoc) * @see org.reprap.Extruder#getExtrusionHeight() */ public double getExtrusionHeight() { return extrusionHeight; } /** * At the top and bottom return the fine width; in between * return the braod one. If the braod one is negative, just do fine. */ public double getExtrusionInfillWidth(double z, double zMax) { if(z < lowerFineThickness) return extrusionInfillWidth; if(z > zMax - upperFineThickness) return extrusionInfillWidth; if(extrusionBroadWidth < 0) return extrusionInfillWidth; return extrusionBroadWidth; } /* (non-Javadoc) * @see org.reprap.Extruder#getCoolingPeriod() */ public int getCoolingPeriod() { return coolingPeriod; } /* (non-Javadoc) * @see org.reprap.Extruder#getOffsetX() */ public double getOffsetX() { return offsetX; } /* (non-Javadoc) * @see org.reprap.Extruder#getOffsetY() */ public double getOffsetY() { return offsetY; } /* (non-Javadoc) * @see org.reprap.Extruder#getOffsetZ() */ public double getOffsetZ() { return offsetZ; } /* (non-Javadoc) * @see org.reprap.Extruder#getColour() */ public Appearance getAppearance() { return materialColour; } /** * @return the name of the material */ public String toString() { return material; } /** * @return determine whether nozzle wipe method is enabled or not */ public boolean getNozzleWipeEnabled() { return nozzleWipeEnabled; } /** * @return the X-cord for the nozzle wiper */ public double getNozzleWipeDatumX() { return nozzleWipeDatumX; } /** * @return the Y-cord for the nozzle wiper */ public double getNozzleWipeDatumY() { return nozzleWipeDatumY; } /** * @return the length of the nozzle movement over the wiper */ public double getNozzleWipeStrokeX() { return nozzleWipeStrokeX; } /** * @return the length of the nozzle movement over the wiper */ public double getNozzleWipeStrokeY() { return nozzleWipeStrokeY; } /** * @return the number of times the nozzle moves over the wiper */ public int getNozzleWipeFreq() { return nozzleWipeFreq; } /** * @return the time to extrude before wiping the nozzle */ public double getNozzleClearTime() { return nozzleClearTime; } /** * @return the time to wait after extruding before wiping the nozzle */ public double getNozzleWaitTime() { return nozzleWaitTime; } /** * Start polygons at a random location round their perimiter * @return */ public boolean randomStart() { return randSt; } /** * Start polygons at an incremented location round their perimiter * @return */ public boolean incrementedStart() { return incrementedSt; } /** * get short lengths which need to be plotted faster * set -ve to turn this off. * @return */ public double getShortLength() { return shortLength; } /** * Factor (between 0 and 1) to use to set the speed for * short lines. * @return */ public double getShortSpeed() { return shortSpeed; } /** * Number of mm to overlap the hatching infill with the outline. 0 gives none; -ve will * leave a gap between the two * @return */ public double getInfillOverlap() { return infillOverlap; } /** * Gets the number of milliseconds to wait before starting a border track * @return */ public double getExtrusionDelayForLayer() { return extrusionDelayForLayer; } /** * Gets the number of milliseconds to wait before starting a hatch track * @return */ public double getExtrusionDelayForPolygon() { return extrusionDelayForPolygon; } /** * Gets the number of milliseconds to wait before opening the valve * for the first track of a layer * @return */ public double getValveDelayForLayer() { return valveDelayForLayer; } /** * Gets the number of milliseconds to wait before opening the valve * for any other track * @return */ public double getValveDelayForPolygon() { return valveDelayForPolygon; } /* (non-Javadoc) * @see org.reprap.Extruder#getExtrusionOverRun() */ public double getExtrusionOverRun() { return extrusionOverRun; } /** * @return the valve overrun in millimeters (i.e. how many mm * before the end of a track to turn off the extrude motor) */ public double getValveOverRun() { return valveOverRun; } /** * The smallest allowable free-movement height above the base * @return */ public double getMinLiftedZ() { return minLiftedZ; } /** * The number of times to go round the outline (0 to supress) * @return */ public int getShells() { return shells; } public void finishedLayer(int layerNumber, Printer printer) throws Exception { if(getCoolingPeriod() > 0 && (layerNumber != 0)) setCooler(true); // Lift up if too close to the bed double rememberZ = printer.getZ(); double minLiftedZ = getMinLiftedZ(); if(rememberZ < minLiftedZ) { printer.moveTo(printer.getX(), printer.getY(), minLiftedZ, false, false); } printer.setSpeed(printer.getFastSpeed()); // Go home printer.moveTo(wipeX, getNozzleWipeDatumY(), minLiftedZ, false, false); printer.moveTo(wipeX, getNozzleWipeDatumY(), rememberZ, false, false); } /** * Deals with all the actions that need to be done between one layer * and the next. */ public void betweenLayers(int layerNumber, Printer printer) throws Exception { // Now is a good time to garbage collect System.gc(); // Cooling period if(getCoolingPeriod() > 0 && (layerNumber != 0)) { Debug.d("Start of cooling period"); // Wait for cooling time Thread.sleep((long)(1000*getCoolingPeriod())); setCooler(false); Thread.sleep((long)(200 * getCoolingPeriod())); Debug.d("End of cooling period"); } } /** * Just about to start the next layer * @param layerNumber */ public void startingLayer(int layerNumber, Printer printer) throws Exception { if (getNozzleWipeEnabled()) { printer.setSpeed(LinePrinter.speedFix(getXYSpeed(), getOutlineSpeed())); printer.moveTo(wipeX, getNozzleWipeDatumY() + 8, printer.getZ(), false, false); setExtrusion(getExtruderSpeed()); if(getExtrusionDelayForLayer() > 0) Thread.sleep((long)getExtrusionDelayForLayer()); printer.moveTo(wipeX, getNozzleWipeDatumY() + getNozzleWipeStrokeY()*0.5, printer.getZ(), false, false); setExtrusion(0); printer.moveTo(wipeX, getNozzleWipeDatumY() + getNozzleWipeStrokeY(), printer.getZ(), false, false); wipeX += getExtrusionInfillWidth(0, 1); // Z value not important for this if(wipeX > getNozzleWipeDatumX() + getNozzleWipeStrokeX()) wipeX = getNozzleWipeDatumX(); } printer.setSpeed(printer.getFastSpeed()); } }