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?
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) + "%");
}
}
}
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.
@ 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.
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.
@ 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.
@ 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
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…
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.