Detecting a USB Connection to choose between VCOM, mass storage or code

Ive coded an application for a Panda II that automatically switches between opening a VCOM port, going to mass storage, or dropping into a data logging mode. My question is if there is a better or more efficient way than what Ive chosen, or is there a potential flaw in this approach. The sequence of how the code works is as follows:

[ol]1. On power up the program tests for a USB client connection by starting mass storage and then testing the state to see if its running.

  1. If the state test doesnt return a running condition, power is coming from Vin (not USB) and the code stops mass storage and jumps to the data logger code.

  2. If the state test shows mass storage is running, it will turn off mass storage and open a cdc VCOM port to see if a user opens a terminal session to interact with the system.

  3. If the VCOM sees no input within a set period of time, VCOM is turned off and the system reopens mass storage so the user can access files on the SD card.[/ol]

I know there are other ways to accomplish this such as just opening a VCOM port and having a user choose what happens next, but for this application there is a need to have an auto start sequence.

Im also having a problem in that periodically on power up the Panda II doesnt even get to the first step in the code and just hangs. I think that is a separate topic though and Ill post it later ion another thread unless someone has some input now.

The code segment that accomplishes this is as follows. Ive deleted large sections of code that arent relevant to my question to make this more readable. Thanks in advance for any input.

using System;
using System.Text;
using System.IO;
using System.Threading;
using System.Collections;

using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
using Microsoft.SPOT.IO;

using GHIElectronics.NETMF.FEZ;
using GHIElectronics.NETMF.Hardware;
using GHIElectronics.NETMF.IO;
using GHIElectronics.NETMF.System;
using GHIElectronics.NETMF.USBClient;
using GHIElectronics.NETMF.Hardware.LowLevel;

namespace PandaIIswitching
{
    public class Program
    {
        public static void Main()
        {
            Debug.Print("Starting code execution");
            Utility.SetLocalTime(RealTimeClock.GetTime());
            // Set up leds for later use by data logger code
            OutputPort led = new OutputPort((Cpu.Pin)FEZ_Pin.Digital.Di52, true);
            OutputPort bled = new OutputPort((Cpu.Pin)FEZ_Pin.Digital.LED, false);
            Debug.Print(DateTime.Now.ToString());
            // Check debug interface
            if (Configuration.DebugInterface.GetCurrent() == Configuration.DebugInterface.Port.USB1)
                throw new InvalidOperationException("Current debug interface is USB. It must be changed to something else before proceeding. Refer to your platform user manual to change the debug interface.");
            // START TESTING FOR USB CONNECTION TO SEE IF THE PANDA II IS BEING POWERED BY USB CLIENT CONNECTION
            Debug.Print("OK, now starting ms for sdcard. will see if it is connected  ");
            USBC_MassStorage ms = USBClientController.StandardDevices.StartMassStorage();
            PersistentStorage sd;
            try
            {
                sd = new PersistentStorage("SD");
            }
            catch
            {
                throw new Exception("SD card not detected");
            }
            ms.AttachLun(0, sd, " ", " ");
            ms.EnableLun(0);
            Debug.Print("Attached and enabled SD Card & will see if it's running");
            USBClientController.State state = USBClientController.GetState();
            Debug.Print("State " + state.ToString());
            if (state == USBClientController.State.Running)
            {
                bled.Write(true);  // visual indication that execution got to this point
                Debug.Print("USBClientController.State is Running.  will disable ms & attempt VCOM");
                ms.DisableLun(0); 
                sd.UnmountFileSystem();
                Thread.Sleep(2000);
                sd.Dispose();
                USBClientController.Stop();
                Thread.Sleep(1000);
                // Start CDC 
                USBC_CDC cdc = USBClientController.StandardDevices.StartCDC();
                try
                {
                    // FOR CLARITY A LARGE SECTION OF CODE TO HANDLE TERMINAL INPUT WAS DELETED HERE
                    // THE IMPORTANT FEATURE IS THAT IF THERE'S NO INPUT WITHIN SO MANY SECONDS THE
                    // EXECUTION JUMPS BACK TO CREATE A MASS STORAGE DEVICE
                    goto sdcard;
                }
                catch
                {
                    // ANY TERMINAL INPUT ERRORS ARE HANDLED HERE
                }
            sdcard:
                // Now reinstate mass storage if VCOM was never used
                USBClientController.Stop();
                Thread.Sleep(200);
                Debug.Print("OK, now starting ms2 for sdcard. will see if it is connected  ");
                USBC_MassStorage ms2 = USBClientController.StandardDevices.StartMassStorage();
                // Assume SD card is connected
                PersistentStorage sd2;
                try
                {
                    sd2 = new PersistentStorage("SD");
                }
                catch
                {
                    throw new Exception("SD card not detected");
                }
                ms.AttachLun(0, sd2, " ", " "); 
                ms.EnableLun(0);
                Debug.Print("Attached and enabled SD Card & will see if it's running");
                USBClientController.State state2 = USBClientController.GetState();
                Debug.Print("State2 " + state2.ToString());
              if (state2 == USBClientController.State.Running)
                {
                    Debug.Print("USBClientController.State is Running.  You now have a mass storage device");
                    Thread.Sleep(Timeout.Infinite);
                }
            }
            else
            {
                ms.DisableLun(0); 
                
                sd.UnmountFileSystem();
                Thread.Sleep(200);
                sd.Dispose();
                
                Debug.Print("SD Card wasn't running - CONTINUE WITH DATA LOGGER");
            }

            // DATA LOGGER CODE IS HERE BUT HAS BEEN DELETED FOR CLARITY
        }
    }
}

A first glance a few things jump out. You don’t need the goto - if your try/catch works, the code will fall through the same path. If you ever feel like you need a goto, that implies code needs refactoring. Use finally blocks with try/catch to explicitly release resources. Don’t wait for GC to do it.

What’s the memory footprint of the app? I’ve had a few odd things happen on a Panda II getting close (10% ish) to max memory, because of GC not cleaning up in a ‘timely’ manner. A recursive method caused the problem…

Thanks for the quick reply EricH. Here’s output from the end of the compile and also the last Garbage Collection. If I read the GC correctly I think there’s over 42 Kbytes available, so no problem, right? Also your comments on goto’s are most appreciated. I did that for expediency as I’m still shifting to the object oriented paradigm. I’ll try catch from now on.

That start up hang really has me puzzled. Here’s the info on memory from my debug output window:

Resolving.

Total: (12092 RAM - 103200 ROM - 54347 METADATA)
AssemblyRef = 144 bytes ( 36 elements)
TypeRef = 1056 bytes ( 264 elements)
FieldRef = 136 bytes ( 34 elements)
MethodRef = 1336 bytes ( 334 elements)
TypeDef = 3000 bytes ( 375 elements)
FieldDef = 1172 bytes ( 579 elements)
MethodDef = 3480 bytes ( 1733 elements)

DebuggingInfo = 1760 bytes

Attributes = 48 bytes ( 6 elements)
TypeSpec = 28 bytes ( 7 elements)
Resources Files = 96 bytes ( 4 elements)
Resources = 320 bytes ( 40 elements)
Resources Data = 1197 bytes
Strings = 12896 bytes
Signatures = 7423 bytes
ByteCode = 32672 bytes

The debugging target runtime is loading the application assemblies and starting execution.
Ready.

GC: 3msec 21648 bytes used, 42732 bytes available
Type 0F (STRING ): 216 bytes
Type 11 (CLASS ): 1908 bytes
Type 12 (VALUETYPE ): 132 bytes
Type 13 (SZARRAY ): 1356 bytes
Type 15 (FREEBLOCK ): 42732 bytes
Type 17 (ASSEMBLY ): 13920 bytes
Type 18 (WEAKCLASS ): 48 bytes
Type 19 (REFLECTION ): 24 bytes
Type 1B (DELEGATE_HEAD ): 216 bytes
Type 1D (OBJECT_TO_EVENT ): 96 bytes
Type 1E (BINARY_BLOB_HEAD ): 468 bytes
Type 1F (THREAD ): 384 bytes
Type 20 (SUBTHREAD ): 48 bytes
Type 21 (STACK_FRAME ): 1176 bytes
Type 27 (FINALIZER_HEAD ): 120 bytes
Type 31 (IO_PORT ): 144 bytes
Type 34 (APPDOMAIN_HEAD ): 72 bytes
Type 36 (APPDOMAIN_ASSEMBLY ): 1320 bytes

On the periodic boot failure problem, is it possibly caused by letting UART RX for COM 1 float as described in this previous thread? If so, is it only COM 1 RX pin that needs pulled up or all COM RX pins?

[url]http://www.tinyclr.com/forum/2/2382/[/url]

Posting to myself it seems, but this may be of use to others with an intermittent boot problem - it took me a while to track the fix down.

As Gus mentioned in the other thread [url]http://www.tinyclr.com/forum/2/2382/[/url] using a pull up or pull down resistor on RX of COM1 does fix this problem.

Not sure it’s worth a new thread since it has been briefly mentioned before, but it can be a frustrating problem since an intermittent failed boot can waste a lot of time.

Maybe there should be a best practice that no unused IO’s be left floating or is that overkill?

I had the same issue ([url]http://tinyclr.com/forum/2/3269/[/url]). I didn’t realize it was a boot failure though. This is something fundamental to the serial debugging interface and should really be included in the various forms of documentation.

[quote]Maybe there should be a best practice that no unused IO’s be left floating or is that overkill?
[/quote]

You should never leave any input floating, A floating input is a big energy waster, not to mention that you can run into larger problems if the input triggers an interrupt (as is the case with the serial input).

Did you test code from first post?
In my case, USB Client state is never ‘Running’ right after EnableLun.
Is that just me?

mass = USBClientController.StandardDevices.StartMassStorage();
Thread.Sleep(200);
mass.AttachLun(0, storage, " ", " ");
mass.EnableLun(0);
var state = USBClientController.GetState(); // State is 'Suspended' (rarely 'Attached')
Thread.Sleep(2000);
state = USBClientController.GetState(); // State is still 'Suspended'
Thread.Sleep(2000);
state = USBClientController.GetState(); // State is 'Running'

[quote]Did you test code from first post?
In my case, USB Client state is never ‘Running’ right after EnableLun.[/quote]

Savvkin, I have tested the code and am still using it daily during the debugging of a larger block of code. I was going to test the snippet you posted to get some quantitative info but my RS232 shield has failed and I’ve been unable to locate a replacement anywhere. I don’t believe a USB debug and mass storage are compatible so I need the RS232 debug.

I think there are some delays in detecting the state, but have not fully characterized them. It seems like it takes many tens of seconds for the FEZ mass storage to be recognized in some cases. Perhaps some enlightenment in this thread?

[url]http://www.tinyclr.com/forum/1/1138/[/url]

You don’t need RS232 shield to debug.
I’m still waiting for the parcel with RS232 too. For now, I just grounded MODE pin and put 7.5K pullup resistor on COM1 RX. Then just blink led several times to show current status :smiley:

Currently I’m starting MassStorage, then waiting until I get Running state.
At this step “Empty cardreader” shows up in windows.
Then I do AttachLun and EnableLun and in less than a second card “inserted” in windows.

Also, this approach helped me to eliminate “Card not formatted” issue.