PR #105 added a "Refresh moving-ref package downloads" step that drops
*-{HEAD,master,main,dist}.tar.gz before each build so buildroot re-fetches
the current ref. That fixed the majestic-webui-dist.tar.gz staleness (the
*-dist.tar.gz case), but it never matched majestic itself.
majestic downloads from S3 as majestic.<family>.<variant>.master.tar.bz2
(majestic.mk) — a DOT separator, .tar.bz2, and an S3 host the old pattern
deliberately spared as "content-addressed". But that S3 object is a moving
`master` build, re-uploaded on every majestic CI run, with no .hash to force
a refetch. So it stayed frozen in the monthly builder-dl cache while the
webui refreshed — the exact desync that shipped a pre-x-groups majestic in a
t31 lite image against the current webui, blanking the settings page with
"No settings groups in schema."
Broaden the eviction to a regex covering both -ref and .ref separators across
gz/bz2/xz, so majestic.*.master.tar.bz2 and majestic-webui-dist.tar.gz are
both dropped while semver/SHA-pinned tarballs keep their cache.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Dahua-OEM 2 MP camera. The SC223A is on hardware I2C0 routed to the
VI-RAW pads (0x112c0060/0x64 func2) rather than the default mipi-sensor
pads (0x30/0x34 func1) the SDK probes, and needs a 27 MHz MCLK
(0x120100f0=0x19; SDK default 0x11/div4 leaves it unresponsive).
S71devmem re-muxes the pads, selects the SENSOR_RSTN reset (GPIO8_7
func2) and fixes MCLK after S70vendor/load_goke; the stock
open_sensor_i2c then reaches the sensor over /dev/i2c-0. No kernel
module needed.
Verified on hardware: chip-id 0xcb3e over HW I2C0, MIPI lock 1920x1080
zero errors, majestic streams correct colour 1080p.
Co-authored-by: build <build@local>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Document the builder overlay model (per-device deltas layered onto a
fresh OpenIPC/firmware clone), the build flow in builder.sh, device
directory anatomy, the package/ and devices/common/ conventions, the CI
workflows (including the moving-ref cache caveat), and a step-by-step
"Adding a new device" walkthrough.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
builder caches BR2_DL_DIR (/tmp/builder-dl) under a monthly key (date +%m), and
actions/cache only saves on a key miss, so the first build of the month freezes it for
weeks. Packages pinned to a moving ref (VERSION = HEAD/branch, or the majestic-webui
`dist` release asset) download as a constant <pkg>-<ref>.tar.gz, so buildroot keeps
reusing the stale tarball — defeating the clean firmware-HEAD re-clone builder does for
freshness.
Empirically, a 2026-06-14 builder nightly shipped a ~2-week-old majestic-webui (full
bootstrap.min.css, no mj-settings.js) while the firmware nightly of the same day shipped
the current slim UI. Drop *-{HEAD,master,main,dist}.tar.gz before each build (master.yml
+ build-one.yml) so buildroot re-fetches the current ref. Mirrors the same fix in
OpenIPC/firmware.
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* Add TP-Link Kasa KC110 (HI3518EV200 + OV2735)
New device variant: hi3518ev200_ultimate_tplink-kasa-kc110.
Contents:
- kc110-board-support package: PTZ binary (mmap'd PL061), gpio-motors
shim for the openipc web GUI, IR control script, and a boot-time
pinmux init that writes 0x1 to the chip7/chip8 pads (0x200f00e8/ec/
f0/fc/100/104). The OpenIPC kernel pinctrl driver does not auto-set
these for the KC110's pan motor / IR-cut / IR-LED pads — the vendor
firmware writes them explicitly in hn_config.sh. Without this,
PL061 GPIODATA writes succeed silently but the pads never drive
the ULN2803 / solenoid / LED.
- Device overlay:
* etc/sensors/ov2735_i2c_1080p.ini — input_mode=0 (MIPI), 1-lane
(was input_mode=4 / 4-lane in upstream cv200 osdrv, which is
wrong for OV2735).
* usr/bin/load_hisilicon — adds an "ov2735" case that mirrors
ov2710_mipi (same MIPI pinmux + clock).
* usr/share/openipc/customizer.sh — first-boot configuration:
RTL8188FU wlandev, sensor config path, IR-cut pin 64, backlight
pin 63, lightMonitor true (AE recovery after IR-cut toggle).
- README.md row.
Hardware notes:
- SoC: Hi3518EV200; Sensor: OV2735 (MIPI 1-lane, RAW10);
Wi-Fi: RTL8188FU (USB); Flash: W25Q128 16 MB NOR.
- PTZ: 4-phase unipolar steppers via ULN2803 darlington array.
Pan = vendor base-10 GPIO 81/74/73/72 = OpenIPC PL061 65/60/59/58;
tilt = 0/1/2/44 = 0/1/2/36 (vendor's "44" is bank 4 pin 4 — i.e.
pin 36 in OpenIPC's PL061 numbering, NOT 44).
- IR-cut filter: GPIO 64 (vendor 80) active-low; IR-LED: GPIO 63
(vendor 77).
Status: PTZ + GPIO + IR fully functional. Video pipeline currently
needs an LD_PRELOAD shim around HI_MIPI_SET_DEV_ATTR because upstream
majestic builds combo_dev_attr_t with enIntfMode=DC / raw_data_type=0
for MIPI sensors that need RAW10. The shim is downstream-only pending
a real fix to majestic upstream; this PR delivers the rest of the
KC110 board support and will produce a fully-functional camera once
the majestic fix lands.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* tplink-kasa-kc110: use ov2735_mipi_1080p.ini
Follow OpenIPC/firmware #2155, which ships the OV2735 MIPI config as a
separate ov2735_mipi_1080p.ini and leaves ov2735_i2c_1080p.ini (the
parallel/DVP config) untouched.
- Rename the device's bundled sensor .ini to ov2735_mipi_1080p.ini and
carry the full corrected MIPI config: input_mode=0 (MIPI),
data_type=2 (RAW10), lane_id 2-lane, Input_mod=5 (MIPI),
Mask_0=0xFFF00000. The previous bundled copy only had input_mode
fixed (data_type/Input_mod/Mask_0 were still the wrong DVP defaults).
- Point customizer.sh's isp.sensorConfig at the new file.
- Exclude the unused parallel ov2735_i2c_1080p.ini from the image.
Verified on a TP-Link Kasa KC110 (OV2735 2-lane MIPI RAW10): clean
image, correct AE, RTSP H.264 1080p.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: moontwister <moontwister@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Mirror of OpenIPC/firmware#2169. PR #102 extended enrich_manifest.py
with SIZES_COMPOUND_RE + SIZES_SIMPLE_RE + parse_sizes() so the public
manifest carried `platforms[<plat>].sizes.url` and `@sizes` lines
pointing at each release's sizes.json asset. The intent was a
browser-side consumer; the GitHub release-asset URLs return no
Access-Control-Allow-Origin header, so every browser fetch was blocked
by CORS.
This commit reverts only the manifest-indexing half of #102. The
release-asset upload itself stays in master.yml — server-side consumers
can still read the JSON via `gh release download` or any CI workflow.
Only the in-manifest URL is removed, because that URL only made sense
for an in-browser reader that turns out can't reach it.
The resulting enrich_manifest.py is bit-for-bit identical to the
pre-#102 state.
Co-authored-by: John <zekccsripfwrocp-15689@yn.furvionx.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Firmware PR #2166 added a `size-report` Makefile target + the
`general/scripts/size_report.py` analyser that emits a per-package /
per-kernel-module `sizes.<soc>-<variant>.json` next to the .tgz. Builder
clones firmware fresh on every run and shells to `make BOARD=...`, so the
script + target are already available downstream — this PR adds the CI wiring
and manifest extension so the JSON ships as a release asset and surfaces
through https://openipc.github.io/builder/manifest.json.
Three changes:
* `builder.sh`: best-effort `make size-report` after the main build, and
copy `sizes.*.json` into the timestamped archive next to `.tgz` /
`rootfs.squashfs.*`. Failure is swallowed so a legacy `OPENIPC_FW_REV` pin
to a pre-#2166 firmware ref can't sink the build.
* `.github/workflows/master.yml`: new `Build size report` step after the
existing `Build firmware`. Mirrors the bimodal rename trick the .tgz path
already does — compound matrix entries (`<soc>_<variant>_<vendor>-<model>`)
share `<soc>_<variant>` and would otherwise collide on the firmware-side
`sizes.<soc>-<variant>.json` filename when uploaded to the same release
tag. Compound entries get the JSON renamed to `sizes.<NAME>.json`; simple
entries keep `sizes.<soc>-<variant>.json`. `${{env.SIZES}}` is added to
all three upload `files:` blocks (dated / nightly / latest).
* `.github/scripts/enrich_manifest.py`: `SIZES_COMPOUND_RE` +
`SIZES_SIMPLE_RE`, a `parse_sizes()` helper that tries compound first
(because `[^._-]+` in the simple soc class would otherwise accept the
compound form too), the sidecar is recorded at the platform level under
a `"sizes"` key in `manifest.json`, and a new `@sizes <build_id>
<platform> <size> <url>` line type is emitted in `manifest.flat`
alongside the existing `@channel` directive.
Schema is unchanged for existing consumers — on-device sysupgrade reads
positional 4-column `manifest.flat` lines and stays inert on unknown
`@`-prefixed directives.
Co-authored-by: John <zekccsripfwrocp-15689@yn.furvionx.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
OpenIPC/firmware PR #2162 (merged 2026-05-30) dropped the legacy
BR2_TOOLCHAIN_EXTERNAL_GCC_8=y + BR2_GCC_VERSION_LEGACY=y block from
its 18 ARM defconfigs and ships gcc 13.3.0 to those platforms now.
OpenIPC/builder's per-device and common Goke defconfigs still pinned
gcc 8, so every Goke job in the 2026-06-02 nightly Build run failed at
the toolchain stage with:
Incorrect selection of gcc version: expected 8.x, got 13.3.0
make[1]: *** [toolchain-external-custom/.stamp_configured] Error 1
Mirror #2162 across the 24 affected defconfigs (8 common + 16
per-device). No other defconfig content changes.
Unrelated 4 Sigmastar rootfs-overflow failures in the same nightly are
tracked separately.
Fixes failing run: https://github.com/OpenIPC/builder/actions/runs/26804792607
Co-authored-by: John <zekccsripfwrocp-15689@yn.furvionx.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Mirrors OpenIPC/firmware#2133. Today's scheduled cleanup run failed
with HTTP 403: Resource not accessible by integration when dispatching
manifest.yml — the prune job only declared contents:write. The refresh
step also fires on the empty-prune path (exit 0 only exits the step,
not the job), which is wasted work since manifest.yml is already kept
fresh by master.yml's workflow_run.
Add actions:write to the permissions block, and gate the refresh on a
step output so it runs only after an actual deletion.
Co-authored-by: John <zekccsripfwrocp-15689@yn.furvionx.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The preflight gate skipped scheduled runs whenever this repo's HEAD
matched the SHA recorded in the previous nightly release body. That key
ignores every upstream input the build actually depends on:
OpenIPC/firmware HEAD, the toolchain release, the kernel tarball, the
vendor osdrv packages, and individual buildroot package upstreams.
builder.sh re-clones OpenIPC/firmware fresh on every run, so those
inputs do move between nightlies even when this repo is quiet.
The concrete failure mode this caused: on 2026-05-26 OpenIPC/firmware
bumped its hisilicon-opensdk pin to b958db4, which contains the V4
mipi_rx vsync IRQ fix (openhisilicon PR #195, closes#194 — torn
frames / 'Timeout from venc channel 0' on hi3516ev200, gk7205v200,
gk7205v210, gk7205v300, hi3516ev300 hardware). The next builder
nightly (2026-05-27 07:00 UTC) ran with this repo unchanged since
2026-05-25, so preflight skipped the entire Firmware matrix. Field
cameras (verified: gk7205v210_lite_vixand-ivg-g4h at IP redacted,
running BUILD_ID nightly-20260526-ee40da8, srcversion
8EF923DF907C1F7F3C6898E — the pre-fix open_mipi_rx fingerprint from
PR #195's verification table) kept pulling the stale 'latest' release
asset and kept showing the regression after sysupgrade.
A cross-input dedupe key would be possible (firmware HEAD + toolchain
URL hash + ...) but enumerates every upstream this build pulls. Simpler
and correct: scheduled nightlies always build. workflow_dispatch is
unaffected — that path was already unconditional.
Co-authored-by: Dmitry Ilyin <zekccsripfwrocp-15689@yn.furvionx.com>
OpenIPC/firmware/.github/workflows/build.yml has a `output/dl` cache
step that mirrors the Buildroot download directory across runs. This
saves both runner-minutes and — importantly — masks transient upstream
mirror failures: once any cron has cached the canonical tarball for a
package, every subsequent run reuses it regardless of whether the
primary URL is healthy that day.
builder's master.yml + build-one.yml had no equivalent. Each cron run
fully re-downloads the entire dl/ tree, so every package's upstream
must be healthy in every run. Hit hard 2026-05-24 by
wireguard-tools-1.0.20210914.tar.xz: it's served from a cgit
`snapshot/` URL on git.zx2c4.com which regenerates the tarball
per-request with shifting sha256, and `sources.buildroot.net` also
served drifted bytes — see kaeru
'wireguard-zx2c4-tarball-regeneration-hash-drift'. Result: random
matrix entries fail with hash mismatches each cron, with selection
roulette-style (93 boards enable wireguard; on any given run a few
roll snake-eyes on the download).
Mirror firmware's pattern with one wrinkle: builder.sh does
`rm -rf openipc` at the top of every run to force a clean re-clone of
firmware, which would obliterate `openipc/output/dl/` if we cached the
default path. Cache `/tmp/builder-dl` instead (outside the openipc
tree) and point Buildroot at it via BR2_DL_DIR env var, honored per
Buildroot manual section "Location of source tarballs". Same restore +
save lifecycle as firmware's, same monthly key shape.
Effect:
- First cron after merge: full download as usual; warms /tmp/builder-dl.
- Subsequent crons: instant cache hit for unchanged packages; only
re-download for version bumps. Removes the wireguard-roulette class
of failure entirely once cache is populated.
build-one.yml gets the same treatment so single-board dispatches share
the cache pool.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
OpenIPC/firmware PR #2090 (2026-05-13, "IMX335 companion to
openhisilicon #99") renamed `general/package/goke-osdrv-gk7205v200/
files/sensor/config/60fps/` to `high-fps/`. The xg521 device-specific
goke-osdrv-gk7205v200.mk override here still references the old path,
causing every cron build of gk7202v300_lite_xg521 to fail:
/usr/bin/install: cannot stat 'general/package/goke-osdrv-gk7205v200//
files/sensor/config/60fps/*.ini': No such file or directory
Update the two install lines to use `high-fps` matching the renamed
upstream directory. Target install path also moves from
/etc/sensors/60fps → /etc/sensors/high-fps for consistency with what
the rest of the V4 sensor presets use.
The matching exclude-list entry in
devices/gk7202v300_lite_xg521/general/scripts/excludes/gk7202v300_lite.list
references /etc/sensors/60fps/... and is now stale; left as-is since
`rm -f` on a non-existent path is a no-op (rootfs_script.sh:
xargs -I % rm -f ${TARGET_DIR}%). Can be cleaned in a follow-up.
Other Goke device overlays (gk7205v200_lite_*, gk7205v210_lite_*,
gk7205v300_lite_*) only have the same exclude-list staleness and no
broken install rule, so they aren't affected by this bug.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
When investigating a regression that lives in OpenIPC/firmware (or its
referenced packages like hisilicon-opensdk) but only manifests in a
builder build, you previously had to either roll firmware master back
or fork builder.sh manually. Adds a clean way:
gh workflow run build-one.yml -f platform=hi3516ev200_fpv \
-f firmware_ref=<sha-or-tag-or-branch>
The workflow exports OPENIPC_FW_REV as env, which builder.sh now
honours: when set, it does a full clone (not --depth=1) and checks
out the requested ref before invoking the firmware build. When unset,
behaviour is unchanged (shallow clone of master HEAD).
Immediate motivation: bisect the +180 KB rootfs growth in
hi3516ev200_fpv between 2026-05-08 (5016 KB) and 2026-05-24 (5196 KB,
76 KB over partition limit). Per kaeru
'hi3516ev200-ev300-fpv-rootfs-size-overflow-2026-05-24'. Suspect is
recent hisilicon-opensdk bumps that brought in source-built sensor
drivers for ev200; bisect across firmware commits varying
HISILICON_OPENSDK_VERSION will pinpoint which bump pushed us past
the 5120 KB limit.
Generally useful long after this specific incident:
- Reproducing user-reported regressions at a specific firmware tag.
- Locking builds to a known-good firmware revision when builder is
ahead of firmware.
- Future cross-repo bisects of any kind.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
PR-Bld-C in the mirror of OpenIPC/firmware's nightly redesign.
Mirrors OpenIPC/firmware#2116. Adds an optional `commit` workflow_-
dispatch input to `build-one.yml` so:
- `gh workflow run build-one.yml -f platform=X -f commit=<sha>`
builds the given SHA from `git bisect run`.
- Output goes to `nightly-bisect-<short>` (prerelease) tag, distinct
from the dated `nightly-YYYYMMDD-<short>` namespace produced by
PR-Bld-A's master.yml. The manifest aggregator's regex only
indexes `^nightly-[0-9]{8}-[0-9a-f]{7}$`, so one-off bisect
rebuilds never enter manifest.{json,flat}.
- Without `commit`, falls back to building HEAD and tags as
`nightly-bisect-<short>-<UTC ts>` so repeated dispatches of the
same HEAD don't collide.
Also:
- Retry budget around `bash builder.sh` matching master.yml +
OpenIPC/firmware#2129's hardening.
- BUILD_ID / BUILD_SHA / BUILD_PLATFORM env at the Build firmware
step level (forward-compat with Phase 3 of the mirror plan).
- Drops dead `env: TAG_NAME: latest` at workflow level.
- Release body carries sha/short/platform/one_off=true for the
aggregator's downstream consumers to easily distinguish bisect
builds from dated nightlies.
See ~/.claude/plans/mirror-nightly-redesign-to-builder.md.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
PR-Bld-B in the mirror of OpenIPC/firmware's nightly redesign. Adds
the two workflows that turn dated nightly-* releases (from PR-Bld-A)
into a queryable index served via GitHub Pages.
`manifest.yml` — triggers on successful `Build` workflow_run completion
(or manual dispatch). Runs `.github/scripts/enrich_manifest.py` to
enumerate dated releases, keep the 90 newest, parse the body
(sha/short/built_at from PR-Bld-A) and asset list, then emit:
- `manifest.json` — rich JSON for hosts/agents/CI.
- `manifest.flat` — whitespace-delimited columns
`build_id platform flash size url` plus `@channel` records,
parseable by pure busybox `awk` (no jq, no jsonfilter) for
on-device sysupgrade in Phase 2.
Both committed to the `gh-pages` branch (already bootstrapped).
`cleanup.yml` — Mondays 06:00 UTC (offset by +1h from firmware's
05:00 UTC cleanup). Deletes dated nightly releases beyond the 90
newest via `gh release delete --cleanup-tag`, then re-triggers
manifest.yml.
`enrich_manifest.py` differs from firmware's only in the asset parser:
builder produces TWO filename forms (per `master.yml`'s COMMON awk
trick that counts underscores in the matrix entry name):
- **Compound** `<soc>_<variant>_<vendor>-<model>-nor.tgz` →
platform key = the full string. This is the common case
(per-device builds).
- **Simple** `openipc.<soc>-<flash>-<variant>.tgz` (firmware-style,
when matrix name has one underscore) → platform key =
`<soc>_<variant>`.
The platform key gets carried into manifest.flat as a single column.
sysupgrade's existing `awk '$2==p'` lookup works unchanged; Phase 3
(model-aware BUILD_PLATFORM field in os-release) will let cameras
filter against the full per-model key.
URLs after merge:
https://openipc.github.io/builder/manifest.jsonhttps://openipc.github.io/builder/manifest.flat
(`gh-pages` branch + Pages config already set up; mirrors firmware-
side setup.)
Concurrency-grouped `gh-pages-manifest` so manifest.yml and
cleanup.yml can't race the gh-pages push.
Includes the retry hardening from firmware OpenIPC/firmware#2129
(4-attempt backoff for transient GitHub API 401/5xx, fast-fail on
permanent 404).
First-run behaviour validated locally: empty index emits explicit
placeholder. Parser unit-tested across 7 synthetic filenames covering
compound, simple, and edge cases.
See ~/.claude/plans/mirror-nightly-redesign-to-builder.md.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Mirror the OpenIPC/firmware nightly redesign (PRs #2111-#2129) to
builder. This commit is PR-Bld-A: the build.yml-side changes only.
Subsequent PRs add the manifest aggregator + retention sweep
(PR-Bld-B) and the build-one.yml commit input (PR-Bld-C).
Changes:
1. **Preflight SHA gate**. New `preflight` job reads `sha=` from the
rolling `nightly` release body. On schedule events it short-
circuits the matrix when HEAD matches the previously-published
SHA; PRs and workflow_dispatch always build. Mirrors firmware's
#2111.
2. **Three release tags per build**:
- `nightly-YYYYMMDD-<short>` (prerelease, content-addressable,
the unit of history — 90-day retention via PR-Bld-B's
cleanup.yml).
- `nightly` (rolling channel pointer; body carries sha/short/
built_at).
- `latest` (legacy alias, kept one cycle so existing sysupgrade
clients hitting the old hardcoded URL don't break).
3. **`BUILD_ID` / `BUILD_SHA` / `BUILD_PLATFORM` env** exported at
the job level from preflight outputs. firmware's rootfs_script.sh
(which builder.sh reuses via its clone of firmware) picks BUILD_ID
+ BUILD_SHA into /etc/os-release automatically. BUILD_PLATFORM is
set to the matrix entry (e.g. `gk7205v200_fpv_caddx-fly`) and is
forward-compatible with Phase 3 of the mirror plan (the firmware-
side rootfs_script.sh update that emits BUILD_PLATFORM as an
os-release field).
4. **Retry budget** around `bash builder.sh`. Matches firmware's
`30 60 120 300 600 1200` ladder, covering toolchain/CDN flakes
(#2115 + #2129 in firmware).
5. **Telegram caption** leads with `Build: <build_id>`; obsolete
`Tag: ${TAG_NAME}` line gone; workflow-level `env: TAG_NAME=
latest` removed.
No on-device firmware changes; builder cameras keep their existing
sysupgrade legacy URL until Phase 2 of the mirror (sysupgrade variant
routing) lands in OpenIPC/firmware.
See ~/.claude/plans/mirror-nightly-redesign-to-builder.md.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The Build workflow's matrix-of-everything makes it slow and noisy to
test a single device profile. Mirrors the build-one workflow already
present in OpenIPC/firmware.
Takes a full platform name (e.g. hi3518ev200_lite_switcam-hs303-v2)
and runs only that build via builder.sh.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add BR2_PACKAGE_HISILICON_OPENSDK=y to 9 hi3516cv200-family profiles
that were missing it. These boards will now get open_ modules built
from openhisilicon source instead of vendor-only blobs from osdrv.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>