Macro Pad

../../_images/termod-s3-example-macropad.png

Tutorial

This example shows how to use Termod S3 as a macro pad.

We use LVGL to make beautiful UI. Here also uses lv_helper

Note

If you haven’t download the code:

Download examples from github termod-s3

Unzip the downloaded termod-s3-main.zip

Or just clone the repository

git clone https://github.com/TAMCTec/termod-s3.git

Open termod-s3/examples/macro_pad/macro_pad.ino with Arduino IDE.

This example use a 22px font LV_FONT_MONTSERRAT_22, you need to enable it in lv_conf.h, the conf file mentioned in Install LVGL Library (Optional).

Open the file, and find the following code, change the 0 to 1 to enable the font.

#define LV_FONT_MONTSERRAT_22 1

Make Sure that USB Mode is set to USB OTG under Tools, and Remember to select ESP32S3 Dev Module and port, then click upload.

Make Icons

To make your own icons, get a picture, better be a png with transparent background, resize it to about 50x50, then use lvgl online image converter to convert it to C array.

Set the output name, it will be the name of the image data variable, so make it “code friendly”. Set Color format to CF_TRUE_COLOR_ALPHA and output to C array. Click Convert, it will download a .c file.

../../_images/macro-pad-image-convertor.png

Then, copy the file to your project, and change the first few line, or it will raise compile error fatal error: lvgl/lvgl.h: No such file or directory

#if defined(LV_LVGL_H_INCLUDE_SIMPLE)
#include "lvgl.h"
#else
#include "lvgl/lvgl.h"
#endif

To

#include "lvgl.h"

Now add a line to .ino file to declare it.

LV_IMG_DECLARE(<name>);

That’s it, you can now use it to create a button:

createIconButton(&<name>, 0, 0, <onPressed>, <onReleased>, <onTap>);

You can see all above in the example for a reference.

Create a shortcut

Some Apps have a keyboard shortcut like CONSUMER_CONTROL_CALCULATOR. You can launch it with ConsumerControl. Others you need to create a keyboard shortcut, and simulate the shortcut with Termod S3.

For Windows 10 and 11, you can make a keyboard shortcut to a desktop shortcut. First, create a shortcut of a app to desktop. Then, right click the shortcut, click Properties.

You will see a shortcut options, click on it and press a shortcut key, like Ctrl+Alt+Shift+1. Then click Apply and OK.

Then in code, simulate it like in the example openKicad.

void openKicad(_lv_event_t* event) {
    Keyboard.press(KEY_LEFT_CTRL);
    Keyboard.press(KEY_LEFT_ALT);
    Keyboard.press(KEY_LEFT_SHIFT);
    Keyboard.press('1');
    Keyboard.releaseAll();
}

You can change keys.

Source code

#if ARDUINO_USB_MODE
#warning This sketch should be used when USB is in OTG mode
void setup(){}
void loop(){}
#else

#include "lv_helper.h"
#include "USB.h"
#include "USBHIDKeyboard.h"
#include "USBHIDConsumerControl.h"

USBHIDConsumerControl ConsumerControl;
USBHIDKeyboard Keyboard;

#define CONSUMER_CONTROL_INTERNET_BROWSER 0x0196

LV_IMG_DECLARE(calculator_icon);
LV_IMG_DECLARE(kicad_icon);
LV_IMG_DECLARE(arduino_icon);
LV_IMG_DECLARE(vscode_icon);

#define KEYBOARD_LAYOUT_MAC 0
#define KEYBOARD_LAYOUT_WINDOWS 1
// If you are using a Mac, set this to KEYBOARD_LAYOUT_MAC
#define KEYBOARD_LAYOUT KEYBOARD_LAYOUT_WINDOWS

#define LAYOUT_WIDTH 4
#define LAYOUT_HEIGHT 3
#define PADDING 2
#define BUTTON_WIDTH 320 / LAYOUT_WIDTH - (2 * PADDING)
#define BUTTON_HEIGHT 240 / LAYOUT_HEIGHT - (2 * PADDING)

static lv_style_t pressedStyle;

lv_obj_t* createButton(int x, int y, void (*onPressed)(_lv_event_t*), void (*onReleased)(_lv_event_t*), void (*onTap)(_lv_event_t*));
void createTextButton(char* text, int x, int y, void (*onPressed)(_lv_event_t*), void (*onReleased)(_lv_event_t*), void (*onTap)(_lv_event_t*));
void createIconButton(const lv_img_dsc_t *image, int x, int y, void (*onPressed)(_lv_event_t*), void (*onReleased)(_lv_event_t*), void (*onTap)(_lv_event_t*));


void setup() {
  Serial.begin(115200);
  lh_init(DISPLAY_LANDSCAPE);

  // Create button style, when button is pressed, glow it
  lv_style_init(&pressedStyle);
  lv_style_set_border_color(&pressedStyle, lv_color_hex(0x33dddd));
  lv_style_set_shadow_color(&pressedStyle, lv_color_hex(0x33dddd));
  lv_style_set_shadow_width(&pressedStyle, 2);
  Keyboard.begin();
  ConsumerControl.begin();
  USB.begin();


  createIconButton(&calculator_icon, 0, 0, NULL, NULL, openCalculator);
  createIconButton(&kicad_icon, 1, 0, NULL, NULL, openKicad);
  createIconButton(&arduino_icon, 2, 0, NULL, NULL, openArduino);
  createIconButton(&vscode_icon, 3, 0, NULL, NULL, openVSCode);
  createTextButton(LV_SYMBOL_VOLUME_MID, 0, 1, volumeDownPressed, volumeDownReleased, NULL);
  createTextButton(LV_SYMBOL_VOLUME_MAX, 1, 1, volumeUpPressed, volumeUpReleased, NULL);
  createTextButton(LV_SYMBOL_MUTE, 2, 1, NULL, NULL, mute);
  createTextButton(LV_SYMBOL_HOME, 3, 1, NULL, NULL, home);
  createTextButton(LV_SYMBOL_COPY, 0, 2, NULL, NULL, copy);
  createTextButton(LV_SYMBOL_PASTE, 1, 2, NULL, NULL, paste);
  createTextButton(LV_SYMBOL_LEFT, 2, 2, NULL, NULL, leftDesktop);
  createTextButton(LV_SYMBOL_RIGHT, 3, 2, NULL, NULL, rightDesktop);
}

void loop() {
  lv_timer_handler();
}

void openCalculator(_lv_event_t* event) {
  ConsumerControl.press(CONSUMER_CONTROL_CALCULATOR);
  ConsumerControl.release();
}

void openKicad(_lv_event_t* event) {
  Keyboard.press(KEY_LEFT_CTRL);
  Keyboard.press(KEY_LEFT_ALT);
  Keyboard.press(KEY_LEFT_SHIFT);
  Keyboard.press('1');
  Keyboard.releaseAll();
}

void openArduino(_lv_event_t* event) {
  Keyboard.press(KEY_LEFT_CTRL);
  Keyboard.press(KEY_LEFT_ALT);
  Keyboard.press(KEY_LEFT_SHIFT);
  Keyboard.press('2');
  Keyboard.releaseAll();
}

void openVSCode(_lv_event_t* event) {
  Keyboard.press(KEY_LEFT_CTRL);
  Keyboard.press(KEY_LEFT_ALT);
  Keyboard.press(KEY_LEFT_SHIFT);
  Keyboard.press('3');
  Keyboard.releaseAll();
}

void volumeDownPressed(_lv_event_t* event) {
  ConsumerControl.press(CONSUMER_CONTROL_VOLUME_DECREMENT);
}
void volumeDownReleased(_lv_event_t* event) {
  ConsumerControl.release();
}
void volumeUpPressed(_lv_event_t* event) {
  ConsumerControl.press(CONSUMER_CONTROL_VOLUME_INCREMENT);
}
void volumeUpReleased(_lv_event_t* event) {
  ConsumerControl.release();
}
void mute(_lv_event_t* event) {
  ConsumerControl.press(CONSUMER_CONTROL_MUTE);
  ConsumerControl.release();
}
void copy(_lv_event_t* event) {
  #if KEYBOARD_LAYOUT == KEYBOARD_LAYOUT_WINDOWS
  Keyboard.press(KEY_LEFT_CTRL);
  #else
  Keyboard.press(KEY_LEFT_GUI);
  #endif
  Keyboard.press('c');
  Keyboard.releaseAll();
}

void paste(_lv_event_t* event) {
  #if KEYBOARD_LAYOUT == KEYBOARD_LAYOUT_WINDOWS
  Keyboard.press(KEY_LEFT_CTRL);
  #else
  Keyboard.press(KEY_LEFT_GUI);
  #endif
  Keyboard.press('v');
  Keyboard.releaseAll();
}

void home(_lv_event_t* event) {
  #if KEYBOARD_LAYOUT == KEYBOARD_LAYOUT_WINDOWS
  Keyboard.press(KEY_LEFT_GUI);
  Keyboard.press('d');
  #endif
  Keyboard.releaseAll();
}

void leftDesktop(_lv_event_t* event) {
  Keyboard.press(KEY_LEFT_CTRL);
  #if KEYBOARD_LAYOUT == KEYBOARD_LAYOUT_WINDOWS
  Keyboard.press(KEY_LEFT_GUI);
  #endif
  Keyboard.press(KEY_LEFT_ARROW);
  Keyboard.releaseAll();
}

void rightDesktop(_lv_event_t* event) {
  Keyboard.press(KEY_LEFT_CTRL);
  #if KEYBOARD_LAYOUT == KEYBOARD_LAYOUT_WINDOWS
  Keyboard.press(KEY_LEFT_GUI);
  #endif
  Keyboard.press(KEY_RIGHT_ARROW);
  Keyboard.releaseAll();
}

// create a button
lv_obj_t* createButton(int x, int y, void (*onPressed)(_lv_event_t*), void (*onReleased)(_lv_event_t*), void (*onTap)(_lv_event_t*)) {
  int top = x * 80 + PADDING;
  int left = y * 80 + PADDING;

  lv_obj_t* btn = lv_obj_create(lv_scr_act());
  lv_obj_set_size(btn, BUTTON_WIDTH, BUTTON_HEIGHT);
  lv_obj_align(btn, LV_ALIGN_TOP_LEFT, top, left);
  lv_obj_clear_flag(btn, LV_OBJ_FLAG_SCROLLABLE);
  lv_obj_add_style(btn, &pressedStyle, LV_STATE_PRESSED);
  if (onPressed != NULL) {
    lv_obj_add_event_cb(btn, onPressed, LV_EVENT_PRESSED, NULL);
  }
  if (onReleased != NULL) {
    lv_obj_add_event_cb(btn, onReleased, LV_EVENT_RELEASED, NULL);
  }
  if (onTap != NULL) {
    lv_obj_add_event_cb(btn, onTap, LV_EVENT_CLICKED, NULL);
  }

  return btn;
}

void createTextButton(char* text, int x, int y, void (*onPressed)(_lv_event_t*), void (*onReleased)(_lv_event_t*), void (*onTap)(_lv_event_t*)) {
  lv_obj_t* btn = createButton(x, y, onPressed, onReleased, onTap);
  lv_obj_t* label = lv_label_create(btn);
  lv_label_set_text(label, text);
  lv_obj_set_style_text_font(label, &lv_font_montserrat_22, 0);
  lv_obj_center(label);
}

void createIconButton(const lv_img_dsc_t *image, int x, int y, void (*onPressed)(_lv_event_t*), void (*onReleased)(_lv_event_t*), void (*onTap)(_lv_event_t*)){
  lv_obj_t* btn = createButton(x, y, onPressed, onReleased, onTap);
  lv_obj_t* img = lv_img_create(btn);
  lv_img_set_src(img, image);
  lv_obj_center(img);
}

#endif /* ARDUINO_USB_MODE */