IR remote to control Kodi running on RPI using arduino pro micro

Hi one and all,

I could really use some help to get this project over the finish line.

To control Kodi (LibreElec 9.0.2) running on a RPI 3, I'm using a 5V arduino pro micro to map IR remote control codes to HID usb keyboard keys.
I only need to control Kodi for video playback and general Kodi settings.

It is baffling because most keys are working perfectly but only two (perhaps more) are not.

Any advice would be most (very) welcome!

Commands that work:
Up, down, right, left, enter (return), escape, volume up/down, pause.

Commands that do not work:
Context menu (sub menu to play, rename, delete, etc) and stop play.
I only need the context menu as stop play can be done by bringing up the play controls with Enter then navigating to stop icon.

Notes:
Plugging a usb keyboard and pressing 'C' brings up the context menu. Pressing 'X' stops a playing track.
In test mode the remote context menu is in fact sending a 'C' and the stop is sending a 'X'.
Pro micro has on board ATmega32U4 at 16mHz.
Code is compiled using IDE v1.8.5.
RPI is powered by external supply which also powers the pro micro via usb connector.
IR receiver is powered from the 5V Vcc.
Video files are mp4 located on a flash drive plugged into RPI usb port.

Presumably Keyboard library (using the latest version) function write(key) is outputting something other than a standard keyboard.

The code:

/*


Keyboard Player controls ref: https://kodi.wiki/view/Remote_controls.
Key Action
--- ------------------------------------------------------------
M Menu (sidebar menu on the default skin) or
     Move (File manager).
C Contextual menu
 Playlist.
X Stop play.
S Shutdown menu.
F Fast forward.
O Codec info.
W Marked as watched / unwatched.
Left / Right                Seek step backward or forward.
     1x 10s, 2x 30s, 3x 1m, 4x 3m...

Ctl+Shift+O        Extended codec info.
Alt+< or >        Decrease, increase playback speed.

Esc Previous menu OR Home screen.
 Exit full screen 
Space Pause/Play.
Enter Player controls.

*/
#include <arduino.h>
#include <IRremote.h>
#include <Keyboard.h> // C:\Program Files (x86)\Arduino\libraries\Keyboard\src\Keyboard.h

#define SETUPMODE 0 // set > 0 to setup remote control & view codes on serial monitor

// OMNI DVD remote control
#define PLAY_PAUSE 0xFF40BF // pause
#define STOP 0xFF807F // stop
#define MENU 0xFFD827 // pin
#define CONTEXT_MENU 0xFFE817 // title 
#define RIGHT 0xFFB847
#define LEFT 0xFF42BD
#define UP 0xFF52AD
#define DOWN 0xFF7887
#define VOLUMEUP 0xFF58A7
#define VOLUMEDOWN 0xFF6897
#define INFO 0xFF18E7 // r/l
#define EXTENDED_INFO 0xFF28D7 // step
#define ESC 0xFFC23D // setup
#define ENTER 0xFFE21D // middle round btn
#define DELETE 0xFF3AC5 // blue btn open/close
//#define REPEAT 0xFFFFFFFF defined in IRremote.h

// IR receiver data output pin
const int RECV_PIN = 7;

uint8_t lastKb = 0; // the last key sent to usb hid
IRrecv irrecv(RECV_PIN);
decode_results results;

typedef struct aKey {
 uint32_t ir;
 uint8_t kb;
};

const aKey irToUsbMap[] = {
 {ENTER, KEY_RETURN},
 {ESC, KEY_ESC},
 {RIGHT, KEY_RIGHT_ARROW},
 {LEFT, KEY_LEFT_ARROW},
 {UP, KEY_UP_ARROW},
 {DOWN, KEY_DOWN_ARROW},
 {PLAY_PAUSE, ' '},
 {STOP, 'X'},
 {MENU, 'M'},
 {CONTEXT_MENU, 'C'},
 {VOLUMEUP, '+'},
 {VOLUMEDOWN, '-'},
 {INFO, 'O'},
 // {EXTENDED_INFO, 'O'},
 {DELETE, KEY_DELETE},
 {REPEAT, lastKb}
};
const int szLookup = sizeof(irToUsbMap) / sizeof(irToUsbMap[0]);

void SendKey(aKey k) {

 if(k.ir == REPEAT) k.kb = lastKb;

 if (SETUPMODE > 0) {
 // In test mode, print the HEX code on the serial monitor
 Serial.print("irtype: ");
 Serial.print(results.decode_type, HEX);
 Serial.print(" value: ");
 Serial.print(results.value, HEX);
 Serial.print(",  ir: ");
 Serial.print(k.ir, HEX);
 Serial.print(" kb: ");
 Serial.print(k.kb, HEX);
 Serial.print(" last: ");
 Serial.println(lastKb, HEX);
 } else {
 // In normal mode emulate keyboard key press
 // if (results.decode_type == NEC) { // don't need this for now
 // remote protocols enum IRTYPES {UNKNOWN, NEC, SONY, RC5, RC6, PANASONIC_OLD, JVC, NECX, HASH_CODE, LAST_PROTOCOL=HASH_CODE}; (defined in irlib.h)
 if(k.kb > 0) { 
 Keyboard.write(k.kb);

 // Keyboard.press(k.kb); <-- Tried this instead of write: doesn't work
 // delay(100); <-- tried longer delays
 // Keyboard.release(k.kb);
 }
 }
 lastKb = k.kb;
}

void setup() {
 irrecv.enableIRIn(); // Start the receiver

 if (SETUPMODE > 0) {
 // In test, start the serial and wait for serial console to connect
 Serial.begin(9600);
 while (!Serial)
 ; // Wait for serial to be connected. Required on Leonardo with integrated USB
 Serial.print("Begin test mode. # codes: ");
 Serial.println(szLookup);
 } else {
 // In normal mode, emulate a keyboard
 Keyboard.begin();
 }
}

void loop() {

 if (irrecv.decode(&results)) {
 int i;
 
 // find the ir code and emulate keyboard keypress
 for (i = 0; i < szLookup; i++) {
 if (irToUsbMap[i].ir == results.value) {
 SendKey(irToUsbMap[i]);
 break;
 // handle modified keys
 } else if (irToUsbMap[i].ir == EXTENDED_INFO) {
 Keyboard.press(KEY_LEFT_CTRL);
 Keyboard.press(KEY_LEFT_ALT);
 Keyboard.press(irToUsbMap[i].kb);
 delay(100);
 Keyboard.releaseAll();
 break;
 }
 }
 if(i == szLookup && SETUPMODE > 0) {
 // In test mode, print the HEX code on the serial monitor
 // only if not found in map table
 Serial.print("irtype: 0x");
 Serial.print(results.decode_type, HEX);
 Serial.print(" value: 0x");
 Serial.println(results.value, HEX);
 }
 irrecv.resume(); // Receive the next value
 }
}

The Kodi keyboard controls are case sensitive. Use lower case letters (i.e. {STOP, 'x'}, {MENU, 'm'}, {CONTEXT_MENU, 'c'}, {VOLUMEUP, '+'}, {VOLUMEDOWN, '-'}, {INFO, 'o'}). I also use a Pro Micro as an IR remote to keyboard adapter for Kodi, and it works like a charm. Good luck!

vanderDecken, big thanks to you! That was like getting to the moon only to realise one forgot to bring a bucket to put the rocks in.

I referenced Kodi from here. They indicate upper case, also using the keyboard I found character command (C + X etc) responded to either case.

Let that be a heads-up sign to anyone else passing this way.

As you say it works like a charm and sure beats interfacing the IR sensor directly into the RPI.