Roger-random / mitutoyo

Translate Mitutoyo Digitmatic SPC data to USB serial via Arduino
MIT License
17 stars 7 forks source link

Additional garbage data when adding delay or display #2

Closed manj9501 closed 2 years ago

manj9501 commented 2 years ago

Hi @Roger-random

My setup consists of a Mitutoyo Height Gauge 570-246 connected to an Arduino UNO.

Can you please me please help me a bit with explaining how adding additional lines of code say, for instance, for adding a display or adding a delay would affect the execution of this program? Asking this since I've connected an I2C SSD1306 OLED display and when I connected the display, I start getting garbage values, along with the correct values, on both the display and the serial. Noteworthy thing is that if I don't have the OLED connected, the code works just fine and outputs correct values to serial monitor all the time.

Code:

// Derived from https://www.instructables.com/id/Interfacing-a-Digital-Micrometer-to-a-Microcontrol/
#include <SPI.h>
#include <Wire.h>

#include <Arduino.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels

// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
// The pins for I2C are defined by the Wire-library. 
// On an arduino UNO:       A4(SDA), A5(SCL)
// On an arduino MEGA 2560: 20(SDA), 21(SCL)
// On an arduino LEONARDO:   2(SDA),  3(SCL), ...
#define OLED_RESET     4 // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

// 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

  // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
  if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
    Serial.println(F("SSD1306 allocation failed"));
    for(;;); // Don't proceed, loop forever
  }
  // Clear the buffer
  display.clearDisplay();
  display.setTextSize(3); // Draw 2X-scale text
  display.setTextColor(SSD1306_WHITE);

}

void loop() {
  getData();
#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.
    display.clearDisplay();
    display.setCursor(0, 0);
    Serial.print(value,decimal);
    display.println(value,decimal);
    // Append units for value
    if (spcdata[12] == 0) {
        Serial.print(" mm");
        display.print(" mm");        
    } else {
        Serial.print(" in");
        display.print(" in");
    }
       display.display();      // Show initial text 
#endif // HUMAN_READABLE_OUTPUT

    Serial.println();

}

void getData(void){
    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;
    }
}

Normal serial output (no display in the circuit):

0.08 mm 0.46 mm 3.19 mm 4.91 mm 7.17 mm 9.45 mm 10.97 mm 12.58 mm 16.28 mm 19.96 mm 22.30 mm 22.80 mm

Abnormal serial output (display connected in the circuit):

0.00 mm 1366530 mm 0.000000000000414 in 0.000000000080 in 0.00 mm 1366530 mm 0.05 mm 1366530 mm 0.000000000010414 in 0.00000821 in 0.05 mm

manj9501 commented 2 years ago

Here's the output with both Hex and human-readable format enabled:

4000800A8FFF7 -0.000000000010965 in 300499105CFFF 0.000000000910637 in F300499105CFF 0.000000000991064 in FF300499105CF 0.000000499105 in FFFF000566041 0.5660 in 330A8FFF70082 -0.01665701 in 00566041FFFF0 0.000000000042665 mm 000566041FFFF 0.000000000604266 in F000566041FFF 0.000000000660426 in FFFF000566541 0.5665 in 3B2A8FFF70082 -0.01665701 in 00566541FFFF0 0.000000000542666 mm 000566541FFFF 0.000000000654266 in F000566541FFF 0.000000000665426 in FF000566541FF 0.000000000566542 in FFF000566541F 5665.4 in FFFF000566541 0.5665 in 9515CFFF30049 166.5300 in

I can notice the bits getting shifted by themselves when the OLED is connected, can you please help me make more sense of this?

Roger-random commented 2 years ago

Can you please me please help me a bit with explaining how adding additional lines of code say, for instance, for adding a display or adding a delay would affect the execution of this program?

The getData() method was written in a straightforward way. This is easy to write, to debug, and to understand, but is not robust against interruptions. This is very common in Arduino code people share online, including this project.

Adding Arduino libraries with background processing will cause interruptions and corrupt getData(). For example, Arduino's standard PWM functionality (analogWrite()) adds a hardware timer interrupt. I am not familiar enough with the Adafruit display library you have added, but this is the likely explanation based on output data you provided.

It is possible to rewrite getData() to be robust against such things, but such complexity was out of scope for my toy project.

This is only a guess based on limited data, the real problem may be something else.

no display in the circuit / display connected in the circuit

Are you saying the data is corrupted if the display is electrically connected without software changes to communicate with that display? (Without Adafruit library.) If so, that hints at a power issue. Please use a voltmeter to verify all the components are receiving the power they need. Use a resistance meter to verify the 1k resistor is as expected, if the resistance is too low that pin may be drawing excessive power. The Mitutoyo indicator may need fresh batteries, etc.

manj9501 commented 2 years ago

Adding Arduino libraries with background processing will cause interruptions and corrupt getData().

Appreciate your two cents on this! I was also thinking on similar lines, while analyzing the phenomenon.

Are you saying the data is corrupted if the display is electrically connected without software changes to communicate with that display? (Without Adafruit library.)

The code on the Arduino UNO is the same that I pasted in my initial comment, compiled with Adafruit library included. I get correct output on the serial when the OLED is not electrically connected in the circuit, but the moment I get connect it in the circuit, the corrupt data starts appearing on the serial monitor as well as the OLED. There's one more thing I noticed: even when the OLED is connected in the circuit, the corrupt data stops appearing when I am moving the height gauge. The moment I bring it to a stationary position, the corruption starts again.

Also I'll do the power checks at my end as well just to make sure.

Roger-random commented 2 years ago

Any more information to help diagnose this problem?

Roger-random commented 2 years ago

Closing due to insufficient information. Please feel free to reopen if more data for diagnostics become available.