Skip to content

Check and Download MCU Firmware

HackingGate edited this page Aug 14, 2026 · 5 revisions

Check and Download MCU Firmware

This page documents the vendor MCU firmware download path and local validation steps for wrapped Photonicat 2 MCU firmware files.

Check the running MCU version first with MCU Version Check.

Use The Vendor Manifest

The vendor OpenWrt MCU production check reads this manifest:

curl -fL -o /tmp/pcat2-mcu-latest.json \
  https://dl.photonicat.com/firmware/pcat2_mcu/latest_img.json

Read the firmware URL from the manifest. Do not derive a download URL from the device version string.

python3 - /tmp/pcat2-mcu-latest.json <<'PY'
import json
import pathlib
import sys
import urllib.parse

base = "https://dl.photonicat.com/firmware/pcat2_mcu/"
data = json.loads(pathlib.Path(sys.argv[1]).read_text())
firmware = data.get("url", "")
if firmware and not firmware.startswith(("http://", "https://")):
    firmware = urllib.parse.urljoin(base, firmware)

print(f"version={data.get('version', '')}")
print(f"url={firmware}")
print(f"sha256={data.get('sha256sum') or data.get('sha256') or ''}")
print(f"date={data.get('date') or data.get('build_date') or ''}")
PY

The full OpenWrt image's pcat-manager-web bytecode uses the manifest's url field and prefixes relative filenames with:

https://dl.photonicat.com/firmware/pcat2_mcu/

For normal updates, use the manifest endpoint rather than relying on a directory listing. For older releases, use the multi-version index below.

List Older MCU Firmware Versions

The vendor serves a second index alongside latest_img.json that lists older wrapped packages:

curl -fL -o /tmp/pcat2-mcu-versions.json \
  https://dl.photonicat.com/firmware/pcat2_mcu/versions.json

It returns a versions array whose entries use the same fields as the production manifest:

{
  "versions": [
    {
      "version": "RA2E1260702000",
      "url": "ota_RA2E120260702104522.bin",
      "date": "260702",
      "changelog-md": "",
      "sha256sum": "ed10b1ae7015c1fe54184e03b9222824cf37525ae642b2dddaee197c168dffa0",
      "message": "Fix broken RTC reading.",
      "upload_timestamp": "2026-07-02 10:45:22"
    }
  ]
}

List what it currently offers:

python3 - /tmp/pcat2-mcu-versions.json <<'PY'
import json
import pathlib
import sys

data = json.loads(pathlib.Path(sys.argv[1]).read_text())
for entry in data.get("versions", []):
    print(entry.get("version", ""), entry.get("url", ""), entry.get("sha256sum", ""))
PY

This index is not complete. Observed on 2026-08-14 it listed RA2E1260702000, RA2E1260515000, and RA2E1260306000, and did not list the then-current manifest target RA2E1260813002 or the earlier RA2E1260730001. Unlisted filenames still download from the same directory when the exact name is known, so treat the index as a convenience listing, not as the set of available files.

No shipped pcat-manager-web build reads this endpoint. The pcat-manager-web_2.2.1-146 IPK still references only latest_img.json, so the version list is server-side and has no vendor UI.

Download an older package by name from the index:

curl -fL -o /tmp/pcat2-mcu-older.bin \
  https://dl.photonicat.com/firmware/pcat2_mcu/ota_RA2E120260702104522.bin

Validate it with the wrapper checks below and confirm the version is the intended rollback target before flashing. Checked on 2026-08-14, ota_RA2E120260702104522.bin downloads with the listed SHA256 and passes the wrapper checks (ARBDPHC2, version RA2E1260702000, wrapped 74564 bytes, payload 74052 bytes, CRC32 valid). Flashing a version older than the running one is a downgrade and requires the updater's force option.

The Fixed ota.bin URL Is Not A Rollback Source

https://dl.photonicat.com/firmware/pcat2_mcu/ota.bin

This URL tracks the current production manifest target. Checked on 2026-08-14 it was byte-identical to the latest_img.json package RA2E1260813002 (SHA256 46ba856cdbc493f01d560581ea5cb6a4499ecb153be10576f7de00f106136d98), and on 2026-08-08 it served the then-current RA2E1260730001. Do not treat it as a previous-version image. Use versions.json for older releases.

Download The Referenced MCU Firmware

After reviewing the manifest, download the referenced file without hardcoding the filename:

FIRMWARE_URL=$(python3 - /tmp/pcat2-mcu-latest.json <<'PY'
import json
import pathlib
import sys
import urllib.parse

base = "https://dl.photonicat.com/firmware/pcat2_mcu/"
data = json.loads(pathlib.Path(sys.argv[1]).read_text())
firmware = data["url"]
if not firmware.startswith(("http://", "https://")):
    firmware = urllib.parse.urljoin(base, firmware)
print(firmware)
PY
)

curl -fL -o /tmp/pcat2-firmware.bin \
  "$FIRMWARE_URL"

Verify the downloaded file against the manifest checksum when one is present:

EXPECTED_SHA256=$(python3 - /tmp/pcat2-mcu-latest.json <<'PY'
import json
import pathlib
import sys

data = json.loads(pathlib.Path(sys.argv[1]).read_text())
print(data.get("sha256sum") or data.get("sha256") or "")
PY
)

ACTUAL_SHA256=$(sha256sum /tmp/pcat2-firmware.bin | awk '{print $1}')
test -n "$EXPECTED_SHA256"
test "$EXPECTED_SHA256" = "$ACTUAL_SHA256"

Also keep the checksum in your notes:

sha256sum /tmp/pcat2-firmware.bin

Find Local Firmware Artifacts

Image extraction is a research or recovery path, not the normal production download path.

Search extracted images for wrapped firmware files by header magic:

rg -a -l 'ARBDPHC2' /tmp/extracted-image

Search extracted filesystems for PMU version strings:

rg -a -o 'RA2E1[0-9]{9}' /tmp/extracted-root

Identify candidate wrapped firmware files from an extracted filesystem:

find /tmp/extracted-root -type f -size +512c -exec sh -c '
  for f do
    if head -c 8 "$f" | grep -q "ARBDPHC2"; then
      printf "%s\n" "$f"
    fi
  done
' sh {} +

A local version string is not the same thing as a downloadable firmware package. Only a file with the ARBDPHC2 wrapper is valid input for pcat-pmu-updater.

Firmware File Format

pcat-pmu-updater accepts a wrapped firmware file:

Offset Size Meaning
0 8 ASCII magic ARBDPHC2
8 14 ASCII firmware version, must start with RA2E1
22 4 Little-endian raw firmware size
26 4 Little-endian CRC32 of raw firmware payload
30 8 Little-endian package timestamp
38 474 Padding to 512-byte header
512 variable Raw RA2E1 firmware payload

The upstream packer is:

rockchip_rk3568_pcat_manager/src/pcat-fwgen.py

The updater validates the wrapper, strips the first 512 bytes, and sends only the raw firmware payload to the PMU.

Validate Before Flashing

Use this local validator before flashing:

python3 - /tmp/pcat2-firmware.bin <<'PY'
import pathlib
import struct
import sys
import zlib

path = pathlib.Path(sys.argv[1])
data = path.read_bytes()

if len(data) < 512:
    raise SystemExit("too small")

if data[:8] != b"ARBDPHC2":
    raise SystemExit("bad magic")

version = data[8:22].decode("ascii", "replace")
if not version.startswith("RA2E1"):
    raise SystemExit(f"bad version: {version}")

raw_size, expected_crc = struct.unpack_from("<II", data, 22)
if raw_size + 512 != len(data):
    raise SystemExit(f"bad size: raw={raw_size} file={len(data)}")

raw = data[512:]
actual_crc = zlib.crc32(raw) & 0xffffffff
if actual_crc != expected_crc:
    raise SystemExit(f"bad crc: expected={expected_crc:08x} actual={actual_crc:08x}")

print(f"path={path}")
print(f"version={version}")
print(f"raw_size={raw_size}")
print(f"crc32={actual_crc:08x}")
PY

Only flash if validation succeeds and the version is the intended target.

Clone this wiki locally