Package org.openpnp.machine.reference.driver

Source Code of org.openpnp.machine.reference.driver.SprinterDriver

/*
   Copyright (C) 2011 Jason von Nieda <jason@vonnieda.org>
  
   This file is part of OpenPnP.
  
  OpenPnP is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    OpenPnP 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 OpenPnP.  If not, see <http://www.gnu.org/licenses/>.
  
   For more information about OpenPnP visit http://openpnp.org
*/

package org.openpnp.machine.reference.driver;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.TimeoutException;

import javax.swing.Action;

import org.openpnp.ConfigurationListener;
import org.openpnp.gui.support.PropertySheetWizardAdapter;
import org.openpnp.gui.support.Wizard;
import org.openpnp.machine.reference.ReferenceActuator;
import org.openpnp.machine.reference.ReferenceHead;
import org.openpnp.machine.reference.ReferenceHeadMountable;
import org.openpnp.machine.reference.ReferenceNozzle;
import org.openpnp.model.Configuration;
import org.openpnp.model.LengthUnit;
import org.openpnp.model.Location;
import org.openpnp.spi.PropertySheetHolder;
import org.simpleframework.xml.Attribute;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* TODO: Consider adding some type of heartbeat to the firmware. 
*/

//Implemented Codes
//-------------------
//G0  -> G1
//G1  - Coordinated Movement X Y Z E
//G2  - CW ARC
//G3  - CCW ARC
//G4  - Dwell S<seconds> or P<milliseconds>
//G28 - Home all Axis
//G90 - Use Absolute Coordinates
//G91 - Use Relative Coordinates
//G92 - Set current position to cordinates given

//RepRap M Codes
//M104 - Set extruder target temp
//M105 - Read current temp
//M106 - Fan on
//M107 - Fan off
//M109 - Wait for extruder current temp to reach target temp.
//M114 - Display current position

//Custom M Codes
//M20  - List SD card
//M21  - Init SD card
//M22  - Release SD card
//M23  - Select SD file (M23 filename.g)
//M24  - Start/resume SD print
//M25  - Pause SD print
//M26  - Set SD position in bytes (M26 S12345)
//M27  - Report SD print status
//M28  - Start SD write (M28 filename.g)
//M29  - Stop SD write
// -  <filename> - Delete file on sd card
//M42  - Set output on free pins, on a non pwm pin (over pin 13 on an arduino mega) use S255 to turn it on and S0 to turn it off. Use P to decide the pin (M42 P23 S255) would turn pin 23 on
//M80  - Turn on Power Supply
//M81  - Turn off Power Supply
//M82  - Set E codes absolute (default)
//M83  - Set E codes relative while in Absolute Coordinates (G90) mode
//M84  - Disable steppers until next move,
//      or use S<seconds> to specify an inactivity timeout, after which the steppers will be disabled.  S0 to disable the timeout.
//M85  - Set inactivity shutdown timer with parameter S<seconds>. To disable set zero (default)
//M92  - Set axis_steps_per_unit - same syntax as G92
//M93  - Send axis_steps_per_unit
//M115  - Capabilities string
//M119 - Show Endstopper State
//M140 - Set bed target temp
//M190 - Wait for bed current temp to reach target temp.
//M201 - Set maximum acceleration in units/s^2 for print moves (M201 X1000 Y1000)
//M202 - Set maximum feedrate that your machine can sustain (M203 X200 Y200 Z300 E10000) in mm/sec
//M203 - Set temperture monitor to Sx
//M204 - Set default acceleration: S normal moves T filament only moves (M204 S3000 T7000) in mm/sec^2
//M205 - advanced settings:  minimum travel speed S=while printing T=travel only,  X=maximum xy jerk, Z=maximum Z jerk
//M206 - set additional homing offset

//M220 - set speed factor override percentage S=factor in percent
//M221 - set extruder multiply factor S100 --> original Extrude Speed

//M301 - Set PID parameters P I and D
//M303 - PID relay autotune S<temperature> sets the target temperature. (default target temperature = 150C)

//M400 - Finish all moves

//M500 - stores paramters in EEPROM
//M501 - reads parameters from EEPROM (if you need to reset them after you changed them temporarily).
//M502 - reverts to the default "factory settings". You still need to store them in EEPROM afterwards if you want to.
//M503 - Print settings

//Debug feature / Testing the PID for Hotend
//M601 - Show Temp jitter from Extruder (min / max value from Hotend Temperature while printing)
//M602 - Reset Temp jitter from Extruder (min / max val) --> Don't use it while Printing
//M603 - Show Free Ram

public class SprinterDriver extends AbstractSerialPortDriver implements Runnable {

/*  @Attribute(required=false)
    private int vacpumpPin;
    @Attribute(required=false)
    private boolean invertVacpump;

*/ 
  private static final Logger logger = LoggerFactory.getLogger(SprinterDriver.class);
//  private static final double minimumRequiredVersion = 0.75;
 
  @Attribute(required=false)
  private int vacuumPin = 31;
 
  @Attribute(required=false)
  private boolean invertVacuum;
 
    @Attribute(required=false)
  private int actuatorPin = 33;
 
  @Attribute(required=false)
  private boolean invertActuator;
 
  @Attribute(required=false)
  private boolean homeX;
 
  @Attribute(required=false)
  private boolean homeY;
 
  @Attribute(required=false)
  private boolean homeZ;
 
  @Attribute(required=false)
  private boolean homeC;
 
  @Attribute
    private double feedRateMmPerMinute;
 
  private double x, y, z, c;
  private Thread readerThread;
  private boolean disconnectRequested;
  private Object commandLock = new Object();
  private boolean connected;
//  private double connectedVersion;
  private Queue<String> responseQueue = new ConcurrentLinkedQueue<String>();
 
  public SprinterDriver() {
      Configuration.get().addListener(new ConfigurationListener.Adapter() {
            @Override
            public void configurationComplete(Configuration configuration)
                    throws Exception {
                connect();
            }
      });
  }
 
    @Override
    public void home(ReferenceHead head) throws Exception {
        if (homeX || homeY || homeZ || homeC) {
            sendCommand(String.format("G28 %s %s %s %s", homeX ? "X" : "", homeY ? "Y" : "", homeZ ? "Z" : "", homeC ? "E" : ""));
            dwell();
        }
        else {
            throw new Exception("No homing axes defined. See the homeX, homeY, homeZ and homeC parameters.");
        }
        // Reset all axes to 0. This is required so that the Head and Driver
        // stay in sync.
        sendCommand("G92 X0 Y0 Z0 E0");
        x = y = z = c = 0;
    }

    @Override
    public void moveTo(ReferenceHeadMountable hm, Location location,
            double speed) throws Exception {
        location = location.subtract(hm.getHeadOffsets());

        location = location.convertToUnits(LengthUnit.Millimeters);

        double x = location.getX();
        double y = location.getY();
        double z = location.getZ();
        double c = location.getRotation();

        StringBuffer sb = new StringBuffer();
        if (!Double.isNaN(x) && x != this.x) {
            sb.append(String.format(Locale.US, "X%2.4f ", x));
        }
        if (!Double.isNaN(y) && y != this.y) {
            sb.append(String.format(Locale.US, "Y%2.4f ", y));
        }
        if (!Double.isNaN(z) && z != this.z) {
            sb.append(String.format(Locale.US, "Z%2.4f ", z));
        }
        if (!Double.isNaN(c) && c != this.c) {
            sb.append(String.format(Locale.US, "E%2.4f ", c));
        }
        if (sb.length() > 0) {
            sb.append(String.format(Locale.US, "F%2.4f ", feedRateMmPerMinute
                    * speed));
            sendCommand("G1" + sb.toString());
            dwell();
        }
        if (!Double.isNaN(x)) {
            this.x = x;
        }
        if (!Double.isNaN(y)) {
            this.y = y;
        }
        if (!Double.isNaN(z)) {
            this.z = z;
        }
        if (!Double.isNaN(c)) {
            this.c = c;
        }
    }

    @Override
    public Location getLocation(ReferenceHeadMountable hm) {
        return new Location(LengthUnit.Millimeters, x, y, z, c).add(hm.getHeadOffsets());
    }

    @Override
    public void pick(ReferenceNozzle nozzle) throws Exception {
        sendCommand(String.format("M42 P%d S%d", vacuumPin, invertVacuum ? 0 : 255));
        dwell();
    }

    @Override
    public void place(ReferenceNozzle nozzle) throws Exception {
        sendCommand(String.format("M42 P%d S%d", vacuumPin, invertVacuum ? 255 : 0));
        dwell();
    }

    @Override
    public void actuate(ReferenceActuator actuator, boolean on)
            throws Exception {
        if (actuator == null || actuator.getIndex() == 0) {
            sendCommand(String.format("M42 P%d S%d", actuatorPin, on ^ invertActuator ? 255 : 0));
            dwell();
        }
    }

    @Override
    public void actuate(ReferenceActuator actuator, double value)
            throws Exception {
        // TODO Auto-generated method stub
       
    }

    @Override
  public void setEnabled(boolean enabled) throws Exception {
    sendCommand(String.format("M84 %s", enabled ? "T" : ""));
    place(null);
    actuate(null, false);
  }

  public synchronized void connect() throws Exception {
    super.connect();

    /**
     * Connection process notes:
     *
     * On some platforms, as soon as we open the serial port it will reset
     * Sprinter and we'll start getting some data. On others, Sprinter may
     * already be running and we will get nothing on connect.
     */
   
    List<String> responses;
    synchronized (commandLock) {
      // Start the reader thread with the commandLock held. This will
      // keep the thread from quickly parsing any responses messages
      // and notifying before we get a chance to wait.
      readerThread = new Thread(this);
      readerThread.start();
      // Wait up to 3 seconds for Sprinter to say Hi
      // If we get anything at this point it will have been the settings
      // dump that is sent after reset.
      responses = sendCommand(null, 3000);
    }

    processConnectionResponses(responses);

    for (int i = 0; i < 5 && !connected; i++) {
      responses = sendCommand("M115", 5000);
      processConnectionResponses(responses);
    }
   
    if (!connected)  {
      throw new Error(
//        String.format("Unable to receive connection response from Sprinter. Check your port and baud rate, and that you are running at least version %f of Sprinter",
//            minimumRequiredVersion));
        String.format("Unable to receive connection response from Sprinter. Check your port and baud rate, and that you are running the latest version of Sprinter."));
    }
   
    // TODO: Version Info
//    if (connectedVersion < minimumRequiredVersion) {
//      throw new Error(String.format("This driver requires Sprinter version %.2f or higher. You are running version %.2f", minimumRequiredVersion, connectedVersion));
//    }
   
    // We are connected to at least the minimum required version now
    // So perform some setup
   
    // Turn off the stepper drivers
    setEnabled(false);
   
    // Reset all axes to 0, in case the firmware was not reset on
    // connect.
    sendCommand("G92 X0 Y0 Z0 E0");
  }
 
  private void processConnectionResponses(List<String> responses) {
    for (String response : responses) {
      if (response.startsWith("FIRMWARE_NAME:") || response.equals("Sprinter")) {
//        String[] versionComponents = response.split(" ");
//        connectedVersion = Double.parseDouble(versionComponents[2]);
        connected = true;
//        logger.debug(String.format("Connected to Sprinter Version: %.2f", connectedVersion));
        logger.debug(String.format("Connected to Sprinter."));
      }
    }
  }

  public synchronized void disconnect() {
    disconnectRequested = true;
    connected = false;
   
    try {
      if (readerThread != null && readerThread.isAlive()) {
        readerThread.join();
      }
    }
    catch (Exception e) {
      logger.error("disconnect()", e);
    }
   
        try {
            super.disconnect();
        }
        catch (Exception e) {
            logger.error("disconnect()", e);
        }
        disconnectRequested = false;
  }

  protected List<String> sendCommand(String command) throws Exception {
    return sendCommand(command, -1);
  }
 
  private List<String> sendCommand(String command, long timeout) throws Exception {
    synchronized (commandLock) {
      if (command != null) {
        logger.debug("> " + command);
        output.write(command.getBytes());
        output.write("\n".getBytes());
      }
      long t = System.currentTimeMillis();
      if (timeout == -1) {
        commandLock.wait();
      }
      else {
        commandLock.wait(timeout);
      }
      logger.debug("Waited {} ms for command to return.", (System.currentTimeMillis() - t));
    }
    List<String> responses = drainResponseQueue();
    return responses;
  }
 
  public void run() {
    while (!disconnectRequested) {
            String line;
            try {
                line = readLine().trim();
            }
            catch (TimeoutException ex) {
                continue;
            }
            catch (IOException e) {
                logger.error("Read error", e);
                return;
            }
            line = line.trim();
      logger.debug("< " + line);
      responseQueue.offer(line);
      // We have a special case of accepting "start" when we are not
      // connected because Sprinter does not send an "ok" when it starts
      // up.
      if (line.equals("ok") || line.startsWith("error: ") || (!connected && line.equals("start"))) {
        // This is the end of processing for a command
        synchronized (commandLock) {
          commandLock.notify();
        }
      }
    }
  }

  /**
   * Causes Sprinter to block until all commands are complete.
   * @throws Exception
   */
  protected void dwell() throws Exception {
    sendCommand("M400");
  }

  private List<String> drainResponseQueue() {
    List<String> responses = new ArrayList<String>();
    String response;
    while ((response = responseQueue.poll()) != null) {
      responses.add(response);
    }
    return responses;
  }
 
    @Override
    public Wizard getConfigurationWizard() {
        return null;
    }

    @Override
    public String getPropertySheetHolderTitle() {
        return getClass().getSimpleName();
    }

    @Override
    public PropertySheetHolder[] getChildPropertySheetHolders() {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public PropertySheet[] getPropertySheets() {
        return new PropertySheet[] {
                new PropertySheetWizardAdapter(getConfigurationWizard())
        };
   
   
    @Override
    public Action[] getPropertySheetHolderActions() {
        // TODO Auto-generated method stub
        return null;
    }   
}
TOP

Related Classes of org.openpnp.machine.reference.driver.SprinterDriver

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.