I am currently working on the optimization of a piece of code that I use to control a Grove Camera.
Doing so, I noticed that if I change the variable CMD_PREFIX (and only that one), from Byte to Char, the sketch uses 1704 bytes of program storage space instead of 2790 bytes.
The difference is quite huge and I really need that space for the whole program, but sadly, the camera is not working whith this variable set as Char.
I understand that the difference between this variable and the others is that it is the only one that cannot be a Char because the value is superior to 127, but I can't explain why the difference is so important.
For instance, if I remove all "if (resp[0] == CMD_PREFIX ... " from the code and stop using this variable, I do not even gain that much space, which I really cannot understand.
Anyone have an idea so that I could gain all that memory ?
I can't open your .ino file on my phone, if you want more people to read it, please post it using code tags.
Your premise cannot be correct. Byte and char are the same size, 8 bits, so changing from one to the other could not affect the space used. The difference is that char is signed, so the range is -128 to +127 and byte is unsigned with a range 0 to 255.
Please post your code in a post instead of an attachment; it's smaller than the 9000 char limit.
Type [code]
Paste your code after that
Type [/code] after that
OP's code
#define PIC_PKT_LEN 256 // Taille des paquets de transfert de la photo de la caméra vers la carte
// Bytes de commande de la caméra
const byte CMD_SYNC = 0x0D;
const byte CMD_INITIAL = 0x01;
const byte CMD_LIGHTFREQ = 0x13;
const byte CMD_PACKAGESIZE = 0x06;
const byte CMD_SNAPSHOT = 0x05;
const byte CMD_GETPICTURE = 0x04;
const char CMD_PREFIX = 0xAA;
const byte CMD_DATA = 0x0A;
const byte CMD_ACK = 0x0E;
// Configuration de couleur pour la caméra
const byte CT_GRAYSCALE_2 = 0x01;
const byte CT_GRAYSCALE_4 = 0x02;
const byte CT_GRAYSCALE_8 = 0x03;
const byte CT_COLOR_12 = 0x05;
const byte CT_COLOR_16 = 0x06;
const byte CT_JPEG = 0x07;
// Resolution de prévisualisation pour la caméra
const byte PR_80x60 = 0x01;
const byte PR_160x120 = 0x03;
// Resolution JPEG pour la caméra
const byte JR_80x64 = 0x01;
const byte JR_160x128 = 0x03;
const byte JR_320x240 = 0x05;
const byte JR_640x480 = 0x07;
// Configuration de fréquence de lumière pour la caméra
const byte FT_50Hz = 0x00;
const byte FT_60Hz = 0x01;
// Type d'image prise par la caméra
const byte PT_SNAPSHOT = 0x01;
const byte PT_PREVIEW = 0x02;
const byte PT_JPEG = 0x05;
// Type de snapshot pris par la caméra
const byte ST_COMPRESSED = 0x00;
const byte ST_UNCOMPRESSED = 0x01;
unsigned long picTotalLen = 0; // Pour stocker la taille de la photo lorsqu'elle est prise
void setup()
{
Serial.begin(115200);
Synchroniser_Camera();
Initialiser_Camera();
Definir_Frequence_Lumiere_Camera();
Definir_Taille_Paquets_Camera();
}
void loop()
{
Prendre_et_Envoyer_Photo_Instatanee();
}
/*********************************************************************/
void sendCmd(byte cmd[])
{
Serial.flush();
Serial.write(CMD_PREFIX);
Serial.write(cmd, 5);
}
/*********************************************************************/
void Synchroniser_Camera()
{
Serial.setTimeout(500);
byte resp[6];
byte cmd[] = {CMD_SYNC, 0x00, 0x00, 0x00, 0x00};
while (1)
{
sendCmd(cmd);
if (Serial.readBytes((byte *)resp, 6) != 6)
continue;
if (resp[0] == CMD_PREFIX && resp[1] == CMD_ACK && resp[2] == CMD_SYNC && resp[4] == 0 && resp[5] == 0)
{
if (Serial.readBytes((byte *)resp, 6) != 6)
continue;
if (resp[0] == CMD_PREFIX && resp[1] == CMD_SYNC && resp[2] == 0 && resp[3] == 0 && resp[4] == 0 && resp[5] == 0)
break;
}
}
cmd[0] = CMD_ACK;
cmd[1] = CMD_SYNC;
sendCmd(cmd);
}
/*********************************************************************/
void Initialiser_Camera()
{
byte resp[6];
byte cmd[] = {CMD_INITIAL, 0x00, CT_JPEG, PR_160x120, JR_640x480};
while (1)
{
sendCmd(cmd);
if (Serial.readBytes((byte *)resp, 6) != 6)
continue;
if (resp[0] == CMD_PREFIX && resp[1] == CMD_ACK && resp[2] == CMD_INITIAL && resp[4] == 0 && resp[5] == 0)
break;
}
}
/*********************************************************************/
void Definir_Frequence_Lumiere_Camera()
{
byte resp[6];
byte cmd[] = {CMD_LIGHTFREQ, FT_50Hz, 0x00, 0x00 , 0x00};
while (1)
{
sendCmd(cmd);
if (Serial.readBytes((byte *)resp, 6) != 6)
continue;
if (resp[0] == CMD_PREFIX && resp[1] == CMD_ACK && resp[2] == CMD_LIGHTFREQ && resp[4] == 0 && resp[5] == 0)
break;
}
}
/*********************************************************************/
void Definir_Taille_Paquets_Camera()
{
byte resp[6];
byte cmd[] = {CMD_PACKAGESIZE, 0x08, PIC_PKT_LEN & 0xFF, (PIC_PKT_LEN >> 8) & 0xFF , 0x00};
while (1)
{
sendCmd(cmd);
if (Serial.readBytes((byte *)resp, 6) != 6)
continue;
if (resp[0] == CMD_PREFIX && resp[1] == CMD_ACK && resp[2] == CMD_PACKAGESIZE && resp[4] == 0 && resp[5] == 0)
break;
}
}
/*********************************************************************/
void Prendre_et_Envoyer_Photo_Instatanee()
{
byte resp[6];
byte cmd[] = {CMD_GETPICTURE, PT_JPEG, 0x00, 0x00, 0x00};
while (1)
{
sendCmd(cmd);
if (Serial.readBytes((byte *)resp, 6) != 6)
continue;
if (resp[0] == CMD_PREFIX && resp[1] == CMD_ACK && resp[2] == CMD_GETPICTURE && resp[4] == 0 && resp[5] == 0)
{
if (Serial.readBytes((byte *)resp, 6) != 6)
continue;
if (resp[0] == CMD_PREFIX && resp[1] == CMD_DATA && resp[2] == PT_JPEG)
{
picTotalLen = ((unsigned long)resp[3]) | ((unsigned long)resp[4] << 8) | ((unsigned long)resp[5] << 16);
break;
}
}
}
Envoi_Photo_TCP();
}
/*********************************************************************/
void Envoi_Photo_TCP()
{
word pktCnt = (picTotalLen) / (PIC_PKT_LEN - 6);
if ((picTotalLen % (PIC_PKT_LEN - 6)) != 0)
pktCnt += 1;
byte cmd[] = {CMD_ACK, 0x00, 0x00, 0x00, 0x00};
byte pkt[PIC_PKT_LEN];
for (word i = 0; i < pktCnt; i++)
{
cmd[3] = i & 0xFF;
cmd[4] = (i >> 8) & 0xFF;
int retry_cnt = 0;
retry:
delay(10);
sendCmd(cmd);
word cnt = Serial.readBytes((byte *)pkt, PIC_PKT_LEN);
byte sum = 0;
for (int y = 0; y < cnt - 2; y++)
{
sum += pkt[y];
}
if (sum != pkt[cnt - 2])
{
if (++retry_cnt < 100)
goto retry;
else
break;
}
Serial.write((const byte *)&pkt[4], cnt - 6);
}
cmd[3] = 0xF0;
cmd[4] = 0xF0;
sendCmd(cmd);
}
@PaulRB
It indeed makes a difference, just checked it.
Changing from byte to char in itself will not save any space, they both take the same amount of storage space.
The reason for the change in size is that your If statements are always false when using CHAR, so the compiler is simply eliminating the if statement and all the code within it. If you set the preferences to show all compiler warnings, you will see numerous instances of
/home/pi/Arduino/sketch_jul28d/sketch_jul28d.ino: In function 'void Synchroniser_Camera()':
/home/pi/Arduino/sketch_jul28d/sketch_jul28d.ino:86:17: warning: comparison is always false due to limited range of data type [-Wtype-limits]
if (resp[0] == CMD_PREFIX && resp[1] == CMD_ACK && resp[2] == CMD_SYNC && resp[4] == 0 && resp[5] == 0) ^
EDIT:
A bit of an explanation of why it is always false. The compiler is interpreting char as a signed number, so when you assign 0xAA to a char, the most significant bit being a 1 makes it a negative number. resp[0] is declared as a byte, which is an unsigned number, and can never be negative, so the two can never be equal. If you assign a value to the char that has a 0 in the most significant bit, the comparison will work and the compiler will let you get away with it.
@PaulRB & sterretje, noted for the best way to post a code, thank you sterretje for doing it.
@sterretje, I agree that the code is strange at that point, and I think that using a loop like that (while(1)) might result one day in an infinite loop and block my program.
Although, the code comes from there (Grove - Serial Camera Kit | Seeed Studio Wiki), which is the link given by the website where I bought my camera.
If you have any idea of how to improve it, I would be very interested.
@david_2018, I did not know about this option, and I can see that the reason you give is obviously the reason of the problem.
The issue now is that the camera only "understands" the 0xAA or equivalent (I tried CMD_PREFIX = 170 and CMD_PREFIX = -86 and get the camera to work, but it did not change the problem of space).
I can try to make "resp" a char instead of a byte, but it was like that in the original code and if I remeber well, I had the same problem.
PS : I hope my english is not too bad, don't hesitate to tell me if some things are not clear.
I don't think there is much you can do to reduce the size of that sketch. Changing the constants from byte to char shouldn't make much difference, most if not all of the constants will not actually be stored in the program, but are replaced with their values by the compiler, and integrated into the actual instructions where they are used.
The reason you were seeing such a massive size difference in the code when you changed CMD_PREFIX to a char was that the compiler was eliminating almost all of your code. When the program starts, it executes the setup() function, which in your sketch is:
When setup calls Synchroniser_Camera(), it executes this function:
void Synchroniser_Camera()
{
Serial.setTimeout(500);
byte resp[6];
byte cmd[] = {CMD_SYNC, 0x00, 0x00, 0x00, 0x00};
while (1)
{
sendCmd(cmd);
if (Serial.readBytes((byte *)resp, 6) != 6)
continue;
if (resp[0] == CMD_PREFIX && resp[1] == CMD_ACK && resp[2] == CMD_SYNC && resp[4] == 0 && resp[5] == 0)
{
if (Serial.readBytes((byte *)resp, 6) != 6)
continue;
// ******** this if statement is always FALSE with CMD_PREFIX as a char**********
if (resp[0] == CMD_PREFIX && resp[1] == CMD_SYNC && resp[2] == 0 && resp[3] == 0 && resp[4] == 0 && resp[5] == 0)
break;
}
}
cmd[0] = CMD_ACK;
cmd[1] = CMD_SYNC;
sendCmd(cmd);
}
When CMD_PREFIX is declared as a char, the final if statement is always false, the compiler removes it and the code within it, because it will never be executed, and then determines that the code will never leave the while (1) loop, so almost everything past that in the sketch is eliminated.
You are only using 8% of the program space of an UNO. A sketch that does nothing but start the Serial port and print an empty line takes almost 1500 bytes, so there is a lot of overhead that you cannot eliminate.