~/contents
After using 7-segment displays for sensor data for a bit I was quite happy to get a few of these LCD modules in the mail. These modules come with an 16x2 LCD and the driver board. There is a blue potentiometer on the back of the module that generally needs to be adjusted. My display default setting was 0 brightness. This is standard 4-wire I2C hookup:
GND > GND 5V > VCC SDA (A4) > SDA SCL (A5) > SCL
To locate the module’s I2C address, plug in the module and load and run I2C Scanner, code reproduced below.
// i2c_scanner
//
// Version 1
// This program (or code that looks like it)
// can be found in many places.
// For example on the Arduino.cc forum.
// The original author is not know.
// Version 2, Juni 2012, Using Arduino 1.0.1
// Adapted to be as simple as possible by Arduino.cc user Krodal
// Version 3, Feb 26 2013
// V3 by louarnold
// Version 4, March 3, 2013, Using Arduino 1.0.3
// by Arduino.cc user Krodal.
// Changes by louarnold removed.
// Scanning addresses changed from 0...127 to 1...119,
// according to the i2c scanner by Nick Gammon
// http://www.gammon.com.au/forum/?id=10896
// Version 5, March 28, 2013
// As version 4, but address scans now to 127.
// A sensor seems to use address 120.
// Version 6, November 27, 2015.
// Added waiting for the Leonardo serial communication.
//
// This sketch tests the standard 7-bit addresses
// Devices with higher bit address might not be seen properly.
#include <Wire.h>
void setup(){
Wire.begin();
Serial.begin(9600);
while (!Serial); // Leonardo: wait for serial monitor
Serial.println("\nI2C Scanner");
}
void loop(){
byte error, address;
int nDevices;
Serial.println("Scanning...");
nDevices = 0;
for(address = 1; address < 127; address++ ) {
// The i2c_scanner uses the return value of
// the Write.endTransmisstion to see if
// a device did acknowledge to the address.
Wire.beginTransmission(address);
error = Wire.endTransmission();
if (error == 0) {
Serial.print("I2C device found at address 0x");
if (address<16)
Serial.print("0");
Serial.print(address,HEX);
Serial.println(" !");
nDevices++;
}
else if (error==4){
Serial.print("Unknown error at address 0x");
if (address<16)
Serial.print("0");
Serial.println(address,HEX);
}
}
if (nDevices == 0)
Serial.println("No I2C devices found\n");
else
Serial.println("done\n");
delay(5000); // wait 5 seconds for next scan
}
It will output to the Serial monitor like so:
16:28:00.597 -> I2C Scanner
16:28:00.597 -> Scanning...
16:28:00.597 -> I2C device found at address 0x27 !
16:28:00.631 -> done
16:28:00.631 ->
There are a number of LCD libraries out there, including some interesting variations such as double-height, bar graphs, LCD effects, and LCD menus. Personally, I found these libraries a little confusing at first, but marcoschwart’s LiquidCrystal_I2C library worked without any tweaks and is also available through the Arduino IDE Library Manager. The CustomChars example code demonstrates how to create your own characters or display hex. This basic example uses elements from several sketches.
#include <LiquidCrystal_I2C.h> // https://github.com/marcoschwartz/LiquidCrystal_I2C
const byte lcdAddr = 0x27; // I2C address
const byte lcdCols = 16; // character per row
const byte lcdRows = 2; // lines
uint8_t heart[8] = {0x0, 0xa, 0x1f, 0x1f, 0xe, 0x4, 0x0};
LiquidCrystal_I2C lcd(lcdAddr, lcdCols, lcdRows);
void setup(){
lcd.init();
lcd.backlight();
lcd.createChar(0, heart);
}
void loop(){
lcd.home();
lcd.print("Hello world!");
delay(1000);
lcd.setCursor(0,1);
lcd.print("Hello world!!");
lcd.write(0);
lcd.write(0);
lcd.write(0);
delay(1000);
lcd.clear();
delay(1000);
}
LCD Button Module
After the fact it occured to me a button shield would be handy. The LCD button shields commonly available use voltage dividers to allow one analog pin read multiple button inputs. I came up with a simple 4-button D-pad configuration that only requires 3 10k resistors and two pins.
The way these types of circuits work, you take an analog reading and the resistance you get back reflects which button is being pushed. For this module, the idle value is 1023 (give or take) and the following button presses return these approximate values:
Left 229 Down 365 Right 458 Up 14
These values may fluxuate slightly, so we check for a value within a range to determine which button was pressed.
#include <LiquidCrystal_I2C.h> // https://github.com/marcoschwartz/LiquidCrystal_I2C
#include <Bounce2.h> // https://github.com/thomasfredericks/Bounce2
const byte BUTTON_PIN = A1;
const byte UP_VALUE = 14;
const byte LEFT_VALUE = 229;
const byte DOWN_VALUE = 367;
const byte RIGHT_VALUE = 462;
const byte VALUE_RANGE = 10;
Bounce debouncer = Bounce();
LiquidCrystal_I2C lcd(0x27,16,2); // set the LCD address to 0x27 for a 16 chars and 2 line display
void setup(){
Serial.begin(9600);
debouncer.attach(BUTTON_PIN, INPUT_PULLUP);
debouncer.interval(25);
lcd.init();
lcd.backlight();
}
void loop(){
debouncer.update();
if(debouncer.fell()){
int buttonPressed = readButton();
Serial.println(buttonPressed);
lcd.clear();
lcd.home();
lcd.print("Pressed button ");
lcd.print(buttonPressed);
lcd.setCursor(0,1);
lcd.print("Value is: ");
int value = analogRead(BUTTON_PIN);
lcd.print(value);
}
}
int readButton(){
byte value = analogRead(BUTTON_PIN);
if(value >= (RIGHT_VALUE - VALUE_RANGE) && value <= (RIGHT_VALUE + VALUE_RANGE)){
return 4;
}
else if(value >= (DOWN_VALUE - VALUE_RANGE) && value <= (DOWN_VALUE + VALUE_RANGE) ){
return 3;
}
else if(value >= (LEFT_VALUE - VALUE_RANGE) && value <= (LEFT_VALUE + VALUE_RANGE) ){
return 2;
}
else if(value >= (UP_VALUE - VALUE_RANGE) && value <= (UP_VALUE + VALUE_RANGE) ){
return 1;
}
else {
return 0;
}
}
Resources: