USB Mass Storage?

I’m trying to get my project to allow me to transfer files from my PC to the SD card in the Spider over the USB client.

I see this line in the docs…

if (Configuration.DebugInterface.GetCurrent() == Configuration.DebugInterface.Port.USB1) punt();

…which always seems to end up as true, even when the debugger isn’t active.

I don’t really care about losing the debugger connection if I’m enabling mass storage so I tried to change things with Configuration.DebugInterface.Set() but that ended up with me having to reburn the firmware - it was quite unhappy.

Am I totally barking up the wrong tree here or do I just need to keep poking at it?

You need to connect mode pin to ground. Please see USB client tutorial.

Uh…

Can you maybe point to which ebook, wiki, or reference web page that tells me where I can find the “mode pin” on a Spider or the USB client module?

Take a look at the inset card that comes in FEZ Spider bag. I think dip switch 4 is connected the mode pin.

Thanks, Joe. I just now spotted that picture in the Spider FW updater, too…

Is this something that’s safe to change while the Spider has power or would that be a stupid idea?

The pin is read only on boot up to determine the mode. Changing it while running does not change the mode or the debugging interface. Keep in mind that you need to access the serial debugging interface at socket 11 of FEZ Spider as I remember and you might need a module like USB-Serial module to access it:
http://www.ghielectronics.com/catalog/product/287

A note on the side: Actually mode pin, at the end, is one of the GPIOs so you can read it in your application.

Thanks!

Yoiks. This was a bit of a pain to figure out!

I fixed that for you and since you asked, here’s my test program which seems to be working well. Not a ton of comments but I did note things that took me a while to figure out.

You need to set switch 4 (leftmost when looking at the Spider with the button above the switches) to the down position and reboot it for the mass storage stuff to work.


using System.Threading;

using Microsoft.SPOT;

using Gadgeteer;
using Gadgeteer.Modules.GHIElectronics;

using GHIElectronics.NETMF.USBClient;
using GHIElectronics.NETMF.Glide;
using GHIElectronics.NETMF.Glide.UI;
using GHIElectronics.NETMF.Hardware;
using GHIElectronics.NETMF.IO;

namespace UsbMassStorage
{
	public partial class Program
	{
		private USBC_MassStorage mMassStorage;

		private Button mButtonPc;
		private TextBox mText;
		private int mCounter = 0;

		private const string GLIDE_XML = "<Glide Version=\"1.0.3\">" +
		"<Window Name=\"instance115\" Width=\"320\" Height=\"240\" BackColor=\"FFFFFF\">" +
		"<Button Name=\"mButtonPc\" X=\"120\" Y=\"16\" Width=\"80\" Height=\"32\" Alpha=\"255\" Text=\"PC Control\" Font=\"3\" FontColor=\"000000\" DisabledFontColor=\"808080\" TintColor=\"000000\" TintAmount=\"0\"/>" +
		"<TextBox Name=\"mText\" X=\"0\" Y=\"80\" Width=\"320\" Height=\"32\" Alpha=\"255\" Text=\"\" TextAlign=\"Left\" Font=\"4\" FontColor=\"000000\"/>" +
		"</Window>/Glide>";

		void ProgramStarted()
		{
			mSdCard.DebugPrintEnabled = true;
			mSdCard.SDCardMounted += new SDCard.SDCardMountedEventHandler(SdCard_SDCardMounted);
			mSdCard.SDCardUnmounted += new SDCard.SDCardUnmountedEventHandler(SdCard_SDCardUnmounted);

			GlideTouch.Initialize();

			Glide.MainWindow = GlideLoader.LoadWindow(GLIDE_XML);

			mText = (TextBox)Glide.MainWindow.GetChildByName("mText");

			mButtonPc = (Button)Glide.MainWindow.GetChildByName("mButtonPc");
			mButtonPc.TapEvent += new OnTap(ButtonPc_TapEvent);

			DisplayCounts();
		}

		void SdCard_SDCardUnmounted(SDCard sender)
		{
			Debug.Print("card unmounted");
		}

		void SdCard_SDCardMounted(SDCard sender, StorageDevice SDCard)
		{
			Debug.Print("card mounted");

			DisplayCounts();
		}

		private void DisplayCounts()
		{
			// make sure there's actually a card in there
			if (!mSdCard.IsCardInserted)
			{
				MessageBox("No card!", "Please insert an SD card and try again");
				return;
			}

			// if it's not mounted, try mounting it then wait a little for it to mount
			if (!mSdCard.IsCardMounted)
			{
				mSdCard.MountSDCard();

				// also wait for the storage device.. not sure that's 100% needed but
				// think i've seen it take a second to come back after the mount
				for (int i = 0; i < 100 && (!mSdCard.IsCardMounted || mSdCard.GetStorageDevice() == null); i++)
					Thread.Sleep(100);
			}

			if (!mSdCard.IsCardMounted)
			{
				MessageBox("Can't mount SD card!", "Unable to mount SD card!");
				return;
			}

			StorageDevice sd = mSdCard.GetStorageDevice();
			if (sd == null)
			{
				// likely are trying to do something without giving time back to the 
				// dispatcher or .net or something.. once we unwind the stack all the
				// way, the storage device will be there
				MessageBox("Can't get StorageDevice!", "There's something wrong with your code");
				return;
			}


			mText.Text = "(" + mCounter++.ToString() + ") Volume: '" + sd.Volume.Name +
				"', dirs = " + sd.ListDirectories(@ "\").Length.ToString() +
				", files = " + sd.ListFiles(@ "\").Length.ToString();

			mText.Invalidate();
		}

		private static void MessageBox(string title, params object []args)
		{
			MessageBox(title, ModalButtons.Ok, args);
		}

		private static ModalResult MessageBox(string title, ModalButtons butts, params object[] args)
		{
			MessageBoxManager mgr = new MessageBoxManager();

			string msg = "";

			foreach (object arg in args)
			{
				msg += " " + arg.ToString();
			}

			return mgr.Show(msg, title, butts);
		}

		private void ButtonPc_TapEvent(object sender)
		{
			bool wasMounted = mSdCard.IsCardMounted;

			try
			{
				// can't do mass storage while we have the card mounted
				if (wasMounted)
					mSdCard.UnmountSDCard();

				// can't do mass storage while we're set to use the usb port for debug
				if (Configuration.DebugInterface.GetCurrent() == Configuration.DebugInterface.Port.USB1)
				{
					MessageBox("USB in use", "USB is currently in use for debugging!");
					return;
				}

				try
				{
					// don't call Start more than once - you'll get exceptions
					if (mMassStorage == null)
						mMassStorage = USBClientController.StandardDevices.StartMassStorage();
				}
				catch (System.Exception ex)
				{
					MessageBox("D'oh! Can't start mass storage", ex.Message);
					return;
				}

				// try getting a PersistantStorage instance for the card
				PersistentStorage storage = null;

				ModalResult result = ModalResult.Ok;
				while (result == ModalResult.Ok)
				{
					try
					{
						storage = new PersistentStorage("SD");
						break;
					}
					catch
					{
						result = MessageBox("SD card not detected", ModalButtons.OkCancel, "Please insert an SD card then press Ok or press Cancel to quit");
					}
				}

				if (result == ModalResult.Ok)
				{
					// stuff works! pop up a modal so the user can tell us when they're done
					mMassStorage.AttachLun(0, storage, " ", " ");
					mMassStorage.EnableLun(0);

					MessageBox("USB<->SD enabled.", "Click Ok when done.");
				}

				// disable the connection
				mMassStorage.DisableLun(0);

				if (storage != null)
				{
					// toss the PersistantStorage instance
					storage.UnmountFileSystem();

					Thread.Sleep(2000); // needed? maybe.. dunno

					// make sure to dispose of it (or use 'using') so we can use the gadgeteer
					// stuff again right away
					storage.Dispose();
				}
			}
			finally
			{
				// remount the card, if it was was mounted before
				if (wasMounted)
					mSdCard.MountSDCard();

				// it's tempting to call DisplayCounts again here but it won't work.. we're
				// still on the call stack from the dispatcher and need to unwind that all 
				// the way before we can use the card here again. Instead, call DisplayCounts
				// on the mounted event
			}
		}
	}
}

edit: other bit of the partial:


using Gadgeteer;
using GTM = Gadgeteer.Modules;

namespace UsbMassStorage
{
    public partial class Program : Gadgeteer.Program
    {
        // GTM.Module defintions
		Gadgeteer.Modules.GHIElectronics.SDCard mSdCard;
		Gadgeteer.Modules.GHIElectronics.UsbClientDP mUsbClient;
		Gadgeteer.Modules.GHIElectronics.Display_T35 mDisplay;

		public static void Main()
        {
			//Important to initialize the Mainboard first
            Mainboard = new GHIElectronics.Gadgeteer.FEZSpider();			

            Program program = new Program();
			program.InitializeModules();
            program.ProgramStarted();
            program.Run(); // Starts Dispatcher
        }

        private void InitializeModules()
        {   
			// Initialize GTM.Modules and event handlers here.		
			mUsbClient = new GTM.GHIElectronics.UsbClientDP(1);
		
			mSdCard = new GTM.GHIElectronics.SDCard(5);
		
			mDisplay = new GTM.GHIElectronics.Display_T35(14, 13, 12, 10);

        }
    }
}