docs: Add lightweight contributor onboarding documentation (#894)
### Summary This PR introduces a lightweight contributor onboarding docs section under `docs/contributing/` and improves local formatting ergonomics for first-time contributors. The goal is to make CrossPoint easier to contribute to for software developers who are new to embedded systems (like me), while keeping onboarding modular and aligned with existing project docs. ### What changed - Added contributor docs hub: `docs/contributing/README.md` - Added focused onboarding pages: - `docs/contributing/getting-started.md` - `docs/contributing/architecture.md` - `docs/contributing/development-workflow.md` - `docs/contributing/testing-debugging.md` - Linked contributor docs from `README.md` for discoverability - Expanded architecture documentation with Mermaid diagrams - Improved `bin/clang-format-fix`: - prefers `clang-format-21` when available - validates formatter version and fails fast with a clear message if too old - handles missing positional arg safely - Updated docs to explain common `clang-format` setup/version issues and install paths (including fallback steps when `clang-format-21` is unavailable in default apt sources) ### Why - There was no dedicated contributor onboarding path; first-time contributors had to infer workflow from multiple files. - New contributors (especially from non-embedded backgrounds) need a clear mental model of architecture, runtime flow, and debugging process. - Local formatting setup caused avoidable friction due to clang-format version mismatch (`.clang-format` expects newer keys used in CI). - The updates make contribution setup more predictable, reduce onboarding confusion, and align local checks with CI expectations. ### Additional context - No firmware behavior/runtime logic was changed; this PR focuses on contributor experience and tooling clarity. --- ### AI Usage > Did you use AI tools to help write this code? Yes, I used AI tools to assist with generating the documentation. I then manually reviewed, tested, and refined the code to ensure it works correctly. please feel free to point out any discrepancies or areas for improvement.
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -10,3 +10,4 @@ build
|
|||||||
**/__pycache__/
|
**/__pycache__/
|
||||||
/compile_commands.json
|
/compile_commands.json
|
||||||
/.cache
|
/.cache
|
||||||
|
/.venv
|
||||||
|
|||||||
@@ -154,6 +154,8 @@ For more details on the internal file structures, see the [file formats document
|
|||||||
|
|
||||||
Contributions are very welcome!
|
Contributions are very welcome!
|
||||||
|
|
||||||
|
If you are new to the codebase, start with the [contributing docs](./docs/contributing/README.md).
|
||||||
|
|
||||||
If you're looking for a way to help out, take a look at the [ideas discussion board](https://github.com/crosspoint-reader/crosspoint-reader/discussions/categories/ideas).
|
If you're looking for a way to help out, take a look at the [ideas discussion board](https://github.com/crosspoint-reader/crosspoint-reader/discussions/categories/ideas).
|
||||||
If there's something there you'd like to work on, leave a comment so that we can avoid duplicated effort.
|
If there's something there you'd like to work on, leave a comment so that we can avoid duplicated effort.
|
||||||
|
|
||||||
|
|||||||
@@ -1,17 +1,32 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
# Check if clang-format is availible
|
# Check if clang-format is available and pick the preferred binary.
|
||||||
command -v clang-format >/dev/null 2>&1 || {
|
if command -v clang-format-21 >/dev/null 2>&1; then
|
||||||
printf "'clang-format' not found in current environment\n"
|
CLANG_FORMAT_BIN="clang-format-21"
|
||||||
printf "install 'clang', 'clang-tools', or 'clang-format' depending on your distro/os and tooling requirements\n"
|
elif command -v clang-format >/dev/null 2>&1; then
|
||||||
exit 1
|
CLANG_FORMAT_BIN="clang-format"
|
||||||
}
|
else
|
||||||
|
printf "'clang-format' not found in current environment\n"
|
||||||
|
printf "Install clang-format-21 (recommended), clang, clang-tools, or clang-format depending on your distro/os and tooling requirements\n"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
GIT_LS_FILES_FLAGS=""
|
GIT_LS_FILES_FLAGS=""
|
||||||
if [[ "$1" == "-g" ]]; then
|
if [[ "${1:-}" == "-g" ]]; then
|
||||||
GIT_LS_FILES_FLAGS="--modified"
|
GIT_LS_FILES_FLAGS="--modified"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
CLANG_FORMAT_VERSION_RAW="$(${CLANG_FORMAT_BIN} --version)"
|
||||||
|
CLANG_FORMAT_MAJOR="$(printf '%s\n' "${CLANG_FORMAT_VERSION_RAW}" | grep -oE '[0-9]+' | head -n1)"
|
||||||
|
|
||||||
|
if [[ -z "${CLANG_FORMAT_MAJOR}" || "${CLANG_FORMAT_MAJOR}" -lt 21 ]]; then
|
||||||
|
echo "Error: ${CLANG_FORMAT_BIN} is too old: ${CLANG_FORMAT_VERSION_RAW}"
|
||||||
|
echo "This repository's .clang-format requires clang-format 21 or newer."
|
||||||
|
echo "Install clang-format-21 and rerun ./bin/clang-format-fix"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
# --- Main Logic ---
|
# --- Main Logic ---
|
||||||
|
|
||||||
@@ -27,4 +42,4 @@ git ls-files --exclude-standard ${GIT_LS_FILES_FLAGS} \
|
|||||||
| grep -v -E '^lib/EpdFont/builtinFonts/' \
|
| grep -v -E '^lib/EpdFont/builtinFonts/' \
|
||||||
| grep -v -E '^lib/Epub/Epub/hyphenation/generated/' \
|
| grep -v -E '^lib/Epub/Epub/hyphenation/generated/' \
|
||||||
| grep -v -E '^lib/uzlib/' \
|
| grep -v -E '^lib/uzlib/' \
|
||||||
| xargs -r clang-format -style=file -i
|
| xargs -r "${CLANG_FORMAT_BIN}" -style=file -i
|
||||||
|
|||||||
11
docs/contributing/README.md
Normal file
11
docs/contributing/README.md
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# Contributing Docs
|
||||||
|
|
||||||
|
This section is a lightweight contributor guide for CrossPoint Reader.
|
||||||
|
It is written for software developers who may be new to embedded development.
|
||||||
|
|
||||||
|
- [Getting Started](./getting-started.md)
|
||||||
|
- [Architecture Overview](./architecture.md)
|
||||||
|
- [Development Workflow](./development-workflow.md)
|
||||||
|
- [Testing and Debugging](./testing-debugging.md)
|
||||||
|
|
||||||
|
If you are new, start with [Getting Started](./getting-started.md).
|
||||||
199
docs/contributing/architecture.md
Normal file
199
docs/contributing/architecture.md
Normal file
@@ -0,0 +1,199 @@
|
|||||||
|
# Architecture Overview
|
||||||
|
|
||||||
|
CrossPoint is firmware for the Xteink X4 (unaffiliated with Xteink), built with PlatformIO targeting the ESP32-C3 microcontroller.
|
||||||
|
|
||||||
|
At a high level, it is firmware that uses an activity-driven application architecture loop with persistent settings/state, SD-card-first caching, and a rendering pipeline optimized for e-ink constraints.
|
||||||
|
|
||||||
|
## System at a glance
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
graph TD
|
||||||
|
A[Hardware: ESP32-C3 + SD + E-ink + Buttons] --> B[open-x4-sdk HAL]
|
||||||
|
B --> C[src/main.cpp runtime loop]
|
||||||
|
C --> D[Activities layer]
|
||||||
|
C --> E[State and settings]
|
||||||
|
D --> F[Reader flows]
|
||||||
|
D --> G[Home/Library/Settings flows]
|
||||||
|
D --> H[Network/Web server flows]
|
||||||
|
F --> I[lib/Epub parsing + layout + hyphenation]
|
||||||
|
I --> J[SD cache in .crosspoint]
|
||||||
|
D --> K[GfxRenderer]
|
||||||
|
K --> L[E-ink display buffer]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Runtime lifecycle
|
||||||
|
|
||||||
|
Primary entry point is `src/main.cpp`.
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
A[Boot] --> B[Init GPIO and optional serial]
|
||||||
|
B --> C[Init SD storage]
|
||||||
|
C --> D[Load settings and app state]
|
||||||
|
D --> E[Init display and fonts]
|
||||||
|
E --> F{Resume reader?}
|
||||||
|
F -->|No| G[Enter Home activity]
|
||||||
|
F -->|Yes| H[Enter Reader activity]
|
||||||
|
G --> I[Main loop]
|
||||||
|
H --> I
|
||||||
|
I --> J[Poll input and run current activity]
|
||||||
|
J --> K{Sleep condition met?}
|
||||||
|
K -->|No| I
|
||||||
|
K -->|Yes| L[Persist state and enter deep sleep]
|
||||||
|
```
|
||||||
|
|
||||||
|
In each loop iteration, the firmware updates input, runs the active activity, handles auto-sleep/power behavior, and applies a short delay policy to balance responsiveness and power.
|
||||||
|
|
||||||
|
## Activity model
|
||||||
|
|
||||||
|
Activities are screen-level controllers deriving from `src/activities/Activity.h`.
|
||||||
|
Some flows use `src/activities/ActivityWithSubactivity.h` to host nested activities.
|
||||||
|
|
||||||
|
- `onEnter()` and `onExit()` manage setup/teardown
|
||||||
|
- `loop()` handles per-frame behavior
|
||||||
|
- `skipLoopDelay()` and `preventAutoSleep()` are used by long-running flows (for example web server mode)
|
||||||
|
|
||||||
|
Top-level activity groups:
|
||||||
|
|
||||||
|
- `src/activities/home/`: home and library navigation
|
||||||
|
- `src/activities/reader/`: EPUB/XTC/TXT reading flows
|
||||||
|
- `src/activities/settings/`: settings menus and configuration
|
||||||
|
- `src/activities/network/`: WiFi selection, AP/STA mode, file transfer server
|
||||||
|
- `src/activities/boot_sleep/`: boot and sleep transitions
|
||||||
|
|
||||||
|
## Reader and content pipeline
|
||||||
|
|
||||||
|
Reader orchestration starts in `src/activities/reader/ReaderActivity.h` and dispatches to format-specific readers.
|
||||||
|
EPUB processing is implemented in `lib/Epub/`.
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
A[Select book] --> B[ReaderActivity]
|
||||||
|
B --> C{Format}
|
||||||
|
C -->|EPUB| D[lib/Epub/Epub]
|
||||||
|
C -->|XTC| E[lib/Xtc reader]
|
||||||
|
C -->|TXT| F[lib/Txt reader]
|
||||||
|
D --> G[Parse OPF/TOC/CSS]
|
||||||
|
G --> H[Layout pages/sections]
|
||||||
|
H --> I[Write section and metadata caches]
|
||||||
|
I --> J[Render current page via GfxRenderer]
|
||||||
|
```
|
||||||
|
|
||||||
|
Why caching matters:
|
||||||
|
|
||||||
|
- RAM is limited on ESP32-C3, so expensive parsed/layout data is persisted to SD
|
||||||
|
- repeat opens/page navigation can reuse cached data instead of full reparsing
|
||||||
|
|
||||||
|
## Reader internals call graph
|
||||||
|
|
||||||
|
This diagram zooms into the EPUB path to show the main control and data flow from activity entry to on-screen draw.
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
A[ReaderActivity onEnter] --> B{File type}
|
||||||
|
B -->|EPUB| C[Create Epub object]
|
||||||
|
B -->|XTC/TXT| Z[Use format-specific reader]
|
||||||
|
|
||||||
|
C --> D[Epub load]
|
||||||
|
D --> E[Locate container and OPF]
|
||||||
|
E --> F[Build or load BookMetadataCache]
|
||||||
|
F --> G[Load TOC and spine]
|
||||||
|
G --> H[Load or parse CSS rules]
|
||||||
|
|
||||||
|
H --> I[EpubReaderActivity]
|
||||||
|
I --> J{Section cache exists for current settings?}
|
||||||
|
J -->|Yes| K[Read section bin from SD cache]
|
||||||
|
J -->|No| L[Parse chapter HTML and layout text]
|
||||||
|
L --> M[Apply typography settings and hyphenation]
|
||||||
|
M --> N[Write section cache bin]
|
||||||
|
|
||||||
|
K --> O[Build page model]
|
||||||
|
N --> O
|
||||||
|
O --> P[GfxRenderer draw calls]
|
||||||
|
P --> Q[HAL display framebuffer update]
|
||||||
|
Q --> R[E-ink refresh policy]
|
||||||
|
|
||||||
|
S[SETTINGS singleton] -. influences .-> J
|
||||||
|
S -. influences .-> M
|
||||||
|
T[APP_STATE singleton] -. persists .-> U[Reading progress and resume context]
|
||||||
|
U -. used by .-> I
|
||||||
|
```
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
|
||||||
|
- "section cache exists" depends on cache-busting parameters such as font and layout-related settings
|
||||||
|
- rendering favors reusing precomputed layout data to keep page turns responsive on constrained hardware
|
||||||
|
- progress/session state is persisted so the reader can reopen at the last position after reboot/sleep
|
||||||
|
|
||||||
|
## State and persistence
|
||||||
|
|
||||||
|
Two singletons are central:
|
||||||
|
|
||||||
|
- `src/CrossPointSettings.h` (`SETTINGS`): user preferences and behavior flags
|
||||||
|
- `src/CrossPointState.h` (`APP_STATE`): runtime/session state such as current book and sleep context
|
||||||
|
|
||||||
|
Typical persisted areas on SD:
|
||||||
|
|
||||||
|
```text
|
||||||
|
/.crosspoint/
|
||||||
|
epub_<hash>/
|
||||||
|
book.bin
|
||||||
|
progress.bin
|
||||||
|
cover.bmp
|
||||||
|
sections/*.bin
|
||||||
|
settings.bin
|
||||||
|
state.bin
|
||||||
|
```
|
||||||
|
|
||||||
|
For binary cache formats, see `docs/file-formats.md`.
|
||||||
|
|
||||||
|
## Networking architecture
|
||||||
|
|
||||||
|
Network file transfer is controlled by `src/activities/network/CrossPointWebServerActivity.h` and served by `src/network/CrossPointWebServer.h`.
|
||||||
|
|
||||||
|
Modes:
|
||||||
|
|
||||||
|
- STA: join existing WiFi network
|
||||||
|
- AP: create hotspot
|
||||||
|
|
||||||
|
Server behavior:
|
||||||
|
|
||||||
|
- HTTP server on port 80
|
||||||
|
- WebSocket upload server on port 81
|
||||||
|
- file operations backed by SD storage
|
||||||
|
- activity requests faster loop responsiveness while server is running
|
||||||
|
|
||||||
|
Endpoint reference: `docs/webserver-endpoints.md`.
|
||||||
|
|
||||||
|
## Build-time generated assets
|
||||||
|
|
||||||
|
Some sources are generated and should not be edited manually.
|
||||||
|
|
||||||
|
- `scripts/build_html.py` generates `src/network/html/*.generated.h` from HTML files
|
||||||
|
- `scripts/generate_hyphenation_trie.py` generates hyphenation headers under `lib/Epub/Epub/hyphenation/generated/`
|
||||||
|
|
||||||
|
When editing related source assets, regenerate via normal build steps/scripts.
|
||||||
|
|
||||||
|
## Key directories
|
||||||
|
|
||||||
|
- `src/`: app orchestration, settings/state, and activity implementations
|
||||||
|
- `src/network/`: web server and OTA/update networking
|
||||||
|
- `src/components/`: theming and shared UI components
|
||||||
|
- `lib/Epub/`: EPUB parser, layout, CSS handling, and hyphenation
|
||||||
|
- `lib/`: supporting libraries (fonts, text, filesystem helpers, etc.)
|
||||||
|
- `open-x4-sdk/`: hardware SDK submodule (display, input, storage, battery)
|
||||||
|
- `docs/`: user and technical documentation
|
||||||
|
|
||||||
|
## Embedded constraints that shape design
|
||||||
|
|
||||||
|
- constrained RAM drives SD-first caching and careful allocations
|
||||||
|
- e-ink refresh cost drives render/update batching choices
|
||||||
|
- main loop responsiveness matters for input, power handling, and watchdog safety
|
||||||
|
- background/network flows must cooperate with sleep and loop timing logic
|
||||||
|
|
||||||
|
## Scope guardrails
|
||||||
|
|
||||||
|
Before implementing larger ideas, check:
|
||||||
|
|
||||||
|
- [SCOPE.md](../../SCOPE.md)
|
||||||
|
- [GOVERNANCE.md](../../GOVERNANCE.md)
|
||||||
43
docs/contributing/development-workflow.md
Normal file
43
docs/contributing/development-workflow.md
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
# Development Workflow
|
||||||
|
|
||||||
|
This page defines the expected local workflow before opening a pull request.
|
||||||
|
|
||||||
|
## 1) Fork and create a focused branch
|
||||||
|
|
||||||
|
- Fork the repository to your own GitHub account
|
||||||
|
- Clone your fork locally and add the upstream repository if needed
|
||||||
|
|
||||||
|
- Branch from `master`
|
||||||
|
- Keep each PR focused on one fix or feature area
|
||||||
|
|
||||||
|
## 2) Implement with scope in mind
|
||||||
|
|
||||||
|
- Confirm your idea is in project scope: [SCOPE.md](../../SCOPE.md)
|
||||||
|
- Prefer incremental changes over broad refactors
|
||||||
|
|
||||||
|
## 3) Run local checks
|
||||||
|
|
||||||
|
```sh
|
||||||
|
./bin/clang-format-fix
|
||||||
|
pio check --fail-on-defect low --fail-on-defect medium --fail-on-defect high
|
||||||
|
pio run
|
||||||
|
```
|
||||||
|
|
||||||
|
CI enforces formatting, static analysis, and build checks.
|
||||||
|
Use clang-format 21+ locally to match CI.
|
||||||
|
If `clang-format` is missing or too old locally, see [Getting Started](./getting-started.md).
|
||||||
|
|
||||||
|
## 4) Open the PR
|
||||||
|
|
||||||
|
- Use a semantic title (example: `fix: avoid crash when opening malformed epub`)
|
||||||
|
- Fill out `.github/PULL_REQUEST_TEMPLATE.md`
|
||||||
|
- Describe the problem, approach, and any tradeoffs
|
||||||
|
- Include reproduction and verification steps for bug fixes
|
||||||
|
|
||||||
|
## 5) Review etiquette
|
||||||
|
|
||||||
|
- Be explicit and concise in responses
|
||||||
|
- Keep discussions technical and respectful
|
||||||
|
- Assume good intent and focus on code-level feedback
|
||||||
|
|
||||||
|
For community expectations, see [GOVERNANCE.md](../../GOVERNANCE.md).
|
||||||
80
docs/contributing/getting-started.md
Normal file
80
docs/contributing/getting-started.md
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
# Getting Started
|
||||||
|
|
||||||
|
This guide helps you build and run CrossPoint locally.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- PlatformIO Core (`pio`) or VS Code + PlatformIO IDE
|
||||||
|
- Python 3.8+
|
||||||
|
- `clang-format` 21+ in your `PATH` (CI uses clang-format 21)
|
||||||
|
- USB-C cable
|
||||||
|
- Xteink X4 device for hardware testing
|
||||||
|
|
||||||
|
If `./bin/clang-format-fix` fails with either of these errors, install clang-format 21:
|
||||||
|
|
||||||
|
- `clang-format: No such file or directory`
|
||||||
|
- `.clang-format: error: unknown key 'AlignFunctionDeclarations'`
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Debian/Ubuntu (try this first)
|
||||||
|
sudo apt-get update && sudo apt-get install -y clang-format-21
|
||||||
|
|
||||||
|
# If the package is unavailable, add LLVM apt repo and retry
|
||||||
|
wget https://apt.llvm.org/llvm.sh
|
||||||
|
chmod +x llvm.sh
|
||||||
|
sudo ./llvm.sh 21
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y clang-format-21
|
||||||
|
|
||||||
|
# macOS (Homebrew)
|
||||||
|
brew install clang-format
|
||||||
|
```
|
||||||
|
|
||||||
|
Then verify:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
clang-format-21 --version
|
||||||
|
```
|
||||||
|
|
||||||
|
The reported major version must be 21 or newer.
|
||||||
|
|
||||||
|
## Clone and initialize
|
||||||
|
|
||||||
|
```sh
|
||||||
|
git clone --recursive https://github.com/crosspoint-reader/crosspoint-reader
|
||||||
|
cd crosspoint-reader
|
||||||
|
```
|
||||||
|
|
||||||
|
If you already cloned without submodules:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
git submodule update --init --recursive
|
||||||
|
```
|
||||||
|
|
||||||
|
## Build
|
||||||
|
|
||||||
|
```sh
|
||||||
|
pio run
|
||||||
|
```
|
||||||
|
|
||||||
|
## Flash
|
||||||
|
|
||||||
|
```sh
|
||||||
|
pio run --target upload
|
||||||
|
```
|
||||||
|
|
||||||
|
## First checks before opening a PR
|
||||||
|
|
||||||
|
```sh
|
||||||
|
./bin/clang-format-fix
|
||||||
|
pio check --fail-on-defect low --fail-on-defect medium --fail-on-defect high
|
||||||
|
pio run
|
||||||
|
```
|
||||||
|
|
||||||
|
## What to read next
|
||||||
|
|
||||||
|
- [Architecture Overview](./architecture.md)
|
||||||
|
- [Development Workflow](./development-workflow.md)
|
||||||
|
- [Testing and Debugging](./testing-debugging.md)
|
||||||
48
docs/contributing/testing-debugging.md
Normal file
48
docs/contributing/testing-debugging.md
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
# Testing and Debugging
|
||||||
|
|
||||||
|
CrossPoint runs on real hardware, so debugging usually combines local build checks and on-device logs.
|
||||||
|
|
||||||
|
## Local checks
|
||||||
|
|
||||||
|
Make sure `clang-format` 21+ is installed and available in `PATH` before running the formatting step.
|
||||||
|
If needed, see [Getting Started](./getting-started.md).
|
||||||
|
|
||||||
|
```sh
|
||||||
|
./bin/clang-format-fix
|
||||||
|
pio check --fail-on-defect low --fail-on-defect medium --fail-on-defect high
|
||||||
|
pio run
|
||||||
|
```
|
||||||
|
|
||||||
|
## Flash and monitor
|
||||||
|
|
||||||
|
Flash firmware:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
pio run --target upload
|
||||||
|
```
|
||||||
|
|
||||||
|
Open serial monitor:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
pio device monitor
|
||||||
|
```
|
||||||
|
|
||||||
|
Optional enhanced monitor:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
python3 -m pip install pyserial colorama matplotlib
|
||||||
|
python3 scripts/debugging_monitor.py
|
||||||
|
```
|
||||||
|
|
||||||
|
## Useful bug report contents
|
||||||
|
|
||||||
|
- Firmware version and build environment
|
||||||
|
- Exact steps to reproduce
|
||||||
|
- Expected vs actual behavior
|
||||||
|
- Serial logs from boot through failure
|
||||||
|
- Whether issue reproduces after clearing `.crosspoint/` cache on SD card
|
||||||
|
|
||||||
|
## Common troubleshooting references
|
||||||
|
|
||||||
|
- [User Guide troubleshooting section](../../USER_GUIDE.md#7-troubleshooting-issues--escaping-bootloop)
|
||||||
|
- [Webserver troubleshooting](../troubleshooting.md)
|
||||||
Reference in New Issue
Block a user