#include <SPI.h>
#include <SD.h>
//#include <SdFat.h>
//#include <sd_defines.h>
//#include <sd_diskio.h>
#include <Wire.h>
#include "RTClib.h"
#include "FS.h"
#include "SD.h"
#include "SPI.h"
#include <EEPROM.h>
#include <MechaQMC5883.h>
#include <math.h>
#include "MS5837.h"
#include <Arduino.h>
#include <U8g2lib.h>
#ifdef U8X8_HAVE_HW_SPI
#include <SPI.h>
#endif
#ifdef U8X8_HAVE_HW_I2C
#include <Wire.h>
#endif
#include <WiFi.h>
#include <WebServer.h>
#include "SPIFFS.h"
#include "CSS.h"
#define ServerVersion "1.0"
String webpage = "";
bool SPIFFS_present = false;
const char ssid[] = "******";
const char password[] = "******";
WebServer server(80);
MechaQMC5883 qmc;
MS5837 depthSensor;
//Input pin definition
//int const TMP36Pin = 4; //the analog pin the TMP36's Vout (sense) pin is connected to the resolution is 10 mV/degree centigrade with a 500 mV offset to allow for negative temperatures
int const buttonPin = 27; //logger's press button attached to this pin
// LCD connection definition
U8G2_ST7920_128X64_F_SW_SPI u8g2(U8G2_R0,14,13,12); //128x64 st7920
//U8G2_ST7920_128X64_F_SW_SPI u8g2(U8G2_R0, /* clock=*/ 18, /* data=*/ 23, /* CS=*/ 15, /* reset=8*/ );
/*
// Let's compare an average of 100
const byte averageCount = 100; //max. 255, byte variable is used
// Variables for the Modified Moving Average
double movingAverage = 0;
double movingAverageSum = 0;
*/
// Timer variables
unsigned long previousAutoLogMillis = 0;
unsigned long currentAutoLogMillis = millis();
int printInterval = 1000; // print out measured results every 1000 msec
unsigned long lastDebounceTime = 0; // the last time the logger input button was toggled
int debounceDelay = 10; // the debounce time; increase if the output flickers
//Drunstate monitoring
int DrunState = 0;
unsigned int DrunTime = 0;
byte DrunHour = 0;
byte DrunMinutes = 0;
byte DrunSecound = 0;
unsigned int lastDrunTime = 0;
unsigned int startTime = 0;
//int thresholdTemp = 25;
int thresholdDepth = 1;
unsigned long previousdepthAreaMillis = 0;
String formDrunTime;
String formDate;
//surface area of the depth profile and avarege depth over time
double depthArea = 0;
double avgDepth = 0;
//magnetic sensor variables
int x = 0;
int y = 0;
int z = 0;
int azimuth = 0;
//magnetic sensor array for avarage calculation, stores 10 values
const byte arraySize = 10;
double myX[arraySize];
double myY[arraySize];
double myZ[arraySize];
double myAzimuth[arraySize];
double avgX = 0;
double avgY = 0;
double avgZ = 0;
double avgAzimuth = 0;
//the current address in the EEPROM where you write the number of starts of the device
int addr = 0;
//number of device starts
byte starts = 0;
//chip pin for the datalogger shield, generally 4,10 or SS
const byte chip = SS;
// logger button state definition
int buttonState = LOW; // the current reading the input button
int lastButtonState = LOW; // the previous reading the input button
void setup(void) {
delay(1000);
// Open serial communications and wait for port to open:
Serial.begin(115200);
while (!Serial) ; // wait for serial port to connect. Needed for native USB port only
Serial.println(F("Serial port connected"));
WiFi.begin(ssid,password);
while (WiFi.status() != WL_CONNECTED) { // Wait for the Wi-Fi to connect
delay(250); Serial.print('.');
}
Serial.println("\nConnected to "+WiFi.SSID()+" Use IP address: "+WiFi.localIP().toString()); // Report which SSID and IP is in use
if (!SPIFFS.begin(true)) {
Serial.println("SPIFFS initialisation failed...");
SPIFFS_present = false;
}
else
{
Serial.println(F("SPIFFS initialised... file access enabled..."));
SPIFFS_present = true;
}
// Join LCD display
u8g2.begin();
u8g2.setBusClock(500000);
//define pin modes
// pinMode(TMP36Pin, INPUT);
pinMode(buttonPin, INPUT);
//Join I2C bus
Wire.begin();
Serial.println(F("I2C bus connected"));
//QMC 5853 magnetic sensor initialization
qmc.init();
//qmc.setMode(Mode_Continuous,ODR_200Hz,RNG_2G,OSR_256);
Serial.println(F("Magnetic sensor initialization..."));
//5837 pressure and temperature sensor initialization
while (!depthSensor.init()) {
depthSensor.setModel(MS5837::MS5837_30BA);
depthSensor.setFluidDensity(997); // kg/m^3 (freshwater, 1029 for seawater)
}
// read a byte the current address of the EEPROM
EEPROM.begin(512);
starts = EEPROM.read(addr);
starts = starts +1;
EEPROM.write(addr, starts);
EEPROM.commit();
Serial.println(F("EEPROM data updated"));
Serial.println(starts);
//SD card initialization
Serial.println("SD card initialization...");
if(!SD.begin()){
Serial.println("Card Mount Failed");
return;
}
uint8_t cardType = SD.cardType();
if(cardType == CARD_NONE){
Serial.println("No SD card attached");
return;
}
Serial.print("SD Card Type: ");
if(cardType == CARD_MMC){
Serial.println("MMC");
} else if(cardType == CARD_SD){
Serial.println("SDSC");
} else if(cardType == CARD_SDHC){
Serial.println("SDHC");
} else {
Serial.println("UNKNOWN");
}
uint64_t cardSize = SD.cardSize() / (1024 * 1024);
Serial.printf("SD Card Size: %lluMB\n", cardSize);
//create log files on SD card
File dataFile = SD.open("/Log_" + String(starts) + ".csv", FILE_WRITE);
dataFile.println("Date,Time,Depth,Temperature,X,Y,Z,Azimuth,AVG");
Serial.println("Log_" + String(starts) + ".csv is created");
File customFile = SD.open("/Dlog_" + String(starts) + ".csv", FILE_WRITE);
customFile.println("Date,Time,Depth,Temperature,X,Y,Z,Azimuth,AVG");
Serial.println("Dlog_" + String(starts) + ".csv is created");
/*
// Pre-load MMA
for (int x=0; x < averageCount; x++)
movingAverageSum = movingAverageSum + analogRead(TMP36Pin);
//Serial.print(movingAverageSum);
//Serial.println(" movingAverageSum ");
// Calculate inital average
movingAverage = movingAverageSum / averageCount;
//Serial.print(movingAverage);
//Serial.println(" movingAverage ");
//Serial.println("MMA preloaded");
*/
// Check if RTC is present
Serial.println("RTC initialization...");
if (! rtc.begin()) {
Serial.println("Couldn't find RTC");
while (1);
}
if (!rtc.isrunning()) {
Serial.println("RTC is NOT running!");
// following line sets the RTC to the date & time this sketch was compiled
rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
// This line sets the RTC with an explicit date & time, for example to set
// January 21, 2014 at 3am you would call:
//rtc.adjust(DateTime(2019, 2, 4, 21, 26, 30));
}
//Serial.println (F("Date,Time,Temperature,X,Y,Z,Azimuth, AVG"));
// transfer internal memory to the display
//----------------------------------------------------------------------
///////////////////////////// Server Commands
server.on("/", HomePage);
server.on("/download", File_Download);
server.on("/upload", File_Upload);
server.on("/fupload", HTTP_POST,[](){ server.send(200);}, handleFileUpload);
server.on("/stream", File_Stream);
server.on("/delete", File_Delete);
server.on("/dir", SPIFFS_dir);
///////////////////////////// End of Request commands
server.begin();
Serial.println("HTTP server started");
// delay(1000);
}
void loop(void) {
DateTime now = rtc.now();
server.handleClient(); // Listen for client connections
// make a string for assembling the data to log:
double dataDepth = 0;
double dataAVG = 0;
int dataYear = 0;
int dataMonth = 0;
int dataDay = 0;
int dataHour = 0;
int dataMinute = 0;
int dataSecond = 0;
int dataTemp = 0;
int dataX = 0;
int dataY = 0;
int dataZ = 0;
int dataAzimuth = 0;
//getting the voltage reading the temperature sensor and append to the string:
// read 5837 sensor data
depthSensor.read();
// read the state of the logger button into a local variable:
int reading = digitalRead(buttonPin);
// If the switch changed, due to noise or pressing:
if (reading != lastButtonState) {
// reset the debouncing timer
lastDebounceTime = millis();
}
/*
//Modified Moving Avarege method used to smooth sensor data
// each interation of loop, update moving average
// Get a new sample
// unsigned int currentValue = analogRead(analogPin);
// Remove previous movingAverage the sum
movingAverageSum = movingAverageSum - movingAverage;
// Replace it with the current sample
movingAverageSum = movingAverageSum + TMP36_Read;
// Recalculate movingAverage
movingAverage = movingAverageSum / averageCount;
//Serial.print(movingAverage);
//Serial.println(" calculated movingAverage ");
// analogRead values go 0 to 1023, analogWrite values 0 to 255
double voltage = movingAverage / 1023; // Gets you mV
//Serial.print(voltage);
//Serial.println(" voltage ");
// calculate the temperature
double temperatureC = (voltage - 0.5) * 100 ; //ing 10 mv per degree wit 500 mV offset to degrees ((voltage - 500mV) / 10)
*/
// check Drun state according to the current temperature
if (depthSensor.depth() < thresholdDepth){
DrunState = LOW;
startTime = millis()/1000;
}
else{
DrunState = HIGH;
}
//calculate Drun time
if (DrunState == HIGH) {
DrunTime = lastDrunTime + millis()/1000 - startTime;
}else{
DrunTime;
lastDrunTime = DrunTime;
}
DrunHour = DrunTime/3600;
DrunMinutes = (DrunTime - (DrunHour * 3600))/60;
DrunSecound = DrunTime - DrunHour * 3600 - DrunMinutes * 60;
formDrunTime = String(DrunHour) + ":" + String(DrunMinutes) + ":" + String(DrunSecound);
//calculate average temperature based on Druntime. tempArea is the are below the temperature vs time function
if (millis() - previousdepthAreaMillis > printInterval){
if (depthSensor.depth() >= thresholdDepth){
depthArea = depthArea + depthSensor.depth() * (1000/printInterval);
}
else{
depthArea;
}
if(depthArea>0){
avgDepth = depthArea/DrunTime;
}
else{
avgDepth = 0;
}
/*Serial.print(DrunTime);
Serial.print(",");
Serial.print(temperatureC, 1);
Serial.print(",");
Serial.print(tempArea);
Serial.print(",");
Serial.println(avgTemp,1);*/
// reset the millis clock
previousdepthAreaMillis = millis();
}
//azimuth calculation //is supporting float too
qmc.read(&x, &y, &z,&azimuth); //azimuth = qmc.azimuth(&y,&x);//you can get custom azimuth
//append temperature an ddepth data to the string:
dataDepth = depthSensor.depth(),1;
dataAVG = avgDepth,1;
dataTemp = depthSensor.temperature(),1;
//append date and time data to the string:
dataYear = now.year();
dataMonth = now.month();
dataDay = now.day();
dataHour = now.hour();
dataMinute = now.minute();
dataSecond = now.second();
formDate = (String(now.year()) + "." + String(now.month()) + "." + String(now.day()) + ".") ;
//append compass data to the string:
dataX = x;
dataY = y;
dataZ = z;
dataAzimuth = azimuth;
// open the file. note that only one file can be open at a time,
// so you have to close this one before opening another.
// SdFile::dateTimeCallback(dateTime);
currentAutoLogMillis = millis();
if (currentAutoLogMillis-previousAutoLogMillis >= printInterval){
// reset the timer
previousAutoLogMillis = millis();
//open the log file for writing
//writeFile(SD, "/GenLog.csv", "Date,Time,X,Y,Z,Azimuth,Druntime,AVG\n");
File dataFile = SD.open("/Log_" + String(starts) + ".csv", FILE_APPEND);
// if the file is opened write data into it:
if (dataFile){
dataFile.print(dataYear);
dataFile.print(".");
dataFile.print(dataMonth);
dataFile.print(".");
dataFile.print(dataDay);
dataFile.print(",");
dataFile.print(dataHour);
dataFile.print(":");
dataFile.print(dataMinute);
dataFile.print(":");
dataFile.print(dataSecond);
dataFile.print(",");
dataFile.print(dataDepth);
dataFile.print(",");
dataFile.print(dataTemp);
dataFile.print(",");
dataFile.print(dataX);
dataFile.print(",");
dataFile.print(dataY);
dataFile.print(",");
dataFile.print(dataZ);
dataFile.print(",");
dataFile.print(dataAzimuth);
dataFile.print(",");
dataFile.print(DrunTime);
dataFile.print(",");
dataFile.println(dataAVG);
// close log file
dataFile.close();
// print to the serial port too:
Serial.print("Date: ");
Serial.print(dataYear);
Serial.print(F("."));
// print2digits(dataMonth);
Serial.print(dataMonth);
Serial.print(F("."));
// print2digits(dataDay);
Serial.print(dataDay);
Serial.print(F("."));
Serial.print(" Time: ");
// print2digits(dataHour);
Serial.print(dataHour);
Serial.print(F(":"));
// print2digits(dataMinute);
Serial.print(dataMinute);
Serial.print(F(":"));
// print2digits(dataSecond);
Serial.print(dataSecond);
Serial.print(F(","));
Serial.print(" Depth: ");
Serial.print(dataDepth,1);
Serial.print(F(","));
Serial.print(" Temperature: ");
Serial.print(dataTemp,1);
Serial.print(F(","));
Serial.print(" xyz: ");
Serial.print(dataX);
Serial.print(F(","));
Serial.print(dataY);
Serial.print(F(","));
Serial.print(dataZ);
Serial.print(F(","));
Serial.print(" Azimuth: ");
Serial.print(dataAzimuth);
Serial.print(" Druntime: ");
// print2digits(DrunHour);
Serial.print(DrunHour);
Serial.print("h ");
// print2digits(DrunMinutes);
Serial.print(DrunMinutes);
Serial.print("m ");
// print2digits(DrunSecound);
Serial.print(DrunSecound);
Serial.print("sec " );
Serial.print(" AVG: ");
Serial.print(dataAVG,1);
Serial.println(F(","));
/* Serial.print("Pressure: ");
Serial.print(depthSensor.pressure());
Serial.println(" mbar");
Serial.print("Temperature: ");
Serial.print(depthSensor.temperature());
Serial.println(" deg C");
Serial.print("Depth: ");
Serial.print(depthSensor.depth());
Serial.println(" m");*/
}
// if the file isn't open, pop up an error:
else {
Serial.println(F("error opening General log file"));
}
// whatever the reading is at, it's been there for longer than the debounce delay, so take it as the actual current state:
if (millis() - lastDebounceTime > debounceDelay) {
// if the button state has changed:
if (reading != buttonState) {
buttonState = reading;
// only toggle the LED if the new button state is HIGH
if (buttonState == HIGH) {
Serial.println(F("Custom log data: "));
for (int i=0; i <= arraySize-1; i++){
myX[i] = x;
Serial.print(myX[i]);
Serial.print(F(" "));
}
Serial.println(F(""));
for (int i=0; i <= arraySize-1; i++){
myY[i] = y;
Serial.print(myY[i]);
Serial.print(F(" "));
}
Serial.println(F(""));
for (int i=0; i <= arraySize-1; i++){
myZ[i] = z;
Serial.print(myZ[i]);
Serial.print(F(" "));
}
Serial.println(F(""));
for (int i=0; i <= arraySize-1; i++){
myAzimuth[i] = azimuth;
Serial.print(myAzimuth[i]);
Serial.print(F(" "));
}
Serial.println(F(""));
avgX = 0;
avgY = 0;
avgZ = 0;
avgAzimuth = 0;
for (int i=0; i <= arraySize-1; i++){
avgX = avgX + myX[i];
avgY = avgY + myY[i];
avgZ = avgZ + myZ[i];
avgAzimuth = avgAzimuth + myAzimuth[i];
}
Serial.println(F("Avarage values: "));
avgX = avgX/arraySize;
avgY = avgY/arraySize;
avgZ = avgZ/arraySize;
avgAzimuth = avgAzimuth/arraySize;
Serial.print(F("Avarage X value: "));
Serial.println(avgX);
Serial.print(F("Avarage Y value: "));
Serial.println(avgY);
Serial.print(F("Avarage Z value: "));
Serial.println(avgZ);
Serial.print(F("Avarage Azimuth value: "));
Serial.println(avgAzimuth);
//open the log file for writing
File customFile = SD.open("/Dlog_" + String(starts) + ".csv", FILE_APPEND);
// if the file is opened write data into it:
customFile.print(dataYear);
customFile.print(".");
customFile.print(dataMonth);
customFile.print(".");
customFile.print(dataDay);
customFile.print(",");
customFile.print(dataHour);
customFile.print(":");
customFile.print(dataMinute);
customFile.print(":");
customFile.print(dataSecond);
customFile.print(",");
customFile.print(dataDepth);
customFile.print(",");
customFile.print(dataTemp);
customFile.print(",");
customFile.print(dataX);
customFile.print(",");
customFile.print(dataY);
customFile.print(",");
customFile.print(dataZ);
customFile.print(",");
customFile.println(dataAzimuth);
customFile.print(",");
customFile.println(DrunTime);
customFile.print(",");
customFile.println(dataAVG);
// close log file
customFile.close();
Serial.println(F("Custom log written"));
}
}
}
// save the reading. Next time through the loop, it'll be the lastButtonState
lastButtonState = reading;
}
// Define UI
u8g2.clearBuffer(); // clear the internal memory
//Draw UI frames
/*u8g2.drawRFrame(0,0,41,24,3);
u8g2.drawRFrame(43,0,42,24,3);
u8g2.drawRFrame(87,0,41,24,3);
u8g2.drawRFrame(0,26,41,24,3);
u8g2.drawRFrame(43,26,42,24,3);
u8g2.drawRFrame(87,26,41,24,3);
u8g2.drawRFrame(0,52,85,12,3); */
u8g2.drawCircle(106, 42, 20, U8G2_DRAW_ALL);
u8g2.drawLine(106, 42, (106+17*sin(2*PI-dataAzimuth*PI/180)), (42-17*cos(2*PI-dataAzimuth*PI/180)));
/*Serial.print("x ");
Serial.println(15*sin(dataAzimuth*2*PI/360));
Serial.print("y ");
Serial.println(15*cos(dataAzimuth*2*PI/360));*/
//Set menu font style and data
u8g2.setFont(u8g2_font_micro_tr); // choose a suitable font
u8g2.drawStr(5,7,"Druntime");
u8g2.drawStr(46,7,"Depth / m");
u8g2.drawStr(94,7,"Azimuth");
u8g2.drawStr(0,33,"Temperature");
u8g2.drawStr(57,33,"AVG");
u8g2.setFont( u8g2_font_5x7_tr); // choose a suitable font
u8g2.setCursor(5,18);
u8g2.print(formDrunTime);
u8g2.setCursor(56,18);
u8g2.print(depthSensor.depth(),1);
u8g2.setCursor(100,18);
u8g2.print(dataAzimuth);
u8g2.setCursor(15,44);
u8g2.print(depthSensor.temperature(),0);
u8g2.setCursor(54,44);
u8g2.print(dataAVG,1);
u8g2.setCursor(2,62);
u8g2.print(formDate);
u8g2.setCursor(58,62);
u8g2.print("Log");
u8g2.setCursor(76,62);
u8g2.print(starts);
u8g2.sendBuffer();
}
/* Additional functions */
/**************************************/
void print2digits(int number) {
if (number >= 0 && number < 10) {
Serial.write('0');
}
Serial.print(number);
}
/*
void dateTime(uint16_t* date, uint16_t* time) {
DateTime now = rtc.now();
// return date using FAT_DATE macro to format fields
*date = FAT_DATE(now.year(), now.month(), now.day());
// return time using FAT_TIME macro to format fields
*time = FAT_TIME(now.hour(), now.minute(), now.second());
}
*/
// All supporting functions here...
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void HomePage(){
SendHTML_Header();
webpage += F("<a href='/download'><button>Download</button></a>");
webpage += F("<a href='/upload'><button>Upload</button></a>");
//webpage += F("<a href='/stream'><button>Stream</button></a>");
webpage += F("<a href='/delete'><button>Delete</button></a>");
webpage += F("<a href='/dir'><button>Directory</button></a>");
append_page_footer();
SendHTML_Content();
SendHTML_Stop(); // Stop is needed because no content length was sent
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void File_Download(){ // This gets called twice, the first pass s the input, the second pass then processes the command line arguments
if (server.args() > 0 ) { // Arguments were received
if (server.hasArg("download")) DownloadFile(server.arg(0));
}
else Input("Enter filename to download","download","download");
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void DownloadFile(String filename){
if (SPIFFS_present) {
File download = SPIFFS.open("/"+filename, "r");
if (download) {
server.sendHeader("Content-Type", "text/text");
server.sendHeader("Content-Disposition", "attachment; filename="+filename);
server.sendHeader("Connection", "close");
server.streamFile(download, "application/octet-stream");
download.close();
} else ReportFileNotPresent("download");
} else ReportSPIFFSNotPresent();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void File_Upload(){
append_page_header();
webpage += F("<h3> File to Upload</h3>");
webpage += F("<FORM action='/fupload' method='post' enctype='multipart/form-data'>");
webpage += F("<input class='buttons' style='width:40%' type='file' name='fupload' id = 'fupload' value=''><br>");
webpage += F("<br><button class='buttons' style='width:10%' type='submit'>Upload File</button><br>");
webpage += F("<a href='/'>[Back]</a><br><br>");
append_page_footer();
server.send(200, "text/html",webpage);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
File UploadFile;
void handleFileUpload(){ // upload a new file to the Filing system
// For further information on 'status' structure, there are other reasons such as a failed transfer that could be used
if(uploadfile.status == UPLOAD_FILE_START)
{
String filename = uploadfile.filename;
if(!filename.startsWith("/")) filename = "/"+filename;
Serial.print("Upload File Name: "); Serial.println(filename);
SPIFFS.remove(filename); // Remove a previous version, otherwise data is appended the file again
UploadFile = SPIFFS.open(filename, "w"); // Open the file for writing in SPIFFS (create it, if doesn't exist)
}
else if (uploadfile.status == UPLOAD_FILE_WRITE)
{
if(UploadFile) UploadFile.write(uploadfile.buf, uploadfile.currentSize); // Write the received bytes to the file
}
else if (uploadfile.status == UPLOAD_FILE_END)
{
if(UploadFile) // If the file was successfully created
{
UploadFile.close(); // Close the file again
Serial.print("Upload Size: "); Serial.println(uploadfile.totalSize);
webpage = "";
append_page_header();
webpage += F("<h3>File was successfully uploaded</h3>");
webpage += F("<h2>Uploaded File Name: "); webpage += uploadfile.filename+"</h2>";
webpage += F("<h2>File Size: "); webpage += file_size(uploadfile.totalSize) + "</h2><br>";
append_page_footer();
server.send(200,"text/html",webpage);
}
else
{
ReportCouldNotCreateFile("upload");
}
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void SPIFFS_dir(){
if (SPIFFS_present) {
File root = SPIFFS.open("/");
if (root) {
root.rewindDirectory();
SendHTML_Header();
//webpage += F("<h3 class='rcorners_m'>SD Card Contents</h3><br>");
webpage += F("<table align='center'>");
webpage += F("<tr><th>Name/Type</th><th style='width:20%'>Type File/Dir</th><th>File Size</th></tr>");
webpage += F("<a href='/'>[Back]</a><br><br>");
printDirectory("/",0);
webpage += F("</table>");
SendHTML_Content();
root.close();
}
else
{
SendHTML_Header();
webpage += F("<h3>No Files Found</h3>");
}
append_page_footer();
SendHTML_Content();
SendHTML_Stop(); // Stop is needed because no content length was sent
} else ReportSPIFFSNotPresent();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void printDirectory(const char * dirname, uint8_t levels){
File root = SPIFFS.open(dirname);
if(!root){
return;
}
if(!root.isDirectory()){
return;
}
File file = root.openNextFile();
while(file){
if (webpage.length() > 1000) {
SendHTML_Content();
}
if(file.isDirectory()){
webpage += "<tr><td>"+String(file.isDirectory()?"Dir":"File")+"</td><td>"+String(file.name())+"</td><td></td></tr>";
printDirectory(file.name(), levels-1);
}
else
{
webpage += "<tr><td>"+String(file.name())+"</td>";
webpage += "<td>"+String(file.isDirectory()?"Dir":"File")+"</td>";
int bytes = file.size();
String fsize = "";
if (bytes < 1024) fsize = String(bytes)+" B";
else if(bytes < (1024 * 1024)) fsize = String(bytes/1024.0,3)+" KB";
else if(bytes < (1024 * 1024 * 1024)) fsize = String(bytes/1024.0/1024.0,3)+" MB";
else fsize = String(bytes/1024.0/1024.0/1024.0,3)+" GB";
webpage += "<td>"+fsize+"</td></tr>";
}
file = root.openNextFile();
}
file.close();
}
#endif
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void SPIFFS_dir(){
String str;
if (SPIFFS_present) {
Dir dir = SPIFFS.openDir("/");
SendHTML_Header();
webpage += F("<h3 class='rcorners_m'>SPIFFS Card Contents</h3><br>");
webpage += F("<table align='center'>");
webpage += F("<tr><th>Name/Type</th><th style='width:40%'>File Size</th></tr>");
while (dir.next()) {
Serial.print(dir.fileName());
webpage += "<tr><td>"+String(dir.fileName())+"</td>";
str = dir.fileName();
str += " / ";
if(dir.fileSize()) {
File f = dir.openFile("r");
Serial.println(f.size());
int bytes = f.size();
String fsize = "";
if (bytes < 1024) fsize = String(bytes)+" B";
else if(bytes < (1024 * 1024)) fsize = String(bytes/1024.0,3)+" KB";
else if(bytes < (1024 * 1024 * 1024)) fsize = String(bytes/1024.0/1024.0,3)+" MB";
else fsize = String(bytes/1024.0/1024.0/1024.0,3)+" GB";
webpage += "<td>"+fsize+"</td></tr>";
f.close();
}
str += String(dir.fileSize());
str += "\r\n";
Serial.println(str);
}
webpage += F("</table>");
SendHTML_Content();
append_page_footer();
SendHTML_Content();
SendHTML_Stop(); // Stop is needed because no content length was sent
} else ReportSPIFFSNotPresent();
}
#endif
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void File_Stream(){
if (server.args() > 0 ) { // Arguments were received
if (server.hasArg("stream")) SPIFFS_file_stream(server.arg(0));
}
else Input("Enter a File to Stream","stream","stream");
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void SPIFFS_file_stream(String filename) {
if (SPIFFS_present) {
File dataFile = SPIFFS.open("/"+filename, "r"); // Now read data SPIFFS Card
if (dataFile) {
if (dataFile.available()) { // If data is available and present
String dataType = "application/octet-stream";
if (server.streamFile(dataFile, dataType) != dataFile.size()) {Serial.print(F("Sent less data than expected!")); }
}
dataFile.close(); // close the file:
} else ReportFileNotPresent("Cstream");
} else ReportSPIFFSNotPresent();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void File_Delete(){
if (server.args() > 0 ) { // Arguments were received
if (server.hasArg("delete")) SPIFFS_file_delete(server.arg(0));
}
else Input(" a File to Delete","delete","delete");
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void SPIFFS_file_delete(String filename) { // Delete the file
if (SPIFFS_present) {
SendHTML_Header();
File dataFile = SPIFFS.open("/"+filename, "r"); // Now read data SPIFFS Card
if (dataFile)
{
if (SPIFFS.remove("/"+filename)) {
Serial.println(F("File deleted successfully"));
webpage += "<h3>File '"+filename+"' has been erased</h3>";
webpage += F("<a href='/delete'>[Back]</a><br><br>");
}
else
{
webpage += F("<h3>File was not deleted - error</h3>");
webpage += F("<a href='delete'>[Back]</a><br><br>");
}
} else ReportFileNotPresent("delete");
append_page_footer();
SendHTML_Content();
SendHTML_Stop();
} else ReportSPIFFSNotPresent();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void SendHTML_Header(){
server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
server.sendHeader("Pragma", "no-cache");
server.sendHeader("Expires", "-1");
server.setContentLength(CONTENT_LENGTH_UNKNOWN);
server.send(200, "text/html", ""); // Empty content inhibits Content-length header so we have to close the socket ourselves.
append_page_header();
server.sendContent(webpage);
webpage = "";
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void SendHTML_Content(){
server.sendContent(webpage);
webpage = "";
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void SendHTML_Stop(){
server.sendContent("");
server.client().stop(); // Stop is needed because no content length was sent
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void Input(String heading1, String command, String arg_calling_name){
SendHTML_Header();
webpage += F("<h3>"); webpage += heading1 + "</h3>";
webpage += F("<FORM action='/"); webpage += command + "' method='post'>"; // Must match the calling argument e.g. '/chart' calls '/chart' after ion but with arguments!
webpage += F("<input type='text' name='"); webpage += arg_calling_name; webpage += F("' value=''><br>");
webpage += F("<type='submit' name='"); webpage += arg_calling_name; webpage += F("' value=''><br><br>");
webpage += F("<a href='/'>[Back]</a><br><br>");
append_page_footer();
SendHTML_Content();
SendHTML_Stop();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void ReportSPIFFSNotPresent(){
SendHTML_Header();
webpage += F("<h3>No SPIFFS Card present</h3>");
webpage += F("<a href='/'>[Back]</a><br><br>");
append_page_footer();
SendHTML_Content();
SendHTML_Stop();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void ReportFileNotPresent(String target){
SendHTML_Header();
webpage += F("<h3>File does not exist</h3>");
webpage += F("<a href='/"); webpage += target + "'>[Back]</a><br><br>";
append_page_footer();
SendHTML_Content();
SendHTML_Stop();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void ReportCouldNotCreateFile(String target){
SendHTML_Header();
webpage += F("<h3>Could Not Create Uploaded File (write-protected?)</h3>");
webpage += F("<a href='/"); webpage += target + "'>[Back]</a><br><br>";
append_page_footer();
SendHTML_Content();
SendHTML_Stop();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
String file_size(int bytes){
String fsize = "";
if (bytes < 1024) fsize = String(bytes)+" B";
else if(bytes < (1024*1024)) fsize = String(bytes/1024.0,3)+" KB";
else if(bytes < (1024*1024*1024)) fsize = String(bytes/1024.0/1024.0,3)+" MB";
else fsize = String(bytes/1024.0/1024.0/1024.0,3)+" GB";
return fsize;
}