# LG ACB8300

The LG ACB8300 is a cheap monitor calibration utility. It is designed for LG monitors and is currently only supported by LG's True Color Pro software.

I bought such a device, even though I don't have a LG display. My initial thought was that the device was just another rebranded colorimeter - but it turned out it was wrong.

This page describes the USB protocol as well as the work towards integrating it into Argyll CMS.

## First steps with the calibrator

At first, I poked around the installation directory of True Color Pro and - to my surprise - found a DLL called LG_ACB8300.dll. This sounds interesting. I fired up Dependency Walker to get the exported functions:

• LG_Calibrator_CloseWaitButton
• LG_Calibrator_DeviceCheck_Signage
• LG_Calibrator_DeviceClose
• LG_Calibrator_DeviceClose_Signage
• LG_Calibrator_DeviceNumberWrite
• LG_Calibrator_DeviceOpen
• LG_Calibrator_DeviceOpen_Signage
• LG_Calibrator_GetAPIVersion
• LG_Calibrator_GetFWVersion
• LG_Calibrator_GetXYZ
• LG_Calibrator_GetXYZ_Signage
• LG_Calibrator_SetMonitorType
• LG_Calibrator_StartWaitButton

I don't know what “Signage” means, but some of the functions sound interesting. I googled them and found another project doing similar work: Calibrate Everything!. The guy over there traced True Color Pro to have some function signatures.

Thanks to his work, I could code a simple Python wrapper and later a simple C wrapper in order to debug the DLL.

## Debugging the LG_ACB8300.dll

I traced parts of the LG_ACB8300.dll with OllyDBG. I built a Python and a small C wrapper around the library in order to have access to the funtions and their parameters. The wrapper simply loads the DLL entry points and calls DeviceOpen, Get_ADC, Get_XYZ, etc. in order to reverse engineer the functionality.

Inside the Get_XYZ function, a lot of floating point magic is going on. After reversing all the floating point assembly, it turns out that the conversion from ADC to XYZ is performing as follows:

2. Do an offset correction (can be read from calibration, subract A from the USB protocol)
3. Apply a correction matrix (can be read from calibration, multiply by M from the USB protocol)
4. Do an offset correction (can be read from calibration, add X from the USB protocol)
5. Apply a monitor correction matrix (multiply by matrix from the DLL)

The monitor correction matrix is hardcoded in the DLL. The SetMonitorType() function simply loads a different matrix into memory.

The data section contains 7 different monitor types. Monitor Type 1 is a simple unity matrix.

For my DLL, the memory locations are:

Monitor Type Location
0 0x1000c150
1 0x1000c198
2 0x1000c1e0
3 0x1000c228
4 0x1000c108
5 0x1000c030
6 0x1000c0c0

The next 9 double numbers at the memory locations define the correction matrices. They are in row-column order.

## Hacking the USB protocol

The USB protocol consists of input reports and output reports, with a fixed length of 43 bytes. The host speaks first, the device responds (most of the time). The first byte is always the command byte, the remaining bytes are either payload or junk. The byte numbers of the table start with the payload, so you have to strip the first byte anyway.

They seem to not clear the send buffer before responding to a command. Thus, you always get the remaining bytes of the previous commands as well!

### Initialisation

Send: 0x01
Response: 0x03

This seems to initialise the device. The bytes in the response are yet unknown.

Get the different calibration matrices and offset values from the device. M is a 3×3 matrix for the ADC to XYZ conversion, A is the first ADC offset, X is the second XYZ offset (both being a vector of length 3).

Send: 0x51
Response: 0x53

Bytes Format Description
0-7 double M[0][0]
8-15 double M[0][1]
16-23 double M[0][2]
24-31 double M[1][0]
32-39 double M[1][1]

Send: 0x52
Response: 0x53

Bytes Format Description
0-7 double M[1][2]
8-15 double M[2][0]
16-23 double M[2][1]
24-31 double M[2][2]

Send: 0x54
Response: 0x53

Bytes Format Description
0-7 double A[0]
8-15 double A[1]
16-23 double A[2]
24-31 double X[0]
32-39 double X[1]

Send: 0x55
Response: 0x53

Bytes Format Description
0-7 double X[2]

Send: 0x31
Response: 0x32

Bytes Format Description
1-2 int unknown
3-4 int Z value
5-6 int Y value
7-8 int X value

Send: 0x80
Response: 0x88

Bytes Format Description
1 int Firmare Version

In order to derive the firmware version, divide the value by 100. My device reports 100 which corresponds to firmware version 1.0.

### Wait for button input

Send: 0x05
Response: 0x03

Whenever the button is pressed, the device sends 0x62.

## ArgyllCMS support

Although the device will probably be never officially supported by ArgyllCMS, I developed an initial driver for the device. You can find the patch for Argyll 1.8.3 here. Be aware that it is only tested on my colorimeter and only on Linux. The patches to the Windows .inf-files are completely untested.