First post here, but clearly I'm doing something wrong or missing something with my code. I'm still learning though and hope to figure this out.
I have an AVRCam from jrobot.com and I have firmware optimized for line tracking that breaks 1 line into 8 individual bounding boxes. The data being transmitted from the camera and confirmed by the camera's software should be as follows:
There are NO spaces between bytes in a color-tracking packet. The current color map will be used to map the sampled pixel values into actual colors.>
Byte 0: 0x0A – Indicating the start of a tracking packet
Byte 1: Number of tracked objects (0x00 – 0x08 are valid)
Byte 2: Color of object tracked in bounding box 1
Byte 3: X upper left corner of bounding box 1
Byte 4: Y upper left corner of bouding box 1
Byte 5: X lower right corner of boudning box 1
Byte 6: Y lower right corner of boudning box 1
Byte 7: Color object tracked in bound box 2
….
Byte x: 0xFF (indicates the end of line, and will be sent after all tracking info
for the current frame has been sent)
Example: The user wants to begin tracking the colors set in the color map.
User: ET\r
AVRcam: ACK\r
AVRcam: 0x0A 0x05 0x04 0x12 0x09 0x36 0x38 0x01 0x25…0xFF
| | | | | | | | | |--End of line
| | | | | | | | |--X upper left corner…
| | | | | | | |--color2
| | | | | | |--Y lower right corner of box1
| | | | | |--X lower right corner of bounding box1
| | | | |--Y upper left corner of bounding box1
| | | |--X upper left corner of bounding box1
| | |
| | |
| | |--color1
| |--number of tracked blobs
|--Indicates start of color-tracking packet
AVRcam: 0x0A 0x05 0x06 0x32 0x39 0x76 0x98…0x0F
But, it doesnt work right otherwise we wouldnt be here. My thought is to use Serial.read() and Serial.peek() to read 0xFF(dec255) and then if Serial.peek() = 0x0A essentially if(Serial.read()==0xFF && Serial.peek() == 0x0A) the next data you would store is the number of tracked objects. I consistently read values like 88 hex etc which are or 100+ decimal values. I understand reading the data in, its syncing the bytes properly.
I've seen mention of reading the data into a string and separating it out but, there arent any data separators so I think I need to do it byte by byte.
The while loop will be entered if there is one byte ready to be read. You then read two bytes. Not a good thing.
Then, you call FillData to read the remaining 40 bytes of the one that has arrived. Again, not a good thing.
My thought is to use Serial.read() and Serial.peek() to read 0xFF(dec255) and then if Serial.peek() = 0x0A essentially if(Serial.read()==0xFF && Serial.peek() == 0x0A) the next data you would store is the number of tracked objects.
You don't need to be peeking. You do need to be waiting for the serial data to arrive.
I consistently read values like 88 hex etc which are or 100+ decimal values. I understand reading the data in, its syncing the bytes properly.
I disagree. You don't understand serial data transmission at all. Hopefully, now you see the problem, and a way to address the issue.
I've seen mention of reading the data into a string and separating it out but, there arent any data separators so I think I need to do it byte by byte.
Especially as it is not character data being received.
I agree with what you are saying regarding the reading and peeking. I just ran a simple sketch to print the incoming bytes in decimal and hex to read what the camera is receiving.
8A 138 << should read A not 8A
83 131<<should read 3 not 83
80 128 << color object 1
6C 108<< Upper left X
B8 184<< Upper left Y
87 135<<Lower Right X
4A 74<< Lower Right Y
80 128<<Color object 2
6C 108<<Upper leftX
FD 253<<Upper left Y
F8 248<<Lower Right X
5D 93<< Lower right Y
80 128
ED 237
5E 94
F8 248
B2 178
FF 255<< end of tracking packet
However the numbers being returned are not correct. The difference between upper left y and lower right y cannot be more than 17 pixels with the given camera firmware. And when tracking in the camera software, I return valid data. Do I need to declare the variable temp different? Or am I doing something else wrong?
will the bytes read into the array differ from the bytes read and printed? I would think they would be the same right? I'm still thinking I'm somehow reading in data that either isnt there, combining things, or somehow goofing up the data so it gets skewed.
If you read into a byte array it will be the same thing. Reading at 115200 doesn't give you a heap of free time. The displaying via hardware serial will cause interrupts to fire. That may account for what looks like to me, the 8 bit being set when you are not expecting it.
I actually just noticed the change in consistency when I dont try and read every value onto the serial terminal. I know that when I get my array built and need to calculate on it, I will miss incoming camera data. I know I can Disable and enable the tracking on the camera and control when the data is sent and when the camera is essentially off. However, the startup time of the camera appears to be about a second and to use this for line following that would be near impossible. I know the serial buffer holds 64 bytes of data, if I choose not too look at it and i hit the 64 bytes, does the 64 bytes in the buffer stay in there or do the new bytes knock the old ones out like a first in first out kind of system. If it holds the 64 in the buffer, I'll need to basically dump that data and read in the new frame and end up taking more time and getting more behind.
I think your problem is simpler than that--I'm not aware of any implementation of SoftwareSerial that can handle 115.2 kbaud reliably. Drop the baud rate, or use the hardware serial port for the higher-speed communications.
Looks like if software serial fills its buffer it sets a flag and discards the incoming byte.
You might be better off using hardware serial for the camera. Software serial is consuming more processor cycles than hardware serial. The hardware serial only needs to interrupt when a byte is received, software serial needs to do stuff for every bit.
At 115200 baud you are getting a byte every 1/11520 seconds (every 86 uS). That isn't a lot of time to be processing it. Especially if you are chewing up processor cycles handling each of those 8 bits.
But let's do one thing at a time. Read in a batch into an array. Print it. Check it's OK. You won't need the debugging displays later will you?
The processor has 16 clock cycles per microsecond. So you can do 1376 clock cycles (16 x 86) per incoming byte. Some instructions take 1 cycle, some take 2, a few take more. So you can do a reasonable amount in the time between bytes.
Again though, using hardware serial gives you somewhat more leeway.
Well, I got my data coming in through hardware serial with no problems. I also have decided that the camera for now will be run off its own Arduino and I'm using I2C to transmit the bytes I want (only 8 bytes out of the whole possible 43) to a second arduino that will do the rest of the processing, calculate deviation from the line and then control the motors(again with a software serial port) I ultimately would have run into trouble timing all the different communications I think.
I took a couple of minutes to play with your packet input logic and came up with three different ways to code essentially the same logic. The first is how I would prefer to do the job if it were my project:
/*----------------------------------------------------------------------------*/
/* Read a data packet from the AVRcam (Method 1, use input function) */
/*----------------------------------------------------------------------------*/
unsigned char gcam(void)
{ while (!cam.available()); /* Wait for data to arrive */
return cam.read(); /* Return received character */
} /* end: gcam() */
unsigned char *get_packet(char *buf)
{ unsigned char n,*p; /* Nr bounding boxes, buffer pointer */
for(;;) /* Until good packet received */
{ while (0x0A != getb()); /* Wait for a LF byte */
if (8 < (n = getb())) continue; /* Go around again if invalid count */
*(p = buf) = n; /* Set pointer to buffer, save count */
for (n*=5; n--; *++p = getb()); /* Read data to buffer following count */
if (0xFF == getb()) break; /* Done if following byte is RO */
} /* end: until good packet received */
return buf; /* Return pointer to input data */
} /* end: get_packet() */
/*----------------------------------------------------------------------------*/
The second is what I might try if I decided that I needed to save the overhead of a function call to read each byte:
/*----------------------------------------------------------------------------*/
/* Read a data packet from the AVRcam (Method 2, in-lined reads) */
/*----------------------------------------------------------------------------*/
unsigned char *get_packet(char *buf)
{ unsigned char n,*p;
for (;;)
{ do
{ while (!cam.available());
} while (0x0A != cam.read());
while (!cam.available());
if (8 < (n = cam.read())) continue;
*(p = buf) = n;
for (n*=5; n--;)
{ while (!cam.available());
*++p = cam.read();
}
while (!cam.available());
if (0xFF == cam.read()) break;
}
return buf;
}
/*----------------------------------------------------------------------------*/
and the third is what I might try if I felt I needed to eliminate as much call overhead as possible by in-lining the code:
/*----------------------------------------------------------------------------*/
/* Read a data packet from the AVRcam (Method 3, in-lined code) */
/*----------------------------------------------------------------------------*/
unsigned char n,*p,buf[41];
for (;;)
{ do
{ while (!cam.available());
} while (0x0A != cam.read());
while (!cam.available());
if (8 < (n = cam.read())) continue;
*(p = buf) = n;
for (n*=5; n--;)
{ while (!cam.available());
*++p = cam.read();
}
while (!cam.available());
if (0xFF == cam.read()) break;
}
/*----------------------------------------------------------------------------*/
All three approaches attempt to sync on the 0x0A byte and verify that the correct number of bytes have actually been received by checking for the 0xFF delimiter, and will discard what they think are bad packets - but it's worth noting that the 0x0A/0xFF framing isn't completely reliable if those values can occur in the packet data.
Thank you for the suggestions. I'll start with the 0x0A-0xFF part you mentioned. 0xFF isnt a possible value in the tracking packet because the highest value available for pixels is 176(Decimal) I believe. However. 0x0A Decimal is 10. Therefore, that is a valid data packet. That is why I feel to be a valid packet, you need to start with the 0xFF and essentially waste the first packet from the previous frame and start when the current byte is 0xFF and the following byte is 0x0A then, you could use the 3 byte, the number of tracked objects to set a limit for the packet by multiplying by 5.
I have decided to use the hardware serial port to communicate with the camera. I used an I2C interface between a separate Arduino board to print the data on the serial terminal. So far the data is 100% reliable in over 100,000 frames after running for an hour. That being said, I can only get the I2C data transfer to work if I use the Wire.onReceive() command and It seems to be interrupt based and is posing an issue with executing my code on the receiving board should I try to do anything with it. I'm going to try sending the data via software serial next I think. Or only transmit the I2C every 3rd or 5th execution of building the array.
I'm reluctant to accept that a 9600 baud data flow should overwhelm an Uno - and wonder if an approach like the one shown below (where the requirement for synchronicity between receipt of data and processing is removed) might allow doing the job on a single Uno...
/*----------------------------------------------------------------------------*/
/* Assumptions: */
/* 1. get_packets() can get serial data and store it into a local buffer */
/* faster than it arrives. */
/* 2. proc() can process a message in less time than it takes to fill and */
/* overrun the serial data source's buffer. */
/*----------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------*/
/* get_packets() */
/* This function dequeues an empty buffer, fills it with data, and adds it to */
/* the full buffer queue for processing. If, at any time, it cannot proceed */
/* (either because all buffers have been filled or because there is no input */
/* data available), it calls proc() to process one buffer of queued data and */
/* retries whatever it could not do. */
/*----------------------------------------------------------------------------*/
void get_packets(void)
{ element *b;
unsigned char n,*p;
for (;;)
{ while (!(b = dequeue(emty))) proc();
for (;;)
{ do
{ while (!cam.available()) proc();
} while (0x0A != cam.read());
while (!cam.available()) proc();
if (8 < (n = cam.read())) continue;
*(p = b->data) = n;
for (n*=5; n--;)
{ while (!cam.available()) proc();
*++p = cam.read();
}
while (!cam.available()) proc();
if (0xFF == cam.read()) break;
}
enqueue(full,b);
}
}
/*----------------------------------------------------------------------------*/
/* proc() */
/* If there is data waiting in the full buffer queue, this function dequeues */
/* and processes it, then adds the buffer to the empty buffer queue to be */
/* refilled. If there is no data awaiting processing, it simply returns. */
/*----------------------------------------------------------------------------*/
void proc(void)
{ element *b;
unsigned char n;
if (b = dequeue(full))
{ if (n = *(p = b->data))
{ ++p;
/* Process n sets of data (n*5 bytes) at p */
}
enqueue(empt,b);
}
}
the baud is 115.2kbps not 9600. Also, can you perhaps explain your code and how the pointers work? I am not a programmer but more a tinkerer and I know that they simplify how you can write the code but isnt the way the pointers work her similar to accessing an array/ writing to an array? The arduino reference page is useless when it comes to explaining pointers and my C book is confusing me. perhaps a literal approach to how this works will help me wrap my head around this one.
The arduino reference page is useless when it comes to explaining pointers
Because pointers are not unique to the Arduino.
and my C book is confusing me. perhaps a literal approach to how this works will help me wrap my head around this one.
Are you familiar with the post office, and the rows of mailboxes that mail is delivered to, for people that don't want mail delivered to their houses?
Memory is arranged like those rows of mailboxes. A pointer is, effectively, a number on a mailbox. The mailbox might, or might not, have a name on it, but it does have a number.
A pointer variable holds the number of the mailbox (the address of the mailbox). You can get the data in the memory location (the mail in the mailbox) using the name on mailbox (a variable) or by pointer (the number on the box).
Notice those boxes without names? They can only be accessed by pointers.
Pointers are useful because you can access a whole string of memory locations by simply incrementing the pointer (changing the number of the mailbox to access).