Best Communication Method

Hi everyone. I’m working on a project for work that I could use some advice on. I’m basically creating a bench top “MTS” machine. I’m working with an Imada DS-2 force sensor to measure force and feeding the sensor data into an Arduino Uno. The uno uses the force sensor data in closed loop to control the position of a stepper motor. The sensor has three methods of communication which are analog, serial or digimatic (I assume similar to calipers and such). Right now I’m using the analog because frankly it’s the simplest to work with and I’m walking the learning curve. I have the machine functioning but now it’s time to start optimizing. The analog signal is pretty noisy though so it’s less than ideal. So I’m wondering which of the three methods would be best from processing speed perspective. I need this thing to be pretty responsive so I’m worried about processing serial data slowing it down. I’ve included a screenshot from the sensor manual of the pin outs for reference.

Thanks in advance!

Image from Original Post so we don’t have to download it. See Image Guide

297bf41c3afbf7dd70b3333ebb6625ce26825d2d.png

…R

Post a link to the sensor datasheet. That way it may be possible to read it.

What is 'a bench top "MTS" machine' ?

How often do you need to read the sensor.

...R

I will include the link, but the image I’ve posted contains the only relevant information.

https://www.checkline.com/res/products/126098/ds2-manual-1.pdf

An MTS (trade name) machine refers to a family of machines that are used in a variety of testing applications that can involve dynamic or static simulations. For example, you could use an MTS machine to test the mechanical properties of a material, or simulate the wear between two different materials over many cycles. They can be pretty complex, but fundamentally they consist of at least two sensors that constantly monitor a force load and a displacement. Here is a link to their website:

I’m actually not sure how often the sensor needs to be read in my particular application, but I would guess something on the order of 50 - 100 Hz.

with the Arduino, RS232 is by hardware, so is much better to implementations. if you want to use digimatic, need to emulate protocol with the IO ports. also assume that the adc integrated on the sensor is precisest and faster thath de Arduino 's ADC.
you only need to corroborate the voltage level of the sensor's comunication. its posible that you need to use a max 232 adaptor level chip. then only conect tx from sensor to rx on arduino and rx of sensor with tx on arduino. Seriel.begin(19200) y listo.
GL

Nginerd:
I will include the link, but the image I've posted contains the only relevant information.

I could not read the image. I can read the PDF.

If you want to use the Digimatic system (whatever that is) you need to be familiar with some Mitutoyo system the details of which are not in that PDF.

I think you should assume you need a Max232 level converter between the RS232 voltage levels and the Arduino's TTL voltage levels.

It says the analog output ranges from 0-1v. Are you using the Arduino's internal 1.1v ADC voltage reference to give maximum resolution.

If you decide to use the RS232 system I see no reason why it should prevent you from getting a sufficient sample rate. Have a look at the examples in Serial Input Basics. It would be best to use an Arduino with multiple HardwareSerial ports such as a Mega, Leoonardo or Micro.

...R

Thanks for the replies! I've been working on using the digimatic output using code from here:

I constructed the interface circuit and I'm really close to getting what I need, but the output has extra information in it so I need to figure out how to modify the code (my weakness).

This is a snippet of the output:

7.8 mm; looptime = 44.51ms
0 mm; looptime = 44.06ms
0 mm; looptime = 43.67ms
0.0000000 mm; looptime = 43.99ms
0 mm; looptime = 44.05ms
0 in; looptime = 43.67ms
0.0000000 in; looptime = 43.98ms
7.7 mm; looptime = 44.51ms

The 7.8 and 7.7 numbers represent the force (in lbs) and are the only piece of information I need. I've studied the code but frankly it's a bit over my head right now. I know how to get rid of the "looptime" data, but as you can see I get several lines of garbage in between the target data. I'm going to keep studying, but would really appreciate any guidance, thanks!

Edit: I realized after posting this that you guys deserved a little more information. The code that I'm using was originally written to be used with digital devices for measuring length (such as micrometers and calipers), hence the "mm" units. Also, the data stream is composed of 13 packets of 4 bits for a total of 52 bits. The binary stream encodes for other information besides the actual measurement number, such as the units, the sign of the number, Hi/Lo set points etc. Basically accommodations for a variety of different instruments. The force gauge that I'm using should use the same protocol since they advertise that it's compatible with "digimatic" interface. I suspect that I might need to tweak the timing of the communications request to fix this, but that's kind of a wild guess.

The problem is that I don't see how I can help without taking time to learn about Digimatic - and life is a bit short to include that in it.

Maybe you can do the learning and produce some questions that can be answered without studying it?

...R

I did a lot of homework last night and found this incredibly helpful thread:

Which had the code below that I was able to modify slightly. Admittedly, my minor alteration is not so elegant, but it works. I just had to change how the code interpreted the location of the decimal and omit a couple of error checks. It’s a hack, but I’ll go back later when I have time and figure out the proper way to revise the code. This is my modified version, for anyone who needs to interface with an Imada DS2 force gage:

int req = 5; //mic REQ line goes to pin 5 through q1 (arduino high pulls request line low)
int dat = 2; //mic Data line goes to pin 2
int clk = 3; //mic Clock line goes to pin 3
int i = 0; int j = 0; int k = 0;
int sign = 0;
int decimal;
int units;
int error = 0;
int v1;
int v2;
int v3;
int v4;
int v5;
int v6;

byte mydata[14];

void setup()
{
 Serial.begin(19200);
 pinMode(req, OUTPUT);
 pinMode(clk, INPUT);
 pinMode(dat, INPUT);
 digitalWrite(clk, HIGH); // enable internal pull ups
 digitalWrite(dat, HIGH); // enable internal pull ups
 digitalWrite(req,LOW); // set request at high
}



void loop()
{   digitalWrite(req, HIGH); // generate set request
   for( i = 0; i < 13; i++ ) {
     k = 0;
     for (j = 0; j < 4; j++) {
     while( digitalRead(clk) == LOW) { } // hold until clock is high
     while( digitalRead(clk) == HIGH) { } // hold until clock is low
       bitWrite(k, j, (digitalRead(dat) & 0x1)); // read data bits, and reverse order )
     }

     mydata[i] = k;
     sign = mydata[4]; 
     v1= mydata[5]; 
     v2= mydata[6];
     v3= mydata[7];
     v4= mydata[8];
     v5= mydata[9];
     v6= mydata[10];  
     decimal = mydata[11];
     units = mydata[12];
          
          
   }
long num;
char buf[7];
buf[0]=v1+'0';
buf[1]=v2+'0';
buf[2]=v3+'0';
buf[3]=v4+'0';
buf[4]=v5+'0';
buf[5]=v6+'0';
buf[6]=0;
num=atol(buf);

//if (units != 0) error=1; 
if (sign != 0) error=2; 
//if (decimal != 3) error=3; 



if (error != 0)
{
 Serial.print ("error ");
 Serial.println (error);
}
else
{
 Serial.println(num*.1);
}


   digitalWrite(req,LOW);
   error=0;
   delay(100); 
}

Ok, new problem. It’s taking too long to read the Digimatic data (about 40 ms) which is an eternity for a stepper motor. The code is setup to step a motor inside of a loop until the force gauge is at the desired set point. So the result is about a 40 ms delay between stepper steps . How can I step the motor independently of reading the digimatic data, but also use that data to control the direction and speed of the stepper. Here’s my code:

//Define Test Parameters
const float load = 5; //Desired load setpoint
const float tolerance = 0.2; //Load setpoint tolerance +/-
const float offset = 1; //Adjusts for discrepancy between digital display and analog signal
const int numReadings = 20; //Total readings to average (analog signal only)
int minStepDelay = 150; //Minimum delay between steps (stepper motor speed, higher is slower)
int stepDelay = 250;  //Initial step delay (set automatically during program run as a function of difference between load setpoint and actual setpoint


// defines pins numbers
const int stepPin = 2;
const int dirPin = 5;
const int enablePin = 8;
float inputPin = A0;
int req = A5; //REQ line goes to pin 5 through q1 (arduino high pulls request line low)
int dat = A2; //Data line goes to pin 2
int clk = A3; //Clock line goes to pin 3



int readings[numReadings];      // the readings from the analog input
int readIndex = 0;              // the index of the current reading
float total = 0;                  // the running total
float average = 0;                // the average
float forceVoltage, forceInput; // Defines variables

//Define Digimatic Interface Variables
int i = 0; int j = 0; int k = 0;
int sign = 0;
int decimal;
int units;
int error = 0;
int v1;
int v2;
int v3;
int v4;
int v5;
int v6;
float forceValue = 0;

byte mydata[14];
float x = 0;


void setup() {
  // Sets the two pins as Outputs
  pinMode(stepPin, OUTPUT);
  pinMode(dirPin, OUTPUT);
  pinMode(enablePin, OUTPUT);
  pinMode(req, OUTPUT);
  pinMode(clk, INPUT);
  pinMode(dat, INPUT);
  digitalWrite(clk, HIGH); // enable internal pull ups
  digitalWrite(dat, HIGH); // enable internal pull ups
  digitalWrite(req, LOW); // set request at high

  //Enable steppers
  digitalWrite(enablePin, LOW);

  Serial.begin(250000);

  //readDigimatic();
  //Serial.println(forceValue);

  //for (int thisReading = 0; thisReading < numReadings; thisReading++) {
  //readings[thisReading] = 0;


}
void loop() {

  //If the input force is below the load setpoint
  while (forceValue < load - tolerance) {
    adjustForce(LOW);
    if (minStepDelay - (20 * abs(forceValue - (load + tolerance))) > 1) {
      stepDelay = minStepDelay - (20 * abs(forceValue - (load + tolerance)));
    }
    else
    {
      stepDelay = 1;
    }
    readDigimatic();
    Serial.print("INCREASING LOAD: ");
    Serial.println(forceValue);
  }

  //If the input force is at the load setpoint
  while (forceValue >= load - tolerance && forceValue <= load + tolerance) {
    if (minStepDelay - (20 * abs(forceValue - (load + tolerance))) > 1) {
      stepDelay = minStepDelay - (20 * abs(forceValue - (load + tolerance)));
    }
    else
    {
      stepDelay = 1;
    }
    readDigimatic();
    //Serial.print("HOLDING: ");
    //Serial.println(forceValue);
  }
  //If input force is above the load setpoint
  while (forceValue > load + tolerance) {
    adjustForce(HIGH);
    for (stepDelay = 150; stepDelay >= 1;) {
      stepDelay = minStepDelay - (10 * abs(forceValue - (load + tolerance)));
      break;
    }
    readDigimatic();
    Serial.print("DECREASING LOAD: ");
    Serial.println(forceValue);
  }
  //delay(1000); // One second delay

  readDigimatic();
  Serial.println(forceValue);

}
//Stepper Routine
void adjustForce(float motorDirection) {
  digitalWrite(dirPin, motorDirection);
  digitalWrite(stepPin, HIGH);
  delayMicroseconds(stepDelay);
  digitalWrite(stepPin, LOW);
  delayMicroseconds(stepDelay);
}
/*
  //Smoothing routine for analog input
  void averageInput(){
  // subtract the last reading:
  total = total - readings[readIndex];
  // read from the sensor:
  readings[readIndex] = analogRead(inputPin);
  // add the reading to the total:
  total = total + readings[readIndex];
  // advance to the next position in the array:
  readIndex = readIndex + 1;

  // if we're at the end of the array...
  if (readIndex >= numReadings) {
    // ...wrap around to the beginning:
    readIndex = 0;
  }

  // calculate the average:
  average = total / numReadings;
  //delay(1);        // delay in between reads for stability

  forceInput = average - offset; // Gets the current force unit from the forceMap function
  // Makes pules with custom delay, depending on the Potentiometer, from which the speed of the motor depends
  }
*/

void readDigimatic() {
  unsigned long startTime = millis();
  digitalWrite(req, HIGH); // generate set request
  for ( i = 0; i < 13; i++ ) {
    k = 0;
    for (j = 0; j < 4; j++) {
      while ( digitalRead(clk) == LOW) { } // hold until clock is high
      while ( digitalRead(clk) == HIGH) { } // hold until clock is low
      bitWrite(k, j, (digitalRead(dat) & 0x1)); // read data bits, and reverse order )
    }

    mydata[i] = k;
    sign = mydata[4];
    v1 = mydata[5];
    v2 = mydata[6];
    v3 = mydata[7];
    v4 = mydata[8];
    v5 = mydata[9];
    v6 = mydata[10];
    decimal = mydata[11];
    units = mydata[12];


  }
  long num;
  char buf[7];
  buf[0] = v1 + '0';
  buf[1] = v2 + '0';
  buf[2] = v3 + '0';
  buf[3] = v4 + '0';
  buf[4] = v5 + '0';
  buf[5] = v6 + '0';
  buf[6] = 0;
  num = atol(buf);

  if (sign == 8) {
    forceValue = num * (-0.1);
    //Serial.println(num*(-.1));
  }
  else
  {
    forceValue = num * 0.1;
    //Serial.println(num*.1);
  }


  digitalWrite(req, LOW);
  error = 0;
  //delay(100);

  unsigned long endTime = millis();
  Serial.print("Digimatic Loop Time: ");
  Serial.println(endTime - startTime);
  return;
}

Why can’t you use the Digimatic readings to determine how many steps should be made until the next reading?

I will think about that, but the Digimatic readings are dynamic. The idea is that there will be external actions being performed that will constantly be causing fluctuations in the force input. The goal is to apply a desired load (lets say 5 lbs for example) and then maintain a 5lb force ,regardless of external forces, by physically adjusting the position of the load point to compensate. The compensation needs to happen pretty quickly! Here’s a visual aid of the load frame for reference (excuse the mess):

Load_Frame.JPG

Not shown is the stepper motor which attaches at the top to the lead screw, which drives the load carriage up and down.

Without meaning to be unkind, that code is an awful mess. There should only be a single call to the readDigimatic() function and I doubt if you should have any WHILE loops as they are almost as bad as the delay() function.

I suspect the WHILEs need to be taken out of the readDigimatic() function also.

WHILE and FOR loops that last more than a number of microseconds slow everything down.

IMHO you should have a variable that represents the most recent Digimatic value and the number of steps or the step speed should be based on that.

Another approach which may work in your particular case is to move a fixed number of steps (maybe only 1) between each Digimatic reading and then decide whether another step is need. If this is suitable it may work with minimal change to your existing code.

Have a look at Planning and Implementing a Program

...R

Without meaning to be unkind, that code is an awful mess.

I’m sure that it is, I’ve already admitted that coding is a weakness for me.

There should only be a single call to the readDigimatic() function

if I don’t call the Digimatic function at the end of each loop, how will I ever change the comparison value to get out of the loop?

I suspect the WHILEs need to be taken out of the readDigimatic() function also.

I can’t confirm or deny this, I didn’t write this part of the code, but I think it may be required to read all of the bits that are being sent from the force gauge.

IMHO you should have a variable that represents the most recent Digimatic value

I thought that’s what I did with the “forceValue” variable.

Another approach which may work in your particular case is to move a fixed number of steps (maybe only 1) between each Digimatic reading

That’s how it already works. The motor is stepped once, the digimatic data is checked and compared to desired force (“load” variable) and then proceeds accordingly. If the values are equal, the motor holds position. If the forceValue is < OR > load, the motor steps in the appropriate direction until they are equal.

The problem is everytime the digimatic is queried, it takes 40ms to get the data, so the motor steps once with a 250 or less microsecond delay, then steps with a 40ms second delay. Which is hardly moving at all. Stepper High/Low pulses are on the order of microseconds, so the 40 millisecond delay is 3 orders of magnitude longer.

What super stepper motor do you have, that steps within microseconds, with thousands of turns per second? :wink:

You didn't understand the point: It's up to you to compute a reasonable number of steps, depending on the difference between the setpoint and the last measurement. Then you can make the stepper turn as fast as your calculation safely allows.

Eventually another (fast) strain gauge can be added to the device, that allows for a fast; maybe rough; adjustment.

What super stepper motor do you have, that steps within microseconds, with thousands of turns per second? :wink:

1 revolution is 200 steps on my motor, with 8x microstepping its 1600 steps. At a 250 microsecond delay between steps that's 0.4 seconds per revolution or about 2 rev per second, or 120 RPM...not exactly "thousands of turns per second". At 40 milliseconds, that works out to 64 seconds per revolution, or 1/64 revolution per second, or about 1 RPM.

t's up to you to compute a reasonable number of steps, depending on the difference between the setpoint and the last measurement. Then you can make the stepper turn as fast as your calculation safely allows.

Actually, no. The stepper speed is limited by the time it takes to read the measurement. And it doesn't matter how many steps it takes to get where it needs to go, the only thing the motor "knows" is that it needs to move in a particular direction until some condition is met. Its not calculating the number of steps to get there (because I can't anyway, its a constantly moving target), The motor is ether on or off until a specific condition is met. Remember, this is closed loop system, the motor movement affects the force change in a constant feedback loop.

Nginerd:
At 40 milliseconds, that works out to 64 seconds per revolution,

Because you want very frequent steps it may be simplest to set up one of the hardware Timers to produce an interrupt every 250µsecs and in the ISR you can cause a step to happen if one is required. But keep the code in the ISR as short as possible. Perhaps something like

void stepISR () {
   if (stepDue == true) {
      digitalWrite(stepPin, HIGH);
      digitalWrite(stepPin, LOW);
   }
}

Within your other code you can decide whether a step should be made and set the direction.

...R

A last question before I give up:

Have you already tested how many microsteps it takes in the same direction, and in reverse direction, until the sensor returns a different value at all?

I don't know anything about interrupts, so I'm educating myself now. So let me see if I understand how the interrupt works: Every 250 microseconds, the void loop will be interrupted to pulse the stepPin if needed. But, that also means that if the program is in the middle of receiving digimatic data, that will be interrupted as well. So if I have that right, I'm concerned that I might end up with a lot of lost data from the digimatic. I need to test it but I think once the data has been requested, it transmits until it's done; i.e. it can't paused. I'm not certain of that but I think that's at least true of each byte of data (it transmits 13 bytes of 4 bits).

Have you already tested how many microsteps it takes in the same direction, and in reverse direction, until the sensor returns a different value at all?

No, but it probably isn't much. The lead screw has a pitch of 5mm, the strain gauge in the sensor probably has a full scale (220lbs) deflection of maybe 300 microns (just a guess). So let's see...at 1600 steps per rev that's 320 steps per mm of translation, so about 96 steps at 220 lb load. The resolution of the gauge is 0.1 lbs...so way less than 1 micro step (0.044 to be exact). That doesn't seem right but you're welcome to check my math. Of course I'm guessing on the gauge deflection, but even if I doubled its still under a step.