micro-manager / mmCoreAndDevices

Micro-Manager's device control layer, written in C++
42 stars 109 forks source link

Interfacing Stage Control with Arduino-Hub controlled stepper #466

Open pchiang5 opened 5 months ago

pchiang5 commented 5 months ago

Thank you for the great tool.

I have a z-axis stepper and a rotary encoder controllable using an Arduino Mega with Marlin firmware. However, when I tried using the Hardware Configuration Wizard, it showed the following error message:

2024-06-04T15:41:44.391039 tid39096 [dbg,dev:COM4] GetAnswer <- echo:SD card released\n 2024-06-04T15:41:44.391066 tid39096 [WRN,Core:dev:COM4] Setting of pre-init property (AnswerTimeout) not permitted on initialized device (this will be an error in a future version of MMCore; for now we continue with the operation anyway, even though it might not be safe) 2024-06-04T15:41:44.391070 tid39096 [dbg,Core:dev:COM4] Will set property "AnswerTimeout" to "300.0000" 2024-06-04T15:41:44.391077 tid39096 [dbg,Core:dev:COM4] Did set property "AnswerTimeout" to "300.0000" 2024-06-04T15:41:44.391100 tid39096 [dbg,dev:COM4] SetCommand -> M114\r 2024-06-04T15:41:44.391119 tid39096 [WRN,Core:dev:COM4] Setting of pre-init property (AnswerTimeout) not permitted on initialized device (this will be an error in a future version of MMCore; for now we continue with the operation anyway, even though it might not be safe) 2024-06-04T15:41:44.391122 tid39096 [dbg,Core:dev:COM4] Will set property "AnswerTimeout" to "300.00" 2024-06-04T15:41:44.391125 tid39096 [dbg,Core:dev:COM4] Did set property "AnswerTimeout" to "300.00" 2024-06-04T15:41:44.692493 tid39096 [IFO,dev:COM4] TERMTIMEOUT error occured! 2024-06-04T15:41:44.692529 tid39096 [ERR,Core] Error occurred in device COM4: Error in device "COM4": (Error message unavailable) (107) 2024-06-04T15:41:44.692560 tid39096 [IFO,dev:RAMPSHub] answer get error! 2024-06-04T15:41:44.692562 tid39096 [IFO,dev:RAMPSHub] Get Current Position (M114) answer get error!_ 2024-06-04T15:41:44.692577 tid39096 [ERR,Core:dev:RAMPSHub] Error in device "RAMPSHub": Serial command failed. Is the device connected to the serial port? (14) 2024-06-04T15:41:44.721620 tid39096 [IFO,App] EDTHangLogger: First heartbeat after miss (8768 ms since timebase) 2024-06-04T15:41:46.127546 tid39096 [dbg,Core] Will unload device RAMPSHub 2024-06-04T15:41:46.127563 tid39096 [dbg,Core] Did unload device RAMPSHub 2024-06-04T15:41:46.127615 tid39096 [dbg,Core] Will load device RAMPSHub from RAMPS 2024-06-04T15:41:46.127644 tid39096 [IFO,Core] Did load device RAMPSHub from RAMPS; label = RAMPSHub

Because this firmware was tested and found to be working by inputting 'M114' in the serial terminal, with the output shown below. I wonder if the error is caused by the \n in the SetCommand -> M114\r? If it is the case, how to avoid the inclusion of this line terminal?

15:49:45.596 -> start 15:49:45.596 -> Marlin 2.1.2.3 15:49:45.596 -> echo: Last Updated: 2024-05-29 | Author: (Credomane, Tinkerine Ditto Pro) 15:49:45.664 -> echo: Compiled: Jun 4 2024 15:49:45.697 -> echo: Free Memory: 2321 PlannerBufferBytes: 1200 15:49:45.937 -> echo:EEPROM version mismatch (EEPROM=? Marlin=V88) 15:49:45.969 -> echo:Hardcoded Default Settings Loaded 15:49:51.809 -> echo:SD card released 15:49:51.849 -> X:0.00 Y:0.00 Z:0.00 E:0.00 Count X:0 Y:0 Z:0 15:49:51.873 -> ok P15 B3

Besides, this stepper can be controlled with the Arduino script below, which provides real-time position reports from a rotary encoder:

#include <Arduino.h>
#include <RotaryEncoder.h>

const int stepPin = 3;
const int dirPin = 6;
const int enablePin = 8;
const int stepsPerRevolution = 1000;
const int stepDelay = 500;

#define PIN_IN1 19
#define PIN_IN2 20

RotaryEncoder *encoder = nullptr;

void checkPosition()
{
  encoder->tick();
}

void setup()
{
  pinMode(stepPin, OUTPUT);
  pinMode(dirPin, OUTPUT);
  pinMode(enablePin, OUTPUT);

  digitalWrite(enablePin, LOW);
  Serial.begin(115200);
  while (!Serial)
    ;
  Serial.println("InterruptRotator example for the RotaryEncoder library.");

  encoder = new RotaryEncoder(PIN_IN1, PIN_IN2, RotaryEncoder::LatchMode::TWO03);

  attachInterrupt(digitalPinToInterrupt(PIN_IN1), checkPosition, CHANGE);
  attachInterrupt(digitalPinToInterrupt(PIN_IN2), checkPosition, CHANGE);
}
static int lastPos = 0;

void loop()
{

  digitalWrite(dirPin, HIGH);
  for (int i = 0; i < 4000; i++)
  {
    digitalWrite(stepPin, HIGH);
    delayMicroseconds(stepDelay);
    position();
    digitalWrite(stepPin, LOW);
    delayMicroseconds(stepDelay);
     position();
  }
  delay(1000);

  digitalWrite(dirPin, LOW);
  for (int i = 0; i < 2000; i++)
  {
    digitalWrite(stepPin, HIGH);
    delayMicroseconds(stepDelay);
     position();
    digitalWrite(stepPin, LOW);
    delayMicroseconds(stepDelay);
     position();
  }
  delay(1000);

  digitalWrite(enablePin, HIGH); // Disable the motor driver to stop the motor
   position();
  delay(1000); // Add a delay here to prevent the loop from re-enabling the motor immediately
}

void position()
{

  encoder->tick();
  int newPos = encoder->getPosition();

  if (lastPos != newPos)
  {
    Serial.print("pos:");
    Serial.print(newPos);
    Serial.print(" dir:");
    Serial.println((int)(encoder->getDirection()));
    lastPos = newPos;
  }

}

A little tweak of the Arduino-Hub sketch could control the stepper movement and report the position below. Is it possible to interface the Stage Controlwith this repurposed Arduino-Hub to control stages like the native z-stage control function in μManager? Thanks again.

   unsigned int version_ = 2;

   // pin on which to receive the trigger (2 and 3 can be used with interrupts, although this code does not use interrupts)
   int inPin_ = 2;
   // to read out the state of inPin_ faster, use 
   int inPinBit_ = 1 << inPin_;  // bit mask 
   const int stepPin = 3;
const int dirPin = 6;
const int enablePin = 8;
   // pin connected to DIN of TLV5618
   int dataPin = 13;
   // pin connected to SCLK of TLV5618
   int clockPin = 4;
   // pin connected to CS of TLV5618
   int latchPin = 5;

   const int SEQUENCELENGTH = 12;  // this should be good enough for everybody;)
   byte triggerPattern_[SEQUENCELENGTH] = {0,0,0,0,0,0,0,0,0,0,0,0};
   unsigned int triggerDelay_[SEQUENCELENGTH] = {0,0,0,0,0,0,0,0,0,0,0,0};
   int patternLength_ = 0;
   byte repeatPattern_ = 0;
   volatile long triggerNr_; // total # of triggers in this run (0-based)
   volatile long sequenceNr_; // # of trigger in sequence (0-based)
   int skipTriggers_ = 0;  // # of triggers to skip before starting to generate patterns
   byte currentPattern_ = 0;
   const unsigned long timeOut_ = 1000;
   bool blanking_ = false;
   bool blankOnHigh_ = false;
   bool triggerMode_ = false;
   boolean triggerState_ = false;

 void setup() {
    pinMode(stepPin, OUTPUT);
  pinMode(dirPin, OUTPUT);
  pinMode(enablePin, OUTPUT);

  digitalWrite(enablePin, LOW);
   // Higher speeds do not appear to be reliable
   Serial.begin(57600);

   pinMode(inPin_, INPUT);
   pinMode (dataPin, OUTPUT);
   pinMode (clockPin, OUTPUT);
   pinMode (latchPin, OUTPUT);

   pinMode(9, OUTPUT);
   pinMode(10, OUTPUT);
   pinMode(11, OUTPUT);

   // Set analogue pins as input:
   DDRC = DDRC & B11000000;
   // Turn on build-in pull-up resistors
   PORTC = PORTC | B00111111;

   digitalWrite(latchPin, HIGH);   
 }

 void loop() {
   digitalWrite(dirPin, HIGH);
   for (int i = 0; i < 1000; i++)
  {
digitalWrite(stepPin, HIGH);
    delayMicroseconds(50);
    digitalWrite(stepPin, LOW);
    delayMicroseconds(50);
  }
 digitalWrite(enablePin, HIGH);

   if (Serial.available() > 0) {
     int inByte = Serial.read();
     switch (inByte) {

       // Set digital output
       case 1 :
          if (waitForSerial(timeOut_)) {
            currentPattern_ = Serial.read();
            // Do not set bits 6 and 7 (not sure if this is needed..)
            currentPattern_ = currentPattern_ & B00111111;
            if (!blanking_)
              PORTB = currentPattern_;
            Serial.write( byte(1));
          }
          break;

       // Get digital output
       case 2:
          Serial.write( byte(2));
          Serial.write( PORTB);
          break;

       // Set Analogue output (TODO: save for 'Get Analogue output')
       case 3:
         if (waitForSerial(timeOut_)) {
           int channel = Serial.read();
           if (waitForSerial(timeOut_)) {
              byte msb = Serial.read();
              msb &= B00001111;
              if (waitForSerial(timeOut_)) {
                byte lsb = Serial.read();
                analogueOut(channel, msb, lsb);
                Serial.write( byte(3));
                Serial.write( channel);
                Serial.write(msb);
                Serial.write(lsb);
              }
           }
         }
         break;

       // Sets the specified digital pattern
       case 5:
          if (waitForSerial(timeOut_)) {
            int patternNumber = Serial.read();
            if ( (patternNumber >= 0) && (patternNumber < SEQUENCELENGTH) ) {
              if (waitForSerial(timeOut_)) {
                triggerPattern_[patternNumber] = Serial.read();
                triggerPattern_[patternNumber] = triggerPattern_[patternNumber] & B00111111;
                Serial.write( byte(5));
                Serial.write( patternNumber);
                Serial.write( triggerPattern_[patternNumber]);
                break;
              }
            }
          }
          Serial.write( "n:");//Serial.print("n:");
          break;

       // Sets the number of digital patterns that will be used
       case 6:
         if (waitForSerial(timeOut_)) {
           int pL = Serial.read();
           if ( (pL >= 0) && (pL <= 12) ) {
             patternLength_ = pL;
             Serial.write( byte(6));
             Serial.write( patternLength_);
           }
         }
         break;

       // Skip triggers
       case 7:
         if (waitForSerial(timeOut_)) {
           skipTriggers_ = Serial.read();
           Serial.write( byte(7));
           Serial.write( skipTriggers_);
         }
         break;

       //  starts trigger mode
       case 8: 
         if (patternLength_ > 0) {
           sequenceNr_ = 0;
           triggerNr_ = -skipTriggers_;
           triggerState_ = digitalRead(inPin_) == HIGH;
           PORTB = B00000000;
           Serial.write( byte(8));
           triggerMode_ = true;           
         }
         break;

         // return result from last triggermode
       case 9:
          triggerMode_ = false;
          PORTB = B00000000;
          Serial.write( byte(9));
          Serial.write( triggerNr_);
          break;

       // Sets time interval for timed trigger mode
       // Tricky part is that we are getting an unsigned int as two bytes
       case 10:
          if (waitForSerial(timeOut_)) {
            int patternNumber = Serial.read();
            if ( (patternNumber >= 0) && (patternNumber < SEQUENCELENGTH) ) {
              if (waitForSerial(timeOut_)) {
                unsigned int highByte = 0;
                unsigned int lowByte = 0;
                highByte = Serial.read();
                if (waitForSerial(timeOut_))
                  lowByte = Serial.read();
                highByte = highByte << 8;
                triggerDelay_[patternNumber] = highByte | lowByte;
                Serial.write( byte(10));
                Serial.write(patternNumber);
                break;
              }
            }
          }
          break;

       // Sets the number of times the patterns is repeated in timed trigger mode
       case 11:
         if (waitForSerial(timeOut_)) {
           repeatPattern_ = Serial.read();
           Serial.write( byte(11));
           Serial.write( repeatPattern_);
         }
         break;

       //  starts timed trigger mode
       case 12: 
         if (patternLength_ > 0) {
           PORTB = B00000000;
           Serial.write( byte(12));
           for (byte i = 0; i < repeatPattern_ && (Serial.available() == 0); i++) {
             for (int j = 0; j < patternLength_ && (Serial.available() == 0); j++) {
               PORTB = triggerPattern_[j];
               delay(triggerDelay_[j]);
             }
           }
           PORTB = B00000000;
         }
         break;

       // Blanks output based on TTL input
       case 20:
         blanking_ = true;
         Serial.write( byte(20));
         break;

       // Stops blanking mode
       case 21:
         blanking_ = false;
         Serial.write( byte(21));
         break;

       // Sets 'polarity' of input TTL for blanking mode
       case 22: 
         if (waitForSerial(timeOut_)) {
           int mode = Serial.read();
           if (mode==0)
             blankOnHigh_= true;
           else
             blankOnHigh_= false;
         }
         Serial.write( byte(22));
         break;

       // Gives identification of the device
       case 30:
         Serial.println("MM-Ard");
         break;

       // Returns version string
       case 31:
         Serial.println(version_);
         break;

       case 40:
         Serial.write( byte(40));
         Serial.write( PINC);
         break;

       case 41:
         if (waitForSerial(timeOut_)) {
           int pin = Serial.read();  
           if (pin >= 0 && pin <=5) {
              int val = analogRead(pin);
              Serial.write( byte(41));
              Serial.write( pin);
              Serial.write( highByte(val));
              Serial.write( lowByte(val));
           }
         }
         break;

       case 42:
         if (waitForSerial(timeOut_)) {
           int pin = Serial.read();
           if (waitForSerial(timeOut_)) {
             int state = Serial.read();
             Serial.write( byte(42));
             Serial.write( pin);
             if (state == 0) {
                digitalWrite(14+pin, LOW);
                Serial.write( byte(0));
             }
             if (state == 1) {
                digitalWrite(14+pin, HIGH);
                Serial.write( byte(1));
             }
           }
         }
         break;

       }
    }

    // In trigger mode, we will blank even if blanking is not on..
    if (triggerMode_) {
      boolean tmp = PIND & inPinBit_;
      if (tmp != triggerState_) {
        if (blankOnHigh_ && tmp ) {
          PORTB = 0;
        }
        else if (!blankOnHigh_ && !tmp ) {
          PORTB = 0;
        }
        else { 
          if (triggerNr_ >=0) {
            PORTB = triggerPattern_[sequenceNr_];
            sequenceNr_++;
            if (sequenceNr_ >= patternLength_)
              sequenceNr_ = 0;
          }
          triggerNr_++;
        }

        triggerState_ = tmp;       
      }  
    } else if (blanking_) {
      if (blankOnHigh_) {
        if (! (PIND & inPinBit_))
          PORTB = currentPattern_;
        else
          PORTB = 0;
      }  else {
        if (! (PIND & inPinBit_))
          PORTB = 0;
        else  
          PORTB = currentPattern_;
      }
    }
}

bool waitForSerial(unsigned long timeOut)
{
    unsigned long startTime = millis();
    while (Serial.available() == 0 && (millis() - startTime < timeOut) ) {}
    if (Serial.available() > 0)
       return true;
    return false;
 }

// Sets analogue output in the TLV5618
// channel is either 0 ('A') or 1 ('B')
// value should be between 0 and 4095 (12 bit max)
// pins should be connected as described above
void analogueOut(int channel, byte msb, byte lsb) 
{
  digitalWrite(latchPin, LOW);
  msb &= B00001111;
  if (channel == 0)
     msb |= B10000000;
  // Note that in all other cases, the data will be written to DAC B and BUFFER
  shiftOut(dataPin, clockPin, MSBFIRST, msb);
  shiftOut(dataPin, clockPin, MSBFIRST, lsb);
  // The TLV5618 needs one more toggle of the clockPin:
  digitalWrite(clockPin, HIGH);
  digitalWrite(clockPin, LOW);
  digitalWrite(latchPin, HIGH);
}
pchiang5 commented 4 months ago

I managed to achieve this by modifying the MM_vLStep_Z.ino as follows. Now, I can add this RAMPS1.4/Arduino Mega setup to Micro-Manager through the Hardware Configuration Wizard and control the z-axis movement via Stage Control. However, several issues remain:

  1. Only coarse adjustment was responsive; the fine adjustment was unresponsive most of the time, accompanied by the following message:

2024-06-24T18:01:43.371442 tid8272 [dbg,dev:COM6] GetAnswer <- 112.8\r 2024-06-24T18:01:43.371458 tid8272 [IFO,dev:ZAxis] ZStage::GetPositionUm() 112.8 2024-06-24T18:01:43.409350 tid8272 [dbg,Core] Will start relative move of ZAxis by offset 1.10000 um 2024-06-24T18:01:43.409367 tid8272 [dbg,dev:COM6] SetCommand -> !dim z 1\r 2024-06-24T18:01:43.409371 tid18036 [dbg,App] [UI] JButton clicked in StageControlFrame. 2024-06-24T18:01:43.409375 tid8272 [dbg,dev:COM6] SetCommand -> !mor z 1.1\r 2024-06-24T18:01:43.409380 tid8272 [dbg,dev:COM6] SetCommand -> ?err\r 2024-06-24T18:01:43.463036 tid8272 [dbg,dev:COM6] GetAnswer <- 0\r 2024-06-24T18:01:43.463055 tid8272 [dbg,Core] Waiting for device ZAxis... 2024-06-24T18:01:43.463063 tid8272 [dbg,dev:COM6] SetCommand -> ?statusaxis\r 2024-06-24T18:01:43.510520 tid8272 [dbg,dev:COM6] GetAnswer <- @\r 2024-06-24T18:01:43.510531 tid8272 [dbg,Core] Finished waiting for device ZAxis 2024-06-24T18:01:43.510546 tid8272 [dbg,dev:COM6] SetCommand -> !dim z 1\r 2024-06-24T18:01:43.510551 tid8272 [dbg,dev:COM6] SetCommand -> ?pos z\r 2024-06-24T18:01:43.555610 tid8272 [dbg,dev:COM6] GetAnswer <- 112.8\r 2024-06-24T18:01:43.555628 tid8272 [IFO,dev:ZAxis] ZStage::GetPositionUm() 112.8 2024-06-24T18:01:43.577342 tid8272 [dbg,Core] Will start relative move of ZAxis by offset 1.10000 um 2024-06-24T18:01:43.577355 tid8272 [dbg,dev:COM6] SetCommand -> !dim z 1\r 2024-06-24T18:01:43.577361 tid18036 [dbg,App] [UI] JButton clicked in StageControlFrame. 2024-06-24T18:01:43.577363 tid8272 [dbg,dev:COM6] SetCommand -> !mor z 1.1\r 2024-06-24T18:01:43.577367 tid8272 [dbg,dev:COM6] SetCommand -> ?err\r 2024-06-24T18:01:43.632655 tid8272 [dbg,dev:COM6] GetAnswer <- 0\r 2024-06-24T18:01:43.632673 tid8272 [dbg,Core] Waiting for device ZAxis... 2024-06-24T18:01:43.632683 tid8272 [dbg,dev:COM6] SetCommand -> ?statusaxis\r 2024-06-24T18:01:43.678655 tid8272 [dbg,dev:COM6] GetAnswer <- @\r 2024-06-24T18:01:43.678664 tid8272 [dbg,Core] Finished waiting for device ZAxis 2024-06-24T18:01:43.678687 tid8272 [dbg,dev:COM6] SetCommand -> !dim z 1\r 2024-06-24T18:01:43.678692 tid8272 [dbg,dev:COM6] SetCommand -> ?pos z\r 2024-06-24T18:01:43.723782 tid8272 [dbg,dev:COM6] GetAnswer <- 112.8\r 2024-06-24T18:01:43.723798 tid8272 [IFO,dev:ZAxis] ZStage::GetPositionUm() 112.8 2024-06-24T18:01:43.753332 tid8272 [dbg,Core] Will start relative move of ZAxis by offset 1.10000 um 2024-06-24T18:01:43.753348 tid8272 [dbg,dev:COM6] SetCommand -> !dim z 1\r 2024-06-24T18:01:43.753351 tid18036 [dbg,App] [UI] JButton clicked in StageControlFrame. 2024-06-24T18:01:43.753357 tid8272 [dbg,dev:COM6] SetCommand -> !mor z 1.1\r 2024-06-24T18:01:43.753361 tid8272 [dbg,dev:COM6] SetCommand -> ?err\r 2024-06-24T18:01:43.816045 tid8272 [dbg,dev:COM6] GetAnswer <- 0\r 2024-06-24T18:01:43.816063 tid8272 [dbg,Core] Waiting for device ZAxis... 2024-06-24T18:01:43.816074 tid8272 [dbg,dev:COM6] SetCommand -> ?statusaxis\r 2024-06-24T18:01:43.862167 tid8272 [dbg,dev:COM6] GetAnswer <- @\r 2024-06-24T18:01:43.862176 tid8272 [dbg,Core] Finished waiting for device ZAxis 2024-06-24T18:01:43.862191 tid8272 [dbg,dev:COM6] SetCommand -> !dim z 1\r 2024-06-24T18:01:43.862197 tid8272 [dbg,dev:COM6] SetCommand -> ?pos z\r 2024-06-24T18:01:43.908262 tid8272 [dbg,dev:COM6] GetAnswer <- 112.8\r 2024-06-24T18:01:43.908278 tid8272 [IFO,dev:ZAxis] ZStage::GetPositionUm() 112.8

  1. The following error occasionally appears, especially when I do Go to for long-distance movement.

2024-06-24T18:05:35.921479 tid18036 [dbg,App] [UI] JButton "Mark" clicked in MMPositionListDlg. 2024-06-24T18:05:37.425562 tid18036 [dbg,App] [UI] clicked in MMPositionListDlg. 2024-06-24T18:05:38.097630 tid18036 [dbg,App] [UI] clicked in MMPositionListDlg. 2024-06-24T18:05:39.641661 tid18036 [dbg,Core] Will start absolute move of ZAxis to position 427.29999 um 2024-06-24T18:05:39.641681 tid18036 [IFO,dev:ZAxis] ZStage::SetPositionUm() 427.3 2024-06-24T18:05:39.641697 tid18036 [dbg,dev:COM6] SetCommand -> !dim z 1\r 2024-06-24T18:05:39.641706 tid18036 [dbg,dev:COM6] SetCommand -> !moa z 427.3\r 2024-06-24T18:05:39.641712 tid18036 [dbg,dev:COM6] SetCommand -> ?err\r 2024-06-24T18:05:40.142462 tid18036 [IFO,dev:COM6] TERM_TIMEOUT error occured! 2024-06-24T18:05:40.142490 tid18036 [ERR,Core] Error occurred in device COM6: Error in device "COM6": (Error message unavailable) (107) 2024-06-24T18:05:40.142542 tid18036 [ERR,Core] Error occurred in device ZAxis: Error in device "ZAxis": Serial command failed. Is the device connected to the serial port? (14)

  1. Once this error occurred, the same errors persisted even when I clicked on the coarse adjustment. Was it due to the digitalPinToInterrupt in my firmware?

2024-06-24T18:10:25.763577 tid18036 [dbg,dev:COM6] SetCommand -> !dim z 1\r 2024-06-24T18:10:25.763592 tid18036 [dbg,dev:COM6] SetCommand -> ?pos z\r 2024-06-24T18:10:25.763599 tid18036 [dbg,dev:COM6] GetAnswer <- 0\r 2024-06-24T18:10:25.763624 tid18036 [IFO,dev:ZAxis] ZStage::GetPositionUm() 0 2024-06-24T18:10:25.764259 tid18036 [dbg,dev:COM6] SetCommand -> !dim z 1\r 2024-06-24T18:10:25.764266 tid18036 [dbg,dev:COM6] SetCommand -> ?pos z\r 2024-06-24T18:10:25.842758 tid18036 [dbg,dev:COM6] GetAnswer <- -347.5\r 2024-06-24T18:10:25.842794 tid18036 [IFO,dev:ZAxis] ZStage::GetPositionUm() -347.5 2024-06-24T18:10:25.843745 tid18036 [dbg,dev:COM6] SetCommand -> !dim z 1\r 2024-06-24T18:10:25.843753 tid18036 [dbg,dev:COM6] SetCommand -> ?pos z\r 2024-06-24T18:10:25.843758 tid18036 [dbg,dev:COM6] GetAnswer <- -347.5\r 2024-06-24T18:10:25.843769 tid18036 [IFO,dev:ZAxis] ZStage::GetPositionUm() -347.5 2024-06-24T18:10:25.844456 tid18036 [dbg,dev:COM6] SetCommand -> !dim z 1\r 2024-06-24T18:10:25.844463 tid18036 [dbg,dev:COM6] SetCommand -> ?pos z\r 2024-06-24T18:10:25.920864 tid18036 [dbg,dev:COM6] GetAnswer <- -347.5\r 2024-06-24T18:10:25.920895 tid18036 [IFO,dev:ZAxis] ZStage::GetPositionUm() -347.5 2024-06-24T18:10:25.921542 tid18036 [dbg,dev:COM6] SetCommand -> !dim z 1\r 2024-06-24T18:10:25.921549 tid18036 [dbg,dev:COM6] SetCommand -> ?pos z\r 2024-06-24T18:10:25.921553 tid18036 [dbg,dev:COM6] GetAnswer <- -347.5\r 2024-06-24T18:10:25.921564 tid18036 [IFO,dev:ZAxis] ZStage::GetPositionUm() -347.5 2024-06-24T18:10:25.922094 tid18036 [dbg,dev:COM6] SetCommand -> !dim z 1\r 2024-06-24T18:10:25.922100 tid18036 [dbg,dev:COM6] SetCommand -> ?pos z\r 2024-06-24T18:10:25.998989 tid18036 [dbg,dev:COM6] GetAnswer <- -347.5\r 2024-06-24T18:10:25.999018 tid18036 [IFO,dev:ZAxis] ZStage::GetPositionUm() -347.5 2024-06-24T18:10:26.010733 tid18036 [dbg,App] [UI] JButton clicked in MainFrame. 2024-06-24T18:10:27.145892 tid18036 [dbg,App] [UI] JButton clicked in StageControlFrame. 2024-06-24T18:10:27.145900 tid8272 [dbg,Core] Will start relative move of ZAxis by offset -11.10000 um 2024-06-24T18:10:27.145921 tid8272 [dbg,dev:COM6] SetCommand -> !dim z 1\r 2024-06-24T18:10:27.145932 tid8272 [dbg,dev:COM6] SetCommand -> !mor z -11.1\r 2024-06-24T18:10:27.145937 tid8272 [dbg,dev:COM6] SetCommand -> ?err\r 2024-06-24T18:10:27.145942 tid8272 [dbg,dev:COM6] GetAnswer <- -347.5\r 2024-06-24T18:10:27.145965 tid8272 [ERR,Core] Error occurred in device ZAxis: Error in device "ZAxis": Unexpected response from serial port. Is the device connected to the correct serial port? (16)

Thanks!


#include <Arduino.h>
#include <RotaryEncoder.h>

#define Z_STEP_PIN         46
#define Z_DIR_PIN          48
#define Z_ENABLE_PIN       62
#define Z_MIN_PIN          18
#define Z_MAX_PIN          19
#define PIN_IN1         20
#define PIN_IN2       21

#define LED_PIN            13

#define FAN_PIN            9
RotaryEncoder *encoder = nullptr;

void checkPosition()
{
  encoder->tick();
}

String cmd = "" ;

//23910 
void setup() {
    Serial.begin(9600);
  pinMode(LED_PIN  , OUTPUT);

  pinMode(Z_STEP_PIN  , OUTPUT);
  pinMode(Z_DIR_PIN    , OUTPUT);
  pinMode(Z_ENABLE_PIN    , OUTPUT);

   digitalWrite(Z_ENABLE_PIN    , HIGH);

pinMode(PIN_IN1, INPUT);
  pinMode(PIN_IN2, INPUT);
 Serial.println("InterruptRotator example for the RotaryEncoder library.");

  encoder = new RotaryEncoder(PIN_IN1, PIN_IN2, RotaryEncoder::LatchMode::TWO03);

  attachInterrupt(digitalPinToInterrupt(PIN_IN1), checkPosition, CHANGE);
  attachInterrupt(digitalPinToInterrupt(PIN_IN2), checkPosition, CHANGE);

}

char c = '*';

void loop()
{
  if (Serial.available()) {
    cmd = Serial.readStringUntil('\r');
    processCommand(cmd);
    cmd = "";
  }
}

float z = 0.0000;
float tz = 0.0000;

void processCommand(String s) {
  if (s.startsWith("?ver")) {
    reply ("Vers:LS");
  } else if (s.startsWith("!autostatus 0")) {
    delay(5);
  } else if (s.startsWith("?det")) {
    reply ("60");
  } else if (s.startsWith("?pitch z")) {
    reply ("0.6096");
  } else if (s.startsWith("?vel z")) {
    reply ("100.0");
  } else if (s.startsWith("?accel z")) {
    reply ("1.0");
  } else if (s.startsWith("!dim z 1")) {
    delay(5);
  } else if (s.startsWith("!dim z 2")) {
    delay(5);
  } else if (s.startsWith("?statusaxis")) {
    reply ("@");
  } else if (s.startsWith("!vel z")) {
    delay(5);
  } else if (s.startsWith("!accel z")) {
    delay(5);
  } else if (s.startsWith("?pos z")) {
    String zs = String(z, 1);
    reply (zs);

  } else if (s.startsWith("?lim z")) {
    reply ("0.0 100.0");
  } else if (s.startsWith("!pos z")) {
    delay(5);
  } else if (s.startsWith("?status")) {
    reply ("OK...");
  } else if (s.startsWith("!dim z 0")) {
    delay(5);
  } else if (s.startsWith("!speed z")) {
    delay(5);
  } else if (s.startsWith("!mor z")) {
    // position();
    String delta = s.substring(s.indexOf("z") + 1);
    tz = z + delta.toFloat();
    moveToTargetPosition();
  } else if (s.startsWith("!moa z")) {
    // position();
    String apos = s.substring(s.indexOf("z") + 1);
    tz = apos.toFloat();
        moveToTargetPosition();
  } else if (s.startsWith("?err")) {
    reply ("0");
  }
}

void reply(String s) {
  Serial.print(s);
  Serial.print("\r");
}
void moveToTargetPosition() {
  float tolerance = 2;  
  // Check if the target position is different from the current position
 if (abs(tz - z) > tolerance)  {
    // Set the direction based on whether the target position is higher or lower than the current position
    int direction = (tz > z) ? LOW : HIGH;
    digitalWrite(Z_DIR_PIN, direction); // Set the direction
    digitalWrite(Z_ENABLE_PIN, LOW); // Enable the motor driver
    // position();

    // Continue sending step pulses until the target position is reached
    while (abs(tz - z) > tolerance) {
      digitalWrite(Z_STEP_PIN, HIGH); // Step signal on
      delayMicroseconds(500); // Adjust delay as needed for step pulse duration
      digitalWrite(Z_STEP_PIN, LOW); // Step signal off
      delayMicroseconds(500); // Adjust delay as needed for step pulse duration
      // position();

      // Update the current position from the encoder
      encoder->tick();
      z = encoder->getPosition()*0.6096;
    }

    // Once the target position is reached, disable the motor driver
    digitalWrite(Z_ENABLE_PIN, HIGH);
  } else {
    // If already at the target position, just disable the motor driver
    digitalWrite(Z_ENABLE_PIN, HIGH);
  }
}
  // Add delay or other code as needed

static int lastPos = 0;

void position()
{

  encoder->tick();
  int newPos = encoder->getPosition();

  if (lastPos != newPos)
  {
    Serial.print("pos:");
    Serial.print(newPos);
    Serial.print(" dir:");
    Serial.println((int)(encoder->getDirection()));
    lastPos = newPos;
  }
}
marktsuchida commented 4 months ago

Thanks for the update. I'm not familiar enough with the RAMPS device adapter to have much insight, but you might try increasing the timeout on COM6 (it's settable in the Hardware Configuration Wizard, or the .cfg file). You probably have it at the default 500 ms; try setting it to be longer than the longest time it takes for the stage travel.