7 Aufgabe 6: Implementierung Teil 1 Ausgabe: Montag, 17.05.2010. Abgabe: (Eintrag ins SVN-System) Montag, 31.5.2010: 18:00 Uhr. 7.1 Anleitung 7.1.1 Gruppe A: Programmierung der BTnode Allgemeines Die BTNode System-Software wurde im Rahmen der Aufgabe 2 bereits eingeführt. Im Rahmen dieser Aufgabe werden Funktionen zum Steuern der LEDs (Einund Ausgabe über GPIO Pins), der I/O Pins, sowie zur Überwachung des Analog/Digital Wandlers bearbeitet. Es sind dies die hardwarenahen Funktionen, beispielsweise die “Aktorfunktionen die in einigen Sequenzdiagrammen aufgeführt sind. ” Der BTNode-Monitor Um Rückmeldungen aus einem BTNode-Programm zu ermöglichen, steht in der BTNutOS Library ein Monitor -Modul zur Verfügung. Der Monitor erlaubt es, Nachrichten von einer BTNode über einen seriellen Port mit einer Art printf-Befehl an einen PC zu senden und sie direkt in einem Terminalprogramm anzuzeigen. Zudem können textbasierte Befehle vom Terminal gestartet werden. Dieses Feature wird durch die ATMega128 UART Ports – UART (Universal Asynchronous Receiver Transmitter) – realisiert. Die ATMega128 hat zwei UART Schnittstellen – UART0 und UART1. UART0 wird benutzt, um die Kommunikation mit dem Bluetooth-Chip zu realisieren. UART1 kann benutzt werden, um ASCII-Text zu einem Terminal, d.h. zu einem Programm im Host Computer zu schicken. Bevor Messages über den Monitor geschickt werden können, muss der UART1 mit folgendem Code eingestellt werden: 1 2 # include < stdio .h > // freopen, includes <io.h> for ioctl # include < dev / usartavr .h > // NutRegisterDevice, APP UART, UART SETSPEED 3 4 5 6 7 8 9 void init_stdout ( void ) { u_long baud = 57600; NutR egiste rDevic e (& APP_UART , 0 , 0) ; freopen ( APP_UART . dev_name , " r + " , stdout ) ; // ”r+”: read+write _ioctl ( _fileno ( stdout ) , UART_SETSPEED , & baud ) ; } 61 Programmierprojekt Sommersemester 2010 Uni Tübingen, Arbeitsbereich Technische Informatik Obige Funktion initialisiert den UART1 mit einer Übertragungsrate von 57600Bits/s, registriert diesen Kanal als ein Device unter dem Betriebssystem BTNut/OS und öffnet eine Verbindung die Schreiben und Lesen erlaubt. Jetzt können mit der Funktion printf Nachrichten geschickt werden, wie folgendes Beispiel zeigt: 1 2 3 4 5 6 7 8 9 int main ( void ) { btn _hardw are_in it () ; /∗ i n i t i a l i z e SRAM ∗/ init_stdout () ; int variable = 13; printf ( " Hello world , " ) ; printf ( " my lucky number is % d \ n " , variable ) ; for (;;) ; /∗ main s h o u l d n e v e r r e t u r n ∗/ } Der Monitor soll beim Debugging von Programmen und Funktionen helfen, er bietet eine bessere Schnittstelle zum Programmierer als die LEDs. Mehr über den Monitor erfahren Sie in den Beispielprogrammen, die mit dem BTNode-Paket geliefert werden. TIPP: Schließen Sie das Terminalprogramm im Host, bevor Sie ein Programm in die BTNode laden, sonst können Fehler im geladenen Code auftreten. AVR Hardware – Ein-/Ausgabe Ports Die allgemeinen Charakteristika des AVR ATMega128L Mikroprozessors wurden schon in der Aufgabe 2 diskutiert. Die ATMega128L enthält 6 bidirektionalen Ein-/AusgabePorts jeweils mit 8 Bits. Da diese Ports unterschiedlichen Funktionalitäten, wie z.B. Seriellschnittstelle, Interrupteingänge und A/D Wandlerschnittstelle, übernehmen können, soll eine Einstellung der vorliegenden Schaltung für die Nutzung programmiert werden. Mehr Informationen können in [1] gefunden werden. Die Schaltung des Ein-/Ausgabe-Ports des AVR Controllers, dargestellt in der Abbildung 7.1, haben true Read-Modify-Write Funktionalität. Das bedeutet, dass es möglich ist, die Richtung eines einzelnen Pins (Ein- oder Ausgabe) zu ändern, ohne versehentlich die Richtung eines anderen Pins mit zu verändern. Dies gilt ebenfalls, wenn der Wert des Treibers eines Output-Pins geändert wird, oder Pull-Up-Transistoren einausgeschaltet werden. Das Betriebssystem des AVR Controllers stellt dazu zwei Funktionen zur Verfügung: • cbi(PORT, Pin-Nr.) - Löscht Pin (Pin-Nr.) an Port (PORT) • sbi(PORT, Pin-NR.) - Setzt Pin (Pin-Nr.) an Port (PORT) 62 Programmierprojekt Sommersemester 2010 Uni Tübingen, Arbeitsbereich Technische Informatik Abbildung 7.1: Schaltbild der General-Purpose I/O-Pins von Atmega128L [1] AVR Hardware – Analog/Digital Wandler In der Natur vorkommende Signale sind analog. Möchte man analoge Signale aus der realen Welt mit der digitalen Welt der Computer in Verbindung bringen, so muss eine Umwandlung der Information von analoger in digitale Form und umgekehrt durchgeführt werden. Ein Analog-Digital-Wandler (auch: Analog-Digital-Umsetzer, A/D-Wandler oder englisch ADC für Analog-to-Digital-Converter) wandelt nach unterschiedlichen Methoden analoge Eingangssignale in digitale Daten bzw. einen Datenstrom um, der dann weiterverarbeitet oder gespeichert werden kann. In der ersten Stufe einer signalverarbeitenden Maschine, der Sample-and-Hold-Stufe“ werden nach einer Filterung des Signals ” in gleichem zeitlichen Abstand ’Proben’ des Signals gezogen, die nun zeitdiskret sind, aber immer noch jeden beliebigen Wert der Amplitude aufweisen können, Abbildung 7.2. Man nennt dieses Signal im Englischen auch “sampled signal . ” 63 Programmierprojekt Sommersemester 2010 Uni Tübingen, Arbeitsbereich Technische Informatik Abbildung 7.2: Signaltypen Nach dem Sample-and-Hold-Baustein wird das Signal nun vom AD-Wandler in digitale Form gebracht. Dabei weist der AD-Wandler jedem kontinuierlichen Wertebereich der Amplitude einen bestimmten Wert zu. So erhält man ein sowohl zeit- als auch amplitudendiskretes Signal, das in einem Signalprozessor verarbeitet werden kann. Das Gegenstück ist der Digital-Analog-Wandler oder DAC. Nach der Bearbeitung im Signalprozessor kann das Signal durch einen D/A-Wandler wieder in analoge Form transformiert werden. Es liegt dann wieder in zeitkontinuierlicher, aber amplitudendiskreter Form vor. Man sieht also, dass das ursprüngliche analoge Signal nicht wieder gänzlich rekonstruiert werden kann. Bei genügend hoher Auflösung der beteiligten Komponenten (A/D-Wandler, Signalprozessor, D/A-Wandler) kann das Ausgangssignal aber als analog angesehen werden. Der ATmega128 CPU verfügt über einen A/D-Wandler mit einer 10-bit Auflösung, Abbildung 7.3. Der A/D-Wandler ist an einen 8-Kanal Multiplexer angeschlossen, der die 8 Pins des Ports F als Eingänge hat. Der A/D-Wandler wird konfiguriert, indem die Special Purpose Register geschrieben werden. Der Status und das Ergebnis der Konvertierung kann wiederum aus den Special Purpose Registern ausgelesen werden. Auch der A/D-Wandler wird durch das Schreiben von Special Purpose Registern konfiguriert. Auf den Status und das Ergebnis der Konvertierung kann dadurch zugegriffen 64 Programmierprojekt Sommersemester 2010 Uni Tübingen, Arbeitsbereich Technische Informatik werden, dass die Special Purpose Register gelesen werden. Im Falle des A/D-Wandlers werden zwei 8-bit Register namens ADMUX und ADCSRA für die Konfiguration und die Statusinformationen verwendet. Die 8-bit Register ADCH und ADCL werden für das Ergebnis der Konvertierung verwendet. Abbildung 7.3: A/D-Wandler im ATmega128L, S.131 aus Dokumentation des ATmega128L (im SVN unter Literatur eingecheckt) Wir wollen jetzt die Spannung der Batterie messen und das Ergebnis mit Hilfe der LEDs anzeigen. Die Lösung sollte wie folgt aussehen: 65 Programmierprojekt Sommersemester 2010 Uni Tübingen, Arbeitsbereich Technische Informatik Der Lückencode der get_battery_voltage Funktion sieht wie folgt aus: Im ersten Schritt wird das Register ADMUX konfiguriert. Wie im Manual der ATmega128L (http://www.atmel.com/dyn/resources/prod_documents/doc2467.pdf) auf der Seite 244 zu sehen ist, ist das BAT SENSE Signal mit dem Pin 3 des Ports F verbunden. Dem Manual entnehmen wir ebenfalls (Seite 239), dass dieser Pin der dritte Kanal des A/D-Wandlers ist. Die Tabelle 98 auf der Seite 244 zeigt, dass die Bits MUX1 und MUX0 vom ADMUX Register gesetzt sein müssen, damit die Spannung am Kanal 3 des A/D-Wandlers gemessen werden kann. Das ADLAR Bit bleibt auf Null gesetzt. Die Bits REFS1 und REFS0 bleiben ebenfalls auf Null gesetzt, weil wir die externe Spannungsreferenz benutzen, die an den AREF Pin des ATmega128 angeschlossen ist. ACHTUNG: BENUTZEN SIE KEINE ANDEREN EINSTELLUNGEN FÜR DIE REFSx BITS, WEIL DADURCH DER MIKROKONTROLLER BESCHÄDIGT WERDEN KÖNNTE! 7.1.2 Gruppe B - Einführung in die MIDlet-Programmierung mit Java2ME Allgemeines In der ersten Implementierungsphase soll ein MIDlet programmiert werden, welches auf Benutzereingaben reagieren kann, eine Inquiry durchführt, die Dienstabfrage an gefundene Geräte schickt und die daraufhin erhaltenen Dienste dem Nutzer präsentiert. Das Nutzen der Dienste sowohl des Host als auch der BTnodes soll in den Aufgaben 7 und 8 implementiert werden. Java2ME ist eine Umsetzung der Programmiersprache Java für embedded consumer products wie etwa Mobiltelefone oder PDAs. JSR 30 und JSR 37 (Java Specification Request) definieren J2ME. Java Micro Edition kann in verschiedenen Konfigurationen eingesetzt werden. Eine Konfiguration ist eine Menge von Java APIs, Klassenbibliotheken und einer virtuellen Maschine, die auf eine bestimmte Gerätefamilie mit ähnlichen Anforderungen konzipiert ist, d.h. eine Konfiguration stellt eine Spezifikation für eine Familie von Endgeräten dar. Die Entwicklung von Konfigurationen für J2ME wird hauptsächlich durch die Firma Sun und deren Entwicklungspartner vorangetrieben. Derzeit existieren zwei Standardkonfigurationen, nämlich Connected Device Configuration (CDC) und Connected Limited Device Configuration (CLDC). 66 Programmierprojekt Sommersemester 2010 Uni Tübingen, Arbeitsbereich Technische Informatik Die CDC (JSR 218) ist eine J2ME Konfiguration für Geräte, die eine komplette Implementierung der JVM und eine Menge von APIs benötigen, die durch das Hinzufügen von Profilen die gesamte Java SE Plattform API einschließen können. Typische Implementierungen benutzen nur einen Teil der SE APIs, indem nur eine bestimmte Anzahl an Profilen eingebunden wird. Die CLDC (JSR 30 und JSR 139) definiert die kleinstmögliche Konfiguration einer J2ME-Laufzeitumgebung. Zu den größten Einschränkungen in Version 1.0 zählt der Verzicht auf Fließkommaberechnungen. Das betrifft sowohl Variablen, die als float, double, als auch als java.lang.Float und java.lang.Double deklariert sind. Weiterentwicklungen der CLDC in Version 1.1 führten u. a. zu einer Wiedereinführung der Fließkommaunterstützung. CLDC wird hauptsächlich in Mobilen Geräten in Verbindung mit dem ”Mobile Information Device Profile”(MIDP) benutzt. MIDP (Mobile Information Device Profile) ist ein Profil der Java 2 Micro Edition (J2ME), das speziell auf die Fähigkeiten kleiner mobiler Endgeräte wie Mobiltelefon oder Pager ausgelegt ist. Profile sind die APIs, die es für eine Konfiguration gibt. Sie stellen eine weitere Art der Anpassungsmöglichkeit der Java2 API an Anforderungen für Gerätefamilien dar. Applikationen auf Basis von MIDP nennt man kurz MIDlets (z.B. Java-Spiele für Handys). Das MIDP umfasst Funktionen zur Ansteuerung und Abfrage von Einhandtastaturen, Miniaturbildschirmen, flüchtigen und nicht-flüchtigen Speichern im Kilobyte-Bereich etc. Es existieren bisher MIDP 1.0 (JSR 37) und MIDP 2.0 (JSR 118), das einige Erweiterungen aufweist. MIDP 3.0 ist bereits als JSR 271 in Bearbeitung. In unserem Projekt verwenden wir Mobiltelefone, die MIDP 2.0-fähig sind. Konfigurationen bieten eine breite Variabilität für verschiedene Einsatzzwecke, sind jedoch oft nicht ausreichend, um die komplette Funktionalität verschiedener Geräte verwenden zu können. Daher gibt es Erweiterungen, die interne Gerätefunktionen unterstützen, wie z.B. Bluetoothfunktionalität (siehe JSR 82), die für unsere Zwecke benutzt werden wird, Abbildung 7.4. Die Entwicklung von Konfigurationen, Profilen und Erweiterungen wird im Rahmen des ”Java Community Process”(JCP) durchgeführt, an denen auch namhafte Firmen wie Nokia, Siemens oder IBM beteiligt sind. Als Benutzeroberfläche bieten MIDP-APIs eine Menge von User-Interface-Elementen (UI). Diese ermöglichen Interaktionen zwischen Benutzer und MIDlet und befinden sich im Paket javax.microedition.lcdui. Die High-Level-API (vgl. Abb. 7.5) stellt Ein- und Ausgabefelder, wie z. B. Textfelder (TextField) oder Fortschrittsanzeigen (Gauge), zur Verfügung. Sie sind der Elternklasse Item untergeordnet. Objekte vom Typ Item“ können auf einem Formular platziert wer” den, sind jedoch nur eingeschränkt positionierbar. Formulare sind Objekte der Klasse Form“. Sie können an das aktuelle Display angehängt werden und verschiedene UI” Elemente beinhalten. Das MIDlet kann Wechsel zwischen Formularen anfordern sowie während der Laufzeit UI-Elemente hinzufügen und auf Benutzereingaben reagieren. 67 Programmierprojekt Sommersemester 2010 Uni Tübingen, Arbeitsbereich Technische Informatik Abbildung 7.4: CLDC/MIDP-Architektur, spezifiziert in JSR082 Die wichtigsten UI-Elemente sind (vgl. Abb. 7.5): • Form: Container für andere UI-Elemente • Item: Repräsentiert einen Menüeintrag. Mehrere Items können in einem Menü zusammengefasst und an ein Formular angehängt werden • Alert: Popup-Nachrichten die den Benutzer über Fehler, Exceptions, Warnungen oder über sonstige Informationen benachrichtigen • ChoiceGroup: Implementiert eine Selektionsmöglichkeit zwischen mehreren Einträgen. Die Auswahl ausschließlich einzelner (engl. single choice“) oder auch meh” rerer Einträge (engl. multiple choice“) ist möglich ” • TextBox: Einzeilige Eingabefelder, in denen der Benutzer Text einfügen bzw. editieren kann • TextField: Ähnlich einer TextBox, allerdings mehrzeilig • Gauge: Fortschrittsanzeige • Ticker: Anzeige von bewegtem Text Im Gegensatz hierzu arbeitet die Low-Level-API auf Pixelebene. Die Klasse Can” vas“ ist der Eingangspunkt für graphische Zeichnungen. Sie selbst beinhaltet hierfür keine Methoden, jedoch stellt sie die Callback-Funktion paint() bereit. Sie wird immer dann aufgerufen, wenn der Programmmanager entscheidet, das Display neu zu zeichnen. Ihr einziger Parameter ist ein Objekt Graphics, welches sämtliche Zeichnungsfunktionen, wie beispielsweise drawLine() zum Zeichnen einer Linie oder fillRect() zum Ausfüllen eines Rechtecks, beinhaltet. 68 Programmierprojekt Sommersemester 2010 Uni Tübingen, Arbeitsbereich Technische Informatik Abbildung 7.5: High level und Low level MIDP API Grundsätzlich kann man zwischen reinen Hintergrundapplikationen und jenen unterscheiden, die mit dem Benutzer interagieren. Interaktive Applikationen können auf das Display über ein Objekt Display zugreifen. Man erhält es als Rückgabeobjekt der statischen Methode getDisplay() mit dem MIDlet als Argument. Die Methode setCurrent() bestimmt, welches Objekt Displayable den Inhalt eines Displays darstellen soll. Displayable ist die Elternklasse von Screen und Canvas. Ihr sind alle UI-Klassen unterstellt. Mit anderen Worten, sie definiert sämtliche Objekte die am Display angezeigt werden können. MIDLET Programmierung auf dem Mobiltelefon Die angegebenen Code-Schnipsel sind Beispiele. Sie haben die erste Kommunikation bereits in Aufgabe 3 durchgeführt. Die endgültige Implementierung hängt von der Spezifikation und von Ihrem Klassendiagramm im Entwurfsdokument ab. In Aufgabe 2 und 3 haben Sie bereits gesehen, welche Funktionalität ein MIDlet bietet. Die Bluetooth Demo aus dem Wireless Toolkit sollte Ihnen dabei zeigen welche einzelnen Schritte für eine Bluetooth Kommunikation nötig sind. Nun ist es Ihre Aufgabe ein eigenes MIDlet zu entwerfen, welches den im Pflichtenheft definierten Anforderungen entspricht. Dabei soll in dieser Aufgabe das Augenmerk auf dem Starten des MIDlets, dem Suchen nach Bluetooth-Geräten in der Umgebung und der Dienstanfrage liegen. Dazu gibt es eine gute Einführung in einer MastersThesis von André N. Klingsheim, siehe [6]. Darin wird der Aufbau eines MIDlets 69 Programmierprojekt Sommersemester 2010 Uni Tübingen, Arbeitsbereich Technische Informatik Schritt für Schritt erklärt und mit beigefügtem Source-Code erläutert. Allerdings benutzt Klingsheim die Service-Discovery“-Methode, die in unserer Umgebung mit dem Nokia ” 6680-Telefon nicht funktioniert (Wir verwenden dafür das Serial Port Profile SPP). Ein weiters gutes Buch ist von Kumar et al. (Java Programming with Bluetooth: Eine Kopie der für Sie interessanten Kapitel finden Sie im Abschnitt “Literatur in unserm SVN). ” Implementieren Sie das dort vorgestellte MIDlet und fügen Sie folgende Funktionalität hinzu: • In der Funktion startApp() soll dem Benutzer ein Willkommensbildschirm präsentiert werden, welcher die Möglichkeit bietet mit der DeviceSearch zu beginnen, oder das Programm zu beenden. • In der Funktion commandAction(Command c, Displayable s) werden die Benutzereingaben verarbeitet. So soll z.B. bei Command OK“ auf Menüeintrag Start ” ” DeviceSearch“, welchen Sie aus dem Objekt Displayable s herauslesen können, die Inquiry gestartet werden, bzw. wie im Beispiel eine Funktion doDeviceDiscovery() ausgeführt werden. • In der Funktion inquiryCompleted(int param) soll dem Benutzer die Liste der gefundenen Geräte präsentiert werden. Dabei müssen Sie darauf achten, dass die Liste der Geräte auf dem Display auswählbar ist, sodass es möglich ist, ein Gerät zu selektieren und die Service-Suche zu starten. • Da die Service-Suche über das DiscoveryListener Interface aufgrund von Kompatibiltätsproblemen nicht einwandfrei funktioniert, können Sie die Funktionen – public void servicesDiscovered(int transID, ServiceRecord[] serviceRecord ){} und – public void serviceSearchCompleted(int transID, int respCode){} leer lassen, müssen sie jedoch aufgrund des Interfaces so übernehmen. Sollte ihre Entwicklungsumgebung automatisch in den Rumpf der Methoden eine UnsupportedOperationException einfügen, so kommentieren Sie diese Zeile bitte aus, denn die Klasse ist nicht jedem Endgerät bekannt und kann daher Fehler verursachen. • In der Funktion doDeviceDisocovery() soll dem Nutzer an allen Stellen an denen //Error handling Code here steht eine Fehlermeldung ausgegeben werden, aus welcher ersichtlich ist, an welcher Stelle das Programm einen Fehler ausgeworfen hat. • Zur Dienstsuche müssen Sie in der Funktion commandAction(Command c, Displayable s) bei Command OK“ und einem ausgewählten Gerät den in der Spezifikation ” definierten String an das betreffende Gerät senden. 70 Programmierprojekt Sommersemester 2010 Uni Tübingen, Arbeitsbereich Technische Informatik Dienstsuche Zur Dienstsuche können Sie folgendes Verfahren benutzen: Zunächst benötigen Sie die Bluetooth-Adresse des betreffenden Geräts. Diese erhalten Sie aus dem Objekt RemoteDevice in der Funktion deviceDiscovered(). Sie benötigen dann eine Verbindung zu diesem RemoteDevice: StreamConnection streamConn = (StreamConnection)Connector.open(url); Die Connector-Klasse aus javax.microedition.io.Connector öffnet eine Verbindung und gibt ein Connection-Objekt zurück, welches in die entsprechende Verbindungsart gecastet werden kann (hier also StreamConnection). Der url-Parameterstring muss dem URLFormat aus RFC 2396 entsprechen. Er hat die allgemeine Form wie folgt: {scheme}:{target}[{params}] • {scheme}: Name des Protkolls, z.B. btspp oder http • {target}: Eine Netzwerkadresse, bluetooth address of remoteDevice:1“ ” • [{params}]: Weitere Parameter der Form ;x=Y“, z.B. ;app-name=MobilerKnoten“ ” ” Wird das ServiceDiscoveryProtocol verwendet, dann wird hinter der target-Netzwerkadresse die PSM (Protocol Service Muliplexer, wichtigster Parameter der L2CAP-Verbindung, siehe [11]) angegeben, um einen speziellen Dienst im Server anzusprechen. PSM 1 (genauer 0x0001) enspricht z.B. dem ServiceDiscoveryProtocol. Da wir in unserer Umgebung das ServiceDiscoveryProtocol nicht verwenden (können), benötigen wir auch den PSM nicht. Wir verwenden, abhängig vom anzusprechenden Gerät, btl2cap und btspp als scheme. Die Connector-Strings sehen dann folgendermaßen aus: BTNode: "btl2cap://"+ [MAC-Adresse des BTN] + ":0xE001" . . . und für den Host: "btspp://"+ [MAC-Adresse des Hosts] + ":1;encrypt=false;authenticate=false" Der Verbindungsaufbau zur BTNode L2CAPConnection l2cap; l2cap = (L2CAPConnection)Connector.open(String BTN-ConnectorString (s. oben)); Eine Nachricht wird folgendermaßen verschickt: byte[] meineNachricht; 71 Programmierprojekt Sommersemester 2010 Uni Tübingen, Arbeitsbereich Technische Informatik l2cap.send(meineNachricht); und um eine Nachricht zu empfangen: byte[] antwort; l2cap.receive(antwort); Verbindungsaufbau zum Host StreamConnection streamConn; streamConn=(StreamConnection)Connector.open(String SPP-ConnectorString(s.o.)); Input-/Output-Streams zum Empfangen und Senden von Daten: DataInputStream inStream = streamConn.openDataInputStream(); DataOutputStream outStream = streamConn.openDataOutputStream(); Senden Sie nun über diese Verbindung den in der Spezifikation festgelegten String. Benutzen Sie für Input und Output über Bluetooth die Funktionen writeUTF(String s) und readUTF(): String request = "ihrString"; outStream.writeUTF(request); Da es sich bei der geöffneten Verbindung um eine Verbindung mit Puffer handelt, müssen Sie diesen leeren und somit die Nachricht senden: outStream.flush(); Schließen Sie danach den OutputSream, nicht jedoch ihre StreamConnection: outStream.close(); Danach müssen Sie nur noch auf die Antwort der Gegenstelle warten: Dazu benötigen Sie den InputStream und warten auf eingehende Daten: inStream = streamConn.openDataInputStream(); String answer=null; answer = inStream.readUTF(); Schließen Sie den InputStream und die StreamConnection: inStream.close(); 72 Programmierprojekt Sommersemester 2010 Uni Tübingen, Arbeitsbereich Technische Informatik streamConn.close(); Parsen Sie nun den erhaltenen answer String und präsentieren dem Benutzer die angebotenen Dienste. Weitere hilfreiche Links • http://www.nokia.de/de/mobiltelefone/modelluebersicht/6680/startseite/150206.html • http://jcp.org/en/jsr/all • http://www.m-software.de/handy-galerie/Nokia/6680.php • http://java.sun.com/products/midp/ • http://java.sun.com/products/cldc/ • http://wireless.klings.org/klings jabwt master thesis.pdf • http://de.wikipedia.org/wiki/MIDP • http://developers.sun.com/techtopics/mobility/midp/articles/bluetooth2/ • http://java.sun.com/products/sjwtoolkit • http://www.mobileinfo.com/ • http://java.sun.com/javame/reference/apis/jsr082/ • http://java.sun.com/javame/reference/apis/jsr139/ • http://java.sun.com/javame/reference/apis/jsr118/ 73 Programmierprojekt Sommersemester 2010 Uni Tübingen, Arbeitsbereich Technische Informatik 7.1.3 Gruppe C - BT-Server-Programmierung mit JavaSE Allgemeines Ziel ist die Programmierung eines Servers im Host, der selbst drei Dienste anbietet. Zusätzlich soll die “Diensteliste als Antwort auf eine Dienstanfrage eines ” entfernten Gerätes die im Host registrierten Dienste an das anfragende Gerät zurückgeben. Für die Programmierung soll die Java BT Implementierung von Benhui Bluecove (für Windows) sowie das Java J2SE Development Kit und die J2SE Runtime Environment 5.0 Update 10 oder höher verwendet werden. Als Entwicklungsumgebung bietet sich eclipse an. Hier muss ein neues Java-Projekt angelegt werden. Es muss die Benhui Bluecove Bibliothek ins Projekt eingebunden werden (über Rechte Maustaste auf Projekt → BuildPath → Configure Build Path → Add External Jars). Das Gegenstück zu dem hier zu entwickelnden Hostprogramm ist das Clientprogramm der Gruppe B. Zum Testen sprechen Sie sich bitte mit der Gruppe B ab. Grundstruktur eines Servers Ein BT-Server legt für jedes Gerät, mit dem er eine Verbindung eingeht, einen Thread an. Die Programmierschritte im Server sind: • Initialisierung des BT-Stacks • Antworte auf eine Dienste-Anfrage • Warte auf Client-Verbindungsanfragen und akzeptiere diese und bearbeite ClientAnforderungen • Schließe den Dienst ab Es existieren einige Tutorials von SUN im Netz, die recht gut sind. Wir empfehlen: • Tutorial 1: http://developers.sun.com/techtopics/mobility/apis/articles/bluetoothcore/ • Tutorial 2: http://developers.sun.com/techtopics/mobility/midp/articles/bluetooth2/ Außerdem zu empfehlen ist die Homepage von Benhui, auf der Beispielprogramme und viele nützliche Informationen sowie ein Diskussionsforum zu finden sind. Initialisierung des Servers Um einen Dienst im Server zu registrieren, ist zunächst die Initialisierung des lokalen Bluetooth-Stacks und des lokalen BT-Gerätes nötig, d.h. der angeschlossene BT-Dongle muss über die JavaAPI initialisiert werden. Ein Aufruf von: LocalDevice localDevice = getLocalDevice(); 74 Programmierprojekt Sommersemester 2010 Uni Tübingen, Arbeitsbereich Technische Informatik aus dem javax.bluetooth.* -Paket erledigt dies. Zusätzlich muss ein StreamConnectionNotifier -Objekt angelegt werden, welche ähnlich wie ein Server-Socket eingehende Verbindung akzeptiert. Die Initialisierung des StreamConnectionNotifiers geschieht über folgenden Aufruf: StreamConnectionNotifier serverconn = (StreamConnectionNotifier)Connector.open (url); Die Connector-Klasse aus javax.microedition.io.Connector öffnet eine Verbindung und gibt ein Connection- Objekt zurück, welches in die entsprechende Verbindungsart gecastet werden kann (hier also StreamConnectionNotifier). Der url-Parameterstring muss dem URL-Format aus RFC 2396 entsprechen. Er hat die allgemeine Form wie folgt: {scheme}:[target][params] • {Scheme}: Name des Protkolls, z.B. btspp oder http. (Bei uns: btspp) • {target}: Eine Netzwerkadresse, für den lokalen Host “localhost:1 , für entfernte ” Geräte eine BT-Adresse • {params}: Weitere Parameter der Form “;x=Y , z.B. “;app-name=HostNode ” ” Hinter der target-Netzwerkadresse wird die PSM (Protocol Service Muliplexer, wichtigster Parameter der L2CAP-Verbindung, siehe [1]) angegeben, um einen speziellen Dienst im Server anzusprechen. PSM 1 (genauer 0x0001) entspricht z.B. dem ServiceDiscoveryProtocol, das wir allerdings nicht verwenden. Auf diese Weise kann später mittels der UUIDs für die Serverdienste ein Dienst gezielt ausgewählt werden (eine PSM entspricht letztlich einer UUID, da die 16- bzw. 32-Bit-PSM intern in eine 128-Bit UUID umgewandelt wird (siehe BluetoothAPI:UUID für eine Beschreibung der UUID-Klasse). Obwohl wir das Service Discovery Protokoll (SDP) nicht verwenden, hier noch einige Bemerkungen dazu: Die UUIDs für die Serverdienste müssen angelegt und im ServiceRecord des Hosts abgelegt werden. Damit sind die Services im ServiceRecord der localDevice registriert und können bei einer ServiceDiscovery durch ein entferntes Gerät gefunden werden. Eine Beschreibung des SDP findet sich in der Bluetooth Core Specification v2.0. Akzeptieren einer Client Verbindung In dieser Aufgabe geht es darum, eingehende Clientverbindungen zu verarbeiten. Die Dienstauswahl geschieht hier vereinfacht über den Austausch von Strings, die im Host geparst werden. Das Mobiltelefon (MT) schickt zuerst einen String, der in der Spezifikation definiert ist, an den Host. Im Host soll dies erkannt werden und die registrierten Dienste ebenfalls als String zurückgeschickt werden (siehe Spezifikation). Dieser String kann vom MT wiederum erkannt werden, welches dann die einzelnen Dienste zur Auswahl freigibt. Das Handling der angebotenen Dienste wird in den folgenden Aufgaben durchgeführt. Hier soll zunächst nur die Dienst-Antwort auf die Dienstanfrage des Client 75 Programmierprojekt Sommersemester 2010 Uni Tübingen, Arbeitsbereich Technische Informatik implementiert werden. Akzeptieren einer Clientverbindung wird durch: StreamConnection clientconn = serverconn.acceptAndOpen(); ausgeführt. Dadurch wird ein StreamConnection Objekt erstellt, über das verschiedene Schreib- und Leseoperationen durchgeführt werden können. Dienstanfrage: “Service Discovery ” Für die Service Discovery sendet in diesem Projekt das Mobiltelefon einen String (über das “Serial Port Profile SPP) “SendServices an den Server. Dieser parst den String ” ” und schickt seinerseits einen String mit der Liste der angebotenen Dienste (also ”TelefonService, Gebäudeplan-Service, Quiz-Service-String”) zurück. Falsche (unverständliche) Anfragen vom Handy sollten mit einer Fehlermeldung beantwortet werden. Benutzen Sie für Input und Output über Bluetooth die Funktionen writeUTF(String s) und readUTF(). Sie müssen zunächst ein DataInputStream Objekt für die eingehende Anfrage erzeugen: DataInputStream in = clientconn.openDataInputStream(); bevor Sie mit String s = in.readUTF(); den eingehenden String lesen können. Analog läuft das Schreiben auf den DataOutputStream ab: DataOutputStream out = clientconn.openDataOutputStream(); out.writeUTF("ihreantwort"); Da es sich bei der geöffneten Verbindung um eine Verbindung mit Puffer handelt, müssen Sie diesen leeren und somit die Nachricht senden: outStream.flush(); Schließen Sie danach den OutputSream, nicht jedoch ihre Stream Connection: outStream.close(); 76 Programmierprojekt Sommersemester 2010 Uni Tübingen, Arbeitsbereich Technische Informatik 7.2 Aufgabe 6 Allgemeines: Für alle Gruppen: Mit der Aufgabe 6 beginnt die Implementierungs-Phase (Programmierung). Für die Implementierung, wie sie im Pflichtenheft und in der Spezifikation beschrieben ist, haben Sie drei Wochen Zeit, (d. h. die Aufgaben 6 bis 8 einschließlich sind Programmieraufgaben). Da Ihre erste Kommunikation bzw. Verbindung mit Ihrer Nachbargruppe bereits in Aufgabe 3 gelöst wurde, ist der weitere Datenaustausch mit den entsprechenden Anzeigen wahrscheinlich wesentlich einfacher. Wir haben die Implementierung der einzelnen Funktionen des Pflichtenhefts etwa gleichmäßig auf die Aufgaben 6 bis 8 verteilt. Da Sie die Funktionen bereits alle in der Spezifikation spezifiziert haben, können Sie natürlich auch vorarbeiten, wenn Sie wollen. Halten Sie sich an Ihre SW-Architektur, an die Sequenzdiagramme, bzw. an Ihr Klassendiagramm aus dem Entwurfsdokument. Achten Sie darauf, dass Ihr Code gut strukturiert, gut lesbar und verständlich ist (Kommentare!). Die Tutoren werden sich Ihren Code zu gegebener Zeit kritisch anschauen. 7.2.1 Gruppe A: Lesen Sie die Anleitung für Aufgabe 6. Die Kommentarköpfe Ihrer Programme bitte so schreiben, dass das Werkzeug doxygen sie versteht und daraus eine Entwicklungsdokumentation erstellen kann. Aufgaben zu den Ein-/Ausgabeports: Vorbereitung der Module Aktorfunktionen“ ” und Notfall-Sensor“ ” 1. Auf der BTnode befinden sich 4 LEDs. Schreiben Sie ein Programm, das mit Hilfe der BTNut/OS API LED 1 einschaltet. 2. Erweitern Sie das unter Punkt 1 entwickelte Programm, um die folgende Punkte zu vervollständigen: a) Die Funktionalität für das Einschalten der LED 1 soll in eine Funktion gekapselt sein. Diese Funktion ist Teil des Aktorfunktionen“-Moduls, das im Ent” wurfsdokument beschrieben ist. Achtung: Nennen Sie die Funktionen/Funktionsaufrufe (Befehle) entsprechend der Beschreibung in der Spezifikation. Schreiben Sie eine zweite Funktion, die das Ausschalten des LEDs bewirkt. b) Die LEDs sind geeignet für Tests, da sie schon korrekt am Mikrokontroller angeschlossen sind. Um die Funktionalität des Ein-/Ausschaltens der Lampe zu implementieren, soll der richtige I/O Port verwendet werden. Ändern Sie die Funktionen, um den Pin 3 auf Port E zu steuern. 3. Der Pin 1 von Port F (PF1) der Atmega128 CPU ist mit dem Notfallsensor verbunden. Schreiben Sie ein Programm mit Hilfe des BTNut/OS API, das diesen Pin als Eingang einstellt. Wird der Notfallsensor aktiviert, soll auf der BTNode die LED 1 eingeschaltet werden. Kapseln Sie diese Funktionalität in eine Funktion. 77 Programmierprojekt Sommersemester 2010 Uni Tübingen, Arbeitsbereich Technische Informatik Diese Funktion ist Teil des Notfall-Sensor“-Moduls, das im Entwurfsdokument ” beschrieben ist und wird beim Notfall-Status-Polling“ benutzt. ” 4. Der Pin 3 von Port E (PE3) der Atmega128 CPU ist mit der Lampe verbunden. Schreiben Sie ein Programm mit Hilfe von BTNut/OS API, das den Status dieses Pins überprüft (Achtung, hier soll der Pin immer als Output bleiben!). Kapseln Sie diese Funktionalität in einer Funktion. Diese Funktion ist Teil des Aktor” funktionen“-Moduls, das im Entwurfsdokument beschrieben ist, und wird bei der Beleuchtung als Status Polling“ benutzt. ” Achtung: Sie haben zwei kleine Platinen erhalten, auf denen jeweils ein Schalter (Notfallschalter) und eine LED montiert ist. Die Port-Verbindungen der Platinen zum Schalter und zur LED sind nicht einheitlich! Aufgaben zum A/D-Wandler. Vorbereitung des Moduls Batterie-Sensor“ ” 1. Jetzt sind Sie an der Reihe, das ADCSRA Register zu konfigurieren. Für maximale Präzision wollen wir die langsamste Konvertierungsgeschwindigkeit benutzen. Wir benutzen keinen Interrupt und führen eine einzige Konvertierung durch. Nachdem der A/D-Wandler eingestellt worden ist, kann mit der Konvertierung begonnen werden. Dies geschieht, sobald das ADSC Bit des ADCSRA Registers gesetzt wird. Wenn die Konvertierung durchgeführt ist, wird dieses Bit automatisch zurückgesetzt. Warten Sie darauf und lesen sie dann das Ergebnis aus den Registern ADCL und ADCH aus. Berechnen Sie die Konvertierungsergebnisse, die Sie für die Batteriespannung von 1 V und 2 V erwarten. Bekanntermaßen beträgt die Referenzspannung 3300 Millivolt, der A/D-Wandler liefert 10-Bit Werte und das BAT SENSE Signal entspricht der Hälfte der Batteriespannung (siehe Schaltkreise Schematics“). ” TIPP1: Falls das Konvertierungsergebnis immer Null ist, stellen Sie sicher, dass Sie (i) Batterien in die BTnode eingebaut haben oder die Batterien-Kontakte an die externe Spannungsversorgung angeschlossen haben und (ii) die Stromversorgung (power switch) angeschaltet ist (wenn die BTnode an das USB-Kabel angeschlossen ist, wird die BTnode mit Strom versorgt auch wenn der power switch ausgeschaltet ist. Dann ist aber das BAT SENSE Signal immer Null). TIPP2: Verwenden Sie die Programmstruktur, die in dem Lückencode vorgegeben ist. 2. Integrieren Sie den in der Anleitung gegebenen Lückencode in ein BTNode Programm. Vervollständigen Sie dieses Programm, so dass Sie die Batteriespannung messen können. 3. Erweitern Sie das in Aufgabe 1.1 entwickelte Programm, um die folgende Punkte zu vervollständigen: 78 Programmierprojekt Sommersemester 2010 Uni Tübingen, Arbeitsbereich Technische Informatik a) Die Funktionalität für die Überwachung der Batterie soll in eine Funktion gekapselt werden. Diese Funktion ist Teil des Batterie-Sensor“-Moduls, das ” im Entwurfsdokument beschrieben ist. Achtung: Nennen Sie Sie diese Funktion und die Funktionsaufrufe bzw. Befehle, entsprechend der Beschreibung im Entwurfsdokument. TIPP: Sie finden mehr Informationen dazu in den Beispielen die in [3] veröffentlicht sind. 7.2.2 Gruppe B: Die Anleitung für Aufgabe 6 gibt Ihnen eine Einführung in die MIDlet-Programmierung, die Sie bereits in Aufgabe 3 kennen gelernt haben. Verwenden Sie die Schnittstellen, die in der Spezifikation beschrieben sind. Die Kommentarköpfe bitte so schreiben, dass das Werkzeug Javadoc sie versteht und daraus eine Entwicklungsdokumentation erstellen kann. (Anmerkungen dazu: siehe unten). 1. Schreiben Sie ein MIDlet entsprechend Ihrem Klassendiagramm im Entwurfsdokument mit den Funktionen: a) Inquiry und Geräte-Anzeige (bereits in Aufgabe 3 gelöst) b) Dienstanfrage beim Host, danach Anzeige der Host-Dienste. c) Der Telefondienst soll ausgewählt werden können. Die Telefondienst-Anfrage wird zum Host geschickt. d) Die Host-Antwort auf die Telefondienst-Anfrage soll angezeigt werden (also z.B. eine Telefonnummer). Verwenden Sie dafür NetBeans (oder eclipse) als Editor. 2. Kompilieren Sie Ihr MIDlet (als Client) entweder mit NetBeans oder mit dem Java Wireless Toolkit. Erstellen Sie eine JAR-Datei und laden Sie diese in Ihr Mobiltelefon Testen Sie Ihre Anwendung zusammen mit Gruppe C. Testen Sie auch Fehlerfälle. 7.2.3 Gruppe C: Die Anleitung für Aufgabe 6 gibt Ihnen eine Einführung in die Java-Programmierung auf dem Host, die Sie bereits in Aufgabe 3 kennen gelernt haben. Verwenden Sie die Schnittstellen, die in der Spezifikation beschrieben sind und halten Sie sich an das Klassendiagramm, das Sie im Entwurfsdokument dargestellt haben. Die Kommentarköpfe Ihres Codes bitte so schreiben, dass das Werkzeug Javadoc (siehe unten) sie versteht und daraus eine Entwicklungsdokumentation erstellen kann. 1. Schreiben Sie ihr erstes Serverprogramm mit den Funktionen: a) Initialisierung mit Sichtbarmachung“ Ihres Geräts für andere BT-Geräte. ” 79 Programmierprojekt Sommersemester 2010 Uni Tübingen, Arbeitsbereich Technische Informatik b) Bereit zum Entgegennehmen der Dienstanfrage von Gruppe B. Anzeige der Anfrage. c) Dienstantwort mit Liste der Dienste an Gruppe B schicken. Anzeige der Antwort. d) Programmieren Sie den Telefondienst. Das MT schickt eine TelefondienstAnfrage, die Sie mit einer Telefonnummer beantworten müssen. Anzeige der Antwort. e) Zeigen Sie in Ihrem GUI-Fenster das Ergebnis der Inquiry an. Testen Sie Ihre Anwendung zusammen mit Gruppe B. Testen Sie auch Fehlerfälle. Bemerkungen zu Javadoc Ein Beispielkommentar einer Klasse und Funktion sieht wie folgt aus: /** * Ein Hello-World-Programm in Java. * Dies ist ein Javadoc-Kommentar. * Praktikum WS 2009. Aufgabe A6-1 * @author Alice Weiss, Gruppe B * Datum: * @version 1.0 */ Literatur: http://de.wikipedia.org/wiki/Javadoc} http://homepages.fh-giessen.de/~hg7132/javaprog/uebungen/javadoc_tutorial.html http://java.sun.com/j2se/javadoc/index.jsp 80 Literaturverzeichnis [1] AVR Spezifikationsblatt. documents/doc2467.pdf. http://www.atmel.com/dyn/resources/prod_ [2] Btnodes - a distributed environment for prototyping ad hoc networks. http://www.btnode.ethz.ch/BTnodes. [3] BTnodes - Documentation. http://www.btnode.ethz.ch/static_docs/doxygen/ btnut/. [4] Sony Ericsson. On-device-debugging tutorial for netbeans. http: //developer.sonyericsson.com/site/global/newsandevents/latestnews/ newssept06/p_getstarted_ondevice_debugging_netbeans_tutorial.jsp. [5] The Eclipse Foundation. Eclipse - open development platform. http://eclipse. org/. [6] André N. Klingsheim. J2me bluetooth programming. http://wireless.klings. org/klings_jabwt_master_thesis.pdf. [7] Sun Microsystems. Java me technology apis & docs. javame/reference/apis.jsp. http://java.sun.com/ [8] Sun Microsystems. Netbeans ide. http://www.netbeans.org. [9] Sun Microsystems. Netbeans mobility pack for midp/cldc 5.5 quick start guide. http://www.netbeans.org/kb/55/quickstart-mobility.html. [10] BlueZ Project. Bluez - official linux bluetooth protocol stack. http://www.bluez. org. [11] Martin Sauter. Grundkurs Mobile Kommunikationssysteme. Vieweg Verlag, 2004. [12] Open Source. Winavr user manual. WinAVR-user-manual.html. http://winavr.sourceforge.net/ [13] Inc. Sun Microsystems. Jsr 82 bluetooth api and obex api. http://java.sun.com/ javame/reference/apis/jsr082/. [14] AvetanaBT Team. Avetanabt - java library for bluetooth (jsr-82). sourceforge.net/projects/avetanabt/. [15] BlueCove Team. Bluecove - java library for bluetooth (jsr-82). 81 http://