Connect a barcode reader to a Xamarin Forms app via Bluetooth

Recently, in Xamarin Forums, have appeared some posts about “how to read barcodes with Xamarin”.

I have already covered the topic in this post some days ago, and I would like to add some thoughts.

First of all, how can I read a barcode? I think there are three solutions.

  • using a device with an integrated barcode scanner. These are often “rugged” (and expansive) devices used for enterprise apps. For example, I am developing an app for a great supermarket chain and they use this kind of device. Performance are higher, speed is higher. The best choice
  • using a device with a camera. This is the worst choice, but is the only choice for smartphone. Every smartphone has a camera, so, every smartphone can read a barcode. The biggest problem is performance. The reading speed is low, the camera has to focus the barcode and the battery runs out quickly. Can be good only for some commercials app downloaded from stores

In addition to these two choices, can we find the third? Yes of course. Maybe not so good like an integrated scanner, but absolutely better than camera. This choice can be a device connected to a bluetooth scanner. Are you laughing? Why?

A bluetooth scanner is born to read barcodes (a camera is born to take photo…) so performance are very high, has it’s integrated battery and there are a lot of models:

  • pistol grip
  • ring scanner
  • pocket scanner

I have created a little repo on GitHub. It’a a good starting point to connect a scanner to an Android device via bluetooth. It use Xamarin Forms to update UI, but the big work is in Android Project.

Which are interesting part of this app?

  1. Return a list of paired devices
  2. Start and Stop receiving data from the scanner
  3. Visualize data received in a list

For point 1 and 2 it uses Dependency services. First of all, in Xamarin Forms project, you should create an Interface like this:

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;

namespace TestBth
{
	public interface IBth
	{
		void Start(string name, int sleepTime, bool readAsCharArray);
		void Cancel();
		ObservableCollection<string> PairedDevices();
	}
}

You found three methods:

  • Start: it starts the reading service from Bluetooth Socket
  • Cancel: it stops the reading service
  • PairedDevices: it returns a list of paired devices

Then in your Android project you should have Interface implementation

Android implementation

using System;
using Android.Bluetooth;
using Java.Util;
using System.Threading.Tasks;
using Java.IO;
using TestBth.Droid;
using System.Threading;
using System.Collections.Generic;
using System.Collections.ObjectModel;

[assembly: Xamarin.Forms.Dependency (typeof (Bth))]
namespace TestBth.Droid
{
	public class Bth : IBth
	{

		private CancellationTokenSource _ct { get; set; }

		const int RequestResolveError = 1000;

		public Bth ()
		{
		}

		#region IBth implementation

		/// <summary>
		/// Start the "reading" loop 
		/// </summary>
		/// <param name="name">Name of the paired bluetooth device (also a part of the name)</param>
		public void Start(string name, int sleepTime = 200, bool readAsCharArray = false){
			
			Task.Run (async()=>loop(name, sleepTime, readAsCharArray));
		}



		private async Task loop(string name, int sleepTime, bool readAsCharArray){
			BluetoothDevice device = null;
			BluetoothAdapter adapter = BluetoothAdapter.DefaultAdapter;
			BluetoothSocket BthSocket = null;

			_ct = new CancellationTokenSource ();
			while (_ct.IsCancellationRequested == false) {
			
				try {
					Thread.Sleep (sleepTime);

					adapter = BluetoothAdapter.DefaultAdapter;

					if(adapter == null)
						System.Diagnostics.Debug.WriteLine("No Bluetooth adapter found.");
					else
						System.Diagnostics.Debug.WriteLine ("Adapter found!!");

					if(!adapter.IsEnabled)
						System.Diagnostics.Debug.WriteLine("Bluetooth adapter is not enabled."); 
					else
						System.Diagnostics.Debug.WriteLine ("Adapter enabled!");

					System.Diagnostics.Debug.WriteLine("Try to connect to " + name);

					foreach (var bd in adapter.BondedDevices) {
						System.Diagnostics.Debug.WriteLine ("Paired devices found: " + bd.Name.ToUpper ());
						if (bd.Name.ToUpper().IndexOf (name.ToUpper ()) >= 0) {

							System.Diagnostics.Debug.WriteLine("Found " + bd.Name + ". Try to connect with it!");
							device = bd;
							break;
						}
					}

					if (device == null)
						System.Diagnostics.Debug.WriteLine ("Named device not found.");
					else {
						UUID uuid = UUID.FromString ("00001101-0000-1000-8000-00805f9b34fb");
						if((int)Android.OS.Build.VERSION.SdkInt >= 10) // Gingerbread 2.3.3 2.3.4
							BthSocket = device.CreateInsecureRfcommSocketToServiceRecord (uuid);
						else
							BthSocket = device.CreateRfcommSocketToServiceRecord (uuid);
					
						if (BthSocket != null) {


							//Task.Run ((Func<Task>)loop); /*) => {
							await BthSocket.ConnectAsync ();


							if(BthSocket.IsConnected){
								System.Diagnostics.Debug.WriteLine("Connected!");
								var mReader = new InputStreamReader(BthSocket.InputStream);
								var buffer = new BufferedReader(mReader);
								//buffer.re
								while (_ct.IsCancellationRequested == false){
									if(buffer.Ready ()){
										//										string barcode =  buffer
										//string barcode = buffer.

										//string barcode = await buffer.ReadLineAsync();
										char[] chr = new char[100];
										//await buffer.ReadAsync(chr);
										string barcode = "";
										if (readAsCharArray) { 
										
											await buffer.ReadAsync(chr);
											foreach (char c in chr) {

												if (c == '\0')
													break;
												barcode += c;
											}

										}else
											barcode = await buffer.ReadLineAsync();
										
										if(barcode.Length > 0){
											System.Diagnostics.Debug.WriteLine("Letto: " + barcode);
											Xamarin.Forms.MessagingCenter.Send<App, string> ((App)Xamarin.Forms.Application.Current, "Barcode", barcode);
										}
										else
											System.Diagnostics.Debug.WriteLine ("No data");

									}
									else
										System.Diagnostics.Debug.WriteLine ("No data to read");

									// A little stop to the uneverending thread...
									System.Threading.Thread.Sleep (sleepTime);
									if(!BthSocket.IsConnected){
										System.Diagnostics.Debug.WriteLine ("BthSocket.IsConnected = false, Throw exception");
										throw new Exception();
									}
								}

								System.Diagnostics.Debug.WriteLine ("Exit the inner loop");

							}
						}
						else
							System.Diagnostics.Debug.WriteLine ("BthSocket = null");

					}


				}
				catch{
				}

				finally{
					if (BthSocket != null)
						BthSocket.Close ();
					device = null;
					adapter = null;
				}			
			}

			System.Diagnostics.Debug.WriteLine ("Exit the external loop");
		}

		/// <summary>
		/// Cancel the Reading loop
		/// </summary>
		/// <returns><c>true</c> if this instance cancel ; otherwise, <c>false</c>.</returns>
		public void Cancel(){
			if (_ct != null) {
				System.Diagnostics.Debug.WriteLine ("Send a cancel to task!");
				_ct.Cancel ();
			}
		}

		public ObservableCollection<string> PairedDevices()
		{
			BluetoothAdapter adapter = BluetoothAdapter.DefaultAdapter;
			ObservableCollection<string> devices = new ObservableCollection<string>();

			foreach (var bd in adapter.BondedDevices)
				devices.Add(bd.Name);
				
			return devices;
		}


		#endregion
	}
}

Read method

The “Read” method is simply a loop that continue to:

Search for a paired device

foreach (var bd in adapter.BondedDevices) {
	System.Diagnostics.Debug.WriteLine ("Paired devices found: " + bd.Name.ToUpper ());
	if (bd.Name.ToUpper().IndexOf (name.ToUpper ()) >= 0) {

		System.Diagnostics.Debug.WriteLine("Found " + bd.Name + ". Try to connect with it!");
		device = bd;
		break;
	}
}

adapter.BondedDevises returns all paired devices. I have a “name” parameter that is the name of the bluetooth scanner I want to use (or a part of the name. For example, if the scanner has a name  like “QuickScan XXX YYY”, I can use “scan” to connect to it).

In “device” I have my bluetooth device!

Create a Bluetooth Socket and use it like a RS232

UUID uuid = UUID.FromString ("00001101-0000-1000-8000-00805f9b34fb");
	
if((int)Android.OS.Build.VERSION.SdkInt >= 10) // Gingerbread 2.3.3 2.3.4
	BthSocket = device.CreateInsecureRfcommSocketToServiceRecord (uuid);
else
	BthSocket = device.CreateRfcommSocketToServiceRecord (uuid);

Here I try to create a socket. The strange number (“0001101…”) is the SPP UUID.

Verify if there are some data in the socket

await BthSocket.ConnectAsync ();

if(BthSocket.IsConnected){
	System.Diagnostics.Debug.WriteLine("Connected!");
	var mReader = new InputStreamReader(BthSocket.InputStream);
	var buffer = new BufferedReader(mReader);

	while (_ct.IsCancellationRequested == false){
		if(buffer.Ready ()){

Created the socket, I connect to it and open a read buffer, so I can receive data from the scanner. “IsCancellationRequested” is used to break the loop (calling “Cancel” method defined in the interface)

Read data and send it to Xamarin Forms app

char[] chr = new char[100];
string barcode = "";
if (readAsCharArray) { 
										
	await buffer.ReadAsync(chr);
	foreach (char c in chr) {

		if (c == '\0')
			break;
		barcode += c;
	}

}else
	barcode = await buffer.ReadLineAsync();
										
if(barcode.Length > 0){
	System.Diagnostics.Debug.WriteLine("Letto: " + barcode);
	Xamarin.Forms.MessagingCenter.Send<App, string> ((App)Xamarin.Forms.Application.Current, "Barcode", barcode);
}
else
	System.Diagnostics.Debug.WriteLine ("No data");

Now I can test if data is present and read it. I use two way to read data, ReadLine and ReadChar. ReadChar should works always…

Now I can “send” a message to “App” with my barcode!

Xamarin Forms implementation

Xamarin Forms part is very simple. You should:

  • Start receiving data
  • Stop receiving data

To start and stop receiving data, you can use this code in your App.cs file.

protected override void OnSleep ()
{
	MessagingCenter.Send<App>(this, "Sleep"); // When app sleep, send a message so I can "Cancel" the connection
}

protected override void OnResume ()
{
	MessagingCenter.Send<App>(this, "Resume"); // When app resume, send a message so I can "Resume" the connection
}

When app “Resume”, I re-activate read. When app “sleep”, I close read. I do nothing when app “Start” because in this sample the “start read” is not automatic.

When I start to read data?

In the MyPageViewModel.cs. In the constructor you can find:

public MyPageViewModel()
{

	MessagingCenter.Subscribe<App>(this, "Sleep",(obj) => 
	{
		// When the app "sleep", I close the connection with bluetooth
		if(_isConnected)
			DependencyService.Get<IBth>().Cancel();

	});

	MessagingCenter.Subscribe<App>(this, "Resume", (obj) =>
	{

		// When the app "resume" I try to restart the connection with bluetooth
		if(_isConnected)
			DependencyService.Get<IBth>().Start(SelectedBthDevice, _sleepTime, true);

	});


	this.ConnectCommand = new Command(() => {
			
		// Try to connect to a bth device
		DependencyService.Get<IBth>().Start(SelectedBthDevice, _sleepTime, true);
		_isConnected = true;

		// Receive data from bth device
		MessagingCenter.Subscribe<App, string> (this, "Barcode", (sender, arg) => {

			// Add the barcode to a list (first position)
			ListOfBarcodes.Insert(0, arg);
		});
	});

	this.DisconnectCommand = new Command(() => { 

		// Disconnect from bth device
		DependencyService.Get<IBth>().Cancel();
		MessagingCenter.Unsubscribe<App, string>(this, "Barcode");
		_isConnected = false;
	});


	try
	{
		// At startup, I load all paired devices
		ListOfDevices = DependencyService.Get<IBth>().PairedDevices();
	}
	catch (Exception ex)
	{
		Application.Current.MainPage.DisplayAlert("Attention", ex.Message, "Ok");
	}
}

I try to explain:

  • When I receive a “sleep” message from App.cs, I “Cancel” the read loop.
  • When I receive a “resume” message from App.cs, I “Start” again the read loop.
  • When I press “Connect” button, I “Start” the read loop
  • I fill the Picker with the list of Paired devices so the user can easily select the device to connect. I use FreshEssential Bindable Picker for this.

That’s all folks. I have tested this approach with 5/6 different scanners and it works fine. There are better methods? Maybe… leave a comment!

38 pensieri su “Connect a barcode reader to a Xamarin Forms app via Bluetooth

  1. I just wanted to thank you for your work and your post.
    Very good explanation and good example, that I’ll use as starting point for my own project.
    Best regards, Yurii.sio2

    "Mi piace"

  2. Thank you very much for your post! It has been very useful for me.
    I would like to know if you could recommend me an implementation like this for Xamarin iOS ? As you can see, I have to do the same in the other platform haha.

    Thank you again!!!

    Seba

    "Mi piace"

  3. I have started to take a look to iOS connection… but seems to be some problems.

    If I have understand correctly, iOS accept BLE devices. Otherwise device should join MFi program, but I am not sure about this.

    Can someone explain better these things?

    If I open “bluetooth” page on my iPhone, I don’t see my Bluetooth Scanner, so I think I can’t pair it as SPP.

    If I connect as HID, I can pair and connect (and it works….)

    "Mi piace"

      • Excellent Seba but I think there are some problems with iOS. Here a response from Apple

        It appears that the Biocontrol Hhr3000 scanner is not an MFI Bluetooth accessory. As such, this means that there is no supported means for an iOS application to connect with the scanner and communicate with an iOS application. You can submit an API enhancement request using the Apple Developer Bug Report web page that iOS provide API’s similar to Android to facilitate the connection and communication with Classic Bluetooth accessories – actually, this has been a popular ER ever since the release of iOS 3. The other means to make this happen would be for the developer of the scanner to become an MFI licensee and implement the iPod Accessory Protocol (iAP2) into the scanner for compatability with iOS. If this happens, then the accessory can use the iOS External Accessory Framework methods to connect and communicate with the scanner.

        Let me know… thanks

        "Mi piace"

  4. Thanks Alessandro! Those comments are very helpful for me. In my case, I am connecting different bluetooth devices and yesterday I awared there are some devices registered on the MFI Program and what is more there is an application that connect with one of them. The next week I will be working on that and I hope I could create a post regarding this BLE connection on Xamarin iOS.

    Once I could create a connection, you will be receiving a message!

    Thank you!

    "Mi piace"

  5. Pingback: Android Bluetooth Device List – Friendly Names – program faq

  6. Hi,
    Has there been any further development for the iOS side? I have a requirement for an app to be able to support Bluetooth bar code scanners and I’d like to stay away from having an SDK and re-publishing the app for each scanner.

    Thanks,
    Mark

    "Mi piace"

  7. Thanks for the great example.
    Please i´m receiving and exception (Exception: Read failed, socket might closed or timeout, read ret:-1)
    when executing “await BthSoket.ConnectAsync();
    The scanner is working ok as an input keyboard. Which is the correct Bluetooth mode comunication to use (HID, SPP or BLE)?
    Sorry i’m newbie with xamarin and Android. Can you help me?

    "Mi piace"

  8. Hi Alessandro, I cloned your repo and am running it on my Android tablet but I am getting some errors as per below.
    Also, I dont understand how does it work. I paired my device to my tablet and was able to connect to it from your app. But all I see is my device name and serial at the top, then a field with “250” in it. What is that?
    And below Disconnect and Connect buttons. As I said I have connected successfully my device (CS3070:11081522500792) and I assume I am supposed to scan a barcode into the field with “250” in it. But I am not sure why it shows 250 at all times and why it does not show what I scanned?

    Here is the output with the error message (shortened):
    Thread started: #2
    Thread started: #3
    Thread started: #4
    Thread started: #5
    [0:] Adapter found!!
    [0:] Adapter enabled!
    [0:] Try to connect to CS3070:11081522500792
    [0:] Paired devices found: CS3070:11081522500792
    [0:] Found CS3070:11081522500792. Try to connect with it!
    08-03 12:40:31.224 W/BluetoothAdapter( 8372): getBluetoothService() called with no BluetoothManagerCallback
    [0:] EXCEPTION: read failed, socket might closed or timeout, read ret: -1
    [0:] Adapter found!!
    [0:] Adapter enabled!
    [0:] Try to connect to CS3070:11081522500792
    [0:] Paired devices found: CS3070:11081522500792
    [0:] Found CS3070:11081522500792. Try to connect with it!
    08-03 12:40:31.923 W/BluetoothAdapter( 8372): getBluetoothService() called with no BluetoothManagerCallback
    [0:] EXCEPTION: read failed, socket might closed or timeout, read ret: -1
    [0:] Adapter found!!
    [0:] Adapter enabled!
    [0:] Try to connect to CS3070:11081522500792
    [0:] Paired devices found: CS3070:11081522500792
    [0:] Found CS3070:11081522500792. Try to connect with it!
    08-03 12:40:32.614 W/BluetoothAdapter( 8372): getBluetoothService() called with no BluetoothManagerCallback
    [0:] EXCEPTION: read failed, socket might closed or timeout, read ret: -1
    [0:] Adapter found!!
    [0:] Adapter enabled!
    [0:] Try to connect to CS3070:11081522500792
    [0:] Paired devices found: CS3070:11081522500792
    [0:] Found CS3070:11081522500792. Try to connect with it!
    08-03 12:40:33.125 W/BluetoothAdapter( 8372): getBluetoothService() called with no BluetoothManagerCallback
    [0:] EXCEPTION: read failed, socket might closed or timeout, read ret: -1

    Thanks

    "Mi piace"

  9. 250 are millisecond. Every 250 milliseconds device try to read from scanner, or try to connect with it.
    You can add some breakpoint in bth.cs loop method… I think connectasync fails. Are you sure SPP is enabled on your scanner?

    "Mi piace"

  10. Thanks for sharing that link Alessandro, very helpful. In addition, I found another also helpful link
    http://www.taltech.com/barcodesoftware/articles/which_barcode_scanner_interface

    So, I can see that both USB and Bluetooth connected scanners have 2 modes:
    – Keyboard Wedge which is basically as if the scanner were the keyboard
    – RS232 serial (USB) but based on my reading, this require some kind of driver program to be installed on the device which then makes “virtual RS232 port” for devices that dont have real serial ports. The article above explains this nice but without mentioning mobile devices.
    – Bluetooth connection seem to be also 2 mode connection (Keyboard Wedge and SPP you mentioned). Is there any driver needed for your article explaining bluetooth SPP connection on Android? What about iOS?
    Thanks again

    "Mi piace"

    • Bluetooth connection seem to be also 2 mode connection (Keyboard Wedge and SPP you mentioned). Is there any driver needed for your article explaining bluetooth SPP connection on Android? What about iOS?

      Driver? Do you mean like ‘windows drivers ‘

      For Android and SPP you can try my repo on GitHub.

      For iOS it’s hard to find a scanner that you can connect to an Apple device because they don’t follow MFi

      "Mi piace"

  11. Hello, first I apologize for my English, but this using the Google translator, first thank you for taking part of your time to teach people like us what it is to be a programmer !!!, now to the problem, I have to create an APP in Xamarin that works on IOS / Android and can connect via bluetooth to a Raspberry, I have done the code, I can find some devices, but I can not detect the raspberry and IOS tells me something of an exception, you could help me with this problem please ??? I do not know how to solve it and tried to search all over the internet for material to be able to do it, also I have seen your contributions in stackoverflow that have helped me get here, thank you very much and I hope for a prompt response from you !!!!

    "Mi piace"

  12. HI, first of all thanks a ton this helped me a lot. I just have one query which you might be able to help me with . I just want to know if it’s possible to pair with unbonded classical bluetooth devices from the code as well or do we have to do it manually from the mobile settings and then search for bonded bluetooth devices ?
    Thanks in advance

    "Mi piace"

  13. This is incredibly helpful and well explained, thank you very much!
    I successfully implemented your solution and now I’m able to read any optical code by using the Zebra RFD8500.
    The issue that I’m having is that a few seconds after scanning any optical code the RFD8500 is beeping four times. The communication is working perfectly; the only issue is that annoying sound.
    I suppose this issue is device-related, but I would like to know if you have seen this problem before.

    Thanks again!

    "Mi piace"

    • Thanks Chamox.
      I don’t know about the sound. Maybe the scanner disconnect from Bluetooth then reconnect? If you test my demo project, you should see in Visual Studio “output” when the bth communication connect and disconnect. Othervise you should take a look to Scanner user manual and see if there are some settings.

      "Mi piace"

  14. HI Acaliaro,

    Thanks for spreading your knowledge! I am planning on testing this code in one of my projects. Will that be okay? I don’t see any licensing so I was just wondering.

    Thanks again,

    "Mi piace"

Rispondi

Inserisci i tuoi dati qui sotto o clicca su un'icona per effettuare l'accesso:

Logo di WordPress.com

Stai commentando usando il tuo account WordPress.com. Chiudi sessione /  Modifica )

Google photo

Stai commentando usando il tuo account Google. Chiudi sessione /  Modifica )

Foto Twitter

Stai commentando usando il tuo account Twitter. Chiudi sessione /  Modifica )

Foto di Facebook

Stai commentando usando il tuo account Facebook. Chiudi sessione /  Modifica )

Connessione a %s...