Bluetooth Port - Assigning Service to COM1

Topics: Bluetooth - Microsoft
Jun 1, 2012 at 4:30 PM
Edited Jun 1, 2012 at 4:40 PM

I've seen a couple posts with no definitive answers and one that presumably solved the issue, but did not post the answer. (http://inthehand.co.uk/forums/p/5255/13369.aspx)

I'm curious if anyone has solved the issue of registering a service to a port.

Scenario / Issue:

I have two applications. Application A connects to COM1 and communicates with whatever device is on COM1. Application B sets what device is registered to COM1. I don't have the ability to modify Application A, just Application B. Application B works successfully, but requires a soft reset (the device, not Application A) before Application A recognizes the new device.

Currently, I am removing any currently paired bluetooth devices, removing any Registry entries for those devices, and then pairing with the new device and saving a registry value for that device. Once the registry is saved, I fire "client.SetServiceState(BluetoothService.SerialPort, true)". This seems to have no effect, however. I tried running my own "RegisterDevice", but I'm not exactly sure how to use "PORTEMUPortParams", as it can't find the class. I was using this as a reference: http://wockets.googlecode.com/svn-history/r12/trunk/InTheHand.Net/Net/Bluetooth/BluetoothSerialPort.cs

Anyways, the issue is that the reboot is taking quite a while and I would much prefer it work without a reboot.

Aug 23, 2012 at 9:21 AM
Edited Aug 23, 2012 at 9:22 AM

Hello teynon, (sorry for my bad English, I'm a French-speaker)

The goal was to create Virtual port on Windows mobile devices and bind them to the Bluetooth device and also to delete the virtual port without the need of a soft-reset.

I had exactly the same problem as you. And I finally got it work.

I can explain exactly how to make it work, it means to make some changes to the 32feet library (mainly the BluetoothSerialPort class).

I have all the code with comments, but I don't know exactly where to post it, as I know it is a requested feature, perhaps it can also be integrated in the next versions of 32feet.

Here is a brief explanation :

The first observation I made was that the virtual serial port was correctly created in SetServiceState, but after a random amount of time it goes away. So I knew that it was a problem with a destructor. The BluetoothSerialPort class has a register functions that works perfectly, so the virtual serial port is created. But its destructor calls the Deregister functions which deletes the virtual port.

Here are the changes I made to BluetoothSerialPort :

  • Add a "Persistent" property, when set to true the virtual serial port is not deleted in the destructor
  • Add a DeletePersistentSerialPort functions which allows to delete the serial port whose name is passed as a parameter
  • Add an Persistent option in CreateClient function allowing to create a virtual port without deleting it in the destructor

So now it is possible to create and delete a virtual port without the need of a reset.

The last change was in the SetServiceState of WindowsBluetoothDeviceInfo class. For serial port service type, this function called the CreateClient of BluetoothSerialPort, that was OK, but the virtual serial port was deleted when the BluetoothSerialPort object was destroyed. So I removed the call to CreateClient in that function.

Now if you want to create a virtual serial port you have to call by yourself the CreateClient function with "persistent" parameter set to true.

I can give the new BluetoothSerialPort class if somebody need it, but I have preferred to post in the right place for code changes.

I hope this will help

Greetings

J-D Gasser

Aug 24, 2012 at 1:33 PM
Edited Aug 24, 2012 at 2:09 PM

J-D Gasser,

   If you could post code, that would be awesome. This would save a lot of hassle for us.

Thanks,
Tom

Aug 27, 2012 at 3:25 PM
Edited Aug 27, 2012 at 3:29 PM

Hello teynon,

sorry for the late answer, I was not working on that project.

I made some changes to my code since last post, it works for your use, but I'm facing another problem now (see next post).

Here is the full BluetoothSerialPort (it is in that class I made the main changes) :

 

// 32feet.NET - Personal Area Networking for .NET
//
// InTheHand.Net.Ports.BluetoothSerialPort
// 
// Copyright (c) 2003-2008 In The Hand Ltd, All rights reserved.
// This source code is licensed under the In The Hand Community License - see License.txt

using System;
using System.Net;
using System.ComponentModel;
using System.Runtime.InteropServices;
using InTheHand.Net.Bluetooth;
using InTheHand.Net;

#if NETCF

namespace InTheHand.Net.Ports
{
	/// <summary>
	/// Represents a virtual COM port.
	/// </summary>
    /// <remarks>Deprecated. Use <see cref="M:InTheHand.Net.Sockets.BluetoothDeviceInfo.SetServiceState(System.Guid,System.Boolean)"/> 
    /// to enable a virtual COM port.
    /// <para>Supported on Windows CE Only.
    /// </para>
    /// </remarks>
	public class BluetoothSerialPort : IDisposable
	{
    #region Members Definition
    private static int m_nNewID = 0;

    private const uint IOCTL_BLUETOOTH_GET_RFCOMM_CHANNEL = 0x1B0060;
    private const uint IOCTL_BLUETOOTH_GET_PEER_DEVICE    = 0x1B0064;
    private const uint GENERIC_READ                       = 0x80000000;
    private const uint GENERIC_WRITE                      = 0x40000000;
    private const uint OPEN_EXISTING                      = 0x00000003;

    private string            portPrefix;
    private int               portIndex;
    private PORTEMUPortParams pep;
    private IntPtr            handle;
    private int               m_nID = -1;
    #endregion

    #region Properties interface
		/// <summary>
		/// The full representation of the port name e.g. &quot;COM5&quot;
		/// </summary>
		public string PortName
		{
			get { return(portPrefix + portIndex.ToString()); }
		}
		/// <summary>
		/// The address of the remote device to which this port will connect (Client Ports only).
		/// </summary>
		public BluetoothAddress Address
		{
			get
			{
				return new BluetoothAddress(pep.device);
			}
		}
		/// <summary>
		/// The Bluetooth service to connect to.
		/// </summary>
		public Guid Service
		{
			get
			{
				return pep.uuidService;
			}
        }
    /// <summary>
		/// Gets whether the port is a local service or for outgoing connections.
		/// </summary>
		/// <value>TRUE for a server port that accepts connections, or to FALSE for a client port that is used to creating outgoing connections.</value>
		public bool Local
		{
			get
			{
				return pep.flocal;
			}
    }
    /// <summary>
    /// Native handle to virtual port.
    /// </summary>
    public IntPtr Handle
    {
      get
      {
        return this.handle;
      }
    }
    #endregion
    
    #region Constructor / Destructor
		internal BluetoothSerialPort(string portPrefix, int portIndex)
		{

      m_nID = m_nNewID;

      m_nNewID ++;

			pep = new PORTEMUPortParams();
      
      pep.uiportflags = RFCOMM_PORT_FLAGS.REMOTE_DCB;

      this.portPrefix = portPrefix;
      this.portIndex  = portIndex;

      System.Diagnostics.Trace.WriteLine(string.Format("BluetoothSerialPort with ID [{0}] created", m_nID));
		}
    #endregion
    
    #region Public Functions
    /// <summary>
    /// Create a virtual server port to listen for incoming connections.
    /// </summary>
    /// <param name="portName">Port name e.g. "COM4"</param>
    /// <param name="service">Bluetooth service to listen on.</param>
    /// <returns></returns>
    public static BluetoothSerialPort CreateServer(string portName, Guid service)
    {
      string portPrefix;
      int portIndex;
      SplitPortName(portName, out portPrefix, out portIndex);

      BluetoothSerialPort bsp = new BluetoothSerialPort(portPrefix, portIndex);
      bsp.pep.flocal = true;
      bsp.pep.uuidService = service;
      bsp.Register();
      return bsp;
    }
    /// <summary>
    /// Create a virtual server port to listen for incoming connections. Auto allocates a port from the COM0-9 range.
    /// </summary>
    /// <param name="service">Service GUID to listen on.</param>
    /// <returns></returns>
    public static BluetoothSerialPort CreateServer(Guid service)
    {
      BluetoothSerialPort bsp = new BluetoothSerialPort("COM", 9);
      bsp.pep.flocal = true;
      bsp.pep.uuidService = service;
      
      for (int iPort = 8; iPort > -1; iPort--)
      {
        try
        {
          bsp.Register();
          break;
        }
        catch
        {
          bsp.portIndex = iPort;
        }
      }
      
      if (bsp.portIndex == 0)
      {
        throw new SystemException("Unable to create a Serial Port");
      }
      
      return bsp;
    }
    /// <summary>
    /// Create a client port for connection to a remote device.
    /// </summary>
    /// <param name="portName">Port name e.g. "COM4"</param>
    /// <param name="endPoint">Remote device to connect to</param>
    /// <returns>A BluetoothSerialPort.</returns>
    public static BluetoothSerialPort CreateClient(string portName, BluetoothEndPoint endPoint)
    {
      string portPrefix;
      int portIndex;
      
      SplitPortName(portName, out portPrefix, out portIndex);
      
      BluetoothSerialPort bsp = new BluetoothSerialPort(portPrefix, portIndex);
      
      bsp.pep.flocal = false;
      bsp.pep.device = endPoint.Address.ToInt64();
      bsp.pep.uuidService = endPoint.Service;

      bsp.Register();

      return bsp;
    }
    /// <summary>
    /// Create a client port for connection to a remote device.  Auto allocates a port from the COM0-9 range.
    /// </summary>
    /// <param name="endPoint">Remote device to connect to.</param>
    /// <returns></returns>
    public static BluetoothSerialPort CreateClient(BluetoothEndPoint endPoint)
    {
      BluetoothSerialPort bsp = new BluetoothSerialPort("COM", 9);
      bsp.pep.flocal = false;
      bsp.pep.device = endPoint.Address.ToInt64();
      bsp.pep.uuidService = endPoint.Service;

      for (int iPort = 8; iPort > -1; iPort--)
      {
        try
        {
          bsp.Register();
          break;
        }
        catch
        {
          bsp.portIndex = iPort;
        }
      }

      if (bsp.portIndex == 0)
      {
        throw new SystemException("Unable to create a Serial Port");
      }

      return bsp;
    }
    /// <summary>
    /// Creates a virtual serial port on the mobile device
    /// </summary>
    /// <param name="p_strPortName">Name of the port to create (e.g. &quot;COM5&quot;)</param>
    /// <param name="p_bepEndPoint">Endpoint representing the Bluetooth connection</param>
    public static void CreateVirtualPort(string p_strPortName, BluetoothEndPoint p_bepEndPoint)
    {
      string l_strPortPrefix;
      int    l_nPortIndex;

      SplitPortName(p_strPortName, out l_strPortPrefix, out l_nPortIndex);

      PORTEMUPortParams l_pepParams = new PORTEMUPortParams();

      //l_pepParams.uiportflags = RFCOMM_PORT_FLAGS.

      l_pepParams.flocal = false;
      l_pepParams.device = p_bepEndPoint.Address.ToInt64();
      l_pepParams.uuidService = p_bepEndPoint.Service;

      System.Diagnostics.Trace.WriteLine(string.Format(
        "Register serial port {0}{1} for device with address [{2}] using service {3}", 
        l_strPortPrefix, l_nPortIndex, p_bepEndPoint.Address, p_bepEndPoint.Service));
      
      IntPtr l_nHandle = RegisterDevice(l_strPortPrefix, l_nPortIndex, "btd.dll", ref l_pepParams);

      System.Diagnostics.Debug.WriteLine(string.Format("RegisterDevice handle: {0}", l_nHandle));

      if(l_nHandle == IntPtr.Zero)
		  {
			  int l_nWin32Error = Marshal.GetLastWin32Error();
        System.Diagnostics.Debug.WriteLine(string.Format("GetLastWin32Error: {0}", l_nWin32Error));
      
        throw(new Win32Exception(l_nWin32Error, "Error creating virtual com port"));
      }
      else
        CloseHandle(l_nHandle);
    }
    /// <summary>
    /// Deletes the virtual serial port from the mobile device
    /// </summary>
    /// <param name="p_strPortName">Name of the port to remove (e.g. &quot;COM5&quot;)</param>
    public static void RemoveVirtualPort(string p_strPortName)
    {
      //================================================================================�
      // FIND THE HANDLE OF THE VIRTAUL SERIAL PORT                                     �
      IntPtr l_nDeviceHandle = INVALID_HANDLE_VALUE;

      //--------------------------------------------------------------------------------�
      // Preapares the FindFirst/NextDevice operations                                  �
      DeviceSearchType l_dstSearchType = DeviceSearchType.DeviceSearchByLegacyName;

      DEVMGR_DEVICE_INFORMATION l_ddiDeviceInfo = new DEVMGR_DEVICE_INFORMATION();
      
      l_ddiDeviceInfo.dwSize = (uint) Marshal.SizeOf(l_ddiDeviceInfo);

      string l_strSearchString = "COM*";
      IntPtr l_nSearchParam = Marshal.StringToBSTR(l_strSearchString);
      
      //--------------------------------------------------------------------------------�
      // FindFirstDevice                                                                �
      IntPtr l_nSearchHandle = FindFirstDevice(l_dstSearchType, l_nSearchParam, ref l_ddiDeviceInfo);

      //................................................................................�
      // Error handling                                                                 �
      if(l_nSearchHandle == INVALID_HANDLE_VALUE)
      {
        throw(new SystemException(
          string.Format("Error {0:X} while searching for virtual serial port {1}", 
          Marshal.GetLastWin32Error(), p_strPortName)));
      }

      //--------------------------------------------------------------------------------�
      // Device found : no need to search further                                       �
      if(DeviceMatchSerialPort(l_ddiDeviceInfo, p_strPortName))
      {
        l_nDeviceHandle = l_ddiDeviceInfo.hDevice;
      }
      //--------------------------------------------------------------------------------�
      // Device not found : search further with FindNextDevice                          �
      else
      {
        while(FindNextDevice(l_nSearchHandle, ref l_ddiDeviceInfo))
        {
          //............................................................................�
          // Error handling                                                             �
          if(l_ddiDeviceInfo.hDevice == INVALID_HANDLE_VALUE)
          {
            throw(new SystemException(
              string.Format("Error {0:X} while searching for virtual serial port {1}", 
              Marshal.GetLastWin32Error(), p_strPortName)));
          }

          //............................................................................�
          // Device found : gets the handle and exit the while loop                     �
          if(DeviceMatchSerialPort(l_ddiDeviceInfo, p_strPortName))
          {
            l_nDeviceHandle = l_ddiDeviceInfo.hDevice;

            break;
          }
        }
      }

      //................................................................................�
      // Close FindFirst/NextDevice handle                                              �
      FindClose(l_nSearchHandle);

      CloseHandle(l_nSearchHandle);

      //--------------------------------------------------------------------------------�
      // Device not found, no need to deregister                                        �
      if(l_nDeviceHandle == INVALID_HANDLE_VALUE) 
      {
        System.Diagnostics.Debug.WriteLine(string.Format(
          "Serial port {0} not found in persistent virtual ports", p_strPortName));
        
        return;
      }

      //================================================================================�
      // CALLS DEREGISTERDEVICE TO CLEAR SERIAL PORT                                    �
      bool l_bSucceed = DeregisterDevice(l_nDeviceHandle);

      //--------------------------------------------------------------------------------�
      // Error handling                                                                 �
      if(!l_bSucceed)
      {
        throw(new SystemException(
          string.Format("Error {0:X} while deregistering virtual serial port {1}", 
          Marshal.GetLastWin32Error(), p_strPortName)));
      }
      else
      {
        System.Diagnostics.Debug.WriteLine(string.Format("Deregister of {0} succeed.", 
          p_strPortName));
      }

      CloseHandle(l_nDeviceHandle);
    }
    /// <summary>
    /// Creates a BluetoothSerialPort instance from an existing open virtual serial port handle.
    /// </summary>
    /// <param name="handle">Handle value created previously by BluetoothSerialPort.</param>
    /// <returns>BluetoothSerialPort wrapper around handle.</returns>
    public static BluetoothSerialPort FromHandle(IntPtr handle)
    {
      BluetoothSerialPort bsp = new BluetoothSerialPort("COM", 0);
      
      bsp.handle = handle;
      
      return bsp;
    }
    /// <summary>
		/// Closes the virtual serial port releasing all resources.
    /// If the port is marked as persistent then the virtual serial port will stay alive.
		/// </summary>
		public void Close()
		{
			GC.KeepAlive(this);

			if(handle != IntPtr.Zero)
			{
				bool success = true;
        
        success = DeregisterDevice(handle);

				if(success)
				{
					handle = IntPtr.Zero;
				}
				else
				{
					throw new SystemException("Error deregistering virtual COM port " + Marshal.GetLastWin32Error().ToString("X"));
				}
			}
    }
    #endregion
    
    #region Private Functions
    private static void SplitPortName(string portName, out string prefix, out int index)
    {
      if (portName.Length < 4) throw(new ArgumentException("Invalid Port Name"));
        
      prefix = portName.Substring(0, 3);

      index = Int32.Parse(portName.Substring(3,1));
    }
    private static bool DeviceMatchSerialPort(DEVMGR_DEVICE_INFORMATION p_ddiDevice, string p_strPortName)
    {
      string l_strDevicePortName = p_ddiDevice.szLegacyName.Trim().TrimEnd(':');

      return(string.Compare(l_strDevicePortName, p_strPortName, true, 
        System.Globalization.CultureInfo.InvariantCulture) == 0);
    }
		private void Register()
		{
		  GC.KeepAlive(this);

      System.Diagnostics.Trace.WriteLine(string.Format("Register serial port: {0}{1}", portPrefix, portIndex));

      handle = RegisterDevice(portPrefix, portIndex, "btd.dll", ref pep);
      System.Diagnostics.Debug.WriteLine(string.Format("RegisterDevice handle: {0}", handle));

      if(handle == IntPtr.Zero)
		  {
			  int l_nWin32Error = Marshal.GetLastWin32Error();
        System.Diagnostics.Debug.WriteLine(string.Format("GetLastWin32Error: {0}", l_nWin32Error));
      
        throw(new Win32Exception(l_nWin32Error, "Error creating virtual com port"));
      }
		}
    #endregion

    #region P/Invokes
    private static IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
    private struct DEVMGR_DEVICE_INFORMATION
    {
      public UInt32 dwSize;
      public IntPtr hDevice;
      public IntPtr hParentDevice;

      [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 6)]
      public string szLegacyName;

      [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
      public string szDeviceKey;

      [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
      public string szDeviceName;

      [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
      public string szBusName;
    }

    private enum DeviceSearchType : int
    {
    DeviceSearchByLegacyName = 0,
    DeviceSearchByDeviceName = 1,
    DeviceSearchByBusName = 2,
    DeviceSearchByGuid = 3,
    DeviceSearchByParent = 4
    }

    [DllImport("coredll.dll", SetLastError = true)]
    private static extern IntPtr CreateFile(
      string lpFileName, 
      uint dwDesiredAccess, 
      uint dwShareMode, 
      IntPtr lpSecurityAttributes, 
      uint dwCreationDisposition, 
      int dwFlagsAndAttributes, 
      IntPtr hTemplateFile);

    [DllImport("coredll.dll", SetLastError = true)]
    private static extern IntPtr RegisterDevice(
      string lpszType,
      int dwIndex,
      string lpszLib,
      ref PORTEMUPortParams dwInfo);
    
    [DllImport("coredll.dll", SetLastError=true)]
    private static extern IntPtr RegisterDevice(
      string lpszType,
      int	dwIndex,
      string lpszLib,
      byte[]	dwInfo);

    [DllImport("coredll.dll", SetLastError=true)]
    private static extern bool DeregisterDevice(
      IntPtr handle);

    [DllImport("coredll.dll", SetLastError = true)]
    private static extern bool DeviceIoControl(
      IntPtr hDevice, 
      int dwIoControlCode, 
      byte[] lpInBuffer, 
      int nInBufferSize, 
      out UInt32 lpOutBuffer, 
      UInt32 nOutBufferSize, 
      out UInt32 lpBytesReturned, 
      int lpOverlapped);
    
    [DllImport("coredll.dll", SetLastError = true, CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Auto)]
    private static extern bool CloseHandle(IntPtr hObject);
    
    [DllImport("coredll.dll", SetLastError = true)]
    private static extern IntPtr FindFirstDevice(DeviceSearchType
      searchType, IntPtr searchParam, ref DEVMGR_DEVICE_INFORMATION pdi);

    [DllImport("coredll.dll", SetLastError = true)] 
    private static extern bool FindNextDevice(IntPtr searchHandle, ref DEVMGR_DEVICE_INFORMATION pdi); 

    [DllImport("coredll.dll", SetLastError = true)] 
    private static extern bool FindClose(IntPtr searchHandle); 
    #endregion

    #region IDisposable Members

    /// <summary>
    /// 
    /// </summary>
    /// <param name="disposing"></param>
    protected virtual void Dispose(bool disposing)
    {
      try
      {
        Close();
      }
      catch { }

      if(disposing)
      {
        portPrefix = null;
      }
      
      System.Diagnostics.Trace.WriteLine(string.Format("BluetoothSerialPort with ID [{0}] disposed", m_nID));
    }

    /// <summary>
    /// 
    /// </summary>
    public void Dispose()
    {
      Dispose(true);
      GC.SuppressFinalize(this);
    }

    /// <summary>
    /// 
    /// </summary>
    ~BluetoothSerialPort()
    {
      Dispose(false);

    }
    #endregion
	}
}
#endif

 

...and you also have to change the WindowsBluetoothDeviceInfo class, please comment out the part of code after "//Try to open port now" of the SetServiceState function :

//try open port now
/*
try
{
  using(InTheHand.Net.Ports.BluetoothSerialPort l_bspSerialClient = InTheHand.Net.Ports.BluetoothSerialPort.CreateClient(availablePorts[availablePorts.Count - 1], new BluetoothEndPoint(this.DeviceAddress, BluetoothService.SerialPort)))
  {
    l_bspSerialClient.Close();
  }
} 
catch(Exception ex)
{
  Utils.MiscUtils.Trace_WriteLine("SetServiceState failure on BluetoothSerialPort.CreateClient: " + ex);
}
*/

Now use the static functions called "CreateVirtualPort" and "RemoveVirtualPort" of BluetoothSerialPort to create and remove the serial port.

If you don't know the serial port number associated to one Bluetooth device, please read it in the registry at "HKLM\Software\Microsoft\Bluetooth\Serial\Ports".

I hope this will help.

Greetings

J-D Gasser

Aug 27, 2012 at 4:14 PM
Edited Aug 27, 2012 at 4:18 PM

Hello everybody,

I'm facing another problem now.

My goal is to :

  1. Remove the virtual serial port created for the Bluetooth device
  2. Remove the paired Bluetooth device
  3. Find a new Bluetooth device
  4. Pair the devic
  5. Create the virtual serial port for that device

So, each time a user click on one button :

  1. Select the paired Bluetooth device using l_bcClient.DiscoverDevices(255, false, true, false, false)
  2. Remove virtual port using BluetoothSerialPort.RemoveVirtualPort
  3. Delete Bluetooth serial port service using BleutoothDeviceInfo.SetServiceState(BluetoothService.SerialPort, false);
  4. Remove pair using BluetoothSecurity.RemoveDevice
  5. Look for the new Bluetooth devices using BluetoothComponent.DiscoverDevicesAsync(255, true, false, true, true, ...)
  6. Pair the device using BluetoothSecurity.PairRequest(BluetoothDeviceInfo.DeviceAddress, "TheKey");
  7. Activate the serial port service using BluetoothDeviceInfo.SetServiceState(BluetoothService.SerialPort, true)
  8. Create the virtual Bluetooth serial port using BluetoothSerialPort.CreateVirtualPort

So everything works perfectly the two first time I click on the button, the third time I click on the button, the call of  RegisterDevice in BluetoothSerialPort.CreateVirtualPort returns error code 110 (ERROR_OPEN_FAILED).

@teynon : If your application creates the virtual port and then quit, then you can use my code without problem, the problem appears only if you try to create virtual port more than 2 times in the run of the application.

If I quit my program and restart the operation then everything works good until the third time I click on the button.

I think an object doesn't release the serial port, but I don't know which one.

Can somebody help me ?

Greetings

J-D Gasser

Jan 21, 2014 at 11:30 AM
hi Jean,

I just want to inquire wether you were able to find a solution regarding the error 110 when triggering the registerdevice? thank you


micky
Jan 21, 2014 at 11:43 AM
Hello,
unfortunately I did not found a solution to that problem. The only thing I did was to display a message telling the user that my program will reset the device and force de device to reset programmatically if it encountered the 110 error.

Greetings

J-D Gasser