RP2040-Zero programmieren: Dual-Core Mikrocontroller fuer Arduino-Einsteiger

Die meisten Mikrocontroller-Projekte laufen komplett sequenziell in einer einzigen loop() ab. Das RP2040-Zero bringt zwei vollwertige ARM-Cortex-M0+ Kerne mit – und damit die Möglichkeit, zwei unabhängige Programme gleichzeitig laufen zu lassen. In diesem Tutorial nutzt du Kern 0 für eine flüssige RGB-LED-Animation und Kern 1 parallel für die Temperaturmessung mit einem DS18B20-Sensor, ganz ohne dass sich beide Aufgaben gegenseitig ausbremsen.

Was das RP2040-Zero besonders macht

Das RP2040-Zero basiert auf dem gleichen RP2040-Chip, der auch im Raspberry Pi Pico steckt – nur in einem noch kompakteren Formfaktor mit USB-C. Der Clou: Der Chip hat zwei gleichwertige ARM-Cortex-M0+ Kerne, die mit bis zu 133MHz takten und unabhängig voneinander Code ausführen können. Über die Arduino-IDE-Unterstützung (Earle-Philhower-Core) stehen dir dafür die Funktionen setup1() und loop1() zur Verfügung – ein zweiter, kompletter Sketch-Zyklus, der parallel zum ersten läuft.

Zusätzlich bringt das Board eine adressierbare WS2812-RGB-LED direkt onboard mit, die sich ideal für Status-Animationen eignet, ohne zusätzliche Hardware zu benötigen. In diesem Tutorial übernimmt genau diese LED die visuelle Ausgabe, während im Hintergrund auf dem zweiten Kern ein Sensor ausgelesen wird.

Was du brauchst

Bauteil Menge ca. Preis Link
RP2040-Zero Board mit Dual-Core Cortex M0+ 1x ab 3,99 € Zum Produkt
DS18B20 wasserdichter Temperatursensor 1x ab 1,39 € Zum Produkt
Breadboard 400 Kontakte 1x ab 3,49 € Zum Produkt
22AWG Kabel 2-adrig 1x Meterware ab 0,99 € Zum Produkt
Widerstand 4,7kΩ (Pull-Up für DS18B20) 1x ab 2,49 € (600er Sortiment) Zum Produkt

Einrichtung in der Arduino IDE

Füge unter „Zusätzliche Boardverwalter-URLs" die URL des Raspberry Pi Pico/RP2040-Boardpakets (Earle Philhower Core) hinzu und installiere darüber das Paket. Wähle danach als Board Waveshare RP2040-Zero bzw. „Generic RP2040" mit passender Flash-Größe aus. Für die WS2812-LED installierst du zusätzlich die Bibliothek Adafruit NeoPixel, für den Temperatursensor OneWire und DallasTemperature.

DS18B20 Kabel RP2040-Zero Pin Funktion
Rot (VCC) 3.3V Spannungsversorgung
Schwarz (GND) GND Masse
Gelb (Data) GPIO 15 OneWire-Datenleitung

Zwischen Data-Leitung und VCC gehört wie beim DS18B20 üblich ein 4,7kΩ Pull-Up-Widerstand. Die onboard WS2812-LED ist bereits fest mit GPIO 16 verdrahtet und braucht keine zusätzliche Verkabelung.

Code 1: Zwei Kerne, zwei parallele Aufgaben

Der Grundbaustein: setup()/loop() laufen auf Kern 0, setup1()/loop1() parallel dazu auf Kern 1 – komplett unabhängig voneinander.

void setup() {
  Serial.begin(115200);
  pinMode(LED_BUILTIN, OUTPUT);
}

void loop() {
  // Läuft auf Kern 0: einfaches Blinken im Sekundentakt
  digitalWrite(LED_BUILTIN, HIGH);
  delay(500);
  digitalWrite(LED_BUILTIN, LOW);
  delay(500);
}

void setup1() {
  // Läuft auf Kern 1, eigener, unabhängiger Setup-Block
}

void loop1() {
  // Läuft auf Kern 1: zählt unabhängig mit und gibt seriell aus
  static unsigned long zaehler = 0;
  Serial.print("Kern 1 laeuft unabhaengig, Zaehler: ");
  Serial.println(zaehler++);
  delay(1000);
}

Beide Schleifen laufen komplett unabhängig – ein delay() auf Kern 0 blockiert Kern 1 in keiner Weise. Das ist der entscheidende Unterschied zu Single-Core-Boards wie dem UNO R3, wo jede blockierende Wartezeit zwangsläufig das gesamte Programm anhält.

Code 2: RGB-Animation auf Kern 0, Temperaturmessung auf Kern 1

Jetzt kombinierst du beide Kerne sinnvoll: Kern 0 zeigt per Onboard-WS2812-LED flüssig den aktuellen Temperaturbereich als Farbe an (blau = kalt, grün = normal, rot = warm), während Kern 1 komplett unabhängig alle 2 Sekunden den DS18B20 ausliest – ein rechenintensiverer OneWire-Read blockiert dadurch nie die LED-Animation.

#include <Adafruit_NeoPixel.h>
#include <OneWire.h>
#include <DallasTemperature.h>

#define LED_PIN     16
#define LED_COUNT    1
#define ONEWIRE_PIN 15

Adafruit_NeoPixel pixel(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);
OneWire oneWire(ONEWIRE_PIN);
DallasTemperature sensoren(&oneWire);

volatile float aktuelleTemperatur = 20.0; // von Kern 1 geschrieben, von Kern 0 gelesen

void setup() {
  Serial.begin(115200);
  pixel.begin();
  pixel.setBrightness(60);
}

void loop() {
  // Kern 0: Farbe je nach zuletzt gemessener Temperatur
  float t = aktuelleTemperatur;

  uint32_t farbe;
  if (t < 18.0) {
    farbe = pixel.Color(0, 0, 255);      // Kalt -> Blau
  } else if (t < 26.0) {
    farbe = pixel.Color(0, 255, 0);      // Normal -> Gruen
  } else {
    farbe = pixel.Color(255, 0, 0);      // Warm -> Rot
  }

  pixel.setPixelColor(0, farbe);
  pixel.show();
  delay(100);
}

void setup1() {
  sensoren.begin();
}

void loop1() {
  sensoren.requestTemperatures();
  float t = sensoren.getTempCByIndex(0);

  if (t != DEVICE_DISCONNECTED_C) {
    aktuelleTemperatur = t;
    Serial.print("Kern 1 - gemessene Temperatur: ");
    Serial.print(t);
    Serial.println(" °C");
  }

  delay(2000);
}

Die Variable aktuelleTemperatur ist als volatile deklariert, damit der Compiler sie bei jedem Zugriff frisch aus dem Speicher liest, statt einen zwischengespeicherten (und möglicherweise veralteten) Wert in einem Register zu verwenden – wichtig, weil hier zwei Kerne auf dieselbe Variable zugreifen.

Code 3: Sanfte Farbübergänge statt harter Sprünge

Statt hart zwischen drei Farben zu springen, berechnest du hier eine stufenlose Farbinterpolation zwischen Blau, Grün und Rot – für eine deutlich hochwertiger wirkende Anzeige.

#include <Adafruit_NeoPixel.h>

#define LED_PIN   16
#define LED_COUNT  1

Adafruit_NeoPixel pixel(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);

extern volatile float aktuelleTemperatur;

uint32_t interpoliereFarbe(float t, float minT, float maxT) {
  float anteil = constrain((t - minT) / (maxT - minT), 0.0, 1.0);

  int r, g, b;
  if (anteil < 0.5) {
    // Blau -> Gruen
    float lokal = anteil * 2.0;
    r = 0;
    g = (int)(255 * lokal);
    b = (int)(255 * (1.0 - lokal));
  } else {
    // Gruen -> Rot
    float lokal = (anteil - 0.5) * 2.0;
    r = (int)(255 * lokal);
    g = (int)(255 * (1.0 - lokal));
    b = 0;
  }

  return pixel.Color(r, g, b);
}

void setup() {
  pixel.begin();
  pixel.setBrightness(60);
}

void loop() {
  uint32_t farbe = interpoliereFarbe(aktuelleTemperatur, 10.0, 35.0);
  pixel.setPixelColor(0, farbe);
  pixel.show();
  delay(50);
}

Diese Funktion lässt sich 1:1 in Code 2 einsetzen, indem du die harte if/else-Farblogik in loop() durch den Aufruf von interpoliereFarbe() ersetzt – das Ergebnis ist ein sanfter Farbverlauf statt sichtbarer Sprünge zwischen den Temperaturbereichen.

Praxistipps

🔒 Geteilte Variablen absichern: Für einfache Werte wie einen einzelnen float reicht volatile meist aus. Bei komplexeren Datenstrukturen (mehrere zusammengehörige Werte) solltest du stattdessen die eingebaute Mutex-Klasse aus dem Philhower-Core nutzen, um Lese-/Schreibkonflikte zwischen den Kernen sicher zu vermeiden.

🌡️ Mehrere DS18B20 an einem Pin: Der OneWire-Bus erlaubt mehrere Sensoren an derselben Datenleitung. Nutze sensoren.getDeviceCount() und getTempCByIndex(i), um z. B. mehrere Messpunkte in einem Gehäuse gleichzeitig zu überwachen.

💡 Boot-Modus: Hältst du beim Einstecken des RP2040-Zero die BOOT-Taste gedrückt, meldet sich das Board als USB-Massenspeicher (BOOTSEL) – praktisch, falls die Arduino IDE das Board einmal nicht automatisch erkennt.

Troubleshooting

⚠️ „Device disconnected" beim DS18B20: Fehlt der 4,7kΩ Pull-Up zwischen Data und VCC, liefert der Sensor keine gültigen Werte. Zweithäufigste Ursache: eine zu lange oder schlecht verlegte Datenleitung ohne Abschirmung.

⚠️ Board wird nicht erkannt: Halte beim Einstecken des USB-Kabels die BOOT-Taste gedrückt, bis das Board als Laufwerk erscheint, und wähle in der Arduino IDE anschließend manuell den passenden COM-Port.

⚠️ Kern 1 startet nicht / loop1() wird nie aufgerufen: Prüfe, ob im Boardmanager wirklich ein RP2040-Board (nicht versehentlich ein AVR-Board) ausgewählt ist – setup1()/loop1() existieren nur im RP2040-Core und werden auf anderen Plattformen stillschweigend ignoriert.

⚡ Starte dein Dual-Core-Projekt

Alle Bauteile für dieses Tutorial findest du bei MAKEROO.

Mehr zum Sensor selbst liest du in unserem DS18B20-OneWire-Tutorial.

Fazit

Mit dem RP2040-Zero nutzt du echte parallele Verarbeitung, ohne dich mit Interrupts oder komplexem Multitasking-Code herumschlagen zu müssen. setup1() und loop1() machen aus deinem Mikrocontroller praktisch zwei unabhängige Boards in einem – ideal für Projekte, bei denen eine flüssige Anzeige und eine unabhängige Sensormessung gleichzeitig laufen sollen.

Zurück zum Blog