Compare commits

...

12 Commits

Author SHA1 Message Date
Eliz
e5f96ec3f8
Merge 201d7f9a85 into 78d6e5931c 2026-02-04 09:18:11 +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
Eliz
201d7f9a85
Merge branch 'crosspoint-reader:master' into master 2026-02-02 17:26:50 +00:00
Eliz Kilic
ead5c5ced4 format 2026-02-01 21:04:25 +00:00
Eliz Kilic
cb7f3313f0 format files 2026-02-01 20:59:25 +00:00
Eliz Kilic
d4c8e13698 remove skip and back 2026-02-01 20:45:29 +00:00
Eliz Kilic
1c1adaf078 fix crash 2026-02-01 20:33:33 +00:00
Eliz
6c2d7c7044 fix import 2026-02-01 20:28:52 +00:00
Eliz Kilic
05b0aaa587 fix import 2026-02-01 20:17:34 +00:00
Eliz Kilic
05b10854dc fix import 2026-02-01 20:14:46 +00:00
Eliz Kilic
0531cee32c auto connect last wifi 2026-02-01 20:09:33 +00:00
10 changed files with 197 additions and 33 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

@ -22,7 +22,7 @@ void readAndValidate(FsFile& file, uint8_t& member, const uint8_t maxValue) {
namespace {
constexpr uint8_t SETTINGS_FILE_VERSION = 1;
// Increment this when adding new persisted settings fields
constexpr uint8_t SETTINGS_COUNT = 23;
constexpr uint8_t SETTINGS_COUNT = 24;
constexpr char SETTINGS_FILE[] = "/.crosspoint/settings.bin";
} // namespace
@ -60,6 +60,7 @@ bool CrossPointSettings::saveToFile() const {
serialization::writeString(outputFile, std::string(opdsUsername));
serialization::writeString(outputFile, std::string(opdsPassword));
serialization::writePod(outputFile, sleepScreenCoverFilter);
serialization::writeString(outputFile, std::string(lastConnectedSSID));
// New fields added at end for backward compatibility
outputFile.close();

View File

@ -137,6 +137,7 @@ class CrossPointSettings {
uint8_t hideBatteryPercentage = HIDE_NEVER;
// Long-press chapter skip on side buttons
uint8_t longPressChapterSkip = 1;
char lastConnectedSSID[33] = "";
~CrossPointSettings() = default;

View File

@ -4,6 +4,8 @@
#include <SDCardManager.h>
#include <Serialization.h>
#include <algorithm>
// Initialize the static instance
WifiCredentialStore WifiCredentialStore::instance;
@ -102,7 +104,7 @@ bool WifiCredentialStore::loadFromFile() {
bool WifiCredentialStore::addCredential(const std::string& ssid, const std::string& password) {
// Check if this SSID already exists and update it
const auto cred = find_if(credentials.begin(), credentials.end(),
auto cred = std::find_if(credentials.begin(), credentials.end(),
[&ssid](const WifiCredential& cred) { return cred.ssid == ssid; });
if (cred != credentials.end()) {
cred->password = password;
@ -123,7 +125,7 @@ bool WifiCredentialStore::addCredential(const std::string& ssid, const std::stri
}
bool WifiCredentialStore::removeCredential(const std::string& ssid) {
const auto cred = find_if(credentials.begin(), credentials.end(),
auto cred = std::find_if(credentials.begin(), credentials.end(),
[&ssid](const WifiCredential& cred) { return cred.ssid == ssid; });
if (cred != credentials.end()) {
credentials.erase(cred);
@ -134,7 +136,7 @@ bool WifiCredentialStore::removeCredential(const std::string& ssid) {
}
const WifiCredential* WifiCredentialStore::findCredential(const std::string& ssid) const {
const auto cred = find_if(credentials.begin(), credentials.end(),
auto cred = std::find_if(credentials.begin(), credentials.end(),
[&ssid](const WifiCredential& cred) { return cred.ssid == ssid; });
if (cred != credentials.end()) {

View File

@ -0,0 +1,75 @@
#include "AutoConnectingActivity.h"
#include <GfxRenderer.h>
#include <WiFi.h>
#include <string>
#include "CrossPointSettings.h"
#include "MappedInputManager.h"
#include "WifiCredentialStore.h"
#include "fontIds.h"
AutoConnectingActivity::AutoConnectingActivity(GfxRenderer& renderer, MappedInputManager& mappedInput,
const std::function<void()>& on_success,
const std::function<void()>& on_failure)
: Activity("AutoConnecting", renderer, mappedInput), on_success(on_success), on_failure(on_failure) {}
void AutoConnectingActivity::onEnter() {
Activity::onEnter();
render();
attemptConnection();
}
void AutoConnectingActivity::onExit() { Activity::onExit(); }
void AutoConnectingActivity::loop() { checkConnectionStatus(); }
void AutoConnectingActivity::attemptConnection() {
connectionStartTime = millis();
std::string ssid = SETTINGS.lastConnectedSSID;
const auto* cred = WIFI_STORE.findCredential(ssid);
if (!cred) {
on_failure();
return;
}
WiFi.mode(WIFI_STA);
WiFi.begin(ssid.c_str(), cred->password.c_str());
}
void AutoConnectingActivity::checkConnectionStatus() {
const wl_status_t status = WiFi.status();
if (status == WL_CONNECTED) {
on_success();
return;
}
if (status == WL_CONNECT_FAILED || status == WL_NO_SSID_AVAIL) {
on_failure();
return;
}
if (millis() - connectionStartTime > CONNECTION_TIMEOUT_MS) {
WiFi.disconnect();
on_failure();
return;
}
}
void AutoConnectingActivity::render() const {
renderer.clearScreen();
const auto pageHeight = renderer.getScreenHeight();
const auto height = renderer.getLineHeight(UI_10_FONT_ID);
const auto top = (pageHeight - height) / 2;
renderer.drawCenteredText(UI_12_FONT_ID, top - 40, "Connecting...", true, EpdFontFamily::BOLD);
std::string ssidInfo = "to " + std::string(SETTINGS.lastConnectedSSID);
if (ssidInfo.length() > 25) {
ssidInfo.replace(22, ssidInfo.length() - 22, "...");
}
renderer.drawCenteredText(UI_10_FONT_ID, top, ssidInfo.c_str());
renderer.displayBuffer();
}

View File

@ -0,0 +1,26 @@
#pragma once
#include <functional>
#include "activities/Activity.h"
class AutoConnectingActivity final : public Activity {
public:
AutoConnectingActivity(GfxRenderer& renderer, MappedInputManager& mappedInput,
const std::function<void()>& on_success, const std::function<void()>& on_failure);
void onEnter() override;
void onExit() override;
void loop() override;
private:
void render() const;
void attemptConnection();
void checkConnectionStatus();
const std::function<void()> on_success;
const std::function<void()> on_failure;
unsigned long connectionStartTime = 0;
static constexpr unsigned long CONNECTION_TIMEOUT_MS = 15000;
};

View File

@ -3,8 +3,10 @@
#include <GfxRenderer.h>
#include <WiFi.h>
#include <algorithm>
#include <map>
#include "CrossPointSettings.h"
#include "MappedInputManager.h"
#include "WifiCredentialStore.h"
#include "activities/util/KeyboardEntryActivity.h"
@ -251,6 +253,11 @@ void WifiSelectionActivity::checkConnectionStatus() {
snprintf(ipStr, sizeof(ipStr), "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
connectedIP = ipStr;
// Save the last connected SSID
strncpy(SETTINGS.lastConnectedSSID, selectedSSID.c_str(), sizeof(SETTINGS.lastConnectedSSID) - 1);
SETTINGS.lastConnectedSSID[sizeof(SETTINGS.lastConnectedSSID) - 1] = '\0';
SETTINGS.saveToFile();
// If we entered a new password, ask if user wants to save it
// Otherwise, immediately complete so parent can start web server
if (!usedSavedPassword && !enteredPassword.empty()) {
@ -311,14 +318,12 @@ void WifiSelectionActivity::loop() {
// Handle save prompt state
if (state == WifiSelectionState::SAVE_PROMPT) {
if (mappedInput.wasPressed(MappedInputManager::Button::Up) ||
mappedInput.wasPressed(MappedInputManager::Button::Left)) {
if (mappedInput.wasPressed(MappedInputManager::Button::Left)) {
if (savePromptSelection > 0) {
savePromptSelection--;
updateRequired = true;
}
} else if (mappedInput.wasPressed(MappedInputManager::Button::Down) ||
mappedInput.wasPressed(MappedInputManager::Button::Right)) {
} else if (mappedInput.wasPressed(MappedInputManager::Button::Right)) {
if (savePromptSelection < 1) {
savePromptSelection++;
updateRequired = true;
@ -341,14 +346,12 @@ void WifiSelectionActivity::loop() {
// Handle forget prompt state (connection failed with saved credentials)
if (state == WifiSelectionState::FORGET_PROMPT) {
if (mappedInput.wasPressed(MappedInputManager::Button::Up) ||
mappedInput.wasPressed(MappedInputManager::Button::Left)) {
if (mappedInput.wasPressed(MappedInputManager::Button::Left)) {
if (forgetPromptSelection > 0) {
forgetPromptSelection--;
updateRequired = true;
}
} else if (mappedInput.wasPressed(MappedInputManager::Button::Down) ||
mappedInput.wasPressed(MappedInputManager::Button::Right)) {
} else if (mappedInput.wasPressed(MappedInputManager::Button::Right)) {
if (forgetPromptSelection < 1) {
forgetPromptSelection++;
updateRequired = true;
@ -360,7 +363,7 @@ void WifiSelectionActivity::loop() {
WIFI_STORE.removeCredential(selectedSSID);
xSemaphoreGive(renderingMutex);
// Update the network list to reflect the change
const auto network = find_if(networks.begin(), networks.end(),
const auto network = std::find_if(networks.begin(), networks.end(),
[this](const WifiNetworkInfo& net) { return net.ssid == selectedSSID; });
if (network != networks.end()) {
network->hasSavedPassword = false;
@ -420,18 +423,23 @@ void WifiSelectionActivity::loop() {
}
// Handle UP/DOWN navigation
if (mappedInput.wasPressed(MappedInputManager::Button::Up) ||
mappedInput.wasPressed(MappedInputManager::Button::Left)) {
if (mappedInput.wasPressed(MappedInputManager::Button::Up)) {
if (selectedNetworkIndex > 0) {
selectedNetworkIndex--;
updateRequired = true;
}
} else if (mappedInput.wasPressed(MappedInputManager::Button::Down) ||
mappedInput.wasPressed(MappedInputManager::Button::Right)) {
} else if (mappedInput.wasPressed(MappedInputManager::Button::Down)) {
if (!networks.empty() && selectedNetworkIndex < static_cast<int>(networks.size()) - 1) {
selectedNetworkIndex++;
updateRequired = true;
}
} else if (mappedInput.wasPressed(MappedInputManager::Button::Left)) {
if (!networks.empty() && networks[selectedNetworkIndex].hasSavedPassword) {
selectedSSID = networks[selectedNetworkIndex].ssid;
state = WifiSelectionState::FORGET_PROMPT;
forgetPromptSelection = 0; // Default to "Cancel"
updateRequired = true;
}
}
}
}
@ -520,7 +528,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;
@ -585,7 +593,7 @@ void WifiSelectionActivity::renderNetworkList() const {
// Draw help text
renderer.drawText(SMALL_FONT_ID, 20, pageHeight - 75, "* = Encrypted | + = Saved");
const auto labels = mappedInput.mapLabels("« Back", "Connect", "", "");
const auto labels = mappedInput.mapLabels("« Back", "Connect", "Forget", "");
renderer.drawButtonHints(UI_10_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
}
@ -689,7 +697,7 @@ void WifiSelectionActivity::renderForgetPrompt() const {
const auto height = renderer.getLineHeight(UI_10_FONT_ID);
const auto top = (pageHeight - height * 3) / 2;
renderer.drawCenteredText(UI_12_FONT_ID, top - 40, "Connection Failed", true, EpdFontFamily::BOLD);
renderer.drawCenteredText(UI_12_FONT_ID, top - 40, "Forget Network", true, EpdFontFamily::BOLD);
std::string ssidInfo = "Network: " + selectedSSID;
if (ssidInfo.length() > 28) {

View File

@ -11,6 +11,7 @@
#include "KOReaderSettingsActivity.h"
#include "MappedInputManager.h"
#include "OtaUpdateActivity.h"
#include "activities/network/WifiSelectionActivity.h"
#include "fontIds.h"
void CategorySettingsActivity::taskTrampoline(void* param) {
@ -95,7 +96,15 @@ void CategorySettingsActivity::toggleCurrentSetting() {
SETTINGS.*(setting.valuePtr) = currentValue + setting.valueRange.step;
}
} else if (setting.type == SettingType::ACTION) {
if (strcmp(setting.name, "KOReader Sync") == 0) {
if (strcmp(setting.name, "Network") == 0) {
xSemaphoreTake(renderingMutex, portMAX_DELAY);
exitActivity();
enterNewActivity(new WifiSelectionActivity(renderer, mappedInput, [this](bool) {
exitActivity();
updateRequired = true;
}));
xSemaphoreGive(renderingMutex);
} else if (strcmp(setting.name, "KOReader Sync") == 0) {
xSemaphoreTake(renderingMutex, portMAX_DELAY);
exitActivity();
enterNewActivity(new KOReaderSettingsActivity(renderer, mappedInput, [this] {

View File

@ -48,11 +48,14 @@ const SettingInfo controlsSettings[controlsSettingsCount] = {
SettingInfo::Toggle("Long-press Chapter Skip", &CrossPointSettings::longPressChapterSkip),
SettingInfo::Enum("Short Power Button Click", &CrossPointSettings::shortPwrBtn, {"Ignore", "Sleep", "Page Turn"})};
constexpr int systemSettingsCount = 5;
constexpr int systemSettingsCount = 6;
const SettingInfo systemSettings[systemSettingsCount] = {
SettingInfo::Enum("Time to Sleep", &CrossPointSettings::sleepTimeout,
{"1 min", "5 min", "10 min", "15 min", "30 min"}),
SettingInfo::Action("KOReader Sync"), SettingInfo::Action("OPDS Browser"), SettingInfo::Action("Clear Cache"),
SettingInfo::Action("Network"),
SettingInfo::Action("KOReader Sync"),
SettingInfo::Action("OPDS Browser"),
SettingInfo::Action("Clear Cache"),
SettingInfo::Action("Check for updates")};
} // namespace

View File

@ -5,6 +5,7 @@
#include <HalGPIO.h>
#include <SDCardManager.h>
#include <SPI.h>
#include <WiFi.h>
#include <builtinFonts/all.h>
#include <cstring>
@ -20,7 +21,9 @@
#include "activities/browser/OpdsBookBrowserActivity.h"
#include "activities/home/HomeActivity.h"
#include "activities/home/MyLibraryActivity.h"
#include "activities/network/AutoConnectingActivity.h"
#include "activities/network/CrossPointWebServerActivity.h"
#include "activities/network/WifiSelectionActivity.h"
#include "activities/reader/ReaderActivity.h"
#include "activities/settings/SettingsActivity.h"
#include "activities/util/FullScreenMessageActivity.h"
@ -211,9 +214,38 @@ void onGoToReader(const std::string& initialEpubPath, MyLibraryActivity::Tab fro
}
void onContinueReading() { onGoToReader(APP_STATE.openEpubPath, MyLibraryActivity::Tab::Recent); }
void onGoHome(); // forward declaration
void withWifi(std::function<void()> on_success) {
if (WiFi.isConnected()) {
on_success();
return;
}
auto on_failure = [on_success]() {
exitActivity();
enterNewActivity(new WifiSelectionActivity(renderer, mappedInputManager, [on_success](bool connected) {
if (connected) {
on_success();
} else {
onGoHome();
}
}));
};
if (strlen(SETTINGS.lastConnectedSSID) > 0) {
exitActivity();
enterNewActivity(new AutoConnectingActivity(renderer, mappedInputManager, on_success, on_failure));
} else {
on_failure();
}
}
void onGoToFileTransfer() {
withWifi([]() {
exitActivity();
enterNewActivity(new CrossPointWebServerActivity(renderer, mappedInputManager, onGoHome));
});
}
void onGoToSettings() {
@ -232,8 +264,10 @@ void onGoToMyLibraryWithTab(const std::string& path, MyLibraryActivity::Tab tab)
}
void onGoToBrowser() {
withWifi([]() {
exitActivity();
enterNewActivity(new OpdsBookBrowserActivity(renderer, mappedInputManager, onGoHome));
});
}
void onGoHome() {