I2C im Interrupt

Hallo Zusammen,

ich bastel gerade an meinem ersten Projekt und bin blutiger Anfänger... ich hoffe jemand kann mir helfen :slight_smile:

Ich möchte gern die ausgelesen Daten einer IR Fernbedienung (Slave Arduino) vom Master einlesen lassen sobald eine Taste gedrückt wurde.
Im Moment setzt der Slave kurz einen Ausgang auf LOW -> dieses Signal will ich am Master benutzen um die Daten "abzuholen". Leider hängt sich das ganze aber auf sobald ich eine Taste drücke. Nun die Frage: kann man die I2C Daten nicht innerhalb eines Interrupts auslesen bzw. in Variablen schreiben?
Danke schon mal im Voraus :slight_smile:

Slave:

// Auslesen einer IR-Fernbedienung mit 24 Tasten
// Ausgabe eines Bytes (Taste) über I2C an Master
// Ausgabe eines "Interrupts" (IRQ) an Master 


   #include <Arduino.h>
   #include <IRremote.h>
   #include <Wire.h>         

#define I2C_ADDR     8                  // i2c slave address
#define IR_SIG_IN    3                  // IR Sensor Signal
#define IRQ_PIN      6                  // Interrupt an Master


byte i2cByte[] = { 101, 102, 103, 104, 105, 106 , 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124 };
byte sendByte = 0;

//______________________________________________________________________________________________________________________________________________
void setup()
{
   Serial.begin(9600);                                                                 // Serielle Ausgabe starten
   IrReceiver.begin(IR_SIG_IN, ENABLE_LED_FEEDBACK, USE_DEFAULT_FEEDBACK_LED_PIN);     // Infrarotempfänger initialisieren
   Wire.onRequest(IRCodeTransmit);                                                     // Auf Anfrage des Master sendByte übertragen
   Wire.begin(I2C_ADDR);                                                               // init I2C Slave mode
   digitalWrite(IRQ_PIN, HIGH);                                                        // Output Pin, Interrupt für Master auf HIGH
   pinMode(IRQ_PIN, OUTPUT);                                                           // IRQ LOW
}

//______________________________________________________________________________________________________________________________________________
void loop(){
  
  readIRcode();                         // start IR-Code empfangen und dekodieren
}

//______________________________________________________________________________________________________________________________________________
void readIRcode(){
    if (IrReceiver.decode()) {
        //Fernbedienung Tasten________________________________________________________________

        switch (IrReceiver.decodedIRData.decodedRawData) {                               
           case 0xF609FF00: sendi2cIRQ(i2cByte[0]);          break;          //Heller
           case 0xE21DFF00: sendi2cIRQ(i2cByte[1]);          break;          //Dunkler
           case 0xE01FFF00: sendi2cIRQ(i2cByte[2]);          break;          //OFF
           case 0xF20DFF00: sendi2cIRQ(i2cByte[3]);          break;          //ON
           case 0xE619FF00: sendi2cIRQ(i2cByte[4]);          break;          //Rot
           case 0xE41BFF00: sendi2cIRQ(i2cByte[5]);          break;          //Grün
           case 0xEE11FF00: sendi2cIRQ(i2cByte[6]);          break;          //Blau
           case 0xEA15FF00: sendi2cIRQ(i2cByte[7]);          break;          //Weiß
           case 0xE817FF00: sendi2cIRQ(i2cByte[8]);          break;          //Orange
           case 0xED12FF00: sendi2cIRQ(i2cByte[9]);          break;          //Hellgrün
           case 0xE916FF00: sendi2cIRQ(i2cByte[10]);         break;          //Lila
           case 0xB24DFF00: sendi2cIRQ(i2cByte[11]);         break;          //Flash
           case 0xBF40FF00: sendi2cIRQ(i2cByte[12]);         break;          //Braun
           case 0xB34CFF00: sendi2cIRQ(i2cByte[13]);         break;          //Grün-Blau
           case 0xFB04FF00: sendi2cIRQ(i2cByte[14]);         break;          //Brombeere
           case 0xFF00FF00: sendi2cIRQ(i2cByte[15]);         break;          //Strobe
           case 0xF50AFF00: sendi2cIRQ(i2cByte[16]);         break;          //Beige
           case 0xE11EFF00: sendi2cIRQ(i2cByte[17]);         break;          //Hellblau-Türkis
           case 0xF10EFF00: sendi2cIRQ(i2cByte[18]);         break;          //Pink-Rot
           case 0xE51AFF00: sendi2cIRQ(i2cByte[19]);         break;          //Fade
           case 0xE31CFF00: sendi2cIRQ(i2cByte[20]);         break;          //Gelb
           case 0xEB14FF00: sendi2cIRQ(i2cByte[21]);         break;          //Türkis
           case 0xF00FFF00: sendi2cIRQ(i2cByte[22]);         break;          //Pink
           case 0xF30CFF00: sendi2cIRQ(i2cByte[23]);         break;          //Smooth
        }      
       IrReceiver.resume(); 
    }
}

//______________________________________________________________________________________________________________________________________________
void sendi2cIRQ(byte x){
  sendByte = x;                     // Taste in sendByte schreiben
  digitalWrite(IRQ_PIN, LOW);       // Interrupt Pin auf LOW (auslösen der Interrupt-Routine am Master Arduino)
  delay(10);
  digitalWrite(IRQ_PIN, HIGH);
}

//______________________________________________________________________________________________________________________________________________
void IRCodeTransmit(){
  Wire.write(sendByte);
}

Master:

//Libraries einbinden
   #include <Arduino.h>
   #include <IRremote.h>
   #include <Adafruit_NeoPixel.h>
   #include <Wire.h>

#define SLAVE_ADDR 8                                       // Adresse des Slave

//Ein- / Ausgänge Arduino                                                     
   const byte IR_READY = 3;                                 // Interrupt - Slave 8 hat neue Daten von Fernbedienung empfangen und ist bereit zum Senden
   const byte PIXEL_DATA_OUT = 6;                           // Ausgang DATA NeoPixel

//Parameter
   int PIXEL_COUNT = 16;                                   //Anzahl LEDs
   int BRIGHTNESS = 0;                                     //Helligkeitswert LDR
   volatile byte IR_BYTE_RCVD = 0;

//Definition Objetkte
   Adafruit_NeoPixel Matteo = Adafruit_NeoPixel(PIXEL_COUNT, PIXEL_DATA_OUT, NEO_GRB + NEO_KHZ800);

//Interrupt_____________________________________________________________________________________________________________________________
void IR_REM_NEW_INPUT() {
  Wire.requestFrom(8, 1);  
  while (Wire.available()) {        // slave may send less than requested
    IR_BYTE_RCVD = Wire.read();
  }
}



//Effekte________________________________________________________________________________________________________________________________

//setAll_______________________________________________________________________
void setAll(int R,int G,int B){
  for(int i=0; i<PIXEL_COUNT; i++){
    Matteo.setPixelColor(i, Matteo.Color(R, G, B));
    Matteo.show();  
  }
}

//Rainbow______________________________________________________________________
void rainbow(int wait) {
  for(long firstPixelHue = 0; firstPixelHue < 1*65536; firstPixelHue += 500) {
    for(int i=0; i<Matteo.numPixels(); i++) { 
      int pixelHue = firstPixelHue + (i * 65536L / Matteo.numPixels());
      Matteo.setPixelColor(i, Matteo.gamma32(Matteo.ColorHSV(pixelHue)));
    }
    Matteo.show();
    delay(wait);
  }
}

//_______________________________________________________________________________________________________________________________________
void setup() {
   Serial.begin(9600);                                                                 //Serielle Ausgabe starten
   Serial.println("Bereit");
   Wire.begin();
   Matteo.begin();                                                                     //LED-Streifen initialisieren
   pinMode(IR_READY, INPUT);
   attachInterrupt(digitalPinToInterrupt(IR_READY), IR_REM_NEW_INPUT, FALLING);        //Einbinden des Interrupts (Änderungen FB)
}

//_______________________________________________________________________________________________________________________________________
void loop() {

  Serial.println(IR_BYTE_RCVD);
  
        switch (IR_BYTE_RCVD) {                               
           case 101: ;                             break;          //Heller
           case 102: ;                             break;          //Dunkler
           case 103: setAll(0,0,0);                break;          //OFF
           case 104: ;                             break;          //ON
           case 105: setAll(255,0,0);              break;          //Rot
           case 106: setAll(0,255,0);              break;          //Grün
           case 107: setAll(0,0,255);              break;          //Blau
           case 108: setAll(255,255,255);          break;          //Weiß
           case 109: setAll(255,127,0);            break;          //Orange
           case 110: setAll(150,255,70);           break;          //Hellgrün 
           case 111: setAll(123,104,238);          break;          //Lila
           case 112: rainbow(100);                 break;          //Flash
           case 113: setAll(205,102,29);           break;          //Braun
           case 114: setAll(34,139,34);            break;          //Grün-Blau
           case 115: setAll(139,34,82);            break;          //Brombeere
           case 116: ;                             break;          //Strobe
           case 117: setAll(255,185,15);           break;          //Beige
           case 118: setAll(135,206,250);          break;          //Hellblau-Türkis
           case 119: setAll(205,50,120);           break;          //Pink-Rot
           case 120: ;                             break;          //Fade
           case 121: setAll(255,255,0);            break;          //Gelb
           case 122: setAll(0,245,255);            break;          //Türkis
           case 123: setAll(255,20,147);           break;          //Pink
           case 124: ;                             break;          //Smooth
           default:                                break;
        }
}

Warum?
setze in der ISR eine boolsche Variable, und werte die ausserhalb der ISR aus. Ist sie ausgewertet, setze sie zurück.

Deine Schnipsel sehen oben nicht gut aus. Editiere den Post nochmal, indem Du in der IDE auf BEARBEITEN - FÜR FORUM KOPIEREN anklickst und dann den Code anStelle dessen einfügst, was nach Deiner Frage kommt.
Sonst schaut da eher wenig Leserschaft drauf.
Ich schon gar nich...

Da die Arduino I2C Methoden selber Interrupts benötigen und in (d)einer ISR die Interrupts gesperrt sind, geht das nicht.

Das funktioniert, Danke :slight_smile:

Gut zu Wissen :+1:

Leider habe ich gerade festgestellt dass sich dieser "Rainbow"-Effekt nicht mit dem Drücken einer anderen Taste abbrechen lässt... das war ursprünglich der Grund fürs auslagern der IR-Fernbedienung auf einen Slave. ich dachte das würde mit dem Interrupt funktionieren... Habt ihr dazu zufällig auch noch ne Idee? :slight_smile:

Die äußere for-Schleife mit dem delay(wait); ist Mist. Wenn du diese Schleife durch loop() erledigen lässt, ist dein Sketch einfacher unterbrechbar.

Vielleicht willst du ja irgendwann mal noch mehr zeitaufwendige/unterbrechbare "shows" realisieren. Da sollte loop jeweils nur (max) einen Schritt ausführen, sofort wieder nach dem IrReceiver schauen und gegebenenfalls die aktuelle (und vom vorigen loop-Durchlauf gemerkte) Show fortsetzen.
Die Master/Slave Aufteilung und I2C macht alles nur unnötig kompliziert.

Beschreibe die Funktionalität noch einmal neu. Ganz ohne Programmierfachbegriffe.
Was kann ein neben dir stehender Beobachter sehen und dann beschreiben wenn du das ganze bedienst?

Die LEDs wechseln die ganze Zeit die Farben. iwo nimmt eine Fernbedienung in die Hand drückt auf eine Taste und dann ......

Einmal das mit der Schleife ändern, wie in #5 beschrieben und zusätzlich einen ATtiny für deine IR-Bedienung einsetzen. Die unterbricht dann deinen Rainbow.

Hier ein Beispiel mit dem ATtiny85

Ja...
Dann mal noch ne Anmerkung:
define ist nicht schick - haben wir in den letzten Tagen sehr oft....
Mach daraus ein const byte ...

Dann bin ich über sendi2cIRQ gestolpert.
Du verbrauchst eine globale Variable.
Variante 1:

void sendi2cIRQ(const byte x){
  Wire.write(x);                     // Taste in sendByte schreiben
  digitalWrite(IRQ_PIN, LOW);       // Interrupt Pin auf LOW (auslösen der Interrupt-Routine am Master Arduino)
  delay(10);
  digitalWrite(IRQ_PIN, HIGH);
}

oder wenn Du das IRCodeTransmit() unbedingt behalten willst:

void sendi2cIRQ(const byte x){
  IRCodeTransmit(x);                     // Taste in sendByte schreiben
  digitalWrite(IRQ_PIN, LOW);       // Interrupt Pin auf LOW (auslösen der Interrupt-Routine am Master Arduino)
  delay(10);
  digitalWrite(IRQ_PIN, HIGH);
}

//______________________________________________________________________________________________________________________________________________
void IRCodeTransmit(const byte b){
  Wire.write(b);
}

Ich habs hinbekommen, Danke für eure Hilfe :slight_smile: Also ganz im Ernst das ist das erste Forum in dem mal keiner meckert oder beleidigt - Ihr seid Klasse :smiley:

Danke für den Tip :+1: ich habs mit nem break; gemacht - und entgegen meiner Befürchtung sieht man im "Rainbow" nicht dass er jedesmal kurz die Daten von der Fernbedienung abfragt.

Das hatte ich schon gefunden (und geklaut :smiley: ) - das habe ich mehr oder weniger 1 zu 1 kopiert und für meinen Slave verwendet (nutze aber einen Nano dafür)

hab ich so übernommen... sollte man #define generell nicht verwenden? ist das in irgendeiner Form anfällig oder einfach (nur) nicht schick?

Danke nochmal an Alle :slight_smile:

1 Like

Ja.
Da ich mich nicht mit der Erklärung schmücken will:

:slight_smile: Schön wenns geht.

1 Like

Das ist doch prima. Genau dafür ist das doch auch da.
Nur das der Nano halt etwas größer ist.

1 Like

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.