I2C Universal User interface



This is an LCD screen 128x84 with three built in fonts giving 8 lines by 21 characters; a rotary encoder with push switch and a spare bush button. All of this is driven by I2C giving a complete user interface or front end to a microcontroller system using only 2 wires.

Output from the host is directed to the LCD display via I2C. This will normally be in the form of text but static images can be used. Special characters could also be formed using host software.

Input to the host is via the rotary encoder. This is ideal for selecting menu style lists or inputting numerical values by turning the control. The encoder also has a push switch. In addition to this there is a push button. The rotary value and buttons are stored on the device and read via the I2C interface. The starting value of the rotary encoder can be set at any time.


The display is a standard 128x64 LCD. The LCD controller is the UC1701 and full access if needed is available via the I2C interface. Currently fitted the display has a white background with white LED for back-lighting. The back light does NOT need to be on to see the display.

The above is provided should any special control be required but otherwise this is included for information only.

The firmware has 3 built in fonts.

Font 1 8x6 will give 8 lines by 21 characters. The layout of the display is in 8 horizontal lines. The text must begin at one of these lines but the column can be 0 to 127. The other fonts are larger and so will display less characters. Fonts are selected via an I2C command and the positioning of the text is also via an I2C command.


Static images can be created and displayed by sending a stream of bytes representing the image over the I2C interface.

Images and special characters are formed using a paint program that must output 32 bit colour, even though only 2 colours are used. It is normal to use a black background and paint with white. It is important not to exceed the display size otherwise distortion will occur. Smaller images can of course be displayed.

The display cannot handle BMP files directly and so they must be converted with a command line utility. Python for Linux and an exe file for Windows users:

More information about this tool.

Linux: python
Windows double click on the gUtil.exe, no need to install

This will only accept BMP files. The output can either be in decimal or hex format. The resulting output may need slight modification depending on the system used. For example a .h file for the Arduino may need the 'char' qualifier changing to uint8_t.

Rotary Encoder

This uses a simple scheme: The initial value is set say to 0 and the host then reads the encode value. If the user has turned the shaft clockwise the number will be greater than 0 if anti-clockwise it will be less than zero, assuming the host uses signed values. The maximum value is 255 which represents about 10 turns.

The encoder switch and button work slightly different. If the user activates one of these by pressing it the I2C read value will be 1 otherwise it will be 0. On reading the button the value is automatically set back to 0.

Raspberry Pi

This has been tested using a Zero W running 2017-03-02-raspbian-jessie. To test the device follow these steps:

1) Connect to the RPi using the 3v3 interface

2) Enable I2C

I2C is disabled by default and so needs enabling either via

sudo raspi-config

sudo raspi-config' or through the RPi desktop. It is also advisable to install i2c tools:

sudo apt-get install -y i2c-tools

You can then use

i2cdetect -y 1

to check that the hardware is connected okay, you should see the default address of 0x23

3) Test



This is a simple example that can easily be expanded upon.


The above zip file has a selection of examples and the library itself.

Rotary Section

void setRot(char v); Sets the rotary value, used to set an initial value, usually 0
uint8_t getRot(); Gets the current value of the rotary encoder
uint8_t getSw(); Gets the value of the rotary encoder push switch, reading will set the value to 0
uint8_t getBtn(); Gets the value of the push button, reading will set the value to 0

EEPROM Section

NOTE: Values written to the EEPROM will not take effect until the device is reset. For what the values mean consult the data sheet in the Device Parameters section.

void deviceAddress(uint8_t adr); Sets new I2C address
void defaultInd(uint8_t value); See text - turns off system splash screen
void defaultContrast(uint8_t value); Contrast level used at start up
void defaultEOL(uint8_t value); End of line character for text
void defaultBL(uint8_t value); Back light value at start up

System Section

void EEwrite(uint8_t adr, uint8_t value); Writes a value to the specified EEPROM address
uint8_t EEread(uint8_t adr); Reads an EEPROM value at the specified address
uint16_t ID(); Gets the device id number as an integer, in this case 4242
void Version(uint8_t *b); Gets the firmware version as two bytes

LCD Section

void lcdReset(); resets the display
void lcdCmd(uint8_t cmd); Sends a command to the LCD controller; a command usually effects the way the display behaves.
void lcdData(uint8_t data); Writes a byte to the display at the current cursor position. Note this is NOT a character but a byte see data sheet.
void lcdPuts(uint8_t *s); Sends a string, the string must be terminates with 0, i.e. a valid C string
void lcdImg(uint8_t *s, uint8_t row, uint8_t col); (compound); Sends a data stream to the LCD display given the starting row and column.
void lcdBL(uint8_t v); Turns the back light on (1) and off (0)
void lcdContrast(uint8_t m); Sets the contrast, 20 is about right, can be set between 0 and 63.
void lcdFont(uint8_t m);  Sets font 1 to 3.
void lcdCls();  Clears screen and homes cursor.
void lcdCol(uint8_t col); Sets column position 0 to 127.
void lcdRow(uint8_t row); Sets page which is effectively the row, there are 8 rows, 0 to 7.
void lcdScroll(uint8_t s); sets the scrolling line - experiment with this


The library is written for the soft I2C and so the special I2C pins need not be used. As it happens in this example library it is fixed by using constants. There is no reason though why this could not be made more flexible in that the pins could be chosen in the sketch. For now to run the example use the following:

Arduino BV4212 (5V interface)
Ground GND
5V V+

Unzip the library file above and try one of the examples.

The Box

There is a 3d Printed box that will take an Arduino Nano and batteries on thingsverse here:

Be warned it is not perfect but works okay, the screw holes are ab bit large so the M3 screws that hold the BV4212 in are a bit loose. Having said that its not bad.


There is room for an on-off switch and battery, 3 are shown but it will take 4.

As an alternative a standard E77 (ebay e77 box) box can be used with an acrylic  or similar lid that  can be laser cut. This saves a considerable amount of time as the 3d printing process is so slow.

Both the above are DXF files that can be used in a laser cutter, they provide the lid for the above box.


This can be printed on sticky back gloss paper. There is one on the picture at the beginning of this text.

The zip file contains a dxf drawing and an SVG file that give accurate dimensions for the overlay