How to send/write and receive/read (BIDI) Bluetooth Data from Blazor
Product JSPrintManager for Blazor Published 05/21/2026 Updated 05/21/2026 Author Neodynamic
Overview
JSPrintManager for Blazor supports Bidirectional (BIDI) Bluetooth Communication through the RFCOMM (Radio frequency communication) protocol from Blazor allowing you to Send/Write & Receive/Read data strings to any BT Address and Channel reachable from the client system.
In this walkthrough, you'll learn how to Send/Write & Receive/Read data strings from Blazor through any BT Address and Channel reachable from the client machine. This solution works with any popular browser like Chrome, Firefox, IE/Edge & Safari on Windows, Linux, Raspberry Pi, Android and Mac systems!
Follow up these steps
- Be sure you install in your dev machine JSPrintManager (JSPM) (Available for Windows, Linux, Raspberry Pi & Mac)
This small app must be installed on each client that will print from your website! - In your Blazor project...
- Add a NuGet reference to the JSPrintManager Razor Component
- Add the JSPrintManager service...
- Add the following statement at the top of your
StartuporProgramfile
using Neodynamic.Blazor;- For Blazor Server
Add the following line in theStartup's ConfigureServicesmethod
services.AddJSPrintManager();For .NET 8+ you must use Interactive Server components and render mode, so add these settings:
builder.Services.AddRazorComponents() .AddInteractiveServerComponents();app.MapRazorComponents<App>() .AddInteractiveServerRenderMode() - For Blazor WebAssembly
Add the following line in theProgram's Mainmethod
builder.Services.AddJSPrintManager();
- For Blazor Server
- Add the following statement at the top of your
- Add the following statement in the
_Imports.razorfile
@using Neodynamic.Blazor - Add a new Razor Page and copy/paste the following code. Please read the source code comments to understand the printing logic!
For .NET 8+ Blazor Server you must use Interactive Server render mode, so add these settings:
@page "/" @rendermode InteractiveServer @inject JSPrintManager JSPrintManager@page "/" @inherits LayoutComponentBase @inject JSPrintManager JSPrintManager @using System @using System.Text <div> <strong>JSPM </strong><span>WebSocket Status </span> @if (JSPrintManager.Status == JSPMWSStatus.Open) { <span class="badge badge-success"> <i class="fa fa-check" /> Open </span> } else if (JSPrintManager.Status == JSPMWSStatus.Closed) { <span class="badge badge-danger"> <i class="fa fa-exclamation-circle" /> Closed! </span> <div> <strong>JSPrintManager (JSPM) App</strong> is not installed or not running! <a href="https://neodynamic.com/downloads/jspm" target="_blank">Download JSPM Client App...</a> </div> } else if (JSPrintManager.Status == JSPMWSStatus.Blocked) { <span class="badge badge-warning"> <i class="fa fa-times-circle" /> This Website is Blocked! </span> } else if (JSPrintManager.Status == JSPMWSStatus.WaitingForUserResponse) { <span class="badge badge-warning"> <i class="fa fa-user-circle" /> Waiting for user response... </span> } </div> @if (JSPrintManager.Status == JSPMWSStatus.Open) { <div class="row"> <div class="col-md-12"> <h2 class="text-center"> BIDI Bluetooth Comm </h2> <hr /> </div> </div> <div class="row"> <div class="col-md-12"> <EditForm Model="@MyBtComm"> <div class="form-group"> <div class="row"> <div class="col-md-6"> <label>Address:</label> <InputText @bind-Value="MyBtComm.Address" class="form-control form-control-sm" placeholder="00.00.00.00.00.00" /> </div> <div class="col-md-2"> <label>Channel:</label> <InputNumber @bind-Value="MyBtComm.Channel" class="form-control form-control-sm" /> </div> <div class="col-md-2"> <button class="btn btn-primary" @onclick="OpenPopup">Show BT Devices...</button> </div> </div> <br /><br /> <div class="row"> <div class="col-md-2"> <button class="btn btn-success btn-lg" @onclick="DoBtBidi"> Start BIDI Comm... </button> </div> <div class="col-md-2"> <button class="btn btn-danger btn-lg" @onclick="StopBtBidi"> Stop </button> </div> </div> <br /><br /> <div class="row"> <div class="col-md-12"> <br /> <strong>Data to Send:</strong> <div class="input-group mb-3"> <InputText @bind-Value="dataToSend" class="form-control form-control-sm" /> <div class="input-group-append"> <button class="btn btn-info" @onclick="DoSendData"> Send... </button> </div> </div> </div> </div> <br /><br /> <div class="row"> <div class="col-md-12"> <div class="text-center"> BT Comm BIDI Messages<br /> <textarea class="terminal" readOnly @bind="Messages" rows="10" cols="100" /> </div> </div> </div> </div> </EditForm> </div> </div> } @if (IsVisible) { <div class="modal fade show" style="display:block; background-color: rgba(0,0,0,0.5);" tabindex="-1"> <div class="modal-dialog"> <div class="modal-content"> <div class="modal-header"> <h5 class="modal-title">Available BT Devices</h5> <button type="button" class="btn-close" @onclick="ClosePopup" aria-label="Close"></button> </div> <div class="modal-body"> <p>@BtDevicesInfo</p> </div> <div class="modal-footer"> <button type="button" class="btn btn-secondary" @onclick="ClosePopup">Close</button> </div> </div> </div> </div> } @code { protected override void OnAfterRender(bool firstRender) { if (firstRender) { // Handle OnGetBluetoothDevices event... JSPrintManager.OnGetBluetoothDevices += () => { var sb = new StringBuilder(); foreach (var btDev in JSPrintManager.BluetoothDevices) { sb.Append($"Name: {btDev.Name}\n DeviceAddress: {btDev.Address}\n----------------------"); } BtDevicesInfo = sb.ToString(); StateHasChanged(); }; JSPrintManager.OnStatusChanged += () => { StateHasChanged(); // Status = Open means that JSPM Client App is up and running! if (JSPrintManager.Status == JSPMWSStatus.Open) { //Try getting BT devices... JSPrintManager.TryGetBluetoothDevices(); } }; // Start WebSocket comm JSPrintManager.Start(); } base.OnAfterRender(firstRender); } protected override void OnInitialized() { JSPrintManager.OnError += () => { var error = JSPrintManager.LastErrorMessage; }; base.OnInitialized(); } private string Messages { get { return MessagesBuffer.ToString(); } set { MessagesBuffer.Clear(); MessagesBuffer.Append(value); } } private string BtDevicesInfo = "No BT Devices found."; private StringBuilder MessagesBuffer = new StringBuilder(); private string dataToSend; // The BTComm info obj private BTComm MyBtComm { get; set; } = new(); private void DoBtBidi() { // handle BT Comm messages JSPrintManager.OnBTCommEvent += () => { var evt = JSPrintManager.BTCommEventCache[JSPrintManager.BTCommEventCache.Count - 1]; switch (evt.Type) { case BTCommEventType.Close: MessagesBuffer.Append("COMM CLOSE" + Environment.NewLine); break; case BTCommEventType.Error: MessagesBuffer.Append("COMM ERROR: " + evt.Data + Environment.NewLine); break; case BTCommEventType.Open: MessagesBuffer.Append("COMM OPEN" + Environment.NewLine); break; case BTCommEventType.DataReceived: MessagesBuffer.Append("<< " + evt.Data); break; case BTCommEventType.DataSent: MessagesBuffer.Append(">> " + evt.Data); break; } StateHasChanged(); }; // Start BIDI BT Comm... JSPrintManager.OpenBTComm(MyBtComm); } private void StopBtBidi() { // Stop BIDI BT Comm... JSPrintManager.CloseBTComm(MyBtComm); } private void DoSendData() { // Send data through BIDI BT Comm... if (string.IsNullOrWhiteSpace(dataToSend) == false) { MyBtComm.DataToSend = dataToSend; JSPrintManager.SendDataBTComm(MyBtComm); } } private bool IsVisible { get; set; } = false; private void OpenPopup() { IsVisible = true; } private void ClosePopup() { IsVisible = false; } }
- That's it! Run your website and play with it.