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);
}
/*************************************************************************************/