Compare commits

...

6 Commits

Author SHA1 Message Date
pablohc
a25a1a5d0f
Merge 3b9a37e075 into 78d6e5931c 2026-02-04 09:18:12 +11:00
Jake Kenneally
78d6e5931c
fix: Correct debugging_monitor.py script instructions (#676)
Some checks are pending
CI / build (push) Waiting to run
## Summary

**What is the goal of this PR?**
- Minor correction to the `debugging_monitor.py` script instructions

**What changes are included?**
- `pyserial` should be installed, NOT `serial`, which is a [different
lib](https://pypi.org/project/serial/)
- Added macOS serial port

## Additional Context

- Just a minor docs update. I can confirm the debugging script is
working great on macOS

---

### AI Usage

While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.

Did you use AI tools to help write this code? _**< NO >**_
2026-02-04 00:33:20 +03:00
Luke Stein
dac11c3fdd
fix: Correct instruction text to match actual button text (#672)
## Summary

* Instruction text says "Press OK to scan again" but button label is
actually "Connect" (not OK)
* Corrects instruction text

---

### AI Usage

Did you use AI tools to help write this code? **No**
2026-02-04 00:32:52 +03:00
pablohc
3b9a37e075 fix: remove unused variables in MyLibraryActivity::loop()
Fixes cppcheck warnings:
- [low:style] Variable 'itemCount' is assigned a value that is never used
- [low:style] Variable 'pageItems' is assigned a value that is never used
2026-01-31 01:55:45 +01:00
pablohc
01651b0385 fix: format code with clang-format
- Apply clang-format-fix to ensure consistent code style
- Minor formatting adjustments in function comments and spacing
- Maintains all functionality while improving readability
2026-01-31 01:43:46 +01:00
pablohc
38b17ec95d refactor: implement state machine for button press detection in MyLibraryActivity
Button Behavior Matrix:
┌─────────────────┬──────────────┬──────────────────┐
│ Action          │ Condition    │ Timing           │
├─────────────────┼──────────────┼──────────────────┤
│ Switch Tab      │ Long press   │ ≥450ms hold      │
│ Skip Page       │ Double press │ <120ms between   │
│                 │              │ 1st PRESS &      │
│                 │              │ 2nd RELEASE      │
│ Move Item       │ Short press  │ <450ms press     │
└─────────────────┴──────────────┴──────────────────┘

Key Fixes:
- Each action has clear, exclusive condition
2026-01-31 00:54:47 +01:00
4 changed files with 152 additions and 43 deletions

View File

@ -102,13 +102,18 @@ After flashing the new features, its recommended to capture detailed logs fro
First, make sure all required Python packages are installed:
```python
python3 -m pip install serial colorama matplotlib
python3 -m pip install pyserial colorama matplotlib
```
after that run the script:
```sh
# For Linux
# This was tested on Debian and should work on most Linux systems.
python3 scripts/debugging_monitor.py
# For macOS
python3 scripts/debugging_monitor.py /dev/cu.usbmodem2101
```
This was tested on Debian and should work on most Linux systems. Minor adjustments may be required for Windows or macOS.
Minor adjustments may be required for Windows.
## Internals

View File

@ -20,9 +20,10 @@ constexpr int RECENTS_LINE_HEIGHT = 65; // Increased for two-line items
constexpr int LEFT_MARGIN = 20;
constexpr int RIGHT_MARGIN = 40; // Extra space for scroll indicator
// Timing thresholds
constexpr int SKIP_PAGE_MS = 700;
constexpr unsigned long GO_HOME_MS = 1000;
// Timing thresholds for button behavior
constexpr int LONG_PRESS_MS = 450; // Long press: change tab
constexpr int DOUBLE_PRESS_MS = 120; // Double press: skip page
constexpr unsigned long GO_HOME_MS = 1000; // Long press back: go to root
void sortFileList(std::vector<std::string>& strs) {
std::sort(begin(strs), end(strs), [](const std::string& str1, const std::string& str2) {
@ -126,6 +127,51 @@ void MyLibraryActivity::taskTrampoline(void* param) {
self->displayTaskLoop();
}
// Action execution: Move one item (short press timeout)
void MyLibraryActivity::executeMoveItem(bool isPrevButton) {
const int itemCount = getCurrentItemCount();
if (itemCount > 0) {
if (isPrevButton) {
selectorIndex = (selectorIndex + itemCount - 1) % itemCount;
} else {
selectorIndex = (selectorIndex + 1) % itemCount;
}
}
}
// Action execution: Skip page (double press)
void MyLibraryActivity::executeSkipPage(bool isPrevButton) {
const int itemCount = getCurrentItemCount();
const int pageItems = getPageItems();
if (itemCount > 0) {
if (isPrevButton) {
int targetPage = (selectorIndex / pageItems) - 1;
if (targetPage < 0) {
targetPage = ((itemCount - 1) / pageItems);
}
selectorIndex = targetPage * pageItems;
} else {
int targetPage = (selectorIndex / pageItems) + 1;
int maxPage = (itemCount - 1) / pageItems;
if (targetPage > maxPage) {
targetPage = 0;
}
selectorIndex = targetPage * pageItems;
}
}
}
// Action execution: Switch tab (long press)
void MyLibraryActivity::executeSwitchTab(bool isPrevButton) {
if (isPrevButton && currentTab == Tab::Files) {
currentTab = Tab::Recent;
selectorIndex = 0;
} else if (!isPrevButton && currentTab == Tab::Recent) {
currentTab = Tab::Files;
selectorIndex = 0;
}
}
void MyLibraryActivity::onEnter() {
Activity::onEnter();
@ -163,8 +209,8 @@ void MyLibraryActivity::onExit() {
}
void MyLibraryActivity::loop() {
const int itemCount = getCurrentItemCount();
const int pageItems = getPageItems();
// Get current time for all timing operations
unsigned long currentTime = millis();
// Long press BACK (1s+) in Files tab goes to root folder
if (currentTab == Tab::Files && mappedInput.isPressed(MappedInputManager::Button::Back) &&
@ -178,13 +224,6 @@ void MyLibraryActivity::loop() {
return;
}
const bool upReleased = mappedInput.wasReleased(MappedInputManager::Button::Up);
const bool downReleased = mappedInput.wasReleased(MappedInputManager::Button::Down);
const bool leftReleased = mappedInput.wasReleased(MappedInputManager::Button::Left);
const bool rightReleased = mappedInput.wasReleased(MappedInputManager::Button::Right);
const bool skipPage = mappedInput.getHeldTime() > SKIP_PAGE_MS;
// Confirm button - open selected item
if (mappedInput.wasReleased(MappedInputManager::Button::Confirm)) {
if (currentTab == Tab::Recent) {
@ -234,38 +273,84 @@ void MyLibraryActivity::loop() {
return;
}
// Tab switching: Left/Right always control tabs
if (leftReleased && currentTab == Tab::Files) {
currentTab = Tab::Recent;
selectorIndex = 0;
updateRequired = true;
return;
// Navigation buttons (UP/LEFT and DOWN/RIGHT have same behavior)
const bool upPressed = mappedInput.isPressed(MappedInputManager::Button::Up);
const bool leftPressed = mappedInput.isPressed(MappedInputManager::Button::Left);
const bool downPressed = mappedInput.isPressed(MappedInputManager::Button::Down);
const bool rightPressed = mappedInput.isPressed(MappedInputManager::Button::Right);
const bool upReleased = mappedInput.wasReleased(MappedInputManager::Button::Up);
const bool leftReleased = mappedInput.wasReleased(MappedInputManager::Button::Left);
const bool downReleased = mappedInput.wasReleased(MappedInputManager::Button::Down);
const bool rightReleased = mappedInput.wasReleased(MappedInputManager::Button::Right);
// Navigation: UP/LEFT move backward, DOWN/RIGHT move forward
const bool prevPressed = upPressed || leftPressed;
const bool nextPressed = downPressed || rightPressed;
const bool prevReleased = upReleased || leftReleased;
const bool nextReleased = downReleased || rightReleased;
// State machine for button press detection
// ==========================================
// IDLE: Wait for first press
if (buttonState == ButtonState::Idle) {
if (prevPressed || nextPressed) {
buttonState = ButtonState::FirstPress;
firstPressTime = currentTime;
isPrevButtonPressed = prevPressed;
}
if (rightReleased && currentTab == Tab::Recent) {
currentTab = Tab::Files;
selectorIndex = 0;
}
// FIRST_PRESS: Button is held, check for long press or release
else if (buttonState == ButtonState::FirstPress) {
const unsigned long holdDuration = currentTime - firstPressTime;
// Check for long press (>=450ms) - switch tab
if (holdDuration >= LONG_PRESS_MS) {
executeSwitchTab(isPrevButtonPressed);
buttonState = ButtonState::WaitingForReleaseAfterLongPress;
updateRequired = true;
return;
}
// Navigation: Up/Down moves through items only
const bool prevReleased = upReleased;
const bool nextReleased = downReleased;
// Check for release (<450ms) - transition to waiting for second press
if ((isPrevButtonPressed && prevReleased) || (!isPrevButtonPressed && nextReleased)) {
buttonState = ButtonState::WaitingForSecondPress;
firstReleaseTime = currentTime;
}
}
// WAITING_FOR_SECOND_PRESS: First button released, waiting for second press
else if (buttonState == ButtonState::WaitingForSecondPress) {
const unsigned long waitDuration = currentTime - firstReleaseTime;
if (prevReleased && itemCount > 0) {
if (skipPage) {
selectorIndex = ((selectorIndex / pageItems - 1) * pageItems + itemCount) % itemCount;
} else {
selectorIndex = (selectorIndex + itemCount - 1) % itemCount;
}
// Timeout (>=120ms without second press) - execute move_item
if (waitDuration >= DOUBLE_PRESS_MS) {
executeMoveItem(isPrevButtonPressed);
buttonState = ButtonState::Idle;
updateRequired = true;
} else if (nextReleased && itemCount > 0) {
if (skipPage) {
selectorIndex = ((selectorIndex / pageItems + 1) * pageItems) % itemCount;
} else {
selectorIndex = (selectorIndex + 1) % itemCount;
return;
}
// Second press detected (<120ms) - double press
if (prevPressed || nextPressed) {
buttonState = ButtonState::DoublePressDetected;
}
}
// DOUBLE_PRESS_DETECTED: Second press detected, wait for release
else if (buttonState == ButtonState::DoublePressDetected) {
// Wait for second button release
if (prevReleased || nextReleased) {
executeSkipPage(isPrevButtonPressed);
buttonState = ButtonState::Idle;
updateRequired = true;
return;
}
}
// WAITING_FOR_RELEASE_AFTER_LONG_PRESS: Ignore release after long press
else if (buttonState == ButtonState::WaitingForReleaseAfterLongPress) {
// Wait for button to be released, then go back to idle
if ((isPrevButtonPressed && prevReleased) || (!isPrevButtonPressed && nextReleased)) {
buttonState = ButtonState::Idle;
}
}
}

View File

@ -22,6 +22,25 @@ class MyLibraryActivity final : public Activity {
int selectorIndex = 0;
bool updateRequired = false;
// State machine for button press detection
enum class ButtonState {
Idle,
FirstPress,
WaitingForSecondPress,
DoublePressDetected,
WaitingForReleaseAfterLongPress
};
ButtonState buttonState = ButtonState::Idle;
unsigned long firstPressTime = 0; // Time of first PRESS
unsigned long firstReleaseTime = 0; // Time of first RELEASE
bool isPrevButtonPressed = false; // Which button was pressed first
// Action execution functions
void executeMoveItem(bool isPrevButton);
void executeSkipPage(bool isPrevButton);
void executeSwitchTab(bool isPrevButton);
// Recent tab state
std::vector<RecentBook> recentBooks;

View File

@ -520,7 +520,7 @@ void WifiSelectionActivity::renderNetworkList() const {
const auto height = renderer.getLineHeight(UI_10_FONT_ID);
const auto top = (pageHeight - height) / 2;
renderer.drawCenteredText(UI_10_FONT_ID, top, "No networks found");
renderer.drawCenteredText(SMALL_FONT_ID, top + height + 10, "Press OK to scan again");
renderer.drawCenteredText(SMALL_FONT_ID, top + height + 10, "Press Connect to scan again");
} else {
// Calculate how many networks we can display
constexpr int startY = 60;