Skip to content

Add Pixelcade v2 board support with protocol auto-detection.#562

Merged
freezy merged 2 commits into
masterfrom
pixelcade-v2
Jun 22, 2026
Merged

Add Pixelcade v2 board support with protocol auto-detection.#562
freezy merged 2 commits into
masterfrom
pixelcade-v2

Conversation

@freezy

@freezy freezy commented Jun 21, 2026

Copy link
Copy Markdown
Owner

Detect the protocol variant, firmware version and panel size from the firmware descriptor returned during the connection handshake, mirroring the reference implementation in vpinball/libdmdutil#90.

  • v1: unchanged (0x1F frame command with bit-planes)
  • v2, firmware < 23: raw [cmd][data] commands carrying RGB565 (0x30) or RGB888 (0x40), no plane splitting
  • v2, firmware >= 23: same payloads wrapped in a length-prefixed frame (0xFE 0xFE | len | cmd | data | 0xAA), with a one-time 0xEF init command to switch the board into framed mode

Add green/blue channel swapping for the v2 raw path so the "rbg" color matrix is honored, panel size detection ('X'=128x32, 'M'=64x32), and per-frame change detection to skip identical frames.

Update DmdDevice.ini to document v1/v2 auto-detection and default the matrix to "rbg", which most panels require.

@alinke can you have a look at this, please?

@freezy
freezy requested a review from Copilot June 21, 2026 11:54
@freezy freezy self-assigned this Jun 21, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@freezy

freezy commented Jun 21, 2026

Copy link
Copy Markdown
Owner Author

@greptileai review this

@greptile-apps

greptile-apps Bot commented Jun 21, 2026

Copy link
Copy Markdown

Greptile Summary

Adds Pixelcade v2 board support by auto-detecting the protocol variant, firmware version, and panel size from the firmware descriptor returned during the connection handshake, mirroring the reference libdmdutil implementation.

  • Protocol auto-detection: ParseFirmware decodes the 8-byte firmware descriptor to set _isV2, _firmwareVersion, _useFraming, and FixedSize, with a safe fall-through to 128x32 v1 defaults for unrecognized descriptors.
  • v2 frame paths: RenderRgb24 and RenderRgb565 branch early for v2 boards, sending raw RGB888/RGB565 payloads via SendV2Frame which handles both the raw (pre-23) and length-prefixed framed (>=23) wire formats, plus per-frame change detection.
  • DmdDevice.ini: Documents v1/v2 auto-detection and flips the default color matrix from rgb to rbg, which is what most panels require.

Confidence Score: 5/5

Safe to merge; the new v2 protocol paths are well isolated from the existing v1 paths, bounds checks are consistent, and the fallback behaviour for unrecognised firmware is conservative.

All new code paths (auto-detection, framed/raw builders, channel-swap helpers, change detection) are internally consistent and sized correctly. The defensive improvements to Dispose and ClearDisplay are straightforward fixes. The only concerns are the INI default change and the lack of digit validation in firmware version parsing, neither of which causes incorrect data transmission on correctly-formatted firmware.

LibDmd/Output/Pixelcade/Pixelcade.cs — specifically the firmware version parsing in ParseFirmware.

Important Files Changed

Filename Overview
LibDmd/Output/Pixelcade/Pixelcade.cs Core v2 protocol implementation: firmware auto-detection, buffer allocation, channel-swap helpers, framed/raw frame builders, and per-frame change detection. Logic is well-structured; buffer sizing and bounds checks are consistent throughout the new paths.
PinMameDevice/DmdDevice.ini Documents v1/v2 auto-detection and changes the default color matrix from rgb to rbg; a behavioural default change that will affect existing v1 users who relied on the old default.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant App
    participant Pixelcade
    participant SerialPort
    participant Device

    App->>Pixelcade: GetInstance(port, colorMatrix)
    Pixelcade->>SerialPort: Open(port)
    SerialPort-->>Pixelcade: 29-byte handshake response
    Pixelcade->>Pixelcade: ParseFirmware(response, 21)
    Pixelcade->>Pixelcade: AllocateBuffers()
    alt "v2 framed firmware >= 23"
        Pixelcade->>SerialPort: Write 0xEF init
        Pixelcade->>SerialPort: BuildFrame enable
    else "v2 raw firmware < 23"
        Pixelcade->>SerialPort: Write enable raw
    else v1
        Pixelcade->>SerialPort: Write enable v1
    end
    loop Per frame
        App->>Pixelcade: RenderRgb565 or RenderRgb24
        alt v1
            Pixelcade->>SerialPort: Write 0x1F planes
        else v2
            opt ColorMatrix Rbg
                Pixelcade->>Pixelcade: SwapGreenBlue
            end
            Pixelcade->>Pixelcade: SendV2Frame skip if unchanged
            alt framed
                Pixelcade->>SerialPort: Write framed packet
            else raw
                Pixelcade->>SerialPort: Write raw command
            end
        end
    end
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant App
    participant Pixelcade
    participant SerialPort
    participant Device

    App->>Pixelcade: GetInstance(port, colorMatrix)
    Pixelcade->>SerialPort: Open(port)
    SerialPort-->>Pixelcade: 29-byte handshake response
    Pixelcade->>Pixelcade: ParseFirmware(response, 21)
    Pixelcade->>Pixelcade: AllocateBuffers()
    alt "v2 framed firmware >= 23"
        Pixelcade->>SerialPort: Write 0xEF init
        Pixelcade->>SerialPort: BuildFrame enable
    else "v2 raw firmware < 23"
        Pixelcade->>SerialPort: Write enable raw
    else v1
        Pixelcade->>SerialPort: Write enable v1
    end
    loop Per frame
        App->>Pixelcade: RenderRgb565 or RenderRgb24
        alt v1
            Pixelcade->>SerialPort: Write 0x1F planes
        else v2
            opt ColorMatrix Rbg
                Pixelcade->>Pixelcade: SwapGreenBlue
            end
            Pixelcade->>Pixelcade: SendV2Frame skip if unchanged
            alt framed
                Pixelcade->>SerialPort: Write framed packet
            else raw
                Pixelcade->>SerialPort: Write raw command
            end
        end
    end
Loading

Reviews (2): Last reviewed commit: "pixelcade: Fix v2 frame change detection..." | Re-trigger Greptile

Comment thread LibDmd/Output/Pixelcade/Pixelcade.cs
Comment thread LibDmd/Output/Pixelcade/Pixelcade.cs
Comment thread LibDmd/Output/Pixelcade/Pixelcade.cs Outdated
freezy added 2 commits June 21, 2026 21:32
Detect the protocol variant, firmware version and panel size from
the firmware descriptor returned during the connection handshake,
mirroring the reference implementation in libdmdutil PR #90.

- v1: unchanged (0x1F frame command with bit-planes)
- v2, firmware < 23: raw [cmd][data] commands carrying RGB565 (0x30)
  or RGB888 (0x40), no plane splitting
- v2, firmware >= 23: same payloads wrapped in a length-prefixed
  frame (0xFE 0xFE | len | cmd | data | 0xAA), with a one-time 0xEF
  init command to switch the board into framed mode

Add green/blue channel swapping for the v2 raw path so the "rbg"
color matrix is honored, panel size detection ('X'=128x32,
'M'=64x32), and per-frame change detection to skip identical frames.

Update DmdDevice.ini to document v1/v2 auto-detection and default
the matrix to "rbg", which most panels require.
The swapped-channel payload lives in the over-sized _swapBuffer, so
relying on data.Length compared the whole buffer instead of just the
meaningful frame bytes. Pass the real payload length down to
SendV2Frame and track it alongside the command, comparing only those
bytes (and resetting the cache on reconnect). Also warn instead of
silently defaulting when an unrecognized panel size code arrives.
@freezy

freezy commented Jun 21, 2026

Copy link
Copy Markdown
Owner Author

@greptileai ready to ship?

@greptile-apps

greptile-apps Bot commented Jun 21, 2026

Copy link
Copy Markdown

All 3 review threads are resolved — the P1 bug (wrong RGB565 payload length), the P2 silent fallback, and the change-detection inconsistency were all addressed. The PR is mergeable with no blocking CI checks.

One note: @alinke was explicitly tagged for a review in the PR description and hasn't weighed in yet. If their sign-off is required, that's the only remaining gate. Otherwise, from a code-review standpoint, it's clean to merge.

Tip: You can customize Greptile's behavior for this repo with .greptile/rules.md and .greptile/config.json.

@freezy
freezy merged commit 36490d8 into master Jun 22, 2026
2 checks passed
@freezy
freezy deleted the pixelcade-v2 branch June 22, 2026 20:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants