Besides supporting analog sensors, the ATOM's Grove port can also be used to interface with I2C sensors. This post demonstrates how to do this using an I2C keyboard. First we read keyboard input into a character array. In the second application we filter and transform incoming characters. Finally, we combine that with the scrolling text message code from an earlier post.
The first two applications should work almost unchanged on regular ESP32 (or even ESP8266) dev boards.
Experimenting (i.e. playing) with the keyboard, there is a problem: the Sym + "." key does NOT return the ">" character.
Store Keyboard Input into a Character Array
The CardKB is handy method of getting user input, but for getting sequences of characters, we must build the result one keypress at a time. The following application does that, and in addition:
- The maximum length of the character array is enforced
- When the backspace key is pressed, the last character of the array is removed
- When the escape key is pressed, the character array is cleared
- When the return key is pressed, input is "finalized" in that no additional characters are added to the result
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 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 | /* * CardKB-Keyboard-00.ino * * By: Mike Klepper * Date: 26 April 2020 * * Read input from an I2C keyboard and store * result in a character array * * Demonstrates basic char array usage * * See post on patriot-geek.blogspot.com * for details */ #include "M5Atom.h" #define CARDKB_ADDR 0x5F #define BKSP 8 #define CR 13 #define ESC 27 const int maxMessageLength = 16; char msg[maxMessageLength]; uint8_t charIndex = 0; bool messageEntered = false; void setup() { M5.begin(true, true, true); delay(20); Wire.begin(26, 32); Serial.println(""); } void loop() { Wire.requestFrom(CARDKB_ADDR, 1); while(Wire.available() && !messageEntered) { char c = Wire.read(); if(c != 0) { Serial.print(c); Serial.print(F(" - ")); Serial.println(c, DEC); if(charIndex >= 0 && charIndex < maxMessageLength || c == BKSP || c == CR || c == ESC) { if(c != BKSP && c != CR && c != ESC) { msg[charIndex++] = c; } else if(c == BKSP && charIndex > 0) { msg[--charIndex] = '\0'; Serial.print(F("Backspace! ")); } else if(c == ESC) { charIndex = 0; memset(msg, 0, sizeof(msg)); Serial.print(F("Resetting input! ")); } else if(c == CR) { messageEntered = true; Serial.print(F("Done! ")); } } else { Serial.print(F("Maximum message length reached! ")); } Serial.print(F("msg = ")); Serial.println(msg); Serial.print(F("charIndex = ")); Serial.println(charIndex); } } } |
First thing to notice is that Wire.h is apparently not included. As it goes, when the second argument of the M5.begin command is true (line 31), the M5 API includes Wire.h for us. Here's a line-by-line explanation of the code:
Line | Comment |
---|---|
18 | I2C bus address of the CardKB |
20 - 22 | ASCII code for backspace, enter key, and escape key |
25 | Array of characters, with length specified in line above |
26 | Current character (think of it as a cursor position) |
27 | When messageEntered becomes true, we are done getting input! |
31 | Second argument is true, so Wire.h will be included |
33 | Initialize the Wire library using pins 26 and 32 for SCL and SDA, respectively |
41 | If messageEntered is false |
43 | Read one byte as a character |
45 | If we have a non-zero character |
47 - 49 | Print it in the serial monitor |
51 | Enforce max length requirement, but let BKSP, CR, and ESC through |
53 | If character is not one of those three |
55 | Increment charIndex and add c to end of the char array |
57 | If incoming character is a backspace |
59 | Decrement charIndex and fill with null terminator |
62 | If it is the escape character |
64 | Reset charIndex |
65 | Empty out the msg char array |
68 - 72 | If character is carriage return, we are done! |
76 | Ignore incoming character as msg is as long as we allow |
79 - 82 | Display the msg and the charIndex after each keypress |
Filtering and Transforming Keyboard Input
In order to display the message entered by the keyboard, we must do the following:
- Lower-case letters are converted to upper case letters (since that is what the 5 x 5 font has available)
- Characters other than letters and digits are ignored (for the same reason)
It makes sense to do these just before the incoming character is added to the msg char array.
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 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 | /* * CardKB-Keyboard-01.ino * * By: Mike Klepper * Date: 26 April 2020 * * Read input from an I2C keyboard, filter and * transform it * * See post on patriot-geek.blogspot.com * for details */ #include "M5Atom.h" #define CARDKB_ADDR 0x5F #define BKSP 8 #define CR 13 #define ESC 27 const int maxMessageLength = 16; char msg[maxMessageLength]; uint8_t charIndex = 0; bool messageEntered = false; void setup() { M5.begin(true, true, true); delay(20); Wire.begin(26, 32); Serial.println(""); } void loop() { Wire.requestFrom(CARDKB_ADDR, 1); while(Wire.available() && !messageEntered) { char c = Wire.read(); if(c != 0) { Serial.print(c); Serial.print(F(" - ")); Serial.println(c, DEC); if(charIndex >= 0 && charIndex < maxMessageLength || c == BKSP || c == CR || c == ESC) { if(c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == ' ') { msg[charIndex++] = c; } else if(c >= 'a' && c <= 'z') { msg[charIndex++] = c - 32; } else if(c == BKSP && charIndex > 0) { msg[--charIndex] = '\0'; Serial.print(F("Backspace! ")); } else if(c == ESC) { charIndex = 0; memset(msg, 0, sizeof(msg)); Serial.print(F("Resetting input! ")); } else if(c == CR) { messageEntered = true; Serial.print(F("Done! ")); } } else { Serial.print(F("Maximum message length reached! ")); } Serial.print(F("msg = ")); Serial.println(msg); Serial.print(F("charIndex = ")); Serial.println(charIndex); } } } |
The only real difference between this code and the previous app's is that instead of indiscriminately adding the incoming character to the end of msg (lines 53-56 in first application) we do the following:
Line | Comment |
---|---|
51 | If the incoming character is either a digit, an upper-case letter, or a space |
53 | Increment charIndex and add it to the end of msg |
55 | Else if the incoming character is a lower-case letter |
57 | Increment charIndex, convert it to uppercase, and add it to the end of msg |
Displaying Input
This last application combines the above keyboard input code with the code for displaying scrolling text from an earlier post, each wrapped in its own function.
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 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 | /* * CardKB-Keyboard-02.ino * * By: Mike Klepper * Date: 26 April 2020 * * Read input from an I2C keyboard and display it! * * See post on patriot-geek.blogspot.com * for details */ #include "M5Atom.h" #include <Adafruit_GFX.h> #include <Adafruit_NeoMatrix.h> #include <Adafruit_NeoPixel.h> #define DISPLAY_PIN 27 #define CARDKB_ADDR 0x5F #define BKSP 8 #define CR 13 #define ESC 27 int directionAndOrientation = NEO_MATRIX_TOP + NEO_MATRIX_LEFT + NEO_MATRIX_ROWS; int pixelType = NEO_GRB + NEO_KHZ800; Adafruit_NeoMatrix matrix = Adafruit_NeoMatrix(5, 5, DISPLAY_PIN, directionAndOrientation + NEO_MATRIX_PROGRESSIVE, pixelType); int xPos = matrix.width(); const int maxMessageLength = 16; char msg[maxMessageLength]; uint8_t charIndex = 0; bool messageEntered = false; void setup() { M5.begin(true, true, true); delay(20); Wire.begin(26, 32); Serial.println(""); matrix.begin(); matrix.setTextWrap(false); matrix.setBrightness(60); matrix.setTextColor(matrix.Color(80, 0, 80)); } void loop() { if(!messageEntered) { getMessageFromCardKB(); } else { displayMessage(msg); } } void getMessageFromCardKB() { Wire.requestFrom(CARDKB_ADDR, 1); while(Wire.available() && !messageEntered) { char c = Wire.read(); if(c != 0) { Serial.print(c); Serial.print(F(" - ")); Serial.println(c, DEC); if(charIndex >= 0 && charIndex < maxMessageLength || c == BKSP || c == CR || c == ESC) { if(c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == ' ') { msg[charIndex++] = c; } else if(c >= 'a' && c <= 'z') { msg[charIndex++] = c - 32; } else if(c == BKSP && charIndex > 0) { msg[--charIndex] = '\0'; Serial.print(F("Backspace! ")); } else if(c == ESC) { charIndex = 0; memset(msg, 0, sizeof(msg)); Serial.print(F("Resetting input! ")); } else if(c == CR) { messageEntered = true; Serial.print(F("Done! ")); } } else { Serial.print(F("Maximum message length reached! ")); } Serial.print(F("msg = ")); Serial.println(msg); Serial.print(F("charIndex = ")); Serial.println(charIndex); } delay(1); } } void displayMessage(char message[]) { Serial.print("Displaying "); Serial.println(msg); String msgString = String(message); int msgLength = msgString.length(); int charWidth = 5; int numTrailingSpaces = 3; int maxLeftPosition = (msgLength + numTrailingSpaces) * (charWidth + 1); int scrollDelay = 100; while(true) { matrix.fillScreen(0); matrix.setCursor(xPos, 0); matrix.print(msgString); if(--xPos < -maxLeftPosition) { xPos = matrix.width(); } matrix.show(); delay(scrollDelay); } } |
Click here to go to the table of contents for this series.
No comments:
Post a Comment