User Tools

Site Tools


projects:farmrobot:batteriemanagement

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
projects:farmrobot:batteriemanagement [2021/03/15 01:35]
jonas [Modbus Python implementation on Raspberry Pi 4B]
projects:farmrobot:batteriemanagement [2021/03/21 19:02] (current)
jonas [Modbus Python and Watson-IoT MQTT publish on Raspberry Pi 4B]
Line 1: Line 1:
 ====== BMS: ====== ====== BMS: ======
 +
 +The battery management system is a important part of the robot.
 +It is responsible to monitor all values regarding electrical power and available energy for the robot.
 +It also has protection features that ensure, that the battery used lasts as long as possible to make the system safe, reliable and sustainable.
 +Based on the measured values, the system can decide when to return to the home base for charging.
 +Also the important metrics can be sent to a control station to let the owner know about the status of the system.
 +This part of the project tries to implement a monitoring system for the important metrics of the power delivery system.
  
 ===== TinyBMS s516 =====  ===== TinyBMS s516 ===== 
Line 83: Line 90:
 Disable Load/Charger Switch Diagnostics: 0 Disable Load/Charger Switch Diagnostics: 0
 </file> </file>
-===== Modbus Python implementation on Raspberry Pi 4B ===== 
-Using FTDI USB adapter which can be interfaced on /dev/ttyUSB0 
  
 +===== Monitoring BMS =====
 +The UART interface is the only port that can make the internally gathered data of the BMS available to other devices.\\
 +But the monitoring device that is communicating with the BMS board is variable.\\
 +For easy and standard python implementation and further appliances, a Raspberry Pi 4B can be used, as it has GPIO and USB interfaces and can run the standard python scripts.\\
 +It could run as the standard network interface, making internet connections available to other systems or can host multiple appliances like camera live streaming, mqtt client and commander for the robot system which can send commands from remote to a device of the robot.\\
 +For more embedded and efficient devices, an Arduino or ESP32 can be used.
 +The ESP32 can run micropython code, which is a reduced feature set of python, which can also be extended with libaries, speciffic to micropython.\\
 +This is an additional hurdle, as the libaries are not identical to the standard python featureset.\\
 +
 +For simplicity the appliances are developed on one plattform, the Raspberry Pi 4B.
 +
 +
 +===== Modbus Python and Watson-IoT MQTT publish on Raspberry Pi 4B =====
 +Using FTDI USB adapter which can be interfaced on /dev/ttyUSB0\\
 +Adding the current user to the "dialout" user group to access serial interfaces without root permissions:
     sudo adduser pi dialout     sudo adduser pi dialout
-    + 
 +Installing prerequisites:\\
     sudo apt install python3     sudo apt install python3
     wget https://bootstrap.pypa.io/get-pip.py     wget https://bootstrap.pypa.io/get-pip.py
     sudo python3 get-pip.py     sudo python3 get-pip.py
-    sudo pip install pymodbus +    sudo pip install pymodbus\\ 
-\\+ 
 +ModbusSerialClient is the Modbus client that is used to interface the registers on the BMS:
     from pymodbus.client.sync import ModbusSerialClient     from pymodbus.client.sync import ModbusSerialClient
  
-Using IBM Watson IoT platform+=== IBM Watson IoT platform ===
  
 Creating a new device and gathering credentials: https://q74k3e.internetofthings.ibmcloud.com/dashboard/devices/browse/ Creating a new device and gathering credentials: https://q74k3e.internetofthings.ibmcloud.com/dashboard/devices/browse/
Line 109: Line 131:
     pip install wiotp-sdk     pip install wiotp-sdk
  
-Modbus communication implementation based on: https://github.com/clarkni5/tinybms/blob/master/python/tinybms.py+Modbus communication implementation based on: https://github.com/clarkni5/tinybms/blob/master/python/tinybms.py\\
  
 +=== Code: ===
 <file py pc_bms_modbus_mqtt.py> <file py pc_bms_modbus_mqtt.py>
 import numpy as np import numpy as np
 import wiotp.sdk.device import wiotp.sdk.device
 +from time import sleep
 +from datetime import datetime
 import pymodbus.client.sync import pymodbus.client.sync
  
-# Modbus client 
 dev_port = '/dev/ttyUSB0' dev_port = '/dev/ttyUSB0'
-modbus_client = pymodbus.client.sync.ModbusSerialClient(method='rtu', port=dev_port, timeout=2, baudrate=115200+modbus_client = pymodbus.client.sync.ModbusSerialClient(method='rtu', port=dev_port, baudrate=115200, parity='N', 
-modbus_client.connect()+                                                        bytesize=8, stopbits=1, timeout=2, strict=False
 +my_config = wiotp.sdk.device.parseConfigFile("device.yaml"
 +mqtt_client = wiotp.sdk.device.DeviceClient(config=my_config, logHandlers=None)
  
-myConfig = wiotp.sdk.device.parseConfigFile("device.yaml") + 
-mqtt_client = wiotp.sdk.device.DeviceClient(config=myConfig, logHandlers=None+def connect_modbus(): 
-mqtt_client.connect()+    if not modbus_client.is_socket_open(): 
 +        modbus_client.connect() 
 +    print("connect_modbus: ok") 
 + 
 + 
 +def connect_mqtt(): 
 +    mqtt_client.connect(
 +    print("connect_mqtt: ok")
  
  
Line 131: Line 164:
  
 def read_registers(address, count): def read_registers(address, count):
-    return modbus_client.read_holding_registers(address, count, unit=0xAA).registers+    while True: 
 +        result = modbus_client.read_holding_registers(address, count, unit=0xAA) 
 +        if not result.isError(): 
 +            register = result.registers 
 +            return register
  
  
-modbus_client.read_holding_registers(0, 1, unit=0xAA)+def ask_registers(): 
 +    read_registers(0, 1) 
 +    lifetime_counter = (convert(read_registers(32, 2), np.uint32))/60  # min 
 +    time_left = (convert(read_registers(34, 2), np.uint32))/60  # min 
 +    pack_voltage = convert(read_registers(36, 2), np.float32)  # V 
 +    pack_current = convert(read_registers(38, 2), np.float32)  # C 
 +    min_cell = (read_registers(40, 1)[0])/1000  # V 
 +    max_cell = (read_registers(41, 1)[0])/1000  # V 
 +    cell_diff = (read_registers(1041)[0])/10000  # V 
 +    soc (convert(read_registers(46, 2), np.uint32))/1000000  # % 
 +    bms_temperature = (read_registers(48, 1)[0])/10  # °C 
 +    bms_online = hex(read_registers(50, 1)[0]) 
 +    max_discharge_current = (read_registers(102, 1)[0])/1000  # A 
 +    max_charge_current = (read_registers(103, 1)[0])/1000  # A 
 +    charge_count = read_registers(111, 1)[0]
  
-lifetime_counter = convert(read_registers(322), np.uint32) +    f_data = { 
-time_left = convert(read_registers(342), np.uint32) +        'lifetime_counter': "%.2f"lifetime_counter, 
-pack_voltage = convert(read_registers(362), np.float32) +        'time_left': "%.2f" % time_left, 
-pack_current = convert(read_registers(382), np.float32) +        'pack_voltage': "%.2f"pack_voltage, 
-min_cell = read_registers(401)[0] +        'pack_current': "%.2f"pack_current, 
-max_cell = read_registers(411)[0] +        'min_cell': "%.2f"min_cell, 
-cell_diff = read_registers(1041)[0] +        'max_cell': "%.2f"max_cell, 
-soc = convert(read_registers(462), np.uint32) +        'cell_diff': "%.2f"cell_diff, 
-bms_temperature = read_registers(481)[0] +        'soc': "%.2f"soc, 
-bms_online = read_registers(50, 1)[0] +        'bms_temperature': "%.1f"bms_temperature, 
-max_discharge_current = read_registers(1021)[0] +        'bms_online': str(bms_online), 
-max_charge_current = read_registers(1031)[0] +        'max_discharge_current': "%.2f"max_discharge_current, 
-charge_count = read_registers(111, 1)[0]+        'max_charge_current': "%.2f"max_charge_current, 
 +        'charge_count': str(charge_count) 
 +        } 
 +    print(f_data) 
 +    print("stored register values: ok") 
 +    return f_data
  
-myData = { + 
-    'lifetime_counter'lifetime_counter, +def publish(p_data)
-    'time_left': time_left, +    mqtt_client.publishEvent(eventId="status"msgFormat="json"data=p_dataqos=0, onPublish=print("publish: ok")) 
-    'pack_voltage': pack_voltage, + 
-    'pack_current': pack_current+ 
-    'min_cell'min_cell, +def output_time()
-    'max_cell': max_cell, +    now = datetime.now() 
-    'cell_diff'cell_diff, +    current_time = now.strftime("%H:%M:%S") 
-    'soc': soc, +    print("-------------------------------------------------------------------") 
-    'bms_temperature': bms_temperature, +    print(current_time) 
-    'bms_online'bms_online, +    print("-------------------------------------------------------------------"
-    'max_discharge_current': max_discharge_current, + 
-    'max_charge_current': max_charge_current, + 
-    'charge_count': charge_count +while True
-    } +    output_time() 
-mqtt_client.publishEvent(eventId="status", msgFormat="json", data=myData, qos=0, onPublish=None+    connect_modbus() 
-mqtt_client.disconnect()+    connect_mqtt() 
 +    my_data = ask_registers() 
 +    publish(my_data
 +    sleep(10)
 </file> </file>
 +{{:projects:farmrobot:run_terminal.png|}}
 +
 +==== Receiving MQTT messages on Watson IoT platform ====
 +Received messages:\\
 +{{:projects:farmrobot:watson_iot_received_messages.png?600|}}\\
 +
 +Raw status data available:\\
 +{{:projects:farmrobot:watson_iot_raw_data.png?600|}}
 +
 +===== Web Interface to view and graph the data =====
 +An easy way to set up a web interface is to host a node-red instance on a server, for example on a stationary Raspberry Pi 4, which can be accesses via network or can be made accessible with port forwarding from the internet.\\
 +
 +Setting up a Raspberry Pi 4B with docker and docker run portainer.
 +Create a new Node-Red Stack with a compose file, which creates a node-red web instance on the device on port 1880.
 +The ip adress is needed which can be requested with:
 +    ifconfig
 +
 +<file yaml compose.yaml>
 +version: "2"
 +
 +services:
 +  node-red:
 +    image: nodered/node-red:latest
 +    environment:
 +      - TZ=Europe/Berlin
 +    ports:
 +      - "1880:1880"
 +    networks:
 +      - node-red-net
 +    volumes:
 +      - ~/data/node-red:/data
 +
 +networks:
 +  node-red-net:
 +</file>
 +
 +The node-red webapp can then be accessed via [[http://ip-adress:1880]]\\
 +The Node-RED Dashboard module is needed to display the data with node-red\\
 +To install, click the Menu Button and choose "Manage palette". Click the "Install" tab and search for "node-red-dashboard".\\
 +Then click on install and install in the pop-up window. Then return to the main view.\\
 +{{:projects:farmrobot:node-red-palette.png?600|}}
 +
 +
 ===== Micropython implementation on espressiv ESP32 DevKitc v4 ===== ===== Micropython implementation on espressiv ESP32 DevKitc v4 =====
 Required software: Linux OS and python3 (sudo apt-get install python3) Required software: Linux OS and python3 (sudo apt-get install python3)
projects/farmrobot/batteriemanagement.1615772139.txt.gz · Last modified: 2021/03/15 01:35 by jonas