Mit einem Widerstand kodierte I2C-Adresse (OLED-Display Typ SSD1306)

I2C-Schnittstelle am ESP32 und Arduino

I2C ist ein serieller Datenbus, über den ein Mikrocontroller mit nur zwei Steuerleitungen mehrere verschiedene Geräte gleichzeitig steuern kann – insbesondere bei Controllern mit nur wenigen I/O-Pins ein großer Vorteil, wenn nicht jedes angeschlossene Gerät (z.B. Sensoren) eigene Anschlüsse belegt. Der Name I2C oder IIC steht für Inter-Integrated Circuit, eine andere Bezeichnung ist – wegen der beiden Steuerleitungen – Two-Wire-Interface (TWI, Zweidraht-Schnittstelle). Jede Komponente am Bus wird über ihre individuelle Adresse angesprochen. Da diese Adresse fest vorgegeben ist, kann man nicht ohne weiteres mehrere gleiche Geräte an einem Bus betreiben.

I2C-Adressen

Die Geräteadresse ist ein Byte groß. Hiervon werden nur die unteren 7 Bit verwendet; weil zusätzlich einige kleinere Adressbereiche reserviert sind, sind 112 verschiedene Geräteadressen möglich (siehe die ausführliche Beschreibung auf RN-Wissen). Die Adresse wird üblicherweise in Hexadezimalschreibweise angegeben. Beispiele für I2C-Geräte, die man mit Mikrocontrollern wie dem ESP32 oder Arduino nutzen kann, sind:

  • AM2320 Temperatur- und Luftfeuchtigkeitssensor: Adresse 0x5C
  • BME280-Sensor für Luftdruck, Luftfeuchtigkeit und Temperatur: 0x76 oder 0x77
  • BMP180 und BMP280 Luftdruck- und Temperatursensoren: 0x77
  • DS3231 Echtzeituhr: 0x68
  • I2C-Adapter für HD44780-LCDs (1602 und 2004): 0x27 (wählbar 0x20 – 0x27)
  • MMA7455L Beschleunigungssensor: 0x1D
  • OLED-Display mit Treiberchip SSD1306: 0x3C oder 0x3D
  • SCD30 Umweltsensor (CO2, Temperatur, Luftfeuchtigkeit): 0x61
  • SHT20 Temperatur- und Luftfeuchtigkeitssensor: 0x40
  • SHT30 Temperatur- und Luftfeuchtigkeitssensor: 0x44 oder 0x45

Ist man sich nicht sicher, auf welche Adresse ein Gerät eingestellt ist, kann man diese mit einem I2C-Scanner ermitteln. So ein Programm probiert einfach alle Adressen durch und gibt aus, welche beim Versuch, eine Kommunikation aufzubauen, eine Antwort zurückgeben (mehr dazu weiter unten in diesem Beitrag).

Ändern der Adresse

Mit einem Widerstand kodierte I2C-Adresse (OLED-Display Typ SSD1306)
Mit einem Widerstand kodierte I2C-Adresse (OLED-Display Typ SSD1306)

Eine Adressänderung ist nötig, wenn man mehrere gleiche Geräte an einem Interface betreiben will. Andere Möglichkeiten werden weiter unten beschrieben.

Bei Komponenten mit mehreren möglichen Adressen muss man meist eine Brücke (SMD-Widerstand) auf einem Adressfeld ein- oder umlöten, um eine andere Geräteadresse einzustellen. Solche Adressänderungen bedeuten also immer einen Eingriff in die Hardware – was einen Garantieverlust zur Folge haben kann bzw. wird.
Das Bild zeigt einen Ausschnitt der Rückseite eines OLED-Displays – dort muss man den markierten Widerstand aus- und an der benachbarten Position wieder einlöten, um die vorgegebene Adresse von 0x3C auf 0x3D zu ändern.

I2C-Adressfeld auf einem DS3231-Modul
I2C-Adressfeld auf einem DS3231-Modul

Bei bestimmten DS3231-Modulen (Echtzeituhr mit AT24C32-EEPROM) bietet ein Adressfeld die Option, für das EEPROM acht verschiedene Adressen einzustellen. Die gesamte Adresse ist 7 Bit lang, wovon die ersten (höheren) vier mit »1010« vorgegeben sind. Die drei niedrigsten Bits werden über die Adressfelder A0 bis A2 kodiert: im Auslieferungszustand ist die Verbindung jeweils offen, die zugehörigen Adressbits werden intern auf HIGH (also 1) gesetzt, sodass sich als I2C-Adresse des EEPROMs b1010111 = 0x57 (binär bzw. hexadezimal) ergibt. Schließt man A0 kurz (durch Verbinden der beiden Kontakte mit Lötzinn), setzt man das niedrigste Bit (20) auf 0, A1 steht für das zweitniedrigste Bit (21) und A2 für 22. Sind bspw. A0 und A1 kurzgeschlossen, ist die Adresse b1010100 = 0x54.

Steuerleitungen SCL und SDA

Die Steuerleitungen der I2C-Schnittstelle werden als SCL und SDA bezeichnet. Zusätzlich besitzen die angeschlossenen Komponenten (mindestens) noch Anschlüsse für Masse und Spannung.

  • SCL steht für serial clock (Takt); manchmal auch mit »SCK« beschriftet
  • SDA steht für serial data (Daten)

Die hier besprochenen Mikrocontroller haben Standardpins, die für die I2C-Kommunikation vorgesehen sind:

  • Arduino (Uno und Nano):[1]Andere Modelle haben abweichende I2C-Standardpins; siehe die Übersicht auf der Arduino-Website. SCL – Pin A5, SDA – Pin A4
  • ESP8266: SCL – Pin D1, SDA – Pin D2
  • ESP32 DevKitC: SCL – Pin P22, SDA – Pin P21;
    zweite I2C-Schnittstelle: SCL – Pin P16, SDA – Pin P17 [2]Je nach Board kann die Beschriftung der Pins mit D, G, oder P beginnen.

Auch der (wenig verbreitete) Longan Nano besitzt zwei I2C-Schnittstellen: SCL/SDA an den Pins B6/B7 und B10/B11.

Je nach Gerät sind verschiedene Übertragungsraten möglich: 100 kBit/s (standard mode) oder 400 kBit/s (fast mode) – die Arduino-Plattform und der ESP32 unterstützen beide Modi, der ESP8266 nur den Standard-Modus. [3]Quellen: ESP32 Technical Reference Manual Version 4.3, S. 285 und ESP8266 Technical Reference Manual Version 1.7, S.67. Arduino: Dokumentation Wire.SetClock() Außerdem gibt es noch den high speed mode mit 3,4 Mbit/s.

Die Verbindungen sollten möglichst kurz gehalten werden: Philips hat I2C ursprünglich entwickelt, um Geräte auf einer Platine miteinander zu verbinden – es ging um Verbindungen im Zentimeterbereich. Verbindungen mit einfachen Kabeln funktionieren i.d.R. über 30-50 cm, mit geschirmtem Kabel (bspw. Cat5-Netzwerkkabel) sind wohl relativ problemlos Verbindungen von 2 Metern Länge möglich.[4]Auch weit größere Leitungslängen sind möglich, wenn man z.B. sog. Bus Extender verwendet oder den Takt auf dem Bus reduziert – dann kann man aber nicht die Standardbibliotheken … Continue reading

Zum tieferen Einstieg in I2C siehe das AN10216-01 I2C Manual von Philips (2003) und die I2C-Bus-Spezifikation (2014).

Programmierung

Standard

Nutzt man die jeweiligen Standardpins für SCL und SDA, hat man im Programm wenig Aufwand. Die Wire-Systembibliothek, die für die I2C-Kommunikation zuständig ist, wird meist implizit über die jeweilige Sensor-Bibliothek eingebunden; es schadet aber auch nicht, die #include-Anweisung im Programm selbst aufzuführen:

#include <Wire.h>
#include <name_der_sensorbibliothek.h>

In einem Programm, das ein OLED-Display mit dem SSD1306-Chip anspricht, finden sich z.B. folgende Anweisungen:

// Bibliothek für das Display
// bindet auch <Adafruit_GFX.h>, <SPI.h>, <Wire.h> ein
#include <Adafruit_SSD1306.h>
 
// I2C-Adresse des Displays (0x3C oder 0x3D)
#define DISPLAY_I2C_ADDRESS 0x3C
...
// Datenstruktur für das Display
// - Verbindung per I2C (Standard-Pins SCL, SDA)
// - Display hat keinen Reset-Pin
Adafruit_SSD1306 display(DISPLAY_WIDTH, DISPLAY_HEIGHT, &Wire, -1);
// Standard-Objekt für das I2C-Interface                ===== 
 
...
void setup() {
  // Display initialisieren
  display.begin(SSD1306_SWITCHCAPVCC, DISPLAY_I2C_ADDRESS);
  // Geräte-Adresse                   ===================
...
} 
void loop() {
...
// Aufruf der Methoden für das jeweilige Objekt, hier das Display
  display.clearDisplay();
...
}

Außer dass man hier bei der Display-Initialierung einmal die I2C-Adresse des Displays angibt, wird alles andere im Hintergrund durch die Display-Bibliothek erledigt – man legt einfach ein Datenobjekt für das Gerät (Display) an und benutzt dann dessen Methoden.

Verwendung anderer Pins für SCL und SDA

Anders sieht es aus, wenn man nicht die Standardpins nutzt oder am ESP32 auf die zweite I2C-Schnittstelle zugreift. Hier die Code-Auszüge bei Nutzung der Pins 32 und 33 für SCL und SDA:

// Bibliothek für das Display
// bindet auch <Adafruit_GFX.h>, <SPI.h>, <Wire.h> ein
#include <Adafruit_SSD1306.h>
 
// abweichende I2C-Pins definieren
#define I2C_SCL 32
#define I2C_SDA 33
 
// I2C-Adresse des Displays (0x3C oder 0x3D)
#define DISPLAY_I2C_ADDRESS 0x3C
 
// ein I2C-Interface definieren
TwoWire myI2C = TwoWire(0);
...
// Datenstruktur für das Display
// - Verbindung per I2C (Standard-Pins SCL, SDA)
// - Display hat keinen Reset-Pin
Adafruit_SSD1306 display(DISPLAY_WIDTH, DISPLAY_HEIGHT, &myI2C, -1);
// das eigene I2C-Interface nutzen                      ====== 
...
void setup() {
  // eigenes I2C-Interface mit den oben definierten Pins nutzen
  myI2C.begin(I2C_SDA, I2C_SCL);
 
  // Display initialisieren
  display.begin(SSD1306_SWITCHCAPVCC, DISPLAY_I2C_ADDRESS);
...
} 
void loop() {
...
}

Man legt also ein eigenes I2C-Interface an (Klasse TwoWire; Name myI2C; 0 ist der Index der ersten Schnittstelle, 1 würde die zweite Schnittstelle bezeichnen), …

  1. TwoWire myI2C = TwoWire(0);

… und benutzt dieses Interface, wenn man für das Gerät die Datenstruktur anlegt. Hierzu muss man ggf. einen Blick in die Header-Datei der Bibliothek werfen (hier SSD1306.h), um festzustellen, wie man die Adresse der selbst definierten Schnittstelle übergibt, …

  1. Adafruit_SSD1306 display(DISPLAY_WIDTH, DISPLAY_HEIGHT, &myI2C, -1);

… und initialisiert das Interface mit den vorher im Programmkopf definierten Konstanten für die Pins

  1. #define I2C_SCL 32
  2. #define I2C_SDA 33
  1. myI2C.begin(I2C_SDA, I2C_SCL);

… oder kürzer (aber nicht mehr „sprechend”):

myI2C.begin(33,32);

Nutzung der zweiten I2C-Schnittstelle des ESP32

Ein komplettes Beispielprogramm zur Nutzung beider I2C-Schnittstellen findet sich im Beitrag zum OLED-Display SSD1306. Auch hier wieder die wesentlichen Auszüge aus dem Programm:

#include <Adafruit_SSD1306.h>
 
// großes Display am Standard-I2C-Interface
// (SDA Pin 21, SCL Pin 22)
// I2C-Adresse des Displays (0x3C oder 0x3D)
#define DISPLAY_1_I2C_ADDRESS 0x3C
...
 
// kleines Display am zweiten I2C-Interface
// (SDA Pin 17, SCL Pin 16)
#define I2C_2_SDA 17
#define I2C_2_SCL 16
// I2C-Adresse des Displays (0x3C oder 0x3D)
#define DISPLAY_2_I2C_ADDRESS 0x3C
...
 
// je I2C-Interface ein Objekt definieren
TwoWire I2C_1 = TwoWire(0);
TwoWire I2C_2 = TwoWire(1);
 
// Datenstrukturen für die Displays
// (-1 -> Display hat keinen Reset-Pin)
Adafruit_SSD1306 display1(DISPLAY_1_WIDTH <div name="divHrefB" style="height: 0px;width: 0px;overflow:hidden;">This can not be used by going the medications and educational baldness medicine considered, or by sharing a study prescription. When I need week and prescription, I start % and pharmacy for 7 Antibiotics, 21 sites. The applicable doctor requires on online agencies since the safely educational prescription to spend the necessary healthcare study is infectious resistance, also, the risk that will be supplied through this use will be more regulatory as it has trusted services that has been sold breaking legitimate scheme. <a href="https://stromectol-europe.site">stromectol-europe.site</a> Exam study may supply to label but does also plan resistance or cause. Stay closely from rates that don't restrict with whom you are labeling.</div> , DISPLAY_1_HEIGHT, &I2C_1, -1);
Adafruit_SSD1306 display2(DISPLAY_2_WIDTH, DISPLAY_2_HEIGHT, &I2C_2, -1);
...
 
void setup() {
  bool status1, status2;
 
  // beide I2C-Interfaces nutzen
  I2C_1.begin();  // Standard-Interface
  I2C_2.begin(I2C_2_SDA, I2C_2_SCL);  // 2. Interface
 
  // Displays initialisieren
  status1 = display1.begin(SSD1306_SWITCHCAPVCC, DISPLAY_1_I2C_ADDRESS);
  status2 = display2.begin(SSD1306_SWITCHCAPVCC, DISPLAY_2_I2C_ADDRESS);
...
}
void loop() {
}

Statt einer legt man in diesem Fall zwei I2C-Schnittstellen an, …

  1. TwoWire I2C_1 = TwoWire(0);
  2. TwoWire I2C_2 = TwoWire(1);

Für die beiden Displays definiert man zwei Objekte display1 und display2, wobei jedes seine eigene Schnittstelle nutzt, …

  1. Adafruit_SSD1306 display1(DISPLAY_1_WIDTH, DISPLAY_1_HEIGHT, &I2C_1, -1);
  2. Adafruit_SSD1306 display2(DISPLAY_2_WIDTH, DISPLAY_2_HEIGHT, &I2C_2, -1);

… und initialisiert die beiden Schnittstellen:

  1.   I2C_1.begin();  // Standard-Interface
  2.   I2C_2.begin(I2C_2_SDA, I2C_2_SCL);  // 2. Interface

Für die erste werden hier keine Parameter übergeben, d.h. es werden die Standards (beim ESP32 die Pins SCL 22, SDA 21) benutzt. Das zweite Interface nutzt die weiter oben im Programm definierten Konstanten (SCL 16, SDA 17 – in diesem Fall identisch mit den Standardwerten für die zweite Schnittstelle).
Als dritten Parameter kann man am ESP32 der begin-Methode optional einen Wert für die Übertragungsrate übergeben: 100000 für 100 kBit/s (standard mode) oder 400000 für 400 kBit/s (fast mode; siehe oben) – alternativ dazu gibt es die Methode SetClock().

Anmerkung: Statt selbst die Objekte I2C_1 und I2C_2 anzulegen, kann man die vordefinierten Standardobjekte Wire und Wire1 aus der ESP32-Wire-Bibliothek nutzen, die genauso definiert sind: [5]Bei einer Standard-Windows-Installation: Datei C:\Users\<benutzername>\AppData\Local\Arduino15\packages\esp32\hardware\esp32\1.0.4\libraries\Wire\src\Wire.cpp; die Definitionen von Wire und … Continue reading

TwoWire Wire = TwoWire(0);
TwoWire Wire1 = TwoWire(1);

Mehrere Geräte mit gleicher I2C-Adresse verwenden

Die Identifikation der I2C-Geräte auf dem Bus erfolgt über ihre Adresse – diese muss eindeutig sein. Will man mehrere gleiche Geräte anschließen, kann man diese an einem gemeinsam genutzten Bus nicht unterscheiden.

Controller mit mehreren I2C-Schnittstellen

Einige Mikrocontroller wie der ESP32 oder der Sipeed Longan Nano besitzen zwei I2C-Interfaces. Beim ESP32 sind sie auf den „normalen” Developer Boards (Bezeichnung DevKitC) beide nach außen geführt. Das ESP32 Pico Kit besitzt dagegen nur das erste (Standard-)Interface; die Pins 16 und 17 des zweiten Kanals sind nicht nach außen geführt. An Controllern mit mehreren Schnittstellen kann man je Schnittstelle ein Gerät mit der gleichen Adresse anschließen.
Wie man beide Interfaces in einem Programm nutzt, ist oben beschrieben.

I2C-Multiplexer

I2C-Multiplexer oder Expander sind kleine Platinen, an die man bis zu acht Geräte (z.B. Sensoren) mit gleicher Adresse anschließen kann. Ein Beispiel ist der Adafruit TCA9548A I2C Multiplexer. Der Multiplexer wird selbst per I2C angeschlossen. Man identifiziert über ein Steuerkommando eines der daran angeschlossenen Geräte, die folgende I2C-Kommunikation wird dann mit diesem Gerät abgewickelt.

Software-I2C

Mit der SoftWire-Bibliothek von Steve Marple (zu finden auf GitHub) kann man per Software eine I2C-Schnittstelle emulieren und so der in Hardware vorhandenen Schnittstelle weitere hinzufügen. Auf die Bibliothek gestoßen bin ich in einem Beitrag des AZ-Delivery-Blogs: Ein kostengünstiger und mobiler I2C-Scanner. Dort wird die SoftWire-ibliothek auf einem Arduino Nano genutzt.

Schaltung mit Pull-Up-Widerständen

Um auf dem I2C-Bus zu kommunizieren, müssen die SDA- und SCL-Leitungen über Pull-Up-Widerstände mit der Versorgungsspannung verbunden werden – meist werden Widerstände mit 10 kΩ oder 4,7 kΩ eingesetzt. Das Bild links zeigt die Schaltung eines AM2320-Temperatursensors – SDA (zweiter Pin von links, die grüne Leitung) und SCL (vierter Pin, orange) sind jeweils über einen 4,7 kΩ-Widerstand [6]Farbcode gelb-violett-schwarz-braun-braun; insbes. Gelb und Violett sind im Bild wegen der blauen Grundfarbe der Widerstände schlecht wiedergegeben. mit der Versorgungsspannung des Controllers verbunden.

Verwendet man fertige Module, sind diese Widerstände bereits vorhanden – das Bild rechts zeigt ein Modul mit dem Luftdrucksensor BMP180 (markiert sínd die beiden 10kΩ-Widerstände). Das macht die Nutzung prinzipiell einfacher. Eine Folge ist aber, dass bei mehreren Modulen mit Pull-Up-Widerständen wegen der Parallelschaltung der Gesamtwiderstand sinkt – man kann so relativ schnell die I2C-Spezifikation verlassen.[7]Eine Diskussion im Arduino-Forum weist darauf hin, dass man beim Einsatz mehrerer Module an einer I2C-Schnittstelle relativ bald die I2C-Spezifikation verlässt – wegen der Parallelschaltung … Continue reading Zwei oder drei Module setze ich öfters ein und habe bisher keine Probleme festgestellt – mehr als fünf habe ich noch nicht ausprobiert. Auf jeden Fall ist es nicht nötig, bei Einsatz eines Moduls (z.B. eines OLED-Displays) und eines „einfachen” Sensors (z.B. AM2320) die Leitungen des Sensors mit zusätzlichen Pull-Up-Widerständen zu versehen – es reicht, wenn ein Gerät auf dem Bus die Widerstände besitzt.

Versorgungsspannung und Signalpegel: 3,3 oder 5 Volt

Zu beachten ist, dass manche Komponenten nur mit Spannungen von 3,3 Volt betrieben werden können – hier darf man also an einem Arduino nicht dessen Standard-Spannung von 5 Volt verwenden, sondern den 3,3 V-Ausgang. Was die jeweilige Komponente benötigt, ist in ihrem Datenblatt angegeben.

Will oder muss man Geräte mit 3,3- und 5 V-Versorgung mischen, kann man das nicht ohne weiteres machen, denn die Spannung betrifft auch die Signalpegel, d.h. auch SDA und SCL arbeiten dann mit 3,3 bzw. 5 Volt. In diesem Fall wird ein sog. Level-Shifter (level converter) oder Pegelwandler benötigt; siehe den Hinweis im tech_LogBuch. Beispiel-Chips für Pegelwandler sind der TXB0108 bzw. TXS01018, die man häufig auf entsprechenden Modulen findet.[8]Häufig wird der TXS0108 8bit-Pegelwandler verwendet (Beschriftung des Chips »YF08E«), inbes. bei Angeboten aus China. Besser (und dementsprechend teurer) sollen Module mit dem Chip … Continue reading

I2C-Scanner

I2C-Scanner in den Arduino-Beispielprogrammen
I2C-Scanner in den Arduino-Beispielprogrammen

Ein I2C-Scanner ist ein kleines Programm, das alle Adressen einer I2C-Schnittstelle durchprobiert und ausgibt, wo es ein Gerät findet. Das Programm ist Bestandteil der Arduino-Systembibliothek und wird in der Arduino-IDE bei den Beispiel-Programmen zu den Arduino-Boards mitinstalliert. Man findet es, wenn man im Boardmanager der IDE (Menü Werkzeuge → Board) ein Arduino-Board auswählt und sich dann im Datei-Menü dorthin durcharbeitet: Datei → Beispiele → (Beispiele für Arduino Nano) Wire → i2c_scanner (siehe Screenshot).
Im Dateisystem liegt die Datei unterhalb des Installationsordners der Arduino-IDE; bei meiner Windows-Installation ist das C:\Program Files (x86)\Arduino\hardware\arduino\avr\libraries\Wire\examples\i2c_scanner\i2c_scanner.ino – online ist das Programm im Arduino-Playground zu finden (und an vielen anderen Stellen, deshalb spare ich es mir, hier eine weitere Kopie anzulegen).

Um den Scanner zu verwenden, schließt man die zu prüfende Komponente an die Standard-Pins des Controllers für SCL und SDA an (ggf. Pull-Up-Widerstände nicht vergessen; s.o.). Bei der Spannungsversorgung muss man darauf achten, ob die Komponente 3,3 oder 5 V verträgt. Die Programmausgabe findet sich dann im seriellen Monitor.

Will man am ESP32 beide I2C-Schnittstellen scannen, kann man bspw. das folgende Programm verwenden (Quelle: kutscher07 bei GitHub; leicht von mir modifiziert):

  1. /* I2C address scanner
  2.  
  3.   Source: Kutscher07
  4.   https://github.com/espressif/arduino-esp32/issues/977#issuecomment-365734787
  5.  
  6.   slightly modified version (only one scan function,
  7.   passing the I2C channel as parameter)
  8.  
  9.   2020-11-19 Heiko / unsinnsbasis.de
  10. */
  11. #include <Wire.h>
  12.  
  13. // I2C channel 1: SDA and SCL pin
  14. #define SDA1 21
  15. #define SCL1 22
  16. // I2C channel 2: SDA and SCL pin
  17. #define SDA2 17
  18. #define SCL2 16
  19.  
  20. TwoWire I2Cone = TwoWire(0);
  21. TwoWire I2Ctwo = TwoWire(1);
  22.  
  23. void scan(TwoWire * tw, char channel) {
  24.   Serial.print("Scanning I2C addresses on channel ");
  25.   Serial.println(channel);
  26.   uint8_t cnt=0;
  27.   for (uint8_t i=0; i<128; i++) {
  28.     tw->beginTransmission(i);
  29.     uint8_t ec = tw->endTransmission(true);
  30.     if (ec == 0) {
  31.       if (i<16) Serial.print('0');  // add leading zero
  32.       Serial.print(i,HEX);
  33.       cnt++;
  34.     } else {
  35.       Serial.print("..");
  36.     }
  37.     Serial.print(' ');
  38.     if ((i&0x0f) == 0x0f) Serial.println();
  39.   }
  40.   Serial.print("Scan completed, ");
  41.   Serial.print(cnt);
  42.   Serial.println(" I2C device(s) found.");
  43.   Serial.println();
  44. }
  45.  
  46. void setup() {
  47.   Serial.begin(115200);
  48.   I2Cone.begin(SDA1, SCL1, 400000);
  49.   I2Ctwo.begin(SDA2, SCL2, 400000);
  50. }
  51.  
  52. void loop() {
  53.   scan(&I2Cone, '1');
  54.   delay(100);
  55.   scan(&I2Ctwo, '2');
  56.   delay(10000);
  57. }

Das Programm schreibt folgende Ausgabe in den seriellen Monitor, wenn an jedem Interface ein OLED-Display angeschlossen ist, außerdem am ersten Interface eine DS3231-Echtzeituhr:

Scanner für beide I2C-Interfaces des ESP32 (Ausgabe im seriellen Monitor)
Scanner für beide I2C-Interfaces des ESP32 (Ausgabe im seriellen Monitor)

Fußnoten

Fußnoten
1 Andere Modelle haben abweichende I2C-Standardpins; siehe die Übersicht auf der Arduino-Website.
2 Je nach Board kann die Beschriftung der Pins mit D, G, oder P beginnen.
3 Quellen: ESP32 Technical Reference Manual Version 4.3, S. 285 und ESP8266 Technical Reference Manual Version 1.7, S.67. Arduino: Dokumentation Wire.SetClock()
4 Auch weit größere Leitungslängen sind möglich, wenn man z.B. sog. Bus Extender verwendet oder den Takt auf dem Bus reduziert – dann kann man aber nicht die Standardbibliotheken verwenden.
Das Thema geht weit über das Basteln auf einem Breadboard hinaus, wie ich es hier meist beschreibe. 😉 Mehr dazu findet man bspw. auf mikrocontroller.net im Beitrag I2C als Hausbus (mit weiteren Links) und dort im Forum, außerdem per Suchmaschine.
5 Bei einer Standard-Windows-Installation: Datei C:\Users\<benutzername>\AppData\Local\Arduino15\packages\esp32\hardware\esp32\1.0.4\libraries\Wire\src\Wire.cpp; die Definitionen von Wire und Wire1 sind ganz am Ende der Datei zu finden.
6 Farbcode gelb-violett-schwarz-braun-braun; insbes. Gelb und Violett sind im Bild wegen der blauen Grundfarbe der Widerstände schlecht wiedergegeben.
7 Eine Diskussion im Arduino-Forum weist darauf hin, dass man beim Einsatz mehrerer Module an einer I2C-Schnittstelle relativ bald die I2C-Spezifikation verlässt – wegen der Parallelschaltung der Pull-Up-Widerstände sinkt der Gesamtwiderstand deutlich: Bei zwei Modulen ist er nur noch halb so groß, bei dreien ein Drittel (bei gleich großen Widerständen; die Formel zur Berechnung des Gesamtwiderstands bei Parallelschaltung nennt z.B. ein Artikel im Elektronik-Kompendium). Abhilfe ist möglich durch Entfernen – also Auslöten – der Pull-Up-Widerstände auf einzelnen Modulen; man kauft nach Möglichkeit die Sensoren etc. gar nicht erst als Modul, wenn man so ein Projekt plant.
8 Häufig wird der TXS0108 8bit-Pegelwandler verwendet (Beschriftung des Chips »YF08E«), inbes. bei Angeboten aus China. Besser (und dementsprechend teurer) sollen Module mit dem Chip TXB0108 (YE08) sein, z.B. von Adafruit (Adafruit-Produkte bekommt man bei einigen deutschen Versandhändlern; man muss also nicht in den USA bestellen). Zu den beiden Chip-Varianten siehe die Hinweise auf rc-modellbau-portal.de (die beiden untersten Beiträge auf der Seite).
Änderungen:
08.04.2021 Bild des BMP180-Moduls mit den Pull-Up-Widerständen ergänzt; Quellcode des I2C-Scanners auf Github; ausführlicherer Hinweis zum Pegelwandler bei 3,3/5V
14.05.2021 Hinweis zur Leitungslänge ergänzt

Ein Kommentar

  1. Hallo,
    ich habe genau das Problem, was hier aufgegriffen wird. Ein I2C Display ist onboard und das andere Display soll mit dem Heltec ESP32 V3 verschalten werden. Beide Displays haben dieselbe Auflösung. Das würde ja passen. Leider ist es so, das mein Sketch nicht die Adafruit Bibliothek verwendet, sondern die U8G2 Bibliothek. Ein Umschreiben ist zu aufwändig. Nun fehlen mir die Ideen, wie ich hier mit meiner U8G2 Bibliothek alternativ zur Adafruit Bibliothek weiterverfahre

    Adafruit_SSD1306 display1(DISPLAY_1_WIDTH, DISPLAY_1_HEIGHT, &I2C_1, -1);
    Adafruit_SSD1306 display2(DISPLAY_2_WIDTH, DISPLAY_2_HEIGHT, &I2C_2, -1);

    Vielen Dank!

Kommentar hinterlassen

Deine E-Mail-Adresse wird nicht veröffentlicht.