Auto-generated project docs with 3D preview

For KiCad PCBs and STL files, made with mdBook
Published on May 05, 2024.

A recurring problem I ran into with my projects is documentation. Especially with 3D printed parts or PCBs it's often required to generate preview images or PDFs so someone interested can take a look without having to clone the repo and install some development tools to open the original files. Doing this manually is time-consuming and repetitive. So it should be automated.

I've come across a couple of similar projects recently, like this or these. But they mostly spit out non-interactive images. So I've decided to go a slightly different and more interactive route.

2D KiCad board visualization
3D KiCad board visualization
3D STL visualization

Using GitHub Actions and GitHub Pages, I'm creating a static documentation website using mdBook. To visualize KiCad schematics and 2D board designs I'm exporting them as svg images and display them interactively using svg-pan-zoom. KiCad PCBs are also exported in 3D as VRML files and displayed using three.js.

To generate these files I'm mostly using shell scripts in the project repository.

Then the workflows can be kept relatively short, just installing the required dependencies, running the proper script and putting the resulting files where they are needed. This way the coupling to the proprietary GitHub features is not that thight and can hopefully be ported to other providers with relative ease.

The whole workflow is relatively customizable. So for some projects I'm for example creating special board previews for etching PCBs at home.

I've implemented this workflow in a couple of projects now:

Take a look there for the implementation details.

Example Implementation

Here are the basics to get you started.

1) mdBook dependency

Fetch the pre-built mdBook binary and place it in your PATH.

2) mdBook initialization

Add a docs directory to your project, either by copying it from one of my repos, or creating a new one and adapting the resulting book.toml config.

mdbook init docs

3) svg-pan-zoom dependency

If you want to visualize SVG images for KiCad schematics and boards, add svg-pan-zoom as a subrepo.

cd docs
git submodule add https://github.com/bumbu/svg-pan-zoom
mkdir -p src/js
cd src/js
ln -s ../../svg-pan-zoom/dist/svg-pan-zoom.js svg-pan-zoom.js
ln -s ../../svg-pan-zoom/dist/svg-pan-zoom.min.js svg-pan-zoom.min.js

4) Generating docs

Add the docs/generate_docs.sh script and adapt the config and calls to other script at the beginning, as needed for your project:

Here is an example generate_docs.sh file.

#!/bin/bash

# SPDX-FileCopyrightText: 2024 Thomas Buck 
# SPDX-License-Identifier: CERN-OHL-S-2.0+
#
#  ------------------------------------------------------------------------------
# | Copyright (c) 2024 Thomas Buck                           |
# |                                                                              |
# | This source describes Open Hardware and is licensed under the CERN-OHL-S v2  |
# | or any later version.                                                        |
# |                                                                              |
# | You may redistribute and modify this source and make products using it under |
# | the terms of the CERN-OHL-S v2 (https://ohwr.org/cern_ohl_s_v2.txt)          |
# | or any later version.                                                        |
# |                                                                              |
# | This source is distributed WITHOUT ANY EXPRESS OR IMPLIED WARRANTY,          |
# | INCLUDING OF MERCHANTABILITY, SATISFACTORY QUALITY AND FITNESS FOR A         |
# | PARTICULAR PURPOSE. Please see the CERN-OHL-S v2 (or any later version)      |
# | for applicable conditions.                                                   |
# |                                                                              |
# | Source location: https://git.xythobuz.de/thomas/drumkit                      |
# |                                                                              |
# | As per CERN-OHL-S v2 section 4, should You produce hardware based on this    |
# | source, You must where practicable maintain the Source Location visible      |
# | on the external case of the Gizmo or other products you make using this      |
# | source.                                                                      |
#  ------------------------------------------------------------------------------

INSCH="../pcb/drumkit.kicad_sch ../pcb2/lars2.kicad_sch"
INPCB="../pcb/drumkit.kicad_pcb ../pcb2/lars2.kicad_pcb"

cd "$(dirname "$0")"

if [ "$1" = "build" ] ; then
    echo "Generating plots"
    ../pcb/generate_plot.sh
    echo
    echo "Generating fab"
    ../pcb/generate_fab.sh
    echo

    echo "Generating plots 2"
    ../pcb2/generate_plot.sh
    echo
    echo "Generating fab 2"
    ../pcb2/generate_fab.sh
    echo

    echo "Generating stls"
    ../3dprint/generate_stls.sh
    echo
fi

rm -rf src/plot
cp -r ../pcb/plot src
cp -r ../pcb2/plot/* src/plot/
cp ../pcb/fab.zip src/plot/fab_pcb.zip
cp ../pcb2/fab.zip src/plot/fab_pcb2.zip

rm -rf src/stl
cp -r ../3dprint/stl src

INSTL=`ls ../3dprint/stl/*.stl`

for path in $INSCH
do
    IN=`echo $path | sed "s:../pcb.\\?/::g"`
    o="src/inc_$IN.md"
    echo "Include for $IN at $o"

    rm -rf $o
    echo "" >> $o

    for f in `ls src/plot/$IN.svg/*.svg | sort -r`; do
        file=`echo $f | sed 's:src/:./:g'`
        name=`echo $f | sed "s:src/plot/$IN.svg/::g" | sed 's:.svg::g'`
        echo "Sheet at $name"

        echo "

$name

" >> $o echo "
" >> $o echo "" >> $o echo "" >> $o echo "
" >> $o echo >> $o echo "[Direct link to \`$name\`]($file)." >> $o echo >> $o done echo done plot_3d() { echo '' >> $1 echo "

Status: \"Preparing 3D model...\"

" >> $1 echo "
" >> $1 echo '' >> $1 } for path in $INPCB do IN=`echo $path | sed "s:../pcb.\\?/::g"` o="src/inc_$IN.md" file="plot/$IN.wrl" name=`echo $file | sed "s:plot/::g" | sed 's:.wrl::g'` echo "Include for $IN at $o, $file, $name" rm -rf $o plot_3d $o $name $file done echo for IN in $INSTL do o=`echo $IN | sed "s:../3dprint/stl/:src/inc_:g" | sed "s:.stl:.stl.md:g"` file=`echo $IN | sed "s:../3dprint/::g"` name=`echo $file | sed "s:stl/::g" | sed 's:.stl::g'` echo "Include for $IN at $o, $file, $name" rm -rf $o plot_3d $o $name $file done echo echo "Generating docs" if [ "$1" = "serve" ] ; then mdbook serve --open elif [ "$1" = "build" ] ; then mdbook build else echo "Invalid command. 'build' or 'serve'." fi

5) Auto-generating and publishing docs

Add the .github/workflows/docs.yml script:

Here is an example docs.yml file.

# SPDX-FileCopyrightText: 2024 Thomas Buck 
# SPDX-License-Identifier: CERN-OHL-S-2.0+
#
#  ------------------------------------------------------------------------------
# | Copyright (c) 2024 Thomas Buck                           |
# |                                                                              |
# | This source describes Open Hardware and is licensed under the CERN-OHL-S v2  |
# | or any later version.                                                        |
# |                                                                              |
# | You may redistribute and modify this source and make products using it under |
# | the terms of the CERN-OHL-S v2 (https://ohwr.org/cern_ohl_s_v2.txt)          |
# | or any later version.                                                        |
# |                                                                              |
# | This source is distributed WITHOUT ANY EXPRESS OR IMPLIED WARRANTY,          |
# | INCLUDING OF MERCHANTABILITY, SATISFACTORY QUALITY AND FITNESS FOR A         |
# | PARTICULAR PURPOSE. Please see the CERN-OHL-S v2 (or any later version)      |
# | for applicable conditions.                                                   |
# |                                                                              |
# | Source location: https://git.xythobuz.de/thomas/drumkit                      |
# |                                                                              |
# | As per CERN-OHL-S v2 section 4, should You produce hardware based on this    |
# | source, You must where practicable maintain the Source Location visible      |
# | on the external case of the Gizmo or other products you make using this      |
# | source.                                                                      |
#  ------------------------------------------------------------------------------

name: Docs

# only build single instance of docs for latest main branch
on:
  push:
    branches:
      - master

jobs:
  deploy:
    runs-on: ubuntu-latest

    permissions:
      contents: write
      pages: write
      id-token: write

    steps:
      - name: Checkout repo
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Checkout repo submodules
        run: git submodule update --init

      - name: Install dependencies
        run: |
          sudo add-apt-repository --yes ppa:kicad/kicad-8.0-releases
          sudo apt update
          sudo apt install -y --install-recommends kicad pipx libfuse2 libegl1 poppler-utils openscad zip

      - name: Install latest mdbook
        run: |
          tag=$(curl 'https://api.github.com/repos/rust-lang/mdbook/releases/latest' | jq -r '.tag_name')
          url="https://github.com/rust-lang/mdbook/releases/download/${tag}/mdbook-${tag}-x86_64-unknown-linux-gnu.tar.gz"
          mkdir mdbook
          curl -sSL $url | tar -xz --directory=./mdbook
          echo `pwd`/mdbook >> $GITHUB_PATH

      - name: Build Book
        run: docs/generate_docs.sh build

      - name: Setup Pages
        uses: actions/configure-pages@v2

      - name: Upload artifact
        uses: actions/upload-pages-artifact@v1
        with:
          path: 'docs/book'

      - name: Deploy to GitHub Pages
        id: deployment
        uses: actions/deploy-pages@v1

And don't forget to set the GitHub Pages source in the repo settings to GitHub Actions:

What to change in the GitHub repo settings

6) STL 3D visualization

If you want to visualize 3D print files, do the same with 3dprint/generate_stls.sh and .github/workflows/scad.yml:

Here is an example generate_stls.sh file.

#!/bin/bash

# ----------------------------------------------------------------------------
# Copyright (c) 2023 Kauzerei (openautolab@kauzerei.de)
# Copyright (c) 2023 Thomas Buck (thomas@xythobuz.de)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# See .
# ----------------------------------------------------------------------------

# space separated list of scad files (without extension)
MODULES="actuator beam enclosure tamb_mount"
OUTDIR="stl"

# enter directory of script (case)
cd "$(dirname "$0")"

# Detect OS
if [[ "$OSTYPE" == "darwin"* ]]; then
    echo "Mac OS X detected"
    SCAD="open -n -a OpenSCAD --args"
else
    echo "Linux detected"
    SCAD="openscad"
fi

echo "deleting previous build output"
rm -rf $OUTDIR
mkdir -p $OUTDIR

for MODULE in $MODULES
do
    PARTS=$(grep -o "part.*//.*\[.*]" ${MODULE}.scad | sed 's/,/ /g' | sed 's/.*\[\([^]]*\)\].*/\1/g')
    echo "generating from ${MODULE}"

    for PART in ${PARTS}
    do
        if [[ "${PART}" != "OPT_"* ]]; then
            echo ${PART}
            FILENAME=$(echo $OUTDIR/${MODULE}_${PART}.stl | tr '[:upper:]' '[:lower:]')
            $SCAD $(pwd)/${MODULE}.scad --D part=\"${PART}\" --o $(pwd)/${FILENAME}
        fi
    done
done

Here is an example scad.yml file.

# SPDX-FileCopyrightText: 2024 Thomas Buck 
# SPDX-License-Identifier: CERN-OHL-S-2.0+
#
#  ------------------------------------------------------------------------------
# | Copyright (c) 2024 Thomas Buck                           |
# |                                                                              |
# | This source describes Open Hardware and is licensed under the CERN-OHL-S v2  |
# | or any later version.                                                        |
# |                                                                              |
# | You may redistribute and modify this source and make products using it under |
# | the terms of the CERN-OHL-S v2 (https://ohwr.org/cern_ohl_s_v2.txt)          |
# | or any later version.                                                        |
# |                                                                              |
# | This source is distributed WITHOUT ANY EXPRESS OR IMPLIED WARRANTY,          |
# | INCLUDING OF MERCHANTABILITY, SATISFACTORY QUALITY AND FITNESS FOR A         |
# | PARTICULAR PURPOSE. Please see the CERN-OHL-S v2 (or any later version)      |
# | for applicable conditions.                                                   |
# |                                                                              |
# | Source location: https://git.xythobuz.de/thomas/drumkit                      |
# |                                                                              |
# | As per CERN-OHL-S v2 section 4, should You produce hardware based on this    |
# | source, You must where practicable maintain the Source Location visible      |
# | on the external case of the Gizmo or other products you make using this      |
# | source.                                                                      |
#  ------------------------------------------------------------------------------

name: STLs

# build for each push and pull request
on: [push, pull_request]

jobs:
  render:
    runs-on: ubuntu-latest

    permissions:
      contents: write

    steps:
      - name: Checkout repo
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Checkout repo submodules
        run: git submodule update --init

      - name: Install dependencies
        run: |
          sudo apt update
          sudo apt-get install -y openscad zip

      - name: Render STLs
        run: |
          ./3dprint/generate_stls.sh

      - name: Upload part files
        uses: actions/upload-artifact@v4.0.0
        with:
          name: drumkit-stl
          path: 3dprint/stl
          if-no-files-found: error

      - name: Archive release files
        if: startsWith(github.ref, 'refs/tags/')
        run: |
          cd 3dprint
          zip -r drumkit-stl stl

      - name: Upload release files
        if: startsWith(github.ref, 'refs/tags/')
        uses: softprops/action-gh-release@v1
        with:
          files: 3dprint/drumkit-stl.zip

And then add something like this to the mdBook sources where you want the visualization to appear:

{{#include inc_enclosure_bottom.stl.md}}

[Direct link to this file](./stl/enclosure_bottom.stl).

7) PCB gerber files

If you want to generate gerber files from PCBs, do something similar with pcb/generate_fab.sh and .github/workflows/kicad.yml:

Here is an example generate_fab.sh file.

#!/bin/bash

# SPDX-FileCopyrightText: 2024 Thomas Buck 
# SPDX-License-Identifier: CERN-OHL-S-2.0+
#
#  ------------------------------------------------------------------------------
# | Copyright (c) 2024 Thomas Buck                           |
# |                                                                              |
# | This source describes Open Hardware and is licensed under the CERN-OHL-S v2  |
# | or any later version.                                                        |
# |                                                                              |
# | You may redistribute and modify this source and make products using it under |
# | the terms of the CERN-OHL-S v2 (https://ohwr.org/cern_ohl_s_v2.txt)          |
# | or any later version.                                                        |
# |                                                                              |
# | This source is distributed WITHOUT ANY EXPRESS OR IMPLIED WARRANTY,          |
# | INCLUDING OF MERCHANTABILITY, SATISFACTORY QUALITY AND FITNESS FOR A         |
# | PARTICULAR PURPOSE. Please see the CERN-OHL-S v2 (or any later version)      |
# | for applicable conditions.                                                   |
# |                                                                              |
# | Source location: https://git.xythobuz.de/thomas/drumkit                      |
# |                                                                              |
# | As per CERN-OHL-S v2 section 4, should You produce hardware based on this    |
# | source, You must where practicable maintain the Source Location visible      |
# | on the external case of the Gizmo or other products you make using this      |
# | source.                                                                      |
#  ------------------------------------------------------------------------------

cd "$(dirname "$0")"

INFILE="lars2.kicad_pcb"
INFILE_SCH="lars2.kicad_sch"
OUTDIR="fabrication"
OUTZIP="fab"

echo "Creating output directory"
rm -rf $OUTDIR
mkdir -p $OUTDIR

echo "Exporting drill files"
#kicad-cli pcb export drill -o $OUTDIR/ --format excellon --generate-map --map-format pdf $INFILE
kicad-cli pcb export drill -o $OUTDIR/ --format gerber --generate-map --map-format gerberx2 $INFILE

echo "Exporting gerber files"
#kicad-cli pcb export gerbers -o $OUTDIR/ $INFILE
kicad-cli pcb export gerbers -o $OUTDIR/ -l F.Cu,B.Cu,F.Mask,B.Mask,F.Paste,B.Paste,F.Silkscreen,B.Silkscreen,Edge.Cuts $INFILE

echo "Exporting BOM files"
kicad-cli sch export python-bom -o $OUTDIR/bom.xml $INFILE_SCH

# TODO convert BOM XML to proper format for JLCPCB

echo "Compressing archive"
rm -rf $OUTZIP.zip
zip -r $OUTZIP fabrication

Here is an example kicad.yml file.

# SPDX-FileCopyrightText: 2024 Thomas Buck 
# SPDX-License-Identifier: CERN-OHL-S-2.0+
#
#  ------------------------------------------------------------------------------
# | Copyright (c) 2024 Thomas Buck                           |
# |                                                                              |
# | This source describes Open Hardware and is licensed under the CERN-OHL-S v2  |
# | or any later version.                                                        |
# |                                                                              |
# | You may redistribute and modify this source and make products using it under |
# | the terms of the CERN-OHL-S v2 (https://ohwr.org/cern_ohl_s_v2.txt)          |
# | or any later version.                                                        |
# |                                                                              |
# | This source is distributed WITHOUT ANY EXPRESS OR IMPLIED WARRANTY,          |
# | INCLUDING OF MERCHANTABILITY, SATISFACTORY QUALITY AND FITNESS FOR A         |
# | PARTICULAR PURPOSE. Please see the CERN-OHL-S v2 (or any later version)      |
# | for applicable conditions.                                                   |
# |                                                                              |
# | Source location: https://git.xythobuz.de/thomas/drumkit                      |
# |                                                                              |
# | As per CERN-OHL-S v2 section 4, should You produce hardware based on this    |
# | source, You must where practicable maintain the Source Location visible      |
# | on the external case of the Gizmo or other products you make using this      |
# | source.                                                                      |
#  ------------------------------------------------------------------------------

name: PCB

# build for each push and pull request
on: [push, pull_request]

jobs:
  fabrication:
    runs-on: ubuntu-latest

    permissions:
      contents: write

    steps:
      - name: Checkout repo
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Checkout repo submodules
        run: git submodule update --init

      - name: Install dependencies
        run: |
          sudo add-apt-repository --yes ppa:kicad/kicad-8.0-releases
          sudo apt update
          sudo apt install -y --install-recommends kicad
          sudo apt-get install -y zip

      - name: Generate fabrication files
        run: |
          ./pcb/generate_fab.sh
          ./pcb2/generate_fab.sh

      - name: Upload board files
        uses: actions/upload-artifact@v4.0.0
        with:
          name: prototype-pcb
          path: pcb/fabrication

      - name: Upload board files
        uses: actions/upload-artifact@v4.0.0
        with:
          name: v2-pcb
          path: pcb2/fabrication

      - name: Rename release files
        if: startsWith(github.ref, 'refs/tags/')
        run: |
          mv pcb/fab.zip fab_proto.zip
          mv pcb2/fab.zip fab_v2.zip

      - name: Upload release files
        if: startsWith(github.ref, 'refs/tags/')
        uses: softprops/action-gh-release@v1
        with:
          files: fab_proto.zip fab_v2.zip

8) PCB visualization

If you want to visualize KiCad schematics and PCBs in 2D and 3D, add pcb/generate_plot.sh:

Here is an example generate_plot.sh file.

#!/bin/bash

# SPDX-FileCopyrightText: 2024 Thomas Buck 
# SPDX-License-Identifier: CERN-OHL-S-2.0+
#
#  ------------------------------------------------------------------------------
# | Copyright (c) 2024 Thomas Buck                           |
# |                                                                              |
# | This source describes Open Hardware and is licensed under the CERN-OHL-S v2  |
# | or any later version.                                                        |
# |                                                                              |
# | You may redistribute and modify this source and make products using it under |
# | the terms of the CERN-OHL-S v2 (https://ohwr.org/cern_ohl_s_v2.txt)          |
# | or any later version.                                                        |
# |                                                                              |
# | This source is distributed WITHOUT ANY EXPRESS OR IMPLIED WARRANTY,          |
# | INCLUDING OF MERCHANTABILITY, SATISFACTORY QUALITY AND FITNESS FOR A         |
# | PARTICULAR PURPOSE. Please see the CERN-OHL-S v2 (or any later version)      |
# | for applicable conditions.                                                   |
# |                                                                              |
# | Source location: https://git.xythobuz.de/thomas/drumkit                      |
# |                                                                              |
# | As per CERN-OHL-S v2 section 4, should You produce hardware based on this    |
# | source, You must where practicable maintain the Source Location visible      |
# | on the external case of the Gizmo or other products you make using this      |
# | source.                                                                      |
#  ------------------------------------------------------------------------------

INSCH="lars2.kicad_sch"
INPCB="lars2.kicad_pcb"
OUTDIR="plot"
LAYER_F="F.Cu,F.Mask,F.Paste,F.Silkscreen,Edge.Cuts,User.Drawings"
LAYER_B="B.Cu,B.Mask,B.Paste,B.Silkscreen,Edge.Cuts,User.Drawings"
LAYER_MANUAL="B.Cu,Edge.Cuts"

cd "$(dirname "$0")"
rm -rf $OUTDIR
mkdir -p $OUTDIR

#  --------------
# | 2D Schematic |
#  --------------

for IN in $INSCH
do
    echo "Exporting schematic $IN"

    for TYPE in pdf svg
    do
        echo "Exporting schematic $TYPE"
        rm -rf $OUTDIR/$IN.$TYPE
        kicad-cli sch export $TYPE \
            -t "KiCad Default" \
            -o $OUTDIR/$IN.$TYPE \
            $IN
        echo
    done
done

for IN in $INPCB
do
    echo "Exporting board $IN"

    #  -----------
    # | 2D Layout |
    #  -----------

    for TYPE in pdf svg
    do
        rm -rf $OUTDIR/$IN.$TYPE

        echo "Exporting board $TYPE"
        kicad-cli pcb export $TYPE \
            -t "KiCad Classic"  \
            -l $LAYER_F,$LAYER_B \
            -o $OUTDIR/$IN.$TYPE \
            $IN
        echo

        echo "Exporting DIY board $VAR"
        rm -rf $OUTDIR/${IN}_diy.$TYPE
        kicad-cli pcb export $TYPE \
            -t "KiCad Default"  \
            -l $LAYER_MANUAL \
            --black-and-white \
            --negative \
            -o $OUTDIR/${IN}_diy.$TYPE \
            $IN
        echo
    done

    #  -----------
    # | 3D Layout |
    #  -----------
    echo "Exporting board 3D"
    kicad-cli pcb export vrml \
        -o $OUTDIR/$IN.wrl \
        $IN
    echo
done

Then include this where you want the 2D schematic visualization to appear:

You can also view the [schematics as PDF](./plot/lars2.kicad_sch.pdf).

{{#include inc_lars2.kicad_sch.md}}

And this where you want to visualize the 2D PCB layout:

You can also view the [2D PCB layout as PDF](./plot/lars2.kicad_pcb.pdf).

<script src="/js/svg-pan-zoom.js" charset="UTF-8"></script>
<div style="background-color: white; border: 1px solid black;">
    <embed type="image/svg+xml" src="/plot/lars2.kicad_pcb.svg" id="pz_drumkit0" style="width: 100%;"/>
    <script>
        document.getElementById('pz_drumkit0').addEventListener('load', function(){
            svgPanZoom(document.getElementById('pz_drumkit0'), {controlIconsEnabled: true, minZoom: 1.0});
        })
    </script>
</div>

[Direct link to this file](./plot/lars2.kicad_pcb.svg).

And add this where you want to visualize the 3D PCB layout:

{{#include inc_lars2.kicad_pcb.md}}

That should more or less be all that's required.

Of course, adapt paths as needed to match your project layout. And test locally by calling the scripts manually on your machine:

./pcb/generate_plot.sh
./3dprint/generate_stls.sh

./docs/generate_docs.sh serve

Hope this helps.