SD Card Problem

I have a problem reading SD Cards. I have a single simple textfile with three short lines.
The card is FAT - formatted, with 512 or 2k blocksize.
Whenever I try to read this file I get a Null-reference exception although the card is mounted and the “sdCard.isInserted” property says that it is.
if I use a button and read inside the “pressed” - event it works.
Any idea what I make wrong??

Here’s my Code:


        void ProgramStarted()
        {
            Debug.Print("Program Started");
            Debug.Print("SD should be auto-mounted");
            characterDisplay.Clear();
            sdCard.DebugPrintEnabled = true;
            try
            {
                if (!sdCard.IsCardMounted)
                {
                    sdCard.Mount();
                }
            }
            catch
            {
                Debug.Print("Mount Error");
            };
            sdCard.Mounted += SdCard_Mounted;
            sdCard.Unmounted += SdCard_Unmounted;
            PulseDebugLED();
            characterDisplay.Clear();
            Debug.Print("Start");
            if (sdCard.IsCardInserted)
            {
                Debug.Print("Card Inserted");
                try
                {
                    Debug.Print("Try read");
                    byte[] _data = sdCard.StorageDevice.ReadFile(@ "\test.txt");
                    Debug.Print("Read success");
                    string _tempStr = ToString(_data);
                    string[] _lines = _tempStr.Split('\n');
                    Debug.Print("");
                    for (int i = 0; i < _lines.Length; i++)
                    {
                        Debug.Print(_lines[i]);
                        if (i < 2)
                        {
                            string _line = _lines[i].Substring(0, _lines[i].Length - 1);
                            characterDisplay.SetCursorPosition(i, 0);
                            characterDisplay.Print(_line);
                        }
                    }

                }
                catch (Exception ex)
                {
                    Debug.Print("Read failed");
                    Debug.Print(ex.ToString());
                    characterDisplay.Print("Card - Error!");
                    characterDisplay.SetCursorPosition(1, 0);
                    characterDisplay.Print(ex.ToString());
                }
                Debug.Print("Read finished");
            }
            else
            {
                Debug.Print("No Card");
            }
            PulseDebugLED();
        }

        private void SdCard_Unmounted(SDCard sender, EventArgs e)
        {
            Debug.Print ("Mounted");
        }

        private void SdCard_Mounted(SDCard sender, GT.StorageDevice device)
        {
            Debug.Print ("Un-Mounted");
        }

        private string[] split (string s)
        {
            //StringBuilder sb = new StringBuilder(s.Length);
            string[] resultStr = s.Split('\n');
            return resultStr;
        }

        private string ToString(byte[] bytes)
        {
            System.Text.StringBuilder response = new System.Text.StringBuilder(16);
            foreach (byte b in bytes) response.Append((char)b);
            return response.ToString();
        }
    }


@ mflume - the ProgramStarted method is for initialization. If you stay in the method, MF does not complete its setup. If you stay in the method too long, you will get a complaint from MF about taking too much time to complete.

also, you should make sure the sd is inserted before mounting, and don’t read until you receive the mount event.

Ok,
I changed the program the way shown below.
I get the following results:
It does not work at all in debug-mode under VS.
The program tries to mount the card endlessly. No mount-event is fired.
If I disconnect the controller from USB and reconnect it standalone without VS then it works if I insert the card after powering on and a following reset.
After that it works also inside VS (not every time)
I use VS2015 Community, the latest (R2) release from GHI under NETMF 4.3, a Reaper board, and a FAT - formatted 16MB SD-Card with 512 Byte Blocksize.
I also tried the 128MB Card delivered with the card - reader and 2K Blocksize.
I also tried a second card reader and another Reaper - Board.
Maybe you have a working example for me?


using Gadgeteer.Modules.GHIElectronics;
using System;
using System.Collections;
using System.Threading;
using Microsoft.SPOT;
using Microsoft.SPOT.Presentation;
using Microsoft.SPOT.Presentation.Controls;
using Microsoft.SPOT.Presentation.Media;
using Microsoft.SPOT.Presentation.Shapes;
using Microsoft.SPOT.Touch;

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



namespace SD_Test_01
{
    public partial class Program
    {
        // This method is run when the mainboard is powered up or reset.   
        void ProgramStarted()
        {
            Debug.Print("Program Started");
            Debug.Print("SD should be auto-mounted");
            characterDisplay.Clear();
            sdCard.DebugPrintEnabled = true;
            PulseDebugLED();
            characterDisplay.Clear();
            sdCard.Mounted += SdCard_Mounted;
            sdCard.Unmounted += SdCard_Unmounted;
            //if (sdCard.IsCardMounted) sdCard.Unmount();
            GT.Timer ticker = new GT.Timer(5000);
            ticker.Tick += Ticker_Tick;
            ticker.Start();
            Debug.Print("Start");
        }

        int _count = 0;

        private void Ticker_Tick(GT.Timer timer)
        {
            try
            {
                if (!sdCard.IsCardMounted)
                {
                    _count++;
                    characterDisplay.SetCursorPosition(0, 0);
                    characterDisplay.Print("Mount " + _count.ToString());
                    sdCard.Mount();
                }
                else
                {
                    characterDisplay.SetCursorPosition(1, 0);
                    characterDisplay.Print("Read");
                    SdCard_Mounted(sdCard, sdCard.StorageDevice);
                }
            }
            catch
            {
                Debug.Print("Mount Error");
            };
            PulseDebugLED();
        }

        private void SdCard_Unmounted(SDCard sender, EventArgs e)
        {
            Debug.Print ("Un-Mounted");
            //sdCard.Mount();
        }

        private void SdCard_Mounted(SDCard sender, GT.StorageDevice device)
        {
            Debug.Print ("Mounted");
            if (sdCard.IsCardInserted)
            {
                Debug.Print("Card Inserted");
                characterDisplay.Clear();
                try
                {
                    Debug.Print("Try read");
                    byte[] _data = device.ReadFile("test.txt");
                    Debug.Print("Read success");
                    string _tempStr = ToString(_data);
                    string[] _lines = _tempStr.Split('\n');
                    Debug.Print("");
                    for (int i = 0; i < _lines.Length; i++)
                    {
                        Debug.Print(_lines[i]);
                        if (i < 2)
                        {
                            string _line = _lines[i].Substring(0, _lines[i].Length - 1);
                            characterDisplay.SetCursorPosition(i, 0);
                            characterDisplay.Print(_line);
                        }
                    }

                }
                catch (Exception ex)
                {
                    Debug.Print("Read failed");
                    Debug.Print(ex.ToString());
                    characterDisplay.Print("Card - Error!");
                    characterDisplay.SetCursorPosition(1, 0);
                    characterDisplay.Print(ex.ToString());
                }
                Debug.Print("Read finished");
            }
            else
            {
                Debug.Print("No Card");
            }
        }

        private string[] split (string s)
        {
            string[] resultStr = s.Split('\n');
            return resultStr;
        }

        private string ToString(byte[] bytes)
        {
            System.Text.StringBuilder response = new System.Text.StringBuilder(16);
            foreach (byte b in bytes) response.Append((char)b);
            return response.ToString();
        }
    }


}

@ mflume - try checking for the card inserted within the timer event, and when it is seen, mount it and kill the timer.

I would also use a thread for doing the reading. the thread can be started when the mounted event occurs. it is not good practice to do any significant processing in an event handed.

@ mflume - Hi, I would mount the sdcard in a new thread, started in the timer tick event. Otherwise, as no other code is processed before the timer tick Event has ended, it waits for ever for the sdcard.mounted event that can not come since the timer tick event has not ended.


public partial class Program
    {
        static string _root;
        GT.Timer mountTimer = new GT.Timer(2000);

        void ProgramStarted()
        {
            sdCard.Mounted += sdCard_Mounted;
            mountTimer.Tick += mountTimer_Tick;
            mountTimer.Start();
            Debug.Print("Program Started");
        }

        void mountTimer_Tick(GT.Timer timer)
        {
            mountTimer.Stop();
            Thread mountThread = new Thread(new ThreadStart(runMountThread));
            mountThread.Start();
        }
        void runMountThread()
        {
            sdCard.Mount();
        }

        void sdCard_Mounted(SDCard sender, GT.StorageDevice device)
        {
            Debug.Print("Card is now mounted");
            String rootDirectory = sdCard.StorageDevice.RootDirectory;
            if (Microsoft.SPOT.IO.VolumeInfo.GetVolumes()[0].IsFormatted)
            {
                _root = VolumeInfo.GetVolumes()[0].RootDirectory;
                string[] files = System.IO.Directory.GetDirectories(_root);
                for (int i = 0; i < files.Length; i++)
                {
                    Debug.Print(files[i]);
                }
                string thePath = _root + @ "\test.txt";
                byte[] _data = sdCard.StorageDevice.ReadFile(thePath);
                string _tempStr = ToString(_data);
                string[] _lines = _tempStr.Split('\n');
                for (int i = 0; i < _lines.Length; i++)
                {
                    Debug.Print(_lines[i]);
                }
            }
            else
            {
                Debug.Print("Cannot use SD-Card");
            }
        }
        private string ToString(byte[] bytes)
        {
            System.Text.StringBuilder response = new System.Text.StringBuilder(16);
            foreach (byte b in bytes) response.Append((char)b);
            return response.ToString();
        }
    }


@ RoSchmi
Thank for your example. Unfortunately here it has the same result like mine.
If I run the program from VS it does not work. The mount - event is not fired.
If I then Reset the controller with the reset Button, remove and reinsert the card and press reset again it works.
After that it works also in VS until I modify the program. Then the procedure starts over again.
The Card seems to be already mounted in VS and not in standalone running.
Very strange…
Btw. I there a reason why you did not use the RunOnce - feature of the timer?
additionally I get an “already mounted” - Exception in the Thread so I used a try - catch to avoid this.

@ mflume - :think: :think: , for me my example worked fine on a Spider II mainboard with VS and without VS. You used a Reaper Mainboard but it is hard to believe that this is the reason for the different behavior. Unfortunately I don’t have a Reaper.
No, there was no special reason that I did not use the RunOnce feature of the timer. In the moment I made the quick test I just was not aware of this better option.

@ RoSchmi
At the moment I believe the reason is, that you never can be sure wether the card is mounted or not depending if in VS or stand alone. Regardless that you don’t touch it.
Based on your example I first try to unmount and then to mount it again. The success - quota is better now.

After all the time trying to make it run, I have the opinion that there is a timing problem.
If I start the program without VS it works every 5th or 10th time (Card inserted).
There are cards which work better than others.
All in all it should be easier to use.
Maybe the GHI team lokks over my problem and gives a working example, gives hints under which circumstances I can get a reliable result or fixes the bug if there is one.

O.K.!
I tried out more than 10 cards from 16MB to 8GB and found out, that 4 of them work all the time, 3 sometimes and the rest never or only by chance.

@ mflume -

Yes, there is timing problem. Some cards are initialize fast then they are good, otherwise they can be failed.
Try to use our test code below to resolve that issue and see if it work on your side.


public partial class Program
    {
        static string _root;
        Thread sdThread;
        
        void ProgramStarted()
        {
            sdThread = new Thread(CheckSD);
            sdThread.Start();
            Debug.Print("Program Started");
        }
        void CheckSD()
        {
            Debug.GC(true);          
            sdCard.Mounted += sdCard_ManuallyMounted;
            if (sdCard.IsCardMounted)
            {
                Debug.Print("SD Card is mounted already.");
                return;
            }
            // Mount manually
            TryMountSDManually();
        }
        void TryMountSDManually()
        {
            
            int step ;
            Debug.Print("Try to remount the card...");
        startOver:
            step = 0;
            Debug.GC(true);
            while (step < 4)
            {
                Debug.Print("step : " + step);
                switch (step)
                {
                    case 0:
                        Thread.Sleep(5000); // Wait for initialize done
                        step++;
                        break;

                    case 1:
                        if (sdCard.IsCardMounted) // We check again to make sure mounting was really failed ?
                        {
                            Debug.Print("====>>>> Card is mounted already. Mounting was slow but it is OK. Good to go =====>");
                            return;
                        }
                        Mainboard.UnmountStorageDevice("SD"); // really failed => renew object
                        step++;
                        break;
                    case 2:
                        Debug.GC(true);
                        Thread.Sleep(5000); // Wait for release some
                        step++;
                        break;
                    case 3:
                       
                        sdCard.Mount(); // Re-mount again!
                        Thread.Sleep(5000);
                        step++;
                        break;                    
                }
            }
            if (sdCard.IsCardMounted == false)
                goto startOver;
            Debug.Print("Re-mount card has succesfully. Good to go!");
        }

        void sdCard_ManuallyMounted(SDCard sender, GT.StorageDevice device)
        {
            
            String rootDirectory = sdCard.StorageDevice.RootDirectory;
            if (Microsoft.SPOT.IO.VolumeInfo.GetVolumes()[0].IsFormatted)
            {
                _root = VolumeInfo.GetVolumes()[0].RootDirectory;
                string[] files = System.IO.Directory.GetDirectories(_root);
                for (int i = 0; i < files.Length; i++)
                {
                    Debug.Print(files[i]);
                }
                string thePath = _root + @ "\test.txt";
                byte[] _data = sdCard.StorageDevice.ReadFile(thePath);
                string _tempStr = ToString(_data);
                string[] _lines = _tempStr.Split('\n');
                for (int i = 0; i < _lines.Length; i++)
                {
                    Debug.Print(_lines[i]);
                }
            }
            else
            {
                Debug.Print("Cannot use SD-Card");
            }
        }
        private string ToString(byte[] bytes)
        {
            System.Text.StringBuilder response = new System.Text.StringBuilder(16);
            foreach (byte b in bytes) response.Append((char)b);
            return response.ToString();
        }
    }

1 Like

Ok, I tried my cards with your program.
I used about 20 cards from different manufactures.

  • 32MB Sandisk MMC -> Fail
  • 16/64MB Panasonic SD -> Good
  • 64MB Kingston SD -> Mount in Step3
  • 256MB Toshiba SD -> Good
  • 8GB Toshiba MicroSD -> Fail
  • 128MB Elite Pro (the one you delivered with the SD - Adapter) -> Fail!
    So the chance to get a working card is very low.
    Maybe you rework the software, That would be very kind!
    Btw.: I used those cards at an ATMega without problems.
    Is there a recommended way to format the cards? I used FAT with 512/1024/2048 Blocksize.

I learned something new here. I learned that C# has a goto statement.

2 Likes

That’s a new one for me, too.

@ mflume -

  • For some cards like 16MB or 64MB, I think it is very old.
  • You can try to use raw NetMF instead of Gadgeeter driver to access SD. There is option to change SD frequency clock. Default clock probably is 24MHz and some card can not work at this frequency.
  • Below are example to use raw NetMF, you need to remove gadgeteer SD driver or they will be conflict.
 public static void Main()
        {
          
            while (ldr1.Read() == true)
            {
                Thread.Sleep(25);
                led1.Write(!led1.Read());
            }
            led1.Write(false);
            SDCard sdPS = new SDCard();
            sdPS.Mount(8000); // try at 8MHz - 12MHz - 16MHz - 24 MHz
            VolumeInfo vol = VolumeInfo.GetVolumes()[0];
            if (vol.IsFormatted == false )
            {
                Debug.Print("Mount Failed...!");
            }
            else
            {
                Debug.Print("Mount OK....");
                Debug.Print("Label = " + vol.VolumeLabel);
                Debug.Print("Size = " + vol.TotalSize);
            }
            

            
        }

@ mflume - if your device supports usb host, use a usb thumb drive. Super easy and reliable.

@ mflume - did you keep in mind that it is recommended to use a short gadgeteer cable?

@ RoSchmi: I used the cable delivered with the card reader.
I tried three card - readers with their cables.
I really believe now that this is a software problem caused by GHI - drivers.
Especially I tried this new Micro-SD with no success.

@ mflume - I cannot confirm your bad results. I just tried the SD-Card reader on a Panda III, which has the G80 Soc, so the same Soc as the reaper Mainboard. Preliminary tests showed no issues to mount a Toshiba 4 GByte HC Class 4 Micro SD Card or a Samsung 32 Gbyte HC Class 10 Micro SD Card.
Are your Cards FAT16 or FAT32 formatted?

@ RoSchmi
I used the Windows10 Format program. Small cards were formatted with FAT(16) bigger ones with FAT32.
I have working cards (small: 16MB/FAT, bigger 128/256MB / FAT32) with both formats and others (32MB/128MB/8GB) which don’t work.