Read from M5 CardKB to Arduino Uno using I2C

Greetings!

I have an M5Stack CardKB wired to an Arduino Uno via SCL/SDA. The Serial Monitor is showing characters passing just fine. The Tab, Shift, Fn, Sym, and Space keys work fine. However, the Backspace, Enter, Esc, and Arrow keys only display a "square" in the serial monitor and do not work as intended.

I thoroughly reviewed the following two threads and had no success. I feel like I am so close to this working. What am I missing to read these particular keys via I2C (and not via UART)?

// Wire M5 CardKB to your Arduino:
// Use standard jumpers to connect like-named pins: Blk to GND, Red to VCC, Yel to SDA, Wht to SCL.

#include <Wire.h>
#define CARDKB_ADDR 0x5F  // Define the I2C address of CardKB.  

void setup() {
  Serial.begin(9600);
  Wire.begin();
}

void loop() {
  Wire.requestFrom(CARDKB_ADDR, 1); // Request 1 byte from the CardKB.
  while (
    Wire.available()) { // If received data is detected,
    char c = Wire.read();  // store the received data.
    if (c != 0) {
      Serial.print(c); // Print the received data.
    }
  }
}

Physical setup:

The top row of buttons pressed, ESC through Backspace, output in the Serial Monitor:
CardKB_TopRow

Thank you so much for any help.

Some of the characters on a keyboard are 'non-printing characters' therefore to make them visible you need to translate them into something that is visible.
For example
Serial.print(c, HEX)
will turn an invisible character into a visible pair of hex characters. For example Esc is displayed as '1B'.

You may find a terminal program such as Realterm useful as it has the option to display the data in multiple formats.

My post was about changing the CardKB from I2C to UART which is not what you want if I'm not mistaken.

And what @mikb55 said: some keys on a keyboard are non printing.

Good luck programming! :slight_smile:

Update:

So, I created an I2C bus and added a SSD1306 OLED in order to test the non-printing characters. I did an I2C Scan and found both addresses as expected: OLED at 0x3c and CardKB at 0x5f. So far so good, right?

I updated my sketch accordingly to use both components since the hardware appears wired and tests correct. However, the keyboard output does appear in the Serial Monitor but does not appear on the OLED.

Bad OLED? No, I tested the OLED by itself with several other sketches and it tests good when the CardKB is unplugged.

I suspect something is amiss in my sketch code for this particular testing setup. Can anyone find anything wrong that jumps out?

Thank you again as always!


#include <Wire.h>
#define CARDKB_ADDR 0x5F  // Define the I2C address of CardKB.  
#define SCREEN_ADDRESS 0x3C  // Define the I2C address of the OLED.

#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

void setup() {
Serial.begin(9600);
Wire.begin();
 
display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS);
display.clearDisplay();
display.setTextSize(2);
display.setTextColor(WHITE);
display.setCursor(0,0);
}

void loop() {
  Wire.requestFrom(CARDKB_ADDR, 1); // Request 1 byte from the CardKB.
  while (
    Wire.available()) { // If received data is detected.
    char c = Wire.read();  // Store the received data.
    if (c != 0) {
      Serial.print(c); // Print the received data to the Serial Monitor.
      display.print(c); // Print the received data to the Screen. 
    }
  }
}

Wiring setup:

I2C Scan results with both devices attached and powered on:

image

...does not update the OLED screen. Take a look at some of the example sketches for that OLED library and you should see that you are missing something.

But even when you have figured that out, you will be no further forward. You don't seem to have understood that you are trying to print non-printable characters. The OLED will not be able to do it any more than Serial Monitor can.

Thank you, Paul! I'll go back and review that "display" command in some other OLED 1306
sketches.

For the non-printable characters, I hear what you are saying. But, for example, I would like to at least see the Enter key perform a carriage return. Does that make sense?

Yes, I get what you mean. But the Adafruit library you are using doesn't have those features. It's not a terminal emulator.

The U8G2 library is an alternative to that Adafruit library and it has an extension called U8log which might do what you want:

I've made several steps of progress today.

  1. Per @PaulRB I had to add a line after my display.print(c) command. That command only writes the data to the OLED buffer. You have to add the line 'display.display()' after it in order to push the buffer data to the screen.

  2. I looked up the keymapping codes in M5Stack's Git and was able to assign those codes directly to the Enter and Back keypresses.
    M5 CardKB raw values

  3. I am able to use an 'if/else if' statement to push a Carriage Return to the screen using 'display.println()' if the hex for 'Enter' was input (0x0D).

  4. I am able to capture the Back key input (0x08) using the same 'if/else if' method, but I have not figured out how to increment the cursor one space to the left no matter where it is onscreen. The 'display.setCursor' command wants exact X and Y coordinates. So, for now the workaround is for the Back/Del key to simply clear the whole screen.

// Wire OLED Display to your Arduino:
// Use standard jumpers to connect like-named pins: GND to GND bus, VCC to VCC bus, SCL to SCL bus, SDA to SDA bus.

// Wire M5 CardKB to your Arduino:
// Use standard jumpers to connect like-named pins: GND to GND bus, VCC to VCC bus, SCL to SCL bus, SDA to SDA bus.

#include <Wire.h>
#define CARDKB_ADDR 0x5F  // Define the I2C address of CardKB.  
#define SCREEN_ADDRESS 0x3C  // Define the I2C address of the OLED.

#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

void setup() {
Serial.begin(9600);
Wire.begin();
 
display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS);
display.clearDisplay();
display.setTextSize(2);
display.setCursor(0,0);
display.display();
}

void loop() {
  Wire.requestFrom(CARDKB_ADDR, 1); // Request 1 byte from the CardKB.
  while (
    Wire.available()) { // If received data is detected.
    char c = Wire.read();  // Store the received data.
    if (c == 0x0D) {  // If the data is an Enter keypress.
      display.println(); // Write a Carriage Return to the OLED buffer.
      display.display(); // Print the Carriage Return to the OLED screen.
    }
    else if (c == 0x08) { // If the data is a Back/Del keypress.
      display.clearDisplay();
//      display.setCursor(0,0); // Write a Backspace/Delete to the OLED buffer? How do I move the cursor 1 space to the left?
      display.display(); // Print the Backspace/Delete to the OLED screen.
    }

    else if (c != 0) {   // If the data is neither of the two above conditions nor is it blank.
      display.print(c); // Write the received data to the OLED buffer.
      display.display(); // Print the received data to the OLED screen. 
    }
  }
}

Successful screen output with functional carriage returns.

I don't have Fritzing but here is the basic schematic of my CardKB and OLED setup. The only differences are the Arduino model (Uno) and the devices themselves.

I hope this thread helps someone.