Raspberry Pi: Web Socket

setup.jpg

Ein neues Tutorial mit dem Pi! Diesmal geht’s um Web-Sockets, nur besser erklärt als beim älteren Post, der mit dem ESP8266 zu tun hatte.

Um was geht’s? Sehr oft findet man Software, die man über den Webbrowser bedient, wie etwa Octoprint, metis oder Eiger (um auch einen kommerziell genutzten Kandidaten zu nennen). Hier zeige ich, wie man das ganze mit Web-Sockets, Java Script und Python realisieren kann.

Zudem gibt’s auch eine Anbindung an die Hardware des Raspberry, um nicht nur im virtuellen Raum zu bleiben.

Mehr dazu findet ihr auf deloarts.com.

Advertisements

Raspberry Pi: GPIO

setup.jpg

Die GPIO-Pins am Raspberry sind schon recht praktisch. Bei nicht-zeitkritischen Anwendungen erspart man sich den externen Mikrocontroller. Außerdem kann man die Pins mit der passenden Lib einem Event hinzufügen, wodurch ein asynchroner Aufbau überaus einfach möglich wird.

Neugierig?

Mehr gibt’s auf deloarts.com.

 


 

Ja, ich weiß: Manchen ist es lieber, den Content nur am Blog zu lassen. Aber wegen der starken Einschränkungen von WordPress was Videos, Prism und Dateien betrifft habe ich letzten Endes doch beschlossen, die Informationen rein auf meine Website zu verlegen und WordPress als ‚Werbung‘, ‚Vorschau‘ und als den Platz für Kommentare zu nutzen.

Nach und nach werde ich auch bestehende Beiträge vollständig auf die Website migrieren. Das mache ich vor allem wegen der nicht vorhandenen Möglichkeit Code ordentlich posten zu können.

Wer zu dieser Lösung einen Kommentar hat: Ich bin gespannt!

3D Printing: OctoPrint on Orange Pi Zero

 

header

Still alive. Zur Zeit ist’s recht ruhig auf den Blogs, was Arduino, Raspberry oder andere derlei Spielereien anbelangt. Vielleicht weil’s der Industrie gerade gut geht und die Leute mehr arbeiten anstatt zu posten? Wie dem auch sei, ich habe mal wieder die Zeit gefunden.

Etwas seltsam ist es schon, ich 3Drucke (ha ha …) mittlerweile schon sehr lange und habe immer noch keinen einzigen Post über das Thema gemacht. Deshalb fange ich mal mit einem Nischenthema an: Ein Tutorial zum Druckhost. Genauer: Es geht um die Installation von OctoPrint auf einem Orange Pi Zero.

Alles weitere findet ihr auf meiner Website!

 

Raspberry Pi: Telegram Bot

conversation.jpg

Der letzte Blogeintrag ist bereits ein paar Wochen her. Warum? Zur Zeit arbeite ich an zwei sehr großen Projekten, die ich, sobald ich sie abgeschlossen habe, hier vorstellen werde. Das eine Projekt hat wieder mit einer Kamera zu tun (nein, diesmal keine Tropfenfotografie). Das andere, welches ich gemeinsam mit ralph mache, hat wieder mit dem ESP-Controller zu tun. Stay tuned.

So viel sei gesagt, hier folgt ein Lückenfüller. Es kommt nichts sonderlich neues, aber wem fad ist, der darf gerne bleiben. Der Titel verrät es schon, es geht um den Raspberry Pi und um Telegram. Das Vorhaben ist, dass man Statusnachrichten vom Pi direkt aufs Smartphone bekommt. Ich finde das immer recht praktisch, weil ich dann nicht erst meinen PC starten muss um dann via Terminal mit dem Pi kommunizieren zu können.

Interessiert? Hier geht’s los!

ESP8266: Telegram-Bot

setup

Einen Mikrocontroller per Messenger Befehle senden oder Daten erhalten? Ja das geht, und es ist auch genau so cool wie es sich anhört! In dem Tutorial baue ich eine WLAN-Türklingel, die euch Nachrichten aufs Smartphone schickt, sobald jemand klingelt.

Außerdem zeige ich weitere Möglichkeiten, die digitalen Ausgänge zu nutzen, Stichwort INPUT_PULLUP.

Also, los geht’s auf meiner Website!

I²C & Bit-Shifting

In letzter Zeit wurde ich häufiger über I²C, beziehungsweise dem damit oft einhergehenden Bit-Shifting gefragt. Konkret meine ich damit den Code der OLED-Lib und dem des Wii-Nunchuk. Beide Geräte unterstützen den I2C-Bus, welcher Thema dieses Eintrages ist. Als Antwort habe ich meistens eine kurze Erklärung, sowie den Verweis auf den Wikipedia-Eintrag zu I²C gegeben. Hier will ich aber etwas genauer darauf eingehen, wie man I²C und Bit-Shifting tatsächlich auf mehreren Controllern nutzt.

setup

(Sorry für das schlechte Bild, hatte im Moment nur meine Handykamera)

Entwickelt wurde der BUS in den 80ern von Phillips. Der genaue Name lautet Inter-Integrated Circuits Bus (IIC), woraus schnell die Abkürzung I²C (i squared c) wurde. Oftmals liest man auch die Abkürzung TWI, diese steht für Two-Wire-Interface. Die Grundzüge sind:

  • Jedes Gerät hat seine eigene 7-bit Adresse. Dies bedeutet es können bis zu 2^7=128 Geräte angeschlossen werden.
  • Eine genaue Taktgeschwindigkeit, um die Daten senden/empfangen zu können.
  • Zwei Leiter, SDA, welcher den Takt vorgibt, und SCL, die Datenleitung.
  • Einen Master, welcher den Takt vorgibt und den Bus administriert.
  • Mehrere Slaves, welche dem Master unterliegen.

Es kann auch mehrere Master geben, Zugriff auf den BUS hat aber immer nur einer, weshalb man sich bei der Verwendung mehrerer Master etwas überlegen muss, damit jeder darauf zugreifen kann – Stichwort: Multi-Master.

Der elektrische Aufbau

Für dieses Beispiel nutze ich die folgenden Bauteile:

Stk. Komponente Ausführung Beschreibung
1 Arduino UNO Master
2 Arduino Nano Slave
2 Widerstand 1kΩ I2C Pull-up
2 Widerstand 220Ω Vorwiderstand LED
1 Widerstand 10kΩ Taster Pull-down
1 Potentiometer 10kΩ
1 Taster

Der folgende Schaltplan zeigt, wie der Aufbau aussehen soll. Wichtig ist, dass man hier nur das Arduino UNO mit Spannung über den USB-Port, den VIN-Pin oder den 7-12V-Anschluss versorgt. Die beiden Nanos dürfen mit keiner externen Quelle versorgt werden. Der Grund ist, dass man mit diesem Aufbau die Schutzmaßnahmen, sowie die automatische Wahl der Versorgung der Boards umgeht. Des weiteren sollte man in diesem Aufbau an keinem der Boards zu viele zusätzliche Geräte anschließen, da alles über den +5V-Pin des UNOs versorgt wird. Der Aufbau ist für dieses Beispiel konzipiert.

shematic

Wer mehrere Geräte an die Powerline (+5V) anschließen will, muss eine zusätzliche 5V-Versorgung verbauen, welche genung Strom für alle Geräte liefert und außerdem in jedem Fall einen stabilen Output hat. In diesem Fall darf man auch das UNO nicht mehr extern mit Spannung versorgen. Will man das UNO (in diesem Fall den Master) dennoch mit einem USB-Gerät kommunizieren lassen, so muss man auf einen externen FTDI-Adapter zurückgreifen, wobei man ihn dann nur mit RX, TX & GND des Masters verbinden darf. Die Logik des Aufbaus folgt dann dem folgenden Schaltplan:

shematic_2

Die beiden Pull-up Widerstände werden gebraucht, weil der BUS LOW-active ist. Das bedeutet, dass die Daten nicht durch eine 1, sondern durch eine 0 dargestellt sind (ähnlich zu einem Bar-Code, wo die weißen Balken die Daten repräsentieren). Die meisten erhältlichen I2C-Geräte haben die Widerstände bereits verbaut, weshalb man sich darum keine Gedanken mehr machen muss.

Der Code

Wie immer findet man das Programm auf GitHub.

Dort findet man drei Verzeichnisse: Master, Slave_Reader und Slave_Writer. Die Idee meines Beispiels ist, dass der Master Daten vom Slave_Reader anfordert und diese dann dem Slave_Writer zur Verfügung stellt. Diese Daten sind der digitale Wert eines Taster und der analoge Wert eines Potentiometers. Der Ablauf sieht wie folgt aus:

  • Der Master stellt einen Request an den Slave_Reader.
  • Der Slave_Reader bekommt diesen, nimmt die Daten auf (digitalRead() & analogRead()), konvertiert diese und schickt sie dem Master.
  • Der Master konvertiert die Daten und schickt sie an den seriellen Monitor, damit man sie am PC lesen kann.
  • Als nächsten Schritt schickt der Master die unkonvertierten Daten an den Slave_Writer.
  • Dieser erhält die Daten, konvertiert sie und bringt zwei LEDs entsprechend zum leuchten.

Mit dem Wort „Konvertierung“ meine ich Bit-Shifting. Doch was ist das?

Bits & Bytes

Um zu vertstehen, warum wir Bit-Shifting benötigen, muss man wissen, dass der I²C-Bus maximal ein Byte größe Nachrichten versenden kann. Man kann zwar mehrere 1-byte-Nachrichten hintereinander schicken, doch eben nur nacheinander. Ein Byte ist acht Bits lang, und ein Bit repräsentiert den kleinsten möglichen Zustand auf einem Rechner (sei es nun ein Mikrocontroller oder PC).

Als binäre Zahl dargestellt ist ein Bit also entweder eine Null oder eine Eins. Als Variable auf dem Mikrocontroller wird ein Bit als bool-Variable dargestellt (1-0, true-false, HIGH-LOW). Ein Byte ist wie bereits bekannt eine Kette aus 8 Bits, die daraus resultierende höchste Zahl ist 255. Wem das Konzept der binären Zahlen fremd ist: Es folgt ein kurzes Beispiel:

Die Zahl 0 als Byte dargestellt wäre eine einfache 8-stellige Kette aus Nullen: 00000000. Sind alle Stellen mit Einsern belegt, so ergibt sich die Zahl 255, oder eben 11111111. Die Zahlen dazwischen ergeben sich über die Summe aus 1 & 0 mit der folgenden Logik:

2^7=128 2^6=64 2^5=32 2^4=16 2^3=8 2^2=4 2^1=2 2^0=1 Summe
0 0 0 0 0 1 1 1 7
1 0 1 0 1 0 1 0 170
1 1 1 1 0 0 0 0 240

Nun lässt sich auch erkennen, warum es binäre Zahlen heißt; das Produkt resultiert immer aus der Potenz mit Basis 2. Des weiteren ist das Bit rechts immer das mit dem niedrigsten Stellenwert (least-significant-bit lsb) und das Bit links mit dem höchsten (most-significant-bit msb). Auf diese Weise können alle Zahlen dargestellt werden, man benötigt lediglich genug Bits. Daher stammt unter anderem die Namensgebung des 64-Bit-Betriebssystems. Mit solch einem Prozessor können 64 Bits (8 Bytes) gleichzeitig berechnet werden. Als Zahl betrachtet sind das 2^64=18446744073709551616 mögliche Zustände.

Generell bedeutet das „B“ in msb und lsb jedoch Byte und nicht Bit.

Auf dem AVR-Controller, welcher auf dem Arduino verbaut ist, ist eine Integer-Variable 2 Bytes groß. Das muss man wissen, denn auf anderen Maschinen (PCs, etc.) ist ein Integer 4 Bytes groß. Man kann also folgendes festhalten:

byte a = 255;           // Von 0 bis 255, 1 Byte: 2^8=256
int b = 32767;          // Von -32767 bis 32767, 2 Bytes (1 Vorzeichenbit): 2^15=32768
unsigned int c = 65535; // Von 0 bis 65535, 2 Bytes: 2^16=65536
/*  
   Das erste (linke) Bit ist im Regelfall immer das
   Vorzeichen-Bit. Durch das Keyword 'unsigned' kann 
   dieses als Datenbit genutzt werden.
*/

Jetzt sieht man auch die Problematik: Mit dem I²C-Bus kann man wie bereits erwähnt nur 1-Byte lange Nachrichten senden. Einen Integer zu senden funktioniert nicht so einfach, wie man es gerne hätte, denn er ist einfach zu lang. Das führt uns zum eigentlichen Thema:

Bit-Shifting

Der Grundgedanke um das Problem zu lösen ist: Teile den 2-Byte großen Integer am Slave in zwei 1 Byte große Pakete auf, schicke diese nacheinader und setze sie am Master wieder zusammen. Klingt einfach, ist es auch.

Hierfür stellt uns der C++ Standard auch die nötigen Werkzeuge zur Verfügung. Wir brauchen die folgenden Operatoren:

<< Nach links shiften
>> Nach rechts shiften
& Bitweises UND
| Bitweises ODER

Zum besseren Verständnis folgt eine kurze Erklärung der Funktion der Operatoren.

Nach links shiften
110010011 << 2 = 001001100
110010011 << 4 = 100110000
110010011 << 6 = 011000000
/* 
   Die Bits werden um die nach dem '<<'-Operator angegeben Zahl
   nach links verschoben. Am rechten Ende werden Nullen angehängt.
*/
Nach rechts shiften
110010011 >> 2 = 001100100
110010011 >> 4 = 000011001
110010011 >> 6 = 000000110
/* 
   Die Bits werden um die nach dem '>>'-Operator angegeben Zahl
   nach rechts verschoben. Am linken Ende werden Nullen angehängt.
*/
Bitweises UND
1100 & 1010 = 1000
1111 & 0000 = 0000
1111 & 1010 = 1010
/* 
   Das bitweise UND gibt nur dann eine Eins, wenn zwei Einsen addiert werden.
   In jedem anderen Fall ist das Ergebnis Null.
*/
Bitweises ODER
1100 | 1010 = 1110
1111 | 0000 = 1111
1111 | 1010 = 1111
/* 
   Das bitweise ODER gibt nur dann eine Null, wenn beide Bits Null sind.
   In jedem anderen Fall ist das Ergebnis eine Eins.
*/

Mit diesem Wissen erklärt sich auch, was im Programm des Slave_Reader passiert. Dort findet man die folgenden Zeilen:

byte msb = (integerValue >> 8) & 0xFF;
byte lsb = (integerValue) & 0xFF;

Das most-significant-byte ist das linke der beiden. Ich rücke also den Integer bitweise um 8 Stellen nach rechts, wodurch die nun linken 8 Bits Nullen sind. Weil man nun trotz allem noch 16 Bits hat UNDe ich bitweise mit 0xFF (binär: 11111111). Auf diese Weise fallen die ersten 8 Bits weg. Im nächsten Schritt widme ich mich dem least-significant-byte – dem rechten Byte. Hier reicht es, einfach die linken 8 Bits zu löschen. Dies erledigt man wieder mit dem bitweisen UND von 0xFF.

Dem ’schöneren‘ Aussehen des Programms wegen schreibe ich die zu sendenden Bytes in ein Byte-Array. Man muss darauf achten, dass dieses Array am Master als auch am Slave die gleichen Daten an den gleichen Indizes hat. Also Integer_A an Stelle 0 & 1, Integer_B an Stelle 2 & 3, usw. Beim tatsächlichen Senden der Daten mit Wire.write(); steht die Zahl für die Größe des Arrays.

byte buffer[3] = {0};
buffer[0] = digitalValue; // Boolsche Variable, benötigt nur 1 Bit Platz
buffer[1] = (analogValue >> 8) & 0xFF; // analogValue ist ein Integer
buffer[2] = (analogValue) & 0xFF;
Wire.write(buffer, 3);

Betrachtet man nun den Code vom Master, so findet man die Zeile

Wire.requestFrom(slaveReader, 3);

, welche die Funktion requestHandler() am Slave_Reader triggert. Die Zahl 3 steht wieder für die Größe des Pakets in Bytes. Der Master fordert also den Slave auf, ihm Daten zu senden. Hat der Slave das getan, so werden die Daten mit den folgenden Zeilen gelesen.

while(Wire.available())
    for (int i = 0; i < 3; i++)
        buffer[i] = Wire.read();

Nun muss man lediglich die zwei Bytes des Integers wieder zusammenfügen. Dies erledigt man kurz und bündig in einer Zeile:

analogValue = (buffer[1] << 8) | buffer[2];

Was passiert hier? Das most-significant-byte befindet sich im Array an der Stelle 1, das lsb an Stelle 2. Deshalb schiebe ich das msb um 8 Stellen nach rechts, erschaffe dadurch eine 16-Bit Binärzahl in der sich an den rechten 8 Stellen lauter Nullen befinden. Diese überschreibe ich mit dem bitweisen ODER mit dem lsb. É voilà, der Integer ist wieder ganz.

Nachdem der Integer am seriellen Monitor ausgegeben wurde gehen wir noch etwas weiter und senden das Array an den Slave_Writer. Durch die drei Zeilen


Wire.beginTransmission(slaveWriter);
Wire.write(buffer, 3);
Wire.endTransmission();
    

im Programm des Masters wird die Verbindung zum Slave hergestellt, es wird das Paket gesendet, und die Verbindung wird getrennt. Die Funktion receiveHandler() im Sourcecode des Slave_Writers wird daduch getriggert und beginnt die Daten zu lesen. Über das gleiche Bit-Shift-Verfahren wie im Master wird der Integer wieder hergestellt. Letztendlich werden die beiden LEDs entsprechend angesteuert.

Die Variable slaveWriter beinhaltet lediglich die hexadezimale Zahl 0x02, welche in Dezimalschreibweise für 2 steht. Kurz: Der Slave_Reader hat die Adresse 0x01 = 1, der Slave_Writer die Adresse 0x02 = 2. Die Adressen können frei gewählt werden.

Letzte Worte

Geräte, welche an dem I²C Bus angeschlossen sind müssen immer mit Spannung versorgt sein. Ein deaktiviertes Gerät sollte deshalb auch nicht am Bus angeschlossen sein. Der Grund ist, dass das Gerät dann Spannung aus den beiden Busleitungen bezieht. Das Resultat ist ein blockierter Bus oder ein defektes Gerät.

Ich habe versucht, alles relevante zu beschreiben, was man wissen muss, um Daten mit dem I²C Bus zu senden. Falls dennoch Fragen offen sind: Ein Kommentar oder die Doku.

GPS w/ uBlox neo6mv2

Zu wissen, wie man GPS Rohdaten von einem entsprechenden Modul ausliest, und diese dann auch verarbeiten kann, kann in vielen Situationen von Vorteil sein. Verwendung findet ein GPS Modul in Multicoptern, Tracker oder um Daten an bestimmten Orten zu sammeln, um nur ein paar Beispiele zu nennen. Interessant wird es, wenn man GPS Daten im Zusammenhang mit anderen Sensordaten verwertet. Etwa die Beschleunigungswerte, gemessen durch eine entsprechende Sensorik, eines Kraftfahrzeuges.

setup

Die Funktionsweise

GPS ist ein Akronym für Global Positioning System. Um Weltweit die Position eines Gerätes bestimmen zu können, benötigt man nicht nur eben dieses Gerät, sondern auch Satelliten.

Entwickelt wurde das GPS vom US-Militär, bevor es seit den 1990er Jahren auch für zivile Zwecke eingesetzt wird. Der Vorteil dieser Positionsbestimmung ist, dass GPS Signal nur empfangen werden können, jedoch nicht gesendet werden. So kann man navigieren, ohne dass es Gegenspieler mitbekommen.

Die Satelliten, welche sich mit einer Relativgeschwindigkeit von etwa 3.9km/s zur Erde bewegen senden ein codiertes Radiosignal aus, welches ihre Umlaufbahnparameter und die Sendezeit beinhält. Dieses wird vom Empfänger verarbeitet, woraus dann die Position auf der Erde errechnet wird. Für eine genaue Bestimmung der Position benötigt man mindestens drei Satelliten (3 Punkte im Raum bestimmen die Position theoretisch exakt). Da die Empfängergeräte jedoch keine hochgenaue Uhr verbaut haben, wird mindestens ein weiterer Satellit benötigt, um die Uhrzeit genau bestimmen zu können.

Weitere Daten, etwa Richtungsangaben (Kompass) oder die Geschwindigkeit können über den Dopplereffekt bestimmt werden. Dabei ist die Signalverzerrung die ausschlaggebende Größe, welche ein Maß für die Daten ist.

Für mehr Informationen zum GPS: Wikipedia.

uBlox Neo6m v2

Die Überschrift lässt erahnen, welches Modul ich nutze. Es ist im Vergleich zu anderen GPS Receivern eines der günstigsten, lässt dennoch in den unterschiedlichen GPS-Modes eine Genauigkeit von bis zu 2.5 Meter laut Hersteller zu. Die GPS-Daten habe ich mit einem professionellem GPS-Gerät bei schönem Wetter auf einer Wiese abgeglichen, und bin zu dem Ergebnis gekommen, dass der eine Meter nicht unbedingt eingehaten wird. Maximal ergab sich eine Abweichung von etwa 6 Metern. Für den Preis von unter 10€ eine beträchtliche Leistung.

module

Die eigentliche Bezeichnung des Moduls lautet GY-GPS6MV2, NEO-6M bezieht sich lediglich auf den Chip, und nicht auf die gesamte Platine. Der NEO-6M Chip arbeitet mit einer maximalen Spannung von 3.6V, da sich auf der Platine aber ein Spannungswandler befindet, darf man den VCC-Pin mit 5V versorgen. Wichtig ist, dass die Signal-Pins (UART) auf keinen Fall mit mehr als 3.6V in Berührung kommen dürfen, denn es befindet sich kein Levelshifterauf dem Board.

Um das Modul dennoch mit einem 5V-Arduino nutzen zu können bedarf es entweder einem Levelshifter-Modul oder man stellt diesen durch einen einfachen Widerstands-Spannungsteiler her. Wie man das macht folgt etwas weiter unten. Laut Datenblatt sollte das GPS-Modul auch funktionieren, wenn man an VCC nur 3.3V (3.6V) anlegt, doch in diesem Fall habe ich nie verwertbare Daten vom Modul erhalten.

Der Aufbau

Wie bereits erwähnt, benötigt man für den Aufbau neben dem Controller und dem GPS Modul einen 2.2kΩ Widerstand und einen 1.0kΩ Widerstand.

shematic

Die Verkabelung erfolgt nach folgender Tabelle:

Arduino GPS LCD
+5V Vcc Vcc
GND GND GND
A4 SDA
A5 SCL
3 RX (mit Levelshifter)
4 TX

Der Grund, warum der TX-Pin (Transmit) keinen Widerstand benötigt ist, weil dieser das Signal an das Arduino sendet. Gegensätzlich dazu gibt das Arduino +5V an Pin 3 aus. Diese Spannung würde dem GPS nicht sonderlich gut tun, deshalb muss sie auf das logische Level des Moduls herabgesetzt werden.

Liquid Crystal Display via I2C

Bei dem Display handelt es sich um das sehr weit verbreitete HD44780 Liquid Crystal Display. Ihm wird lediglich ein Rucksack-Modul verpasst, welches die I2C-Befehle in HD44780er-Befehle umwandelt. Optional kann man das Display auch weglassen, das Programm muss man dafür nicht verändern. Wer will, kann die überflüssigen Zeilen aber auch löschen.

Display

Das Display-Modul nutzt wie bereits erwähnt den I2C-Bus. Er wird genau wie der letzte von mir vorgestellte Display angeschlossen:

  • SDA: A4
  • SCL: A5
  • Vcc: +5V
  • GND: Ground

Backpack

Der Code

Das Programm befindet sich auf GitHub.

Im Verzeichnis gps_basic findet man das Programm, welches die Grundidee beinhaltet. Das zweite, gps_tracker, behandle ich etwas später. Um die Vorgänge im Programm zu verstehen, muss man zuerst den Aufbau des Protokolls kennen, das vom GPS an das Arduino seriell gesendet wird. Im Ordner lib findet man die von mir verwendeten und funktionierenden Programmbibliotheken.

Zur Bestimmung der Position nutze ich die Daten des $GPGLL-Protokolls, und um zu erfahren, mit wie vielen Satelliten das GPS im Moment verbunden ist lese ich die Daten des $GPGSA-Protokolls.

$GPGLL

Dieses Protokoll sendet die geografische Position (Längen- und Breitengrade) und die Zeit. Der Aufbau sieht mit Beispielswerten wie folgt aus:

$GPGLL,4704.46496,N,01241.63682,E,110152.00,A,D*29

Man erkennt, dass die Daten mit einem Komma getrennt sind, was der Idee des csv-Formatsentspricht. Die Rohdaten ergeben nach der Verarbeitung folgende Nutzdaten:

  • 4704.46496,N: 47° 04.46496′ Nord
  • 01241.63682,E: 12° 24.63682′ Ost
  • 110152.00: Uhrzeit nach UTC+00
  • A: Daten validiert.
  • D*29: Checksumme

Die Koordinaten werden im Format Grad & Dezimalminuten ausgegeben. Wem geografische Kooridnaten nichts oder nur wenig sagen: Wikipedia. Grundsätzlich gilt aber, dass die Erde in Kugelkooridinaten mit Längengrade (Longitude) und Breitengrade (Latitude) unterteilt wird. Die geografische Länge hat eine Spannweite von -180° (Westen) bis + 180° (Osten), ausgehend von Greenwich in Großbritanien. Die geografische Breite reicht von -90° (Süden) bis +90° (Norden). Die Hauptachse ist in diesem Fall der Äquator.

$GPGSA

Dieses Protokoll enthält Daten über den GPS-Mode, die Anzahl der aktiv verbundenen Satelliten und über die Verringerung der Genauigkeit (Dilution of Precision [DOP]).

$GPGSA,A,3,ID,ID,ID,ID,ID,ID,ID,ID,ID,ID,ID,1.7,1.1,1.3*55

  • A,3: Automatische Wahl, 3D
  • ID: ID des Satelliten (11 Satelliten maximal)
  • 1.7: PDOP
  • 1.1: HDOP
  • 1.3: VDOP
  • *55: Checksumme

Eine Liste aller GPS-NMEA Sätze gibt es hier.

Data Parser

Will man die Daten vom GPS einfach nur über das Arduino zum seriellen Monitor durchschleusen, so genügt es am Beginn des Programms die Kommentar-Slashes der Zeile 13 zu entfernen.

#define DATA_PARSER

Da dieser Task aber sehr langweilig ist, gehen wir gleich weiter zum eigentlichen Code.

Serielle Daten aufnehmen

In der loop() wird gewartet, bis serielle Daten vom GPS (gps ist hier eine Instanz von SoftwareSerial) verfügbar sind. Sobald das Dollarzeichen auftaucht werden die Daten in einen Buffer geschrieben. Taucht das Sternsymbol auf, welches Teil der Checksumme ist, wird das Lesen beendet und die Daten werden zur Weiterverabeitung – processRawData() – frei gegeben.

while (gps.available())
{
	GPSRX = gps.read();

	if (GPSRX == '$')
	{
		RXbuffer = F("$");
		enableRead = true;
	}
	else if (GPSRX == '*')
	{
		processRawData(RXbuffer);
		enableRead = false;
	}
	else if (enableRead)
	{
		RXbuffer += GPSRX;
	}
}
Datenverarbeitung

In der Funktion processRawData() wird zuerst geprüft, ob die Daten des $GPGLL-Protokolls validiert sind. Ist dies der Fall, so wird der Inhalt über splitString() aufgeteilt. Da die Grade jedoch in einem Strang mit den Minuten kommen muss man eine kleine Division einfügen. Hier nutze ich die Eigenschaften von Integer- und Floatvariablen (Gleitkommazahlen löschen).

buffer = (splitString(splitString(data, ',', 1), '.', 0)).toInt();
latitude.degrees = buffer / 100;
latitude.minutes = (buffer - latitude.degrees * 100) + splitString(splitString(data, ',', 1), '.', 1).toFloat() / 100000;

Die selbe Vorgehensweise habe ich auch bei den Breitengraden, der Zeit und dem anderen Protokoll, allerdings mit den angepassten Positionen im Protokoll.

Das war bereits alles, was es zu verarbeiten gibt. So einfach kann man GPS-Daten für sich nutzen.

User Interface

Die Aktualisierungsgeschwindigkeit der Daten sowohl am Display als auch am seriellen Monitor wird vom GPS vorgegeben. Das Modul hat eine maximale Aktualisierungsrate von 1Hz, und diese Zeit nutze ich auch.

In der setUI()-Funktion werden die Daten auf ein einheitliches Format gebracht, sodass sie immer gleich am Display und im seriellen Monitor dargestellt werden. Zusätzlich hat man am Beginn des Programms die Wahl, ob man die Daten seriell im csv-Format ausgeben will. Dazu genügt es die Definitionen entsprechend auszukommentieren.

//#define CSV
//#define CSV_MS_EXCEL
#define NO_CSV

Der Unterschied zwischen ’normalen‘ csv und MS Excel csv ist, dass Excel die Daten mit Strichpunkten anstatt einem Beistrich trennt, und der Dezimalpunkt kein Punkt, sondern ein Komma ist. Das folgende Bild zeigt die drei möglichen Ausgaben.

ui_modes

Die Anzahl der Satelliten und die Warnung der nicht-validierten Daten wird nur im NO_CSV Modus ausgegeben.

GPS Tracker

Da jetzt klar ist was dahinter steckt, kann man einen Schritt weiter gehen und sich mit Hilfe eines SD-Kartenmoduls einen GPS-Tracker erstellen. Den Display habe ich komplett weggelassen, da es mir in erster Linie darum geht, Geodaten während dem Gehen zu sammeln. Der Display würde hier nur unnötig Energie verbrauchen.

gps_tracker

Als SD-Kartenleser kommt das Modul von Catalex zum Einsatz, welches ich bereits hierbeschrieben habe, allerdings nutze ich es in diesem Tutorial mit dem ’normalen‘ SPI, und nicht mit SoftSPI. Der Grund ist, dass ich kein weiteres SPI-Gerät nutze, das durch das SD-Modul gestört werden könnte. Für mehr Infos dazu: Einfach den zugehörigen Beitrag lesen.

sd_module

Der elektrische Aufbau

Im Grunde sieht der Aufbau aus wie oben, lediglich mit LCD– und Card Reader++. Die Verbindung der Pins folgt der Tabelle:

Arduino GPS Card Reader
+5V Vcc Vcc
GND GND GND
3 RX (mit Levelshifter)
4 TX
10 CS
11 MOSI
12 MISO
13 SCK

shematic_tracker

Der Code

Das Programm liegt im Verzeichnis gps_tracker auf GitHub. Die Grundbausteine sind gleich geblieben, ich habe es lediglich um die Funktionen der SD Karte erweitert. Das arduino schreibt nun immer wenn das GPS Daten schreibt diese auf die Karte und zeigt sie auch, optional, am seriellen Monitor an.

File csvFile = SD.open(F("log.csv"), FILE_WRITE);
csvFile.println(csvData);
csvFile.close();

Sind die Daten fehlerhaft oder ist keine Satellitenverbindung vorhanden, so werden keine Daten geschrieben. Dies ist etwas nachteilig, da man nicht weis, ob das Programm auch ordnungsgemäß ausgeführt wird. Man könnte auch hier eine LED verbauen, die anzeigt, ob die Daten validiert wurden, oder ob eine Verbindung zu den Satelliten besteht. Aber mein Tutorial soll ja auch nur als Denkanstoß dienen. Ich würde mich freuen, wenn coole Lösungen für diverse Probleme auftauchen.

Nur noch eine kleine Anmerkung: Wenn man zusätzlich Sensordaten senden will, dann erkennt man schnell die Vorteile des csv-Dateiformats. Man hängt diese einfach hinten an.

Falls sonst noch Fragen aufgetaucht sind, der Code ist gut auskommentiert und ein Kommentar ist immer erwünscht.