OTA Updates for Arduino Projects - Part II
A followup of the previous post on OTA updates. The second step is getting the bits downloaded. I added downloading capabilities to the HTTPRequest
implementation.
This is relatively easy by first detecting that it is a file coming down the wire. I do that by checking for the Content-Disposition
header presence:
...
if(strncmp(line, "Content-Disposition", 19)==0){
strtok(line, "\"");
const char * fileName = strtok(NULL, "\"");
strcpy(response.fileName, fileName);
response.file = 1;
return;
}
...
Some quick and dirty parsing (don’t you love strtok
?) gives me the fileName
. For this implementation, I always assume 8.3
filename format. I realize the strcpy
is kind of dangerous, but since I control both ends, I am OK.
response.file
is a simple flag that then tells the parser a file is going to be downloaded. (response
is a simple class to represent the HTTP Response).
HTTPResponse * processFileDownload(){
//Initialize SD
if(!SD.begin(SD_CS)){
return NULL;
}
File file = SD.open(response.fileName, O_RDWR | O_CREAT);
file.seek(0); // Write from the beginning
int bytesWritten = 0;
int bytesReady = 0;
while(bytesReady = client.available()){
/*
This is an arbitrary buffer. It looks like the WiFi card can return anything between
500-1000 bytes
Serial.print(bytesReady);
*/
char data[700];
int r = client.readBytes(data, 700);
file.write(data, r);
bytesWritten += r;
}
file.close();
client.stop();
//Did we get everything?
if(bytesWritten == response.length){
return &response;
}
//Truncated?
return NULL;
}
A few noteworthy things:
client.available()
returns the number of bytes ready to read. I am only using this variable to get debug information.- I’m using a buffer to get a chunk of bytes. I’ve experimented with 200-1000 bytes. Somewhere ~700 was delivering good throughput for me. Your mileage might vary.
- For smaller footprint projects where memory is limited, I tend to avoid dynamic memory allocation, and try to reuse buffers as much as possible. I have a buffer ready to use in the helper
HTTPResponse
object. - I check that the actual total bytes I read matches the size reported in the
HTTPResponse.length
field. In all the experiments I’ve ran, I’ve never seen data being truncated, but … better be safe. - I’m considering adding some kind of integrity validation, perhaps a checksum of sorts? I’m not sure this is needed yet. If all bytes get downloaded, I have little reason to think the file is corrupted.
- I experimented with a few files of various sizes, from a few KB to 200KB (about the end size of the sketch). It worked quite well in all cases, and I was greatly surprised at the speed.
- Given how well this works, perhaps I can use the file transfer approach to do other things? (e.g. download offline content seems like a good candidate)
In the next part, I will focus on building the binary outside of the IDE.