Reading multiple Mitutoyo dial indicators

Hi,
as an Arduino beginner I am having difficulties with my project. The goal is to read four Mitutoyo dial indicators in experiments for measuring autogenous shrinkage of cement paste using corrugated tubes test method: Article on corrugated tube protocol

After doing some search I found rather many sources which i could use for building my project, like this in Instructables:
https://www.instructables.com/Interfacing-a-Digital-Micrometer-to-a-Microcontrol/

or this with more advanced codding by Roger Cheng:
Arduino Interface for Mitutoyo SPC Data Port

And of course there are multiple discussions in Arduino forum as well.

As basis for my code i choose the guide from Instructables since it met my level of coding understanding. I edited the code so it would read data from four Mitutoyo dial indicators connected to Arduino Mega board (using schematics from Instructables), which then is connected to laptop and the data is recorded with Excel using Data streamer add in.
Here is my code (please excuse me, I know it is not too elegant):

int req1 = 25; //mic REQ line goes to pin  through q1 (arduino high pulls request line low)
int dat1 = 22; //mic Data line goes to pin 
int clk1 = 23; //mic Clock line goes to pin 

int req2 = 31; //mic REQ line goes to pin  through q1 (arduino high pulls request line low)
int dat2 = 28; //mic Data line goes to pin 
int clk2 = 29; //mic Clock line goes to pin 

int req3 = 37; //mic REQ line goes to pin 5 through q1 (arduino high pulls request line low)
int dat3 = 34; //mic Data line goes to pin 
int clk3 = 35; //mic Clock line goes to pin 

int req4 = 43; //mic REQ line goes to pin  through q1 (arduino high pulls request line low)
int dat4 = 40; //mic Data line goes to pin 
int clk4 = 41; //mic Clock line goes to pin 



int i = 0;
int j = 0;
int k = 0;
int signCh = 8;
int sign1 = 0;
int sign2 = 0;
int sign3 = 0;
int sign4 = 0;
int decimal;
float dpp;
int units;

byte mydata1[14];
byte mydata2[14];
byte mydata3[14];
byte mydata4[14];
String value_str;
long value_int; //was an int, could not measure over 32mm
float value1;
float value2;
float value3;
float value4;

void setup() {
  // put your setup code here, to run once:
Serial.begin(9600);

pinMode(req1, OUTPUT);

pinMode(clk1, INPUT_PULLUP);

pinMode(dat1, INPUT_PULLUP);

digitalWrite(req1,LOW); // set request at high



pinMode(req2, OUTPUT);

pinMode(clk2, INPUT_PULLUP);

pinMode(dat2, INPUT_PULLUP);

digitalWrite(req2,LOW); // set request at high


pinMode(req3, OUTPUT);

pinMode(clk3, INPUT_PULLUP);

pinMode(dat3, INPUT_PULLUP);

digitalWrite(req3,LOW); // set request at high


pinMode(req4, OUTPUT);

pinMode(clk4, INPUT_PULLUP);

pinMode(dat4, INPUT_PULLUP);

digitalWrite(req4,LOW); // set request at high

}

void loop() {
  // put your main code here, to run repeatedly:
// Input 1      ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
digitalWrite(req1, HIGH); // generate set request
for( i = 0; i < 13; i++ ) {
k = 0;
for (j = 0; j < 4; j++) {
while( digitalRead(clk1) == LOW) {
} // hold until clock is high
while( digitalRead(clk1) == HIGH) {
} // hold until clock is low
bitWrite(k, j, (digitalRead(dat1) & 0x1));
}

mydata1[i] = k;

}
sign1 = mydata1[4];
value_str = String(mydata1[5]) + String(mydata1[6]) + String(mydata1[7]) + String(mydata1[8] + String(mydata1[9] + String(mydata1[10]))) ;
decimal = mydata1[11];
units = mydata1[12];

value_int = value_str.toInt();
if (decimal == 0) dpp = 1.0;
if (decimal == 1) dpp = 10.0;
if (decimal == 2) dpp = 100.0;
if (decimal == 3) dpp = 1000.0;
if (decimal == 4) dpp = 10000.0;
if (decimal == 5) dpp = 100000.0;

value1 = value_int / dpp;
// Input 2      ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
digitalWrite(req2, HIGH); // generate set request
for( i = 0; i < 13; i++ ) {
k = 0;
for (j = 0; j < 4; j++) {
while( digitalRead(clk2) == LOW) {
} // hold until clock is high
while( digitalRead(clk2) == HIGH) {
} // hold until clock is low
bitWrite(k, j, (digitalRead(dat2) & 0x1));
}

mydata2[i] = k;

}
sign2 = mydata2[4];
value_str = String(mydata2[5]) + String(mydata2[6]) + String(mydata2[7]) + String(mydata2[8] + String(mydata2[9] + String(mydata2[10]))) ;
decimal = mydata2[11];
units = mydata2[12];

value_int = value_str.toInt();
if (decimal == 0) dpp = 1.0;
if (decimal == 1) dpp = 10.0;
if (decimal == 2) dpp = 100.0;
if (decimal == 3) dpp = 1000.0;
if (decimal == 4) dpp = 10000.0;
if (decimal == 5) dpp = 100000.0;

value2 = value_int / dpp;
// Input 3      ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
digitalWrite(req3, HIGH); // generate set request
for( i = 0; i < 13; i++ ) {
k = 0;
for (j = 0; j < 4; j++) {
while( digitalRead(clk3) == LOW) {
} // hold until clock is high
while( digitalRead(clk3) == HIGH) {
} // hold until clock is low
bitWrite(k, j, (digitalRead(dat3) & 0x1));
}

mydata3[i] = k;

}
sign3 = mydata3[4];
value_str = String(mydata3[5]) + String(mydata3[6]) + String(mydata3[7]) + String(mydata3[8] + String(mydata3[9] + String(mydata3[10]))) ;
decimal = mydata3[11];
units = mydata3[12];

value_int = value_str.toInt();
if (decimal == 0) dpp = 1.0;
if (decimal == 1) dpp = 10.0;
if (decimal == 2) dpp = 100.0;
if (decimal == 3) dpp = 1000.0;
if (decimal == 4) dpp = 10000.0;
if (decimal == 5) dpp = 100000.0;

value3 = value_int / dpp;
//  Input 4   ////////////////////////////////////////////////////////////////////////////////////////////////
digitalWrite(req4, HIGH); // generate set request
for( i = 0; i < 13; i++ ) {
k = 0;
for (j = 0; j < 4; j++) {
while( digitalRead(clk4) == LOW) {
} // hold until clock is high
while( digitalRead(clk4) == HIGH) {
} // hold until clock is low
bitWrite(k, j, (digitalRead(dat4) & 0x1));
}

mydata4[i] = k;

}
sign4 = mydata4[4];
value_str = String(mydata4[5]) + String(mydata4[6]) + String(mydata4[7]) + String(mydata4[8] + String(mydata4[9] + String(mydata4[10]))) ;
decimal = mydata4[11];
units = mydata4[12];

value_int = value_str.toInt();
if (decimal == 0) dpp = 1.0;
if (decimal == 1) dpp = 10.0;
if (decimal == 2) dpp = 100.0;
if (decimal == 3) dpp = 1000.0;
if (decimal == 4) dpp = 10000.0;
if (decimal == 5) dpp = 100000.0;

value4 = value_int / dpp;
///////////////////////////////////////////////////////////////////////////
if (sign1 == 0) {
Serial.print(value1,decimal); // 1 caliper
Serial.print(",");
}
if (sign1 == 8) {
Serial.print("-"); Serial.print(value1,decimal);
Serial.print(",");
}

if (sign2 == 0) {
Serial.print(value2,decimal); // 2 caliper
Serial.print(",");
}
if (sign2 == 8) {
Serial.print(value2,decimal); // 2 caliper
Serial.print(",");
}

if (sign3 == 0) {
Serial.print(value3,decimal); // 3 caliper
Serial.print(",");
}
if (sign3 == 8) {
Serial.print("-"); Serial.println(value3,decimal);
}

if (sign4 == 0) {
Serial.println(value4,decimal); // 2 caliper
}
if (sign4 == 8) {
 
Serial.print("-"); Serial.println(value4,decimal);
}




digitalWrite(req1,LOW);
digitalWrite(req2,LOW);
digitalWrite(req3,LOW);
digitalWrite(req4,LOW);
delay(5000);

}

I must say that everything works fine in trials, but I have noticed a problem which might cause big issues for data reading of the experiment. I accidentally noticed that one indicator has bad contacts with data cable. It might seem that everything is connected but the indicator is not sending data and when it happens the serial stream stops. That is it stops for all other indicators as well, despite that they are well connected.
The issue is that the test should last 7 days or maybe up to 28 days in some cases and if connection accident would happen just in one dial the recording would be stopped and data for unknow period of time would be lost for all of rest working units.

If I understand correct the issue is in this part of code:

while( digitalRead(clk1) == LOW) {
} // hold until clock is high
while( digitalRead(clk1) == HIGH) {
} // hold until clock is low

Do I understand correct, if dial is disconnected Arduino waits permanently until clock signal is high? Is there way to go around this situation, e.g. with introducing waiting time for 1 sec and if no signal comes assign value as 0 and then go on with values from other dials.

I would appreciate your help and advises.

You're on the right track. A quick fix could be replacing the poor working Toyo.
Instructables are often named "destructables" due to poor quality, or even worse.
Present the code from the other institution. The available equipment here won't handle it.

1 Like

That's true!

Please post it again in your next post, but before that, please click Tools>Auto Format in the IDE. That will make it a little more elegant, or at least readable!

There is much duplication in the code, it can easily be shrunk to around 1/3 of the current size. We can help with that as well as making the code robust to broken sensors.

1 Like

Thanks for fast reply.
The first version of my code was shorter, but I after not working (which I didn't know yet the contact problem) I thought that I had to address all dials separately so i "improved" the code. So here is the auto formatted code as noted by PaulRB:

int req1 = 25;  //mic REQ line goes to pin  through q1 (arduino high pulls request line low)
int dat1 = 22;  //mic Data line goes to pin
int clk1 = 23;  //mic Clock line goes to pin

int req2 = 31;  //mic REQ line goes to pin  through q1 (arduino high pulls request line low)
int dat2 = 28;  //mic Data line goes to pin
int clk2 = 29;  //mic Clock line goes to pin

int req3 = 37;  //mic REQ line goes to pin 5 through q1 (arduino high pulls request line low)
int dat3 = 34;  //mic Data line goes to pin
int clk3 = 35;  //mic Clock line goes to pin

int req4 = 43;  //mic REQ line goes to pin  through q1 (arduino high pulls request line low)
int dat4 = 40;  //mic Data line goes to pin
int clk4 = 41;  //mic Clock line goes to pin



int i = 0;
int j = 0;
int k = 0;
int signCh = 8;
int sign1 = 0;
int sign2 = 0;
int sign3 = 0;
int sign4 = 0;
int decimal;
float dpp;
int units;

byte mydata1[14];
byte mydata2[14];
byte mydata3[14];
byte mydata4[14];
String value_str;
long value_int;  //was an int, could not measure over 32mm
float value1;
float value2;
float value3;
float value4;

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);

  pinMode(req1, OUTPUT);

  pinMode(clk1, INPUT_PULLUP);

  pinMode(dat1, INPUT_PULLUP);

  digitalWrite(req1, LOW);  // set request at high



  pinMode(req2, OUTPUT);

  pinMode(clk2, INPUT_PULLUP);

  pinMode(dat2, INPUT_PULLUP);

  digitalWrite(req2, LOW);  // set request at high


  pinMode(req3, OUTPUT);

  pinMode(clk3, INPUT_PULLUP);

  pinMode(dat3, INPUT_PULLUP);

  digitalWrite(req3, LOW);  // set request at high


  pinMode(req4, OUTPUT);

  pinMode(clk4, INPUT_PULLUP);

  pinMode(dat4, INPUT_PULLUP);

  digitalWrite(req4, LOW);  // set request at high
}

void loop() {
  // put your main code here, to run repeatedly:
  // Input 1      ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  digitalWrite(req1, HIGH);  // generate set request
  for (i = 0; i < 13; i++) {
    k = 0;
    for (j = 0; j < 4; j++) {
      while (digitalRead(clk1) == LOW) {
      }  // hold until clock is high
      while (digitalRead(clk1) == HIGH) {
      }  // hold until clock is low
      bitWrite(k, j, (digitalRead(dat1) & 0x1));
    }

    mydata1[i] = k;
  }
  sign1 = mydata1[4];
  value_str = String(mydata1[5]) + String(mydata1[6]) + String(mydata1[7]) + String(mydata1[8] + String(mydata1[9] + String(mydata1[10])));
  decimal = mydata1[11];
  units = mydata1[12];

  value_int = value_str.toInt();
  if (decimal == 0) dpp = 1.0;
  if (decimal == 1) dpp = 10.0;
  if (decimal == 2) dpp = 100.0;
  if (decimal == 3) dpp = 1000.0;
  if (decimal == 4) dpp = 10000.0;
  if (decimal == 5) dpp = 100000.0;

  value1 = value_int / dpp;
  // Input 2      ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  digitalWrite(req2, HIGH);  // generate set request
  for (i = 0; i < 13; i++) {
    k = 0;
    for (j = 0; j < 4; j++) {
      while (digitalRead(clk2) == LOW) {
      }  // hold until clock is high
      while (digitalRead(clk2) == HIGH) {
      }  // hold until clock is low
      bitWrite(k, j, (digitalRead(dat2) & 0x1));
    }

    mydata2[i] = k;
  }
  sign2 = mydata2[4];
  value_str = String(mydata2[5]) + String(mydata2[6]) + String(mydata2[7]) + String(mydata2[8] + String(mydata2[9] + String(mydata2[10])));
  decimal = mydata2[11];
  units = mydata2[12];

  value_int = value_str.toInt();
  if (decimal == 0) dpp = 1.0;
  if (decimal == 1) dpp = 10.0;
  if (decimal == 2) dpp = 100.0;
  if (decimal == 3) dpp = 1000.0;
  if (decimal == 4) dpp = 10000.0;
  if (decimal == 5) dpp = 100000.0;

  value2 = value_int / dpp;
  // Input 3      ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  digitalWrite(req3, HIGH);  // generate set request
  for (i = 0; i < 13; i++) {
    k = 0;
    for (j = 0; j < 4; j++) {
      while (digitalRead(clk3) == LOW) {
      }  // hold until clock is high
      while (digitalRead(clk3) == HIGH) {
      }  // hold until clock is low
      bitWrite(k, j, (digitalRead(dat3) & 0x1));
    }

    mydata3[i] = k;
  }
  sign3 = mydata3[4];
  value_str = String(mydata3[5]) + String(mydata3[6]) + String(mydata3[7]) + String(mydata3[8] + String(mydata3[9] + String(mydata3[10])));
  decimal = mydata3[11];
  units = mydata3[12];

  value_int = value_str.toInt();
  if (decimal == 0) dpp = 1.0;
  if (decimal == 1) dpp = 10.0;
  if (decimal == 2) dpp = 100.0;
  if (decimal == 3) dpp = 1000.0;
  if (decimal == 4) dpp = 10000.0;
  if (decimal == 5) dpp = 100000.0;

  value3 = value_int / dpp;
  //  Input 4   ////////////////////////////////////////////////////////////////////////////////////////////////
  digitalWrite(req4, HIGH);  // generate set request
  for (i = 0; i < 13; i++) {
    k = 0;
    for (j = 0; j < 4; j++) {
      while (digitalRead(clk4) == LOW) {
      }  // hold until clock is high
      while (digitalRead(clk4) == HIGH) {
      }  // hold until clock is low
      bitWrite(k, j, (digitalRead(dat4) & 0x1));
    }

    mydata4[i] = k;
  }
  sign4 = mydata4[4];
  value_str = String(mydata4[5]) + String(mydata4[6]) + String(mydata4[7]) + String(mydata4[8] + String(mydata4[9] + String(mydata4[10])));
  decimal = mydata4[11];
  units = mydata4[12];

  value_int = value_str.toInt();
  if (decimal == 0) dpp = 1.0;
  if (decimal == 1) dpp = 10.0;
  if (decimal == 2) dpp = 100.0;
  if (decimal == 3) dpp = 1000.0;
  if (decimal == 4) dpp = 10000.0;
  if (decimal == 5) dpp = 100000.0;

  value4 = value_int / dpp;
  ///////////////////////////////////////////////////////////////////////////
  if (sign1 == 0) {
    Serial.print(value1, decimal);  // 1 caliper
    Serial.print(",");
  }
  if (sign1 == 8) {
    Serial.print("-");
    Serial.print(value1, decimal);
    Serial.print(",");
  }

  if (sign2 == 0) {
    Serial.print(value2, decimal);  // 2 caliper
    Serial.print(",");
  }
  if (sign2 == 8) {
    Serial.print(value2, decimal);  // 2 caliper
    Serial.print(",");
  }

  if (sign3 == 0) {
    Serial.print(value3, decimal);  // 3 caliper
    Serial.print(",");
  }
  if (sign3 == 8) {
    Serial.print("-");
    Serial.println(value3, decimal);
  }

  if (sign4 == 0) {
    Serial.println(value4, decimal);  // 4 caliper
  }
  if (sign4 == 8) {

    Serial.print("-");
    Serial.println(value4, decimal);
  }




  digitalWrite(req1, LOW);
  digitalWrite(req2, LOW);
  digitalWrite(req3, LOW);
  digitalWrite(req4, LOW);
  delay(1000);
}

And here is the other code (by Roger Cheng ), as requested by Railroader:

// Derived from https://www.instructables.com/id/Interfacing-a-Digital-Micrometer-to-a-Microcontrol/

#include <Arduino.h>

// When defined, will send SPC data as hexadecimal over serial.
#define HEX_DATA_OUTPUT

// When defined, will convert raw data to human readable string
// and send over serial.
// #define HUMAN_READABLE_OUTPUT

int req = 5;  //Arduino pin for REQ line, drives transistor to ground SPC port pin 5 (~REQ)
int dat = 2;  //Arduino pin for Data line, connects directly to SPC port pin 2 (DATA)
int clk = 3;  //Arduino pin for Clock line, connects directly to SPC port pin 3 (~CLK)

int i = 0;
int j = 0;
int k = 0;

// Mitutoyo SPC data port transmits 13 4-bit values
// Byte Meaning
// 0    Header, always 0xF
// 1    Header, always 0xF
// 2    Header, always 0xF
// 3    Header, always 0xF
// 4    Sign. Zero is positive. 0x8 is negative.
// 5    Most significant digit (MSD) of measurement
// 6    Next digit of measurement
// 7    Next digit of measurement
// 8    Next digit of measurement
// 9    Next digit of measurement
// 10   Least significant digit (LSD) of measurement
// 11   Digit to place decimal point
// 12   Unit. Zero is mm. Nonzero (b1000 is 1?) is inch

byte spcdata[13];  // The raw data sent by instrument

#ifdef HUMAN_READABLE_OUTPUT
float value;  // The value calculated from raw data
int decimal;  // Number of digits that are after decimal point
#endif

void setup() {
  Serial.begin(9600);
  pinMode(req, OUTPUT);
  pinMode(clk, INPUT_PULLUP);
  pinMode(dat, INPUT_PULLUP);
  digitalWrite(req, LOW);  // set request at high
}

void loop() {
  digitalWrite(req, HIGH);  // generate set request

  for (i = 0; i < 13; i++) {
    k = 0;

    // Timing diagram indicates data bit has been valid for about 120
    // microseconds before the clock signal is raised, and remains
    // valid for about 120 microseconds afterwards. This code reads data
    // bit at the falling clock edge.
    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));
    }

    // After reading the first 4-bit value, we can drop REQ output.
    if (i == 0) {
      digitalWrite(req, LOW);
    }
    spcdata[i] = k;
  }

#ifdef HEX_DATA_OUTPUT
  for (i = 0; i < 13; i++) {
    Serial.print(spcdata[i], HEX);
  }
#ifdef HUMAN_READABLE_OUTPUT
  // Need a separator if we're printing human readable as well
  Serial.print(" ");
#endif  // HUMAN_READABLE_OUTPUT
#endif  //HEX_DATA_OUTPUT

#ifdef HUMAN_READABLE_OUTPUT
  // Combine measurement digits into a number
  value = 0;
  for (i = 5; i <= 10; i++) {
    value *= 10;
    value += spcdata[i];
  }

  // Adjust number for decimal point position
  decimal = spcdata[11];
  value /= pow(10, decimal);

  // Adjust if number is negative
  if (spcdata[4] == 0x8) {
    value *= -1;
  }

  // Print resulting value to serial port, to specified
  // number of digits after decimal point.
  Serial.print(value, decimal);

  // Append units for value
  if (spcdata[12] == 0) {
    Serial.print(" mm");
  } else {
    Serial.print(" in");
  }
#endif  // HUMAN_READABLE_OUTPUT

  Serial.println();
}
About 80% of your code should be reworked with struts and arrays - make it shorter, easier to read and debug.

I think everything could be simplified with a little function:

const byte errorCode = 255;
const unsigned long timeout = 1000;

byte readNybble(byte s) {
  byte k = 0;
  unsigned long time;

  // Timing diagram indicates data bit has been valid for about 120
  // microseconds before the clock signal is raised, and remains
  // valid for about 120 microseconds afterwards. This code reads data
  // bit at the falling clock edge.
  for (byte j = 0; j < 4; j++) {
    time = micros();
    while (digitalRead(clk[s]) == LOW and micros() - time < timeout); // hold until clock is high
    if (digitalRead(clk[s]) == LOW) return errorCode;
    time = micros();
    while (digitalRead(clk[s]) == HIGH and micros() - time < timeout); // hold until clock is low
    if (digitalRead(clk[s]) == HIGH) return errorCode;
    bitWrite(k, j, (digitalRead(dat) & 0x1));
  }

  return k;
}

With this function, there would be no need for the data arrays you had before. But it would be good to have arrays for the clock, data and req pins.

void loop() {

  for (byte s=0; s<4; s++) { // repeat for each sensor

    digitalWrite(req[s], HIGH);  // generate set request
    if (readNybble(s) != errorCode) { // check nybble 0 is received ok
      for (byte n=1; n<=3; n++) readNybble(s); // discard nybbles 1 to 3
      byte sign = readNybble(s); // nybble 4
      float value = readNybble(s); // nybble 5 is first value digit
      for (byte n=6; n<=10; n++) value = value * 10 + readNybble(s); // read value digits from nybbles 6 to 10
      value = value / pow(10, readNybble(s)); // nybble 11 is decimal point position;
      if (sign == 8) value = -value;
      byte units = readNybble(s); // nybble 12
      ...

Thank you Lastchancename for advise and especially PaulRB for the code lines. Although they look simple but they still managed to heat up my brain considerably.
So here is my try to put everything together. While compiling don't bring errors but i struggle to print the data. If I print "value" I get 0.00, if I print "unit" I get 255 mostly, with some 15 in-between.

#include <Arduino.h>

int req[] = {
  25, 31, 37, 43  //Arduino pin for REQ line, drives transistor to ground SPC port pin 25, 31, 37, 43 (~REQ)
};
int dat[] = {
  22, 28, 34, 40  //Arduino pin for Data line, connects directly to SPC port pin 22, 28, 34, 40 (DATA)
};
int clk[] = {
  23, 29, 35, 41  //Arduino pin for Clock line, connects directly to SPC port pin 23, 29, 35, 41 (~CLK)
};

int i = 0;
int j = 0;
int k = 0;

const byte errorCode = 255;
const unsigned long timeout = 1000;

// Mitutoyo SPC data port transmits 13 4-bit values
// Byte Meaning
// 0    Header, always 0xF
// 1    Header, always 0xF
// 2    Header, always 0xF
// 3    Header, always 0xF
// 4    Sign. Zero is positive. 0x8 is negative.
// 5    Most significant digit (MSD) of measurement
// 6    Next digit of measurement
// 7    Next digit of measurement
// 8    Next digit of measurement
// 9    Next digit of measurement
// 10   Least significant digit (LSD) of measurement
// 11   Digit to place decimal point
// 12   Unit. Zero is mm. Nonzero (b1000 is 1?) is inch

void setup() {
  Serial.begin(9600);
  for (int s = 0; s < 4; s++) {  // Repeats for all 4 dial sensors
    pinMode(req[s], OUTPUT);
    pinMode(clk[s], INPUT_PULLUP);
    pinMode(dat[s], INPUT_PULLUP);
    digitalWrite(req[s], LOW);  // set request at high
  }
}

void loop() {
  for (byte s = 0; s < 4; s++) {  // repeat for each sensor

    digitalWrite(req[s], HIGH);                                           // generate set request
    if (readNybble(s) != errorCode) {                                     // check nybble 0 is received ok
      for (byte n = 1; n <= 3; n++) readNybble(s);                        // discard nybbles 1 to 3
      byte sign = readNybble(s);                                          // nybble 4
      float value = readNybble(s);                                        // nybble 5 is first value digit
      for (byte n = 6; n <= 10; n++) value = value * 10 + readNybble(s);  // read value digits from nybbles 6 to 10
      value = value / pow(10, readNybble(s));                             // nybble 11 is decimal point position;
      if (sign == 8) value = -value;
      byte units = readNybble(s);  // nybble 12
      //Serial.println(value);
      Serial.println(units);
      //Serial.print(", ");
      //Serial.println();
      digitalWrite(req[s], LOW);
    }
    
  }
  
}

byte readNybble(byte s) {
  byte k = 0;
  unsigned long time;

  // Timing diagram indicates data bit has been valid for about 120
  // microseconds before the clock signal is raised, and remains
  // valid for about 120 microseconds afterwards. This code reads data
  // bit at the falling clock edge.
  for (byte j = 0; j < 4; j++) {
    time = micros();
    while (digitalRead(clk[s]) == LOW and micros() - time < timeout)
      ;  // hold until clock is high
    if (digitalRead(clk[s]) == LOW) return errorCode;
    time = micros();
    while (digitalRead(clk[s]) == HIGH and micros() - time < timeout)
      ;  // hold until clock is low
    if (digitalRead(clk[s]) == HIGH) return errorCode;
    bitWrite(k, j, (digitalRead(dat) & 0x1));
  }

  return k;
}

I found yet another similar case with reading Mitutoyo instrument, which might be useful. This is designed for sending data to excel with press of the button and has some code lines to track the presence of communication with instrument (if I understood this correctly):
Mitutoyo SPC - Input measurements to excel
And here is the code:

// ******************************************************************************
// Mitutoyo SPC.ino
// https://btbm.ch/mitutoyo-spc/
// 2022 Grease monkey - No rights reserved
// !!! Don't laugh at the code. I change engine oils for a living !!!
// ******************************************************************************

#include <Keyboard.h>

#define DATA1 16
#define CK    14
#define BUTTON 15
#define REQ   10       


// Global variables
byte decimal_places = 0;

void setup() {
  Serial.begin(115200);
  pinMode(DATA1, INPUT);
  pinMode(CK, INPUT);
  pinMode(BUTTON, INPUT);
  pinMode(REQ, OUTPUT);
  Keyboard.begin();
}

void loop(){
  // Detect button pressed and send to keyboard
  if (!digitalRead(BUTTON)){
    send_to_keyboard(read_SPC());
    delay(500);
  }

  // Send value to serial port if any character is received
  if (Serial.available()) {
    Serial.println(read_SPC(), decimal_places);
    // Empty the Serial input buffer
    while (Serial.available()) byte tmp = Serial.read();
  }
}

float read_SPC() {
  byte spc_data[13]; // Data received from instrument
  
  // Request measurement
  digitalWrite(REQ, HIGH);
  
  // Read 14 digits
  for(byte digit = 0; digit < 13; digit++ ) {   
    // Read one digit
    byte tmp_byte = 0; // Temporary storage of bits
    int watchdog = 0; // Watchdog in case the clock signal does not change
    for (byte bit_no = 0; bit_no < 4; bit_no++) {
      // Wait until the Clock signal is HIGH
      while( digitalRead(CK) == LOW) {
        watchdog++;
        if (watchdog > 3200) break;
      }
      watchdog = 0;
      while( digitalRead(CK) == HIGH) {
        watchdog++;
        if (watchdog > 3200) break;
      }
      // Reset the watchdog
      watchdog = 0;
      // Write the bit to tmp_byte
      bitWrite(tmp_byte, bit_no, (digitalRead(DATA1) & 0x1));
    }
    
    // return the REQ signal to High level before the last CK signal (at the 52nd bit) is outputted
    if (digit == 0) digitalWrite(REQ,LOW);
    
    // Store the digit to the data array
    spc_data[digit] = tmp_byte;
  }
  
  // Create a number from the SPC digits
  long tmp_value = 0;
  for( byte n = 5; n <= 10; n++) {
    tmp_value *= 10;
    tmp_value += spc_data[n];
  }
  // If the sign is negative (the fifth digit is the sign)
  if (spc_data[4] == 0x8) tmp_value *= -1;
  
  // Adjust number for decimal point position
  decimal_places = spc_data[11];
  
  return (float)tmp_value / pow(10, decimal_places); 
}


void send_to_keyboard(float value){    
  Keyboard.print(value, decimal_places);
  // Press RETURN
  Keyboard.press(176);
  delay(30);
  Keyboard.release(176);
}

Neither @lastchancename or I responded to this post because you didn't reply directly to either of our posts or put "@" in front of our names, so the forum did not notify us. I only spotted your reply by chance.

Getting 255 for unit means that the readNybble() function timed out waiting for clock edges from the indicator. But in order for that part of the code to run, it must have received the first value successfully!

Let's add some debugging code to the function:

byte readNybble(byte s) {
  byte k = 0;
  unsigned long time;

  // Timing diagram indicates data bit has been valid for about 120
  // microseconds before the clock signal is raised, and remains
  // valid for about 120 microseconds afterwards. This code reads data
  // bit at the falling clock edge.
  for (byte j = 0; j < 4; j++) {
    time = micros();
    while (digitalRead(clk[s]) == LOW and micros() - time < timeout)
      ;  // hold until clock is high
    if (digitalRead(clk[s]) == LOW) {
      Serial.print("Error reading value from indicator ");
      Serial.println(s);
      return errorCode;
    }
    time = micros();
    while (digitalRead(clk[s]) == HIGH and micros() - time < timeout)
      ;  // hold until clock is low
    if (digitalRead(clk[s]) == HIGH) {
      Serial.print("Error reading value from indicator ");
      Serial.println(s);
      return errorCode;
    }
    bitWrite(k, j, (digitalRead(dat) & 0x1));
  }
  Serial.print("Read value ");
  Serial.print(k);
  Serial.print(" from indicator ");
  Serial.println(s);
  return k;
}

Oh I see, sorry @PaulRB, @Railroader and @lastchancename for not replaying correctly so you have missed my messages. And i was wondering why...
I have implemented the debugging code by @PaulRB. The messages of error is more random and doesn't seem to have some pattern to me. Here is the example:

Read value 15 from indicator 3
Read value 15 from indicator 3
Read value 15 from indicator 3
Read value 15 from indicator 3
Read value 15 from indicator 3
Read value 15 from indicator 3
Read value 15 from indicator 0
Error reading value from indicator 0
Read value 15 from indicator 0
Read value 15 from indicator 0
Error reading value from indicator 0
Read value 15 from indicator 0
Error reading value from indicator 0
Read value 15 from indicator 0
Read value 15 from indicator 0
Read value 15 from indicator 0
Read value 15 from indicator 0
Read value 15 from indicator 0
Read value 15 from indicator 0
Error reading value from indicator 1
Read value 15 from indicator 2
Error reading value from indicator 2
Read value 15 from indicator 2
Read value 15 from indicator 2
Read value 15 from indicator 2
Read value 15 from indicator 2
Read value 15 from indicator 2
Read value 15 from indicator 2
Error reading value from indicator 2
Read value 15 from indicator 2
Error reading value from indicator 2
Read value 15 from indicator 2
Read value 15 from indicator 2
Error reading value from indicator 3
Read value 15 from indicator 0
Error reading value from indicator 0
Read value 15 from indicator 0
Error reading value from indicator 0
Read value 15 from indicator 0
Read value 15 from indicator 0
Read value 15 from indicator 0

The positive thing is that when I disconnect sensor Serial flow doesn't stop.

Your first link calls for me being cookied. Request denied.
Plowing through entire projects calls for more then is available. It's better to extract the points You want to refer to.

Good luck.

Ok, I see, thank you!
But to use out the opportunity I'd like to kindly point out that in post #4 I did include another code as you have requested. Sorry again for disordering communication routine.

Present the code from the other institution. The available equipment here won't handle it.

Yes You did. Just more to analyze then I want take on. Making one unit work would be natural, not pushing for multiple.

This looks really bad. There are errors showing against all 4 sensors, and every nybble is read as 15 (= 1111 in binary).

I can't understand how it is working at all, having seen the output in post #10. Maybe there is a problem with my suggested changes to the code, but I don't know what.

Do you have access to an oscilloscope or logic analyser to take a closer look at the signals coming from the indicators?

Can you post links to the specs of the indicators that describes the digital outputs, and post a schematic showing how everything is connected between the Arduino and the indicators?

Do you think using e.g. four Arduino nano units streaming serial to Uno would be better solution?

Despite that it sounds strange, but the code from my first post works. I have made hybrid version of it and your code. I added also control of period that data should be red. However data reading still stops if one of sensors are disconnected:


```cpp
#include <Arduino.h>

int req[] = {
  25, 31, 37, 43  //Arduino pin for REQ line, drives transistor to ground SPC port pin 25, 31, 37, 43 (~REQ)
};
int dat[] = {
  22, 28, 34, 40  //Arduino pin for Data line, connects directly to SPC port pin 22, 28, 34, 40 (DATA)
};
int clk[] = {
  23, 29, 35, 41  //Arduino pin for Clock line, connects directly to SPC port pin 23, 29, 35, 41 (~CLK)
};


int i = 0;
int j = 0;
int k = 0;
int signCh = 8;
int sign;
int decimal;
float dpp;
int units;

byte mydata[14];
String value_str;
long value_int;  //was an int, could not measure over 32mm
float value;

unsigned long time;
const unsigned long timeout = 50000;

unsigned long dataPeriod = 0.5;  // time step in seconds to send measurements to serial port for excel
unsigned long duration = 0;



void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  for (int s = 0; s < 4; s++) {
    pinMode(req[s], OUTPUT);
    pinMode(clk[s], INPUT_PULLUP);
    pinMode(dat[s], INPUT_PULLUP);
    digitalWrite(req[s], LOW);  // set request at high
  }
}

void loop() {
  // put your main code here, to run repeatedly:

  if (millis() - duration >= (dataPeriod * 1000)) {
    for (byte s = 0; s < 4; s++) {
      digitalWrite(req[s], HIGH);  // generate set request
      for (i = 0; i < 13; i++) {
        time = micros();
        k = 0;
        for (j = 0; j < 4; j++) {
          while (digitalRead(clk[s]) == LOW) {
          }  // hold until clock is high
          //if (digitalRead(clk[s]) == LOW) bitWrite(k, j, 0 & 0x1);
          time = micros();
          while (digitalRead(clk[s]) == HIGH) {
          }  // hold until clock is low
          bitWrite(k, j, (digitalRead(dat[s]) & 0x1));
        }
        mydata[i] = k;
      }
      sign = mydata[4];
      value_str = String(mydata[5]) + String(mydata[6]) + String(mydata[7]) + String(mydata[8] + String(mydata[9] + String(mydata[10])));
      decimal = mydata[11];
      units = mydata[12];

      value_int = value_str.toInt();
      if (decimal == 0) dpp = 1.0;
      if (decimal == 1) dpp = 10.0;
      if (decimal == 2) dpp = 100.0;
      if (decimal == 3) dpp = 1000.0;
      if (decimal == 4) dpp = 10000.0;
      if (decimal == 5) dpp = 100000.0;

      value = value_int / dpp;

      if (sign == 0) {
        Serial.print(value, decimal);
        // Serial.print(",");
      }
      if (sign == 8) {
        Serial.print("-");
        Serial.print(value, decimal);
        // Serial.print(",");
      }
      digitalWrite(req[s], LOW);
      if (s < 3) {
        Serial.print(",");
      }
      //delay(1000);
    }
    Serial.println();
    duration = millis();
  }
}

Unfortunately I don't have access to oscilloscope and my skills in this area are not great either. The units I am using are not new and I couldn't find data sheet for this model. The only diagram that might be useful is in post by Roger Cheng (see my post #1), where you could find timings of data signal. I only can assume that this should be valid in my case as well.
What idea comes to my mind, that connectivity check in code influences data reading somehow, and if i comment that part out, the data reading from your code still brings only value of 15 no mater what sensors are measuring.

The while costruction makes it hang. Build in a time out for the waiting.

What did you add? I don't see anything that would prevent it hanging. For example these lines will make it hang of an indicator is off line:

          while (digitalRead(clk[s]) == LOW) {
          }  // hold until clock is high

The code I suggested has this:

    time = micros();
    while (digitalRead(clk[s]) == LOW and micros() - time < timeout)
      ;  // hold until clock is high
    if (digitalRead(clk[s]) == LOW) {
      Serial.print("Error reading value from indicator ");
      Serial.println(s);
      ....

So if the clock line from the indicator is still low after timeout microseconds, the while-loop stops looping. The code then checks to see if the clock line is still low, to know if the while-loop ended because of the timeout versus the clock line went high.

Thank you for your respond.
Yes, you are right. In this code at post #16 I did not add timeout check and error code message. I added the data read out frequency which I called "dataPeriod" so in this way one can set how often serial will send data to excel (e.g. every 30 seconds).

But, most importantly, I intended to show that the data read out from sensors works if code has lines taken from Instructables (see my code at post #1) but for some reason it fails to read data if I use shorter way with function you wrote and this I can't understand why.

The other point I noticed that timeout for reading clk status should be set around 50000 micros for data to be able to be red from sensors. However, again, even with longer timeout still your function won't read data, but longer code from Instructables will work.

Here is code with timeout and Error message implemented as you suggested. In here the check if sensor is connected works, however when one sensor is switched off it prints out ~50 messages "Error reading value...". I assume it's related to micros Timeout which I set to 50000. But here we fall into circle, since measured values wont be red properly if Timeout is too short.

#include <Arduino.h>

int req[] = {
  25, 31, 37, 43  //Arduino pin for REQ line, drives transistor to ground SPC port pin 25, 31, 37, 43 (~REQ)
};
int dat[] = {
  22, 28, 34, 40  //Arduino pin for Data line, connects directly to SPC port pin 22, 28, 34, 40 (DATA)
};
int clk[] = {
  23, 29, 35, 41  //Arduino pin for Clock line, connects directly to SPC port pin 23, 29, 35, 41 (~CLK)
};


int i = 0;
int j = 0;
int k = 0;
int signCh = 8;
int sign;
int decimal;
float dpp;
int units;

byte mydata[14];
String value_str;
long value_int;  //was an int, could not measure over 32mm
float value;

int flag = 0;
int flagMax = 600000;

unsigned long time;
const unsigned long timeout = 50000;

unsigned long dataPeriod = 1;  // time step in seconds to send measurements to serial port for excel
unsigned long duration = 0;



void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  for (int s = 0; s < 4; s++) {
    pinMode(req[s], OUTPUT);
    pinMode(clk[s], INPUT_PULLUP);
    pinMode(dat[s], INPUT_PULLUP);
    digitalWrite(req[s], LOW);  // set request at high
  }
}

void loop() {
  // put your main code here, to run repeatedly:

  if ((millis() - duration) >= (dataPeriod * 1000)) {
    for (byte s = 0; s < 4; s++) {
      digitalWrite(req[s], HIGH);  // generate set request
      for (i = 0; i < 13; i++) {
        time = micros();
        k = 0;
        for (j = 0; j < 4; j++) {
          while (digitalRead(clk[s]) == LOW and micros() - time < timeout) {

          }  // hold until clock is high
          if (digitalRead(clk[s]) == LOW) {
            Serial.print("Error reading value from indicator ");
            Serial.println(s);
            break;
          };
          time = micros();
          flag = 0;
          while (digitalRead(clk[s]) == HIGH and micros() - time < timeout) {

          }  // hold until clock is low
          if (digitalRead(clk[s]) == HIGH) {
            Serial.print("Error reading value from indicator ");
            Serial.println(s);
          }
          bitWrite(k, j, (digitalRead(dat[s]) & 0x1));
        }
        mydata[i] = k;
      }
      sign = mydata[4];
      value_str = String(mydata[5]) + String(mydata[6]) + String(mydata[7]) + String(mydata[8] + String(mydata[9] + String(mydata[10])));
      decimal = mydata[11];
      units = mydata[12];

      value_int = value_str.toInt();
      if (decimal == 0) dpp = 1.0;
      if (decimal == 1) dpp = 10.0;
      if (decimal == 2) dpp = 100.0;
      if (decimal == 3) dpp = 1000.0;
      if (decimal == 4) dpp = 10000.0;
      if (decimal == 5) dpp = 100000.0;

      value = value_int / dpp;

      if (sign == 0) {
        Serial.print(value, decimal);
        // Serial.print(",");
      }
      if (sign == 8) {
        Serial.print("-");
        Serial.print(value, decimal);
        // Serial.print(",");
      }
      digitalWrite(req[s], LOW);
      if (s < 3) {
        Serial.print(",");
      }
      //delay(1000);
    }
    Serial.println();
    duration = millis();
  }
}

However, here is an other code where I used ideas from our discussion and also from code in post #8 (where author uses check called "watchdog" together with break command).
Here the sensor connection check is implemented but no error message is sent, comma is printed instead. I use break here to exit of while loop. For duration to check if sensor is connected I use millis, and by experimenting I found that 350 milliseconds works best to check sensor connection. However, these 350 millis adds up to data read out period. This means that if data is red every 1 second, in case of sensor error, it becomes 1.35s and this will sum up for longer period of time.
I have tried to count loop with "flag", but somehow waiting time for sensor check becomes ~1-2 seconds.
The code is not that elegant...

#include <Arduino.h>

int req[] = {
  25, 31, 37, 43  //Arduino pin for REQ line, drives transistor to ground SPC port pin 25, 31, 37, 43 (~REQ)
};
int dat[] = {
  22, 28, 34, 40  //Arduino pin for Data line, connects directly to SPC port pin 22, 28, 34, 40 (DATA)
};
int clk[] = {
  23, 29, 35, 41  //Arduino pin for Clock line, connects directly to SPC port pin 23, 29, 35, 41 (~CLK)
};


int i = 0;
int j = 0;
int k = 0;
int signCh = 8;
int sign;
int decimal;
float dpp;
int units;

byte mydata[14];
String value_str;
long value_int;  //was an int, could not measure over 32mm
float value;

int flag = 0;
int flagMax = 600000;

unsigned long time;
const unsigned long timeout = 5000;

unsigned long dataPeriod = 1;  // time step in seconds to send measurements to serial port for excel
unsigned long duration = 0;



void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  for (int s = 0; s < 4; s++) {
    pinMode(req[s], OUTPUT);
    pinMode(clk[s], INPUT_PULLUP);
    pinMode(dat[s], INPUT_PULLUP);
    digitalWrite(req[s], LOW);  // set request at high
  }
}

void loop() {
  // put your main code here, to run repeatedly:

  if ((millis() - duration) >= (dataPeriod * 1000)) {
    for (byte s = 0; s < 4; s++) {
      digitalWrite(req[s], HIGH);  // generate set request
      for (i = 0; i < 13; i++) {
        time = micros();
        k = 0;
        for (j = 0; j < 4; j++) {
          while (digitalRead(clk[s]) == LOW) {
            //flag++;
            //if (flag>flagMax) break;
            if ((millis() - duration) >= ((dataPeriod + 0.35) * 1000)) break;
          }  // hold until clock is high

          time = micros();
          flag = 0;
          while (digitalRead(clk[s]) == HIGH) {
            //flag++;
            //if (flag>flagMax) break;
            if ((millis() - duration) >= ((dataPeriod + 0.35) * 1000)) break;
          }  // hold until clock is low
          bitWrite(k, j, (digitalRead(dat[s]) & 0x1));
        }
        mydata[i] = k;
      }
      sign = mydata[4];
      value_str = String(mydata[5]) + String(mydata[6]) + String(mydata[7]) + String(mydata[8] + String(mydata[9] + String(mydata[10])));
      decimal = mydata[11];
      units = mydata[12];

      value_int = value_str.toInt();
      if (decimal == 0) dpp = 1.0;
      if (decimal == 1) dpp = 10.0;
      if (decimal == 2) dpp = 100.0;
      if (decimal == 3) dpp = 1000.0;
      if (decimal == 4) dpp = 10000.0;
      if (decimal == 5) dpp = 100000.0;

      value = value_int / dpp;

      if (sign == 0) {
        Serial.print(value, decimal);
        // Serial.print(",");
      }
      if (sign == 8) {
        Serial.print("-");
        Serial.print(value, decimal);
        // Serial.print(",");
      }
      digitalWrite(req[s], LOW);
      if (s < 3) {
        Serial.print(",");
      }
      //delay(1000);
    }
    Serial.println();
    duration = millis();
  }
}