Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
name: build

on:
push:
branches: [main]
pull_request:

permissions:
contents: read

jobs:
firmware:
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- name: Check out repository
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Set up Python
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: "3.13"
- name: Install PlatformIO
run: python -m pip install platformio==6.1.18
- name: Build release firmware
run: platformio run -e xteink_x4_release
16 changes: 7 additions & 9 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,21 @@
*.sublime-project
*.sublime-workspace

# Build output
# Device backups and compiled output
backup.bin
*.bin
*.elf
*.hex
*.map

# OS files
# Local configuration (may contain Wi-Fi credentials)
src/config_local.h

# Editor, OS, and log files
.DS_Store
Thumbs.db

# Editor backup files
*~
*.swp
*.swo
*.bak

# Local configuration (may contain WiFi credentials)
src/config_local.h

# Log files
*.log
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2026 Noah Ingwers

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
93 changes: 62 additions & 31 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
# X4 Games
# X4 Games and Utilities

A collection of classic games for the **Xteink X4** e-paper reader, designed to work safely alongside Papyrix firmware.
Community firmware for classic games and small utilities on the **Xteink X4**
e-paper reader. The project uses the Papyrix partition layout as a compatibility
reference and provides an application image that can be written to the `app0`
offset after the device layout has been verified.

> **Status:** hardware-specific prototype. Back up the full device before
> flashing and review `partitions.csv` against your exact hardware revision.
> Compatibility and recovery are not guaranteed.

## Included Apps

Expand All @@ -16,7 +23,7 @@ A collection of classic games for the **Xteink X4** e-paper reader, designed to
### Utilities
| App | Description |
|-----|-------------|
| **Portfolio Tracker** | Stock/IRA portfolio monitor with real-time quotes via Yahoo Finance API |
| **Portfolio Tracker** | Local holdings display using an unofficial Yahoo Finance quote endpoint |

## Hardware

Expand All @@ -27,13 +34,17 @@ This firmware is designed for the **Xteink X4**:

## Safety

⚠️ **This firmware is designed to be SAFE:**
The supplied configuration is intended to reduce flashing risk:

- It uses the documented Papyrix partition layout.
- The built application image is intended for the `app0` offset at `0x10000`.
- NVS is outside that application partition and is not rewritten by the manual
application-only command below.
- Two application partitions are defined for OTA-capable builds.

- ✅ Uses the **exact same partition layout** as Papyrix
- ✅ Does **NOT** touch the bootloader (0x0-0x8FFF)
- ✅ Preserves NVS data (WiFi credentials, calibration)
- ✅ Supports OTA with dual app partitions (failsafe)
- ✅ Can coexist with Papyrix (flash to app0 partition)
These properties describe the checked-in configuration; they are not a safety
certification. A wrong device revision, flash size, offset, or interrupted write
can still make the device unbootable.

### Partition Layout

Expand All @@ -58,8 +69,8 @@ coredump data coredump 0xff0000 0x10000 (64KB) - Crash dumps

```bash
# Clone the repository
git clone https://github.com/your-repo/x4-games.git
cd x4-games
git clone https://github.com/noah-ing/X4.git
cd X4

# Build the firmware
pio run
Expand All @@ -73,32 +84,29 @@ pio run -e xteink_x4_debug

## Flashing

### Option 1: PlatformIO (Recommended)

```bash
# Connect X4 via USB-C and flash
pio run -t upload

# Monitor serial output (optional, for debugging)
pio device monitor
```

### Option 2: esptool (Manual)
### Option 1: Application-only esptool command

```bash
# Flash only the app partition (safe, preserves bootloader/NVS)
# Build first, then write only the application image to app0
esptool.py --chip esp32c3 --port /dev/ttyACM0 --baud 460800 \
write_flash -z 0x10000 .pio/build/xteink_x4/firmware.bin
```

### Option 3: Papyrix Flasher
Replace `/dev/ttyACM0` with the device port. This command is deliberately
explicit: PlatformIO's standard ESP32 upload target can also write supporting
images, so it is not the documented application-only path here.

### Option 2: Papyrix Flasher

If you have [papyrix-flasher](https://github.com/bigbag/papyrix-flasher) installed:

```bash
papyrix-flasher flash .pio/build/xteink_x4/firmware.bin
```

Confirm the flasher's offsets and backup behavior for the installed version
before using it.

## ⚠️ Backup First!

Before flashing any custom firmware, **backup your factory firmware**:
Expand Down Expand Up @@ -164,28 +172,48 @@ esptool.py --chip esp32c3 --port /dev/ttyACM0 write_flash 0 backup.bin

## Configuring the Portfolio Tracker

To track your IRA or stock portfolio, edit `src/main.cpp` and uncomment/add your holdings:
Create the ignored local configuration file, then add only the values you want
compiled into your device firmware:

```bash
cp src/config_local.example.h src/config_local.h
```

```cpp
// In setup(), before gameMenu.addGame(&stockTracker):
stockTracker.addHolding("VTI", 50.0, 10000.00); // Symbol, shares, cost basis
stockTracker.addHolding("VXUS", 30.0, 5000.00);
inline void configureLocalPortfolio(StockTracker& tracker) {
tracker.setWiFi("YOUR_WIFI_SSID", "YOUR_WIFI_PASSWORD");
tracker.addHolding("VTI", 1.0F, 100.0F); // symbol, shares, cost basis
}
```

The tracker uses Yahoo Finance's unofficial API for real-time quotes. WiFi connection is required.
The tracker uses an unofficial Yahoo Finance endpoint for delayed or near-real-time
quotes. That endpoint may change or become unavailable without notice. Wi-Fi is
required; no API key is needed. Holdings and cost basis are compiled into the
firmware. `src/config_local.h` is ignored by Git; keep credentials and personal
portfolio values there and never force-add it to a commit.

The tracker is an informational display, not investment advice, a broker, or a
source of guaranteed market data.

HTTPS requests validate the endpoint against the embedded DigiCert Global Root
G2 certificate after the device clock is synchronized over NTP. If Yahoo changes
its certificate chain, update the trusted root deliberately; do not replace this
with an insecure TLS mode. The quote endpoint remains unofficial and should not
be treated as an availability or data-quality guarantee.

## Development

### Project Structure

```
x4-games/
X4/
├── platformio.ini # Build configuration
├── partitions.csv # Flash partition table
├── include/
│ ├── x4_hardware.h # Pin definitions
│ ├── display.h # Display wrapper
│ ├── input.h # Button handling
│ ├── yahoo_root_ca.h # Trusted root for the quote client
│ ├── game.h # Game base class
│ ├── menu.h # Game launcher
│ ├── games/
Expand All @@ -198,6 +226,7 @@ x4-games/
│ └── stocktracker.h
└── src/
├── main.cpp # Entry point
├── config_local.example.h # Ignored local-config template
├── display.cpp
├── input.cpp
├── game.cpp
Expand Down Expand Up @@ -240,4 +269,6 @@ MIT License - See LICENSE file for details.

## Disclaimer

This is a community project and is **not affiliated with Xteink**. Flash custom firmware at your own risk. Always backup your device before flashing.
This is a community project and is **not affiliated with Xteink, Papyrix, or
Yahoo**. Flash custom firmware at your own risk. Always back up your device
before flashing.
21 changes: 21 additions & 0 deletions SECURITY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Security policy

## Supported code

Security fixes are maintained on the default branch only. This is experimental
community firmware, not a supported device recovery tool or safety-certified
flashing system.

## Reporting

Please report suspected vulnerabilities through GitHub's private vulnerability
reporting flow for this repository. Do not include Wi-Fi credentials, portfolio
data, device backups, or other personal information in a public issue.

For reports involving the quote client, identify whether the issue is in this
firmware or the third-party endpoint. This project does not authorize testing of
Yahoo, Xteink, Papyrix, or any other external system.

For flashing issues, include the hardware revision, exact command, image offset,
and a redacted partition table. A report does not imply that a damaged device can
be recovered.
7 changes: 4 additions & 3 deletions include/apps/stocktracker.h
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
/**
* Stock Portfolio Tracker for X4 - Enhanced Edition
* Full-featured stock/IRA portfolio monitoring with charts
* Experimental local holdings display for X4
* Charts and quotes are fetched from an unofficial Yahoo Finance endpoint.
*
* Features:
* - Real-time quotes via Yahoo Finance API
* - Delayed or near-real-time quotes when the upstream endpoint is available
* - Historical price charts (1D, 1W, 1M, 3M, 1Y)
* - Sparkline mini-charts in list view
* - Market indices (S&P 500, NASDAQ, DOW)
Expand Down Expand Up @@ -134,6 +134,7 @@ class StockTracker : public Game {

// API functions
bool connectWiFi();
bool synchronizeClock();
void disconnectWiFi();
bool fetchQuote(const char* symbol, StockHolding& holding);
bool fetchHistory(const char* symbol, StockHolding& holding, ChartRange range);
Expand Down
3 changes: 3 additions & 0 deletions include/display.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include <Fonts/FreeSans9pt7b.h>
#include <Fonts/FreeSans12pt7b.h>
#include <Fonts/FreeSans18pt7b.h>
#include <Fonts/FreeSansBold9pt7b.h>
#include <Fonts/FreeSansBold12pt7b.h>
#include <Fonts/FreeSansBold18pt7b.h>
#include <Fonts/FreeSansBold24pt7b.h>
Expand All @@ -21,6 +22,8 @@ typedef GxEPD2_BW<GxEPD2_426_GDEQ0426T82, GxEPD2_426_GDEQ0426T82::HEIGHT> Displa

class Display {
public:
Display();

void begin();

// Full refresh (slower, no ghosting)
Expand Down
4 changes: 2 additions & 2 deletions include/x4_hardware.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
* X4 Hardware Abstraction Layer
* Pin definitions and hardware constants for the Xteink X4
*
* SAFE: This code only reads buttons and controls the display.
* It does NOT modify bootloader, flash partitions, or system files.
* This header defines the display, button, SD-card, and battery-monitor pins.
* Confirm the pinout against the exact hardware revision before flashing.
*/

#ifndef X4_HARDWARE_H
Expand Down
32 changes: 32 additions & 0 deletions include/yahoo_root_ca.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#ifndef YAHOO_ROOT_CA_H
#define YAHOO_ROOT_CA_H

// DigiCert Global Root G2. Source: the DigiCert public root program.
// Valid 2013-08-01 through 2038-01-15. Replace deliberately if the endpoint's
// certificate chain changes; do not fall back to an unauthenticated TLS client.
static const char YAHOO_ROOT_CA[] = R"CERT(
-----BEGIN CERTIFICATE-----
MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBh
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaUNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH
MjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVT
MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j
b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEcyMIIBIjANBgkqhkiG
9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/RrohCgiN9RlUyfuI
2/Ou8jqJkTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxpsMNzaHxmx
1x7e/dfgy5SDN67sH0NO3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQ
q2EGnI/yuum06ZIya7XzV+hdG82MHauVBJVJ8zUtluNJbd134/tJS7SsVQepj5Wz
tCO7TG1F8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyMUNGPHgm+F6HmIcr9g+UQ
vIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQABo0IwQDAP
BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV
5uNu5g/6+rkS7QYXjzkwDQYJKoZIhvcNAQELBQADggEBAGBnKJRvDkhj6zHd6mcY
1Yl9PMWLSn/pvtsrF9+wX3N3KjITOYFnQoQj8kVnNeyIv/iPsGEMNKSuIEyExtv4
NeF22d+mQrvHRAiGfzZ0JFrabA0UWTW98kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NG
Fdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBHQRFXGU7Aj64GxJUTFy8bJZ91
8rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/iyK5S9kJRaTe
pLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl
MrY=
-----END CERTIFICATE-----
)CERT";

#endif // YAHOO_ROOT_CA_H
8 changes: 4 additions & 4 deletions partitions.csv
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# Partition table for X4 Games - Matches Papyrix exactly for compatibility
# This ensures safe flashing that preserves NVS data and supports OTA
# Partition table used by this X4 firmware, based on the Papyrix layout.
# Verify these offsets against the exact device revision before flashing.
# ============================================================================
# IMPORTANT: This table matches the official Papyrix partition layout.
# Bootloader is NOT included here - it remains untouched at 0x0-0x8FFF
# This CSV does not contain a bootloader partition. Whether a flashing command
# rewrites other regions depends on the command and image set used.
# ============================================================================
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 0x5000,
Expand Down
Loading