Browse Source

2022-01-04

- Weiterentwicklung, um verschiedene Fernsteuerungen zu unterstützen, z.B. Pioneer und NEC
- Mehrfachauslösung wird nun in mehreren Stufen schneller, bei guter Response des einzelnen Tastendrucks (zumindest bei RC5/RC6 Remotes)
- RC5 und RC6-Fernsteuerungen werden nun mit eigener Funktion ausgewertet, dadurch weniger Verzögerung bei diesen Typen
- Handling für POWER-Button verbessert:
  - kurzer Tastendruck: keine Reaktion
  - kurz gedrückt halten: Windows-Taste (dient vA zum Aufwecken aus dem Standby)
  - etwas länger gedruckt halten: STANDBY
  - lang gedrückt halten: SHUTDOWN
- ​Handling für TaskSwitch-Taste NEU:
  - kurzer Tastendruck: Windows-Taste
  - etwas längerer Tastendruck: ALT+TAB, danach kann durch kurze Tastendrücke im ALT+TAB Dialog navigiert werden bzw. mit dem Steuerkreuz
  - langer Tastendruck: ALT+F4 wird gesendet
- viele kleine Verbesserungen
FloKra 2 years ago
parent
commit
c2f3fc286e

+ 20 - 0
CHANGELOG.md

@@ -0,0 +1,20 @@
+- 2022-01-04
+
+  - Weiterentwicklung, um verschiedene Fernsteuerungen zu unterstützen, z.B. Pioneer und NEC
+  - Mehrfachauslösung wird nun in mehreren Stufen schneller, bei guter Response des einzelnen Tastendrucks (zumindest bei RC5/RC6 Remotes)
+  - RC5 und RC6-Fernsteuerungen werden nun mit eigener Funktion ausgewertet, dadurch weniger Verzögerung bei diesen Typen
+  - Handling für POWER-Button verbessert: 
+    - kurzer Tastendruck: keine Reaktion
+    - kurz gedrückt halten: Windows-Taste (dient vA zum Aufwecken aus dem Standby)
+    - etwas länger gedruckt halten: STANDBY
+    - lang gedrückt halten: SHUTDOWN
+  - ​	Handling für TaskSwitch-Taste NEU:
+    - kurzer Tastendruck: Windows-Taste
+    - etwas längerer Tastendruck: ALT+TAB, danach kann durch kurze Tastendrücke im ALT+TAB Dialog navigiert werden bzw. mit dem Steuerkreuz
+    - langer Tastendruck: ALT+F4 wird gesendet
+  - viele kleine Verbesserungen
+
+  
+
+- 2021-11-22
+  initiale Version

+ 12 - 2
README.md

@@ -12,6 +12,16 @@ Daher habe ich mich dann entschieden, die Sondertasten einfach Tastenkombination
 
 Der Code ist simpel gestrickt und die Tastencodes meiner Fernsteuerung sind hard coded. Also eher ein prove-of-concept. 
 
+Update 01/2022:
+
+Unterstützung für RC5/RC6-Fernbedienungen verbessert, Power-Button Handling verbessert, TaskSwitch-Handling neu (ALT+TAB), bessere Unterstützung für verschiedene IR-Codes gleichzeitig, z.B. Pioneer und NEC. 
+
+Mehrfachauslösung wird nun in mehreren Stufen schneller, bei guter Response des einzelnen Tastendrucks (zumindest bei RC5/RC6 Remotes).
+
+[CHANGELOG](CHANGELOG.md)
+
+
+
 ![](ir-fb.jpg)
 
 
@@ -51,7 +61,7 @@ Aufbauend auf das Arduino IRrecvDumpV2 code example.
   - Steuerkreuz auf Pfeiltasten zur Menünavigation
 - die wichtigsten Tasten der Fernbedienung senden die naheliegenden Befehle, 
 - Sondertasten willkürlich festgelegte Tastenkombinationen, z.B. um mit AutoHotkey ein Programm zu starten, wenn die blaue Taste der Fernbedienung gedrückt wurde
-- Mehrfachauslösung bei gehaltener Taste unterdrücken (außer bei bestimmten Tasten)
+- Mehrfachauslösung bei gehaltener Taste unterdrücken (außer bei bestimmten Tasten), bei Tasten mit konfiguriertem Repeat wird die Rate in 2 Stufen schneller. 
 
 
 
@@ -117,7 +127,7 @@ IR-Code demzufolge: 0x7** oder 0xF** - ** siehe Tabelle
 
 ## AutoHotkey
 
-Um Aktionen auf die Tastenkombinationen der Sondertasten auszuführen verwende ich ein simples AHK-Script, welches ein weiteres AHK-Script startet. Details siehe [/AutoHotkey](/AutoHotkey)
+Um Aktionen auf die Tastenkombinationen der Sondertasten auszuführen verwende ich ein simples AHK-Script, welches ein weiteres AHK-Script startet. Details siehe [AutoHotkey](AutoHotkey)
 
 
 

File diff suppressed because it is too large
+ 517 - 777
src/IR-PC-HID-Remote/IR-PC-HID-Remote.ino


+ 44 - 0
src/IR-PC-HID-Remote/IRfunctions.ino

@@ -0,0 +1,44 @@
+//+=============================================================================
+// Display IR code
+//
+void  ircode (decode_results *results)
+{
+  if (useSerial) {
+    // Panasonic has an Address
+    if (results->decode_type == PANASONIC) {
+      Serial.print(results->address, HEX);
+      Serial.print(":");
+    }
+
+    // Print Code
+    Serial.print(results->value, HEX);
+  }
+}
+
+//+=============================================================================
+// Display encoding type
+//
+void  encoding (decode_results *results)
+{
+  if (useSerial) {
+    switch (results->decode_type) {
+      default:
+      case UNKNOWN:      Serial.print("UNKNOWN");       break ;
+      case NEC:          Serial.print("NEC");           break ;
+      case SONY:         Serial.print("SONY");          break ;
+      case RC5:          Serial.print("RC5");           break ;
+      case RC6:          Serial.print("RC6");           break ;
+      case DISH:         Serial.print("DISH");          break ;
+      case SHARP:        Serial.print("SHARP");         break ;
+      case JVC:          Serial.print("JVC");           break ;
+      case SANYO:        Serial.print("SANYO");         break ;
+      case MITSUBISHI:   Serial.print("MITSUBISHI");    break ;
+      case SAMSUNG:      Serial.print("SAMSUNG");       break ;
+      case LG:           Serial.print("LG");            break ;
+      case WHYNTER:      Serial.print("WHYNTER");       break ;
+      case AIWA_RC_T501: Serial.print("AIWA_RC_T501");  break ;
+      case PANASONIC:    Serial.print("PANASONIC");     break ;
+      case DENON:        Serial.print("DENON");         break ;
+    }
+  }
+}

+ 178 - 0
src/IR-PC-HID-Remote/handleButton.ino

@@ -0,0 +1,178 @@
+// Button handling for IR remotes using Pioneer, NEC, Sony... code
+// - These DO NOT send a toggle bit on every new button press, so it is difficult to differ hold and re-pressing
+// - On re-pressing this function will send the button again as fast as possible. It uses timers to filter out unintended multi triggering,
+//   so this can be a little bit delayed (RC5 and RC6 remotes are better in this case)
+// - If a button is held down, the remote will go on sending the same code every 100ms or so.
+//   This function will then, after some ignored codes, start with some slow repeats, and then switch to fast repeat rate (with the rate of the incoming codes).
+
+// special case Pioneer Protocol:
+// Pioneer Protocol transmits 2 different NEC 32bit codes on each keypress at once
+// and always with one repetition, so a short keypress sends 4 NEC codes
+// i.E.:
+//  NEC, 0xD52A34CB, 32
+//  NEC, 0xF50AF708, 32
+//  NEC, 0xD52A34CB, 32
+//  NEC, 0xF50AF708, 32
+// Each pair takes about 184ms -> 368ms for one button press.
+// On button hold the same codes are transmitted continuously.
+// So the shortest repeat time possible is about every 183ms - but ONLY when the button is held.
+// If the button is quickly short pressed it looks just like the same on receiver side.
+// As there is always at least 1 repetition, it is mandatory to filter these out, or a single
+// button press would always result in 2 events.
+
+
+uint8_t handleButton_lastSentKey;
+unsigned long handleButton_lastSentKey_begin;
+unsigned long handleButton_repeatedReceivedMillis;
+unsigned long handleButton_repeatMillis;
+
+#define HANDLEBUTTON_FIRST_REPEAT_INTERVAL 500  // minimum ms for the first repeat(s) to trigger an event. the actual value will be slightly more, as it will be only triggert on the next receive from the RC
+// so basically it must be a multiple of the time a transmit takes the RC
+
+#define HANDLEBUTTON_SLOW_REPEATS 2                     // how many slow repeats should occur before fast repeats are engaged
+
+
+
+
+#define HANDLEBUTTON_REPEAT_TIMEOUT_0 560         // should be slightly more than 3 transmits of the slowest RC used, so min. 560 for the Pioneer RC
+#define HANDLEBUTTON_MAX_REPEATED_RECEIVE_MS_0 200  // slightly more than one transmit, used to detect repetitions
+#define HANDLEBUTTON_AVOID_REPEAT_DELAY_0 200     // must be more than one transmits time or repeats could occur also on buttons configured as repeat=false
+
+
+#define HANDLEBUTTON_REPEAT_TIMEOUT_1 130         // should be slightly more than 3 transmits of the slowest RC used, so min. 560 for the Pioneer RC
+#define HANDLEBUTTON_MAX_REPEATED_RECEIVE_MS_1 120  // slightly more than one transmit, used to detect repetitions
+#define HANDLEBUTTON_AVOID_REPEAT_DELAY_1 100     // must be more than one transmits time or repeats could occur also on buttons configured as repeat=false
+
+
+#define HANDLEBUTTON_REPEAT_TIMEOUT_2 120         // should be slightly more than 3 transmits of the slowest RC used, so min. 560 for the Pioneer RC
+#define HANDLEBUTTON_MAX_REPEATED_RECEIVE_MS_2 110  // slightly more than one transmit, used to detect repetitions
+#define HANDLEBUTTON_AVOID_REPEAT_DELAY_2 100     // must be more than one transmits time or repeats could occur also on buttons configured as repeat=false
+
+unsigned int handleButton_lastKey_countRepeats = 0;
+
+
+
+void handleButton(uint8_t _key) {
+  handleButton(_key, false, 0);
+}
+
+void handleButton(uint8_t _key, bool _repeat) {
+  handleButton(_key, _repeat, 0);
+}
+
+void handleButton(uint8_t _key, bool _repeat, uint8_t _rcType) {
+  // _repeat: enable/disable repeat for this button - if false, holding the button will not trigger repeated events
+  // _rcType: 0 = RC code types without toggle bit as in RC5/RC6, therefore a higher delay is required to avoid repeated events at single button presses
+  //          1 = other (slower?) remote
+  //          2 = ...
+
+  unsigned int _minRepeatInterval = 0;
+
+  if (debug2) {
+    Serial.print(F("    loopTime="));
+    Serial.print((millis() - lastReceivedMillis));
+    Serial.println("ms");
+  }
+  lastReceivedMillis = millis();
+
+  unsigned int _handleButton_repeat_Timeout, _handleButton_maxRepeatedReceiveMs, _handleButton_avoidRepeatDelay;
+  if (_rcType == 1) {
+    _handleButton_repeat_Timeout = HANDLEBUTTON_REPEAT_TIMEOUT_1;
+    _handleButton_maxRepeatedReceiveMs = HANDLEBUTTON_MAX_REPEATED_RECEIVE_MS_1;
+    _handleButton_avoidRepeatDelay = HANDLEBUTTON_AVOID_REPEAT_DELAY_1;
+  }
+  else if (_rcType == 2) {
+    _handleButton_repeat_Timeout = HANDLEBUTTON_REPEAT_TIMEOUT_2;
+    _handleButton_maxRepeatedReceiveMs = HANDLEBUTTON_MAX_REPEATED_RECEIVE_MS_2;
+    _handleButton_avoidRepeatDelay = HANDLEBUTTON_AVOID_REPEAT_DELAY_2;
+  }
+  else { // PIONEER o.A.
+    _handleButton_repeat_Timeout = HANDLEBUTTON_REPEAT_TIMEOUT_0;
+    _handleButton_maxRepeatedReceiveMs = HANDLEBUTTON_MAX_REPEATED_RECEIVE_MS_0;
+    _handleButton_avoidRepeatDelay = HANDLEBUTTON_AVOID_REPEAT_DELAY_0;
+  }
+
+  if (_repeat) _minRepeatInterval = 1;
+
+  // if this is the POWER button, always use repeat, regardless of function call variable
+  if (_key == BTN_POWER) _minRepeatInterval = 1;
+
+  if (_minRepeatInterval > 0) {
+    if (handleButton_lastSentKey == _key && (millis() - handleButton_repeatMillis) < _handleButton_repeat_Timeout && (millis() - handleButton_repeatedReceivedMillis) < _handleButton_maxRepeatedReceiveMs) {
+      if (debug) {
+        Serial.print(F("    R="));
+        Serial.print(handleButton_lastKey_countRepeats + 1);
+        Serial.print(F(", handleButton_lastSentKey_begin="));
+        Serial.print(millis() - handleButton_lastSentKey_begin);
+        Serial.print(F("ms, handleButton_repeatMillis="));
+        Serial.print(millis() - handleButton_repeatMillis);
+        Serial.print(F("ms, handleButton_repeatedReceivedMillis="));
+        Serial.print(millis() - handleButton_repeatedReceivedMillis);
+        Serial.println(F("ms"));
+      }
+
+      if ( (millis() - handleButton_repeatMillis) >= HANDLEBUTTON_FIRST_REPEAT_INTERVAL) {
+        // slow repeats
+        sendKey(_key);
+        if (debug2) {
+          Serial.print(F("    SLOW REPEAT  "));
+        }
+        
+        handleButton_repeatMillis = millis();
+        handleButton_lastKey_countRepeats++;
+      }
+      else if ( handleButton_lastKey_countRepeats >= HANDLEBUTTON_SLOW_REPEATS && (millis() - handleButton_repeatMillis) >= _minRepeatInterval) {
+        // fast repeats
+        sendKey(_key);
+        if (debug2) {
+          Serial.print(F("    FAST REPEAT  "));
+        }
+        
+        handleButton_repeatMillis = millis();
+        handleButton_lastKey_countRepeats++;
+      }
+      else {
+        // waiting to reach HANDLEBUTTON_FIRST_REPEAT_INTERVAL
+        //handleButton_repeatMillis = millis();
+        if (debug2) {
+          Serial.println(F("    REPEAT WAITING  "));
+        }
+      }
+    }
+    else {
+      // this is the initial button press
+      handleButton_lastSentKey_begin = millis();
+      handleButton_repeatMillis = millis();
+      handleButton_lastSentKey = _key;
+      handleButton_lastKey_countRepeats = 0;
+
+      sendKey(_key);
+      if (debug2) {
+        Serial.print(F("    INITIAL  "));
+      }
+      if (debug) {
+        Serial.print(F("    R="));
+        Serial.println(handleButton_lastKey_countRepeats);
+      }
+    }
+
+    if (handleButton_lastSentKey == _key) {
+      handleButton_repeatedReceivedMillis = millis();
+      if (debug2) {
+        Serial.println(F("    REPRECV  "));
+      }
+    }
+  }
+  else if (!_repeat) {
+    // only send once
+    if ( handleButton_lastSentKey != _key || (handleButton_lastSentKey == _key && (millis() - handleButton_lastSentKey_begin) > _handleButton_avoidRepeatDelay)) {
+      sendKey(_key);
+      if (debug2) Serial.println(F("    ONCE"));
+      //handleButton_lastSentKey_begin = millis();
+      handleButton_lastSentKey = _key;
+    }
+    if (handleButton_lastSentKey == _key) {
+      handleButton_lastSentKey_begin = millis();
+    }
+  }
+}

+ 85 - 0
src/IR-PC-HID-Remote/handleButton_RCx.ino

@@ -0,0 +1,85 @@
+// Button handling for RC5 and RC6 Remotes
+// - these send a toggle bit on every new button press, so it is easy to differ hold and re-pressing
+// - on re-pressing this function will send the button again immediately. it uses the _currPrefix which has to be passed by the calling function
+//   and which must be only the single byte of the RCx code containing the toggle bit
+// - if a button is held down, the remote will not toggle this bit and goes on sending the same code every 100ms or so
+//   this function will then, after some ignored codes, start with some slow repeats, then switch to medium repeat rate and finally repeat with the rate of the incoming codes
+
+// configuration variables for RCx button handling
+uint8_t handleButton_RCx_ignoreFirstRepeats = 4;          // repeats to ignore when a button is beeing held (the first one is always sent)
+uint8_t handleButton_RCx_slowRepeats = 4;                 // amount of slow repeats to send
+uint8_t handleButton_RCx_slowRepeatsNumFastRepeats = 4;   // how many fast repeats (=sending rate of the RC remote) represent one slow repeat?
+uint8_t handleButton_RCx_mediumRepeats = 5;               // amount of medium repeats to send
+uint8_t handleButton_RCx_mediumRepeatsNumFastRepeats = 2; // how many fast repeats (=sending rate of the RC remote) represent one slow repeat?
+
+// global variables for RCx button handling
+unsigned int handleButton_RCx_btnRepeatsCounter;
+uint8_t handleButton_RCx_lastButton, handleButton_RCx_lastPrefix;
+uint8_t handleButton_RCx_btnSlowRepeatsCounter, handleButton_RCx_btnSlowRepeatsCounter2;
+uint8_t handleButton_RCx_btnMediumRepeatsCounter, handleButton_RCx_btnMediumRepeatsCounter2;
+
+// the function
+void handleButton_RCx(uint8_t _btn, bool _repeat, uint8_t _currPrefix) {
+  if (_btn != handleButton_RCx_lastButton || (_btn == handleButton_RCx_lastButton) && (_currPrefix != handleButton_RCx_lastPrefix)) {
+    if (debug2) {
+      Serial.println(F("RCx BUTTON INITIAL"));
+    }
+
+    sendKey(_btn);
+    handleButton_RCx_btnRepeatsCounter = 0;
+    handleButton_RCx_btnSlowRepeatsCounter = 0;
+    handleButton_RCx_btnMediumRepeatsCounter = 0;
+  }
+  else if ((_btn == handleButton_RCx_lastButton) && (_currPrefix == handleButton_RCx_lastPrefix) && _repeat) {
+    if (handleButton_RCx_btnRepeatsCounter >= handleButton_RCx_ignoreFirstRepeats) {
+      if (handleButton_RCx_btnSlowRepeatsCounter == 0) {
+        handleButton_RCx_btnSlowRepeatsCounter++;
+        handleButton_RCx_btnSlowRepeatsCounter2 = 0;
+        if (debug2) {
+          Serial.print(F("RCx BUTTON REPEAT SLOW #"));
+          Serial.println(handleButton_RCx_btnSlowRepeatsCounter);
+        }
+        sendKey(_btn);
+      }
+      else if (handleButton_RCx_btnSlowRepeatsCounter < handleButton_RCx_slowRepeats) {
+        if (handleButton_RCx_btnSlowRepeatsCounter2 < handleButton_RCx_slowRepeatsNumFastRepeats) {
+          handleButton_RCx_btnSlowRepeatsCounter2++;
+        }
+        else {
+          handleButton_RCx_btnSlowRepeatsCounter++;
+          handleButton_RCx_btnSlowRepeatsCounter2 = 0;
+          if (debug2) {
+            Serial.print(F("RCx BUTTON REPEAT SLOW #"));
+            Serial.println(handleButton_RCx_btnSlowRepeatsCounter);
+          }
+          sendKey(_btn);
+        }
+      }
+      else if (handleButton_RCx_btnMediumRepeatsCounter <= handleButton_RCx_mediumRepeats) {
+        if (handleButton_RCx_btnMediumRepeatsCounter2 < handleButton_RCx_mediumRepeatsNumFastRepeats) {
+          handleButton_RCx_btnMediumRepeatsCounter2++;
+        }
+        else {
+          handleButton_RCx_btnMediumRepeatsCounter++;
+          handleButton_RCx_btnMediumRepeatsCounter2 = 0;
+          if (debug2) {
+            Serial.print(F("RCx BUTTON REPEAT MEDIUM #"));
+            Serial.println(handleButton_RCx_btnMediumRepeatsCounter);
+          }
+
+          sendKey(_btn);
+        }
+      }
+      else {
+        if (debug2) {
+          Serial.println(F("RCx BUTTON REPEAT FAST"));
+        }
+        sendKey(_btn);
+      }
+    }
+    handleButton_RCx_btnRepeatsCounter++;
+
+  }
+  handleButton_RCx_lastPrefix = _currPrefix;
+  handleButton_RCx_lastButton = _btn;
+}

+ 96 - 0
src/IR-PC-HID-Remote/handlePowerButton.ino

@@ -0,0 +1,96 @@
+unsigned long powerButton_triggeredMillis = 0;
+unsigned long powerButton_lastPressedMillis = 0;
+unsigned long powerButton_disableMillis = 0;
+unsigned int powerButton_execLoopAfter = 250;
+unsigned int powerButton_disableAfterAction = 1500;
+unsigned int powerButton_pressDurationMin_shortHoldAction = 250;
+unsigned int powerButton_pressDurationMax_shortHoldAction = 600;
+unsigned int powerButton_pressDurationMin_sleep = 800;
+unsigned int powerButton_pressDurationMax_sleep = 2000;
+unsigned int powerButton_pressDuration_shutdown = 3000;
+
+void handlePowerButton() {
+  powerButton_lastPressedMillis = millis();
+  if (powerButton_disableMillis > 0 ) powerButton_disableMillis = millis();
+
+  if (powerButton_disableMillis == 0) {
+    if (powerButton_triggeredMillis == 0) {
+      powerButton_triggeredMillis = millis();
+      if(useSerial) Serial.println(F("POWER INITIALLY PRESSED"));
+      //Serial.println(F("sending ALT+F4"));
+      //BootKeyboard.press(KEY_LEFT_ALT);
+      //BootKeyboard.press(KEY_F4);
+      //delay(50);
+      //BootKeyboard.releaseAll();
+    }
+    else if ( (millis() - powerButton_triggeredMillis) >= powerButton_pressDuration_shutdown) {
+      // LONG PRESSED
+      powerButton_triggeredMillis = 0;
+      powerButton_lastPressedMillis = 0;
+      powerButton_disableMillis = millis();
+      if (useSerial) {
+        Serial.println(F("POWER LONG HOLD"));
+        Serial.println(F("sending SYSTEM_POWER_DOWN"));
+      }
+      if (sendHID) System.write(SYSTEM_POWER_DOWN);
+    }
+  }
+}
+
+void handlePowerButton_loop() {
+  // handle Power Button short press
+
+  if (powerButton_triggeredMillis > 0) {
+    if (powerButton_lastPressedMillis > 0) {
+      if ((millis() - powerButton_lastPressedMillis) > powerButton_execLoopAfter) {
+        unsigned int tDelta = powerButton_lastPressedMillis - powerButton_triggeredMillis;
+
+        if (debug) {
+          Serial.print(F("POWER held for "));
+          Serial.print(tDelta);
+          Serial.println("ms");
+        }
+
+        if (tDelta >= powerButton_pressDurationMin_sleep && tDelta <= powerButton_pressDurationMax_sleep) {
+          // SHORT PRESSED
+          if (useSerial) {
+            Serial.println(F("POWER SHORT HOLD"));
+            Serial.println(F("sending SYSTEM_SLEEP"));
+          }
+          if (sendHID) System.write(SYSTEM_SLEEP);
+          powerButton_triggeredMillis = 0;
+          powerButton_lastPressedMillis = 0;
+          powerButton_disableMillis = millis();
+        }
+        else if (tDelta >= powerButton_pressDurationMin_shortHoldAction && tDelta < powerButton_pressDurationMax_shortHoldAction) {
+          // VERY SHORT HOLD
+          if (useSerial) {
+            Serial.println(F("POWER VERY SHORT HOLD"));
+            Serial.println("sending WIN KEY");
+          }
+          if (sendHID) BootKeyboard.write(KEY_LEFT_WINDOWS);
+
+          powerButton_triggeredMillis = 0;
+          powerButton_lastPressedMillis = 0;
+          //powerButton_disableMillis = millis();
+        }
+        else {
+          // button was pressed too long for "SHORT PRESSED"
+          if (useSerial) {
+            Serial.println(F("POWER BUTTON CANCELLED"));
+          }
+          powerButton_triggeredMillis = 0;
+          powerButton_lastPressedMillis = 0;
+        }
+      }
+    }
+  }
+
+  if (powerButton_disableMillis > 0) {
+    if ((millis() - powerButton_disableMillis) > powerButton_disableAfterAction) {
+      powerButton_triggeredMillis = 0;
+      powerButton_lastPressedMillis = 0;
+      powerButton_disableMillis = 0;
+    }
+  }
+}

+ 140 - 0
src/IR-PC-HID-Remote/handleTaskSwButton.ino

@@ -0,0 +1,140 @@
+unsigned long taskSwButton_triggeredMillis = 0;
+unsigned long taskSwButton_lastPressedMillis = 0;
+unsigned long taskSwButton_disableMillis = 0;
+unsigned long taskSwButton_tabKeyDownMillis = 0;
+unsigned long taskSwButton_winKeyDownMillis = 0;
+unsigned int taskSwButton_execLoopAfter = 250;
+unsigned int taskSwButton_disableAfterAction = 1000;
+unsigned int taskSwButton_pressDurationMin_veryShortHold = 1;
+unsigned int taskSwButton_pressDurationMax_veryShortHold = 250;
+unsigned int taskSwButton_pressDurationMin_shortHold = 350;
+unsigned int taskSwButton_pressDurationMax_shortHold = 900;
+unsigned int taskSwButton_pressDuration_longHold = 1000;
+
+unsigned int taskSwButton_tabKey_Up_After = 2000;
+//unsigned int taskSwButton_winKey_Up_After = 3000;
+
+void handleTaskSwitchButton() {
+  taskSwButton_lastPressedMillis = millis();
+  if (taskSwButton_disableMillis > 0 ) taskSwButton_disableMillis = millis();
+
+  if (taskSwButton_disableMillis == 0) {
+    if (taskSwButton_triggeredMillis == 0) {
+      taskSwButton_triggeredMillis = millis();
+      if(useSerial) Serial.println(F("TASKSWITCH INITIALLY PRESSED"));
+
+      if ( taskSwButton_tabKeyDownMillis > 0 ) {
+        taskSwButton_tabKeyDownMillis = millis();
+        BootKeyboard.write(KEY_TAB);
+      }
+      //      else if ( taskSwButton_winKeyDownMillis > 0 ) {
+      //        taskSwButton_winKeyDownMillis = millis();
+      //        BootKeyboard.write(KEY_ESC);
+      //      }
+    }
+    else if ( (millis() - taskSwButton_triggeredMillis) >= taskSwButton_pressDuration_longHold) {
+      // LONG PRESSED
+      taskSwButton_triggeredMillis = 0;
+      taskSwButton_lastPressedMillis = 0;
+      taskSwButton_disableMillis = millis();
+      if (useSerial) {
+        Serial.println(F("TASKSWITCH LONG HOLD"));
+      }
+      if (sendHID) {
+        if(useSerial) Serial.println(F("sending ALT+F4"));
+        BootKeyboard.press(KEY_LEFT_ALT);
+        BootKeyboard.press(KEY_F4);
+        delay(50);
+        BootKeyboard.releaseAll();
+      }
+    }
+  }
+}
+
+void handleTaskSwitchButton_extendAltTabTimeout() {
+  if(taskSwButton_tabKeyDownMillis > 0) taskSwButton_tabKeyDownMillis = millis();
+}
+
+void handleTaskSwitchButton_loop() {
+  // handle Power Button short press
+
+  if (taskSwButton_triggeredMillis > 0) {
+    if (taskSwButton_lastPressedMillis > 0) {
+      if ((millis() - taskSwButton_lastPressedMillis) > taskSwButton_execLoopAfter) {
+        unsigned int tDelta;
+        if(taskSwButton_lastPressedMillis == taskSwButton_triggeredMillis) tDelta = 1;
+        else tDelta = taskSwButton_lastPressedMillis - taskSwButton_triggeredMillis;
+
+        if (debug) {
+          Serial.print(F("TASKSWITCH held for "));
+          Serial.print(tDelta);
+          Serial.println("ms");
+        }
+
+        if (tDelta >= taskSwButton_pressDurationMin_shortHold && tDelta <= taskSwButton_pressDurationMax_shortHold) {
+          // SHORT PRESSED
+          if (useSerial) {
+            Serial.println(F("TASKSWITCH SHORT HOLD"));
+            Serial.println("sending ALT+TAB");
+          }
+          if (sendHID) {
+            // sending ALT+TAB (leaving TAB engaged
+            BootKeyboard.press(KEY_LEFT_ALT);
+            BootKeyboard.write(KEY_TAB);
+          }
+          taskSwButton_tabKeyDownMillis = millis();
+
+          taskSwButton_triggeredMillis = 0;
+          taskSwButton_lastPressedMillis = 0;
+          //taskSwButton_disableMillis = millis();
+        }
+        else if (tDelta >= taskSwButton_pressDurationMin_veryShortHold && tDelta < taskSwButton_pressDurationMax_veryShortHold) {
+          // VERY SHORT HOLD
+          if (useSerial) Serial.println(F("POWER VERY SHORT HOLD"));
+
+          //if ((millis() - taskSwButton_winKeyDownMillis) > taskSwButton_winKey_Up_After) {
+          if (taskSwButton_tabKeyDownMillis == 0) {
+            if (useSerial) Serial.println("sending WIN-KEY");
+            if (sendHID) {
+              BootKeyboard.write(KEY_LEFT_WINDOWS);
+            }
+            //taskSwButton_winKeyDownMillis = millis();
+          }
+
+          taskSwButton_triggeredMillis = 0;
+          taskSwButton_lastPressedMillis = 0;
+          //taskSwButton_disableMillis = millis();
+        }
+        else {
+          // button was pressed too long for "SHORT PRESSED"
+          if (useSerial) {
+            Serial.println(F("TASKSWITCH BUTTON CANCELLED"));
+          }
+          taskSwButton_triggeredMillis = 0;
+          taskSwButton_lastPressedMillis = 0;
+        }
+      }
+    }
+  }
+
+  if (taskSwButton_disableMillis > 0) {
+    if ((millis() - taskSwButton_disableMillis) > taskSwButton_disableAfterAction) {
+      taskSwButton_triggeredMillis = 0;
+      taskSwButton_lastPressedMillis = 0;
+      taskSwButton_disableMillis = 0;
+    }
+  }
+
+  if (taskSwButton_tabKeyDownMillis > 0) {
+    if ((millis() - taskSwButton_tabKeyDownMillis) > taskSwButton_tabKey_Up_After) {
+      taskSwButton_tabKeyDownMillis = 0;
+      if (sendHID) BootKeyboard.releaseAll();
+    }
+  }
+
+  //  if (taskSwButton_winKeyDownMillis > 0) {
+  //    if ((millis() - taskSwButton_winKeyDownMillis) > taskSwButton_winKey_Up_After) {
+  //      taskSwButton_winKeyDownMillis = 0;
+  //    }
+  //  }
+}

+ 508 - 0
src/IR-PC-HID-Remote/sendKeys.ino

@@ -0,0 +1,508 @@
+void printKey() {
+  if (useSerial) {
+    Serial.print(F("KEY_"));
+  }
+}
+void printMedia() {
+  if (useSerial) {
+    Serial.print(F("MEDIA_"));
+  }
+}
+void printArrow() {
+  if (useSerial) {
+    Serial.print(F("ARROW_"));
+  }
+}
+
+
+
+
+
+
+void sendKey(uint8_t _key) {
+  if(debug) Serial.println(F("SENDING KEY"));
+  switch (_key) {
+    //    case BTN_POWER:
+    //      //handlePowerButton();
+    //      if (useSerial) {
+    //        printKey();
+    //        Serial.println(F("POWER"));
+    //      }
+    //      break;
+    case BTN_OK_ENTER:
+      if (sendHID) BootKeyboard.write(KEY_ENTER);
+      if (useSerial) {
+        printKey();
+        Serial.println(F("OK_ENTER"));
+      }
+      break;
+    case BTN_UP:
+      if (sendHID) BootKeyboard.write(KEY_UP_ARROW);
+      if (useSerial) {
+        printKey();
+        Serial.println(F("UP"));
+      }
+      handleTaskSwitchButton_extendAltTabTimeout();
+      break;
+    case BTN_DOWN:
+      if (sendHID) BootKeyboard.write(KEY_DOWN_ARROW);
+      if (useSerial) {
+        printKey();
+        Serial.println(F("DOWN"));
+      }
+      handleTaskSwitchButton_extendAltTabTimeout();
+      break;
+    case BTN_RIGHT:
+      if (sendHID) BootKeyboard.write(KEY_RIGHT_ARROW);
+      if (useSerial) {
+        printKey();
+        Serial.println(F("RIGHT"));
+      }
+      handleTaskSwitchButton_extendAltTabTimeout();
+      break;
+    case BTN_LEFT:
+      if (sendHID) BootKeyboard.write(KEY_LEFT_ARROW);
+      if (useSerial) {
+        printKey();
+        Serial.println(F("LEFT"));
+      }
+      handleTaskSwitchButton_extendAltTabTimeout();
+      break;
+    case BTN_PLAY:
+      if (sendHID) Consumer.write(MEDIA_PLAY_PAUSE);
+      if (useSerial) {
+        printKey();
+        printMedia();
+        Serial.println(F("PLAY"));
+      }
+      break;
+    case BTN_PAUSE:
+      if (sendHID) Consumer.write(MEDIA_PLAY_PAUSE);
+      if (useSerial) {
+        printKey();
+        printMedia();
+        Serial.println(F("PAUSE"));
+      }
+      break;
+    case BTN_STOP:
+      if (sendHID) Consumer.write(MEDIA_STOP);
+      if (useSerial) {
+        printKey();
+        printMedia();
+        Serial.println(F("STOP"));
+      }
+      break;
+    case BTN_REC:
+      if (useSerial) {
+        printKey();
+        Serial.println(F("REC"));
+      }
+      break;
+    case BTN_REWD:
+      if (sendHID) Consumer.write(MEDIA_REWIND);
+      if (useSerial) {
+        printKey();
+        printMedia();
+        Serial.println(F("REWD"));
+      }
+      break;
+    case BTN_FFWD:
+      if (sendHID) Consumer.write(MEDIA_FAST_FORWARD);
+      if (useSerial) {
+        printKey();
+        printMedia();
+        Serial.println(F("FFWD"));
+      }
+      break;
+    case BTN_PREV:
+      if (sendHID) Consumer.write(MEDIA_PREVIOUS);
+      if (useSerial) {
+        printKey();
+        printMedia();
+        Serial.println(F("PREV"));
+      }
+      break;
+    case BTN_NEXT:
+      if (sendHID) Consumer.write(MEDIA_NEXT);
+      if (useSerial) {
+        printKey();
+        printMedia();
+        Serial.println(F("NEXT"));
+      }
+      break;
+    case BTN_BACK:
+      if (sendHID) BootKeyboard.write(KEY_BACKSPACE);
+      if (useSerial) {
+        printKey();
+        Serial.println(F("BACK"));
+      }
+      break;
+    case BTN_MENU:
+      if (sendHID) BootKeyboard.write(KEY_ESC);
+      if (useSerial) {
+        printKey();
+        Serial.println(F("MENU"));
+      }
+      break;
+    case BTN_HOME:
+      if (sendHID) BootKeyboard.write(KEY_LEFT_WINDOWS);
+      if (useSerial) {
+        printKey();
+        Serial.println(F("HOME"));
+      }
+      break;
+    case BTN_RED:
+      if (sendHID) {
+        BootKeyboard.press(KEY_LEFT_CTRL);
+        BootKeyboard.press(KEY_LEFT_ALT);
+        BootKeyboard.press(KEY_LEFT_WINDOWS);
+        BootKeyboard.press(KEY_F7);
+        delay(50);
+        BootKeyboard.releaseAll();
+      }
+      if (useSerial) {
+        printKey();
+        Serial.println(F("RED"));
+        Serial.println("sending CTRL+ALT+WIN+F7");
+      }
+      break;
+    case BTN_GREEN:
+      if (sendHID) {
+        BootKeyboard.press(KEY_LEFT_CTRL);
+        BootKeyboard.press(KEY_LEFT_ALT);
+        BootKeyboard.press(KEY_LEFT_WINDOWS);
+        BootKeyboard.press(KEY_F8);
+        delay(50);
+        BootKeyboard.releaseAll();
+      }
+      if (useSerial) {
+        printKey();
+        Serial.println(F("GREEN"));
+        Serial.println(F("sending CTRL+ALT+WIN+F8"));
+      }
+      break;
+    case BTN_YELLOW:
+      if (sendHID) {
+        BootKeyboard.press(KEY_LEFT_CTRL);
+        BootKeyboard.press(KEY_LEFT_ALT);
+        BootKeyboard.press(KEY_LEFT_WINDOWS);
+        BootKeyboard.press(KEY_F9);
+        delay(50);
+        BootKeyboard.releaseAll();
+      }
+      if (useSerial) {
+        printKey();
+        Serial.println(F("YELLOW"));
+        Serial.println(F("sending CTRL+ALT+WIN+F9"));
+      }
+      break;
+    case BTN_BLUE:
+      if (sendHID) {
+        BootKeyboard.press(KEY_LEFT_CTRL);
+        BootKeyboard.press(KEY_LEFT_ALT);
+        BootKeyboard.press(KEY_LEFT_WINDOWS);
+        BootKeyboard.press(KEY_F10);
+        delay(50);
+        BootKeyboard.releaseAll();
+      }
+      if (useSerial) {
+        printKey();
+        Serial.println(F("BLUE"));
+        Serial.println("sending CTRL+ALT+WIN+F10");
+      }
+      break;
+    case BTN_STATUS:
+      if (useSerial) {
+        printKey();
+        Serial.println(F("STATUS"));
+      }
+      break;
+    case BTN_RETURN:
+      if (useSerial) {
+        printKey();
+        Serial.println(F("RETURN"));
+      }
+      break;
+    case BTN_SETUP:
+      if (useSerial) {
+        printKey();
+        Serial.println(F("SETUP"));
+      }
+      break;
+    case BTN_GUIDE:
+      if (sendHID) {
+        BootKeyboard.press(KEY_LEFT_CTRL);
+        delay(2);
+        BootKeyboard.press(KEY_LEFT_ALT);
+        delay(2);
+        BootKeyboard.press(KEY_LEFT_WINDOWS);
+        delay(2);
+        BootKeyboard.press(KEY_F5);
+        delay(20);
+        BootKeyboard.releaseAll();
+      }
+      if (useSerial) {
+        printKey();
+        Serial.println(F("GUIDE"));
+        Serial.println(F("sending CTRL+ALT+WIN+F5"));
+      }
+      break;
+    case BTN_RADIO:
+      if (sendHID) {
+        BootKeyboard.press(KEY_LEFT_CTRL);
+        delay(2);
+        BootKeyboard.press(KEY_LEFT_ALT);
+        delay(2);
+        BootKeyboard.press(KEY_LEFT_WINDOWS);
+        delay(2);
+        BootKeyboard.press(KEY_F6);
+        delay(20);
+        BootKeyboard.releaseAll();
+      }
+      if (useSerial) {
+        printKey();
+        Serial.println(F("RADIO"));
+        Serial.println(F("sending CTRL+ALT+WIN+F6"));
+      }
+      break;
+    case BTN_PREVCH:
+      if (useSerial) {
+        printKey();
+        Serial.println(F("PREVCH"));
+      }
+      break;
+    case BTN_CONTEXT:
+      if (useSerial) {
+        printKey();
+        Serial.println(F("CONTEXT"));
+      }
+      BootKeyboard.write(KEY_C);
+      break;
+    case BTN_INFO:
+      if (useSerial) {
+        printKey();
+        Serial.println(F("INFO"));
+      }
+      BootKeyboard.write(KEY_I);
+      break;
+    case BTN_CH_DOWN:
+      if (sendHID) {
+        BootKeyboard.write(KEY_PAGE_DOWN);
+        //BootKeyboard.releaseAll();
+      }
+      if (useSerial) {
+        printKey();
+        Serial.println(F("CH_DOWN"));
+      }
+      break;
+    case BTN_CH_UP:
+      if (sendHID) {
+        BootKeyboard.write(KEY_PAGE_UP);
+        //BootKeyboard.releaseAll();
+      }
+      if (useSerial) {
+        printKey();
+        Serial.println(F("CH_UP"));
+      }
+      break;
+    case BTN_PAGE_DOWN:
+      if (sendHID) {
+        BootKeyboard.write(KEY_PAGE_DOWN);
+        //BootKeyboard.releaseAll();
+      }
+      if (useSerial) {
+        printKey();
+        Serial.println(F("PAGE_DOWN"));
+      }
+      break;
+    case BTN_PAGE_UP:
+      if (sendHID) {
+        BootKeyboard.write(KEY_PAGE_UP);
+        //BootKeyboard.releaseAll();
+      }
+      if (useSerial) {
+        printKey();
+        Serial.println(F("PAGE_UP"));
+      }
+      break;
+    case BTN_VOL_DOWN:
+      if (sendHID) Consumer.write(MEDIA_VOLUME_DOWN);
+      if (useSerial) {
+        printKey();
+        printMedia();
+        Serial.println(F("VOLUME_DOWN"));
+      }
+      break;
+    case BTN_VOL_UP:
+      if (sendHID) Consumer.write(MEDIA_VOLUME_UP);
+      if (useSerial) {
+        printKey();
+        printMedia();
+        Serial.println(F("VOLUME_UP"));
+      }
+      break;
+    case BTN_MUTE:
+      if (sendHID) Consumer.write(MEDIA_VOLUME_MUTE);
+      if (useSerial) {
+        printKey();
+        printMedia();
+        Serial.println(F("VOLUME_MUTE"));
+      }
+      break;
+    case BTN_TV:
+      if (sendHID) {
+        BootKeyboard.press(KEY_LEFT_CTRL);
+        delay(2);
+        BootKeyboard.press(KEY_LEFT_WINDOWS);
+        delay(2);
+        BootKeyboard.press(KEY_LEFT_ALT);
+        delay(2);
+        BootKeyboard.press(KEY_F1);
+        delay(20);
+        BootKeyboard.releaseAll();
+      }
+      if (useSerial) {
+        printKey();
+        Serial.println(F("TV"));
+        Serial.println(F("sending CTRL+ALT+WIN+F1"));
+      }
+      break;
+    case BTN_VIDEOS:
+      if (sendHID) {
+        BootKeyboard.press(KEY_LEFT_CTRL);
+        delay(2);
+        BootKeyboard.press(KEY_LEFT_ALT);
+        delay(2);
+        BootKeyboard.press(KEY_LEFT_WINDOWS);
+        delay(2);
+        BootKeyboard.press(KEY_F2);
+        delay(20);
+        BootKeyboard.releaseAll();
+      }
+      if (useSerial) {
+        printKey();
+        Serial.println(F("VIDEOS"));
+        Serial.println(F("sending CTRL+ALT+WIN+F2"));
+      }
+      break;
+    case BTN_MUSIC:
+      if (sendHID) {
+        BootKeyboard.press(KEY_LEFT_CTRL);
+        delay(2);
+        BootKeyboard.press(KEY_LEFT_ALT);
+        delay(2);
+        BootKeyboard.press(KEY_LEFT_WINDOWS);
+        delay(2);
+        BootKeyboard.press(KEY_F3);
+        delay(20);
+        BootKeyboard.releaseAll();
+      }
+      if (useSerial) {
+        printKey();
+        Serial.println(F("MUSIC"));
+        Serial.println(F("sending CTRL+ALT+WIN+F3"));
+      }
+      break;
+    case BTN_PICTURES:
+      if (sendHID) {
+        BootKeyboard.press(KEY_LEFT_CTRL);
+        delay(2);
+        BootKeyboard.press(KEY_LEFT_WINDOWS);
+        delay(2);
+        BootKeyboard.press(KEY_LEFT_ALT);
+        delay(2);
+        BootKeyboard.press(KEY_F4);
+        delay(20);
+        BootKeyboard.releaseAll();
+      }
+      if (useSerial) {
+        printKey();
+        Serial.println(F("PICTURES"));
+        Serial.println(F("sending CTRL+ALT+WIN+F4"));
+      }
+      break;
+    case BTN_STAR:
+      if (useSerial) {
+        printKey();
+        Serial.println(F("STAR"));
+      }
+      break;
+    case BTN_HASH:
+      if (useSerial) {
+        printKey();
+        Serial.println(F("HASH"));
+      }
+      break;
+
+    case BTN_0:
+      if (sendHID) BootKeyboard.write('0');
+      if (useSerial) {
+        printKey();
+        Serial.println("0");
+      }
+      break;
+    case BTN_1:
+      if (sendHID) BootKeyboard.write('1');
+      if (useSerial) {
+        printKey();
+        Serial.println("1");
+      }
+      break;
+    case BTN_2:
+      if (sendHID) BootKeyboard.write('2');
+      if (useSerial) {
+        printKey();
+        Serial.println("2");
+      }
+      break;
+    case BTN_3:
+      if (sendHID) BootKeyboard.write('3');
+      if (useSerial) {
+        printKey();
+        Serial.println("3");
+      }
+      break;
+    case BTN_4:
+      if (sendHID) BootKeyboard.write('4');
+      if (useSerial) {
+        printKey();
+        Serial.println("4");
+      }
+      break;
+    case BTN_5:
+      if (sendHID) BootKeyboard.write('5');
+      if (useSerial) {
+        printKey();
+        Serial.println("5");
+      }
+      break;
+    case BTN_6:
+      if (sendHID) BootKeyboard.write('6');
+      if (useSerial) {
+        printKey();
+        Serial.println("6");
+      }
+      break;
+    case BTN_7:
+      if (sendHID) BootKeyboard.write('7');
+      if (useSerial) {
+        printKey();
+        Serial.println("7");
+      }
+      break;
+    case BTN_8:
+      if (sendHID) BootKeyboard.write('8');
+      if (useSerial) {
+        printKey();
+        Serial.println("8");
+      }
+      break;
+    case BTN_9:
+      if (sendHID) BootKeyboard.write('9');
+      if (useSerial) {
+        printKey();
+        Serial.println("9");
+      }
+      break;
+  }
+}

Some files were not shown because too many files changed in this diff