I2C Verbindung zwischen einem Arduino Leonardo und einem WeMos D1 R1

Hallo zusammen,
Ich plane die Umsetzung eines Projekts, bei dem ein Master, das SparkFun Redboard Qwiic, Signale von den Slaves empfängt und diese seriell an den Computer weiterleitet.
Die WeMos D1 R1 als Slaves sollen verschiedene Inputelemente wie Taster, Potis oder Encoder haben und daraufhin eindeutige Signale an den Master senden.

Um diese Struktur aufzubauen möchte ich vorher in kleinen Schritten mich mit I2C und dem Master- Slavesystem vertraut machen. Aus diesem Grund versuche ich meinen Arduino Leonardo mit folgenden Bsp-Code:

#include <Wire.h>

void setup() {
 Wire.begin();        // join i2c bus (address optional for master)
  Serial.begin(9600);  // start serial for output
}

void loop() {
  Wire.requestFrom(8, 6);    // request 6 bytes from slave device #8

  while (Wire.available()) { // slave may send less than requested
    char c = Wire.read(); // receive a byte as character
    Serial.println(c);         // print the character
  }

  delay(500);
}

und den WeMos mit dem Code:

#include <Wire.h>

void setup() {
 Wire.begin(8);                // join i2c bus with address #8
  Wire.onRequest(requestEvent); // register event
}

void loop() {
  delay(100);
}

// function that executes whenever data is requested by master
// this function is registered as an event, see setup()
void requestEvent() {

  Wire.write("hello "); // respond with message of 6 bytes
  // as expected by master
}

zu bespielen.
Das klappt auch alles so weit, nur das ich dann auf dem Seriel Monitor kein "hello" bekomme.
Nun habe ich die Vermutung, dass mein WeMos nicht die Ardesse 8 hat.
Aus diesem Grund habe ich den folgenden Sketch drauf gespielt um die Adresse herauszufinden:

# include "Wire.h"

void setup()
{
 Wire.begin();
  Serial.begin(9600);
  delay(500);
  Serial.print("I2C Scanner");
}

void loop()
{
  byte Fehler, Adresse;
  int Geraete = 0;
  Serial.println("Starte Scanvorgang");

  for (Adresse = 1; Adresse < 127; Adresse++ )
  {
    // Übertragung starten
    Wire.beginTransmission(Adresse);

    // wenn die Übertragung beendet wird
    Fehler = Wire.endTransmission();

    if (Fehler == 0)
    {
      Serial.print("I2C Gerät gefunden - Adresse: 0x");
      if (Adresse < 16) Serial.print("0");
      Serial.print(Adresse, HEX);
      Serial.println("");
      Geraete++;
    }
  }
  if (Geraete == 0) Serial.println("Keine I2C Geräte gefunden\n");
  else Serial.println("Scanvorgang abgeschlossen");

  delay(10000);
}

nun bekomme ich folgende Antwort:
"Starte Scanvorgang
Keine I2C Geräte gefunden"

Kann mir irgendjemand weiterhelfen?

Hallo,

vorweg, hast du Levelshifter im I2C Bus? Wegen 5V vs. 3,3V.

Ich habe 2 ältere Bsp. gefunden.
Die Bsp. in der IDE der "Wire" Lib kannste dir auch nochmal anschauen.

MASTER
/*
  Doc_Arduino - german Arduino Forum
  IDE 1.8.15
  Arduino Mega2560
  08.08.2021
  >>> I2C MASTER <<<
*/

#include <Streaming.h>  // https://github.com/janelia-arduino/Streaming
#include <Wire.h>

const byte SLAVE_ADDR {33};

// definiere Nachricht
union Nachricht
{
  struct
  {
    char cmd[2];
    byte value;
  };
  byte asArray[sizeof(cmd)+sizeof(value)];  // Array[Summe aller struct Datentypen], für Zugriff über Index
} sendDaten;                                // union Buffer anlegen

const uint8_t sizeMessage = sizeof(Nachricht);

struct Datensatz
{
  char cmd[2];
  byte value;
};

const Datensatz befehle [] =
{
  {"A", 0},
  {"B", 55},
  {"C", 180},
  {"D", 125},
};

void setup (void)
{
  Wire.begin();
  Serial.begin(9600);
  Serial.println(F("\nuC Reset #### I2C MASTER ####\n"));
  Serial << (F("Nachrichtenlänge: ")) << sizeMessage << endl;
}

void loop (void)
{   
    for (const Datensatz &d : befehle)
    { 
      // lesen und zuweisen
      strlcpy(sendDaten.cmd, d.cmd, sizeof(sendDaten.cmd) );
      sendDaten.value = d.value;

      // Debugausgabe
      Serial << (F("sizeMessage: ")) << sizeMessage << '\t' 
             << (F("cmd: "))   << sendDaten.cmd << '\t' 
             << (F("value: ")) << sendDaten.value << endl;      

      // senden       
      const byte errorCode = sendToSlaveI2C(SLAVE_ADDR, sendDaten.asArray, sizeMessage);
      errorAnalyseI2C(Serial, errorCode);
  
      delay(2000);
    }
}

// ----- Funktionen ------------------------------------------------------

void errorAnalyseI2C(Stream& stream, const byte error)
{
  switch (error)
  {
    case 0:  stream.println(F("no error"));                                 break;
    case 1:  stream.println(F("data too long to fit in transmit buffer"));  break;
    case 2:  stream.println(F("received NACK on transmit of address"));     break;
    case 3:  stream.println(F("received NACK on transmit of data"));        break;
    case 4:  stream.println(F("other error"));                              break;
    default: stream.println(F("default - unknown error number"));           break;
  }
}

byte sendToSlaveI2C (const byte addr, const byte *buffer, const byte zeichenAnzahl)
{
  byte errorCode {0};                   // Fehlerstatus setzen

  // Serial.print("Zeichenanzahl: "); Serial.println(zeichenAnzahl);

  // sendet nicht den kompletten Buffer, nur die Zeichen bis '\0'
  Wire.beginTransmission(addr);
  for (byte i = 0; i < zeichenAnzahl; i++)
  {
    Wire.write(buffer[i]);
  }  
  Wire.write('\n');     // Endeerkennung für Empfänger

  errorCode = Wire.endTransmission();    // Byte(s) schreiben & Error Code merken
  return errorCode;
}

/*
void showSerialData (Stream& stream, char *const buffer)
{
   stream.print("eingelesen: "); stream.println(buffer);
}

bool readSerial (Stream &stream, char *const buffer, const byte bufferSize)     // Einleseroutine
{
  static byte index {0};
  bool finish {false};

  if (stream.available() > 0) {
    char c = stream.read();
    if (c >= 32 && index < bufferSize) {
      buffer[index++] = c;
    }
    else if (c == '\n') {    // Endeerkennung 'LineFeed'
      buffer[index] = '\0';
      index = 0;
      finish = true;
    }
  }
  return finish;
}
*/
SLAVE
/*
  Doc_Arduino - german Arduino Forum
  IDE 1.8.15
  Arduino Mega2560
  08.08.2021
  >>> I2C SLAVE <<<
*/
#include <Streaming.h>  // https://github.com/janelia-arduino/Streaming
#include <Wire.h>

const byte SLAVE_ADDR {33};

// definiere Nachricht
union Nachricht
{
  struct
  {
    char cmd[2];  // +1 für Null Terminator
    byte value;
  };
  byte asArray[sizeof(cmd)+sizeof(value)];  // Summe aller struct Datentypen, für Zugriff über Index
} empfDaten, sendDaten;                     // zwei gleiche union Buffer anlegen

const uint8_t sizeMessage = sizeof(Nachricht);
bool endeEmpfang {false};

void setup (void)
{
  Wire.begin(SLAVE_ADDR);
  Wire.onReceive(receiveEventI2C);
  Serial.begin(9600);
  Serial.println("\nuC Reset #### I2C SLAVE ####\n");
}

void loop (void)
{
  if (endeEmpfang)
  {
    endeEmpfang = false;
    
    // Debugausgabe
      Serial << (F("cmd: "))   << empfDaten.cmd << '\t' 
             << (F("value: ")) << empfDaten.value << endl;

    if (strcmp (empfDaten.cmd, "A") == 0) {
      Serial.println("-A-");
    }
    else if (strcmp (empfDaten.cmd, "B") == 0) {
      Serial.println("-B-");
    }
    else if (strcmp (empfDaten.cmd, "C") == 0) {
      Serial.println("-C-");
    }
    else if (strcmp (empfDaten.cmd, "D") == 0) {
      Serial.println("-D-");
    }

    /*   
    // >> single charachter only, ansonsten strcmp / strncmp <<
    const int var = atoi(empfDaten.cmd);
    switch (var)
    {
      case 'A': Serial.println("A"); break;
      case 'B': Serial.println("B"); break;
      case 'C': Serial.println("C"); break;
      case 'D': Serial.println("D"); break;   
      default:  Serial.println("unknown"); break;
    }
    */
  }  
}

// ----- Funktionen ------------------------------------------------------

void receiveEventI2C (int countReceiveBytes)
{
  static byte index {0};
  
  while (Wire.available() )
  {
    char c = Wire.read();
                                     // Zwangsweise verwendet "unused'
    if (index < sizeMessage && index < countReceiveBytes) {
      empfDaten.asArray[index++] = c;
    }
    else if (c == '\n') {    // Endeerkennung 'LineFeed'
      index = 0;
      endeEmpfang = true;
      
      // Debugausgabe
      //Serial << (F("countReceiveBytes: ")) << countReceiveBytes << endl;
      /*
             << (F("cmd: "))   << empfDaten.cmd << '\t' 
             << (F("value: ")) << empfDaten.value << endl;
      */       
    }
  }
}

Der ESP8266 tut sich mit dem Slavemode relativ schwer.
Soweit mir bekannt tut es zwischen ESP8266 einigermaßen.
Mit anderen Geräten gibt/gab es teilweise massive Probleme.

Der Hauptgrund scheint zu sein, dass dieser ESP keine Hardware Einheit hat, welches das abhandelt. Ist alles in Software.

Bei meinen letzten Experimenten, war bei 35kHz Schluss.

1 Like

Hallo,
ich denke beim ESP legt Wire.begin() die Pins fest

https://arduino-esp8266.readthedocs.io/en/latest/libraries.html#i2c-wire-library

Hey,

Nein ich benutze keinen Levelshifter, ich habe den Arduino und den WeMos beide über den 3,3V Anschluss verkabelt. Braucht man den da trotzdem?

Danke für die Bespiele die schau ich mir auch nochmal an :slight_smile:

Hallo,
wenn Du den Arduino mit der USB versorgst dann gibt er 5V an den Ausgängen raus. Damit kann der ESP Eingang sterben.

der 3,3V Pin am Arduino ist ein Ausgang da kommen 3,3V raus , rein kann das nix. Der Arduino würde auch mit 3,3V nicht laufen.

Und wenn du den Levelshifter dazwischen hast: Denke an die PullUp Widerstände (4 Stück)

Die üblichen I2C-Levelshifter haben diese Pullups on Board.

Ich finde es immer wieder erstaunlich, das 8bit Prozessoren mit 16MHz den Master für mehrere 32bit 240MHz Slaves sein sollen. Hört sich für mit total falsch an.

1 Like

Ich bin noch neu in der Welt der Arduino-Programmierung und habe bisher lediglich kleine Projekte umgesetzt.
Obwohl ich bereits viel zu diesem Thema gelesen habe, versuche ich mich schnell einzuarbeiten. Bitte seht mir meine Unwissenheit nach und vielen Dank für die konstruktiven Hinweise und Tipps.

Beachte nochmal den Hinweis von combie in #3. Den ESP als Slave zu nehmen ist keine gute Idee.

Gruß Tommy

Dann erkläre doch bitte genauer, was du vor hast.

Mit wieviel THz läuft dein I2C Bus?

@kawkas
Wenn der ESP8266 kein I2C Slave per Hardware kann wird das wohl leider nichts werden. Wusste ich nicht. Ein ESP32 soll das mit Umständen können. Link aber wohl immer noch nicht und wohl nie wie man das sonst gewohnt ist. ESP eben.

Oder drehst die Funktion Master-Slave um wenn das ins Konzept passt. Bspw. sendet der ESP zyklisch seine Daten. Unabhängig ob der Arduino Slave neue Daten genau jetzt benötigt oder nicht.

Ergänzung: I2C-Adressen <10 sind für Sonderfunktionen reserviert.

Gruß Tommy


Die Idee besteht darin, eine modulare Plattform zu schaffen, auf der die Bedienelemente angeordnet werden können. Ziel ist es, HMI-Bedienelemente unkompliziert in beliebiger Reihenfolge in Reihe zu schalten (wahrscheinlich mit dem Qwiic-System) und dann Signale (für die jeweiligen Bedienelemente) an ProtoPie oder TouchDesigner zu senden.
Diese Signale sollen so simpel und gleichzeitig eindeutig sein, dass man sich in der Logik der entsprechenden Nachfolge-Programme seine gewünschten Funktionen etablieren kann.

scheint aber nicht für den ESP8266 zu gelten oder schliest Du daraus das werte >10 die Adresse angeben?

das könnte zumindest für den ESP32 ja dann zutreffen.

aus der Doku

I2C (Wire library)

Wire library currently supports master mode up to approximately 450KHz. Before using I2C, pins for SDA and SCL need to be set by calling Wire.begin(int sda, int scl), i.e. Wire.begin(0, 2) on ESP-01, else they default to pins 4(SDA) and 5(SCL).

für den ESP32
hier

Gruß Heinz

reservierte I2C Adressen sind meines Erachtens

if (address <= 0x07 || address >= 0x78) Serial.print(F(" (reserved)")); 

0x08 ist imho in Ordnung.

Ok, dann hatte ich das falsch im Kopf.

Gruß Tommy

Hallo bedenke i2c ist für Entfernungen von 50 cm gedacht. Es soll Leute geben die haben auch schon mal 5m geschafft.

Ah ok ihr redet von Standard Adressen für verschiedene Slave devices.

Grüße Heinz