Public Access
Site: roro9stack.net, phase 1 (the home page, Install with a browser flasher, Downloads)
A Zola site in site/, from the design: both themes on the device's palette, notched shapes, self-hosted fonts, the wordmark and icons, two generated hero drawings, nine App cards (the mesh messenger marked planned), real screenshots, the updates and build sections. The Install page builds ESP Web Tools' manifest in the browser from the Gitea API (it needs Caddy to allow the origin), refuses any download that isn't on the project's server, and falls back to the esptool steps. Downloads lists the releases. The focus ring shows on notched controls. A page checker fails the build if a page loads from another origin. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}Not found: roro9stack{% endblock %}
|
||||
{% block main %}
|
||||
<div class="wrap page">
|
||||
<h1>Not found.</h1>
|
||||
<p class="prose">There's no page here. Try <a href="/">the home page</a>, <a href="/install/">Install</a> or <a href="/downloads/">Downloads</a>.</p>
|
||||
</div>
|
||||
{% endblock main %}
|
||||
@@ -0,0 +1,59 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>{% block title %}roro9stack: open firmware for the Cardputer ADV{% endblock %}</title>
|
||||
<meta name="description" content="{% block description %}{{ config.description }}{% endblock %}">
|
||||
<meta name="color-scheme" content="dark light">
|
||||
<meta name="author" content="Clément Martin">
|
||||
<meta name="copyright" content="© 2026 Clément Martin">
|
||||
{% if current_url %}<link rel="canonical" href="{{ current_url }}">{% endif %}
|
||||
<link rel="icon" href="/img/favicon.svg" type="image/svg+xml">
|
||||
<link rel="preload" href="/fonts/DMMono-Medium.woff2" as="font" type="font/woff2" crossorigin>
|
||||
<link rel="preload" href="/fonts/HankenGrotesk.woff2" as="font" type="font/woff2" crossorigin>
|
||||
<link rel="stylesheet" href="/css/site.css">
|
||||
<script src="/js/theme.js"></script>
|
||||
{% block head %}{% endblock %}
|
||||
</head>
|
||||
<body>
|
||||
<a class="skip" href="#main">Skip to the content</a>
|
||||
|
||||
<header class="wrap bar">
|
||||
<a class="brand" href="/" aria-label="roro9stack, home">
|
||||
<img class="logo logo-dark" src="/img/logo-dark.svg" alt="roro9stack" width="201" height="28">
|
||||
<img class="logo logo-light" src="/img/logo-light.svg" alt="" width="201" height="28">
|
||||
</a>
|
||||
<nav aria-label="Main">
|
||||
<a href="/#apps">Apps</a>
|
||||
<a href="/install/">Install</a>
|
||||
<a href="/downloads/">Downloads</a>
|
||||
<a href="{{ config.extra.blog }}">Blog</a>
|
||||
<button class="link-btn js-only" id="theme-toggle" type="button">Light</button>
|
||||
<a class="btn btn-primary n-md" href="{{ config.extra.repo }}">Source</a>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<main id="main">
|
||||
{% block main %}{% endblock %}
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<div class="wrap">
|
||||
<div class="foot-row">
|
||||
<span>© 2026 Clément Martin · Licensed GPL-3.0</span>
|
||||
<nav aria-label="Footer">
|
||||
<a href="{{ config.extra.repo }}">Source</a>
|
||||
<a href="{{ config.extra.repo }}/releases">Releases</a>
|
||||
<a href="{{ config.extra.repo }}/src/branch/main/docs">Docs in the repository</a>
|
||||
<a href="{{ config.extra.repo }}/issues">Issues</a>
|
||||
<a href="{{ config.extra.blog }}">Blog</a>
|
||||
<a href="mailto:{{ config.extra.contact }}">Contact</a>
|
||||
</nav>
|
||||
</div>
|
||||
<small>roro9stack is an independent project. It isn't affiliated with or endorsed by M5Stack or Meshtastic: those names belong to their owners.</small>
|
||||
<small>This site sets no cookies and has no analytics, and its pages load nothing from other sites. The Install page asks the project's own server, git.twis.la, for the latest release. Questions, corrections: <a href="mailto:{{ config.extra.contact }}">{{ config.extra.contact }}</a>, or the issue tracker.</small>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,34 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}Downloads: roro9stack{% endblock %}
|
||||
{% block description %}{{ page.description }}{% endblock %}
|
||||
{% block main %}
|
||||
{% set releases = load_data(url=config.extra.api ~ "/releases?limit=30&draft=false&pre-release=false", format="json", required=false) %}
|
||||
<div class="wrap page">
|
||||
<header>
|
||||
<span class="eyebrow">Downloads</span>
|
||||
<h1>{{ page.title }}</h1>
|
||||
<p class="lead">{{ page.description }} To put one on a device, <a class="accent-link" href="/install/">install it from your browser</a>.</p>
|
||||
</header>
|
||||
|
||||
<div class="prose">
|
||||
<p>Each release has a <strong>signed update file</strong> (<code>.ota</code>: copy it to <code>/updates</code> on the SD card and install it from Settings → Firmware or the Storage App, or push it over Wi-Fi), a <strong>factory image</strong> (<code>-factory.bin</code>, the whole flash at offset 0 for a first install over USB), the <strong>symbols</strong> (<code>.elf.gz</code>, to decode a crash report) and <code>SHA256SUMS</code>. Debug Builds aren't published.</p>
|
||||
</div>
|
||||
|
||||
{% if releases %}
|
||||
<div class="releases">
|
||||
{% for r in releases %}
|
||||
<article class="release n-md">
|
||||
<h2>{{ r.tag_name }}</h2>
|
||||
<p class="meta">{{ r.published_at | date(format="%Y-%m-%d") }} · <a class="accent-link" href="{{ r.html_url }}">release notes</a></p>
|
||||
<p>{{ r.body | split(pat="\n\n") | first }}</p>
|
||||
<p class="files">
|
||||
{% for a in r.assets %}<a href="{{ a.browser_download_url }}">{{ a.name }}</a> <span class="muted">({{ a.size | filesizeformat }})</span>{% endfor %}
|
||||
</p>
|
||||
</article>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<p class="prose">The list of releases couldn't be read when this page was built. They're all on <a class="accent-link" href="{{ config.extra.repo }}/releases">Gitea</a>.</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock main %}
|
||||
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 29 KiB |
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 22 KiB |
@@ -0,0 +1,139 @@
|
||||
{% extends "base.html" %}
|
||||
{% block main %}
|
||||
{% set latest = load_data(url=config.extra.api ~ "/releases/latest", format="json", required=false) %}
|
||||
{% set apps = load_data(path="data/apps.toml", format="toml") %}
|
||||
{% set screens = load_data(path="data/screens.toml", format="toml") %}
|
||||
{% set px = [0,1,1,1,0, 1,0,0,0,1, 1,0,0,0,1, 0,1,1,1,1, 0,0,0,0,1, 0,0,0,0,1, 0,1,1,1,0] %}
|
||||
|
||||
<section class="wrap hero" aria-labelledby="hero-title">
|
||||
<div class="hero-grid">
|
||||
<div>
|
||||
<div class="eyebrow-row"><span class="dot"></span><span class="eyebrow">Open firmware · GPL-3.0{% if latest %} · <span class="v">{{ latest.tag_name }}</span>{% endif %}</span></div>
|
||||
<h1 id="hero-title">Open firmware for a <span class="accent">pocket computer.</span></h1>
|
||||
</div>
|
||||
<div>
|
||||
<p class="lead">roro9stack is firmware for the M5Stack Cardputer ADV with the Cap LoRa-1262: a LoRa scanner that reads Meshtastic traffic, GNSS, IRC, a Gemini browser, Wi-Fi tools, notes and more.</p>
|
||||
<div class="actions">
|
||||
<a class="btn btn-primary n-md" href="/install/">Install{% if latest %} {{ latest.tag_name }}{% endif %}</a>
|
||||
<a class="btn btn-secondary n-md" href="{{ config.extra.repo }}">Read the source</a>
|
||||
</div>
|
||||
<ul class="facts">
|
||||
<li>GPL-3.0</li>
|
||||
<li>Docker-only build</li>
|
||||
<li>Signed updates</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="slab n-lg">
|
||||
<div class="device">{% include "illustrations/sweep.html" %}</div>
|
||||
<div class="device">{% include "illustrations/sky.html" %}</div>
|
||||
</div>
|
||||
<p class="caption">Drawings of the LoRa Scanner's Sweep view and the GNSS sky view, not screenshots: the real screens are further down.</p>
|
||||
</section>
|
||||
|
||||
<section class="wrap" aria-labelledby="status-title">
|
||||
<div class="status-box n-md">
|
||||
<h2 id="status-title" class="eyebrow">Where it stands</h2>
|
||||
<p>Early software{% if latest %}, {{ latest.tag_name }}{% endif %}. The radio listens; it doesn't transmit yet. Sending and reading mesh messages is planned and waits for a second node to test with. <a href="{{ config.extra.repo }}/milestones">The milestones</a> show what's done and what's next.</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="wrap" id="need" aria-labelledby="need-title">
|
||||
<div class="section-head">
|
||||
<span class="eyebrow">Before you start</span>
|
||||
<h2 id="need-title">What you need.</h2>
|
||||
</div>
|
||||
<ul class="need">
|
||||
<li><strong>An M5Stack Cardputer ADV</strong><span>The handheld the firmware runs on.</span></li>
|
||||
<li><strong>The Cap LoRa-1262</strong><span>The LoRa Scanner and GNSS need it. The other Apps don't use it.</span></li>
|
||||
<li><strong>A microSD card</strong><span>For Notes, Storage, Logs, Captures and Tracks.</span></li>
|
||||
<li><strong>Wi-Fi</strong><span>For IRC, Gemini and updates.</span></li>
|
||||
<li><strong>A computer, for the first install</strong><span>Chrome or Edge, and a USB-C cable. After that the device updates itself.</span></li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section class="wrap" id="apps" aria-labelledby="apps-title">
|
||||
<div class="section-head">
|
||||
<span class="eyebrow">What's inside</span>
|
||||
<h2 id="apps-title">Apps for the radio, the web and the card.</h2>
|
||||
</div>
|
||||
<div class="cards">
|
||||
{% for a in apps.app %}
|
||||
<article class="card n-lg{% if a.planned %} planned{% endif %}">
|
||||
<div class="ic i{{ a.icon }}" aria-hidden="true"></div>
|
||||
<div class="card-top"><span class="tag">{{ a.tag }}</span><span class="num">{{ a.num }}</span></div>
|
||||
<h3>{{ a.title }}</h3>
|
||||
<p>{{ a.text }}</p>
|
||||
<span class="fact">{{ a.fact }}</span>
|
||||
</article>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<p class="cards-note">Plus Settings, with its Wi-Fi and Firmware pages.</p>
|
||||
</section>
|
||||
|
||||
<section class="wrap" id="screens" aria-labelledby="screens-title">
|
||||
<div class="section-head">
|
||||
<span class="eyebrow">On the device</span>
|
||||
<h2 id="screens-title">The real screens.</h2>
|
||||
</div>
|
||||
<div class="shots">
|
||||
{% for s in screens.screen %}
|
||||
<figure class="shot n-md">
|
||||
<img src="/screens/{{ s.file }}" width="480" height="270" alt="{{ s.alt }}" loading="lazy">
|
||||
<figcaption>{{ s.caption }}</figcaption>
|
||||
</figure>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="wrap" id="updates" aria-labelledby="updates-title">
|
||||
<div class="updates n-lg split">
|
||||
<div>
|
||||
<span class="eyebrow">Updates</span>
|
||||
<h2 id="updates-title">Update over Wi-Fi. It rolls back if it fails.</h2>
|
||||
<p class="lead">Install a new release from the project's server in Settings, from the SD card, or push one from your computer. Every update is signed: the device checks the signature before it writes anything, restarts, and runs the new firmware on Probation. If it crashes, or can't reconnect Wi-Fi, it goes back to the previous firmware and says so.</p>
|
||||
<div class="terminal n-md"><span class="p">$</span> scripts/flash.sh --ota 10.39.39.12</div>
|
||||
</div>
|
||||
<div class="aside">
|
||||
<div class="num-row"><span class="big c1">60 s</span><p>The longest the device waits for you to stop typing before it restarts into an update.</p></div>
|
||||
<div class="num-row"><span class="big c2">3 min</span><p>How long new firmware has to reconnect Wi-Fi on Probation before it rolls back.</p></div>
|
||||
<div class="num-row"><span class="big c2">3</span><p>Crash restarts in a row, and it starts in Safe Mode: only Wi-Fi, updates and the Debug Console, so a fix can still be pushed.</p></div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="wrap split" id="build" aria-labelledby="build-title">
|
||||
<div>
|
||||
<span class="eyebrow">Build and flash</span>
|
||||
<h2 id="build-title">Only Docker needed.</h2>
|
||||
<p class="lead">PlatformIO and the ESP32 toolchain run in a container. The first build downloads about 1 GB; after that, builds take under a minute.</p>
|
||||
<p class="muted">If the upload can't connect, hold G0, the button next to the screen, while plugging in USB, then retry.</p>
|
||||
<p>Prefer a ready-made build? <a class="accent-link" href="/install/">Install it from your browser</a>, or take the factory image and the signed update file from <a class="accent-link" href="/downloads/">the downloads</a>.</p>
|
||||
</div>
|
||||
<div class="blocks n-lg">
|
||||
<div class="c"># run the tests, then build</div>
|
||||
<div><span class="p">$</span> scripts/ci.sh</div>
|
||||
<div class="gap"></div>
|
||||
<div class="c"># flash over USB-C</div>
|
||||
<div><span class="p">$</span> scripts/flash.sh</div>
|
||||
<div class="gap"></div>
|
||||
<div class="c"># or update over Wi-Fi</div>
|
||||
<div><span class="p">$</span> scripts/flash.sh --ota 10.39.39.12</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="wrap" aria-labelledby="cta-title">
|
||||
<div class="cta n-lg">
|
||||
<div>
|
||||
<h2 id="cta-title">Open source, GPL-3.0.</h2>
|
||||
<p>Read the code, flash it, send a patch.</p>
|
||||
<div class="actions">
|
||||
<a class="btn n-md" href="{{ config.extra.repo }}">Source</a>
|
||||
<a class="btn n-md" href="{{ config.extra.repo }}/releases">Releases</a>
|
||||
<a class="btn n-md" href="{{ config.extra.repo }}/issues">Issues</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="nine" aria-hidden="true">{% for v in px %}<i{% if v == 1 %} class="on"{% endif %}></i>{% endfor %}</div>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock main %}
|
||||
@@ -0,0 +1,37 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}Install roro9stack: flash a Cardputer ADV from your browser{% endblock %}
|
||||
{% block description %}{{ page.description }}{% endblock %}
|
||||
{% block head %}
|
||||
<script type="module" src="/vendor/esp-web-tools/install-button.js"></script>
|
||||
<script src="/js/install.js" defer></script>
|
||||
{% endblock %}
|
||||
{% block main %}
|
||||
<div class="wrap page">
|
||||
<header>
|
||||
<span class="eyebrow">Install</span>
|
||||
<h1>{{ page.title }}</h1>
|
||||
<p class="lead">{{ page.description }}</p>
|
||||
</header>
|
||||
|
||||
<div class="flasher n-lg" id="flasher" data-api="{{ config.extra.api }}" data-releases-path="/twisla/roro9stack/releases/download/">
|
||||
<h2>Install from your browser</h2>
|
||||
<p id="flasher-status" class="muted" role="status">Looking for the latest release…</p>
|
||||
<dl id="flasher-info" hidden>
|
||||
<dt>Version</dt><dd id="f-version"></dd>
|
||||
<dt>Size</dt><dd id="f-size"></dd>
|
||||
<dt>SHA-256</dt><dd id="f-sha" class="hash"></dd>
|
||||
<dt>Release</dt><dd><a class="accent-link" id="f-link" href="{{ config.extra.repo }}/releases">release notes</a></dd>
|
||||
</dl>
|
||||
<esp-web-install-button id="f-button" hidden>
|
||||
<button slot="activate" class="btn btn-primary n-md" type="button">Install roro9stack</button>
|
||||
<span slot="unsupported" class="msg">This browser can't flash a device. Use Chrome or Edge on a desktop computer, or follow the esptool steps below.</span>
|
||||
<span slot="not-allowed" class="msg">Flashing needs a secure (https) page.</span>
|
||||
</esp-web-install-button>
|
||||
<noscript><p class="msg">Flashing from this page needs JavaScript. Use the esptool steps below.</p></noscript>
|
||||
</div>
|
||||
|
||||
<div class="prose">
|
||||
{{ page.content | safe }}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock main %}
|
||||
Reference in New Issue
Block a user