diff --git a/.gitignore b/.gitignore index 2fb6b27..f0038d0 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ .vscode/c_cpp_properties.json .vscode/extensions.json .vscode/launch.json +.DS_Store diff --git a/git_rev_macro.py b/git_rev_macro.py new file mode 100644 index 0000000..d357f82 --- /dev/null +++ b/git_rev_macro.py @@ -0,0 +1,9 @@ +import subprocess + +revision = ( + subprocess.check_output(["git", "rev-parse", "HEAD"]) + .strip() + .decode("utf-8") + +) +print("'-DGIT_REV=\"%s\"'" % revision[:6]) diff --git a/include/config.h b/include/config.h index 8fb3ce0..5276a58 100644 --- a/include/config.h +++ b/include/config.h @@ -26,9 +26,9 @@ //#define CHECKBATTERY //SCALE CONFIG -#define LINE1 (char*)"FW: 3.0.1" -#define LINE2 (char*)"Built-date(YYYYMMDD): 20251009" -#define LINE3 (char*)"S/N: HDS001" //Serial number +#define LINE1 (char*)"FW: 3.0.2" +#define LINE2 (char*)"Built-date " +#define LINE3 __DATE__ //Serial number #define VERSION /*version*/ LINE1, /*compile date*/ LINE2, /*sn*/ LINE3 //About info #define FIRMWARE_VER LINE1 @@ -61,10 +61,9 @@ #define ACEBUTTON //ACEBUTTON ACEBUTTONT #define DEBOUNCE 100 -#define LONGCLICK 1000 -#define DOUBLECLICK 300 -#define CLICK 200 -#define BUTTON_KEY_DELAY 150 +#define LONGPRESS_DELAY 900 // threshold for long press +#define DOUBLECLICK_DELAY 600 // max gap between two clicks +#define CLICK_DELAY 180 // max press time that still counts as a click //DISPLAY #define Margin_Top 0 //显示边框 diff --git a/include/menu.h b/include/menu.h index b6a6c42..7040087 100644 --- a/include/menu.h +++ b/include/menu.h @@ -56,6 +56,8 @@ void btnFuncWhileConnectedOn(); void btnFuncWhileConnectedOff(); void autoSleepOn(); void autoSleepOff(); +void quickBootOn(); +void quickBootOff(); // Top-level menu options // 1/5 define the 1st level menu @@ -74,6 +76,7 @@ Menu menuFlipScreen = { "Flip Screen", NULL, NULL, NULL }; Menu menuTimeOnTop = { "Time On Top", NULL, NULL, NULL }; Menu menuBtnFuncWhileConnected = { "Button with BLE", NULL, NULL, NULL }; Menu menuAutoSleep = { "Auto Sleep", NULL, NULL, NULL }; +Menu menuQuickBoot = { "Quick Boot", NULL, NULL, NULL }; // 2/5 define the 2st level menu #ifdef BUZZER @@ -124,7 +127,7 @@ Menu menuTimeOnTopOff = { "Weight On Top", timeOnTopOff, NULL, &menuTimeOnTop }; Menu *timeOnTopMenu[] = { &menuTimeOnTopBack, &menuTimeOnTopOn, &menuTimeOnTopOff }; -// Enable button fucntion while BLE connected +// Enable button function while BLE connected Menu menuBtnFuncWhileConnectedBack = { "Back", NULL, NULL, &menuBtnFuncWhileConnected }; Menu menuBtnFuncWhileConnectedOn = { "Enable Buttons", btnFuncWhileConnectedOn, @@ -136,11 +139,18 @@ Menu *btnFuncWhileConnectedMenu[] = { &menuBtnFuncWhileConnectedBack, &menuBtnFuncWhileConnectedOn, &menuBtnFuncWhileConnectedOff }; +// Auto sleep function Menu menuAutoSleepBack = { "Back", NULL, NULL, &menuAutoSleep }; Menu menuAutoSleepOn = { "Auto Sleep On", autoSleepOn, NULL, &menuAutoSleep }; Menu menuAutoSleepOff = { "Auto Sleep Off", autoSleepOff, NULL, &menuAutoSleep }; Menu *autoSleepMenu[] = { &menuAutoSleepBack, &menuAutoSleepOn, &menuAutoSleepOff }; +// Quick boot function(aka no delay when pressing the button to boot the scale) +Menu menuQuickBootBack = { "Back", NULL, NULL, &menuQuickBoot }; +Menu menuQuickBootOn = { "Quick Boot On", quickBootOn, NULL, &menuQuickBoot }; +Menu menuQuickBootOff = { "Quick Boot Off", quickBootOff, NULL, &menuQuickBoot }; +Menu *quickBootMenu[] = { &menuQuickBootBack, &menuQuickBootOn, &menuQuickBootOff }; + // Menu menuFactoryBack = { "Back", NULL, NULL, &menuFactory }; // Menu menuCalibrateVoltage = { "Calibrate 4.2v", calibrateVoltage, NULL, // &menuFactory }; Menu menuFactoryDebug = { "Debug Info", enableDebug, NULL, @@ -157,7 +167,7 @@ Menu *mainMenu[] = { &menuCalibration, &menuWifi, // &menuWiFiUpdate, &menuAbout, &menuLogo, &menuHeartbeat, &menuFlipScreen, &menuTimeOnTop, - &menuBtnFuncWhileConnected, &menuAutoSleep, + &menuBtnFuncWhileConnected, &menuAutoSleep, &menuQuickBoot, //, &menuFactory }; // &menuHolder1, &menuHolder2, &menuHolder3, &menuHolder4, @@ -184,6 +194,7 @@ void linkSubmenus() { menuTimeOnTop.subMenu = timeOnTopMenu[0]; menuBtnFuncWhileConnected.subMenu = btnFuncWhileConnectedMenu[0]; menuAutoSleep.subMenu = autoSleepMenu[0]; + menuQuickBoot.subMenu = quickBootMenu[0]; // menuFactory.subMenu = factoryMenu[0]; } @@ -200,6 +211,9 @@ void exitMenu() { delay(1000); b_menu = false; // Optionally reset or perform an exit action + t_menuExitTime = millis(); + // Capture the moment when menu exit begins to establish a reference point + // This enables timing-based protection against unintended triggers } #ifdef BUZZER @@ -389,6 +403,26 @@ void autoSleepOff() { Serial.println("Autosleep off stored in EEPROM."); } +void quickBootOn() { + b_quickBoot = true; + actionMessage = "Quick Boot On"; + t_actionMessage = millis(); + t_actionMessageDelay = 1000; + EEPROM.put(i_addr_quickBoot, b_quickBoot); + EEPROM.commit(); + Serial.println("Quick boot on stored in EEPROM."); +} + +void quickBootOff() { + b_quickBoot = false; + actionMessage = "Quick Boot Off"; + t_actionMessage = millis(); + t_actionMessageDelay = 1000; + EEPROM.put(i_addr_quickBoot, b_quickBoot); + EEPROM.commit(); + Serial.println("Quick boot off stored in EEPROM."); +} + void calibrate() { b_menu = false; b_calibration = true; // 让按钮进入校准状态3 @@ -401,7 +435,7 @@ void calibrate() { void calibration(int input) { if (b_calibration == true) { bool newDataReady = false; - char *c_calval = (char *)""; + char c_calval[25]; if (i_button_cal_status == 1) { if (input == 0) { scale.setSamplesInUse(16); @@ -908,17 +942,15 @@ void wifiUpdate() { void showAbout() { actionMessage = FIRMWARE_VER; - // actionMessage2 = PCB_VER; + actionMessage2 = LINE3; b_showAbout = true; - u8g2.setFont(FONT_M); + u8g2.setFont(FONT_S); u8g2.firstPage(); do { - if (AC(actionMessage.c_str()) < 0) - u8g2.setFont(FONT_S); - // u8g2.drawStr(AC(actionMessage.c_str()), AM() - 12, - // actionMessage.c_str()); u8g2.drawStr(AC(actionMessage2.c_str()), AM() + - // 12, actionMessage2.c_str()); - u8g2.drawStr(AC(actionMessage.c_str()), AM(), actionMessage.c_str()); + u8g2.setFont(FONT_S); + u8g2.drawStr(AC(actionMessage.c_str()), AM() - 24, actionMessage.c_str()); + u8g2.drawStr(AC(actionMessage2.c_str()), AM(), actionMessage2.c_str()); + u8g2.drawStr(AC(GIT_REV), AM()+ 24, GIT_REV); } while (u8g2.nextPage()); #ifdef BUZZER buzzer.off(); @@ -1122,6 +1154,9 @@ void selectMenu() { } else if (currentSelection == &menuAutoSleep) { currentMenu = autoSleepMenu; currentMenuSize = getMenuSize(autoSleepMenu); + } else if (currentSelection == &menuQuickBoot) { + currentMenu = quickBootMenu; + currentMenuSize = getMenuSize(quickBootMenu); } // else if (currentSelection == &menuFactory) { // currentMenu = factoryMenu; diff --git a/include/parameter.h b/include/parameter.h index ec0f9d3..12b505b 100644 --- a/include/parameter.h +++ b/include/parameter.h @@ -88,10 +88,37 @@ float f_filtered_temperature = 0; bool b_ads1115InitFail = true; //ads1115 not detected flag bool b_wifiOnBoot = false; bool b_autoSleep = true; - +bool b_quickBoot = false; +unsigned int i_buttonBootDelay = 500; +bool b_showChargingUI = false; //电子秤参数和计时点 -bool b_weight_in_serial = false; +// Enhanced tracking system global variables +static float f_tracking_offset = 0.0; // Current tracking offset +static float f_tracking_target = 0.0; // Current tracking target weight +static unsigned long t_last_tracking_update = 0; // Last tracking update time +static const unsigned long TRACKING_UPDATE_INTERVAL = 5000; // Tracking update interval 5 seconds +static const float TRACKING_THRESHOLD = 0.1; // Tracking stability threshold +static const int i_STABLE_COUNT_THRESHOLD = 5; // Stable count threshold +static const float MAX_TRACKING_ADJUSTMENT = 0.5; // Maximum single adjustment + +static unsigned long t_last_status_display = 0; +static const unsigned long STATUS_DISPLAY_INTERVAL = 5000; +static bool b_weight_in_serial = true; + +static int i_stable_count = 0; // Stable state counter +static bool b_tracking_enabled = true; // Tracking enable flag +static bool b_tracking_active = false; // Whether tracking is currently active + +// Stable output system global variables +static float f_previous_stable_value = 0.0; // Previous stable output value +static float f_current_raw_value = 0.0; // Current raw input value +static float STABLE_OUTPUT_THRESHOLD = 0.1; // Minimum change to update output +static bool b_stable_output_enabled = true; // Stable output enable flag +static unsigned long t_last_stable_change = 0; // Time of last stable change + + + bool b_negativeWeight = false; bool b_weight_quick_zero = false; //Tare后快速显示为0优化 @@ -157,6 +184,10 @@ bool b_extraction = false; //萃取模式标识 int b_mode = 0; //0 = pourover; 1 = espresso; bool b_menu = false; +unsigned long t_menuExitTime = 0; +// Timestamp recording when the menu exit process started +// Used to implement a protection period preventing unintended operations + bool b_calibration = false; //Calibration flag bool b_ota = false; //wifi ota flag int i_calibration = 0; //0 for manual cal, 1 for smart cal @@ -221,6 +252,7 @@ int i_addr_timeOnTop = i_addr_screenFlipped + sizeof(b_screenFlipped); int i_addr_btnFuncWhileConnected = i_addr_timeOnTop + sizeof(b_timeOnTop); //b_btnFuncWhileConnected int i_addr_enableWifiOnBoot = i_addr_btnFuncWhileConnected + sizeof(b_btnFuncWhileConnected); //b_wifiOnBoot int i_addr_autoSleep = i_addr_enableWifiOnBoot + sizeof(b_wifiOnBoot); +int i_addr_quickBoot = i_addr_autoSleep + sizeof(b_autoSleep); //int i_addr_enableWifiOnBoot = i_addr_btnFuncWhileConnected + sizeof(b_wifiOnBoot); diff --git a/include/power.h b/include/power.h index be4577a..9d0b583 100644 --- a/include/power.h +++ b/include/power.h @@ -200,21 +200,23 @@ void shut_down_now() { void shut_down_low_battery(float voltage) { - Serial.print("Low battery, voltage:"); - Serial.println(voltage); - refreshOLED((char*)"Low battery", FONT_M); + if (t_batteryRefresh > 0){ + Serial.print("Low battery, voltage:"); + Serial.println(voltage); + refreshOLED((char*)"Low battery", FONT_M); #ifdef ESPNOW - if (b_espnow) { - b_power_off = 1; - updateEspnow(1); - } - sendBlePowerOff(3); + if (b_espnow) { + b_power_off = 1; + updateEspnow(1); + } + sendBlePowerOff(3); #endif #ifdef BUZZER - buzzer.off(); + buzzer.off(); #endif - delay(1000); - esp32_sleep(); + delay(1000); + esp32_sleep(); + } } void shut_down_now_nobeep() { diff --git a/platformio.ini b/platformio.ini index ae85f39..29ad451 100644 --- a/platformio.ini +++ b/platformio.ini @@ -16,7 +16,7 @@ data_dir = web_apps [env:esp32s3] ;-- esp32 -platform = https://github.com/pioarduino/platform-espressif32/releases/download/55.03.31/platform-espressif32.zip +platform = https://github.com/pioarduino/platform-espressif32/releases/download/stable/platform-espressif32.zip board = esp32-s3-devkitc-1 framework = arduino #board_build.partitions = @@ -27,6 +27,7 @@ build_flags = ; -DESP32 -D CONFIG_ASYNC_TCP_RUNNING_CORE=1 -DELEGANTOTA_USE_ASYNC_WEBSERVER=1 + !python3 git_rev_macro.py # -D DEBUG # diff --git a/src/hds.ino b/src/hds.ino index 7eb5b0b..ef0a4dd 100644 --- a/src/hds.ino +++ b/src/hds.ino @@ -145,6 +145,15 @@ void buttonCircle_Released() { } void buttonCircle_Pressed() { + if (b_showChargingUI && i_buttonBootDelay == 0) { + //change GPIO_power_on_with from BATTERY_CHARGING to enter scale loop + GPIO_power_on_with = BUTTON_CIRCLE; + b_showChargingUI = false; + b_ble_enabled = true; + ble_init(); + wifi_init(); + } + if (b_menu) { navigateMenu(1); // Navigate to next menu item } @@ -160,6 +169,11 @@ void buttonCircle_Pressed() { } void buttonSquare_Pressed() { + if (b_showChargingUI && i_buttonBootDelay == 0) { + //change GPIO_power_on_with from BATTERY_CHARGING to enter scale loop + GPIO_power_on_with = BUTTON_SQUARE; + } + if (b_menu) { selectMenu(); // Select current menu item } @@ -175,9 +189,12 @@ void buttonSquare_Pressed() { b_powerOff = true; } if (!b_menu && !b_calibration && (!deviceConnected || b_btnFuncWhileConnected)) { - scaleTimer(); + if (millis() - t_menuExitTime > 500) + // Check if enough time has passed since menu exit (500ms protection period) + scaleTimer(); } + Serial.println("[] button pressed"); sendUsbButton(2, 1); if (deviceConnected) { @@ -259,9 +276,7 @@ void buttonCircle_LongPressed() { b_ble_enabled = true; ble_init(); wifi_init(); - } else if (GPIO_power_on_with == BUTTON_CIRCLE || GPIO_power_on_with == BUTTON_SQUARE) { - buttonCircle_DoubleClicked(); - } + } // sendUsbButton(1, 2); // if (deviceConnected) { // Serial.println("Send O button long pressed BLE command"); @@ -296,20 +311,20 @@ void button_init() { buttonCircle.init(BUTTON_CIRCLE); buttonSquare.init(BUTTON_SQUARE); config1.setEventHandler(aceButtonHandleEvent); - //config1.setFeature(ButtonConfig::kFeatureClick); + config1.setFeature(ButtonConfig::kFeatureClick); //config1.setFeature(ButtonConfig::kFeatureSuppressAfterClick); config1.setFeature(ButtonConfig::kFeatureDoubleClick); - config1.setFeature(ButtonConfig::kFeatureSuppressClickBeforeDoubleClick); + // config1.setFeature(ButtonConfig::kFeatureSuppressClickBeforeDoubleClick); config1.setFeature(ButtonConfig::kFeatureLongPress); //config1.setFeature(ButtonConfig::kFeatureSuppressAfterLongPress); - config1.setDoubleClickDelay(DOUBLECLICK); - config1.setLongPressDelay(LONGCLICK); + // config1.setDoubleClickDelay(DOUBLECLICK); + // config1.setLongPressDelay(LONGCLICK); + config1.setClickDelay(CLICK_DELAY); + config1.setDoubleClickDelay(DOUBLECLICK_DELAY); + config1.setLongPressDelay(LONGPRESS_DELAY); } -void wifi_init() { - if (!readBoolEEPROMWithValidation(i_addr_enableWifiOnBoot, false)) { - return; - } +void _wifi_init(void *args) { b_wifiEnabled = true; setupWifi(); startWebServer(); @@ -342,13 +357,30 @@ void wifi_init() { } } }); + vTaskDelete(NULL); +} +void wifi_init() { + if (!b_wifiOnBoot) { + return; + } + xTaskCreate(_wifi_init, "Wifi Init Task", configMINIMAL_STACK_SIZE + 2048, NULL, 0, NULL); } - void setup() { Serial.begin(115200); while (!Serial) // Wait for the Serial port to initialize (typically used in Arduino to ensure the Serial monitor is ready) ; + if (!EEPROM.begin(512)) { + Serial.println("EEPROM init failed!"); + while (1) { + delay(1000); + } + } + + if (readBoolEEPROMWithValidation(i_addr_quickBoot, false)) + i_buttonBootDelay = 0; + + Serial.println("EEPROM init success"); button_init(); linkSubmenus(); pinMode(BATTERY_CHARGING, INPUT_PULLUP); @@ -379,29 +411,30 @@ void setup() { b_ble_enabled = true; } while (true && GPIO_power_on_with > 0) { + if (i_buttonBootDelay == 0){ + Serial.println("Quick boot. Powering on..."); + // Execute power on logic + break; // Exit loop to continue with other code + } + if (digitalRead(GPIO_power_on_with) == LOW) { // Button is pressed if (!b_button_pressed) { t_power_on_button = millis(); b_button_pressed = true; // Mark button as pressed } - if (millis() - t_power_on_button >= 500) { + if (millis() - t_power_on_button >= i_buttonBootDelay) { Serial.println("Button held for 0.5 second. Powering on..."); // Execute power on logic break; // Exit loop to continue with other code } } else { - // If the button is released - if (b_button_pressed) { - //Power on by button press - Serial.println("Button released before 0.5 second."); - Serial.println("Going to sleep now."); - stopWebServer(); - stopWifi(); - esp32_sleep(); - //shut_down_now_nobeep(); - break; // Exit loop to enter sleep mode - } + Serial.println("Button released before 0.5 second."); + Serial.println("Going to sleep now."); + stopWebServer(); + stopWifi(); + esp32_sleep(); + break; // Exit loop to enter sleep mode b_button_pressed = false; // Reset mark } } @@ -426,17 +459,9 @@ void setup() { ADS_init(); #endif delay(50); - updateBattery(BATTERY_PIN); if (b_ble_enabled) { ble_init(); } - if (!EEPROM.begin(512)) { - Serial.println("EEPROM init failed!"); - while (1) { - delay(1000); - } - } - Serial.println("EEPROM init success"); Serial.println("Begin!"); #if defined(ACC_MPU6050) || defined(ACC_BMA400) ACC_init(); @@ -493,7 +518,7 @@ void setup() { //adc init unsigned long stabilizingtime = 500; //taring duration. longer for better reading. - boolean _tare = true; //电子秤初始化去皮,如果不想去皮则设为false + bool _tare = true; //电子秤初始化去皮,如果不想去皮则设为false //whether the scale will tare on start. scale.begin(); scale.setSamplesInUse(4); //设置灵敏度 @@ -596,18 +621,16 @@ void setup() { //calibration value is not valid, go to calibration procedure. } #endif - wifi_init(); - // //wifiota - // #ifdef WIFI + b_wifiOnBoot = readBoolEEPROMWithValidation(i_addr_enableWifiOnBoot, false); + if (b_wifiOnBoot && GPIO_power_on_with != BATTERY_CHARGING) { + wifi_init(); + } + // Enter Menu if (digitalRead(BUTTON_CIRCLE) == LOW && digitalRead(BUTTON_SQUARE) == LOW) { - //wifiOta(); - b_menu = true; refreshOLED((char *)"HDS Setup", FONT_EXTRACTION); delay(1000); - //showMenu(); } - // #endif #if DEBUG Serial.print("digitalRead(BUTTON_CIRCLE):"); @@ -632,8 +655,10 @@ void setup() { Serial.print(LINE1); Serial.print("\t"); Serial.print(LINE2); - // Serial.print("\t"); - // Serial.println(LINE3); + Serial.print("\t"); + Serial.print(LINE3); + Serial.print("\t"); + Serial.println(GIT_REV); Serial.print("\tCal_Val: "); Serial.print(f_calibration_value); Serial.print("\tHB_DET: "); @@ -697,8 +722,357 @@ void setup() { Serial.println("Setup complete..."); t_bootTare = millis(); b_bootTare = true; + updateBattery(BATTERY_PIN); } +/** + * Enhanced adaptive tracking system + * Tracks both zero and stable weights to prevent oscillation + */ +void updateAdaptiveTracking(float current_weight) { + unsigned long current_time = millis(); + + if (!b_tracking_enabled) { + return; + } + + // Calculate weight difference from current tracking target + float weight_diff = current_weight - f_tracking_target; + + // Check if weight is stable (within tracking threshold) + if (fabs(weight_diff) <= TRACKING_THRESHOLD) { + i_stable_count++; + + // Update tracking target to slowly follow stable weights + if (i_stable_count >= 3) { // Start adjusting target after 3 stable readings + float adjustment = weight_diff * 0.1; // Slow adaptation + + // Limit maximum adjustment to prevent large jumps + if (fabs(adjustment) > MAX_TRACKING_ADJUSTMENT) { + adjustment = (adjustment > 0) ? MAX_TRACKING_ADJUSTMENT : -MAX_TRACKING_ADJUSTMENT; + } + + f_tracking_target += adjustment; + } + + } else { + // Weight changed significantly - likely a real weight change + i_stable_count = 0; + b_tracking_active = false; + + // If weight change is large and persistent, update tracking target + if (fabs(weight_diff) > TRACKING_THRESHOLD * 2) { + // Consider this as a new stable weight after verification + if (verifyWeightStability(current_weight)) { + f_tracking_target = current_weight; + b_tracking_active = true; + if (b_weight_in_serial) { + Serial.print("New weight target set: "); + Serial.println(f_tracking_target, 4); + } + } + } + } + + // Perform tracking adjustment when conditions are met + if (i_stable_count >= i_STABLE_COUNT_THRESHOLD) { + if (current_time - t_last_tracking_update >= TRACKING_UPDATE_INTERVAL) { + performTrackingAdjustment(current_weight); + } + } +} + +/** + * Perform the actual tracking adjustment + */ +void performTrackingAdjustment(float current_weight) { + float old_offset = f_tracking_offset; + + // Calculate new offset based on current weight and target + float calculated_offset = current_weight - f_tracking_target; + + // Apply slow adaptation to prevent sudden changes + f_tracking_offset = f_tracking_offset * 0.8 + calculated_offset * 0.2; + + // Activate tracking if not already active + if (!b_tracking_active) { + b_tracking_active = true; + } + + // Debug output + if (b_weight_in_serial) { + Serial.print("Tracking adjustment: Offset "); + Serial.print(old_offset, 4); + Serial.print("g -> "); + Serial.print(f_tracking_offset, 4); + Serial.print("g | Target: "); + Serial.print(f_tracking_target, 4); + Serial.print("g | Raw: "); + Serial.print(current_weight, 4); + Serial.println("g"); + } + + // Reset counters + i_stable_count = i_STABLE_COUNT_THRESHOLD - 2; // Keep near threshold for continuous tracking + t_last_tracking_update = millis(); +} + +/** + * Verify if a weight is stable enough to be considered a new target + */ +bool verifyWeightStability(float current_weight) { + static float last_verified_weight = 0.0; + static int verification_count = 0; + + if (fabs(current_weight - last_verified_weight) <= TRACKING_THRESHOLD) { + verification_count++; + } else { + verification_count = 0; + } + + last_verified_weight = current_weight; + + // Require 3 consecutive stable readings to verify new weight + return (verification_count >= 3); +} + +/** + * Apply tracking compensation to raw weight + */ +float applyTrackingCompensation(float raw_weight) { + if (b_tracking_active && b_tracking_enabled) { + return raw_weight - f_tracking_offset; + } + return raw_weight; +} + +/** + * Apply stable output filtering + * Returns the same value if change is below threshold + */ +float applyStableOutput(float current_value) { + if (!b_stable_output_enabled) { + return current_value; // Bypass stable filtering if disabled + } + + float change = fabs(current_value - f_previous_stable_value); + + // If change is significant, update the stable value + if (change >= STABLE_OUTPUT_THRESHOLD) { + f_previous_stable_value = current_value; + t_last_stable_change = millis(); + + // Debug output for significant changes + if (b_weight_in_serial) { + Serial.print("Output updated: "); + Serial.print(current_value, 4); + Serial.print("g (Change: "); + Serial.print(change, 4); + Serial.println("g)"); + } + } + + // Always return the stable value (may be same as previous) + return f_previous_stable_value; +} + +/** + * Enhanced pureScale function with all features + */ +void pureScale() { + static bool b_newDataReady = 0; + + if (scale.update()) b_newDataReady = true; + + if (b_newDataReady) { + float raw_weight = scale.getData(); + f_current_raw_value = raw_weight; // Store for status display + + // Apply adaptive tracking compensation + float compensated_weight = applyTrackingCompensation(raw_weight); + + // Apply stable output filtering + float stable_weight = applyStableOutput(compensated_weight); + + // Dead zone processing (apply to stable weight) + if (stable_weight >= -0.14 && stable_weight <= 0.14) { + f_displayedValue = 0.0; + } else { + f_displayedValue = stable_weight; + } + + f_weight_before_input = f_displayedValue; + + // Update adaptive tracking (use compensated weight, not stable weight) + updateAdaptiveTracking(compensated_weight); + + // Convert and display + dtostrf(f_displayedValue, 7, i_decimal_precision, c_weight); + if (b_weight_in_serial == true) { + //Serial.println(trim(c_weight)); + + // Display status periodically + unsigned long current_time = millis(); + if (current_time - t_last_status_display >= STATUS_DISPLAY_INTERVAL) { + displayEnhancedStatus(raw_weight, compensated_weight, stable_weight); + t_last_status_display = current_time; + } + } + b_newDataReady = false; + } + + // Handle tare operation + if (scale.getTareStatus()) { + t_tareStatus = millis(); + b_weight_quick_zero = false; + resetTracking(); + resetStableOutput(); // Also reset stable output + } + + // Quick zero handling + if (b_weight_quick_zero || b_bootTare) { + f_displayedValue = 0.0; + } +} + +/** + * Reset tracking system (for tare/zero operations) + */ +void resetTracking() { + f_tracking_offset = 0.0; + f_tracking_target = 0.0; + i_stable_count = 0; + b_tracking_active = false; + t_last_tracking_update = millis(); + if (b_weight_in_serial) { + Serial.println("Tracking system reset"); + } +} + +/** + * Reset stable output system + */ +void resetStableOutput() { + f_previous_stable_value = 0.0; + t_last_stable_change = millis(); + if (b_weight_in_serial) { + Serial.println("Stable output reset"); + } +} + +/** + * Enable/disable stable output + */ +void setStableOutputEnabled(bool enabled) { + b_stable_output_enabled = enabled; + if (!enabled) { + resetStableOutput(); + } + Serial.print("Stable output "); + Serial.println(enabled ? "enabled" : "disabled"); +} + +/** + * Set stable output threshold + */ +void setStableOutputThreshold(float threshold) { + STABLE_OUTPUT_THRESHOLD = threshold; + Serial.print("Stable threshold set to: "); + Serial.println(threshold, 4); +} + +/** + * Enable/disable tracking system + */ +void setTrackingEnabled(bool enabled) { + b_tracking_enabled = enabled; + if (!enabled) { + resetTracking(); + } + Serial.print("Tracking system "); + Serial.println(enabled ? "enabled" : "disabled"); +} + +/** + * Enhanced status display with all system info + */ +void displayEnhancedStatus(float raw_weight, float compensated_weight, float stable_weight) { + Serial.println("=== Enhanced Scale Status ==="); + Serial.print("Raw Input: "); + Serial.print(raw_weight, 4); + Serial.print("g | Compensated: "); + Serial.print(compensated_weight, 4); + Serial.print("g | Stable Output: "); + Serial.print(stable_weight, 4); + Serial.println("g"); + + Serial.print("Stable Output: "); + Serial.print(b_stable_output_enabled ? "ON" : "OFF"); + Serial.print(" | Threshold: ±"); + Serial.print(STABLE_OUTPUT_THRESHOLD, 4); + Serial.println("g"); + + Serial.print("Last Stable Change: "); + Serial.print((millis() - t_last_stable_change) / 1000); + Serial.println("s ago"); + + // Tracking status + Serial.print("Tracking System: "); + Serial.print(b_tracking_enabled ? "ON" : "OFF"); + Serial.print(" | Active: "); + Serial.println(b_tracking_active ? "YES" : "NO"); + + Serial.print("Tracking Offset: "); + Serial.print(f_tracking_offset, 4); + Serial.print("g | Target: "); + Serial.print(f_tracking_target, 4); + Serial.println("g"); + + Serial.print("Stable Count: "); + Serial.print(i_stable_count); + Serial.print("/"); + Serial.println(i_STABLE_COUNT_THRESHOLD); + + Serial.println("============================="); +} + +/** + * Get current tracking offset + */ +float getTrackingOffset() { + return f_tracking_offset; +} + +/** + * Get current stable output value + */ +float getStableOutputValue() { + return f_previous_stable_value; +} + +// Optional: Manual control functions +/** + * Manual override - set specific tracking offset + */ +void setManualTrackingOffset(float offset) { + f_tracking_offset = offset; + b_tracking_active = true; + Serial.print("Manual tracking offset set: "); + Serial.println(offset, 4); +} + +/** + * Manual override - set specific stable value + */ +void setManualStableValue(float value) { + f_previous_stable_value = value; + t_last_stable_change = millis(); + Serial.print("Manual stable value set: "); + Serial.println(value, 4); +} + + +/* void pureScale() { static boolean newDataReady = 0; static boolean scaleStable = 0; @@ -764,6 +1138,7 @@ void pureScale() { ratio_temp = 0.0; dtostrf(ratio_temp, 7, i_decimal_precision, c_brew_ratio); } +*/ void serialCommand() { if (Serial.available()) { @@ -953,6 +1328,7 @@ void loop() { #endif //DEBUG if (millis() - t_batteryRefresh > i_batteryRefreshTareInterval){ updateBattery(BATTERY_PIN); + t_batteryRefresh = millis(); } checkBattery(); if (b_menu) { @@ -961,8 +1337,10 @@ void loop() { if (b_chargingOLED) { if (digitalRead(BATTERY_CHARGING) == LOW && !b_calibration) { float perc = map(f_batteryVoltage * 1000, showEmptyBatteryBelowVoltage * 1000, showFullBatteryAboveVoltage * 1000, 0, 100); //map funtion doesn't take float as input. - chargingOLED((int)perc, f_batteryVoltage); //show charging ui + chargingOLED((int)perc, f_batteryVoltage); + b_showChargingUI = true; //show charging ui } else { + b_showChargingUI = false; if (f_batteryVoltage > 4.1) { //charging complete Serial.println("Charging compelete."); @@ -1417,4 +1795,4 @@ void drawTare() { // u8g2.drawStr(AC((char *)"TARE"), 60, (char *)"TARE"); u8g2.drawBox(30, 62, 128 - 30 * 2, 2); } -} \ No newline at end of file +} diff --git a/src/wifi_setup.cpp b/src/wifi_setup.cpp index 602c27c..0e6851a 100644 --- a/src/wifi_setup.cpp +++ b/src/wifi_setup.cpp @@ -43,6 +43,7 @@ void setupAP() { Serial.println("WiFi: DecentScale"); Serial.print("IP: "); Serial.println(WiFi.softAPIP()); + b_wifiEnabled = true; } void connectToWifi() { @@ -60,9 +61,11 @@ void connectToWifi() { wifiCounter++; delay(1000); Serial.println("."); + // Toggle, to emulate blinking? + b_wifiEnabled = !b_wifiEnabled; if (wifiCounter > 20) { WiFi.disconnect(true); - delay(100); + delay(200); setupAP(); break; } @@ -72,6 +75,7 @@ void connectToWifi() { Serial.println(WiFi.SSID().c_str()); Serial.println("IP address: "); Serial.println(WiFi.localIP().toString().c_str()); + b_wifiEnabled = true; } void stopWifi() { WiFi.disconnect(true); }