I2C-Bus mit Arduino verstehen: I2C-Scanner, Adresskonflikte lösen & mehrere Sensoren an zwei Kabeln

Ein OLED-Display, ein Luftdrucksensor, ein Gyroskop und eine Echtzeituhr – vier Module, und trotzdem brauchst du am Arduino nur zwei Datenleitungen. Möglich macht das der I2C-Bus, der wahrscheinlich wichtigste Standard in der Maker-Elektronik. Solange alles funktioniert, denkt niemand darüber nach. Aber sobald zwei Module dieselbe Adresse haben, das Display nach dem vierten Sensor schwarz bleibt oder ein längeres Kabel alles zerlegt, hilft nur noch: verstehen, was auf diesen zwei Drähten eigentlich passiert. Genau das machen wir hier – mit fertigem I2C-Scanner, einer Lösung für Adresskonflikte und den Pull-Up-Regeln, die dir stundenlange Fehlersuche ersparen.

Was I2C überhaupt ist

I2C (gesprochen „I-Quadrat-C“, von Inter-Integrated Circuit) wurde in den 1980ern von Philips erfunden, um Chips auf einer Platine mit möglichst wenigen Leitungen zu verbinden. Das Ergebnis: Ein Bus mit exakt zwei Signalleitungen, an dem theoretisch bis zu 127 Geräte hängen dürfen.

Leitung Bedeutung Wer treibt sie?
SDA Serial Data – die Nutzdaten Master und Slave abwechselnd
SCL Serial Clock – der Takt immer der Master
VCC Versorgungsspannung
GND gemeinsame Masse (Pflicht!)

Ein Gerät ist der Master (dein Arduino oder ESP32) und gibt den Takt vor. Alle anderen sind Slaves und antworten nur, wenn sie mit ihrer Adresse angesprochen werden. Die Kommunikation läuft immer nach demselben Muster ab: Der Master zieht SDA auf Low, während SCL noch High ist (das ist die Start-Bedingung), schickt dann die 7-Bit-Adresse des gewünschten Slaves plus ein Bit für Lesen oder Schreiben. Das angesprochene Gerät antwortet mit einem ACK – einem einzelnen Low-Impuls, der „ja, ich bin da“ bedeutet. Danach fließen die Datenbytes.

ℹ️ Das ACK ist der Schlüssel zu allem: Dieses eine Bestätigungsbit ist der Grund, warum ein I2C-Scanner überhaupt funktioniert. Der Master ruft nacheinander jede mögliche Adresse auf und schaut, ob jemand antwortet. Kommt ein ACK, existiert dort ein Gerät. Kommt keins, ist die Adresse leer. Mehr steckt hinter dem Scanner-Sketch nicht – und genau deshalb ist er dein wichtigstes Diagnosewerkzeug.

Open-Drain: Warum Pull-Up-Widerstände Pflicht sind

Hier steckt der wichtigste Unterschied zu allem, was du bisher am Arduino gemacht hast. Ein normaler GPIO-Pin kann aktiv High und aktiv Low ausgeben. I2C-Pins können das nicht – sie arbeiten im Open-Drain-Modus: Jedes Gerät kann die Leitung nur nach Masse ziehen (Low), aber niemals aktiv auf High treiben.

Wer macht dann High? Die Pull-Up-Widerstände. Sie ziehen die Leitung nach VCC, solange niemand sie nach unten zerrt. Ohne Pull-Ups hängt der Bus in der Luft, und nichts funktioniert.

💡 Warum dieser Umweg? Weil so nie ein Kurzschluss entstehen kann. Zieht Gerät A die Leitung auf Low, während Gerät B sie loslassen will, passiert genau nichts Schlimmes – die Leitung ist eben Low. Würden beide aktiv treiben, käme High gegen Low, und einer der beiden Chips würde durchbrennen. Open-Drain macht den geteilten Bus überhaupt erst möglich.

Wie groß müssen die Pull-Ups sein?

Situation Empfohlener Wert Anmerkung
1–2 Breakout-Module, kurze Kabel 4,7 kΩ meist schon auf den Modulen verbaut
Mehrere Module, > 30 cm Kabel 2,2 kΩ schnellere Flanken
400 kHz Fast Mode, längerer Bus 1,5–2,2 kΩ Untergrenze beachten
Sehr viele Module am Bus 1,0 kΩ nicht kleiner – sonst zu viel Strom

⚠️ Die Pull-Up-Falle bei fertigen Modulen: Fast jedes Breakout-Board – OLED, BMP280, MPU-6050, DS3231 – hat bereits eigene Pull-Ups von meist 4,7 kΩ oder 10 kΩ an Bord. Das ist bequem, solange du ein Modul nutzt. Hängst du vier Module parallel an denselben Bus, liegen deren Pull-Ups parallel. Vier mal 4,7 kΩ parallel ergeben rund 1,2 kΩ – damit fließt schon ordentlich Strom, und manche Chips schaffen es nicht mehr, die Leitung sauber auf Low zu ziehen. Symptom: Der Bus läuft mit zwei Modulen, mit vier bleibt das Display schwarz. Lösung: Auf allen bis auf einem Modul die Pull-Up-Widerstände entfernen oder deren Lötbrücke auftrennen. Auf vielen Boards ist dafür extra ein Jumper vorgesehen.

Für eigene Pull-Ups eignen sich die 4,7-kΩ- und 2,2-kΩ-Werte aus dem 600er Widerstands-Sortiment. Je ein Widerstand von SDA nach VCC und von SCL nach VCC – mehr braucht es nicht.

Was du brauchst

1x Arduino UNO R3 (ATmega328P, USB-C) – Zum Produkt
1x BMP280 Luftdruck- & Temperatursensor (I2C) – Zum Produkt
1x GY-521 MPU-6050 Gyroskop & Beschleunigungssensor – Zum Produkt
1x DS3231 Echtzeituhr (RTC) mit AT24C32 EEPROM – Zum Produkt
1x 0.96 Zoll OLED Display SSD1306 (I2C) – Zum Produkt
1x GY-530 VL53L0X Laser-Entfernungssensor (I2C) – Zum Produkt
Widerstands-Sortiment (für Pull-Ups: 2,2 kΩ / 4,7 kΩ) – Zum Produkt
1x Solderless Breadboard 400 Kontakte – Zum Produkt

Wo liegen die I2C-Pins?

Jedes Board hat seine eigenen. Diese Tabelle klebst du dir am besten an die Wand:

Board SDA SCL Logikpegel
Arduino UNO R3 A4 A5 5 V
Arduino Nano V3 A4 A5 5 V
Arduino UNO R4 Minima A4 / SDA A5 / SCL 5 V
ESP32 WROOM-32 GPIO 21 GPIO 22 3,3 V
ESP32-C3 Mini frei wählbar (Standard 8) frei wählbar (Standard 9) 3,3 V
Wemos D1 Mini (ESP8266) D2 (GPIO 4) D1 (GPIO 5) 3,3 V
RP2040-Zero GPIO 4 (Standard) GPIO 5 (Standard) 3,3 V

ℹ️ Vorteil ESP32: Auf ESP-Boards sind SDA und SCL nicht fest verdrahtet, sondern frei zuweisbar. Mit Wire.begin(sdaPin, sclPin); legst du sie auf beliebige GPIOs. Praktisch, wenn dir ein anderes Modul die Standardpins wegnimmt.

1Der I2C-Scanner – dein wichtigstes Werkzeug

Bevor du auch nur eine Zeile Sensorcode schreibst, läuft dieser Sketch. Er klopft jede Adresse von 1 bis 126 ab und meldet, wer antwortet. Findet der Scanner dein Modul nicht, ist jede Bibliothek zwecklos – dann ist die Verkabelung schuld.

#include <Wire.h>

void setup() {
  Wire.begin();
  Serial.begin(9600);
  while (!Serial) delay(10);
  Serial.println("\nI2C-Scanner gestartet");
}

void loop() {
  byte fehler;
  int gefunden = 0;

  Serial.println("Suche Geraete...");

  for (byte adresse = 1; adresse < 127; adresse++) {
    // Uebertragung starten und sofort beenden.
    // endTransmission() liefert 0, wenn jemand mit ACK geantwortet hat.
    Wire.beginTransmission(adresse);
    fehler = Wire.endTransmission();

    if (fehler == 0) {
      Serial.print("  Geraet gefunden bei 0x");
      if (adresse < 16) Serial.print("0");
      Serial.print(adresse, HEX);
      Serial.print("  (dezimal ");
      Serial.print(adresse);
      Serial.println(")");
      gefunden++;
    }
    else if (fehler == 4) {
      Serial.print("  Unbekannter Fehler bei 0x");
      if (adresse < 16) Serial.print("0");
      Serial.println(adresse, HEX);
    }
  }

  if (gefunden == 0) {
    Serial.println("Kein I2C-Geraet gefunden!");
    Serial.println("-> SDA/SCL vertauscht? GND verbunden? Pull-Ups da?");
  } else {
    Serial.print("Fertig. ");
    Serial.print(gefunden);
    Serial.println(" Geraet(e) am Bus.\n");
  }

  delay(5000);
}

Die Adressen, die dir ständig begegnen

Adresse Modul Alternative
0x3C OLED SSD1306 128x64 / 128x32 0x3D (Lötbrücke)
0x68 MPU-6050 (GY-521) 0x69 (AD0 auf High)
0x68 DS3231 RTC – Konflikt mit MPU-6050! nicht änderbar
0x57 AT24C32 EEPROM auf dem DS3231-Modul 0x50–0x57 (Jumper)
0x76 BMP280 / BME280 0x77 (SDO auf High)
0x29 VL53L0X ToF-Sensor per Software änderbar
0x27 LCD mit PCF8574-Backpack 0x20–0x27 (Lötbrücken)
0x70 TCA9548A Multiplexer 0x70–0x77

⚠️ Der Klassiker – 0x68 doppelt belegt: MPU-6050 und DS3231 nutzen beide ab Werk die Adresse 0x68. Hängst du beide an denselben Bus, antworten sie gleichzeitig, und du bekommst Datenmüll oder gar nichts. Glück im Unglück: Der MPU-6050 lässt sich umadressieren – leg seinen AD0-Pin auf VCC, und er hört fortan auf 0x69. Der DS3231 bleibt stur auf 0x68, seine Adresse ist im Chip festverdrahtet.

2Verkabelung: Vier Module an zwei Leitungen

Das Schöne am Bus: Alle Module hängen parallel an denselben zwei Leitungen. Auf dem Breadboard nutzt du dafür am besten zwei freie Reihen als SDA- und SCL-Schiene.

Modul VCC GND SDA SCL Adresse
OLED SSD1306 5V GND A4 A5 0x3C
BMP280 3V3 oder 5V* GND A4 A5 0x76
MPU-6050 (AD0 auf VCC) 5V GND A4 A5 0x69
DS3231 RTC 5V GND A4 A5 0x68
VL53L0X 3V3 oder 5V* GND A4 A5 0x29

* Diese Breakouts haben einen eigenen Spannungsregler und einen Pegelwandler an Bord und vertragen deshalb beides. Der nackte Chip darunter läuft nur mit 3,3 V – häng also nie einen blanken BMP280-Chip ohne Breakout an 5 V.

ℹ️ Gemeinsame Masse nicht vergessen: Wenn du Module aus einer separaten Stromquelle versorgst, müssen die Massen von Arduino und Netzteil verbunden sein. Ohne gemeinsamen GND hat der Bus keine Bezugsspannung, und du misst nur Zufall. Das ist der zweithäufigste Grund für „Scanner findet nichts“ – gleich nach vertauschtem SDA/SCL.

3Alle Sensoren gleichzeitig auslesen

Jetzt der Beweis, dass es funktioniert: BMP280, MPU-6050 und DS3231 gleichzeitig am Bus, Ausgabe auf dem OLED. Installiere vorher über den Bibliotheksverwalter Adafruit BMP280 Library, Adafruit MPU6050, RTClib, Adafruit SSD1306 und Adafruit GFX Library.

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Adafruit_BMP280.h>
#include <Adafruit_MPU6050.h>
#include <RTClib.h>

Adafruit_SSD1306 display(128, 64, &Wire, -1);
Adafruit_BMP280  bmp;
Adafruit_MPU6050 mpu;
RTC_DS3231       rtc;

bool bmpOk = false, mpuOk = false, rtcOk = false;

void setup() {
  Serial.begin(9600);
  Wire.begin();

  // --- OLED auf 0x3C ---
  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println("OLED (0x3C) nicht gefunden!");
  }
  display.clearDisplay();
  display.setTextColor(SSD1306_WHITE);
  display.setTextSize(1);

  // --- BMP280 auf 0x76 (manche Module: 0x77) ---
  bmpOk = bmp.begin(0x76);
  if (!bmpOk) bmpOk = bmp.begin(0x77);
  if (!bmpOk) Serial.println("BMP280 nicht gefunden (0x76/0x77)");

  // --- MPU-6050 auf 0x69, weil AD0 auf VCC liegt ---
  mpuOk = mpu.begin(0x69);
  if (!mpuOk) Serial.println("MPU-6050 nicht gefunden (0x69)");

  // --- DS3231 auf 0x68 ---
  rtcOk = rtc.begin();
  if (!rtcOk) Serial.println("DS3231 nicht gefunden (0x68)");

  // Uhrzeit beim ersten Start setzen
  if (rtcOk && rtc.lostPower()) {
    rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
    Serial.println("RTC neu gestellt.");
  }

  Serial.println("Setup fertig.");
}

void loop() {
  display.clearDisplay();
  display.setCursor(0, 0);

  // --- Uhrzeit ---
  if (rtcOk) {
    DateTime jetzt = rtc.now();
    display.printf("%02d:%02d:%02d  %02d.%02d.\n",
                   jetzt.hour(), jetzt.minute(), jetzt.second(),
                   jetzt.day(), jetzt.month());
  } else {
    display.println("RTC ?");
  }

  // --- Luftdruck & Temperatur ---
  if (bmpOk) {
    display.printf("T: %.1f C\n", bmp.readTemperature());
    display.printf("p: %.0f hPa\n", bmp.readPressure() / 100.0F);
  } else {
    display.println("BMP280 ?");
  }

  // --- Lage ---
  if (mpuOk) {
    sensors_event_t a, g, temp;
    mpu.getEvent(&a, &g, &temp);
    display.printf("aX:%5.1f aY:%5.1f\n", a.acceleration.x, a.acceleration.y);
    display.printf("aZ:%5.1f\n", a.acceleration.z);
  } else {
    display.println("MPU6050 ?");
  }

  display.display();
  delay(500);
}

Vier I2C-Geräte, vier Adressen, zwei Kabel. Genau dafür wurde der Bus erfunden. Beachte das Muster mit den bmpOk/mpuOk/rtcOk-Flags: So läuft der Sketch weiter, auch wenn ein Modul fehlt – statt in einer while(true)-Endlosschleife hängenzubleiben, siehst du auf dem Display ein Fragezeichen und weißt sofort, wer fehlt.

4Bus-Geschwindigkeit anpassen

Standardmäßig läuft I2C im Standard Mode mit 100 kHz. Das ist konservativ und funktioniert immer. Wenn du ein OLED mit 128x64 Pixeln 20-mal pro Sekunde neu zeichnen willst, wird es damit aber eng – dann lohnt der Fast Mode mit 400 kHz.

void setup() {
  Wire.begin();

  // Bustakt setzen - NACH Wire.begin()!
  Wire.setClock(400000);   // 400 kHz Fast Mode
  // Wire.setClock(100000); // 100 kHz Standard (Default)
  // Wire.setClock(10000);  // 10 kHz - fuer lange Kabel

  Serial.begin(9600);
}
Modus Takt Wann nutzen?
Low Speed 10 kHz lange Kabel, störige Umgebung
Standard Mode 100 kHz Default, läuft immer
Fast Mode 400 kHz Displays, häufige Sensorabfragen
Fast Mode Plus 1 MHz nur ESP32/RP2040, kurze Leitungen

⚠️ Der langsamste Teilnehmer gibt den Takt vor: Fast Mode funktioniert nur, wenn alle Geräte am Bus ihn beherrschen. Ein einziges altes Modul, das nur 100 kHz kann, lässt bei 400 kHz den ganzen Bus zusammenbrechen – nicht nur seine eigene Kommunikation. Wenn nach dem Umstellen auf 400 kHz plötzlich gar nichts mehr geht, ist das die erste Verdächtige.

5Adresskonflikte: Wenn Umadressieren nicht geht

Zwei MPU-6050 gleichzeitig? Kein Problem – AD0 des einen auf GND (0x68), des anderen auf VCC (0x69). Aber drei MPU-6050? Oder vier VL53L0X-Sensoren für einen Roboter? Ab hier gehen dir die Adressen aus. Dafür gibt es zwei Lösungen.

Lösung A: Software-Umadressierung (VL53L0X)

Manche Chips lassen sich per Software umadressieren – die neue Adresse gilt allerdings nur bis zum nächsten Stromausfall. Der Trick: Alle Sensoren zunächst über ihren XSHUT-Pin abschalten, dann einen nach dem anderen aufwecken und umadressieren.

#include <Wire.h>
#include <Adafruit_VL53L0X.h>

#define XSHUT_1  2
#define XSHUT_2  3
#define XSHUT_3  4

Adafruit_VL53L0X sensor1, sensor2, sensor3;

void setup() {
  Serial.begin(9600);

  // Alle Sensoren per XSHUT in den Reset ziehen
  pinMode(XSHUT_1, OUTPUT);
  pinMode(XSHUT_2, OUTPUT);
  pinMode(XSHUT_3, OUTPUT);
  digitalWrite(XSHUT_1, LOW);
  digitalWrite(XSHUT_2, LOW);
  digitalWrite(XSHUT_3, LOW);
  delay(10);

  // Sensor 1 aufwecken, auf 0x30 umziehen
  digitalWrite(XSHUT_1, HIGH);
  delay(10);
  if (!sensor1.begin(0x30)) Serial.println("Sensor 1 fehlt!");

  // Sensor 2 aufwecken, auf 0x31 umziehen
  digitalWrite(XSHUT_2, HIGH);
  delay(10);
  if (!sensor2.begin(0x31)) Serial.println("Sensor 2 fehlt!");

  // Sensor 3 bleibt auf der Standardadresse 0x29
  digitalWrite(XSHUT_3, HIGH);
  delay(10);
  if (!sensor3.begin(0x29)) Serial.println("Sensor 3 fehlt!");

  Serial.println("Drei ToF-Sensoren bereit.");
}

void loop() {
  VL53L0X_RangingMeasurementData_t m1, m2, m3;

  sensor1.rangingTest(&m1, false);
  sensor2.rangingTest(&m2, false);
  sensor3.rangingTest(&m3, false);

  Serial.print("Links: ");
  Serial.print(m1.RangeStatus != 4 ? m1.RangeMilliMeter : 0);
  Serial.print(" mm | Mitte: ");
  Serial.print(m2.RangeStatus != 4 ? m2.RangeMilliMeter : 0);
  Serial.print(" mm | Rechts: ");
  Serial.print(m3.RangeStatus != 4 ? m3.RangeMilliMeter : 0);
  Serial.println(" mm");

  delay(200);
}

Damit steuerst du drei VL53L0X-Sensoren gleichzeitig – links, Mitte, rechts. Perfekt für einen Roboter, der Hindernissen ausweichen soll. Mehr zum Sensor selbst steht im Artikel VL53L0X Laser-Entfernungssensor mit Arduino.

Lösung B: Der TCA9548A Multiplexer

Wenn sich ein Chip überhaupt nicht umadressieren lässt – acht identische Sensoren mit fester Adresse zum Beispiel – hilft ein I2C-Multiplexer. Der TCA9548A sitzt zwischen Arduino und Sensoren und schaltet acht getrennte Bus-Kanäle durch. Jeder Kanal darf dieselbe Adresse tragen, weil immer nur einer aktiv ist:

#include <Wire.h>

#define TCA_ADDR 0x70

// Kanal 0-7 am Multiplexer aktivieren
void tcaKanal(uint8_t kanal) {
  if (kanal > 7) return;
  Wire.beginTransmission(TCA_ADDR);
  Wire.write(1 << kanal);   // Bitmaske: genau ein Kanal an
  Wire.endTransmission();
}

void setup() {
  Wire.begin();
  Serial.begin(9600);
  while (!Serial) delay(10);

  // Jeden Kanal einzeln scannen
  for (uint8_t k = 0; k < 8; k++) {
    tcaKanal(k);
    Serial.print("--- Kanal ");
    Serial.print(k);
    Serial.println(" ---");

    for (uint8_t adr = 1; adr < 127; adr++) {
      if (adr == TCA_ADDR) continue;   // den Muxer selbst ueberspringen

      Wire.beginTransmission(adr);
      if (Wire.endTransmission() == 0) {
        Serial.print("  Geraet bei 0x");
        Serial.println(adr, HEX);
      }
    }
  }
  Serial.println("Scan fertig.");
}

void loop() {
  // Beispiel: drei identische Sensoren nacheinander auslesen
  for (uint8_t k = 0; k < 3; k++) {
    tcaKanal(k);
    // ... hier ganz normal den Sensor auslesen,
    //     als haenge nur er allein am Bus
    delay(100);
  }
}

💡 Der Denkfehler beim Multiplexer: Viele erwarten, dass der TCA9548A die Adressen übersetzt. Tut er nicht. Er ist schlicht ein elektronischer Umschalter – er verbindet den Hauptbus mit genau einem (oder mehreren) der acht Unterbüsse. Deshalb musst du vor jedem Sensorzugriff tcaKanal() aufrufen. Vergisst du das, redest du mit dem falschen Sensor.

Troubleshooting: Der I2C-Bus schweigt

⚠️ Scanner findet gar nichts: Arbeite diese Liste in dieser Reihenfolge ab. In 95 % der Fälle steckt einer dieser vier Punkte dahinter – (1) SDA und SCL vertauscht, (2) GND nicht verbunden, (3) Modul bekommt keine Versorgungsspannung, (4) Pull-Ups fehlen komplett. Miss mit dem Multimeter: Im Ruhezustand müssen sowohl SDA als auch SCL auf VCC-Niveau liegen. Liegt eine der beiden auf 0 V, hängt der Bus.

⚠️ Scanner findet 0x00 und alle Adressen: Klassisches Zeichen für einen Kurzschluss zwischen SDA und GND oder dafür, dass ein Modul die Leitung dauerhaft nach unten zieht. Zieh alle Module ab und steck sie einzeln wieder an – so findest du den Übeltäter in einer Minute.

⚠️ Es läuft mit zwei Modulen, mit vier nicht mehr: Zu viele parallele Pull-Ups (siehe oben). Rechne nach: Vier Module mit je 4,7 kΩ ergeben 1,2 kΩ Gesamtwiderstand. Entferne die Pull-Ups auf allen Modulen bis auf eines.

⚠️ Kabel länger als 50 cm, dann Aussetzer: Die Buskapazität steigt mit der Kabellänge, die Flanken werden flach. Gegenmaßnahmen in dieser Reihenfolge: Takt auf 50 oder 10 kHz senken, Pull-Ups auf 2,2 kΩ verkleinern, verdrilltes Kabel nehmen (SDA mit GND verdrillen, SCL mit GND). Über zwei Meter wird I2C prinzipiell unzuverlässig – dafür nimmst du besser CAN-Bus.

⚠️ Sketch hängt sich sporadisch auf: Der berüchtigte Bus-Lockup. Wenn der Master mitten in einer Übertragung resettet, kann ein Slave SDA dauerhaft auf Low halten und wartet ewig auf Taktimpulse. Abhilfe: Vor Wire.begin() neun manuelle Taktimpulse auf SCL ausgeben, um den Slave freizuschaufeln – oder pragmatisch: kurz den Strom aller Module trennen.

⚠️ 5-V-Board mit 3,3-V-Sensor: Ein Arduino UNO zieht SDA/SCL auf 5 V. Ein reiner 3,3-V-Chip ohne Pegelwandler nimmt das dauerhaft übel. Fertige Breakouts wie BMP280 oder MPU-6050 haben den Wandler an Bord und sind unkritisch – bei nackten Chips brauchst du einen Level-Shifter.

Praxistipps

  • Scanner vor jedem Projekt: Zehn Sekunden Scan sparen dir eine Stunde Bibliotheks-Debugging. Wenn der Scanner das Modul nicht sieht, findet es auch keine Library.
  • Adressen dokumentieren: Schreib die Adresse jedes Moduls als Kommentar in den Sketch. Beim nächsten Umbau in sechs Monaten dankst du dir das.
  • Ein Modul nach dem anderen: Bau den Bus schrittweise auf und lass nach jedem neuen Modul den Scanner laufen. Dann weißt du sofort, welches Modul den Bus zerlegt.
  • endTransmission()-Rückgabewert prüfen: 0 = alles gut, 2 = keine Antwort auf die Adresse, 3 = keine Antwort auf die Daten, 4 = sonstiger Fehler, 5 = Timeout. Diese Zahl sagt dir mehr als jede Vermutung.
  • Mehr Pins auf dem ESP32: Der ESP32 hat zwei komplett getrennte I2C-Controller. Mit TwoWire zweiterBus = TwoWire(1); und zweiterBus.begin(sda2, scl2); baust du dir einen zweiten Bus – damit löst du Adresskonflikte ganz ohne Multiplexer.
  • OLED sparsam neu zeichnen: Ein voller Bildschirm-Refresh schiebt 1024 Byte über den Bus. Bei 100 kHz dauert das rund 100 ms. Zeichne nur, was sich ändert, oder geh auf 400 kHz.

🚀 Deine I2C-Bauteile bei MAKEROO

Alle Module aus diesem Tutorial – alle sprechen I2C, alle passen an denselben Bus:

Fazit

I2C ist der unscheinbare Held fast jedes Maker-Projekts: zwei Kabel, ein Dutzend Module, und der Arduino behält trotzdem seine Pins für Wichtigeres. Wenn du drei Dinge aus diesem Artikel mitnimmst, dann diese: Erstens läuft der I2C-Scanner vor jedem Projekt – er beantwortet in zehn Sekunden die Frage, ob dein Problem in der Hardware oder im Code steckt. Zweitens sind Pull-Ups keine Nebensächlichkeit, sondern der Grund, warum ein Bus mit vier Modulen anders funktioniert als mit einem. Und drittens sind Adresskonflikte kein Weltuntergang: AD0-Pin umlegen, per Software umadressieren oder im Extremfall einen TCA9548A davorsetzen.

Wer diese Zusammenhänge einmal verstanden hat, sucht bei einem schwarzen OLED nicht mehr blind in der Bibliothek, sondern greift zum Scanner und zum Multimeter – und findet die Ursache meist in unter zwei Minuten. Genau das ist der Unterschied zwischen Nachbauen und Verstehen.

Zurück zum Blog