diff --git a/data/html/index.html b/data/html/index.html index 3fe5e1888..0d4e9a12a 100644 --- a/data/html/index.html +++ b/data/html/index.html @@ -113,7 +113,7 @@
- +
diff --git a/data/js/app.js b/data/js/app.js index 180f4f1e5..9e25dcbef 100644 --- a/data/js/app.js +++ b/data/js/app.js @@ -9,6 +9,7 @@ const vueApp = Vue.createApp({ showPostSucceeded: false } }, + el: '#calibrationForm', methods: { fetchParameters() { fetch("/parameters") @@ -72,6 +73,11 @@ const vueApp = Vue.createApp({ 4: 'Scale Parameters' } return sectionNames[sectionId] + }, + confirmSubmission() { + if(confirm('Are you sure you want to toggle the calibration?')) { + this.$el.submit(); + } } }, computed: { diff --git a/platformio.ini b/platformio.ini index c6a40e474..f1f383ff4 100644 --- a/platformio.ini +++ b/platformio.ini @@ -35,4 +35,4 @@ debug_init_break = tbreak setup monitor_port = socket://silvia.local:23 upload_protocol = espota upload_port = silvia.local -upload_flags = --auth=otapass \ No newline at end of file +upload_flags = --auth=otapass diff --git a/src/EmbeddedWebserver.h b/src/EmbeddedWebserver.h index a32dd193d..d9a840109 100644 --- a/src/EmbeddedWebserver.h +++ b/src/EmbeddedWebserver.h @@ -282,7 +282,7 @@ void serverSetup() { int calibrate = flipUintValue(calibrationON); setCalibration(calibrate); - debugPrintf("Toggle tare mode: %i \n", calibrate); + debugPrintf("Toggle calibration mode: %i \n", calibrate); request->redirect("/"); }); diff --git a/src/Storage.cpp b/src/Storage.cpp index c3f0ba00e..e80a3fd69 100644 --- a/src/Storage.cpp +++ b/src/Storage.cpp @@ -38,7 +38,11 @@ typedef struct __attribute__((packed)) { uint8_t freeToUse6[21]; uint8_t pidBdOn; double pidKpBd; + #if SINGLE_HX711 == 1 uint8_t freeToUse7[2]; + #else + float scale2Calibration; + #endif double pidTnBd; uint8_t freeToUse8[2]; double pidTvBd; @@ -87,7 +91,8 @@ static const sto_data_t itemDefaults PROGMEM = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }, // free to use 0, // STO_ITEM_USE_PID_BD AGGBKP, // STO_ITEM_PID_KP_BD - {0xFF, 0xFF}, // free to use + SCALE2_CALIBRATION_FACTOR, //STO_ITEM_SCALE2_CALIBRATION_FACTOR + // {0xFF, 0xFF}, // free to use AGGBTN, // STO_ITEM_PID_TN_BD {0xFF, 0xFF}, // free to use AGGBTV, // STO_ITEM_PID_TV_BD @@ -279,6 +284,13 @@ static inline int32_t getItemAddr(sto_item_id_t itemId, uint16_t* maxItemSize = size = STRUCT_MEMBER_SIZE(sto_data_t,scaleCalibration); break; + #if SINGLE_HX711 == 0 + case STO_ITEM_SCALE2_CALIBRATION_FACTOR: + addr = offsetof(sto_data_t,scale2Calibration ); + size = STRUCT_MEMBER_SIZE(sto_data_t,scale2Calibration); + break; + #endif + case STO_ITEM_SCALE_KNOWN_WEIGHT: addr = offsetof(sto_data_t,scaleKnownWeight ); size = STRUCT_MEMBER_SIZE(sto_data_t,scaleKnownWeight); diff --git a/src/Storage.h b/src/Storage.h index 6f12e28e8..a00202ae4 100644 --- a/src/Storage.h +++ b/src/Storage.h @@ -43,6 +43,9 @@ typedef enum STO_ITEM_STANDBY_MODE_ON, // Enable tandby mode STO_ITEM_STANDBY_MODE_TIME, // Time until heater is turned off STO_ITEM_SCALE_CALIBRATION_FACTOR, // Calibration factor for scale + #if SINGLE_HX711 == 0 + STO_ITEM_SCALE2_CALIBRATION_FACTOR, // Calibration factor for scale 2 + #endif STO_ITEM_SCALE_KNOWN_WEIGHT, // Calibration weight for scale STO_ITEM_RESERVED_30, // reserved STO_ITEM_RESERVED_21, // reserved diff --git a/src/brewvoid.h b/src/brewvoid.h index 8e5aebaf2..69a224e6e 100644 --- a/src/brewvoid.h +++ b/src/brewvoid.h @@ -62,6 +62,9 @@ boolean brewPIDDisabled = false; // is PID disabled for delay const unsigned long intervalWeight = 200; // weight scale unsigned long previousMillisScale; // initialisation at the end of init() HX711_ADC LoadCell(PIN_HXDAT, PIN_HXCLK); + #if SINGLE_HX711 == 0 + HX711_ADC LoadCell2(PIN_HXDAT2, PIN_HXCLK); + #endif #endif /** @@ -439,11 +442,7 @@ void brew() { break; case 41: // waiting time brew - if (timeBrewed > totalBrewTime || (weightBrew > (weightSetpoint - scaleDelayValue))) { - brewCounter = kBrewFinished; - } - - if (timeBrewed > totalBrewTime) { + if (weightBrew > (weightSetpoint - scaleDelayValue)) { brewCounter = kBrewFinished; } diff --git a/src/defaults.h b/src/defaults.h index 84f667f84..0e9925e91 100644 --- a/src/defaults.h +++ b/src/defaults.h @@ -24,6 +24,9 @@ int writeSysParamsToStorage(void); #define TEMPOFFSET 0 // brew temperature setpoint #define STEAMSETPOINT 120 // steam temperature setpoint #define SCALE_CALIBRATION_FACTOR 1.00 // Raw data is divided by this value to convert to readable data +#if SINGLE_HX711 == 0 +#define SCALE2_CALIBRATION_FACTOR 1.00 // Raw data is divided by this value to convert to readable data +#endif #define SCALE_KNOWN_WEIGHT 267.00 // Calibration weight for scale (weight of the tray) #define AGGKP 62 // PID Kp (regular phase) #define AGGTN 52 // PID Tn (regular phase) diff --git a/src/display.h b/src/display.h index 3979dfd79..d15401e59 100644 --- a/src/display.h +++ b/src/display.h @@ -246,7 +246,7 @@ void displayShottimer(void) { u8g2.print(timeBrewed / 1000, 1); u8g2.print("s"); u8g2.setCursor(64, 38); - u8g2.print(weightBrew, 0); + u8g2.print(weightBrew, 1); u8g2.print("g"); u8g2.setFont(u8g2_font_profont11_tf); displayWaterIcon(119, 1); @@ -261,8 +261,8 @@ void displayShottimer(void) { u8g2.print(lastBrewTime/1000, 1); u8g2.print("s"); u8g2.setCursor(64, 38); - u8g2.print(weightBrew, 0); - u8g2.print(" g"); + u8g2.print(weightBrew, 1); + u8g2.print("g"); u8g2.setFont(u8g2_font_profont11_tf); displayWaterIcon(119, 1); u8g2.sendBuffer(); diff --git a/src/main.cpp b/src/main.cpp index 8535f838a..0b1937381 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -45,6 +45,12 @@ hw_timer_t *timer = NULL; #endif #if (BREWMODE == 2 || ONLYPIDSCALE == 1) + #define HX711_ADC_config_h + #define SAMPLES 32 + #define IGN_HIGH_SAMPLE 1 + #define IGN_LOW_SAMPLE 1 + #define SCK_DELAY 1 + #define SCK_DISABLE_INTERRUPTS 0 #include #endif @@ -177,6 +183,9 @@ double brewTempOffset = TEMPOFFSET; double setpoint = brewSetpoint; double steamSetpoint = STEAMSETPOINT; float scaleCalibration = SCALE_CALIBRATION_FACTOR; +#if SINGLE_HX711 == 0 +float scale2Calibration = SCALE_CALIBRATION_FACTOR; +#endif float scaleKnownWeight = SCALE_KNOWN_WEIGHT; uint8_t usePonM = 0; // 1 = use PonM for cold start PID, 0 = use normal PID for cold start double steamKp = STEAMKP; @@ -241,6 +250,9 @@ SysPara sysParaWeightSetpoint(&weightSetpoint, WEIGHTSETPOINT_MIN, WEIGH SysPara sysParaStandbyModeOn(&standbyModeOn, 0, 1, STO_ITEM_STANDBY_MODE_ON); SysPara sysParaStandbyModeTime(&standbyModeTime, STANDBY_MODE_TIME_MIN, STANDBY_MODE_TIME_MAX, STO_ITEM_STANDBY_MODE_TIME); SysPara sysParaScaleCalibration(&scaleCalibration, -100000, 100000, STO_ITEM_SCALE_CALIBRATION_FACTOR); +#if SINGLE_HX711 == 0 +SysPara sysParaScale2Calibration(&scale2Calibration, -100000, 100000, STO_ITEM_SCALE2_CALIBRATION_FACTOR); +#endif SysPara sysParaScaleKnownWeight(&scaleKnownWeight, 0, 2000, STO_ITEM_SCALE_KNOWN_WEIGHT); // Other variables @@ -2008,7 +2020,7 @@ void setup() { }; editableVars["SCALE_CALIBRATION"] = { - .displayName = F("Calibration factor"), + .displayName = F("Calibration factor scale 1"), .hasHelpText = false, .helpText = "", .type = kFloat, @@ -2019,6 +2031,21 @@ void setup() { .maxValue = 100000, .ptr = (void *)&scaleCalibration }; + + #if SINGLE_HX711 == 0 + editableVars["SCALE2_CALIBRATION"] = { + .displayName = F("Calibration factor scale 2"), + .hasHelpText = false, + .helpText = "", + .type = kFloat, + .section = sScaleSection, + .position = 32, + .show = [] { return true; }, + .minValue = -100000, + .maxValue = 100000, + .ptr = (void *)&scale2Calibration + }; + #endif #endif editableVars["VERSION"] = { @@ -2062,6 +2089,9 @@ void setup() { if (ONLYPIDSCALE == 1 || BREWMODE == 2) { mqttVars["weightSetpoint"] = []{ return &editableVars.at("SCALE_WEIGHTSETPOINT"); }; mqttVars["scaleCalibration"] = []{ return &editableVars.at("SCALE_CALIBRATION"); }; + #if SINGLE_HX711 == 0 + mqttVars["scale2Calibration"] = []{ return &editableVars.at("SCALE2_CALIBRATION"); }; + #endif mqttVars["scaleKnownWeight"] = []{ return &editableVars.at("SCALE_KNOWN_WEIGHT"); }; mqttVars["tareON"] = []{ return &editableVars.at("TARE_ON"); }; mqttVars["calibrationON"] = []{ return &editableVars.at("CALIBRATION_ON"); }; @@ -2219,6 +2249,11 @@ void setup() { temperature -= brewTempOffset; + // Init Scale by BREWMODE 2 or SHOTTIMER 2 + #if (BREWMODE == 2 || ONLYPIDSCALE == 1) + initScale(); + #endif + // Initialisation MUST be at the very end of the init(), otherwise the // time comparision in loop() will have a big offset unsigned long currentTime = millis(); @@ -2236,11 +2271,6 @@ void setup() { previousMillisPressure = currentTime; #endif - // Init Scale by BREWMODE 2 or SHOTTIMER 2 - #if (BREWMODE == 2 || ONLYPIDSCALE == 1) - initScale(); - #endif - setupDone = true; enableTimer1(); @@ -2648,6 +2678,9 @@ int readSysParamsFromStorage(void) { if (sysParaStandbyModeOn.getStorage() != 0) return -1; if (sysParaStandbyModeTime.getStorage() != 0) return -1; if (sysParaScaleCalibration.getStorage() != 0) return -1; + #if SINGLE_HX711 == 0 + if (sysParaScale2Calibration.getStorage() != 0) return -1; + #endif if (sysParaScaleKnownWeight.getStorage() != 0) return -1; return 0; @@ -2686,6 +2719,9 @@ int writeSysParamsToStorage(void) { if (sysParaStandbyModeOn.setStorage() != 0) return -1; if (sysParaStandbyModeTime.setStorage() != 0) return -1; if (sysParaScaleCalibration.setStorage() != 0) return -1; + #if SINGLE_HX711 == 0 + if (sysParaScale2Calibration.setStorage() != 0) return -1; + #endif if (sysParaScaleKnownWeight.setStorage() != 0) return -1; return storageCommit(); diff --git a/src/scalevoid.h b/src/scalevoid.h index 26752bc16..3cc05bfbd 100644 --- a/src/scalevoid.h +++ b/src/scalevoid.h @@ -8,42 +8,45 @@ #if (BREWMODE == 2 || ONLYPIDSCALE == 1) -void calibrate() { - calibrationON = 0; - LoadCell.setCalFactor(1.0); +void calibrate(HX711_ADC loadCell, int pin, sto_item_id_t name, float *calibration) { + loadCell.setCalFactor(1.0); u8g2.clearBuffer(); u8g2.drawStr(0, 22, "Calibration coming up"); - u8g2.drawStr(0, 32, "Empty the scale"); + u8g2.drawStr(0, 32, "Empty scale "); + u8g2.print(pin, 0); u8g2.sendBuffer(); - debugPrintf("Taking scale zero point\n"); - LoadCell.update(); - LoadCell.tare(); - delay(2000); - debugPrintf("Put load on scale\n"); + debugPrintf("Taking scale %d to zero point\n", pin); + loadCell.update(); + loadCell.tare(); + debugPrintf("Put load on scale %d within the next 10 seconds\n", pin); u8g2.clearBuffer(); u8g2.drawStr(2, 2, "Calibration in progress."); u8g2.drawStr(2, 12, "Place known weight"); u8g2.drawStr(2, 22, "on scale in next"); - u8g2.drawStr(2, 32," 10 seconds"); + u8g2.drawStr(2, 32,"10 seconds"); u8g2.drawStr(2, 42, number2string(scaleKnownWeight)); u8g2.sendBuffer(); delay(10000); debugPrintf("Taking scale load point\n"); - LoadCell.refreshDataSet(); - scaleCalibration = LoadCell.getNewCalibration(scaleKnownWeight); - LoadCell.setCalFactor(scaleCalibration); - debugPrintf("New calibration: %f\n", scaleCalibration); + // increase scale samples temporarily to ensure a stable reading + loadCell.setSamplesInUse(128); + loadCell.refreshDataSet(); + *calibration = loadCell.getNewCalibration(scaleKnownWeight); + loadCell.setSamplesInUse(SCALE_SAMPLES); + debugPrintf("New calibration: %f\n", *calibration); u8g2.sendBuffer(); - writeSysParamsToStorage(); + storageSet(name, *calibration, true); u8g2.clearBuffer(); u8g2.drawStr(2, 2, "Calibration done!"); u8g2.drawStr(2, 12, "New calibration:"); - u8g2.drawStr(2, 22, number2string(scaleCalibration)); + u8g2.drawStr(2, 22, number2string(*calibration)); u8g2.sendBuffer(); - delay(5000); - + delay(2000); } +float w1 = 0.0; +float w2 = 0.0; + /** * @brief Check measured weight */ @@ -55,19 +58,38 @@ void checkWeight() { return; } + // check for new data/start next conversion: if (LoadCell.update()) newDataReady = true; + #if SINGLE_HX711 == 0 + // weirdly, the library examples do not check for updates on the second cell before getting the values... + LoadCell2.update(); + #endif if (newDataReady) { if (currentMillisScale - previousMillisScale >= intervalWeight) { - previousMillisScale = currentMillisScale; - weight = LoadCell.getData(); - newDataReady = 0; + previousMillisScale = currentMillisScale; + newDataReady = false; + w1 = LoadCell.getData(); + + #if SINGLE_HX711 == 0 + w2 = LoadCell2.getData(); + // debugPrintf("Current weight: %.2f from %.2f and %.2f\n", weight, w1, w2); + #endif } } + #if SINGLE_HX711 == 0 + weight = w1 + w2; + #else + weight = w1; + #endif + if (calibrationON) { - while (!LoadCell.update()); - calibrate(); + calibrate(LoadCell, PIN_HXDAT, STO_ITEM_SCALE_CALIBRATION_FACTOR, &scaleCalibration); + #if SINGLE_HX711 == 0 + calibrate(LoadCell2, PIN_HXDAT2, STO_ITEM_SCALE2_CALIBRATION_FACTOR, &scale2Calibration); + #endif + calibrationON = 0; } if (tareON) { @@ -79,6 +101,11 @@ void checkWeight() { delay(2000); u8g2.sendBuffer(); LoadCell.tare(); + LoadCell.setCalFactor(scaleCalibration); + #if SINGLE_HX711 == 0 + LoadCell2.setCalFactor(scale2Calibration); + LoadCell2.tare(); + #endif u8g2.drawStr(0, 32, "done"); u8g2.sendBuffer(); delay(2000); @@ -86,50 +113,67 @@ void checkWeight() { } -/** - * @brief Initialize scale - */ void initScale() { - readSysParamsFromStorage(); - LoadCell.begin(128); - long stabilizingtime = 5000; // tare preciscion can be improved by adding a few seconds of stabilizing time + boolean shouldCalibrate = calibrationON; + + LoadCell.begin(); + #if SINGLE_HX711 == 0 + LoadCell2.begin(); + #endif + // u8g2.clearBuffer(); + // u8g2.drawStr(0, 2, "Taring scale"); + // u8g2.drawStr(0, 12, "remove any load!"); + // u8g2.drawStr(0, 22, "...."); + // u8g2.sendBuffer(); + // delay(2000); + + unsigned long stabilizingtime = 5000; // tare preciscion can be improved by adding a few seconds of stabilizing time boolean _tare = true; //set this to false if you don't want tare to be performed in the next step - u8g2.clearBuffer(); - u8g2.drawStr(0, 2, "Taring scale,"); - u8g2.drawStr(0, 12, "remove any load!"); - u8g2.drawStr(0, 22, "...."); - u8g2.sendBuffer(); - delay(2000); - LoadCell.startMultiple(stabilizingtime, _tare); - delay(5000); + + #if SINGLE_HX711 == 1 + while(!LoadCell.startMultiple(stabilizingtime, _tare)); + #else + byte loadCellReady = 0; + byte loadCell2Ready = 0; + // run startup, stabilization and tare, both modules simultaniously + // this parallel start seems to be the most important part to get accurate readings with two HX711s connected + while ((loadCellReady + loadCell2Ready) < 2) { + if (!loadCellReady) loadCellReady = LoadCell.startMultiple(stabilizingtime, _tare); + if (!loadCell2Ready) loadCell2Ready = LoadCell2.startMultiple(stabilizingtime, _tare); + } + #endif if (LoadCell.getTareTimeoutFlag() || LoadCell.getSignalTimeoutFlag() ) { - debugPrintf("Timeout, check MCU>HX711 wiring and pin designations"); + debugPrintf("Timeout, check MCU>HX711 wiring for scale"); u8g2.drawStr(0, 32, "failed!"); u8g2.drawStr(0, 42, "Scale not working..."); // scale timeout will most likely trigger after OTA update, but will still work after boot u8g2.sendBuffer(); - scaleFailure = true; delay(5000); + scaleFailure = true; + return; } - else { - if (calibrationON) { - LoadCell.setCalFactor(1.0); - while (!LoadCell.update()); - calibrate(); - LoadCell.setSamplesInUse(SCALE_SAMPLES); - u8g2.drawStr(0, 52, "done."); - delay(2000); - } - else { - LoadCell.setCalFactor(scaleCalibration); // set calibration factor (float) - LoadCell.setSamplesInUse(SCALE_SAMPLES); - u8g2.drawStr(0, 42, number2string(scaleCalibration)); - u8g2.drawStr(0, 52, "done."); - u8g2.sendBuffer(); - delay(2000); - } + + #if SINGLE_HX711 == 0 + if (LoadCell2.getTareTimeoutFlag() || LoadCell2.getSignalTimeoutFlag() ) { + debugPrintf("Timeout, check MCU>HX711 wiring for scale 2"); + u8g2.drawStr(0, 32, "failed!"); + u8g2.drawStr(0, 42, "Scale not working..."); // scale timeout will most likely trigger after OTA update, but will still work after boot + u8g2.sendBuffer(); + delay(5000); + scaleFailure = true; + return; } + #endif + + LoadCell.setCalFactor(scaleCalibration); + LoadCell.setSamplesInUse(SCALE_SAMPLES); + #if SINGLE_HX711 == 0 + LoadCell2.setCalFactor(scale2Calibration); + LoadCell2.setSamplesInUse(SCALE_SAMPLES); + #endif + + calibrationON = 0; } /** @@ -138,7 +182,7 @@ void initScale() { void shottimerscale() { switch (shottimerCounter) { case 10: // waiting step for brew switch turning on - if (preinfusionpause == 0 || preinfusion == 0) { + if (preinfusionPause == 0 || preinfusion == 0) { if (timeBrewed > 0) { weightPreBrew = weight; shottimerCounter = 20; @@ -146,7 +190,7 @@ void shottimerscale() { } else { if (timeBrewed > preinfusion*1000) { weightPreBrew = weight; - shottimercounter = 20; + shottimerCounter = 20; } } diff --git a/src/userConfig_sample.h b/src/userConfig_sample.h index 0246fc26b..dd720a7fe 100644 --- a/src/userConfig_sample.h +++ b/src/userConfig_sample.h @@ -66,6 +66,7 @@ enum MACHINE { // Brew Scale #define SCALE_SAMPLES 2 // Load cell sample rate +#define SINGLE_HX711 0 // 1 = Only a single HX711 with two channels, 0 = one HX711 per load cell // Backflush values #define FILLTIME 5000 // time in ms the pump is running