Работа с Bluetooth в Delphi. Часть 4. Передача данных через Bluetooth.


Наконец, после долгого перерыва я добрался и до заключительной, как я надеюсь, части статьи о Bluetooth.

Здесь я постараюсь изложить, как все-таки передавать данные через Bluetooth. Я не буду приводить здесь каких-либо готовых примеров приложений. Дам только теорию. К практике, я думаю, вы перейдете сами.

Как вы помните из предыдущих статей, мы используем исключительно Windows API для работы с Bluetooth. Сразу хочу оговориться, что описанные здесь способы не будут работать с драйверами BlueSoliel и VIDCOMM. В конце статьи я расскажу, как установить драйверы от Microsoft, если вы этого еще не сделали.

Итак, приступаем.

Что вы должны знать

Прежде, чем начать излагать основной материал, я хочу сформулировать требования к вашим знаниям.

Вы должны понимать работу с сетями в Microsoft Windows, знать термины и определения, данные мною в предыдущих статьях. Я буду часто отсылать к пройденному материалу, чтобы не повторяться.

Вы также должны более или менее разбираться в технологии Winsock.

Bluetooth и Winsock

Как ни странно это звучит, но Microsoft решила реализовать всю функциональность по передаче данных посредством Windows Socket Model. Тем, кто писал что-либо для IrDA это должно показаться знакомым.

На мой взгляд - правильное решение. Зачем огород городить, когда уже есть проверенные средства.

Я не буду описывать здесь все правила применения функций WinSock к работе с Bluetooth. Остановлюсь лишь на практической стороне вопроса. А именно - передача данных.

В статье мы сделаем простенький Bluetooth-клиент, который будет подсоединяться к удаленному устройству как к модему и позволит вам выполнять AT-команды. Весьма полезная вещь. Учтите, что данный клиент будет требовать авторизации устройств, и не будет требовать наличия в системе каких-либо виртуальных COM-портов.

Сервисы и профили

Сервисы и профили... Это два краеугольных понятия Bluetooth. В некотором смысле - они идентичны.

Сервис - приложение-сервер, которое регистрирует определенным образом параметры в стеке протоколов Bluetooth. Наименование (GUID) всех сервисов строго определены Bluetooth.org.

Профиль - соглашения и стандарты работы сервиса. Понятнее объяснить не смогу.

Начало

Итак, прежде чем можно будет использовать библиотеку WinSock, ее необходимо инициализировать. Делается это вызовом функции WSAStartup. Так она выглядит:

function WSAStartup(
         wVersionRequired: Word;
     var lpWSAData: WSAData): Integer; stdcall;

Не буду описывать все параметры, так как они есть в любой справочной системе (MSDN, Delphi). Скажу только, что для использования WinSock с Bluetooth необходимо указать в качестве параметра wVersionRequired номер версии $0202.

Так выглядит вызов этой функции:

var
  Data: WSADATA;
begin
  if WSAStartUp($0202, Data) <> 0 then
    raise Exception.Create('Winsock Initialization Failed.');

По окончании работы с WinSock библиотеку необходимо освободить. Для этого существует функция WSACleanup.

function WSACleanup: Integer; stdcall;

Вызывается она просто, без всяких параметров. Возвращаемое значение, в принципе, можно не проверять:

begin
  WSACleanup;

Создание клиента

После того, как библиотека инициализирована, мы можем вызывать функции WinSock. Давайте создадим простой сокет, для работы с Bluetooth устройствами. Для этого необходимо вызвать функцию socket.

function socket(af, type_, protocol: Integer): TSocket; stdcall;

Так это делается:

var
  ASocket: TSocket;
begin
  ASocket := socket(AF_BTH, SOCK_STREAM, BTHPROTO_RFCOMM);
  if ASocket = INVALID_SOCKET then RaiseLastOsError;

Функция вернет корректный описатель сокета, либо INVALID_SOCKET в случае ошибки. Запомните, что Bluetooth поддерживает только потоковые сокеты (SOCK_STREAM).

Далее нам необходимо заполнить структуру SOCKADDR_BTH. В эту структуру записывается информация о сервере, к которому нам нужно подключиться (адрес, сервис и т.п.). Делается это следующим образом:

var
  Addr: SOCKADDR_BTH;
  AddrSize: DWORD;
begin
  AddrSize := SizeOf(SOCKADDR_BTH);
  FillChar(Addr, AddrSize, 0);

  with Addr do begin
    addressFamily := AF_BTH;
    btAddr := ADeviceAddress;
    serviceClassId := SerialPortServiceClass_UUID;
    port := DWORD(BT_PORT_ANY);
  end;

Здесь в переменной ADeviceAddress должен быть адрес устройства (Int64), присоединяемся к любому порту (BT_PORT_ANY) сервиса SerialPortServiceClass.

Далее вызываем функцию connect, которая имеет вид:

function connect(s: TSocket; name: PSockAddr; namelen: Integer): Integer; stdcall;

Делается это так:

begin
  if connect(ASocket, @Addr, AddrSize) <> 0 then RaiseLastOsError;

Если функция выполнится успешно, вернет 0, в противном случае отличное от нуля значение.

После того, как соединение установлено, можно передавать и принимать данные через сокет функциями send и recv.

function send(s: TSocket; var buf; len, flags: Integer): Integer; stdcall;
function recv(s: TSocket; var buf; len, flags: Integer): Integer; stdcall;

Функции возвращают количество переданных или принятых байт в случае успеха и отрицательное число в случае ошибки. Количество переданных или принятых байт может быть меньше, чем указанная в параметре len длина буфера. Тогда вам нужно повторить передачу/прием оставшихся байт.

Закрытие сокета осуществляется вызовом функции closesocket:

function closesocket(s: TSocket): Integer; stdcall;

Возвращаемое значение можно проигнорировать (если вы знаете, что делаете).

Вышеуказанный материал не представляет ничего нового для тех, кто хоть раз программировал под WinSock. Единственное, на что следует обратить внимание, это новые константы AF_BTH и BTHPROTO_RFCOMM.

Создание сервера

Как и создание клиента, создание сервера ничем не отличается от создания сервера для любой службы WinSock.

Итак, начнем. Сокет создается также как и в приведенном выше примере для клиента. Точно также заполняем структуру Addt: SOCKADDR_BTH. Только в качестве адреса устройства указываем 0. Далее, необходимо привязать сокет к адресу. Делается это функцией bind:

function bind(s: TSocket; name: PSockAddr; namelen: Integer): Integer; stdcall;

Которая вызывается следующим образом:

begin
  if Bind(ASocket, @Addr, AddrSize) <> 0 then RaiseLastOsError;

Далее вызываем функцию listen, для того чтобы сервер начал прослушивать сокет на предмет подключения клиентов и функцию accept для приема входящего подключения:

function listen(s: TSocket; backlog: Integer): Integer; stdcall;
function accept(s: TSocket; addr: PSockAddr; addrlen: PINT): TSocket; stdcall;

Делается это так:

var
  AClientSocket: TSocket;
begin
  if listen(ASocket, 10) <> 0 then RaiseLastOSError;
  AClientSocket = accept(ASocket, nil, nil);

После подключения клиента можно работать с AClientSocket - передавать и принимать данные.

Если вы не желаете больше принимать входящие подключения, закройте слушающий сокет.

Что осталось за кадром

Как и обещал, я коротко описал процедуры, необходимые для построения простого клиента и сервера, которые будут работать с Bluetooth через WinSock.

Однако здесь я не рассматривал вопросы регистрации сервисов и протоколы верхнего уровня.

Приведенной здесь информации достаточно для того, чтобы вы могли создать приложение клиент, которое соединится с вашим телефоном по Bluetooth и сможет выполнять AT-команды.

Более полную информацию и рабочие примеры можно найти здесь: www.btframework.com Там же приведено решение по установке драйверов от Microsoft.

Всегда буду рад ответить на ваши вопросы: mike@btframework.com



Дополнительно

Нашли ошибку на сайте? Выделите текст и нажмите Shift+Enter

Код для блога бета

Выделите HTML-код в поле, скопируйте его в буфер и вставьте в свой блог.