Cerberus and SDCard module (again)

Hi,

Experimenting with cerberus (last firmware) and micro sd card module (http://www.ghielectronics.com/catalog/product/359) I’ve got around a few rough edges. Mainly :

  • never try to mount an already mounted card (causes boot lock up)
  • when attempting to reinsert an sdcard after ejecting it first, the first access to the sdcard will eventually causes a lock up.
  • you can workaround the previous issue, by checking if it is the second time the card has been mounted and rebooting the board instead.

Working code :

using System;
using System.Collections;
using System.Threading;
using Microsoft.SPOT;

using Gadgeteer.Networking;
using GT = Gadgeteer;
using GTM = Gadgeteer.Modules;
using Gadgeteer.Modules.GHIElectronics;

using Microsoft.SPOT.IO;
using System.IO;

namespace TestSD
{
    public partial class Program
    {

        static private string root;
        static private GT.Timer writeTimer;
        static private GT.Timer BlinkTimer;
        static private bool MountedOnce = false;
        // This method is run when the mainboard is powered up or reset.   
        void ProgramStarted()
        {
            /*******************************************************************************************
            Modules added in the Program.gadgeteer designer view are used by typing 
            their name followed by a period, e.g.  button.  or  camera.
            
            Many modules generate useful events. Type +=<tab><tab> to add a handler to an event, e.g.:
                button.ButtonPressed +=<tab><tab>
            
            If you want to do something periodically, use a GT.Timer and handle its Tick event, e.g.:
                GT.Timer timer = new GT.Timer(1000); // every second (1000ms)
                timer.Tick +=<tab><tab>
                timer.Start();
            *******************************************************************************************/

            BlinkTimer = new GT.Timer(1000);
            BlinkTimer.Tick += new GT.Timer.TickEventHandler(BlinkTimer_Tick);
            BlinkTimer.Start();

            writeTimer = new GT.Timer(5000);
            writeTimer.Tick += new GT.Timer.TickEventHandler(writeTimer_Tick);

            sdCard.SDCardMounted += new SDCard.SDCardMountedEventHandler(sdCard_SDCardMounted);

            sdCard.SDCardUnmounted += new SDCard.SDCardUnmountedEventHandler(sdCard_SDCardUnmounted);

            button.ButtonPressed += new Button.ButtonEventHandler(button_ButtonPressed);
            // Use Debug.Print to show messages in Visual Studio's "Output" window during debugging.
            Debug.Print("Program Started");
        }

        static private bool toggleled = true;
        void BlinkTimer_Tick(GT.Timer timer)
        {
            if (sdCard.IsCardMounted)
            {
                Mainboard.SetDebugLED(toggleled);
                toggleled = !toggleled;
            }
            else
            {
                Mainboard.SetDebugLED(false);
            }
        }


        void button_ButtonPressed(Button sender, Button.ButtonState state)
        {
            if (sdCard.IsCardMounted)
            {
                sdCard.UnmountSDCard();
            }
        }

        void sdCard_SDCardUnmounted(SDCard sender)
        {
            writeTimer.Stop();
        }

        void sdCard_SDCardMounted(SDCard sender, GT.StorageDevice SDCard)
        {
            if (MountedOnce)
            {
                Thread.Sleep(100);
                Microsoft.SPOT.Hardware.PowerState.RebootDevice(true);
            }
            else
            {
                root = SDCard.RootDirectory;
                writeTimer.Start();
                MountedOnce = true;
            }
        }


        void writeTimer_Tick(GT.Timer timer)
        {
            if (sdCard.IsCardMounted)
            {
                string fileName = Path.Combine(root, "file.txt");
                Stream stream;
                if (File.Exists(fileName))
                {
                    stream = File.OpenWrite(fileName);
                    stream.Position = stream.Length;
                }
                else
                {
                    stream = File.Create(fileName);
                }
                using (var writer = new StreamWriter(stream))
                {
                    Debug.Print("Writing ok");
                    writer.WriteLine(Guid.NewGuid().ToString());
                }
                stream.Dispose();
            }
            else timer.Stop();
        }



    }
}

Is anybody experiencing the same thing, did I forgot something obvious and/or arcane ?

Regards

2 Likes

I have no idea when I’ll get around to it, but I plan on using the SD Module onto a Cerb40 based PCB I’ll be designing… so thanks for sharing your efforts on this. Doesn’t really help you… but thanks all the same!

@ fradav - I totally believe this. I was never able to make the MicroSD card reliable over the long term. I have at times thought of writing my own managed library. But instead I abandoned using the MicroSD card for non-volatile storage.

SD Card works fine as long as you follow these rules:

Always flush and close your files, then flushall and unmount device before removing card.

Of cource this is not always possible when your app freezes up and has to be reset or get kicked by the watchdog.

@ David@ Emrol - So my code above follow all of those rules, correct ?

not completely, you writer is not closed and disposed, stream is not closed before disposed.
[edit] and indeed dispose is closing, tgif (again)

@ David@ Emrol - “using” keyword automatically disposes when out of scope. Unless I am mistaken, disposing streams should close them : is there something like a timing issue so we have to explicitly close before dispose ?
The part of the code you call a reader is the “stream” variable ?

Is the following better, then ?


        void writeTimer_Tick(GT.Timer timer)
        {
            if (sdCard.IsCardMounted)
            {
                string fileName = Path.Combine(root, "file.txt");
                Stream stream;
                if (File.Exists(fileName))
                {
                    stream = File.OpenWrite(fileName);
                    stream.Position = stream.Length;
                }
                else
                {
                    stream = File.Create(fileName);
                }
                using (var writer = new StreamWriter(stream))
                {
                    Debug.Print("Writing ok");
                    writer.WriteLine(Guid.NewGuid().ToString());
                    writer.Close();
                    writer.Dispose();
                }
                stream.Close();
                stream.Dispose();
            }
            else timer.Stop();
        }

Yes, stream indeed, and dispose will close, my mistake :slight_smile:

A quick update for ghi people, my experiences with the last firmware :

  • cpu hogs when using sdcard have been eradicated, good work guys!

  • BUT, it’s still far from perfect. there is still a problem with the “append to file” use case :

    When creating the file and appending data to it in the same session, it’s ok. But if you reset or umount/remount the card and directly try to append data to file, it simply trashes the file and directory structure. I have reproduced this on three different cards with the code in the first post.

    Workaround for the moment : when checking if the file exists for the first time after the sdcard mounted or the system booted, rename it and create it again, Otherwise you will lose your filesystem integrity.

If you own other devices then can you try it? I am curious if this is a bug in Cerberus or it is a NETMF bug.

Ok, will try on an old netduino plus