Differences
This shows you the differences between two versions of the page.
| Both sides previous revision Previous revision Next revision | Previous revision | ||
| projects:acb8300 [2016/01/02 22:56] – Andreas Böhler | projects:acb8300 [2016/01/25 23:20] (current) – [Wait for button inpurt] Andreas Böhler | ||
|---|---|---|---|
| Line 6: | Line 6: | ||
| This page describes the USB protocol as well as the work towards integrating it into Argyll CMS. | 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_DeviceNumberRead | ||
| + | * LG_Calibrator_DeviceNumberWrite | ||
| + | * LG_Calibrator_DeviceOpen | ||
| + | * LG_Calibrator_DeviceOpen_Signage | ||
| + | * LG_Calibrator_GetADC | ||
| + | * LG_Calibrator_GetAPIVersion | ||
| + | * LG_Calibrator_GetFWVersion | ||
| + | * LG_Calibrator_GetXYZ | ||
| + | * LG_Calibrator_GetXYZ_Signage | ||
| + | * LG_Calibrator_SetMonitorType | ||
| + | * LG_Calibrator_StartWaitButton | ||
| + | |||
| + | I don't know what " | ||
| + | |||
| + | 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 ===== | ===== Debugging the LG_ACB8300.dll ===== | ||
| - | I traced parts of the LG_ACB8300.dll with OllyDBG. I built 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. | + | 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: | 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: | ||
| - Read ADC value from device | - Read ADC value from device | ||
| - | - Do an offset correction (can be read from calibration) | + | - Do an offset correction (can be read from calibration, **subract** A from the USB protocol) |
| - | - Apply a correction matrix (can be read from calibration) | + | - Apply a correction matrix (can be read from calibration, **multiply** by M from the USB protocol) |
| - | - Do an offset correction (can be read from calibration) | + | - Do an offset correction (can be read from calibration, **add** X from the USB protocol) |
| - | - Apply a monitor correction matrix | + | - Apply a monitor correction matrix |
| The monitor correction matrix is hardcoded in the DLL. The SetMonitorType() function simply loads a different matrix into memory. | The monitor correction matrix is hardcoded in the DLL. The SetMonitorType() function simply loads a different matrix into memory. | ||
| Line 38: | Line 62: | ||
| ===== Hacking the USB protocol ===== | ===== 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 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). |
| + | |||
| + | 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 ==== | ==== Initialisation ==== | ||
| Send: '' | Send: '' | ||
| - | Response: '' | + | Response: '' |
| + | |||
| + | This seems to initialise the device. The bytes in the response are yet unknown. | ||
| ==== Read Calibration ==== | ==== Read Calibration ==== | ||
| + | |||
| + | Get the different calibration matrices and offset values from the device. M is a 3x3 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: '' | Send: '' | ||
| Response: '' | Response: '' | ||
| + | |||
| + | ^ 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: '' | Send: '' | ||
| Response: '' | Response: '' | ||
| + | |||
| + | ^ 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: '' | Send: '' | ||
| Response: '' | Response: '' | ||
| + | |||
| + | ^ 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: '' | Send: '' | ||
| - | Response: '' | + | Response: '' |
| + | |||
| + | ^ Bytes ^ Format ^ Description ^ | ||
| + | | 0-7 | double | X[2] | | ||
| ==== Read ADC values ==== | ==== Read ADC values ==== | ||
| + | |||
| + | Read ADC values from the device. | ||
| Send: '' | Send: '' | ||
| Response: '' | Response: '' | ||
| + | |||
| + | ^ Bytes ^ Format ^ Description ^ | ||
| + | | 1-2 | int | unknown | | ||
| + | | 3-4 | int | Z value | | ||
| + | | 5-6 | int | Y value | | ||
| + | | 7-8 | int | X value | | ||
| ==== Read Firmware version ==== | ==== Read Firmware version ==== | ||
| Line 69: | Line 131: | ||
| Response: '' | Response: '' | ||
| - | ==== Wait for button | + | ^ Bytes ^ Format ^ Description ^ |
| + | | 1 | int | Firmare Version | | ||
| + | |||
| + | In order to derive the firmware version, divide the value by 100. My device reports '' | ||
| + | |||
| + | ==== Wait for button | ||
| Send: '' | Send: '' | ||
| Line 75: | Line 142: | ||
| Whenever the button is pressed, the device sends '' | Whenever the button is pressed, the device sends '' | ||
| + | |||
| + | ===== 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 {{: | ||