ZKETECH EBC-A20 Battery Tester
Table of Contents
1. Introduction
The ZKETECH EBC-A20 is a battery tester with integrated charging and discharging circuits. Its basic capabilities are:
- voltage range when discharging 0 - 30 V
- voltage range while charging 0 - 18 V
- charging current range 0.1 - 5 A
- discharge current range: 0.1 - 20 A
The device is pretty popular and there are multiple places where a manual can be downloaded with more detailed specifications. The focus of this article is the control and monitoring interface provided by the battery tester
2. Control interface
The device is equipped with a Mini USB socket on the back where a provided cable is connected. The cable contains a USB <-> serial converter as the Mini USB connector actually carries serial port signals. I have not checked what happens when an ordinary USB cable is plugged there and connected to a USB hub. I hope the designers took that possibility into account.
The control interface can be used by a vendor-provided piece of software - "EB Software(English Version)_v1.8.8.rar" (mirror). This piece of software can also be found relatively easily.
The physical layer of the control interface is a 9600 bps, 8 bit, odd parity.
2.1. Control protocol
2.1.1. Packet structure
The control protocol exchanges packets (also called "frames" in this document) between the controller PC and the battery tester. The packets are binary encoded, a typical packet is shown below in hexadecimal:
fa 0c 00 00 02 1e 00 00 00 00 00 0a 01 3c 00 0a 09 24 f8
The basic structure of the encoded packet is shown below:
Offset | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | Notes | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Value | fa | 0c | 00 | 00 | 02 | 1e | 00 | 00 | 00 | 00 | 00 | 0a | 01 | 3c | 00 | 0a | 09 | 24 | f8 | ||
Description | SOF | CRC | EOF |
Each packet begins with a 0xfa byte and ands with 0xf8. These are marked with SOF and EOF. The payload are the bytes between offset 1 and 16 (inclusively). A packet also contains a "CRC" which is a simple checksum not an actual Cyclic redundancy check. It can be calculated by performing a XOR operation on the payload bytes:
>>> hex(0x0c ^ 0x00 ^ 0x00 ^ 0x02 ^ 0x1e ^ 0x00 ^ 0x00 ^ 0x00 ^ 0x00 ^ 0x00 ^ 0x0a ^ 0x01 ^ 0x3c ^ 0x00 ^ 0x0a ^ 0x09) '0x24' >>>
A number of packet types sent by the battery tester to the PC or vice-versa have been identified, an example of which is the packet sent when the controlling software requests cell charging:
Type | Offset | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Notes |
---|---|---|---|---|---|---|---|---|---|---|---|---|
Start charging | ||||||||||||
fa | 21 | 00 | 32 | 01 | 3c | 00 | 0a | 1c | f8 | |||
SOF | PT | I1 | I2 | V1 | V2 | CUT1 | CUT2 | CRC | EOF |
The first payload byte is the packet type identifier - 0x21 represents the Start charging packet. Next there are 3 16-bit fields encoding:
- the charging current (in units of 10 mA)
- the charging voltage (in units of 10 mV)
- the cutoff current (in units of 10 mA)
Each of those values is encoded using the following scheme:
>>> I1 = 0x00 >>> I2 = 0x32 >>> 240 * I1 + I2 50
In effect this is a "base240" encoding and it's purpose is likely to prevent values larger than 240 (0xf0) to be present in the packet bytes. This makes sense when you consider the fact that bytes 0xfa and 0xf8 are used as "control codes" to mark start and end of packets. In summary the packet above encodes:
>>> I1 = 0x00 >>> I2 = 0x32 >>> 240 * I1 + I2 50 # charging current 50 * 10 mA = 500 mA >>> V1 = 0x01 >>> V2 = 0x3c >>> 240 * V1 + V2 300 # charging voltage 300 * 10 mV = 3V >>> CUT1 = 0x00 >>> CUT2 = 0x0a >>> 240 * CUT1 + CUT2 10 # cutoff current 10 * 10 mA = 100 mA >>>
What follows is a table with descriptions of all of the packets that have been observed so far together with their types, encoded fields and other extra information.
2.1.2. Field reference
Fields like current or voltage have common encoding for all packet types:
Field name | Description | Unit | Notes |
---|---|---|---|
PT | Packet type | ||
DT | Device type | 0x09 is EBC-A20 | |
(FW1, FW2) | Firmware version | Displayed in the ZKETECH aplication, for example 302 is displayed as 'V3.02' | |
(T1, T2) | Time | min | |
(I1, I2) | Current | 10 mA | |
(V1, V2 | Voltage | 1 mV | |
(C1, C2) | Charge count | 1 mAh | |
(E1, E2) | Unknown | Might be energy in Wh | |
(CC1, CC2) | Charging current | 10 mA | Charging |
(CV1, CV2) | Charging voltage | 10 mV | |
(CUT1, CUT2) | Cutoff current | 10 mA | |
(CC1, CC2) | Charging current | 10 mA | Discharging |
(CUTV1, CUTV2) | Cutoff voltage | 10 mV |
2.1.3. Packet reference
This table does not exhaust all of the packet that I have seen while sniffing the traffic between the original software and the battery tester. A bit more is documented in the Python code.
Kind | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Description | ||||||||||||||||||||
Status | fa | 02 | 00 | 00 | 0a | 13 | 00 | 14 | 00 | 00 | 00 | 32 | 01 | 0a | 00 | 0a | 09 | 35 | f8 | |
Tester idle, | SOF | PT | I1 | I2 | V1 | V2 | C1 | C2 | E1 | E2 | CC1 | CC2 | CV1 | CV2 | CUT1 | CUT2 | DT | CRC | EOF | |
sent after connecting | ||||||||||||||||||||
Status | fa | 66 | 00 | 00 | 08 | 88 | 00 | 14 | 00 | 00 | 01 | 3e | 0c | 8f | 09 | 05 | 09 | 4b | f8 | |
SOF | PT | I1 | I2 | V1 | V2 | C1 | C2 | E1 | E2 | FW1 | FW2 | unk | unk | unk | unk | DT | CRC | EOF | ||
Idle FW report, | ||||||||||||||||||||
sent after connecting | ||||||||||||||||||||
Maybe HW version too? | ||||||||||||||||||||
unk means unknown | ||||||||||||||||||||
Status | fa | 0c | 00 | 32 | 07 | de | 00 | 00 | 00 | 00 | 00 | 32 | 01 | b4 | 00 | 0a | 09 | 63 | f8 | |
SOF | PT | I1 | I2 | V1 | V2 | C1 | C2 | E1 | E2 | CC1 | CC2 | CV1 | CV2 | CUT1 | CUT2 | DT | CRC | EOF | ||
CC-CV charging in progress | ||||||||||||||||||||
Status | fa | 70 | 00 | 00 | 04 | 53 | 00 | 00 | 00 | 00 | 01 | 3e | 0c | 8f | 09 | 05 | 09 | 63 | f8 | |
SOF | PT | I1 | I2 | V1 | V2 | C1 | C2 | E1 | E2 | FW1 | FW2 | unk | unk | unk | unk | DT | CRC | EOF | ||
CC-CV charging FW report, | ||||||||||||||||||||
sent for a few | ||||||||||||||||||||
seconds after | ||||||||||||||||||||
charging begins | ||||||||||||||||||||
Status | fa | 16 | 00 | 0a | 0a | 64 | 00 | 14 | 00 | 00 | 00 | 32 | 01 | 0a | 00 | 0a | 09 | 5c | f8 | |
SOF | PT | I1 | I2 | V1 | V2 | C1 | C2 | E1 | E2 | CC1 | CC2 | CV1 | CV2 | CUT1 | CUT2 | DT | CRC | EOF | ||
CC-CV charging end | ||||||||||||||||||||
sent when current cutoff | ||||||||||||||||||||
threshold is reached | ||||||||||||||||||||
Status | fa | 00 | 00 | 00 | 10 | 49 | 00 | 00 | 00 | 00 | 00 | 32 | 01 | 3c | 00 | 78 | 09 | 27 | f8 | |
SOF | PT | I1 | I2 | V1 | V2 | C1 | C2 | E1 | E2 | CC1 | CC2 | CV1 | CV2 | CUT1 | CUT2 | DT | CRC | EOF | ||
CC discharge idle | ||||||||||||||||||||
Status | fa | 0a | 00 | 32 | 0f | 41 | 00 | 02 | 00 | 00 | 00 | 32 | 01 | 3c | 00 | 3c | 09 | 4e | f8 | |
SOF | PT | I1 | I2 | V1 | V2 | C1 | C2 | E1 | E2 | CC1 | CC2 | CV1 | CV2 | CUT1 | CUT2 | DT | CRC | EOF | ||
CC discharge in progress | ||||||||||||||||||||
Status | fa | 14 | 00 | 32 | 0c | 77 | 01 | 59 | 00 | 00 | 00 | 32 | 01 | 3c | 00 | 78 | 09 | 7b | f8 | |
SOF | PT | I1 | I2 | V1 | V2 | C1 | C2 | E1 | E2 | CC1 | CC2 | CV1 | CV2 | CUT1 | CUT2 | DT | CRC | EOF | ||
CC discharge end | ||||||||||||||||||||
Command | fa | 05 | 00 | 00 | 00 | 00 | 00 | 00 | 05 | f8 | ||||||||||
SOF | PT | CRC | EOF | |||||||||||||||||
Connect | ||||||||||||||||||||
After this '-PC-' appears | ||||||||||||||||||||
on LCD | ||||||||||||||||||||
Command | fa | 06 | 00 | 00 | 00 | 00 | 00 | 00 | 06 | f8 | ||||||||||
SOF | PT | CRC | EOF | |||||||||||||||||
Disconnect | ||||||||||||||||||||
Command | fa | 02 | 00 | 00 | 00 | 00 | 00 | 00 | 02 | f8 | ||||||||||
SOF | PT | CRC | EOF | |||||||||||||||||
Stop | ||||||||||||||||||||
Used for both charging and | ||||||||||||||||||||
discharging | ||||||||||||||||||||
Command | fa | 21 | 00 | 32 | 01 | 3c | 00 | 78 | 02 | f8 | ||||||||||
SOF | PT | CC1 | CC2 | CV1 | CV2 | CUT1 | CUT2 | CRC | EOF | |||||||||||
Start CC-CV charging | ||||||||||||||||||||
Command | fa | 0a | 00 | 03 | 00 | 00 | 00 | 00 | 02 | f8 | ||||||||||
SOF | PT | T1 | T2 | CRC | EOF | |||||||||||||||
CC-CV time? | ||||||||||||||||||||
sent every minute while | ||||||||||||||||||||
charging | ||||||||||||||||||||
Command | fa | 01 | 00 | 03 | 00 | 00 | 00 | 00 | 02 | f8 | ||||||||||
SOF | PT | CC1 | CC2 | CUTV1 | CUTV2 | T1 | T2 | CRC | EOF | |||||||||||
Start CC discharge | ||||||||||||||||||||
(CUTV1, CUTV2) is the cutoff | ||||||||||||||||||||
voltage | ||||||||||||||||||||
(T1, T2) is the time limit |