2016/03/25

Building a DIY filament width sensor using digital caliper

This post is about how to build a DIY filament width sensor using digital caliper with built-in data port.


Left: Filament Puller, Center: Digital Caliper, Right: Control Box.
Description:

Filament Puller

It has 1 stepper motor and 2 rollers. The rollers are driven by the stepper motor to pull the filament away from the digital caliper.

Digital Caliper

It has a data port which enables the reading of data by external devices such as Arduino. For details about its data format and other info., please refer to my other post.
http://wei48221.blogspot.tw/2016/01/using-digital-caliper-for-digital-read.html

Control Box

It houses the electronics for reading the read outs from the digital caliper, reading the motor speed control input from the trimpot, controlling the motor speed and displaying the read outs on the LCD.



Schematic:


Code:

The code uses interrupt and timer to achieve concurrent operation of reading from the digital caliper and the trimpot, displaying the info. on LCD, and keeping the stepper motor running.

#include "TimerOne.h"

// I2C LCD Configuration & Initialization Section
//-------------------------------------------------------------------------------------
#include <Wire.h>
#include <LCD.h>
#include <LiquidCrystal_I2C.h>

#define I2C_ADDR 0x27     // <-- Add the LCD's I2C address here. It could be found using I2C Scanner.
#define BACKLIGHT_PIN 3   // These are the pins between the LCD and the I2C module, not between the LCD and Arduino
#define En_pin  2
#define Rw_pin  1
#define Rs_pin  0
#define D4_pin  4
#define D5_pin  5
#define D6_pin  6
#define D7_pin  7

LiquidCrystal_I2C  lcd(I2C_ADDR,En_pin,Rw_pin,Rs_pin,D4_pin,D5_pin,D6_pin,D7_pin);
// End of LCD Configuration & Initialization Section
//-------------------------------------------------------------------------------------
// DRO Configuration Section
int bit_array[25];                     // For storing the data bit. bit_array[0] = data bit 1 (LSB), bit_array[23] = data bit 24 (MSB).
volatile unsigned long time_now = 0;   // For storing the time when the clock signal is changed from HIGH to LOW (falling edge trigger of data output).

int CLOCK_PIN = 2;                     // Arduino Uno Pin 2 as Clock Pin
int DATA_PIN = 3;                      // Arduino Uno Pin 3 as Data Pin
// End of DRO Configuration Section
//-------------------------------------------------------------------------------------

int d = 0;
int d_old = -1;
int motorState = LOW;                  // motorState used to set the motor

unsigned long step_change_time = 0;    // The last time the state of the stepper motor step pin was changed (HIGH -> LOW, LOW -> HIGH)
int motor_state = 0;                   // Motor step pin status (0 -> LOW, 1 -> HIGH)

void setup() {
  Serial.begin(9600);

  Timer1.initialize(500000);          // initialize timer1, and set a 1/2 second period
  Timer1.pwm(9, 512);                 // setup pwm on pin 9, 50% duty cycle, for detail, see http://playground.arduino.cc/Code/Timer1

  // Setup the LCD's number of columns and rows:
  lcd.begin (20,4);

  // Setup the LCD's backlight as HIGH
  lcd.setBacklightPin(BACKLIGHT_PIN,POSITIVE);
  lcd.setBacklight(HIGH);

  lcd.home (); // go home (0, 0)
  lcd.print("Width:");    // Filament Width

  lcd.setCursor(0, 1);
  lcd.print("Speed:");    // Roller Speed

  // Setup the DRO pins
  pinMode(CLOCK_PIN, INPUT);
  pinMode(DATA_PIN, INPUT);

  attachInterrupt(digitalPinToInterrupt(CLOCK_PIN), readData, CHANGE);    // Using interrupt to capture DRO readout data

  // Setup the puller motor control pins
  pinMode(A3, INPUT);           // Trimpot input for roller wheel RPM control (0: Slowest, 1023: Fastest)
  pinMode(6, OUTPUT);           // Filament puller roller wheel direction pin
  pinMode(9, OUTPUT);           // Filament puller roller wheel step pin - with PWM function
  digitalWrite(6, LOW);         // Low -> pulling the filament away from extruder nozzle
 
}

void readData() {
  if (digitalRead(CLOCK_PIN) == HIGH) time_now = millis();

  if (digitalRead(CLOCK_PIN) == LOW) {
    if ((millis() - time_now) > 100) decode();      // If the HIGH pulse is longer than 100 millis we are at the start of a new bit sequence
                                                    // Call the decode function to collect the bit stream and decode it
  }
}

void loop() {
  int x = analogRead(A3); // Reads the analog value on pin A3 into x, value range: 0 ~ 1024

  d = (x/100) * 1;        // RPM range is adjusted by changing the multiplier, was 100
  if (d <= 1) d = 1;      // Get rid of d = 0 to give enough delay for stepper motor to function properly

  Timer1.setPeriod(d*2000);
  Timer1.pwm(9, 512);
}

void decode() {
  int sign = 1;
  int i = 0;
  float value = 0.0;
  float result = 0.0;

  bit_array[i] = digitalRead(DATA_PIN);       // Store the 1st bit (start bit) which is always 1.
  while (digitalRead(CLOCK_PIN) == HIGH) {};

  for (i = 1; i <= 24; i++) {
    while (digitalRead(CLOCK_PIN) == LOW) { } // Wait until clock returns to HIGH
    bit_array[i] = digitalRead(DATA_PIN);  
    while (digitalRead(CLOCK_PIN) == HIGH) {} // Wait until clock returns to LOW
  }

  for (i = 1; i <= 20; i++) {                 // Turning the value in the bit array from binary to decimal.
      value = value + (pow(2, i-1) * bit_array[i]);
  }

  if (bit_array[21] == 1) sign = -1;          // Bit 21 is the sign bit. 0 -> +, 1 => -

  if (bit_array[24] == 1) {                   // Bit 24 tells the measureing unit (1 -> in, 0 -> mm)
     result = (value*sign) / 2000.00;
     interrupts();                            // Temporary re-enable interrupt for LCD library to function
        lcd.setCursor(7, 0);
        lcd.print(result,4);
        lcd.print(" in     ");
        lcd.setCursor(7, 1);
        lcd.print(d);                         // Roller Speed
        lcd.print(" ");
     noInterrupts();                          // Disable the temporary re-enabled interrupt
  } else {
     result = (value*sign) / 100.00;  
     interrupts();
         lcd.setCursor(7, 0);
         lcd.print(result,2);
         lcd.print(" mm     ");
         lcd.setCursor(7, 1);
         lcd.print(d);                        // Roller Speed
         lcd.print(" ");
      noInterrupts();
  }
}

Issue:

Digital caliper measures the clamp's travel distance by measuring the variation in capacitance but the capacitance tends to drift due to factors such as the build quality of the caliper, environment, etc. For normal use, because the caliper is zero out each time before taking the measurement, the capacitance drift can be ignored because the capacitance of the clamp's starting point and the ending point are known and the caliper will be zero out again before each use. However, when the caliper is used in an environment that doesn't zero out for long period of time, the accuracy of the measurement is greatly impacted by the capacitance drift. Because of the above issue, other ways for measuring small distance (with 0.01mm accuracy) need to be used.

Reference:

https://www.arduino.cc/en/Reference/AnalogWrite

Secrets of Arduino PWM
https://www.arduino.cc/en/Tutorial/SecretsOfArduinoPWM

PWM Frequency
http://playground.arduino.cc/Code/PwmFrequency

Controlling multiple speed-independent stepper motors simultaneously with Arduino.
http://www.libremechanics.com/?q=node/28

2 comments:

  1. Great use for the SPC caliper output. Maybe a cheaper Mitutoyo with Absolute location would work better for your needs.

    ReplyDelete