USB mini keyboard on Arduino Pro Micro

I know that many DIY enthusiasts have ever tried to make their own USB keyboard and / or mouse to automate the sending of commands. This can be seen from the number of questions on this topic on Stack Overflow. You can think of many applications for this kind of device. From the simplest “mouse pull” so that the computer does not go into sleep mode (create an imitation of user activity), to control various devices. In my case, the device is designed to control the “smart dacha” system. Rather, to test its functions. By pressing the button, the corresponding file from the SD card opens, and its contents are displayed to the computer as if you were entering it from a conventional keyboard.

The device itself is connected to the smart dacha server via USB and is identified by it as a USB keyboard. The server itself is a Fujitsu-Siemens ESPRIMO Q5020 mini-computer, which looks like an old Mac Mini. Works under Ubuntu 20. However, describing the whole project of the “smart summer cottage” is not the topic of this article (I hope someday I will write about the global project as well). Here I will only describe the creation of a homemade USB keyboard.

Since the device was originally conceived as a test device, I did not particularly pursue its appearance, and as a case I used a plastic junction box with a size of about 100x100x70. A very convenient box for homemade products: ideal dimensions, it is easy to make any holes with an ordinary stationery knife, a sealed case.

Arduino Pro Micro (Leonardo)
Arduino Pro Micro (Leonardo)

The heart of this box is the Arduino Pro Micro based on the Atmega32u4. This particular board was chosen because it has a hardware USB port, which is the main one for my project. Because it’s still a USB keyboard. And to be precise, the USB HID device (Human Interface Device), this category includes keyboards, mice, joysticks and other input devices. I decided not to bother with the connectors, but to solder everything directly to the board.

4x4 Keypad
4×4 Keypad

As a keyboard, I took a 4×4 Keypad, which I have been idle for about 7 years. Finally, it was used. I really do not like soldering a large number of wires (so to speak, I suffer from wire phobia). Therefore, I purchased an i2c module on the PCF8574T for the keyboard. The module already had a soldered connector to which the keyboard cable is connected. As a result, we only need to solder 4 wires: power, as well as SDA, SCL. The device address is selected by three DIP switches on the module board. I have them all OFF, which corresponds to the address 0x20.

i2c module on PCF8574T
i2c module on PCF8574T

In the case of the Arduino Pro Micro, SDA connects to Pin 2, SCL to Pin 3. I don’t remember exactly, but it seems that the Arduino Nano uses the same pins for i2c. I also wanted to somehow display the state and current mode of the device. It was possible, of course, to connect a full-fledged screen, for example, an LCD1602 module with the same i2c. Or it is just stupid to make an indication using several LEDs.

7-segment indicator on TM1637
7-segment indicator on TM1637

But I decided to take something in between – a module consisting of four seven-segment displays with a built-in TM1637 controller. The connection is similar to i2c, although the pins are called DIO and CLK here. Soldering to pins 7 and 8 on the Arduino board, respectively. The example from the TM1637 standard library works right away, without any modifications.

And of course, we need a module for reading SD cards. I have chosen a module with an SPI interface.

SD module with SPI interface
SD module with SPI interface

Connection: Pin 10 -> CS, Pin 16 -> MOSI, Pin 14 -> MISO, Pin 15 -> SCK. There were no problems here either, and the example from the library “wound up” immediately, without additional modifications.

Libraries used in the project (either standard or loaded through the library manager in the Arduino IDE):

TM1637 Driver by AKJ v2.11 (downloadable), SD by Arduino (standard), I2CKeyPad by Rob Tillaart v0.3.1 (downloadable), Keyboard by Arduino (standard), Mouse by Arduino (standard)

Complete device
Complete device

When assembled, the device looks like this. The keyboard is glued on top with double-sided tape (it was already on the keyboard initially). The indicator on the front panel did not fit, so I screwed it to the side panel – this is even more convenient, given that the box is on the table, and the indicator turns out to be turned towards the user.

SD module
SD module

For the SD card, I had to cut a slot in the side wall. Therefore, you can only insert or remove a MicroSD card with tweezers. But this is not scary, since the tweezers are always at hand. And you don’t need to do this too often – just write the files to the card once.

Looks scary, but works great
Looks scary, but works great

The device is controlled as follows. When connected to a computer via USB, it is detected as a HID device. Further, on the built-in keyboard, select the mode with the buttons A, B, C, D. In my case, only the A button is enabled so far. Press it, the corresponding symbol lights up on the indicator. Next, use the numeric key to select the script that will be transferred via the USB port. In this case, the program searches on the SD card in the root directory for a file with a name corresponding to the pressed key: from 0.txt to 9.txt. After that, the file is read line by line and immediately issued to the keyboard port. That is, everything happens as if you would type text from the selected file from a regular keyboard, only instantly. You can make the effect of a “typewriter” – for this, in some parts of the sketch, you can uncomment delay (). You can stop entering text at any time by holding down the “asterisk” key

… To start it again, you need to press the A key, and then the number corresponding to the desired file.

// Подключение к модулям Keypad, LED и SD
// Pin 2  -> KEY SDA
// Pin 3  -> KEY SCL
// Pin 7  -> LED DIO
// Pin 8  -> LED CLK
// Pin 10 -> SD  CS
// Pin 16 -> SD  MOSI
// Pin 14 -> SD  MISO
// Pin 15 -> SD  SCK

#include "Wire.h"
#include "I2CKeyPad.h"
#include "Mouse.h"
#include "Keyboard.h"
#include "TM1637.h"
#include "SPI.h"
#include "SD.h"
//#include "my_mouse.h"

// 7-сегментный индикатор на 4 знакоместа на TM1637
TM1637 tm(7, 8);
// Клавиатура на 16 клавиш (4х4), подключенная по i2c
I2CKeyPad keyPad(0x20);

// Режим не выбран (пауза)
#define MODE_0                0
// Была нажата клавиша А
#define MODE_A                1
// Была нажата клавиша B
#define MODE_B                2
// Была нажата клавиша C
#define MODE_C                3
// Была нажата клавиша D
#define MODE_D                4
// Режим чтения из файла на SD
#define MODE_FILE             5
// Расположение клавиш на клавиатуре
char keymap[19] = "123A456B789C*0#DNF";  // N = NoKey, F = Fail
// Текущий режим работы программы (пауза)
char mode = MODE_0;
// Последняя нажатая клавиша
int last_key = -1;
// Файл на SD карте
File f;
// Сообщение на дисплее и имя файла на SD
String msg, fname;
// Переменная для хранения строк из файла
String data;
// Номер текущей строки файла
int line = 0;

void setup()
{
  // Инициализируем клавиатуру
  Keyboard.begin();
  // Инициализируем мышку
  Mouse.begin();
  // Инициализируем монитор
  Serial.begin(115200);
  // Инициализация i2c
  Wire.begin();
  Wire.setClock(400000);
  if (keyPad.begin() == false)
  {
    // Мы потеряли клавиатуру на 16 клавиш :-(
    tm.display("KBER");
    while (1);
  }
  keyPad.loadKeyMap(keymap);
  // Инициализация дисплея с яркостью 1 (этого достаточно, все что выше - вырвиглаз)
  tm.begin();
  tm.setBrightness(1);
  tm.clearScreen();
  // CS = 10
  if (!SD.begin(10))
  {
    // Не инициализировалась SD карта
    tm.display("SDER");
    while (1);
  }
  // Если все ок, показать на дисплее соответствующую надпись и ждать команды
  tm.display("OK ");
}
// true, если нажатая клавиша - цифровая
bool is_num(int key)
{
  if ((key >= 0 && key <= 2) || (key >= 4 && key <= 6) || (key >= 8 && key <= 10)) return true;
  return false;
}

void loop()
{
  // Если режим - чтение файла
  if (mode == MODE_FILE)
  {
    // если строка закончилась (или еще не была прочитана)
    if (data.length() == 0)
    {
      // если еще не достигнут конец файла
      if (f.available())
      {
        // читать из файла строку до символа перевода строки
        data = f.readStringUntil('n');
        // перевод строки в отладочную консоль
        Serial.println("");
        //delay(random(2000, 5000));
        tm.display("    ");
        if (line > 0)
        {
          // Если номер текущей строки >0, посылаем символ перевода строки
          Keyboard.write('n');
        }
        line++;
      }
      else
      {
        // закрыть файл, показать EOF и переключить режим в "паузу"
        f.close();
        tm.display("EOF ");
        mode = MODE_0;
      }
    }
    else
    {
      // если в строке еще есть символы, отправляем в буфер клавиатуры первый символ из строки
      Keyboard.write(data[0]);
      // отправляем его же в отладочную консоль
      Serial.write(data[0]);
      // печатаем его на встроенном экранчике
      tm.display(data[0]);
      // удаляем его из строки
      data.remove(0,1);
      //delay(random(300, 1000));
    }
  }
  // если нажата клавиша на встроенной клавиатуре
  if (keyPad.isPressed())
  {
    // получаем символ
    char ch = keyPad.getChar();
    // получаем код клавиши
    int key = keyPad.getLastKey();
    if (last_key != key)
    {
      if (key == 12)
      {
        mode = MODE_0;
        //mouse_active = 0;
        //mouse_count = 0;
        //tm.display("OFF ");
      }
      // нажата кнопка А
      if (key == 3)
      {
        mode = MODE_A;
        tm.display("A   ");
      }
      // нажата кнопка B
      if (key == 7)
      {
        mode = MODE_B;
        tm.display("B   ");
      }
      // если активирован режим А и нажата цифровая клавиша
      if (mode == MODE_A && is_num(key))
      {
        // на экранчик выводим А (нажатая цифра)
        line = 0;
        msg = "A  ";
        msg.concat(ch);
        tm.display(msg);
        // имя файла - (нажатая цифра).txt, например: 1.txt
        fname = "";
        fname.concat(ch);
        fname.concat(".txt");
        // попробуем открыть выбранный файл
        f = SD.open(fname, FILE_READ);
        if (!f)
        {
          // если не удалось (например, файл не существует), то печатаем на экранчике Err (нажатая цифра), Например Err1
          msg = "ERR";
          msg.concat(ch);
          tm.display(msg);
          // возвращаем режим "пауза"
          mode = MODE_0;
        }
        else
        {
          // включаем режим чтения файла
          mode = MODE_FILE;
        }
      }
      // Режим B - зарезервирован на будущее
      if (mode == MODE_B && is_num(key))
      {
        msg = "B  ";
        msg.concat(ch);
        tm.display(msg);
      }
      last_key = key;
      Serial.print(key);
      Serial.print(" t");
      Serial.println(ch);
    }
  }
  else
  {
    // Клавиша отпущена
    last_key = -1;
  }
}

In the future, I plan to expand the functionality of the device by adding “playback” of mouse movements from a CSV file. This functionality is almost written, and will correspond to the B key on the keyboard. As I said earlier, this device is a test device – so that you can launch certain functions of the “smart house” from the physical keyboard, and not just through the web interface. However, other uses can be found for it. For example, “spam” with commands through the console of online games clients. And finally, the sketch itself for Arduino.

Similar Posts

Leave a Reply