According to Wikipedia, DDC/CI (Command Interface) standard was introduced in August 1998, which specifies a means for a computer to send commands to the monitor, as well as receive sensor data from the monitor, over a bidirectional link. My second external monitor is Dell P2222H, which just supports this functionality. Therefore, it is possible to adjust brightness and contrast from software without bothering to manipulate a button on the back.

On Linux, this can be achieved via ddcutil. To make it work with NVIDIA graphics card, we should copy /usr/share/ddcutil/data/90-nvidia-i2c.conf into the folder /etc/X11/xorg.conf.d/ and modify the device name in this file to be the same as that is specified in /etc/X11/xorg.conf for nvidia driver (if xorg.conf exists). For example,

Section "Device"
Driver "nvidia"
Identifier "Device0"
Option     "RegistryDwords"  "RMUseSwI2c=0x01; RMI2cSpeed=100"
# solves problem of i2c errors with nvidia driver
# per https://devtalk.nvidia.com/default/topic/572292/-solved-does-gddccontrol-work-for-anyone-here-nvidia-i2c-monitor-display-ddc/#4309293
EndSection

Then restart X11 or reboot the system, we can now control the monitor using ddcutil on the command line. Typical usage of ddcutil:

  • Detect connected monitors.

    sudo ddcutil detect
    

    My computer reports

    Display 1
    I2C bus:             /dev/i2c-1
    EDID synopsis:
    Mfg id:           BNQ
    Model:            BenQ PD2700U
    Serial number:    ETJCK80010CL0
    Manufacture year: 2019
    EDID version:     1.4
    VCP version:         2.2
        
    Display 2
    I2C bus:             /dev/i2c-2
    EDID synopsis:
    Mfg id:           DEL
    Model:            DELL P2222H
    Serial number:    H35TPK3
    Manufacture year: 2022
    EDID version:     1.4
    VCP version:         2.1
    
  • Get the capabilities supported by the monitor, for example, display 2 is my Dell monitor.

    sudo ddcutil --display 2 capabilities
        
    Model: P2222H
    MCCS version: 2.1
    Commands:
    Command: 01 (VCP Request)
    Command: 02 (VCP Response)
    Command: 03 (VCP Set)
    Command: 07 (Timing Request)
    Command: 0c (Save Settings)
    Command: e3 (Capabilities Reply)
    Command: f3 (Capabilities Request)
    VCP Features:
    Feature: 02 (New control value)
    Feature: 04 (Restore factory defaults)
    Feature: 05 (Restore factory brightness/contrast defaults)
    Feature: 08 (Restore color defaults)
    Feature: 10 (Brightness)
    Feature: 12 (Contrast)
    Feature: 14 (Select color preset)
    Values:
    05: 6500 K
    08: 9300 K
    0b: User 1
    0c: User 2
    Feature: 16 (Video gain: Red)
    Feature: 18 (Video gain: Green)
    Feature: 1A (Video gain: Blue)
    Feature: 52 (Active control)
    Feature: 60 (Input Source)
    Values:
    01: VGA-1
    0f: DisplayPort-1
    11: HDMI-1
    Feature: AA (Screen Orientation)
    Values:
    01: 0 degrees
    02: 90 degrees
    04: 270 degrees
    Feature: AC (Horizontal frequency)
    Feature: AE (Vertical frequency)
    Feature: B2 (Flat panel sub-pixel layout)
    Feature: B6 (Display technology type)
    Feature: C6 (Application enable key)
    Feature: C8 (Display controller type)
    Feature: C9 (Display firmware level)
    Feature: CC (OSD Language)
    Values:
    02: English
    03: French
    04: German
    06: Japanese
    09: Russian
    0a: Spanish
    0d: Chinese (simplified / Kantai)
    0e: Portuguese (Brazil)
    Feature: D6 (Power mode)
    Values:
    01: DPM: On,  DPMS: Off
    04: DPM: Off, DPMS: Off
    05: Write only value to turn off display
    Feature: DC (Display Mode)
    Values:
    00: Standard/Default mode
    03: Movie
    05: Games
    Feature: DF (VCP Version)
    Feature: E0 (manufacturer specific feature)
    Feature: E1 (manufacturer specific feature)
    Feature: E2 (manufacturer specific feature)
    Values: 00 02 04 0E 12 14 (interpretation unavailable)
    Feature: F1 (manufacturer specific feature)
    Feature: F2 (manufacturer specific feature)
    Feature: FD (manufacturer specific feature)
    Feature: FE (manufacturer specific feature)
    

    From above, we can see the VCP feature code for brightness is 10 and that for contrast is 12.

  • Get all VCP parameter values.

    sudo ddcutil --display 2 getvcp all
    

    It returns the following information.

    VCP code 0x02 (New control value             ): One or more new control values have been saved(0x02)
    VCP code 0x0b (Color temperature increment   ): Invalid value: 0
    VCP code 0x0c (Color temperature request     ): 3000 + 2 * (feature 0B color temp increment) degree(s) Kelvin
    VCP code 0x0e (Clock                         ): current value =    50, max value =   100
    VCP code 0x10 (Brightness                    ): current value =    40, max value =   100
    VCP code 0x12 (Contrast                      ): current value =    30, max value =   100
    VCP code 0x14 (Select color preset           ): User 2 (sl=0x0c)
    VCP code 0x16 (Video gain: Red               ): current value =   100, max value =   100
    VCP code 0x18 (Video gain: Green             ): current value =   100, max value =   100
    VCP code 0x1a (Video gain: Blue              ): current value =    85, max value =   100
    VCP code 0x1e (Auto setup                    ): Auto setup not active (sl=0x00)
    VCP code 0x20 (Horizontal Position (Phase)   ): current value =   249, max value =   100
    VCP code 0x30 (Vertical Position (Phase)     ): current value =    11, max value =   100
    VCP code 0x3e (Clock phase                   ): current value =     0, max value =   100
    VCP code 0x52 (Active control                ): Value: 0x14
    VCP code 0x60 (Input Source                  ): DisplayPort-1 (sl=0x0f)
    VCP code 0x62 (Audio speaker volume          ): current value =    15, max value =   255
    VCP code 0x6c (Video black level: Red        ): current value =   128, max value =   255
    VCP code 0x6e (Video black level: Green      ): current value =   128, max value =   255
    VCP code 0x70 (Video black level: Blue       ): current value =   128, max value =   255
    VCP code 0x8d (Audio Mute                    ): Invalid value (sl=0x00)
    VCP code 0xaa (Screen Orientation            ): 0 degrees (sl=0x01)
    VCP code 0xac (Horizontal frequency          ): 1964 hz
    VCP code 0xae (Vertical frequency            ): 60.00 hz
    VCP code 0xb2 (Flat panel sub-pixel layout   ): Red/Green/Blue vertical stripe (sl=0x01)
    VCP code 0xb4 (Source Timing Mode            ): mh=0x00, ml=0x02, sh=0x00, sl=0x01
    VCP code 0xb6 (Display technology type       ): LCD (active matrix) (sl=0x03)
    VCP code 0xc0 (Display usage time            ): Usage time (hours) = 123 (0x00007b) mh=0xff, ml=0xff, sh=0x00, sl=0x7b
    VCP code 0xc6 (Application enable key        ): 0x45cc
    VCP code 0xc8 (Display controller type       ): Mfg: Mstar (sl=0x05), controller number: mh=0x00, ml=0x00, sh=0x56
    VCP code 0xc9 (Display firmware level        ): 41.2
    VCP code 0xca (OSD                           ): OSD Enabled (sl=0x02)
    VCP code 0xcc (OSD Language                  ): English (sl=0x02)
    VCP code 0xd6 (Power mode                    ): DPM: On,  DPMS: Off (sl=0x01)
    VCP code 0xdc (Display Mode                  ): Standard/Default mode (sl=0x00)
    VCP code 0xde (Scratch Pad                   ): SL: 0x00 ,  SH: 0x00
    VCP code 0xdf (VCP Version                   ): 2.1
    

With the above command and information, I can create scripts for setting brightness and contrast for day and evening respectively.

# For day
sudo ddcutil --display 2 setvcp 10 55
sudo ddcutil --display 2 setvcp 12 50
sudo ddcutil --display 2 setvcp 1a 85

# For evening
sudo ddcutil --display 2 setvcp 10 40
sudo ddcutil --display 2 setvcp 12 30
sudo ddcutil --display 2 setvcp 1a 75