SerCam on Fez Hydra with .NET MF 4.2 and VS2012?

Having a lot of trouble parsing the limited documentation with for the Serial Camera L2 module. I have installed the latest Gadgeteer bits from the beta forum and am running on VS2012. When I setup my new project, i use the SerCam in the design view. When i try to write the code to capture a picture, the methods described in all the forum posts i have read are not even there (i.e. TakePicture() ). It appears to use some kind of streaming instead which i can’t seem to figure out. I went to the Gadgeteer source code on codeplex and found the SerCam module thinking this must be the latest code/driver (http://gadgeteer.codeplex.com/SourceControl/changeset/view/28532#273388). When i take a look at the source, the example in the comments in the top of SerCam_42.cs doesn’t even match the implementation below in the same file! For example, it refers to a PictureCaptured event, which doesn’t exist.

Since there is no simple getting started doc on the GHI site, i am a bit lost as to how to capture a simple picture (to save to file on SD card or transmit to a web service).

Has anybody cracked this nut with the new bits and VS2012 (and Fez Hydra)? If so, any chance you could post a .sln of a working picture taker so i can use it to troubleshoot?

Thank you!
Craig

We are in the middle of releasing the next SDK so some things get updated before other things.

We will get back to you on this shortly.

Welcome to the community.

Here is example code



        void ThreadTakePicture()
        {
            while (true)
            {
                if (button.IsPressed)
                {
                    cam.StartStreaming();
                    while (cam.isNewImageReady == false)
                    {
                        Thread.Sleep(25);
                    }
                    cam.DrawImage(LCD, 0, 0, SystemMetrics.ScreenWidth,SystemMetrics.ScreenHeight);
                    display_T35.SimpleGraphics.DisplayImage(LCD, 0, 0);
                   cam.StopStreaming();
                }
                Thread.Sleep(25);
            }
        }

And you can move cam.StartStreaming() and cam.StopStreaming() out of while loop if you don’t want to call them everytime take a picture.

Just make sure that you have to startstreamming before take a picture( like webcam)

Thanks Gus, I look forward to updated docs/driver for the Serial Cameras. In the meantime, I am trying to get Dat’s solution working. I don’t have a T35 display module so I am just trying to write the image file to an SD Card. The code seems to run without exception but when i plug the (FAT formatted) SD card into my PC to take a look, there are no files. Is there some way to render the image in Visual Studio so i can see if I am getting good data from the camera? Any ideas on the SD issue?

Here is my entire program.cs


using System;
using System.Collections;
using System.Threading;
using System.IO;
using Microsoft.SPOT;
using Microsoft.SPOT.Presentation;
using Microsoft.SPOT.Presentation.Controls;
using Microsoft.SPOT.Presentation.Media;
using Microsoft.SPOT.Touch;

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

namespace LizardCam
{
    public partial class Program
    {

        double currentHumidity;
        double currentTemp;
        string rootDirectory;
        

        // 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();
            *******************************************************************************************/

            //setup Camera
            serCam.SetImageSize(SerCam.Camera_Resolution.SIZE_QQVGA);
            
            //setup SD
            sdCard.MountSDCard();
            Thread.Sleep(500);
            rootDirectory = sdCard.GetStorageDevice().RootDirectory;
            sdCard.SDCardMounted += sdCard_SDCardMounted;
            sdCard.SDCardUnmounted += sdCard_SDCardUnmounted;

            //turn on temp/humidity sensor
            temperatureHumidity.MeasurementComplete += temperatureHumidity_MeasurementComplete;
            temperatureHumidity.StartContinuousMeasurements();

            //update display regularly
            GT.Timer sensorTimer = new GT.Timer(500);
            sensorTimer.Tick += sensorTimer_Tick;
            sensorTimer.Start();

            //startup picture thread
            Thread picThread = new Thread(ThreadTakePicture);
            picThread.Start();

            // Use Debug.Print to show messages in Visual Studio's "Output" window during debugging.
            Debug.Print("Program Started");
        }


        void ThreadTakePicture()
        {
            while (true)
            {
                if (button.IsPressed)
                {
                    serCam.StartStreaming();
                    while (serCam.isNewImageReady == false)
                    {
                        Thread.Sleep(25);
                    }

                    Bitmap bitmap = new Bitmap(serCam.GetImageData(), Bitmap.BitmapImageType.Jpeg);
                    
                    //save the picture
                    FileStream imgFile = new FileStream(rootDirectory + @ "\lizard.jpg", FileMode.OpenOrCreate);
                    imgFile.Write(bitmap.GetBitmap(), 0, bitmap.Width * bitmap.Height * 3 + 54);
                    imgFile.Close();

                    //serCam.DrawImage(LCD, 0, 0, SystemMetrics.ScreenWidth, SystemMetrics.ScreenHeight);
                    //display_T35.SimpleGraphics.DisplayImage(LCD, 0, 0);
                    serCam.StopStreaming();
                }
                Thread.Sleep(25);
            }
        }



        void sdCard_SDCardUnmounted(SDCard sender)
        {
            Debug.Print("The SD card has been unmounted");
            Debug.Print("DO NOT try to access it without mounting it again first");
        }

        void sdCard_SDCardMounted(SDCard sender, GT.StorageDevice SDCard)
        {
            Debug.Print("SD card has been successfully mounted. You can now read/write/create/delete files");
            Debug.Print("Unmount before removing");
        }

        void temperatureHumidity_MeasurementComplete(GTM.Seeed.TemperatureHumidity sender, double temperature, double relativeHumidity)
        {
            currentHumidity = relativeHumidity;
            currentTemp = temperature;
            Debug.Print("temp=" + temperature + " hum: " + relativeHumidity);
        }

        void sensorTimer_Tick(GT.Timer timer)
        {           
            //update display
            Util.printToDisplay(char_Display, "Temp: " + Util.ConvertCelsiusToFahrenheit(currentTemp).ToString().Substring(0, 4) + ((char)0xDF) + "F", "Humidity: " + currentHumidity.ToString().Substring(0, 4) + "%");
        }
    }
}

break points in your code, in the section where the file system actions occur. What is happening in there? What does your bitmap object look like?

@ Brett - I set a break right after creating the bitmap object from serCam data (see attached image)

You have to call UnmountSD before you take the SD out, otherwise on some SD, data will be lost.

Please note although when you took the SD out, Unmounted event will be called but that is only uninitialize and reset some variables. Some SD you have to call sdCard.Unmount(…) before you take SD out.

and, what device are you using?

@ Dat - I am using the normal SD Module (https://www.ghielectronics.com/catalog/product/271). I will try the unmount tonight - thanks for the suggestion. Does the rest of the code look correct in terms of getting the JPG from the camera and saving to a file? I was not sure about the filestream write params. I found the *3+54 bit for somebody saving a .BMP - not sure what is different with .JPG file.

Thanks for your help!
-C

SD is tricky if you don’t flush, close and unmount. I have a release button and a led on my board so I can press the button, code closes everything nice and pop the led on to show it’s safe to remove the card. Works like a charm, before I used this approach I formatted my SD card 10 times a day.

1 Like

@ David@ Emrol - @ Dat calling unmount on the SD Card seems to work - i am now getting a file but it is not a valid JPG file. I assume that my filestream write code is incorrect - specifically the “count” parameter. I am using width * height * 3 + 54 which i read was for BMP but i suppose there is different header data for JPG.

Anybody know the correct way to write the JPG file using a filestream?

Thanks again for getting me this far - making progress!
-C

if you want write a JPG file, there is very simple.

serCam.GetImageData() => will return JPG data array, only thing you need to do is write it down to yuor SD, and name it like xxx.jpg. It will work fine.

And, JPG and BMP is different completely.

And, you should use like this:


data[] = serCam.GetImageData();
file.Write(data,0,data.length);

There is no wh3 +54, event with BMP format.

@ Dat - It worked! Thank you. Finally, i got a picture from my SerCam (although blurry - need to adjust focus).

Final question: seeing as how the SD card requires unmount in order to get the data, is there a reliable destructor for the entire program that I can put the unmount call in so i don’t need to mount/unmount in my TakePicture() logic?

I like David@ Emrol’s solution, only dismount at the request of the user, through some means like a button press. Another possible solution you can try (no guarantees) would just be to use flush() to make sure the data is written.

@ Brett - Thanks Brett. I get a System.IO exception when I call flush() for some reason so unmount seems to be the only thing that works. I will try to setup a mount/unmount control as David@ Emrol suggested. Thanks!

Your IO error means something is wrong with how you did it - lets explore that :slight_smile:

Here’s how I flush and close my file in an app that can’t guarantee it will have the file open when power is removed - it doesn’t work flawlessly to create consistent files, but it does work reasonably well…

                    FileStream myFile = File.Open(SDCard.RootDirectory + FileName, FileMode.Append);
                    myFile.Write(logMsg, 0, logMsg.Length);
                    myFile.Flush();
                    myFile.Close();
                    myFile.Dispose();

As you can see I just open the file, write, then flush/close/dispose as quick as possible.

1 Like

To make that code slightly more robust I would suggest you use a using statement.


using (FileStream myFile = File.Open(SDCard.RootDirectory + FileName, FileMode.Append))
{
  myFile.Write(logMsg, 0, logMsg.Length);
  myFile.Flush();
}

That will ensure that Dispose is called even if something in the using block throws an exceptions, for example even if Flush threw an exception the stream will still be closed. Close just calls Dispose anyway so having the using statement ensure Dispose is called should be sufficient.

1 Like

cool, amended in my app !