.NET Gadgeteer digital camera with a SerCam

[UPDATE:  C# and VB solutions are at the end, the first part is ONLY an explanation. You do not need to follow the steps, just look at the code at the very end if you are after a fix. ]

I have had a few people mention that they hit issues when swapping from the GHI Camera module to a GHI SerCam module. So here is an explanation and example:

First remember that things are constantly being updated so make sure you have the updates! (Updates/Debug help)

I am using NetMF QFE2, version 4.2 with the GHI software last updated Feb 18, 2013, (I also have no idea if the source code on codeplex has been updated to reflect this release.)

(Expect this post to get out of date, it is here to help those who need a workaround NOW – solution at the bottom)

Issue 1

1) The NullReferenceException:

Create a new project and call the take picture method on the camera.

 

  1. void button_ButtonPressed(Button sender, Button.ButtonState state)
  2. {
  3.     serCam.TakePicture();
  4. }

When you run the code it throws an exception. The exception is in the SerCam.dll and you do not have the source code in your project so you cant see what is going on.

image

I downloaded the latest GHI source from codeplex (http://gadgeteer.codeplex.com/)

Link the downloaded code into your project:

1) Delete the reference to the GHI SerCam.dll

2) Add the 4.2 SerCam project to your solution (codeplex source folder : MainModulesGHIElectronicsSerCamSoftwareSerCamSerCam_42).

3) Add a reference from your project to the codeplex SerCam.

Right now you are good to go, when it deploys it will now halt execution at the code that is causing the problem :

 image

The problem here is that the code assumes that there is at least one delegate to call when the StartDataCaptured event is triggered. A simple work around (if you don’t want to change the source) is to ensure that all events have something to call, even if it is empty. Then when you run the code you should not get the NullReferenceException.

  1. void ProgramStarted()
  2. {            
  3.     Debug.Print("Program Started");
  4.     serCam.StartDataCaptured += new SerCam.StartDataCapturedEventHandler(serCam_StartDataCaptured);
  5.     serCam.FinishDataCaptured += new SerCam.FinishDataCapturedEventHandler(serCam_FinishDataCaptured);
  6.     serCam.OnDataCaptured += new SerCam.DataCapturedEventHandler(serCam_OnDataCaptured);
  7.     serCam.TakePicture();
  8. }
  9.  
  10. }
  11.  
  12. void serCam_OnDataCaptured(SerCam sender, byte[] data)
  13. {            
  14. }
  15.  
  16. void serCam_FinishDataCaptured(SerCam sender)
  17. {
  18. }
  19.  
  20. void serCam_StartDataCaptured(SerCam sender, int sizeImage)
  21. {
  22. }

 

Issue 2

The methods are not the same as the camera I am used to, where is the Picture object, how do i get it and why is it not returned to me?

There are 3 events on the SerCam, one that is triggered at the start of a picture capture (StartDataCaptured), one that is triggered once the picture has been captured (FinishDataCaptured) and one that is triggered when some data is available (OnDataCaptured). The key here is that you only get the picture back in chunks, handy if memory is an issue. You have to take these chunks and piece them together. If we take a picture and set a breakpoint in the ReadFrameBuffer method (the one that triggers the OnDataCaptured event)  we can see that the picture is split into blocks, each block is returned as it is read. There are 95 blocks in this particular picture, so we have to put them all back together to reconstruct the picture for the display.

image

<snip>

image

 

Solution/workaround

Make sure there is code to execute for every event and rebuild the picture.

1) First create a Byte array to store the reconstructed picture in your program, I will call it rawPicData:

  1. namespace SerCamExample
  2. {
  3.     public partial class Program
  4.     {
  5.  
  6.         byte[] rawPicData;
  7.         void ProgramStarted()
  8.         {            
  9.             Debug.Print("Program Started");

3) When we start to take a picture lets reset the rawPicData array. That way we know it is initialised and is reset for every picture.

  1. void serCam_StartDataCaptured(SerCam sender, int sizeImage)
  2. {
  3.     rawPicData = new byte[] { };
  4. }

4) Every time there is a block (chunk) of picture data ready, we read it and append it to the end of the picture array (Not the optimal solution, it will be a bit slow, but it is one line). I added a debug line so you can see that something is happening.

  1. void serCam_OnDataCaptured(SerCam sender, byte[] data)
  2. {
  3.     rawPicData = Microsoft.SPOT.Hardware.Utility.CombineArrays(rawPicData, data);
  4.     Debug.Print("Working…");
  5. }

5) Finally when the camera has finished taking a picture and you have read all the blocks, the FinishedDataCaptured event will be triggered. Now you can take the byte array and convert it into a picture object. You can only do this once you have ALL the blocks and you should check for an exception, in case of an error.  In this case it is Jpeg

  1. var picB = new Bitmap(rawPicData, Bitmap.BitmapImageType.Jpeg);

You can then display the picture:

  1. display_T35.SimpleGraphics.DisplayImage(picB, 0, 0);

Source Code

If you read nothing above and just want a working example here it is:

  1. using System;
  2. using System.Collections;
  3. using System.Threading;
  4. using Microsoft.SPOT;
  5. using Microsoft.SPOT.Presentation;
  6. using Microsoft.SPOT.Presentation.Controls;
  7. using Microsoft.SPOT.Presentation.Media;
  8. using Microsoft.SPOT.Touch;
  9. using Gadgeteer.Networking;
  10. using GT = Gadgeteer;
  11. using GTM = Gadgeteer.Modules;
  12. using Gadgeteer.Modules.GHIElectronics;
  13.  
  14. namespace SerCamExample
  15. {
  16.     public partial class Program
  17.     {
  18.         byte[] rawPicData;
  19.         void ProgramStarted()
  20.         {            
  21.             Debug.Print("Program Started");
  22.             serCam.StartDataCaptured += new SerCam.StartDataCapturedEventHandler(serCam_StartDataCaptured);
  23.             serCam.FinishDataCaptured += new SerCam.FinishDataCapturedEventHandler(serCam_FinishDataCaptured);
  24.             serCam.OnDataCaptured += new SerCam.DataCapturedEventHandler(serCam_OnDataCaptured);           
  25.             button.ButtonPressed += new Button.ButtonEventHandler(button_ButtonPressed);        
  26.         }
  27.         
  28.         void serCam_OnDataCaptured(SerCam sender, byte[] data)
  29.         {
  30.             rawPicData = Microsoft.SPOT.Hardware.Utility.CombineArrays(rawPicData, data);
  31.             Debug.Print("Working…");
  32.         }
  33.  
  34.         void serCam_FinishDataCaptured(SerCam sender)
  35.         {
  36.             var picB = new Bitmap(rawPicData, Bitmap.BitmapImageType.Jpeg);
  37.             display_T35.SimpleGraphics.DisplayImage(picB, 0, 0);
  38.         }
  39.  
  40.         void serCam_StartDataCaptured(SerCam sender, int sizeImage)
  41.         {
  42.             rawPicData = new byte[] { };
  43.         }
  44.  
  45.         void button_ButtonPressed(Button sender, Button.ButtonState state)
  46.         {
  47.             serCam.TakePicture();
  48.         }        
  49.     }
  50. }

And in VB (Thanks to Sue)

  1. Imports GT = Gadgeteer
  2. Imports GTM = Gadgeteer.Modules
  3. Imports Gadgeteer.Modules.GHIElectronics
  4.  
  5.  
  6. Partial Public Class Program
  7.     ' Dim datajpg As Byte() = {8}
  8.     Dim datajpg() As Byte = New Byte() {}
  9.     Dim index As Integer
  10.     ' This is run when the mainboard is powered up or reset.
  11.     Public Sub ProgramStarted()       
  12.         ' Use Debug.Print to show messages in Visual Studio's "Output" window during debugging.
  13.         Debug.Print("Program Started")
  14.         serCam.SetImageSize(serCam.Camera_Resolution.SIZE_QVGA)
  15.  
  16.     End Sub
  17.  
  18.     Private Sub button_ButtonPressed(sender As Gadgeteer.Modules.GHIElectronics.Button, state As Gadgeteer.Modules.GHIElectronics.Button.ButtonState) Handles button.ButtonPressed
  19.         serCam.TakePicture()
  20.         Debug.Print("Taken picture")
  21.  
  22.     End Sub
  23.  
  24.     Private Sub serCam_FinishDataCaptured(sender As Gadgeteer.Modules.GHIElectronics.SerCam) Handles serCam.FinishDataCaptured
  25.         Dim picture As Bitmap = New Bitmap(datajpg, Bitmap.BitmapImageType.Jpeg)
  26.         display_T35.SimpleGraphics.DisplayImage(picture, 0, 0)
  27.  
  28.     End Sub
  29.  
  30.     Private Sub serCam_OnDataCaptured(sender As Gadgeteer.Modules.GHIElectronics.SerCam, data() As Byte) Handles serCam.OnDataCaptured
  31.  
  32.         datajpg = Microsoft.SPOT.Hardware.Utility.CombineArrays(datajpg, data)
  33.     End Sub
  34.  
  35.     Private Sub serCam_StartDataCaptured(sender As Gadgeteer.Modules.GHIElectronics.SerCam, sizeImage As Integer) Handles serCam.StartDataCaptured
  36.         ' At present need an empty sub here to stop a Null Reference error occurring.
  37.         ' Hopefully this will be fixed soon (11/4/2013)
  38.     End Sub
  39. End Class

Leave a Reply

Your email address will not be published. Required fields are marked *