blog.smart-java.nl
Ordina J-Technologies – Java Blog



Spelen met Java Bluetooth

By: Hedzer Westra, 21 February 2009

Bluetooth logo

De Bluetooth JSR-82 en de API daarbij zijn al enkele jaren oud (v1.1: 2002), maar voor mij was het een nieuwe. Hiermee kun je vanuit Java met bluetooth apparaten communiceren. Het leek me wel leuk om, al forenzend in de trein een eBook te lezen en tegelijkertijd te zien wie er allemaal elke dag met me meereist. Deze zelfde exercitie is natuurlijk ook uitstekend in de file of op vaste locaties uit te voeren…

Inmiddels hebben heel veel apparaten een bluetooth (‘bt’) module: GPS ontvangers, WiiMotes, smartphones, billboards, verkeerslichten, you name it. Elk ingeschakeld apparaat adverteert zijn nummer en meestal een (door de fabrikant of eigenaar ingestelde) naam. De leukste tot nu toe vond ik “smartföhn”, maar voornamelijk zie je de naam van de eigenaar, of het merk & type van het apparaat verschijnen. O, en laat ik vooral ‘Dennis Ordina’ niet ongenoemd laten! Welke collega met Nokia telefoon met bt adres 001A165A1686 zat op 4 februari om kwart voor zes in de intercity van Amsterdam Bijlmer naar Utrecht CS? :-) Als laatste vermelding: één telefoon had als naam het 06-nummer. Lijkt me niet handig, behalve als je wilt dat Jan en alleman je belt…

De naam Bluetooth komt overigens van de 10e-Eeuwse koning van Denemarken en Noorwegen: Harald Blåtand (niet te verwarren met Blauwbaard!). Het logo is afgeleid van de runentekens van zijn initialen.
De laatste bluetoothversie (uit 2007) is 2.1+EDR (3Mbps), en er wordt gewerkt aan een high speed variant die moderne WiFi-snelheden haalt. De radius is afhankelijk van de ‘class’ 100, 10 of 1 meter.

Begrippen

Eerst vuur ik enkele begrippen op je af uit de ‘bt’ wereld, waarna wat codevoorbeelden langskomen.

Protocol bluetooth is opgebouwd als OSI network stack van protocollen, waarvan de interessantste L2CAP (Logical Link Control and Adaptation Protocol), RFCOMM (Radio Frequency Communications) en OBEX (OBject EXchange) zijn. Het zijn op elkaar gestapelde transportprotocollen net zoals TCP en IP op elkaar gestapeld zijn. L2CAP is packet oriented en RFCOMM stream oriented. OBEX is session oriented.
Profiel interfacespecificatie; ruwweg een netwerkprotocol op applicatieniveau. Er zijn er zo’n 25 gedefinieerd; voorbeelden zijn FTP (file transfer), HID (muis/keyboard), OPP (vCards), BIP (plaatjes) en PAN (piconet). JSR-82 omvat alleen de basisprofielen GAP, SPP, SDAP, GOEP en de daarbij horende protocollen SDP (GAP+SDAP), OBEX (GOEP), RFCOMM (SPP) en L2CAP. Andere profielen moet je zelf uitprogrammeren!
Bt adres vergelijkbaar aan een Ethernet MAC-adres. Bestaat uit 12 hex digits, waarvan de eerste 6 verdeeld zijn onder de fabrikanten. Aan het adres kun je dus al zien of het bijvoorbeeld een Nokia of Motorola apparaat betreft!
Master net zoals er bij TCP/IP-verbindingen servers (daemons) zich registeren op een bekende poort en clients hiernaar connecten, is bt ook client/server georiënteerd; de server heet ‘master’ en registreert (publiceert) services. Dit vind je terug in bt UUIDs en URLs.
UUID uniek nummer om een service (gedefinieerd in een profiel) te identificeren, vergelijkbaar aan een server IP port number. In 3 lengtes: 4, 8 of 32 hex digits. Er bestaat een simpele conversie om 4/8-digit UUID’s naar volledige lengte te converteren: concateneren met het vaste nummer 00001000800000805F9B34FB. Enkele voorbeeldwaarden: OBEX File Transfer = 0×1106, Human Interface Device Profile (WiiMote!) = 0×1124.
BCC Bluetooth Control Centre; een GUI om je device, services & security te configureren, en te zoeken naar & pairen met andere devices. Meegeleverd met je bt driver.
URLs je maakt connecties met behulp van URLs in het bekende formaat. Server URLs bevatten altijd “localhost”, de service UUID en eventueel parameters zoals een naam. Client URLs bevatten het remote bt adres, het kanaal- of PSM [Protocol Service Multiplexer] nummer – vergelijkbaar met een client IP port number; is niet hetzelfde als een service UUID – en eventueel (security) parameters. Enkele voorbeelden:
o OBEX/GOEP
Server btgoep://localhost:ed495afe28ed11da94d900e08161165f
Client btgoep://00A3920B2C22:12
o RFCOMM/SPP
Server btspp://localhost:;name=Sample SPP Server
Client btspp://0050CD00321B:3;authenticate={true|false}; authorize={true|false};encrypt={true|false}
o L2CAP
Client btl2cap://0050CD00321B:1003; ReceiveMTU=512;TransmitMTU=512
Piconet mininetwerk van maximaal 8 bt devices – meer verbindingen ondersteunt bluetooth niet. Een netwerk van meerdere aan elkaar gekoppelde piconets heet een scatternet.
Service class elk bt device kan met zijn service class aangeven welke service types hij ondersteunt (bijv. positioning, networking of audio) en van welk (sub)type hij is (bijv. Computer/Laptop of Peripheral/Joystick).
Discovery scannen naar devices en/of services. Na discovery levert JSR-82 je URLs zodat je die niet zelf hoeft op te bouwen. Een bt device kan altijd (‘global’/’general’) discoverable zijn (GIAC), een beperkte tijd (‘limited’ – LIAC) of helemaal niet. Dit poor-mans beveiligingsmechanisme is bekend van WiFi routers. Ook daar is de fabrieksinstelling meestal niet de veiligste… Discovery is niet erg snel; een complete sweep van devices & hun services duurt afhankelijk van het aantal devices in bereik één tot meerdere minuten, dit onder andere omdat er geen parallelle service discoveries kunnen draaien.
Service record bij discovery van een service ontvang je een lijst van attributen met onder anderen een leesbare naam en kanaal (RFCOMM&OBEX)- of PSM (L2CAP) nummer. Helaas vullen niet alle bt devices dit service record exact volgens de specs.
Pairing pas nadat apparaten aan elkaar bekend zijn gemaakt middels een PIN-code (meestal 0000…) kan communicatie gestart worden. Helaas voor mijn tagger kan dit alleen via de BCC; de JSR-82 API voorziet er niet in om een PIN-code door te geven. Bij pairing wordt overigens ook een gedeelde 128b sleutel afgesproken, die daarna gebruikt wordt als autorisatie en/of encryptie aangezet worden. Dat laatste kan dan wel weer vanuit Java. Discovery is overigens wel mogelijk zonder te pairen; mijn tagger kan van elk device dat in bereik is en in bluetooth discovery mode staat (hetgeen bijna elke telefoon is, vanuit de winkel) de volgende attributen uitvragen: bt adres, naam, service class, services inclusief naam, UUID en URL. Als een pairing wel bestaat (in JSR-82 termen: het device is ‘trusted’), is het mogelijk om een connectie op te bouwen, bijv. simpelweg om de pingtijd te meten.

Codevoorbeelden

De betrokken Java packages zijn: javax.bluetooth, javax.obex en javax.microedition.io.
Wat betreft dat laatste package: JSR-82 heeft veel raakvlakken met J2ME (termen in het verlengde hiervan: MIDP, CLDC en CDC), wat logisch is omdat bluetooth in 1998 door Nokia bedacht is als draadloze communicatie van mobieltjes naar andere apparaten. Mocht je iets met J2ME & bt willen doen (laat me je resultaten weten!): telefoons kunnen zelf ook JSR-82 implementeren, maar niet alle telefoons met bt & Java doen dat! Zie de bluecove Wiki voor een lijst.

De API is vrij klein. De belangrijkste klassen zijn DiscoveryAgent, LocalDevice en RemoteDevice.

Twee problemen die ik ben tegengekomen met deze API:
1. Een afgrijselijke klasse is DataElement. Dit is een wrapper voor diverse soorten datatypes. Je zult er helaas mee moeten leven.
2. Als je alleen het bt adres van een device weet, dan moet je eerst discoveren. Je kunt namelijk niet op basis van dit adres een RemoteDevice (laten) instantiëren. Maar als je een URL kent kun je wel meteen connecten! Vreemd…

Nu volgen enkele zeer kleine & korte codevoorbeelden – check voor enkele compleet werkende applicaties de Eclipse workspace op de Ordina Wiki. (Helaas zijn de syntax highlighting & tabs verloren gegaan bij kopiëren vanuit Word naar WordPress…)

// setup: get local device & discovery agent
LocalDevice localDevice = LocalDevice.getLocalDevice();
DiscoveryAgent discoveryAgent = localDevice.getDiscoveryAgent();

// start global device inquiry
MyDiscoveryListener discoveryListener = new MyDiscoveryListener();
boolean limited = false;
boolean started = discoveryAgent.startInquiry(
limited ? DiscoveryAgent.LIAC : DiscoveryAgent.GIAC,
discoveryListener);

// retrieve name of first remote device in list
RemoteDevice remoteDevice = discoveryListener.getDiscoveredDevices().get(0).getRemoteDevice();
String deviceName = remoteDevice.getFriendlyName(true);

// search for services using RFCOMM on the first remote device
UUID[] uuids = new UUID[]{new UUID(BluetoothConstants.PROTOCOL_RFCOMM)};
int transId = discoveryAgent.searchServices(null, uuids, remoteDevice,
discoveryListener);

// retrieve a connection URL using the first service record
ServiceRecord record = discoveryListener.getServiceRecords().get(transId)[0];
String url = record.getConnectionURL(ServiceRecord.NOAUTHENTICATE_NOENCRYPT, false);

// open L2CAP, RFCOMM & OBEX connections
L2CAPConnection l2capConnection = (L2CAPConnection) Connector.open(url);

StreamConnection rfcommConnection = (StreamConnection) Connector.open(url);

ClientSession obexConnection = (ClientSession) Connector.open(url);
HeaderSet hsConnectReply = obexConnection.connect(null);
if (hsConnectReply.getResponseCode() != ResponseCodes.OBEX_HTTP_OK) {
// 196 = not found, 204 = precon failed, 160 = ok
LOG.error(“expected OBEX_HTTP_OK but got ” + hsConnectReply.getResponseCode());
}

Hoe service registratie werkt (dit is: opzetten van een server connectie die wacht op clients) kun je terugvinden in de Eclipse workspace.

OBEX – OBject EXchange

OBEX is ‘geleend’ van de IrDa (infrarood) wereld, vandaar dat de API een eigen package javax.obex heeft. Binnen bt werkt OBEX bovenop RFCOMM, maar OBEX ondersteunt ook IrDa en TCP/IP als transportlaag.

Ondersteunde diensten zijn o.a. FTP (phone2PC), Object Push (phone2phone – zogezegd gebruikt bij toothing, Sync, basic imaging en basic printing.

Het protocol doet denken aan HTTP. Herkenbare concepten zijn:
• Headers 12 gedefinieerde parameters, o.a. name, type en length
• Sessions op basis van connectie ID
• Content types text/vcard, x-obex/folder-listing, etc.
• Commands CONNECT, PUT, GET, SETPATH, ABORT, CREATE-EMPTY, PUT-DELETE en DISCONNECT
• Error codes OBEX_HTTP_OK, OBEX_HTTP_NOT_FOUND, etc.

Voorbeelden van uit te wisselen data zijn: vCard, vCalendar, vMessage, vNotes en platte bestanden.

De belangrijkste 4 klassen/interfaces en hun supertypes zijn:
• ClientSession > Connection
• HeaderSet
• Operation > ContentConnection > StreamConnection > OutputConnection & InputConnection > Connection
• SessionNotifier > Connection

JSR-82 implementaties & bt stacks

Bluecove is momenteel de enige actief onderhouden, bruikbare en gratis JSR-82 implementatie, maar werkt gelukkig redelijk goed. Bluecove heeft wel een bluetooth stack nodig. Voor Windows wordt widcomm aangeraden en BlueSoleil ten zeerste afgeraden – dit kan ik beamen! De bluetooth stack wordt meegeleverd met je chipset, dus kiezen kun je niet. Gelukkig bevat mijn Ordina laptop (HP Compaq 6910p) een widcomm ingebouwd. Wel eerst even een service pack van 100MB installeren! Voordat ik dat deed werd de widcomm niet herkend en ben ik met een USB bt stikkie van cygnet aan het klooien geweest. Helaas zat daar BlueSoleil bij en dat ging niet erg lekker.

Op Linux kun je gebruik maken van de BlueZ stack. Ik heb ‘m niet geprobeerd – laat me je ervaringen weten!

Een mogelijk client programma is Nokia PC Suite, die bijvoorbeeld. een remote file browser biedt. Helaas installeert Nokia zoveel dingen achter je rug om (koppelt onder andere de JAR extensie aan een Nokia application installer!) dat ik deze afraad.

Overigens werkt ook de combinatie widcomm & bluecove niet vlekkeloos. Enkele keren heb ik een JVM crash gehad, een vastlopende BT stack (een volledige herstart helpt, maar soms is killen van BTStackServer.exe voldoende), een BCC die mijn connecties afpakte (alleen een unpair/pair-actie verhelpt dit), et cetera. Als je de documentatie van bluecove mag geloven, ligt dit eerder aan de slechte staat van bluetooth stacks, danwel aan een mismatch tussen stacks en JSR-82, dan aan bluecove zelf. Toch krijg ik een beetje een déjà-vu met JMF en Java Serial: ook dit zijn Java extensies waarvoor eigenlijk nooit een goede (reference) implementatie is gekomen, en door Sun een beetje aan hun lot lijken te zijn overgelaten.

Documentatie

Er bestaat een bluetooth SIG die alle specs publiceert; onder andere die van alle profielen. Helaas moet je voor de meest informatie lid zijn, en dat kunnen alleen betalende bedrijven of universiteiten. Ik heb ze gecontacteerd voor een simpele lijst met standaard UUID nummers, en kreeg pas na lang aandringen antwoord – natuurlijk pas nadat ik mijn code af had. De online documentatie & tutorials die ik wél gevonden heb is helaas vaak slecht, oud, weinig diepgaand en lastig te vinden – ik heb hier en daar door open source code moeten snuffelen voor ontbrekende informatie (zoals voornoemde UUID nummers).

Voor mijn source code, verwijzingen naar boeken, meer technische informatie, een lijst van geregistreerde UUIDs, attributen en service classes, en extra URLs verwijs ik je naar de Ordina Wiki pagina die ik hiervoor opgezet heb.

Mocht je zelf ook aan de gang gaan met Java bluetooth, dan hoor ik graag van je terug wat je resultaten zijn. Codebijdragen zijn natuurlijk welkom op de Wiki!

Hedzer Westra

Hedzer Westra

Eén reactie op “Spelen met Java Bluetooth”

  1. Jan-Kees van Andel zegt:

    Hmm, bluetooth. Nooit iets mee gedaan eigenlijk. Ben wel blij met deze korte intro, want ik kende de termen niet eens. :s

    Ps. Over je code highlighting, Wordpress heeft gewoon een code highlighting plugin.

    Gewoon je code tussen <pre lang="LANGUAGE" line="1"> </pre> zetten. Line is optioneel. Voor ondersteunde talen, zie: http://wordpress.org/extend/plugins/wp-syntax/other_notes/
    Zelfs scala wordt ondersteund. ;-)

Laat een reactie achter