diff --git a/CG_scale.ino b/CG_scale.ino index 956365a..2c5822b 100644 --- a/CG_scale.ino +++ b/CG_scale.ino @@ -4,7 +4,7 @@ (c) 2019 by M. Lehmann ------------------------------------------------------------------ */ -#define CGSCALE_VERSION "1.0.58" +#define CGSCALE_VERSION "1.0.62" /* ****************************************************************** @@ -58,6 +58,7 @@ #include #include #include +#include #endif // load settings @@ -76,6 +77,7 @@ HX711_ADC LoadCell_3(PIN_LOADCELL3_DOUT, PIN_LOADCELL3_PD_SCK); #if defined(ESP8266) ESP8266WebServer server(80); IPAddress apIP(ip[0], ip[1], ip[2], ip[3]); +File fsUploadFile; // a File object to temporarily store the received file #endif // serial menu @@ -119,7 +121,8 @@ enum { P_PASSWORD_STA = P_SSID_STA + MAX_SSID_PW_LENGHT + 1, P_SSID_AP = P_PASSWORD_STA + MAX_SSID_PW_LENGHT + 1, P_PASSWORD_AP = P_SSID_AP + MAX_SSID_PW_LENGHT + 1, - EEPROM_SIZE = P_PASSWORD_AP + MAX_SSID_PW_LENGHT + 1 + P_MODELNAME = P_PASSWORD_AP + MAX_SSID_PW_LENGHT + 1, + EEPROM_SIZE = P_MODELNAME + MAX_MODELNAME_LENGHT + 1 #endif }; @@ -195,42 +198,40 @@ bool updateMenu = true; int menuPage = 0; String errMsg[5] = ""; int errMsgCnt = 0; +#if defined(ESP8266) +char curModelName[MAX_MODELNAME_LENGHT + 1] = ""; +#endif // Restart CPU void(* resetCPU) (void) = 0; -// reset to factory defaults -void resetDefault() { - // reset eeprom - for (int i = 0; i < EEPROM_SIZE; i++) { - EEPROM.write(i, 0xFF); - } - Serial.end(); -#if defined(ESP8266) - EEPROM.commit(); -#endif - resetCPU(); -} - - // save calibration factor void saveCalFactor1() { LoadCell_1.setCalFactor(calFactorLoadcell1); EEPROM.put(P_LOADCELL1_CALIBRATION_FACTOR, calFactorLoadcell1); +#if defined(ESP8266) + EEPROM.commit(); +#endif } void saveCalFactor2() { LoadCell_2.setCalFactor(calFactorLoadcell2); EEPROM.put(P_LOADCELL2_CALIBRATION_FACTOR, calFactorLoadcell2); +#if defined(ESP8266) + EEPROM.commit(); +#endif } void saveCalFactor3() { LoadCell_3.setCalFactor(calFactorLoadcell3); EEPROM.put(P_LOADCELL3_CALIBRATION_FACTOR, calFactorLoadcell3); +#if defined(ESP8266) + EEPROM.commit(); +#endif } @@ -269,6 +270,7 @@ void setup() { Serial.begin(9600); #if defined(ESP8266) + // init filesystem SPIFFS.begin(); EEPROM.begin(EEPROM_SIZE); #endif @@ -339,11 +341,21 @@ void setup() { EEPROM.get(P_PASSWORD_AP, password_AP); } + if (EEPROM.read(P_MODELNAME) != 0xFF) { + EEPROM.get(P_MODELNAME, curModelName); + } + + // load current model + if (!openModelJson(curModelName)) { + curModelName[0] = '\0'; + } + // Start by connecting to a WiFi network WiFi.mode(WIFI_STA); WiFi.begin(ssid_STA, password_STA); long timeoutWiFi = millis(); + #endif // init OLED display @@ -379,7 +391,7 @@ void setup() { while ((loadcell_1_rdy + loadcell_2_rdy + loadcell_3_rdy) < 3) { loadcell_1_rdy = LoadCell_1.startMultiple(STABILISINGTIME); loadcell_2_rdy = LoadCell_2.startMultiple(STABILISINGTIME); - if (nLoadcells > 2) { + if (nLoadcells == 3) { loadcell_3_rdy = LoadCell_3.startMultiple(STABILISINGTIME); } else { loadcell_3_rdy = 1; @@ -465,8 +477,15 @@ void setup() { server.on("/getParameter", getParameter); server.on("/getWiFiNetworks", getWiFiNetworks); server.on("/saveParameter", saveParameter); - server.on("/resetDefault", resetDefault); server.on("/autoCalibrate", autoCalibrate); + server.on("/saveModel", saveModel); + server.on("/openModel", openModel); + server.on("/deleteModel", deleteModel); + + // When the client upload file + server.on("/models.html", HTTP_POST, []() { + server.send(200, "text/plain", ""); + }, handleFileUpload); // If the client requests any URI server.onNotFound([]() { @@ -607,36 +626,54 @@ void loop() { case MENU_LOADCELLS: nLoadcells = Serial.parseInt(); EEPROM.put(P_NUMBER_LOADCELLS, nLoadcells); +#if defined(ESP8266) + EEPROM.commit(); +#endif menuPage = 0; updateMenu = true; break; case MENU_DISTANCE_X1: distanceX1 = Serial.parseFloat(); EEPROM.put(P_DISTANCE_X1, distanceX1); +#if defined(ESP8266) + EEPROM.commit(); +#endif menuPage = 0; updateMenu = true; break; case MENU_DISTANCE_X2: distanceX2 = Serial.parseFloat(); EEPROM.put(P_DISTANCE_X2, distanceX2); +#if defined(ESP8266) + EEPROM.commit(); +#endif menuPage = 0; updateMenu = true; break; case MENU_DISTANCE_X3: distanceX3 = Serial.parseFloat(); EEPROM.put(P_DISTANCE_X3, distanceX3); +#if defined(ESP8266) + EEPROM.commit(); +#endif menuPage = 0; updateMenu = true; break; case MENU_REF_WEIGHT: refWeight = Serial.parseFloat(); EEPROM.put(P_REF_WEIGHT, refWeight); +#if defined(ESP8266) + EEPROM.commit(); +#endif menuPage = 0; updateMenu = true; break; case MENU_REF_CG: refCG = Serial.parseFloat(); EEPROM.put(P_REF_CG, refCG); +#if defined(ESP8266) + EEPROM.commit(); +#endif menuPage = 0; updateMenu = true; break; @@ -668,12 +705,18 @@ void loop() { case MENU_RESISTOR_R1: resistorR1 = Serial.parseFloat(); EEPROM.put(P_RESISTOR_R1, resistorR1); +#if defined(ESP8266) + EEPROM.commit(); +#endif menuPage = 0; updateMenu = true; break; case MENU_RESISTOR_R2: resistorR2 = Serial.parseFloat(); EEPROM.put(P_RESISTOR_R2, resistorR2); +#if defined(ESP8266) + EEPROM.commit(); +#endif menuPage = 0; updateMenu = true; break; @@ -684,6 +727,9 @@ void loop() { enableBatVolt = false; } EEPROM.put(P_ENABLE_BATVOLT, enableBatVolt); +#if defined(ESP8266) + EEPROM.commit(); +#endif menuPage = 0; updateMenu = true; break; @@ -694,7 +740,19 @@ void loop() { break; case MENU_RESET_DEFAULT: if (Serial.read() == 'J') { - resetDefault(); + // reset eeprom + for (int i = 0; i < EEPROM_SIZE; i++) { + EEPROM.write(i, 0xFF); + } + Serial.end(); +#if defined(ESP8266) + EEPROM.commit(); + // delete json model file + if (SPIFFS.exists(MODEL_FILE)) { + SPIFFS.remove(MODEL_FILE); + } +#endif + resetCPU(); } menuPage = 0; updateMenu = true; @@ -923,7 +981,30 @@ void getRawValue() { // send parameters to client void getParameter() { + char buff[8]; String response = ""; + float weightTotal_saved = 0; + float CG_length_saved = 0; + float CG_trans_saved = 0; + + StaticJsonBuffer jsonBuffer; + //DynamicJsonBuffer jsonBuffer(JSONBUFFER_SIZE); + + if (SPIFFS.exists(MODEL_FILE)) { + // read json file + File f = SPIFFS.open(MODEL_FILE, "r"); + JsonObject& root = jsonBuffer.parseObject(f); + f.close(); + // check if model exists + if (root.success() && root.containsKey(curModelName)) { + JsonObject& object = root[curModelName]; + weightTotal_saved = object["wt"]; + CG_length_saved = object["cg"]; + CG_trans_saved = object["cglr"]; + } + } + + // parameter list response += nLoadcells; response += "&"; response += distanceX1; @@ -959,6 +1040,18 @@ void getParameter() { response += ssid_AP; response += "&"; response += password_AP; + response += "&"; + response += curModelName; + response += "&"; + dtostrf(weightTotal_saved, 5, 1, buff); + response += buff; + response += "g&"; + dtostrf(CG_length_saved, 5, 1, buff); + response += buff; + response += "mm&"; + dtostrf(CG_trans_saved, 5, 1, buff); + response += buff; + response += "mm"; server.send(200, "text/html", response); } @@ -1020,14 +1113,53 @@ void saveParameter() { EEPROM.put(P_PASSWORD_AP, password_AP); EEPROM.commit(); - server.send(200, "text/html", "ok"); + // save current model to json + saveModelJson(curModelName); + + server.send(200, "text/plain", "saved"); } // calibrate cg scale void autoCalibrate() { while (!runAutoCalibrate()); - server.send(200, "text/html", "ok"); + server.send(200, "text/plain", "parameters saved"); +} + + +// save new model +void saveModel() { + if (server.hasArg("modelname")) { + if (saveModelJson(server.arg("modelname"))) { + server.send(200, "text/plain", "saved"); + return; + } + } + server.send(404, "text/plain", "404: Save model failed !"); +} + + +// open model +void openModel() { + if (server.hasArg("modelname")) { + if (openModelJson(server.arg("modelname"))) { + server.send(200, "text/plain", "opened"); + return; + } + } + server.send(404, "text/plain", "404: Open model failed !"); +} + + +// delete model +void deleteModel() { + if (server.hasArg("modelname")) { + if (deleteModelJson(server.arg("modelname"))) { + server.send(200, "text/plain", "deleted"); + return; + } + } + server.send(404, "text/plain", "404: Delete model failed !"); } @@ -1046,6 +1178,7 @@ String getContentType(String filename) { // send file to the client (if it exists) bool handleFileRead(String path) { + // If a folder is requested, send the index file if (path.endsWith("/")) path += "index.html"; String contentType = getContentType(path); @@ -1060,6 +1193,175 @@ bool handleFileRead(String path) { file.close(); return true; } + return false; } + + +// upload a new file to the SPIFFS +void handleFileUpload() { + + HTTPUpload& upload = server.upload(); + + if (upload.status == UPLOAD_FILE_START) { + String filename = upload.filename; + if (!filename.startsWith("/")) filename = "/" + filename; + if (filename != MODEL_FILE ) server.send(500, "text/plain", "wrong file !"); + // Open the file for writing in SPIFFS (create if it doesn't exist) + fsUploadFile = SPIFFS.open(filename, "w"); + filename = String(); + } else if (upload.status == UPLOAD_FILE_WRITE) { + // Write the received bytes to the file + fsUploadFile.write(upload.buf, upload.currentSize); + } else if (upload.status == UPLOAD_FILE_END) { + // If the file was successfully created + if (fsUploadFile) { + fsUploadFile.close(); + // Redirect the client to the success page + server.sendHeader("Location", "/models.html"); + server.send(303); + } else { + server.send(500, "text/plain", "500: couldn't create file"); + } + } + +} + + +// save model to json file +bool saveModelJson(String modelName) { + + if (modelName.length() > MAX_MODELNAME_LENGHT) { + return false; + } + + StaticJsonBuffer jsonBuffer; + //DynamicJsonBuffer jsonBuffer(JSONBUFFER_SIZE); + + if (SPIFFS.exists(MODEL_FILE)) { + // read json file + File f = SPIFFS.open(MODEL_FILE, "r"); + JsonObject& root = jsonBuffer.parseObject(f); + f.close(); + if (!root.success()) { + return false; + } + // check if model exists + if (root.containsKey(modelName)) { + writeModelData(root[modelName]); + } else { + // otherwise create new + writeModelData(root.createNestedObject(modelName)); + } + // write to file + if (root.success()) { + f = SPIFFS.open(MODEL_FILE, "w"); + root.printTo(f); + f.close(); + } else { + return false; + } + } else { + // creat new json + JsonObject& root = jsonBuffer.createObject(); + writeModelData(root.createNestedObject(modelName)); + // write to file + if (root.success()) { + File f = SPIFFS.open(MODEL_FILE, "w"); + root.printTo(f); + f.close(); + } else { + return false; + } + } + + return true; +} + + +// read model data from json file +bool openModelJson(String modelName) { + + StaticJsonBuffer jsonBuffer; + //DynamicJsonBuffer jsonBuffer(JSONBUFFER_SIZE); + + if (SPIFFS.exists(MODEL_FILE)) { + // read json file + File f = SPIFFS.open(MODEL_FILE, "r"); + JsonObject& root = jsonBuffer.parseObject(f); + f.close(); + if (!root.success()) { + return false; + } + // check if model exists + if (root.containsKey(modelName)) { + JsonObject& object = root[modelName]; + // load parameters from model + distanceX1 = object["x1"]; + distanceX2 = object["x2"]; + distanceX3 = object["x3"]; + } else { + return false; + } + + // save current model name to eeprom + modelName.toCharArray(curModelName, MAX_MODELNAME_LENGHT + 1); + EEPROM.put(P_MODELNAME, curModelName); + EEPROM.commit(); + + return true; + } + + return false; +} + + +// delete model from json file +bool deleteModelJson(String modelName) { + + StaticJsonBuffer jsonBuffer; + //DynamicJsonBuffer jsonBuffer(JSONBUFFER_SIZE); + + if (SPIFFS.exists(MODEL_FILE)) { + // read json file + File f = SPIFFS.open(MODEL_FILE, "r"); + JsonObject& root = jsonBuffer.parseObject(f); + f.close(); + if (!root.success()) { + return false; + } + // check if model exists + if (root.containsKey(modelName)) { + root.remove(modelName); + } else { + return false; + } + // if no models in json, kill it + if (root.size() == 0) { + SPIFFS.remove(MODEL_FILE); + } else { + // write to file + if (root.success()) { + File f = SPIFFS.open(MODEL_FILE, "w"); + root.printTo(f); + f.close(); + } else { + return false; + } + } + return true; + } + + return false; + +} + +void writeModelData(JsonObject& object) { + object["wt"] = weightTotal; + object["cg"] = CG_length; + object["cglr"] = CG_trans; + object["x1"] = distanceX1; + object["x2"] = distanceX2; + object["x3"] = distanceX3; +} #endif diff --git a/data/CG_scale_mechanics.png b/data/CG_scale_mechanics.png deleted file mode 100644 index 249ca62..0000000 Binary files a/data/CG_scale_mechanics.png and /dev/null differ diff --git a/data/CG_scale_mechanics.png.gz b/data/CG_scale_mechanics.png.gz new file mode 100644 index 0000000..4e4e5e2 Binary files /dev/null and b/data/CG_scale_mechanics.png.gz differ diff --git a/data/airplane.png b/data/airplane.png new file mode 100644 index 0000000..be0b7e3 Binary files /dev/null and b/data/airplane.png differ diff --git a/data/home.png b/data/home.png new file mode 100644 index 0000000..7ac34cd Binary files /dev/null and b/data/home.png differ diff --git a/data/index.html b/data/index.html index d6d4430..304f62b 100755 --- a/data/index.html +++ b/data/index.html @@ -11,6 +11,8 @@ - - + -


-
+
Weight
-
-
+
+ + - + + +
CG
-
-
+
+ + - + + +
CG left/right
-
-
+
+ + - + + +
Battery
-
-
+
+ + - + +
+
+ +

(c) 2019 M. Lehmann - Version: 0.0

+ \ No newline at end of file diff --git a/data/jquery-3.3.1.slim.min.js.gz b/data/jquery-3.3.1.slim.min.js.gz new file mode 100644 index 0000000..6471788 Binary files /dev/null and b/data/jquery-3.3.1.slim.min.js.gz differ diff --git a/data/models.html b/data/models.html new file mode 100755 index 0000000..c20788f --- /dev/null +++ b/data/models.html @@ -0,0 +1,199 @@ + + + + + + + + CG scale by M. Lehmann + + + + + + + +
+ + + + + + + + + + +
NameWeight [g]CG [mm]CG left-right [mm]
+ +
+
+ + +
+ + + +
+ Backup + +
+
+
+

(c) 2019 M. Lehmann - Version: 0.0

+ + + \ No newline at end of file diff --git a/data/settings.html b/data/settings.html index 1e969ab..f8b75b0 100755 --- a/data/settings.html +++ b/data/settings.html @@ -10,6 +10,7 @@ - - + -


-
+

WiFi settings

@@ -271,9 +253,8 @@


- - - + + mechanics
@@ -287,9 +268,13 @@


-
-

(c) 2019 M. Lehmann - Version: 0.0

+ \ No newline at end of file diff --git a/data/settings.png b/data/settings.png index 0a23d1d..e767b2b 100644 Binary files a/data/settings.png and b/data/settings.png differ diff --git a/data/weight.png b/data/weight.png old mode 100644 new mode 100755 diff --git a/settings_ESP8266.h b/settings_ESP8266.h index e36ee12..d014f18 100644 --- a/settings_ESP8266.h +++ b/settings_ESP8266.h @@ -137,3 +137,10 @@ U8G2_SH1106_128X64_NONAME_1_HW_I2C oledDisplay(U8G2_R0, /* reset=*/ U8X8_PIN_NON #define SSID_AP "CG scale" #define PASSWORD_AP "" const char ip[4] = {1,1,1,1}; // default IP address + + +// **** Model memory settings **** + +#define MAX_MODELNAME_LENGHT 15 // max chars +#define MODEL_FILE "/models.json" // file to store models +#define JSONBUFFER_SIZE 20000 // max file size in bytes