Maker.io main logo

Qwiic Digital Desk Sign with MicroMod

2022-09-08 | By SparkFun Electronics

License: See Original Project Displays

Courtesy of SparkFun

Guide by BBOYHO

Introduction

‎"Where's Bobby?" A question that comes up when at work. While I'm usually at my desk, there are ‎times that I need to walk away for lunch, take a 15-minute break, head into a meeting, or check ‎inventory. To help notify others of where I may be, I made the Qwiic-enabled digital desk sign using ‎the SAMD51's USB host and a USB keyboard to type short custom messages while I am away!‎

Where is Bobby

Required Materials

To follow along with this tutorial, you will need the following materials. You may not need everything ‎though depending on what you have. Add it to your cart, read through the guide, and adjust the cart ‎as necessary.‎

You Will Also Need

Note: Not all keyboards are compatible. The code used in this tutorial was tested on the following ‎keyboards listed below. We found that the Logitech K400 Plus Wireless Touch Keyboard was not ‎compatible with this project.‎

You will also need a USB keyboard. The following keyboards were tested.‎

  • Dell KB216t

  • Logitech K120‎

Tools

You will need a soldering iron, solder, and general soldering accessories.‎

Suggested Reading

If you aren't familiar with the MicroMod ecosystem, we recommend reading here for an overview. ‎We recommend reading here for an overview if you decide to take advantage of the Qwiic ‎connector.‎

micromod_1

MicroMod Ecosystem

qwiic_2

Qwiic Connect System

We also recommend checking out these tutorials before continuing. If you are using a Qwiic PIR ‎motion sensor with the project, you could also look at its associated tutorial.‎

  • Installing an Arduino Library: How do I install a custom Arduino library? It's easy! This ‎tutorial will go over how to install an Arduino library using the Arduino Library Manager. For libraries ‎not linked with the Arduino IDE, we will also go over manually installing an Arduino library.‎

  • Installing Arduino IDE: A step-by-step guide to installing and testing the Arduino software on ‎Windows, Mac, and Linux.‎

  • I2C: An introduction to I2C, one of the main embedded communications protocols in use today.‎

  • AVR-Based Serial Enabled LCDs Hookup Guide: The AVR-based Qwiic Serial Enabled ‎LCDs are a simple and cost-effective solution to include in your project. These screens are based ‎on the HD44780 controller and include ATmega328P with an Arduino compatible bootloader. They ‎accept control commands via Serial, SPI and I2C (via PTH headers or Qwiic connector). In this ‎tutorial, we will show examples of a simple setup and go through each communication option.‎

  • MicroMod SAMD51 Processor Board Hookup Guide: This tutorial covers the basic ‎functionality of the MicroMod SAMD51 and highlights the features of the ARM Cortex-M4F ‎development board.‎

  • MicroMod All the Pins (ATP) Carrier Board: Access All the Pins (ATP) of the MicroMod ‎Processor Board with the Carrier Board!‎

  • Qwiic PIR Hookup Guide: Get started passively monitoring motion using the Panasonic ‎EKMC and EKMB sensors with the SparkFun Qwiic PIR.‎

Hardware Assembly

If you have not already, make sure to check out the Getting Started with MicroMod: Hardware ‎Hookup for information on inserting your Processor Board to your Carrier Board.‎

started_3

Getting Started with MicroMod

Dive into the world of MicroMod - a compact interface to connect a microcontroller to various ‎peripherals via the M.2 Connector!‎

Your board should look like the image below after connecting the MicroMod SAMD51 Processor ‎Board to the MicroMod ATP. To program, insert the USB-C cable.‎

cable_4

Since the SAMD51 uses the USB connector for USB host, we'll strip and solder wires to the board ‎to connect an external 5V wall adapter.‎

connect_5

Then we'll strip the other end of the wires and insert them into a female barrel jack adapters with "+" ‎to VIN and "" to GND. We'll secure the wires by twisting them together.‎

secure_6

Due to the design of the MicroMod SAMD51's host pins, we'll need to solder another USB Type A ‎connector breakout to the ATP's host pins using male headers. If you have an adapter to convert ‎the USB Type C to Type A, you can also use that as well.‎

solder_7

While I could solder the breakout directly to the ATP's host pins, I decided to solder female headers ‎to the board to be able to easily disconnect the USB keyboard.‎

pins_8

Add a Qwiic cable between the Qwiic SerLCD and MicroMod's Qwiic connector labeled as I2C.‎

qwiic_9

Insert a keyboard to the USB breakout board.‎

keyboard_10

When you have finished programming the SAMD51, you can insert a 5V wall adapter for power.‎

program_11

Warning! When programming your SAMD51 MicroMod, make sure to disconnect the 5V ‎adapter to avoid conflicting voltages with the USB port.‎

To save power and the screen, you can also add a PIR motion sensor or distance sensor to toggle ‎the screen on and off. An additional Qwiic cable and Qwiic PIR motion sensor was added between ‎the MicroMod ATP and Qwiic SerLCD.‎

save_12

The 20x40 SerLCD was hard to see any messages with it flat on a table, so a panel was eventually ‎cut out from a SparkFun cardboard box to mount the project. for the scope of this tutorial, won't go ‎over the specifics of cutting the cardboard in this tutorial or mounting the electronics to the panel.‎

panel_13

Arduino Example Code

Note: If this is your first-time using Arduino IDE or board add-on, please review the following ‎tutorials. ‎

The example code can be found in the following GitHub repository. The example includes the ‎original host keyboard code from Arduino. The code was modified to work with a 20x4 SerLCD and ‎eventually the 16x2. To save power and the screen, the code was further modified to be used with ‎the Qwiic PIR motion sensor in order to toggle the RGB LED and screen. For the scope of this ‎project tutorial, we will be using examples 1b and 2.‎

GITHUB: QWIIC DIGITAL DESK SIGN WITH MICROMOD

Arduino SAMD Board Add-Ons

Since we are using the SAMD51, you will need to install the board add-on. Head over to the tutorial ‎for instructions on installing the board definitions.‎

micromod_14

MicroMod SAMD51 Processor Board Hookup Guide

This tutorial covers the basic functionality of the MicroMod SAMD51 and highlights the features of ‎the ARM Cortex-M4F development board.

Additional Libraries

If you are using the Qwiic PIR motion sensor, make sure to download and install the library ‎as stated in its tutorial.‎

pir_15

Qwiic PIR Hookup Guide

Get started passively monitoring motion using the Panasonic EKMC and EKMB sensors with the ‎SparkFun Qwiic PIR.‎

The Arduino Library Manager is the easiest way to install the library. Open the Library Manager, ‎search for "SparkFun Qwiic PIR Arduino Library" and click the "Install" button to download the ‎latest version. If you prefer manually installing the library from the GitHub repository, you can ‎download it here:‎

DOWNLOAD THE QWIIC PIR ARDUINO LIBRARY

Example 1b: Qwiic Digital Desk Sign

If you are using an external 5V wall adapter, make sure to disconnect it before inserting the USB C ‎cable to your computer's COM port to upload the project's code. Let's upload a project's sketch to ‎the board. Copy and paste the following code in the Arduino IDE. Head to Tools > Board to select ‎the correct board definition (in this case, SparkFun MicroMod SAMD51. Select the correct COM ‎port that the board enumerated to. Hit upload.‎

Copy Code
/******************************************************************************
  Host Keyboard Controller with Qwiic Serial LCD 16x2 and 20x4
  Date Modified: 5 Aug 2021
  Modified by
  Ho Yun "Bobby" Chan
  Keyboard Controller Code Originally created 8 Oct 2012
  by Cristian Maglie

  ========== DESCRIPTION==========

  This project code takes input from a USB keyboard with
  the SAMD51's USB host pins and outputs characters to
  the Qwiic RGB Serial Enabled LCD 20x4. Leave a message
  behind as you walk away from your desk!

  Note: Not all keyboards are compatible. This code was tested on the following
  keyboards.

      - Dell KB216t
      - Logitech K120

  This example also builds off the example from Arduino which
  originally showed the output of a USB Keyboard connected to
  the Native USB port on an Arduino Due (SAMD21) Board.

  http://arduino.cc/en/Tutorial/KeyboardController

  This code is part of the public domain.

******************************************************************************/

#include <KeyboardController.h> // Require keyboard control library
#include <Wire.h> //Needed for I2C to SerLCD

#define SerLCD_Address 0x72  //If using SerLCD with I2C
#define SERIAL_PORT_MONITOR Serial1 //debug via hardware UART pins

//assuming that we are using a SerLCD 20x4 screen
//these variables keep track of cursor location
int row = 0;
int remappedRow = row;
int column = 0;

//depending on what screen you are using, we are counting 0 as well
//int maxRow = 1;     // for 16x2
int maxRow = 3;        //for 20x4
//int maxColumn = 15; // for 16x2
int maxColumn = 19;    //for 20x4

boolean rgb_backlight = true; //used for function keys to immediately turn on/off backlight
boolean blink_box = true; //used to keep track of blink box as a "cursor"

//values to keep track of rgb backlight
int rVal = 157; //128 = Off, 157 = 100%
int gVal = 187; //158 = Off, 187 = 100%
int bVal = 217; //188 = Off, 217 = 100%

//values used to turn off rgb in Power Save Mode
int rVal_OFF = 128;
int gVal_OFF = 158;
int bVal_OFF = 188;

int lcdContrast = 40; //used to keep track of contast: Range is 255 to 0, 40 is default


// Initialize USB Controller
USBHost usb;

// Attach keyboard controller to USB
KeyboardController keyboard(usb);

//boolean capsLock = false; //used to keep track of capsLock, this is not used in this code
boolean numLock = false; //display numbers if numLock on, we'll assume that the keyboard resets every time so it's off by default





// This function intercepts key press
void keyPressed() {
  SERIAL_PORT_MONITOR.print("Pressed:  ");
  //printKey(); //disabled so we are not sending two key presses to the SerLCD
}




// This function intercepts key release
void keyReleased() {
  SERIAL_PORT_MONITOR.print("Released: ");
  printKey();
}




void printKey() {
  // getOemKey() returns the OEM-code associated with the key
  int tempKey = keyboard.getOemKey();
  SERIAL_PORT_MONITOR.print(" key:");
  SERIAL_PORT_MONITOR.print(tempKey);

  // getModifiers() returns a bits field with the modifiers-keys
  int mod = keyboard.getModifiers();
  SERIAL_PORT_MONITOR.print(" mod:");
  SERIAL_PORT_MONITOR.print(mod);

  SERIAL_PORT_MONITOR.print(" => ");

  if (mod & LeftCtrl)
    SERIAL_PORT_MONITOR.print("L-Ctrl ");
  if (mod & LeftShift)
    SERIAL_PORT_MONITOR.print("L-Shift ");
  if (mod & Alt)
    SERIAL_PORT_MONITOR.print("Alt ");
  if (mod & LeftCmd)
    SERIAL_PORT_MONITOR.print("L-Cmd ");
  if (mod & RightCtrl)
    SERIAL_PORT_MONITOR.print("R-Ctrl ");
  if (mod & RightShift)
    SERIAL_PORT_MONITOR.print("R-Shift ");
  if (mod & AltGr)
    SERIAL_PORT_MONITOR.print("AltGr ");
  if (mod & RightCmd)
    SERIAL_PORT_MONITOR.print("R-Cmd ");

  // getKey() returns the ASCII translation of OEM key
  // combined with modifiers.
  SERIAL_PORT_MONITOR.write(keyboard.getKey());
  SERIAL_PORT_MONITOR.println();

  /*
    USB SCAN CODES TO KEYCAP (US LAYOUT)
    https://www.win.tue.nl/~aeb/linux/kbd/scancodes-10.html

      CHARACTER KEYS
      USB SCAN CODE = KEYCAP

      4 to 29 = 'a' to 'z' and 'A' to 'Z' with Shift/CapsLock
      30 to 39 = '1' to '9', '0', and '!@#$%^&*()' with Shift
      40 = 'Enter'
      42 = 'Backspace'
      44 = 'Space'
      45 = '-' or '_' with Shift
      46 = '=' or '+' with Shift
      47 = '[' or '{' with Shift
      48 = ']' or '}' with Shift
      49 = '\' or '|' with Shift
      51 = ';' or ':' with Shift
      52 = `'` or '"' with Shift
      53 = '`' or '~' with Shift
      54 = ',' or '<' with Shift
      55 = '.' or '>' with Shift
      56 = '/' or '?' with Shift

      CURSOR CONTROL D-PAD (US LAYOUT)
      79 = `→` (e.g. right)
      80 = `←` (e.g. left)
      81 = `↓` (e.g. down)
      82 = `↑` (e.g. up)

      NUMERIC KEYPAD (US LAYOUT), NUMLOCK MUST BE ENABLED
      83 = 'NumLock'
      84 = '/'
      85 = '*'
      86 = '-'
      87 = '+'
      88 = 'Enter'
      89 = 'End'             or '1' w/ NumLock
      90 = '↓' (e.g. down)   or '2' w/ NumLock
      91 = 'Page Down'       or '3' w/ NumLock
      92 = '←' (e.g. left)   or '4' w/ NumLock
      93 =                      '5' w/ NumLock
      94 = '→' (e.g. right)  or '6' w/ NumLock
      95 = 'Home'             or '7' w/ NumLock
      96 = '↑' (e.g. up)      or '8' w/ NumLock
      97 = 'Page Up'          or '9' w/ NumLock
      98 = 'Insert'           or '0' w/ NumLock
      99 = 'Delete'           or '.' w/ NumLock

      OTHER
      41 = 'Escape'
      73 = 'Insert'
      74 = 'Home'
      75 = 'Page Up'
      76 = 'Delete'
      77 = 'End'
      78 = 'Page Down'

      FUNCTION KEYS
      58 = `F1`
      59 = `F2`
      60 = `F3`
      61 = `F4`
      62 = `F5`
      63 = `F6`
      64 = `F7`
      65 = `F8`
      66 = `F9`
      67 = `F10`
      68 = `F11`
      69 = `F12`



  */

  //----------TYPEWRITER AND KEYPAD KEYS (QWERTY, US LAYOUT) ----------
  if ((tempKey >= 4 && tempKey <= 39) ||
      (tempKey >= 44 && tempKey <= 49) ||
      (tempKey >= 51 && tempKey <= 56) ||
      (tempKey >= 84 && tempKey <= 87) ||
      ((tempKey >= 89 && tempKey <= 97) && numLock == true) ||
      tempKey == 73 ||
      tempKey == 98 ||
      (tempKey == 99 && numLock == true)) {

    Wire.beginTransmission(SerLCD_Address);

    if ( (tempKey == 49) && ((mod == 2) || (mod == 32)) ) {
      //Note: When sending the pipeline character(`|`),
      //we'll need to send 2x since the character
      //is also used as a setting character.
      Wire.write(keyboard.getKey());
      delay(50);//short delay before sending next line
      Wire.write(keyboard.getKey());
    }
    else if ((tempKey == 49) && (mod == 0)) {
      //Note: When sending back slash (`\`),
      //we will load the custom character using
      //printCustomChar() through I2C
      printCustomChar(0);
    }
    else if ( (tempKey == 53) && ((mod == 2) || (mod == 32)) ) {
      //Note: When sending tilde (`~`),
      //we will load the custom character using
      //printCustomChar() through I2C
      printCustomChar(1);
    }
    else if (tempKey == 73) {
      //Note: When sending 'insert',
      //we will load the custom character '♥' using
      //printCustomChar() through I2C
      printCustomChar(2);
    }
    else if (tempKey == 98 && numLock == false) {
      //Note: When sending keypad insert,
      //we will load the custom character '♡' using
      //printCustomChar() through I2C
      printCustomChar(3);
    }
    else {
      Wire.write(keyboard.getKey());
    }


    //after typing the cursor will move automatically to
    //next position so let's keep track of it
    if (column < maxColumn) {
      //if we are not at the end of the row, move cursor to next position
      column = column + 1;
    }
    else {
      //if we are at the end of the row, reset cursor to the beginning of the row
      column = 0;

      if (row < maxRow) {
        //if we are not on the last line, move cursor to the next line
        row = row + 1;
      }
      else {
        //if we are on the last line, move cursor to the first line
        row = 0;
      }
    }

    //remap according to SerLCD's line number
    if (row == 0) {
      remappedRow = 0;
    }
    else if (row == 1) {
      remappedRow = 64;
    }
    else if (row == 2) {
      remappedRow = 20;
    }
    else if (row == 3) {
      remappedRow = 84;
    }

    Wire.write(254); //Send command character
    Wire.write(128 + remappedRow + column); //update cursor position
    Wire.endTransmission(); //Stop I2C transmission

  }





  /*NOTE: These keys move the cursor so it is kept
    as separate condition statements. We will
    also include most of the numLock keys when 'NUMLOCK'
    is disabled.*/
  else if ((tempKey >= 79 && tempKey <= 82) ||
           tempKey == 40 ||
           tempKey == 74 ||
           tempKey == 77 ||
           tempKey == 75 ||
           tempKey == 78 ||
           tempKey == 83 ||
           tempKey == 88 ||
           ((tempKey >= 89 && tempKey <= 97) && numLock == false) ) {


    //move cursor based on d-pad or Enter key

    //----------NUMLOCK KEY----------
    if (tempKey == 83) {
      numLock = !numLock;// change state of numLock if pressed by inverting it
    }




    //----------LEFT KEY----------
    else if ((tempKey == 80) || (tempKey == 92 && numLock == false)) {

      if (column > 0) {
        //if we are at the beginning of the row
        column = column - 1;
      }
      else {
        //if we are at the end of the row
        column = maxColumn;

        if (row > 0) {
          //if we are not on the last line move up a line
          row = row - 1;
        }
        else {
          //if we are on the last line
          row = maxRow;
        }
      }
    }



    //----------RIGHT KEY----------
    else if ( (tempKey == 79) || (tempKey == 94 && numLock == false) ) {

      if (column < maxColumn) {
        //if we are at the before the end of the row
        column = column + 1;
      }
      else {
        //if we are at the end of the row, reset cursor position
        column = 0;

        if (row < maxRow) {
          //move to next line if we are before the last line
          row = row + 1;
        }
        else {
          //move to the first line if we have reached the end
          row = 0;
        }
      }
    }



    //----------UP KEY----------
    else if ( (tempKey == 82) || (tempKey == 96 && numLock == false) ) {
      if (row > 0) {
        //if we are not on the first line
        row = row - 1;
      }
      else {
        //if we are on the first line, move cursor to the last line
        row = maxRow;
      }
    }



    //----------DOWN KEY----------
    else if ( (tempKey == 81) || (tempKey == 90 && numLock == false) ) {
      if (row < maxRow) {
        //if we are not on the first line
        row = row + 1;
      }
      else {
        //if we are on the last line, move cursor to the first line
        row = 0;
      }
    }



    //----------ENTER KEY----------
    else if (tempKey == 40 || tempKey == 88) {
      //move to the first position of the next line
      column = 0;
      if (row < maxRow) {
        //if we are not on the first line, move cursor to the next line
        row = row + 1;
      }
      else {
        //if we are on the last line, move cursor to the first line
        row = 0;
      }
    }

    //----------HOME KEY----------
    else if (tempKey == 74 || (tempKey == 95 && numLock == false) ) {
      //move to the first position of the next line
      column = 0;
    }

    //----------END KEY----------
    else if (tempKey == 77 || (tempKey == 89 && numLock == false)) {
      //move to the first position of the next line
      column = maxColumn;
    }

    //----------PAGE UP KEY----------
    else if (tempKey == 75 || (tempKey == 97 && numLock == false)) {
      //move to the top row
      row = 0;
    }

    //----------PAGE DOWN KEY----------
    else if (tempKey == 78 || (tempKey == 91 && numLock == false)) {
      //move to the bottom row
      row = maxRow;
    }

    else if (tempKey == 93 && numLock == false ) {
      blink_box = !blink_box;// change state of blink_box if pressed by inverting it

      Wire.beginTransmission(SerLCD_Address);
      Wire.write(254); //Send command character

      if (blink_box == false) {
        Wire.write( (1 << 3) | (1 << 2) ); //Cursor off, blinking box off
      }
      else {
        //if blink_box == true

        Wire.write( (1 << 3) | (1 << 2) | (1 << 0) ); //Cursor off, blinking box on
      }

      Wire.endTransmission(); //Stop I2C transmission

    }


    //remap according to SerLCD's line number
    if (row == 0) {
      remappedRow = 0;
    }
    else if (row == 1) {
      remappedRow = 64;
    }
    else if (row == 2) {
      remappedRow = 20;
    }
    else if (row == 3) {
      remappedRow = 84;
    }

    Wire.beginTransmission(SerLCD_Address);
    Wire.write(254); //Send command character
    Wire.write(128 + remappedRow + column); //update cursor position
    Wire.endTransmission(); //Stop I2C transmission

  }




  //----------ESCAPE KEY----------
  else if (tempKey == 41) {
    //Make the escape key clear screen and reset cursor position.
    Wire.beginTransmission(SerLCD_Address);
    Wire.write('|'); //Put LCD into setting mode
    Wire.write('-'); //Send clear display command
    //Control the cursor
    Wire.write(254); //Send command character
    Wire.write( (1 << 3) | (1 << 2) | (1 << 0) ); //Cursor on, blinking box on
    blink_box = true;
    // reset cursor position to (0,0) after using clear display command
    row = 0;
    remappedRow = row;
    column = 0;

    Wire.write(254); //Send command character
    Wire.write(128 + remappedRow + column); //update cursor position
    Wire.endTransmission(); //Stop I2C transmission

  }



  //----------BACKSPACE KEY----------
  else if (tempKey == 42) {
    //Backspace
    //- 'Delete' by replacing with 'Space',
    //- move backward,
    //- then move backward again since cursor moves after sending character


    for (int i = 0 ; i < 2; i++) {

      if (i == 0) {
        //"Delete character" by replacing character with a space
        //and then moving cursor backwards after this condition statement.
        //We do this only once.

        Wire.beginTransmission(SerLCD_Address);
        Wire.write(32); //Send 'Space' Character
        Wire.endTransmission(); //Stop I2C transmission

        //cursor moved automatically to next position so let's keep track of it
        if (column < maxColumn) {
          column = column + 1;
        }
        else {
          column = 0;

          if (row < maxRow) {
            row = row + 1;
          }
          else {
            row = 0;
          }
        }
      }

      //For backspace, we are replacing the current position
      //with a space. This moves the cursor forward once space
      //To correct this, we are going to move backward twice
      //with the help of the for() loop.
      if (column > 0) {
        column = column - 1;
      }
      else {
        column = maxColumn;

        if (row > 0) {
          row = row - 1;
        }
        else {
          row = maxRow;
        }
      }


      //remap according to SerLCD's line number
      if (row == 0) {
        remappedRow = 0;
      }
      else if (row == 1) {
        remappedRow = 64;
      }
      else if (row == 2) {
        remappedRow = 20;
      }
      else if (row == 3) {
        remappedRow = 84;
      }

      Wire.beginTransmission(SerLCD_Address);
      Wire.write(254); //Send command character
      Wire.write(128 + remappedRow + column); //update cursor position
      Wire.endTransmission(); //Stop I2C transmission
      delay(50);




    }//end for() loop for controlling cursor with 'Backspace'
  }//end condition statement for 'Backspace'



  //----------DELETE KEY----------
  else if (tempKey == 76 || (tempKey == 99 && numLock == false) ) {
    //'Delete'
    //- 'Delete' by replacing with 'Space',
    //- move backward again since cursor moves after sending character
    //Note: Delete for the SerLCD is not like the traditional
    //forward delete key. For the SerLCD, we are simply
    //going to delete the character at the cursor position.
    //The characters ahead of the cursor will not be shifted
    //to the left.

    Wire.beginTransmission(SerLCD_Address);
    Wire.write(32); //Send 'Space' Character

    //cursor moved automatically to next position so let's keep track of it
    if (column < maxColumn) {
      column = column + 1;
    }
    else {
      column = 0;

      if (row < maxRow) {
        row = row + 1;
      }
      else {
        row = 0;
      }
    }

    //move cursor back to where it was
    if (column > 0) {
      column = column - 1;
    }
    else {
      column = maxColumn;

      if (row > 0) {
        row = row - 1;
      }
      else {
        row = maxRow;
      }
    }


    //remap according to SerLCD's line number
    if (row == 0) {
      remappedRow = 0;
    }
    else if (row == 1) {
      remappedRow = 64;
    }
    else if (row == 2) {
      remappedRow = 20;
    }
    else if (row == 3) {
      remappedRow = 84;
    }

    Wire.write(254); //Send command character
    Wire.write(128 + remappedRow + column); //update cursor position
    Wire.endTransmission(); //Stop I2C transmission


  }//end condition statement for 'Delete'



  else if (tempKey == 58) {
    //`F1`
    //rVal-

    if (rVal > 128) {
      rVal = rVal - 1;
    }
    else {
      rVal = 128;
    }

    Wire.beginTransmission(SerLCD_Address);
    Wire.write('|'); //Put LCD into setting mode
    Wire.write(rVal); //Update red value
    Wire.endTransmission(); //Stop I2C transmission

  }


  else if (tempKey == 59) {
    //`F2`
    //rVal+

    if (rVal < 157 ) {
      rVal = rVal + 1;
    }
    else {
      rVal = 157;
    }

    Wire.beginTransmission(SerLCD_Address);
    Wire.write('|'); //Put LCD into setting mode
    Wire.write(rVal); //Update red value
    Wire.endTransmission(); //Stop I2C transmission
  }


  else if (tempKey == 60) {
    //`F3`
    //gVal-

    if (gVal > 158 ) {
      gVal = gVal - 1;
    }
    else {
      gVal = 158;
    }

    Wire.beginTransmission(SerLCD_Address);
    Wire.write('|'); //Put LCD into setting mode
    Wire.write(gVal); //Update green value
    Wire.endTransmission(); //Stop I2C transmission

  }


  else if (tempKey == 61) {
    //`F4`
    //gVal+

    if (gVal < 187 ) {
      gVal = gVal + 1;
    }
    else {
      gVal = 187;
    }

    Wire.beginTransmission(SerLCD_Address);
    Wire.write('|'); //Put LCD into setting mode
    Wire.write(gVal); //Update green value
    Wire.endTransmission(); //Stop I2C transmission

  }

  else if (tempKey == 62) {
    //`F5`
    //bVal-

    if (bVal > 188 ) {
      bVal = bVal - 1;
    }
    else {
      bVal = 188;
    }

    Wire.beginTransmission(SerLCD_Address);
    Wire.write('|'); //Put LCD into setting mode
    Wire.write(bVal); //Update blue value
    Wire.endTransmission(); //Stop I2C transmission
  }

  else if (tempKey == 63) {
    //`F6`
    //bVal+

    if (bVal < 217 ) {
      bVal = bVal + 1;
    }
    else {
      bVal = 217;
    }

    Wire.beginTransmission(SerLCD_Address);
    Wire.write('|'); //Put LCD into setting mode
    Wire.write(bVal); //Update blue value
    Wire.endTransmission(); //Stop I2C transmission

  }

  else if (tempKey == 64) {
    //`F7`
    //backlight ON/OFF

    if (rVal == 128 && gVal == 158 &&  bVal == 188)
    { //if all values are off, we will turn it all ON in the next condition statement
      rgb_backlight = 0;
    }
    else {
      //if any of the LEDs is partially on, we will turn it all OFF in the next condition statement
      rgb_backlight = 1;
    }


    if (rgb_backlight == 0)
    { // rgb_backlight == false //OFF, so turn ON

      rgb_backlight = 1;// set it ON
      rVal = 157;
      gVal = 187;
      bVal = 217;


      Wire.beginTransmission(SerLCD_Address);

      Wire.write('|'); //Put LCD into setting mode
      Wire.write(rVal); //Set red backlight

      Wire.write('|'); //Put LCD into setting mode
      Wire.write(gVal); //Set green backlight

      Wire.write('|'); //Put LCD into setting mode
      Wire.write(bVal); //Set blue backlight

      Wire.endTransmission(); //Stop I2C transmission

    }
    else
    { // rgb_backlight == true //ON, so turn OFF

      rgb_backlight = 0;// set it OFF
      rVal = 128;
      gVal = 158;
      bVal = 188;

      Wire.beginTransmission(SerLCD_Address);

      Wire.write('|'); //Put LCD into setting mode
      Wire.write(rVal); //Set red backlight

      Wire.write('|'); //Put LCD into setting mode
      Wire.write(gVal); //Set green backlight

      Wire.write('|'); //Put LCD into setting mode
      Wire.write(bVal); //Set blue backlight

      Wire.endTransmission(); //Stop I2C transmission

    }


  }


  else if (tempKey == 65) {
    //`F8`
    //set custom color, let's set it to cyan (0%, 100%, 100%)

    rVal = 128;
    gVal = 187;
    bVal = 217;

    Wire.beginTransmission(SerLCD_Address);

    Wire.write('|'); //Put LCD into setting mode
    Wire.write(rVal); //Set red backlight

    Wire.write('|'); //Put LCD into setting mode
    Wire.write(gVal); //Set green backlight

    Wire.write('|'); //Put LCD into setting mode
    Wire.write(bVal); //Set blue backlight
    Wire.endTransmission(); //Stop I2C transmission

  }




  else if (tempKey == 66) {
    //`F9`
    //Custom Message 1

    Wire.beginTransmission(SerLCD_Address);
    Wire.write('|'); //Put LCD into setting mode
    Wire.write('-'); //Send clear display command

    // reset cursor position to (0,0) after using clear display command
    row = 0;
    remappedRow = row;
    column = 0;


    if (maxRow == 3 && maxColumn == 19) {
      //Note: We'll send the lines in separate I2C transmissions
      //instead of all at once. Sending several lines freezes the SerLCD.

      Wire.print("Gone dancing! I'll  ");
      Wire.endTransmission(); //Stop I2C transmission
      delay(50);//short delay before sending next line

      Wire.beginTransmission(SerLCD_Address);
      Wire.print("be back at 12:30pm! ");
      Wire.endTransmission(); //Stop I2C transmission
      delay(50);//short delay before sending next line

      Wire.beginTransmission(SerLCD_Address);
      Wire.print("         =)         ");
      Wire.endTransmission(); //Stop I2C transmission
      delay(50);//short delay before sending next line

      Wire.beginTransmission(SerLCD_Address);
      Wire.print("SparkFun Electronics");
      Wire.endTransmission(); //Stop I2C transmission
      delay(50);//short delay before sending next line


      //move cursor position so it's out of the way of the text;
      row = 2;
      remappedRow = 20;
      column = 19;
    }
    else {
      //Note: We'll send the lines in separate I2C transmissions
      //instead of all at once. Sending several lines freezes the SerLCD.

      Wire.print("Gone dancing!   ");
      Wire.endTransmission(); //Stop I2C transmission
      delay(50);//short delay before sending next line

      Wire.beginTransmission(SerLCD_Address);
      Wire.print("BRB at 12:30pm! ");
      Wire.endTransmission(); //Stop I2C transmission
      delay(50);//short delay before sending next line

      //move cursor position so it's out of the way of the text;
      row = 1;
      remappedRow = 64;
      column = 15;
    }

    Wire.beginTransmission(SerLCD_Address);

    Wire.write(254); //Send command character
    Wire.write(128 + remappedRow + column); //update cursor position
    Wire.endTransmission(); //Stop I2C transmission
    delay(50);//short delay before sending next line

  }




  else if (tempKey == 67) {
    //`F10`
    //Custom Message 2

    Wire.beginTransmission(SerLCD_Address);
    Wire.write('|'); //Put LCD into setting mode
    Wire.write('-'); //Send clear display command

    // reset cursor position to (0,0) after using clear display command
    row = 0;
    remappedRow = row;
    column = 0;

    if (maxRow == 3 && maxColumn == 19) {

      //Note: We'll send the lines in separate I2C transmissions
      //instead of all at once. Sending several lines freezes the SerLCD.

      Wire.print("Out for lunch! I'll ");
      Wire.endTransmission(); //Stop I2C transmission
      delay(50);//short delay before sending next line

      Wire.beginTransmission(SerLCD_Address);
      Wire.print("be back at  2:00pm! ");
      Wire.endTransmission(); //Stop I2C transmission
      delay(50);//short delay before sending next line

      Wire.beginTransmission(SerLCD_Address);
      Wire.print("        ^_^         ");
      Wire.endTransmission(); //Stop I2C transmission
      delay(50);//short delay before sending next line

      Wire.beginTransmission(SerLCD_Address);
      Wire.print("SparkFun Electronics");
      Wire.endTransmission(); //Stop I2C transmission
      delay(50);//short delay before sending next line


      //move cursor position so it's out of the way of the text;
      row = 2;
      remappedRow = 20;
      column = 19;
    }
    else {
      //Note: We'll send the lines in separate I2C transmissions
      //instead of all at once. Sending several lines freezes the SerLCD.

      Wire.print("Out for lunch!  ");
      Wire.endTransmission(); //Stop I2C transmission
      delay(50);//short delay before sending next line

      Wire.beginTransmission(SerLCD_Address);
      Wire.print("BRB at  2:00pm! ");
      Wire.endTransmission(); //Stop I2C transmission
      delay(50);//short delay before sending next line

      //move cursor position so it's out of the way of the text;
      row = 1;
      remappedRow = 64;
      column = 15;
    }

    Wire.beginTransmission(SerLCD_Address);

    Wire.write(254); //Send command character
    Wire.write(128 + remappedRow + column); //update cursor position
    Wire.endTransmission(); //Stop I2C transmission
    delay(50);//short delay before sending next line

  }




  else if (tempKey == 68) {
    //`F11`
    //Custom Message 3

    Wire.beginTransmission(SerLCD_Address);
    Wire.write('|'); //Put LCD into setting mode
    Wire.write('-'); //Send clear display command

    // reset cursor position to (0,0) after using clear display command
    row = 0;
    remappedRow = row;
    column = 0;


    if (maxRow == 3 && maxColumn == 19) {

      //Note: We'll send the lines in separate I2C transmissions
      //instead of all at once. Sending several lines freezes the SerLCD.

      Wire.print("In a meeting! I'll  ");
      Wire.endTransmission(); //Stop I2C transmission
      delay(50);//short delay before sending next line

      Wire.beginTransmission(SerLCD_Address);
      Wire.print("be back at  2:00pm! ");
      Wire.endTransmission(); //Stop I2C transmission
      delay(50);//short delay before sending next line

      Wire.beginTransmission(SerLCD_Address);
      Wire.print("                    ");
      Wire.endTransmission(); //Stop I2C transmission
      delay(50);//short delay before sending next line

      Wire.beginTransmission(SerLCD_Address);
      Wire.print("SparkFun Electronics");
      Wire.endTransmission(); //Stop I2C transmission
      delay(50);//short delay before sending next line


      //move cursor position so it's out of the way of the text;
      row = 2;
      remappedRow = 20;
      column = 19;
    }
    else {
      //Note: We'll send the lines in separate I2C transmissions
      //instead of all at once. Sending several lines freezes the SerLCD.

      Wire.print("In a meeting!   ");
      Wire.endTransmission(); //Stop I2C transmission
      delay(50);//short delay before sending next line

      Wire.beginTransmission(SerLCD_Address);
      Wire.print("BRB at  2:00pm! ");
      Wire.endTransmission(); //Stop I2C transmission
      delay(50);//short delay before sending next line

      //move cursor position so it's out of the way of the text;
      row = 1;
      remappedRow = 64;
      column = 15;
    }

    Wire.beginTransmission(SerLCD_Address);

    Wire.write(254); //Send command character
    Wire.write(128 + remappedRow + column); //update cursor position
    Wire.endTransmission(); //Stop I2C transmission
    delay(50);//short delay before sending next line

  }



  else if (tempKey == 69) {
    //`F12`
    //Custom Message 4

    Wire.beginTransmission(SerLCD_Address);
    Wire.write('|'); //Put LCD into setting mode
    Wire.write('-'); //Send clear display command

    // reset cursor position to (0,0) after using clear display command
    row = 0;
    remappedRow = row;
    column = 0;

    if (maxRow == 3 && maxColumn == 19) {

      //Note: We'll send the lines in separate I2C transmissions
      //instead of all at once. Sending several lines freezes the SerLCD.

      Wire.print("Bobby's desk is here");
      Wire.endTransmission(); //Stop I2C transmission
      delay(50);//short delay before sending next line

      Wire.beginTransmission(SerLCD_Address);
      Wire.print("   Working remote!  ");
      Wire.endTransmission(); //Stop I2C transmission
      delay(50);//short delay before sending next line

      Wire.beginTransmission(SerLCD_Address);
      Wire.print(" See you virtually! ");
      Wire.endTransmission(); //Stop I2C transmission
      delay(50);//short delay before sending next line

      Wire.beginTransmission(SerLCD_Address);
      Wire.print("SparkFun Electronics");
      Wire.endTransmission(); //Stop I2C transmission
      delay(50);//short delay before sending next line


      //move cursor position so it's out of the way of the text;
      row = 2;
      remappedRow = 20;
      column = 19;
    }
    else {
      //Note: We'll send the lines in separate I2C transmissions
      //instead of all at once. Sending several lines freezes the SerLCD.

      Wire.print("Bobby's desk is ");
      Wire.endTransmission(); //Stop I2C transmission
      delay(50);//short delay before sending next line

      Wire.beginTransmission(SerLCD_Address);
      Wire.print("here!GoneVirtual");
      Wire.endTransmission(); //Stop I2C transmission
      delay(50);//short delay before sending next line

      //move cursor position so it's out of the way of the text;
      row = 1;
      remappedRow = 64;
      column = 15;
    }

    Wire.beginTransmission(SerLCD_Address);

    Wire.write(254); //Send command character
    Wire.write(128 + remappedRow + column); //update cursor position
    Wire.endTransmission(); //Stop I2C transmission
    delay(50);//short delay before sending next line

  }



  delay(50); //The maximum update rate of OpenLCD is about 100Hz (10ms). A smaller delay will cause flicker

}





//Set Custom Characters for 5x8 Character Position
//'\'
//0x0,0x10,0x8,0x4,0x2,0x1,0x0,0x0
//%0,000,00,0,,%1,%0,%0
byte back_slash[8] = {
  0b00000,
  0b10000,
  0b01000,
  0b00100,
  0b00010,
  0b00001,
  0b00000,
  0b00000
};





//'~'
//0x0,0x0,0x0,0x8,0x15,0x2,0x0,0x0
//%0,%0,00,101,,%0,%0,%0
byte tilde[8] = {
  0b00000,
  0b00000,
  0b01000,
  0b10101,
  0b00010,
  0b00000,
  0b00000,
  0b00000
};





//'♥'
//0x0,0x0,0xa,0x1f,0x1f,0xe,0x4,0x0
//%0,%0,10,111,111,10,0,%0
byte heart[8] = {
  0b00000,
  0b00000,
  0b01010,
  0b11111,
  0b11111,
  0b01110,
  0b00100,
  0b00000
};





//'♡'
//0x0,0x0,0xa,0x15,0x11,0xa,0x4,0x0
//%0,%0,10,101,001,10,0,%0
byte empty_heart[8] = {
  0b00000,
  0b00000,
  0b01010,
  0b10101,
  0b10001,
  0b01010,
  0b00100,
  0b00000
};





//Given a character number (0 to 7 is valid)
//Given an 8 byte array
//Record this data as a custom character to CGRAM
void loadCustomCharacter(byte charNumber, byte charData[])
{
  if (charNumber > 7) charNumber = 7; //Error correction

  Wire.write('|'); //Send setting character
  Wire.write(27 + charNumber); //27 is the first custom character spot

  for (byte x = 0 ; x < 8 ; x++) //There are 8 bytes of data we need to load
    Wire.write(charData[x]); //Write 8 bytes of graphic data to display
}





//Display a given custom character that was previously loaded into CGRAM
void printCustomChar(byte charNumber)
{
  if (charNumber > 7) charNumber = 7; //Error correction

  Wire.write('|'); //Send setting character
  Wire.write(35 + charNumber); //Tell LCD to display custom char # 0-7
}





void setup() {
  SERIAL_PORT_MONITOR.begin(115200);
  //while (!SERIAL_PORT_MONITOR); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection
  SERIAL_PORT_MONITOR.println("Keyboard Controller Program started");

  if (usb.Init() == -1)
    SERIAL_PORT_MONITOR.println("OSC did not start.");

  delay(3000);//wait a second for the SerLCD to initialize before setting it up

  Wire.begin(); //Join the I2C bus
  Wire.setClock(400000);   // Set clock speed to be the fastest for better communication (fast mode)

  Wire.beginTransmission(SerLCD_Address);
  //Send custom characters to display
  //These are recorded to SerLCD and are remembered even after power is lost
  //There is a maximum of 8 custom characters that can be recorded
  loadCustomCharacter(0, back_slash);
  delay(50);
  loadCustomCharacter(1, tilde);
  delay(50);
  Wire.endTransmission(); //Stop I2C transmission

  Wire.beginTransmission(SerLCD_Address);
  loadCustomCharacter(2, heart);
  delay(50);
  loadCustomCharacter(3, empty_heart);
  delay(50);

  Wire.write('|'); //Put LCD into setting mode
  Wire.write(rVal); //Set red backlight

  Wire.write('|'); //Put LCD into setting mode
  Wire.write(gVal); //Set green backlight

  Wire.write('|'); //Put LCD into setting mode
  Wire.write(bVal); //Set blue backlight

  Wire.endTransmission(); //Stop I2C transmission

  delay(50);//short delay before sending next line

  Wire.beginTransmission(SerLCD_Address);
  Wire.write('|'); //Put LCD into setting mode
  Wire.write('-'); //Send clear display command
  //Control the cursor
  Wire.write(254); //Send command character
  Wire.write( (1 << 3) | (1 << 2) | (1 << 0) ); //Cursor on, blinking box on
  blink_box = true;
  Wire.endTransmission(); //Stop I2C transmission

  delay(50); //short delay before sending next line

}





void loop() {
  // Process USB tasks
  usb.Task();

}





//Given a number, i2cSendValue chops up an integer into four values and sends them out over I2C
void i2cSendValue(int value)
{
  Wire.beginTransmission(SerLCD_Address); // transmit to device #1

  Wire.write('|'); //Put LCD into setting mode
  Wire.write('-'); //Send clear display command

  Wire.endTransmission(); //Stop I2C transmission
}

Note: Since we are using the USB pins on the SAMD51 for host mode, you'll notice that port will ‎disappear as it is running the sketch. If you need to upload code again to the board, you will need ‎to hit the reset button twice to enter bootloader mode in order to upload code to the SAMD51 again.‎

What You Should See

Unplug the USB cable from your computer and insert it into the MicroMod ATP's host port. Power ‎up the board with the 5V wall adapter. Type a custom message using a USB keyboard to see the ‎output on the Qwiic SerLCD!‎

unplug_16

Use the d-pad to move the cursor around and adjust the message.‎

message_17

Example 2: Qwiic Digital Desk Sign w/ PIR

If you are using an external 5V wall adapter, make sure to disconnect it before inserting the USB C ‎cable to your computer's COM port to upload the project's code. Let's upload a project's sketch to ‎the board. Copy and paste the following code in the Arduino IDE. Head to Tools > Board to select ‎the correct board definition (in this case, SparkFun MicroMod SAMD51. Select the correct COM ‎port that the board enumerated to. Hit upload.‎

Copy Code
/******************************************************************************
  Host Keyboard Controller with Qwiic Serial LCD and Qwiic PIR
  Date Modified: 5 Aug 2021
  Modified by
  Ho Yun "Bobby" Chan
  Keyboard Controller Code Originally created 8 Oct 2012
  by Cristian Maglie

  ========== DESCRIPTION==========

  This project code takes input from a USB keyboard with
  the SAMD51's USB host pins and outputs characters to
  the Qwiic RGB Serial Enabled LCD 4x20. Leave a message
  behind as you walk away from your desk!

  Note: Not all keyboards are compatible. This code was tested on the following
  keyboards.

      - Dell KB216t
      - Logitech K120

  This example also builds off the example from Arduino which
  originally showed the output of a USB Keyboard connected to
  the Native USB port on an Arduino Due (SAMD21) Board.

  http://arduino.cc/en/Tutorial/KeyboardController

  This code is part of the public domain.

******************************************************************************/

#include <KeyboardController.h> // Require keyboard control library
#include <SparkFun_Qwiic_PIR.h>
QwiicPIR pir;

//#include <Wire.h> //Was needed for I2C to SerLCD and Qwiic PIR but it is defined in the Qwiic PIR library so we comment it out

#define SerLCD_Address 0x72  //If using SerLCD with I2C
#define SERIAL_PORT_MONITOR Serial1 //debug via hardware UART pins

//assuming that we are using a SerLCD 20x4 screen
//these variables keep track of cursor location
int row = 0;
int remappedRow = row;
int column = 0;

//depending on what screen you are using, we are counting 0 as well
//int maxRow = 1;     // for 16x2
int maxRow = 3;        //for 20x4
//int maxColumn = 15; // for 16x2
int maxColumn = 19;    //for 20x4

boolean rgb_backlight = true; //used for function keys to immediately turn on/off backlight
boolean blink_box = true; //used to keep track of blink box as a "cursor"

//values to keep track of rgb backlight
int rVal = 157; //128 = Off, 157 = 100%
int gVal = 187; //158 = Off, 187 = 100%
int bVal = 217; //188 = Off, 217 = 100%

//values used to turn off rgb in Power Save Mode
int rVal_OFF = 128;
int gVal_OFF = 158;
int bVal_OFF = 188;

int lcdContrast = 40; //used to keep track of contast: Range is 255 to 0, 40 is default


boolean activity = true; //used to keep track of movement or keyboard presses
const int noActivityMillis = 10000; //used to compare amount of time when no activity to turn of screen
int currentMillis = 0;  //get time based on how long the Arduino has been running
int lastActivityMillis = 0;





// Initialize USB Controller
USBHost usb;

// Attach keyboard controller to USB
KeyboardController keyboard(usb);

//boolean capsLock = false; //used to keep track of capsLock, this is not used in this code
boolean numLock = false; //display numbers if numLock on, we'll assume that the keyboard resets every time so it's off by default




// This function intercepts key press
void keyPressed() {
  SERIAL_PORT_MONITOR.print("Pressed:  ");
  //printKey(); //disabled so we are not sending two key presses to the SerLCD
}




// This function intercepts key release
void keyReleased() {
  SERIAL_PORT_MONITOR.print("Released: ");
  printKey();
}




void printKey() {
  activity = true;

  // getOemKey() returns the OEM-code associated with the key
  int tempKey = keyboard.getOemKey();
  SERIAL_PORT_MONITOR.print(" key:");
  SERIAL_PORT_MONITOR.print(tempKey);

  // getModifiers() returns a bits field with the modifiers-keys
  int mod = keyboard.getModifiers();
  SERIAL_PORT_MONITOR.print(" mod:");
  SERIAL_PORT_MONITOR.print(mod);

  SERIAL_PORT_MONITOR.print(" => ");

  if (mod & LeftCtrl)
    SERIAL_PORT_MONITOR.print("L-Ctrl ");
  if (mod & LeftShift)
    SERIAL_PORT_MONITOR.print("L-Shift ");
  if (mod & Alt)
    SERIAL_PORT_MONITOR.print("Alt ");
  if (mod & LeftCmd)
    SERIAL_PORT_MONITOR.print("L-Cmd ");
  if (mod & RightCtrl)
    SERIAL_PORT_MONITOR.print("R-Ctrl ");
  if (mod & RightShift)
    SERIAL_PORT_MONITOR.print("R-Shift ");
  if (mod & AltGr)
    SERIAL_PORT_MONITOR.print("AltGr ");
  if (mod & RightCmd)
    SERIAL_PORT_MONITOR.print("R-Cmd ");

  // getKey() returns the ASCII translation of OEM key
  // combined with modifiers.
  SERIAL_PORT_MONITOR.write(keyboard.getKey());
  SERIAL_PORT_MONITOR.println();

  /*
    USB SCAN CODES TO KEYCAP (US LAYOUT)
    https://www.win.tue.nl/~aeb/linux/kbd/scancodes-10.html

      CHARACTER KEYS
      USB SCAN CODE = KEYCAP

      4 to 29 = 'a' to 'z' and 'A' to 'Z' with Shift/CapsLock
      30 to 39 = '1' to '9', '0', and '!@#$%^&*()' with Shift
      40 = 'Enter'
      42 = 'Backspace'
      44 = 'Space'
      45 = '-' or '_' with Shift
      46 = '=' or '+' with Shift
      47 = '[' or '{' with Shift
      48 = ']' or '}' with Shift
      49 = '\' or '|' with Shift
      51 = ';' or ':' with Shift
      52 = `'` or '"' with Shift
      53 = '`' or '~' with Shift
      54 = ',' or '<' with Shift
      55 = '.' or '>' with Shift
      56 = '/' or '?' with Shift

      CURSOR CONTROL D-PAD (US LAYOUT)
      79 = `→` (e.g. right)
      80 = `←` (e.g. left)
      81 = `↓` (e.g. down)
      82 = `↑` (e.g. up)

      NUMERIC KEYPAD (US LAYOUT), NUMLOCK MUST BE ENABLED
      83 = 'NumLock'
      84 = '/'
      85 = '*'
      86 = '-'
      87 = '+'
      88 = 'Enter'
      89 = 'End'             or '1' w/ NumLock
      90 = '↓' (e.g. down)   or '2' w/ NumLock
      91 = 'Page Down'       or '3' w/ NumLock
      92 = '←' (e.g. left)   or '4' w/ NumLock
      93 =                      '5' w/ NumLock
      94 = '→' (e.g. right)  or '6' w/ NumLock
      95 = 'Home'             or '7' w/ NumLock
      96 = '↑' (e.g. up)      or '8' w/ NumLock
      97 = 'Page Up'          or '9' w/ NumLock
      98 = 'Insert'           or '0' w/ NumLock
      99 = 'Delete'           or '.' w/ NumLock

      OTHER
      41 = 'Escape'
      73 = 'Insert'
      74 = 'Home'
      75 = 'Page Up'
      76 = 'Delete'
      77 = 'End'
      78 = 'Page Down'

      FUNCTION KEYS
      58 = `F1`
      59 = `F2`
      60 = `F3`
      61 = `F4`
      62 = `F5`
      63 = `F6`
      64 = `F7`
      65 = `F8`
      66 = `F9`
      67 = `F10`
      68 = `F11`
      69 = `F12`



  */

  //----------TYPEWRITER AND KEYPAD KEYS (QWERTY, US LAYOUT) ----------
  if ((tempKey >= 4 && tempKey <= 39) ||
      (tempKey >= 44 && tempKey <= 49) ||
      (tempKey >= 51 && tempKey <= 56) ||
      (tempKey >= 84 && tempKey <= 87) ||
      ((tempKey >= 89 && tempKey <= 97) && numLock == true) ||
      tempKey == 73 ||
      tempKey == 98 ||
      (tempKey == 99 && numLock == true)) {

    Wire.beginTransmission(SerLCD_Address);

    if ( (tempKey == 49) && ((mod == 2) || (mod == 32)) ) {
      //Note: When sending the pipeline character(`|`),
      //we'll need to send 2x since the character
      //is also used as a setting character.
      Wire.write(keyboard.getKey());
      delay(50);//short delay before sending next line
      Wire.write(keyboard.getKey());
    }
    else if ((tempKey == 49) && (mod == 0)) {
      //Note: When sending back slash (`\`),
      //we will load the custom character using
      //printCustomChar() through I2C
      printCustomChar(0);
    }
    else if ( (tempKey == 53) && ((mod == 2) || (mod == 32)) ) {
      //Note: When sending tilde (`~`),
      //we will load the custom character using
      //printCustomChar() through I2C
      printCustomChar(1);
    }
    else if (tempKey == 73) {
      //Note: When sending 'insert',
      //we will load the custom character '♥' using
      //printCustomChar() through I2C
      printCustomChar(2);
    }
    else if (tempKey == 98 && numLock == false) {
      //Note: When sending keypad insert,
      //we will load the custom character '♡' using
      //printCustomChar() through I2C
      printCustomChar(3);
    }
    else {
      Wire.write(keyboard.getKey());
    }


    //after typing the cursor will move automatically to
    //next position so let's keep track of it
    if (column < maxColumn) {
      //if we are not at the end of the row, move cursor to next position
      column = column + 1;
    }
    else {
      //if we are at the end of the row, reset cursor to the beginning of the row
      column = 0;

      if (row < maxRow) {
        //if we are not on the last line, move cursor to the next line
        row = row + 1;
      }
      else {
        //if we are on the last line, move cursor to the first line
        row = 0;
      }
    }

    //remap according to SerLCD's line number
    if (row == 0) {
      remappedRow = 0;
    }
    else if (row == 1) {
      remappedRow = 64;
    }
    else if (row == 2) {
      remappedRow = 20;
    }
    else if (row == 3) {
      remappedRow = 84;
    }

    Wire.write(254); //Send command character
    Wire.write(128 + remappedRow + column); //update cursor position
    Wire.endTransmission(); //Stop I2C transmission

  }





  /*NOTE: These keys move the cursor so it is kept
    as separate condition statements. We will
    also include most of the numLock keys when 'NUMLOCK'
    is disabled.*/
  else if ((tempKey >= 79 && tempKey <= 82) ||
           tempKey == 40 ||
           tempKey == 74 ||
           tempKey == 77 ||
           tempKey == 75 ||
           tempKey == 78 ||
           tempKey == 83 ||
           tempKey == 88 ||
           ((tempKey >= 89 && tempKey <= 97) && numLock == false) ) {


    //move cursor based on d-pad or Enter key

    //----------NUMLOCK KEY----------
    if (tempKey == 83) {
      numLock = !numLock;// change state of numLock if pressed by inverting it
    }




    //----------LEFT KEY----------
    else if ((tempKey == 80) || (tempKey == 92 && numLock == false)) {

      if (column > 0) {
        //if we are at the beginning of the row
        column = column - 1;
      }
      else {
        //if we are at the end of the row
        column = maxColumn;

        if (row > 0) {
          //if we are not on the last line move up a line
          row = row - 1;
        }
        else {
          //if we are on the last line
          row = maxRow;
        }
      }
    }



    //----------RIGHT KEY----------
    else if ( (tempKey == 79) || (tempKey == 94 && numLock == false) ) {

      if (column < maxColumn) {
        //if we are at the before the end of the row
        column = column + 1;
      }
      else {
        //if we are at the end of the row, reset cursor position
        column = 0;

        if (row < maxRow) {
          //move to next line if we are before the last line
          row = row + 1;
        }
        else {
          //move to the first line if we have reached the end
          row = 0;
        }
      }
    }



    //----------UP KEY----------
    else if ( (tempKey == 82) || (tempKey == 96 && numLock == false) ) {
      if (row > 0) {
        //if we are not on the first line
        row = row - 1;
      }
      else {
        //if we are on the first line, move cursor to the last line
        row = maxRow;
      }
    }



    //----------DOWN KEY----------
    else if ( (tempKey == 81) || (tempKey == 90 && numLock == false) ) {
      if (row < maxRow) {
        //if we are not on the first line
        row = row + 1;
      }
      else {
        //if we are on the last line, move cursor to the first line
        row = 0;
      }
    }



    //----------ENTER KEY----------
    else if (tempKey == 40 || tempKey == 88) {
      //move to the first position of the next line
      column = 0;
      if (row < maxRow) {
        //if we are not on the first line, move cursor to the next line
        row = row + 1;
      }
      else {
        //if we are on the last line, move cursor to the first line
        row = 0;
      }
    }

    //----------HOME KEY----------
    else if (tempKey == 74 || (tempKey == 95 && numLock == false) ) {
      //move to the first position of the next line
      column = 0;
    }

    //----------END KEY----------
    else if (tempKey == 77 || (tempKey == 89 && numLock == false)) {
      //move to the first position of the next line
      column = maxColumn;
    }

    //----------PAGE UP KEY----------
    else if (tempKey == 75 || (tempKey == 97 && numLock == false)) {
      //move to the top row
      row = 0;
    }

    //----------PAGE DOWN KEY----------
    else if (tempKey == 78 || (tempKey == 91 && numLock == false)) {
      //move to the bottom row
      row = maxRow;
    }

    else if (tempKey == 93 && numLock == false ) {
      blink_box = !blink_box;// change state of blink_box if pressed by inverting it

      Wire.beginTransmission(SerLCD_Address);
      Wire.write(254); //Send command character

      if (blink_box == false) {
        Wire.write( (1 << 3) | (1 << 2) ); //Cursor off, blinking box off
      }
      else {
        //if blink_box == true

        Wire.write( (1 << 3) | (1 << 2) | (1 << 0) ); //Cursor off, blinking box on
      }

      Wire.endTransmission(); //Stop I2C transmission

    }


    //remap according to SerLCD's line number
    if (row == 0) {
      remappedRow = 0;
    }
    else if (row == 1) {
      remappedRow = 64;
    }
    else if (row == 2) {
      remappedRow = 20;
    }
    else if (row == 3) {
      remappedRow = 84;
    }

    Wire.beginTransmission(SerLCD_Address);
    Wire.write(254); //Send command character
    Wire.write(128 + remappedRow + column); //update cursor position
    Wire.endTransmission(); //Stop I2C transmission

  }




  //----------ESCAPE KEY----------
  else if (tempKey == 41) {
    //Make the escape key clear screen and reset cursor position.
    Wire.beginTransmission(SerLCD_Address);
    Wire.write('|'); //Put LCD into setting mode
    Wire.write('-'); //Send clear display command
    //Control the cursor
    Wire.write(254); //Send command character
    Wire.write( (1 << 3) | (1 << 2) | (1 << 0) ); //Cursor on, blinking box on
    blink_box = true;
    // reset cursor position to (0,0) after using clear display command
    row = 0;
    remappedRow = row;
    column = 0;

    Wire.write(254); //Send command character
    Wire.write(128 + remappedRow + column); //update cursor position
    Wire.endTransmission(); //Stop I2C transmission

  }



  //----------BACKSPACE KEY----------
  else if (tempKey == 42) {
    //Backspace
    //- 'Delete' by replacing with 'Space',
    //- move backward,
    //- then move backward again since cursor moves after sending character


    for (int i = 0 ; i < 2; i++) {

      if (i == 0) {
        //"Delete character" by replacing character with a space
        //and then moving cursor backwards after this condition statement.
        //We do this only once.

        Wire.beginTransmission(SerLCD_Address);
        Wire.write(32); //Send 'Space' Character
        Wire.endTransmission(); //Stop I2C transmission

        //cursor moved automatically to next position so let's keep track of it
        if (column < maxColumn) {
          column = column + 1;
        }
        else {
          column = 0;

          if (row < maxRow) {
            row = row + 1;
          }
          else {
            row = 0;
          }
        }
      }

      //For backspace, we are replacing the current position
      //with a space. This moves the cursor forward once space
      //To correct this, we are going to move backward twice
      //with the help of the for() loop.
      if (column > 0) {
        column = column - 1;
      }
      else {
        column = maxColumn;

        if (row > 0) {
          row = row - 1;
        }
        else {
          row = maxRow;
        }
      }


      //remap according to SerLCD's line number
      if (row == 0) {
        remappedRow = 0;
      }
      else if (row == 1) {
        remappedRow = 64;
      }
      else if (row == 2) {
        remappedRow = 20;
      }
      else if (row == 3) {
        remappedRow = 84;
      }

      Wire.beginTransmission(SerLCD_Address);
      Wire.write(254); //Send command character
      Wire.write(128 + remappedRow + column); //update cursor position
      Wire.endTransmission(); //Stop I2C transmission
      delay(50);




    }//end for() loop for controlling cursor with 'Backspace'
  }//end condition statement for 'Backspace'



  //----------DELETE KEY----------
  else if (tempKey == 76 || (tempKey == 99 && numLock == false) ) {
    //'Delete'
    //- 'Delete' by replacing with 'Space',
    //- move backward again since cursor moves after sending character
    //Note: Delete for the SerLCD is not like the traditional
    //forward delete key. For the SerLCD, we are simply
    //going to delete the character at the cursor position.
    //The characters ahead of the cursor will not be shifted
    //to the left.

    Wire.beginTransmission(SerLCD_Address);
    Wire.write(32); //Send 'Space' Character

    //cursor moved automatically to next position so let's keep track of it
    if (column < maxColumn) {
      column = column + 1;
    }
    else {
      column = 0;

      if (row < maxRow) {
        row = row + 1;
      }
      else {
        row = 0;
      }
    }

    //move cursor back to where it was
    if (column > 0) {
      column = column - 1;
    }
    else {
      column = maxColumn;

      if (row > 0) {
        row = row - 1;
      }
      else {
        row = maxRow;
      }
    }


    //remap according to SerLCD's line number
    if (row == 0) {
      remappedRow = 0;
    }
    else if (row == 1) {
      remappedRow = 64;
    }
    else if (row == 2) {
      remappedRow = 20;
    }
    else if (row == 3) {
      remappedRow = 84;
    }

    Wire.write(254); //Send command character
    Wire.write(128 + remappedRow + column); //update cursor position
    Wire.endTransmission(); //Stop I2C transmission


  }//end condition statement for 'Delete'



  else if (tempKey == 58) {
    //`F1`
    //rVal-

    if (rVal > 128) {
      rVal = rVal - 1;
    }
    else {
      rVal = 128;
    }

    Wire.beginTransmission(SerLCD_Address);
    Wire.write('|'); //Put LCD into setting mode
    Wire.write(rVal); //Update red value
    Wire.endTransmission(); //Stop I2C transmission

  }


  else if (tempKey == 59) {
    //`F2`
    //rVal+

    if (rVal < 157 ) {
      rVal = rVal + 1;
    }
    else {
      rVal = 157;
    }

    Wire.beginTransmission(SerLCD_Address);
    Wire.write('|'); //Put LCD into setting mode
    Wire.write(rVal); //Update red value
    Wire.endTransmission(); //Stop I2C transmission
  }


  else if (tempKey == 60) {
    //`F3`
    //gVal-

    if (gVal > 158 ) {
      gVal = gVal - 1;
    }
    else {
      gVal = 158;
    }

    Wire.beginTransmission(SerLCD_Address);
    Wire.write('|'); //Put LCD into setting mode
    Wire.write(gVal); //Update green value
    Wire.endTransmission(); //Stop I2C transmission

  }


  else if (tempKey == 61) {
    //`F4`
    //gVal+

    if (gVal < 187 ) {
      gVal = gVal + 1;
    }
    else {
      gVal = 187;
    }

    Wire.beginTransmission(SerLCD_Address);
    Wire.write('|'); //Put LCD into setting mode
    Wire.write(gVal); //Update green value
    Wire.endTransmission(); //Stop I2C transmission

  }

  else if (tempKey == 62) {
    //`F5`
    //bVal-

    if (bVal > 188 ) {
      bVal = bVal - 1;
    }
    else {
      bVal = 188;
    }

    Wire.beginTransmission(SerLCD_Address);
    Wire.write('|'); //Put LCD into setting mode
    Wire.write(bVal); //Update blue value
    Wire.endTransmission(); //Stop I2C transmission
  }

  else if (tempKey == 63) {
    //`F6`
    //bVal+

    if (bVal < 217 ) {
      bVal = bVal + 1;
    }
    else {
      bVal = 217;
    }

    Wire.beginTransmission(SerLCD_Address);
    Wire.write('|'); //Put LCD into setting mode
    Wire.write(bVal); //Update blue value
    Wire.endTransmission(); //Stop I2C transmission

  }

  else if (tempKey == 64) {
    //`F7`
    //backlight ON/OFF

    if (rVal == 128 && gVal == 158 &&  bVal == 188)
    { //if all values are off, we will turn it all ON in the next condition statement
      rgb_backlight = 0;
    }
    else {
      //if any of the LEDs is partially on, we will turn it all OFF in the next condition statement
      rgb_backlight = 1;
    }


    if (rgb_backlight == 0)
    { // rgb_backlight == false //OFF, so turn ON

      rgb_backlight = 1;// set it ON
      rVal = 157;
      gVal = 187;
      bVal = 217;


      Wire.beginTransmission(SerLCD_Address);

      Wire.write('|'); //Put LCD into setting mode
      Wire.write(rVal); //Set red backlight

      Wire.write('|'); //Put LCD into setting mode
      Wire.write(gVal); //Set green backlight

      Wire.write('|'); //Put LCD into setting mode
      Wire.write(bVal); //Set blue backlight

      Wire.endTransmission(); //Stop I2C transmission

    }
    else
    { // rgb_backlight == true //ON, so turn OFF

      rgb_backlight = 0;// set it OFF
      rVal = 128;
      gVal = 158;
      bVal = 188;

      Wire.beginTransmission(SerLCD_Address);

      Wire.write('|'); //Put LCD into setting mode
      Wire.write(rVal); //Set red backlight

      Wire.write('|'); //Put LCD into setting mode
      Wire.write(gVal); //Set green backlight

      Wire.write('|'); //Put LCD into setting mode
      Wire.write(bVal); //Set blue backlight

      Wire.endTransmission(); //Stop I2C transmission

    }


  }


  else if (tempKey == 65) {
    //`F8`
    //set custom color, let's set it to cyan (0%, 100%, 100%)

    rVal = 128;
    gVal = 187;
    bVal = 217;

    Wire.beginTransmission(SerLCD_Address);

    Wire.write('|'); //Put LCD into setting mode
    Wire.write(rVal); //Set red backlight

    Wire.write('|'); //Put LCD into setting mode
    Wire.write(gVal); //Set green backlight

    Wire.write('|'); //Put LCD into setting mode
    Wire.write(bVal); //Set blue backlight
    Wire.endTransmission(); //Stop I2C transmission

  }




  else if (tempKey == 66) {
    //`F9`
    //Custom Message 1

    Wire.beginTransmission(SerLCD_Address);
    Wire.write('|'); //Put LCD into setting mode
    Wire.write('-'); //Send clear display command

    // reset cursor position to (0,0) after using clear display command
    row = 0;
    remappedRow = row;
    column = 0;


    if (maxRow == 3 && maxColumn == 19) {
      //Note: We'll send the lines in separate I2C transmissions
      //instead of all at once. Sending several lines freezes the SerLCD.

      Wire.print("Gone dancing! I'll  ");
      Wire.endTransmission(); //Stop I2C transmission
      delay(50);//short delay before sending next line

      Wire.beginTransmission(SerLCD_Address);
      Wire.print("be back at 12:30pm! ");
      Wire.endTransmission(); //Stop I2C transmission
      delay(50);//short delay before sending next line

      Wire.beginTransmission(SerLCD_Address);
      Wire.print("         =)         ");
      Wire.endTransmission(); //Stop I2C transmission
      delay(50);//short delay before sending next line

      Wire.beginTransmission(SerLCD_Address);
      Wire.print("SparkFun Electronics");
      Wire.endTransmission(); //Stop I2C transmission
      delay(50);//short delay before sending next line


      //move cursor position so it's out of the way of the text;
      row = 2;
      remappedRow = 20;
      column = 19;
    }
    else {
      //Note: We'll send the lines in separate I2C transmissions
      //instead of all at once. Sending several lines freezes the SerLCD.

      Wire.print("Gone dancing!   ");
      Wire.endTransmission(); //Stop I2C transmission
      delay(50);//short delay before sending next line

      Wire.beginTransmission(SerLCD_Address);
      Wire.print("BRB at 12:30pm! ");
      Wire.endTransmission(); //Stop I2C transmission
      delay(50);//short delay before sending next line

      //move cursor position so it's out of the way of the text;
      row = 1;
      remappedRow = 64;
      column = 15;
    }

    Wire.beginTransmission(SerLCD_Address);

    Wire.write(254); //Send command character
    Wire.write(128 + remappedRow + column); //update cursor position
    Wire.endTransmission(); //Stop I2C transmission
    delay(50);//short delay before sending next line

  }




  else if (tempKey == 67) {
    //`F10`
    //Custom Message 2

    Wire.beginTransmission(SerLCD_Address);
    Wire.write('|'); //Put LCD into setting mode
    Wire.write('-'); //Send clear display command

    // reset cursor position to (0,0) after using clear display command
    row = 0;
    remappedRow = row;
    column = 0;

    if (maxRow == 3 && maxColumn == 19) {

      //Note: We'll send the lines in separate I2C transmissions
      //instead of all at once. Sending several lines freezes the SerLCD.

      Wire.print("Out for lunch! I'll ");
      Wire.endTransmission(); //Stop I2C transmission
      delay(50);//short delay before sending next line

      Wire.beginTransmission(SerLCD_Address);
      Wire.print("be back at  2:00pm! ");
      Wire.endTransmission(); //Stop I2C transmission
      delay(50);//short delay before sending next line

      Wire.beginTransmission(SerLCD_Address);
      Wire.print("        ^_^         ");
      Wire.endTransmission(); //Stop I2C transmission
      delay(50);//short delay before sending next line

      Wire.beginTransmission(SerLCD_Address);
      Wire.print("SparkFun Electronics");
      Wire.endTransmission(); //Stop I2C transmission
      delay(50);//short delay before sending next line


      //move cursor position so it's out of the way of the text;
      row = 2;
      remappedRow = 20;
      column = 19;
    }
    else {
      //Note: We'll send the lines in separate I2C transmissions
      //instead of all at once. Sending several lines freezes the SerLCD.

      Wire.print("Out for lunch!  ");
      Wire.endTransmission(); //Stop I2C transmission
      delay(50);//short delay before sending next line

      Wire.beginTransmission(SerLCD_Address);
      Wire.print("BRB at  2:00pm! ");
      Wire.endTransmission(); //Stop I2C transmission
      delay(50);//short delay before sending next line

      //move cursor position so it's out of the way of the text;
      row = 1;
      remappedRow = 64;
      column = 15;
    }

    Wire.beginTransmission(SerLCD_Address);

    Wire.write(254); //Send command character
    Wire.write(128 + remappedRow + column); //update cursor position
    Wire.endTransmission(); //Stop I2C transmission
    delay(50);//short delay before sending next line

  }




  else if (tempKey == 68) {
    //`F11`
    //Custom Message 3

    Wire.beginTransmission(SerLCD_Address);
    Wire.write('|'); //Put LCD into setting mode
    Wire.write('-'); //Send clear display command

    // reset cursor position to (0,0) after using clear display command
    row = 0;
    remappedRow = row;
    column = 0;


    if (maxRow == 3 && maxColumn == 19) {

      //Note: We'll send the lines in separate I2C transmissions
      //instead of all at once. Sending several lines freezes the SerLCD.

      Wire.print("In a meeting! I'll  ");
      Wire.endTransmission(); //Stop I2C transmission
      delay(50);//short delay before sending next line

      Wire.beginTransmission(SerLCD_Address);
      Wire.print("be back at  2:00pm! ");
      Wire.endTransmission(); //Stop I2C transmission
      delay(50);//short delay before sending next line

      Wire.beginTransmission(SerLCD_Address);
      Wire.print("                    ");
      Wire.endTransmission(); //Stop I2C transmission
      delay(50);//short delay before sending next line

      Wire.beginTransmission(SerLCD_Address);
      Wire.print("SparkFun Electronics");
      Wire.endTransmission(); //Stop I2C transmission
      delay(50);//short delay before sending next line


      //move cursor position so it's out of the way of the text;
      row = 2;
      remappedRow = 20;
      column = 19;
    }
    else {
      //Note: We'll send the lines in separate I2C transmissions
      //instead of all at once. Sending several lines freezes the SerLCD.

      Wire.print("In a meeting!   ");
      Wire.endTransmission(); //Stop I2C transmission
      delay(50);//short delay before sending next line

      Wire.beginTransmission(SerLCD_Address);
      Wire.print("BRB at  2:00pm! ");
      Wire.endTransmission(); //Stop I2C transmission
      delay(50);//short delay before sending next line

      //move cursor position so it's out of the way of the text;
      row = 1;
      remappedRow = 64;
      column = 15;
    }

    Wire.beginTransmission(SerLCD_Address);

    Wire.write(254); //Send command character
    Wire.write(128 + remappedRow + column); //update cursor position
    Wire.endTransmission(); //Stop I2C transmission
    delay(50);//short delay before sending next line

  }



  else if (tempKey == 69) {
    //`F12`
    //Custom Message 4

    Wire.beginTransmission(SerLCD_Address);
    Wire.write('|'); //Put LCD into setting mode
    Wire.write('-'); //Send clear display command

    // reset cursor position to (0,0) after using clear display command
    row = 0;
    remappedRow = row;
    column = 0;

    if (maxRow == 3 && maxColumn == 19) {

      //Note: We'll send the lines in separate I2C transmissions
      //instead of all at once. Sending several lines freezes the SerLCD.

      Wire.print("Bobby's desk is here");
      Wire.endTransmission(); //Stop I2C transmission
      delay(50);//short delay before sending next line

      Wire.beginTransmission(SerLCD_Address);
      Wire.print("   Working remote!  ");
      Wire.endTransmission(); //Stop I2C transmission
      delay(50);//short delay before sending next line

      Wire.beginTransmission(SerLCD_Address);
      Wire.print(" See you virtually! ");
      Wire.endTransmission(); //Stop I2C transmission
      delay(50);//short delay before sending next line

      Wire.beginTransmission(SerLCD_Address);
      Wire.print("SparkFun Electronics");
      Wire.endTransmission(); //Stop I2C transmission
      delay(50);//short delay before sending next line


      //move cursor position so it's out of the way of the text;
      row = 2;
      remappedRow = 20;
      column = 19;
    }
    else {
      //Note: We'll send the lines in separate I2C transmissions
      //instead of all at once. Sending several lines freezes the SerLCD.

      Wire.print("Bobby's desk is ");
      Wire.endTransmission(); //Stop I2C transmission
      delay(50);//short delay before sending next line

      Wire.beginTransmission(SerLCD_Address);
      Wire.print("here!GoneVirtual");
      Wire.endTransmission(); //Stop I2C transmission
      delay(50);//short delay before sending next line

      //move cursor position so it's out of the way of the text;
      row = 1;
      remappedRow = 64;
      column = 15;
    }

    Wire.beginTransmission(SerLCD_Address);

    Wire.write(254); //Send command character
    Wire.write(128 + remappedRow + column); //update cursor position
    Wire.endTransmission(); //Stop I2C transmission
    delay(50);//short delay before sending next line

  }



  delay(50); //The maximum update rate of OpenLCD is about 100Hz (10ms). A smaller delay will cause flicker

}





//Set Custom Characters for 5x8 Character Position
//'\'
//0x0,0x10,0x8,0x4,0x2,0x1,0x0,0x0
//%0,000,00,0,,%1,%0,%0
byte back_slash[8] = {
  0b00000,
  0b10000,
  0b01000,
  0b00100,
  0b00010,
  0b00001,
  0b00000,
  0b00000
};





//'~'
//0x0,0x0,0x0,0x8,0x15,0x2,0x0,0x0
//%0,%0,00,101,,%0,%0,%0
byte tilde[8] = {
  0b00000,
  0b00000,
  0b01000,
  0b10101,
  0b00010,
  0b00000,
  0b00000,
  0b00000
};





//'♥'
//0x0,0x0,0xa,0x1f,0x1f,0xe,0x4,0x0
//%0,%0,10,111,111,10,0,%0
byte heart[8] = {
  0b00000,
  0b00000,
  0b01010,
  0b11111,
  0b11111,
  0b01110,
  0b00100,
  0b00000
};





//'♡'
//0x0,0x0,0xa,0x15,0x11,0xa,0x4,0x0
//%0,%0,10,101,001,10,0,%0
byte empty_heart[8] = {
  0b00000,
  0b00000,
  0b01010,
  0b10101,
  0b10001,
  0b01010,
  0b00100,
  0b00000
};





//Given a character number (0 to 7 is valid)
//Given an 8 byte array
//Record this data as a custom character to CGRAM
void loadCustomCharacter(byte charNumber, byte charData[])
{
  if (charNumber > 7) charNumber = 7; //Error correction

  Wire.write('|'); //Send setting character
  Wire.write(27 + charNumber); //27 is the first custom character spot

  for (byte x = 0 ; x < 8 ; x++) //There are 8 bytes of data we need to load
    Wire.write(charData[x]); //Write 8 bytes of graphic data to display
}





//Display a given custom character that was previously loaded into CGRAM
void printCustomChar(byte charNumber)
{
  if (charNumber > 7) charNumber = 7; //Error correction

  Wire.write('|'); //Send setting character
  Wire.write(35 + charNumber); //Tell LCD to display custom char # 0-7
}





void setup() {
  SERIAL_PORT_MONITOR.begin(115200);
  //while (!SERIAL_PORT_MONITOR); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection
  SERIAL_PORT_MONITOR.println("Keyboard Controller Program started");

  if (usb.Init() == -1)
    SERIAL_PORT_MONITOR.println("OSC did not start.");

  delay(3000);//wait a second for the SerLCD to initialize before setting it up

  Wire.begin(); //Join the I2C bus
  Wire.setClock(400000);   // Set clock speed to be the fastest for better communication (fast mode)

  Wire.beginTransmission(SerLCD_Address);
  //Send custom characters to display
  //These are recorded to SerLCD and are remembered even after power is lost
  //There is a maximum of 8 custom characters that can be recorded
  loadCustomCharacter(0, back_slash);
  delay(50);
  loadCustomCharacter(1, tilde);
  delay(50);
  Wire.endTransmission(); //Stop I2C transmission

  Wire.beginTransmission(SerLCD_Address);
  loadCustomCharacter(2, heart);
  delay(50);
  loadCustomCharacter(3, empty_heart);
  delay(50);

  Wire.write('|'); //Put LCD into setting mode
  Wire.write(rVal); //Set red backlight

  Wire.write('|'); //Put LCD into setting mode
  Wire.write(gVal); //Set green backlight

  Wire.write('|'); //Put LCD into setting mode
  Wire.write(bVal); //Set blue backlight

  Wire.endTransmission(); //Stop I2C transmission

  delay(50);//short delay before sending next line

  Wire.beginTransmission(SerLCD_Address);
  Wire.write('|'); //Put LCD into setting mode
  Wire.write('-'); //Send clear display command
  //Control the cursor
  Wire.write(254); //Send command character
  Wire.write( (1 << 3) | (1 << 2) | (1 << 0) ); //Cursor on, blinking box on
  blink_box = true;
  Wire.endTransmission(); //Stop I2C transmission

  delay(50); //short delay before sending next line

  //check if pir will acknowledge over I2C
  if (pir.begin() == false) {
    SERIAL_PORT_MONITOR.println("Device did not acknowledge! Freezing.");
    Wire.beginTransmission(SerLCD_Address);
    Wire.print("PIR !Connected");
    Wire.endTransmission(); //Stop I2C transmission
    while (1);
  }

  SERIAL_PORT_MONITOR.println("PIR acknowledged. Waiting 30 Seconds while PIR warms up");
  Wire.beginTransmission(SerLCD_Address);
  Wire.print("Waiting 30 secs for PIR up");
  Wire.endTransmission(); //Stop I2C transmission
  for (uint8_t seconds = 0; seconds < 30; seconds++)
  {
    SERIAL_PORT_MONITOR.println(seconds);

    delay(1000);
  }

  SERIAL_PORT_MONITOR.println("PIR warm!");

  //Use this function call to change the debounce time of the PIR sensor
  //The parameter is the debounce time in milliseconds
  pir.setDebounceTime(500);

  Wire.beginTransmission(SerLCD_Address);
  Wire.write('|'); //Put LCD into setting mode
  Wire.write('-'); //Send clear display command
  Wire.endTransmission(); //Stop I2C transmission
  delay(50); //short delay before sending next line

  Wire.beginTransmission(SerLCD_Address);
  Wire.write('|'); //Send command character
  Wire.write('/'); //disable system messages since current buffer on the SerLCD does not save what is on screen or custom characters correctly
  Wire.endTransmission(); //Stop I2C transmission
  delay(50); //short delay before sending next line




}





void loop() {
  if (activity == true) {

    lastActivityMillis = millis(); //save time when event occurred

    if (lcdContrast != 5) {

      //turn on screen once
      Wire.beginTransmission(SerLCD_Address);
      //Control the cursor and display
      Wire.write('|'); //Put LCD into setting mode
      Wire.write(24); //Value to change contrast
      Wire.write(5); //Contrast from 255 to 0; 5 is default

      //turn on backlight to previous value
      Wire.write('|'); //Put LCD into setting mode
      Wire.write(rVal); //Set red backlight

      Wire.write('|'); //Put LCD into setting mode
      Wire.write(gVal); //Set green backlight

      Wire.write('|'); //Put LCD into setting mode
      Wire.write(bVal); //Set blue backlight

      Wire.endTransmission(); //Stop I2C transmission

      lcdContrast = 5;
    }
    activity = false; //reset flag
  }
  else {
    //check time to see if we are over 10s
    currentMillis = millis();

    if ( ( currentMillis - lastActivityMillis ) < noActivityMillis ) {
      //do nothing since t < 10s
    }
    else {
      //it's been over 10s then let's turn off screen

      if (lcdContrast != 255) {
        //turn off screen once
        Wire.beginTransmission(SerLCD_Address);
        //display off by turning contrast off
        Wire.write('|'); //Put LCD into setting mode
        Wire.write(24); //Value to change contrast
        Wire.write(255);//Contrast from 255 to 0; 5 is default

        delay(50);

        Wire.write('|'); //Put LCD into setting mode
        Wire.write(rVal_OFF); //Set red backlight

        Wire.write('|'); //Put LCD into setting mode
        Wire.write(gVal_OFF); //Set green backlight

        Wire.write('|'); //Put LCD into setting mode
        Wire.write(bVal_OFF); //Set blue backlight

        Wire.endTransmission(); //Stop I2C transmission
        lcdContrast = 255;
      }
    }
  }

  delay(50);




  // Process USB tasks
  usb.Task();

  //check if there is an available PIR event, and tell us what it is!
  if (pir.available()) {
    if (pir.objectDetected()) {
      SERIAL_PORT_MONITOR.println("Object Detected");
      activity = true;
    }
    if (pir.objectRemoved()) {
      SERIAL_PORT_MONITOR.println("Object Removed");
    }
    pir.clearEventBits();
  }

}





//Given a number, i2cSendValue chops up an integer into four values and sends them out over I2C
void i2cSendValue(int value)
{
  Wire.beginTransmission(SerLCD_Address); // transmit to device #1

  Wire.write('|'); //Put LCD into setting mode
  Wire.write('-'); //Send clear display command

  Wire.endTransmission(); //Stop I2C transmission
}

Note: Since we are using the USB pins on the SAMD51 for host mode, you'll notice that port will ‎disappear as it is running the sketch. If you need to upload code again to the board, you will need ‎to hit the reset button twice to enter bootloader mode in order to upload code to the SAMD51 again.‎

What You Should See

Unplug the USB cable from your computer and insert it into the MicroMod ATP's host port. Power ‎up the board with the 5V wall adapter. You should see something similar to Example 1b. However, ‎the RGB Character LCD turn off whenever there is no activity from the keyboard or motion. This ‎will save a little power as well as grab someone's attention when the Character LCD lights up and ‎displays a custom message.‎

Making It Better

There’s always room for improvement. After the project was completed, I realized that the project ‎could be improved. Below are a list of possible upgrades and improvements that could be ‎implemented for future builds.‎

  • Efficient Arduino Code

    • After the finishing the code to display a message on the serial character LCD, I found ‎out there was also an SerLCD Arduino Library to control the serial character LCD. The ‎examples used in this tutorial could be updated with latest SerLCD Arduino Library ‎functions to reduce redundancies in the code.

    • ‎I could also create modular function to move the cursor on the serial character LCD to ‎reduce redundancies in code.

    • ‎To make the code more efficient, run slightly faster, and save space on the processor, ‎I could use #ifdef's use Serial.print() only when I am debugging the code. The ‎tradeoff is that this would create more lines of code. *The display would react slowly to ‎the button press. Adjusting the delay between each I2C transmission and using the ‎SerLCD Arduino Library could increase amount of characters sent to improve the ‎performance.

  • More macros to include more predefined messages

    • The predefined messages were limited to just the function keys. I could include more ‎predefined messages by using button combinations.‎

  • Alternative Processors

    • When writing the code, the only processor that had USB host was the MicroMod ‎SAMD51 Processor Board. Now that the MicroMod Teensy Processor Board is ‎available, the code could be modified to use with PJRC's USB Host Library, which can ‎have additional keyboard support. There's also the MicroMod RP2040 Processor ‎Board also has host support through the Raspberry Pi Pico SDK.

  • Alternative Displays

    • The 16x2 and 20x4 Character LCDs only provide a limited number of characters on ‎the display. Using a different display from the SparkFun catalog could allow for more ‎space to add longer messages or add intricate animations depending on the type of ‎display.‎

Troubleshooting

Not working as expected and need help? ‎

If you need technical assistance and more information on a product that is not working as you ‎expected, we recommend heading on over to the SparkFun Technical Assistance page for some ‎initial troubleshooting.

SPARKFUN TECHNICAL ASSISTANCE PAGE‎

If you don't find what you need there, the SparkFun Forums are a great place to find and ask for ‎help. If this is your first visit, you'll need to create a Forum Account to search product forums and ‎post questions.

CREATE NEW FORUM ACCOUNT         LOG INTO SPARKFUN FORUMS

  • USB Keyboard Not Compatible

    • Not all USB keyboards are compatible with the USBHost Arduino Library. Try using a ‎different keyboard. Another alternative is to use the MicroMod Teensy Processor ‎Board, which has additional keyboard support.‎

Resources and Going Further

For more information, check out the resources below regarding this project!‎

Mfr Part # 10288
ADAPT TERM BL 2POS TO 2.1MM JCK
SparkFun Electronics
Mfr Part # 16398
SPARKFUN 20X4 SERLCD - RGB BACKL
SparkFun Electronics
$389.04
View More Details
Mfr Part # 12700
SPARKFUN USB TYPE A FEMALE BREAK
SparkFun Electronics
Mfr Part # 16791
MICROMOD SAMD51 PROCESSOR
SparkFun Electronics
$156.00
View More Details
Mfr Part # 14427
QWIIC CABLE - 100MM
SparkFun Electronics
Mfr Part # 15426
CBL USB2.0 A PLUG TO C PLG 0.98'
SparkFun Electronics
Mfr Part # 16885
MICROMOD ATP CARRIER BOARD
SparkFun Electronics
$168.76
View More Details
Mfr Part # 14456
SOLDERING IRON - 60W (ADJUSTABLE
SparkFun Electronics
$237.25
View More Details
Mfr Part # 08022
HOOK-UP SOLID 22AWG BLACK 25'
SparkFun Electronics
Mfr Part # 08024
HOOK-UP SOLID 22AWG YELLOW 25'
SparkFun Electronics
Add all DigiKey Parts to Cart
Have questions or comments? Continue the conversation on TechForum, DigiKey's online community and technical resource.