Last active
August 29, 2015 14:19
-
-
Save harrisonhjones/7f7eb97600069279e16e to your computer and use it in GitHub Desktop.
Spark Download File by Chunk
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* Includes ------------------------------------------------------------------*/ | |
#include "application.h" | |
#include "flashHelper.h" | |
#include "Adafruit_mfGFX.h" | |
#include "Adafruit_SharpMem.h" | |
extern char* itoa(int a, char* buffer, unsigned char radix); | |
// Debug | |
// Turn ON DEBUG statements | |
//#define DBG_DEBUG(x); Serial.print("[DEBUG] "); Serial.print(x); Serial.println(); | |
//#define DBG_DEBUG_2(x,y); Serial.print("[DEBUG] "); Serial.print(x); Serial.print(" = "); Serial.print(y); Serial.println(); | |
// Turn OFF DEBUG statements | |
#define DBG_DEBUG(x); | |
#define DBG_DEBUG_2(x,y); | |
#define DBG_INFO(x); Serial.print("[INFO] "); Serial.print(x); Serial.println(); | |
#define DBG_INFO_2(x,y); Serial.print("[INFO] "); Serial.print(x); Serial.print(" = "); Serial.print(y); Serial.println(); | |
#define DBG_WARN(x); Serial.print("[WARN] "); Serial.print(x); Serial.println(); | |
#define DBG_WARN_2(x,y); Serial.print("[WARN] "); Serial.print(x); Serial.print(" = "); Serial.print(y); Serial.println(); | |
#define DBG_ERROR(x); Serial.print("[ERROR] "); Serial.print(x); Serial.println(); | |
#define DBG_ERROR_2(x,y); Serial.print("[ERROR] "); Serial.print(x); Serial.print(" = "); Serial.print(y); Serial.println(); | |
// Sharp Memory Display | |
Adafruit_SharpMem display(SCK, MOSI, A2); //SCK, MOSI, SS | |
#define BLACK 0 | |
#define WHITE 1 | |
void initSharpMemoryDisplay(); | |
// HTTP Connection | |
TCPClient client; | |
const char host[] = "example.com"; // the host of the backend server | |
const unsigned int port = 80; | |
// System Mode | |
SYSTEM_MODE(SEMI_AUTOMATIC); | |
/* Defines */ | |
#define NUM_CHUNKS 40 | |
#define CHUNK_SIZE 240 | |
#define CHUNK_DL_RETRY 10 | |
#define BINARY_SIZE 9600 | |
#define STATUS_UL_RETRY 10 | |
#define HTTP_CONNECTED_TIMEOUT 2000 // Give the server 2 seconds to respond | |
// getNEOFile Return Values | |
#define SUCCESS 1 | |
// -1 to -100 Misc Errors | |
#define ERR_END_OF_DOWNLOAD_CHUNK -1 | |
#define ERR_RETRY_LIMIT -2 | |
#define ERR_INVALID_SLOT_NUMBER -3 | |
#define ERR_END_OF_UPLOAD_STATUS -4 | |
// -101 to -200 HTTP ERRORS | |
#define ERR_HTTP_CANNOT_CONNECT -101 | |
#define ERR_HTTP_TIMEOUT -102 | |
#define ERR_HTTP_BAD_STATUS -103 | |
#define ERR_HTTP_BAD_CONTENT_LENGTH -104; | |
// -201 to -300 Flash Write Errors | |
#define ERR_UNABLE_TO_WRITE_CHUNK -201 | |
#define ERR_UNABLE_TO_CLEAR_FLASH -202 | |
/* Variables */ | |
/* Functions */ | |
bool writeChunkToFlash(unsigned chunkNumber, unsigned char chunkData[CHUNK_SIZE], unsigned char imgSlotNum); // Define a function for writing a chunk to flash memory | |
int downloadChunk(unsigned chunkNumber, unsigned char imgSlotNum); // Define the main function to grab a file from a remote server | |
int downloadImage(unsigned char imgSlotNum); | |
bool clearFlash(unsigned char imgSlotNum); | |
int uploadStatusA(unsigned long wifiTime, unsigned long downloadTime, int downloadReturnValue); | |
int uploadStatus(unsigned long wifiTime, unsigned long downloadTime, int downloadReturnValue); | |
void downloadInitialImages(); | |
int dispSlot(String slotString); | |
unsigned char dispSlotNum = 0; | |
bool slotChanged = false; | |
// OTA Slot Downloading | |
int downSlot(String slotString); | |
int downSlotNum = -1; // Do not download anything | |
/* Quick Background on Slots */ | |
#define SLOT_LOADING 0 | |
#define SLOT_ERROR 1 | |
#define SLOT_ABOUT 2 | |
#define SLOT_MAIN 20 | |
#define EEPROM_DEVICE_INITIALIZED 0 | |
/* This function is called once at start up ----------------------------------*/ | |
void setup() | |
{ | |
unsigned long downloadStart = 0; | |
unsigned long downloadTime = 0; | |
unsigned long wifiStart = 0; | |
unsigned long wifiTime = 0; | |
Serial.begin(9600); | |
initSharpMemoryDisplay(); | |
wifiStart = millis(); | |
DBG_INFO("Turning on WiFi & Connecting") | |
WiFi.on(); | |
WiFi.connect(); | |
while(WiFi.ready() == false); | |
DBG_INFO("WiFi connected"); | |
// Wait for a second for the WiFi connection to work apparently | |
delay(1000); | |
wifiTime = millis() - wifiStart; | |
uint8_t deviceInitalized = EEPROM.read(EEPROM_DEVICE_INITIALIZED); | |
DBG_INFO_2("Device Initialized", deviceInitalized); | |
if(deviceInitalized!=1) // Check to see if the device is initialized | |
downloadInitialImages(); | |
else | |
{ | |
display.clearDisplay(); | |
display.loadImageFromFlash(SLOT_MAIN); | |
} | |
/* | |
// Download the latest .NEO FILE | |
DBG_INFO_2("Downloading an image for slot",SLOT_MAIN); | |
downloadStart = millis(); | |
int retVal = downloadImage(SLOT_MAIN); | |
downloadTime = millis() - downloadStart; | |
// If there was a complete failure display the error image otherwise serve the main image | |
if(retVal < 0) | |
{ | |
DBG_ERROR_2("There was a critical error with the download process. Error", retVal) | |
display.clearDisplay(); | |
display.loadImageFromFlash(SLOT_ERROR); | |
} | |
else | |
{ | |
display.clearDisplay(); | |
display.loadImageFromFlash(SLOT_MAIN); // Load the loading image | |
} | |
DBG_INFO_2("Download Time", downloadTime); | |
*/ | |
// Send the device's status to the server | |
int retVal = 0; | |
retVal = uploadStatus(wifiTime, downloadTime, retVal); | |
if(retVal < 0) | |
{ | |
DBG_ERROR_2("There was a critical error with the download process. Error", retVal); | |
} | |
else | |
{ | |
DBG_INFO_2("Status uploaded. Attempts", retVal); | |
} | |
DBG_INFO("Setup Finished"); | |
//Spark.sleep(SLEEP_MODE_DEEP, 3600); | |
Spark.connect(); | |
Spark.function("dispSlot", dispSlot); | |
Spark.function("downSlot", downSlot); | |
RGB.control(false); | |
} | |
/* This function loops forever --------------------------------------------*/ | |
void loop() | |
{ | |
if(slotChanged == true) | |
{ | |
slotChanged = false; | |
display.clearDisplay(); | |
display.loadImageFromFlash(dispSlotNum); // Load the loading image | |
} | |
if(downSlotNum > 0) | |
{ | |
Serial.print("Down Slot Num: "); | |
Serial.println(downSlotNum); | |
display.clearDisplay(); | |
display.loadImageFromFlash(SLOT_LOADING); // Load the loading image | |
downloadImage(downSlotNum); | |
dispSlotNum = downSlotNum; | |
downSlotNum = -1; // Disable future slot downloading | |
slotChanged = true; // Enable automatic slow changing | |
} | |
} | |
int dispSlot(String slotString) | |
{ | |
//parse the string into an integer | |
int state = atoi(slotString.c_str()); | |
// If the given slot is -1 reset the EEPROM so the next boot up loads all the images again. | |
if (state == -1) | |
{ | |
EEPROM.write(EEPROM_DEVICE_INITIALIZED, 0x00); | |
return 2; | |
} | |
//Check that the value it's been given is in the right range | |
if (state > 20) {state = 20;} | |
else if (state < 0) {state = 0;} | |
dispSlotNum = (unsigned char) state; | |
slotChanged = true; | |
return 1; | |
} | |
int downSlot(String slotString) | |
{ | |
//parse the string into an integer | |
int slotNum = atoi(slotString.c_str()); | |
if(slotNum > 79) | |
{ | |
return -2; | |
} | |
if(slotNum < 0) | |
{ | |
return -1; | |
} | |
downSlotNum = slotNum; | |
return 1; | |
} | |
void downloadInitialImages() | |
{ | |
unsigned long downloadStart = 0; | |
unsigned long downloadTime = 0; | |
RGB.control(true); | |
RGB.color(0,0,255); | |
// Download the neo file for slot SLOT_LOADING | |
DBG_INFO_2("Downloading an image for slot",SLOT_LOADING); | |
downloadStart = millis(); | |
int retVal = downloadImage(SLOT_LOADING); | |
downloadTime = millis() - downloadStart; | |
// If there was a complete failure display the error image otherwise serve the main image | |
if(retVal < 0) | |
{ | |
DBG_ERROR_2("There was a critical error with the download process. Error", retVal) | |
display.clearDisplay(); | |
} | |
else | |
{ | |
display.clearDisplay(); | |
display.loadImageFromFlash(SLOT_LOADING); // Load the loading image | |
} | |
DBG_INFO_2("Download Time", downloadTime); | |
RGB.color(0,127,255); | |
// Download the neo file for slot SLOT_ERROR | |
DBG_INFO_2("Downloading an image for slot",SLOT_ERROR); | |
downloadStart = millis(); | |
retVal = downloadImage(SLOT_ERROR); | |
downloadTime = millis() - downloadStart; | |
// If there was a complete failure display the error image otherwise serve the main image | |
if(retVal < 0) | |
{ | |
DBG_ERROR_2("There was a critical error with the download process. Error", retVal) | |
display.clearDisplay(); | |
} | |
else | |
{ | |
display.clearDisplay(); | |
display.loadImageFromFlash(SLOT_ERROR); // Load the loading image | |
} | |
DBG_INFO_2("Download Time", downloadTime); | |
RGB.color(0,255,255); | |
// Download the neo file for slot SLOT_ABOUT | |
DBG_INFO_2("Downloading an image for slot",SLOT_ABOUT); | |
downloadStart = millis(); | |
retVal = downloadImage(SLOT_ABOUT); | |
downloadTime = millis() - downloadStart; | |
// If there was a complete failure display the error image otherwise serve the main image | |
if(retVal < 0) | |
{ | |
DBG_ERROR_2("There was a critical error with the download process. Error", retVal) | |
display.clearDisplay(); | |
} | |
else | |
{ | |
display.clearDisplay(); | |
display.loadImageFromFlash(SLOT_ABOUT); // Load the loading image | |
} | |
DBG_INFO_2("Download Time", downloadTime); | |
RGB.color(0,255,127); | |
// Download the neo file for slot SLOT_MAIN | |
DBG_INFO_2("Downloading an image for slot",SLOT_MAIN); | |
downloadStart = millis(); | |
retVal = downloadImage(SLOT_MAIN); | |
downloadTime = millis() - downloadStart; | |
// If there was a complete failure display the error image otherwise serve the main image | |
if(retVal < 0) | |
{ | |
DBG_ERROR_2("There was a critical error with the download process. Error", retVal) | |
display.clearDisplay(); | |
} | |
else | |
{ | |
display.clearDisplay(); | |
display.loadImageFromFlash(SLOT_MAIN); // Load the loading image | |
} | |
DBG_INFO_2("Download Time", downloadTime); | |
DBG_INFO("Setting the deviceInitalized EEPROM parameter to 1"); | |
EEPROM.write(EEPROM_DEVICE_INITIALIZED, 0x01); | |
} | |
void initSharpMemoryDisplay() | |
{ | |
SPI.begin(); | |
SPI.setBitOrder(MSBFIRST); | |
SPI.setDataMode(SPI_MODE0); | |
SPI.setClockDivider(SPI_CLOCK_DIV16); | |
display.init(); // Initialize the hardware ports since cannot be done in class instantation above | |
display.begin(); | |
display.clearDisplay(); | |
display.loadImageFromFlash(0); // Load the loading image | |
} | |
bool clearFlash(unsigned char imgSlotNum) | |
{ | |
// Each "slot" is 12288 (0x3000) bytes wide (because the flash can only be erased in 4KB chunks) | |
// Only 80 slots available | |
if(imgSlotNum > 79) | |
{ | |
DBG_WARN_2("Invalid image slot number",imgSlotNum); | |
return false; | |
} | |
// So the slot starts @ 0x80000 + imgSlotNum * 0x3000 | |
DBG_INFO_2("Erasing starting at ", 0x80000 + imgSlotNum * 0x3000); | |
sFLASH_EraseSector(0x80000 + imgSlotNum * 0x3000); // Erase 4KB (4096 bytes) sector | |
delay(20); | |
DBG_INFO_2("Erasing starting at ", 0x81000 + imgSlotNum * 0x3000); | |
sFLASH_EraseSector(0x81000 + imgSlotNum * 0x3000); // Erase 4KB (4096 bytes) sector | |
delay(20); | |
DBG_INFO_2("Erasing starting at ", 0x82000 + imgSlotNum * 0x3000); | |
sFLASH_EraseSector(0x82000 + imgSlotNum * 0x3000); // Erase 4KB (4096 bytes) sector | |
delay(20); | |
return true; | |
} | |
/** | |
* writeRowToFlash | |
* | |
* Writes a chunk into flash | |
* | |
* @return bool representing the success (true) or failure (false) of the function | |
* | |
*/ | |
bool writeChunkToFlash(unsigned chunkNumber, unsigned char chunkData[CHUNK_SIZE], unsigned char imgSlotNum) | |
{ | |
// Write to flash memory here | |
int extFlashOffset = chunkNumber*CHUNK_SIZE + imgSlotNum * 0x3000; | |
//DBG_INFO_2("Flash (psuedo)written starting at address",0x80000 + extFlashOffset); | |
if(SparkFlash_writeB(chunkData, CHUNK_SIZE, extFlashOffset)) | |
{ | |
DBG_DEBUG_2("Flash written starting at address",0x80000 + extFlashOffset); | |
return true; | |
} | |
else | |
{ | |
DBG_WARN_2("Unable to write flash starting at address",0x80000 + extFlashOffset); | |
return false; | |
} | |
} | |
int downloadImage(unsigned char imgSlotNum) | |
{ | |
// Only 80 slots available | |
if(imgSlotNum > 79) | |
{ | |
DBG_WARN_2("Invalid image slot number",imgSlotNum); | |
return ERR_INVALID_SLOT_NUMBER; | |
} | |
if(clearFlash(imgSlotNum) == false) | |
{ | |
DBG_WARN("Unable to clear flash"); | |
return ERR_UNABLE_TO_CLEAR_FLASH; | |
} | |
// Grab control of the RGB LED | |
//RGB.control(true); | |
// Turn it "off" | |
//RGB.color(0,0,0); | |
int val = 0; | |
// Initalize a few variables to track local (each chunk) and total (all chunks) retry counts | |
unsigned int retryCount = 0; | |
unsigned char totalRetryCount = 0; | |
// Attempt to download each chunk | |
for(unsigned char i = 0; i<NUM_CHUNKS; i++) | |
{ | |
// Atempt to download each chunk CHUNK_DL_RETRY times | |
while(retryCount < CHUNK_DL_RETRY) | |
{ | |
if(downloadChunk(i, imgSlotNum) != SUCCESS) | |
{ | |
// If downloadChunk was unsuccessful, increment the retry counters and try again | |
retryCount++; | |
totalRetryCount++; | |
delay(500); | |
} | |
else | |
{ | |
// If downloadChunk was successful, reset the local retry counter and go to the next chunk | |
retryCount = 0; | |
break; | |
} | |
} | |
if(retryCount >= CHUNK_DL_RETRY) | |
{ | |
// If we hit a retry limit exit the function with an error | |
//RGB.color(255,0,0); | |
return ERR_RETRY_LIMIT; | |
} | |
val = map(i, 0, NUM_CHUNKS-1, 0, 255); | |
//RGB.color(0,val,0); | |
} | |
// Return the total retry count (ideally 0) | |
return totalRetryCount; | |
} | |
/** | |
* downloadChunk | |
* | |
* Grabs part of a NEO file from a web server | |
* | |
* @return int representing the success or failure of the function. | |
* | |
* Negative return values represent failures | |
* Positive return values represent successes | |
* Should not return 0 | |
* Return Values: | |
* | |
*/ | |
int downloadChunk(unsigned chunkNumber, unsigned char imgSlotNum) | |
{ | |
DBG_INFO_2("Downloading chunk", chunkNumber); | |
unsigned char chunkData[CHUNK_SIZE]; | |
unsigned long httpLastDataTime = 0; | |
unsigned int bytesRead = 0; | |
// HTTP Downloadings Buffers, Variables, etc | |
bool parsingHeader= true; | |
char buffer[33]; | |
char statusCodeStr[] = "NaN"; | |
char c; | |
unsigned int contentLength = 0; // Detected HTTP Content Length | |
unsigned short statusCode = 0; // Detected HTTP Status Code | |
DBG_DEBUG("\tStep 1 - Connecting to server\n"); | |
if (client.connect(host, port) == false) | |
{ | |
DBG_ERROR("\tUnable to connect"); | |
DBG_WARN("\tStopping TCPClient"); | |
client.stop(); | |
return ERR_HTTP_CANNOT_CONNECT; | |
} | |
DBG_DEBUG("\tStep 2 - Sending HTTP Request\n"); | |
client.write("GET /sample.php HTTP/1.1\nHost: example.com:80\nRange: bytes="); | |
Serial.print("Bytes: "); | |
itoa(chunkNumber*CHUNK_SIZE,buffer,10); | |
client.write(buffer); | |
//Serial.print(buffer); | |
Serial.write(buffer); | |
client.write("-"); | |
Serial.write("-"); | |
itoa ((chunkNumber+1)*CHUNK_SIZE-1,buffer,10); | |
client.write(buffer); | |
Serial.write(buffer); | |
Serial.println(); | |
// Send Device ID | |
client.write("\nX-Device-ID: "); | |
Spark.deviceID().toCharArray(buffer, 100); | |
client.write(buffer); | |
// Send Device ID | |
client.write("\nX-Device-Slot: "); | |
itoa (imgSlotNum,buffer,10); | |
client.write(buffer); | |
client.write("\n\n"); | |
//Serial.write("\n\n"); | |
httpLastDataTime = millis(); | |
while(client.connected()) { | |
// Check for timeout condition | |
if((millis() - httpLastDataTime) > HTTP_CONNECTED_TIMEOUT) | |
{ | |
// Timeout condition reached! | |
DBG_WARN("\tTimeout condition reached"); | |
// Clear out any left over data (so the CC3000 won't crash) | |
while(client.available()) | |
client.read(); | |
DBG_WARN("\tStopping TCPClient"); | |
client.stop(); | |
DBG_WARN("\tReturning with error"); | |
return ERR_HTTP_TIMEOUT; | |
} | |
while(client.available()) { | |
// If there's data to read, reset the timeout | |
httpLastDataTime = millis(); | |
if (parsingHeader) { | |
DBG_DEBUG("\tStart parsing header"); | |
// Parsing the Status Code | |
client.find((char*)"HTTP/1.1 "); // Move the pointer up to the start of the status code | |
client.readBytes(statusCodeStr, 3); // Read the status code into the statusCode variable | |
statusCode = atoi(statusCodeStr); | |
DBG_DEBUG_2("\tStatus Code",statusCode); | |
// Bad Status (not 206) | |
if(statusCode != 206) | |
{ | |
DBG_WARN_2("\tBad Status",statusCode); | |
while(client.available()) | |
client.read(); | |
DBG_WARN("\tStopping TCPClient"); | |
client.stop(); | |
DBG_WARN("\tReturning with error"); | |
return ERR_HTTP_BAD_STATUS; | |
} | |
// Parsing the Content Length | |
client.find((char*)"Content-Length: "); // Move the pointer up to the start of the content length | |
while (isdigit(c = client.read())) { // While we are reading the content length number | |
contentLength = contentLength*10 + (c - '0'); // Decode the ASCII number to a "real" number | |
} | |
DBG_DEBUG_2("\tContent Length",contentLength); | |
// Bad Content Length | |
if(contentLength != CHUNK_SIZE) | |
{ | |
DBG_WARN_2("\tBad Content Length",contentLength); | |
while(client.available()) | |
client.read(); | |
DBG_WARN("\tStopping TCPClient"); | |
client.stop(); | |
DBG_WARN("\tReturning with error"); | |
return ERR_HTTP_BAD_CONTENT_LENGTH; | |
} | |
// Parsing the actual content | |
client.find((char*)"\n\r\n"); // Move the pointer to the start of the content | |
parsingHeader = false; | |
DBG_DEBUG("\tEnd parsing header"); | |
} else { | |
chunkData[bytesRead++] = client.read(); | |
// Serial.write("."); | |
if(bytesRead == CHUNK_SIZE) | |
{ | |
DBG_DEBUG("\tSuccessful Read of CHUNK_SIZE"); | |
// Clear out any left over data (so the CC3000 won't crash) | |
while(client.available()) | |
client.read(); | |
DBG_DEBUG("\tStopping TCPClient"); | |
client.stop(); | |
if(writeChunkToFlash(chunkNumber, chunkData, imgSlotNum) == false) | |
{ | |
DBG_ERROR("\tUnable to write chunk to flash"); | |
return ERR_UNABLE_TO_WRITE_CHUNK; | |
} | |
else | |
return SUCCESS; | |
} | |
} | |
} | |
} | |
return ERR_END_OF_DOWNLOAD_CHUNK; | |
} | |
/** | |
* uploadStatus | |
* | |
* Upload the status of the device | |
* | |
* @return int representing the success or failure of the function. | |
* | |
* Negative return values represent failures | |
* Positive return values represent successes | |
* Should not return 0 | |
* Return Values: | |
* | |
*/ | |
int uploadStatus(unsigned long wifiTime, unsigned long downloadTime, int downloadReturnValue) | |
{ | |
// Initalize a few variables to track retry counts | |
unsigned char retryCount = 0; | |
// Attempt to upload device status | |
while(retryCount < STATUS_UL_RETRY) | |
{ | |
int retVal = uploadStatusA(wifiTime, downloadTime, downloadReturnValue); | |
if(retVal != SUCCESS) | |
{ | |
// If uploadStatusA was unsuccessful, increment the retry counter and try again | |
DBG_WARN_2("Status upload sent unsuccessfully. Return value", retVal); | |
retryCount++; | |
delay(500); | |
} | |
else | |
{ | |
// If uploadStatusA was successful, finish | |
DBG_INFO("Status upload sent successfully!"); | |
return retryCount; | |
} | |
} | |
if(retryCount >= STATUS_UL_RETRY) | |
{ | |
// If we hit a retry limit exit the function with an error | |
return ERR_RETRY_LIMIT; | |
} | |
// Return the total retry count (ideally 0) | |
return retryCount; | |
} | |
int uploadStatusA(unsigned long wifiTime, unsigned long downloadTime, int downloadReturnValue) | |
{ | |
DBG_INFO("Uploading device status"); | |
unsigned long httpLastDataTime = 0; | |
unsigned int bytesRead = 0; | |
// HTTP Downloadings Buffers, Variables, etc | |
bool parsingHeader= true; | |
char buffer[100]; | |
char statusCodeStr[] = "NaN"; | |
char c; | |
unsigned int contentLength = 0; // Detected HTTP Content Length | |
unsigned short statusCode = 0; // Detected HTTP Status Code | |
DBG_INFO("\tStep 1 - Connecting to server\n"); | |
if (client.connect(host, port) == false) | |
{ | |
DBG_ERROR("\tUnable to connect"); | |
DBG_WARN("\tStopping TCPClient"); | |
client.stop(); | |
return ERR_HTTP_CANNOT_CONNECT; | |
} | |
DBG_INFO("\tStep 2 - Sending HTTP Request\n"); | |
client.write("GET /sample.php HTTP/1.1\nHost: example.com:80"); | |
// Send Device ID | |
client.write("\nX-Device-ID: "); | |
Spark.deviceID().toCharArray(buffer, 100); | |
client.write(buffer); | |
// Send Battery Level | |
client.write("\nX-Device-Battery-mVoltage: "); | |
itoa(3300,buffer,10); | |
client.write(buffer); | |
// Send Time | |
client.write("\nX-Device-Time: "); | |
itoa(Time.now(),buffer,10); | |
client.write(buffer); | |
// Send Next Update | |
client.write("\nX-Device-Next-Update: "); | |
itoa(Time.now()+360,buffer,10); | |
client.write(buffer); | |
// Send WiFi Time | |
client.write("\nX-Device-WiFi-Time: "); | |
itoa(wifiTime,buffer,10); | |
client.write(buffer); | |
// Send Download Time | |
client.write("\nX-Device-Download-Time: "); | |
itoa(downloadTime,buffer,10); | |
client.write(buffer); | |
// Send Download Return Value | |
client.write("\nX-Device-Download-Value: "); | |
itoa(downloadReturnValue,buffer,10); | |
client.write(buffer); | |
client.write("\n\n"); | |
//Serial.write("\n\n"); | |
httpLastDataTime = millis(); | |
while(client.connected()) { | |
// Check for timeout condition | |
if((millis() - httpLastDataTime) > HTTP_CONNECTED_TIMEOUT) | |
{ | |
// Timeout condition reached! | |
DBG_WARN("\tTimeout condition reached"); | |
// Clear out any left over data (so the CC3000 won't crash) | |
while(client.available()) | |
client.read(); | |
DBG_WARN("\tStopping TCPClient"); | |
client.stop(); | |
DBG_WARN("\tReturning with error"); | |
return ERR_HTTP_TIMEOUT; | |
} | |
while(client.available()) { | |
// If there's data to read, reset the timeout | |
httpLastDataTime = millis(); | |
if (parsingHeader) { | |
DBG_DEBUG("\tStart parsing header"); | |
// Parsing the Status Code | |
client.find((char*)"HTTP/1.1 "); // Move the pointer up to the start of the status code | |
client.readBytes(statusCodeStr, 3); // Read the status code into the statusCode variable | |
statusCode = atoi(statusCodeStr); | |
DBG_DEBUG_2("\tStatus Code",statusCode); | |
// Bad Status (not 200) | |
if(statusCode != 200) | |
{ | |
DBG_WARN_2("\tBad Status",statusCode); | |
while(client.available()) | |
client.read(); | |
DBG_WARN("\tStopping TCPClient"); | |
client.stop(); | |
DBG_WARN("\tReturning with error"); | |
return ERR_HTTP_BAD_STATUS; | |
} | |
// Parsing the Content Length | |
client.find((char*)"Content-Length: "); // Move the pointer up to the start of the content length | |
while (isdigit(c = client.read())) { // While we are reading the content length number | |
contentLength = contentLength*10 + (c - '0'); // Decode the ASCII number to a "real" number | |
} | |
DBG_DEBUG_2("\tContent Length",contentLength); | |
// Bad Content Length (not 2 for "ok") | |
if(contentLength != 2) | |
{ | |
DBG_WARN("\tBad Content Length"); | |
while(client.available()) | |
client.read(); | |
DBG_WARN("\tStopping TCPClient"); | |
client.stop(); | |
DBG_WARN("\tReturning with error"); | |
return ERR_HTTP_BAD_CONTENT_LENGTH; | |
} | |
// Parsing the actual content | |
client.find((char*)"\n\r\n"); // Move the pointer to the start of the content | |
parsingHeader = false; | |
DBG_DEBUG("\tEnd parsing header"); | |
} else { | |
// For now just read the response. In the future we might want to actually do something with it? | |
client.read(); | |
bytesRead++; | |
// Serial.write("."); | |
if(bytesRead == 2) | |
{ | |
DBG_INFO("\tSuccessful Read of 2"); | |
// Clear out any left over data (so the CC3000 won't crash) | |
while(client.available()) | |
client.read(); | |
DBG_INFO("\tStopping TCPClient"); | |
client.stop(); | |
return SUCCESS; | |
} | |
} | |
} | |
} | |
return ERR_END_OF_UPLOAD_STATUS; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment