chegewara / EspTinyUSB

ESP32S2 native USB library. Implemented few common classes, like MIDI, CDC, HID or DFU (update).
MIT License
489 stars 70 forks source link

MODIFIER key doese not work #26

Closed rahmanshaber closed 3 years ago

rahmanshaber commented 3 years ago

I am trying to use the MODIFIER keys from this list, but it does not work. I get no output. see the processKeys function, i tried both commented and un-commented one. Commented one also does not work also it only gives output from some keys fromthe array. but un-commented one, every key works except the HID_KEY_CONTROL_LEFT. https://github.com/hathach/tinyusb/blob/master/src/class/hid/hid.h#L282


// firmware V4.1
// mutantC v4

#include "Arduino.h"
#include "hidcomposite.h"
#include <Keypad.h>

HIDcomposite device;

const int LED_1 = 17; 
const int LED_2 = 5; 
const int POWER_MAIN =37;
const int POWER_OFF =26;
const int POWER_EX =39;
const int POWER_LCD =38;
const int BUZZER =40;
const int PI_STATE =42;
const int BATTERY_MESUREMENT =18;

// Thumbstick, set pin numbers for the five buttons:
const int rightButton = 9;
const int upButton= 8;
const int leftButton = 4;
const int downButton= 10;
const int mouseButton = 3;

// For Thumbstick
int range = 10;              // output range of X or Y movement; affects movement speed
int responseDelay = 10;     // response delay of the mouse, in ms

// Switch Stats
static bool keymapState = 0;
static bool expansionState = 0;
static bool lcdState = 0;
bool powerstate = 0;

// For Keypad
const byte ROWS = 5;
const byte COLS = 11;

// For Battery Voltage
int value = 0;
float voltage;

#define POWEROFF              0x91
#define KMAP_SWITCH           0x92
#define EXPANSION_SWITCH      0x93
#define LCD_SWITCH            0x94
#define NOTIFICATION_LED      0x95
#define KMOUSE_LEFT           0x96
#define KMOUSE_RIGHT          0x97
#define KBATTERY_SWITCH       0x98
#define NOTIFICATION_BUZZER   0x99

static uint8_t keymapAlpha [] = {
  KMOUSE_RIGHT, LCD_SWITCH, EXPANSION_SWITCH, KBATTERY_SWITCH, NOTIFICATION_BUZZER, POWEROFF, NOTIFICATION_LED,  'E',  'E',  KMAP_SWITCH,  KMOUSE_LEFT,
  HID_KEY_1,           HID_KEY_2,  HID_KEY_3, HID_KEY_4,  HID_KEY_5,  HID_KEY_6, HID_KEY_7, HID_KEY_8, HID_KEY_9,  HID_KEY_0,   HID_KEY_ARROW_LEFT ,
  HID_KEY_Q,           HID_KEY_W,  HID_KEY_E, HID_KEY_R,  HID_KEY_T,  HID_KEY_Y, HID_KEY_U, HID_KEY_I, HID_KEY_O,  HID_KEY_P,   HID_KEY_ARROW_RIGHT        ,
  HID_KEY_CAPS_LOCK  , HID_KEY_A,  HID_KEY_S, HID_KEY_D,  HID_KEY_F,  HID_KEY_G, HID_KEY_H, HID_KEY_J, HID_KEY_K,  HID_KEY_L,   HID_KEY_BACKSPACE,
  HID_KEY_CONTROL_LEFT, HID_KEY_GUI_LEFT, HID_KEY_Z,  HID_KEY_X, HID_KEY_C, HID_KEY_V, HID_KEY_B,  HID_KEY_N,   HID_KEY_M,   HID_KEY_SPACE,  HID_KEY_RETURN             
};

static uint8_t keymapSymbols[] = {
  KMOUSE_RIGHT, LCD_SWITCH, EXPANSION_SWITCH, KBATTERY_SWITCH, NOTIFICATION_BUZZER, POWEROFF, NOTIFICATION_LED,  'E',  'E',  KMAP_SWITCH,  KMOUSE_LEFT,
  HID_KEY_ESCAPE,       HID_KEY_APOSTROPHE, HID_KEY_BACKSLASH,  HID_KEY_SLASH,  HID_KEY_SLASH,  HID_KEY_SLASH, HID_KEY_SLASH, HID_KEY_SEMICOLON,  HID_KEY_APOSTROPHE,  HID_KEY_BRACKET_RIGHT,  HID_KEY_ARROW_LEFT,
  HID_KEY_TAB,          HID_KEY_BRACKET_RIGHT,  HID_KEY_ARROW_UP, HID_KEY_BRACKET_RIGHT, HID_KEY_BRACKET_RIGHT,      HID_KEY_BRACKET_RIGHT,  HID_KEY_BRACKET_RIGHT,    HID_KEY_BRACKET_RIGHT,      HID_KEY_BRACKET_RIGHT,  HID_KEY_BRACKET_RIGHT, HID_KEY_ARROW_RIGHT,
  HID_KEY_CAPS_LOCK,    'E',  HID_KEY_ARROW_DOWN, 'E',   HID_KEY_BRACKET_RIGHT, HID_KEY_BRACKET_RIGHT , HID_KEY_BRACKET_LEFT      , HID_KEY_BRACKET_RIGHT, HID_KEY_BRACKET_RIGHT, HID_KEY_BRACKET_RIGHT, HID_KEY_BACKSPACE,
  HID_KEY_CONTROL_LEFT , HID_KEY_GUI_LEFT,  HID_KEY_EQUAL, HID_KEY_MINUS,   HID_KEY_SLASH,   HID_KEY_BRACKET_RIGHT,      HID_KEY_KEYPAD_EQUAL,      HID_KEY_COMMA,     HID_KEY_PERIOD,     HID_KEY_BRACKET_RIGHT,  HID_KEY_RETURN
};

static char dummyKeypad[ROWS][COLS] = {
  { 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10},
  {11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21},
  {22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32},
  {33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43},
  {44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54}
};

byte rowPins[ROWS] = {2, 41, 11, 12, 13};                     //connect to the row pinouts of the keypad
byte colPins[COLS] = {6, 21, 33, 36, 34, 35, 16, 1, 14, 15, 7}; //connect to the column pinouts of the 

//initialize an instance of class NewKeypad
Keypad Dummy  = Keypad(makeKeymap(dummyKeypad), rowPins, colPins, ROWS, COLS);

void setup(void){

  // Set PIN Stater
  pinMode(LED_1, OUTPUT);
  pinMode(LED_2, OUTPUT);
  pinMode(POWER_MAIN, OUTPUT);
  pinMode(POWER_OFF, INPUT_PULLUP);
  pinMode(POWER_EX, OUTPUT);
  pinMode(POWER_LCD, OUTPUT);
  pinMode(BUZZER, OUTPUT);
  pinMode(BATTERY_MESUREMENT, INPUT_PULLUP);
  pinMode(PI_STATE, INPUT_PULLUP);

  // Set thumbstick PIN Stater
  pinMode(upButton, INPUT_PULLUP);
  pinMode(downButton, INPUT_PULLUP);
  pinMode(leftButton, INPUT_PULLUP);
  pinMode(rightButton, INPUT_PULLUP);
  pinMode(mouseButton, INPUT_PULLUP);

  // Set Switch State
  digitalWrite(POWER_MAIN, HIGH);
//  digitalWrite(POWER_EX, LOW);
  digitalWrite(POWER_LCD, LOW);
  digitalWrite(BUZZER, LOW);
  powerstate = digitalRead(POWER_OFF);

  // Serial setup
  Serial.begin(115200);

  // Set up Keyboard and Mouse HID
  device.begin();

  // Set up keypad matrix
  Dummy.setHoldTime(1);
  Dummy.setDebounceTime(0);
  Dummy.addEventListener(keypadEvent);
  delay(1000);
}

void loop(void){

//  digitalWrite(LED_1, HIGH);
//  digitalWrite(LED_2, HIGH);
//  delay(1000);                   
//  digitalWrite(LED_1, LOW);    
//  digitalWrite(LED_2, LOW);

//  Serial.println(powerstate);
//  Serial.println(digitalRead(POWER_OFF));

  // Check Mouse state
  thumbstick ();

  // Check Keyboard state
  processKeys();
}

static void selectAlphabet(void)
{
  digitalWrite(LED_1, LOW);
  keymapState = 0;
}

static void selectSymbols(void)
{
  digitalWrite(LED_1, HIGH);
  keymapState = 1;
}

static void switchKeymap(void)
{
  if (keymapState)
    selectAlphabet();
  else
    selectSymbols();
}

static uint8_t mapKey(char key)
{
  if (keymapState)
    return keymapSymbols[key];

  return keymapAlpha[key];
}

static void keypadEvent(KeypadEvent key)
{
  if (Dummy.getState() != PRESSED)
    return;

  uint8_t kcode = mapKey(key);

  switch (kcode) {
  case KMAP_SWITCH:
    switchKeymap();
  break;
  case KBATTERY_SWITCH:
    battery_state();
  break;
  case KMOUSE_LEFT:
    mouse_left();
  break;
  case KMOUSE_RIGHT:
    mouse_rgiht();
  break;
  case NOTIFICATION_BUZZER:
    notificationBuzzer();
  break;
  case EXPANSION_SWITCH:
    power_expansion();
  break;
  case NOTIFICATION_LED:
    notificationLED();
  break;
  case LCD_SWITCH:
    power_lcd();
  break;
  case POWEROFF:
    poweroff();
  break;
  }
}

//static uint8_t modifierKey = 0x00;
//
//static void processKeys(void)
//{
// if (!Dummy.getKeys())
//   return;
//
// int i;
//
// for (i = 0; i < LIST_MAX; i++) {
//   if (!Dummy.key[i].stateChanged)
//     continue;
//
//   uint8_t key = mapKey(Dummy.key[i].kchar);
//
//   if (key == KMAP_SWITCH || key == KBATTERY_SWITCH || key == NOTIFICATION_BUZZER || key == KMOUSE_LEFT || key == KMOUSE_RIGHT || key == EXPANSION_SWITCH || key == NOTIFICATION_LED || key == LCD_SWITCH || key == POWEROFF)
//     continue;
//
//   switch (Dummy.key[i].kstate) {
//   case PRESSED: {
//       if (key & HID_KEY_SHIFT_LEFT) {
//           modifierKey = key;
//       } else if (key & HID_KEY_CONTROL_LEFT) {
//           modifierKey = key;
//       } else if (key & HID_KEY_ALT_LEFT) {
//           modifierKey = key;
//       } else if (key & HID_KEY_GUI_LEFT) {
//           modifierKey = key;
//       } else {
//           if (modifierKey & 0x00) { // if empty modifier
//                device.sendKey(key);
//                modifierKey = 0x00;
//           } else {
//                device.sendKey(key, modifierKey);
//                modifierKey = 0x00;
//           }
//       }
//   }
//   break;
//   case RELEASED: {
//     device.sendRelease();
//     modifierKey = 0x00;
//   }
//   break;
//   }
// }
//}

static void processKeys(void)
{
  if (!Dummy.getKeys())
    return;

  int i;

  for (i = 0; i < LIST_MAX; i++) {
    if (!Dummy.key[i].stateChanged)
      continue;

    uint8_t key = mapKey(Dummy.key[i].kchar);

    if (key == KMAP_SWITCH || key == KBATTERY_SWITCH || key == NOTIFICATION_BUZZER || key == KMOUSE_LEFT || key == KMOUSE_RIGHT || key == EXPANSION_SWITCH || key == NOTIFICATION_LED || key == LCD_SWITCH || key == POWEROFF)
      continue;

    switch (Dummy.key[i].kstate) {
    case PRESSED: {
//      uint8_t modifierKey = HID_KEY_NONE;
//      if (key & HID_KEY_SHIFT_LEFT) {yg
//        modifierKey = HID_KEY_SHIFT_LEFT;
//      }
//      device.sendPress(key, modifierKey);
      device.sendKey(key);
    }
    break;
    case RELEASED:
      device.sendRelease();
    break;
    }
  }
}

void thumbstick (void){

  // read the thumbstick buttons:
  int upState = digitalRead(upButton);
  int downState = digitalRead(downButton);
  int rightState = digitalRead(rightButton);
  int leftState = digitalRead(leftButton);

  // calculate the movement distance based on the button states:
  int  xDistance = (leftState - rightState) * range;
  int  yDistance = (upState - downState) * range;

  // if X or Y is non-zero, move:
  if ((xDistance != 0) || (yDistance != 0)) {
    device.move(xDistance, yDistance);
  }

  // a delay so the mouse doesn't move too fast:
  delay(responseDelay);
}

void mouse_left (void){
  device.pressLeft();
  delay(responseDelay); 
}

void mouse_rgiht (void){
  device.pressRight();
  delay(responseDelay);
}

static void power_lcd(void)
{
  if(lcdState){
    digitalWrite(POWER_LCD, LOW);
    Serial.println("POWER_LCD=LOW");
    lcdState = 0;
  } else {
    digitalWrite(POWER_LCD, HIGH);
    Serial.println("POWER_LCD=HIGH");
    lcdState = 1;
  }
}

static void power_expansion(void)
{
  if(expansionState){
    digitalWrite(POWER_EX, LOW);
    Serial.println("POWER_EX=LOW");
    expansionState = 0;
  } else {
    digitalWrite(POWER_EX, HIGH);
    Serial.println("POWER_EX=HIGH");
    expansionState = 1;
  }
}

static void notificationLED(void)
{
  Serial.println("notificationLED");
  digitalWrite(LED_2, HIGH);
  delay(25);
  digitalWrite(LED_2, LOW);
  delay(25);
  digitalWrite(LED_2, HIGH);
  delay(25);
  digitalWrite(LED_2, LOW);
  delay(25);
  digitalWrite(LED_1, HIGH);
  delay(25);
  digitalWrite(LED_1, LOW);
  delay(25);
  digitalWrite(LED_1, HIGH);
  delay(25);
  digitalWrite(LED_1, LOW);
  delay(25);
}

static void notificationBuzzer(void)
{
  Serial.println("notificationBuzzer");
  digitalWrite(BUZZER, HIGH);
  delay(50);
  digitalWrite(BUZZER, LOW);
  delay(50);
  digitalWrite(BUZZER, HIGH);
  delay(50);
  digitalWrite(BUZZER, LOW);
}

static void poweroff(void)
{
//  uint8_t i;

  /* Ask RPi to powerdown */
//  Keyboard.write(CONSUMER_POWER);

//  /* Wait for about 16 seconds */
//  for (i = 0; i < 20; i++) {
//    notification();
//  }
 // delay(16000);
  /* And finally turn off the power */
  Serial.println("poweroffOFF");
  digitalWrite(POWER_MAIN, LOW);
}

static void battery_state(void)
{
  Serial.println("battery_state");
  Serial.println(read_voltage());
}

static float read_voltage(void)
{
  // Get Battery voltage
  value = analogRead(BATTERY_MESUREMENT);
  voltage = value * 5.0/1023;
//  Serial.print("Voltage= ");
//  Serial.println(voltage);
  delay(200);
  return voltage;
}
chegewara commented 3 years ago

Thanks for reporting, i will test it, because from code level it seems to be ok.

What OS are you using, what keyboard layout?

rahmanshaber commented 3 years ago

Windows. i will use the device in the linux, image

chegewara commented 3 years ago

Ok, for modifiers you are not using values: https://github.com/hathach/tinyusb/blob/master/src/class/hid/hid.h#L410-L417 instead you have to use: https://github.com/hathach/tinyusb/blob/master/src/class/hid/hid.h#L282-L289

rahmanshaber commented 3 years ago

So the commented processkey code will work fine, it's not an issue in the function? i mean this one


//static uint8_t modifierKey = 0x00;
//
//static void processKeys(void)
//{
// if (!Dummy.getKeys())
//   return;
//
// int i;
//
// for (i = 0; i < LIST_MAX; i++) {
//   if (!Dummy.key[i].stateChanged)
//     continue;
//
//   uint8_t key = mapKey(Dummy.key[i].kchar);
//
//   if (key == KMAP_SWITCH || key == KBATTERY_SWITCH || key == NOTIFICATION_BUZZER || key == KMOUSE_LEFT || key == KMOUSE_RIGHT || key == EXPANSION_SWITCH || key == NOTIFICATION_LED || key == LCD_SWITCH || key == POWEROFF)
//     continue;
//
//   switch (Dummy.key[i].kstate) {
//   case PRESSED: {
//       if (key & HID_KEY_SHIFT_LEFT) {
//           modifierKey = key;
//       } else if (key & HID_KEY_CONTROL_LEFT) {
//           modifierKey = key;
//       } else if (key & HID_KEY_ALT_LEFT) {
//           modifierKey = key;
//       } else if (key & HID_KEY_GUI_LEFT) {
//           modifierKey = key;
//       } else {
//           if (modifierKey & 0x00) { // if empty modifier
//                device.sendKey(key);
//                modifierKey = 0x00;
//           } else {
//                device.sendKey(key, modifierKey);
//                modifierKey = 0x00;
//           }
//       }
//   }
//   break;
//   case RELEASED: {
//     device.sendRelease();
//     modifierKey = 0x00;
//   }
//   break;
//   }
// }
//}
chegewara commented 3 years ago

Probably yes, i didnt check your code. The point is that modifier is a 8 bit value, where each bit is a special key. The HID_KEY_ALT_LEFT are used to build hid report descriptor, and you could eventually use it as key, not modifier, value, but most likely descriptor is not prepared to use it this way.

rahmanshaber commented 3 years ago

okay, i tested with KEYBOARD_MODIFIER_LEFTCTRL and sendKey function it does not work. can you give me an example code to test the modifier keys. like hold ctrl then v to past.

chegewara commented 3 years ago

This is code i tested with, on linux, with keyboard layout test app:

/**
 * Simple HID keyboard
 * author: chegewara
 */
#include "Arduino.h"
#include "hidkeyboard.h"

HIDkeyboard dev;

class MyHIDCallbacks: public HIDCallbacks{
  void onData(uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize) {
    Serial.printf("ID: %d, type: %d, size: %d\n", report_id, (int)report_type, bufsize);  
     for (size_t i = 0; i < bufsize; i++)
    {
        Serial.printf("%d\n", buffer[i]);
    }   
  }
};

void dataCB(uint8_t report_id, uint8_t report_type, uint8_t const* buffer, uint16_t bufsize)
{
    for (size_t i = 0; i < bufsize; i++)
    {
        Serial.printf("%d\n", buffer[i]);
        Serial.printf("%c\n", buffer[i]);
    }
}

void setup()
{
    Serial.begin(115200);
    dev.begin();
    dev.setCallbacks(new MyHIDCallbacks());
}

void loop()
{
    delay(1000);
    // dev.sendKey(0, KEYBOARD_MODIFIER_LEFTCTRL); // press and release CTRL
    dev.sendPress(0, KEYBOARD_MODIFIER_LEFTCTRL); // hold CTRL
}

I can see the Control L is being pressed hold (you cant see it on this screenshot, because any action with mouse or keyboard change its state in that app, but i can confirm CTRL is held with mouse wheel changing font size in apps):

Screenshot from 2021-02-28 08-23-30

rahmanshaber commented 3 years ago

okay, i had some issues in the code and i fixed some of it, and now ctrl and windows key works. but still can't get the combination working, i think library has something to do with it. can you add 2 button, use 1 for ctrl and other for v, see if combination is working, i mean the past. Also when i press E i get the gui key, means the start menu shows up. my if function can't filter the difference as the gui modifier uses 8. MODIFIER_GUI_LEFT == HID_KEY_E == 0x08 is there a way to know that a modifier key is pressed?



static void processKeys(void)
{
 if (!Dummy.getKeys())
   return;

 int i;

 for (i = 0; i < LIST_MAX; i++) {
   if (!Dummy.key[i].stateChanged)
     continue;

   uint8_t key = mapKey(Dummy.key[i].kchar);

   if (key == KMAP_SWITCH || key == KBATTERY_SWITCH || key == NOTIFICATION_BUZZER || key == KMOUSE_LEFT || key == KMOUSE_RIGHT || key == EXPANSION_SWITCH || key == NOTIFICATION_LED || key == LCD_SWITCH || key == POWEROFF)
     continue;

   switch (Dummy.key[i].kstate) {
   case PRESSED: {
       if (key == KEYBOARD_MODIFIER_LEFTCTRL) {
           Serial.print("LEFT MODIFIER PRESSED");
           device.sendPress(0, KEYBOARD_MODIFIER_LEFTCTRL);
       } else if (key == KEYBOARD_MODIFIER_LEFTGUI) {
           Serial.print("LEFT GUI PRESSED");
           device.sendPress(0, KEYBOARD_MODIFIER_LEFTGUI);
       } else {
           Serial.print("SOME KEY PRESSED");
           device.sendPress(key);
       }
   }
   break;
   case RELEASED: {
     device.sendRelease();
   }
   break;
   }
 }
}
chegewara commented 3 years ago

The library works fine. You have to send key at the same time with modifier: device.sendPress(key, KEYBOARD_MODIFIER_LEFTGUI);

When you send device.sendRelease(); all keys are released, also modifier keys.

rahmanshaber commented 3 years ago

the combination works, but can't figure out how to solve that GUI_key issue. Pressing e press windows key. how to recognize modifier key

chegewara commented 3 years ago

https://github.com/hathach/tinyusb/blob/master/src/class/hid/hid.h#L282-L289

rahmanshaber commented 3 years ago

i used those modifier keys and working great. but issue is my if function can't differenceit between this keys, as they have assigned same values. MODIFIER_GUI_LEFT == HID_KEY_E == 0x08 what can i do to solve this issue. everytime i press E key i get the Gui_key


static void processKeys(void)
{
 if (!Dummy.getKeys())
   return;

 int i;

 for (i = 0; i < LIST_MAX; i++) {
   if (!Dummy.key[i].stateChanged)
     continue;

   uint8_t key = mapKey(Dummy.key[i].kchar);

   if (key == KMAP_SWITCH || key == KBATTERY_SWITCH || key == NOTIFICATION_BUZZER || key == KMOUSE_LEFT || key == KMOUSE_RIGHT || key == EXPANSION_SWITCH || key == NOTIFICATION_LED || key == LCD_SWITCH || key == POWEROFF)
     continue;

   switch (Dummy.key[i].kstate) {
   case PRESSED: {
       if (key == KEYBOARD_MODIFIER_LEFTCTRL) {
           Serial.print("LEFT MODIFIER PRESSED");
           device.sendPress(0, KEYBOARD_MODIFIER_LEFTCTRL);
       } else if (key == KEYBOARD_MODIFIER_LEFTGUI) {
           Serial.print("LEFT GUI PRESSED");
           device.sendPress(0, KEYBOARD_MODIFIER_LEFTGUI);
       } else {
           Serial.print("SOME KEY PRESSED");
           device.sendPress(key);
       }
   }
   break;
   case RELEASED: {
     device.sendRelease();
   }
   break;
   }
 }
}
chegewara commented 3 years ago

You are mixing 2 things. HID_KEY_E is a key value, MODIFIER_GUI_LEFT is a modifier value. If you have problem in code then you have problem with design.

You could use HID_KEY_CONTROL_LEFT in your code to detect key, but to send as modifier you have to use KEYBOARD_MODIFIER_LEFTCTRL.

rahmanshaber commented 3 years ago

Thanks a lot, i got my code working. Thanks a lot for this nice ardinuo library.
I will share my working code if in case someone needs it. Here is my project https://mutantc.gitlab.io/index.html


// firmware V4.1
// mutantC v4

#include "Arduino.h"
#include "hidcomposite.h"
#include <Keypad.h>

HIDcomposite device;

const int LED_1 = 17; 
const int LED_2 = 5; 
const int POWER_MAIN =37;
const int POWER_OFF =26;
const int POWER_EX =39;
const int POWER_LCD =38;
const int BUZZER =40;
const int PI_STATE =42;
const int BATTERY_MESUREMENT =18;

// Thumbstick, set pin numbers for the five buttons:
const int rightButton = 9;
const int upButton= 8;
const int leftButton = 4;
const int downButton= 10;
const int mouseButton = 3;

// For Thumbstick
int range = 10;              // output range of X or Y movement; affects movement speed
int responseDelay = 10;     // response delay of the mouse, in ms

// Switch Stats
static bool keymapState = 0;
static bool expansionState = 0;
static bool lcdState = 0;
bool powerstate = 0;

// For Keypad
const byte ROWS = 5;
const byte COLS = 11;

// For Battery Voltage
int value = 0;
float voltage;

#define POWEROFF              0x91
#define KMAP_SWITCH           0x92
#define EXPANSION_SWITCH      0x93
#define LCD_SWITCH            0x94
#define NOTIFICATION_LED      0x95
#define KMOUSE_LEFT           0x96
#define KMOUSE_RIGHT          0x97
#define KBATTERY_SWITCH       0x98
#define NOTIFICATION_BUZZER   0x99

static uint8_t keymapAlpha [] = {
  KMOUSE_RIGHT, LCD_SWITCH, EXPANSION_SWITCH, KBATTERY_SWITCH, NOTIFICATION_BUZZER, POWEROFF, NOTIFICATION_LED,  'E',  'E',  KMAP_SWITCH,  KMOUSE_LEFT,
  HID_KEY_1,           HID_KEY_2,  HID_KEY_3, HID_KEY_4,  HID_KEY_5,  HID_KEY_6, HID_KEY_7, HID_KEY_8, HID_KEY_9,  HID_KEY_0,   HID_KEY_ARROW_LEFT ,
  HID_KEY_Q,           HID_KEY_W,  HID_KEY_E, HID_KEY_R,  HID_KEY_T,  HID_KEY_Y, HID_KEY_U, HID_KEY_I, HID_KEY_O,  HID_KEY_P,   HID_KEY_ARROW_RIGHT        ,
  HID_KEY_CAPS_LOCK  , HID_KEY_A,  HID_KEY_S, HID_KEY_D,  HID_KEY_F,  HID_KEY_G, HID_KEY_H, HID_KEY_J, HID_KEY_K,  HID_KEY_L,   HID_KEY_BACKSPACE,
  HID_KEY_CONTROL_LEFT, HID_KEY_GUI_LEFT, HID_KEY_Z,  HID_KEY_X, HID_KEY_C, HID_KEY_V, HID_KEY_B,  HID_KEY_N,   HID_KEY_M,   HID_KEY_SPACE,  HID_KEY_RETURN             
};

static uint8_t keymapSymbols[] = {
  KMOUSE_RIGHT, LCD_SWITCH, EXPANSION_SWITCH, KBATTERY_SWITCH, NOTIFICATION_BUZZER, POWEROFF, NOTIFICATION_LED,  'E',  'E',  KMAP_SWITCH,  KMOUSE_LEFT,
  HID_KEY_ESCAPE,       HID_KEY_APOSTROPHE, HID_KEY_BACKSLASH,  HID_KEY_SLASH,  HID_KEY_SLASH,  HID_KEY_SLASH, HID_KEY_SLASH, HID_KEY_SEMICOLON,  HID_KEY_APOSTROPHE,  HID_KEY_BRACKET_RIGHT,  HID_KEY_ARROW_LEFT,
  HID_KEY_TAB,          HID_KEY_BRACKET_RIGHT,  HID_KEY_ARROW_UP, HID_KEY_BRACKET_RIGHT, HID_KEY_BRACKET_RIGHT,      HID_KEY_BRACKET_RIGHT,  HID_KEY_BRACKET_RIGHT,    HID_KEY_BRACKET_RIGHT,      HID_KEY_BRACKET_RIGHT,  HID_KEY_BRACKET_RIGHT, HID_KEY_ARROW_RIGHT,
  HID_KEY_CAPS_LOCK,    'E',  HID_KEY_ARROW_DOWN, 'E',   HID_KEY_BRACKET_RIGHT, HID_KEY_BRACKET_RIGHT , HID_KEY_BRACKET_LEFT      , HID_KEY_BRACKET_RIGHT, HID_KEY_BRACKET_RIGHT, HID_KEY_BRACKET_RIGHT, HID_KEY_BACKSPACE,
  HID_KEY_CONTROL_LEFT , HID_KEY_GUI_LEFT,  HID_KEY_EQUAL, HID_KEY_MINUS,   HID_KEY_SLASH,   HID_KEY_BRACKET_RIGHT,      HID_KEY_KEYPAD_EQUAL,      HID_KEY_COMMA,     HID_KEY_PERIOD,     HID_KEY_BRACKET_RIGHT,  HID_KEY_RETURN
};

static char dummyKeypad[ROWS][COLS] = {
  { 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10},
  {11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21},
  {22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32},
  {33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43},
  {44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54}
};

byte rowPins[ROWS] = {2, 41, 11, 12, 13};                     //connect to the row pinouts of the keypad
byte colPins[COLS] = {6, 21, 33, 36, 34, 35, 16, 1, 14, 15, 7}; //connect to the column pinouts of the 

//initialize an instance of class NewKeypad
Keypad Dummy  = Keypad(makeKeymap(dummyKeypad), rowPins, colPins, ROWS, COLS);

void setup(void){

  // Set PIN Stater
  pinMode(LED_1, OUTPUT);
  pinMode(LED_2, OUTPUT);
  pinMode(POWER_MAIN, OUTPUT);
  pinMode(POWER_OFF, INPUT_PULLUP);
  pinMode(POWER_EX, OUTPUT);
  pinMode(POWER_LCD, OUTPUT);
  pinMode(BUZZER, OUTPUT);
  pinMode(BATTERY_MESUREMENT, INPUT_PULLUP);
  pinMode(PI_STATE, INPUT_PULLUP);

  // Set thumbstick PIN Stater
  pinMode(upButton, INPUT_PULLUP);
  pinMode(downButton, INPUT_PULLUP);
  pinMode(leftButton, INPUT_PULLUP);
  pinMode(rightButton, INPUT_PULLUP);
  pinMode(mouseButton, INPUT_PULLUP);

  // Set Switch State
  digitalWrite(POWER_MAIN, HIGH);
//  digitalWrite(POWER_EX, LOW);
  digitalWrite(POWER_LCD, LOW);
  digitalWrite(BUZZER, LOW);
  powerstate = digitalRead(POWER_OFF);

  // Serial setup
  Serial.begin(115200);

  // Set up Keyboard and Mouse HID
  device.begin();

  // Set up keypad matrix
  Dummy.setHoldTime(1);
  Dummy.setDebounceTime(0);
  Dummy.addEventListener(keypadEvent);
  delay(1000);
}

void loop(void){

//  digitalWrite(LED_1, HIGH);
//  digitalWrite(LED_2, HIGH);
//  delay(1000);                   
//  digitalWrite(LED_1, LOW);    
//  digitalWrite(LED_2, LOW);

//  Serial.println(powerstate);
//  Serial.println(digitalRead(POWER_OFF));

  // Check Mouse state
  thumbstick ();

  // Check Keyboard state
  processKeys();
}

static void selectAlphabet(void)
{
  digitalWrite(LED_1, LOW);
  keymapState = 0;
}

static void selectSymbols(void)
{
  digitalWrite(LED_1, HIGH);
  keymapState = 1;
}

static void switchKeymap(void)
{
  if (keymapState)
    selectAlphabet();
  else
    selectSymbols();
}

static uint8_t mapKey(char key)
{
  if (keymapState)
    return keymapSymbols[key];

  return keymapAlpha[key];
}

static void keypadEvent(KeypadEvent key)
{
  if (Dummy.getState() != PRESSED)
    return;

  uint8_t kcode = mapKey(key);

  switch (kcode) {
  case KMAP_SWITCH:
    switchKeymap();
  break;
  case KBATTERY_SWITCH:
    battery_state();
  break;
  case KMOUSE_LEFT:
    mouse_left();
  break;
  case KMOUSE_RIGHT:
    mouse_rgiht();
  break;
  case NOTIFICATION_BUZZER:
    notificationBuzzer();
  break;
  case EXPANSION_SWITCH:
    power_expansion();
  break;
  case NOTIFICATION_LED:
    notificationLED();
  break;
  case LCD_SWITCH:
    power_lcd();
  break;
  case POWEROFF:
    poweroff();
  break;
  }
}

static uint8_t modifierKey = 0;

static void processKeys(void)
{
 if (!Dummy.getKeys())
   return;

 int i;

 for (i = 0; i < LIST_MAX; i++) {
   if (!Dummy.key[i].stateChanged)
     continue;

   uint8_t key = mapKey(Dummy.key[i].kchar);

   if (key == KMAP_SWITCH || key == KBATTERY_SWITCH || key == NOTIFICATION_BUZZER || key == KMOUSE_LEFT || key == KMOUSE_RIGHT || key == EXPANSION_SWITCH || key == NOTIFICATION_LED || key == LCD_SWITCH || key == POWEROFF)
     continue;

   switch (Dummy.key[i].kstate) {
   case PRESSED: {
       if (key == HID_KEY_CONTROL_LEFT) {
           device.sendPress(0, KEYBOARD_MODIFIER_LEFTCTRL);
           modifierKey = KEYBOARD_MODIFIER_LEFTCTRL;
       } else if (key == HID_KEY_GUI_LEFT) {
           device.sendPress(0, KEYBOARD_MODIFIER_LEFTGUI);
           modifierKey = KEYBOARD_MODIFIER_LEFTCTRL;
       } else {
           device.sendPress(key, modifierKey);
           modifierKey = 0;
       }
   }
   break;
   case RELEASED: {
     device.sendRelease();
     modifierKey = 0;
   }
   break;
   }
 }
}

void thumbstick (void){

  // read the thumbstick buttons:
  int upState = digitalRead(upButton);
  int downState = digitalRead(downButton);
  int rightState = digitalRead(rightButton);
  int leftState = digitalRead(leftButton);

  // calculate the movement distance based on the button states:
  int  xDistance = (leftState - rightState) * range;
  int  yDistance = (upState - downState) * range;

  // if X or Y is non-zero, move:
  if ((xDistance != 0) || (yDistance != 0)) {
    device.move(xDistance, yDistance);
  }

  // a delay so the mouse doesn't move too fast:
  delay(responseDelay);
}

void mouse_left (void){
  device.pressLeft();
  delay(responseDelay); 
}

void mouse_rgiht (void){
  device.pressRight();
  delay(responseDelay);
}

static void power_lcd(void)
{
  if(lcdState){
    digitalWrite(POWER_LCD, LOW);
    Serial.println("POWER_LCD=LOW");
    lcdState = 0;
  } else {
    digitalWrite(POWER_LCD, HIGH);
    Serial.println("POWER_LCD=HIGH");
    lcdState = 1;
  }
}

static void power_expansion(void)
{
  if(expansionState){
    digitalWrite(POWER_EX, LOW);
    Serial.println("POWER_EX=LOW");
    expansionState = 0;
  } else {
    digitalWrite(POWER_EX, HIGH);
    Serial.println("POWER_EX=HIGH");
    expansionState = 1;
  }
}

static void notificationLED(void)
{
  Serial.println("notificationLED");
  digitalWrite(LED_2, HIGH);
  delay(25);
  digitalWrite(LED_2, LOW);
  delay(25);
  digitalWrite(LED_2, HIGH);
  delay(25);
  digitalWrite(LED_2, LOW);
  delay(25);
  digitalWrite(LED_1, HIGH);
  delay(25);
  digitalWrite(LED_1, LOW);
  delay(25);
  digitalWrite(LED_1, HIGH);
  delay(25);
  digitalWrite(LED_1, LOW);
  delay(25);
}

static void notificationBuzzer(void)
{
  Serial.println("notificationBuzzer");
  digitalWrite(BUZZER, HIGH);
  delay(50);
  digitalWrite(BUZZER, LOW);
  delay(50);
  digitalWrite(BUZZER, HIGH);
  delay(50);
  digitalWrite(BUZZER, LOW);
}

static void poweroff(void)
{
//  uint8_t i;

  /* Ask RPi to powerdown */
//  Keyboard.write(CONSUMER_POWER);

//  /* Wait for about 16 seconds */
//  for (i = 0; i < 20; i++) {
//    notification();
//  }
 // delay(16000);
  /* And finally turn off the power */
  Serial.println("poweroffOFF");
  digitalWrite(POWER_MAIN, LOW);
}

static void battery_state(void)
{
  Serial.println("battery_state");
  Serial.println(read_voltage());
}

static float read_voltage(void)
{
  // Get Battery voltage
  value = analogRead(BATTERY_MESUREMENT);
  voltage = value * 5.0/1023;
//  Serial.print("Voltage= ");
//  Serial.println(voltage);
  delay(200);
  return voltage;
}

// written by
// Cyril Hrubis @metan-ucw
// Abrar @s96Abrar
// rahmanshaber @rahmanshaber