Benutzer-Werkzeuge

Webseiten-Werkzeuge


0x0017

Dies ist eine alte Version des Dokuments!


Messuhren mit dem PC auslesen

Abbildung 1: Digital-Messuhr (12,7 mm)
Absolute-System mit RB6-Datenausgang.
(Genauigkeit 2 um, Ablesung 0,001 mm).

Digitale Messuhren ( Messschieber, Dickenmessgeräte …) haben häufig eine Datenschnittstelle. Diese Datenschnittstelle wird entweder an extra dafür vorgesehene externe Hardware angeschlossen, um Messwerte auszulesen oder mit einem Datenkabel und entsprechender Software (meist nur für Windows verfügbar) mit dem PC verbunden. Diese Lösungen sind einerseits recht teuer (80€-300€), und anderseits für die eigentliche Weiterverarbeitung äußerst unpraktisch, so dass sich meist der Kauf dieser Extrakomponenten für Hobbyisten nicht lohnt. Hier zeigen wir, wie Hardwarekomponenten, die nur wenige Euro kosten, mit Messuhren, die über eine serielle Schnittstelle (Digimatic Protokoll) verfügen, kommunizieren können. Praktisch setzen wir das in unserer CNC-Fräse ein, die mit LINUXCNC gesteuert wird. Es wären aber auch Roboterprojekte denkbar, in denen mit wenig Aufwand genaue Messungen durchgeführt werden könnten.

Übersicht

Abbildung 2: Der fertige Digimatic-Serial-Umwandler. Diese Ausführung ist für den täglichen Einsatz. Der verbaute Arduino Nano (Klon) verwendet Steckleisten, so dass ein Austausch (falls er doch einmal defekt sein sollte) schnell erfolgen kann.

Die wesentliche Hardware ist eine Messuhr (mit RB6-Schnittstelle), ein Arduino und ein Datenkabel. Es werden zwei Programme entwickelt. Das erste Programm zeigt, wie man grundsätzlich das Digimatic-Protokoll mit dem Arduino ausliest. Das zweite Programm demonstriert, wie man darauf aufbauend eigene Messsoftware entwickeln kann, wobei der Arduino dann als Elektronikkomponente zwischen der Messuhr und dem PC vermittelt.

Abbildung 3: Messprinzip: Eine Messuhr wird über ein Kabel mit dem Messadapter verbunden. Der Messadapter besteht im Wesentlichen aus einem Arduino, der wiederum für die Kommunikation mit dem PC sorgt.

Hardware

Abbildung 4: Für die Kommunikation werden vier Leitungen: DATA, CLK, REQ und GND verwendet. Da der RB6-Datenausgang fünf Leitungen hat, ist eine Leitung unbenutzt.

Software

Details

Hardware

Schaltplan

Abbildung 5: Schaltplan Der Emitter des Transistors geht auf GND und der Kollektor wird mit PIN 5 des Wannensteckers verbunden. Der REQ PIN 5 des Arduino wird dann über den Widerstand mit der Basis des Transistors verbunden. Alle anderen Leitungen können direkt verbunden werden.

Arduino Prototyp Entwicklung

Abbildung 6: Arduino Prototypentwicklung Ein Steckbrett und ein Arduino ist Ausgang für erste Versuche, die Messuhr anzusprechen. Da Messuhren teuer sind, sollte doppelt geprüft werden, ob Wannenstecker-Pins, Transistor und Steckbrücken richtig miteinander verbunden sind.

Arduino mit Messadapter

Abbildung 7: Messadapter Ein Messadapter vereinfacht die Prototypentwicklung und kann auch schon für erste Messungen in der Praxis herhalten. Um den Adapter benutzen zu können, werden noch vier Steckbrückenkabel und ein Arduino benötigt.

Fertiges Elektronikgehäuse

Abbildung 8: Elektronikgehäuse Für den Dauereinsatz ist ein Elektronikgehäuse am Besten geeignet. Wer keine Fräse hat, um die Ausschnitte für USB und Wannenstecker zu fertigen, hat nur die Möglichkeit, den klassischen Weg (Bohrer und Säge) zu beschreiten.

Software

Digimatic-Protokoll

Arduino Testprogramm

Das folgende Programm (Arduino Sketch) läuft auf dem Arduino, der wiederum mit der Messuhr verbunden ist. Über das USB-Kabel des Arduinos werden die gelesenen Messwerte der Uhr vom Arduino auf den PC übertragen und über den integrierten seriellen Monitor der Arduino-IDE ausgegeben.

| minimal solution - reading gauge indicator
/* minimal solution - reading gauge indicator (here a device from Absolute-System)
   @author torsten roehl
   @see this code is based on https://www.instructables.com/id/Interfacing-a-Digital-Micrometer-to-a-Microcontrol/
   (there is a little big in the code from instructables - only ocurs for high inch values! )
*/
 
 
/*  ADJUST AREA START  */
 
/* pin configuration */
int pinREQ = 5;
int pinDATA = 2;
int pinCLK = 3;
 
/* pooling intervall */
int sleep = 100;
/* baudrate */
int bd = 9600;
 
/*  ADJUST AREA END  */
 
/* Keep FIXED! - data protocol format */
byte digits[13];
float value;
 
void setup() {
 
  /*    SERIAL_8N1 (the default)  */
  Serial.begin(bd);
 
  pinMode(pinREQ, OUTPUT);
  pinMode(pinCLK, INPUT_PULLUP);
  pinMode(pinDATA, INPUT_PULLUP);
  digitalWrite(pinREQ, LOW); // set request at high
 
}
 
void loop() {
 
  readDialGauge();
  decodeDialGauge();
  serialInfoMonitor();
 
  delay(sleep);
}
 
 
/*
   Read the dial gauge.
   After a call to this function the array digits[13] contains all data according to the data protocol.
   For normal data reading the first four digits should by all 'F', the function returns false if this is not the case.
 
 
 
*/
boolean readDialGauge() {
 
  /**
     Generate request by making REQ active (low level)!
  */
  digitalWrite(pinREQ, HIGH); // generate set request
 
  /* read 13 nibble (52 bit) */
  for (int i = 0; i < 13; i++ ) {
    int digit = 0;
    for (int j = 0; j < 4; j++) {
 
      while ( digitalRead(pinCLK) == LOW) {}/* Wait for CLOCK=HIGH */
      while ( digitalRead(pinCLK) == HIGH) {} /* Wait for CLOCK=LOW  */
 
      bitWrite(digit, j /*pos*/, (digitalRead(pinDATA) & 0x1));
    }
 
    digits[i] = digit;
  }
 
  digitalWrite(pinREQ, LOW);
 
 
  /* d1-d4 should be all oxF */
  int header = digits[0] + digits[1] + digits[2] + digits[3];
  return header == 60 ? true : false;
}
 
 
/*
   decodeDialGauge (according to the digomatic-protocol)
   Note: A call to readDialGauge is necessary to use this function!
 
   i) value - contains the (signed) value read out from the gauge indicator
   ii) units - if units is equal to 0 then mm is used, otherwise (1) inch is in use
   iii) decimal - the number of decimal. mm corresponds to 3 decimal numbers, whereas inch has five decimal numbers.
 
   return (signed) measured value
*/
 
void decodeDialGauge() {
 
  int units = digits[12];    // 0=mm or 1=inch
  int decimal = digits[11];  // 3 for mm and 5 for inch
 
  value = (float)digits[5] * 10000 + (float)digits[6] * 10000 + (float) digits[7] * 1000 + (float)digits[8] * 100 + (float)digits[9] * 10 + (float) digits[10];
  /* assuming digits[11] is allways 3 */
  value /= 1000.0;
 
  if (units == 1 )
    value /= 100.0;
 
  if (digits[4] == 8)
    value *= -1.0;
}
 
/*
   Helper function: This function decodes and writes the data from the dial gauge to the arduino serial monitor for debugging purposes.
   Note: A call to readDialGauge and decodeGauge is necessary to use this  function!
 
*/
void serialInfoMonitor() {
 
  int units = digits[12];// 0=mm or 1=inch
  int decimal = digits[11];// 3 for mm and 5 for inch
 
  String info = " (mm)";
  if (units == 1 )
    info = " (inch)";
 
  Serial.print(value, decimal );
  Serial.println(info);
 
}

Messsoftware

| Arduino
/* minimal solution - reading gauge indicator (here a device from Absolute-System)
   @author torsten roehl   
*/
 
 
/*  ADJUST AREA START  */
/* pin configuration */
int pinREQ = 5;
int pinDATA = 2;
int pinCLK = 3;
 
/* baudrate */
int bd = 9600;
/*  ADJUST AREA END  */
 
/* Keep FIXED! - data protocol format */
byte digits[13];
 
/* serial communication protocol */
byte cmd;
const byte PROTOCOL_REQ =  55;
 
 
void setup() {
 
  /*    SERIAL_8N1 (the default)  */
  Serial.begin(bd);
 
  pinMode(pinREQ, OUTPUT);
  pinMode(pinCLK, INPUT_PULLUP);
  pinMode(pinDATA, INPUT_PULLUP);
  digitalWrite(pinREQ, LOW); // set request at high
 
}
 
void loop() {
 
 
  if (Serial.available() > 0) {
 
    cmd =  Serial.read();
 
    switch (cmd) {
 
      case PROTOCOL_REQ:
 
        readDialGauge();
        send();
        break;
 
    }
 
  }
 
}
 
 
/*
   Read the dial gauge.
   After a call to this function the array digits[13] contains all data according to the data protocol.
   For normal data reading the first four digits should be all 'F', the function returns false, if this is not the case.
*/
 
boolean readDialGauge() {
 
  /**
     Generate request by making REQ active (low level)!
  */
  digitalWrite(pinREQ, HIGH); // generate set request
 
  /* read 13 nibble (52 bit) */
  for (int i = 0; i < 13; i++ ) {
    int digit = 0;
    for (int j = 0; j < 4; j++) {
 
      while ( digitalRead(pinCLK) == LOW) {}/* Wait for CLOCK=HIGH */
      while ( digitalRead(pinCLK) == HIGH) {} /* Wait for CLOCK=LOW  */
 
      bitWrite(digit, j /*pos*/, (digitalRead(pinDATA) & 0x1));
    }
 
    digits[i] = digit;
  }
 
  digitalWrite(pinREQ, LOW);
 
 
  /* d1-d4 should be all oxF */
  int header = digits[0] + digits[1] + digits[2] + digits[3];
  return header == 60 ? true : false;
}
 
 
/*
   This function sends 13 bytes via the serial interface.
   The method sends the whole digimatic protocol for client side processing purposes.
*/
void send() {
 
  for (int i = 0; i < 13; i++ ) {
    Serial.write(digits[i] );
  }
 
}

Java Beispiel

Um mit Java auf den Arduino über die serielle Schnittstelle zugreifen zu können, verwenden wir die jSSC (Java Simple Serial Connector) API. Sie besteht aus nur einer jar-Datei und wird auch von der Arduino-IDE selbst verwendet. In Eclipse wird sie über Eigenschaften (Java Build Path - Add External JAR's…) dem Projekt hinzugefügt.

Programmstruktur

  • main-Methode: In der main-Methode kann optional die Geräteschnittstelle mit angegeben werden. Als Standard ist /dev/ttyUSB0 gesetzt. Das Programm wird abbrechen, wenn nicht die richtige Schnittstelle ausgewählt wurde. Bei Linux ist es häufig so, dass wiederholtes ein- und abstecken der USB-Verbindung des Arduinos die Schnittstellen /dev/ttyUSB0,/dev/ttyUSB1 usw. erzeugt.
  • Wichtig ist der Thread.sleep(4000), da andernfalls die Schnittstelle nicht richtig arbeiten kann!.
  • Das Protokoll funktioniert so, dass der Client einen Request (55) sendet. Anschließend kann der dann einen vollständigen Datensatz (13 Bytes) empfangen. Das Protokoll kann leicht für weitere Kommandos erweitert werden, wobei zu beachten ist, dass sowohl der Server (Arduino) und der Client das gleiche Protokoll verwenden müssen.
  • Das Programm gibt (oldValue) nur dann Messwerte aus, wenn sich eine Änderung ergeben hat.
  • Der vollständige Datensatz wird mit der folgenden Zeile eingelesen: byte[] buffer = serialPort.readBytes(13);
  • Die decodeDialGaugeAsString-Methode dekodiert die 13 Bytes und erzeugt eine lesbare Ausgabe. Sie bietet genügend Raum für Verbesserungen, demonstriert aber, wie man grundsätzlich das Protokoll auswertet.
|Demoprogramm (Skelett) für eigene Erweiterungen
/**
 * @author torsten roehl
 * Serversoftware: dial_gauge_v01.ini 
 * Verwendete API: jssc.jar
 * Demoprogramm (Skelett) für eigene Erweiterungen
 */
 
import jssc.SerialPort;
import jssc.SerialPortException;
import jssc.SerialPortTimeoutException;
 
public class DialGauge01 {
 
	public static void main(String[] args) throws InterruptedException, SerialPortTimeoutException {
 
		String dev = "/dev/ttyUSB0";
		if (args.length == 1)
			dev = args[0];
 
		SerialPort serialPort = new SerialPort(dev);
 
		try {
 
			serialPort.openPort();
			serialPort.setParams(9600, 8, 1, 0);
			System.out.println("...	initializing the serial port");
			Thread.sleep(4000); /* necessary, otherwise serial will fail!!! */
 
			byte REQ = 55; /* Protokoll */
 
			String strValue;
			String oldValue = "";
 
			while (serialPort.isOpened()) {
 
				// send request to read gauge value!
				boolean isReady = serialPort.writeByte(REQ);
 
				if (isReady) {
 
					byte[] buffer = serialPort.readBytes(13);
					strValue = DialGauge01.decodeDialGaugeAsString(buffer);
 
					if (!oldValue.equals(strValue))
						System.out.println("Messwert: " + strValue);
					oldValue = strValue;
				}
 
				Thread.sleep(10);
			}
 
			serialPort.closePort();
 
			System.out.println("port is closed!");
		} catch (SerialPortException ex) {
			System.out.println(ex);
		}
	}
 
	/*
	 * Working but q&d programming! 
	 * Auswertung fuer unsere Messuhr. Es gilt:
	 * Format (mm): ##.###    (3 Nachkommastellen)
	 * Format (inch): #.##### (5 Nachkommastellen)
	 */
	public static String decodeDialGaugeAsString(byte digits[]) {
 
		int units = digits[12]; // 0=mm or 1=inch
		String info = " (mm)";
 
		String str = "" + digits[5] +""+ digits[6] +""+ digits[7] +""+ digits[8] +""+ digits[9] +""
				+ digits[10];
 
		/* mm */
		if (units == 0) {
 
			String s1 = str.substring(0, 3);
 
			if (s1.charAt(0) == '0')
				s1 = s1.substring(1);
			if (s1.charAt(0) == '0')
				s1 = s1.substring(1);
 
			String s2 = str.substring(3);
			str = s1 + "." + s2;
 
		}
 
		/* inch */
		if (units == 1) {
			info = " (inch)";
			String s1 = str.substring(0, 1);
			String s2 = str.substring(1);
			str = s1 + "." + s2;
		}
 
		/* sign */
		if (digits[4] == 8)
			str = "-" + str;
 
		return str + info;
 
	}
 
}

Download/Links

Download

0x0017.1675246532.txt.gz · Zuletzt geändert: 2023/02/01 10:15 von torsten.roehl