Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Link device instance to unique_identifier instead of port #718

Closed
2 tasks
mr-manuel opened this issue Jun 16, 2023 · 31 comments
Closed
2 tasks

Link device instance to unique_identifier instead of port #718

mr-manuel opened this issue Jun 16, 2023 · 31 comments
Labels
enhancement New feature or request

Comments

@mr-manuel
Copy link
Collaborator

mr-manuel commented Jun 16, 2023

Don't link the device instance to the port name, but to the unique_identifier.

This prevents that the batteries change sometimes device instance and MQTT battery ID causing missing history in the VRM portal and unfunctioning nodes in Node-RED. The root cause is that sometimes on reboot or unplug/plug of the USB to serial adapter the ttyUSB* number changes.

  • It has to be verified that the unique_identifier is not already used (some BMS does not have one, then it's concatenated from hardware version and battery capacity:

    def unique_identifier(self) -> str:
    """
    Used to identify a BMS when multiple BMS are connected
    If not provided by the BMS/driver then the hardware version and capacity is used,
    since it can be changed by small amounts to make a battery unique.
    On +/- 5 Ah you can identify 11 batteries
    """
    string = (
    "".join(filter(str.isalnum, str(self.hardware_version))) + "_"
    if self.hardware_version is not None and self.hardware_version != ""
    else ""
    )
    string += str(self.capacity) + "Ah"
    return string

  • If the unique_identifier exists, prevent the driver from starting and mention in the logfile that the capacity of multiple connected batteries has do differ.

Anything else?

@mr-manuel mr-manuel added the enhancement New feature or request label Jun 16, 2023
@mr-manuel
Copy link
Collaborator Author

@ogurevich maybe you have time to look into this?

@ogurevich
Copy link
Contributor

I'll try to understand that tomorrow and have a couple of thoughts

Do all BMS have a serialNumber ?

Screenshot 2023-06-16 at 21 46 17 Screenshot 2023-06-16 at 21 48 59

@mr-manuel
Copy link
Collaborator Author

The value serial is fed by the code linked above, so yes all BMS display a serial, which is in the most cases not the real serial but a concatenated value that tries to be unique.

@ogurevich
Copy link
Contributor

I'm still thinking about what can be a reasonable solution here.

At the moment three tasks can be solved, that I believe the desired behavior can be realized. We can consider together how this can be implemented. We need at least:

  • a function that saves the last seen (used) batteries and the corresponding assigned IDs somewhere in VRM.
  • a function that builds the UniqueID for recognized devices when the driver is started and tries to find it by comparing stored information, then we also have the ID, which can then be assigned again.
  • the third function would be responsible for notifying the user in case of a conflict. Writing to the log as already suggested in the topic and perhaps also as an info alarm. In this way, the user is informed in any case.

Would an SQLite database, for example, be conceivable for this purpose?

@mr-manuel
Copy link
Collaborator Author

  1. The data is already stored in the dbus under com.victronenergy.settings
  2. There is already a function for the unique identifier, but not for the rest

@ogurevich
Copy link
Contributor

yes, thank you, i see. There are: Enabled, Name (unique Name), Service
The idea is what if we could store a little more information about a battery, no matter where, also the info: "last seen" and "last seen SOC", so we can match the battery to the name when the driver starts make smarter. We can compare different factors and thus assign the right battery. In case the formed UniqueName result in the same values. This is still a rough idea, it should mature.

@ogurevich
Copy link
Contributor

ok, I entered the value: PrivDatagur01 in the User Private Data field. This is how the values in the screenshots result
IMG_1851
Screenshot 2023-06-18 at 22 58 01
Screenshot 2023-06-18 at 22 57 16

@mr-manuel
Copy link
Collaborator Author

Yes, thats correct. Since on serial connection I found no way to read the serial number.

@ogurevich
Copy link
Contributor

In my setup, the BMSs are also connected via USB 👍

@arrow1800
Copy link

arrow1800 commented Nov 13, 2023

any updates on this? would love to have a fix for this. very hard to read bms data with changing device instances

@mr-manuel
Copy link
Collaborator Author

It‘s a work in progress since it takes a lot of time. You a very welcome to open a PR for this.

@arrow1800
Copy link

arrow1800 commented Nov 22, 2023

It‘s a work in progress since it takes a lot of time. You a very welcome to open a PR for this.

fair enough : - ).

I'm still thinking about what can be a reasonable solution here.

At the moment three tasks can be solved, that I believe the desired behavior can be realized. We can consider together how this can be implemented. We need at least:

  • a function that saves the last seen (used) batteries and the corresponding assigned IDs somewhere in VRM.
  • a function that builds the UniqueID for recognized devices when the driver is started and tries to find it by comparing stored information, then we also have the ID, which can then be assigned again.
  • the third function would be responsible for notifying the user in case of a conflict. Writing to the log as already suggested in the topic and perhaps also as an info alarm. In this way, the user is informed in any case.

Would an SQLite database, for example, be conceivable for this purpose?

can't we construct or calculate a device id based on any of the already existing properties instead of trying to save data and rematching that to the existing BMS on reboot? maybe even try to generate a hash of 2 numeric characters in total based on a few properties

@arrow1800
Copy link

something like this:


import hashlib

def generate_limited_hash(input_data, min_value, max_value):
    sha256_hash = hashlib.sha256()

    # Convert the input to bytes
    input_bytes = str(input_data).encode('utf-8')

    # Update the hash 
    sha256_hash.update(input_bytes)

    # Get the resulting hash value as a hexadecimal string
    hash_result = sha256_hash.hexdigest()

    # Convert the hexadecimal hash to a decimal number
    decimal_hash = int(hash_result, 16)

    # Limit the hash to the specified range
    limited_hash = min_value + (decimal_hash % (max_value - min_value + 1))

    return limited_hash


input_data = "a combination of the unique i3dent4ifier and some other fields"

min_value = 10
max_value = 50

hash_value = generate_limited_hash(input_data, min_value, max_value)
print(f"Input: {input_data}, Limited Hash: {hash_value}")

@mr-manuel
Copy link
Collaborator Author

I see no difference in this. Since you cannot save the hash to the BMS it makes no sense for me. You have to recalculate the hash based on the unique data we try to generate.

@arrow1800
Copy link

ok, so the problem is there is no unique data? what about the first post, a combination of hardware id and capacity

@mr-manuel
Copy link
Collaborator Author

Yes the problem is, that some BMS do not have unique data. This is already solved since a few months:

def unique_identifier(self) -> str:
"""
Used to identify a BMS when multiple BMS are connected
If not provided by the BMS/driver then the hardware version and capacity is used,
since it can be changed by small amounts to make a battery unique.
On +/- 5 Ah you can identify 11 batteries
"""
string = (
"".join(filter(str.isalnum, str(self.hardware_version))) + "_"
if self.hardware_version is not None and self.hardware_version != ""
else ""
)
string += str(self.capacity) + "Ah"
return string

@arrow1800
Copy link

i am a bit confused now; so we already have a unique identifier method that is able to unique identify a BMS. that is good, what are we missing then ?

could this strategy work?

  1. generate a unique identifier for each bms using the already implemented method above unique_identifier()
  2. translate the return value of this method to a unique and fixed VRM ID using the hash method above.
  3. set hash as VRM ID, this way we dont have to store the VRM ID as it can just be generated everytime the drivers boots up

@mr-manuel
Copy link
Collaborator Author

  • a function that builds the UniqueID for recognized devices when the driver is started and tries to find it by comparing stored information, then we also have the ID, which can then be assigned again.

  • the third function would be responsible for notifying the user in case of a conflict. Writing to the log as already suggested in the topic and perhaps also as an info alarm. In this way, the user is informed in any case.

This is missing.

  1. When a BMS is attached the driver has to check, if the BMS has already assigned a VRM ID, if not then it has to assign the next free VRM ID and store this connection somewhere with an expiration date.
  2. If the BMS is not connected for x days, then the BMS with the VRM ID should be removed from the list.
  3. If a BMS with the same ID is already connected the driver has to throw an error.

@arrow1800
Copy link

yea but that is my whole point. if we calculate the vrm id we dont have to store anything.

@mr-manuel
Copy link
Collaborator Author

mr-manuel commented Nov 24, 2023

VRM ID is the wrong name, is the device instance and has to be a value between 1 and 255. Calculating a hash for that low possibilities causes more problems than storing the data somewhere.

Did you try your algorithm with some random data and see how often values are the same?

@mr-manuel mr-manuel changed the title Link VRM ID to unique_identifier instead of port Link device instance to unique_identifier instead of port Nov 24, 2023
@arrow1800
Copy link

i can write a simulation. can you give me some possible values for the field 'hardware version' ? i only have one type of BMS

@mr-manuel
Copy link
Collaborator Author

You have to check some issues here on GitHub and the logs.

@ramack
Copy link
Contributor

ramack commented Nov 24, 2023

I can give you some from my 4 Heltec BMSes
Screenshot_20231124-161107
Screenshot_20231124-161044
Screenshot_20231124-161020
Screenshot_20231124-160612

@ramack
Copy link
Contributor

ramack commented Nov 24, 2023

In case there is a hash-generated instance number which is already used, we could still fall back to
a) increasing numbers
b) using the USB port device name
c) integrating the capacity

@mr-manuel
Copy link
Collaborator Author

We want to to this to prevent using the USB port device name

@arrow1800
Copy link

arrow1800 commented Nov 24, 2023

I can give you some from my 4 Heltec BMSes ![Screenshot_20231124-161107](https://private-user-

thnx!

and ah! the Heltec BMS.. that one actually has a serial number that can be read :)

serial1 = mbdev.read_registers(2, number_of_registers=4)

self.unique_identifier = "-".join("{:04x}".format(x) for x in serial1)

@ramack
Copy link
Contributor

ramack commented Nov 24, 2023

We want to to this to prevent using the USB port device name

Clearly, but my impression is, that it will work for most systems perfectly with the hasing and only where this clashes we'd need a backup.

@arrow1800
Copy link

arrow1800 commented Nov 24, 2023

looked into it a bit more.

one more question @mr-manuel; the method you mentioned in the first post "unique_identifier()" doesnt seem to be used anywhere in code, is that right?

instead every specific BMS class inherits from the battery class and has its own implementation in which the unique_identifier property/variable is set. however not every BMS class seems to implement this fully (only 4 classes are setting the property for now). #link

so whats missing here is that in the base class (battery) the unique_identifier property should be set to a default value with the unique_identifier method, which can then be overwritten later on by a specific BMS class if implemented.

so this line: link

should change to:

self.unique_identifier = unique_identifier()

so at least all BMS classes have some form of a unique_identifier set. and in the case that a serial number or a custom field can actually be read from the BMS the default value can be overwritten with a more accurate value from the bms itself.

at the end; the unique_identifier property is written to the dbus path /serial.

so now we have a baseline for which i can build a simulation. if i'm still correct so far, so please confirm :)

@mr-manuel
Copy link
Collaborator Author

@arrow1800 can you join our Discord server?

@arrow1800
Copy link

@arrow1800 can you join our Discord server?

accepted

mr-manuel added a commit to mr-manuel/venus-os_dbus-serialbattery that referenced this issue Nov 26, 2023
@mr-manuel
Copy link
Collaborator Author

This was solved and will be merged with the dev branch in the next days.

This was referenced Nov 26, 2023
Louisvdw pushed a commit that referenced this issue Feb 28, 2024
* fix Sinowealth not loading
#702

* fix unique identifier function

* enable BMS over config, if disabled by default
Now you can also add more then one BMS for BMS_TYPE

* show battery port in log

* ANT BMS fixes
Fixed that other devices are recognized as ANT BMS

* Sinowealth BMS fixes
Fixed that other devices are recognized as Sinowealth BMS

* improved publish_battery error handling
switched from error count to seconds

* Improve Battery Voltage Handling in Linear Absorption Mode

* Refactor change time() to int(time()) for consistency in max_voltage_start_time and tDiff calculation
* Refactor battery voltage calculations for efficiency and clarity
* Remove penalty_buffer
* Reset max_voltage_start_time wenn we going to bulk(dynamic) mode

* updated changelog

* fix reply processing

* Reduce the big inrush current, if the CVL jumps
from Bulk/Absorbtion to Float
fix #659

* Check returned data lenght for Seplos BMS

Be stricter about the return data we accept, might fix the problem of grid meters accidently being recognized as a Seplos

* Validate current, voltage, capacity and SoC for all BMS
This prevents that a device, which is no BMS, is detected as BMS

* removed double check

* bump version

* fix validation if None

* updated changelog

* proposal to #659 formatted :)

* bugfix proposal to #659

* refactor setting float charge_mode

* fix type error, removed bluetooth cronjob

* updated changelog

* fix rs485 write communication errors by inserting sleeps, add debug print for charge mode and fix crash on write soc failures

* fix write problem on set_soc. also changed the switch charge/discharge function, just in case

* debug msg

* Bluetooth optimizations

* Fixes by @peterohman
#505 (comment)

* fix #712

* fix meaningless time to go values

* fix meaningless time to go values

* Duration of transition to float depends on number of cells

* Float transition - Voltage drop per second

* Update hlpdatabms4s.py

* Validate setting of FLOAT_CELL_VOLTAGE and avoid misconfiguration

* consider utils.LINEAR_RECALCULATION_EVERY to refresh CVL

* cleanup

* consider utils.LINEAR_RECALCULATION_EVERY to refresh CVL

* small refactor, introduced set_cvl_linear function to set CVL only once every LINEAR_RECALCULATION_EVERY seconds

* fix typo

* updated changelog

* remove debug msg

* remove debug msg

* undo debug change

* Daly BMS make auto reset soc configurable

* added debug and error information for CVL

* fix proposal for #733 (#735)

* Added: Tollerance to enter float voltage once the timer is triggered

* Add bulk voltage
Load to bulk voltage every x days to reset the SoC to 100% for some BMS

* JKBMS disable high voltage warning on bulk
reenable after bulk was completed

* fixed error

* disable high voltage warning for all BMS
when charging to bulk voltage

* fix error and change default value
measurementToleranceVariation from 0.025 to 0.5 else in OffGrid mode max voltage is always kept

* Added temperature names to dbus/mqtt

* Use current avg of last 300 cycles for TTG & TTS

* Calculate only positive Time-to-SoC points

* added current average of last 5 minutes

* make CCL and DCL more clear

* fix small error

* bugfix: LLTJBD BMS SOC different in Xiaoxiang app and dbus-serialbattery

* black formatting

* JDB BMS - Control FETs for charge, discharge and disable / enable balancer (#761)

* feature: Allow to control charge / discharge FET
* feature: Allow to enable / disable balancer

* bugfix: Cycle Capacity is in 10 mAh

Fixes SoC with factor 100 * 100% percentage

* JBD BMS show balancer state in GUI page IO (#763)

* Bump version

* Fix typos

* Smaller fixes
- fixes #792 (comment)

* Removed comments from utils.py
This should make more clear that there are no values to change

* Updated changelog

* possible fix for LLT/JBS connection problems
#769
#777

* bugfix: LLT/JBD BMS general packet data size check

* improved reinstall and disable script

* LLT/JBD BMS - Improved error handling and automatical driver restart
in case of error. Should fix:
- #730
- #769
- #777

* Fixed Building wheel for dbus-fast won't finish on weak systems
Fixes #785

* Support for Daly CAN Bus (#169)

* support for Daly CAN Bus
* fix constructor args
* revert port, needs fix
* add can filters
* comment logger

Some changes are still needed to work with the latest version. They will follow in a next PR.

---------

Co-authored-by: Samuel Brucksch <[email protected]>
Co-authored-by: Manuel <[email protected]>

* JKBMS BLE - Introduction of automatic SOC reset (HW Version 11) (#736)

* Introduction of automatic SOC reset for JK BMS (HW Version 11)
* Fixed value mapping
* Rework of the code to make it simpler to use without additional configuration.
Moved execution of SOC reset. It's now executed while changing from "Float" to "Float Transition".
* Implementation of suggested changes
Persist initial BMS OVP and OVPR settings
Make use of max_cell_voltage to calculate trigger value for OVP alert

* Added: Daly CAN and JKBMS CAN

* added CAN bms to installation script
optimized CAN drivers

* smaller fixes

* Trigger JK BLE SOC reset when using Step Mode

* Moved trigger_soc_reset()

* fixes LLT/JBD SOC > 100%
#769

* changed VOLTAGE_DROP behaviour

* Fix JKBMS not starting if BMS manuf. date is empty

* corrected bulk, absorption and soc reset terms

* fix typo

* add JKBMS_BLE debugging data

* fix small error

* Some changes for lost bluetooth connection / hci_uart stack restart

* added logging to config

* add sleep before starting driver
prevents lot of timeouts after reinstalling the driver, since the restart is now much faster than before

* changed post install info

* fix error

* Daly BMS fixed embedded null byte
#837

* added info for SoC reset to default config file

* fix for #716
#716

* fix for #716 and JKBMS model recognition
#716

* optimized logging

* fix JKBMS recognition

* added debugging

* fixes #716
#716

* Bind device instance to unique_identifier
#718

* added data types to battery class
disabled unused variables

* save current charge state
#840

* correct file permissions

* updated changelog

* added periodic saveChargeDetails

* fix some small errors

* fix issue with ruuvi tags
When there are hundreds of unused ruuvi tags in the settings list that where added because thei where nearby the driver does not start correctly. These stale entries are disabled on the driver startup.
The issue was already filed to Victron developers

* CVL with i-controller instead of penaltysum

* cvl_controller: switch to choose PenaltySum or ICOntroller + documentation

* docu enhancement

* Add setting and install logic for usb bluetooth module

* round temperatures

* changed battery disconnect behaviour

* Fixes #891
#891

* updated changelog

* Add bluetooth device note to config.default.ini

* Fix typo in bluetooth note in config.default.ini

* fixed error in new cvl_controller

* fixed float division by zero and code optimization

* Restart MAX_VOLTAGE_TIME_SEC if cell diff > CELL_VOLTAGE_DIFF_KEEP_MAX_VOLTAGE_TIME_RESTART

* Calculation of the SOC based on coloumb-counting (#868)

* Calculation of the SOC in the driver based on coloumb-counting

* soc_calc: add current correction before integration

* soc_calc: correction map for current

* Soc_calc: CorrectionMap, switch to turn on/off correction, selectable initial value

* soc_calc: Bugfix

* soc_calc: Bugfix

* store soc in dbus for restart

* store soc in dbus for restart (formatted)

* store soc in dbus for restart (bugfix)

* save soc_calc only after change > 1.0

* store soc in dbus for restart (bugfix)

* logger does not work this way. do not know why

* writing and reading to dbus works

* Removed options: SOC_CALC_CURRENT_CORRECTION, SOC_CALC_RESET_VALUE_ON_RESTART, SOC_CALC_INIT_VALUE
sort soc_calc alphabetically

* fixed comments

* Updated changelog, small fixes

* Changed: PUBLISH_CONFIG_VALUES from 0/1 to True/False

* Changed: Code optimizations
- Changed some variables to be more clear
- Added comments for easier code understanding

* Calculated SOC: Added two decimals, added BMS SOC for MQTT & Node-RED

* Updated changelog, small fixes

* Changed: PUBLISH_CONFIG_VALUES from 0/1 to True/False

* Changed: Code optimizations
- Changed some variables to be more clear
- Added comments for easier code understanding

* Calculated SOC: Added two decimals, added BMS SOC for MQTT & Node-RED

* Fix #898
#898

* Changed: Fix issue loading settings from dbus

* Added nightly install option
makes it easier for users to pretest fixes

* Changed: more detailed error output when an exception happens

* Possible fix for #912
#912

* Fixes #919
#919

* Changed: Exit script with error, if port excluded
else the serialstarter stops at the dbus-serialbattery

* Fixed some smaller errors

* Updated pre-release workflow

* Fix JK BMS connection restart when bluetooth fails.

This fix installs a new thread to monitor the state of the original
scraping thread.
If scraping thread dies, it verifies that it did not because the
scraping was intentionally stopped by calling stop_scrapping.
When restarting the scrapper, it first calls the bluetooth
reset lambda function that was passed in the class contructor, such that
bluetooth is ready to make a proper connection.

* Fixes #916
#916

* Added Venus OS version to logfile

* Fix #840
#840

* Small code formatting fixes

* Optimized reinstall script. Restart GUI only on changes.

* Display debugging data in GUI when DEBUG enabled

* Install script now shows repositories and version numbers

* Update daly_can.py

Fixing #950 for DalyBMS

* Update jkbms_can.py

Fixing #950 for Jk BMS

* Fix black lint check

* Fixes #970
#970

* Fixed some errors in restoring values from dbus settings

* Moved sleep on start for all BMS

* Update config description

* Reworked a part of the default config

* fix typo in stopping services when reinstalling

* Fix Time-to-SoC and Time-to-Go calculation

* Add changelog info

* Round sum and diff voltage

* Temperature limitation variables where changed

* SoC limitation variables where changed

* Added error messages

* Remove unneeded code

* Reset SoC to 0% if empty

* Add GUIv2 for dbus-serialbattery

* Check free space before installing

* Added new GUIv2 version

* Removed Python 2 compatibility

* Changelog update

* Code cleanup
- Removed: get_temperatures()
- Removed: update_last_seen()

* Bluetooth code optimizations

* Fixed some JKBMS BLE not starting
#819

* Check if packages are already installed before install

* Fixed some SOC calculation errors

* Fixed None SOC on driver start

* Do not show and allow button change when callback is missing for:
- ForceChargingOff
- ForceDischargingOff
- TurnBalancingOff

* Check if a device instance is already used by creating a PID file

* Log and execute SOC reset to 100% or 0% only once

* Update GitHub workflow and issue templates

* Fixed LLT/JBD BMS with only on temperature sensor #791
#971

* Fix warning on reinstall

* Fix missing IO control for JBDBMS #992
#992

* Prepare for removing dev branch

---------

Co-authored-by: ogurevich <[email protected]>
Co-authored-by: Bernd Stahlbock <[email protected]>
Co-authored-by: wollew <[email protected]>
Co-authored-by: Oleg Gurevich <[email protected]>
Co-authored-by: peterohman <[email protected]>
Co-authored-by: Strawder, Paul <[email protected]>
Co-authored-by: Paul Strawder <[email protected]>
Co-authored-by: Samuel Brucksch <[email protected]>
Co-authored-by: Samuel Brucksch <[email protected]>
Co-authored-by: ArendsM <[email protected]>
Co-authored-by: Meik Arends <[email protected]>
Co-authored-by: Marvo2011 <[email protected]>
Co-authored-by: cflenker <[email protected]>
Co-authored-by: cflenker <[email protected]>
Co-authored-by: Cupertino Miranda <[email protected]>
Co-authored-by: Martin Polehla <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

4 participants