diff --git a/.gitignore b/.gitignore
index 02adc87..cebb29c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,4 +5,4 @@
fp-info-cache
.cache
*-backups/
-
+build/
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..4a53fb7
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,31 @@
+.PHONY: web
+
+# inspired by https://github.com/RoboticsBrno/RB0002-BatteryPack/blob/master/Makefile
+
+
+BOARDS = epdiy-v7 extension-cable
+
+web: build/web/index.html
+
+.SECONDEXPANSION:
+build/boards/%: $$*/$$*.kicad_pcb $$*/$$*.kicad_sch
+ mkdir -p build/boards
+ kicad-cli sch export pdf $(word 2,$^) -o $@_schematic.pdf
+ kicad-cli sch export python-bom $(word 2,$^) -o $@_BoM.xml
+ xsltproc -o $@_BoM.csv present/bom2grouped_csv_jlcpcb.xsl $@_BoM.xml
+ #
+ kicad-cli pcb export pos $< --side front --format csv --units mm -o $@_top_pos.csv
+ kicad-cli pcb export step --subst-models $< -o $@_model.step
+ xz $@_model.step
+ touch $@
+
+build/web/index.html: README.md present/template/index.html $(addprefix build/boards/,$(BOARDS))
+ mkdir -p build/web
+ kikit present boardpage \
+ -d $< \
+ --name "EPDiy" \
+ $(shell for board in ${BOARDS}; do echo -n "-b" $$board "\"\$$(cmark $$board/README.md)\"" "$$board/$$board.kicad_pcb " ; done) \
+ --repository 'https://github.com/vroland/epdiy-hardware' \
+ --template present/template/ \
+ $$(for f in build/boards/*; do echo "-r $$f"; done) \
+ build/web
diff --git a/README.md b/README.md
index 09b6849..c698b13 100644
--- a/README.md
+++ b/README.md
@@ -1,28 +1,20 @@
-## EPDiy Hardware
+# EPDiy Hardware
Design files for [EPDiy](https://github.com/vroland/epdiy) controllers, controller variants, display adaptors, and other related hardware.
-#### What is EPDiy?
-
+## What is EPDiy?
Please refer to the [main project repository](https://github.com/vroland/epdiy) for more information.
-#### Cloning
+## Cloning
Make sure to clone recursively, to get hardware library submodules:
```
git clone git@github.com:vroland/epdiy.git --recursive
```
-### Available Hardware
-
-#### Epdiy V7
-
-At `epdiy-v7`.
+## Available Hardware
-The V7 controller iteration is based on the ESP32S3. Highlights of the new board include:
-* Support for high-resolution displays with 16 data lines
-* Much faster updates thanks to the ESP32S3 SOC and the LCD peripheral
-* An on-board RTC, LiPo charging
+TODO: Link.
- Creative Commons Attribution-ShareAlike 4.0 International License.
+Creative Commons Attribution-ShareAlike 4.0 International License
diff --git a/epdiy-v7/README.md b/epdiy-v7/README.md
new file mode 100644
index 0000000..7d3e5f0
--- /dev/null
+++ b/epdiy-v7/README.md
@@ -0,0 +1,7 @@
+### Epdiy V7
+
+The V7 controller iteration is based on the ESP32S3. Highlights of the new board include:
+
+- Support for high-resolution displays with 16 data lines
+- Much faster updates thanks to the ESP32S3 SOC and the LCD peripheral
+- An on-board RTC, LiPo charging
diff --git a/extension-cable/README.md b/extension-cable/README.md
new file mode 100644
index 0000000..8a2152c
--- /dev/null
+++ b/extension-cable/README.md
@@ -0,0 +1,3 @@
+### 33 Pin Extension Cable
+
+Extension cable for 33 pin FPC connections.
diff --git a/present/bom2grouped_csv_jlcpcb.xsl b/present/bom2grouped_csv_jlcpcb.xsl
new file mode 100644
index 0000000..7257b3b
--- /dev/null
+++ b/present/bom2grouped_csv_jlcpcb.xsl
@@ -0,0 +1,104 @@
+
+
+
+
+
+]>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Comment,Designator,Footprint,LCSC
+
+
+
+
+
+
+
+
+ &nl;
+ "","
+
+
+
+
+
+ ,
+
+ ","
+
+ ","
+ "
+
+
+
+
+
+
+
+
+
+
+
+
+ ,"
+
+
+
+
+
+
+
+
+
+
+
+
+ "
+
+
+
+
diff --git a/present/template/index.html b/present/template/index.html
new file mode 100644
index 0000000..7f86c7b
--- /dev/null
+++ b/present/template/index.html
@@ -0,0 +1,62 @@
+
+
+
+
+
+
+
+ {{name}}
+
+
+
+
+
+ Last update: {{datetime}}
+ {{#if gitRev}}(git revision
+ {{#if repo}}
{{/if}}
+ {{gitRevShort}}
+ {{#if repo}}{{/if}})
+ {{/if}}
+
+
+ {{{description}}}
+
+
Available Boards
+
+ {{#each boards}}
+
+
{{{this.comment}}}
+
+
+
Front
+
+
+
+
Back
+
+
+
+
+
+ {{/each}}
+
+
+
+
+
+
diff --git a/present/template/style.css b/present/template/style.css
new file mode 100644
index 0000000..26781c0
--- /dev/null
+++ b/present/template/style.css
@@ -0,0 +1,314 @@
+/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */
+html{
+ line-height:1.15;
+ -webkit-text-size-adjust:100%
+}
+body{
+ margin:0
+}
+h1{
+ font-size:2em;
+ margin:.67em 0
+}
+pre{
+ font-family:monospace,monospace;
+ font-size:1em
+}
+a{
+ background-color:transparent
+}
+b,strong{
+ font-weight:bolder
+}
+code{
+ font-family:monospace,monospace;
+ font-size:1em
+}
+small{
+ font-size:80%
+}
+img{
+ border-style:none
+}
+input{
+ font-family:inherit;
+ font-size:100%;
+ line-height:1.15;
+ margin:0
+}
+input{
+ overflow:visible
+}
+[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{
+ height:auto
+}
+[type=search]{
+ -webkit-appearance:textfield;
+ outline-offset:-2px
+}
+[type=search]::-webkit-search-decoration{
+ -webkit-appearance:none
+}
+[hidden]{
+ display:none
+}
+h1,h2,h3,h4,h5,h6,p,pre{
+ margin:0
+}
+ul{
+ margin:0;
+ padding:0
+}
+ul{
+ list-style:none
+}
+html{
+ font-family:system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;
+ line-height:1.5
+}
+*{
+ box-sizing:border-box;
+ border:0 solid #e2e8f0
+}
+img{
+ border-style:solid
+}
+input::-webkit-input-placeholder{
+ color:#a0aec0
+}
+input::-moz-placeholder{
+ color:#a0aec0
+}
+input:-ms-input-placeholder{
+ color:#a0aec0
+}
+input::-ms-input-placeholder{
+ color:#a0aec0
+}
+input::placeholder{
+ color:#a0aec0
+}
+table{
+ border-collapse:collapse
+}
+h1,h2,h3,h4,h5,h6{
+ font-size:inherit;
+ font-weight:inherit
+}
+a{
+ color:inherit;
+ text-decoration:inherit
+}
+input{
+ padding:0;
+ line-height:inherit;
+ color:inherit
+}
+code,pre{
+ font-family:Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace
+}
+.container{
+ width:100%
+}
+@media (min-width:640px){
+ .container{
+ max-width:640px
+ }
+}
+@media (min-width:768px){
+ .container{
+ max-width:768px
+ }
+}
+@media (min-width:1024px){
+ .container{
+ max-width:1024px
+ }
+}
+@media (min-width:1280px){
+ .container{
+ max-width:1280px
+ }
+}
+.bg-gray-200{
+ background-color:#edf2f7
+}
+.rounded{
+ border-radius:.25rem
+}
+.block{
+ display:block
+}
+.inline{
+ display:inline
+}
+.flex{
+ display:flex
+}
+.table{
+ display:table
+}
+.hidden{
+ display:none
+}
+.flex-wrap{
+ flex-wrap:wrap
+}
+.flex-auto{
+ flex:1 1 auto
+}
+.clearfix:after{
+ content:"";
+ display:table;
+ clear:both
+}
+.my-2{
+ margin-top:.5rem;
+ margin-bottom:.5rem
+}
+.my-3{
+ margin-top:.75rem;
+ margin-bottom:.75rem
+}
+.mx-auto{
+ margin-left:auto;
+ margin-right:auto
+}
+.mb-1{
+ margin-bottom:.25rem
+}
+.mt-16{
+ margin-top:4rem
+}
+.overflow-auto{
+ overflow:auto
+}
+.overflow-hidden{
+ overflow:hidden
+}
+.p-2{
+ padding:.5rem
+}
+.py-1{
+ padding-top:.25rem;
+ padding-bottom:.25rem
+}
+.py-3{
+ padding-top:.75rem;
+ padding-bottom:.75rem
+}
+.px-3{
+ padding-left:.75rem;
+ padding-right:.75rem
+}
+.px-4{
+ padding-left:1rem;
+ padding-right:1rem
+}
+.fixed{
+ position:fixed
+}
+.absolute{
+ position:absolute
+}
+.relative{
+ position:relative
+}
+.top-0{
+ top:0
+}
+.right-0{
+ right:0
+}
+.bottom-0{
+ bottom:0
+}
+.left-0{
+ left:0
+}
+.text-center{
+ text-align:center
+}
+.text-right{
+ text-align:right
+}
+.text-sm{
+ font-size:.875rem
+}
+.w-full{
+ width:100%
+}
+body{
+ font-size:1rem;
+ font-weight:400;
+ color:#718096
+}
+h1{
+ font-size:1.875rem
+}
+h1,h2{
+ font-weight:700;
+ font-family:system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;
+ overflow-wrap:normal;
+ word-break:normal;
+ color:#1a202c;
+ padding-top:1.5rem;
+ padding-bottom:.5rem
+}
+h2{
+ font-size:1.5rem
+}
+h3{
+ font-weight:700;
+ font-family:system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;
+ overflow-wrap:normal;
+ word-break:normal;
+ color:#1a202c;
+ padding-top:1.5rem;
+ padding-bottom:.5rem;
+ font-size:1.25rem
+}
+p{
+ text-align:justify
+}
+a{
+ color:#63b3ed
+}
+a:hover{
+ color:#2b6cb0
+}
+ul{
+ list-style-type:disc;
+ list-style-position:inside;
+ margin-top:.25rem;
+ margin-bottom:.25rem
+}
+code{
+ padding:.25rem
+}
+code,pre code{
+ background-color:#edf2f7
+}
+pre code{
+ display:block;
+ margin-top:1rem;
+ margin-bottom:1rem;
+ padding:1rem .75rem;
+ width:100%
+}
+
+.boardPreview{
+ display:block;
+ max-width:100%;
+ max-height:400px;
+ width:auto;
+ height:auto;
+ margin-left:auto;
+ margin-right:auto
+}
+@media (min-width:768px){
+ .md\:w-1\/3{
+ width:33.333333%
+ }
+}
+
diff --git a/present/template/template.json b/present/template/template.json
new file mode 100644
index 0000000..7a88dac
--- /dev/null
+++ b/present/template/template.json
@@ -0,0 +1,4 @@
+{
+ "type": "HtmlTemplate",
+ "resources": ["*.css"]
+}