#!/usr/bin/env python3
# Laurent GUERBY 20210422
# Copyright licence: dedicated to the public domain
# Protocol for chargery BMS16T : https://raw.githubusercontent.com/Eumobong/Chargery/master/BMS24T-16T-8T%20Additional%20Protocol%20Info%20V1.25.pdf
# on debian 10 : apt-get install python3-serial

import serial
import sys
import datetime

m0=bytes([0x24,0x24])
dev_name = '/dev/ttyUSB0'; 

def get_value(b,high_first,scale):
    r=range(len(b))
    if not high_first: r=reversed(r)
    x=0
    for i in r:
        x*=256
        x+=b[i]
    return float(x)/scale

def parse_chargery(b):
    prefix=datetime.datetime.now().isoformat()
    print(prefix,"parse",b)
    cmd=b[2]
    data_len=b[3]
    if len(b)>data_len: b=b[:data_len]
    if b[-1]!=sum(b[:-1])%256:
        print(prefix,"bad_checksum################")
        return
    if cmd==0x56:
        c_v=[]
        for i in range(int((data_len-13)/2)):
            v=get_value(b[4+i*2:4+(i+1)*2],True,1000.0)
            c_v.append(v)
            print(prefix,"cell_v",i,v)
        print(prefix,"cell_v_total",sum(c_v))
        print(prefix,"cell_v_min",min(c_v))
        print(prefix,"cell_v_avg",sum(c_v)/len(c_v))
        print(prefix,"cell_v_max",max(c_v))
        print(prefix,"cell_v_delta",max(c_v)-min(c_v))
        print(prefix,"Wh",get_value(b[-9:-5],False,1000.0))
        print(prefix,"Ah",get_value(b[-5:-1],False,1000.0))
    elif cmd==0x57:
        print(prefix,"end_v",get_value(b[4:6],True,1000.0))
        print(prefix,"current_mode",b[6])
        print(prefix,"current",get_value(b[7:9],True,10.0))
        print(prefix,"temp1",get_value(b[9:11],True,10.0))
        print(prefix,"temp2",get_value(b[11:13],True,10.0))
        print(prefix,"soc",b[13])
    elif cmd==0x58:
        print(prefix,"current_mode_1",b[4])
        print(prefix,"current_1",get_value(b[5:7],False,10.0))
        for i in range(int((data_len-8)/2)):
            print(prefix,"cell_r",i,get_value(b[7+i*2:7+(i+1)*2],False,10.0))
    else:
        print(prefix,"bad_cmd################")
    sys.stdout.flush()
            
        
ser=serial.Serial(dev_name, 115200, bytesize=8, parity='N', stopbits=1)
x0=bytes()
while ser.is_open:
    x=ser.read()
    x0+=x
    a=x0.find(m0)
    if a>=0:
        if len(x0)-a>=4 and len(x0)-a>=x0[a+3]:
            b=a+x0[a+3]
            parse_chargery(x0[a:b])
            x0=x0[b:]
    if len(x0)>1024:
        #in case something is very wrong avoid filling memory
        x0=bytes()
