Skip to main content

mapcan

CAN signal maps as code.
One YAML file → documentation, DBC, C firmware code and Python test helpers.

CI Latest release PyPI Python 3.12 | 3.13 | 3.14 Platforms License: Apache-2.0

CANopen SAE J1939 CAN 2.0A/B CAN FD

Quick start · Installation · Outputs · Web editor · Commands · Map format · Development


mapcan creates and edits CAN signal maps for any device or system. The map lives in a single YAML file, which is the single source of truth: documents, firmware code and files for bus analyzers are all generated from it, so they never drift apart.

  • Protocols — CANopen (IDs from the function code and node_id), SAE J1939 (IDs from PGN, priority and address), CAN 2.0A/B with explicit 11/29-bit IDs, CAN FD with DLC up to 64.
  • Signals — signed and unsigned integers of any length and position, little- and big-endian; enumerations, flags and bit fields; physical quantities with factor, offset, unit and range; float32/float64; byte arrays; multiplexing; default and "not available" values.
  • Frames — cycle time, direction, sender and receivers, CANopen SDO exchanges. Each frame is described once, at its sender; what every device receives is derived for the editor, the documents (including an Excel communication matrix) and DBC.
  • Validation — every error points to a file, line and column, before anything is generated.
mapcan: 1

document:
  title: Electric vehicle example map
  version: 0.1.0
  language: en

buses:
  - name: powertrain
    bitrate: 500000
    protocol: can
    devices:
      - name: bms
        frames:
          - name: bms_status
            id: 0x100
            length: 8
            cycle_ms: 100
            receivers: [vcu]
            signals:
              - {name: pack_voltage, byte: 0, encoding: float, unit: V, min: 0, max: 1000}
              - {name: pack_current, byte: 4, encoding: float, unit: A, initial: 0}
      - name: vcu

Quick start

mapcan app

The editor opens in its own window. Click New map… and pick a template — CANopen, J1939, CAN with explicit IDs or an empty map — or start from one of the examples on the start screen. Add devices, frames and signals in the tree, and draw signals with the mouse on the frame's bit layout. Errors show up as you type; Ctrl+S saves the file.

Prefer a text editor? The mapcan extension for VS Code validates maps as you type and adds the Map view, a form with the bit layout, hovers and quick fixes (vscode/README.md); any editor with a YAML language server gets completion and hints from the JSON Schema — see docs/format.md.

Installation

Platform How
Windows Download mapcan-X.Y.Z-setup.exe from Releases and run it. No Python or administrator rights needed.
Linux x64 Download mapcan-X.Y.Z-linux-x64.tar.gz, extract it and run mapcan/mapcan-app (editor in the browser) or mapcan/mapcan (CLI).
Any OS with Python 3.12 or newer uv tool install "mapcan[desktop]" or pipx install "mapcan[desktop]" (PyPI)
Docker ghcr.io/ordu1453/mapcan:<version> — everything needed for CI
VS Code The mapcan extension (also on Open VSX for VSCodium, Cursor and others), with uv tool install "mapcan[lsp]" for its language server

The package is split into parts: pip install mapcan is the core (validation, DBC, EDS, Markdown, HTML, C and Python code: enough for CI and firmware builds). Extras add the rest: web (the editor in a browser), pdf, xlsx, lsp (the language server for VS Code), desktop (the app with all its exports) and all. A command that needs a missing part says which one to install.

No external programs are required: PDFs are built by the bundled Typst compiler, and the PT Sans and PT Serif fonts (OFL) ship with the package.

Windows installer details

The installer adds mapcan to the Start menu and the desktop, to "Open with" for .yaml files and, optionally, the mapcan command to PATH. Launching it again or double-clicking a map opens the map in the window that is already running. A banner at the top of the editor announces new versions; the update installs over the current one. A portable build is also available: mapcan-X.Y.Z-windows-x64.zip, run mapcan-app.exe.

The installer is not code-signed, so SmartScreen may warn about an unknown publisher — click More info → Run anyway.

Offline install (corporate network without PyPI)

Build a wheelhouse on a machine with access and copy the folder over:

pip wheel . -w wheelhouse
pip install --no-index --find-links wheelhouse mapcan
Moving from canmap

The tool used to be called canmap. The Windows installer upgrades the old version by itself; with uv/pipx run uv tool uninstall canmap, then install again. Settings move from ~/.canmap to ~/.mapcan, and the canmap PDF template is read as mapcan. Maps need the mapcan: 1 key (see docs/format.md).

Outputs

Output Command
🖥️ Web editor: tree, forms, bit layout, live validation mapcan app / mapcan serve
✅ Validation with file and line numbers mapcan validate my-map.yaml
📊 Excel: addressing, contents, bit layouts, changes mapcan export xlsx my-map.yaml
📄 PDF: title page, contents, bit diagrams, tables mapcan export pdf my-map.yaml
📝 Markdown for a README or wiki, HTML page with search mapcan export md / mapcan export html
🔌 DBC for CANalyzer, SavvyCAN, PCAN-View, Wireshark mapcan export dbc my-map.yaml -o dbc/
🧩 EDS of each CANopen device: mapped objects and PDO parameters mapcan export eds my-map.yaml -o eds/
⚙️ C: ID #defines, enums, masks, structs, pack/unpack mapcan gen c my-map.yaml -o firmware/
🐍 Python module for tests and test benches mapcan gen py my-map.yaml -o tools/
📦 Everything at once, for CI mapcan build my-map.yaml -o build/
🔀 Comparison of two map versions, changelog mapcan diff old.yaml my-map.yaml
📥 Start from an existing DBC mapcan import dbc vehicle.dbc -o my-map.yaml

Documents can be produced in English or Russian. The default comes from the map's document.language; override it with --lang or the language switch in the editor. The ESKD template (gost-2.105) is always produced in Russian.

mapcan import dbc turns DBC nodes into devices (the sender of a frame owns it), and brings over frames with their IDs, length, cycle or send type and receivers, and signals with their layout, scaling, units, ranges, value tables, initial values and multiplexing. A bus whose frames are marked J1939 (VFrameFormat) becomes a j1939 bus with PGNs and device addresses; any other becomes a can bus with explicit IDs. Value names become identifiers (Not available → NOT_AVAILABLE, the original text goes to the description), and whatever DBC cannot express is reported as a warning.

Generated C code

Each bus gets <bus>.h and <bus>.c with no dynamic memory and no libc beyond stdint.h, stdbool.h and stddef.h. The code compiles cleanly with -Wall -Wextra -Wconversion for x86 and Cortex-M. For examples/vehicle-can.yaml:

#include "powertrain.h"

vcu_command_t cmd = {0};
cmd.contactor_request = VCU_COMMAND_CONTACTOR_REQUEST_CLOSE;
cmd.torque_request = vcu_command_torque_request_from_phys(120.5f);   /* N·m → raw */
uint8_t buf[8];
int len = vcu_command_pack(buf, sizeof buf, &cmd);                   /* → VCU_COMMAND_ID */

bms_status_t st;
if (bms_status_unpack(&st, rx_data, rx_len) == 0 && st.pack_voltage > 350.0f) { ... }

Next to <FRAME>_ID and <FRAME>_LENGTH, the header tells the CAN driver how to send the frame: <FRAME>_IS_EXTENDED for a 29-bit ID, and on a CAN FD bus <FRAME>_IS_FD and <FRAME>_BRS (bit rate switch, 0 or 1). The classes of the Python module carry the same as IS_EXTENDED, IS_FD and BITRATE_SWITCH, named after the arguments of python-can's can.Message:

msg = can.Message(arbitration_id=VcuCommand.ID, data=cmd.pack(), is_extended_id=VcuCommand.IS_EXTENDED,
                  is_fd=VcuCommand.IS_FD, bitrate_switch=VcuCommand.BITRATE_SWITCH)

Validation

Export and generation validate the map first and stop on errors (--force generates anyway). validate exits with code 1 on errors, and with --strict on warnings too.

$ mapcan validate tests/data/invalid_map.yaml
tests/data/invalid_map.yaml:21:25: error[number-comma]: value '0,1': decimal comma used, should be 0.1
tests/data/invalid_map.yaml:24:17: error[number-cyrillic-x]: value '0х196': the 0x prefix uses a Cyrillic “х”
tests/data/invalid_map.yaml:33:22: error[invalid-identifier]: value name 'NO DATA': only Latin letters, digits and _ are allowed
tests/data/invalid_map.yaml:62:36: error[duplicate-signal-name]: flag 'dc_status' already exists in frame 'pcu_state' (signal)
...
FAIL: errors: 12, warnings: 2, notes: 4

mapcan rules lists every rule; any of them can be turned off with --disable CODE.

Web editor

mapcan app                    # own window; start screen: new map, open, recent files, examples
mapcan app my-map.yaml        # open a map right away
mapcan serve my-map.yaml      # the same in the browser
  • Map tree with search by name, ID, PGN and description.
  • Property forms that adapt to the bus protocol.
  • Bit layout editor: drag across bits to add a signal, drag a signal to move it, drag its edge to resize; overlaps are highlighted.
  • Value and flag tables, live validation and a YAML tab.
  • Preview and download of every format.
  • Comparison with any git revision, with the changelog written into the revision history.

Files are saved with their comments and formatting intact, so git diff shows only the lines you changed. More in docs/web.md.

Examples

File What it shows
minimal.yaml Every kind of signal in one CANopen frame: enum, bit field, quantities, big-endian
j1939.yaml SAE J1939: engine (EEC1, ET1), instrument cluster, addressed PDU1 request, "not available" 0xFF/0xFFFF
canopen-node.yaml CANopen drive: PDO signals mapped to CiA 402 and manufacturer objects, EDS export
tractor/ A map in two files: include adds a J1939 powertrain bus, a device on the main file's bus and shared value tables
vehicle-can.yaml Explicit 11/29-bit IDs: float32, multiplexed cell voltages, receivers, CAN FD with DLC 64 and float64

The same examples are on the editor's start screen: pick one, choose where to save a copy, and it opens.

Commands

mapcan app         [FILE|DIR] [--browser] [--readonly]
mapcan serve       [FILE|DIR] [--host 127.0.0.1] [--port 8765] [--readonly] [--no-browser] [--token T | --no-auth]
mapcan validate    FILE [--strict] [--info] [--format json] [--disable CODE]
mapcan export xlsx FILE [-o OUT] [--style style.yaml] [--lang en]
mapcan export pdf  FILE [-o OUT] [--template mapcan|gost-2.105|my.typ] [--font-path DIR] [--lang en]
mapcan export md   FILE [-o OUT] [--template my.md.j2] [--lang en]
mapcan export html FILE [-o OUT] [--template my.html.j2] [--lang en]
mapcan export dbc  FILE [-o DIR|file.dbc] [--bus NAME]
mapcan export eds  FILE [-o DIR] [--bus NAME]
mapcan gen c       FILE [-o DIR] [--prefix my_] [--bus NAME]
mapcan gen py      FILE [-o DIR] [--bus NAME]
mapcan build       FILE [-o DIR] [--formats xlsx,pdf,md,html,dbc,eds,c,py] [--pdf-template NAME]
mapcan diff        OLD NEW [--format text|md|json]
mapcan import dbc  FILE.dbc [-o FILE.yaml] [--title TEXT] [--lang en] [--force]
mapcan migrate     FILE… [--check]      # former field names → current ones
mapcan fmt         FILE… [--check]      # keys in one order, comments and layout kept
mapcan schema      [-o schema.json] [--part]   # --part: schema of an included file
mapcan template    --list | pdf [--name gost-2.105] -o FILE | md|html|xlsx-style -o FILE
mapcan rules

Custom styling

Format How to customize
PDF Two built-in templates: mapcan (standard layout) and ESKD gost-2.105 (frame and title block). Copy one with mapcan template pdf --name gost-2.105 -o corp.typ, edit it, then export with --template corp.typ. Document details (designation, letter, signatures, approval blocks) and the company logo are set in document.print. See docs/templates.md.
Excel mapcan template xlsx-style -o style.yaml, adjust fonts, colors and columns, then mapcan export xlsx map.yaml --style style.yaml.
Markdown / HTML Jinja2 templates over the same data: mapcan template md -o my.md.j2.

CI

docs/ci-example.md shows a pipeline for map repositories: when the YAML changes, the map is validated and the PDF, Excel, DBC and C headers are rebuilt and kept as artifacts. The Docker image ghcr.io/ordu1453/mapcan:<version> contains everything needed.

Development

CONTRIBUTING.md covers the setup, tests and conventions; ARCHITECTURE.md explains how the code is organized and what to change to add a field to the format.

uv sync                           # environment from uv.lock (with every extra)
uv run pre-commit install         # run ruff before every commit
uv run mapcan --help
uv run pytest
UPDATE_GOLDEN=1 uv run pytest     # refresh reference files after intentional changes

The tests compile the generated C code with gcc (and arm-none-eabi-gcc if installed) and compare pack/unpack byte for byte with the Python module and cantools for every example. The UI lives in web/ (Vue 3 + TypeScript + Vite); npm run build there writes the bundle to src/mapcan/web/static/.

Without uv
python -m venv .venv
.venv/Scripts/activate            # Linux/macOS: source .venv/bin/activate
pip install -e ".[all]"
Releasing a version
  1. Bump the version in pyproject.toml, src/mapcan/__init__.py, web/package.json and CITATION.cff (also its date-released) (uv lock, npm run build in web/), commit.
  2. Create and push a tag: git tag -a vX.Y.Z -m "mapcan X.Y.Z: …" && git push origin main vX.Y.Z.

release.yml runs the tests, checks that the tag matches the version, builds the Windows installer and zip (PyInstaller + Inno Setup), the wheel and the Docker image, and publishes a GitHub Release (the tag annotation becomes the release notes) and then the package on PyPI. Running the workflow by hand (Actions → release → Run workflow) makes a trial build without publishing; with Publish to TestPyPI checked, the package goes to TestPyPI to check the upload and the project page.

Building the Windows version locally:

uv sync --extra desktop --group package
uv run pyinstaller packaging/mapcan.spec --noconfirm        # → dist/mapcan/
iscc /DAppVersion=X.Y.Z packaging/mapcan.iss                # → dist/mapcan-X.Y.Z-setup.exe
Releasing the VS Code extension
  1. If the extension needs a newer language server, release mapcan first and raise MIN_SERVER in vscode/src/version.ts.
  2. In vscode/: npm version X.Y.Z --no-git-tag-version; in vscode/CHANGELOG.md rename ## Unreleased to ## X.Y.Z; commit.
  3. Create and push a tag: git tag -a extension-vX.Y.Z -m "mapcan for VS Code X.Y.Z" && git push origin main extension-vX.Y.Z.
  4. Upload the .vsix of the GitHub Release to the Visual Studio Marketplace: the first time New extension → Visual Studio Code, later ⋯ → Update at the extension.

extension-release.yml checks that the tag matches vscode/package.json and that the changelog has the section, runs the typecheck and the integration tests, packages the .vsix, publishes a GitHub Release with it and the changelog section, and then the extension on Open VSX (secret OVSX_PAT, namespace ordu1453). The Marketplace is published by hand (step 4): a token for it needs an Azure DevOps organization, which needs an Azure subscription. With a secret VSCE_PAT (a token of the publisher ordu1453 with the scope Marketplace → Manage) the workflow publishes there too; without it that step only warns. Running the workflow by hand makes a trial .vsix (an artifact) without releasing.

Repository layout
examples/                   example maps (CANopen, J1939, CAN/CAN FD)
schema/mapcan.schema.json   JSON Schema (mapcan schema -o …)
docs/                       format, PDF templates, web editor, CI, logo
src/mapcan/
  model.py                  data model (pydantic)
  loader.py                 YAML loading with node positions
  protocols.py, canopen.py  CAN ID computation: CANopen, J1939, explicit IDs
  resolve.py                references, SDO layout
  validate.py               validation rules
  bits.py                   bit layout, pack/unpack
  docmodel.py               shared view for documents
  export/                   xlsx, pdf (Typst), md/html (Jinja2), dbc
  codegen/                  C and Python
  diff.py                   map version comparison, changelog
  launcher.py               mapcan app: window, single instance, port selection
  updates.py                new version check on GitHub Releases
  web/                      web editor: FastAPI, YAML operations, built UI (static/)
  templates/                document templates (pdf/ — Typst)
  fonts/                    PT Sans, PT Serif (OFL)
web/                        UI sources (Vue 3 + TypeScript + Vite)
  src/form/                 node forms shared with the VS Code form: fields, conditions, help from the schema
vscode/                     VS Code extension (language server client, map tree, node form in webview/)
packaging/                  Windows build: PyInstaller, Inno Setup installer, icon and logo
tests/                      tests; data/ — fixtures, golden/ — reference files

Roadmap

  • Import existing maps from DBC and Excel
  • Live bus: log decoding, monitor, simulator

License

mapcan is free and open source, licensed under the Apache License 2.0. Copyright 2026 Ali Ordu — see NOTICE.

  • Your outputs are yours. Files generated from your map — C and Python code, DBC, PDF, Excel, Markdown and HTML — belong to you and may be used under any terms, including in closed-source firmware. The "Generated by mapcan" header is a courtesy, not a requirement.
  • The map format is open. The format specification (docs/format.md) is available under CC BY 4.0; the JSON Schema (schema/mapcan.schema.json) and the example maps (examples/) are dedicated to the public domain under CC0 1.0. Anyone may implement readers, writers and tools for the format.
  • Third-party components bundled with mapcan are listed in THIRD_PARTY_NOTICES.md.

If mapcan helps your project, a star on GitHub or a citation (CITATION.cff) is much appreciated.

Metadata

Release files for mapcan 0.21.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for mapcan 0.21.1
File Size Uploaded
mapcan-0.21.1.tar.gz 3.0 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for mapcan 0.21.1
File Interpreter ABI Platform
mapcan-0.21.1-py3-none-any.whl Python 3 none any Details

Total release size: 5.0 MB

Release files / mapcan-0.21.1.tar.gz

Download URL mapcan-0.21.1.tar.gz
Size 3.0 MB
Tags Source
SHA-256 checksum
How to use checksums
022e7ab45a9a05d1aaf0b1470153dc31fa509a5f51e309916bf806ef602c110f
BLAKE2b-256 checksum
How to use checksums
2f6bdb25309c5d3aa30d0b00e84782af35b214b78077518a7f38f0c38baa4589
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 14, 2026.

Transparency log

Release files / mapcan-0.21.1-py3-none-any.whl

Download URL mapcan-0.21.1-py3-none-any.whl
Size 2.0 MB
Tags Python 3
SHA-256 checksum
How to use checksums
54d3542a72d275146201c65bfe0ac7badaa0314e09260e312010f7ec7d61fc2e
BLAKE2b-256 checksum
How to use checksums
4b0337df5dcb79647742a77e2b3e2b07fed6a8f6cd5d581d60a639564405b938
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 14, 2026.

Transparency log

Release history Release notifications | RSS feed

0.23.1

2 release files

0.23.0

2 release files

0.22.0

2 release files

This release

0.21.1 This release

2 release files

0.21.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page