Multimeter von Voltcraft (Conrad)




ME22

Die Conrad-Geräte sind recht preisgünstig und funktionieren soweit ich das beurteilen kann recht gut.
Ein wahnsinniger Nachteil ist allerdings die sehr jurz gehaltene Dokumentation, die oft auch noch falsch ist.
Oft findet man bei Bernd Kunze wichtige Informationen: http://members.tripod.de/berndkunze/selfprog.htm
Das vorliegende Gerät konnte fast nicht über den Rechner Ausgelesen werden, da laut Datenblatt  die Schnitstellenparameter folgende sein sollten:
7 Bit; 2 Stopbits; 1200 Baud
Mit diesen Werten konnte das vorhandene Gerät allerdings nicht dazu bewegt werden Daten zu senden. Viel surfen im Netzt lieferte immer die gleichen Erkenntnisse. Erst der Download eines DOS-Basicquelltextes offenbarte das Geheimnis: Das ME22 sendet jetzt mit 2400 Baud
Damit funktionierte das Gerät dann endlich.
Das vorliegende C++-Programm für Linux ist, wie üblich hier, sehr rudimentär, sicherlich schlecht programmiert aber es funktioniert und der Quelltext müsste leicht verständlich sein. Alles was es macht, ist einige Werte vom Multimeter zu holen und auf dem Bildschirm darzustellen. Es läßt sich einfach an die jeweiligen Bedürfnisse anpassen.
Bessere Programme für die meisten Conrad-Multimeter gibt es bei Andreas Blücher: http://www.abluecher.de/LINUX/MESS_ARTIKEL/index.html, von Ihm gibt es auch einen Artikel über Grundlegendes zum Auslesen der seriellen Schnittstelle unter http://www.linux-magazin.de/ausgabe/1999/08/Messen/messen.html kann ich nur empfehlen!

GDM703

Bis diesem Gerät gilt das oben gesagte natürlich auch - fast keine Dokumentation.
Hier helfen wieder die Seiten von Bernd Kunze weiter.
Das Hauptproblem bei diesem Gerät ist, dass es dauernd sendet, also man nicht wie bei allen anderen beschriebenen Geräten die Daten anfordert.
Das C++-Programm hier ist noch nicht sehr weit entwickelt, aber es liest immerhin schon mal einen Messwert aus und schreibt ihn auf den Bildschirm.
Das Auslesen des Graphikbilschirms klappt eigentlch auch, allerding liegen die Daten in einem Character-Array der Länge 1024 vor, mit dem man momentan noch recht wenig anfangen kann - zumal die vom Gerät geschickten Werte auch noch jede Menge Fehler haben:

Kleiner Auszug, aus einem Dos-Programm von Bernd Kunze:

FOR Zeile = 0 TO 7
    FOR Spalte = 0 TO 127
                      'Zuständiges Byte innerhalb des Zeichenstring ermitteln
                        Ort = (Zeile * 128) + Spalte + 1
                        Pixelmuster = ASC(MID$(GrafikDisplay$, Ort, 1))

                      'Durch Fehler im Meßgerät werden die Bytes nicht in der
                      'richtigen Reihenfolge übertragen. Teilweise gehören Bytes
                      'nicht zur Anzeige, teilweise ist die Position um +64 Byte
                      'versetzt und ein Byte am Schluß fehlt vollständig.

                      'Hier erfolgt die Korrektur der Fehler
                        SELECT CASE Ort
                        CASE 1, 1 + 64, 1 + 128, 1 + 2 * 128, 1 + 3 * 128, 1 + 4
                          'Pixel an dieser Stelle löschen
                            Pixelmuster = 0
                        CASE 1 + 128 + 64, 1 + (2 * 128) + 64, 1 + (3 * 128) + 6
                          'Korrektur der Position
                            Pixelmuster = ASC(MID$(GrafikDisplay$, Ort + 64, 1))
                        CASE 1 + (7 * 128) + 64
                          'Byteinhalt fehlt, kann aber rekonstruiert werden
                            Pixelmuster = 255
                        END SELECT

                        FOR i = 0 TO 7
                          'Byte in Einzelbits zerlegen und Farbe zuweisen,
                          'wenn das Bit gesetzt ist
                            IF Pixelmuster AND (2 ^ i) THEN Farbe = 2 ELSE Farbe
                          'Pixel auf dem Bildschirm zeichnen
                            PSET (Links + Spalte, Oben + (Zeile * 8) + i), Farbe
                        NEXT i

     NEXT Spalte
NEXT Zeile

............und irgentwie hatte ich dann keine Lust mehr, diese Daten in in sinnvolles Format zu bringen, aber falls sich jemand erbarmt dies zu übernehmen würde ich mich über das Ergebnis freuen. Bei dem vorliegenden Programm holt das Gerät die Messwerte sofort ab. Ein Problem, das man noch lösen sollte, ist die Tatsache, dass wenn man mehrere Werte innerhalb des Programms abholen will, immer erst Werte geholt werden, die noch irgentwie im Puffer der Schnittstelle stecken, also nicht die aktuellen Werte der Anzeige.
Wenn man die Schnittstelle öffnet, einen Wert holt und sie dann wieder zumacht gibt es dieses Problem nicht, beim Öffnen wird der Inhalt wohl geflusht.
Das kann nicht schwer sein, ich weiss blos nicht wie, wäre also sehr Dankbar für einen Tipp.
Falls das Gerät im graphischen Modus betrieben wird, wartet das Programm bis unter 'storage' der 'call' Knopf gedrückt wird und liest dann die Werte des Graphicdisplays aus - man kann nur wie erwähnt recht wenig mit diesen Daten anfangen.

Also hier sind jetzt nochmal die Programme zum Download, viel Spass
                                                                                                                   Ralph

ME22
GDM703

Oder hier direkt der Quelltext zum Anschauen:
 
 
 
//me22.cpp

#include <fstream>       //includet auch iostream 
#include <unistd.h>      //read, write, close
#include <fcntl.h>
#include <sys/termios.h>
#include <sys/ioctl.h>
 

class Dmm {
  private:
    fstream me;
    int initTTY(char*);
  public:
    Dmm(char*);
    ~Dmm();
    int liesMesswert(); 
};
 

Dmm::Dmm(char* device){   //Konstruktor

  //initTTY(device);
  me.open(device, ios::app | ios::in);
  if (!me) {
    cerr<<"Device "<<device<<" kann nicht geoeffnet werden!"<< endl;
  exit(1); 
  }
  else { 
    cout << "Device " << device << endl;
  }

  initTTY(device);

Dmm::~Dmm(){              //Destruktor
  me.close();

 

int Dmm::initTTY(char* device){  //Initialisierung der Schnittstelle

  int modelines = 0;
  int fd = open(device, O_RDWR | O_NOCTTY);
  cout << "fd= " << fd << endl;
  if (fd == -1){
    cerr << "RS-232 kann nicht initialisiert werden!" << endl;
  exit(1);
  }

  struct termios settings;

  //Setzen der Inputflags
  settings.c_iflag = IGNBRK | IGNPAR;

  //Setzen der Outputflags
  settings.c_oflag = 0;

  //Setzen der Controlflags
  settings.c_cflag = CS7     // 7 Bit pro Byte
                   | CSTOPB  // 2 Stopbits
                   | CREAD   // Zeichen koennen von gelesen werden
                   | CLOCAL; // Ignoriere Modemstatus, lokaler Anschluss

  //Setzen der Localflags
  settings.c_lflag = 0;      // keine besonderen Angaben noetig

  // Maximale Zeit, die beim Lesen gewartet wird (in 0.1s)
  settings.c_cc[VTIME] = 10; // kleinere Werte für Geräte mit höheren
                             // Übertragungsgeschwindigkeiten 
                             // z.B. Me32 -> 30

  // Minimale Anzahl der zu lesenden Bytes
  settings.c_cc[VMIN] = 0;

  //Setzen der Uebertragungsgeschwindigkeit
  if (cfsetspeed(&settings, B2400)!=0){   //Me-22 hat jetzt 2400 Baud !!!
    cerr << "Fehler beim Setzen der Übertragungsgeschwindigkeit" << endl;
  return -1;
  } 

  //Einstellungen an Schnittstelle uebergeben
  if (tcsetattr(fd, TCSANOW, &settings) == -1){
    cerr << "Fehler bei tcsetattr TCSANOW" << endl; 
  return -1;
  } 

  //Lesen der IO-Control Parameter
  if (ioctl(fd, TIOCMGET, &modelines) == -1){ 
    cerr << "Fehler bei ioctl TIOCMGET" << endl;
  return -1;
  }

  //Setzen des RTS-Signals, Spannungen an RTS-Leitung (?)
  modelines &= ~TIOCM_RTS;
  if (ioctl(fd,TIOCMSET,&modelines) == -1){ 
    cerr << "Fehler bei ioctl TIOCMSET" << endl;
  return -1;
  }

  close(fd);
}
 
 

int Dmm::liesMesswert(){
  me << "D\n" << endl;    //Messwert-String anfordern, "D\r" tuts auch

  char* header, einheit; 
  float messwert;

  me >> header >> messwert >> einheit;
  cout << header << " " << messwert << " " << einheit << endl; 

 

int main(int argc, char* argv[]){

  Dmm* multimeter;

  if (argc > 1) multimeter = new Dmm(argv[1]);
  else          multimeter = new Dmm("/dev/ttyS0");

  for(int i = 1; i < 10; i++){   //kleine Schleife
    multimeter -> liesMesswert();
  }

  delete multimeter;

return 0;
}
 

 

//gdm.cpp

nclude <fstream> 
#include <unistd.h> 
#include <fcntl.h>
#include <sys/termios.h>
#include <sys/ioctl.h>
 

class Dmm {
  private:
    ifstream me;
    int fd;
    int initTTY(char*);
    int liesMesswert();
    int liesGraph(); 
  public:
    Dmm(char*);
    ~Dmm();
    int liesWert();

};
Dmm::Dmm(char* device){   //Konstruktor
 me.open(device, ios::in);
  //initTTY(device);
  /*me.open(device,ios:app | ios::in);
  if (!me) {
    cerr<<"Device "<<device<<" kann nicht geoeffnet werden!"<< endl;
  exit(1); 
  }
  else { 
    cout << "Device " << device << endl;
  }*/

  initTTY(device);

Dmm::~Dmm(){              //Destruktor
 close(fd);
 me.close();

 

int Dmm::initTTY(char* device){  //Initialisierung der Schnittstelle

  int modelines = 0;
  fd = open(device, O_RDWR | O_NOCTTY);
  //cout << "fd= " << fd << endl;
  if (fd == -1){
    cerr << "RS-232 kann nicht initialisiert werden!" << endl;
  exit(1);
  }
 

  struct termios settings;

  //Setzen der Inputflags
  settings.c_iflag = IGNBRK | IGNPAR;

  //Setzen der Outputflags
  settings.c_oflag = 0;

  //Setzen der Controlflags
  settings.c_cflag = CS8     // 8 Bit pro Byte
                   | CREAD   // Zeichen koennen von gelesen werden
                   | CLOCAL; // Ignoriere Modemstatus, lokaler Anschluss

  //Setzen der Localflags
  settings.c_lflag = 0;      // keine besonderen Angaben noetig

  // Maximale Zeit, die beim Lesen gewartet wird (in 0.1s)
  settings.c_cc[VTIME] = 10; // kleinere Werte für Geräte mit höheren
                             // Übertragungsgeschwindigkeiten 
                             // z.B. Me32 -> 30

  // Minimale Anzahl der zu lesenden Bytes
  settings.c_cc[VMIN] = 0;

  //Setzen der Uebertragungsgeschwindigkeit
  if (cfsetspeed(&settings, B9600)!=0){   //GDM703 hat 9600 Baud !!!
    cerr << "Fehler beim Setzen der Übertragungsgeschwindigkeit" << endl;
  return -1;
  } 

  //Einstellungen an Schnittstelle uebergeben
  if (tcsetattr(fd, TCSANOW, &settings) == -1){
    cerr << "Fehler bei tcsetattr TCSANOW" << endl; 
  return -1;
  } 

}

int Dmm::liesMesswert(){
char ch2;
char val[6]="leer";
float messwert=2;
for (int i=1; i<3; i++){    //Müll vor dem eigentlich Zahlenwert
read(fd,&ch2,1);
}
for (int i=0; i<5; i++){   //Der eigentliche Messwert
    read(fd, &val[i], 1);
    }
  cout << val << endl;

int Dmm::liesGraph(){

char ch2='a';
char val[1024];
int daten[128];
for (int i=0; i<1024 ; i++){
   read(fd,&val[i],1); cout << i << ": "<< (unsigned int)val[i] <<endl;
    }
read (fd,&ch2,1);//EOT holen (3)
cout << "test" << endl;
// cout << i << ": "val << endl;

 int Dmm::liesWert(){

char ch='a';
cout << "Falls das Graphikdisplay ausgelesen werden soll: 'Storage-call'
drücken!"<<endl; 
while((int)ch != 2){   //2 heisst jetzt kommt ein Zeichensatz
read(fd,&ch,1);
}
read(fd,&ch,1); //Ein Zeichen holen: was kommt (A,B,...-Messwert, Z-Graph)
cout << ch << endl;
if (ch=='A' || ch=='B' || ch=='E' || ch=='H' 
|| ch=='J' || ch=='L' || ch=='M') liesMesswert();   //Ein Messwert!
else {if (ch=='Z') {liesGraph();}            //Graphikdaten werden gesendet
else liesWert();}                         //Falls Schwachsinn kommt nochmal
// cout << i << ": "val << endl;

int main(int argc, char* argv[]){
float value;
  Dmm* multimeter;

  if (argc > 1) multimeter = new Dmm(argv[1]);
  else          multimeter = new Dmm("/dev/ttyS0");
  multimeter -> liesWert();
 delete multimeter;
return 0;
}
 
 

 


 
 
 
 
 
 
 

Ralph Uhl 2001