Erschütterungssensor am Multi Function Shield via Interrupt abfragen

Das Multi Function Shield, das ich ja bereits vorgestellt habe und das mittlerweile ja auch um einen Drehgeber erweitert wurde, bietet mit der Möglichkeit zur einfachen Anzeige von Daten auf seinem 4-fach-7-Segment-Display und den freien Anschlusspins eine gute Grundlage, Sensoren ausprobieren.

Heute geht es um Erschütterungs- Kontakte / Sensoren. Davon finden sich gleich zwei Bauteile in meiner 37-in-one-Sensoren-Bulk-Sammlung.

Hardware: Erschütterungssensoren


Erschütterungs- oder auch Klopf-Sensoren sollen Erschütterungen registrieren. Dazu haben sie meist einen einfachen mechanischen Aufbau: Ein mittiger starrer Draht und ein in geringem Abstand davon befindliche, bewegliche Spirale. Wird den Sonsor nun durch Anstoßen erschüttert, so vibriert die Spirale und berührt dabei den inneren Draht, was zu einem Kurzschluss führt, der am Signal-Ausgang gemessen werden kann.

Das Foto zeigt zwei Module / Bauarten. Das linke Modul KY-031 ist etwas unempfindlicher und benötigt eine größere Erschütterung, bevor es auslöst. Das linke Modul KY-002 hingegen ist empfindlicher und reagiert auch schon auf kleine Erschütterungen.

Die drei Anschlüsse (von oben nach unten, Vorderseite oben, Pins rechts) sind : Außer dem Klopfsensor ist noch ein 10 kΩ Widerstand verbaut. Das Signal geht auf HIGH, sobald der Kontakt geschlossen wird. Je nach Erschütterungsstärke sind dies mehr oder weniger sehr kurze Spitzen, die durch die Vibration des Spirale entstehen.

Ein Ausflug in die Welt der Interrupts


Da die Signale nur so kurz sind, ist es wichtig, diese möglichst häufig abzufragen, damit man keines verpasst. Ich habe mich darum in der Software für eine Abfrage per Hardware-Interrupt entschieden. Beim Arduino Uno gibt es allerdings nur 2 Pins, die diesen unterstützen: Pin 2, den wir benutzen, und Pin 3, der bereits durch den Buzzer auf dem Multi Function Shield belegt ist.

Aus diesem Grund ist das Signal nicht an Pin 5 am 4x3 Header angeschlossen, sondern am Header darüber an Pin 2, der den Interrupt unterstützt. Die sonstige Verkabelung ist einfach und kann dem Bild rechts entnommen werden.

Die Zeilen #define PinData 2 // wo ist Digital Out angeschlossen? ... pinMode (PinData, INPUT_PULLUP); //Interrupt geht auf dem UNO nur auf Pin 2 und 3 attachInterrupt (digitalPinToInterrupt(PinData), sensorAlarm, FALLING); definieren Pin 2 als ständig HIGH (INPUT_PULLUP). Ein Kurzschluss im Sensor bringt die Leitung auf LOW. Weil beim attachInterrupt ein FALLING als Aktionsmodus definiert ist, wird jedesmal, wenn die Flanke von HIGH (normal) auf LOW (Kurzschluss) fällt, die angegebene Funktion sensorAlarm aufgerufen.

Ein so definierter Interrupt funktioniert unabhängig vom übrigen Programm. Das übrige Programm wird im Falle eines Interrupts kurz angehalten und die Interrupt-Routine ausgeführt. Nach dessen Ausführung geht es dann mit dem normalen Programmablauf im Loop() weiter.

Es ist wichtig, dass die Interrupt-Routine schnell wieder beendet werden kann, damit das Hauptprogramm nicht ins Stocken gerät. Darum haben dort Befehle wie delay() oder andere, zeitraubende Aufrufe nichts zu suchen. Ich schalte in meiner Routine lediglich die LED 2 dauerhaft an: void sensorAlarm() { MFS.writeLeds(LED_2, ON); } Außerdem muss man daran denken, dass Interrupts vom sonstigen Programm getrennt ausgeführt werden und deshalb einen anderen Kontext benutzen. Möchte man Variablen im Hauptprogramm, als auch in der Interrupt-Routine benutzen und verändern, so muss man der (selbstverständlich globalen) Variablendefinition ein volatile voranstellen, damit dies richtig funktioniert.

Durch den Interrupt erreichen wir, dass auch das kleinste Aufflackern durch eine Erschütterung registriert wird, selbst wenn das Hauptprogramm selbst schwer beschäftigt ist.

Software

Außerdem soll die LED 1 jedesmal leuchten, wenn ein Kurzschluss durch den Sensor ausgelöst wird. Da diese aufgrund die kurze Impulse aber nur sehr sehr kurz flackert und dies fast nicht erkennbar ist, gibt es auch ein akustisches Signal durch den Buzzer. Auch dies ist nur hörbar, wenn man genau hinhört oder es mehrere Auslösungen hintereinander gibt.

Unsere LED 2, die nach einem erstmaligen Impuls dank Interrupt gesetzt wird und dann an bleibt zeigt uns eine Auslösung aber zuverlässig an. Außerdem wird die 7-Segment-Anzeige benutzt, um die Impulse zu zählen. Auch hier sehen wir beständig jede Veränderung.

Mit dem Taster 1 können wir LED 2 und den Zähler wieder löschen und von vorn beginnen.

In Aktion sieht das Programm mit dem Versuchsaufbau so aus:



Und das zugehörige Programm ist folgendes:

Source-Code

//////////////////////////////////////////////////////// // (C) 2018 by Oliver Kuhlemann // // Bei Verwendung freue ich mich über Namensnennung, // // Quellenangabe und Verlinkung // // Quelle: http://cool-web.de/arduino/ // //////////////////////////////////////////////////////// #include <TimerOne.h> // für Timings und Interrupts #include <MultiFuncShield.h> // API für das Multi Function Shield #define PinData 2 // wo ist Digital Out angeschlossen? void setup() { Timer1.initialize(); MFS.initialize(&Timer1); // initialize multi-function shield library pinMode (PinData, INPUT_PULLUP); //Interrupt geht auf dem UNO nur auf Pin 2 und 3 attachInterrupt (digitalPinToInterrupt(PinData), sensorAlarm, FALLING); } void sensorAlarm() { MFS.writeLeds(LED_2, ON); } void sensorAlarmOff() { MFS.writeLeds(LED_2, OFF); } void loop() { MFS.beep(1, 5, 2); // bereit byte wert=0; byte btn=0; int cnt=0; while (1) { btn=MFS.getButton(); if (btn == BUTTON_1_PRESSED) { sensorAlarmOff(); MFS.write(" "); cnt=0; } wert=digitalRead(PinData); MFS.writeLeds(LED_1, !wert); // GPIO 3 = Beeper digitalWrite (3, wert); if (wert == LOW) { cnt++; MFS.write (cnt); } // delay(1); } }