Das VUSBduino-Logging-Keyboard       

von Ralf Beesner                      
Elektronik-Labor   Projekte   AVR 

 

Auf dem Markt gibt es zahlreiche USB-Logger. Ich hatte trotzdem mal "auf Arbeit" angefangen, einen eigenen zu entwickeln, als ich zwischen zwei beruflichen Projekten ein paar Tage Leerlauf hatte. Die Überlegung war, dass viele Praktiker in der industriellen Elektronik zwar nicht programmieren, aber mit Excel umgehen können. Das Gerät sollte als virtuelles Keyboard arbeiten und die Messwerte mehrerer Kanäle direkt in ein Excel-Kalkulationsblatt schreiben.

Im Gegensatz zu vielen marktüblichen Geräten sollte es direkt den Output von industriellen Analogsensoren (die meisten arbeiten mit 4...20mA Stromschleife, einige mit 0...10V Spannung) und passiven PT100- und PT1000- Sensoren verarbeiten. Zum Einsatz kam die VUSB-Lösung von  www.obdev.at . Um mir Leben etwas einfacher zu machen, hatte ich den VUSB-Beispielcode lediglich für mehrere Kanäle "aufgebohrt" und so abgeändert, dass ein externer Triggerimpuls einen einzelnen Messzyklus startete. Ein zweiter Mikrocontroller war für das Timing der Messzeiten zuständig und gab nach Zeitablauf jeweils einen Triggerimpuls an den Hauptcontroller ab.

Ich hatte das Projekt jedoch abgebrochen, obwohl es halbwegs funktionierte; teils aus Zeitmangel, teils, weil die erzielbare Genauigkeit mit den zur Verfügung stehenden Bauteilen wohl nicht professionellen Ansprüchen genügt hätte.


Da die Programmierung mit der Arduino-IDE sehr viel einfacher ist, habe ich das ganze als Hobbyprojekt erneut aufgegriffen. Die Hardware ist jedoch etwas vereinfacht (nur 0...20mA bzw. 0...10V Sensoren, keine externe ADC-Referenz).

Gewöhnungsbedürftig ist sicherlich, dass das virtuelle Keyboard auch das Ausgabemedium für die Konfiguration und Bedienung ist. Wenn man Knöpfe drückt, beginnt das Gerät, Dialoge ins gerade offene PC-Fenster zu schreiben. Das vereinfacht die Hardware, erfordert vom Nutzer allerdings Disziplin. Am besten legt man eine Excel- (oder LibreOffice-) Datei mit zwei Kalkulationsblättern an. In das erste wird der Konfigurationsdialog geschrieben. Wenn alles konfiguriert ist, wechselt man in das zweite Kalkulationsblatt und schaltet "scharf". Danach steckt man am besten die Maus ab, damit man nicht versehentlich das Kalkulationsblatt verlässt ;-) .


Sensoren

Die am häufigsten eingesetzten Sensoren messen Temperaturen, Drücke, Durchflussmengen und Abstände. Der Sensor im Titelbild ist ein Abstandssensor nach dem Triangulationsprinzip; er sendet einen schwachen roten Laserstrahl aus und empfängt über einen versetzt angeordneten Empfänger das von der Schmalseite der Raspberry-Verpackung reflektierte Streulicht. Es wird über eine bündelnde Linse auf einen lichtempfindlichen Chip projiziert. Mit dem Abstand der reflektierenden Fläche variiert der Einfallswinkel des zurückgestreuten Lichts und damit der Ort, an dem der Lichtfleck auf den Chip geworfen wird. Der Chip setzt das in eine zum Ort proportionale Spannung um, diese wird durch die interne Elektronik in einen abstandsproportionalen Strom umgewandelt.

4 mA entspricht dem Mindestabstand, 20 mA dem Maximalabstand - siehe auch den Wikipedia-Artikel  "Einheitssignal" . Einige neuere Sensoren kommen übrigens mit 4mA für die Eigenstromversorgung aus; sie weisen keinen Masseanschluss auf, sondern werden zweipolig angeschlossen.



Abbildung 1: Prinzip der Sensorausgänge


Es gibt übrigens auch recht preiswerte Triangulationssensoren von Sharp, die gern in Hobby-Roboter-Projekten eingesetzt werden. Sie sind jedoch weniger genau, die Ausgangsspannung ist stark nichtlinear, und sie sind langsam. Die Profi-Versionen kosten daher ein Vielfaches (der Listenpreis des oben abgebildeten Sensors lag vor zehn Jahren bei etwa 700 Euro).


Hardware

Als Hardware hatte ich zunächst den auf einem ATtiny 167 basierenden Digispark Pro eingesetzt. Er ist jedoch aufgrund eines groben Designfehlers nur eingeschränkt nutzbar: unbegreiflicherweise wurden AVCC und AGND nicht beschaltet, so dass der ADC und der gesamte Port A nur über Schleichwege mit der Betriebsspannung versorgt werden. Da das über irgendwelche Diodenstrecken erfolgt, ist die ADC-Referenz "AVCC" undefiniert (etwa um ca. 0,7V niedriger als VCC) und die Messwerte sind völlig unbrauchbar.

Danach hatte ich einen Arduino Micro eingesetzt, der aufgrund seines Hardware-USBs sehr schnell ist und mühelos den PC mit so vielen Messwerten überfluten kann, dass sich - je nach Anwendung - der PC daran "verschluckt". Da ich aber nach Anwendungen für mein neues Spielzeug  VUSBduino  suchte, habe ich nun diesen verwendet. Der Sketch lässt sich aber leicht auf einen Arduino Micro oder Leonardo umschreiben.



Abbildung 2: Schaltplan



Abbildung 3: Streifenraster-Platinen-Layout

Der VUSBduino ist für diese Aufgabe schon etwas knapp an Pins. Sieben Pins sind frei verwendbar - drei werden für Bedientaster und eine Signal-LED verwendet, so bleiben nur vier Messkanäle übrig. Die Taster liegen an den Pins PB2, PA7 und PA6. An PA2...PA5 legen die vier Messkanäle, die ersten drei sind für Sensoren mit Stromausgang 4...20 mA beschaltet, der vierte für einen Sensor mit Spannungsausgang 0...10 V (man kann ihn auch zur Messung der Betriebsspannung verwenden, ggf. über einen Vorwiderstand). An PB0 und PB1 liegt der Quarz, PA0 und PA1 bilden die USB-Schnittstelle.

Als ADC-Referenz muss die Betriebsspannung herhalten. Der 3,3 V Spannungsregler ist möglicherweise sogar genauer als die interne 1,1 V Referenz des ATtiny ;-) .

Die Widerstände sind so bemessen, dass 20 mA (bzw. 10 V) einen AD-Wert von 1000 ergeben; das vereinfacht die Rechenformeln, mit denen man im Kalkulationsblatt den AD-Wert in die tatsächlich vom Sensor gemessenen physikalischen Einheiten umrechnen muss.

Von diesen Widerständen hängt die Messgenauigkeit ab, man sollte also 1%-Metallschichtwiderstände nehmen. Spezielle 0,1%-Messwiderstände sind wohl nicht sinnvoll, da die Spannungsreferenz nicht genau genug ist.

Als Massnahme gegen Überspannung sind nur die 10 kOhm-Widerstände vorgesehen, welche die IC-Analogeingänge vor versehentlich angelegten 24V bewahren. Die 180 Ohm-Widerstände lassen sich schlecht gegen Überspannung schützen und dürften an 24 V sehr schnell zu rauchen anfangen, weil sie dann 133 mA bzw. 3,2W aufnehmen.... Sollten sie das überstehen, ist aber vermutlich die Genauigkeit dahin, man sollte sie dann austauschen.


Software

Der Sketch nutzt die externe  "Metro"-Bibliothek , um eine 100 Millisekunden-Zeitbasis zu generieren. In der Setup-Routine werden im wesentlichen die internen Pullup-Widerstände für die drei Taster aktiviert. Die Main-Loop (am Ende des Sketches) ist sehr kurz und ruft nur die Funktionen configure() und measure() auf.

Configure() fragt den Menü-Taster ab und schaltet bei jedem Drücken des Tasters in den nächsten Menüpunkt. Die Menüpunkte sind (in bestem Denglisch) in die Funktionen menu_analog_inputs(), menu_multiplicator(), menu_basiszeit() und measure() ausgelagert.

Mit der Taste Plus/Start kann man die Werte in den Menüeinstellungen erhöhen, bis sie wieder auf den Minimalwert überrollen:

- Anzahl der Messkanäle (1 - 4)
- Basiszeit ( 1 - 60)
- Multiplikator, mit dem der Basiszeit multipliziert wird (100 ms, Sekunde, Minute, Stunde)
- Messung (Start - Stop)

Mit der Taste Minus/Stop kann man im Menü Basiszeit den Wert auch vermindern und im Menü Messung den Messvorgang stoppen. In den übrigen Menüs wirkt die Taste nicht, da es dort nur wenige Werte gibt, die recht einfach mit der Plustaste korrigiert werden können, wenn man sich vertan hat.

Im Menü Basiszeit wird bei Werten über 15 jeweils um 5 erhöht bzw. vermindert und über 60 wieder auf 1 zurückgesetzt.

Da die Keyboard-Library ein amerikanisches Keyboard emuliert, sind in den Dialog-Strings y und z vertauscht und keine Umlaute möglich.


Bedienung

Logging-Keyboard an die Sensoren und 24V anschließen (neben der USB-Buchse befinden sich fünf Masseanschlüsse, im danebenliegenden Klemmenblock die vier Messeingänge; der einzelne Fünferblock an der breiten Seite der Platine ist der 24V-Verteiler), dann an den USB des Rechners. In das Kalkulations-"Schmierblatt" wechseln und durch Drücken der Menütaste den Dialog beginnen. Das Keyboard quittiert jeden Tastendruck mit einer Ausgabe (die mit einem Zeilenvorschub abgeschlossen wird).

- mit Plustaste Anzahl der Messkanäle wählen (1...4), nach Erreichen der Maximalzahl beginnt sie wieder mit 1
- mit Menütaste nächsten Menüpunkt "Multiplikator" aufrufen
- mit Plustaste "100 ms", "Sekunden", "Minuten" oder "Stunden" wählen
- mit Menütaste nächsten Menüpunkt "Basiszeit" aufrufen
- Wert zwischen 1 und 60 mit Plus- oder Minustaste wählen, ab 15 wird auf Fünferschritte gewechselt
- mit Menütaste nächsten Menüpunkt "Messung" aufrufen
- ins Kalkulations-Messblatt wechseln
- Starten durch Drücken der Plus-Taste
- Stoppen durch Drücken der Minus-Taste.



Abbildung 4: Einstell-Dialog


Wenn das Keyboard im Messmodus läuft, leuchtet die LED. Da kein eigener Pin für die LED übrig war, leuchtet sie auch bei Drücken der Menü-Taste. Man kann den Bug aber zum Feature erklären: so erhält man eine Kontrollmöglichkeit, ob das Gerät vom USB mit Betriebsspannung versorgt wird.

Das Keyboard sendet die Messwerte der zuvor eingestellten Zahl von Kanälen mit TABs als Trenner und mit Return als Abschluss. Dadurch werden die Werte in Excel (bzw. im hier verwendeteten LibreOffice Calc) zunächst in nebeneinander liegende Zellen geschrieben. Der Abschluss mit RETURN wechselt in die linke Zelle der nächsten Zeile.

Ggf. muss das Verhalten bei RETURN in den Excel-Optionen eingestellt werden (Excel verwende ich seit 10 Jahren nicht mehr; in LibreOffice Calc heißt die zu aktivierende Einstellung "die Eingabetaste bewegt die Auswahl nach unten", sie findet sich unter "Extras - Optionen - LibreOffice Calc - Allgemein").

Man kann entweder erst einmal die Messreihe herunterschreiben lassen und später rechts von diesem Zahlenblock mit den Werten weiterrechnen, man kann aber auch das Blatt vorbereiten, indem man den Platz für den Zahlenblock mit den Messwerten frei lässt und links davon bereits den Rechengang vorbereitet (ein Beispiel ist beigefügt).

Ein Messwert von 1000 entspricht bei Sensoren mit Spannungsausgang 10V, ein Messwert von 0 entspricht 0V. Bei Sensoren mit Stromausgang entspricht ein Messwert von 200 einem Strom von 4 mA, dies ist der Mindestwert der physikalischen Einheit, die der Sensor misst.

/*
 Virtuelles Keyboard. Liest im einstellbaren Takt max. 4 Analog-Kanäle aus
 und schreibt die ADC-Werte ins aktuelle Fenster auf dem PC.

 Hardware: VUSB-ATtiny84

 Bedienkonzept: 3 Tasten; Konfigurationsdialog-Ausgabe per Keyboard.

 Zuordnung der Pins (erst Arduino-Notation, dann ATtiny-Hardware):

 Analog in:           A2 ... A5   PinA.2 ... PinC.5
 Aufnahme-Anzeige     Digital 8   PinB.2
 Tasten:
 Minus  Cancel        Digital 6   PinA.6
 Plus  Start          Digital 7   PinA.7
 Menue                Digital 8   PinB.2

 Menuepunkte:
        1: Anzahl Kanaele
        2: Multiplikator
        3: Zeit-Basiswert
        4: Start/Cancel
*/


#include <VUSB_Keyboard.h>
#include <Metro.h>

#define minus 6
#define cancel 6
#define plus 7
#define start 7
#define menu 8
#define led 8

Metro Timer = Metro(100); // Wert ist in ms unsigned char i;
unsigned char menue;
unsigned char mult_menue;
unsigned char chan;
unsigned char maxChan = 1 ;
unsigned char plusminus = 1;
unsigned int aVal[4] = {0};
unsigned int timerTicks; // max. 12h -> 60 * 60 * 12 = 43200 -> unsigned int reicht unsigned int timerInterval;
unsigned int base = 1;
unsigned int multiplicator = 1 ;
unsigned char timerIntervalSet;

void setup() {

  analogReference(DEFAULT);
  pinMode (minus, INPUT_PULLUP);
  pinMode (plus, INPUT_PULLUP);
  pinMode(menu, INPUT_PULLUP);

  VUSB_Keyboard.delay(2000);
}


void configure() {

  if (digitalRead(menu) == 0) {
    menue++;
    if (menue > 4) {
      menue = 1;
    }
    VUSB_Keyboard.delay (200);
  }

  switch (menue) {

    case 1:
      VUSB_Keyboard.println (F( "Menue Anyahl analoge Inputs" ));
      while (digitalRead(menu) == 1) {
        menu_analog_inputs() ;
      }
      break;
    case 2:
      VUSB_Keyboard.println (F( "Menue Multiplikator" ));
      while (digitalRead(menu) == 1) {
        menu_multiplicator() ;
      }
      break;
    case 3:
      VUSB_Keyboard.println (F( "Menue Basisyeit" ));
      while (digitalRead(menu) == 1) {
        menu_basiszeit() ;
      }
      break;
    case 4:
      VUSB_Keyboard.println (F( "Menue Messung" ));
      while (digitalRead(menu) == 1) {
        measure();
      }
      break;
  }
}


void menu_analog_inputs() {

  if (digitalRead(plus) == 0) {
    maxChan++ ;
    if (maxChan > 4) {
      maxChan = 1;
    }
    VUSB_Keyboard.print (F( "Analoge Inputs> " ));
    VUSB_Keyboard.println (maxChan);
  }
  VUSB_Keyboard.delay (200);
}


void menu_multiplicator() {

  if (digitalRead(plus) == 0) {
    mult_menue++;
    if (mult_menue > 4) {
      mult_menue = 1;
    }
    switch (mult_menue) {

      case 1:
        multiplicator = 1;
        VUSB_Keyboard.println (F( "Multiplikator> 100 msec" ));
        break;
      case 2:
        multiplicator = 10;
        VUSB_Keyboard.println (F( "Multiplikator> Sekunden" ));
        break;
      case 3:
        multiplicator = 600;
        VUSB_Keyboard.println (F( "Multiplikator> Minuten" ));
        break;
      case 4:
        multiplicator = 36000;
        VUSB_Keyboard.println (F( "Multiplikator> Stunden" ));
        break;
      default:
        multiplicator = 1;
        VUSB_Keyboard.println (F( "Multiplikator> 100 msec" ));
        break;
    }
  }
  VUSB_Keyboard.delay (200);
}


void menu_basiszeit() {

  if ((digitalRead(plus) == 0) || (digitalRead(minus) == 0)) {
    if (digitalRead(plus) == 0) {
      plusminus++;
    }
    if (digitalRead(minus) == 0) {
      plusminus--;
    }
    if (plusminus > 60) {
      plusminus = 0;
    }
    if (plusminus < 16) {
      base = plusminus;
    }
    if (plusminus > 15) {
      base = (5 * (plusminus - 15) + 15);
    }
    timerInterval = base * multiplicator;
    if (timerInterval > 43200) {
      VUSB_Keyboard.println (F("Maximal 12 Stunden moeglich! Bitte neu waehlen! " ));
      plusminus = 1;
      timerInterval = 3600;
    }
    else {
      VUSB_Keyboard.print (F( "Basis ) " ));
      VUSB_Keyboard.print (base, DEC);
      VUSB_Keyboard.print (F( " Multiplikator ) " ));
      VUSB_Keyboard.print (multiplicator, DEC);
      VUSB_Keyboard.print (F( " Intervall ) " ));
      if (multiplicator < 10) {
        VUSB_Keyboard.print (timerInterval, DEC);
        VUSB_Keyboard.println (F( "00 msec" ));
      }
      else {
        VUSB_Keyboard.print (timerInterval / 10, DEC);
        VUSB_Keyboard.println (F( " sec" ));
      }
    }
  }
  VUSB_Keyboard.delay (200);
}


void measure() {

  if (digitalRead(start) == 0) {
    Timer.reset();
    pinMode(led, OUTPUT);
    digitalWrite(led, 0); // LED leuchtet bei LOW     if (timerInterval == 0) { // falls ohne gesetztes timerInterval gestartet wurde       timerInterval = 1;     }     timerTicks = timerInterval;                 // Ausgabe der ersten Werte sofort nach Start     while (true) { // Kernschleife       if (Timer.check() == 1) { // Sekunde um         timerTicks++;         if (timerTicks >= timerInterval) { // gewaehltes Zeitintervall ist um           timerTicks = 0;           for (i = 0; i < (maxChan); i++) { // Werte ausgeben             aVal[i] = analogRead(i + 2 );
            aVal[i] = analogRead(i + 2 );
            VUSB_Keyboard.print(aVal[i], DEC);
            VUSB_Keyboard.print("\t");
          }
          VUSB_Keyboard.print("\r\n");
        }
      }
      if (digitalRead(cancel) == 0) {
        VUSB_Keyboard.println (F( "Ende" ));
        pinMode(led, INPUT); // LED aus, Pin wird wieder Input         digitalWrite(led, 1);
        goto ende;
      }
    }
  }
ende:   ; } void loop() {

  configure();                                         // Konfigurationstasten abfragen   measure(); }
Da man das durch die Arduino-Umgebung erzeugte Hex-File aus dem Temp-Ordner fischen kann, habe ich es beigefügt. Man kann es auch ohne Arduino-IDE und ohne Bootloader direkt in einen ATtiny 84 flashen. Die Fusebits müssen für Quarztakt und Vorteiler 1:1 gesetzt sein (lfuse 0xff, hfuse 0xdd).


Download des Sketches und des Hexfiles:  0715-vusb-logging-kb-soft.zip
Download des Excel-Beispiels:  0715-vusb-logging-kb-test.xls 


Elektronik-Labor  Projekte  AVR