I am integrating a LinkSprite JPEG camera with an Arduino Pro Mini and an SD card. SD card is connected to SDI pins (10-13) Camera is on 4,5 with SopftwareSerial. I'm getting correct return messages from camera to supplied commands (reset, image size, take picture, read) but for some reason I am not getting a properly formatted JPEG image on the SD card. My purpose in posting is to ask people who have integrated this camera what kind of pitfalls they ran into to see if I've fallen down that same hole.
I have attempted two approaches to reading out the data. 1) is to read the data out in 32 byte chunks and 2) is to read out the whol image and store the data one byte at a time. Neither has worked (obviously, or I would not be writing...) but the behavior I am seeing with either approach is unexpected and I attribute this to the fact that I am a software hobbiest and am new to serial ports and integration projects such as this.
Approach 1) Reading 32 bytes at a time: I have the most success with this but I have an intermittent issue that the image size returned by the camera is sometime 2x too big (24Kb vs 12Kb) campared to the amount of data I read before hitting the end of image tag. Last night, for instance, the data I read out was about the same size as the size reported by the camera but minutes later with the same scene it started returning the 2x size again. I know the reported size is All other numbers in the camera returned message are correct expet the size number. Not sure what could be causing this. I'm guessing I could be missing every other byte for some reason. Some insights would be helpful here if you think this might be how I'm using the serial port.
Approach 2) Reading the whole image at once: When I do this I am able to read about 80 bytes or so before I start getting repeating "FF"s. I assumed I was reading out the data faster than the camera could send it to the serial port but even when I put a delay statement into the read loop the image data stops after 80 bytes or so. I'm worried that the process of writing to the SD card takes too long and the serial buffer is getting bored...
I will post code later today, I do not have it on this computer but I wanted to pose the question and see if I could get some lessons learned from people who have been down this path.
Thanks in advance.
Jim
salacain:
I assumed I was reading out the data faster than the camera could send it to the serial port but even when I put a delay statement into the read loop the image data stops after 80 bytes or so.
This immediately makes me guess that you are not reading the SoftwareSerial correctly, because if you are there should be no possibility of reading bytes faster than they were being written and no possible need for delays. If the delays are big enough to prevent underflow then naturally they will also be getting big enough to provoke overflow on the serial link.
I suggest you need to post your code before you get any further. If I were you, I'd remove the SD card from the equation and produce a minimal sketch that just read frames from the camera and reported the size. That will enable everyone to focus on the problem without having to understand your whole sketch.
OK, I stripped out the SD card code from the JPEG camera sketch and it did behave more correctly (image sizes are consistently more reasonable) though apparently not exact so I think I still have issues.
Here is the JPEG camera software (attached) and here is the output from this code.
Serial began
Clearing input buffer
Reset Command Sent
76
0
26
0
0
Clearing input buffer
Take Photo Command Sent
76
0
36
0
0
Clearing input buffer (before Image Size)
Get Image Size:
76
0
34
0
4
0
0
2E
B8
11960
Image End: FF D9
packets: 0 bytes read: 11776 bytes remaining: 184
OK, I stripped out the SD card code from the JPEG camera sketch and it did behave more correctly (image sizes are consistently more reasonable) though apparently not exact so I think I still have issues.
This stuff?
const int chipSelect = 10;
File dataFile;
if (!SD.begin(chipSelect)) {
Serial.println(" Card failed, or not present"); // don't do anything more:
return;
}
dataFile = SD.open(charBuf, FILE_WRITE); // Open new file on SD Card
The LinkSprite appears to send data in response to various commands. It is up to you to read that data before buffers overflow.
On the other hand, you need to wait for the data to arrive. Here, you see that there is at least one byte available to read, and read all 37 of them. Not really a good idea, in my opinion.
Ooops... Need to hit the save button before I attach the sketch. Here's the code I said I would attach...
As for waiting for the data to arrive, my experience with the camera is that the 42 byte that the camera is sending always gets into the buffer faster than I can read them and the buffer won't overflow because the camera only delivers 32 bytes of image data and 5 bytes each of preamble and trailing data.
I'm guessing that the better software practice is to read all the data out of the buffer and keep looping on the status test until I get all 42 bytes? I'm alway interested in improving my technique.
As for the delay someone said I should give the camera a little time to start sending the data. I suppose the delay could allow the buffer to overflow but in this particular case the camera is sending so little data there is no risk of that. Nevertheless, when I attempt to read out the entire image at once I can see this being an issue. How do I know if the buffer has undergone an overflow?
I haven't reviewed the whole sequence of interactions with the camera, but three things leap out at me:
After sending a command to the camera, you then throw away the contents of the serial buffer. Two questions: Why would there be anything in the serial buffer at this point? How do you know that you aren't throwing away part of the response to the command that you just sent? It would seem more sensible to empty the buffer (if necessary) immediately before sending the command. But I question why there would be any unexpected input in the buffer anyway. If there is, I'd say you're doing something wrong elsewhere.
When reading the response from the camera you check there is at least one input character available and then read lots of characters. It's just a matter of luck and timing how much data is actually in the buffer. You need a better scheme for reading bytes from the buffer as they arrive and forming them into a message. I suggest pulling them out of the serial port into a byte array and see what's in the array to decide whether you have got a complete message there. I don't know enough about that serial protocol to say how you should recognise the start/end of a message, but you absolutely need to have some way.
You have some delay() calls in the code to read from the serial port. If these are necessary for correct behaviour then you're doing it wrong. (For example, putting a fixed delay after sending the command will increase the chance that a whole response will be waiting for you to read by the time you start reading from the serial port - but if you process the serial port correctly, you will not be relying on having the whole message waiting for you.
OK, per your request, I have removed the delay()'s with no ill effect.
I have restructured the flow to send the read command and then wait until the camera has sent the full 42 byts of data intot he serial buffer. I then read the serial buffer, and remove the preamble and trailing data to get at the image data. As I'm testing this approach, I'm printing out the data in the serial buffer. I should be seeing a HEX 76 as the first value sent by the camera but for some reason the first value is a HEX FF followed by a HEX 76. I added a buffer purge prior to the camera read command but even with that I'm getting the FF. Any thoughts why that value would be showing up in the buffer? I'm going to keep noodling on this but would appreciate isight into why this might be happening.
It's conceivable that the camera is sending 0xFF for some reason (do you have a spec for the serial protocol being used?), but this is also the value that you get if you call serial.read() when there is no data available in the buffer. So, make sure that you have called serial.available() and got back a positive answer before you do that read().
OK, last night I restructured the sketch. I put in a while loop that waits until all 42 bytes from the camera are in the serial buffer. Then I read the data into an array and then evaluate the protocol compliance of the received data. This worked very well and has taught me a lot about how to really use a serial port. There are some minor errors cropping up in some of the data packets but I attribute those single digit differences to bit errors resulting from the jumper wire construction of my prototype circuit.
I haven't had a chance to look at the image that comes out of this process yet but I'm still concerned about the inconsistency between the image size value I get from the camera and the actual size that is determined by how many bytes I read. When I applied the same process to reading the serial buffer for the image size message it consistently gives me an image size which is close to twice the size of the image I actually read. The camera gives my the size in a two byte message with a MSB and an LSB. I use the arduino word command to combine the two into a single long value. I'm worried that I'm either combining these values incorrectly. (Remeber, I'm not a software person...) what is the best way to check that I'm getting a correct value?
I have gotten this working using the example provided months ago, with one exception that I recall. There was something wrong with the last buffer being sent. Instead of debugging this, I just ignore it, leaving a few bad pixels at the bottom of the image.
@sbright33:
So did you take the approach to read the image in chunks (like 32 bytes at a time) or did you take the approach of reading the whole image at once? Just curious...
OK, I added the SD card code back into the sketch and ran the program (getting basically the same results I was getting with no SD code included) and the file does not open on my PC. It says the file was not correctly formatted (photoshop says that the file may be incomplete or truncated).
I guess one thing I can look into is whether all the data is making it to the SD card.
The current code (without the SD card code) is below
I removed the code. It was an old version without the updates. I will post the updated code later... Sorry.
Early in my attempts to get the camera to respond to commands, I found that I needed to clear out the serial buffer prior to sending the command so that when I got the camera response it would be "clean". Now that I understand how the camera operates much better I can probably discard that code and look for the specific number of bytes that I know the camera is sending.
I am reposting the code below. Apparently the previous post was an old version which did not include the changes that I had made. This version watches the serial port to make sure the data has been delivered prior to reading out the data.
Regarding the code I have in there to clear the buffer, there are discrete steps in the process of using the camera and my early attempts to read data from the camera I ran into issues with data being in the serial buffer that I did not expect. Clearing out the buffer after some of the steps helped stabilize the interface. Now that I understand the camera's protocol better I am able to get rid of some of the code found its way in as part of the debugging process. Still, I am getting some data back from the camera that is not documented in the camera's protocol. For instance, after I send the "Take Photo" command, I get back the correct 5 byte message from the camera but I also get back about 20 or 30 additional bytes that are mysterious to me. The data is not part of the image so I have no problem clearing it out but I would like to know what the data is.
With this software, everything looks like it should be working so at this point I'm looking at the occasional bit errors that are cropping up and wondering if that is creating an invalid jpeg file. I'm going to replace my flimsy jumper wires with something more robust to see if that cleans up the data.
#include <SoftwareSerial.h>
byte incomingbyte, sizeMessage[8], readtest[2];
long a=0x0000; //Read Starting address
int i=0, b = 0, packetcount=0, imageSize, test;
boolean ImageEnd = 0;
uint8_t MH,ML;
void SendResetCmd();
void SendTakePhotoCmd();
void SendReadDataCmd();
void StopTakePhotoCmd();
void GetImageSize();
SoftwareSerial mySerial(4,5); //Configure pin 4 and 5 as soft serial port
void setup()
{
Serial.begin(38400);
mySerial.begin(38400);
// Reset Camera *************************************
SendResetCmd();
while(mySerial.available()>0)
{
incomingbyte=mySerial.read();
}
// After reset, wait 2-3 second to send take picture command
delay(2000);
// Take Picture **************************************
SendTakePhotoCmd();
Serial.println("Take Photo");
while(mySerial.available()>0)
{
incomingbyte=mySerial.read();
Serial.println(incomingbyte, HEX);
}
// Clear Serial Buffer ********************************
while(mySerial.available()>0)
{
Serial.println("Clear Buffer");
incomingbyte=mySerial.read();
Serial.println(incomingbyte, HEX);
}
// Get Image Size **************************************
GetImageSize();
i=0;
Serial.println("Get Image Size");
while(mySerial.available()<9)
{
Serial.print(" is: ");
Serial.print(mySerial.available());
}
Serial.print(" is: ");
Serial.println(mySerial.available());
while(mySerial.available()>0)
{
incomingbyte=mySerial.read();
Serial.println(incomingbyte, HEX);
sizeMessage[i] = incomingbyte;
i++;
}
imageSize=word(sizeMessage[7], sizeMessage[8]);
Serial.print("imageSize: ");
Serial.println(imageSize);
}
void loop()
{
byte a[42];
packetcount = 0;
// Read Data Packet ********************************
while(!ImageEnd)
{
// Serial.println("Image End loop");
SendReadDataCmd();
packetcount++;
i = 0;
while(mySerial.available()<42)
{
Serial.print(" sb: ");
Serial.print(mySerial.available());
}
Serial.print(" sb: ");
Serial.println(mySerial.available());
while(i < 42) // Read 42 bytes in from serial buffer
{
while(mySerial.available()>0)
{
incomingbyte=mySerial.read();
a[i] = incomingbyte;
i++;
}
}
// Test packet to see if preable 5 bytes equals the trailing 5 bytes
test = 0;
for ( i=0; i <5; i++)
{
if( !(a[i] == a[i+37])) test += 1;
Serial.print(i);
Serial.print(": ");
Serial.print(a[i], HEX);
Serial.print(" ");
Serial.print(a[i+37], HEX);
Serial.print(" ");
}
if( test>0)
{
Serial.print("Bad Packet*************************");
Serial.println(test);
}
else
{
Serial.println(".");
}
// Loop through data packet
for ( i=4; i <38; i++)
{
b++;
if((a[i-1]==0xFF)&&(a[i]==0xD9)) // Test for end of image tag
{
Serial.print("Image End: ");
Serial.print(a[i-1],HEX);
Serial.print(" ");
Serial.println(a[i],HEX);
Serial.print("packets: ");
Serial.print( packetcount);
Serial.print(" bytes read: ");
Serial.print( b);
Serial.print(" bytes remaining: ");
Serial.println( imageSize-b);
packetcount++;
ImageEnd=1;
}
}
}
}
//Send Reset command
void SendResetCmd()
{
mySerial.write(0x56);
mySerial.write(byte(0x00));
mySerial.write(0x26);
mySerial.write(byte(0x00));
}
//Send take picture command
void SendTakePhotoCmd()
{
mySerial.write(0x56);
mySerial.write(byte(0x00));
mySerial.write(0x36);
mySerial.write(0x01);
mySerial.write(byte(0x00));
}
//Read data
void SendReadDataCmd()
{
MH=a/0x100;
ML=a%0x100;
mySerial.write(0x56);
mySerial.write(byte(0x00));
mySerial.write(0x32);
mySerial.write(0x0c);
mySerial.write(byte(0x00));
mySerial.write(0x0a);
mySerial.write(byte(0x00));
mySerial.write(byte(0x00));
mySerial.write(MH);
mySerial.write(ML);
mySerial.write(byte(0x00));
mySerial.write(byte(0x00));
mySerial.write(byte(0x00));
mySerial.write(0x20);
mySerial.write(byte(0x00));
mySerial.write(0x0a);
a+=0x20; //address increases 32£¬set according to buffer size
}
void StopTakePhotoCmd()
{
mySerial.write(0x56);
mySerial.write(byte(0x00));
mySerial.write(0x36);
mySerial.write(0x01);
mySerial.write(0x03);
}
void GetImageSize()
{
mySerial.write(0x56);
mySerial.write(byte(0x00));
mySerial.write(0x34);
mySerial.write(0x01);
mySerial.write(byte(0x00));
}
Some people have reported reading in the whole image at once, I presume into an SD card. It could be that they driving the camera directly from a PC rather than a 'duino and then they wouldn't have a size limit.
I thought I would give an update to my issues regarding my attempts to integrate the LinkSprite JPEG camera to the Arduino Pro Mini. I have been attempted to run the camera at 3.3V. After getting inconsistent data out of the camera and after working on improving my abilities with regard to the management of the Serial Buffer (Thanks!) and I was still getting inconsistent results. I commanded the software to reread the same data three times (data within the camera starting at a particular address) and in some cases I got the same image values, in some cases one of the three was different and in some cases all three values were different. Broken camera? I went to the SparkFun page for the camera and it states as a message near the top of the page that when attempting to power this camera at 3.3V, one must decrease the baud rate from 38400 or one will get inconsistent results. Sounds like my problem. I attempted to decrease the baud rate to 19200 but when I would send the command to change the rate I would never get a response from the camera. I suspect that when operated at 3.3V changing the baud rate is too much of a load. Next option was to operate the camera at 5V. I installed a voltage divider to bring the camera transmit data down to 3.3V and ran the code and got no response. I suspected that the camera, when operated at 5V, needs to see incoming data at 5V as well so I installed a logic level shifter to bring the 3.3V data up to 5V and still no response. I'm pretty well ready to throw this camera out the window at this point. I think my final option is the follow the procedure for checking out the camera using X-CTU. My problem now is I'm a Mac user and I operate X-CTU via VM-Ware and I need to figure out how to configure VM-Ware to set up a COM3 port. I know this is out of scope but suggestions are welcome. Thanks again to all who helped get me to this point even if ther is not a lot of joy (yet).