<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Home on Systems &amp; Embedded Software Engineer</title><link>https://elvirh.com/en/</link><description>Recent content in Home on Systems &amp; Embedded Software Engineer</description><generator>Hugo</generator><language>en-US</language><lastBuildDate>Thu, 12 Feb 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://elvirh.com/en/index.xml" rel="self" type="application/rss+xml"/><item><title>IT Messe Wien — System Analysis Challenge (triscon)</title><link>https://elvirh.com/en/projects/achievements/it-messe-wien/</link><pubDate>Thu, 12 Feb 2026 00:00:00 +0000</pubDate><guid>https://elvirh.com/en/projects/achievements/it-messe-wien/</guid><description>&lt;div class="result-box"&gt;
 &lt;h2 id="-result"&gt;🏆 Result&lt;a class="anchor" href="#-result"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Reached maximum score in a browser-based challenge at IT Messe Wien&lt;/li&gt;
&lt;li&gt;Won: Nintendo Switch&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;section class="container-section section-accent"&gt;
&lt;h2 id="-what-made-this-interesting"&gt;🧠 What Made This Interesting&lt;a class="anchor" href="#-what-made-this-interesting"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The challenge imposed no restrictions on how the score could be achieved.&lt;/p&gt;
&lt;p&gt;Instead of optimizing gameplay, I treated it as a systems problem:&lt;/p&gt;
&lt;div class="question-list"&gt;
 &lt;p class="question"&gt;Where is validation actually enforced?&lt;/p&gt;
 &lt;p class="question"&gt;What does the backend really trust?&lt;/p&gt;
 &lt;p class="question"&gt;How does score flow through the system?&lt;/p&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section class="container-section section-accent"&gt;
&lt;h2 id="-overview"&gt;📖 Overview&lt;a class="anchor" href="#-overview"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I analyzed the client-server interaction with a focus on trust boundaries and score flow rather than UI behavior. That turned a simple browser game into a systems analysis exercise.&lt;/p&gt;</description></item><item><title>Deterministic Multiplayer Engine (ESP32-S3)</title><link>https://elvirh.com/en/projects/projects/retrolink/</link><pubDate>Sun, 01 Feb 2026 00:00:00 +0000</pubDate><guid>https://elvirh.com/en/projects/projects/retrolink/</guid><description>&lt;h2 id="overview"&gt;Overview&lt;a class="anchor" href="#overview"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Deterministic multiplayer retro engine running on ESP32-S3 using ESP-IDF.&lt;/p&gt;
&lt;p&gt;Architecture:
Fixed-step simulation (host-only) -&amp;gt; snapshot replication -&amp;gt; pure snapshot rendering -&amp;gt; DMA present.&lt;/p&gt;
&lt;p&gt;No client prediction. No ISR rendering. Pixel-identical frames.&lt;/p&gt;
&lt;figure class="project-video"&gt;
 &lt;video controls playsinline preload="metadata" poster="/en/projects/projects/retrolink/img1.jpg"&gt;
 &lt;source src="https://elvirh.com/en/projects/projects/retrolink/Espvideo1.mp4" type="video/mp4"&gt;
 Your browser does not support the video tag.
 &lt;/video&gt;
 &lt;figcaption&gt;RetroLink running on real hardware&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id="technical-highlights"&gt;Technical Highlights&lt;a class="anchor" href="#technical-highlights"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Double-buffered rendering&lt;/li&gt;
&lt;li&gt;DMA display output&lt;/li&gt;
&lt;li&gt;Tile/sprite-first pipeline&lt;/li&gt;
&lt;li&gt;Deterministic sync over ESP-NOW&lt;/li&gt;
&lt;li&gt;Minimal network overhead&lt;/li&gt;
&lt;li&gt;Real hardware validation&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="key-lessons"&gt;Key Lessons&lt;a class="anchor" href="#key-lessons"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Determinism simplifies debugging&lt;/li&gt;
&lt;li&gt;Rendering must be separated from simulation&lt;/li&gt;
&lt;li&gt;Network simplicity beats clever hacks&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="technologies"&gt;Technologies&lt;a class="anchor" href="#technologies"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;ESP32-S3&lt;/li&gt;
&lt;li&gt;ESP-IDF&lt;/li&gt;
&lt;li&gt;ESP-NOW&lt;/li&gt;
&lt;li&gt;SPI DMA display pipeline&lt;/li&gt;
&lt;li&gt;ILI9341&lt;/li&gt;
&lt;li&gt;Double-buffered rendering&lt;/li&gt;
&lt;/ul&gt;
&lt;details &gt;&lt;summary&gt;Flowchart&lt;/summary&gt;&lt;div class="markdown-inner"&gt;

&lt;div class="mermaid-block" data-mermaid-block id="mermaid-f1ea81cc65b879ca198c51fa8fbd7f3a-0"&gt;
 &lt;div class="mermaid-source" hidden&gt;flowchart TD
 classDef phase fill:#eef4ff,stroke:#2563eb,stroke-width:1px,color:#0f172a;
 classDef io fill:#e8fff0,stroke:#16a34a,stroke-width:1px,color:#0f172a;
 classDef invariant fill:#fff7e6,stroke:#d97706,stroke-width:1px,color:#0f172a;

 A[&amp;#34;Phase 1: Simulation (Host Only)&amp;#34;] --&amp;gt; B[&amp;#34;Phase 2: Snapshot and Replication&amp;#34;]
 B --&amp;gt; C[&amp;#34;Phase 3: Rendering (All Devices)&amp;#34;]
 C --&amp;gt; D[&amp;#34;Phase 4: Present via SPI DMA&amp;#34;]

 A --&amp;gt; A1[&amp;#34;Fixed-step update of gameplay and animation state&amp;#34;]
 B --&amp;gt; B1[&amp;#34;Complete frozen snapshot is network truth&amp;#34;]
 C --&amp;gt; C1[&amp;#34;Pure function of snapshot (idempotent)&amp;#34;]
 D --&amp;gt; D1[&amp;#34;Render back -&amp;gt; Present back -&amp;gt; Sync front -&amp;gt; Swap&amp;#34;]

 D --&amp;gt; E[&amp;#34;LCD sees only complete coherent frames&amp;#34;]
 E --&amp;gt; F[&amp;#34;Pixel-identical output on identical snapshots&amp;#34;]

 class A,B,C,D phase;
 class A1,B1,C1,D1 io;
 class E,F invariant;&lt;/div&gt;
 &lt;div class="mermaid-target" aria-label="Mermaid diagram"&gt;&lt;/div&gt;
&lt;/div&gt;&lt;script src="https://elvirh.com/mermaid.min.js"&gt;&lt;/script&gt;
 &lt;script&gt;
 (() =&gt; {
 const config = {"theme":"neutral","flowchart":{"htmlLabels":true,"curve":"basis","useMaxWidth":true,"nodeSpacing":40,"rankSpacing":50,"padding":12}};

 const renderMermaidBlocks = async () =&gt; {
 if (!window.mermaid) return;

 if (!window.__mermaidConfigured) {
 mermaid.initialize({ startOnLoad: false, ...config });
 window.__mermaidConfigured = true;
 }

 const blocks = document.querySelectorAll("[data-mermaid-block]:not([data-rendered])");
 for (const block of blocks) {
 const source = block.querySelector(".mermaid-source");
 const target = block.querySelector(".mermaid-target");
 if (!source || !target) continue;

 const code = source.textContent || "";
 const renderId = "m-" + Math.random().toString(36).slice(2);

 try {
 const { svg, bindFunctions } = await mermaid.render(renderId, code);
 target.innerHTML = svg;
 if (typeof bindFunctions === "function") bindFunctions(target);
 block.dataset.rendered = "true";
 } catch (error) {
 console.error("Mermaid render failed", error);
 target.innerHTML = "&lt;pre&gt;" + code.replace(/[&amp;&lt;&gt;]/g, (ch) =&gt; ({ "&amp;": "&amp;amp;", "&lt;": "&amp;lt;", "&gt;": "&amp;gt;" }[ch])) + "&lt;/pre&gt;";
 block.dataset.rendered = "error";
 }
 }
 };

 if (!window.__mermaidRenderHooked) {
 window.__mermaidRenderHooked = true;
 if (document.readyState === "loading") {
 document.addEventListener("DOMContentLoaded", renderMermaidBlocks, { once: true });
 } else {
 renderMermaidBlocks();
 }
 window.addEventListener("load", renderMermaidBlocks);
 } else {
 renderMermaidBlocks();
 }
 })();
 &lt;/script&gt;
 &lt;style&gt;
 .mermaid-block {
 width: 100%;
 }
 .mermaid-target {
 width: 100%;
 overflow-x: auto;
 }
 .mermaid-target svg {
 display: block;
 max-width: 100%;
 height: auto;
 margin: 0 auto;
 }
 &lt;/style&gt;
&lt;/div&gt;&lt;/details&gt;
&lt;details &gt;&lt;summary&gt;Details&lt;/summary&gt;&lt;div class="markdown-inner"&gt;

RetroLink follows a strict contract. These are architectural rules, not preferences.

### Phase Order (Never Changes)

- `Simulation -&gt; Snapshot -&gt; Rendering -&gt; Present`
- Host only simulates authoritative state.
- Clients never simulate, predict, smooth, or invent state.
- Rendering is snapshot-only and must be idempotent.

### Deterministic Rendering Rules

- Every visible pixel change must come from exactly one simulation tick.
- `render()` and draw helpers must not use `millis()`, `micros()`, timers, or randomness.
- Rendering must not mutate gameplay state.
- No role-based branching in rendering logic.

### Display and Buffer Rules

- Always render to back buffer only.
- Always present back buffer through RetroGoDisplay SPI DMA queue.
- After present: copy back to front, then swap.
- Front buffer is safety sync only, never a render target.
- Any tearing, flicker, ghosting, or frame desync is a critical defect.

### Interrupt and Networking Safety

- No display API calls from ISR or ESP-NOW callbacks.
- ISR path may only store packet data and set flags.
- Display work is deferred to main loop / task context.
- SPI transaction payloads must be stable memory, never stack buffers.

### Memory and Asset Policy

- DMA buffers must be internal DMA-capable RAM, not PSRAM.
- Large transient decode buffers may use PSRAM.
- Core gameplay visuals are tiles and sprites, not full-frame RGB565 assets.
- PNG assets are allowed in SPIFFS for UI and loading paths, decoded outside `render()`.

### Why This Works

This contract is what keeps multiplayer output deterministic across devices while preserving display stability on constrained hardware. The architecture was shaped by failures (white screens, bus corruption, stack-lifetime DMA bugs, and desync) and is intentionally strict to prevent regressions.
&lt;/div&gt;&lt;/details&gt;
&lt;section class="x-media" data-gallery="gallery-f1ea81cc65b879ca198c51fa8fbd7f3a"&gt;
 &lt;div class="x-media__head"&gt;
 &lt;h2&gt;Gallery&lt;/h2&gt;
 &lt;p&gt;4 images&lt;/p&gt;</description></item><item><title>Ekonio.cloud - Self-Hosted Engineering Platform</title><link>https://elvirh.com/en/projects/projects/ekonio-cloud/</link><pubDate>Thu, 12 Feb 2026 00:00:00 +0000</pubDate><guid>https://elvirh.com/en/projects/projects/ekonio-cloud/</guid><description>&lt;h2 id="overview"&gt;Overview&lt;a class="anchor" href="#overview"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://ekonio.cloud"&gt;Ekonio.cloud&lt;/a&gt; is my self-hosted infrastructure environment used to deploy and operate internal engineering tools and structured procurement workflows.&lt;/p&gt;
&lt;p class="link-callout"&gt;
 &lt;a href="https://ekonio.cloud" target="_blank" rel="noopener"&gt;
 &lt;span&gt;Ekonio.cloud&lt;/span&gt;
 &lt;span aria-hidden="true"&gt;↗&lt;/span&gt;
 &lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;The project represents full ownership of the software lifecycle:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Architecture design&lt;/li&gt;
&lt;li&gt;Backend development&lt;/li&gt;
&lt;li&gt;Data modeling&lt;/li&gt;
&lt;li&gt;Deployment&lt;/li&gt;
&lt;li&gt;Server configuration&lt;/li&gt;
&lt;li&gt;Ongoing maintenance&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It serves as a production-grade environment for structured data workflows, automation scripts, and system experiments.&lt;/p&gt;
&lt;h2 id="technical-highlights"&gt;Technical Highlights&lt;a class="anchor" href="#technical-highlights"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;VPS provisioning and Linux server configuration&lt;/li&gt;
&lt;li&gt;Docker-based service deployment&lt;/li&gt;
&lt;li&gt;Symfony backend architecture&lt;/li&gt;
&lt;li&gt;Entity-based data modeling (Component, Supplier, PriceDetail, OrderDetail)&lt;/li&gt;
&lt;li&gt;CSV import pipelines for structured bulk ingestion&lt;/li&gt;
&lt;li&gt;Price tier tracking and historical cost analysis&lt;/li&gt;
&lt;li&gt;Automated scraping integration for supplier comparison&lt;/li&gt;
&lt;li&gt;Secure remote access and environment isolation&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This environment supports real-world operational tooling rather than demonstration code.&lt;/p&gt;</description></item><item><title>USB-C PD Debugging &amp; Firmware Recovery</title><link>https://elvirh.com/en/projects/achievements/usb-c-repair/</link><pubDate>Tue, 10 Feb 2026 00:00:00 +0000</pubDate><guid>https://elvirh.com/en/projects/achievements/usb-c-repair/</guid><description>&lt;h2 id="overview"&gt;Overview&lt;a class="anchor" href="#overview"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Laptop was stuck at 5 V over USB-C with no proper PD contract negotiation, making the system appear dead.&lt;/p&gt;
&lt;p&gt;Even after replacing a visibly damaged power-path component, behavior stayed unchanged, which indicated a deeper control-layer fault.&lt;/p&gt;
&lt;h2 id="technical-highlights"&gt;Technical Highlights&lt;a class="anchor" href="#technical-highlights"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Diagnosed NX20P5090 failure in the USB-C power path&lt;/li&gt;
&lt;li&gt;Measured CC and VBUS behavior during PD negotiation&lt;/li&gt;
&lt;li&gt;Verified behavior after TPS65988 replacement&lt;/li&gt;
&lt;li&gt;Identified external SPI configuration dependency&lt;/li&gt;
&lt;li&gt;Regenerated and reflashed TPS65988 configuration&lt;/li&gt;
&lt;li&gt;Restored full USB-C PD negotiation&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="key-lessons"&gt;Key Lessons&lt;a class="anchor" href="#key-lessons"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Hardware repair now includes firmware and configuration state&lt;/li&gt;
&lt;li&gt;A valid power rail set does not guarantee functional system behavior&lt;/li&gt;
&lt;li&gt;Controller configuration memory can be as critical as controller silicon&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="technologies"&gt;Technologies&lt;a class="anchor" href="#technologies"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;USB-C Power Delivery&lt;/li&gt;
&lt;li&gt;NX20P5090&lt;/li&gt;
&lt;li&gt;TPS65988&lt;/li&gt;
&lt;li&gt;Oscilloscope-based signal validation&lt;/li&gt;
&lt;li&gt;SPI flash reprogramming&lt;/li&gt;
&lt;li&gt;TI configuration tooling&lt;/li&gt;
&lt;/ul&gt;
&lt;details &gt;&lt;summary&gt;Flowchart&lt;/summary&gt;&lt;div class="markdown-inner"&gt;

&lt;div class="mermaid-block" data-mermaid-block id="mermaid-0a2dc6199f4db88c7acb843294786eec-0"&gt;
 &lt;div class="mermaid-source" hidden&gt;flowchart TD
 A[Laptop stuck at 5V over USB-C] --&amp;gt; B[Replaced NX20P5090 power switch]
 B --&amp;gt; C[PD negotiation still failed]
 C --&amp;gt; D[Measured CC and VBUS behavior]
 D --&amp;gt; E[Replaced TPS65988 controller]
 E --&amp;gt; F[Behavior unchanged]
 F --&amp;gt; G[Checked external SPI flash configuration]
 G --&amp;gt; H[Regenerated and reflashed TI configuration]
 H --&amp;gt; I[PD negotiation restored]&lt;/div&gt;
 &lt;div class="mermaid-target" aria-label="Mermaid diagram"&gt;&lt;/div&gt;
&lt;/div&gt;&lt;script src="https://elvirh.com/mermaid.min.js"&gt;&lt;/script&gt;
 &lt;script&gt;
 (() =&gt; {
 const config = {"theme":"neutral","flowchart":{"htmlLabels":true,"curve":"basis","useMaxWidth":true,"nodeSpacing":40,"rankSpacing":50,"padding":12}};

 const renderMermaidBlocks = async () =&gt; {
 if (!window.mermaid) return;

 if (!window.__mermaidConfigured) {
 mermaid.initialize({ startOnLoad: false, ...config });
 window.__mermaidConfigured = true;
 }

 const blocks = document.querySelectorAll("[data-mermaid-block]:not([data-rendered])");
 for (const block of blocks) {
 const source = block.querySelector(".mermaid-source");
 const target = block.querySelector(".mermaid-target");
 if (!source || !target) continue;

 const code = source.textContent || "";
 const renderId = "m-" + Math.random().toString(36).slice(2);

 try {
 const { svg, bindFunctions } = await mermaid.render(renderId, code);
 target.innerHTML = svg;
 if (typeof bindFunctions === "function") bindFunctions(target);
 block.dataset.rendered = "true";
 } catch (error) {
 console.error("Mermaid render failed", error);
 target.innerHTML = "&lt;pre&gt;" + code.replace(/[&amp;&lt;&gt;]/g, (ch) =&gt; ({ "&amp;": "&amp;amp;", "&lt;": "&amp;lt;", "&gt;": "&amp;gt;" }[ch])) + "&lt;/pre&gt;";
 block.dataset.rendered = "error";
 }
 }
 };

 if (!window.__mermaidRenderHooked) {
 window.__mermaidRenderHooked = true;
 if (document.readyState === "loading") {
 document.addEventListener("DOMContentLoaded", renderMermaidBlocks, { once: true });
 } else {
 renderMermaidBlocks();
 }
 window.addEventListener("load", renderMermaidBlocks);
 } else {
 renderMermaidBlocks();
 }
 })();
 &lt;/script&gt;
 &lt;style&gt;
 .mermaid-block {
 width: 100%;
 }
 .mermaid-target {
 width: 100%;
 overflow-x: auto;
 }
 .mermaid-target svg {
 display: block;
 max-width: 100%;
 height: auto;
 margin: 0 auto;
 }
 &lt;/style&gt;
&lt;/div&gt;&lt;/details&gt;
&lt;details &gt;&lt;summary&gt;Details&lt;/summary&gt;&lt;div class="markdown-inner"&gt;

This repair started as a power-path failure and quickly turned into a mixed hardware and firmware fault analysis.

The first confirmed fault was the NX20P5090 USB-PD protection and switch device. Replacing it restored basic path integrity but did not recover PD contract negotiation.

Signal-level checks on CC and VBUS showed the policy engine was not completing valid negotiation states. Replacing the TPS65988 controller also produced no functional change.

That narrowed the fault domain to external dependencies, specifically the SPI flash used for TPS65988 configuration. After rebuilding and flashing the configuration image with TI tooling, the controller resumed normal policy behavior and negotiated correctly.

Key lesson: do not treat PD controllers as standalone silicon. Configuration memory is part of the active control plane.
&lt;/div&gt;&lt;/details&gt;
&lt;section class="x-media" data-gallery="gallery-0a2dc6199f4db88c7acb843294786eec"&gt;
 &lt;div class="x-media__head"&gt;
 &lt;h2&gt;Gallery&lt;/h2&gt;
 &lt;p&gt;4 images&lt;/p&gt;</description></item><item><title>Runtime Rendering and Client-Server Observability (PoC)</title><link>https://elvirh.com/en/projects/achievements/runtime-observability-poc/</link><pubDate>Thu, 12 Feb 2026 00:00:00 +0000</pubDate><guid>https://elvirh.com/en/projects/achievements/runtime-observability-poc/</guid><description>&lt;h2 id="overview"&gt;Overview&lt;a class="anchor" href="#overview"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This proof-of-concept explores how modern SaaS applications expose structured system behavior at runtime, even when that structure is abstracted behind complex interfaces.&lt;/p&gt;
&lt;p&gt;Using browser-level instrumentation and execution tracing, the project demonstrates how client-side rendering pipelines and network interactions can be observed, analyzed, and modeled without modifying backend systems.&lt;/p&gt;
&lt;p&gt;The objective was not feature manipulation, but architectural understanding: identifying where authority resides, how state transitions occur, and how frontend abstractions relate to backend contracts.&lt;/p&gt;</description></item><item><title>OptiMOS™ 5 Power Stage Design &amp; Simulation</title><link>https://elvirh.com/en/projects/projects/optimos-mosfet/</link><pubDate>Tue, 10 Feb 2026 00:00:00 +0000</pubDate><guid>https://elvirh.com/en/projects/projects/optimos-mosfet/</guid><description>&lt;h2 id="overview"&gt;Overview&lt;a class="anchor" href="#overview"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;OptiMOS™ 5 IQDH29NE2LM5CGSC.&lt;/p&gt;
&lt;p&gt;What surprised me most wasn’t the topology — but how much performance is hidden in a very small package.
According to the datasheet, this device can handle extremely high pulsed currents when used correctly, which pushed me to dig deeper into pulse conditions, thermal limits, and PCB layout rather than just headline numbers.
I designed the PCB, reviewed the datasheet in detail, and validated assumptions using LTspice (electrical + thermal behavior).&lt;/p&gt;</description></item><item><title>AMS Job Market Analysis Tool</title><link>https://elvirh.com/en/projects/projects/ams-tool/</link><pubDate>Sun, 27 Aug 2023 00:00:00 +0000</pubDate><guid>https://elvirh.com/en/projects/projects/ams-tool/</guid><description>&lt;h2 id="overview"&gt;Overview&lt;a class="anchor" href="#overview"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Python tool to collect and analyze publicly available remote job listings from the Austrian Public Employment Service (AMS).&lt;/p&gt;
&lt;p&gt;Exports:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Job titles&lt;/li&gt;
&lt;li&gt;Descriptions&lt;/li&gt;
&lt;li&gt;Excel datasets&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Second script:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Analyzes programming language demand&lt;/li&gt;
&lt;li&gt;Generates trend graphs&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="technical-highlights"&gt;Technical Highlights&lt;a class="anchor" href="#technical-highlights"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Automated collection of publicly available job listings&lt;/li&gt;
&lt;li&gt;Structured extraction of titles and descriptions&lt;/li&gt;
&lt;li&gt;Data cleaning and normalization pipeline&lt;/li&gt;
&lt;li&gt;Programming-language demand analysis&lt;/li&gt;
&lt;li&gt;Excel export for stakeholder-friendly reporting&lt;/li&gt;
&lt;li&gt;Chart generation for trend visibility&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="key-lessons"&gt;Key Lessons&lt;a class="anchor" href="#key-lessons"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Small automation workflows can produce decision-ready market insights&lt;/li&gt;
&lt;li&gt;Data normalization is essential before trend analysis&lt;/li&gt;
&lt;li&gt;Export format matters when sharing results with non-technical stakeholders&lt;/li&gt;
&lt;li&gt;Public datasets often require structured preprocessing before becoming useful&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="technologies"&gt;Technologies&lt;a class="anchor" href="#technologies"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Python&lt;/li&gt;
&lt;li&gt;HTTP data extraction&lt;/li&gt;
&lt;li&gt;Pandas&lt;/li&gt;
&lt;li&gt;Excel export&lt;/li&gt;
&lt;li&gt;Data visualization&lt;/li&gt;
&lt;/ul&gt;
&lt;details &gt;&lt;summary&gt;Flowchart&lt;/summary&gt;&lt;div class="markdown-inner"&gt;

&lt;div class="mermaid-block" data-mermaid-block id="mermaid-7f3939afcd213639858afa961fa88d2c-0"&gt;
 &lt;div class="mermaid-source" hidden&gt;flowchart TD
 A[AMS remote listings] --&amp;gt; B[Python collector]
 B --&amp;gt; C[Raw dataset]
 C --&amp;gt; D[Normalize and deduplicate]
 D --&amp;gt; E[Extract language keywords]
 E --&amp;gt; F[Aggregate demand frequency]
 F --&amp;gt; G[Excel export]
 F --&amp;gt; H[Demand trend charts]&lt;/div&gt;
 &lt;div class="mermaid-target" aria-label="Mermaid diagram"&gt;&lt;/div&gt;
&lt;/div&gt;&lt;script src="https://elvirh.com/mermaid.min.js"&gt;&lt;/script&gt;
 &lt;script&gt;
 (() =&gt; {
 const config = {"theme":"neutral","flowchart":{"htmlLabels":true,"curve":"basis","useMaxWidth":true,"nodeSpacing":40,"rankSpacing":50,"padding":12}};

 const renderMermaidBlocks = async () =&gt; {
 if (!window.mermaid) return;

 if (!window.__mermaidConfigured) {
 mermaid.initialize({ startOnLoad: false, ...config });
 window.__mermaidConfigured = true;
 }

 const blocks = document.querySelectorAll("[data-mermaid-block]:not([data-rendered])");
 for (const block of blocks) {
 const source = block.querySelector(".mermaid-source");
 const target = block.querySelector(".mermaid-target");
 if (!source || !target) continue;

 const code = source.textContent || "";
 const renderId = "m-" + Math.random().toString(36).slice(2);

 try {
 const { svg, bindFunctions } = await mermaid.render(renderId, code);
 target.innerHTML = svg;
 if (typeof bindFunctions === "function") bindFunctions(target);
 block.dataset.rendered = "true";
 } catch (error) {
 console.error("Mermaid render failed", error);
 target.innerHTML = "&lt;pre&gt;" + code.replace(/[&amp;&lt;&gt;]/g, (ch) =&gt; ({ "&amp;": "&amp;amp;", "&lt;": "&amp;lt;", "&gt;": "&amp;gt;" }[ch])) + "&lt;/pre&gt;";
 block.dataset.rendered = "error";
 }
 }
 };

 if (!window.__mermaidRenderHooked) {
 window.__mermaidRenderHooked = true;
 if (document.readyState === "loading") {
 document.addEventListener("DOMContentLoaded", renderMermaidBlocks, { once: true });
 } else {
 renderMermaidBlocks();
 }
 window.addEventListener("load", renderMermaidBlocks);
 } else {
 renderMermaidBlocks();
 }
 })();
 &lt;/script&gt;
 &lt;style&gt;
 .mermaid-block {
 width: 100%;
 }
 .mermaid-target {
 width: 100%;
 overflow-x: auto;
 }
 .mermaid-target svg {
 display: block;
 max-width: 100%;
 height: auto;
 margin: 0 auto;
 }
 &lt;/style&gt;
&lt;/div&gt;&lt;/details&gt;
&lt;details &gt;&lt;summary&gt;Details&lt;/summary&gt;&lt;div class="markdown-inner"&gt;

The AMS toolchain was built as a repeatable data collection and analysis workflow for remote software roles. Script one retrieves listings, parses structured fields, and stores normalized records for downstream processing. Data quality controls address duplicated posts and inconsistent text fragments. Script two analyzes language and technology demand by scanning job descriptions and aggregating frequency trends.

The output includes both tabular datasets and visual summaries. Excel export was selected for portability, enabling distribution to non-technical stakeholders without additional tooling.

The project demonstrates how lightweight automation can transform fragmented public listings into structured, decision-ready market insights.
&lt;/div&gt;&lt;/details&gt;
&lt;section class="x-media" data-gallery="gallery-7f3939afcd213639858afa961fa88d2c"&gt;
 &lt;div class="x-media__head"&gt;
 &lt;h2&gt;Gallery&lt;/h2&gt;
 &lt;p&gt;3 images&lt;/p&gt;</description></item></channel></rss>