Wire.RequestFrom und Wire.onReceive

Hallo,

ich habe eine I2C "Netzwerk" mit 2 Arduino's und einem Kompassmodul. Ich habe eine Funktion geschrieben die den Kompass ausliest. Dabei sende ich mit Wire.write() einen Befehl und mit Wire.RequestFrom() erhalte ich dann meine Daten(insgesamt 6 Pakete die dann zu 3 Variablen zusammengefügt werden). Von dem 2 Arduino empfange ich auch Daten, dort aber jeweils nur 1 Paket. Deshalb hab ich mir eine Wire.onReceive() Funktion geschrieben. In dieser befindet sich eine Switch - Case Anweisung die die Daten auswertet. Meine Frage lautet nun wenn ich beginne vom Kompassmodul Daten zu empfangen, springe ich dann automatisch in die onReceive Schleife oder wird das mit RequestFrom abgearbeitet?

onReceive() und onRequest() sind dazu da Callback Funktionen zu registrieren. Das funktioniert im Prinzip wie attachInterrupt() und ist auch in der Tat ein Interrupt. Die Funktion die man als Parameter übergibt wird automatisch aufgerufen wenn Daten ankommen, bzw. wenn Daten angefordert werden.

Eine Schleife ist allerdings was anderes :slight_smile:

EDIT:
Diese Funktionen laufen allerdings nur auf Slaves! Auf dem Master werden die Daten mit read() per Hand ausgelesen. In dem Fall mit dem Kompass ist der Arduino ein Master. Du forderst daher Daten mit requestFrom() an und machst danach sofort entsprechend oft read(). Schau dir mal RTC Libs als Anhaltspunkt an.

Hier ist z.B. eine vereinfachte Funktion um eine DS1307 Real Time Clock auszulesen:

void DS1307RTC::read(tmElements_t &tm)
{
  Wire.beginTransmission(DS1307_CTRL_ID);
  Wire.write((uint8_t)0x00); 
  Wire.endTransmission();

  Wire.requestFrom(DS1307_CTRL_ID, tmNbrFields);

  tm.Second = bcd2dec(Wire.read() & 0x7f);   
  tm.Minute = bcd2dec(Wire.read() );
  tm.Hour =  bcd2dec(Wire.read() & 0x3f);  // mask assumes 24hr clock
  tm.Wday = bcd2dec(Wire.read() + 1);
  tm.Day = bcd2dec(Wire.read() );
  tm.Month = bcd2dec(Wire.read() );
  tm.Year = y2kYearToTm((bcd2dec(Wire.read())));
}

Erst wird die Adresse mit beginTransmission() geschickt. Dann wird das Register mit write() adressiert und die Übertragung mit endTransmission() abgeschlossen. Dann fordert man die Daten mit requestFrom() an und macht sofort danach entsprechend oft read()

Hier ist Code für die Kommunikation zwischen Arduinos:

Da wird einmal gezeigt wie der Slave nur mit onRequest() reagiert, aber auch wie er Daten mit onReceive() empfängt

Vielen Dank für deine schnelle Antwort.

Stimmt Schleife ist falsch. Eigentlich meinte ich in die Funktion in der onReceive enthalten ist.
Und mit der Befehl Wire.onReceive(Funktion1) sollte er normal automatisch in die Funktion1 springen wenn Daten anliegen oder?
Und wenn ich dann an einer anderen Stelle mit Wire.requestFrom() Daten abfrage und somit wieder Daten anliegen, springt er dann auch in die Funktion1 oder kann ich nach dem requestFrom die Daten mit Wire.read() auslesen?

Ich hab das grad eben mal ausprobiert. Indem ich im setup() ein den onReceive(Funktion1) Befehl hatte und im loop den Kompass ganz normal mit requestFrom und danach mit wire.read() ausgelesen habe. Dabei ist er nie in die Funktion1 gesprungen und so sollte es auch sein.

Ich bin mir nur leider nicht ganz sicher ob das immer so ist :~

Ich habe nochmal was dazu geschrieben

onReceive() ist eine Funktion für einen I2C Slave. Wenn du so ein Modul ausliest ist der Arduino Master und das Modul Slave. Du musst also read() verwenden. read() liefert dir einfach direkt ein Byte zurück, dass man in eine Variable schreibt. Wie gesagt, lade dir mal eine RTC Lib runter und schau wie das da gemacht ist. Ist genau das gleiche Prinzip für alle anderen I2C Devices.

Du musst auch das Modul korrekt adressieren. Sowohl die Device Adresse als auch die Register Adresse! Normalerweise schickt man erst die Device Adresse mit beginTransmission(), dann die Register Adresse mit write()/endTransmission() und macht dann requestFrom(). Schau dir dazu auch das Datenblatt an wie die Device Adresse aussieht!

onReceive() kannst du also an dieser Stelle nicht verwenden! Das ist was wenn du Daten an einen Arduino schickst, der ein I2C Slave ist. Aber nicht um irgendein IC auszulesen.

Meine Frage war eigentlich ob einer der Befehle Vorrang hat oder ob ich die Daten einmal so und einmal anders abfragen kann.
Wenn ich nämlich die Daten vom Kompass abfrage mit Wire.requestFrom soll er nicht in die Funktion vom onReceive springen.
Aber die Daten vom 2. Arduino möchte ich mit Wire.onReceive() empfangen.

Es ist nämlich so:

Arduino II:
Dieser hat ein Touchpanel. Wenn ich dort nun etwas drücke sendet er die Daten an Arduino I.
Dieser Arduino muss folglich Master sein denn er soll unabhängig von den anderen zu jedem Zeitpunkt senden können.

Arduino I:
Steuert meine Motoren. Er muss auf die eingaben von Arduino II reagieren => ist dieser Arduino der Slave.

Komass:
Das Kompassmodul hängt auf Arduino I. Dieses Modul ist jedoch immer Slave und wenn ich nun mit Arduino I mit Wire.write() den Befehl für das Kompassmodul auf die Datenleitung lege sollte dieser doch auch den Befehl ausfürhen oder?

Somit ist in meinem System Arduino II Master Arduino I Slave und gleichzeitig Master für den Kompass und der Kompass ist Slave.

Dieser Arduino muss folglich Master sein denn er soll unabhängig von den anderen zu jedem Zeitpunkt senden können.

Das wäre zwar einfach, muss aber nicht unbedingt.

Somit ist in meinem System Arduino II Master Arduino I Slave und gleichzeitig Master für den Kompass und der Kompass ist Slave.

Gleichzeitig geht leider nicht.
Arduino I immer zum Kompass lesen von Slave auf Master umschalten und wieder zurück ist sicher komplizierter als
Arduino I als Master abwechselnd Kompass und Arduino II ( der dann Slave wäre ) abfragen, ob eine neue Bedien-Anforderung vorliegt. Mag sein, dass andere flinker mit einem Touchscreen umgehen als ich, für Arduinos ist das jedenfalls relativ langsam.
Sicher kann auch die erste optische Rückmeldung auf Touchscreen - Aktionen lokal auf dem Arduino II ( Slave ) erfolgen.
Bis die Motoren auf dem Master Arduino I reagieren, kann es sicher mal 10 ms dauern, oder ?