AdSense

Sonntag, 29. September 2013

Raspberry PI - C++ - Socket öffnen

(English version) Heute will ich einen kleinen Post zum Thema Socket öffnen in C++ schreiben. Der Grund dafür ist, dass mein Raspberry PI vieles ansteuert, ausliest, etc. Das dann alles über Dateien zu regeln ist keine so schöne Lösung, daher will ich direkt über das Netzwerk mit dem Raspberry PI reden können. Dafür muss am Raspberry PI ein Netzwerk-Socket aufgemacht werden. Vieles von dem Code habe ich einfach aus anderen Quellen übernommen, da ich ja nur das Empfangen und Senden der Nachricht benötige. Außerdem habe ich die Nachrichtenlänge auf 256 Bytes festgesetzt. Das Ganze geht folgendermaßen:

//define variables 
int sockfd, newsockfd, portno = 82; //number of port
socklen_t clilen;
char buffer[256];
struct sockaddr_in serv_addr, cli_addr;
int n; 
 
//create socket 
sockfd = socket(AF_INET, SOCK_STREAM, 0);

bzero((char *) &serv_addr, sizeof(serv_addr));

serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(portno);

bind(sockfd, (struct sockaddr *) &serv_addr,
        sizeof(serv_addr));
listen(sockfd,5);
clilen = sizeof(cli_addr); 
 
//accept tcp clients 
while(true)
{
  //accept client 
  newsockfd = accept(sockfd, 
            (struct sockaddr *) &cli_addr, 
            &clilen); 
  bzero(buffer,256);
  //read client message into buffer
  n = read(newsockfd,buffer,256);
  //write message to client
  n = write(newsockfd,"I got your message, this should be 256 bytes long",256);

  close(newsockfd);
}
close(sockfd);

Freitag, 27. September 2013

Raspberry PI - C++ - Threads

(English version) Threads in C# zu implementieren ist total einfach. Will man jedoch auf dem Raspberry PI soetwas mit C++ haben, so muss man ein paar Kleinigkeiten beachten. Ein Thread wird folgendermaßen erzeugt und gestartet, #include <thread> muss ganz oben stehen.

std::thread networkThread (networkCommunication);
networkThread.detach();


Zum Compilieren müssen jetzt aber noch zwei Optionen angegeben werden, sonst funktioniert das nicht:

-std=c++0x
-pthread


Das ganze Programm wird dann wie folgt compiliert:

g++ ./myProgram.c++ -o outputName -std=c++0x -pthread

Damit kann mann dann auch am Raspberry PI Threads erzeugen und lustige Sachen damit machen.

Dienstag, 24. September 2013

Einfluss der Corioliskraft auf ein Scharfschützen-Projektil

(English version) Vor einiger Zeit sah ich den Film Shooter (Wikipedia). Recht am Anfang wird über einen Schuss aus 1,5 km Entfernung gesprochen. Dabei wird auch erwähnt, dass bei diesem Schuss sogar die Corioliskraft eine Rolle spielt. Das will ich heute hier durchrechnen.

Zuerst eine kurze Einführung zum Thema Corioliskraft. Die Corioliskraft zählt zu den sogenannten Scheinkräften, welche in rotierenden Bezugssystemen auftritt. Die Kraft lässt sich recht einfach herleiten, indem man die Bewegungsgleichungen eines freien Teilchens in ein rotierendes Koordinatensystem transformiert (siehe dazu auch z.B. H.-R. Trebin, Theoretische Physik 1, Mechanik). Für meine Rechnung ist dies jedoch nicht vonnöten. Anschaulich erklärt tritt die Kraft auf, weil sich die Erde unter dem Geschoss weiterdreht.

Kommen wir zur Rechnung. Um eine maximale Wirkung der Corioliskraft zu haben gehe ich davon aus, dass der Schuss entlang eines Längengrades ausgeführt wird. Geht man von einem Breitengrad von etwa 45° aus, so ist das Ziel etwa 1 km weiter von der Rotationsachse der Erde entfernt, daher bewegt sich das Ziel senkrecht zur Flugbahn etwas schneller als das Projektil. Nun will ich diese beiden Geschwindigkeiten berechnen. Der Erd-Durchmesser ist etwa 12 730 km. Im 45. Breitengrad entspricht das einem Radius von ziemlich genau 4500 km, also einem Umfang von 28274 km. Das führt bei einer Umlaufzeit von 86400 Sekunden zu einer Geschwindigkeit von 327,245 m/s. Vergrößert sich der Radius nun um 1 km, so ist der neue Umfang 28281 km und die neue Geschwindigkeit 327,326 m/s. Das macht eine Differenz von 0,081 m/s.

Im Film wird von etwa 6-10 Sekunden Flugzeit gesprochen, das würde 0,5 bis 0,8 Metern Abweichung vom Ziel entsprechen. Ich persönlich schätze die Flugzeit jedoch etwas anders, ein Geschoss mit 1000 m/s braucht für die Strecke nur 1,5 Sekunden, das macht dann immerhin 12 cm aus (was bereits über Treffer oder nicht Treffer entscheiden kann).

Real Time Clock DS1307

(English Version) Manchmal benötigt man bei Arduino-Projekten die aktuelle Uhrzeit oder das aktuelle Datum - z.B. wenn man einen Datenlogger bauen möchte. Eine Möglichkeit wäre natürlich, Arduino-intern eine softwarebasierte Uhr zu programmieren. Das hat jedoch den Nachteil, dass es Rechenzeit verbraucht. Der viel größere Nachteil ist jedoch, dass die Uhrzeit verloren ist, wenn der Arduino von der Versorgungsspannung getrennt wird. Die Uhrzeit müsste also bei jeder Inbetriebnahme neu eingestellt werden. Eine Lösung bieten hier Real-Time-Clock-Module wie das "Tiny RTC". Es arbeitet mit einem DS1307-IC und hat eine Stützbatterie, läuft also auch dann weiter, wenn der Arduino nicht mehr unter Strom steht.

Der DS1307 wird über I2C angesprochen - der Anschluss des Moduls an den Arduino ist also denkbar einfach: einfach SDA und SCL mit den entsprechenden Pins am Arduino verbinden sowie die Betriebsspannung anlegen. Wichtig ist dabei, dass die Anschlüsse auf der rechten Seite (also der Seite mit den 7 Pins) verwendet werden. Die Pins auf der anderen Seite dienen zum Anschluss des EEPROM-Chips, der ebenfalls auf dem Modul verbaut ist.
Auf der rechten Seite gibt es noch einen Pin zum Zugriff auf die Batterie-Spannung (BAT) sowie den Pin SQ. Der IC kann ein Rechtecksignal erzeugen, das hier abgegriffen werden kann. Darauf werde ich aber nicht näher eingehen. Auf dem Modul kann außerdem ein Temperatursensor vom Typ DS18B20 aufgelötet werden. Auf den Sensor könnte mit dem letzten verbleibenden Pin DS zugegriffen werden. Aber auch hierauf werde ich in diesem Beitrag nicht weiter eingehen.

Der IC kann Datum und Uhrzeit (Jahr, Monat, Tag, Wochentag, Stunde, Minute, Sekunde) ausgeben. Die Werte werden in einem SRAM-Register gespeichert und können von dort über I2C ausgelesen werden. Das Register ist wie folgt aufgebaut:
In den Bits 0 bis 6 des Registers 0 sind die Sekunden gespeichert, das Bit 7 CH gibt an, ob die Uhr aktiv ist oder nicht. Wird das Bit auf 1 gesetzt, wird der Takt angehalten und die Uhrzeit wird nicht mehr weiter gezählt. Ist das Bit gelöscht, läuft der Takt und somit auch die Uhr weiter.
Die Bits 0 bis 6 des Registers 1 enthalten die Minuten.
Die Stunden werden im Register 2 gespeichert. Ist das Bit 6 des Registers gesetzt, werden die Stunden im 12h-Format angegeben, das Bit 5 gibt dabei AM(low)/PM(high) an. Ist Bit 6 gelöscht, können die Stunden (wie für Europäer gewohnt) im 24h-Format ausgelesen werden.
Register 3 enthält den aktuellen Wochentag - dabei entspricht 1 Sonntag, 2 Montag usw.
In den Registern 4 bis 6 werden Tag, Monat und Jahr gespeichert.
Register 7 dient zur Konfiguration des Rechtecksignal-Generators. Die restlichen Register können als batteriegestützer RAM verwendet und frei beschrieben werden.

Die Zahlen werden im BCD-Format (Binary Coded Decimal) ausgegeben. Dabei geben die Bits 0 bis 3 eines Bytes die Einer und die restlichen Bits die Zehner einer Zahl aus. Die Zahl 18 wäre somit also 0001'1000, die Zahl 34 entspräche 0011'0100 und so weiter.

Der DS1307 kann über die Adresse 0x68 im I2C-Bus angesprochen werden.

Bei der ersten Inbetriebnahme des Moduls muss die Uhrzeit eingestellt werden.
#include "Wire.h"

#define DS1307_ADDRESS 0x68

void setup(){
  Wire.begin();
    
  Serial.begin(9600);
  
  byte second =      0; //0-59
  byte minute =      44; //0-59
  byte hour =        22; //0-23
  byte weekDay =     1; //1-7
  byte monthDay =    22; //1-31
  byte month =       9; //1-12
  byte year  =       13; //0-99

  Wire.beginTransmission(DS1307_ADDRESS);
    
  Wire.write(0x00); //Pointer auf Register 0
  
  Wire.write(decToBcd(second));
  Wire.write(decToBcd(minute));
  Wire.write(decToBcd(hour));
  Wire.write(decToBcd(weekDay));
  Wire.write(decToBcd(monthDay));
  Wire.write(decToBcd(month));
  Wire.write(decToBcd(year));

  Wire.endTransmission();
  
}

void loop(){
}

byte decToBcd(byte val){
//Dezimal in BCD konvertieren
  return ( (val/10*16) + (val%10) );
}

In den Variablen kann die aktuelle Uhrzeit bzw. das Datum eingestellt werden. Mit dem Befehl beginTransmission() wird dann die Übertragung zum Modul gestartet. Dzu wird der Pointer auf das Register 0 (das mit den Sekunden) gesetzt. Nun können Uhrzeit und Datum übertragen werden. Dazu werden die in den Variablen gespeicherten Werte zuerst vom Dezimal- ins BCD-Format konvertiert und dann mit write() ins Register des ICs geschrieben. Nach dem write()-Befehl springt der Arduino automatisch zum nächsten Register.

Ist der IC einmal richtig eingestellt, zählt er munter bis ans Ende aller Tage (bzw. bis ans Ende der Batterie) die Zeit hoch und kann jederzeit mit folgenden Befehlen ausgelesen werden:

#include "Wire.h"
#define DS1307_ADDRESS 0x68

void setup(){
  Wire.begin();
  Serial.begin(9600);
}

void loop(){
  
  //Pointer auf Register 0 setzen
  Wire.beginTransmission(DS1307_ADDRESS);
  Wire.write(0x00);
  Wire.endTransmission();

  //Zeit abfragen
  Wire.requestFrom(DS1307_ADDRESS, 7);

  int second = bcdToDec(Wire.read());
  int minute = bcdToDec(Wire.read());
  int hour = bcdToDec(Wire.read());
  int weekDay = bcdToDec(Wire.read());
  int monthDay = bcdToDec(Wire.read());
  int month = bcdToDec(Wire.read());
  int year = bcdToDec(Wire.read());

  //Datum ausgeben im Format dd.mm.yy - hh:mm:ss
  Serial.print(monthDay);
  Serial.print(".");
  Serial.print(month);
  Serial.print(".");
  Serial.print(year);
  Serial.print(" - ");
  Serial.print(hour);
  Serial.print(":");
  Serial.print(minute);
  Serial.print(":");
  Serial.println(second);
  delay(1000);
}

byte bcdToDec(byte val)  {
//BCD in Dezimal konvertieren
  return ( (val/16*10) + (val%16) );
}

In der loop() wird zuerst der Pointer auf das Register 0 gesetzt (wir wollen als erstes die Sekunden auslesen), dann werden mit dem Befehl requestFrom() aus diesem und den folgenden Registern 7 Bytes angefordert. Diese werden dann mit dem read()-Befehl empfangen, vom BCD- ins Dezimal-Format konvertiert und zum Schluss über die serielle Schnittstelle ausgegeben.

Mittwoch, 18. September 2013

ATMega - Alles um den Faktor 8 langsamer?

(English version) Genau dieses Problem trat bei mir auf: Ich habe einen ATMega 1284-P welcher mit einem 16 MHz Quarz läuft. Allerdings lief alles um den Faktor 8 zu langsam. Die Fuses (Ich benutze AVR Studio 4, da sieht man nicht alles) waren alle korrekt eingestellt. Nach einiger Zeit auf Google kam ich endlich auf die Lösung: Das Fuse-Bit CKDIV8 ist 0, also wird die Clock durch 8 geteilt. Leider kann man auf dieses Fuse-Bit im AVR Studio 4 nicht direkt zugreifen, es gibt jedoch eine recht einfache Möglichkeit, den Clock Divider auszuschalten:

#include <avr/power.h> 

Und dann direkt am Anfang vom Programm:

clock_prescale_set(clock_div_1);

Damit war bei mir das Problem dann behoben.

Dienstag, 17. September 2013

Musikplayer - Handy-Fernsteuerung

(English version) Wie in diesem Post beschrieben habe ich einen eigenen Musikplayer mit C# und WPF geschrieben. Da es bei größeren Wiedergabelisten durchaus vorkommen kann, dass einem mal ein Lied nicht gefällt, gibt es Hotkeys für den PC. Was ist aber, wenn man gerade gemütlich auf dem Bett liegt und man keine Lust hat, aufzustehen? Dafür habe ich eine App geschrieben, mit welcher man die grundlegenden Hotkeys (Play/Pause, nächstes Lied, vorheriges Lied) ausführen kann. Diese App kann man hier herunterladen.

Zur Verwendung der App: Die App kann man einerseits starten und dann auf die entsprechenden Buttons klicken, es gibt jedoch auch ein Widget, welches den Klick zum App Öffnen spart. Die App bietet jedoch mehr Funktionen, sogar das suchen nach einem Lied ist möglich.

In der App muss man nun IP, Port (Der Player macht standardmäßig einen Socket auf Port 7200 auf) und Passwort (welches man im Player mit F2 festlegen kann) eingeben. Dabei kann die IP lokal sein (z.B. wenn das Handy im selben Netzwerk ist wie das Handy) oder auch eine Internet-IP. Dafür muss man jedoch am Router noch eine entsprechende Port-Weiterleitung einrichten. Das Handy kann den Player in diesem Fall unabhängig davon steuern, ob es im WLAN oder im mobilen Netzwerk ist.

Montag, 16. September 2013

Android - Anwendungsdaten schreiben

(English version) Viele Android-Apps belegen einen gewissen (meist recht geringen) Speicherplatzt für "Daten". Diese Daten sind z.B. zum Speichern von Passwörtern oder ähnlichem nützlich. Ich will nun erklären, wie man auf diese Daten zugreifen kann. Zuerst kommt das Lesen:

SharedPreferences sharedPref = this.getPreferences(Context.MODE_PRIVATE);
String defaultStringValue = "";
String stringValue = sharedPref.getString("nameOfString", defaultStringValue);


Dies ist also analog zu einem C#-Dictionary mit einem String als Key und (hier) einem String als Value. Als Value kann jedoch auch ein Integer benutzt werden, die Syntax ist analog dazu. Existiert dieser Key nicht, so wird defaultStringValue zurückgegeben.

Nun zum Schreiben:

SharedPreferences sharedPref = this.getPreferences(Context.MODE_PRIVATE);
String stringValue = "asdf";
SharedPreferences.Editor editor = sharedPref.edit();
editor.putString("
nameOfString", stringValue);
editor.commit();


Hier kann natürlich auch Int analog dazu verwendet werden. Außerdem können viele editor.put...-Befehle direkt hintereinander stehen und erst am Ende muss das editor.commit(); stehen. Damit sollte der grundlegende Zugriff auf Anwendungsdaten geklärt sein. Falls man von einem Widget aus auf Anwendungsdaten zugreifen will, muss man SharedPreferences benutzen. Was sich im Code ändert ist dann:

SharedPreferences sharedPref = this.getSharedPreferences("nameOfPreferences", 0);

Mittwoch, 11. September 2013

Pin Change Interrupt mit dem ATMega oder Arduino

(English version) Viele Microcontroller (z.B. ein ATMega 168) haben nicht nur einige externe Interrupts sondern auch sogenannte Pin Change Interrups. Diese Interrupts funktionieren auf allen Kanälen. Die Grundidee ist: Wenn sich an einem Kanal was ändert wird eine Interrupt-Funktion aufgerufen. Allerdings ist das hier nicht so bequem wie mit "normalen" Interrupts, man bekommt nämlich nicht gesagt, welcher Kanal sich verändert hat und man kann auch nicht einstellen, auf welche Flanke der Interrupt warten soll, wann immer sich etwas bei einem Kanal ändert wird die Interrupt-Funktion aufgerufen.

Ich beschreibe nun am ATMega168 wie das genau aussieht. Ich benutze die Kanäle PB0, PD5..7 als Interrupt-Kanäle. Um nun die Interrupts anzuschalten gehe ich folgendermaßen vor:

PCICR |= (1 << PCIE0)|(1 << PCIE2);

Das schaltet die Interrupts für PCINT0 und PCINT2 an, also für PORTB und PORTD. Nun muss man die Kanäle definieren, welche diese Interrupts auslösen sollen:

PCMSK0 |= (1 << PCINT0);
PCMSK2 |= (1 << PCINT23)|(1 << PCINT22)|(1 << PCINT21);


Nun muss man nur noch die Interrupts anschalten (sei();). Benutzt man die Arduino-Entwicklungsumgebung, so geschieht all dies in der setup-Funktion. Als nächstes gibt es die Funktionen, welche aufgerufen werden, wenn die Interrupts ausgelöst werden. Diese müssen folgendermaßen heißen:

ISR(PCINT2_vect)
{
}


und

ISR(PCINT0_vect)
{
}


Um nun herauszufinden, welcher Kanal einen Interrupt hatte, kann man die Kanäle auslesen und nachschauen ob sich etwas verändert hat. Oder wenn man einfach 4 Schalter über Interrupts abfragen will, so kann man auslesen welcher Schalter gedrückt wurde:

int Button0 = digitalRead(8); 
int Button1 = digitalRead(7); 
int Button2 = digitalRead(6);  
int Button3 = digitalRead(5);

Montag, 9. September 2013

Vergleich verschiedener Leuchtmittel

(English version) Heute möchte ich einige Leuchtmittel, welche als Zimmerbeleuchtung dienen könnten, vergleichen. Die Kandidaten sind:
a) Eine "alte" Halogenlampe
b) Eine spiralförmige Energiesparlampe (Link)
c) Eine COB-LED (Link)

Zuerst die Spezifikationen:
a) Leistung: 40 W
Lichtstrom: 390 Lum
Leuchtdauer: 2 000 h
Preis: 2 €

b) Leistung: 23 W
Lichtstrom: 1 550 Lum
Leuchtdauer: 10 000 h
Preis: 8 €

c) Leistung: 7 W
Lichtstrom: 560 Lum
Leuchtdauer: >10 000 h (keine Angabe auf Pollin.de, vermutlich jedoch 25 000 h)
Preis: 6 € (Mit Kühlkörper, die LED an sich nur 3 €)

Der erste Vergleichspunkt ist natürlich: Wie viel Licht bekommt man bei welcher Leistung? Ich gebe hier die Werte in Lum/W an:
a) 10
b) 67
c) 80
Dass hier die LED und die Energiesparlampe gewinnen, ist klar.

Nun aber zum nächsten Punk: Was kostet so eine Lampe überhaupt? Bzw. wie viel Licht bekommtn man pro Euro? Hier die Werte in Lum/€ an:
a) 195
b) 194
c) 95
Hier verliert die LED, da sie einfach für die erbrachte Leistung teurer ist als die anderen Lampen.

Als entscheidenden Vergleichspunkt will ich nun ein Zimmer mit den entsprechenden Lampen 5 Jahre lang beleuchten. Geht man von 5000 Lum aus und einer Brenndauer von 6 Stunden pro Tag, so sind das im Jahr ganz grob 2000 Stunden. Von den einzelnen Lampen bräuchte man etwa:
a) 13
b) 3
c) 9
Berücksichtigt man die Lebensdauer, so müssen alle Halogenlampen jedes Jahr getauscht werden, während die Energiesparlampen und die LEDs alle 5 Jahre durchstehen. Das erhöht die Kosten für die Halogenlampen um den Faktor 5. Die Anschaffungskosten für die Lampen betragen in Euro:
a) 128
b) 26
c) 53
allerdings muss bei der LED berücksichtigt werden, dass die Hälfte der Kosten für den Kühlkörper sind. Als nächstes nun der Stromverbrauch:
a) 1026 kWh
b) 148 kWh
c) 125 kWh
Geht man von 20 ct / kWh aus, so kommt man auf Kosten für 5 Jahre in Euro:
a) 205
b) 30
c) 25

Am günstigsten ist also die Energiesparlampe. Jedoch muss bei der LED beachtet werden: Die Anschaffungskosten sind eigentlich nur halb so hoch wegen den Kühlkörpern, ebenfalls hält eine LED vermutlich deutlich länger als eine Energiesparlampe, sodass auf längere Sicht die LED vorne liegt, da sie außerdem noch einen etwas geringeren Stromverbrauch hat. Dass die Halogenlampe überhaupt nicht mitreden kann sollte klar sein.

LED Zimmerbeleuchtung - selbst gemacht

(English version) Bei Pollin gibt es seit einiger zeit sogenannte COB-LEDs (Pollin). Das Tolle an diesen LEDs ist, dass sie nichts an Vorwiderständen oder ähnlichem brauchen sondern einfach eine Spannungsquelle (bei mir ist das ein Laptop-Netzteil mit 24V) und ein Kühlkörper, welchen es auch bei Pollin gibt.
Mit 560 lm ist die LED ziemlich hell, ich werde in einem separaten Post einige Leuchtquellen vergleichen. Was an LED-Leuchtquellen sehr gut ist: Sie sind effektiv und laufen lange, daher wird die Zukunft vielleicht den LED-Lampen gehören. Das einzige Problem, welches es bei dieser LED gibt ist, dass sie auf relativ kleiner Fläche sehr hell ist und daher sogar als eventuell gefährlich für das Auge gilt. Daher musste ich mir einen Lampenschirm basteln. Getan habe ich dies aus Pergamin (Butterbrotpapier). Die Bastelanleitung ist auch ganz einfach, man sollte lediglich beachten, dass b > a/2 sein muss, sonst gibt es ein Loch in der Mitte:
An den roten Kanten wird eingeschnitten. Danach an den senkrechten gestrichelten Linien um 90° geknickt, sodass das Papier einen geschlossenen Zylinder ergibt. Dann werden die Laschen oben noch umgeknickt und es ergibt sich eine Art "Eimer", welcher über die Lampe gestülpt werden kann. Das Endergebnis sieht dann so aus:

Sonntag, 8. September 2013

SD-Karten mit dem Arduino beschreiben und auslesen

Wer seinen Arduino als Datenlogger nutzt, um z.B. einen Temperaturverlauf auf zu zeichnen, steht schnell vor dem Problem, wie die erfassten Daten gespeichert werden sollen. Natürlich könnte man alle Daten irgendwie in einem Array speichern und dann per serielle Schnittstelle an den PC übertragen. Doch hier ist der Speicher begrenzt und fällt einmal der Strom aus, sind die Daten futsch. Außerdem benötigt man zusätzliche Software, um die Daten von der seriellen Schnittstelle in Empfang zu nehmen und aus zu werten. Das Problem mit dem flüchtigen Speicher könnte man umgehen, wenn man die Daten im EEPROM des Arduinos speichert - doch auch hier ist der Speicherplatz und vorallem die Zahl der Schreibzyklen begrenzt. Auch das Problem mit der Übertragung bleibt bestehen.

Am Besten speichert man seine Daten daher auf einer Speicherkarte. Hier sind die Daten auch bei Stromausfall sicher gespeichert, außerdem lässt sich die Speicherkarte direkt mit dem PC verbinden und auslesen. Wenn man die Daten im richtigen Format speichert, kann man sie auch gleich mit Excel, Matlab oder ähnlichen Programmen auswerten.

Hardware

Bei Ebay gibt es SD-Karten-Module bereits für einen Euro. Ich habe mich für das Modul von LC Technology entschieden:
Die Pinbelegung ist klar ersichtlich. Neben Pins für die Spannungsversorgung (+5V, +3,3V und GND) gibt es Pins für den SPI-Bus, mit dem auf die Speicherkarte zugegriffen werden kann. Die Pins sind zwar zweireihig angeordnet, die obere und die untere Reihe sind aber jeweils intern verbunden. SD-Karten arbeiten mit einem Spannungspegel von 3,3V. Das Modul kann wahlweise mit 3,3V oder mit 5V versorgt werden. Im letzteren Fall bringt ein eingebauter Spannungswandler die Spannung automatisch auf 3,3V. Die SPI-Pins werden allerdings NICHT gewandelt, sie dürfen also auch NICHT direkt mit dem Arduino verbunden werden - sonst wird die SD-Karte gegrillt. Hier muss also ein Pegelwandler zwischen geschaltet werden.

Der fertige Aufbau sieht so aus:



Das Modul wird über den 5V-Ausgang vom Arduino versorgt. Die SPI-Pins werden über den Pegelwandler wie folgt mit dem Arduino verbunden:
MOSI an Pin 11
MISO an Pin 12
SCK an Pin 13
CS an Pin 4

Ich habe einen bi-direktionalen Wandler genommen und lasse alle Pins über den Wandler laufen. Beides ist streng genommen nicht nötig. Theoretisch reicht auch ein Pegelwandler, der nur in eine Richtung von 5V auf 3,3V wandelt (z.B. den 74HC4050). Über diesen Wandler laufen dann nur die Pins MOSI, SCK und CS. Der MISO-Pin kann direkt mit dem Arduino verbunden werden.

Ist alles richtig verkabelt, kann die SD-Karte in das Modul gesteckt werden. Der Arduino kann mit SD-Karten im Format FAT16 und FAT32 umgehen, laut Arduino-Referenz wird FAT16 allerdings bevorzugt.

Software

Die SD-Library für Arduino liefert einige Beispiele, mit der die Funktion des Aufbaus gleich getestet werden kann. Ich habe mich für das Beispiel "Datalogger" entschieden. Im Beispiel werden die Werte der drei analogen Eingänge in einer txt-Datei auf der Karte gespeichert.

#include <SD.h>

const int chipSelect = 4;

void setup()
{
 // Open serial communications and wait for port to open:
  Serial.begin(9600);
  
  Serial.print("Initializing SD card...");
  // make sure that the default chip select pin is set to
  // output, even if you don't use it:
  pinMode(10, OUTPUT);
  
  // see if the card is present and can be initialized:
  if (!SD.begin(chipSelect)) {
    Serial.println("Card failed, or not present");
    // don't do anything more:
    return;
  }
  Serial.println("card initialized.");
}

void loop()
{
  // make a string for assembling the data to log:
  String dataString = "";

  // read three sensors and append to the string:
  for (int analogPin = 0; analogPin < 3; analogPin++) {
    int sensor = analogRead(analogPin);
    dataString += String(sensor);
    if (analogPin < 2) {
      dataString += ","; 
    }
  }

  // open the file. note that only one file can be open at a time,
  // so you have to close this one before opening another.
  File dataFile = SD.open("datalog.txt", FILE_WRITE);

  // if the file is available, write to it:
  if (dataFile) {
    dataFile.println(dataString);
    dataFile.close();
    // print to the serial port too:
    Serial.println(dataString);
  }  
  // if the file isn't open, pop up an error:
  else {
    Serial.println("error opening datalog.txt");
  } 
}

In der setup()-Funktion wird zuerst eine serielle Verbindung aufgebaut, mit dem Befehl SD.begin(chipSelect) wird dann die SD-Karte initialisiert. Als Parameter wird hier der Pin übergeben, an dem CS angeschlossen ist. War die Initialisierung erfolgreich, geht es in den loop()-Funktion weiter. Hier werden die Werte der analogen Eingänge ausgelesen und in einer String-Variable gespeichert . Mit dem Befehl SD.open() wird dann die Datei "datalog.txt" im Schreib-Modus geöffnet.  Ist das Öffnen erfolgreich, wird der Wert der String-Variable in die Datei geschrieben. Dann wird die Datei wieder geschlossen.

Ähnlich einfach ist das Auslesen von Dateien.

  dataFile = SD.open("test.txt");
  if (dataFile) {
    Serial.println("test.txt:");
    
    // read from the file until there's nothing else in it:
    while (dataFile.available()) {
     Serial.write(dataFile.read());
    }
    // close the file:
    data.close();
  } else {
   // if the file didn't open, print an error:
    Serial.println("error opening test.txt");
  }

 Auch hier muss zuerst wieder die Datei mit dem open()-Befehl geöffnet werden. Wurde die Datei erfolgreich geöffnet, kann sie mit dem Befehl read() Zeile für Zeile ausgelesen werden, bis das Ende der Datei erreich wurde. Dies wird mit dem Befehl available() überprüft. Abschließend wird die Datei mit dem close()-Befehl wieder geschlossen.

Freitag, 6. September 2013

C# - Globale Hotkeys, welche wirklich global sind

(English version) Mein Musikplayer benutzt Hotkeys. Diese funktionieren auch relativ gut. Leider nur relativ. Bei einigen Spielen (z.B. League of Legends) funktionieren die Hotkeys nicht mehr und man kann z.B. kein Lied mehr weiterschalten. Um dieses Problem zu beheben habe ich nach einer neuen Möglichkeit gesucht, globale Hotkeys zu implementieren. Schlussendlich habe ich eine Implementierung von meinem Freund von http://csharp-tricks.blogspot.de/ übernommen und sie etwas weiter entwickelt. Die Hotkeys sind nun eine eigene Klasse, welche folgendermaßen benutzt wird:

KeyHook MyHook = new KeyHook(this, KeyboardHookProcedure);
MyHook.Hook(KeyHook.KeyboardHookProc);

KeyHook.KeyPressed += KeyHook_KeyPressed;
KeyHook.KeyReleased += KeyHook_KeyReleased;


Die KeyHook_KeyPressed und KeyHook_KeyReleased Funktionen sehen folgendermaßen aus:

void KeyHook_KeyPressed(int keyCode, List<int> pressedKeys)
{}


keyCode ist einfach ein Integer, welcher Wert für welche Taste steht kann man hier nachlesen. Mehr gibt es zu diesen Hotkeys nicht zu sagen, die Events für KeyPressed und KeyReleased habe ich selber eingebaut, da es davor etwas unsauber gelöst wurde. Nun noch der Code der Klasse KeyHook:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace anyNamespace
{
    public class KeyHook
    {
        public delegate int HookProc(int nCode, IntPtr wParam, IntPtr lParam);

        //Declare hook handle as int.
        static int hHook = 0;

        //Declare keyboard hook constant.
        //For other hook types, you can obtain these values from Winuser.h in Microsoft SDK.
        const int WH_KEYBOARD_LL = 13;

        public delegate void KeyPressedEventHandler(int keyCode, List<int> pressedKeys);
        public static event KeyPressedEventHandler KeyPressed;

        public delegate void KeyReleasedEventHandler(int keyCode, List<int> pressedKeys);
        public static event KeyReleasedEventHandler KeyReleased;

        [StructLayout(LayoutKind.Sequential)]
        private class keyboardHookStruct
        {
            public int vkCode;
            public int scanCode;
            public int flags;
            public int time;
            public int dwExtraInfo;
        }

        //Import for SetWindowsHookEx function.
        //Use this function to install thread-specific hook.
        [DllImport("user32.dll", CharSet = CharSet.Auto,
         CallingConvention = CallingConvention.StdCall)]
        private static extern int SetWindowsHookEx(int idHook, HookProc lpfn,
        IntPtr hInstance, int threadId);

        //Import for UnhookWindowsHookEx.
        //Call this function to uninstall the hook.
        [DllImport("user32.dll", CharSet = CharSet.Auto,
         CallingConvention = CallingConvention.StdCall)]
        private static extern bool UnhookWindowsHookEx(int idHook);

        //Import for CallNextHookEx.
        //Use this function to pass the hook information to next hook procedure in chain.
        [DllImport("user32.dll", CharSet = CharSet.Auto,
         CallingConvention = CallingConvention.StdCall)]
        private static extern int CallNextHookEx(int idHook, int nCode,
        IntPtr wParam, IntPtr lParam);

        [DllImport("kernel32.dll")]
        static extern IntPtr LoadLibrary(string lpFileName);

        //static Form1 View;
        static MainWindow mainWindow;

        const int MOD_SHIFT = 0x0004;

        IntPtr LL = (IntPtr)LoadLibrary("User32");

        public KeyHook(MainWindow _mainWindow, HookProc proc)
        {
            mainWindow = _mainWindow;
            Hook(proc);

        }

        ~KeyHook()
        {
            UnHook();
        }

        HookProc _proc;
        public int Hook(HookProc proc)
        {
            _proc = proc;
            hHook = SetWindowsHookEx(WH_KEYBOARD_LL, _proc, LL, 0);
            return hHook;
        }

        public bool UnHook()
        {
            bool ret = UnhookWindowsHookEx(hHook);
            if (ret)
                hHook = 0;
            return ret;
        }

        public static List<int> pressedKeys = new List<int>();

        public static int KeyboardHookProc(int nCode, IntPtr wParam, IntPtr lParam)
        {
            if (nCode < 0)
            {
                return CallNextHookEx(hHook, nCode, wParam, lParam);
            }
            else
            {
                keyboardHookStruct MyKeyboardHookStruct = (keyboardHookStruct)Marshal.PtrToStructure(lParam, typeof(keyboardHookStruct));
                if ((MyKeyboardHookStruct.flags & 128) == 128)
                {
                    if (pressedKeys.Contains(MyKeyboardHookStruct.vkCode))
                    {
                        pressedKeys.Remove(MyKeyboardHookStruct.vkCode);
                        if (KeyReleased != null)
                        {
                            KeyReleased(MyKeyboardHookStruct.vkCode, pressedKeys);
                        }
                    }
                }
                if ((MyKeyboardHookStruct.flags & 128) == 0)
                {
                    if (!pressedKeys.Contains(MyKeyboardHookStruct.vkCode))
                    {
                        pressedKeys.Add(MyKeyboardHookStruct.vkCode);
                        if (KeyPressed != null)
                        {
                            KeyPressed(MyKeyboardHookStruct.vkCode, pressedKeys);
                        }
                    }
                }
                return CallNextHookEx(hHook, nCode, wParam, lParam);
            }
        }
    }
}

Montag, 2. September 2013

C# WPF - Invoken

(English version) Im vorigen Post taucht das Problem auf: Ich darf auf bestimmte Grafik-Sachen nur aus dem Haupt-Thread zugreifen. Dummerweise kann es jedoch immer vorkommen, dass man auch aus einem anderen Thread etwas tun will. Die Lösung dafür heißt: Invoken. Wie das geht zeige ich nun. Zuerst ein Beispiel, wie etwas nicht funktioniert:

void threadActivity()
{
    Thread.Sleep(5000);
    TaskbarItemInfo.ProgressValue = 0.2;
}


Sobald 5 Sekunden um sind wird ein Fehler auftreten: InvalidOperationException wurde nicht behandelt. Der aufrufende Thread kann nicht auf dieses Objekt zugreifen, da sich das Objekt im Besitz eines anderen Threads befindet. Wie schon angekündigt heißt die Lösung hierfür: Invoken. Der veränderte Code sieht nun folgendermaßen aus:

void threadActivity()
{
    Thread.Sleep(5000);
    Application.Current.Dispatcher.BeginInvoke(new Action(() =>
    {
        TaskbarItemInfo.ProgressValue = 0.2;
    }));
}


Sobald nun 5 Sekunden um sind wird sich der Fortschrittsbalken in der Taskleiste auf den Wert 0.2 einstellen. Zur Syntax: alles in den inneren geschweiften Klammern wird im Haupt-Thread ausgeführt, innerhalb der Klammern kann man auf Variablen, auf welche man außerhalb der geschweiften Klammern zugreifen kann, auch zugreifen. Auch darf ruhig deutlich mehr als nur ein Befehl darin stehen (wobei es ratsamer ist, nur möglichst kurze Code-Bausteine zu invoken).

C# WPF - Fortschrittsbalken in der Taskleiste anzeigen

(English version) Bei Downloads mit dem Firefox unter Windows 7 wird in der Taskleiste ein Fortschrittsbalken angezeigt. Das selber zu realisieren ist total einfach. In dem entsprechenden Fenster muss

<Window.TaskbarItemInfo>
    <TaskbarItemInfo/>
</Window.TaskbarItemInfo>


eingefügt werden, direkt vor dem standartmäßigen <Grid>. Damit kann man nun auf TaskbarItemInfo zugreifen. Das sieht dann z.B. folgendermaßen aus:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();


        TaskbarItemInfo.ProgressState = System.Windows.Shell.TaskbarItemProgressState.Normal;
 
        Loaded += MainWindow_Loaded;
    }

    void MainWindow_Loaded(object sender, RoutedEventArgs e)
    {
        TaskbarItemInfo.ProgressValue = 0.5;

    }
}


Man kann nun auch verschiedene ProgressStates benutzen, dementsprechend verändert der Fortschrittsbalken seine Farbe. Ebenfalls kann man über TaskbarItemInfo auch ein Symbol in der Taskleiste festlegen.

Will man all dies jedoch aus einem anderen Thread tun, so wird es nicht funktionieren. Warum das so ist erkläre ich in diesem Post. Hier nur schnell die Lösung dafür. Statt

TaskbarItemInfo.ProgressValue = 0.2

benutzt man einfach

Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
    TaskbarItemInfo.ProgressValue = 0.2;
}));

Android - Widget "stürzt ab" bei Drehung des Bildschirmes

(English version) Die genaue Fehlerbeschreibung ist: Ich habe ein Widget, wenn ich drauf klicke, möchte ich, dass sich die entsprechende App öffnet. Das funktioniert auch meistens. Manchmal passiert aber bei einem Klick überhaupt nichts, meistens nachdem der Bildschirm gedreht wurde. Schuld daran ist, dass bei manchen Sachen (z.B. Bildschirm drehen) die onUpdate-Funktion nicht aufgerufen wird, sondern lediglich die onReceive-Funktion. Da ich in der onUpdate-Funktion folgende Zeilen drin hatte

Intent intent = new Intent(context, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);

remoteViews.setOnClickPendingIntent(R.id.button, pendingIntent);


wurde normalerweise die MainActivity gestartet. Nicht aber, wenn onReceive aufgerufen wurde. Daher musste ich in die onReceive-Funktion diese drei Zeilen mit hinein nehmen (intent habe ich in localIntent umbenannt, da onReceive als Parameter bei mir Intent intent hat). Danach funktioniert alles wunderbar.