On many of the I2C backpacks there is a jumper that controls the backlight on and off but if the jumper is removed and a PWM pin is connected to the top jumper pin the backlight brightness can be controlled with analogWrite(). The hd44780 library backlight() and noBacklight() functions still operate as normal (backlight on or off). Probably LiquidCrystal, too, but i don't use that library.
Example code tested on real hardware.
// hd44780 library see https://github.com/duinoWitchery/hd44780
// thehd44780 library is available through the IDE library manager
// 2004 I2C LCD backlight control by groundFungus aka c.goulding
#include <Wire.h>
#include <hd44780.h> // main hd44780 header
#include <hd44780ioClass/hd44780_I2Cexp.h> // i2c expander i/o class header
const byte backlightPin = 3;
const byte ldrPin = A0;
hd44780_I2Cexp lcd; // declare lcd object: auto locate & auto config expander chip
// LCD geometry
const int LCD_COLS = 20;
const int LCD_ROWS = 4;
void setup()
{
pinMode(backlightPin, OUTPUT);
pinMode(ldrPin, INPUT_PULLUP);
lcd.begin(LCD_COLS, LCD_ROWS);
lcd.backlight();
lcd.clear();
lcd.print("Hello World");
lcd.setCursor(0, 1);
lcd.print("Millis ");
lcd.setCursor(0,2);
lcd.print("LDR value = ");
}
void loop()
{
updateLCD();
}
void updateLCD()
{
static unsigned long lcdTimer = 0;
unsigned long lcdInterval = 500; // update 2 times per second
if (millis() - lcdTimer >= lcdInterval)
{
lcdTimer = millis();
int lightVal = analogRead(ldrPin);
lcd.setCursor(8, 1);
lcd.print(" "); // overwrite old data
lcd.setCursor(8, 1); // reset the cursor
lcd.print(millis());
lcd.setCursor(11,2);
lcd.print(" ");
lcd.setCursor(11,2);
lcd.print(lightVal);
// brighter light, dimmer backlight
analogWrite(backlightPin, lightVal/4);
}
}