Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 4 of 4

Thread: Teensy LC based lathe DRO

  1. #1
    Junior Member
    Join Date
    Apr 2019
    Posts
    8

    Teensy LC based lathe DRO

    A few years ago, I had a small machining project to do on my old manual lathe, and it seemed to me that it would be somewhat faster to do the work accurately if I added DROs (Digital Read Outs) to the lathe carriage and cross slide axes.

    iGaging sells a line of DRO kits that while light on features, are reasonably accurate and very affordable. I bought a set and installed them on my lathe. They worked well for my project and I was satisfied with them.

    Click image for larger version. 

Name:	Old_DROs.jpg 
Views:	15 
Size:	117.2 KB 
ID:	23224

    Since then however, Iíve begun really feeling the limitations of these budget instruments.

    First, they run from coin cell batteries, and a couple of times now Iíve gone out to use the lathe to discover the batteries were dead and needed to be replaced.

    Second, theyíre a bit laggy. There is about eight tenths of a second between a movement on the scales and the display showing that new position. Itís a little maddening for precision work since to hit a particular number, you have to really slowly sneak up to the number.

    Third, the scales can only be set to read linear position. That makes a lot of sense for general purposes, but when mounted on the cross slide of a lathe that means itís displaying radius. Again, that makes perfect sense, but in practice Iím often trying to hit a specific diameter, and I end up having to take measurements and divide by two. Many higher end lathe DROs let you display the cross slide as radius or diameter. The whole point of DROs is to let the machinist work more quickly and more accurately, making fewer mistakes because the computer is doing the math for you.

    Fourth, the preset function (where you set the position counter to a specific value) is very slow and cumbersome since there is no keypad. It reminds me of sending a text message on an old flip phone. In practice it was so awkward and slow that I never used it.

    Poking around on Youtube, I found a video where someone had gotten an Arduino to read the output of a digital caliper, and I wondered if anyone had tried that with an iGaging DRO scale. A little searching found the really nice ďTouch DROĒ project, where Yuiri B. has documented how to talk to a few different types of iGaging scales.

    My first thought was I have to try this myself. I got out a breadboard and a Teensy LC and after a little experimentation was indeed able to read the scales.

    Click image for larger version. 

Name:	breadboard_testing.jpg 
Views:	9 
Size:	115.1 KB 
ID:	23225

    Click image for larger version. 

Name:	DRO_test.jpg 
Views:	10 
Size:	114.4 KB 
ID:	23226

    Digging through my electronics parts box, I soldered up a board with two 7 segment 8 digit led displays, a pair of 4 character 14 segment LED displays for input feedback, a 4x6 button keypad, a pezio buzzer for audio feedback, and a shift register with 8 LEDs hooked up to it.

    A little bit of software later, and I have a custom DIY DRO for my lathe.

    Click image for larger version. 

Name:	close view.jpg 
Views:	8 
Size:	90.4 KB 
ID:	23227

    The keypad lets me toggle settings, like switching the display between imperial and metric, switching between radius and diameter display, absolute or incremental position display, and to allow easy numeric entry for presets.

    There is still some lag. It takes 93 milliseconds to read both scales, but I suspect there is some internal smoothing going on inside the scale sensor and the best I can do is about a third second of lag. Thatís two and a half times better than what I had before, so Iím happy about that.

    This ended up being two weekends of work, but I had a lot of fun doing it and now Iíve got a much more capable and responsive DRO.

    John Knoll

  2. #2
    Member
    Join Date
    Sep 2015
    Location
    Mechelen, Belgium
    Posts
    30
    hi John,

    nicely done.
    any change sketch/diagram ia available to the general public?

    Regards, Otto.

  3. #3
    Junior Member
    Join Date
    Apr 2019
    Posts
    8
    If you'd like to reproduce this yourself, here's how to read the iGaging Absolute DRO Plus scales.

    These scales use the Mitutoyo 52 bit data protocol transmitted over a microUSB cable. The scale DOES NOT use USB protocol, they're just using the cables and connectors because they're cheap and readily available.

    This is how the microUSB connector is wired:
    VCC -> 3.3v
    D- -> Clock
    D+ -> Data
    ID -> REQ
    GND -> GND

    This code demonstrates reading from two of these scales simultaneously. It should be pretty obvious how to extend this to additional axes.

    Code:
    //**************************************************************//
    //  Name    : iGaging_Scale_reader                                
    //  Author  : John Knoll 
    //  Date    : 28 Dec 2020    
    //  Modified: 10 Jan 2021                                  
    //  Notes   : Code to read 52bit iGaging Absolute DRO sales
    //****************************************************************//
    
    /*
    iGaging 52 bit scales use a micro USB cable to connect the read head to the display.
    The microUSB connector DOES NOT use USB protocol. It's using the connector since they're cheap and convenient
    
    The way this is wired:
    VCC -> 3.3v
    D-  -> Clock
    D+  -> Data
    ID  -> REQ
    GND -> GND
    
    */
    
    //pin definitions
    int const xclockPin = 2; // X Clock
    int const xDataPin = 3; // X Data
    int const xREQPin = 4; // X REQ - Pull this down to request data from the scale
    
    int const yclockPin = 5; // Y Clock
    int const yDataPin = 6; // Y Data
    int const yREQPin = 7; // Y REQ - Pull this down to request data from the scale
    
    //variables that will store the readout 
    float gX_Position,gY_Position;
    unsigned long lastupdate,nextaverage;
    long readcount,accumulation;
    float averageReadDuration;
    
    volatile int xBitCounter,yBitCounter;
    volatile char xBit[52],yBit[52];
    
    /*************************************************************************************/
    
    void setup() 
      {
      Serial.begin(115200);
      gX_Position = gY_Position = gZ_Position = 0;
      readcount = 0;
      averageReadDuration = 0;
      accumulation = 0;
    
      pinMode(xREQPin, OUTPUT);
      pinMode(xclockPin, INPUT_PULLUP);
      pinMode(xDataPin, INPUT_PULLUP);
    
      pinMode(yREQPin, OUTPUT);
      pinMode(yclockPin, INPUT_PULLUP);
      pinMode(yDataPin, INPUT_PULLUP);
      
      digitalWriteFast(xREQPin, HIGH); // Set to high - Pull this low to request data
      digitalWriteFast(yREQPin, HIGH); 
    
      attachInterrupt(digitalPinToInterrupt(xclockPin), ReadXbit, FALLING);
      attachInterrupt(digitalPinToInterrupt(yclockPin), ReadYbit, FALLING);
      xBitCounter = yBitCounter = 0;
    
      lastupdate = nextaverage = millis();
      nextaverage += 1000;
      }
    
    /*************************************************************************************/
    
    void loop() 
      {
      unsigned long elapsed,currenttime;
    
      if (!ReadScales()) Serial.println("Communication timeout");
     
      currenttime = millis();
      elapsed = currenttime - lastupdate;
      lastupdate = currenttime;
      readcount++;
      accumulation += elapsed;
      if (currenttime > nextaverage)
        {
        averageReadDuration = ((float)accumulation) / ((float)readcount);
        Serial.print("Average duration of read: ");
        Serial.println(averageReadDuration);
        accumulation = readcount = 0;
        nextaverage = currenttime + 1000;
        Serial.print("X Position: ");
        Serial.print (gX_Position);
        Serial.print("  Y Position: ");
        Serial.println (gY_Position);
        }
      }
    
    /*************************************************************************************/
    // Scale reading routine.
    // The 52 bit communication protocol used by the iGaging displays uses a pretty clow clock.
    // To read all 52 bits, the scale requires 93 milliseconds.  That's about 1/500th second per bit.
    // on a 48Mhz Teensy LC, 1/500 second is an eternity.  While I can't speed up the clock of the scale,
    // I can speed up reading all scales simulatneously
    //
    // The 52 bit protocol requires 3 data lines: 1 output and 2 inputs
    // REQ pin - output.  Pull this low to start the process.  Pulling it down asks the scale to start sending data.
    // Clock pin - input.  Wait for this to toggle.  It starts low, goes high for a moment, and when it goes low again, you can read the data pin.
    // Data pin - input.  When the clock goes low, read the incoling bit on this pin.
    //
    // Data format is 13 4 bit "characters".  Each 4 bit character is an integer or a flag.
    // character 1 - 3 ignore.  These are always 1s
    // character 4 - Sign.  If this value is 8, what follows is a negative number.  Otherwise it's positive.
    // character 5 - Highest place.  On these scales this is the thousands place.
    // character 6 - hundreds place
    // character 7 - tens place
    // character 8 - ones place
    // character 9 - tenths place
    // character 10 - hundredths place
    // character 11 - the decimal point location.  For my purposes, this can be ignored because the scales I'm talking to always return '2'
    // character 12 - inch/metric.  I'm ignoring this since the scales I'm talking to are always metric
    // 
    // For these scales, this number is in millimeters.
    //
    // There are two interrupt service routines, ReadXbit() and ReadYbit() that are set to be called on the falling edge of the clock lines.
    // The routines read the data and stash it away in xBit[] and yBit[]
    //
    // This routine starts by pulling the REQ lines low.  That tells the scales we want to recieve data.
    // We then wait while the ISRs read the data.  Reads are done when the bit counters indicate that we've read all 52 bits.
    // If the scales don't send the data within 1 second, we time out and return false.
    // Set the REQ lines high to tell the scales we're done.
    // Assemble the characters into a floating point number.
    
    
    boolean ReadScales(void)
      {
      unsigned long Timeout;
      int xdigit,xbit,whichbit,Xnum,Ynum;
      boolean isXneg,isYneg;
      boolean donereading;
      
      xBitCounter = yBitCounter = 0;
      Xnum = Ynum = 0;
      donereading = isXneg = isYneg = false;
      Timeout = millis() + 1000;
      
      // Request data from scales
      digitalWriteFast(xREQPin,LOW); 
      digitalWriteFast(yREQPin,LOW);
      
      // loop until the reads are done
      while (!donereading)
        {
        if ((xBitCounter == 52) && (yBitCounter == 52)) donereading = true;  // Check to see if both reads are done
        if (millis() > Timeout) // we've waited a second with no reply.  Time out.
          {
          gX_Position = 0; // set the output variables to zero.
          gY_Position = 0;
          digitalWriteFast(xREQPin, HIGH); // take the REQ pins down
          digitalWriteFast(yREQPin, HIGH);
          return false; // signal failure of read
          }
        }
      
      // Tell the scales we're done
      digitalWriteFast(xREQPin, HIGH);
      digitalWriteFast(yREQPin, HIGH);
      
      // assemble the digits
      gX_Position = 0;
      gY_Position = 0;
      for (whichbit = 0; whichbit < 52; whichbit++)
        {
        xdigit = whichbit>>2; // what digit are we building?
        xbit = whichbit % 4; // which bit are we on?
          
        if (xBit[whichbit]) bitWrite(Xnum, xbit, 1);
        if (yBit[whichbit]) bitWrite(Ynum, xbit, 1);
        if (xbit == 3) // We've finished with this digit. Process it.
          {
          switch (xdigit)
            {
            case 4: // this is the sign.  If either number is negative, remember that.  Negate the result after we've finished building it.
              if (Xnum == 8) isXneg = true;
              if (Ynum == 8) isYneg = true;
              break;
            case 5:
              gX_Position += Xnum * 1000.0;
              gY_Position += Ynum * 1000.0;
              break;
            case 6:
              gX_Position += Xnum * 100.0;
              gY_Position += Ynum * 100.0;
              break;
            case 7:
              gX_Position += Xnum * 10.0;
              gY_Position += Ynum * 10.0;
              break;
            case 8:
              gX_Position += Xnum;
              gY_Position += Ynum;
              break;
            case 9:
              gX_Position += Xnum / 10.0;
              gY_Position += Ynum / 10.0;
              break;
            case 10:
              gX_Position += Xnum / 100.0;
              gY_Position += Ynum / 100.0;
              break;
            default:
              break;
            }
          Xnum = Ynum = 0; // zero out the accumulators
          }
        }
      if (isXneg) gX_Position = -gX_Position;
      if (isYneg) gY_Position = -gY_Position;
      return true;
      }
    
    /*************************************************************************************/
    
    void ReadXbit() 
      {
        if (xBitCounter < 52 ) xBit[xBitCounter++] = digitalReadFast(xDataPin);
      }
    
    /*************************************************************************************/
    
    void ReadYbit() 
      {
        if (yBitCounter < 52 ) yBit[yBitCounter++] = digitalReadFast(yDataPin);
      }
    
    /*************************************************************************************/

  4. #4
    Member
    Join Date
    Sep 2015
    Location
    Mechelen, Belgium
    Posts
    30
    hi John,

    thank you, much apreciated!

    Regards, Otto.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •