/*
Newtle Kim Keyboard Project #3
Macro Keyboard for MAC
Reads an digital input on pin 4 for Encoder Switch
Connect the encoder CLK to pin 2, DT to pin 3.
This example code is in the public domain.
Please refer to the video below for details on how to make it in detail.
https://www.youtube.com/@newtlekim/videos
3Dmodeling :
All designs and sources are open, but be sure to list the sources.
Source : https://www.youtube.com/@newtlekim
Put the keywords below in parentheses.
Keyboard.release()
KEY_LEFT_GUI : ⌘ Command
KEY_LEFT_CTRL : ⌃ Control
KEY_LEFT_ALT : ⌥ Option
32 : Space
KEY_LEFT_SHIFT : Shift
KEY_CAPS_LOCK : Capslock
KEY_TAB Tab : Tab
KEY_ESC : ESC
*/
#include <Keyboard.h>
const uint8_t Keyboard_Volume_Down_MUTE = 0x7f;
const uint8_t Keyboard_Volume_Up_RAW = 0x80;
const uint8_t Keyboard_Volume_Down_RAW = 0x81;
#define CLK 2 // CLK D2
#define DT 3 // DT D3
#define SW 4 // Encoder Switch D4
int counter = 0;
int currentStateCLK;
int lastStateCLK;
void setup() {
Keyboard.begin();
pinMode(A1, INPUT_PULLUP);
pinMode(A2, INPUT_PULLUP);
pinMode(5, OUTPUT);
pinMode(6, OUTPUT);
pinMode(7, OUTPUT);
pinMode(8, OUTPUT);
pinMode(9, OUTPUT);
pinMode(CLK,INPUT);
pinMode(DT,INPUT);
pinMode(SW,INPUT_PULLUP);
lastStateCLK = digitalRead(CLK);
attachInterrupt(0, updateEncoder, CHANGE);
attachInterrupt(1, updateEncoder, CHANGE);
}
void loop() {
int sensorVal = digitalRead(SW);
if (sensorVal == LOW) {
detachInterrupt(0);
detachInterrupt(1);
Keyboard.pressRaw(Keyboard_Volume_Down_MUTE);
delay(500);
Keyboard.releaseRaw(Keyboard_Volume_Down_MUTE);
attachInterrupt(0, updateEncoder, CHANGE);
attachInterrupt(1, updateEncoder, CHANGE);
}
//9 Key
digitalWrite(5, LOW);
if (digitalRead(A1)) Keyboard.releaseAll();
else{
Keyboard.press(KEY_LEFT_ALT); // ^
Keyboard.press(KEY_LEFT_GUI); //Command
Keyboard.press(KEY_ESC); // ESC
delay(500);
}
digitalWrite(5, HIGH);
digitalWrite(6, LOW);
// 8 key
if (digitalRead(A1)) Keyboard.releaseAll();
else{
Keyboard.press(KEY_LEFT_GUI); //⌘
Keyboard.press(KEY_LEFT_SHIFT); //shift
Keyboard.press('5'); // 5
}
// 4 key
if (digitalRead(A2)) Keyboard.releaseAll();
else{
Keyboard.press(KEY_LEFT_CTRL); // Ctl
Keyboard.press(KEY_LEFT_GUI); //⌘
Keyboard.press('q'); // ESC
delay(500);
}
digitalWrite(6, HIGH);
digitalWrite(7, LOW);
if (digitalRead(A1)) Keyboard.releaseAll();
else{
Keyboard.press(KEY_LEFT_GUI); //⌘
Keyboard.press(KEY_LEFT_SHIFT); //shift
Keyboard.press('4'); // 4
delay(100);
Keyboard.press(32); // space
delay(500);
}
if (digitalRead(A2)) Keyboard.releaseAll();
else{
Keyboard.press(KEY_LEFT_CTRL); //Ctl
Keyboard.press(KEY_LEFT_GUI); //⌘
Keyboard.press('f'); // f
delay(500);
}
digitalWrite(7, HIGH);
digitalWrite(8, LOW);
// 6 key
if (digitalRead(A1)) Keyboard.releaseAll();
else {
Keyboard.press(KEY_LEFT_GUI); //⌘
Keyboard.press(KEY_LEFT_SHIFT); //shift
Keyboard.press('4'); // 4
delay(500);
}
// 2key
if (digitalRead(A2)) Keyboard.releaseAll();
else{
Keyboard.press(KEY_LEFT_GUI); //⌘
Keyboard.press(KEY_LEFT_ALT); // Option
Keyboard.press('d'); // d
delay(500);
}
digitalWrite(8, HIGH);
digitalWrite(9, LOW);
// 5 key
if (digitalRead(A1)){
Keyboard.releaseAll();
}
else{
Keyboard.press(KEY_LEFT_GUI); //⌘
Keyboard.press(KEY_LEFT_SHIFT); //Shift
Keyboard.press('3'); // space
delay(500);
}
// 1 Key
if (digitalRead(A2)){
Keyboard.releaseAll();
}
else {
Keyboard.press(KEY_LEFT_CTRL); //control
Keyboard.press(KEY_LEFT_GUI); //⌘
Keyboard.press(32); // space
delay(500);
}
digitalWrite(9, HIGH);
}
void updateEncoder(){
currentStateCLK = digitalRead(CLK);
if (currentStateCLK != lastStateCLK && currentStateCLK == 1){
if (digitalRead(DT) != currentStateCLK) {
delay(10);
Keyboard.pressRaw(Keyboard_Volume_Up_RAW);
Keyboard.releaseRaw(Keyboard_Volume_Up_RAW);
}
else {
delay(10);
Keyboard.pressRaw(Keyboard_Volume_Down_RAW);
Keyboard.releaseRaw(Keyboard_Volume_Down_RAW);
}
}
lastStateCLK = currentStateCLK;
}
Keyboard.h
/*
Keyboard.h
Copyright (c) 2015, Arduino LLC
Original code (pre-library): Copyright (c) 2011, Peter Barrett
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef KEYBOARD_h
#define KEYBOARD_h
#include "HID.h"
#if !defined(_USING_HID)
#warning "Using legacy HID core (non pluggable)"
#else
//================================================================================
//================================================================================
// Keyboard
#define KEY_LEFT_CTRL 0x80
#define KEY_LEFT_SHIFT 0x81
#define KEY_LEFT_ALT 0x82
#define KEY_LEFT_GUI 0x83
#define KEY_RIGHT_CTRL 0x84
#define KEY_RIGHT_SHIFT 0x85
#define KEY_RIGHT_ALT 0x86
#define KEY_RIGHT_GUI 0x87
#define KEY_UP_ARROW 0xDA
#define KEY_DOWN_ARROW 0xD9
#define KEY_LEFT_ARROW 0xD8
#define KEY_RIGHT_ARROW 0xD7
#define KEY_BACKSPACE 0xB2
#define KEY_TAB 0xB3
#define KEY_RETURN 0xB0
#define KEY_ESC 0xB1
#define KEY_INSERT 0xD1
#define KEY_DELETE 0xD4
#define KEY_PAGE_UP 0xD3
#define KEY_PAGE_DOWN 0xD6
#define KEY_HOME 0xD2
#define KEY_END 0xD5
#define KEY_CAPS_LOCK 0xC1
#define KEY_F1 0xC2
#define KEY_F2 0xC3
#define KEY_F3 0xC4
#define KEY_F4 0xC5
#define KEY_F5 0xC6
#define KEY_F6 0xC7
#define KEY_F7 0xC8
#define KEY_F8 0xC9
#define KEY_F9 0xCA
#define KEY_F10 0xCB
#define KEY_F11 0xCC
#define KEY_F12 0xCD
#define KEY_F13 0xF0
#define KEY_F14 0xF1
#define KEY_F15 0xF2
#define KEY_F16 0xF3
#define KEY_F17 0xF4
#define KEY_F18 0xF5
#define KEY_F19 0xF6
#define KEY_F20 0xF7
#define KEY_F21 0xF8
#define KEY_F22 0xF9
#define KEY_F23 0xFA
#define KEY_F24 0xFB
// Supported keyboard layouts
extern const uint8_t KeyboardLayout_de_DE[];
extern const uint8_t KeyboardLayout_en_US[];
extern const uint8_t KeyboardLayout_es_ES[];
extern const uint8_t KeyboardLayout_fr_FR[];
extern const uint8_t KeyboardLayout_it_IT[];
// Low level key report: up to 6 keys and shift, ctrl etc at once
typedef struct
{
uint8_t modifiers;
uint8_t reserved;
uint8_t keys[6];
} KeyReport;
class Keyboard_ : public Print
{
private:
KeyReport _keyReport;
const uint8_t *_asciimap;
void sendReport(KeyReport* keys);
public:
Keyboard_(void);
void begin(const uint8_t *layout = KeyboardLayout_en_US);
void end(void);
size_t write(uint8_t k);
size_t write(const uint8_t *buffer, size_t size);
size_t press(uint8_t k);
size_t release(uint8_t k);
size_t writeRaw(uint8_t k);
size_t pressRaw(uint8_t k);
size_t releaseRaw(uint8_t k);
void releaseAll(void);
};
extern Keyboard_ Keyboard;
#endif
#endif
Keyboard.cpp
/*
Keyboard.cpp
Copyright (c) 2015, Arduino LLC
Original code (pre-library): Copyright (c) 2011, Peter Barrett
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "Keyboard.h"
#include "KeyboardLayout.h"
#if defined(_USING_HID)
//================================================================================
//================================================================================
// Keyboard
static const uint8_t _hidReportDescriptor[] PROGMEM = {
// Keyboard
0x05, 0x01, // USAGE_PAGE (Generic Desktop) // 47
0x09, 0x06, // USAGE (Keyboard)
0xa1, 0x01, // COLLECTION (Application)
0x85, 0x02, // REPORT_ID (2)
0x05, 0x07, // USAGE_PAGE (Keyboard)
0x19, 0xe0, // USAGE_MINIMUM (Keyboard LeftControl)
0x29, 0xe7, // USAGE_MAXIMUM (Keyboard Right GUI)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x75, 0x01, // REPORT_SIZE (1)
0x95, 0x08, // REPORT_COUNT (8)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x95, 0x01, // REPORT_COUNT (1)
0x75, 0x08, // REPORT_SIZE (8)
0x81, 0x03, // INPUT (Cnst,Var,Abs)
0x95, 0x06, // REPORT_COUNT (6)
0x75, 0x08, // REPORT_SIZE (8)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x73, // LOGICAL_MAXIMUM (115)
0x05, 0x07, // USAGE_PAGE (Keyboard)
0x19, 0x00, // USAGE_MINIMUM (Reserved (no event indicated))
0x29, 0x73, // USAGE_MAXIMUM (Keyboard Application)
0x81, 0x00, // INPUT (Data,Ary,Abs)
0xc0, // END_COLLECTION
};
Keyboard_::Keyboard_(void)
{
static HIDSubDescriptor node(_hidReportDescriptor, sizeof(_hidReportDescriptor));
HID().AppendDescriptor(&node);
_asciimap = KeyboardLayout_en_US;
}
void Keyboard_::begin(const uint8_t *layout)
{
_asciimap = layout;
}
void Keyboard_::end(void)
{
}
void Keyboard_::sendReport(KeyReport* keys)
{
HID().SendReport(2,keys,sizeof(KeyReport));
}
uint8_t USBPutChar(uint8_t c);
// press() adds the specified key (printing, non-printing, or modifier)
// to the persistent key report and sends the report. Because of the way
// USB HID works, the host acts like the key remains pressed until we
// call release(), releaseAll(), or otherwise clear the report and resend.
size_t Keyboard_::press(uint8_t k)
{
uint8_t i;
if (k >= 136) { // it's a non-printing key (not a modifier)
k = k - 136;
} else if (k >= 128) { // it's a modifier key
_keyReport.modifiers |= (1<<(k-128));
k = 0;
} else { // it's a printing key
k = pgm_read_byte(_asciimap + k);
if (!k) {
setWriteError();
return 0;
}
if ((k & ALT_GR) == ALT_GR) {
_keyReport.modifiers |= 0x40; // AltGr = right Alt
k &= 0x3F;
} else if ((k & SHIFT) == SHIFT) {
_keyReport.modifiers |= 0x02; // the left shift modifier
k &= 0x7F;
}
if (k == ISO_REPLACEMENT) {
k = ISO_KEY;
}
}
// Add k to the key report only if it's not already present
// and if there is an empty slot.
if (_keyReport.keys[0] != k && _keyReport.keys[1] != k &&
_keyReport.keys[2] != k && _keyReport.keys[3] != k &&
_keyReport.keys[4] != k && _keyReport.keys[5] != k) {
for (i=0; i<6; i++) {
if (_keyReport.keys[i] == 0x00) {
_keyReport.keys[i] = k;
break;
}
}
if (i == 6) {
setWriteError();
return 0;
}
}
sendReport(&_keyReport);
return 1;
}
// release() takes the specified key out of the persistent key report and
// sends the report. This tells the OS the key is no longer pressed and that
// it shouldn't be repeated any more.
size_t Keyboard_::release(uint8_t k)
{
uint8_t i;
if (k >= 136) { // it's a non-printing key (not a modifier)
k = k - 136;
} else if (k >= 128) { // it's a modifier key
_keyReport.modifiers &= ~(1<<(k-128));
k = 0;
} else { // it's a printing key
k = pgm_read_byte(_asciimap + k);
if (!k) {
return 0;
}
if ((k & ALT_GR) == ALT_GR) {
_keyReport.modifiers &= ~(0x40); // AltGr = right Alt
k &= 0x3F;
} else if ((k & SHIFT) == SHIFT) {
_keyReport.modifiers &= ~(0x02); // the left shift modifier
k &= 0x7F;
}
if (k == ISO_REPLACEMENT) {
k = ISO_KEY;
}
}
// Test the key report to see if k is present. Clear it if it exists.
// Check all positions in case the key is present more than once (which it shouldn't be)
for (i=0; i<6; i++) {
if (0 != k && _keyReport.keys[i] == k) {
_keyReport.keys[i] = 0x00;
}
}
sendReport(&_keyReport);
return 1;
}
void Keyboard_::releaseAll(void)
{
_keyReport.keys[0] = 0;
_keyReport.keys[1] = 0;
_keyReport.keys[2] = 0;
_keyReport.keys[3] = 0;
_keyReport.keys[4] = 0;
_keyReport.keys[5] = 0;
_keyReport.modifiers = 0;
sendReport(&_keyReport);
}
size_t Keyboard_::write(uint8_t c)
{
uint8_t p = press(c); // Keydown
release(c); // Keyup
return p; // just return the result of press() since release() almost always returns 1
}
size_t Keyboard_::write(const uint8_t *buffer, size_t size) {
size_t n = 0;
while (size--) {
if (*buffer != '\r') {
if (write(*buffer)) {
n++;
} else {
break;
}
}
buffer++;
}
return n;
}
size_t Keyboard_::pressRaw(uint8_t k)
{
uint8_t i;
// Add k to the key report only if it's not already present
// and if there is an empty slot.
if (_keyReport.keys[0] != k && _keyReport.keys[1] != k &&
_keyReport.keys[2] != k && _keyReport.keys[3] != k &&
_keyReport.keys[4] != k && _keyReport.keys[5] != k) {
for (i=0; i<6; i++) {
if (_keyReport.keys[i] == 0x00) {
_keyReport.keys[i] = k;
break;
}
}
if (i == 6) {
setWriteError();
return 0;
}
}
sendReport(&_keyReport);
return 1;
}
// releaseRaw() takes the specified key out of the persistent key report and
// sends the report. This tells the OS the key is no longer pressed and that
// it shouldn't be repeated any more.
size_t Keyboard_::releaseRaw(uint8_t k)
{
uint8_t i;
// Test the key report to see if k is present. Clear it if it exists.
// Check all positions in case the key is present more than once (which it shouldn't be)
for (i=0; i<6; i++) {
if (0 != k && _keyReport.keys[i] == k) {
_keyReport.keys[i] = 0x00;
}
}
sendReport(&_keyReport);
return 1;
}
size_t Keyboard_::writeRaw(uint8_t c)
{
uint8_t p = pressRaw(c); // Keydown
releaseRaw(c); // Keyup
return p; // just return the result of press() since releaseRaw() almost always returns 1
}
Keyboard_ Keyboard;
#endif