Skip to content

Add configurable manual feed amount, global feed settings, and feed history fixes - #27

Merged
Friedjof merged 4 commits into
masterfrom
feature/manual-feed-amount
Jun 10, 2026
Merged

Add configurable manual feed amount, global feed settings, and feed history fixes#27
Friedjof merged 4 commits into
masterfrom
feature/manual-feed-amount

Conversation

@bschlagheck

@bschlagheck bschlagheck commented Jun 6, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • add a persistent manual feed amount setting
  • use the saved manual feed amount for both Feed Now and hardware button manual feeding
  • move global feed settings out of the schedules section
  • increase portion sliders from 1..5 to 1..10
  • fix feed history routing and ring buffer persistence
  • update config import/export, mock behavior, and README screenshots

Details

This PR introduces a new manual_portion_units config value that is stored on the device and shared by both manual feed entry points:

  • Feed Now in the web UI
  • hardware button manual feed

The UI now has a separate Feed settings block above the schedules section. Global feed settings are no longer shown inside the schedules area.

Portion size (g per unit) remains a global setting and now sits alongside the new manual feed amount control.

Portion unit validation has been expanded consistently from 1..5 to 1..10 across firmware, frontend, and mock API.

Feed history fixes

This PR also fixes feed history retrieval and persistence:

  • /api/status/history is now routed correctly
  • feed history no longer resolves to the generic /api/status handler
  • the feed history ring buffer write index is now persisted in NVS
  • feed history order can now be reconstructed correctly after reboot
  • history entries are returned in reverse chronological order

Additionally, FeedingService::feed() is now aligned with the current 1..10 portion range.

Validation

  • verified the updated flow in the local browser mock
  • rebuilt embedded web assets
  • ran make build successfully

Included updates

  • firmware config and API changes for manual_portion_units
  • hardware button manual feed now uses the saved manual amount
  • frontend layout update for global feed settings
  • config import/export support for the new field
  • feed history routing and persistence fix
  • refreshed README mobile screenshots

@bschlagheck
bschlagheck requested a review from Friedjof June 6, 2026 17:35
@bschlagheck bschlagheck changed the title Add configurable manual feed amount and global feed settings Add configurable manual feed amount, global feed settings, and feed history fixes Jun 6, 2026
@bschlagheck

Copy link
Copy Markdown
Collaborator Author

Sorry new feature and bugfix in one PR.

@Friedjof

Friedjof commented Jun 8, 2026

Copy link
Copy Markdown
Owner

I think the new feed history ordering still has an off-by-historyCount bug for partially filled ring buffers.

feedHistoryIndex / writeIndex points to the next write position. With a fresh history of 3 entries, the valid slots are 0,1,2 and writeIndex == 3. The current formula:

uint8_t ringIndex = (writeIndex + historyCount - 1 - i) % MAX_FEED_HISTORY;

would read 5,4,3 for MAX_FEED_HISTORY == 10, so /api/status/history skips empty entries instead of returning the latest feeds. The same issue happens after reboot because feedHistIdx persists the next write position.

The base should be the slot before writeIndex, e.g.:

uint8_t ringIndex = (writeIndex + MAX_FEED_HISTORY - 1 - i) % MAX_FEED_HISTORY;

That yields 2,1,0 for the partially filled case and still works once the buffer wraps.

@bschlagheck

Copy link
Copy Markdown
Collaborator Author

Good catch, you were right.

writeIndex points to the next write slot, so the newest entry must be derived from writeIndex - 1, not from historyCount - 1.

I updated the calculation to:

(writeIndex + MAX_FEED_HISTORY - 1 - i) % MAX_FEED_HISTORY

This fixes partially filled buffers and still works after wrap-around and after reboot with the persisted write index.

  • Commit: c76e196
  • Message: fix: correct partial feed history ordering

@Friedjof

Copy link
Copy Markdown
Owner

Thanks, this looks close. I found two follow-ups before merge:

  1. lastFeedTime is still lost after reboot. src/main.cpp restores the persisted feed history, but FeedingService::loadFeedHistory(...) only restores the ring buffer state and never reconstructs lastFeedUnix. /api/status still reads lastFeedTime only from that field, so after a reboot the dashboard falls back to Never until the next feeding even though the history popup already contains entries.

  2. The mock validation for manual_portion_units does not match the firmware yet. In web/public/mock/api.js, the validation only runs when the value is truthy, so 0 can still slip through in mock mode even though the device rejects it. That means the mock can silently test a config state that the real device will never accept.

make build passes on the PR head, so these look like follow-up correctness issues rather than build problems.

@bschlagheck

Copy link
Copy Markdown
Collaborator Author

Sorry for that!
Both points were valid.

I fixed lastFeedTime restoration after reboot by reconstructing lastFeedUnix from the newest valid feed history entry during FeedingService::loadFeedHistory(...).

I also tightened the mock validation so manual_portion_units is validated by field presence and value range instead of truthiness, which means 0 no longer slips through in mock mode. I additionally
normalize persisted mock config to avoid stale invalid localStorage states.

make build still passes on the updated branch.

@Friedjof
Friedjof merged commit 4c2b009 into master Jun 10, 2026
1 check passed
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.

2 participants