Als regelmäßiger Bahnfahrer hatte ich mich schon lange darüber geärgert, dass ich immer eine komplette Reiseabfrage machen muss, wenn ich sehen will, wann mein nächster Zug geht.
Also ganz konkret: ich bin mit meiner Arbeit fertig und will die nächste Verbindung nach Hause wissen
oder – ich will wissen, ob der Zug an meinem Bahnhof Verspätung hat
Schritt: Reiseportal der DB aufrufen
Ankunft / Abfahrt wählen
Bahnhof eingeben
Abfrage starten
Zugegebenermaßen kann man Lesezeichen jeweils für Schritt 2 setzen, es bleibt aber immer noch Schritt 3 und 4.
Die Lösung
Es gibt eine Schnittstelle für „parametrisierte Übergabe“ an die Reiseauskunft, deren Beschreibung ich im Wiki des FHEM gefunden habe. DBPlan im FHEM Wiki oder als direkter Link zum pdf.
Die Abfrage der nächstmöglichen Reiseverbindung von Frankfurt-Niederrad nach Köln-Ehrenfeld sieht also jetzt (oneCLick) so aus und lässt sich als Lesezeichen ablegen.
Man kann die Bahnhöfe meist im Klartext eingeben, nur manchmal, wenn es eine Mehrdeutigkeit gibt, kommt es zu einer weiteren Abfrage (Beispiel: Köln-Chorweiler). Das kann man umgehen, wenn man statt des Bahnhofes im Klartext die Bahnhofkennziffer, die sogenannte IBNR eingibt. Die IBNR für alle Bahnhöfe findet man über die Suche auf dieser Homepage eines wahrhaft Bahnbegeisterten. Der Link oben sieht dann also so aus:
Einen besonderen Augenmerk möchte ich hier noch auf den Parameter productsFilter=111110000 lenken. Damit lässt sich einstellen, ob man alle Verkehrsmittel, also auch Straßenbahnen oder Busse, Schiffe usw oder nur die Schienenfahrzeuge wie hier sehen will. Näheres dazu in der Dokumentation der Parameter im PDF.
Eine letzte Frage bleibt unbeantwortet: Warum findet man diese Dokumentation eigentlich nicht auf den Seiten der Deutschen Bahn AG? An Google gibt die Bahn diese Daten ja auch weiter. Schön wäre jetzt noch, wenn man die Ausgabe als json oder xml zurückbekommen würde, dann wäre der Weg für die persönliche Abfahrtstafel an der Wohnungstüre geebnet.
Seit kurzem bin ich begeisterter Besitzer einer Sony DSC-HX90V. Für die Fernsteuerung dieser Kamera hat Sony eine WiFi Schnittstelle eingebaut und bietet eine App für Smartphones an, siehe PlayMemories. Leider gibt es keine weitere Schnittstelle (IR, elektrisch, Funk) um die Kamera remote zu bedienen.
Die Idee
Warum also sollte man die Kamera nicht über die WiFi Schnittstelle fernsteuern und damit gleichzeitig eine Schnittstelle per Mikrocontroller schaffen. Eine Realisierung einer WiFi Remote für eine GoPro wurde vor kurzem auf Hackaday vorgestellt. Eine Suche nach entsprechenden Programmen für Sony Kameras war ohne Erfolg. Auch die Sony Dokumentation der API brachte mich nicht weiter, da ich kein Android programmiere.
Über das ESP8266 Forum, fand ich zumindest Interessierte mit der gleichen Idee, aber keine Lösung.
Reverse Engineering zur Lösung
Durch Zufall stieß ich auf die TimeLapse App von Thibaud Michel.
Mit Hilfe eines WiFi PacketAnalysers habe ich mir auf dem Tablett das Protokoll näher angeschaut und dann mit der Sony Doku der APK verglichen.
Nach einiger Recherche fand ich heraus, dass es sich um ein einfaches HTTP POST Verfahren handelt mit der Übergabe der Parameter in einem json Format. Das nachzubilden mit dem ESP8266 war nur noch Fleißarbeit.
Proof of Concept
Die hier vorgestellte Lösung ist ein erster „Proof of Concept“ ohne jegliches Fehlerhandling und soll zunächst nur die Funktion des Schnittstelle darstellen. An einem ESP8266-01 ist lediglich ein Pushbutton zur Auslösung der Kamera angeschlossen der Rest ist Programm. Als Test wurde das Programm auf meinem Prototyp Board entwickelt. Auf diesem Board ist die 3,3V Stromversorgung und der FTDI zur Programmierung und zur Ausgabe von Debug Informationen fest verbunden. Der ESP8266 ist steckbar. Einzige zusätzliche Komponente ist der Pushbutton zur Fernauslösung der Kamera. Eine Beschreibung des Protoboards und die Einbindung der ESP8266 Libraries in die Arduino IDE findet sich hier. Die prinzipielle Verschaltung hier. Der Code ist ausführlich kommentiert und weicht nicht erheblich von den bekannten WiFiClient Codes ab, sollte also leicht verständlich sein. Auf eine Auswertung der json Antworten der Kamera wurde verzichtet.
//----------------------------------------------------------------------------------------------------------------------
// This program is based on: WiFiClient from ESP libraries
//
// Camera handling by Reinhard Nickels https://glaskugelsehen.wordpress.com/
// tested with DSC-HX90V, more about protocol in documentation of CameraRemoteAPI https://developer.sony.com/develop/cameras/
//
// Licenced under the Creative Commons Attribution-ShareAlike 3.0 Unported (CC BY-SA 3.0) licence:
// http://creativecommons.org/licenses/by-sa/3.0/
//
// Requires Arduino IDE with esp8266 core: https://github.com/esp8266/Arduino install by boardmanager
//----------------------------------------------------------------------------------------------------------------------
#include <ESP8266WiFi.h>
#define DEBUG 1
#define BUTTON 2 // pushbutoon on GPIO2
volatile int counter;
const char* ssid = "DIRECT-GKC2:DSC-HX90V";
const char* password = "5uv2LSwj"; // your WPA2 password
const char* host = "192.168.122.1"; // fixed IP of camera
const int httpPort = 8080;
char JSON_1[] = "{\"version\":\"1.0\",\"id\":1,\"method\":\"getVersions\",\"params\":[]}";
char JSON_2[] = "{\"version\":\"1.0\",\"id\":1,\"method\":\"startRecMode\",\"params\":[]}";
char JSON_3[] = "{\"version\":\"1.0\",\"id\":1,\"method\":\"startLiveview\",\"params\":[]}";
char JSON_4[] = "{\"version\":\"1.0\",\"id\":1,\"method\":\"stopLiveview\",\"params\":[]}";
char JSON_5[] = "{\"version\":\"1.0\",\"id\":1,\"method\":\"actTakePicture\",\"params\":[]}";
// char JSON_6[]="{\"method\":\"getEvent\",\"params\":[true],\"id\":1,\"version\":\"1.0\"}";
unsigned long lastmillis;
WiFiClient client;
void setup() {
Serial.begin(115200);
delay(10);
pinMode(BUTTON, INPUT_PULLUP);
attachInterrupt(2, pulleddown, FALLING); // handled by interrupt to debounce
// We start by connecting to a WiFi network
Serial.println();
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) { // wait for WiFi connection
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
delay(1000);
httpPost(JSON_1); // initial connect to camera
httpPost(JSON_2); // startRecMode
httpPost(JSON_3); //startLiveview - in this mode change camera settings (skip to speedup operation)
}
void loop() {
if (buttonpressed()){
Serial.println("pressed..");
httpPost(JSON_4); //stopLiveview (skip to speedup operation)
httpPost(JSON_5); //actTakePicture
httpPost(JSON_3); //startLiveview (skip to speedup operation)
}
}
void httpPost(char* jString) {
if (DEBUG) {Serial.print("Msg send: ");Serial.println(jString);}
Serial.print("connecting to ");
Serial.println(host);
if (!client.connect(host, httpPort)) {
Serial.println("connection failed");
return;
}
else {
Serial.print("connected to ");
Serial.print(host);
Serial.print(":");
Serial.println(httpPort);
}
// We now create a URI for the request
String url = "/sony/camera/";
Serial.print("Requesting URL: ");
Serial.println(url);
// This will send the request to the server
client.print(String("POST " + url + " HTTP/1.1\r\n" + "Host: " + host + "\r\n"));
client.println("Content-Type: application/json");
client.print("Content-Length: ");
client.println(strlen(jString));
// End of headers
client.println();
// Request body
client.println(jString);
Serial.println("wait for data");
lastmillis = millis();
while (!client.available() && millis() - lastmillis < 8000) {} // wait 8s max for answer
// Read all the lines of the reply from server and print them to Serial
while (client.available()) {
String line = client.readStringUntil('\r');
Serial.print(line);
}
Serial.println();
Serial.println("----closing connection----");
Serial.println();
client.stop();
}
void pulleddown() // Interrupt handler
{
counter++;
}
boolean buttonpressed() { // function to check if pressed
if (counter!=0) {
counter=0;
delay(10); // je nach Schalter
if (counter==0 && !digitalRead(BUTTON)) return true;
}
return false;
}
Im Programm muss die SSID und das Passwort der benutzten Kamera eingetragen werden. Die Kamera wird nach dem Einschalten über das Menue in den Fernsteuermodus gebracht. Nach dem Einschalten des ESP8266 wird die Verbindung zur Kamera hergestellt und die Kamera wird in einen Modus versetzt, wo Einstellungen (Zoom, Blende, Zeit, …) möglich sind.
Ein Druck auf den Pushbutton löst die Kamera aus und die Kamera kehrt in den vorhergehenden Modus zurück.
Über die serielle Schnittstelle werden Debug Informationen ausgegeben, hier ein Beispiel nach dem Einschalten des ESP bis nach der ersten Fernauslösung der Kamera.
Connecting to DIRECT-GKC2:DSC-HX90V
.......
WiFi connected
IP address:
192.168.122.115
Msg send: {"version":"1.0","id":1,"method":"getVersions","params":[]}
connecting to 192.168.122.1
connected to 192.168.122.1:8080
Requesting URL: /sony/camera/
wait for data
HTTP/1.1 200 OK
Connection: close
Content-Length: 51
Content-Type: application/json
{"result":[["1.0","1.1","1.2","1.3","1.4"]],"id":1}
----closing connection----
Msg send: {"version":"1.0","id":1,"method":"startRecMode","params":[]}
connecting to 192.168.122.1
connected to 192.168.122.1:8080
Requesting URL: /sony/camera/
wait for data
HTTP/1.1 200 OK
Connection: close
Content-Length: 21
Content-Type: application/json
{"result":[0],"id":1}
----closing connection----
Msg send: {"version":"1.0","id":1,"method":"startLiveview","params":[]}
connecting to 192.168.122.1
connected to 192.168.122.1:8080
Requesting URL: /sony/camera/
wait for data
HTTP/1.1 200 OK
Connection: close
Content-Length: 75
Content-Type: application/json
{"result":["http:\/\/192.168.122.1:8080\/liveview\/liveviewstream"],"id":1}
----closing connection----
pressed..
Msg send: {"version":"1.0","id":1,"method":"stopLiveview","params":[]}
connecting to 192.168.122.1
connected to 192.168.122.1:8080
Requesting URL: /sony/camera/
wait for data
HTTP/1.1 200 OK
Connection: close
Content-Length: 21
Content-Type: application/json
{"result":[0],"id":1}
----closing connection----
Msg send: {"version":"1.0","id":1,"method":"actTakePicture","params":[]}
connecting to 192.168.122.1
connected to 192.168.122.1:8080
Requesting URL: /sony/camera/
wait for data
HTTP/1.1 200 OK
Connection: close
Content-Length: 108
Content-Type: application/json
{"result":[["http:\/\/192.168.122.1:8080\/postview\/memory\/DCIM\/100MSDCF\/DSC01272.JPG?size=Scn"]],"id":1}
----closing connection----
Msg send: {"version":"1.0","id":1,"method":"startLiveview","params":[]}
connecting to 192.168.122.1
connected to 192.168.122.1:8080
Requesting URL: /sony/camera/
wait for data
HTTP/1.1 200 OK
Connection: close
Content-Length: 75
Content-Type: application/json
{"result":["http:\/\/192.168.122.1:8080\/liveview\/liveviewstream"],"id":1}
----closing connection----
Dieser Blogeintrag beschreibt in einfacher Weise die grundsätzliche Funktion der WiFi Fernsteuerung einer Sony Kamera. Die Funktionsweise lässt sich zumindest auf die Kameras übertragen, die PlayMemories kompatibel sind. Möglicherweise ist dann die Serveradresse der Kamera zu modifizieren. Der Schritt zu komplexeren Lösungen ist reine Programmierarbeit. Ersetzt man den Pushbutton mit einer Zeitschleife kann man sehr einfach die Einzelbilder für TimeLapse Videos (Zeitraffer) aufnehmen. Die Auslösung kann natürlich auch durch Sensoren (Annäherung, Bewegung, …) erfolgen. Prinzipiell ist auch der Start und Stop einer Videoaufnahme (Kamera im Videomodus) möglich. Die entsprechenden json Kommandos sind in der Doku der APK enthalten. Wenn man auf den Modus „Liveview“ verzichtet ist die Auslöseverzögerung nur minimal (siehe Kommentar im Code, diese Zeilen dann auskommentieren), allerdings verliert man dann die Möglichkeit Kameraeinstellungen zu ändern. Für einen Batteriebetrieb ist der ESP8266 bekanntermaßen nicht so gut geeignet, aber für einen einzelnen Shot braucht es nicht viel Zeit. Für Timelapse Projekte wäre es sicher sinnvoll den Sleep Mode des ESP zu nutzen (was bei der Bauform ESP-01 nicht funktioniert ohne Modifikation). Ich würde hier das Modell ESP-12 empfehlen, das außerdem CE und FCC zertifiziert ist.
Nachdem ich mich in meinen letzten Blogs intensiv mit dem ESP8266 auseinandergesetzt habe, steht logischerweise eine Erweiterung meines Tutorials zur Speicherung von Messwerten in eine MySQL DB unter Verwendung des ESP8266 an. Das Programm wurde weitestgehend analog dem bisherigen Programm aufgebaut, so dass es relativ einfach zu verstehen sein sollte. Im Programm müssen noch die persönlichen Daten für das WLAN und den Host eingetragen werden.
/*
Programm zur Speicherung von Messwerten in einer webbasierten MySQL DB
based on standard programs of the ESP8266Wifi library
and examples on sparkfun.com
*/
#include <ESP8266WiFi.h>
#define CYCLE 60000
const char* ssid = "hier deine SSID";
const char* password = "und das Passwort des WLANs";
const char* host = "hier deinen Host eintragen";
unsigned long value = 0;
unsigned int lastcall = CYCLE;
int conn_time;
void setup() {
Serial.begin(115200);
delay(10);
// We start by connecting to a WiFi network
Serial.println();
Serial.println();
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
conn_time++;
if (conn_time > 20) {
break;
}
}
if (WiFi.status() == WL_CONNECTED) {
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
}
else {
Serial.println("");
Serial.println("no WiFi connection");
ESP.deepSleep(100000);
Serial.println("gone to sleep");
}
}
void loop() {
while (millis() - lastcall < CYCLE) {
delay(1);
}
lastcall = millis();
++value;
Serial.print("connecting to ");
Serial.println(host);
// Use WiFiClient class to create TCP connections
WiFiClient client;
const int httpPort = 80;
if (!client.connect(host, httpPort)) {
Serial.println("connection failed");
return;
}
// We now create a URI for the request
String url = "/w2mysql.php";
url += "?A0=";
url += millis();
url += "&A1=";
url += value;
Serial.print("Requesting URL: ");
Serial.println(url);
// This will send the request to the server
client.print(String("GET ") + url + " HTTP/1.1\r\n" +
"Host: " + host + "\r\n" +
"Connection: close\r\n\r\n");
delay(1000);
// Read all the lines of the reply from server and print them to Serial
while (client.available()) {
String line = client.readStringUntil('\r');
Serial.print(line);
}
Serial.println();
Serial.println("closing connection");
}
Der ESP8266 gehört inzwischen zu meinen Standardboards. Die Integration in die Arduino IDE ist erfolgt und die Zahl der Libraries nimmt ständig zu. Um ein Gefühl für die Performance des Boards zu bekommen habe ich das Primzahlenprogramm genutzt, mit dem ich bereits den STM32 Maple gegen den Arduino UNO getestet habe.
Das Ergebnis als Übersicht, alle Zeitangaben in Millisekunden.
Datentyp
Suchbereich
Arduino
Maple
ESP8266
uint8_t
255
550
30
83
uint16_t
65535
10760
241
1054
uint32_t
2*65535
84551
583
2919
uint32_t
1000000
abgebrochen
10042
30369
Auf eine Besonderheit möchte ich an dieser Stelle hinweisen:
Ich hatte anfänglich Schwierigkeiten beim ESP mit dem uint32_t Datenformat. Aus zunächst unerklärlichen Gründen gab es immer Abstürze des Programms aufgrund von Watchdog Fehlern. Das hängt wohl damit zusammen, dass der ESP seine komplette WLAN Abarbeitung im Hintergrund erledigt. Wenn aber längere Loops im Programm sind, bekommt er offensichtlich Schwierigkeiten. Um das zu vermeiden habe ich den Befehl yield() in die Loop eingebaut. Das verhindert den Absturz, frisst aber auch ca. 6μs je Aufruf.
In meinem letzten Beitrag habe ich beschrieben wie der ESP8266 angeschlossen werden muss, um mit der Arduinio IDE geflashed zu werden. Zu diesem Zeitpunkt war eine eigenständige IDE (siehe Link) notwendig. Heute ist die Integration des ESP8266 in die Standard IDE sehr viel einfacher. Adafruit hat eine ausführliche Anleitung veröffentlicht, die bis jetzt allerding nicht auf Deutsch verfügbar ist. Deshalb und da die Anleitung auch für den ESP8266-01 gültig ist, diese Anleitung hier. Zunächst installieren wir uns die aktuellste IDE von arduino.cc. In meinem Fall ist es die Version 1.6.5.
Nach der Installation der IDE wird diese gestartet. Unter Datei->Voreinstellungen muss nun unter Additional Boards Manager URLs folgende Eintragung vorgenommen werden: http://arduino.esp8266.com/staging/package_esp8266com_index.json . Mit OK wird das Fenster geschlossen. (Mit der stableVersion hatte ich einen CRC Fehler, deshalb hier die staging-Version).
Auf ein Detail möchte ich an dieser Stelle noch hinweisen. Es ist zweckmäßig für die ESP Sketches ein eigenes Sketchbook Verzeichnis unter Sketchbook-Speicherort zu definieren, da für den ESP teilweise eigene Libraries gebraucht werden, die nicht mit den AVR Libraries kompatibel sind.
Jetzt kommt der eigentliche Clou. Arduino hat mit dem Board Manager ein tolles Tool geschaffen, um neue Boards in die IDE zu integrieren. Unter Werkzeuge->Platine rufen wir jetzt den Boards Manager auf und geben in das Suchfeld ESP8266 ein. Die Boards Erweiterung der ESP Community wird schnell gefunden.
Wir wählen die Erweiterung aus und wählen Install. Nach dem Download der Erweiterung, meldet der Board-Manager INSTALLED.
Jetzt können wir unter Werkzeuge->Platine die Veränderung bewundern. Für den ESP8266-01 wählen wir jetzt das Generic ESP8266 Module.
Wenn wir dann Werkzeuge->Platine erneut aufrufen, sehen wir weitere Einstellungen für den ESP. Hier können wir getrost die default-Einstellungen übernehmen.
Zusätzlich tauchen unter Datei->Beispiele spezifische Sketches für den ESP8266 auf. Wer Lust hat kann jetzt mit der Beschaltung und Beschreibung meines letzten Blog Posts das Blink Beipiel flashen.
Da mit der Aufbau auf dem Steckbrett auf Dauer zu fehleranfällig war, habe ich mir eine Programmier-Platine auf einer Streifenraster-Platine zusammengelötet.
Rechts unten kann ich die GPIO Pins des ESP abgreifen, die Buchse ist mit langen Pins nach unten ausgestattet, so dass ich die Platine auch auf ein Steckbrett stecken kann. Für die Erzeugung der 3.3V habe ich ein AMS1117 Modul integriert. Um in den Flash Mode zu kommen habe ich einen Pushbutton für GPIO0 auf GND und einen Schalter, um die Spannungsversorgung zu unterbrechen eingebaut. Vor dem Flashen wird GPIO0 auf GND gezogen, dann die Spannungsversorgung kurz unterbrochen. Der ESP ist jetzt im Flash Mode. Wie gewohnt wird das Programm mit dem -> hochgeladen.
Hier die Verschaltung zum Flashen des ESP8266. Wenn ein 3,3V FTDI zur Verfügung steht, kann man sich den Spannungsteiler auf der TX Leitung zum ESP sparen.
Variante 1: 3,3V FTDI (an der oberen Leiste des Steckbretts liegen 3,3V an)
Variante 2: 5V FTDI (an der oberen Leiste des Steckbretts liegen 3,3V an)
Zum Flashen wird zunächst der Schalter umgelegt und damit der GPIO0 auf GND gezogen. Dann einmal die 3,3V Spannungsversorgung aus- und wieder einschalten. Jetzt ist der ESP im Flash Modus.
Programm hochladen. Der ESP bekommt nach dem Flashen ein Reset und läuft direkt los, allerdings werden Einstellungen für den Modus (Access Point, Client, usw) erst nach einem Aus- und Wiedereinschalten übernommen. Ich schalte nach dem Flashen den GPIO0 wieder auf offen und mache die Schaltung einmal stromlos, so bin ich sicher, das alles übernommen ist.
Der FTDI ist der wohl bekannteste USB Seriell Wandler auf dem Markt und wird in der Mikrocontroller Entwicklung sehr häufig eingesetzt, nämlich immer dann, wenn es darum, geht ein serielles Device direkt über USB anzusprechen. Ich setze den FTDI gerne ein, wenn ich ein HC-05 Bluetooth Modul mit AT Befehlen umkonfiguriere oder wenn ich die FW Version eines ESP8366 neu flashen will.
Doch was, wenn man gerade keinen FTDI zur Hand hat? Dumm gelaufen, macht aber nix, wenn man zumindest einen Arduino UNO zur Hand hat.
Auf dem UNO ist mit dem ATmega 16U2 bereits ein USB-Seriell Wandler eingebaut und der zweite wichtige Punkt ist, dass man den ATmega328, also den Chip auf dem die Arduino Programme laufen, entnehmen kann. Leider funktionioert das Beschriebene deshalb auch nicht mit der SMD Version.
Als erstes entnehmen wir vorsichtig den Chip von der Platine und passen auf, dass keine Beinchen verbiegen. An Pin 0 und 1 auf dem Platine kann jetzt das serielle Signal abgenommen werden. Wichtig ist zu beachten, dass die Beschriftung jetzt sozusagen für den Empfänger gilt und deshlab müssen RX und TX zwischen den beiden Devices 1:1 verbunden werden.
Ich habe das mal mit einem HC-05 aufgebaut. Der Einfachheit halber bin ich davon ausgegangen, dass der HC-05 in diesem Fall 5V tolerant ist.
Als serielles Terminal kann jetzt die Arduino IDE genommen werden, ich persönlich bevorzuge Cutecom unter Linux. Die einzustellende Baudrate enspricht der Baudrate des HC-05.
Die ist der erste Blogpost zum Thema „What IF“ – kleine Tips, die das Leben einfacher machen. Weitere werden folgen.
Zugegebenermaßen ein wirklich unfairer Vergleich, was sollte man schon bei dem Vergleich eines STM32 mit 32Bit und 72MHz Taktfrequenz gegen einen 8Bit Atmel mit 16MHz erwarten? Der Erstere ist natürlich schneller – aber wieviel schneller?
Zunächst geht es erst mal darum möglichst gleiche Ausgangspositionen zu schaffen. Für den Vergleich wählte ich ein Programm zur Suche von Primzahlen. Dieses Programm ist der Erweiterung für die Arduino IDE beigefügt. Um den Maple mit der Arduino IDE zu flashen habe ich nach dieser Anleitung die IDE 1.6.0 erweitert. Im Arduino Forum gibt es dazu einen inzwischen über 100 Seiten (!!) langen Post. Das Flashen ist etwas umständlich, man muss erst den Maple in den Perpetual bootloader mode bringen und nach dem Upload die Reset Taste drücken. Aber es geht hier weniger um die Bedienbarkeit, ich bin ja froh, dass ich überhaupt mit der Arduino IDE arbeiten kann.
Das Programm sucht die Primzahlen bis zu einer gegebenen Zahl. Da ich größere Geschwindigkeitsunterschiede bei großen Zahlen bereits erwartet habe, habe ich zunächst mit uint8_t als Datentyp begonnen.
Hier das Programm, in der Kommentierung sieht man die Unterschiede für beide Prozessoren im Code, die lediglich im setup() vorhanden sind. Damit man überhaupt im Millisekunden Bereich was messen kann, wird die Schleife 255x durchlaufen.
/*
PrimeNos3: by Nick Gammon
Maple Mini port m. ray burnette: Compiled under Arduino 1.6.0
PUBLIC DOMAIN EXAMPLE
*/
#define BAUD 115200
uint8_t limit = 255;
/* Arduino part
#define BOARD_LED_PIN 13
void setup() {
// initialize the digital pin as an output.
pinMode(BOARD_LED_PIN, OUTPUT);
Serial.begin(BAUD);
Serial.println("Prime Number Generator V2");
} */ //Arduino part
// Maple part
void setup() {
// initialize the digital pin as an output.
pinMode(BOARD_LED_PIN, OUTPUT);
Serial.begin(BAUD); // BAUD has no effect on USB serial: placeholder for physical UART
// wait for serial monitor to be connected.
while (!(Serial.isConnected() && (Serial.getDTR() || Serial.getRTS())))
{
toggleLED();
delay(100); // fast blink
}
Serial.println("Prime Number Generator V2");
} // //Maple part
void loop() {
uint8_t i;
Serial.print("Searching prime numbers between 1 and "); Serial.println(limit);
unsigned long starttime = millis();
for (uint8_t k = 0; k < 255; k++) {
for (i = 3; i < limit; i += 2) {
// This loop stops either when j*j>i or when i is divisible by j.
// The first condition means prime, the second, not prime.
uint8_t j = 3;
for (; j * j <= i && i % j != 0; j += 2); // No loop body
// if (j * j > i) Serial.println(i); // output prime numbers
}
}
Serial.print("Test run for ");
Serial.print((millis() - starttime));
Serial.println(" milliseconds\r\n");
delay(2000);
}
Ergebnis: Arduino UNO 550ms / Maple 30ms — immerhin also ein Faktor von 18,3 erstaunlich viel, da der Unterschied in der Taktfrequenz ja nur Faktor 4,4 ist.
In nächsten Test habe ich den Datentyp auf uint16_t geändert und die Primzahlen bis zur Grenze von 65535 gesucht. Der 255-malige Durchlauf wurde auf 1x reduziert.
Ergebnis: Arduino UNO 10760ms / Maple 241ms — also immerhin schon ein Faktor von 44,7. Hier zeigen die 32Bit des STM ihre Wirkung.
Im nächsten Test bin ich dann auf uint32_t und einen Suchbereich bis 2x 65535 gegangen.
Ergebnis: Arduino UNO 84551ms / Maple 583ms — den Maple mit seinem 32Bit Prozessor kümmert das recht wenig, die Rechenzeit ist erstaunlicherweise nur etwas mehr als doppelt so lang, wie beim vorherigen Grenzwert. Der Arduino quittiert den längeren Datentyp sofort mit einer 7,9x längeren Rechenzeit.
Im letzten Test habe ich dann die Grenze nochmal weiter erhöht, musste den Test aber abbrechen, weil mir die Geduld fehlte, auf den Arduino zu warten. Der Maple rechnete für eine Grenze von 1000000 (eine Million) nur 10042ms.
Die Ergebnisse hier nochmals als Übersicht, alle Zeitangaben in Millisekunden.
Datentyp
Suchbereich
Arduino
Maple
uint8_t
255
550
30
uint16_t
65535
10760
241
uint32_t
2*65535
84551
583
uint32_t
1000000
abgebrochen
10042
Fazit: Dass der Maple soviel schneller bei der Verarbeitung von 32Bit Zahlen ist, habe ich in der Tat nicht erwartet. In Zukunft werde ich bei Performance kritischen Verarbeitungen den Maple sicher mal nutzen. Momentan ist aber die Unterstützung der Standard Libraries noch ziemlich rudimentär, aber das wird sich hoffentlich weiter entwickeln. Arduino bringt mit dem Due ja auch einen 32Bit Prozessor in die Familie. Auf Dauer geht an 32Bit kein Weg vorbei. Bleibt zu hoffen, dass Arduino die Integration in die IDE und die Entwicklung der Libraries schnell vorantreibt.
*mir ist bewusst, dass die Abbildung keinen Arduino zeigt, sondern einen Arduino Nachbau, Die Messergebnisse sind aber sicher portierbar. Zusätzlich mächte ich betonen, dass ich auch Orginale Arduinos in meiner Sammlung habe und jedem empfehle, mit einem Orginalen anzufangen, um die Idee Arduino zu unterstützen.
In den letzten Monaten ist ein regelrechter Hype ausgebrochen um den ESP8266, ein Chip aus chinesischer Entwicklung, der auf einer winzigen Fläche sowohl Access Point als auch WiFi Client sein kann. Inzwischen exisitieren verschiedene Breakout Boards, die zunächst eine UART-WiFi Schnittstelle bereitstellen und auf eBay für unter 3 Euro gehandelt werden. Da der Chip einen 32bit Prozessor enthält kann er aber auch mit eigener Firmware bestückt werden. Ich möchte mich hier nicht näher mit Details beschäftigen, da es bereits viele Webseiten zu diesem Thema gibt. Eine kurze und knappe Zusammenfassung auf deutsch mit Verlinkung zu weiteren Seiten hat das FabLab Nürnberg erstellt.
Inzwischen konnte ich einige Erfahrungen mit diesem Modul machen und solange man kein Highspeed WiFi braucht ist das Modul eine gute Wahl und macht für kleines Geld IoT möglich. Die ersten Versuche mit diesem Modul habe ich in Verbindung mit einem Arduino MEGA gemacht. Der MEGA hat mehrere in Hardware implementierte serielle Schnittstellen, so kann man sowohl die Verbindung zum Computer als auch die Verbindung zum ESP8266 mit 115200 Baud laufen lassen. In den neueren Firmwares des ESP ist die Baudrate aber auch einstellbar, so dass ein Betrieb an einer SoftSerial Schnittstelle bei z.B. 9600 Baud, damit an einem UNO, auch möglich ist. Die Verbindung des ESP an den MEGA muss über Pegelwandler 5V->3.3V erfolgen, die Schnittstelle ist hier nicht tolerant. Ich habe die Verbindung von TX (MEGA) zu RX (ESP) über einen Spannungsteile aus 1k und 2.2k herunter geteilt. Der ESP braucht einen eigenen 3.3V Spannungswandler, der 3.3V Ausgang des Arduino liefert zu wenig Strom für den ESP (max 200mA).
Für erste Experimente, bei denen die AT Befehle „per Hand“ eingegeben werden, eignet sich das folgende kurze Programm.
void setup() {
Serial.begin(115200);
Serial1.begin(115200);
Serial.println("Program started");
}
void loop() {
if(Serial1.available()) {
char ser = Serial1.read();
/*if (ser==10) { // kann genutzt werden, um ein Gefühl für die Antwortzeiten zu bekommen
Serial.print(" ");
Serial.print(millis());
}*/
Serial.print(ser);
}
if(Serial.available()) {
char ser = Serial.read();
Serial1.print(ser);
}
}
Die meisten Einstellungen des Chip bleiben über ein Power on Reset bestehen. Es ist also nicht notwendig, im Programm die Einstellungen jedes mal neu zu setzen. Ich habe mir zu diesem Zweck ein Programm erstellt mit den wichtigsten Einstellungen (Access Point, SSID, PASS, Client usw.). Mit diesem Programm mache ich die Grundkonfiguration des Chip.
// ESP_Minimal.ino - sketch to quickly view and change some of ESP8266 parameters
// most of the ESP parameters are stored in eeprom, so also outlive the power on reset
// special thanks to http://rayshobby.net/?p=9734
// sketch is written for ArduinoMega, ESP connected with voltage devider to Serial1
// This code is in the public domain.
#define SET // for just viewing parameters, set this as comment
#define BUFFER_SIZE 512
#define esp Serial1
#define dbg Serial //For debugging purposes
char buffer[BUFFER_SIZE];
char OKrn[] = "OK\r\n";
int mode;
boolean wait_for_esp_response(int timeout, char* term=OKrn) {
unsigned long t=millis();
bool found=false;
int i=0;
int len=strlen(term);
// wait for at most timeout milliseconds
// or if OK\r\n is found
while(millis()<t+timeout) {
if(esp.available()) {
buffer[i++]=esp.read();
if(i>=len) {
if(strncmp(buffer+i-len, term, len)==0) {
found=true;
break;
}
}
}
}
buffer[i]=0;
dbg.print(buffer);
return found;
}
void setup() {
esp.begin(115200); //esp baud rate
dbg.begin(115200); //debugger baud rate
dbg.println("start of program ...");
esp.println("AT"); // check for existence of ESP8266
if (!wait_for_esp_response(1000)) {
dbg.println("no ESP found");
while(true); // hang forever
}
// some important checks of operation modes
esp.println("AT+CWMODE?"); // check mode 1=Client, 2=AP, 3=both
wait_for_esp_response(1000);
char *result = strstr(buffer, "+CWMODE:");
if (result!=0) {
mode=atoi(result+8);
if (mode!=1) {
esp.println("AT+CWSAP?"); // actual AP configuration
}
if (mode!=3) {
esp.println("AT+CWJAP?"); // actual connected WiFi
wait_for_esp_response(1000);
}
}
esp.println("AT+CIPMUX?"); // check connection mode 0=single, 1=multiple
wait_for_esp_response(1000);
esp.println("AT+CIPMODE?"); // check data mode 0=normal, 1=transparent
wait_for_esp_response(1000);
esp.println("AT+CIFSR"); // actual IP adress
wait_for_esp_response(1000);
#ifdef SET
esp.println("AT+CWMODE=3"); // set mode 1=Client, 2=AP, 3=both
wait_for_esp_response(1000);
// set AP or Client
esp.println("AT+CWMODE?"); // check mode 1=Client, 2=AP, 3=both
wait_for_esp_response(1000);
result = strstr(buffer, "+CWMODE:");
if (result!=0) {
if (mode!=1) { // AP settings
esp.println("AT+CWSAP=\"ESP_9F2556\",\"blablabla\",1,3"); // set AP: SSID, PASS, channel, encryption
wait_for_esp_response(1000); // 0=open, 1=WEP, 2=WPA_PSK, 3=WPA2_PSK, 4=WPA_WPA2_PSK
}
if (mode!=2) { // Client settings
esp.println("AT+CWJAP=\"yourSSID\",\"passphrase\""); // connect client to AP
wait_for_esp_response(5000); // was really that long!
}
}
#endif
esp.println("AT+RST"); // RESET, necessary for some changes of settings
wait_for_esp_response(3000, "ready");
}
void loop() {
}
Die zu ändernden Parameter werden direkt im Code verändert. Wenn die erste Zeile #define SET auskommentiert wird, werden die wichtigsten Parameter nur angezeigt.
Die Experimente zu meinem Tutorial „Speicherung von Arduino Messdaten auf Webserver und deren Darstellung“ setzten alle auf einen Arduino mit Ethernet Shield zur Verbindung mit dem Internet auf. Nun gibt es Situationen, wo kein Ethernet Anschluss in Reichweite ist, trotzdem aber die Verbindung ins Internet benötigt gewollt ist. Oder es ist Mobilität gefordert, während Daten übertragen werden. Das Arduino Wifi Shield wäre eine Lösung, ist aber mit fast 70€ eine sehr kostspielige und zusätzlich muss das Programm daraufhin angepasst werden. Eine wesentlich kostengünstigere und ohne Anpassung des Programms einsetzbare Lösung ist ein Wireless Client. Ich habe mich für den TP-Link TL-WR702N entschieden, der schon inklusive Versand für weniger als 20€ zu bekommen ist.
Der TL-WR702N ist im Auslieferungszustand ein Access Point, für unsere Anwendung muss er aber als Client oder als Bridge konfiguriert werden. Der TL-WR702N stellt als Client über WLAN die Verbindung zum Access Point her, der Arduino mit Ethernet Shield wird mit dem TL-WR702N über die Ethernet Schnittstelle verbunden und verhält sich genauso, wie bei direkter Verbindung nur über Ethernet.
Die Konfiguration als Client ist im Handbuch ausreichend beschrieben, deshalb möchte ich hier nicht näher darauf eingehen. Einen Sonderfall möchte ich an dieser Stelle näher beschreiben, nämlich, wenn ich keinen Rechner zur Verfügung habe, den TL-WR702N zu konfigurieren. Eine Konfiguration über Wireless, z.B. mit einem Smartphone oder Tablet ist im Handbuch nicht beschrieben.
Nachfolgend gehe ich davon aus, dass der TL-WR702N im Auslieferungszustand vorliegt. Dieser Zustand kann jederzeit hergestellt werden, indem der Reset Taster mit einem spitzen Gegenstand z.B. einer aufgebogenen Büroklammer, für ca. 7 Sekunden gedrückt wird, bis die Betriebs LED mehrmals kurz blinkt. Die nachfolgenden Schritte habe ich mit meinem Nexus 7 durchgespielt, mit anderen Geräten kann das leicht variieren.
Zunächst verbinde ich mich mit dem Tablet auf den Access Point des TL-WR702N. Die SSID, nach der ich suche, steht auf dem Typenschild des TL-WR702N, ebenso die zugehörige Passphrase.
Wenn ich die Verbindung hergestellt habe, wird mein Tablet eine IP aus dem 192.168.0.x Netz erhalten haben. Im Browser des Tablet gebe ich 192.168.0.254 ein, die IP des TL-WR702N zur Konfiguration. In der Abfragemaske wird für User: admin und Passwort: admin eingegeben. Wir kommen auf die Konfiguration des Routers und Erstellen eine Konfiguration für eine Bridge.
Der Vorteil der Bridge Konfiguration ist, dass ich später noch über WLAN auf die Konfigurationsschnittstelle komme, um Änderungen zu machen. Das ist im Client Modus nur über Ethernet möglich. Wichtig ist jetzt, dass über Quick Setup gegangen wird, sonst verliert man nach Reboot die Konfigurationsseite. Also Quick Setup, Next, Bridge, Next.
Unter Survey sehe ich die zur Verfügung stehenden WLANs und kann den gewünschten Access Point direkt mit connect auswählen. Nach Eingabe des Password für dieses WLAN gehe ich weiter mit Next. Wenn die Meldung erscheint „Your AP channel is not the same as the Bridged AP’s channel, do you want to change your channel to the Bridged AP’s channel?“ kann ich das mit OK bestätigen. Im nächsten Fenster wird kann die bestehende Verschlüsselung bestätigt werden oder eine neue Passphrase vergeben werden. Mit Next kommt man auf das letzte Fenster zum Reboot des Routers.
Beim Reboot verliere ich die Verbindung zur Konfiguration des Routers. Mein Tablet verliert möglicherweise auch die Anbindung zu Access Point oder verbindet sich zu einem anderen bekannten Access Point. Eigentlich ist jetzt alles fein und ich kann mein Ethernet Shield mit dem Router verbinden und loslegen. Wenn ich die Konfiguration mit dem Tablet bearbeiten will, muss ich die IP des Routers herausfinden. Wenn ich nicht die Möglichkeit habe, die IP auf meinem Router nachzusehen, kann ich folgenden Trick anwenden. Die Android App Ping-Tools hilft mir dabei. Ich stelle über das Menü links oben die Funktion UPnP scanner ein und schon sehe ich meinen TP-Link mit seiner IP-Adresse.
Über diese IP in der Browserzeile komme ich jetzt wieder auf die Konfigurationsschnittstelle, falls ich noch etwas ändern möchte. Sinnvoll wäre z.B. die Änderung des Standard Passworts für den Admin Zugang.
Der Stromverbrauch des TL-WR702N liegt übrigens nur bei ca, 150mA. Bei einer mobilen Anwendung kann der Router also ohne weiteres aus der 5V Schnittstelle des Arduino mit versorgt werden oder Arduino und der TL-WR702N werden über eine Powerbank mit USB Schnittstelle versorgt.
Als nächstes steht jetzt ein Test mit dem TP-Link TL-MR3020 an, der für unter 30€ mit gleichem Verfahren einen Arduino ins 3G/4G Mobilfunknetz und damit ins Internet bringt. Zusätzlich ist nur ein USB Stick mit Mobilfunkkarte notwendig.