LINK: ESTABLISHED
BOOTING PERSONAL TERMINAL...    LOADING USER PROFILE...    APPLYING CRT FILTERS...    PRESS [NAV] TO SWITCH SECTIONS    //    SELF-HOSTED AGGREGATE ANALYTICS ACTIVE.  
MODE: DESKTOP

briefings Embedded Engineering: Wenn dein Code direkt mit der Welt spricht


https://wieerwill.dev/talks/2604-embedded-engineering
DATE 04/04/2026
TAGS DE

Embedded Engineering: Wenn dein Code direkt mit der Welt spricht

Web-Apps und Desktop-Tools hat jedes IT'wesen schon geschrieben. Aber was, wenn dein Code direkt LEDs, Motoren oder Sensoren steuert? In diesem kurzen Vortrag tauchen wir in die Welt der Embedded-Systeme ein: kleine Computer ohne Betriebssystem, direkt auf der Hardware, aber mit moderner Sprache und solider Architektur.

Mit einem Mix aus Coding, Praxisbeispielen und technischen Einblicken zeige ich dir, wie Embedded-Entwicklung mit Rust nicht nur sicher, sondern auch richtig spannend wird.

Easterhegg 2026 · EH23 · Koblenz

Embedded Engineering

Wenn dein Code direkt mit der Welt spricht

04.04.2026

RoboCup war mein Gateway Drug

  • Roboter sind “Software mit Konsequenzen”
  • Du merkst sofort, ob du Mist gebaut hast: er fährt, kippt, brennt, verliert
  • Embedded ist der Teil, der die Physik trifft

Fun Fact: 2027 RoboCup WM in Nürnberg

robocup.de

Embedded ist überall

  • “Computer” ist nicht immer Laptop
  • Oft ist es ein Chip + ein Job
  • pro Tag hunderte um dich

Viele dieser “unsichtbaren” Rechner sind Cortex-M-Klasse: klein, billig, zuverlässig

Embedded = Probleme in kleinen Lösungsraum

  • Wenig RAM/Flash → plane Datenflüsse, statt “wir skalieren später”
  • Echtzeit/IO → Nebenläufigkeit ist nicht optional
  • Updates sind teuer → Debugbarkeit ist Teil des Produkts
  • Hardware ist “API”, aber mit Lieferzeiten

Unser Mini-Labor: Blue Pill

  • Board: STM32F103 (“Blue Pill”)
  • Debug: SWD (z.B. ST-Link / probe-rs fähig)
  • IO-Experimente:
    • LED (digital / PWM)
    • Button (digital)
    • “Sensor” (Potentiometer → ADC)

Sichtbare Physik als Feedback-Loop

Hello World ohne Display: Blinky

  • Kein Terminal
  • Kein Browser
  • Nur Strom + ein Pin

Wenn das blinkt, lebt die Kette:

Toolchain → Flash → CPU → GPIO → Elektronen

Wenn’s schiefgeht, gibt’s Debug-Ausgabe…

Mercado Viagens from Brasil, CC BY 2.0, Wikimedia Commons

Mini-Glossar

  • MCU: CPU + RAM/Flash + Peripherie auf einem Chip
  • GPIO: Pin als Ein/Aus
  • ADC: Spannung → Zahl
  • PWM: “Analog tun” mit Digital-Pins (Duty Cycle)
  • Interrupt: Hardware weckt Task
  • HAL: Treiber-Schicht: “Pin A9” wird “LED”
rust-embedded.org/bookshelf/

Pinout

Analog vs. Digital

  • Die Welt ist analog
  • Dein Code ist digital
  • Embedded heißt: Übersetzen bzw. Abschätzen

ADC/DAC sind am Ende nur Methoden, Spannung und Zeit zu formen

PWM: Zeit als Stellschraube

  • Du hast nur “0/1” (Digital)
  • Du willst aber dimmbar / Servo / Motor
  • Lösung: Duty Cycle

PWM ist kein echtes Analog. Aber oft reicht’s nd es ist billig

UART: Bits + Timing + Agreement

  • Kein “Netzwerk”
  • Kein “Stack”
  • Nur: Startbit, Datenbits, Stopbit
  • Praktisch: Logs, GPS, Debug-Konsole
  • I²C/SPI sind die gleichen Grundideen – nur andere Verträge

MCU: CPU + Peripherie + Bus

Das “Programm” ist nur ein Teil. Der Rest sind Timer, DMA, UART, ADC, GPIO, Interrupts.

Azya52, CC0, Wikimedia Commons

Memory-Mapped IO

  • Peripherie ist Speicherbereich
  • Register sind Adressen
  • Lesen/Schreiben → Hardware reagiert

”Transistoren” plötzlich wie API

Bare Metal in Rust

no_std ist Absicht

#![no_std]
#![no_main]

use cortex_m_rt::entry;

#[entry]
fn main() -> ! {
    loop {
        // your code hits physics here
    }
}
  • Kein OS → kein std
  • verlinke nur, was du wirklich brauchst (core, optional alloc)
  • “Komfort” kostet in Embedded oft: Flash, RAM, Determinismus
docs.rust-embedded.org/book/intro/no-std.html

Ownership & Borrowing

der Compiler ist dein zweites Paar Augen

  • Kein GC → trotzdem Memory Safety
  • Werte werden besessen (move), nicht “irgendwo referenced”
  • Du leihst Daten (borrow) statt sie “mal kurz zu teilen”
  • Ergebnis: viele Fehler verhindert, bevor du überhaupt flashst
doc.rust-lang.org/book/ch04-00-understanding-ownership.html

Ownership: Copy vs Borrow

let eins: i32 = 1;
let mut zwei: i32 = 2;

zwei = 3;              // ok: nur neue Zuweisung
let drei = eins;       // ok: Copy, eins bleibt gültig
let vier = &eins;      // ok: Borrow (immutable reference)

println!("{}", eins);  // ok: eins ist noch da
println!("{}", drei);  // ok
println!("{}", vier);  // ok: deref implizit bei Display

Der Debug-Loop muss banal sein

https://probe.rs/docs/tools/cargo-embed/

  • cargo embed = build → flash → reset → RTT → GDB
  • Logs früh aktivieren (RTT/defmt), sonst LED-Morsecode
  • Ziel: eine Änderung → sofort Feedback
  • wenn das frustet, verlierst du 80% deiner Zeit/Energie
probe.rs

Weniger Handarbeit: Datasheet → SVD → API

  • CMSIS-SVD beschreibt Register/Bitfelder als XML
  • Generatoren bauen daraus typsichere Register-APIs
  • Ergebnis: weniger Copy-Paste, weniger “falscher Bitshift”
  • Rust verliert keine Hardware-Tiefe (nur Tippfehler)

Minimal bare-metal blinky

#![no_std]
#![no_main]

use cortex_m::asm::delay;
use cortex_m_rt::entry;
use panic_halt as _;
use stm32f1::stm32f103;

#[entry]
fn main() -> ! {
    let dp = stm32f103::Peripherals::take().unwrap();

    // Clock für GPIOC
    dp.RCC.apb2enr.modify(|r, w| unsafe {
        w.bits(r.bits() | (1 << 4))
    });

    // PC13 als Output Push-Pull @ 2 MHz
    dp.GPIOC.crh.modify(|r, w| unsafe {
        let mut bits = r.bits();
        bits &= !(0xF << 20);        // PC13-Feld (4 Bits) löschen
        bits |=  (0x2 << 20);        // MODE13=10, CNF13=00 setzen
        w.bits(bits)
    });

    const BLINK_DELAY_CYCLES: u32 = 2_000_000; // Cycles! not ms

    loop {
        // PC13 LOW (reset) -> LED oft AN
        dp.GPIOC.bsrr.write(|w| unsafe { w.bits(1 << (13 + 16)) });
        delay(BLINK_DELAY_CYCLES);

        // PC13 HIGH (set) -> LED AUS
        dp.GPIOC.bsrr.write(|w| unsafe { w.bits(1 << 13) });
        delay(BLINK_DELAY_CYCLES);
    }
}

Problem: Tasks nacheinander ausführen

#![no_std]
#![no_main]

use cortex_m_rt::entry;
use panic_halt as _;
use stm32f1xx_hal::{gpio::PinState, pac, prelude::*};

#[entry]
fn main() -> ! {
    let dp = pac::Peripherals::take().unwrap();
    let cp = cortex_m::Peripherals::take().unwrap();

    let mut flash = dp.FLASH.constrain();
    let rcc = dp.RCC.constrain();
    let clocks = rcc.cfgr.freeze(&mut flash.acr);
    let mut delay = cp.SYST.delay(&clocks);

    let mut gpioa = dp.GPIOA.split();
    let mut gpioc = dp.GPIOC.split();

    let mut led = gpioc.pc13.into_push_pull_output_with_state(&mut gpioc.crh, PinState::High);
    let button = gpioa.pa0.into_pull_up_input(&mut gpioa.crl);

    let mut wait_ms: u16 = 300;
    let mut was_up = true;

    loop {
        let _ = led.toggle();

        if was_up && button.is_low() {
            if wait_ms > 1 {
                wait_ms /= 2;
            }
            was_up = false;
        } else if button.is_high() {
            was_up = true;
        }

        delay.delay_ms(wait_ms);
    }
}

Nebenläufigkeit ist kein Feature.

Sie ist Vorraussetzung für vieles

Async ist kein OS

Es ist ein fairer Scheduler

Embassy Executor Modell

Embassy (embedded + async)

  • Tasks sind statisch allokiert (kein Heap nötig)
  • CPU schläft, wenn nix los ist (Interrupts wecken)
  • Fairness: ein Task kann nicht “alles” belegen
  • Du bekommst “blink + button + sensor” ohne Thread-Tetris
docs.embassy.dev/embassy-executor

Embassy Async Beispiel

#![no_std]
#![no_main]

use core::sync::atomic::{AtomicU32, Ordering};
use embassy_executor::Spawner;
use embassy_stm32::{
    exti::ExtiInput,
    gpio::{Level, Output, Pull, Speed},
};
use embassy_time::Timer;
use panic_probe as _;

static BLINK_DELAY_MS: AtomicU32 = AtomicU32::new(3000);

#[embassy_executor::task]
async fn led_task(mut led: Output<'static, embassy_stm32::peripherals::PC13>) {
    loop {
        led.toggle();
        let d = BLINK_DELAY_MS.load(Ordering::Relaxed) as u64;
        Timer::after_millis(d).await;
    }
}

#[embassy_executor::task]
async fn button_task(mut button: ExtiInput<'static, embassy_stm32::peripherals::PA0>) {
    loop {
        button.wait_for_falling_edge().await;
        let d = (BLINK_DELAY_MS.load(Ordering::Relaxed) / 2);
        BLINK_DELAY_MS.store(d, Ordering::Relaxed);
        button.wait_for_rising_edge().await;
    }
}

#[embassy_executor::main]
async fn main(spawner: Spawner) -> ! {
    let p = embassy_stm32::init(Default::default());
    let led = Output::new(p.PC13, Level::High, Speed::Low);
    let button = ExtiInput::new(p.PA0, p.EXTI0, Pull::Up);

    spawner.spawn(led_task(led)).unwrap();
    spawner.spawn(button_task(button)).unwrap();

    core::future::pending::<()>().await;
    unreachable!()
}

Memory Safety ist Key-Faktor

  • Android 2025: Memory-Safety-Vulns fallen erstmals unter 20%
  • In deren Auswertung: Rust ~1000× geringere Memory-Safety-Bug-Dichte als C/C++
  • Aber: unsafe existiert! Wichtig ist Kapselung + Review-Disziplin
security.googleblog.com/2025/11/rust-in-android-move-fast-fix-things.html

Playbook: Erste Stunde Embedded Rust

  1. Board + Debugger: Strom + SWD steht
  2. Blinky: GPIO + Timer/Ticks
  3. Logs an: RTT/defmt
  4. Ein Input: Button oder ADC (Poti)
  5. Ein Output: PWM (LED dimmen)
  6. Dann erst: UART / I²C / SPI

Erst Feedback, dann Komplexität.

Decision Matrix: Rust vs C/C++ vs MicroPython

Rust

  • (+) Guardrails (Ownership/Types)
  • (+) Einheitliches Tooling (Cargo/Docs/Tests)
  • (–) Setup/Compile-Zeit
  • (–) unsafe braucht Regeln

C/C++ / MicroPython

  • C/C++: (+) Kontrolle, (–) Footguns
  • MicroPython: (+) REPL/Tempo, (–) Grenzen bei Echtzeit/Footprint
  • Beides: Ökosystem stark, DX weniger “einheitlich”
micropython.org/

Zusammenfassend

Embedded mit Rust ist “weniger Stress”, nicht “weniger nah dran”

  • Du arbeitest weiter mit Timern, Interrupts, Registern
  • mit besseren Verträgen zwischen Modulen (Traits/HAL)
  • Ziel: schneller iterieren, Fehler vornherein Verhindern

Mehr?

Auf der Easterhegg23

“The Bunny is a Lie, but Rust is the Truth” 2026-04-04 15:15–16:15, Test Chamber 01

“Consent Theater & Interface Oper”, 2026-04-05 10:15–10:45, The Rabbit Hole

“Arduino For Total Newbies”, 2026-04-05 13:45–17:15, Solder Enrichment Center

END OF BRIEFING

EMBED // MEDIA.CCC.DE

Watch the Stream right here

DURATION31 min
VIEWS99+
SPEAKERWieErWill
RELEASE2026-04-04
End of record. Further disclosure requires clearance.