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
57 changes: 54 additions & 3 deletions .github/ISSUE_TEMPLATE/bug.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,61 @@ body:
validations:
required: true
- type: input
id: hardware
id: profile
attributes:
label: Hardware and platform
description: Exact board/profile and iPadOS, Android, browser, or desktop version
label: Exact installer profile ID
description: For installer problems, use the exact profile ID shown on /flash; otherwise enter "Not installer-related."
validations:
required: true
- type: input
id: module
attributes:
label: Exact board model and module marking
description: Copy the board model and the text printed on the module; enter "Not applicable" only when no board is involved.
validations:
required: true
- type: input
id: memory
attributes:
label: Memory configuration
description: Flash capacity, PSRAM capacity, and PSRAM type (Octal, Quad, none, or unknown).
validations:
required: true
- type: input
id: browser
attributes:
label: Browser
description: Browser name and exact version; enter "Not applicable" when the browser is not involved.
validations:
required: true
- type: input
id: operating_system
attributes:
label: Desktop operating system
description: Desktop operating system and exact version; enter "Not applicable" when the installer is not involved.
validations:
required: true
- type: input
id: installer_stage
attributes:
label: Failed installer stage
description: For example page load, browser check, profile selection, artifact verification, port selection, erase, write, verification, first boot, or recovery; otherwise enter "Not installer-related."
validations:
required: true
- type: input
id: tablet
attributes:
label: Exact tablet or device model
description: Name the iPad or Android device used with the app; enter "Not applicable" only when the app is not involved.
validations:
required: true
- type: input
id: tablet_os
attributes:
label: Tablet operating system
description: Tablet operating system and exact version (iPadOS or Android); enter "Not applicable" only when the app is not involved.
validations:
required: true
- type: textarea
id: steps
attributes:
Expand Down
36 changes: 20 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ compatible microcontroller board over Bluetooth Low Energy. Its normal
workflow needs no USB serial connection, Wi-Fi onboarding, cloud account, or
telemetry.

- Website and browser installer: [pyble.dev](https://pyble.dev)
- Website and browser-installer status: [pyble.dev](https://pyble.dev)
- iPad external beta:
[join with TestFlight](https://testflight.apple.com/join/yU4e8s6d)
- License: [MIT](LICENSE)
Expand All @@ -27,7 +27,7 @@ telemetry.
</p>

<p align="center">
<em>Actual PyBLE app: a GPIO 48 NeoPixel Blocks program and its generated MicroPython on the pictured ESP32-S3 board.</em>
<em>Actual PyBLE app in landscape: GPIO 48 NeoPixel Blocks beside the generated MicroPython.</em>
</p>

## What works
Expand Down Expand Up @@ -62,16 +62,17 @@ BLE GATT peripheral. It supports:
- board naming and identify support; and
- upstream MicroPython’s standard `neopixel` module.

The public browser installer currently offers qualified images for:
The public browser installer is currently unavailable pending v0.4.2 HIL on
both exact current profiles:

| Installer profile | Typical target | Availability |
| ----------------- | ---------------------------------- | ------------------------------------------- |
| `esp32-4mb` | Classic ESP32, 4 MB flash | Available |
| `esp32-s3-n16r8` | ESP32-S3, 16 MB flash / 8 MB PSRAM | Available |
| `esp32-c3-4mb` | ESP32-C3, 4 MB flash | Source target; public installer pending HIL |
| Installer profile | Exact target constraint | Public status |
| ----------------- | ------------------------------------------------------------ | --------------------------------------------------- |
| `esp32-4mb` | Classic ESP32, 4 MiB external SPI flash; no PSRAM assumed | v0.4.2 HIL pending; installer unavailable |
| `esp32-s3-n16r8` | ESP32-S3, 16 MiB flash / 8 MiB Octal PSRAM; N16R8-class only | v0.4.2 HIL pending; installer unavailable |
| `esp32-c3-4mb` | ESP32-C3, 4 MiB external SPI flash; no PSRAM assumed | Planned; no public image; exact-profile HIL pending |

These are the initial validated ports, not a chip-family allowlist. A future
board is compatible when it has a maintained PyBLE agent port, BLE GATT
These are the initial port targets, not a chip-family allowlist. A future board
is compatible when it has a maintained PyBLE agent port, BLE GATT
peripheral support, adequate resources, PBLE/1 conformance, recovery testing,
and hardware-validation evidence. Stock MicroPython plus generic Bluetooth
hardware is not sufficient by itself.
Expand Down Expand Up @@ -102,14 +103,17 @@ shared conformance corpus, documentation, and CI atomically.

## Try PyBLE

1. Open [pyble.dev/flash](https://pyble.dev/flash) in desktop Chrome or Edge.
2. Select the exact supported profile for your board and flash the qualified
agent firmware. Flashing erases the board; review the installer warning and
back up files first.
3. Install the iPad beta from
1. Install the iPad beta from
[TestFlight](https://testflight.apple.com/join/yU4e8s6d), or build the
Flutter app locally.
4. Open PyBLE, scan for the board, connect, and run an example.
2. Check [pyble.dev/flash](https://pyble.dev/flash) in desktop Chrome or Edge.
The public installer is currently unavailable pending v0.4.2 HIL. Wait for
that page to show an active release version, your exact profile, and an
enabled install action.
3. Only after that gate opens, back up the board, confirm its exact memory
profile, and use the one-time wired installer. Flashing erases the board.
4. Open PyBLE, scan for the provisioned board, connect, and run an example over
BLE.

See [support and troubleshooting](https://pyble.dev/support) for browser,
Bluetooth, and recovery requirements.
Expand Down
34 changes: 34 additions & 0 deletions docs/specifications/website.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,40 @@ platform cropping can make it unreliable. A separate local square TestFlight
card MAY include the exact invitation QR, visible destination, and plain-text
instructions.

### 3.4 Pre-activation release claims and support intake

Until the first v0.4.2 public firmware selector passes the complete gate in §7,
the repository README and home page MUST say that the browser installer is
unavailable pending v0.4.2 HIL on both exact current profiles. They MUST NOT
describe either profile or its browser image as released, available, or
qualified. The home-page target cards MUST identify the profiles and constraints
as `esp32-4mb` (classic ESP32, 4 MiB external SPI flash, no PSRAM assumed) and
`esp32-s3-n16r8` (ESP32-S3, 16 MiB flash, 8 MiB **Octal** PSRAM), and give each
the truthful pending-HIL/unavailable state. `esp32-c3-4mb` remains a separate
planned, unavailable profile.

README getting-started instructions MUST gate destructive flashing on `/flash`
showing an active version, exact profile, and enabled install action. While the
installer is unavailable, they MUST NOT instruct a reader to select or flash a
supposedly qualified public image. A real-app capture caption MUST describe only
what is visible; it MUST NOT claim that a physical board is pictured when the
capture shows only the app.

While the installer is unavailable, the wide social card MUST describe the
workflow as one-time USB setup followed by everyday use over BLE. It MUST NOT
advertise an enabled web flasher; it MAY explicitly label firmware HIL as
pending. Its mechanically rendered PNG and authored SVG MUST remain paired by
reviewed content and exact-dimension tests.

The support route MUST link directly to the preferred GitHub bug template at
`https://github.com/PyBLE-dev/PyBLE/issues/new?template=bug.yml`. Installer
intake MUST request the exact profile ID, board/model and module marking, flash
capacity, PSRAM capacity and type, browser name/version, desktop operating
system/version, failed installer stage, redacted error text, exact tablet/device
model, and tablet operating system/version (iPadOS or Android). The issue
template and support-page checklist MUST agree on those fields and remind users
to remove secrets and personal identifiers.

## 4. Brand and visual contract

The canonical prompt-chip SVG in `app/assets/branding/` is the source asset. A
Expand Down
88 changes: 88 additions & 0 deletions tests/publication/test_public_claims.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# SPDX-License-Identifier: MIT
# Part of PyBLE (https://pyble.dev) — see /LICENSE.

from __future__ import annotations

import unittest
from pathlib import Path

REPO_ROOT = Path(__file__).resolve().parents[2]


def markdown_section(document: str, heading: str) -> str:
marker = f"## {heading}\n"
start = document.index(marker) + len(marker)
end = document.find("\n## ", start)
return document[start:] if end < 0 else document[start:end]


class PublicClaimsTest(unittest.TestCase):
@classmethod
def setUpClass(cls) -> None:
cls.readme = (REPO_ROOT / "README.md").read_text(encoding="utf-8")
cls.bug_template = (
REPO_ROOT / ".github" / "ISSUE_TEMPLATE" / "bug.yml"
).read_text(encoding="utf-8")

def test_readme_is_truthful_before_v042_hil_completes(self) -> None:
firmware = markdown_section(self.readme, "What works")

self.assertIn(
"public browser installer is currently unavailable pending v0.4.2 HIL",
firmware,
)
self.assertIn("`esp32-4mb`", firmware)
self.assertIn("Classic ESP32, 4 MiB external SPI flash", firmware)
self.assertIn("`esp32-s3-n16r8`", firmware)
self.assertIn("16 MiB flash / 8 MiB Octal PSRAM", firmware)
self.assertNotIn("currently offers qualified images", firmware)
self.assertNotIn("| Available", firmware)

def test_readme_caption_describes_only_the_visible_app(self) -> None:
caption_start = self.readme.index("<em>Actual PyBLE app")
caption_end = self.readme.index("</em>", caption_start)
caption = self.readme[caption_start:caption_end]

self.assertIn("in landscape", caption)
self.assertIn("GPIO 48 NeoPixel Blocks", caption)
self.assertIn("generated MicroPython", caption)
self.assertNotRegex(caption, r"(?i)pictured|board|module")

def test_readme_try_steps_are_gated_on_an_active_installer(self) -> None:
try_section = markdown_section(self.readme, "Try PyBLE")

self.assertIn("currently unavailable pending v0.4.2 HIL", try_section)
self.assertIn("active release version", try_section)
self.assertIn("enabled install action", try_section)
self.assertNotRegex(try_section, r"(?is)select .*qualified\s+agent firmware")

def test_bug_template_collects_the_exact_installer_diagnostics(self) -> None:
for field_id in (
"profile",
"module",
"memory",
"browser",
"operating_system",
"installer_stage",
"tablet",
"tablet_os",
):
self.assertIn(f" id: {field_id}\n", self.bug_template)

for wording in (
"Exact installer profile ID",
"Exact board model and module marking",
"Flash capacity, PSRAM capacity, and PSRAM type",
"Browser name and exact version",
"Desktop operating system and exact version",
"Failed installer stage",
"Exact tablet or device model",
"Tablet operating system and exact version",
):
self.assertIn(wording, self.bug_template)

self.assertRegex(self.bug_template, r"(?i)remove.*(?:secret|credential)")


if __name__ == "__main__":
unittest.main()
Binary file modified tools/web/public/social/pyble-beta-og-1200x630.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 7 additions & 6 deletions tools/web/public/social/pyble-beta-og-1200x630.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
26 changes: 23 additions & 3 deletions tools/web/src/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -999,7 +999,7 @@ h3 {
.target-grid > div {
display: flex;
min-width: 0;
min-height: 190px;
min-height: 275px;
padding: 25px 17px;
align-items: center;
justify-content: flex-end;
Expand Down Expand Up @@ -1062,19 +1062,39 @@ h3 {

.target-grid strong {
color: var(--white);
font-size: 0.95rem;
font-family: "SFMono-Regular", Consolas, "Liberation Mono", monospace;
font-size: 0.78rem;
}

.target-grid small {
color: #899db6;
font-size: 0.68rem;
}

.target-grid__target-name {
margin-top: 6px;
color: #dceaff;
font-size: 0.78rem;
font-weight: 700;
}

.target-grid__constraint {
margin-top: 10px;
line-height: 1.45;
}

.target-grid .target-grid__status {
margin-top: 9px;
color: var(--warning);
font-weight: 700;
line-height: 1.4;
}

.target-grid > .target-grid__target--planned {
border-style: dashed;
}

.target-grid__target--planned small {
.target-grid__target--planned .target-grid__status {
color: var(--warning);
}

Expand Down
Loading
Loading