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

Screenshot MyApp1

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":

 

Screenshot Winix BT 1

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.

Sceenshot BT devices

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