Monthly archives: Dezember, 2015

Kapazität messen mit dem Arduino

In dieser Folge beschäftigen wir uns mit dem Messen von Kondensatoren, also Kapazitäten mit dem Arduino. Ich experimentiere schon seit einiger Zeit damit herum und habe vor ein paar Wochen günstige Arduino-Nano Clones aus China bekommen, mit denen ich nun ein kleines Projekt realisieren wollte.

title_image

Das Messen von Kapazitäten ist hierfür geradezu ideal. Nur wie misst man diese eigentlich? Dazu benötigen wir zunächst ein wenig Theorie, aber keine Angst, es hält sich in Grenzen.

Legt man an an einen Kondensator über einen Vorwiderstand eine Gleichspannung an, so lädt sich dieser nach einer bestimmten Zeit fast vollständig auf die angelegte Spannung auf. Gleichzeitig bezeichnet man diese Art der Schaltung schon als RC-Glied, oder Tiefpass. Die Eigenschaften als Tiefpass wollen uns an dieser Stelle aber nicht weiter interessieren.

RC-Glied

RC-Glied

Nun können wir einige Überlegungen anstellen.

Aus der Elektrotechnik wissen wir schon etwas über die Zeitkonstante "Tau", diese berechnet sich folgendermaßen:

tau = R*C

Diese Zeitkonstante besagt nach welcher Zeit sich der Kondensator auf ca. 63% der angelegten Spannung aufgeladen hat, das können wir also gut gebrauchen. Wie kommen wir denn aber nun auf die Einheit Sekunden? Ganz einfach, die Kapazität in Farad gemessen lässt sich auch schreiben als:

F = frac{A * s}{V}

Und der Widerstand in Omega gemessen ist natürlich laut dem Ohm'schen Gesetz frac{U}{I} also in Einheiten ausgedrückt frac{V}{A}.

Wir erhalten also die Einheit für R*C:

R * C = frac{A * s * V}{V * A}

Ampere(A) und Volt(V) kürzen sich raus und es bleibt nur noch die Sekunde, so ein Zufall!

Rein interessehalber schauen wir uns noch die vollständige Formel an, nach der sich die Spannung U an einem Kondensator zu einem beliebigen Zeitpunkt berechnen lässt, sozusagen die Ladekennlinie des Kondensators:

U(t) = U_{rm max} cdot (1 - e^{-frac{t}{tau}})

Das können wir uns auch mal als Grafik ausgeben, dann wird der Zusammenhang gleich viel klarer (Klicken zum Vergößern).

RC-Glied

RC-Glied

Die beiden gestrichelten Linien zeigen den Punkt an dem sich der Kondensator auf 63% der angelegten Spannung aufgeladen hat, also genau nach 1*Tau (reimt sich).

Natürlich verhält sich ein RC Glied in der Realität fast genauso. Im folgenden Bild habe ich ein RC-Glied mit 100nF und 1kOhm an eine Rechteckspannung mit ca. 250HZ angeschlossen und die Spannung am Kondensator gemessen, hier lässt sich der Lade -und Entladevorgang sehr gut beobachten:

RC-Glied an 250Hz

RC-Glied an 250Hz

Jetzt haben wir unser Handwerkszeug zusammen und können unsere Schaltung entwerfen.

Grundsätzlich müssen wir also einen Kondensator über einen Vorwiderstand aufladen und die Zeit messen, die benötigt wird, um den Kondensator aufzuladen. Weiterhin müssen wir den Kondensator auch entladen können. Also benötigen wir einen Widerstand zum Laden und einen zum Entladen, weiterhin müssen wir den ADC des Arduino bemühen, um die Spannung am Kondensator zu messen. Diese kann natürlich niemals höher als die Betriebssspannung des Arduino werden.

Um die ganze Sache abzurunden, benötigen wir ein Display, in diesem Falle ein HD44780 kompatibles 16x2, um die Messung anzuzeigen und einen Taster, um den Messvorgang starten zu können.

Das führt uns dann zu folgender Schaltung:

Schaltplan für den Kapazitätsmesser

Schaltplan für den Kapazitätsmesser

Der Arduino lädt nun über Pin D13 den Kondensator und misst die Zeit, die dafür benötigt wird. Die Messung der Spannung erfolgt am Pin A0 und die Entladung wird über Pin D10 vorgenommen. An JP3 wird dann der Taster zum Starten der Messung angeschlossen. JP2 dient der Spannungsversorgung, hier werden 5V angeschlossen. Alternativ kann die Schaltung auch über die USB-Buchse des Arduino versorgt werden.

Auf dem Steckbrett aufgebaut könnte das so aussehen:

Schaltung auf dem Steckbrett

Schaltung auf dem Steckbrett

Die eigentliche Messroutine ist erschreckend einfach:

	digitalWrite(chargePin, HIGH);  // set chargePin HIGH and capacitor charging
	startTime = millis();

	/* Measure Capacity */
	while (analogRead(analogPin) < 648){       // 647 = 63.2% of 1023, which corresponds to full-scale voltage 
	}

	elapsedTime = millis() - startTime;
	if (elapsedTime > 1) {  //output only if time was measured

		// convert milliseconds to seconds ( 10^-3 ) and Farads to microFarads ( 10^6 ),  net 10^3 (1000)  
		microFarads = ((float)elapsedTime / resistorValue) * 1000;
        }

Hier wird der Ladepin (D13) auf High Pegel, also 5V gesetzt. Wir starten die Zeitmessung und der Ladevorgang beginnt. Der ADC vom Arduino hat eine Auflösung von 10 Bit, also 1024 Schritte. Somit entsprechen 63.2% etwa frac{5V * 0.632}{5V / 1024} = 647 Schritten.

Warten wir also so lange, bis dieser Wert am ADC erreicht ist, können wir anschliessend die Ladezeit ablesen und unsere Kapazität berechnen (Zeile 12). Dann kann der ermittelte Wert auf dem Display ausgegeben werden.

Der ursprüngliche Source Code stammt von Paul Badger, wurde vom Make Magazin erweitert und von mir noch für das LCD und die Tastensteuerung modifiziert,

Das ist eigentlich auch schon die ganze Zauberei. Natürlich kann man die Schaltung noch verbessern, so lassen sich mit dem 10k Ladewiderstand keine kleinen Kapazitäten < 1µF messen. Hier wäre es wünschenswert die noch freien Pins zu verwenden und den Ladewiderstand automatisch umzuschalten.

Vielleicht schauen wir uns in einer der späteren Folgen an, wie man diese Schaltung auf einer Platine aufbaut und in ein schickes Gehäuse einbaut.

Alle Projektdateien, Quelltexte usw. finden sich hier. Viel Spass beim Nachbauen.


Matlab und das Picoscope

Was tut man, wenn man ein Picoscope 3203D besitzt und Messwerte von dort in Matlab einlesen (und sich deswegen extra die Instruments Toolbox gekauft hat :()möchte?

Die kurze Antwort: das geht so erstmal nicht so einfach, weil dieses Modell zur Zeit (noch) nicht unmittelbar unterstützt wird. Zumindest habe ich es noch nicht zum Laufen bekommen.

Die lange Antwort: Es gibt zum Glück einen einfachen Workaround!

Die Picoscope Software kann zum Glück Messdaten im Matlab Format speichern. Hierzu zeichnet man ganz normal sein Eingangssignal auf und speichert diese Wellenform dann mit "Datei -> Wellenform speichern unter" als Matlab-4 Datei.

Picoscope Triangle

Picoscope Messung mit Dreiecksspannung

Picoscope speichert dann 32 Wellenformen mit jeweils 1000000 Samples. Wir benötigen zum Matlab Import erstmal nur eine davon und kopieren diese in unser Matlab Arbeitsverzeichnis. Nennen wir sie mal "triangle_01.mat".

Jetzt basteln wir uns ein kleines Script, was diese Daten 1 zu 1 ausplottet:

Fs = 500000000;       % Sampling frequency

data = importdata('triangle_01.mat');

T = 1/Fs;             % Sampling period
L = data.Length;      % Length of signal
t = (0:L-1)*T;        % Time vector

G = transpose(data.B);

plot(t,G);
grid on;

print('triangle.eps','-depsc');

Wichtig ist nur, daß man in der ersten Zeile die Sampling Frequenz richtig einstellt, damit später im Plot die Zeitachse richtig beschriftet wird.

Weiterhin ist die Zeile 9 vielleicht verwirrend, diese ist aber nötig, da der Import einen Spaltenvektor erzeugt, der plot Befehl aber einen Zeilenvektor erwartet. Mit transpose drehen wir diesen einfach um. Wenn alles richtig eingegeben ist, erhalten wir einen Plot, der wie folgt aussehen sollte:

Matlab Ausgabe

Matlab Ausgabe

Der Plot wird dann noch zusätzlich im Arbeitsverzeichnis als EPS-Vektordatei ausgegeben, so einfach kann das Leben sein!


Picoscope 3203D im Test

Seit einiger Zeit plagt mich das Thema, welches Oszilloskop ich nun erwerben soll, um mein altes Hameg HM512 zu ersetzen, welches ja nun mittlerweile weit über 40(!) Jahre alt ist.

Ich hatte bereits das "Vergnügen", mit einem Rigol DS1052E und einem DS2072A zu arbeiten. Ich spare mir an dieser Stelle die Details. Nur soviel dazu: Wer keinen Düsenjet auf seinem Schreibtisch stehen haben will, sollte tunlichst die Finger davon lassen! Insgesamt ist alles aus dem Hause Rigol, was ich bisher in den Fingern hatte, als billiger Schrott zu bezeichnen, das schliesst das Tischmultimeter DM3058E und das Labornetzteil DP832 ein. Einzig der Funktionsgenerator DG4102 vermochte zu überzeugen. Alle anderen Geräte scheiden allein wegen der lauten Lüfter aus, die allesamt eine absolute Zumutung sind! Scheinbar sind alle Chinesen taub.

Aber genug davon, wenden wir uns den angenehmen Dingen zu...

(mehr …)


Raspberry Pi Wecker mit Webradio (Teil 5)

Webradio steuern

Wie versprochen geht es heute weiter mit der Steuerung des Webradios. Wir wollen uns heute ansehen, wie man den Raspberry Pi über den GPIO Port ansteuert und dort Taster abfragt und eine LED ansteuert.

Hierzu müssen wir zunächst ein wenig Software installieren. Zu aller erst benötigt man die WiringPi Bibliothek. Eine Anleitung, wie man diese herunterlädt und installiert gibt es hier.

Anschliessend muss man noch den Music Player Daemon installieren und dazu den passenden Client, das geschieht mittels

sudo apt-get install mpc mpd

Jetzt öffnet man die Datei /etc/mpd.conf mit einem beliebigen Editor wie z.B. nano oder vim und sucht sich die Zeile, in der steht "bind to address" hier nehmen wir das # am Anfang weg und starten den Daemon mit

sudo /etc/init.d/mpd restart

neu, damit die Änderungen übernommen werden.

Als nächstes benötigen wir eine Playlist, die eine Liste von URLs zu Radio-Streams enthält, die gibt es zuhauf im Internet. Als Beispiel mag diese hier dienen:

http://www.ndr.de/resources/metadaten/audio/m3u/n-joy.m3u
http://mp3-live.swr3.de/swr3_m.m3u
http://mp3-live.dasding.de/dasding_m.m3u
http://mp3-live.swr.de/swr1bw_m.m3u
http://www.ndr.de/resources/metadaten/audio/m3u/ndr2_sh.m3u
http://www.chromanova.de/chromanova.-.chill.high.pls

Speichert diese einfach als webradio.pls im Ordner /home/pi/webradio ab. Diese benötigen wir später.

Jetzt kommt zunächst die WiringPi Bibliothek ins Spiel. Damit wir das Webradio von aussen steuern können, benötigen wir wenigstens 3 Tasten und eine LED. Die drei Tasten bekommen die Funktionen Start/Stop und Next (Nächstes Element der Playlist), eine Taste halten wir uns frei, damit wir später die Weckerfunktionen steuern können, die wir in einer der nächsten Folgen behandeln.

Verdrahten wir also unseren Raspberry GPIO Port wie folgt:

Raspberry Buttons

Raspberry Buttons

Die Pins 1,3, und 5 vom Stecker SV1 verbinden wir noch mittels dreier 10kOhm WIderstände mit Plus. Ich habe dies über einen Wannenstecker gelöst, damit ich die Buttons auf einer externen Platine montieren kann, die an die Oberseite des Gehäuses befestigt wird. Dazu in einer der späteren Folgen mehr.

Wenden wir uns der Software zu und erstellen uns ein kleines C-Programm, daß die Tasten auswertet und die LED schaltet.

#include <stdio.h>
#include <wiringPi.h>
#include <stdlib.h>
#include <syslog.h>

#define DEBOUNCE_TIME   150

int debounceTime = 0;

int on = 0;

void startStopRadio(void) {

        if (millis () < debounceTime) {
                debounceTime = millis () + DEBOUNCE_TIME ;
                return ;
        }

        if (on) {
                on = 0;
                system("mpc stop");
                syslog(LOG_NOTICE,"On.\n");
        }
        else {
                on = 1;
                system("mpc play");
                syslog(LOG_NOTICE,"Off.\n");
        }

        digitalWrite(1,on);

    while (digitalRead (0) == LOW)
                delay(1) ;

    debounceTime = millis () + DEBOUNCE_TIME ;

}

void nextSong(void) {

        if (millis () < debounceTime) {
                debounceTime = millis () + DEBOUNCE_TIME ;
                return;
        }

        // action

        system("mpc next");
        syslog(LOG_NOTICE,"Next song.\n");

        while (digitalRead (3) == LOW)
                delay(1);

        debounceTime = millis () + DEBOUNCE_TIME;
}

int main (int argc, char** argv)
{
        setlogmask (LOG_UPTO (LOG_NOTICE));

        openlog (NULL, LOG_CONS | LOG_PID | LOG_NDELAY, LOG_LOCAL1);
        syslog (LOG_NOTICE, "Webradio control started by User %d", getuid ());

        int pin;

    if (wiringPiSetup () == -1) {
        syslog(LOG_NOTICE,"wiringPiSetup failed.\n");
                return 1 ;
        }

        system("gpio edge 0 falling");
        system("gpio edge 3 falling");

        pinMode(0,INPUT);
        pinMode(1,OUTPUT);
        pinMode(3,INPUT);

        if (wiringPiISR (0, INT_EDGE_FALLING, &startStopRadio) < 0) {
                syslog(LOG_NOTICE,"wiringPiISR for pin 0 failed.\n");
                return 1;
        }

        if (wiringPiISR (3, INT_EDGE_FALLING, &nextSong) < 0) {
                syslog(LOG_NOTICE,"wiringPiISR for pin 3 failed.\n");
                return 1;
        }

        for(;;){
                delay(100);
        }

        return 0;

}

Die beiden Funktionen startStopRadio und nextSong werden in Zeile 78 und 83 mit wiringPiISR an die fallende Flanke der jeweiligen Pins gebunden. Das heisst, daß jedesmal wenn der Pegel an den entsprechenden Pins auf LOW wechselt, wird ein Interrupt ausgelöst und die jeweilige Funktion aufgerufen.Wir erinnern uns, daß wir die Pins der Taster ja auf Masse gelegt haben und mit den Tastern auf Plus "ziehen".

Ansonsten läuft das Programm nun in einer Endlosschleife und wartet auf Ereignisse. In der Funktion startStopRadio wird auch noch die LED eingeschaltet, wenn das Webradio läuft.

Das Ganze speichern wir nun als webradio.c (auch wieder im Ordner /home/pi/webradio) und kompilieren mit

gcc -o webradio webradio.c -lwiringPi

Jetzt fehlt uns nur noch ein Skript, was das Programm beim Systemstart als Dienst startet:

#! /bin/sh
### BEGIN INIT INFO
# Provides:          webradio
# Required-Start:    $all
# Required-Stop:     $remote_fs $syslog
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: Webradio
# Description:       Webradio button control
### END INIT INFO

# Author: Matthias Pueski <matthias@pueski.de>
#

# Do NOT "set -e"

# PATH should only include /usr/* if it runs after the mountnfs.sh script
PATH=/sbin:/usr/sbin:/bin:/usr/bin
DESC="Webradio"
NAME=webradio
DAEMON=/usr/sbin/$NAME
DAEMON_ARGS=""
PIDFILE=/var/run/$NAME.pid
SCRIPTNAME=/etc/init.d/$NAME

# Exit if the package is not installed
[ -x "$DAEMON" ] || exit 0

# Read configuration variable file if it is present
[ -r /etc/default/$NAME ] && . /etc/default/$NAME

# Load the VERBOSE setting and other rcS variables
. /lib/init/vars.sh

# Define LSB log_* functions.
# Depend on lsb-base (>= 3.2-14) to ensure that this file is present
# and status_of_proc is working.
. /lib/lsb/init-functions

#
# Function that starts the daemon/service
#
do_start()
{
        # Return
        #   0 if daemon has been started
        #   1 if daemon was already running
        #   2 if daemon could not be started
        start-stop-daemon -b --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \
                || return 1
        start-stop-daemon -b --start --quiet --pidfile $PIDFILE --exec $DAEMON -- \
                $DAEMON_ARGS \
                || return 2
        # Add code here, if necessary, that waits for the process to be ready
        # to handle requests from services started subsequently which depend
        # on this one.  As a last resort, sleep for some time.

        mpc clear
        mpc load /home/pi/webradio/webradio.pls

}

#
# Function that stops the daemon/service
#
do_stop()
{
        # Return
        #   0 if daemon has been stopped
        #   1 if daemon was already stopped
        #   2 if daemon could not be stopped
        #   other if a failure occurred
        start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE --name $NAME
        RETVAL="$?"
        [ "$RETVAL" = 2 ] && return 2
        # Wait for children to finish too if this is a daemon that forks
        # and if the daemon is only ever run from this initscript.
        # If the above conditions are not satisfied then add some other code
        # that waits for the process to drop all resources that could be
        # needed by services started subsequently.  A last resort is to
        # sleep for some time.
        start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON
        [ "$?" = 2 ] && return 2
        # Many daemons don't delete their pidfiles when they exit.
        rm -f $PIDFILE
        return "$RETVAL"
}

#
# Function that sends a SIGHUP to the daemon/service
#
do_reload() {
        #
        # If the daemon can reload its configuration without
        # restarting (for example, when it is sent a SIGHUP),
        # then implement that here.
        #
        start-stop-daemon --stop --signal 1 --quiet --pidfile $PIDFILE --name $NAME
        return 0
}

case "$1" in
  start)
        [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME"
        do_start
        case "$?" in
                0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
                2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
        esac
        ;;
  stop)
        [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
        do_stop
        case "$?" in
                0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
                2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
        esac
        ;;
  status)
        status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $?
        ;;
  #reload|force-reload)
        #
        # If do_reload() is not implemented then leave this commented out
        # and leave 'force-reload' as an alias for 'restart'.
        #
        #log_daemon_msg "Reloading $DESC" "$NAME"
        #do_reload
        #log_end_msg $?
        #;;
  restart|force-reload)
        #
        # If the "reload" option is implemented then remove the
        # 'force-reload' alias
        #
        log_daemon_msg "Restarting $DESC" "$NAME"
        do_stop
        case "$?" in
          0|1)
                do_start
                case "$?" in
                        0) log_end_msg 0 ;;
                        1) log_end_msg 1 ;; # Old process is still running
                        *) log_end_msg 1 ;; # Failed to start
                esac
                ;;
          *)
                # Failed to stop
                log_end_msg 1
                ;;
        esac
        ;;
  *)
        #echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload}" >&2
        echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2
        exit 3
        ;;
esac

:

Dieses Skript liegt übrigens als Vorlage unter /etc/init.d/skeleton und kann für jeden beliebigen Zweck angepasst werden. Hierzu kopiert man sich einfach das Skript und passt die Variablen an seine Zwecke an. Man kopiert nun das Skript nach /etc/init.d und das Program nach /usr/sbin und führt folgenden Befehl aus:

update-rc.d webradio defaults

Jetzt wird das Skript nach jedem Start automatisch geladen und startet das Webradio Program im Hintergrund. Starten können wir es natürlich jetzt schon mit:

/etc/init.d/webradio start

Nun sollte sich das Radio mittels Taster starten lassen und die LED einschalten. Ob alles klappt, kann man sich mit

tail -f /var/log/syslog

ausgeben lassen. Falls es nicht funktioniert, muss man natürlich auf Fehlersuche gehen.

Das war es für heute. In der nächsten Folge löten wir uns die Uhrenplatine zusammen, die ich bereits habe fertigen lassen und machen uns Gedanken über die Alarmfunktionen.

Bis dahin wünsche ich allen Lesern fröhliches Basteln. 😉


(c) 2016 Matthias Pueski