Serial camera on Hydra not finishing taking a picture

I just got my serial camera today and tried the example code from Dat in his post at http://www.tinyclr.com/forum/topic?id=9506&page=1#msg94738 (although changed to use a button to trigger when to capture a photo instead of a loop, I did try your threaded loop method but with same result), but the FinishedDataCapture event never fires. I added some Debug.Print lines, below is the output. This is on a Hydra. Am I doing something wrong?

Here is the code that produced the above…


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.Touch;

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

namespace SerialCameraScratch
{
    public partial class Program
    {
        byte[] datajpg;
        int index = 0;

        void ProgramStarted()
        {
            button.ButtonPressed += new Button.ButtonEventHandler(button_ButtonPressed);

            //serCam.SetImageSize(SerCam.Camera_Resolution.SIZE_QQVGA);
            serCam.OnDataCaptured += new SerCam.DataCapturedEventHandler(serCam_OnDataCaptured);
            serCam.FinishDataCaptured += new SerCam.FinishDataCapturedEventHandler(serCam_FinishDataCaptured);
            serCam.StartDataCaptured += new SerCam.StartDataCapturedEventHandler(serCam_StartDataCaptured);
            display_TE35.SimpleGraphics.AutoRedraw = true;
        }

        void button_ButtonPressed(Button sender, Button.ButtonState state)
        {
            if (serCam.isBusy)
            {
                Debug.Print("Camera is busy, try again in a couple of seconds.");
            }
            else
            {
                Debug.Print("Taking picture...");
                serCam.TakePicture();
            }
        }

        void serCam_StartDataCaptured(SerCam sender, int sizeImage)
        {
            index = 0;
            datajpg = new byte[sizeImage];
            Debug.Print("Image size to capture: " + sizeImage);
        }

        void serCam_FinishDataCaptured(SerCam sender)
        {
            Debug.Print("Finished capturing image from camera, displaying on screen.");
            display_TE35.SimpleGraphics.DisplayImage(new Bitmap(datajpg, Bitmap.BitmapImageType.Jpeg), 0, 0);
        }

        void serCam_OnDataCaptured(SerCam sender, byte[] data)
        {
            if (index + data.Length > datajpg.Length)
            {
                Debug.Print("serCam_OnDataCaptured() ERROR.");
            }
            Array.Copy(data, 0, datajpg, index, data.Length);
            index += data.Length;

            Debug.Print("OnDataCaptured: Index = " + index + ", datajpg.Length = " + datajpg.Length + ", data.Length = " + data.Length + ", datajpg.Length - index = " + (datajpg.Length - index));
        }
    }
}

Is this all output? I don’t see the output for the last 40 bytes.

That was what I noticed too. I wonder if there’s a condition where the last 40 bytes is never de-spooled.

Checking on codeplex this is the section of the driver that would be the problem (because each call to this is made with a “BLOCK_SIZE” of 128):

        private void ReadBytes(byte[] data, int offset, int count)
        {
            //serialPort.Read(data, offset, count);
            DateTime startTime = DateTime.Now;
            do
            {
                int bytesRead = serialPort.Read(data, offset, count);
                offset += bytesRead;
                count -= bytesRead;

                if ((DateTime.Now - startTime).Milliseconds > serialPort.ReadTimeout)
                    throw new Exception("Timeout");
            } while (count > 0);

            if (count != 0) throw new Exception("Data size mismatch");
        }

I wonder if the serial port read is not timing out in that last read. Hoss, can you grab the codeplex code and step into it?
http://gadgeteer.codeplex.com/SourceControl/changeset/view/24955#273380 (I hope that’s the right link :slight_smile: it’s sercam_42.cs )

Exactly. :slight_smile: It never finishes sending data and never fires the FinishedCaptureData event.

FYI, the value of the last line (40 in this example), changes every time I run the program, but is always under 128. I’m guessing this is because the jpeg data is a little different each time it takes a picture. But it does mean that its always the last chunk/block of data it stops on.

Sounds good, I’ll give that a try tonight.

@ Brett - I think that is it. It blocks until it gets all 128 that are requested and there is only 40 left. Good catch!

So I stepped through SerCam_42.cs. This is getting on the complex side for me, but I think I see where the issue is.


        private void ReadFrameBuffer(int size)
        {
            StartDataCaptured(this, size);
            CleanSerialPort();
            byte[] size4byte = new byte[4];
            byte[] header = new byte[10];
            int blocks = size / BLOCK_SIZE;
            int remaining = size%BLOCK_SIZE;
            size4byte[0] = (byte)(size >> 24);
            size4byte[1] = (byte)(size >> 16);
            size4byte[2] = (byte)(size >> 8);
            size4byte[3] = (byte)(size);
           

            byte[] data = new byte[BLOCK_SIZE];
            byte[] send = new byte[] { 0x56, 0x00, 0x32, 0x0C, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x00, size4byte[0], size4byte[1], size4byte[2], size4byte[3], 0x0B, 0xB8 };
            serialPort.Write(send, 0, send.Length);
            Thread.Sleep(30);
            // Read header
            ReadBytes(header, 0, 5);

            while (blocks > 0)
            {
                ReadBytes(data, 0, BLOCK_SIZE);
                OnDataCaptured(this, data);
                blocks--;

            }
            if (remaining > 0)
            {
                data = new byte[remaining];
                ReadBytes(data, 0, remaining);
                OnDataCaptured(this, data);
            }
            
            // read header
            ReadBytes(header, 5, 5);
            FinishDataCaptured(this);
        }

Keep in mind, because its jpeg, the size of the total data changes slightly each run so numbers I say here are just what I got on this particular debugging session.

So in the above code, the

remaining

variable was 32, and in that if statement, when it called

ReadBytes(data, 0, remaining);

(below), the serialPort.Read() only read in 8 bytes, even though it was expecting 32. So the DoWhile is true because 8 > 0, so it gets stuck in the DoWhile because from this point on, SerialPort.Read() always returns 0 bytes so DoWhile never goes false.


        private void ReadBytes(byte[] data, int offset, int count)
        {
            //serialPort.Read(data, offset, count);
            DateTime startTime = DateTime.Now;
            do
            {
                int bytesRead = serialPort.Read(data, offset, count);
                offset += bytesRead;
                count -= bytesRead;

                if ((DateTime.Now - startTime).Milliseconds > serialPort.ReadTimeout)
                    throw new Exception("Timeout");
            } while (count > 0);

            if (count != 0) throw new Exception("Data size mismatch");
        }

I think this line (or something related to it) is not calculating the remaining bytes correctly.

int remaining = size%BLOCK_SIZE;

In the driver, this is how it is instructed to take the picture

   private void ts()
        {
            isBusy = true;
            ResetCamera();
            StopFrameBufferControl();
            ReadFrameBuffer(GetFrameBufferLength());
            isBusy = false;
        }


I wonder a couple of things. Does StopFrameBufferControl ever return FALSE? That condition isn’t checked.

Then, I wonder if the header is meant to be subtracted from the remaining size, in the ReadFrameBuffer portion.

           ReadBytes(header, 0, 5);

But in your example, there’s a 24-byte mismatch in Remaining and what you get - can you do more tests to see if that stays the same, as it might show an error in the calculation in GetFrameBufferLength() instead.

@ Brett - Thanks!

I verified that StopFrameBufferControl() is returning True every time.

Here is a list of the left over remaining bytes for several iterations…
24
25
24
25
15
25
25
25
14

So it does vary each time, however it does appear to like 25 the most.

For convince, here is the code that gets the value for the size variable…


        private int GetFrameBufferLength()
        {
            CleanSerialPort();

            byte[] send = new byte[] { 0x56, 0x00, 0x34, 0x01, 0x00 };

            serialPort.Write(send, 0, send.Length);

            Thread.Sleep(DELAY_TIME);

            int size = 0;
            byte[] receive = ReadBytes();

            if (receive != null && receive.Length >= 9)
            {
                if (receive[0] == 0x76 && receive[1] == 0 && receive[2] == 0x34 && receive[3] == 0 && receive[4] == 0x4)
                {
                    size = receive[5] << 24 | receive[6] << 16 | receive[7] << 8 | receive[8];
                }
            }
            return size;
        }

Anyone got any ideas or other things to try for this issue?

@ andre.m - I’m not exactly sure what that means. If it just means to try using it without SerCam_42.cs, I might be able to try, although not sure what I would do any different than their code which is already using SerialPort.

We are working on this. It looks like we have problem when using UART + debug VS2010 at the same time.
If you use external power or do not run debug, it should be fine.

it works good on Spider so driver has no problem.

You can run VS2010 debug but:

  • Use external power
  • Remove all Debug.Print(“xxxx”) in serCam_OnDataCaptured event. This event requires very very quick to catch data from SerCam, if we add debug in there, data will be lost

Thanks Dat! It seems that power is an issue with Hydra as that has been a recommendation for pretty much every issue I’ve had so far. If i had known it was that big an issue at time of purchase, I wouldn’t have bought the USB only power and just spent the extra on the external power one. It would be good if the descriptions for Hydra and power modules either required DP module or highly recommend it.

I’ll order the DP, hopefully it helps with other issues too. :slight_smile:

@ andre.m - TE35, camera and a button. I had same results using a powered hub, but don’t know if my hub is any better than PC’s port. I should hack up a USB cable and put a meter on it. Is the 130ma an average or max?

Ran into this tonight, ouch. Hopefully progress is happening and a fix is coming soon.

I’ve been busy last couple of weeks and haven’t been able to spend much time on this, but I too hope a fix is coming. :slight_smile:

I did try running code without any Debug.Print() lines, and connected to a USB power adapter that is 1000ma, but same issue. Even tried without LCD screen attached so camera was only module plugged into the board (I put one Debug.Print() in the finished event, but it never fired).

We are going to change the SerCam driver same with WebCam so it will be easier to use.
We will test this issue while we are doing that.