Das VUSBduino-Logging-Keyboard
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