Skip to content

Merge Feature/ai tuning port#101

Open
eamars wants to merge 34 commits into
eamars:mainfrom
Jump73:feature/ai-tuning-port
Open

Merge Feature/ai tuning port#101
eamars wants to merge 34 commits into
eamars:mainfrom
Jump73:feature/ai-tuning-port

Conversation

@eamars
Copy link
Copy Markdown
Owner

@eamars eamars commented Mar 16, 2026

This is just an comparison between the base and the AI tuning port.

Jump73 and others added 30 commits March 12, 2026 04:46
New files ported from Jump73/OpenTrickler-v2 (rp2350 branch):
- ai_tuning.c/h: AI/ML auto-tuning system with drop telemetry,
  adaptive step-halving and Gaussian Process PID refinement,
  records last 10 drops with coarse/fine Kp/Kd history
- rest_ai_tuning.c/h: REST API endpoints for AI tuning session
- error.c/h: centralized error handling system (100+ error codes,
  8-entry circular buffer, reports to LED/display/REST)
- rest_errors.c/h: REST API for error querying and clearing
- flash_storage.c/h: flash-based persistence for ML history
  (4KB sector at end of 2MB flash, faster than I2C EEPROM)
- app_state.c/h: application state management for mode transitions
- display_config.c/h: display type/rotation/brightness persistence
- styles.css.h, favicon.ico.h, web_portal_basic.html.h: web resources

Modified files:
- src/gng_scale.c: fix partial-frame carryover on timeout — reset
  buffer index and flush stale RX data at start of each poll cycle;
  add 200ms response timeout with mutex-protected scale_write()
- src/rest_endpoints.c: register AI tuning, error, display_config
  endpoints; add flash_storage_init/ai_tuning_init/rest_ai_tuning_init
  calls; add /basic, /styles.css, /favicon.ico handlers

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Replace missing encoder.h with mini_12864_module.h in ai_tuning.c and rest_ai_tuning.c
- Add acquire/release_display_buffer_access() declarations to display.h
- Add EEPROM_AI_TUNING_CONFIG_BASE_ADDR to eeprom.h
- Add coarse_time_target_ms and total_time_target_ms fields to eeprom_charge_mode_data_t
- Remove duplicate display_rotation_t from display_config.h (use mini_12864_module.h)
- Remove duplicate exit_state definition from menu.c (owned by app_state.c)
- Add app_state.h include to menu.c

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- charge_mode_wait_for_complete() now consults ai_tuning_get_motor_mode()
  and ai_tuning_get_next_params() to inject tuned Kp/Kd into the PID loop
- Phase 2 (FINE_ONLY): coarse pre-charge using session->coarse_kp_best
  before fine PID loop runs
- Motors skipped per AI motor mode (COARSE_ONLY skips fine, FINE_ONLY
  skips normal coarse)
- ai_tuning_record_drop() called after each charge with full telemetry
- ai_tuning_record_charge() called during normal charges when
  ml_data_collection_enabled is true
- RST during AI tuning calls ai_tuning_cancel()
- charge_mode_menu() exits after cup removal when ai_tuning_is_complete()
- charge_mode.h: add ml_data_collection_enabled field, bump EEPROM rev to 10
- profile.h: export profile_get_selected_idx() declaration

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- charge_mode.cpp: extend JSON response with c14 (coarse_time_target_ms),
  c15 (total_time_target_ms), c16 (ml_data_collection_enabled)
- web_portal.html: add AI tuning live panel (aiTuningMainPanel) and ML
  suggestions banner to trickler section; add full AI Tuning settings
  page with profile/target/time-target fields, status display, start/
  cancel/apply/clear-history controls, advanced Kp/Kd range config, and
  ML data collection toggle; add AI Tuning nav item; add all supporting
  JS functions (loadAiTuningPage, refreshAiTuningStatus,
  pollAiTuningMainPanel, aiTuningStart/Cancel/Apply/ClearHistory,
  aiTuningSaveThresholds, aiToggleMlDataCollection,
  aiTuningSaveAdvancedConfig)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… sync

- Profile selectors: dynamically populated with profile names from
  /rest/profile_summary; AI tuning and Settings profiles stay in sync
  (change in one updates the other via onAiTuningProfileChanged /
  onQuickProfileSelectConfirmed)
- Live panel: add Phase badge (Phase 1: Coarse / Phase 2: Fine), Drop
  count, Testing Kp/Kd, Last drop result (+overthrow, time)
- Settings page redesign matching OpenTrickler-v2:
  - Info alert at top
  - Thresholds form (c5 Coarse Stop, c6 Fine Stop, c14 Coarse Time,
    c15 Total Time) with Apply Thresholds button
  - Status badge + drop count
  - Cycle Report log (scrollable, appends each drop result)
  - Current Test Parameters stat boxes (4 values, shown when active)
  - Recommended Parameters stat boxes in success colour (shown on complete)
  - Avg Overthrow + Avg Time statistics
  - Scale Zero / Tare button
  - Start / Cancel / Apply Recommended Parameters buttons
  - Advanced Settings collapsed table (Kp/Kd Min/Max + Noise Margin)
  - ML Data Collection section: toggle, drop count, ML suggestions
    (Fine Kp/Kd) with Apply ML Suggestions, collapsible Drop History
    table with #/Profile/Overthrow/Time columns

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add 'mlRecommendationBanner' (green alert) on trickler page shown
  when ML data collection has accumulated enough drops for suggestions
- Banner click calls onMlRecommendationClicked(): navigates to
  Settings > AI Tuning and scrolls to the ML suggestions section
  where user can review Fine Kp/Kd and apply to profile
- pollAiTuningMainPanel() checks /rest/ai_tuning_history every ~5s
  (every 10th 500ms tick) to detect new suggestions without hammering REST
- AI Tuning complete banner (mlSuggestionsBanner) and ML recommendation
  banner (mlRecommendationBanner) are now independent

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Define _aiCfgDefaults matching ai_tuning.c #define values:
  Coarse Kp [0.01..1.0], Coarse Kd [0.01..2.0],
  Fine Kp [0.01..5.0], Fine Kd [0.01..20.0], Noise Margin 0.05
- aiLoadAdvancedConfig() now uses firmware value when non-zero, falls
  back to hardcoded default — fields are never blank on first open
- aiTuningSaveAdvancedConfig() reuses shared _aiCfgFieldMap to avoid
  duplication

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Apply firmware defaults (c5=5, c6=0.03, c14=500ms, c15=3000ms) when
REST response values are zero or undefined — same pattern used for
Advanced Settings fields. Also populate defaults on fetch error.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Dropdown under "Charge Control" shows all named profiles and switches
the active firmware profile immediately on selection. All profile
selectors (trickler, AI tuning, settings) stay in sync with each other.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add current_profile_idx to ai_tuning_history_t (HISTORY_REV 4→5)
- ai_tuning_record_charge() auto-clears history when profile_idx
  differs from the last recorded profile, so each profile gets a
  fresh ML session; stale flash data from old rev is also discarded
- Expose profile_idx in /rest/ai_tuning_history JSON response

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Firmware:
- Defer ai_tuning_record_charge() to charge_mode_wait_for_cup_removal()
  matching v2 pattern — flash_safe_execute was timing out during active
  charge loop with WiFi stack running on core 1
- Use settled scale reading (after 1s delay) for overthrow calculation
  instead of immediate post-charge reading

Web UI:
- Auto-refresh ML history count every 5s while on AI Tuning settings page
  (start on page open, stop on navigate away)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Auto-refresh now calls full aiLoadHistory() (badge + table + suggestions
  all stay in sync) instead of partial badge-only fetch
- ML Recommendation banner requires both has_suggestions=true AND count>=3
  so it won't show after a clear or when stale has_suggestions is in flash

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Cache profile list after first fetch so subsequent page navigations
(Trickler, AI Tuning, Settings) populate dropdowns instantly without
waiting for a new HTTP request. RP2040's single-threaded HTTP server
was serializing 4-5 concurrent fetches, leaving dropdowns empty for
several seconds. Profile index stays in sync on every profile change.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- REST: Fix circular buffer iteration order in http_rest_ai_tuning_history()
  to return drops in chronological order (oldest first). When buffer is full,
  start from next_idx; when partial, start from 0.

- JS aiLoadHistory(): use Array.isArray() guard for drops, check count===0
  (matching v2 pattern) to avoid early return when drops parse incorrectly,
  update _mlHasSuggestions for shared use by pollAiTuningMainPanel.

- JS pollAiTuningMainPanel(): eliminate duplicate /rest/ai_tuning_history
  fetch when setInterval is already polling (on AI Tuning settings page) —
  reuse cached _mlHasSuggestions value instead.

- Fix banner assignment: mlRecommendationBanner (green) is now correctly
  controlled by AI session isComplete; mlSuggestionsBanner (blue) by ML
  data collection suggestions (_mlHasSuggestions).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… change

- Remove AI Tuning banners from Trickler page (mlSuggestionsBanner,
  mlRecommendationBanner) — user requested these be disabled on this page.
  Also removed the associated _tick polling loop that was hammering REST.

- Fix profile dropdown showing #N instead of name: call loadProfileDropdowns()
  when entering Settings page so names are available immediately from cache.

- Clear ML drop history when profile changes in onTricklerProfileChanged,
  onQuickProfileSelectConfirmed, onAiTuningProfileChanged — each profile
  has independent learning data and switching should reset the history.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
confirm() can block or fail silently on embedded/mobile browsers.
Clear now works immediately without confirmation popup.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Fix critical bug: in COARSE_ONLY mode (Phase 1), PID loop now exits at
  coarse_stop_threshold instead of fine_stop_threshold. Previously with no
  fine motor running, the loop would never exit when there was no overshoot.
- Defer AI telemetry recording to cup_removal (1s settle) for accurate weight,
  matching v2 logic. Phase 1 overthrow now computed against coarse stop point.
- Add charge mode state steps widget (Wait/Charging/Remove Cup/Return Cup) to
  AI Tuning settings page, updated by _pollAiTuningSettingsPage via Promise.all.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- In COARSE_ONLY (Phase 1) and FINE_ONLY (Phase 2) modes, stop motor when
  PID output <= 0 instead of forcing min_speed. Matches v2 behavior — without
  this the motor kept running at min_speed past the target causing huge overshoot.
- Fix _pollAiTuningSettingsPage: replace Promise.all (stops on any error) with
  sequential fetch — charge_mode_state failure no longer kills AI status polling.
  Polling now retries after 1s on error instead of stopping permanently.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…tches v2)

Previously RP2040 used a single measurement + fixed-speed + fixed-time pre-charge
for Phase 2 (FINE_ONLY). v2 uses a full PID loop with recommended_coarse_kp/kd
from Phase 1, stopping when precharge_error < coarse_stop_threshold, then 1s
settle. The fixed-time approach could massively overshoot, leaving the fine PID
starting from beyond the coarse stop point.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add pulse mode (c18-c21): short motor bursts near target instead of
  continuous PID, helps with slow scales; threshold 0.3-1.0 gr range
- Add auto-zero on cup return (c17): optionally force-zeros scale when
  cup is returned to tray
- Add scale fail safety: emergency stop motors after 10 consecutive
  failed scale reads (~2s) in both main charge loop and pre-charge loop
- Bump EEPROM_CHARGE_MODE_DATA_REV 10→12 for new fields
- Expand JSON buffer 512→640 bytes for c17-c21 fields
- Add Pulse Mode and Auto-Zero UI to Charge Mode settings page

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds two new EEPROM-persisted settings (rev 13):
- stabilization_enabled: true = fixed wait, false = adaptive SD-based (up to 3s)
- stabilization_time_ms: fixed wait duration when enabled (default 2000ms)

Replaces the hard-coded 1000ms vTaskDelay in charge_mode_wait_for_cup_removal()
with the configurable logic so scale readings used for overthrow/underthrow
decisions and ML recording are reliably settled.

Adds c22/c23 REST parsing, JSON response fields, and UI controls in web_portal.html.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
LWIP_HTTPD_MAX_CGI_PARAMETERS defaulted to 16, silently dropping
all params beyond the 16th in a GET request. The charge mode form
sends 24 params (c1-c23 + ee), so c17 onwards (auto-zero, pulse mode,
stabilization, EEPROM save) were never received by the server.

Also raise LWIP_HTTPD_MAX_REQUEST_URI_LEN from 128 to 512 to prevent
URL truncation for large forms.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds a shield/checkmark icon button between Settings and Purge that
jumps directly to the AI Tuning settings page with one tap, without
requiring the user to open Settings first.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
ML History:
- AI_TUNING_HISTORY_SIZE: 10 → 50 records (continuous recording per profile)
- ML suggestion threshold: 10 → 3 drops in web UI
- JSON buffer: 2048 → 4096 bytes for larger history response
- History rev bumped to 6

Stabilization timing:
- Add CHARGE_MODE_STABILIZING (state 5) between charging and cup removal
- After motors stop → state=STABILIZING, display shows "Stabilizing..."
- After wait completes → state=WAIT_FOR_CUP_REMOVAL, display shows "Remove Cup"
- Web UI steps bar shows "Settling..." during state 5, "Remove Cup" after
- Both main charge view and AI tuning charge view updated

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- First suggestion shown only after 10 normal-dispensing drops recorded
- Suggestion recalculates at each subsequent 10-drop milestone (20, 30, ...)
- After applying (history reset to 0), requires 10 new drops again
- AI auto-tuning path (ai_tuning_record_drop) is unaffected

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- New driver (ohaus_scale.c/h) with tare command T\r\n and sign-aware
  float parser for Ohaus output format (e.g. "ST,GS, +      0.0000 g")
- Register SCALE_DRIVER_OHAUS_PR = 9 in enum, set_scale_driver() and
  get_scale_driver_string()
- Add "Ohaus PR Series" option to web UI scale driver selector
- Fix NULL dereference in http_rest_scale_action when force_zero is NULL
  (Generic Scale Driver has no tare command)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Jump73 and others added 4 commits March 22, 2026 14:03
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
pioasm generated stepper.pio.h with pio_version=1 but RP2040 has PIO
hardware version 0. pio_add_program() returns PICO_ERROR_VERSION_MISMATCH
causing motor init to fail at boot with "PIO ERR" on display.

Add .pio_version 0 to stepper.pio source so pioasm regenerates the header
correctly. RP2350 was unaffected (PIO version 1 hardware).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- ohaus_scale.c: add strtof fallback parser for output without explicit
  sign (e.g. "ST,GS,      0.5000 g\r\n"), matching ESP32 implementation
- lwipopts_examples_common.h: increase MEM_SIZE 4000->8000 to reduce
  lwIP heap exhaustion causing intermittent HTTP failures
- web_portal.html: clear form fields before fetch to avoid showing stale
  values; add .catch() error handlers to showSettingsSection and putForm
- scale.c: call set_scale_driver() and set_scale_baudrate() in
  scale_config_save() so display menu driver changes take effect
  immediately on EEPROM save without requiring reboot

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- charge_mode.cpp: change fine stop condition < to <= so motor stops
  exactly at threshold (e.g. error=0.03 stops at 23.30, not 23.30001)
- charge_mode.cpp: update defaults: coarse_stop=4, coarse_time=7000ms,
  total_time=15000ms
- ai_tuning.c: start AI tuning from existing profile Kp/Kd values
  (clamped to min/max range) instead of always starting from midpoint;
  fall back to midpoint only for new/empty profiles
- web_portal.html: fix "Failed to save settings: Failed to fetch" alert
  when rebooting - show friendly reboot message instead of error

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@NemesisXB
Copy link
Copy Markdown

Just my 2c. Could you please not call it AI auto tuning as there is no AI involved. It is basically just heuristic auto-tuning. My vote is "PID Auto-tuning" or just "Auto-tuning".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants