Compare commits

...

7 Commits

Author SHA1 Message Date
Maik Allgöwer
3cb4ef759e
Merge 5f1b0d5c84 into 78d6e5931c 2026-02-03 22:47:29 +01:00
Jake Kenneally
78d6e5931c
fix: Correct debugging_monitor.py script instructions (#676)
Some checks failed
CI / build (push) Has been cancelled
## 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
Maik Allgöwer
5f1b0d5c84
Add Sunlight Fading Fix configuration option to user guide 2026-02-03 10:31:56 +01:00
Maik Allgöwer
6871ca7762 Bump open-x4-sdk to latest revision 2026-02-01 14:44:58 +01:00
Maik Allgöwer
e87319688b style: Fix formatting 2026-01-29 00:24:25 +01:00
Maik Allgöwer
988723d985 feat: Implement fix for sunlight fading issue 2026-01-28 23:51:36 +01:00
12 changed files with 40 additions and 14 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

@ -148,6 +148,9 @@ The Settings screen allows you to configure the device's behavior. There are a f
- **Reader Paragraph Alignment**: Set the alignment of paragraphs; options are "Justified" (default), "Left", "Center", or "Right".
- **Time to Sleep**: Set the duration of inactivity before the device automatically goes to sleep.
- **Refresh Frequency**: Set how often the screen does a full refresh while reading to reduce ghosting.
- **Sunlight Fading Fix**: Configure whether to enable a software-fix for the issue where white X4 models may fade when used in direct sunlight
- "OFF" (default) - Disable the fix
- "ON" - Enable the fix
- **OPDS Browser**: Configure OPDS server settings for browsing and downloading books. Set the server URL (for Calibre Content Server, add `/opds` to the end), and optionally configure username and password for servers requiring authentication. Note: Only HTTP Basic authentication is supported. If using Calibre Content Server with authentication enabled, you must set it to use Basic authentication instead of the default Digest authentication.
- **Check for updates**: Check for firmware updates over WiFi.

View File

@ -411,7 +411,9 @@ void GfxRenderer::invertScreen() const {
}
}
void GfxRenderer::displayBuffer(const HalDisplay::RefreshMode refreshMode) const { display.displayBuffer(refreshMode); }
void GfxRenderer::displayBuffer(const HalDisplay::RefreshMode refreshMode) const {
display.displayBuffer(refreshMode, fadingFix);
}
std::string GfxRenderer::truncatedText(const int fontId, const char* text, const int maxWidth,
const EpdFontFamily::Style style) const {
@ -669,7 +671,7 @@ void GfxRenderer::copyGrayscaleLsbBuffers() const { display.copyGrayscaleLsbBuff
void GfxRenderer::copyGrayscaleMsbBuffers() const { display.copyGrayscaleMsbBuffers(display.getFrameBuffer()); }
void GfxRenderer::displayGrayBuffer() const { display.displayGrayBuffer(); }
void GfxRenderer::displayGrayBuffer() const { display.displayGrayBuffer(fadingFix); }
void GfxRenderer::freeBwBufferChunks() {
for (auto& bwBufferChunk : bwBufferChunks) {

View File

@ -28,6 +28,7 @@ class GfxRenderer {
HalDisplay& display;
RenderMode renderMode;
Orientation orientation;
bool fadingFix;
uint8_t* bwBufferChunks[BW_BUFFER_NUM_CHUNKS] = {nullptr};
std::map<int, EpdFontFamily> fontMap;
void renderChar(const EpdFontFamily& fontFamily, uint32_t cp, int* x, const int* y, bool pixelState,
@ -36,7 +37,8 @@ class GfxRenderer {
void rotateCoordinates(int x, int y, int* rotatedX, int* rotatedY) const;
public:
explicit GfxRenderer(HalDisplay& halDisplay) : display(halDisplay), renderMode(BW), orientation(Portrait) {}
explicit GfxRenderer(HalDisplay& halDisplay)
: display(halDisplay), renderMode(BW), orientation(Portrait), fadingFix(false) {}
~GfxRenderer() { freeBwBufferChunks(); }
static constexpr int VIEWABLE_MARGIN_TOP = 9;
@ -51,6 +53,9 @@ class GfxRenderer {
void setOrientation(const Orientation o) { orientation = o; }
Orientation getOrientation() const { return orientation; }
// Fading fix control
void setFadingFix(const bool enabled) { fadingFix = enabled; }
// Screen ops
int getScreenWidth() const;
int getScreenHeight() const;

View File

@ -28,7 +28,9 @@ EInkDisplay::RefreshMode convertRefreshMode(HalDisplay::RefreshMode mode) {
}
}
void HalDisplay::displayBuffer(HalDisplay::RefreshMode mode) { einkDisplay.displayBuffer(convertRefreshMode(mode)); }
void HalDisplay::displayBuffer(HalDisplay::RefreshMode mode, bool turnOffScreen) {
einkDisplay.displayBuffer(convertRefreshMode(mode), turnOffScreen);
}
void HalDisplay::refreshDisplay(HalDisplay::RefreshMode mode, bool turnOffScreen) {
einkDisplay.refreshDisplay(convertRefreshMode(mode), turnOffScreen);
@ -48,4 +50,4 @@ void HalDisplay::copyGrayscaleMsbBuffers(const uint8_t* msbBuffer) { einkDisplay
void HalDisplay::cleanupGrayscaleBuffers(const uint8_t* bwBuffer) { einkDisplay.cleanupGrayscaleBuffers(bwBuffer); }
void HalDisplay::displayGrayBuffer() { einkDisplay.displayGrayBuffer(); }
void HalDisplay::displayGrayBuffer(bool turnOffScreen) { einkDisplay.displayGrayBuffer(turnOffScreen); }

View File

@ -31,7 +31,7 @@ class HalDisplay {
void drawImage(const uint8_t* imageData, uint16_t x, uint16_t y, uint16_t w, uint16_t h,
bool fromProgmem = false) const;
void displayBuffer(RefreshMode mode = RefreshMode::FAST_REFRESH);
void displayBuffer(RefreshMode mode = RefreshMode::FAST_REFRESH, bool turnOffScreen = false);
void refreshDisplay(RefreshMode mode = RefreshMode::FAST_REFRESH, bool turnOffScreen = false);
// Power management
@ -45,7 +45,7 @@ class HalDisplay {
void copyGrayscaleMsbBuffers(const uint8_t* msbBuffer);
void cleanupGrayscaleBuffers(const uint8_t* bwBuffer);
void displayGrayBuffer();
void displayGrayBuffer(bool turnOffScreen = false);
private:
EInkDisplay einkDisplay;

@ -1 +1 @@
Subproject commit bd4e6707503ab9c97d13ee0d8f8c69e9ff03cd12
Subproject commit c8ce3949b3368329c290ca1858e65dc3416fc591

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
@ -61,6 +61,7 @@ bool CrossPointSettings::saveToFile() const {
serialization::writeString(outputFile, std::string(opdsPassword));
serialization::writePod(outputFile, sleepScreenCoverFilter);
// New fields added at end for backward compatibility
serialization::writePod(outputFile, fadingFix);
outputFile.close();
Serial.printf("[%lu] [CPS] Settings saved to file\n", millis());
@ -148,6 +149,8 @@ bool CrossPointSettings::loadFromFile() {
if (++settingsRead >= fileSettingsCount) break;
readAndValidate(inputFile, sleepScreenCoverFilter, SLEEP_SCREEN_COVER_FILTER_COUNT);
if (++settingsRead >= fileSettingsCount) break;
serialization::readPod(inputFile, fadingFix);
if (++settingsRead >= fileSettingsCount) break;
// New fields added at end for backward compatibility
} while (false);

View File

@ -137,6 +137,8 @@ class CrossPointSettings {
uint8_t hideBatteryPercentage = HIDE_NEVER;
// Long-press chapter skip on side buttons
uint8_t longPressChapterSkip = 1;
// Sunlight fading compensation
uint8_t fadingFix = 0;
~CrossPointSettings() = default;

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;

View File

@ -11,7 +11,7 @@
const char* SettingsActivity::categoryNames[categoryCount] = {"Display", "Reader", "Controls", "System"};
namespace {
constexpr int displaySettingsCount = 6;
constexpr int displaySettingsCount = 7;
const SettingInfo displaySettings[displaySettingsCount] = {
// Should match with SLEEP_SCREEN_MODE
SettingInfo::Enum("Sleep Screen", &CrossPointSettings::sleepScreen, {"Dark", "Light", "Custom", "Cover", "None"}),
@ -22,7 +22,9 @@ const SettingInfo displaySettings[displaySettingsCount] = {
{"None", "No Progress", "Full w/ Percentage", "Full w/ Progress Bar", "Progress Bar"}),
SettingInfo::Enum("Hide Battery %", &CrossPointSettings::hideBatteryPercentage, {"Never", "In Reader", "Always"}),
SettingInfo::Enum("Refresh Frequency", &CrossPointSettings::refreshFrequency,
{"1 page", "5 pages", "10 pages", "15 pages", "30 pages"})};
{"1 page", "5 pages", "10 pages", "15 pages", "30 pages"}),
SettingInfo::Enum("Sunlight Fading Fix", &CrossPointSettings::fadingFix, {"OFF", "ON"}),
};
constexpr int readerSettingsCount = 9;
const SettingInfo readerSettings[readerSettingsCount] = {

View File

@ -344,6 +344,8 @@ void loop() {
gpio.update();
renderer.setFadingFix(SETTINGS.fadingFix);
if (Serial && millis() - lastMemPrint >= 10000) {
Serial.printf("[%lu] [MEM] Free: %d bytes, Total: %d bytes, Min Free: %d bytes\n", millis(), ESP.getFreeHeap(),
ESP.getHeapSize(), ESP.getMinFreeHeap());