Main Site Documentation

(C# problem) Declaring SerialPort as Static - eats up all of Fez's memory


#1

I’m back again with a problem. :’(

I know I’ve already created a topic like this a few months ago.

The problem is, when serialport is declared as STATIC. The garbage collector would ignore the serialport and the serialport ends up consuming all memory of the FEZ.

The workaround I did was to delete the static declaration and declared the serialport on each class that needs it. But as a consequence serialport.datareceived event would not fire.

I’ve attached some of the parts of the program.

    class Program
    {
        static SerialPort Serial1;
        static PersistentStorage ps;
        static string finalfile;
        static byte[] chbyte = new byte[1] { 0 };
        static string myString;
        static System.Text.Encoding enc = System.Text.Encoding.UTF8;

        public static void Main()
        {
            Debug.GC(true);
            Serial1 = new SerialPort("COM1");
            Serial1.BaudRate = 115200;
            Serial1.Parity = Parity.None;
            Serial1.StopBits = StopBits.One;
            Serial1.DataBits = 8;
            Serial1.Handshake = Handshake.None;
            Serial1.Open();
            Debug.Print(DateTime.Now.ToString());
            Serial1.DataReceived += new SerialDataReceivedEventHandler(DataReceived);
            USBHostController.DeviceConnectedEvent += DeviceConnectedEvent;
            USBHostController.DeviceDisconnectedEvent += DeviceDisconnectedEvent;
            Thread.Sleep(Timeout.Infinite);
        }

        static void DeviceConnectedEvent(USBH_Device device)
        {
            if (device.TYPE == USBH_DeviceType.MassStorage)
            {
                Debug.Print("USB Mass Storage detected...");
                Thread.Sleep(1000);
                ps = new PersistentStorage(device);
                ps.MountFileSystem();
            }
        }

        private static void DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            Debug.Print("DataReceived Event");
            try
            {
                Serial1.Read(chbyte, 0, 1);
                Thread.Sleep(200);
                myString = new string(enc.GetChars(chbyte));
            }
            catch
            {
            }
           
            if (string.Compare(myString, "r") == 0)
            {
                Debug.Print("receiving file from the computer");
                receivefile();
            }
            if (string.Compare(myString, "s") == 0)
            {
                Debug.Print("sending file to the computer");
                byte[] name = new byte[100];
                Serial1.Read(name, 0, 100);
                string myfile = new string(enc.GetChars(name));
                finalfile = myfile.Trim();
                Sendfile();
            }
            else if (string.Compare(myString, "d") == 0)
            {
                Debug.Print("Sending filenames");
                fileexplore();

            }
            else if (string.Compare(myString, "p") == 0)
            {
                Debug.Print("ping");
                byte[] ping = new byte[1];
                ping = enc.GetBytes("p");
                Serial1.Write(ping, 0, 1);
            }     
        }

        static void Sendfile()
        {
            string rootDirectory = VolumeInfo.GetVolumes()[0].RootDirectory;
            string originalFile = rootDirectory + @ "\" + finalfile;
            FileStream fs1 = new FileStream(originalFile, FileMode.Open, FileAccess.Read);
            byte[] end1 = new byte[2];
            int xx = 0, bytes = 6000;
            string size;
            byte[] filesize = new byte[20];
            size = fs1.Length.ToString();
            size = String.Concat(size, "                    ");
            size = size.Substring(0, 20);
            filesize = enc.GetBytes(size);
            if (fs1.Length > bytes)
            {
                while (xx < (fs1.Length / bytes))
                {

                    byte[] ByteArray = new byte[bytes];
                    fs1.Read(ByteArray, 0, bytes);
                    Thread.Sleep(500);
                    Serial1.Write(ByteArray, 0, bytes);
                    xx++;
                    if (xx == ((fs1.Length / bytes) - 1))
                    {
                        fs1.Read(ByteArray, 0, ((int)fs1.Length % bytes));
                        Serial1.Write(ByteArray, 0, ((int)fs1.Length % bytes));
                    }
                }
            }
            if (fs1.Length <= 5000)
            {
                byte[] ByteArray = new byte[fs1.Length];
                fs1.Read(ByteArray, 0, (int)fs1.Length);
                int arraySize = (int)fs1.Length;
                Serial1.Write(ByteArray, 0, arraySize);
            }
            end1 = enc.GetBytes("\r\n");
            Serial1.Write(end1, 0, 2);
            Thread.Sleep(2000);
            Debug.Print("Fs1 closed");
            fs1.Close();


            // Compare size of original an new file
            FileInfo fileInfo1 = new FileInfo(originalFile);

            Debug.Print("Size of original file: " +
            fileInfo1.Length.ToString());
        }
}

[quote]GC: 3msec 63024 bytes used, 1356 bytes available
Type 0F (STRING ): 636 bytes
Type 11 (CLASS ): 4104 bytes
Type 12 (VALUETYPE ): 108 bytes
Type 13 (SZARRAY ): 8412 bytes
Type 15 (FREEBLOCK ): 1356 bytes
Type 17 (ASSEMBLY ): 17112 bytes
Type 18 (WEAKCLASS ): 48 bytes
Type 19 (REFLECTION ): 24 bytes
Type 1B (DELEGATE_HEAD ): 828 bytes
Type 1C (DELEGATELIST_HEAD ): 96 bytes
Type 1D (OBJECT_TO_EVENT ): 336 bytes
Type 1E (BINARY_BLOB_HEAD ): 25920 bytes
Type 1F (THREAD ): 1152 bytes
Type 20 (SUBTHREAD ): 144 bytes
Type 21 (STACK_FRAME ): 1404 bytes
Type 27 (FINALIZER_HEAD ): 528 bytes
Type 31 (IO_PORT ): 504 bytes
Type 34 (APPDOMAIN_HEAD ): 72 bytes
Type 36 (APPDOMAIN_ASSEMBLY ): 1596 bytes
Failed allocation for 10 blocks, 120 bytes[/quote]


#2

Lots of issues in your program.

Have you tried to get a simple program working with the serial port?

The least possible amount of work should be done in the DataReceived handler. You are tying up an internal thread which will cause all sorts of issues.

If you are just reading one byte from the serial port why use DataReceived? Just do a block read for one byte. You can then do anything you want at that point.

Why all the sleeps? You should not need them.

Too late for me to do an complete analysis of your program.


#3

I also have a class for receiving files from the PC which also uses READ(). I use sleeps especially for the sendfile() loop so that the receiving program in my computer will not be left behind.

I need DataReceived event so that I can continue using the program after a successful operation.
If the datareceived event would not trigger, I can only do one operation(ping, send, receive, get file names) then restart the fez to do another one.

Maybe tomorrow if you have some spare time, can you look at it again? :slight_smile: I’m in a GMT+8 timezone country so it is just morning here.

I can upload the whole project (Fez program - C# and PC program - VB) in rar format if needed.


#4

Since the Main() method never actually ends you can easily declare your serialport there. And in the events, not entirely sure but the “sender” object should be the serialport instance so that should be a viable way to get it and send back a response.


#5

you should add a small sleep to it.


#6

Yes, but I still need to declare it outside in static so that other methods can see it, but because it is static the Garbage collector won’t free its used memory.

The event works fine if the serialport is in static but it is still useless because Fez crashes because the serial eats all available memory…

Is there a way to disable this
"Serial1.DataReceived += new SerialDataReceivedEventHandler(DataReceived);"
Temporarily while the SENDFILE() is running? And enable it again after SENDFILE ends?


#7

I don’t know the answer, so I’m little help here sorry.

I would however say that this is your problem:

[quote]The workaround I did was to delete the static declaration and declared the serialport on each class that needs it. But as a consequence serialport.datareceived event would not fire.
[/quote]

You need to figure out a better way to deal with the issue, the decision you made here is most likely wrong. You should not try to:

[quote]Is there a way to disable this
"Serial1.DataReceived += new SerialDataReceivedEventHandler(DataReceived);"
Temporarily while the SENDFILE() is running? And enable it again after SENDFILE ends?
[/quote]
as all you’re doing there is piling onto an already bad situation.

Taking a step back, have you thought about searching for others who are doing this kind of thing? http://forums.netduino.com/index.php?/topic/132-a-few-deploy-via-serial-questions/ was a working example I found in a quick search, perhaps that might be another thing you could try…


#8

ok, some more thoughts here from a 2 min brain storm.

In your event handler you evaluate what data you recieve, and then take that action.

You need to stop that - that event is like an “interrupt” to tell you you have more data to read.

You can go back to polling the serial port for data, as that will help you unwind the program logic that is screwing you over. If you get a S or an R, send or read data as appropriate…

Or you can think of this more like a state machine and in the event handler just update the state, not act on that state.

In the event handler you need to evaluate the current state and the next state.

If Current state = idle and data recieved = ‘S’, new state = SEND.
If current state = idle and data recieved = “R” new state = RECV
if current state = RECV then append data recieved to the object
if current state = SEND then (this might be an ACK back from the PC for instance?)


I actually think that all this stems from you READ data in SENDFILE(), if you can figure out a way to not do that you will be better off.


#9

I also want to scrap that event but my inexperience is limiting me. Most of the code there are from different codes examples from the Fez ebook. Thank you GHI for that wonderful book. :slight_smile:

I think I’ve solved it. But I still pushed on using the event. I hope I did not dig my problem deeper in doing so.

        if (string.Compare(myString, "r") == 0)
        {
            Serial1.DataReceived -= new SerialDataReceivedEventHandler(DataReceived);
            Debug.Print("receiving file from the computer");
            receivefile();
            Serial1.DiscardInBuffer();
            Serial1.DiscardOutBuffer();
            Serial1.DataReceived += new SerialDataReceivedEventHandler(DataReceived);
        }

So when RECEIVEFILE() is running, the event will be temporarily disabled. Because while it is running, the event is also constantly firing the DataReceived() making the Fez crash.
(Sendfile is innocent, I was confused with the namings I did, because SENDFILE() in my PC VB prog == RECEIVEFILE() in the Fez C# prog :smiley: )

As of now it is working fine. I can now receive and send files without restarting the Fez after every operation. The only problem now is receiving and sending doesn’t have an error checking. Success of the transfer is very random.

Thanks everyone for the help and support. :slight_smile:


#10

My thoughts again: all you’ve done is do something inside the handler and masked the badness by temporarily stopping the handler from being called.

You can stop here if you think you have your answer, since you have something that is “doing the job”, but you might never get to the bottom of this…

The concept of a handler is to allow multi threading, a key (cool) concept that NetMF allows. A serial handler is all about removing “blocking” while you wait to read or write data, so you can have other threads doing stuff in the background. What you have done is handle the first character in a separate thread, and then forced that thread to do all the hard work and block based on data coming in and going out.

If the entire concept of your app is to:

wait for a command; if it is SEND then pump a file back to the PC; if it is RECV then take a file from the PC and write it to (USB/SD/whatever)

…then you can certainly use some form of threading. If I was tackling this I’d implement a circular buffer for reading from PC and writing to storage since the speeds of those two mediums may be significantly different. (practically I bet Serial will be the slow point, but if the storage was an SPI flash chip for instance, the serial link may not be the slowest bit)

Again, the concept of a handler like this is not to consume lots of time doing what you want, but to do short sharp actions. Exactly like an interrupt service routine (ISR) on any microcontroller. You should grab a character off the serial port and stuff it in a buffer, and let the MAIN loop handle things.

just my thoughts - as i said if you want to say what you have is good enough then that’s cool. But this might be something that will give you greater insight in the longer term.

If nothing else, I say good luck to you, like us all we have a journey to take (I have some huge steps that I am yet to start!)


#11

Kein like I said, the “sender” object you have here:

private static void DataReceived(object sender, SerialDataReceivedEventArgs e)

You can cast to a SerialPort and get your reference.


#12

Yes, I know. I’ve just covered the problem with another possible source of problem. :’(

I really want to follow those tips but I am really at a loss with all those programming terms. I’ve learned programming from examples. I see a code, copy it, run it. Analyze what lines of codes are responsible for those outputs. Edit the code(add some codes I know or remove some existing lines to see what is the visible effect of that line in that code.)

But I always google those unfamiliar words but sometimes the explanation also uses more unfamiliar words so I also check them but I still need to try it to really understand.

I’ve never really knew what that “sender” does even if it is in every method I create. I’ll look into it.

[quote]If the entire concept of your app is to:

wait for a command; if it is SEND then pump a file back to the PC; if it is RECV then take a file from the PC and write it to (USB/SD/whatever)

…then you can certainly use some form of threading. If I was tackling this I’d implement a circular buffer for reading from PC and writing to storage since the speeds of those two mediums may be significantly different.[/quote]

Yes, that’s the concept. But I still don’t know how I can implement threading on it. Also, if its okay can you elaborate on the circular buffer in reading and writing. My professor also recommended implementing that when I was still finding a way to read and write the bytes.

I thought what I did is already a circular buffer. ::slight_smile:

  • Fez store the first 6000 bytes out of a possble 100000++ bytes to a buffer
  • the write that buffer immediately to the file
  • overwrite the buffer with the next 6000 bytes
    -and so on

I can only save upto 6000 bytes to a variable because the domino does not have enough memory to store a large file.


#13

[quote]I really want to follow those tips but I am really at a loss with all those programming terms. I’ve learned programming from examples. I see a code, copy it, run it. Analyze what lines of codes are responsible for those outputs. Edit the code(add some codes I know or remove some existing lines to see what is the visible effect of that line in that code.)

But I always google those unfamiliar words but sometimes the explanation also uses more unfamiliar words so I also check them but I still need to try it to really understand.
[/quote]

This is how I have always felt about math! It never did me any good to have a professor try to explain one thing I did not understand with more things I did not understand. I think it is a good idea when trying to explain a concept to use analogies from everyday life, it has always helped me visualize the concept. So perhaps a good goal for folks trying to explain a programming concept to try and use analogies that everyone will understand rather than more programming terms.