Raspberry Pi: Как работать с Bluetooth на Java
Продолжая работать над своей задумкой, настало время получить доступ на Raspberry Pi к Bluetooth, используя Java. Bluetooth API для Java описан в спецификации JSR-82. Поиски в Internet выдали, что сейчас широко распространена библиотека BlueCove, которая реализует JSR-82. Её настройке и будет посвящена эта статья.
Библиотека представляет из себя Java API, который через JNI обращается к реализации Bluetooth для конкретной платформы. Поэтому главным условием является наличие подходящей библиотеки dll для Windows и so для Linux.
Windows
Для начала работы под Windows достаточно скачать jar-файлы (я использовал версию 2.1.1) и добавить в проект. Дальше можно запустить простую программу для проверки работоспособности:
public class BlueToothTest { public static void main(String[]args) throws IOException { System.out.println("Local address: " + LocalDevice.getLocalDevice().getBluetoothAddress()); } }
Сперва проверка делалась под Windows 7 на ноутбуке Toshiba и эксперимент завершился ошибкой. Похоже, что версии для Toshiba ещё нет.
Exception in thread «main» java.lang.UnsatisfiedLinkError: com.intel.bluetooth.BluetoothStackToshiba.getLibraryVersion()I
at com.intel.bluetooth.BluetoothStackToshiba.getLibraryVersion(Native Method)
at com.intel.bluetooth.BlueCoveImpl.setBluetoothStack(BlueCoveImpl.java:1005)
Потом попытка была на ноутбуке DELL и она увенчалась успехом:
BlueCove version 2.1.1-SNAPSHOT on winsock
Local address: BC77377D6332
BlueCove stack shutdown completed
Linux
Для Linux ситуация сложнее. Для начала использования библиотеки нужно выполнить некоторые подготовительные действия и установить необходимые пакеты.
sudo apt-get install bluetooth bluez-utils blueman
sudo apt-get install libbluetooth-dev
sudo apt-get install ant
Для запуска пробной программы используется Runnable Jar File, который создаётся в Eclipse через Export.
java -jar bluetooth.jar
Попытка выполнить такой файл на Raspberry оканчивается ошибкой:
java.lang.UnsatisfiedLinkError: /tmp/bluecove_pi_0/libbluecove_arm.so: /tmp/bluecove_pi_0/libbluecove_arm.so: cannot open shared object file: No such file or directory
Это говорит о том, что для Linux придётся скомпилировать свою собственную версию so-файла.
mkdir bluecovelib
cd bluecovelib
wget http://snapshot.bluecove.org/distribution/download/2.1.1-SNAPSHOT/2.1.1-SNAPSHOT.63/bluecove-gpl-2.1.1-SNAPSHOT-sources.tar.gz
tar -zxvf bluecove-gpl-2.1.1-SNAPSHOT-sources.tar.gzmkdir -p bluecove/target
cd bluecove/target
wget http://snapshot.bluecove.org/distribution/download/2.1.1-SNAPSHOT/2.1.1-SNAPSHOT.63/bluecove-2.1.1-SNAPSHOT.jarcd ../../bluecove-gpl-2.1.1-SNAPSHOT
ant all
Результатом будет bluecove-gpl-2.1.1-SNAPSHOT.jar с нужной библиотекой so, которую можно использовать в проекте.
Для Raspberry компиляция прошла успешно и тестовая программа сразу заработала:
BlueCove version 2.1.1-SNAPSHOT on bluez
Local address: 001A7DDA7113
BlueCove stack shutdown completed
А вот попытка компиляции на Ubuntu привела к череде проблем, первой из которых была ошибка:
java.lang.UnsatisfiedLinkError: /tmp/bluecove_merl1n_0/libbluecove.so: /tmp/bluecove_merl1n_0/libbluecove.so: undefined symbol: sdp_extract_pdu
Несколько часов поисков вывели меня на команд ldd, которая показывает зависимости.
merl1n@merl1n:~/bluecovelib2/dir$ ldd -r -v libbluecove.so
statically linked
undefined symbol: stdout (./libbluecove.so)
undefined symbol: sdp_extract_pdu (./libbluecove.so)
undefined symbol: free (./libbluecove.so)
undefined symbol: ioctl (./libbluecove.so)
undefined symbol: sdp_record_alloc (./libbluecove.so)
undefined symbol: recv (./libbluecove.so)
undefined symbol: connect (./libbluecove.so)
undefined symbol: hci_inquiry (./libbluecove.so)
undefined symbol: strerror (./libbluecove.so)
undefined symbol: hci_read_remote_name (./libbluecove.so)
undefined symbol: snprintf (./libbluecove.so)
undefined symbol: sdp_service_search_req (./libbluecove.so)
undefined symbol: hci_read_remote_version (./libbluecove.so)
undefined symbol: sdp_close (./libbluecove.so)
undefined symbol: memcpy (./libbluecove.so)
undefined symbol: sdp_device_record_unregister (./libbluecove.so)
undefined symbol: malloc (./libbluecove.so)
undefined symbol: vsnprintf (./libbluecove.so)
undefined symbol: socket (./libbluecove.so)
undefined symbol: fflush (./libbluecove.so)
undefined symbol: dlclose (./libbluecove.so)
undefined symbol: send (./libbluecove.so)
undefined symbol: sdp_list_append (./libbluecove.so)
undefined symbol: accept (./libbluecove.so)
undefined symbol: hci_close_dev (./libbluecove.so)
undefined symbol: hci_open_dev (./libbluecove.so)
undefined symbol: sdp_device_record_update (./libbluecove.so)
undefined symbol: hci_send_req (./libbluecove.so)
undefined symbol: bind (./libbluecove.so)
undefined symbol: setsockopt (./libbluecove.so)
undefined symbol: dlopen (./libbluecove.so)
undefined symbol: sdp_device_record_register (./libbluecove.so)
undefined symbol: sdp_data_get (./libbluecove.so)
undefined symbol: sdp_record_free (./libbluecove.so)
undefined symbol: listen (./libbluecove.so)
undefined symbol: dlsym (./libbluecove.so)
undefined symbol: memset (./libbluecove.so)
undefined symbol: hci_write_current_iac_lap (./libbluecove.so)
undefined symbol: hci_get_route (./libbluecove.so)
undefined symbol: poll (./libbluecove.so)
undefined symbol: sdp_service_attr_req (./libbluecove.so)
undefined symbol: shutdown (./libbluecove.so)
undefined symbol: hci_read_current_iac_lap (./libbluecove.so)
undefined symbol: hci_read_local_name (./libbluecove.so)
undefined symbol: getpeername (./libbluecove.so)
undefined symbol: fputc (./libbluecove.so)
undefined symbol: sdp_list_free (./libbluecove.so)
undefined symbol: getsockopt (./libbluecove.so)
undefined symbol: sdp_connect (./libbluecove.so)
undefined symbol: hci_send_cmd (./libbluecove.so)
undefined symbol: fwrite (./libbluecove.so)
undefined symbol: hci_read_class_of_dev (./libbluecove.so)
undefined symbol: __errno_location (./libbluecove.so)
undefined symbol: hci_read_bd_addr (./libbluecove.so)
undefined symbol: sdp_gen_record_pdu (./libbluecove.so)
undefined symbol: fcntl (./libbluecove.so)
undefined symbol: sdp_attr_remove (./libbluecove.so)
undefined symbol: close (./libbluecove.so)
undefined symbol: vfprintf (./libbluecove.so)
undefined symbol: free (./libbluecove.so)
undefined symbol: getsockname (./libbluecove.so)
дальнейшие поиски показали, что проблема оказалась в компиляторе gcc, настройки по-умолчанию которого отличаются от тех, что используются на моей Raspberry Pi. Для исправления скрипта компиляции bluecove-gpl-2.1.1-SNAPSHOT.jar пришлось сделать следующие изменения
- <property name=»bluecove.native.linker.options» value=»-nodefaultlibs«/> => <property name=»bluecove.native.linker.options» value=»»/>
- <arg value=»-Wl,-soname,libbluecove${library_sufix}-${product_version}»/> => <arg value=»-Wl,—no-as-needed,-soname,libbluecove${library_sufix}-${product_version}»/> (—no-as-needed — параметр отвечает за то, чтоб подключать все библиотеки, а не только те, функции которых упомянаются в коде)
- перестановка строк
<arg value=»-L${libs-universal}»/>
<arg value=»-lbluetooth»/>
<arg line=»${bluecove.native.linker.options}»/>
<arg value=»-Wl,-soname,libbluecove${library_sufix}-${product_version}»/>
=>
<arg value=»-L${libs-universal}»/>
<arg line=»${bluecove.native.linker.options}»/>
<arg value=»-Wl,—no-as-needed,-soname,libbluecove${library_sufix}-${product_version}»/>
<arg value=»-lbluetooth»/>
После этих изменений результат команды ldd становится нужным. Он показывается, что наша новая библиотека зависит от четырёх других so-файлов и больше нет ошибки с неизвестным символом sdp_extract_pdu.
merl1n@merl1n:~/bluecovelib2/dir$ ldd -v -r libbluecove.so
linux-gate.so.1 => (0x0029e000)
libbluetooth.so.3 => /usr/lib/i386-linux-gnu/libbluetooth.so.3 (0x0024b000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0x0029f000)
/lib/ld-linux.so.2 (0x00c1f000)
undefined symbol: dlopen (./libbluecove.so)
undefined symbol: dlclose (./libbluecove.so)
undefined symbol: dlsym (./libbluecove.so)Version information:
./libbluecove.so:
libc.so.6 (GLIBC_2.1.3) => /lib/i386-linux-gnu/libc.so.6
libc.so.6 (GLIBC_2.0) => /lib/i386-linux-gnu/libc.so.6
/usr/lib/i386-linux-gnu/libbluetooth.so.3:
libc.so.6 (GLIBC_2.3) => /lib/i386-linux-gnu/libc.so.6
libc.so.6 (GLIBC_2.7) => /lib/i386-linux-gnu/libc.so.6
libc.so.6 (GLIBC_2.1.3) => /lib/i386-linux-gnu/libc.so.6
libc.so.6 (GLIBC_2.15) => /lib/i386-linux-gnu/libc.so.6
libc.so.6 (GLIBC_2.4) => /lib/i386-linux-gnu/libc.so.6
libc.so.6 (GLIBC_2.3.4) => /lib/i386-linux-gnu/libc.so.6
libc.so.6 (GLIBC_2.0) => /lib/i386-linux-gnu/libc.so.6
/lib/i386-linux-gnu/libc.so.6:
ld-linux.so.2 (GLIBC_2.3) => /lib/ld-linux.so.2
ld-linux.so.2 (GLIBC_PRIVATE) => /lib/ld-linux.so.2
ld-linux.so.2 (GLIBC_2.1) => /lib/ld-linux.so.2
Тестовая программа тоже запускается без проблем
merl1n@merl1n:~/bluecovelib2$ java -jar bluetooth.jar
BlueCove version 2.1.1-SNAPSHOT on bluez
Local address: 4CEDDE7F40FE
BlueCove stack shutdown completed
Надеюсь, мой опыт был полезен.