Even on the basis of the simplest - a light bulb with a button - a sketch, you can assemble a completely independent device. Do you think it will be a desk lamp? Not. Let's try to assemble the simplest version of the chord keyboard.

Chord keyboard is
a device that allows you to enter text, but in contrast to the usual full-size qwerty keyboard, the chord has a small number of buttons (usually from 3-5 to a dozen). In this case, the symbol is entered by simultaneously pressing not one, but several buttons - a chord. Also, a character can be entered not by one but several consecutive clicks. On the computer keyboard, we also use chords from time to time - for example, by entering a capital letter by simultaneously pressing the corresponding key and shift. Or using a variety of spells like Shift + Ctrl + S or Ctrl + Alt + Del. What kind of chord keyboard can be assembled using only one button? Recall the good old time-tested Morse code.
')
Of course, Morse code is not very suitable for a full-fledged set of texts, but it is excellent for an example of creating a completely meaningful working device based on the Arduino. So, we are going to create not a case study, but a prototype of a full-fledged commercial device. It should not just turn button presses into symbols, but also give the opportunity to switch the layout, and in the absence of user activity, go into sleep mode. In addition, there must be some kind of feedback that allows the user to be aware of the current state of the keyboard.
Let's start with the development of the alpha version of the device. For alpha, we need:
- Arduino-compatible board
- button
- Light-emitting diode
- 330 Ohm resistor
- 1 kΩ resistor
- 10 kΩ resistor
- bread board
- 7 wires for breadboard
- USB / miniUSB cable
Behavior of the device.The logic of recognizing points and dashes will be the simplest. If the button is pressed for less than a while, the device will determine the signal as a point. If longer - as a dash.
Long press - switch the keyboard layout from Cyrillic to Latin and back. As the indicator we will use the LED. Let the extinguished LED indicate the Cyrillic layout, burning - Latin. When the button is pressed during printing, the LED will light up (when Cyrillic is on, on Latin, on the contrary, it will go out).
If the key is not pressed for a minute - the device goes into sleep mode. In sleep mode, let the diode blink slowly. Exit from sleep mode - long press the button. When this pressing becomes sufficient to bring the device out of hibernation, the LED lights up to full brightness so that it becomes clear that the device is awake and the button can be released.
Sketch of the sketch:
Let's collect the scheme shown in the figure above. Black wiring on the diagram goes to the "ground", red - to + 5V. Do not forget that you need to connect the diode so that the negative, shorter output of the diode is connected to the “ground”.
The sketch of the sketch can be logically divided into 2 circuits - diode and button.
The LED has a very small internal resistance, and if we connect it directly, the high current generated in the circuit can burn the microcontroller port. Therefore, connecting a small 330 ohm resistance, we will limit the current. The amount of current is easy to estimate using the Ohm’s law, which states that the current is equal to the ratio of voltage to resistance: I = V / R. In our case, we take the resistance of the LED for 0 and get the current equal to 5 Volts / 330 Ohm = 15.1515 ... milliamperes by the formula. The ports of the microcontroller can withstand current up to 40 mA (how many your diode and microcontroller can withstand - see the specifications), so 15-16 mA is quite within the acceptable range.
Button chain is somewhat more complicated. As for the R2 resistor, everything is clear. We connect it to limit the current through the pressed button. Why do you need a resistor R3? Let's try to understand the "physics" of the digital port. If we connect a button without this resistor connecting the “ground” to port7, then when the button is not pressed, only blue wiring will be connected to the 7th port, through which no current flows. It would seem that this means that nothing is connected to the port. Not at all! This posting, playing the role of an antenna, will begin to collect pickups from the electrical grid, computer, etc. As a result, digital garbage, a chaotic sequence of logical zeros and ones, will fall on the port entrance - as if we were constantly randomly pressing / releasing a button. The resistance of R3 brings a “ground” to the port so that when the button is not pressed, a very specific potential arrives at the port - the potential of “earth”, a logical “zero”. When we close the button, the port will go + 5V - a logical “one”.
Sketch for morse keyboard:
#define BUTTON 7 #define LED 11 // Maximum dot duration, ms. All that is pressed 1/4 second or faster is a point. // The rest is a dash. #define MAX_DOT_PRESS_TIME 250 // To switch the layout, hold the button for 2 seconds. #define SWITCH_LAYOUT_TIME 2000 // Maximum pause between points and a dash in a letter - 0.3 seconds. // If the pause is longer, we consider the input of the letter completed and proceed to the next letter. #define DELIMITER_TIME 300 // If the button is not pressed for more than a minute, go to sleep mode. #define SLEEP_TIME 60000 // Exit from sleep mode by holding down the button for 2 seconds. #define WAKEUP_TIME 2000 // Modes of operation #define TYPING_MODE 0 #define SLEEP_MODE 1 int mode; // Current and previous button states. int btnState; int btnPrevState; // Time for pressing and releasing the button. unsigned long int timePress; unsigned long int timeRelease; // These characters will denote points and dashes. #define MORSE_DOT '*' #define MORSE_TIRE '-' // The maximum length of the Morse code symbol (in dots and dashes) #define MAX_MORSE_SYMBOL_LENGTH 8 // Buffer for writing Morse-symbol. byte morseSymbol [MAX_MORSE_SYMBOL_LENGTH]; unsigned int morseSymbolLen; // Morse code table. The nth element of the code corresponds to the nth character of the layout. char * code [] = {"* -", "- ***", "* -", "- *", "- **", "*", "*** -", "- - ** "," ** "," * --- "," - * - "," * - ** "," - "," - * "," --- "," * - * "," * - * "," *** "," - "," ** - "," ** - * "," **** "," - * - * "," --- * "," ---- "," - * - "," - * - "," - ** - "," ** - ** "," ** - "," * - * - "," * ---- "," ** --- "," *** - "," **** - "," ***** "," - **** " , "- ***", "--- **", "---- *", "-----", "......", "* - * - * -" , "--- ***", "- * - * -", "- * - * -", "* ---- *", "* - ** - *", "- *** * - "," - ** - * "," ** - ** "," - ** - "," - *** - "," ******** "," * - * - * "," ** - * - "," "}; // Cyrillic layout. char * layoutCyrillic [] = {"a", "b", "c", "g", "d", "e", "g", "g", "u", "d", "k" , "l", "m", "n", "o", "p", "p", "s", "t", "y", "f", "x", "c", " h "," sh "," u "," s "," "," e "," yu "," i "," 1 "," 2 "," 3 "," 4 "," 5 " , "6", "7", "8", "9", "0", ".", ",", ":", ";", "(", "\ '", "\" " , "-", "/", "?", "!", "* DELIMITER *", "* ERR *", "@", "* END *", "" "; // Latin layout. Char * layoutLatin [] = {"a", "b", "w", "g", "d", "e", "v", "z", "i", "j", "k", " l "," m "," n "," o "," p "," r "," s "," t "," u "," f "," h "," c "," ö " , "ch", "q", "y", "x", "é", "ü", "ä", "1", "2", "3", "4", "5", " 6 "," 7 "," 8 "," 9 "," 0 ",". ",", ",": ","; "," ("," \ '"," \ "", " - "," / ","? ","! "," * DELIMITER * "," * ERR * "," @ "," * END * "," "}; char ** currentLayout; char ** newLayout ; // Brightness blinking in sleep mode #define MAX_SLEEP_BRIGHTNESS 16 int sleepBrightness;. // brightness at the exit from the sleep mode 255 #define WAKEUP_BRIGHTNESS void setup () {Serial.begin (9600);. pinMode (LED, oUTPUT); pinMode ( BUTTON, INPUT); mode = TYPING_MODE; morseSymbolLen = 0; btnPrevState = LOW; timePress = millis (); timeRelease = millis (); currentLayout = layoutLatin; newLayout = 0; } // Send the entered character to the computer. void sendMorseSymbol () {int i, j; if (morseSymbolLen <1) {return; } for (i = 0; code [i] [0]! = '\ 0'; i ++) {// Compare the entered character with the characters from the Morse code table. for (j = 0; (j <morseSymbolLen) && (code [i] [j]! = '\ 0'); j ++) {if (code [i] [j]! = morseSymbol [j]) {j = -one; break; }} if ((j! = -1) && (j == morseSymbolLen) && (code [i] [j] == '\ 0')) {// The symbol from the Morse code table corresponds to the entered character. // Send the character to the computer. Serial.print (currentLayout [i]); morseSymbolLen = 0; return; }} // Character not found in the table. Print the unrecognized symbol. Serial.print ("["); for (i = 0; i <morseSymbolLen; i ++) {Serial.print (morseSymbol [i]); } Serial.print ("]"); morseSymbolLen = 0; } void loop () {switch (mode) {case TYPING_MODE: typingLoop (); break; case SLEEP_MODE: sleepLoop (); break; }} void typingLoop () {int ledLevel; // If the Cyrillic layout is on, the diode will be lit when pressed. // If the Latin layout is on, the diode will go out when pressed. if (newLayout == 0) {ledLevel = (currentLayout == layoutCyrillic)? 255: 0; } else {ledLevel = (newLayout == layoutCyrillic)? 255: 0; } btnState = digitalRead (BUTTON); if (btnState == HIGH) {analogWrite (LED, ledLevel); if (btnPrevState == LOW) {// The user pressed the button. timePress = millis (); } else {if ((newLayout == 0) && ((millis () - timePress)> SWITCH_LAYOUT_TIME)) {// If the button has been held down for a long time, change the layout. if (currentLayout == layoutLatin) {newLayout = layoutCyrillic; Serial.println ("\ nLayout: cyrillic \ n"); } else {newLayout = layoutLatin; Serial.println ("\ nLayout: latin \ n"); }}}} else {analogWrite (LED, 255-ledLevel); if (btnPrevState == HIGH) {// The user released the button. timeRelease = millis (); // If we just changed the layout, change it. if (newLayout! = 0) {sendMorseSymbol (); currentLayout = newLayout; newLayout = 0; } else {if ((timeRelease - timePress)> MAX_DOT_PRESS_TIME) {morseSymbol [morseSymbolLen ++] = MORSE_TIRE; } else {morseSymbol [morseSymbolLen ++] = MORSE_DOT; } if (morseSymbolLen> MAX_MORSE_SYMBOL_LENGTH) {// We entered the maximum number of Morse signals that can be // in the Morse symbol. We analyze the symbol and send it to the computer. sendMorseSymbol (); }}} else {unsigned long int timePause = millis () - timeRelease; if (timePause> SLEEP_TIME) {sleepBrightness = MAX_SLEEP_BRIGHTNESS; mode = SLEEP_MODE; Serial.println ("\ nSLEEP_MODE \ n"); } else if ((timePause> DELIMITER_TIME) && (morseSymbolLen> 0)) {// If the pause was too long, finish typing the letter. sendMorseSymbol (); }}} btnPrevState = btnState; delay (10); } void sleepLoop () {// We sleep, so we rarely check the button status - every 0.3 s. delay (300); // Determine whether the button was pressed long enough to exit hibernation. boolean flagWakeUp = ((millis () - timePress)> = WAKEUP_TIME); // Blink the LED. analogWrite (LED, sleepBrightness); sleepBrightness = MAX_SLEEP_BRIGHTNESS - sleepBrightness; btnState = digitalRead (BUTTON); if (btnState == LOW) {if (btnPrevState == HIGH) {timeRelease = millis (); if (flagWakeUp) {// Wake up. mode = TYPING_MODE; Serial.println ("\ nTYPING_MODE \ n"); }}} else {if (btnPrevState == LOW) {timePress = millis (); } else {if (flagWakeUp) {// Signal the user that the button was held long enough // for the device to wake up. analogWrite (LED, WAKEUP_BRIGHTNESS); }}} btnPrevState = btnState; }
Copy the text of the sketch in the Arduino IDE. Verify button

compile the sketch.
If the compilation went smoothly, upload the code to the microcontroller - connect the assembled device layout to the computer with a miniUSB cable and press the Upload button

. If the IDE declares that it cannot connect to the device, indicate which USB port the Arduino is on by going to the Tools -> Serial Port menu and selecting our device from the list.
The program will begin work immediately after uploading the sketch to the Arduino. Button

launch the COM port monitor. Let's try typing the word "hello." In Morse code, it will look like this:
* - * * - * ** * - * -At the same time, a continuously burning light bulb (indicating that we are in the Latin layout) will go out while the button is pressed. If we have adjusted to the rhythm of the program and correctly “tapped” all the dots and dashes (not forgetting the pauses between letters), the Latin “priwet” will appear on the screen of the COM port monitor:

Switch to Cyrillic by pressing and holding the button. The light bulb, following the press, will go out. As soon as the press is long enough and the layout switches, the LED will light up. When switching from Cyrillic to Latin, the light will behave exactly the opposite - it will light up, and when you switch the layout, it will go out.
Say hello again. Instead of the expected Russian letters, cracks will come out in the COM-monitor window. Naturally - the sketch sends Cyrillic to Unicode.
Let's leave the device alone for a minute, let's not press buttons on it. After a minute, the sleep mode will turn on, which will be signaled by the LED, blinking a dim light.
Alpha version is ready. She does exactly what we planned. But before the release version of the device so far away. It would be worth working the following points:
- Indication with one monochromatic LED is not clear. This is exactly the version of the interface that is completely unintuitive and understandable only to the creator of the device. However, with the help of a single monochromatic diode it would hardly have been possible to do something radically better.
- The fixed border between the duration of a dot and the duration of a dash is inconvenient. This approach binds the user to the rhythm that our Morse keyboard is tuned to. You can, of course, work on the logic, forcing the program to adapt to the speed of the user. This would be a good exercise for a novice programmer. But from the point of view of a commercial project - a waste of time. It is much easier and more convenient to replace one-button input, for example, with two-button input: one for a dot, the second for a dash.
- One of the modes of using chord keyboard - blind printing. Therefore, it would be worthwhile to supplement the LED display with vibration - so that the user can feel the state of the keyboard without looking at it.
We will try to work on the above in the next article.
Previous articles:Arduino: first acquaintance