Lautstärke mit Apple Remote - Taste lange gedrückt umsetzen

Hallo,
bin neu hier und auch Anfänger was Arduino Programmierung angeht.
Ich möchte ein Motorpoti mit meiner Apple Unibody Fernbedienung steuern.
Zum auswerten der IR Signale habe ich diese Library verwendet: GitHub - Arduino-IRremote/Arduino-IRremote: Infrared remote library for Arduino: send and receive infrared signals with multiple protocols
Funktioniert auch soweit ganz gut, hier aber mein Problem:

Die Ausgänge sollen nur bei gedrückter Taste aktiv sein, ich kann sie nur einschalten. Bzw. nach einer kurzen Zeit selbst ausschalten wie im Beispiel. Das Problem hierbei ist: die Apple Remote basiert auf dem NEC Protokoll, bei lange gedrückter Taste kommt ein Wiederholungscode, der bei jeder Taste gleich ist. Habe schon paar Sachen ausprobiert, aber alles ohne zufriedenstellendes Ergebnis. Kann mir jemand weiterhelfen, wie man das praktisch umsetzten könnte?

Vielen Dank schonmal im Vorraus.
Alex

Der Code:

#define DEBUG 1
#include <IRremote.h>
/* https://github.com/shirriff/Arduino-IRremote */

#define RECV_PIN 11

IRrecv irrecv(RECV_PIN);
decode_results results;

void PrintDecodeType(int type)
{
  switch(type)
  {
    case NEC:Serial.println("NEC ");break;
    case SONY:Serial.println("SONY ");break;
    case RC5:Serial.println("RC5 ");break;
    case RC6:Serial.println("RC6 ");break;
    case DISH:Serial.println("DISH ");break;
    case SHARP:Serial.println("SHARP ");break;
    case PANASONIC:Serial.println("PANASONIC ");break;
    case JVC:Serial.println("JVC ");break;
    case SANYO:Serial.println("SANYO ");break;
    case MITSUBISHI:Serial.println("MITSUBISHI ");break;
    case UNKNOWN:Serial.println("UNKNOWN ");break;
  }  
}

void MotorOff()
{
  digitalWrite(A0,LOW);
  digitalWrite(A1,LOW);
  digitalWrite(A2,LOW);
  digitalWrite(A3,LOW);
  digitalWrite(A4,LOW);
  digitalWrite(A5,LOW);
  
  digitalWrite(2,LOW);
  digitalWrite(3,LOW);
  digitalWrite(4,LOW);
  digitalWrite(5,LOW);
  digitalWrite(6,LOW);
  digitalWrite(7,LOW);
}

void MotorUp()
{
  digitalWrite(A0,HIGH);
  digitalWrite(A1,HIGH);
  digitalWrite(A2,HIGH);
  digitalWrite(A3,HIGH);
  digitalWrite(A4,HIGH);
  digitalWrite(A5,HIGH);
  
  digitalWrite(2,LOW);
  digitalWrite(3,LOW);
  digitalWrite(4,LOW);
  digitalWrite(5,LOW);
  digitalWrite(6,LOW);
  digitalWrite(7,LOW);
}

void MotorDown()
{
  digitalWrite(A0,LOW);
  digitalWrite(A1,LOW);
  digitalWrite(A2,LOW);
  digitalWrite(A3,LOW);
  digitalWrite(A4,LOW);
  digitalWrite(A5,LOW);
  
  digitalWrite(2,HIGH);
  digitalWrite(3,HIGH);
  digitalWrite(4,HIGH);
  digitalWrite(5,HIGH);
  digitalWrite(6,HIGH);
  digitalWrite(7,HIGH);
}

void setup()
{
    pinMode(13, OUTPUT); // PIN 13 wird auf dauer an geschaltet
digitalWrite(13, HIGH);


  pinMode(A0,OUTPUT);
  pinMode(A1,OUTPUT);
  pinMode(A2,OUTPUT);
  pinMode(A3,OUTPUT);
  pinMode(A4,OUTPUT);
  pinMode(A5,OUTPUT);
  
  pinMode(2,OUTPUT);
  pinMode(3,OUTPUT);
  pinMode(4,OUTPUT);
  pinMode(5,OUTPUT);
  pinMode(6,OUTPUT);
  pinMode(7,OUTPUT);
  
  MotorOff();
  
  Serial.begin(9600);
  Serial.println("Ready.");
    
  irrecv.enableIRIn(); // Start the receiver
}

void loop()
{
   if (irrecv.decode(&results))
    {
     PrintDecodeType(results.decode_type);
      Serial.print(results.bits, DEC);
      Serial.print("  ");
      Serial.println(results.value, HEX);
      irrecv.resume(); // Receive the next value
      
    if (results.value==2011287729) Serial.println ("Up");
    if (results.value==2011279537) Serial.println ("Down");
    if (results.value==2011238577) Serial.println ("Left");
    if (results.value==2011291825) Serial.println ("Right");
    if (results.value==4294967295) Serial.println ("still pressed");
    
    if (results.decode_type==NEC)
      {
         
         if (results.value==2011287729)
         {
            MotorUp();
            delay(200);
                        MotorOff();                        
         }
         else if (results.value==2011279537)
         {
            MotorDown();
            delay(200);
                        MotorOff();
         }         
       }
        }
            }

Rein theoretisch: letzten Code und Timestamp merken und alle Repeats dann mit Code wiederholen. Nach definiertem Timeout Code verwerfen.

Danke, theoretisch hab ich mir das auch so in etwa vorgestellt, leider haperts an der Umsetzung.
Ich könnte den letzten Code in eine Variable schreiben und dann wiederholt auslesen. Nur wie mache ich das richtig?

Man könnte die zwei Motorbewegungsfunktionen zu einer zusammenfassen, an die du den IR Code aus dem Loop übergibst und innerhalb derer du abfragst, welcher Code übergeben wurde und je nach dem den Motor rauf oder runter bewegst.
In der Loop musst du dann nur noch abfragen, ob der Code kein Wiederholungscode ist -> dann den Code in die Variable für den letzten Code speichern und die Motorfunktion aufrufen und ihr den Code übergeben.
Anschliessend fragst du ab, ob es doch ein Wiederholungscode ist -> wenn ja, rufst du die Funktion auf und übergibst ihr die Variable "letzten Code".
Müsste doch gehen oder?

Die Abfrage, ob der Code ein Wiederholungscode hat hat bisher nie funktioniert, da liegen immer paar ms dazwischen, um das zu überspringen fehlen mir die Programmierkenntnisse.
Wollte das anfangs mit einem Timer bewerkstelligen, hab dann bemerkt, daß die verwendete Library wohl einige Timer verwendet, um den Code der verschiedenen möglichen Remotecodes zu entschlüsseln.

Letzendlich hat mir die Seite Apple Remote | H i F i D U I N O
sehr weitergeholfen.

Für alle, die auch ihre Apple Fernbedienung zum steuern eines Motorpotis verwenden wollen:

Der Code:

/***************************************************************************************************
Volume Control with Apple Remote and Alps Motorpoti

***************************************************************************************************/

#include <pins_arduino.h> // This is needed for a new pulseIn function for remote

#define REMOTEPIN 11 // Pin for IR receiver (remote control)

// The following variables for the remote control feature
int duration; // Duration of the IR pulse
int mask;
int c1; // Byte 1 of the 32-bit remote command code
int c2; // Byte 2 of the 32-bit remote command code
int c3; // Byte 3 of the 32-bit remote command code
int c4; // Byte 4 of the 32-bit remote command code
int IRkey; // The unique code (Byte 3) of the remote key
int previousIRkey; // The previous code (used for repeat)

/*
The following fucntion defines a new pulseIn function because the pulseIn function in the Arduino
library does not exit if there is a pulse that does not end. Typically, this would not cause any
problems if you are reading true pulses, but because the remote code I wrote measures "UP pulses"
there is a chance that some noise would trigger a sinle pulse where the current pulseIn function
would hang.
The reason is the following: the IR receiver when it is not receiving any signals outputs HIGH.
If there is a signal (a real pulse), it outputs LOW and then HIGH. If I were to measure DOWN putlses,
this would be fine, but because the NEC protocol in the Apple remote uses distance between pulses to
codify its information, I measure the time between pulses which is an "UP pulse". In reality these UP
pulses are not really pulses, but the time between the real pulses from the remote control.
This code is taken from the Arduino code base (thanks to users in the Arduino forum) and modified to
check for end of pulse
*/

unsigned long newpulseIn(uint8_t pin, uint8_t state, unsigned long timeout)
{
uint8_t bit = digitalPinToBitMask(pin);
uint8_t port = digitalPinToPort(pin);
uint8_t stateMask = (state ? bit : 0);
unsigned long width = 0;

unsigned long numloops = 0;
unsigned long maxloops = microsecondsToClockCycles(timeout) / 16;

// wait for any previous pulse to end
while ((*portInputRegister(port) & bit) == stateMask)
if (numloops++ == maxloops)
return 0;

// wait for the pulse to start
while ((*portInputRegister(port) & bit) != stateMask)
if (numloops++ == maxloops)
return 0;

// wait for the pulse to stop
while ((*portInputRegister(port) & bit) == stateMask){
if(width++ == maxloops) // added the check for end of pulse
return 0;
}
return clockCyclesToMicroseconds(width * 20+16); // Recalibrated because of additional code
// in the width loop
}

/*
The following function returns the code from the Apple Aluminum remote control. The Apple remote is
based on the NEC infrared remote protocol. Of the 32 bits (4 bytes) coded in the protocol, only the
third byte corresponds to the keys. The function also handles errors due to noise (returns 255) and
the repeat code (returs zero)

The Apple remote returns the following codes:

Up key: 238 135 011 089
Down key: 238 135 013 089
Left key: 238 135 008 089
Right key: 238 135 007 089
Center key: 238 135 093 089 followed by 238 135 004 089 (don't know why there is two commands)
Menu key: 238 135 002 089
Play key: 238 135 094 089 followed by 238 135 004 089 (don't know why there is two commands)
*/

int getIRkey() {
c1=0;
c2=0;
c3=0;
c4=0;
duration=1;
while((duration=newpulseIn(REMOTEPIN, HIGH, 15000)) < 2000 && duration!=0)
{
// Wait for start pulse
}
if (duration == 0) // This is an error no start or end of pulse
return(255); // Use 255 as Error

else if (duration<3000) // This is the repeat
return (0); // Use zero as the repeat code

else if (duration<5000){ // This is the command get the 4 byte
mask = 1;
for (int i = 0; i < 8; i++){ // get 8 bits
if(newpulseIn(REMOTEPIN, HIGH, 3000)>1000) // If "1" pulse
c1 |= mask; // Put the "1" in position
mask <<= 1; // shift mask to next bit
}
mask = 1;
for (int i = 0; i < 8; i++){ // get 8 bits
if(newpulseIn(REMOTEPIN, HIGH, 3000)>1000) // If "1" pulse
c2 |= mask; // Put the "1" in position
mask <<= 1; // shift mask to next bit
}
mask = 1;
for (int i = 0; i < 8; i++){ // get 8 bits
if(newpulseIn(REMOTEPIN, HIGH, 3000)>1000) // If "1" pulse
c3 |= mask; // Put the "1" in position
mask <<= 1; // shift mask to next bit
}
mask = 1;
for (int i = 0; i < 8; i++){ // get 8 bits
if(newpulseIn(REMOTEPIN, HIGH, 3000)>1000) // If "1" pulse
c4 |= mask; // Put the "1" in position
mask <<= 1; // shift mask to next bit
}
// Serial.println(c1, HEX); //For debugging
// Serial.println(c2, HEX); //For debugging
// Serial.println(c3, HEX); //For debugging
// Serial.println(c4, HEX); //For debugging
return(c3);
}
}

/************************ MAIN PROGRAM ************************************************************/

void setup() {

Serial.begin(9600);
Serial.println("Ready.");
pinMode(REMOTEPIN, INPUT); // Pin for IR sensor
digitalWrite(REMOTEPIN, HIGH); // Enable pull-up resistor

pinMode(A0,OUTPUT);
pinMode(A1,OUTPUT);
pinMode(A2,OUTPUT);
pinMode(A3,OUTPUT);
pinMode(A4,OUTPUT);
pinMode(A5,OUTPUT);

pinMode(2,OUTPUT);
pinMode(3,OUTPUT);
pinMode(4,OUTPUT);
pinMode(5,OUTPUT);
pinMode(6,OUTPUT);
pinMode(7,OUTPUT);

}

void MotorOff()
{
digitalWrite(A0,LOW);
digitalWrite(A1,LOW);
digitalWrite(A2,LOW);
digitalWrite(A3,LOW);
digitalWrite(A4,LOW);
digitalWrite(A5,LOW);

digitalWrite(2,LOW);
digitalWrite(3,LOW);
digitalWrite(4,LOW);
digitalWrite(5,LOW);
digitalWrite(6,LOW);
digitalWrite(7,LOW);
}

void MotorUp()
{
digitalWrite(A0,HIGH);
digitalWrite(A1,HIGH);
digitalWrite(A2,HIGH);
digitalWrite(A3,HIGH);
digitalWrite(A4,HIGH);
digitalWrite(A5,HIGH);

digitalWrite(2,LOW);
digitalWrite(3,LOW);
digitalWrite(4,LOW);
digitalWrite(5,LOW);
digitalWrite(6,LOW);
digitalWrite(7,LOW);
}

void MotorDown()
{
digitalWrite(A0,LOW);
digitalWrite(A1,LOW);
digitalWrite(A2,LOW);
digitalWrite(A3,LOW);
digitalWrite(A4,LOW);
digitalWrite(A5,LOW);

digitalWrite(2,HIGH);
digitalWrite(3,HIGH);
digitalWrite(4,HIGH);
digitalWrite(5,HIGH);
digitalWrite(6,HIGH);
digitalWrite(7,HIGH);
}

/*
The following code is for the remote control. It handles the codes generated by the Apple remote
control except 0 has been designated for "repeat" and 255 designated as an error code. The current
Apple remote does not generate these codes.
*/
void loop()
{

while(digitalRead(REMOTEPIN)==LOW){
if((IRkey=getIRkey())==255){
// Do nothing
}
else if(IRkey==0){ // Repeat
IRkey=previousIRkey;
}
else { // New command
previousIRkey=IRkey;
}
switch(IRkey){
case 11: // 11 is the up key, we will use for volume up

{
Serial.println ("Up");
MotorUp();
delay(100);
MotorOff();
}
break;
case 13: // 13 is the down key, we will use for volume down

{
Serial.println ("Down");
MotorDown();
delay(100);
MotorOff();
}
break;
}
} // End of remote control code

} // End of loop()