Reading Digital caliper with Arduino
(03-30-2015, 06:35 PM)chucketn Wrote: If you don't mind, go ahead and send me one. I've been under the weather the last couple of days, and have a couple Dr's appts to find out what's up.

Chuck

It'll take me a couple of days to get it sent out.

Ed
Reply
Thanks given by:
Updated the code to add displaying the actual digital caliper display.

Ed

Code:
// Do not remove the include below
#include <util/atomic.h>

#define INTERRUPT_NUMBER 0
#define CLOCK_PIN 2                              // The pin associated with the clock pulse
#define CLOCK_TRANSITION FALLING                 // What will happen on the clock pulse that will want us to read the data pin
#define CLOCK_TERMINATION INPUT_PULLUP           // What kind of termination do we want on the clock pin

#define DATA_PIN 3                               // The pin associated with the data
#define DATA_TERMINATION INPUT_PULLUP            // What kind of termination do we want on the data pin
#define DATA_STATE_INDICATING_A_ONE HIGH         // Does a HIGH or LOW on the data line indicate a value of 1

#define CLOCK_NOISE_FILTER 10                    // How many microseconds long must the clock be stable for it to be consider 'good'
#define MINIMUM_BURST_START_PULSE_WIDTH 80000    // At a minimum, how many microseconds long is the pulse at the beginning of a burst
#define MAXIMUM_TIME_FOR_A_SINGLE_BURST 10000    // The maximum amount of time that a burst should take before considered invalid

#define DATA_BIT_ZERO_VALUE 0UL                  // For readability ...
#define DATA_BIT_ONE_VALUE 1UL                   // For readability ...

#define DATA_BITS_IN_A_VALID_BURST 24            // The number of data bits that are expected in a complete burst of data
volatile boolean initialized = false;            // Lets the interrupt get set up right after application start up
volatile boolean processingBurst = false;        // Does the interrupt think it is processing a burst
volatile byte dataBitsReceived = 0;              // Data bit counter used by the interrupt routine
volatile unsigned long lastGoodClockTime = 0;    // The last time the interrupt saw what it thought was a valid clock pulse
volatile unsigned long burstStartTime = 0;       // The time when the interrupt thought the burst started
volatile unsigned long inProcessBurstData = 0;   // The burst of data as it is being captured by the interrupt routine

volatile unsigned long capturedBurstData = 0;    // A complete and validated burst of data
volatile byte capturedBurstCounter = 0;          // A rolling counter of the number of complete bursts we have seen. It must be a byte so increments will be atomic.

// This looks like a lot of code for an interrupt but the if statements should ensure that
// only a small portion of it actually runs during any given interrupt

void clockTransitioned()
{
    unsigned long timeAtInterupt = micros();
    unsigned long intervalSinceLastInterrupt = timeAtInterupt - lastGoodClockTime;

     // If we do not have our ducks in a row so we will get organized

     if (!initialized)
         {
             lastGoodClockTime = timeAtInterupt;
             processingBurst = false;
             initialized = true;
         }

         // Is the interrupt because of noise?
    else if (intervalSinceLastInterrupt < CLOCK_NOISE_FILTER)
        {
             /*
            Note that we are going to do absolutely nothing in this case, not even update the last valid clock time.
            We are really waiting for a transition that is at least CLOCK_NOISE_FILTER later than the
            last transition that we thought was good.
            */
        }

         // Is the interrupt because of a 'beginning of burst' pulse?
     else if (intervalSinceLastInterrupt > MINIMUM_BURST_START_PULSE_WIDTH)
         {
            if (!processingBurst)
                 {
                     lastGoodClockTime = timeAtInterupt;
                     burstStartTime = timeAtInterupt;
                     (digitalRead(DATA_PIN) == DATA_STATE_INDICATING_A_ONE) ? (inProcessBurstData = DATA_BIT_ONE_VALUE) : (inProcessBurstData = DATA_BIT_ZERO_VALUE);
                     dataBitsReceived = 1;
                     processingBurst = true;
                 }

                // OK, we were in the process of receiving a burst but just got another 'beginning of burst' pulse.
                // We'll need to do something here but I will have to figure out exactly what after another cup of coffee.
            else
                {
        
                }
        }

         // OK, we are here because we are initialized and we do not think it is noise or a 'beginning of burst' pulse.
         // Let see if we believe it is a valid data bit.
     else
        {
                // If we are not in the process of reading a burst then we have a problem and need more coffee
            if (!processingBurst)
                {

                }

                 // If it has taken longer than we thought is should to show up then we have a problem ... see the coffe issue mentined earlier
            else if ((timeAtInterupt - burstStartTime) > MAXIMUM_TIME_FOR_A_SINGLE_BURST)
                {

                }

                  // We seem to be at the right place at the right time so grab what ever is on the data line.
            else
                {
                    lastGoodClockTime = timeAtInterupt;                  
                    inProcessBurstData = inProcessBurstData << 1;                      // Make room for the next data bit and then set it                  
    
                    if (digitalRead(DATA_PIN) == 1)     // We only need to do something if the data line indicates a ONE as the shift has already put in the ZERO value
                        {
                            bitSet(inProcessBurstData, 0);                            
                        }
                  
                    else
                        bitClear(inProcessBurstData, 0);
                        
                        dataBitsReceived += 1;
                
                    if (dataBitsReceived == DATA_BITS_IN_A_VALID_BURST)     // Are we done with this burst?
                        {
                            capturedBurstData = inProcessBurstData;
                            capturedBurstCounter = 24;
                            processingBurst = false;                      
                        }
                }
        }
}

// Variables used by the application loop
unsigned long burstData = 0;
unsigned long tempData = 0;
byte bitCount = 0;
float data[] = {.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096};
float caliperData = 0;

//The setup function is called once at startup of the sketch
void setup() {
// Pin Set Up
pinMode(DATA_PIN, INPUT_PULLUP);
pinMode(CLOCK_PIN, INPUT_PULLUP);

attachInterrupt(INTERRUPT_NUMBER, clockTransitioned, CLOCK_TRANSITION);

Serial.begin(115200);
Serial.println("Ready: ");}

// The loop function is called in an endless loop
void loop()

  {
      if (capturedBurstCounter == 24)
           {                                                 // If there is something new available then get it and print it
                            
               ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
                 {                                          // Needs to be marked as atomic so the interrupt does not mess with it while we get a local copy of it
                       burstData = ~capturedBurstData;
                       bitCount = capturedBurstCounter;
                       capturedBurstCounter = 0;
                 }
        
                burstData &= 0x00ffffff;
                                int y = 0;
                                caliperData = 0;
                                for(int x = 23; x >= 0; x--)
                                  {
                                      if (bitRead(burstData, x))
                                         {
                                             bitSet(tempData, y);
                                             caliperData += data[y];
                                         }
                                      else
                                        bitClear(tempData, y);
                                        
                                      ++y;
                                  }
                
                Serial.print(bitCount);
                Serial.print(", ");
                Serial.print(tempData, HEX);
                Serial.print(", ");
                Serial.print(tempData, BIN);
                                Serial.print(", ");
                                Serial.println((caliperData / 1000), 4);
            }
    }
Reply
Thanks given by:
Got my scope going (finally) and decided to take a look at my caliper. Mine's the older one and nothing was making sense at first but eventually I got it figured out.

This one is POS ground, and the data is inverted. If it's high it's 0, if it's low it's 1. But you still read on the rising edge of the clock. The first 24 bits are pretty meaningless since they're absolute position from when the battery was inserted. The second 24 bits are the the relative position from where it was last zeroed.

After figuring out the oddness of the data, I take the binary data, invert and feed it into a binary calculator backwards (LSB is sent first). Convert that to DEC and divide that number by 20480. I had the caliper set to 1.0005" and it sometimes read 1.001. The number I got was 20498. Divide that by 20480 and got 1.00087890625 (I doubt they're that accurate) so it explains why it was bouncing between 1.0005 and 1.001.

I think Chuck's is easier to read.

Edit: I didn't check the other calipers I have. One has a funky door and the plug won't go it (without case mods) and the other is upstairs in the office and I didn't feel like climbing them steps again.
Logan 200, Index 40H Mill, Boyer-Shultz 612 Surface Grinder, HF 4x6 Bandsaw, a shear with no name, ...
the nobucks boutique etsy shop  |  the nobucks boutique
Reply
Thanks given by:
(04-09-2015, 07:41 PM)Vinny Wrote: Got my scope going (finally) and decided to take a look at my caliper.  Mine's the older one and nothing was making sense at first but eventually I got it figured out.  

This one is POS ground, and the data is inverted.  If it's high it's 0, if it's low it's 1.  But you still read on the rising edge of the clock.  The first 24 bits are pretty meaningless since they're absolute position from when the battery was inserted.  The second 24 bits are the the relative position from where it was last zeroed.

After figuring out the oddness of the data, I take the binary data, invert and feed it into a binary calculator backwards (LSB is sent first).  Convert that to DEC and divide that number by 20480.  I had the caliper set to 1.0005" and it sometimes read 1.001.  The number I got was 20498.  Divide that by 20480 and got 1.00087890625 (I doubt they're that accurate) so it explains why it was bouncing between 1.0005 and 1.001.

I think Chuck's is easier to read.

Edit: I didn't check the other calipers I have.  One has a funky door and the plug won't go it (without case mods) and the other is upstairs in the office and I didn't feel like climbing them steps again.

Vinny,

It sounds like you have the older one that used a positive ground system per Rick Sparber's PDF document. Not sure why they did it that way. 17428

Ed
Reply
Thanks given by:
Chuck,

If you haven't built the 1.6V power supply circuit yet, here's an easier way out.

http://www.mpja.com/04-28-15.asp?r=340726&s=1

Ed
Reply
Thanks given by:
good find!
Logan 200, Index 40H Mill, Boyer-Shultz 612 Surface Grinder, HF 4x6 Bandsaw, a shear with no name, ...
the nobucks boutique etsy shop  |  the nobucks boutique
Reply
Thanks given by: EdK
I had posted this project on multiple forums, as many folks do. I had a response on another forum that somehow slipped by, or I was dealing with other, bigger issues, anyway, I missed that post. Somehow, I ran across the update yesterday, while looking for something else. I read the update, which contained some Arduino code and decided to try it,
Long story short version, it worked with my scale. It outputted the scale reading, inch, or mm, to the serial monitor! I played with the code all day yesterday, and succeeded in getting it to display on a 16x2 LCD. Better yet, this code does not need a level shifter, external power for the caliper, just hook the caliper up to the Arduino Uno and add the LCD, and it works.
I am now working on adding a Hall effect tach function, and 2 or 3 more scales.
Also, having seen Rod's post today about members getting help and not giving thanks, I want to thank all those that contributed to this thread. I truly respect the effort, and apologize to anyone I forgot to thank along the way. I never meant to not thank you all. I did have great frustration with this project as nothing worked, and other pressing issues, but that's life, eh? I was actually given the solution, or at least the seed to the solution back in June, but did not find it until yesterday.
I will clean up the code I have working and post it here soon. I have to translate some of the comments from French to English, and take some clear pictures of the working set up.
At the present state, I can display 1 HF scale on one LCD. The op of the solution had his version displaying 1 scale, in inch and mm, and the tach output, on the VGA input of a flat panel computer Monitor!
Again, thanks to ALL that contributed to this thread. I will share my present progress, just let me clean it up a bit.

Chuck
Micromark 7x14 Lathe, X2 Mill , old Green 4x6 bandsaw
The difficult takes me a while, the impossible takes a little longer.
Reply
Thanks given by:
Chuck,

Looking forward to the code, and especially the non-level-shifted connections.

Arvid
Reply
Thanks given by:
I just received parts for my lawn tractor. Must repair asap as SWMBO is on my butt, and lawn is hip high on a tall man.

So, I have decided to post my working code with French comments. I will get back to this, but must get lawn equip repaired and lawn mowed.

I am working on a variable speed motor to mount a magnet on for a hall effect set up to develop the Android tach. Also dug out another HF 'Pittsburg' Caliper, new in the box and identical to the one used with this sketch, to work on 2 axis. Will purchase a coupe more soon.

Chuck
Edit: I connected only the caliper clock and data lines, left battery in. I wired per attached diagram. Coed does not cover tach or video out to Flat Pannel.I'm trying to contact author to get rest of code.


Attached Files Thumbnail(s)
   

.txt   Arduino_HF_Caliper.txt (Size: 4.38 KB / Downloads: 12)
Micromark 7x14 Lathe, X2 Mill , old Green 4x6 bandsaw
The difficult takes me a while, the impossible takes a little longer.
Reply
Thanks given by:
Clever solution. He's using the A/D inputs thus not needing the level shifter circuitry.

Ed
Reply
Thanks given by:




Users browsing this thread: 10 Guest(s)