I have a set of buttons wired to an arduino leonardo that are meant to send either single keystrokes to a computer "a, b, c..." etc, or a key sequence like ctrl+z (to perform an undo command.) Effectively this is a little usb keyboard with pre defined buttons. The way the struct is now, I can only manage to send one keystroke, never a modifier key+letter like I need.
The full code is listed here under "working code": Arduino sending keystrokes via push-buttons. Proper bouncing and manually setting buttons?
It just debounces the buttons and sends back actions (just single letters right now) using the ones defined in the struct. (thanks very much jwpat7)
The part in question is this
struct ButtonStruct buttons[nSwitches] = {
{0, sw0, 'A'},
{0, sw1, 'B'},
{0, sw2, 'C'},
{0, sw3, 'D'},
{0, sw4, 'E'},
{0, sw5, 'F'},
{0, sw6, 'G'},
{0, sw7, 'H'}};
I effectively want to replace the 'A' in {0, sw0, 'A'} to something that sends ctrl+z keystroke/keysequence instead of the letter 'A'.
I've tried KEY_LEFT_CTRL, setting a variable like char ctrlKey = KEY_LEFT_CTRL; and trying {0, sw0, ctrlKey, 'z'}, the "substitution" hex here: http://www.nthelp.com/ascii.htm and a number of other things.
I've read through a lot and I can't seem to find anything relevant. I've been told it's possible to use pointers to create a key-sequence but a -> operand in there just throws errors. I'm new and I realize I might have a core principal wrong, but isn't there some simple syntax to send a ctrl+z command (and then releasing it obviously) using that space? When I put {0, sw0, KEY_LEFT_CTRL, 'z'} it just reports "Lz", using the last letter in KEY_LEFT_CTRL instead of treating it as a modifier. I'm so confused
(Also right now one press = one character and doesn't continually report like holding down a key on a regular keyboard. I'd like to fix that too but I'd settle for just a bloody ctrl+z at this point.)
EDIT: Current Code (compiled!)
enum { sw0=2, sw1=3, sw2=4, sw3=5, sw4=6, sw5=7, sw6=8, sw7=9}; // Switchbutton lines
enum { nSwitches=8, bounceMillis=42}; // # of switches; debounce delay
struct ButtonStruct {
unsigned long int bounceEnd; // Debouncing-flag and end-time
// Switch number, press-action, release-action, and prev reading
byte swiNum, swiActP, charMod, swiActR, swiPrev;
};
struct ButtonStruct buttons[nSwitches] = {
{0, sw0, 'z', KEY_LEFT_CTRL},
{0, sw1, 'B'},
{0, sw2, 'C'},
{0, sw3, 'D'},
{0, sw4, 'E'},
{0, sw5, 'F'},
{0, sw6, 'G'},
{0, sw7, 'H'}};
//--------------------------------------------------------
void setup() {
for (int i=0; i<nSwitches; ++i)
pinMode(buttons[i].swiNum, INPUT_PULLUP);
Keyboard.begin();
}
//--------------------------------------------------------
byte readSwitch (byte swiNum) {
// Following inverts the pin reading (assumes pulldown = pressed)
return 1 - digitalRead(swiNum);
}
//--------------------------------------------------------
void doAction(byte swin, char code, char tosend, char chmodi) {
if (chmodi) { // See if modifier needed
Keyboard.press(chmodi);
Keyboard.press(tosend);
delay(100);
Keyboard.releaseAll();
} else {
Keyboard.write(tosend);
}
}
//--------------------------------------------------------
void doButton(byte bn) {
struct ButtonStruct *b = buttons + bn;
if (b->bounceEnd) { // Was button changing?
// It was changing, see if debounce time is done.
if (b->bounceEnd < millis()) {
b->bounceEnd = 0; // Debounce time is done, so clear flag
// See if the change was real, or a glitch
if (readSwitch(b->swiNum) == b->swiPrev) {
// Current reading is same as trigger reading, so do it
if (b->swiPrev) {
doAction(b->swiNum, 'P', b->swiActP, b->charMod);
}
}
}
} else { // It wasn't changing; but see if it's changing now
if (b->swiPrev != readSwitch(b->swiNum)) {
b->swiPrev = readSwitch(b->swiNum);
b->bounceEnd = millis()+bounceMillis; // Set the Debounce flag
}
}
}
//--------------------------------------------------------
long int seconds, prevSec=0;
void loop() {
for (int i=0; i<nSwitches; ++i)
doButton(i);
}
1 Answer 1
(削除) First off, consider trying '032円' or '\x1A' for the ctrl-z. (See, eg, How to send ctrl+z in C on stackoverflow.) (削除ここまで)
To send a ctrl-z, use something like:
Keyboard.press(ctrlKey);
Keyboard.press('z');
delay(100);
Keyboard.releaseAll();
For more examples of using modifier keys, see the KeyboardPress page at arduino.cc, and the example program KeyboardLogout.ino
. [To coordinate this with the struct
data structure approach, you could use code somewhat like
tosend = b->actionChar1;
if (tosend < ' ') { // See if modifier needed
Keyboard.press(ctrlKey);
Keyboard.press(tosend+96); // or is it 64?
delay(100);
Keyboard.releaseAll();
} else {
Keyboard.write(tosend);
}
[You'll need to test which offset, 96 or 64, works in the above code; I don't have a Leonardo or similar to run this on, and haven't looked at the Keyboard library source to see how it treats this.] Another way is to add a modifier-byte field in the struct
, and issue it if its non-zero.]
Second, suppose the current form of ButtonStruct
is analogous to
struct ButtonStruct {
unsigned long int bounceEnd; // Debouncing-flag and end-time
// Switch number, press-action and prev reading
byte swiNum, actionChar1, swiPrev;
};
where actionChar1
is the action character in use now. You could add another byte to the struct
, say actionChar2
, and after a statement like Keyboard.write(b->actionChar1);
add the following:
if (b->actionChar2)
Keyboard.write(b->actionChar2);
which says to write out actionChar2
if it is non-zero.
In some of the questions linked to the previous question, you may see advice that Keyboard.write
won't send non-printing characters. I don't know if that's so, but in any case, there are mods available for Keyboard
(ie, changes to the library) that allow non-printing characters and special characters to be sent.
Also note, if your program gets close to using all the RAM memory, you may want to keep buttons[]
in ProgMem and access it from there so it doesn't need to be copied into RAM. (See the three ProgMem questions that are linked in the top right sidebar . . .) If you change the struct to use strings instead of single chars for its actions, using less RAM will increase in importance.
Edit re: "what I don't know how to change ---> {0, sw0, 'A'}":
{0, sw0, 'A'}
initializes an element of buttons[]
.
In buttons[]
, each element is a ButtonStruct
, because buttons[]
is an array of ButtonStruct
s. (For more about structs, see syntax and examples in eg Struct (C programming language) in wikipedia.)
You control what information can be in each element of buttons[]
by modifying the definition of ButtonStruct
. If you need more data items in each buttons[]
entry, then add more data items in the definition of ButtonStruct
. Likewise for fewer.
After you decide what information you need to have available to process a button press, modify ButtonStruct
to contain exactly that information. It may be that some information will apply in some cases but not in all. If so, when you initialize buttons[]
, have some value (say 0) that stands for not-used, and fill in not-used spots with that value.
For example, if you need key modifiers (shift, ctrl, alt) add a modifier item to ButtonStruct
.
Whenever you change ButtonStruct
, you need to change buttons[]
initialization to match. For example, if ButtonStruct
were to include Keyboard Modifiers and looked like
struct ButtonStruct {
// Switch number, press-actions and prev reading
byte swiNum, actionChar, charMod, swiPrev;
unsigned long int bounceEnd; // Debouncing-flag and end-time
};
[with some items in a different order than before!] your initializer elements in the buttons[]
declaration could look like
{ sw29, 'Q' },
where charMod
, swiPrev
, and bounceEnd
are left to default to zero, or like
{ sw37, 'R', KEY_LEFT_CTRL},
where swiPrev
and bounceEnd
default to zero.
Then, in your button-action code you could say:
tosend = b->actionChar;
chmodi = b->charMod;
if (chmodi) { // See if modifier needed
Keyboard.press(chmodi);
Keyboard.press(tosend);
delay(100);
Keyboard.releaseAll();
} else {
Keyboard.write(tosend);
}
For those elements of buttons[]
where you specified a modifier, the if
's first branch executes, and for those where you didn't, ie, for plain unmodified characters, the second one does.
There are all sorts of variants on these methods. For example, instead of specifying action characters and modifiers in the buttons[]
array, you could change it so that you specify function names, or could have several classes of actions. If 80% of the actions were to issue one simple character, make that class 0, so the action class can usually default to 0.
-
I'm afraid this doesn't help. Using {0, sw0, '032円'} or the '\x1A' reports nothing when the button is pressed. I have also previously tried the method of adding another action but it still leaves me with no clear syntax when trying to report a modifier like KEY_LEFT_CTRL in the struct. It never registers a ctrl at all much less combines it with 'z'..sylcat– sylcat2015年11月12日 22:17:43 +00:00Commented Nov 12, 2015 at 22:17
-
This post is mistaken as it overlooks the fundamental fact that keyboards don't send ASCII codes - rather they send scan codes.Chris Stratton– Chris Stratton2015年11月13日 01:49:26 +00:00Commented Nov 13, 2015 at 1:49
-
1@ChrisStratton, yes, physical keyboards send scan codes, but the Arduino Keyboard library API accepts (for most calls) ASCII codes, not scan codes. It also accepts some modifier-key codes like I mentioned in an answer-edit a few minutes agoJames Waldby - jwpat7– James Waldby - jwpat72015年11月13日 02:48:06 +00:00Commented Nov 13, 2015 at 2:48
-
I apologise, I haven't made something clear. THIS is what I don't know how to change ---> {0, sw0, 'A'} I haven't used a setup like that before. All these methods and I still need to know what goes in here---> {0, sw0, 'A'} to get new output. I need some kind of alternate example for that bit of code to understand why I'm seeing the strange output I'm seeing. The only thing that ever works in there is 'A' or a single character. If I use this setup without knowing how to change it I'm going to try something like {0, sw0, actionChar1} or worse and It's going to fail again and again.sylcat– sylcat2015年11月13日 20:47:40 +00:00Commented Nov 13, 2015 at 20:47
-
@sylcat, see editJames Waldby - jwpat7– James Waldby - jwpat72015年11月14日 06:17:51 +00:00Commented Nov 14, 2015 at 6:17
swiActR
take it out of the struct. In current code,{sw0, 'R', KEY_LEFT_CTRL}
inits bounceEnd equal to sw0, swiNum to R, and swiActP to KEY_LEFT_CTRL. Initializers have to be in the right order, ie have to match the order declared in struct. Values can be left off at the end, to default to 0, but can't be left off the frontchar action, char chmodi
tochar tosend, char chmodi
and delete the two lines with b-> at front. In doButton add b->charMod to the first doAction call. Deleteelse { doAction(b->swiNum, 'R', b->swiActR);
if you don't plan to do an action for button releases, else perhaps fix the code up with a modifier for the release action char. Just make the function calls and function defs match up, only use vars that are in scope, etc