Communication protocol of the Proteus EcoMeter TEK603

The receiver device has a USB port, which has a CP2102 USB<->RS232 converter. With 115200 baud, 8N1 it is possible to communicate with the device.

Package format

All data to or from the device is transmitted as a package.

  • 2 byte header (‘SI’ = 0x53,0x49)
  • 2 byte length of the complete package (16 bit, big-endian)
  • 1 byte command (1: data send to the device, 2: data received from the device)
  • 1 byte flags:

    • bit 0: set the clock (hour/minutes/seconds) in the device on upload
    • bit 1: force reset the device (set before an update of the device)
    • bit 2: a non-empty payload is send to the device
    • bit 3: force recalculate the device (set on upload after changing the Sensor Offset, Outlet Height or the lookup table)
    • bit 4: live data received from the device
    • bit 5: n/a
    • bit 6: n/a
    • bit 7: n/a
  • 1 byte hour – used to transmit the current time to the device

  • 1 byte minutes
  • 1 byte seconds

  • 2 byte eeprom start (16 bit, big-endian) – unused in live data

  • 2 byte eeprom end (16 bit, big-endian)

  • n bytes payload

  • 2 byte CRC16 (16 bit, big-endian)

The app never always read more than about 800 bytes of data at once, e.g. reading the history is done in 4 reads:

  • 0x0000..0x0031 Header
  • 0x0032..0x0289 Offsets
  • 0x028a..0x03e5 This 365 entry history
  • 0x03e6..0x06d0 ..continuation of the history

Live data

With an open connection and without significant change of the fluid level, the current level is send every 30/60 minutes (water/oil) to the computer. There seems to be no way to request it. Because the wireless transmitter only sends the data in that frequency, it probably makes very little sense.

If the level changes (refilling the tank) it updates much more often.

Live data has the following payload:

  • 1 byte temperature in Fahrenheit + 40. The get the temperature in Celcius you need to calculate it like this => temperatureC = ((temperatureF - 40 - 32) / 1.8)
  • 2 byte sensor level in cm (16-bit, big-endian)
  • 2 byte usable capacity in l (16-bit, big-endian)
  • 2 byte full capacity of the tank in l (16-bit, big-endian)

EEPROM content

The EEPROM has three distinct areas:

  • Header and configuration
  • tank air space table (liters of tank volume based on the level above the fluid (0..300cm))
  • 365 days of average usage – a round-robin buffer

Header and configuration

If there is no description, the value is unknown.

  • 0x00: 0xcc (204) Magic byte, always checked to test the communication with the device.
  • 0x01: 0x18 0x3D 0xff 0xca
  • 0x05: 0x03 Type of the tank A..D (0..3) The tank offset table is calculated by the device based on the setting (A-C). If the table is modified via a computer, the type is set to D (3).
  • 0x06: 0x0096 Height of the tank in cm (here: 150) tankHeight
  • 0x08: 0x0048 Width of the tank? (Probably entered during configuration)
  • 0x0a: 0x0064 Height of the tank without the rounding? (Probably entered during configuration)
  • 0x0c: 0x2710 Volume of the tank (10000l)
  • 0x0e: 0x01
  • 0x0f: 0x00

  • 0x10: 0x00

  • 0x11: 0x00 0/1 Alarm Off/On
  • 0x12: 0x01
  • 0x13: 0x002d 45 Index to todays entry in the history historyIndex. Because the history starts yesterday, you need to start with the next index for yesterday.
  • 0x15: 0x02
  • 0x16: 0x00
  • 0x17: 0x43
  • 0x18: 0x00
  • 0x19: 0x34
  • 0x1a: 0x00
  • 0x1b: 0x3a
  • 0x1c: 0x05db (16-bit, big-endian, cost of fuel: 0.00 – 15.00 currency/l)
  • 0x1e: 0x00
  • 0x1f: 0x00

  • 0x20: 0x00

  • 0x21: 0x00
  • 0x22: 0x00
  • 0x23: 0x00
  • 0x24: 0x00
  • 0x25: 0x00
  • 0x26: 0x00
  • 0x27: 0x00 Sensor Offset sensorOffset in cm (0..70cm)
  • 0x28: 0x08 Outlet Height outletHeight in cm (0..70cm)
  • 0x29: 0x1b
  • 0x2a: 0x00
  • 0x2b: 0x03
  • 0x2c: 0x1c
  • 0x2d: 0x00
  • 0x2e: 0x94
  • 0x2f: 0x00
  • 0x30: 0x00 0xDD

Tank Air Space Table tankAirSpaceTable

The distance from the bottom of the transmitter to the top of the fluid. The table has one value per cm tankHeight – up to 300cm is supported. If the distance between the top and the transmitter is 0, the capacity is the maximum value. This table is calculated by the tank type A-C automatically, but can be customized to match any shape (tank type D). It is used to convert the level in cm into liter.

The outletHeight is an offset that is added to the level to return the volume of the tank. It can be used to define a reserve.


The history table is 365 entries large. Each entry is 3 bytes. It is a cyclic buffer with an index pointer in the header pointing at todays position. To go into the past increment the index and wrap around at 365 till you hit today again.

The 3 bytes have this format: 0xaa 0xbc 0xcc

  • ‘aa’ changes up/down a bit, but it is also not clear what it means.
  • ‘b’ also unknown. I’ve only seen values of 1 or 2 for it.
  • ‘ccc’ is the average oil usage in 0,1l on that day (divide by 10 to get liters). 0 if there was no oil used on a day.

The level of the tank based on todays usage is estimated in liters as follows:

  usage = ccc / 10 - sensorOffset
  lowValue = tankAirTable[(int)usage]
  highValue = tankAirTable[(int)usage+1]
  usage = highValue + (lowValue - highValue) * (1 - (usage - (int)usage))
  usage = usage - tankAirTable[tankHeight - outletHeight]