Android 7" Tablet als SX-Fahr und Schaltgerät
Anlass zu diesem Versuch war eine Werbemail von notebooksbilliger.de mit dem
"Schnäppchenangebot"
des xoro Tab721 für 50€ mit android 4.3 samt Bluetooth und USB.
Nach prompter Lieferung hatte ich als absoluter Anfänger in Sachen "Smart-Touch"
unerwartet wenig
Probleme bis der Browser Kontakt mit dem www hatte.
Das "Abenteuer" App-Entwicklung fand einen guten Anfang.
Der wichtigste Test stand aber noch aus, die datentechnische Verbindung via
Blutooth-SPP (serial port protocoll)
und die Nutzung der USB Schnittstelle. Mein Freund hat mich auf google PlayStore
hingewiesen als Fundgrube
für Apps aller Art. Siehe da, man hat sogar reichlich Auswahl an BT-Manager
Apps. Ich habe mich dann für
einen deutschen Entwicklernamen entschieden und einen Glücksgriff getan, denn
Jürgen Hausladen hat sich als
sehr hilfsbereiter Mann entpuppt, der rasch auf Hilfebitten per mail reagiert.
Download und Installation klappten problemlos. BT-Geräte die auf "sichtbar"
geschaltet sind, werden binnen
Sekunden erkannt und aufgelistet. Die Windows übliche Verbindungsprozedur mit
dem HTerm an dem virtuellen
COM-port klappt sofort, wenn das Tablet ebenfalls "sichtbar" ist und HTerm
"connected" ist.
Schon wandern getippte Texte hin und her!! Prima !!
Android Entwicklungs IDE
Wie nicht anders zu erwarten ist die Informationsmenge zu android App
Entwicklung unüberschaubar groß.
Aber schon aus der Häufigkeit in Buchtiteln und Forumsbeiträgen scheint
"eclipse" die erste Wahl zu sein.
Relativ neu ist das google android Studio, derzeit, im Feb. 2014, noch als
beta-Version kostenlos zu haben.
Weil schon der Name zu MS VisualStudio und AtmelStudio passt habe ich dieses
Paket installiert.
Mit etwas mehr Erfahrung in objektorientierter Programmierung gelingt das
Nachvollziehen des üblichen
"Hello world" Programmes mit Nachlesen einigermaßen rasch, wenn man der
Installationsanweisung genau
gefolgt ist, incl. setzen der Umgebungsvariablen für den Pfad auf das Java SDK.
Das war's dann aber mit rasch und ohne Probleme, wenn "Ihre erste App" auch auf
dem eigentlichen Gerät,
in meinem Fall das xoro721 erscheinen soll. "Quick Start" und die berühmten
"Erste Schritte" haben mir nicht
wirklich weitergeholfen. Es gibt zwar eine höchst einfachen Weg eine App auf dem
Zielgerät zu installieren
und zu starten, aber nur wenn die Voraussetzung gegeben ist:
1. Für das Gerät muss der passende ADB-Treiber auf dem PC
installiert sein !!
Nur dann erkennt Studio ein "running
device".
2. Im Gerät muss das USB-debugging in den
"Entwicklereinstellungen" aktiviert sein.
3. Das Gerät muss mit dem PC verbunden sein
Dann genügen zwei Clicks, 1. auf "Run" und 2. "ok" für das "running device".
Leider hat das mit dem xoro nicht geklappt und ich habe binnen 6 Wochen keinen
passenden ADB-Treiber
gefunden. Der xoro-support hatte auch nur einen fragwürdigen link auf einen
nicht funktionierenden
Treiber.
Ergo musste ich die Kopiermethode benutzen:
Das Gerät per mitgeliefertem USB-Kabel anschliessen, und das
PC-Betriebssystem, bei mir
Windows 7/64Bit prof., zeigt das Gerät bzw. dessen Speicher im Explorer mit der
Ordnerstruktur an,
wenn der USB-Speicheruzugriff in android freigegeben wurde, und nur dann. Das
USB-debugging
muss dabei aber gesperrt sein !!
Windows üblich die .apk im Entwicklungsverzeichnis suchen und auf das Tablet
kopieren.
Freundlicherweise bringt das xoro eine Install-App mit, sodass dieser Vorgang
mit ein paar Touches
tatsächlich mit dem Start der App endet. Hurra!!
Nahezu unverzichtbar ist für mich die Debugfunktion des Studio, die natürlich
nur mit dem "running device"
also passendem ADB-Treiber nutzbar ist.
Das "Drama" habe ich deshalb mit der Anschaffung eines google nexus 7 (2013) 16
Gb fortgesetzt.
Überzeugt davon, dass die neueste google Software das Referenzgerät nexus 7 in
der aktuellen Version
klaglos unterstützt.
Denk'ste !! Weitere Überraschungen und Frust war angesagt!
Da hatte ich endlich die "Entwickleroptionen" gefunden, d.h. 7 mal auf Build
geklickt, den ominösen
Haken in der Checkbox gesetzt, und trotzdem hat mein Studio kein "running
device".
Zum auswachsen und davon-running !!
Doch nach einigen Stunden fand ich auf der google nexus 7 site den
download "latest_usb_driver_windows.zip"
mit Datum 30.01.2014. Damit gelang auch die Installation und Studio listet mein
nexus 7 endlich
als "running device".
Software für App's tippen oder .... herbeiclicken
....
Ich halte die Entwicklung, schon etwas komplexerer
Programme in neuer Umhebung von Null an, für reichlich aussichtslos.
Deshalb habe ich wieder zu der Methode "modifizieren
eines Beispielprogrammes" gegriffen, weil dabei jeder Schritt
sichtbar ist und per Debug Daten und Funktionen im
Detail verfolgt werden können.
Erste Übung war, die wichtigsten Objekte, wie Buttons, Textfelder, Checkboxes
und weiterem Basis-widgets
im Layout zu platzieren und zu verwenden.
Leider hat der Autor meiner simplen Ausgangs-App ein "relatives Layout" gewählt,
das nach meinen Versuchen
das Prädikat "Unbrauchbar" erhalten hat. Was im C#-Designer ein Kinderspiel ist,
klappt bei mir im android
Studio überhaupt nicht. Aber es gibt ja auch die "linearen layouts" die man fast
beliebig schachteln kann. Eine
wichtige Erkenntnis, die für mich ausreichende Gestaltungsmöglichkeit gibt.
Ein übergeordnetes vertikales, lineares layout und für die zeilenweise Anordnung
von Objekten, horizontale
Layouts, mit relativer Lage der Objekte zueinander. Meine Arbeitsweise ist der
häufige Wechsel von Design-
und Textansicht der layout.xml Datei. Unhandlicher als MS-Studio aber akzeptabel
für geringere optische Ansprüche
und unbedarfte Anfänger, wie ich einer bin.
Sehr bequem und elegant sind Eingaben in Editfelder, weil die automatisch
eingeblendete Touchtastatur typabhängig
ist. Alle Zeichen für Texte, Ziffern und dazugehörige Sonderzeichen als
Nummernblock. Umfangreich und gut anwendbar
sind auch die Konvertierungsmethoden der Java Daten-, String- und
Bufferklassen.
Geradezu ExtraKlasse ist die intelligente Vorschlagfunktion im Editor. Auch
lange, "sprechende" Namen von Variablen,
Klassen und Methoden werden angeboten und sind mit "tab" komplett übernommen.
Ähnliches gilt für Argumente in
Methoden. Erspart Tippen und Schreibfehler. Perfekte Anfängerunterstützung!
Das Codieren der Logik und Datenfummelei fällt mir einigermaßen leicht wegen der
Übung mit C und C#.
My Application 1
Dieser screenshot stammt nicht aus dem Studio, sondern
ist eine "life" Aufnahme auf dem nexus 7.
Power-Taste und die Lautstärkewippe "Leiser" gemeinsam
etwa 1 Sekunde gedrückt und schon ist die
Datei unter pictures/screenshots gespeichert, in
voller 1:1 Auflösung des Display. Praktisch!
Mit diesem Programm habe ich für mich wichtige
"Objekte" und ihre Verwendung geübt. Einige Tage hat
es allerdings gedauert, ohne Bücher zu kaufen, aber mit online recherchen.
Natürlich haben Lehrbücher und gedruckte Referenzwälzer ihre
Daseinsberechtigung, nicht nur für die
Autoren und Händler.
Ob und wie man Lösungen, Hinweise oder Ansätze für einfache
Aufgabenstellungen, wo in welchem
Buch findet, ist bei online Suche nebenbei beantwortet oder total egal, wenn man
fündig geworden ist.
Mit den "richtigen" Schlagworten und Begriffen finden Suchmaschinen immer eine
hilfreiche Quelle,
weil diese "Bücherei" unendlich groß ist und immer geöffnet hat.
Beispiel:
Mit zwei Tasten soll ein Zähler gestartet und gestopt werden und der akt.
Zählerstand in einem
Textfeld ablesbar sein. Eine nicht zu schwierige Aufgabe auf den ersten Blick,
die ich für mich
brauchbar erledigen konnte. Mit Sicherheit gibt es noch andere Möglichkeiten.
Dass man die beiden Buttons erzeugen muss, im Designer oder in der .xml getippt,
und die Ereignismethode
"onClick" eintragen muss, ist bekannt:
<Button
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="T-Start"
android:id="@+id/button4"
android:clickable="true"
android:enabled="true"
android:onClick="onB4start" />
<Button
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="T-stop"
android:id="@+id/button5"
android:layout_alignParentTop="true"
android:layout_toRightOf="@+id/button4"
android:onClick="onB5stop"
android:clickable="true"
android:enabled="true" />
Und das Textfeld:
<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:textAppearance="?android:attr/textAppearanceLarge"
android:text="textView3"
android:id="@+id/textView3"
android:layout_alignBottom="@+id/button4"
android:layout_alignEnd="@+id/button"
android:layout_weight="1"
android:textAlignment="center"
android:layout_marginLeft="10dp"
android:layout_marginRight="5dp" />
Programmcode im java-Programm und natürlich die verwendeten Variablen:
int cnt = 0;
boolean tim1flag = false; // Timerstart freigegeben
Timer tim1; // Updatetimer
// Timer Start ************************************************************
public void onB4start(View v)
{
// Mehrfachstart des Timers verhindern ********************************
if (tim1flag == false) {
tim1flag = true; // Neustart sperren
tim1 = new Timer(); // Timer erzeugen
// Timertask erzeugen Start nach 1 Sek. dann alle 100 mS
tim1.schedule(new tim1Task(), 1000, 100);
}
else {
Toast.makeText(getApplicationContext(),
"Timer läuft schon !", Toast.LENGTH_LONG).show();
}
}
// Timer Stop *************************************************************
public void onB5stop(View v) {
if (tim1flag) {
tim1.cancel();
tim1flag = false; // Neustart
freigeben
} else {
Toast.makeText(getApplicationContext(),
"Timer schon gestoppt !!", Toast.LENGTH_LONG).show();
}
}
// Timer abgelaufen wird in separatem Thread ausgeführt. Deshalb keine
// direkten GUI Aktionen möglich, sondern via Handler "Runnable" starten.
public class tim1Task extends TimerTask
{
@Override public void run()
{
cnt++; // Zählen
myHandler.post(myRunnable);
// Start Ausgaberoutine
}
}
final Handler myHandler = new Handler();
// Diese Ausgaberoutine wird vom Timertask gestartet **********************
final Runnable myRunnable = new Runnable() {
public void run() {
TextView myTextView3=(TextView)
findViewById(R.id.textView3);
myTextView3.setText("cnt = " +
Integer.toString(cnt));
}
};
Wem das auf den zweiten Blick nicht trivial erscheint, ist mit mir einer
Meinung.
Später stellt man fest, dass es sich wirklich um keine besondere Schwierigkeit
handelt.
Die Themen, "process, thread, task" füllen viele Kapitel in vielen Büchern, auch
solchen über
java und android. Objektorientierte und ereignisgesteuerte Systeme und Programme
organisieren und verwalten die Abarbeitung der zugeordneten Programmabschnitte
und
deren Kommunikation untereinander. Eine hochkomplexe Aufgabe mit sehr
unterschiedlichen
Philosophien der Betriebssystem-Entwickler.
Dem Anwender ist es eigentlich egal wie das Betriebssystem dafür sorgt, dass bei
Click
auf den Button seine Ereignisroutine ausgeführt wird. Während dieser Ausführung
können aber andere Ereignisse auftreten, die sogar wichtiger sind oder keinen
Aufschub
der Bearbeitung erlauben. Ein solches Ereignis ist z.B. ein Timerintervall das
zu Ende geht
und eine Zählererhöhung bewirken soll. Weil der Anwender den aktuellen
Zählerstand auch
ablesen möchte, muss der Zählerwert konvertiert werden und die Anzeige geändert
werden.
Dass ganz nebenbei noch ein Download laufen kann, e-mails eintreffen dürfen,
wird als
selbstverständlich erachtet.
So versteht man, dass der Anwender im Wesentlichen nur Aufträge an das
Betriebsystem
erteilt und möglichst sofortige Erledigung erwartet.
Klar ist auch, dass sich das Betriebssystem vor dem Anwender schützen muss
wenn es am
Leben bleiben will, oder muss, wenn andere, quasi parallele Anwendungen laufen.
Dieser Schutz vor "bösen Anwendern" ist leichter möglich, wenn diese keinen
auführbaren
Maschinencode erzeugen, sondern nur Anweisungen, die erst einen Interpreteter in
der
jeweiligen "runtime Umgebung" durchlaufen und so auf "Verträglichkeit" überprüft
werden.
Einst war es Basic, jetzt auch java und C#.
Mit komplexen und leistungsfähigen Klassen ist es möglich die modernen "Mittel"
relativ
einfach zu verwenden. Als Beispiel meine "Zweite App":
Mit dieser App kann ich "drahtlos" Selectrix Adressen schalten und SX1 Loks
fahren. Also nichts besonderes und
schon gar nichts neues, aber für mich eine interessante Beschäftigung. Ausserdem
hatte ich endlich einen Grund
das längst geplante SX-Businterface zu realisieren.
Die Funkhardware musste ein Bluetooth-SPP Modul sein, weil android diese
Schnittstelle unterstützt und ein
MicroChip RovingNetworks RN41 Modul im Fundus liegt.
Die Softwarebasis war auf Hinweis von Jürgen Hausladen ein Beispielprogramm aus
2009 namens "BluetoothChat".
Den kompletten Projektordner für eclipse hat er mir gemailt. Vielen Dank
!!
Im android Studio gibt es eine Importfunktion für eclipse-Projekte mit
Konvertierung ins Studioformat. Was sich
hinter den project management tools "Gradle und Maven" verbirgt, weiß ich nicht.
Das "BlauzahnGeplauder" ist letztlich ein Terminalemulator via BT-SPP (serial
port profile). Im Gegensatz zu C#
und Windows gibt es in android keine seriellen ports und keine COM-Hardware.
Auch die Bluetooth Funktionalität
und der Gerätemanager sind so nicht vorhanden.
BluetoothChat besteht aus 3 Java-Programmteilen:
1. BluetoothChat.java
2. BluetoothChatService.java
3. DeviceListActivity.java
und den zugehörigen Layout.xml Dateien.
Das Projekt wurde fehlerfrei compiliert und auf meinem nexus 7 installiert
und gestartet.
Die Device Suche war erfolgreich und mein Modul FireFly-98BC erkannt und
gelistet.
Eine Verbindung kam jedoch nicht zustande. So war ich "gezwungen" die Struktur
zu verstehen und bin nach elender Sucherei
in "BluetoothChatService.java auf dieser Zeile fündig geworden:
private static final UUID MY_UUID_SECURE = UUID.fromString("fa87c0d0-afac-11de-8a39-0800200c9a66");
Ich hatte den Verdacht, dass diese ID nicht zu meinem Modul passt. Siehe da per google finde ich diesen Hinweis:
..... try using the well-known SPP UUID
00001101-0000-1000-8000-00805F9B34FB
Ergo habe ich den Versuch gemacht und kühn geändert in:
// Unique UUID for this application
private
static final UUID MY_UUID_SECURE =
UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
private
static final UUID MY_UUID_INSECURE =
UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
Alles soweit ok. Die Verbindung klappt sofort !! Hurra!!
Warum auch immer !!??
Dass SECURE und INSECURE jetzt die selbe ID haben, ist mir erstmal egal !!
Bei meinen AVR-Programmen mit dem BTM222 hatte ich gelernt, dass durch die
Paketbildung im SPP, Zeichenfolgen seltsam
zerstückelt werden und man im Empfang darauf achten muss, wenn man vollständige
Telegramme braucht.
Zuverlässig kommt nur 1 einziges Byte im android Empfangsbuffer an. Ich
erwarte aber von meinem Interface auch 112 Byte
als Telegramm "am Stück".
Dieses Problem hatten auch "Tausend" andere vor mir und es gibt einige
Code-Schnippsel als workaround, geholfen hat mir
endgültig nur eigenes Nachdenken.
In ChatService gibt es eine "while(true) Schleife wobei true für die bestehende
Verbindung steht.
Im "while(true)" Block habe ich geändert:
// Keep listening to the InputStream while connected
while (true) {
try {
// Read from the InputStream
bytes = mmInStream.read(buffer); // BUFFER AUSLESEN
und Länge setzen
} catch (IOException e) {
Log.e(TAG, "disconnected", e);
connectionLost();
// Start the service over to restart listening mode
BluetoothChatService.this.start();
break;
}
if (sxmode != 8) // Kein Blocktransfer
!**************************
// Send the obtained bytes to the UI Activity
mHandler.obtainMessage(BluetoothChat.MESSAGE_READ, bytes, -1,
buffer).sendToTarget();
// Blocktransfer = sxmode == 8
*************************************
else if (sxmode == 8) {
if (anz < 112) // Wenn noch nicht alle 112 da sind
{
for (int n = 0; bytes > n; n++)
telbuf[n +
anz] = buffer[n]; // neue Bytes anhängen
anz += bytes; // neue Anzahl Bytes
im Telegramm
// Wenn wenigstens 112 Bytes
erhalten, ist Block vollständig
if (anz >= 112) {
mHandler.obtainMessage(BluetoothChat.MESSAGE_READ, anz, -1,
telbuf).sendToTarget();
anz = 0;
// für nächsten Aufruf
sxmode = 0;
// Blocktransfer HIER beendet, NICHT in main !!!
}
}
} // Ende Blocktransfer Empfang
} // Ende while
Die "write methode" ist auch ergänzt, weil ich die lokale Variable
"sxmode" aus dem Kommando an das Interface
ableite:
/**
* Write to the connected OutStream.
* @param buffer The bytes to write
*/
public void write(byte[] buffer) {
// Schreibbefehl Blocktransfer in lokalem sxmode
merken für Empfang *******
if(buffer[0]==(byte)0x78) sxmode = 8;
try {
mmOutStream.write(buffer);
// Share the sent message back to the UI Activity
mHandler.obtainMessage(BluetoothChat.MESSAGE_WRITE, -1, -1,
buffer).sendToTarget();
} catch (IOException e) {
Log.e(TAG, "Exception during write", e);
}
}
Mehr Überraschungen hat mir das Original BluetoothChat nicht
bereitet.
Die Anpassung der Bedienoberfläche ist im BluetoothChat eingebaut und ging
problemlos.
Jonathan Bohmer, im April 2014