stefankoelle initial

This commit is contained in:
2026-08-14 12:26:58 +02:00
parent da8acc849a
commit 02cdd9c6f4
50 changed files with 2033 additions and 74 deletions
+6
View File
@@ -3,8 +3,14 @@ name: Build & Deploy moonweb-site
on:
push:
branches: [main]
paths-ignore:
- 'stefankoelle/**'
- '.github/workflows/deploy-stefankoelle.yml'
pull_request:
branches: [main]
paths-ignore:
- 'stefankoelle/**'
- '.github/workflows/deploy-stefankoelle.yml'
# Cancel in-progress runs for the same branch
concurrency:
+107
View File
@@ -0,0 +1,107 @@
name: Build & Deploy stefankoelle.de
on:
push:
branches: [main]
paths:
- 'stefankoelle/**'
- 'shared/**'
- '.github/workflows/deploy-stefankoelle.yml'
pull_request:
branches: [main]
paths:
- 'stefankoelle/**'
- 'shared/**'
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
NODE_VERSION: "22"
jobs:
build:
name: Build stefankoelle.de
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v7
with:
node-version: ${{ env.NODE_VERSION }}
cache: "npm"
- name: Install dependencies
run: npm ci
- name: Build stefankoelle
run: npm run build:stefankoelle
- name: Validate build output
run: |
if [ ! -d "dist/stefankoelle" ]; then
echo "Error: dist/stefankoelle not found"
exit 1
fi
if [ -z "$(ls -A dist/stefankoelle 2>/dev/null)" ]; then
echo "Error: dist/stefankoelle is empty"
exit 1
fi
echo "Build output:"
find dist/stefankoelle -type f | head -20
- name: Upload build artifact
uses: actions/upload-artifact@v7
with:
name: dist-stefankoelle
path: dist/stefankoelle
retention-days: 7
deploy:
name: Deploy stefankoelle.de
needs: build
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
steps:
- name: Download build artifact
uses: actions/download-artifact@v8
with:
name: dist-stefankoelle
path: dist/stefankoelle
- name: Deploy via SFTP to IONOS
uses: wlixcc/sftp-deploy-action@v1
with:
local_path: './dist/stefankoelle/*'
server: ${{ secrets.IONOS_SFTP_HOST }}
username: ${{ secrets.IONOS_SFTP_USER }}
password: ${{ secrets.IONOS_SFTP_PASSWORD }}
server_path: '/deploy/stefankoelle/'
port: 22
summary:
name: Deploy Summary
needs: deploy
runs-on: ubuntu-latest
if: always()
steps:
- name: Print deployment summary
run: |
echo "## Deployment Summary" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
if [ "${{ needs.deploy.result }}" = "success" ]; then
echo "✅ stefankoelle.de deployed successfully!" >> $GITHUB_STEP_SUMMARY
else
echo "❌ Deployment failed. Check the deploy job for details." >> $GITHUB_STEP_SUMMARY
fi
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Site" >> $GITHUB_STEP_SUMMARY
echo "| Site | URL |" >> $GITHUB_STEP_SUMMARY
echo "|------|-----|" >> $GITHUB_STEP_SUMMARY
echo "| stefankoelle | https://stefankoelle.de |" >> $GITHUB_STEP_SUMMARY
# Required repo secrets:
# IONOS_SFTP_HOST — IONOS SFTP server hostname
# IONOS_SFTP_USER — IONOS SFTP username
# IONOS_SFTP_PASSWORD — IONOS SFTP password
+60 -67
View File
@@ -2,9 +2,9 @@
## Projektübersicht
Monorepo für 5 statische Websites unter moonweb.org, basierend auf Eleventy (11ty), deployt auf Cloudflare Pages.
Monorepo für 5 statische Websites unter moonweb.org + stefankoelle.de, basierend auf Eleventy (11ty).
### Websites
### Websites (Cloudflare Pages)
| Site | Domain | Zweck | Status |
|------|--------|-------|--------|
@@ -14,82 +14,76 @@ Monorepo für 5 statische Websites unter moonweb.org, basierend auf Eleventy (11
| code | code.moonweb.org | GitHub-Projekte (aggregiert via .moonweb.yml) | Fertig (10 Repos) |
| retro | retro.moonweb.org | Physische Retro-Hardware | Nur Übersicht (WIP) |
### Externe Sites (nicht im Monorepo)
### Externe Sites (SFTP-Deployment)
| Site | Domain | Zweck | Status |
|------|--------|-------|--------|
| stefankoelle | stefankoelle.de | Lebenslauf, Kontakt, Projekte | Onepager + LED-Matrix-Detailseite |
### Andere Sites (nicht im Monorepo)
- stefankoelle.de — Lebenslauf, Kontakt (extern gehostet)
- www.moonweb.org — 2000er Internet-Zeitkapsel
- 28k8.moonweb.org — 90er BBS/Scene-Archiv
## Dateistruktur
```
moonweb-site/
├── hub/ # Eleventy-Config + index.njk
├── infra/ # Eleventy-Config + index.njk + 3 Subseiten
│ ├── backup-strategy/
│ ├── monitoring/
│ └── dev-workflow/
├── smarthome/ # Eleventy-Config + index.njk + 6 Subseiten
│ ├── homematic-mqtt/
│ ├── tasmota-energy/
│ ├── balkonpi/
│ ├── airplay-audio/
│ ├── octoprint/
│ └── tubearchivist/
├── code/ # Eleventy-Config + index.njk
│ └── _data/repos.json # Vom GitHub-Aggregator generiert
│ └── _data/repos.json
├── retro/ # Eleventy-Config + index.njk
├── stefankoelle/ # Eleventy-Config + Onepager
│ ├── eleventy.config.js
│ ├── _includes/
│ │ ├── stefankoelle.njk # Layout (Header, Nav, Footer)
│ │ └── cv-content.njk # CV-Partial (zentral fuer Web + PDF)
│ ├── index.njk # Onepager (alle Sections)
│ ├── cv-print.njk # CV-only fuer spaeteren PDF-Druck
│ ├── assets/ # CSS, JS, Bilder, Favicons
│ ├── ledmatrix/ # LED Matrix WebServer Dokumentation
│ └── pdf/ # PDF-CSS (fuer spaeteren WeasyPrint-Bau)
├── shared/ # Gemeinsame Komponenten
│ ├── _includes/
│ │ ├── base.njk # Basis-Layout (Header, Site-Switcher, Footer)
│ │ └── card-grid.njk # Card-Grid Template
│ ├── base.css # Basis-CSS (Layout, Cards, Typography)
│ └── theme-*.css # Accent-Farben pro Site
│ ├── base.css
│ └── theme-*.css
├── scripts/
│ └── github-aggregator/ # Python-Skript für code.moonweb.org
│ ├── aggregate.py
│ ├── example.moonweb.yml
│ └── README.md
│ └── github-aggregator/
├── .github/workflows/
── build-deploy.yml # CI/CD: Build + Deploy zu Cloudflare Pages
├── DESIGN.md # Initiales Konzept mit Design-Entscheidungen (deutsch)
├── SPEC.md # Vollständige Spezifikation (148 Zeilen)
├── PLAN.md # Implementierungsplan (65 Zeilen)
├── README.md # Projekt-README
├── TODO.md # Offene Items + Workflow
── package.json # npm scripts für dev/build
── build-deploy.yml # CI/CD: Cloudflare Pages (5 Sites)
│ └── deploy-stefankoelle.yml # CI/CD: IONOS SFTP (stefankoelle.de)
├── DESIGN.md
├── SPEC.md
├── PLAN.md
├── README.md
── TODO.md
└── package.json
```
## Dokumentation
- `DESIGN.md` — Initiales Konzept mit Design-Entscheidungen (deutsch)
- `SPEC.md` — Vollständige Spezifikation (148 Zeilen)
- `PLAN.md` — Implementierungsplan (65 Zeilen)
- `README.md` — Projekt-README
- `TODO.md` — Offene Items + Workflow für weitere Arbeit
- `TODO.md` — Offene Items + Workflow für weitere Arbeit
## Technischer Stack
- **SSG:** Eleventy (11ty) v3.1.6
- **Templates:** Nunjucks (.njk)
- **CSS:** Variables-basiert mit Accent-Farben pro Site
- **Deploy:** Cloudflare Pages (5 separate Projects)
- **CI/CD:** GitHub Actions
- **GitHub-Aggregator:** Python (liest .moonweb.yml aus Repos)
- **Deploy:** Cloudflare Pages (5 separate Projects)
- **Deploy (moonweb):** Cloudflare Pages (5 separate Projects)
- **Deploy (stefankoelle):** IONOS SFTP
- **CI/CD:** GitHub Actions (2 Workflows)
## Entwicklung
```bash
npm install
npm run dev:hub # localhost:8081
npm run dev:infra # localhost:8082
npm run dev:smarthome # localhost:8083
npm run dev:code # localhost:8084
npm run dev:retro # localhost:8085
npm run build # Alle Sites bauen
npm run dev:hub # localhost:8081
npm run dev:infra # localhost:8082
npm run dev:smarthome # localhost:8083
npm run dev:code # localhost:8084
npm run dev:retro # localhost:8085
npm run dev:stefankoelle # localhost:8086
npm run build # Alle Sites bauen
```
## Design-Prinzipien
@@ -101,36 +95,35 @@ npm run build # Alle Sites bauen
5. **Keine Analytics** — Keine Tracking-Tools
6. **Sensible Daten** — Infra-Content wird manuell redigiert (keine IPs, Keys, Passwörter)
## Content-Regeln (SPEC.md §5)
## stefankoelle.de
| Site | Detailseiten? | Regel |
|------|---------------|-------|
| smarthome | Ja | Nur wenn genug Content vorhanden |
| code | Nie | Nur Übersicht + GitHub-Links |
| infra | Kaum | Bewusst oberflächlich (sensible Daten) |
| retro | Ja, minimal | Rudimentär, WIP erlaubt |
## GitHub-Aggregator
`scripts/github-aggregator/aggregate.py` liest `.moonweb.yml` aus allen public Repos von `skoelle` und schreibt `code/_data/repos.json`. Manuell ausführen:
```bash
export GITHUB_TOKEN=ghp_xxx
python scripts/github-aggregator/aggregate.py
```
- Eigene Eleventy-Config (nicht shared base.njk)
- Eigenes CSS-Design (nicht moonweb design system)
- Onepager mit Anchor-Links (bleibt so)
- Deploy via SFTP auf IONOS `/deploy/stefankoelle/`
- CV-Partial zentral pflegbar (einmal aendern → Web + PDF aktualisieren)
- LED Matrix als eigene Seite unter stefankoelle.de/ledmatrix/
## CI/CD
`.github/workflows/build-deploy.yml` baut alle 5 Sites (hub, infra, smarthome, code, retro) und deployt sie zu Cloudflare Pages.
### Cloudflare Pages (moonweb)
`.github/workflows/build-deploy.yml` baut hub, infra, smarthome, code, retro.
Benötigte Secrets:
- `CLOUDFLARE_API_TOKEN`
- `CLOUDFLARE_ACCOUNT_ID`
### IONOS SFTP (stefankoelle.de)
`.github/workflows/deploy-stefankoelle.yml` baut stefankoelle.de und deployed per SFTP.
Benötigte Secrets:
- `IONOS_SFTP_HOST`
- `IONOS_SFTP_USER`
- `IONOS_SFTP_PASSWORD`
## Offene Punkte
- retro/ ist bewusst rudimentär gehalten
- Einige Cards in smarthome/ und infra/ verlinken noch auf "#" (Placeholder)
- Deploy erfolgt noch nicht automatisch (Phase 3 ausstehend)
- stefankoelle.de ist extern gehostet, wird nur verlinkt (nicht im Monorepo)
- PDF-Build fuer CV (WeasyPrint) — lokal testen, spaeter in CI integrieren
- Querverlinkungen stefankoelle.de <-> smarthome (zukuenftig)
- Ledmatrix ggf. nach smarthome verschieben (when ready)
+23 -5
View File
@@ -1,6 +1,6 @@
# TODO — moonweb-site Offene Items
Stand: 2026-08-13
Stand: 2026-08-14
## Aktueller Status
@@ -10,20 +10,34 @@ Stand: 2026-08-13
| Phase 1 — Content | ✅ fertig | hub, code, infra, smarthome komplett, retro bewusst rudimentär |
| Phase 2 — CI/CD | ✅ fertig | build-deploy.yml mit Caching + Validation + Summary |
| Phase 3 — Launch | ✅ fertig | Cloudflare Pages live, Smoke-Test bestanden |
| Phase 4 — Deferred | ⏸️ zurückgestellt | Ledmatrix-Migration, 28k8, Analytics |
| Phase 4 — Deferred | ⏸️ zurückgestellt | 28k8, Analytics |
| Phase 5 — stefankoelle.de | ✅ fertig | Ins Monorepo migriert, Eleventy-Build, SFTP-Deployment |
## Offene Items
### P4 — Deferred (nicht dringend)
- [ ] Ledmatrix-Migration nach smarthome
- [ ] 28k8.moonweb.org Zukunft klären
- [ ] Perplexity-Rückkanal
- [ ] Apex-Domain Redirect (moonweb.org → hub.moonweb.org)
- [ ] stefankoelle.de in Monorepo portieren
- [ ] PDF-Build fuer CV (WeasyPrint, lokal testen)
- [ ] Querverlinkungen stefankoelle.de <-> smarthome
- [ ] Ledmatrix ggf. nach smarthome verschieben
## Schon erledigt (Stand 2026-08-13)
## Schon erledigt (Stand 2026-08-14)
### stefankoelle.de Migration (2026-08-14)
- [x] Eleventy-Config + package.json erweitert
- [x] Onepager-Layout mit Anchor-Links beibehalten
- [x] CV-Partial (zentral fuer Web + PDF)
- [x] CSS ausgelagert + modernisiert (Custom Properties, box-sizing)
- [x] Assets in assets/ verschoben
- [x] Ledmatrix als eigene Seite integriert
- [x] Alte Dateien geloescht (index.html, browserconfig.xml, sitemap.txt)
- [x] Deployment-Workflow fuer IONOS SFTP
- [x] Cloudflare-Workflow um stefankoelle erweitert (paths-ignore)
### Vorherige Erledigungen (Stand 2026-08-13)
- [x] Impressum angelegt + Footer-Link auf allen Sites
- [x] Favicons (SVG mit Emojis) pro Subdomain — `shared/favicon/*.svg`, per Passthrough Copy
- [x] Meta descriptions auf allen 33 Seiten
@@ -51,3 +65,7 @@ Alle 5 Sites live auf Cloudflare Pages:
- ✅ smarthome.moonweb.org — Smart Home Übersicht + Detailseiten
- ✅ infra.moonweb.org — Infra-Übersicht (redigiert)
- ✅ retro.moonweb.org — Ehrliche minimale Übersicht (WIP)
stefankoelle.de live auf IONOS (SFTP-Deployment):
- ✅ stefankoelle.de — Onepager mit CV, Projects, Languages, Impressum
- ✅ stefankoelle.de/ledmatrix/ — LED Matrix WebServer Dokumentation
+4 -2
View File
@@ -9,13 +9,15 @@
"dev:smarthome": "npx @11ty/eleventy --config=smarthome/eleventy.config.js --serve --port=8083",
"dev:code": "npx @11ty/eleventy --config=code/eleventy.config.js --serve --port=8084",
"dev:retro": "npx @11ty/eleventy --config=retro/eleventy.config.js --serve --port=8085",
"dev": "npm run dev:hub & npm run dev:infra & npm run dev:smarthome & npm run dev:code & npm run dev:retro",
"dev:stefankoelle": "npx @11ty/eleventy --config=stefankoelle/eleventy.config.js --serve --port=8086",
"dev": "npm run dev:hub & npm run dev:infra & npm run dev:smarthome & npm run dev:code & npm run dev:retro & npm run dev:stefankoelle",
"build:hub": "npx @11ty/eleventy --config=hub/eleventy.config.js",
"build:infra": "npx @11ty/eleventy --config=infra/eleventy.config.js",
"build:smarthome": "npx @11ty/eleventy --config=smarthome/eleventy.config.js",
"build:code": "npx @11ty/eleventy --config=code/eleventy.config.js",
"build:retro": "npx @11ty/eleventy --config=retro/eleventy.config.js",
"build": "npm run build:hub && npm run build:infra && npm run build:smarthome && npm run build:code && npm run build:retro"
"build:stefankoelle": "npx @11ty/eleventy --config=stefankoelle/eleventy.config.js",
"build": "npm run build:hub && npm run build:infra && npm run build:smarthome && npm run build:code && npm run build:retro && npm run build:stefankoelle"
},
"devDependencies": {
"@11ty/eleventy": "^3.1.6"
+97
View File
@@ -0,0 +1,97 @@
<div id="cvcontent">
<h2>CV</h2>
<div class="contact-info">
<h3>Stefan Kölle</h3>
<p>Senior Software Developer & Software Architect<br>
Neumarkter Str. 86c, 81673 München<br>
E-Mail: <a id="email-link" href="#">[email protected]</a></p>
</div>
<div class="profile">
<h3>Profile</h3>
<p>Experienced Senior Software Developer and Software Architect with over 20 years of experience in developing and managing microservices and IT architectures. Specialized in .NET Core and Kubernetes, with hands-on experience in AI-assisted development using Claude Code. Clear focus on efficient, innovative, and sustainable solutions. Highly skilled in executing complex projects, building cloud-native services, and optimizing processes through modern technologies.</p>
</div>
<div class="core-technologies">
<h3>Core Technologies</h3>
<ul>
<li>.NET Core 10 (Dockerized), Clean Architecture</li>
<li>Claude Code / AI-assisted Development</li>
<li>SolR, Kubernetes, Terraform, Azure (WebApps, Tables, Blobs)</li>
<li>Uptrends, Sentry, Loki/Grafana, Quartz.NET</li>
<li>Squidex, Brevo (Sendinblue), Mailgun, OpenAI</li>
</ul>
</div>
<h3>Senior Software Developer / Senior Software Architect</h3>
<p><strong>TENHIL GmbH & Co. KG</strong> (2025 - present)</p>
<ul>
<li><b>MCP-Server for ChatGPT Integration (2026)</b> Built an MCP server for a major platform integration using Claude Code, from concept to production in 6 half-days solo. Full spec, Confluence documentation, Jira epics and stories, and PO artifacts fully generated within the same workflow.</li>
<li><b>AI-driven Project Documentation (2026)</b> Using Claude Code to generate complete project documentation end-to-end, including Confluence pages, Jira epics and stories, covering the full PO process without manual overhead.</li>
<li><b>AI-Tooling Repository (2026):</b> Built a structured AI-assisted development framework for the .NET team, providing reusable workflows for plan-and-implement processes, QA testing, and code reviews. Presented at internal Demo Day with full documentation.</li>
<li>Regular presenter at internal monthly Demo Days, sharing AI tooling advances and new project outcomes with the engineering organization.</li>
<li><b>Kubernetes Migration:</b> Migrating background workers from Azure Docker to Kubernetes, including job restart logic, state persistence across runs, and monitoring.</li>
<li><b>AI-assisted Development Workflow:</b> Established a production-ready Claude Code workflow for rapid delivery, reducing time-to-production for new services significantly.</li>
<li><b>Infrastructure Cost Optimization (2025)</b> Reduced monthly infrastructure costs by €1,850 through strategic decommissioning of legacy systems and cloud routing migration, delivering ~€22,000 in annual savings.</li>
<li><b>QA Automation Framework</b> Built a scalable integration test system enabling continuous, code-based QA coverage growth and cross-codebase bug detection across multiple services.</li>
<li><b>AI Workshop</b> Organized and led an internal team workshop on AI-assisted search query parsing and natural language processing for job search optimization.</li>
<li><b>Active Services Migration to Kubernetes (2025)</b> Planned migration of active production services from legacy hosting to Kubernetes, including documentation and architectural concepts.</li>
<li>Consistently delivering full-time output while working part-time, recognized across multiple product teams for high productivity and reliable delivery.</li>
<li><b>Technologiestack:</b> .NET Core, Kubernetes, Docker, TypeScript, Claude Code, OpenAI, Terraform, Sentry, Loki/Grafana.</li>
</ul>
<p><strong>TENHIL GmbH & Co. KG</strong> (08/2022 - 2024)</p>
<ul>
<li><b>OpenAI Integration (2024)</b> Integrated OpenAI-based salary data generation into a multi-tenant microservice, enabling automated, AI-driven content enrichment at scale.</li>
<li>Solely responsible for designing and building multiple core backend microservices end-to-end, including application services, geo data, feed generation, salary data, and email delivery integrations.</li>
<li>Collaborating closely with the Product Owner to create and implement new services.</li>
<li>Technologies: .NET Core 10, Docker, Azure, Terraform, Uptrends, Sentry, Loki, Quartz.NET.</li>
</ul>
<h3>Senior Software Developer / Senior Software Architect</h3>
<p><strong>stellenanzeigen.de GmbH & Co. KG</strong> (11/2018 - 07/2022)</p>
<ul>
<li>Architecting hosting solutions and migrating services to Azure.</li>
<li>Implementing a CMS based on Squidex and managing server infrastructure.</li>
</ul>
<h3>Managing Director</h3>
<p><strong>Regio-Jobanzeiger GmbH & Co. KG</strong> (10/2014 - 12/2017)</p>
<h3>Managing Director, CTO</h3>
<p><strong>stellenanzeigen.de GmbH & Co. KG</strong> (01/2011 - 10/2018)</p>
<ul>
<li>Reorganized the company, focusing on performance marketing and core products.</li>
<li>Implemented the "SmartReach" performance marketing product.</li>
<li>Significant projects: Multi-brand strategy, founding "Yourfirm" and "Regio Jobanzeiger", integrating "Kalaydo".</li>
</ul>
<h3>CTO, Member of Executive Management</h3>
<p><strong>stellenanzeigen.de GmbH & Co. KG</strong> (01/2004 - 12/2010)</p>
<ul>
<li>Led product management, software development, IT, customer service, and HTML ad creation.</li>
<li>Introduced agile software development and virtualized all physical servers.</li>
</ul>
<h3>Head of IT / Development</h3>
<p><strong>stellenanzeigen.de GmbH & Co. KG</strong> (06/2000 - 12/2003)</p>
<ul>
<li>Developed a multi-client web frontend and restructured the ad creation process.</li>
</ul>
<h3>Web Developer</h3>
<p><strong>IXOS AG</strong> (05/1999 - 05/2000)</p>
<ul>
<li>Managed web development environment and internal departmental websites.</li>
</ul>
<h3>Education</h3>
<p><strong>FH Augsburg</strong> - Computer Science (1996 - 1999)</p>
<h3>Internships</h3>
<p><strong>IXOS AG</strong> - Working Student (10/1997 - 04/1999)</p>
<ul>
<li>Redesigned the Jukeman website and prepared training rooms for R/3 training.</li>
</ul>
</div>
+87
View File
@@ -0,0 +1,87 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="theme-color" content="#ffffff">
<title>{% if title %}{{ title }}{% else %}Stefan Koelle{% endif %}</title>
<meta name="description" content="{{ description }}">
<link rel="apple-touch-icon" sizes="57x57" href="/assets/apple-icon-57x57.png">
<link rel="apple-touch-icon" sizes="60x60" href="/assets/apple-icon-60x60.png">
<link rel="apple-touch-icon" sizes="72x72" href="/assets/apple-icon-72x72.png">
<link rel="apple-touch-icon" sizes="76x76" href="/assets/apple-icon-76x76.png">
<link rel="apple-touch-icon" sizes="114x114" href="/assets/apple-icon-114x114.png">
<link rel="apple-touch-icon" sizes="120x120" href="/assets/apple-icon-120x120.png">
<link rel="apple-touch-icon" sizes="144x144" href="/assets/apple-icon-144x144.png">
<link rel="apple-touch-icon" sizes="152x152" href="/assets/apple-icon-152x152.png">
<link rel="apple-touch-icon" sizes="180x180" href="/assets/apple-icon-180x180.png">
<link rel="icon" type="image/png" sizes="192x192" href="/assets/android-icon-192x192.png">
<link rel="icon" type="image/png" sizes="32x32" href="/assets/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="96x96" href="/assets/favicon-96x96.png">
<link rel="icon" type="image/png" sizes="16x16" href="/assets/favicon-16x16.png">
<link rel="manifest" href="/assets/manifest.json">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/css/all.min.css">
<meta name="msapplication-TileColor" content="#ffffff">
<meta name="msapplication-TileImage" href="/assets/ms-icon-144x144.png">
<link rel="stylesheet" href="/assets/style.css">
</head>
<body class="blog">
<div id="container">
<div id="wrapper-content">
<div id="links">
<a href="#home" class="separator"><i class="fa-solid fa-home"></i></a>
<a href="#cv" class="separator">CV</a>
<a href="#personalprojects" class="separator">Personal Projects</a>
<a href="#programminglanguages" class="separator">Programming Languages</a>
<a href="#impressum">Impressum</a>
<div class="flex-break"></div>
<a href="https://www.linkedin.com/in/stefankoelle/" target="_blank"><i class="fa-brands fa-linkedin"></i></a>
<a href="https://www.xing.com/profile/Stefan_Koelle" target="_blank"><i class="fa-brands fa-xing"></i></a>
<a href="https://github.com/skoelle" target="_blank"><i class="fa-brands fa-github"></i></a>
<a href="https://sourceforge.net/u/skoelle/profile/" target="_blank"><i class="fa-solid fa-project-diagram"></i></a>
<a href="https://stackoverflow.com/users/25233/stefan-koelle" target="_blank"><i class="fa-brands fa-stack-overflow"></i></a>
<a href="https://www.reddit.com/user/stefankoelle/" target="_blank"><i class="fa-brands fa-reddit"></i></a>
<a href="https://www.youtube.com/@stefankoelle" target="_blank"><i class="fa-brands fa-youtube"></i></a>
<a href="https://soundcloud.com/stefan-koelle" target="_blank"><i class="fa-brands fa-soundcloud"></i></a>
<a href="https://www.mixcloud.com/stefan-koelle/" target="_blank"><i class="fa-brands fa-mixcloud"></i></a>
<a href="https://www.pinterest.com/stefankoelle/" target="_blank"><i class="fa-brands fa-pinterest"></i></a>
<a href="https://www.facebook.com/stefan.koelle" target="_blank"><i class="fa-brands fa-facebook"></i></a>
<a href="https://www.instagram.com/stefankoelle/" target="_blank"><i class="fa-brands fa-instagram"></i></a>
<a href="https://www.threads.net/@stefankoelle" target="_blank"><i class="fa-brands fa-threads"></i></a>
<a href="https://x.com/stefankoelle" target="_blank"><i class="fa-brands fa-x-twitter"></i></a>
<a href="https://bsky.app/profile/stefankoelle.bsky.social" target="_blank"><i class="fa-brands fa-bluesky"></i></a>
<a rel="me" href="https://mastodon.social/@stefankoelle" target="_blank"><i class="fa-brands fa-mastodon"></i></a>
</div>
<div id="head">
<div id="foto"><img src="/assets/stefan-koelle-foto.jpg" alt="Foto von Stefan Koelle" style="width: 100%;"></div>
</div>
<div id="background-image"></div>
{{ content | safe }}
</div>
</div>
<script src="/assets/email.js"></script>
<script>
document.querySelectorAll('a[href^="#"]').forEach(function(anchor) {
anchor.addEventListener('click', function(e) {
var href = this.getAttribute('href');
if (href !== '#') {
e.preventDefault();
var target = document.querySelector(href);
if (target) {
target.scrollIntoView({ behavior: 'smooth' });
}
}
});
});
</script>
</body>
</html>
Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

+17
View File
@@ -0,0 +1,17 @@
function obfuscateEmail(user, domain, tld) {
return user + '@' + domain + '.' + tld;
}
document.addEventListener('DOMContentLoaded', function() {
var emailLink = document.getElementById('email-link');
if (emailLink) {
emailLink.href = 'mailto:' + obfuscateEmail('cv', 'stefankoelle', 'de');
emailLink.textContent = obfuscateEmail('cv', 'stefankoelle', 'de');
}
});
document.addEventListener('DOMContentLoaded', function() {
var emailLink = document.getElementById('email-impressum');
if (emailLink) {
emailLink.href = 'mailto:' + obfuscateEmail('impressum', 'stefankoelle', 'de');
emailLink.textContent = obfuscateEmail('impressum', 'stefankoelle', 'de');
}
});
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

+41
View File
@@ -0,0 +1,41 @@
{
"name": "App",
"icons": [
{
"src": "\/android-icon-36x36.png",
"sizes": "36x36",
"type": "image\/png",
"density": "0.75"
},
{
"src": "\/android-icon-48x48.png",
"sizes": "48x48",
"type": "image\/png",
"density": "1.0"
},
{
"src": "\/android-icon-72x72.png",
"sizes": "72x72",
"type": "image\/png",
"density": "1.5"
},
{
"src": "\/android-icon-96x96.png",
"sizes": "96x96",
"type": "image\/png",
"density": "2.0"
},
{
"src": "\/android-icon-144x144.png",
"sizes": "144x144",
"type": "image\/png",
"density": "3.0"
},
{
"src": "\/android-icon-192x192.png",
"sizes": "192x192",
"type": "image\/png",
"density": "4.0"
}
]
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

+184
View File
@@ -0,0 +1,184 @@
/* stefankoelle.de — Modernized CSS (visually identical to original) */
:root {
--color-text: #665544;
--color-text-dark: #333;
--color-bg: #f4f4f4;
--color-surface: rgba(255, 255, 255, 0.9);
--color-surface-solid: #ffffff;
--color-link: #333;
--color-nav-bg: #eeeeee;
--color-shadow: rgba(0, 0, 0, 0.1);
--font-main: 'Roboto', sans-serif;
--transition: color 0.3s;
}
*, *::before, *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
color: var(--color-text);
font: 1em/1.6 var(--font-main);
background-color: var(--color-bg);
}
a { color: var(--color-link); }
h1 { font-size: 2.5em; }
h2 { font-size: 2em; margin-bottom: 10px; }
h3 { font-size: 1.5em; margin-top: 20px; margin-bottom: 10px; }
p { margin-bottom: 10px; }
.profile, .core-technologies, .contact-info {
margin-bottom: 20px;
max-width: 500px;
}
/* Navigation */
#head { display: none; }
#links {
padding: 20px;
font-size: 1.25em;
text-align: center;
min-height: 20px;
background: var(--color-nav-bg);
position: fixed;
top: 0;
width: 100%;
z-index: 1000;
display: flex;
flex-wrap: wrap;
justify-content: center;
align-items: center;
gap: 10px;
}
.flex-break {
flex-basis: 100%;
}
#links a {
color: var(--color-text-dark);
text-decoration: none;
font-weight: normal;
transition: var(--transition);
}
#links a.separator::after {
content: " | ";
margin-left: 4px;
}
#links i.fa-brands {
margin-right: 8px;
}
/* Sections */
.headlinespace {
height: 560px;
padding: 80px;
position: relative;
}
#headline {
padding: 20px;
background: var(--color-surface);
width: calc(25% - 20px);
position: relative;
left: 20px;
box-shadow: 0 2px 4px var(--color-shadow);
}
.space {
margin-top: 1060px;
padding: 150px;
position: relative;
}
#cvcontent,
#personalprojectscontent,
#programminglanguagescontent,
#impressumcontent {
padding: 20px;
background: var(--color-surface);
width: calc(50% - 20px);
position: relative;
left: 20px;
box-shadow: 0 2px 4px var(--color-shadow);
}
#impressumcontent {
padding-top: 100px;
height: 200px;
}
/* Background */
#background-image {
position: fixed;
top: 120px;
left: 0;
width: 100%;
height: calc(100% - 120px);
background: url('/assets/stefan-koelle-vollbild.jpg') no-repeat top center fixed;
background-size: cover;
background-position: 0 120px;
z-index: -1;
}
/* Responsive */
@media (max-width: 1024px) {
#container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
body {
color: var(--color-text-dark);
font-size: 1em;
}
h1 { font-size: 2em; }
h2 { font-size: 1.75em; }
h3 { font-size: 1.25em; }
#headline,
#cvcontent,
#personalprojectscontent,
#programminglanguagescontent,
#impressumcontent {
width: calc(100% - 40px);
padding: 20px;
left: 0;
background: var(--color-surface-solid);
margin-bottom: 20px;
box-shadow: 0 2px 4px var(--color-shadow);
}
.headlinespace {
height: 20px;
padding: 20px;
}
.space {
margin-top: 40px;
width: 20px;
padding: 20px;
}
#background-image { display: none; }
#links {
padding: 20px;
background: var(--color-nav-bg);
position: relative;
width: calc(100% - 40px);
}
#head { display: block; padding-top: 20px; }
}
+38
View File
@@ -0,0 +1,38 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CV - Stefan Koelle</title>
<link rel="stylesheet" href="/assets/style.css">
<style>
@media print {
body { background: white; }
#links, #head, #background-image, .headlinespace, .space,
#personalprojectscontent, #programminglanguagescontent, #impressumcontent {
display: none !important;
}
#container { max-width: 100%; padding: 0; }
#headline { position: static; width: 100%; box-shadow: none; background: none; }
#cvcontent {
position: static;
width: 100%;
box-shadow: none;
background: none;
padding: 0;
}
}
</style>
</head>
<body class="blog">
<div id="container">
<div id="wrapper-content">
<div id="headline">
<h1>Stefan Kölle</h1>
Senior Software Developer & Software&nbsp;Architect
</div>
{% include "cv-content.njk" %}
</div>
</div>
</body>
</html>
+19
View File
@@ -0,0 +1,19 @@
module.exports = function (eleventyConfig) {
eleventyConfig.addGlobalData("site", { url: "https://stefankoelle.de" });
eleventyConfig.addFilter("date", (d) => d.toISOString());
eleventyConfig.addPassthroughCopy({ "stefankoelle/assets": "assets" });
eleventyConfig.addPassthroughCopy({ "stefankoelle/ledmatrix/assets": "ledmatrix/assets" });
eleventyConfig.addPassthroughCopy({ "stefankoelle/robots.txt": "robots.txt" });
return {
dir: {
input: "stefankoelle",
includes: "_includes",
output: "dist/stefankoelle"
},
serverOptions: {
host: "0.0.0.0",
port: 8086
}
};
};
+273
View File
@@ -0,0 +1,273 @@
---
title: "Stefan Koelle"
description: "Experienced Senior Software Developer and Software Architect with over 20 years of experience. Specialized in .NET Core and Kubernetes, with hands-on experience in AI-assisted development using Claude Code."
layout: stefankoelle.njk
---
<div class="headlinespace">
<a id="home"></a>
</div>
<div id="headline">
<h1>Stefan Kölle</h1>
Senior Software Developer & Software&nbsp;Architect
</div>
<div class="space">
<a id="cv"></a>
</div>
{% include "cv-content.njk" %}
<div class="space">
<a id="personalprojects"></a>
</div>
<div id="personalprojectscontent">
<h2>Personal Projects</h2>
<h3>Home Network Projects (2025-2026)</h3>
<ul>
<li><b>Docker Host VM 110 (Proxmox PVE)</b><br>Deployed new virtualized Docker host (VM 110,) with Debian 12.2 as fresh infrastructure replacement for legacy VM 101. Implemented optimized Docker engine with containerized services including Portainer, Prometheus, Grafana, Gitea, MQTT broker, and monitoring exporters. Successfully migrated nginx-proxy-manager database from Docker to MariaDB LxC for improved stability.</li>
<li><b>InfluxDB Time-Series Integration</b><br>Deployed InfluxDB 2.x for sensor data collection from Tasmota smart plugs and HomematicIP thermostats, replacing older MySQL-based storage with modern time-series architecture.</li>
<li><b>LED Matrix ESP32 InfluxDB Time-Series Integration</b><br>Implemented data persistence layer for <a href="/ledmatrix/">LED Matrix ESP32</a> sensor systems (DHT22 temperature/humidity) with InfluxDB 2.x backend. Automated collection of matrix display metrics and environmental sensor data at 60-second intervals, enabling historical trend analysis and long-term performance monitoring.</li>
<li><b>Interactive Air Quality Dashboard (luftwerte.html)</b><br>Developed dynamic HTML5 dashboard consuming real-time sensor data directly from InfluxDB backend via Flux query API. Displays aggregated temperature and humidity measurements across all network locations with responsive design supporting multiple device form factors. Serves as primary interface for environmental monitoring accessible via nginx reverse proxy.</li>
<li><b>Tasmota Energy Monitoring</b><br>Implemented smart plug energy tracking system with 8 Tasmota-enabled devices, integrating MQTT data collection and visualization for home automation monitoring.</li>
<li><b>MQTT Broker Migration</b><br>Migrated MQTT infrastructure from Synology NAS to Docker-based VM for improved stability and integration with modern smart home automation, while maintaining backward compatibility with legacy devices.</li>
<li><b>Four Managed Zyxel GS1200-8HP Switches</b><br>Deployed four managed Zyxel switches across network topology with SNMP exporter integration for centralized monitoring. Serverraum (192.168.178.250), Wohnzimmer (192.168.178.251), Powerlan (192.168.178.252), and OpenWRT office (192.168.188.250). Each switch monitored via Prometheus with dedicated export ports (9436-9439) providing real-time network traffic and port status visibility.</li>
<li><b>Multi-WAN OpenWrt Router (Office Workstation)</b><br>Implemented advanced OpenWrt routing system in office workspace with multi-WAN capabilities, connected as Zyxel switch target (192.168.188.250). Enables intelligent traffic distribution, failover support, and advanced VLAN segmentation for hybrid network connectivity during retro-computing sessions and general office networking.</li>
<li><b>Network Monitoring & SNMP Integration</b><br>Extended Prometheus infrastructure with SNMP exporters for all managed switches, enabling unified visibility of hardware statistics, port utilization, and network performance metrics across home network environment.</li>
<li><b>IoT VLAN Isolation (VLAN 189)</b><br>Implemented dedicated isolated network segment for IoT devices using Zyxel GS1200-8 switch with OpenWRT, separating smart home devices from legacy systems through VLAN segmentation.</li>
</ul>
<h3>Retro Computing (2022/2023)</h3>
<ul>
<li>MiSTer and MiST FPGA Systems for preservation.</li>
<li>DOS System, Amiga System, and Atari ST System with the environment from the early 90s.</li>
</ul>
<h3>Upgrade of old Atari Mega 4 (2021)</h3>
<ul>
<li>Gotek Drive</li>
<li>More RAM</li>
<li>Alternative TOS ROMs</li>
<li>SD HDD Adapter</li>
</ul>
<h3>Home Automation (since 2018)</h3>
<ul>
<li>Arduino/ESP32/Raspberry/Home Automation.</li>
<li><a href="/ledmatrix/">LED Matrix ESP32</a> for Date, Time, Weather, and Status messages (Web API).</li>
<ul>
<li>Attached Light sensor, DHT22 Temperature Sensor (Web API).</li>
<li>Every room has its LED matrix.</li>
</ul>
<li>dotnet core application (Master on iMac and 4 clients on Raspberry PIs).</li>
<li>MQTT for message transfer and remote control via Tasmota RF Module (433MHz).</li>
<li>Evaluation of power meters from washing machine and tumbler.</li>
<li>Info message when finished on LED matrices.</li>
<li>MP3 player (e.g., automatic play in the bathroom when the light is on).</li>
<li>One Raspberry Pi with pilight and RF433 Module for activating remote control power plugs.</li>
</ul>
<h3>Proxmox Server with Various LxC Linux (since 2020)</h3>
<ul>
<li>pihole</li>
<li>suricata</li>
<li>webone</li>
<li>minecraft bedrock server (multiple instances)</li>
<li>divoom-rest</li>
</ul>
<h3>Various Docker Systems (since 2016)</h3>
<ul>
<li>portainer</li>
<li>nginx</li>
<li>grafana</li>
<li>prometheus</li>
<li>cadvisor</li>
<li>nodeexporter</li>
<li>gitae</li>
<li>stirling-pdf</li>
<li>stretto</li>
<li>tubearchivist</li>
<li>tubesync</li>
<li>healthchecks</li>
<li>watchtower</li>
</ul>
<h3>Walk-on Song (2019)</h3>
<ul>
<li>When someone enters the office, their jingle is played.</li>
<li>ESP32 scans WiFi, if a known device is found for the first time per day, the jingle is played.</li>
<li>MP3 Module for playback on attached speaker.</li>
</ul>
<h3>Kids RFID MP3 Player (2018)</h3>
<ul>
<li>Interactive audio playback system utilizing RFID technology.</li>
<li>Powered by an Arduino Nano for compact and efficient operation.</li>
<li>Plays specific audio files when RFID-tagged objects or cards are placed on the device.</li>
<li>Designed for simplicity and intuitive use, suitable for all ages.</li>
<li>Customizable: users can upload and assign personalized audio content to RFID tags.</li>
<li>Ideal for educational and entertainment purposes.</li>
</ul>
<h3>Dotnet Community Activity (2009 - 2011)</h3>
<ul>
<li>Attended multiple .NET Open Spaces, including <a href="https://aztec-project.org/blog/dotnet-openspace-sued-2009.html" target="_blank">Ulm 2009</a> and <a href="https://aztec-project.org/blog/dotnet-openspace-leipzig-2009.html" target="_blank">Leipzig 2009</a>.</li>
<li>Developed a schema-less object storage system named <a href="https://aztec-project.org" target="_blank">StupidDB</a>.</li>
<li>Contributed to a <a href="https://aztec-project.org/#blog" target="_blank">developer blog</a> with colleagues.</li>
<li>Actively participated in Coding Dojos at AutoScout24.</li>
</ul>
<h3>Owner &amp; Developer, <a href="https://www.inseco.de" target="_blank">inseco.de</a> (2003 - 2008)</h3>
<ul>
<li>Managed approximately 20 clients, primarily from the gastronomy and club sectors.</li>
<li>Developed a CMS system for maintaining their websites, programmed in PHP and MySQL.</li>
<li>Included modules for photo galleries, menus, and events.</li>
</ul>
<h3>Home-Network Projects during IT Manager Phase (2000 - 2006)</h3>
<ul>
<li>Set up a home network with various Linux servers and Windows NT 4.0.</li>
<li>Implemented a predecessor to the pfSense firewall.</li>
<li>Managed a FreeBSD server with Apache, Sendmail, and Perl scripts.</li>
<li>Maintained an NT 4.0 file server.</li>
<li>Administered Windows 98 and Windows 2000 hosts.</li>
<li>More info at <a href="https://www.moonweb.org/network/" target="_blank">moonweb.org/network/</a></li>
</ul>
<h3>Mailbox/Fidonet (1994 - 2000)</h3>
<ul>
<li>Operated a BBS and Fidonet Node.</li>
<li>Managed a node in Augsburg with an uplink directly to the NC in Munich.</li>
<li>Software was running on an OS/2 computer</li>
<li>Utilized 2x ISDN and 1x MODEM connections.</li>
<li>Had about 20 subnodes and points.</li>
<li>Served as the Bavaria HUB/HOST for various old networks.</li>
<li>Developed several small software tools in Turbo Pascal for DOS.</li>
</ul>
<h3>PC Intro / Game / Software Development (1992 - 1994)</h3>
<ul>
<li>Released <a href="https://www.moonweb.org/phobia/" target="_blank">Phobia Welcome Intro (Sourcecode/Picture/Video)</a> for 486 PC in 1994</li>
<li>Released <a href="https://www.moonweb.org/tcm/" target="_blank">Tr@anceMission Intros (Sourcecode/Pictures/Videos)</a> in 1992 and 1993</li>
<li>Developed Turbo Pascal Graphics Library <a href="https://github.com/skoelle/turbopascal-xlib-unit" target="_blank">X-Lib Unit (Sourcecode)</a></li>
<li>Used Turbo Pascal and inline assembler for Development</li>
</ul>
<h3>Atari ST Software Development (1990 - 1992)</h3>
<ul>
<li>Completed game 'Atom oh no' in 1992 (GFA Basic)</li>
</ul>
</div>
<div class="space">
<a id="programminglanguages"></a>
</div>
<div id="programminglanguagescontent">
<h2>Programming Languages</h2>
<p>Since 2025, AI-assisted development with Claude Code has become a core part of my workflow. This approach enables faster delivery from idea to production, better documentation artifacts, and cross-technology work across .NET, TypeScript, and Kubernetes.</p>
<h3>.NET Core</h3>
<ul>
<li>Intensively worked with .NET Core starting in 2018 at stellenanzeigen.de GmbH &amp; Co. KG and continued at TENHIL GmbH &amp; Co. KG from 2022.</li>
<li>Migrated old VB and ASP-Classic applications to modern .NET Core applications.</li>
<li>Planned and implemented hosting architecture on Azure.</li>
<li>Introduced a CMS based on Squidex.</li>
<li>Developed new multi-client microservices for all brands using .NET Core 8, Docker, Terraform, Azure WebApps, Azure Tables, Azure Blobs, Uptrends, Sentry, Loki, and Quartz.NET.</li>
<li>Created microservices including Jobs Per Mail, JobApply application service, GeoService, Feedexporter, and Salary Data Service.</li>
</ul>
<h3>TypeScript</h3>
<ul>
<li>Started using TypeScript in professional projects in 2024 at TENHIL GmbH &amp; Co. KG.</li>
<li>Built an MCP (Model Context Protocol) server for ChatGPT integration, developed solo from concept to production in 6 half-days using Claude Code.</li>
<li>Implemented a TypeScript-based widget renderer for AI-driven applications, including integration with the OpenAI Chat Kit.</li>
<li>Applied TypeScript as the primary language for new AI-focused microservices where it was the right technology choice.</li>
</ul>
<h3>Python</h3>
<ul>
<li>Using Python since 2020 for private projects and tooling.</li>
<li>Applied in various automation scripts and data processing projects in a home lab environment.</li>
<li>Since 2026, using Python increasingly in home network projects driven by AI-assisted development with Open Code.</li>
</ul>
<h3>.NET Framework</h3>
<ul>
<li>Gained experience with the .NET Framework between 2004 and 2010 as CTO at stellenanzeigen.de GmbH &amp; Co. KG.</li>
<li>Led the migration from ASP Classic to the .NET Framework.</li>
<li>Hosted applications on Windows IIS and used MSSQL as the database.</li>
<li>Split software into different layers and enabled communication via SOAP web services.</li>
<li>Introduced TDD and DDD, and engaged with the .NET community through OpenSpaces and CodingDojos.</li>
</ul>
<h3>PHP</h3>
<ul>
<li>Developed a website CMS system (activetool.de) for multiple clients from 2003 to 2008.</li>
<li>Created a PHP Ticketing System and worked with MySQL databases.</li>
</ul>
<h3>ASP-Classic</h3>
<ul>
<li>Started with ASP-Classic on PC (Windows) in 1999 while working as a web developer at IXOS AG.</li>
<li>Managed the web development environment and built internal departmental websites.</li>
<li>Introduced search functions, request systems, and link checkers to optimize web servers.</li>
<li>Developed a Visual Basic application for generating static web pages.</li>
<li>Designed and implemented a workflow for maintaining the website using ASP scripts and SQL databases.</li>
<li>Led the IT and development department at stellenanzeigen.de GmbH &amp; Co. KG from 2000 to 2003, migrating applications from idc/htx to ASP-Classic and developing a multi-client web frontend for newspaper publishers.</li>
</ul>
<h3>Java</h3>
<ul>
<li>Began using Java on PC (Windows) in 1998.</li>
<li>Participated in a Security Hash Team Project at iXOS Software AG, developing the Java frontend.</li>
<li>Utilized development environments such as Kawa and IBM Visual Age.</li>
</ul>
<h3>C++</h3>
<ul>
<li>First used C++ on PC (Windows) in 1997.</li>
<li>Worked on an R/3 Integration Project during practical work at iXOS Software AG.</li>
<li>Completed one semester of C++ on UNIX (Solaris) during studies in 1998.</li>
</ul>
<h3>PASCAL</h3>
<ul>
<li>Started with PASCAL on PC in 1993, using TurboPascal 6.0, 7.0, and Borland Pascal 7.0.</li>
<li>Developed DOSMENU software for DOS PCs and tools for Fidonet.</li>
<li>Created two games: a Wheel of Fortune clone and a two-player Tetris.</li>
<li>Studied PASCAL for one year in school (1994) and one semester on UNIX (Solaris) during studies in 1996.</li>
</ul>
<h3>Assembler</h3>
<ul>
<li>Began learning Assembler on the Atari ST in 1992, creating simple programs with Pure Assembler, part of the Pure C Pack.</li>
<li>Developed a small text mode demo with sound using DevPAK.</li>
<li>Gained experience on PC in 1994, creating four demos using inline assembler in TurboPascal.</li>
</ul>
<h3>C</h3>
<ul>
<li>First encountered C on the Atari ST in 1992, creating simple programs with Pure C, an advanced version of Turbo C.</li>
<li>Completed one semester of C during studies in 1997.</li>
</ul>
<h3>BASIC</h3>
<ul>
<li>Gained initial experience with BASIC on the EPSON HX-20 in 1988 and Atari ST in 1989.</li>
<li>Developed two small games (Bowling, LiveStyleCity) using OmicronBasic.</li>
<li>Created games such as AtomOhNo (an Atomix clone) and TOPchangethecolor with GFA-Basic and TOPpack.</li>
<li>Produced graphic demos on the Atari ST around 1991.</li>
<li>Developed simple programs using Amiga Basic in 1991.</li>
<li>Studied BASIC for two years in school (1991-1992), including coding a Snake clone during class.</li>
</ul>
</div>
<div class="space">
<a id="impressum"></a>
</div>
<div id="impressumcontent">
<h2>Impressum</h2>
<p>Stefan Kölle<br>Neumarkter Str. 86c<br>81673 München</p>
<p>Phone/Fax: +49-(89)-20006547<br>E-Mail: <a id="email-impressum" href="#">[email protected]</a></p>
</div>
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 242 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 568 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 514 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 874 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

+416
View File
@@ -0,0 +1,416 @@
/* LED Matrix WebServer — Standalone CSS */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--color-primary: #2196F3;
--color-primary-dark: #1976D2;
--color-accent: #00BCD4;
--color-success: #4CAF50;
--color-warning: #FF9800;
--color-error: #F44336;
--color-bg: #f5f7fa;
--color-surface: #ffffff;
--color-text: #333333;
--shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
--shadow-lg: 0 8px 24px rgba(0, 0, 0, 0.15);
--transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", sans-serif;
background: var(--color-bg);
color: var(--color-text);
line-height: 1.6;
}
/* Header & Navigation */
header {
background: var(--color-surface);
box-shadow: var(--shadow);
position: sticky;
top: 0;
z-index: 100;
}
nav {
max-width: 1200px;
margin: 0 auto;
padding: 1rem 2rem;
display: flex;
justify-content: space-between;
align-items: center;
}
.logo {
font-size: 1.5rem;
font-weight: 700;
color: var(--color-primary);
text-decoration: none;
display: flex;
align-items: center;
gap: 0.5rem;
}
.nav-menu {
display: flex;
gap: 2rem;
list-style: none;
align-items: center;
}
.nav-menu a {
color: var(--color-text);
text-decoration: none;
font-weight: 500;
transition: var(--transition);
position: relative;
}
.nav-menu a::after {
content: '';
position: absolute;
bottom: -4px;
left: 0;
width: 0;
height: 2px;
background: var(--color-primary);
transition: var(--transition);
}
.nav-menu a:hover::after {
width: 100%;
}
/* Hero Section */
.hero {
background: linear-gradient(135deg, var(--color-primary) 0%, var(--color-accent) 100%);
color: white;
padding: 4rem 2rem;
text-align: center;
}
.hero-content {
max-width: 1200px;
margin: 0 auto;
display: grid;
grid-template-columns: 1fr 1fr;
gap: 3rem;
align-items: center;
}
.hero-text h1 {
font-size: 2.5rem;
margin-bottom: 1rem;
line-height: 1.2;
}
.hero-text p {
font-size: 1.1rem;
margin-bottom: 2rem;
opacity: 0.95;
}
.version-badge {
display: inline-block;
background: rgba(255, 255, 255, 0.2);
padding: 0.5rem 1rem;
border-radius: 20px;
font-size: 0.9rem;
margin-bottom: 1rem;
border: 1px solid rgba(255, 255, 255, 0.3);
}
.cta-buttons {
display: flex;
gap: 1rem;
justify-content: flex-start;
}
.btn {
padding: 0.75rem 1.5rem;
border-radius: 6px;
border: none;
font-weight: 600;
cursor: pointer;
transition: var(--transition);
text-decoration: none;
display: inline-block;
font-size: 1rem;
}
.btn-primary {
background: white;
color: var(--color-primary);
}
.btn-primary:hover {
transform: translateY(-2px);
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.2);
}
.btn-secondary {
background: rgba(255, 255, 255, 0.2);
color: white;
border: 2px solid white;
}
.btn-secondary:hover {
background: white;
color: var(--color-primary);
}
.hero-image {
text-align: center;
}
.hero-image img {
max-width: 100%;
height: auto;
}
@media (max-width: 768px) {
.hero-content {
grid-template-columns: 1fr;
}
.hero-text h1 {
font-size: 1.8rem;
}
.cta-buttons {
flex-direction: column;
}
}
/* Main Content */
main {
max-width: 1200px;
margin: 0 auto;
padding: 3rem 2rem;
}
.section {
margin-bottom: 3rem;
}
.section-title {
font-size: 2rem;
margin-bottom: 1.5rem;
color: var(--color-primary);
display: flex;
align-items: center;
gap: 0.5rem;
}
.section-title::before {
content: '';
width: 4px;
height: 2rem;
background: var(--color-primary);
border-radius: 2px;
}
/* Cards */
.cards-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 2rem;
margin-bottom: 2rem;
}
.card {
background: var(--color-surface);
border-radius: 8px;
padding: 1.5rem;
box-shadow: var(--shadow);
transition: var(--transition);
border-left: 4px solid var(--color-primary);
}
.card:hover {
transform: translateY(-4px);
box-shadow: var(--shadow-lg);
}
.card-title {
font-size: 1.2rem;
font-weight: 600;
margin-bottom: 0.5rem;
color: var(--color-primary);
}
.card-subtitle {
font-size: 0.85rem;
color: var(--color-accent);
margin-bottom: 1rem;
}
.card-content {
font-size: 0.95rem;
line-height: 1.6;
}
/* Features */
.features {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 2rem;
}
.feature {
display: flex;
gap: 1rem;
}
.feature-icon {
font-size: 2rem;
color: var(--color-primary);
flex-shrink: 0;
}
.feature-text h3 {
margin-bottom: 0.5rem;
color: var(--color-primary);
}
.feature-text p {
font-size: 0.95rem;
}
/* Specs Table */
.specs-table {
background: var(--color-surface);
border-radius: 8px;
overflow: hidden;
box-shadow: var(--shadow);
margin-bottom: 2rem;
}
.specs-table table {
width: 100%;
border-collapse: collapse;
}
.specs-table th {
background: var(--color-primary);
color: white;
padding: 1rem;
text-align: left;
font-weight: 600;
}
.specs-table td {
padding: 1rem;
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
}
.specs-table tr:last-child td {
border-bottom: none;
}
.specs-table tr:nth-child(even) {
background: rgba(33, 150, 243, 0.02);
}
/* Code Block */
.code-block {
background: var(--color-surface);
border-left: 4px solid var(--color-accent);
padding: 1.5rem;
border-radius: 6px;
overflow-x: auto;
margin: 1.5rem 0;
box-shadow: var(--shadow);
}
.code-block pre {
margin: 0;
font-family: "Monaco", "Menlo", "Ubuntu Mono", monospace;
font-size: 0.9rem;
line-height: 1.5;
}
.code-label {
font-size: 0.85rem;
color: var(--color-accent);
font-weight: 600;
margin-bottom: 0.5rem;
text-transform: uppercase;
letter-spacing: 1px;
}
/* Footer */
footer {
background: var(--color-surface);
border-top: 1px solid rgba(0, 0, 0, 0.1);
padding: 3rem 2rem;
margin-top: 4rem;
}
.footer-content {
max-width: 1200px;
margin: 0 auto;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 2rem;
margin-bottom: 2rem;
}
.footer-section h4 {
margin-bottom: 1rem;
color: var(--color-primary);
}
.footer-section ul {
list-style: none;
}
.footer-section a {
color: var(--color-text);
text-decoration: none;
opacity: 0.8;
transition: var(--transition);
}
.footer-section a:hover {
color: var(--color-primary);
opacity: 1;
}
.footer-bottom {
text-align: center;
padding-top: 2rem;
border-top: 1px solid rgba(0, 0, 0, 0.1);
opacity: 0.7;
}
/* Responsive */
@media (max-width: 768px) {
nav {
flex-direction: column;
gap: 1rem;
}
.nav-menu {
flex-direction: column;
gap: 1rem;
}
main {
padding: 2rem 1rem;
}
.section-title {
font-size: 1.5rem;
}
.hero-text h1 {
font-size: 2rem;
}
}
+562
View File
@@ -0,0 +1,562 @@
---
title: "LED Matrix WebServer"
description: "WiFi-controlled LED display solution with REST API, DHT22 sensor, and OTA updates."
---
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="{{ description }}">
<meta name="author" content="Stefan Kölle">
<title>{{ title }} - ESP32 Projekt</title>
<link rel="stylesheet" href="assets/style.css">
</head>
<body>
<header>
<nav>
<a href="#" class="logo">🔌 LED Matrix WebServer</a>
<ul class="nav-menu">
<li><a href="#overview">Overview</a></li>
<li><a href="#features">Features</a></li>
<li><a href="#hardware">Hardware</a></li>
<li><a href="#api">API</a></li>
</ul>
</nav>
</header>
<section class="hero">
<div class="hero-content">
<div class="hero-text">
<div class="version-badge">v1.08 (DHT) • January 2026</div>
<h1>LED Matrix WebServer</h1>
<p>WiFi-controlled LED display solution with REST API, DHT22 sensor, and OTA updates for your home network.</p>
<div class="cta-buttons">
<a href="#overview" class="btn btn-primary">Project</a>
<a href="#api" class="btn btn-secondary">API Dokumentation</a>
</div>
</div>
<div class="hero-image">
<p style="font-size: 4rem; opacity: 0.9;">📊</p>
</div>
</div>
</section>
<main>
<section class="section" id="overview">
<h2 class="section-title">Overview</h2>
<p style="font-size: 1.1rem; margin-bottom: 2rem;">
The LED Matrix WebServer system is a fully configurable LED display solution for home networks.
It is based on the NodeMCU-32S ESP32 with up to 12 cascaded MAX7219 8x8 LED modules.
Using a simple REST API, messages can be displayed, animations controlled, and sensor data queried.
</p>
<div class="cards-grid">
<div class="card">
<div class="card-title">🎯 Easy to use</div>
<div class="card-subtitle">Web-based control</div>
<div class="card-content">Control your LED matrix via HTTP REST API from anywhere in the network</div>
</div>
<div class="card">
<div class="card-title">🌐 WiFi integrated</div>
<div class="card-subtitle">Fritz!Box Mesh compatible</div>
<div class="card-content">Automatic WiFi connection and reconnect</div>
</div>
<div class="card">
<div class="card-title">📡 OTA updates</div>
<div class="card-subtitle">Update firmware wirelessly</div>
<div class="card-content">Update your firmware without a USB connection</div>
</div>
<div class="card">
<div class="card-title">🌡️ Sensors</div>
<div class="card-subtitle">DHT22 integrated</div>
<div class="card-content">Read temperature and humidity</div>
</div>
<div class="card">
<div class="card-title">🔊 Buzzer sounds</div>
<div class="card-subtitle">Tone sequences</div>
<div class="card-content">Predefined melodies for alarms and notifications</div>
</div>
<div class="card">
<div class="card-title">📦 Extensible</div>
<div class="card-subtitle">Analog inputs</div>
<div class="card-content">GPIO 34/35/36 available for external sensors</div>
</div>
</div>
</section>
<section class="section" id="hardware">
<h2 class="section-title">Hardware Components</h2>
<div class="specs-table">
<table>
<thead>
<tr>
<th>Component</th>
<th>Model / Type</th>
<th>Features</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Microcontroller</strong></td>
<td>NodeMCU-32S ESP32</td>
<td>32-Bit Dual-Core 240MHz, WiFi + Bluetooth</td>
</tr>
<tr>
<td><strong>LED Display</strong></td>
<td>12× MAX7219 8x8 Module</td>
<td>cascaded</td>
</tr>
<tr>
<td><strong>Sensor</strong></td>
<td>DHT22 (AM2302)</td>
<td>Temperature ±0.5°C, Humidity ±2%</td>
</tr>
<tr>
<td><strong>Buzzer</strong></td>
<td>Passive Buzzer</td>
<td>GPIO 16 PWM controlled via NPN transistor</td>
</tr>
<tr>
<td><strong>Power Supply</strong></td>
<td>USB-C oder externe 5V PSU</td>
<td>5V for MAX7219, 3.3V internal for logic</td>
</tr>
</tbody>
</table>
</div>
<h3 style="margin-top: 2rem; margin-bottom: 1rem; color: var(--color-primary);">Pin Configuration</h3>
<div class="code-block">
<div class="code-label">GPIO Pin Assignment</div>
<pre>LedMatrix Software V1.08
NodeMCU-32S ESP32 (USB powered)
POWER SUPPLY:
Micro USB ──→ ESP32 (5V regulator generates 3.3V internal)
SPI SIGNALS (MAX7219):
VIN 5V ──→ MAX7219 VCC
GND ──→ MAX7219 GND
GPIO 5 ──→ MAX7219 CS (Chip Select)
GPIO 18 ──→ MAX7219 CLK (Clock)
GPIO 23 ──→ MAX7219 DIN (Data In)
Modul 1 DOUT → Modul 2 DIN → Modul 3 DIN
DHT22 SENSOR:
GPIO 27 ──→ DHT22 Data
3V3 ──→ DHT22 VCC
GND ──→ DHT22 GND
BUZZER:
GPIO 16 ──→ Buzzer Control (via transistor)
GPIO 16 ──1kΩ── NPN Transistor BASE
NPN Transistor COLLECTOR ──→ Buzzer +
NPN Transistor EMITTER ──→ GND
Buzzer - ──→ GND (ESP32)
ANALOG INPUTS (optional):
GPIO 34 ──→ Analog Sensor 1
GPIO 35 ──→ Analog Sensor 2
GPIO 36 ──→ Analog Sensor 3</pre>
</div>
</section>
<section class="section" id="installation">
<h2 class="section-title">Installation &amp; Setup</h2>
<p style="margin-bottom: 2rem;">
Here are photos of various LED Matrix installations in the home network. The systems show different colors and configurations.
</p>
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 2rem; margin-bottom: 2rem;">
<div style="background: var(--color-surface); border-radius: 8px; overflow: hidden; box-shadow: var(--shadow); transition: var(--transition);">
<img src="assets/ledmatrix.jpg" alt="Red LED Matrix Installation" style="width: 100%; height: auto; display: block;">
<div style="padding: 1rem;">
<h4 style="color: var(--color-primary); margin-bottom: 0.5rem;">Red Installation</h4>
<p style="font-size: 0.9rem;">LED Matrix with red backlight on iMac</p>
</div>
</div>
<div style="background: var(--color-surface); border-radius: 8px; overflow: hidden; box-shadow: var(--shadow); transition: var(--transition);">
<img src="assets/ledmatrix-az.jpg" alt="LED Matrix above AnyCubic Printer" style="width: 100%; height: auto; display: block;">
<div style="padding: 1rem;">
<h4 style="color: var(--color-primary); margin-bottom: 0.5rem;">AnyCubic Printer Setup</h4>
<p style="font-size: 0.9rem;">LED Matrix installation above 3D printer</p>
</div>
</div>
<div style="background: var(--color-surface); border-radius: 8px; overflow: hidden; box-shadow: var(--shadow); transition: var(--transition);">
<img src="assets/ledmatrix-yellow.jpg" alt="Green LED Matrix Installation" style="width: 100%; height: auto; display: block;">
<div style="padding: 1rem;">
<h4 style="color: var(--color-primary); margin-bottom: 0.5rem;">Green-yellow Installation</h4>
<p style="font-size: 0.9rem;">LED Matrix with green/yellow LED configuration</p>
</div>
</div>
<div style="background: var(--color-surface); border-radius: 8px; overflow: hidden; box-shadow: var(--shadow); transition: var(--transition);">
<img src="assets/ledmatrix-kind.jpg" alt="Green LED Matrix Close-up" style="width: 100%; height: auto; display: block;">
<div style="padding: 1rem;">
<h4 style="color: var(--color-primary); margin-bottom: 0.5rem;">Green Close-up</h4>
<p style="font-size: 0.9rem;">Detailed view of a green LED Matrix with wiring</p>
</div>
</div>
<div style="background: var(--color-surface); border-radius: 8px; overflow: hidden; box-shadow: var(--shadow); transition: var(--transition);">
<img src="assets/ledmatrix-blue.jpg" alt="Blue LED Matrix Installation" style="width: 100%; height: auto; display: block;">
<div style="padding: 1rem;">
<h4 style="color: var(--color-primary); margin-bottom: 0.5rem;">Blue Installation</h4>
<p style="font-size: 0.9rem;">LED Matrix with blue brightness<br>Many pixels defective</p>
</div>
</div>
<div style="background: var(--color-surface); border-radius: 8px; overflow: hidden; box-shadow: var(--shadow); transition: var(--transition);">
<img src="assets/ledmatrix-green.jpg" alt="Cyan LED Matrix Installation" style="width: 100%; height: auto; display: block;">
<div style="padding: 1rem;">
<h4 style="color: var(--color-primary); margin-bottom: 0.5rem;">Cyan Installation</h4>
<p style="font-size: 0.9rem;">Multiple modules with cyan-colored lighting<br>Some pixels defective</p>
</div>
</div>
</div>
</section>
<section class="section" id="features">
<h2 class="section-title">Feature Overview</h2>
<div class="features">
<div class="feature">
<div class="feature-icon">📱</div>
<div class="feature-text">
<h3>REST API</h3>
<p>HTTP-based control of all functions from anywhere in the network</p>
</div>
</div>
<div class="feature">
<div class="feature-icon">🎬</div>
<div class="feature-text">
<h3>Animations</h3>
<p>MD_Parola Library with various scroll modes</p>
</div>
</div>
<div class="feature">
<div class="feature-icon">💾</div>
<div class="feature-text">
<h3>EEPROM Storage</h3>
<p>Persistently save and load messages</p>
</div>
</div>
<div class="feature">
<div class="feature-icon">🔄</div>
<div class="feature-text">
<h3>Asynchronous Web Engine</h3>
<p>Non-blocking AsyncWebServer for reliable performance</p>
</div>
</div>
<div class="feature">
<div class="feature-icon">🌡️</div>
<div class="feature-text">
<h3>Sensor Integration</h3>
<p>DHT22 read every 60 seconds</p>
</div>
</div>
<div class="feature">
<div class="feature-icon">🔊</div>
<div class="feature-text">
<h3>Sounds &amp; Alarms</h3>
<p>4 predefined melodies: Error, Done, Warning, Completed</p>
</div>
</div>
</div>
</section>
<section class="section" id="api">
<h2 class="section-title">REST API Documentation</h2>
<p style="margin-bottom: 2rem;">
All endpoints are accessible via HTTP. The LED Matrix is recognized as <code>ledmatrix.fritz.box</code> in the network.
</p>
<h3 style="margin-top: 2rem; margin-bottom: 1rem; color: var(--color-primary);">Base Endpoints</h3>
<div class="code-block">
<div class="code-label">Welcome Page HTML</div>
<pre>GET /
Response: HTML Welcome Page with menu</pre>
</div>
<div class="code-block">
<div class="code-label">Firmware-Version</div>
<pre>GET /version
Response: v1.08 (dht)</pre>
</div>
<h3 style="margin-top: 2rem; margin-bottom: 1rem; color: var(--color-primary);">Message &amp; Display</h3>
<div class="code-block">
<div class="code-label">Display message (static)</div>
<pre>GET /print?text=Hallo%20Welt
Displays a static message on the LED matrix (max 75 characters)</pre>
</div>
<div class="code-block">
<div class="code-label">Message with scroll animation</div>
<pre>GET /scroll?text=Scrolling%20Text
Text scrolls from left to right across the matrix
Switches back to the last print message afterwards</pre>
</div>
<div class="code-block">
<div class="code-label">Test display</div>
<pre>GET /test
Displays "Hello world" on the LED matrix</pre>
</div>
<div class="code-block">
<div class="code-label">Change LED brightness</div>
<pre>GET /set?intensity=8
Value: 0 (dark) to 15 (bright)</pre>
</div>
<div class="code-block">
<div class="code-label">Turn off display</div>
<pre>GET /off
Turns off the LED matrix (power saving)</pre>
</div>
<h3 style="margin-top: 2rem; margin-bottom: 1rem; color: var(--color-primary);">Sensor Data</h3>
<div class="code-block">
<div class="code-label">Read temperature</div>
<pre>GET /temperature
Response: "23.5" (degrees Celsius)</pre>
</div>
<div class="code-block">
<div class="code-label">Read humidity</div>
<pre>GET /humidity
Response: "65.2" (percent relative humidity)</pre>
</div>
<div class="code-block">
<div class="code-label">Read analog inputs</div>
<pre>GET /analog34
GET /analog35
GET /analog36
Response: Value between 0-4095
GET /analogvalues
Response: "1234,2345,3456" (comma-separated values)</pre>
</div>
<h3 style="margin-top: 2rem; margin-bottom: 1rem; color: var(--color-primary);">Sounds &amp; Buzzer</h3>
<div class="code-block">
<div class="code-label">Success melody (Done)</div>
<pre>GET /sound/erledigt
Plays positive tone sequence</pre>
</div>
<div class="code-block">
<div class="code-label">Warnings melody</div>
<pre>GET /sound/warnung
Plays warning tone sequence</pre>
</div>
<div class="code-block">
<div class="code-label">Error melody</div>
<pre>GET /sound/fehler
Plays error tone sequence</pre>
</div>
<div class="code-block">
<div class="code-label">Completed melody</div>
<pre>GET /sound/abgeschlossen
Plays success tone sequence</pre>
</div>
<h3 style="margin-top: 2rem; margin-bottom: 1rem; color: var(--color-primary);">Control &amp; System</h3>
<div class="code-block">
<div class="code-label">Restart system</div>
<pre>GET /restart
ESP32 will be restarted (with display shutdown)</pre>
</div>
<h3 style="margin-top: 2rem; margin-bottom: 1rem; color: var(--color-primary);">curl Examples</h3>
<div class="code-block">
<div class="code-label">curl: display text</div>
<pre>curl "http://ledmatrix.fritz.box/print?text=Hallo%20Welt"</pre>
</div>
<div class="code-block">
<div class="code-label">curl: text with scroll</div>
<pre>curl "http://ledmatrix.fritz.box/scroll?text=Breaking%20News"</pre>
</div>
<div class="code-block">
<div class="code-label">curl: read temperature</div>
<pre>curl "http://ledmatrix.fritz.box/temperature"</pre>
</div>
<div class="code-block">
<div class="code-label">curl: play sound</div>
<pre>curl "http://ledmatrix.fritz.box/sound/erledigt"</pre>
</div>
<div class="code-block">
<div class="code-label">curl: set brightness</div>
<pre>curl "http://ledmatrix.fritz.box/set?intensity=10"</pre>
</div>
</section>
<section class="section">
<h2 class="section-title">Technical Specifications</h2>
<div class="specs-table">
<table>
<thead>
<tr>
<th>Property</th>
<th>Value</th>
<th>Notes</th>
</tr>
</thead>
<tbody>
<tr>
<td>Firmware Version</td>
<td>v1.08 (DHT)</td>
<td>January 2026</td>
</tr>
<tr>
<td>Power Supply</td>
<td>USB-C 5V</td>
<td>~500mA for logic</td>
</tr>
<tr>
<td>WiFi Standard</td>
<td>802.11 b/g/n</td>
<td>2.4GHz</td>
</tr>
<tr>
<td>LED Display</td>
<td>12× 8x8 Module (96×8 Pixel)</td>
<td></td>
</tr>
<tr>
<td>DHT22 Range</td>
<td>-40°C to +80°C</td>
<td>Humidity 0-100% RH</td>
</tr>
<tr>
<td>Update Method</td>
<td>OTA (AsyncElegantOTA)</td>
<td>Wireless via HTTP</td>
</tr>
<tr>
<td>DHT22 Update Interval</td>
<td>60 seconds</td>
<td>Minimum read interval</td>
</tr>
<tr>
<td>Message Buffer</td>
<td>75 characters</td>
<td>Max. length of a message</td>
</tr>
</tbody>
</table>
</div>
</section>
<section class="section">
<h2 class="section-title">Code Quality</h2>
<p style="margin-bottom: 1.5rem;">
The code has been thoroughly reviewed and classified as production-ready.
</p>
<div class="cards-grid">
<div class="card">
<div class="card-title">✅ Strengths</div>
<div class="card-content">
<ul style="list-style-position: inside; margin-top: 0.5rem;">
<li>Robust WiFi management</li>
<li>Non-blocking Async-WebServer</li>
<li>Error handling for DHT</li>
<li>OTA updates implemented</li>
</ul>
</div>
</div>
<div class="card">
<div class="card-title">⚠️ Potential for Improvement</div>
<div class="card-content">
<ul style="list-style-position: inside; margin-top: 0.5rem;">
<li>Hard-coded SSID/password</li>
<li>Expand input validation</li>
</ul>
</div>
</div>
<div class="card">
<div class="card-title">📊 Evaluation</div>
<div class="card-content">
<div style="margin-top: 1rem;">
<strong>Grade: A-</strong><br>
Production ready<br>
Uptime: 30-45 days normal
</div>
</div>
</div>
</div>
</section>
</main>
<footer>
<div class="footer-content">
<div class="footer-section">
<h4>Projekt</h4>
<ul>
<li><a href="#overview">Overview</a></li>
<li><a href="#features">Features</a></li>
<li><a href="#hardware">Hardware</a></li>
<li><a href="#api">API</a></li>
</ul>
</div>
<div class="footer-section">
<h4>Technologie</h4>
<ul>
<li>ESP32 NodeMCU-32S</li>
<li>Arduino IDE</li>
<li>MD_Parola Library</li>
<li>AsyncWebServer</li>
</ul>
</div>
<div class="footer-section">
<h4>API</h4>
<ul>
<li><a href="#api">REST Endpoints</a></li>
<li>DHT22 Sensors</li>
<li>Analog Inputs</li>
<li>Sound Control</li>
</ul>
</div>
</div>
<div class="footer-bottom">
<p>&copy; 2026 <a href="https://stefankoelle.de/">Stefan Kölle</a>. LED Matrix WebServer v1.08.</p>
</div>
</footer>
<script>
document.querySelectorAll('a[href^="#"]').forEach(function(anchor) {
anchor.addEventListener('click', function(e) {
var href = this.getAttribute('href');
if (href !== '#') {
e.preventDefault();
var target = document.querySelector(href);
if (target) {
target.scrollIntoView({ behavior: 'smooth' });
}
}
});
});
</script>
</body>
</html>
+94
View File
@@ -0,0 +1,94 @@
/* cv-style.css — Clean CV layout for PDF print */
:root {
--color-text: #333;
--color-heading: #1a1a1a;
--color-accent: #665544;
--color-border: #ddd;
--font-main: 'Roboto', sans-serif;
}
@page {
size: A4;
margin: 20mm;
}
body {
font-family: var(--font-main);
font-size: 10pt;
line-height: 1.5;
color: var(--color-text);
}
h1 {
font-size: 22pt;
color: var(--color-heading);
margin-bottom: 4pt;
border: none;
}
h2 {
font-size: 14pt;
color: var(--color-accent);
border-bottom: 2px solid var(--color-accent);
padding-bottom: 4pt;
margin-top: 16pt;
margin-bottom: 8pt;
border-left: none;
}
h3 {
font-size: 11pt;
color: var(--color-heading);
margin-top: 10pt;
margin-bottom: 4pt;
}
p { margin-bottom: 6pt; }
ul {
margin: 4pt 0 8pt 16pt;
padding: 0;
}
li {
margin-bottom: 3pt;
}
a {
color: var(--color-text);
text-decoration: none;
}
.contact-info {
margin-bottom: 12pt;
}
.profile, .core-technologies {
margin-bottom: 12pt;
}
/* Hide non-CV elements when generating PDF */
#links, #head, #background-image,
.headlinespace, .space,
#personalprojectscontent,
#programminglanguagescontent,
#impressumcontent {
display: none !important;
}
#headline {
position: static;
width: 100%;
box-shadow: none;
background: none;
padding: 0 0 12pt 0;
}
#cvcontent {
position: static;
width: 100%;
box-shadow: none;
background: none;
padding: 0;
}
+5
View File
@@ -0,0 +1,5 @@
User-agent: *
Allow: /
Disallow: /deploy/
Sitemap: https://stefankoelle.de/sitemap.xml