Recent

Author Topic: Bluetoothlaz - Installation and example comments  (Read 3138 times)

TopCat

  • New member
  • *
  • Posts: 9
Bluetoothlaz - Installation and example comments
« on: January 11, 2021, 08:38:13 pm »
I've had some issues installing the <bluetoothlaz> package and running the examples under Lazarus 2.0.10, fpc 3.0.4 and Raspian OS.
Ref: https://wiki.lazarus.freepascal.org/Bluetooth
Under the heading "Installation", I think a step may have been left out from the statement "Choose the bluetooth/bluetoothlaz.lpk and open it. That's all. The IDE now knows the package."
When the file is opened, a dialog appears. I found I had to Compile the file then Use | Add to project. Once that was done Lazarus recognized the package.
This is probably obvious to experienced users but I'm not one of them.
I then tried to run the examples. I got numerous errors of clashing types and I can't understand the statement  " c_close(device_sock);" at the end of the FindBlueToothClick event handler, which caused an error. I'm still learning the differences between fpc Pascal and Delphi but Lazarus didn't recognize "C_Close". I wonder if it is a typo?
Can anyone guide me towards a working example of code that demonstrates how to identify and connect to a Bluetooth device (it's a serial mini-printer)? I've connected to it via the Raspian UI and sent data successfully using LazSerial methods. The bit I need is how to identify the bluetooth printer and connect to it directly through code without using the Raspbian UI.
Many thanks to anyone who can help a newbie at this.

af0815

  • Hero Member
  • *****
  • Posts: 1289
Re: Bluetoothlaz - Installation and example comments
« Reply #1 on: January 12, 2021, 06:44:14 am »
I have a inofficial fork here https://github.com/afriess/bluetoothlaz. I have one sample running with a Bluetooth RFID / Barcode Reader running well. Running on Debian Buster and actual Raspbian.

The error on c_close is normaly before. If you call close it popup, have you looked if the handle is valid in the debugger ? One goal is to have all the needed parts for bluetooth must be installed with sudo apt-get install libbluetooth-dev to run the sample code correct.

Code: Pascal  [Select][+][-]
  1. uses Bluetooth, unixtype, Sockets;
  2.  
  3. { TForm1 }
  4.  
  5. procedure TForm1.BuFindBluetoothClick(Sender: TObject);
  6. var
  7.  device_id, device_sock: cint;
  8.  scan_info: array[0..127] of inquiry_info;
  9.  scan_info_ptr: Pinquiry_info;
  10.  found_devices: cint;
  11.  DevName: array[0..255] of Char;
  12.  PDevName: PCChar;
  13.  RemoteName: array[0..255] of Char;
  14.  PRemoteName: PCChar;
  15.  i: Integer;
  16.  timeout1: Integer = 5;
  17.  timeout2: Integer = 5000;
  18. begin
  19.  // get the id of the first bluetooth device.
  20.  device_id := hci_get_route(nil);
  21.  if (device_id < 0) then
  22.    raise Exception.Create('FindBlueTooth: hci_get_route')
  23.  else
  24.    writeln('device_id = ',device_id);
  25.  
  26.  // create a socket to the device
  27.  device_sock := hci_open_dev(device_id);
  28.  if (device_sock < 0) then
  29.    raise Exception.Create('FindBlueTooth: hci_open_dev')
  30.  else
  31.    writeln('device_sock = ',device_sock);
  32.  
  33.  // scan for bluetooth devices for 'timeout1' seconds
  34.  scan_info_ptr:=@scan_info[0];
  35.  FillByte(scan_info[0],SizeOf(inquiry_info)*128,0);
  36.  found_devices := hci_inquiry_1(device_id, timeout1, 128, nil, @scan_info_ptr, IREQ_CACHE_FLUSH);
  37.  
  38.  writeln('found_devices (if any) = ',found_devices);
  39.  
  40.  if (found_devices < 0) then
  41.    raise Exception.Create('FindBlueTooth: hci_inquiry')
  42.  else
  43.      begin
  44.        PDevName:=@DevName[0];
  45.        ba2str(@scan_info[0].bdaddr, PDevName);
  46.        writeln('Bluetooth Device Address (bdaddr) DevName = ',PChar(PDevName));
  47.  
  48.        PRemoteName:=@RemoteName[0];
  49.        // Read the remote name for 'timeout2' milliseconds
  50.        if (hci_read_remote_name(device_sock,@scan_info[0].bdaddr,255,PRemoteName,timeout2) < 0) then
  51.          writeln('No remote name found, check timeout.')
  52.        else
  53.          writeln('RemoteName = ',PChar(RemoteName));
  54.      end;
  55.  
  56.  hci_close_dev(device_sock);
  57. end;
  58.  


But the code in  http://sourceforge.net/projects/lazarus-ccr/files/Bluetooth/ is imho more complete than the wiki code (and the code in my inoffcial fork is actual running on some Raspis)






« Last Edit: January 12, 2021, 07:03:17 am by af0815 »
regards
Andreas

TopCat

  • New member
  • *
  • Posts: 9
Re: Bluetoothlaz - Installation and example comments
« Reply #2 on: January 12, 2021, 03:08:16 pm »
Thanks af0815, your code worked.d
The original code worked after I changed the last statement of btnFindBluetoothClick from  <c_close(device_sock);> to your <hci_close_dev(device_sock);> and added <unixtype> to the uses clause. That appears to be missing from the original samples at: https://wiki.lazarus.freepascal.org/Bluetooth.
My next challenge is to figure out how to connect to the Bluetooth printer so I can send text to it. I've tried the code in procedure ConnectRFCOMMClick taken from the original sample.  Although this runs, I haven't figured out how to relate the output from BuFindBluetoothClick to the settings needed in ConnectRFCOMMClick to output text to my printer. I've changed the value assigned to bt_addr, the device address, to the address of my printer. If I set <channel> to the value of the device_sock generated by BuFindBluetoothClick (12) , the procedure is unable to connect. By experiment I found that setting <channel> to 1 executed the code to send a message but nothing got through to the printer (which is switched on!).
I was hoping that ConnectRFCOMM would make a serial connection to the bluetooth printer and enable me to send further text by outputting to e.g. /dev/rfcomm0 or similar.
I'd be grateful for any insights or words of wisdom you can offer.

af0815

  • Hero Member
  • *****
  • Posts: 1289
Re: Bluetoothlaz - Installation and example comments
« Reply #3 on: January 12, 2021, 03:57:28 pm »
In the complete sample in the git you have a line with cnt:= fprecv(s,@bt_message, 1024, 0) if you change this to fpwrite you can send the messagedata to the device.

In my application i have put all together in a thread to send command to the Scanner and read the data depending if i adressed the rfid reader or the barcode part of the scanner. You have no device in /dev/rfcomm. All the communication must be done by fprecv and fpwrite, no com emulation by rfcom-driver is made.
regards
Andreas

TopCat

  • New member
  • *
  • Posts: 9
Re: Bluetoothlaz - Installation and example comments
« Reply #4 on: January 13, 2021, 06:21:55 pm »
Andreas,
Thanks for your help. I made use of your code from https://github.com/afriess/bluetoothlaz and changed the line
cnt:= fprecv(s,@bt_message, 1024, 0);
to
cnt:= fpsend(s,@bt_message, 1024, 0);
as fpwrite does not appear to be supported.
The good news is that it sent data to the printer  :). Unfortunately, there was a lot of it, all in hex  :(
I've tried code like this to send a string to the printer but without success. Just lots of hex.
  if status = 0 then
    begin
     msg := 'Here is my test output' + #13#10;
     cnt := fpsend(s, @msg, sizeof(msg), 0);
     if cnt = -1 then
       Memo1.Lines.Add('Error occured');
     // close socket after message is sent
     fpshutdown(s, 0);
     Memo1.Lines.Add('end');
    end; 
I've obviously misunderstood the (limited) explanation of the fpsend function and / or handling the mapping of the string to the buffer.
I can achieve what I want by using the Raspbian Bluetooth Manager to create a rfcomm serial connection to the printer, then simply run this code to print out a string: lsPrintr is a TLazSerial component.
procedure TMainForm.btnBluetoothClick(Sender: TObject);
begin
  if lsPrinter.Active = True then
    lsPrinter.Close;
  lsPrinter.BaudRate:= br__9600;
  lsPrinter.Device := '/dev/rfcomm0';
  lsPrinter.Open;
  lsPrinter.WriteData('Test output' + #13#10);
end;
I was hoping to avoid needing (others) to use the Bluetooth Manager to create a connection to the printer before running my code but it looks like the simplest way to do it, unless you can offer any additional thoughts?
Many thanks for your support with this. It is appreciated.
Terry

af0815

  • Hero Member
  • *****
  • Posts: 1289
Re: Bluetoothlaz - Installation and example comments
« Reply #5 on: January 15, 2021, 12:41:28 pm »
This a complete threaded code from a running sample, which is reading and writing to/from a RFID Reader. The reason for this is, rfcomm is not built automatic if the communication direktion is not the default. I have tested this with 2 RasPis. On one the rfcomm is built on the other not. One must be the initator and one the responder. This is only the logical sight, it have nothing to do with the transferdirection of the data later.

Code: Pascal  [Select][+][-]
  1. const
  2.   FIONREAD = $541B;
  3.   FIONBIO  = $5421;
  4.   FIOASYNC = $5452;
  5.  
  6. procedure TBTReadThread.Execute;
  7. var
  8.  readlen:cint32;
  9.  writelen:cint32;
  10.  bt_msg: string;
  11.  FDS : Tfdset;
  12.  watchdog: integer;
  13.  TimeV: TTimeVal;
  14.  timeout:integer;
  15. begin
  16.   FInLoop:=false;
  17.   if DummyCmd = '' then
  18.     DummyCmd:= '.ec -x'#13; // Set echo default als Dummycommando
  19.  opt := SizeOf(loc_addr);
  20.  //remote device address
  21.  bt_addr := FDevice;
  22.  //allocate socket
  23.  s := fpsocket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
  24.  //channel that destination device is listening on
  25.  channel := 1;
  26.  loc_addr.rc_family := AF_BLUETOOTH;
  27.  loc_addr.rc_bdaddr := bd_addr;
  28.  loc_addr.rc_channel := channel;
  29.  str2ba(@bt_addr, @bd_addr);
  30.  loc_addr.rc_bdaddr := bd_addr;
  31.  Debugln('Trying connection to ', bt_addr);
  32.  
  33.  status := fpconnect(s, @loc_addr, opt);
  34.  if status <> 0 then begin
  35.    res:=4;
  36.    MustDie:= true;
  37.  end;
  38.  Debugln('channel: ', IntToStr(channel), '  result: ', IntToStr(status));
  39.  try
  40.    bt_msg:='';
  41.    Owner.AktData:='';
  42.    watchdog:= 0;
  43.    while not MustDie do begin
  44.      FInLoop:=true;
  45.      try
  46.        res:= FpIOCtl(s,FIONREAD,@readlen);
  47.        //Debugln('FpIOCtl=', IntToStr(res), ' Cnt=', IntToStr(readlen));
  48.      except
  49.        MustDie:= true;
  50.        res:=1;
  51.        continue;
  52.        //Synchronize(@CallStatus);
  53.      end;
  54.      if (res <> 0) then begin
  55.         Synchronize(@CallStatus);
  56.      end;
  57.      if (res = 0) and (readlen > 0)  then begin
  58.        SetLength(bt_msg,readlen);
  59.        cnt:= fprecv(s,Pchar(bt_msg), readlen, 0);
  60.        {$ifdef DEBUG}DebugLn('after fprec', ' cnt=',IntToStr(cnt));{$endif}
  61.        if cnt <> 0 then begin
  62.          Owner.AktData:= Owner.AktData + bt_msg;
  63.          Synchronize(@CallEvent);
  64.        end;
  65.      end;
  66.      if Length(FData) > 0 then begin
  67.        writelen:= FpWrite(s,pchar(FData),length(FData));
  68.        if writelen=Length(FData) then begin
  69.          FData:='';
  70.          {$ifdef DEBUG}DebugLn('FpWrite ok=', IntToStr(writelen));{$endif}
  71.        end
  72.        else
  73.          if writelen= -1 then begin
  74.            DebugLn('Error after FpWrite');
  75.            MustDie:= true;
  76.            res:=3;
  77.            continue;
  78.          end
  79.          else begin
  80.            DebugLn('Problem after FpWrite =',IntToStr(writelen));
  81.          end;
  82.      end
  83.      else begin
  84.        inc(watchdog);
  85.        if watchdog > (FCheckTime*10) then begin
  86.          watchdog:=0;
  87.          writelen:= FpWrite(s,pchar(DummyCmd),Length(DummyCmd));
  88.          if writelen= -1 then begin
  89.            DebugLn('Watchdog Error after FpWrite');
  90.            MustDie:= true;
  91.            res:=4;
  92.            continue;
  93.          end
  94.          else begin
  95.            {$ifdef DEBUG}DebugLn('Watchdog FpWrite ok=', IntToStr(writelen)); {$endif}
  96.          end;
  97.        end;
  98.      end;
  99.      Sleep(100);
  100.    end;
  101.    Synchronize(@CallStatus);
  102.  finally
  103.    FInLoop:=False;
  104.    // close socket
  105.    fpshutdown(s, 0);
  106.    CloseSocket(s);
  107.    Terminate;
  108.  end;
  109.  Debugln('end');
  110. end;
  111.  
regards
Andreas

TopCat

  • New member
  • *
  • Posts: 9
Re: Bluetoothlaz - Installation and example comments
« Reply #6 on: January 16, 2021, 04:32:19 pm »
PROBLEM SOLVED  ;D
Andreas, I'd almost given up, particularly with the thought of getting into threads that I have little experience of. However, I extracted and modified a section of your TBTReadThread.Execute code and inserted that to replace part of the code in your example ConnectRFClick. In case it is of use to others, I've shown the modified ConnectRFClick method below.
With Buetooth turned on, running the ConnectRFClick procedure printed the string FData directly to the printer. No other steps were needed.
A big THANK YOU for all your support and your patience.
Terry
Code: Pascal  [Select][+][-]
  1. procedure TForm1.ConnectRFClick(Sender: TObject);
  2. var
  3.   loc_addr: sockaddr_rc;
  4.   opt: longint;
  5.   s, status: unixtype.cint;
  6.   bd_addr: bdaddr_t;
  7.   channel: byte;
  8.   bt_addr: array[0..17] of char;
  9.   //bt_message: array[0..1023] of char; //Variable not used
  10.   //cnt: ssize_t; //Variable not used
  11.   {Additional declarations}
  12.   FData: string;
  13.   writelen: integer;
  14. begin
  15.   opt := SizeOf(loc_addr);
  16.   //remote device address
  17.   //bt_addr := '00:07:80:66:0A:5E';
  18.   bt_addr := '00:15:0E:E4:03:49'; {my printer address}
  19.   //allocate socket
  20.   s := fpsocket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
  21.   //channel that destination device is listening on
  22.   channel := 1;
  23.   loc_addr.rc_family := AF_BLUETOOTH;
  24.   //loc_addr.rc_bdaddr := bd_addr; //Not needed. Same code 3 lines down.
  25.   loc_addr.rc_channel := channel;
  26.   str2ba(@bt_addr, @bd_addr);
  27.   loc_addr.rc_bdaddr := bd_addr;
  28.   writeln('Trying connection to ', bt_addr);
  29.  
  30.   status := fpconnect(s, @loc_addr, opt);
  31.   writeln('channel: ', channel, '  result: ', status);
  32.  
  33.   FData := 'Lets try printing this...';
  34.   FData := FData + #13#10 + 'and one more line...';
  35.   FData := FData + #13#10; {Add CRLF to get output}
  36.   if status = 0 then
  37.     begin
  38.       {Code extracted and adapted from procedure TBTReadThread.Execute;
  39.        Successfully sends FData string to the Bluetooth printer}
  40.       if Length(FData) > 0 then
  41.         begin
  42.           //writelen:= FpWrite(s,pchar(FData),length(FData));
  43.           writelen:= fpsend(s,pchar(FData),length(FData), 0);
  44.           if writelen=Length(FData) then
  45.             begin
  46.               FData:='';
  47.               writeln{DebugLn}('FpWrite ok=', IntToStr(writelen));
  48.             end
  49.           else
  50.             if writelen= -1 then
  51.               begin
  52.                 writeln{DebugLn}('Error after FpWrite');
  53.                 //MustDie:= true;
  54.                 //res:=3;
  55.                 //continue;
  56.               end
  57.             else
  58.               begin
  59.                 writeln{DebugLn}('Problem after FpWrite =',IntToStr(writelen));
  60.               end;
  61.         end;
  62.   (*
  63.     cnt:=0;
  64.     while cnt >= 0 do
  65.     begin
  66.       bt_message := '';
  67.       //cnt:= fprecv(s,@bt_message, 1024, 0);
  68.       cnt:= fpsend(s,@bt_message, 1024, 0);
  69.       if cnt = 0 then
  70.       begin
  71.         writeln('leer');
  72.       end
  73.       else
  74.         Write(bt_message);
  75.     end;
  76.   *)
  77.     // close socket after message is sent
  78.     fpshutdown(s, 0);
  79.     writeln('end');
  80.   end;
  81. end;
« Last Edit: January 16, 2021, 04:45:45 pm by TopCat »

af0815

  • Hero Member
  • *****
  • Posts: 1289
Re: Bluetoothlaz - Installation and example comments
« Reply #7 on: January 16, 2021, 05:22:26 pm »
The threaded version is not so complicaded. The execute is the major part, allother is only tranfer the data thread safe. The execute can send data and receive and do a alive with the dummy command to detect if the connection is broken. This was one of my troubles to detect safe, if the connection is broken.

If the connection is detected as broken, the not shown code handle tis, look for a device and reconnect it, show the user that the connection is broken and ...

This is the reason for the more complex code.  :)
regards
Andreas

TopCat

  • New member
  • *
  • Posts: 9
Re: Bluetoothlaz - Installation and example comments
« Reply #8 on: January 16, 2021, 05:47:44 pm »
Andreas,
Understood. I don't need to deal with two-way data transfer by Bluetooth, so I hope that my code will be adequate. We'll see
I've learnt quite a lot about Linux, Lazarus and Free Pascal while trying to solve this problem, in addition to the specifics that you have helped with. Thank you for your time.

 

TinyPortal © 2005-2018