NETMF Port USBClient connection detection (fixed - sample code included)

In the original NETMF code we polled the Microsoft.SPOT.Hardware.UsbClient.UsbController.PortState for state Running to detect when a computer was connected to the USB Client interface.

In TinyCLR we dont have that state? (we dont have port state, only device state)

namespace GHIElectronics.TinyCLR.Devices.UsbClient
{
public enum DeviceState
{
Detached = 0,
Attached = 1,
Powered = 2,
Default = 3,
Address = 4,
Configured = 5,
Suspended = 6
}
}

What would be a correct way to detect when a connection is made?

Configured = 5

It looks like this is changing state only if you AttachLogicalUnit and enable, so this works:

        var sd = StorageController.FromName(SC20260.StorageController.SdCard);
        var usbclientController = UsbClientController.GetDefault();
        var usbclient_masstorage = new MassStorage(usbclientController);
        usbclient_masstorage.AttachLogicalUnit(sd.Hdc);
        usbclient_masstorage.Enable();	

But i want the sd card as logging storage as long that there is no cable attached.
In NETMF this worked like:

        MyUSBClient = new MassStorage(0x1B9F, 0xF002, 0x100, 250, "Leclanche", "MFC Controller", "2.1", "", 1);
		Controller.ActiveDevice = MyUSBClient;
		
		//This check is in a low priority polling thread
		if (Controller.State == UsbController.PortState.Running && MyUSBClientLoaded == 0)
		{
			MyUSBClient.AttachLogicalUnit(MySD, 0, "Leclanché", "MFC SD");
			MyUSBClient.EnableLogicalUnit(0);
		}

Yes, that works. There is an event, look at our example:

USB Client (ghielectronics.com)

We are releasing SDK soon, let us know any other problem if you see :))

A small check, but the event is not triggered like this?

using System.Threading;
using System.Diagnostics;
using GHIElectronics.TinyCLR.Pins;
using GHIElectronics.TinyCLR.Devices.Storage;
using GHIElectronics.TinyCLR.Devices.UsbClient;

namespace Test_USB_sample_code
{
    public class Startup
    {
        private static StorageController Mysd;
        private static UsbClientController MyusbclientController;
        private static MassStorage usbclient_masstorage;

        static void Main()
        {
            Mysd = StorageController.FromName(SC20260.StorageController.SdCard);
            MyusbclientController = UsbClientController.GetDefault();

            usbclient_masstorage = new MassStorage(MyusbclientController);
            usbclient_masstorage.DeviceStateChanged += Usbclient_masstorage_DeviceStateChanged; 

            while (true)
            {
                if (usbclient_masstorage.DeviceState == DeviceState.Configured)
                {
                    Debug.WriteLine("Connected");
                    Thread.Sleep(500);
                }
                Debug.WriteLine("Not Connected");
                Thread.Sleep(500);
            }
        }

        private static void Usbclient_masstorage_DeviceStateChanged(RawDevice sender, DeviceState state)
        {
            if (state == DeviceState.Configured)
            {
                Debug.WriteLine("Attach sd");
                usbclient_masstorage.AttachLogicalUnit(Mysd.Hdc);
                usbclient_masstorage.Enable();
            }
            else
            {
                Debug.WriteLine("Detach sd");
                usbclient_masstorage.Disable();
                usbclient_masstorage.RemoveLogicalUnit(Mysd.Hdc);
            }
        }
    }
}

You need Enable() to start USBClient. To enable you need add AttachLogicalUnit.

Whenever you want to work with PC or listen event from PC, need to be in enabled state.
Whenever you want to work with SITCore File System, need to be in disabled state.

As I understand, your code should become like this:

        private static StorageController Mysd;
        private static UsbClientController MyusbclientController;
        private static MassStorage usbclient_masstorage;
        static void DoTestSD2()
        {
            Mysd = StorageController.FromName(SC20260.StorageController.SdCard);
            MyusbclientController = UsbClientController.GetDefault();

            usbclient_masstorage = new MassStorage(MyusbclientController);
            usbclient_masstorage.DeviceStateChanged += Usbclient_masstorage_DeviceStateChanged;

            usbclient_masstorage.AttachLogicalUnit(Mysd.Hdc);
            usbclient_masstorage.Enable();

            while (true)
            {
                if (usbclient_masstorage.DeviceState == DeviceState.Configured)
                {
                    // Masstorage connect and ready
                    connected = true;

                    Debug.WriteLine("Connected");
                }
                else
                {
                    // If cable was disconnected
                    if (connected) // was connected
                    {
                        connected = false;
                        Debug.WriteLine("Disconnecting....");
                        usbclient_masstorage.Disable(); // No more event

                        // Mount file system
                        //....
                        // Umount system

                        usbclient_masstorage.Enable(); // Enable again to listen event for state changed.

                    }
                    else
                    {
                        // Do something else
                        
                    }
                }

                Thread.Sleep(1000);

            }
        }

        static bool connected = false;
        private static void Usbclient_masstorage_DeviceStateChanged(GHIElectronics.TinyCLR.Devices.UsbClient.RawDevice sender, DeviceState state)
      {
            

            
     }

So is Sitcore is using the sd card as logging device, it’s not possible to detect the connection plugged in between device and computer? Is this correct?

If this is the case, can we like AttachLogicalUnit with a “dummy” or so just to get the detection and replace this with sd.hdc after?

when you enable(), event /state will be changed when cable is connected (5) or disconnected (6). So I am not sure what you mean “not possible”.

If you mean insert SD card detection then that will be different story.

No, it has to be xxx.hdc., and usually you just call this once and no need to remove or attach again.

The main question is: are we able to have a streamwriter open to a log file (from the main sitcore application) on the sd card and log data after:

        usbclient_masstorage.AttachLogicalUnit(Mysd.Hdc);
        usbclient_masstorage.Enable();

Basicly the sitcore appliaction is active on multiple interfaces, it’s logging data to the sd card during that process. At some point you have a service engineer on site that needs to extract this data, and if needed deploy updates over the USB Client interface. The only interface availible to the service engineer is the USB Client connection, so detecting service engineer that is connecting his laptop is the trigger we have to be able to implement. But before this event the sd card must be availible for logging.

No, you can’t open file stream after called Enable().

And we can not detect the connection whithout attaching the logical unit on Sitcore?

On NETMF this was working fine.

No,

Can I understand NetMF you can open file when MSC is connecting to PC?

On NETMF you can detect the connection while the logical unit is not attached.
Basicly this is the stripped down NETMF code:

        MyUSBClient = new MassStorage(0x1B9F, 0xF002, 0x100, 250, "Leclanche", "MFC Controller", "2.1", "", 1);
		Controller.ActiveDevice = MyUSBClient;
		
		//This check is in a low priority polling thread
		if (Controller.State == UsbController.PortState.Running && MyUSBClientLoaded == 0)
		{
			MyUSBClient.AttachLogicalUnit(MySD, 0, "Leclanché", "MFC SD");
			MyUSBClient.EnableLogicalUnit(0);
		}

Yes, it is different.

In netmf I remember start() somewhere before, then enable().

TinyCLR has only Enable() and Disable() and, we can say that Enable() = (start + enable) in netmf.

Can you have USB running as something else (eg CDC) and then switch it to Mass Storage programmatically once you see a connection?
We do this with NetMF, I’m not familiar enough with TinyCLR to know if it is possible.

:index_pointing_at_the_viewer: Thanks C_Born, this is something that actualy will work !

1 Like

If anyone has the same issue, this i a workaround using a dummy mouse interface.

using System.Threading;
using System.Diagnostics;
using GHIElectronics.TinyCLR.Pins;
using GHIElectronics.TinyCLR.Devices.Storage;
using GHIElectronics.TinyCLR.Devices.UsbClient;

namespace Test_USB_sample_code
{
    public class Startup
    {
        private static bool Connected;
        private static StorageController Mysd;
        private static UsbClientController MyusbclientController;
        private static MassStorage usbclient_masstorage;
        private static Mouse usbclient_mouse;

        static void Main()
        {
            //Use a flag to know the state
            Connected = false;

            //SD Card
            Mysd = StorageController.FromName(SC20260.StorageController.SdCard);

            //Active mouse as dummy device
            MyusbclientController = UsbClientController.GetDefault();
            usbclient_mouse = new Mouse(MyusbclientController,true);
            usbclient_mouse.DeviceStateChanged += Usbclient_mouse_DeviceStateChanged;
            usbclient_mouse.Enable();

            while (true)
            {
                Thread.Sleep(500);
            }
        }

        private static void Usbclient_mouse_DeviceStateChanged(RawDevice sender, DeviceState state)
        {
            Debug.WriteLine("Dummy mouse state change to " + state.ToString());

            if (state == DeviceState.Configured)
            {
                Debug.WriteLine("USB cable connected");

                //disable mouse interface and unsubscribe the event handler
                usbclient_mouse.Disable();
                usbclient_mouse.DeviceStateChanged -= Usbclient_mouse_DeviceStateChanged;
                usbclient_mouse.Dispose();
                    
                //close files and flush all data to SD card + set flag to switch to memory logging
                

                //create massstorage interface
                MyusbclientController = UsbClientController.GetDefault();
                usbclient_masstorage = new MassStorage(MyusbclientController);
                usbclient_masstorage.DeviceStateChanged += Usbclient_masstorage_DeviceStateChanged;

                usbclient_masstorage.AttachLogicalUnit(Mysd.Hdc);
                usbclient_masstorage.Enable();
            }
        }

        private static void Usbclient_masstorage_DeviceStateChanged(RawDevice sender, DeviceState state)
        {
            Debug.WriteLine("massstorage state change to " + state.ToString() );
            
            //connected and mounted
            if (state == DeviceState.Configured )
            {
                //set the flag
                Connected = true;
                Debug.WriteLine("Flag Connected = true");
            }

            //state = suspended and connected => cable disconnected
            if (state == DeviceState.Suspended && Connected == true)
            {
                Debug.WriteLine("USB cable disconnected");

                //re-set the flag
                Connected = false;
                Debug.WriteLine("Flag Connected = false");

                //close and dispose the massstorage interface
                usbclient_masstorage.Disable();
                usbclient_masstorage.DeviceStateChanged -= Usbclient_masstorage_DeviceStateChanged;
                usbclient_masstorage.RemoveLogicalUnit(Mysd.Hdc); 
                usbclient_masstorage.Dispose();

                //re-connect logging class to SD card


                //Active mouse as dummy device
                MyusbclientController = UsbClientController.GetDefault();
                usbclient_mouse = new Mouse(MyusbclientController, true);
                usbclient_mouse.DeviceStateChanged += Usbclient_mouse_DeviceStateChanged;
                usbclient_mouse.Enable();
            }
        }
    }
}


2 Likes