Overview
SegLCDLib uses a layered architecture separating concerns between abstract interface, transport, controller protocol handling, and display-specific implementations.
LCD Build Selection
By default, all LCD model implementations should remain enabled. This keeps the library usable from Arduino IDE without build flags.
Build systems that can pass preprocessor defines, such as PlatformIO or ESPHome, may use an explicit selection mode:
-DSEGLCD_DISABLE_ALL_LCDS
-DSEGLCD_ENABLE_PCF85134_XYGAX_SEG_I2C
Intended meaning:
SEGLCD_DISABLE_ALL_LCDS: start with no display-specific LCD models enabled.
SEGLCD_ENABLE_<MODEL>: add selected LCD model implementations back.
- Core, controller drivers and transports are not affected by
SEGLCD_DISABLE_ALL_LCDS.
Do not rely on a user #include <SegLCD_...h> to enable the matching implementation automatically. Each .cpp file is compiled as a separate translation unit, so headers included from the user sketch cannot reliably define model-selection macros for library implementation files.
┌─────────────────────────────────────────────────────┐
│ User Application (Arduino Sketch) │
└──────────────────┬──────────────────────────────────┘
│
┌──────────────────▼──────────────────────────────────┐
│ LCD Implementation Layer │
│ ├─ SegLCD_PCF85176_TempHumidity │
│ ├─ SegLCD_PCF85176_6DigitSignalBatteryProgress │
│ ├─ SegLCD_HT1621_4SegDegree │
│ └─ ... (one class per display variant) │
└──────────────────┬──────────────────────────────────┘
│ inherits
┌──────────────────▼──────────────────────────────────┐
│ SegLCDLib Base Class (SegLCDLib : public Print) │
│ - Abstract interface (pure virtual methods) │
│ - LCD API 1.0 spec compliance │
│ - Cursor positioning (row, col) │
└──────────────────┬──────────────────────────────────┘
│ base of drivers
┌──────────────────▼──────────────────────────────────┐
│ Controller Driver Layer │
│ ├─ SegDriver_PCx85 (I2C via transport) │
│ ├─ SegDriver_HT1621 (3-wire via transport) │
│ ├─ SegDriver_HT1622 (3-wire via transport) │
│ └─ SegDriver_VK0192 (3-wire via transport) │
└──────────────────┬──────────────────────────────────┘
│ uses
┌──────────────────▼──────────────────────────────────┐
│ Transport Layer │
│ ├─ SegTransportI2C / SegTransportI2CArduino │
│ └─ SegTransport3Wire / SegTransport3WireArduino │
└──────────────────┬──────────────────────────────────┘
│ wraps
┌──────────────────▼──────────────────────────────────┐
│ Hardware Communication │
│ ├─ Wire / TwoWire bus │
│ └─ GPIO pins for DATA / WR / CS │
└─────────────────────────────────────────────────────┘
Layer 1: Abstract Base Class
File: src/SegLCDLib.h
Defines the common interface for all LCD implementations:
virtual void clear() = 0;
virtual void setCursor(uint8_t row, uint8_t col) = 0;
virtual size_t write(uint8_t ch) = 0;
virtual void command(uint8_t cmd) = 0;
};
Abstract base class for LCD segment display drivers.
Definition SegLCDLib.h:45
virtual void command(uint8_t command)=0
Send RAW command to controller.
virtual void init()
Logical display sections that can be targeted by higher-level rendering logic.
Definition SegLCDLib.cpp:21
virtual void on()=0
Turn the display on.
virtual void off()=0
Turn the display off.
virtual void setCursor(uint8_t row, uint8_t col)
Set cursor on exact digit.
Definition SegLCDLib.cpp:92
virtual size_t write(uint8_t)
Definition SegLCDLib.h:113
virtual void clear()
Clear all visible segments on the display.
Definition SegLCDLib.cpp:80
Key Features
- Inherits from
Print: Enables print(), println(), write() from Arduino standard library
- LCD API 1.0 Spec: Follows Arduino's LCD API specification (https://playground.arduino.cc/Code/LCDAPI/)
- Cursor System: Row/column addressing for positioning output
- Row 0: First display row (for simple LCDs, the only row)
- Column: Digit position within that row
- Example: T1T2 LCD has Row 0 (clock), Row 1 (T1 temps), Row 2 (T2 temps)
Enumerations
};
};
enum BacklightMode {
BACKLIGHT_DIGITAL,
BACKLIGHT_PWM
};
ModeBias
Bias mode used by LCD controller.
Definition SegLCDLib.h:31
@ MODE_BIAS_13
1/3 bias
Definition SegLCDLib.h:32
@ MODE_BIAS_12
1/2 bias
Definition SegLCDLib.h:33
ModeDrive
Drive mode used by LCD controller.
Definition SegLCDLib.h:21
@ MODE_DRIVE_12
1/2 multiplexing
Definition SegLCDLib.h:23
@ MODE_DRIVE_13
1/3 multiplexing
Definition SegLCDLib.h:24
@ MODE_DRIVE_14
1/4 multiplexing
Definition SegLCDLib.h:25
@ MODE_DRIVE_STATIC
Static drive (1/1 duty)
Definition SegLCDLib.h:22
Layer 2: Transport Layer
Transports encapsulate low-level bus access and are injected into controller drivers.
Define SEGLCD_DISABLE_ARDUINO_TRANSPORT to disable Arduino transport declarations and implementation when the target environment provides its own transport and does not expose Arduino Wire.h.
I2C Transport
Files: src/SegTransport.h, src/SegTransportArduino.h
virtual void write(uint8_t address, uint8_t data) = 0;
virtual void write(uint8_t address, uint8_t* data,
size_t length) = 0;
};
Abstract I2C transport.
Definition SegTransport.h:54
virtual void write(uint8_t address, uint8_t data)=0
Write one byte to an I2C device.
Abstract base class for low-level bus transports.
Definition SegTransport.h:14
Arduino implementation:
};
Arduino TwoWire implementation of the I2C transport.
Definition SegTransportArduino.h:38
3-Wire Transport
Files: src/SegTransport.h, src/SegTransportArduino.h
virtual void set_cs(uint8_t chipselect,
bool state) = 0;
virtual void write(uint16_t data, uint8_t bitCount) = 0;
};
Abstract 3-wire serial transport.
Definition SegTransport.h:23
virtual void set_cs(uint8_t chipselect, bool state)=0
Drive chip-select line.
virtual void write(uint16_t data, uint8_t bitCount)=0
Write bits to the 3-wire bus, MSB first.
Arduino implementation:
};
Arduino GPIO implementation of the 3-wire transport.
Definition SegTransportArduino.h:14
void write(uint16_t data, uint8_t bitCount) override
Write bits to the 3-wire bus, MSB first.
Definition SegTransportArduino.cpp:25
Layer 3: Controller Drivers
Controllers handle protocol-level communication with LCD hardware. Each driver abstracts a specific protocol.
PCF85134 / PCF85176 / PCF8576 (I2C)
File: src/SegDriver_PCx85.h, src/SegDriver_PCF85134.h, src/SegDriver_PCF85176.h
void _writeAddrRam();
};
Base class for PCx85 LCD segment display drivers.
Definition SegDriver_PCx85.h:12
uint8_t _subaddress
Definition SegDriver_PCx85.h:117
uint8_t _address
Definition SegDriver_PCx85.h:116
SegTransportI2C & _transport
Definition SegDriver_PCx85.h:115
void _writeRam(uint8_t *data, size_t length, uint8_t address=0) override=0
Low-level method to write a data buffer to display RAM.
size_t _ramBufferSize
Size of allocated RAM buffer in bytes.
Definition SegLCDLib.h:394
uint8_t * _ramBuffer
Dynamic RAM buffer for display data (allocated by derived classes).
Definition SegLCDLib.h:389
Features:
- I2C protocol (2 pins: SDA, SCL)
- Uses shared dynamic RAM buffer from
SegLCDLib
- Sends I2C transactions through
SegTransportI2C
- I2C address set by SA0 pin (0x38 or 0x39)
- A0-A2 are protocol subaddresses (not I2C address pins)
- Multiplexed display support (1/3 or 1/4 duty)
- Bias and drive mode configuration
Subclass Chain:
HT1621 / HT1622 (3-Wire Serial)
Files: src/SegDriver_HT1621.h, src/SegDriver_HT1622.h
void _writeRam(uint8_t* data,
size_t length, uint8_t address);
};
void command(uint8_t command) override
Send command to the controller.
Definition SegDriver_3Wire.cpp:22
uint8_t _cs
Chip select pin for the display.
Definition SegDriver_3Wire.h:95
SegTransport3Wire & _transport
Definition SegDriver_3Wire.h:94
virtual void _writeRam(uint8_t *data, size_t length, uint8_t address)
Write multiple bytes to RAM starting at specified address.
Definition SegDriver_3Wire.cpp:58
HT1621 LCD segment display driver.
Definition SegDriver_HT1621.h:12
Features:
- 3-wire serial protocol: Clock, Data, Chip Select
- Uses shared dynamic RAM buffer from
SegLCDLib
- Sends DATA/WR/CS signaling through
SegTransport3Wire
- Typical controller RAM capacity is 16 bytes (HT1621) or 32 bytes (HT1622), but concrete LCD classes may allocate only the used portion
- Configurable BIAS and DRIVE modes
- Integrated into LCD module (no separate IC on board)
- No hardware peripheral required beyond GPIO pins
Protocol:
- Assert CS through transport
- Send command/address prefix
- Send data bits MSB first through transport
- Release CS through transport
VK0192 (3-Wire Serial)
File: src/SegDriver_VK0192.h
};
VK0192 LCD segment display driver.
Definition SegDriver_VK0192.h:12
Features:
- Uses shared dynamic RAM buffer from
SegLCDLib
- Sends 3-wire signaling through
SegTransport3Wire, same protocol family and timing as HT1621/HT1622
- VK0192 address space is 24×8 bit RAM (48 4-bit addresses 0-47)
- The reference
SegLCD_VK0192_5DigSigBattProgress display maps digits to non-sequential RAM addresses, but this reflects that specific module's SEG/COM pin layout, not a property of the VK0192 controller itself — controller-level RAM addressing is sequential, same as HT1621/HT1622
Layer 4: LCD Implementations
LCD classes combine a controller driver with display-specific segment mapping and symbols.
Design Pattern: LCD Inherits from Controller
Unlike typical composition patterns, LCD classes inherit directly from controller drivers:
virtual size_t write(uint8_t ch);
};
Implementation of the PCF85176 controllers.
Definition SegDriver_PCF85176.h:9
Temperature/humidity LCD with dual rows (PCF85176).
Definition SegLCD_PCF85176_TempHum.h:21
size_t write(uint8_t ch) override
Definition SegLCD_PCF85176_TempHum.cpp:112
Rationale:
- Controllers are always integrated into the LCD module (not separate components)
- Single instance serves both controller and display logic
- Reduces memory overhead (no separate controller instance)
- Simplifies user code (one object to manage)
Structure of LCD Class
public:
size_t write(uint8_t ch);
private:
static const uint8_t _charMap[256];
static const uint8_t DIGITS = 6;
};
6-digit LCD with signal/battery/progress indicators (PCF85176).
Definition SegLCD_PCF85176_6DigSigBattProgress.h:21
uint8_t _mapSegments(uint8_t val)
Definition SegLCD_PCF85176_6DigSigBattProgress.cpp:318
void setProgress(uint8_t value)
Definition SegLCD_PCF85176_6DigSigBattProgress.cpp:52
size_t write(uint8_t ch) override
Definition SegLCD_PCF85176_6DigSigBattProgress.cpp:204
void setSignalLevel(uint8_t value)
Definition SegLCD_PCF85176_6DigSigBattProgress.cpp:33
void setBatteryLevel(uint8_t value)
Definition SegLCD_PCF85176_6DigSigBattProgress.cpp:14
void init() override
Logical display sections that can be targeted by higher-level rendering logic.
Definition SegLCD_PCF85176_6DigSigBattProgress.cpp:9
Cursor and Positioning System
SegLCDLib uses row/column addressing inherited from LCD API:
setCursor(row, col);
write(character);
Row/Column Semantics
For most displays (single row):
setCursor(0, 0): Start of display
setCursor(0, 1): 2nd digit
setCursor(1, x): Invalid (most displays have no row 1)
For multi-row displays (e.g., T1T2 LCD):
setCursor(0, 0): Clock display, digit 1
setCursor(1, 0): T1 temperature, digit 1
setCursor(2, 0): T2 temperature, digit 1
Memory Management
RAM Buffering
All drivers maintain an in-memory buffer mirroring controller RAM:
uint8_t* _ramBuffer = nullptr;
size_t _ramBufferSize = 0;
_allocateBuffer(RAM_SIZE);
_ramBuffer[0] |= 0x01;
_writeRam(_ramBuffer[0], 0);
Benefits:
- Minimize I2C/3-wire transactions (one bulk write vs. multiple small writes)
- Support masked writes (update specific bits without full byte)
- Reduce latency (batch updates before hardware sync)
Segment Mapping
Each LCD class implements _mapSegments() to convert logical segments to physical RAM addresses:
_ramBuffer[addressForDigit0] |= bitsFor5;
_writeRam(_ramBuffer[addressForDigit0], hwAddressForDigit0);
Mapping varies by:
- Controller: Different RAM layouts (PCF vs HT vs VK)
- Display: Segment wiring (which output pin connects to which segment)
Initialization Flow
When user calls lcd.init():
1. Controller initialization (BIAS, DRIVE modes)
2. Enable display (turn on voltage supply)
3. Clear RAM buffer (all zeros)
4. Write buffer to hardware
5. Reset cursor to (0, 0)
Adding New Displays
Step 1: Identify Controller
Step 2: Create LCD Class
virtual size_t write(uint8_t ch);
void _mapSegments(uint8_t digit, uint8_t segments);
};
Instantiate with transport from user code:
SegLCD_PCF85176_MyDisplay lcd(transport);
For 3-wire displays:
SegLCD_HT1621_MyDisplay lcd(transport, csPin);
Step 3: Implement Segment Mapping
- Use RAW LCD variant to test physical wiring
- Record which output pins light which segments
- Implement
_mapSegments() to convert logical→physical
Step 4: Add Feature Methods
- Display-specific symbols (battery, signal, degree, etc.)
- Implement
write() to output characters correctly
See Adding New LCD for detailed tutorial.
Datasheet References
- PCF85134: I2C segment LCD controller
- PCF85176: I2C 40×8 segment LCD controller
- HT1621/1622: 3-wire serial LCD drivers
- VK0192: 3-wire serial LCD driver, same protocol family as HT1621/HT1622
Key Design Decisions
| Decision | Rationale |
| LCD inherits controller | Controllers integrated in LCD modules, not external |
| Transport injected into drivers | Separate bus access from controller protocol and ease Arduino/non-Arduino backends |
| RAM buffering | Minimize I2C/serial transactions, batch updates |
| Row/column addressing | Follow Arduino LCD API standard for consistency |
| Print inheritance | Enable standard print() and println() operations |
| Base driver per protocol | Reduce code duplication (PCF, HT1621, HT1622, VK0192 each once) |
Related Documentation