agrobot_base/Python/dalybms/soc_monitor.py

142 lines
4.2 KiB
Python
Raw Normal View History

import ctypes
import time
import os
# Carrega a DLL
dll = ctypes.cdll.LoadLibrary(os.path.abspath("ControlCANFD.dll"))
DEVICE_TYPE = 41
DEVICE_INDEX = 0
CHANNEL_INDEX = 0 # troca pra 1 se estiver usando CAN2
class VCI_INIT_CONFIG(ctypes.Structure):
_fields_ = [
("AccCode", ctypes.c_uint),
("AccMask", ctypes.c_uint),
("Reserved", ctypes.c_uint),
("Filter", ctypes.c_ubyte),
("Timing0", ctypes.c_ubyte),
("Timing1", ctypes.c_ubyte),
("Mode", ctypes.c_ubyte),
]
class VCI_CAN_OBJ(ctypes.Structure):
_fields_ = [
("ID", ctypes.c_uint),
("TimeStamp", ctypes.c_uint),
("TimeFlag", ctypes.c_ubyte),
("SendType", ctypes.c_ubyte),
("RemoteFlag", ctypes.c_ubyte),
("ExternFlag", ctypes.c_ubyte),
("DataLen", ctypes.c_ubyte),
("Data", ctypes.c_ubyte * 8),
("Reserved", ctypes.c_ubyte * 3),
]
def u16_be(hi, lo):
return ((hi & 0xFF) << 8) | (lo & 0xFF)
def parse_0x90(data_bytes):
total_v_raw = u16_be(data_bytes[0], data_bytes[1])
meas_v_raw = u16_be(data_bytes[2], data_bytes[3])
current_raw = u16_be(data_bytes[4], data_bytes[5])
soc_raw = u16_be(data_bytes[6], data_bytes[7])
total_v = total_v_raw / 10.0
meas_v = meas_v_raw / 10.0
current = (current_raw - 30000) / 10.0
soc = soc_raw / 10.0
print(f"🔋 Tensão total : {total_v:.1f} V (raw={total_v_raw})")
print(f" Tensão medida: {meas_v:.1f} V (raw={meas_v_raw})")
print(f" Corrente : {current:.1f} A (raw={current_raw})")
print(f" SOC : {soc:.1f} % (raw={soc_raw})")
# -------- init device --------
ret = dll.VCI_OpenDevice(DEVICE_TYPE, DEVICE_INDEX, 0)
if ret != 1:
print("❌ Falha ao abrir dispositivo.")
raise SystemExit
print("✅ Dispositivo aberto.")
# baud 250 kbps
baud_250k = ctypes.c_uint(0x1C0008)
ret = dll.VCI_SetReference(DEVICE_TYPE, DEVICE_INDEX, CHANNEL_INDEX, 0, ctypes.byref(baud_250k))
if ret != 1:
print("❌ Falha ao configurar baudrate.")
raise SystemExit
print("🔧 Baudrate configurado para 250 kbps.")
config = VCI_INIT_CONFIG()
config.AccCode = 0
config.AccMask = 0xFFFFFFFF
config.Reserved = 0
config.Filter = 0
config.Timing0 = 0
config.Timing1 = 0
config.Mode = 0
ret = dll.VCI_InitCAN(DEVICE_TYPE, DEVICE_INDEX, CHANNEL_INDEX, ctypes.byref(config))
if ret != 1:
print("❌ Falha ao inicializar CAN.")
raise SystemExit
ret = dll.VCI_StartCAN(DEVICE_TYPE, DEVICE_INDEX, CHANNEL_INDEX)
if ret != 1:
print("❌ Falha ao iniciar CAN.")
raise SystemExit
dll.VCI_ClearBuffer(DEVICE_TYPE, DEVICE_INDEX, CHANNEL_INDEX)
print("🚀 Canal iniciado.\n")
recv_buffer = (VCI_CAN_OBJ * 100)()
try:
while True:
# monta request 0x90
send_obj = VCI_CAN_OBJ()
send_obj.ID = 0x18900140 # PC -> BMS, DataID 0x90
send_obj.SendType = 0
send_obj.RemoteFlag = 0
send_obj.ExternFlag = 1 # extended
send_obj.DataLen = 8
for i in range(8):
send_obj.Data[i] = 0x00
dll.VCI_Transmit(DEVICE_TYPE, DEVICE_INDEX, CHANNEL_INDEX,
ctypes.byref(send_obj), 1)
# espera resposta rápida
start = time.time()
resp_ok = False
while time.time() - start < 0.5:
count = dll.VCI_Receive(
DEVICE_TYPE,
DEVICE_INDEX,
CHANNEL_INDEX,
ctypes.byref(recv_buffer),
100,
50
)
if count > 0:
for i in range(count):
obj = recv_buffer[i]
if obj.ID == 0x18904001 and obj.DataLen == 8:
data = [obj.Data[j] for j in range(8)]
print(f"\n📥 RX ID=0x{obj.ID:08X} Data=" +
" ".join(f"{b:02X}" for b in data))
parse_0x90(data)
resp_ok = True
break
if resp_ok:
break
time.sleep(0.05)
if not resp_ok:
print("\n⚠️ Sem resposta do BMS para 0x90 nesse ciclo.")
time.sleep(1.0)
except KeyboardInterrupt:
print("\n🧹 Encerrando monitor...")