Skip to content

Instantly share code, notes, and snippets.

@Jerware
Last active March 9, 2016 18:46
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Jerware/cfc10296de1a85424800 to your computer and use it in GitHub Desktop.
Save Jerware/cfc10296de1a85424800 to your computer and use it in GitHub Desktop.
#pragma SPARK_NO_PREPROCESSOR
#include "SdFat.h"
#define WEBDUINO_SERIAL_DEBUGGING 1
#include "WebServer.h"
void statusLedFlicker();
void helloCmd(WebServer &server, WebServer::ConnectionType type, char *, bool);
void uploadCmd(WebServer & server, WebServer::ConnectionType type, char * url_tail, bool tail_complete);
void LEDCmd(WebServer &server, WebServer::ConnectionType type, char *url_tail, bool tail_complete);
void deleteCmd(WebServer &server, WebServer::ConnectionType type, char *url_tail, bool tail_complete);
void processWebServer();
void WebServerLaunch();
void listFiles(uint8_t flags, uint8_t indent);
/***************************************************
Upload Test
****************************************************/
// Pick an SPI configuration.
// See SPI configuration section below (comments are for photon).
#define SPI_CONFIGURATION 0
//------------------------------------------------------------------------------
// Setup SPI configuration.
#if SPI_CONFIGURATION == 0
// Primary SPI with DMA
// SCK => A3, MISO => A4, MOSI => A5, SS => A2 (default)
SdFat sd;
const uint8_t SD_CS = SS;
#elif SPI_CONFIGURATION == 1
// Secondary SPI with DMA
// SCK => D4, MISO => D3, MOSI => D2, SS => D1
SdFat sd(1);
const uint8_t SD_CS = D1;
#elif SPI_CONFIGURATION == 2
// Primary SPI with Arduino SPI library style byte I/O.
// SCK => A3, MISO => A4, MOSI => A5, SS => A2 (default)
SdFatLibSpi sd;
const uint8_t SD_CS = SS;
#elif SPI_CONFIGURATION == 3
// Software SPI. Use any digital pins.
// MISO => D5, MOSI => D6, SCK => D7, SS => D0
SdFatSoftSpi<D5, D6, D7> sd;
const uint8_t SD_CS = D0;
#endif // SPI_CONFIGURATION
//------------------------------------------------------------------------------
SdFile myFile; // set filesystem
/* This creates an instance of the webserver. By specifying a prefix
* of "", all pages will be at the root of the server. */
#define PREFIX ""
WebServer webserver(PREFIX, 80);
#define STATUS_LED D7
boolean statusLedState;
void setup(void) {
Serial.begin(57600);
Serial.println("Hello there!");
// SD Init
Serial.print("Init SD: ");
if (!sd.begin(SD_CS, SPI_FULL_SPEED)) {
Serial.println("fail");
return;
}
Serial.println("OK!");
// web server setup
WebServerLaunch();
// debug LED setup
pinMode(STATUS_LED, OUTPUT);
digitalWrite(STATUS_LED, HIGH);
}
void loop() {
processWebServer();
}
void statusLedFlicker()
{
if (statusLedState == false)
{
digitalWrite(STATUS_LED, LOW);
}
else
{
digitalWrite(STATUS_LED, HIGH);
}
statusLedState = !statusLedState;
}
// WebServer
void WebServerLaunch()
{
/* setup our default command that will be run when the user accesses
* the root page on the server */
webserver.setDefaultCommand(&helloCmd);
/* run the same command if you try to load /index.html, a common
* default page name */
webserver.addCommand("index.html", &helloCmd);
webserver.addCommand("led", &LEDCmd);
webserver.addCommand("delete", &deleteCmd);
webserver.addCommand("upload", &uploadCmd);
/* start the webserver */
webserver.begin();
}
void processWebServer()
{
char buff[64];
int len = 64;
/* process incoming connections one at a time forever */
webserver.processConnection(buff, &len);
}
// Modified LS function from SDFAT lib to print to webserver
void listFiles(WebServer &server, uint8_t flags, uint8_t indent) {
// make sure we're looking at root
char currentDirectory[13];
sd.vwd()->getName(currentDirectory, 13);
sd.chdir("/");
myFile.rewind();
server.println("<p>SD CONTENTS:<br>");
while (myFile.openNext(sd.vwd(), O_READ)) {
if (!myFile.isHidden() || (flags & LS_A)) {
// get folder name
char foundfile[13];
myFile.getName(foundfile, 13);
// add requested indents
for (uint8_t i = 0; i < indent; i++) {
server.print("&nbsp;");
}
// print slash if folder
if (myFile.isDir()) {
server.print('/');
}
server.print(foundfile);
server.println("<br>");
}
myFile.close();
}
sd.chdir(currentDirectory);
}
void LEDCmd(WebServer &server, WebServer::ConnectionType type, char *url_tail, bool tail_complete)
{
statusLedFlicker();
}
void deleteCmd(WebServer &server, WebServer::ConnectionType type, char *url_tail, bool tail_complete)
{
// delete all files in root, leaving directories
// make sure we're looking at root
char currentDirectory[13];
sd.vwd()->getName(currentDirectory, 13);
sd.chdir("/");
myFile.rewind();
while (myFile.openNext(sd.vwd(), O_READ))
{
myFile.printName(&Serial);
char fileNameString[13];
myFile.getName(fileNameString, 13);
if (!myFile.isDir())
{
myFile.close();
sd.remove(fileNameString);
}
else myFile.close();
}
sd.chdir(currentDirectory);
}
void helloCmd(WebServer &server, WebServer::ConnectionType type, char *url_tail, bool tail_complete)
{
/* this line sends the standard "we're all OK" headers back to the
browser */
server.httpSuccess();
/* if we're handling a GET or POST, we can output our data here.
For a HEAD request, we just stop after outputting headers. */
if (type == WebServer::HEAD)
return;
/* this defines some HTML text in read-only memory aka PROGMEM.
* This is needed to avoid having the string copied to our limited
* amount of RAM. */
P(Page_start) = "<html><head>\n<title>Upload Test</title>\n";
server.printP(Page_start);
server.println("<script src='https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js'></script>\n");
server.println("</head><body>\n");
server.println("<script>");
server.print("function funky(cmd){ $.ajax('http://");
server.print(WiFi.localIP());
server.println("/' + cmd); }");
server.println("</script>\n");
server.printP("<H2><p>Upload Test</p></H2>\n");
server.println("<table><tr>");
server.println("<td><form><input type='button' value='LED TOGGLE' onclick='funky(\"led\")'></form></td>\n");
server.println("<td><form><input type='button' value='DELETE ROOT FILES' onclick='funky(\"delete\")'></form></td>\n");
server.println("</tr></table>");
server.println("<p>Upload Here</p>");
server.println("<p><form method='post' enctype='multipart/form-data' id='uploader' action='/upload' method='POST'>");
server.println("<input type='file' name='my_file[]' multiple>");
server.println("<input type='submit' value='Upload'>");
server.println("</form>");
server.println("<p>");
server.println("SSID: ");
server.println(WiFi.SSID());
server.println("<br>");
server.println("Local IP: ");
server.println(WiFi.localIP());
server.println("<br>");
server.println("Subnet Mask: ");
server.println(WiFi.subnetMask());
server.println("<br>");
server.println("Gateway IP: ");
server.println(WiFi.gatewayIP());
server.println("<br>");
server.println("---");
server.println("<br>");
server.println("deviceID: ");
String myID = System.deviceID();
server.println(myID);
server.println("<br>");
server.printlnf("System version: %s", System.version().c_str());
server.println("<br>");
server.print("free memory: ");
uint32_t freemem = System.freeMemory();
server.println(freemem);
server.println("");
// readHTML(server, "/00system/wifi/index.htm");
listFiles(server, 0, 1);
}
void uploadCmd(WebServer & server, WebServer::ConnectionType type, char * url_tail, bool tail_complete)
{
// make sure we upload to the root
char currentDirectory[13];
sd.vwd()->getName(currentDirectory, 13);
sd.chdir("/");
sd.vwd()->rewind();
Serial.println("uploadCmd Called...");
int numUploads = 0;
long nbytes;
boolean success = false;
if (type == WebServer::POSTMULTI) // this is what is expected: a multipart form
{
Serial.println("POSTMULTI detected...");
bool repeat;
int act = -1;
char ch;
while ((act = server.readNextFormPart(act)) != -4)
{ // if readnextformpart returns -4, either an error occurred or end of forms was reached.
numUploads++;
Serial.print(numUploads);
Serial.print(" : ");
Serial.print("m_filename: ");
Serial.println(server.m_filename);
if (strlen(server.m_filename) > 0)
{ // filename is not empty: a file has been submitted. filename is read in readnextformpart.
int buffer[5]; // a simple FIFO buffer is used to make sure the last two bytes are not written to file. A better way could(should) be used. for now, this works
int b_read = 0;
int b_write = 0;
int diff = 0; // buffer read/write indexes, and the number of bytes the read buffer is behind
SdFile filetoload;
filetoload.clearWriteError();
if (filetoload.open(server.m_filename, O_CREAT | O_WRITE | O_TRUNC))
{ // returns true if file was opened succesfully
nbytes = 0; // number of bytes read
success = true;
while ((act = server.readNextFormPart(-3)) >= 0)
{ // as long as a valid byte is read. (-3) as input means it is returning reading bytes and exiting if a boundary was found.
nbytes++;
buffer[b_write] = act; // put data in buffer
b_write = (b_write + 1) % 5; //updat buffer index
diff++;
if (diff > 2)
{ // start writing after a 2-byte delay
nbytes++;
ch = buffer[b_read];
filetoload.write(buffer[b_read]); //write data to files
if (filetoload.getWriteError())
{
break;
}
b_read = (b_read + 1) % 5;
diff--;
}
}
// free(buffer);
// this previous section needs some cleaning up. writing to a much larger buffer and writing the buffer in a single step to the file would probably be much faster.
if (filetoload.getWriteError())
{
success = false;
Serial.println("write error!!");
}
Serial.println("Finished writing!");
filetoload.close();
}
else
{
Serial.println("Failed to open file!!");
}
}
}
} // end if POSTMULTI
server.httpSuccess();
if (success)
{
server.print("<html><body><h1>");
server.print(numUploads);
server.println("file(s) uploaded.</h1><p>");
server.print("Bytes: ");
server.println(nbytes);
server.print("<p>SD Contents:");
listFiles(server, 0, 1);
server.println("</p></body></html>");
}
else
{
server.println("<html><body><h1>Problems occurred. File may not have uploaded correctly</h1></body></html>");
}
sd.chdir(currentDirectory);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment