├── secrets.yaml
├── images
├── Atom_CAN_Kit.png
├── Atom_S3_Lite.png
├── HA_Dashboard.png
├── CAN_Protocol_Table.png
├── ESPHome_Web_Server.png
├── JK-BMS_24S_GPS_port.png
├── Atom_Lite_ESP32-PICO_pin.png
├── lovelace-cards-contribution.png
├── Atomic_CANBus_Base_CA-IS3050G.png
├── JK-BMS-CAN_LFP_Cut-Off_Values.png
├── PCB_ESP32_JK-BMS-CAN_Prototype.jpg
├── JK-BMS-CAN_Charging_Logic_Diagram.png
├── JK-BMS-CAN_Li-ion_Cut-Off_Values.png
├── Auto_Charge_Voltage_Logic_Flowchart.png
├── PCB_ESP32_JK-BMS-CAN_powered_by_JK-BMS.png
├── JK-BMS-CAN_Cut-Off_Charging_Logic_for_LFP_Diagram.png
└── JK-BMS-CAN_Cut-Off_Charging_Logic_for_LFP_Equation.png
├── documents
├── BMS LV CAN Protocol
│ ├── SMA CAN protocol.pdf
│ ├── SMA CAN Protocol Mapping.pdf
│ ├── SEPLOS BMS CAN Protocoll V1.0.pdf
│ ├── Sol-Ark CAN Bus Protocol V1.3.pdf
│ ├── BMS_Protocol _CAN_Hybrid_EN_V1.0.pdf
│ ├── Master-LV-communication-guide-EN-8000.pdf
│ ├── CAN-Bus-protocol-DEYE-low-voltage-V1.2-20180408.pdf
│ ├── CAN-Bus-protocol-PYLON-low-voltage-V1.2-20180408.pdf
│ ├── Growatt-BMS-CAN-Bus-protocol-low-voltage-V1.04-1.pdf
│ └── Growatt-BMS-CAN-Bus-protocol-low-voltage-V1.04-1-translate.pdf
└── README
│ ├── Cut-Off_Charging_Logic.md
│ └── Auto_Charge_Voltage_Logic.md
├── README.md
├── config
├── config_atom-lite-esp32-pico.yaml
├── config_generic-esp32-devkit-v1.yaml
└── config_atom-s3-lite-esp32-s3.yaml
├── LICENSE.txt
└── old_version
├── esp32_wire_jk-bms-can.yaml
└── esp32_ble_jk-bms-can.yaml
/secrets.yaml:
--------------------------------------------------------------------------------
1 | wifi_ssid: YourSSID
2 | wifi_password: YourPassword
3 | domain : .local
4 |
--------------------------------------------------------------------------------
/images/Atom_CAN_Kit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sleeper85/esphome-jk-bms-can/HEAD/images/Atom_CAN_Kit.png
--------------------------------------------------------------------------------
/images/Atom_S3_Lite.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sleeper85/esphome-jk-bms-can/HEAD/images/Atom_S3_Lite.png
--------------------------------------------------------------------------------
/images/HA_Dashboard.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sleeper85/esphome-jk-bms-can/HEAD/images/HA_Dashboard.png
--------------------------------------------------------------------------------
/images/CAN_Protocol_Table.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sleeper85/esphome-jk-bms-can/HEAD/images/CAN_Protocol_Table.png
--------------------------------------------------------------------------------
/images/ESPHome_Web_Server.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sleeper85/esphome-jk-bms-can/HEAD/images/ESPHome_Web_Server.png
--------------------------------------------------------------------------------
/images/JK-BMS_24S_GPS_port.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sleeper85/esphome-jk-bms-can/HEAD/images/JK-BMS_24S_GPS_port.png
--------------------------------------------------------------------------------
/images/Atom_Lite_ESP32-PICO_pin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sleeper85/esphome-jk-bms-can/HEAD/images/Atom_Lite_ESP32-PICO_pin.png
--------------------------------------------------------------------------------
/images/lovelace-cards-contribution.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sleeper85/esphome-jk-bms-can/HEAD/images/lovelace-cards-contribution.png
--------------------------------------------------------------------------------
/images/Atomic_CANBus_Base_CA-IS3050G.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sleeper85/esphome-jk-bms-can/HEAD/images/Atomic_CANBus_Base_CA-IS3050G.png
--------------------------------------------------------------------------------
/images/JK-BMS-CAN_LFP_Cut-Off_Values.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sleeper85/esphome-jk-bms-can/HEAD/images/JK-BMS-CAN_LFP_Cut-Off_Values.png
--------------------------------------------------------------------------------
/images/PCB_ESP32_JK-BMS-CAN_Prototype.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sleeper85/esphome-jk-bms-can/HEAD/images/PCB_ESP32_JK-BMS-CAN_Prototype.jpg
--------------------------------------------------------------------------------
/images/JK-BMS-CAN_Charging_Logic_Diagram.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sleeper85/esphome-jk-bms-can/HEAD/images/JK-BMS-CAN_Charging_Logic_Diagram.png
--------------------------------------------------------------------------------
/images/JK-BMS-CAN_Li-ion_Cut-Off_Values.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sleeper85/esphome-jk-bms-can/HEAD/images/JK-BMS-CAN_Li-ion_Cut-Off_Values.png
--------------------------------------------------------------------------------
/images/Auto_Charge_Voltage_Logic_Flowchart.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sleeper85/esphome-jk-bms-can/HEAD/images/Auto_Charge_Voltage_Logic_Flowchart.png
--------------------------------------------------------------------------------
/images/PCB_ESP32_JK-BMS-CAN_powered_by_JK-BMS.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sleeper85/esphome-jk-bms-can/HEAD/images/PCB_ESP32_JK-BMS-CAN_powered_by_JK-BMS.png
--------------------------------------------------------------------------------
/documents/BMS LV CAN Protocol/SMA CAN protocol.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sleeper85/esphome-jk-bms-can/HEAD/documents/BMS LV CAN Protocol/SMA CAN protocol.pdf
--------------------------------------------------------------------------------
/documents/BMS LV CAN Protocol/SMA CAN Protocol Mapping.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sleeper85/esphome-jk-bms-can/HEAD/documents/BMS LV CAN Protocol/SMA CAN Protocol Mapping.pdf
--------------------------------------------------------------------------------
/images/JK-BMS-CAN_Cut-Off_Charging_Logic_for_LFP_Diagram.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sleeper85/esphome-jk-bms-can/HEAD/images/JK-BMS-CAN_Cut-Off_Charging_Logic_for_LFP_Diagram.png
--------------------------------------------------------------------------------
/images/JK-BMS-CAN_Cut-Off_Charging_Logic_for_LFP_Equation.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sleeper85/esphome-jk-bms-can/HEAD/images/JK-BMS-CAN_Cut-Off_Charging_Logic_for_LFP_Equation.png
--------------------------------------------------------------------------------
/documents/BMS LV CAN Protocol/SEPLOS BMS CAN Protocoll V1.0.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sleeper85/esphome-jk-bms-can/HEAD/documents/BMS LV CAN Protocol/SEPLOS BMS CAN Protocoll V1.0.pdf
--------------------------------------------------------------------------------
/documents/BMS LV CAN Protocol/Sol-Ark CAN Bus Protocol V1.3.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sleeper85/esphome-jk-bms-can/HEAD/documents/BMS LV CAN Protocol/Sol-Ark CAN Bus Protocol V1.3.pdf
--------------------------------------------------------------------------------
/documents/BMS LV CAN Protocol/BMS_Protocol _CAN_Hybrid_EN_V1.0.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sleeper85/esphome-jk-bms-can/HEAD/documents/BMS LV CAN Protocol/BMS_Protocol _CAN_Hybrid_EN_V1.0.pdf
--------------------------------------------------------------------------------
/documents/BMS LV CAN Protocol/Master-LV-communication-guide-EN-8000.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sleeper85/esphome-jk-bms-can/HEAD/documents/BMS LV CAN Protocol/Master-LV-communication-guide-EN-8000.pdf
--------------------------------------------------------------------------------
/documents/BMS LV CAN Protocol/CAN-Bus-protocol-DEYE-low-voltage-V1.2-20180408.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sleeper85/esphome-jk-bms-can/HEAD/documents/BMS LV CAN Protocol/CAN-Bus-protocol-DEYE-low-voltage-V1.2-20180408.pdf
--------------------------------------------------------------------------------
/documents/BMS LV CAN Protocol/CAN-Bus-protocol-PYLON-low-voltage-V1.2-20180408.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sleeper85/esphome-jk-bms-can/HEAD/documents/BMS LV CAN Protocol/CAN-Bus-protocol-PYLON-low-voltage-V1.2-20180408.pdf
--------------------------------------------------------------------------------
/documents/BMS LV CAN Protocol/Growatt-BMS-CAN-Bus-protocol-low-voltage-V1.04-1.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sleeper85/esphome-jk-bms-can/HEAD/documents/BMS LV CAN Protocol/Growatt-BMS-CAN-Bus-protocol-low-voltage-V1.04-1.pdf
--------------------------------------------------------------------------------
/documents/BMS LV CAN Protocol/Growatt-BMS-CAN-Bus-protocol-low-voltage-V1.04-1-translate.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sleeper85/esphome-jk-bms-can/HEAD/documents/BMS LV CAN Protocol/Growatt-BMS-CAN-Bus-protocol-low-voltage-V1.04-1-translate.pdf
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # JK-BMS-CAN
2 |
3 | [](https://www.gnu.org/licenses/gpl-3.0)
4 | [](https://github.com/Sleeper85/esphome-jk-bms-can/releases/latest)
5 | 
6 | 
7 | 
8 | [](https://www.buymeacoffee.com/sleeper85)
9 |
10 | > [!IMPORTANT]
11 | > This version is no longer supported and is replaced by the `multi-BMS` version `YamBMS`.
`YamBMS` works with one or multiple BMS.
12 |
13 | ### >>> [YamBMS ( Yet another multi-BMS Merging Solution )](https://github.com/Sleeper85/esphome-yambms) <<<
14 |
--------------------------------------------------------------------------------
/config/config_atom-lite-esp32-pico.yaml:
--------------------------------------------------------------------------------
1 |
2 | # *** These are the specific settings you need to change in the YAML if you use the Atom Lite ESP32-PICO board ***
3 |
4 | # 1) Choose the configuration file you want to use:
5 | # - esp32_ble_jk-bms-can.yaml : If you don't want to connect a wire between the BMS and the ESP32 choose the Bluetooth version.
6 | # - esp32_wire_jk-bms-can.yaml : If you prefer to use a wired connection between the BMS and the ESP32 choose the Wire version.
7 |
8 | # 2) Edit the YAML file you have chosen with the information below :
9 |
10 | # +--------------------------------------+
11 | # | ESP32 CAN/serial port pins |
12 | # +--------------------------------------+
13 | # GPIO pins your CAN bus transceiver ATOMIC CANBus Base (CA-IS3050G)
14 | can_tx_pin: GPIO22
15 | can_rx_pin: GPIO19
16 | # GPIO pins your JK-BMS UART-TTL is connected to the grove port of Atom Lite
17 | tx_pin: GPIO32
18 | rx_pin: GPIO26
19 |
20 | # +--------------------------------------+
21 | # | ESP32 settings |
22 | # +--------------------------------------+
23 | # Atom Lite is not stable with the Bluetooth version
24 | esp32:
25 | board: m5stack-atom
26 | framework:
27 | type: arduino
28 |
--------------------------------------------------------------------------------
/config/config_generic-esp32-devkit-v1.yaml:
--------------------------------------------------------------------------------
1 |
2 | # *** These are the specific settings you need to change in the YAML if you use the ESP32 DevKit v1 30 pin board ***
3 |
4 | # 1) Choose the configuration file you want to use:
5 | # - esp32_ble_jk-bms-can.yaml : If you don't want to connect a wire between the BMS and the ESP32 choose the Bluetooth version.
6 | # - esp32_wire_jk-bms-can.yaml : If you prefer to use a wired connection between the BMS and the ESP32 choose the Wire version.
7 |
8 | # 2) Edit the YAML file you have chosen with the information below :
9 |
10 | # +--------------------------------------+
11 | # | ESP32 CAN/serial port pins |
12 | # +--------------------------------------+
13 | # GPIO pins your CAN bus transceiver (TJA1050, TJA1051T or SN65HVD230) is connected to the ESP32 TX->TX and RX->RX !
14 | can_tx_pin: GPIO23
15 | can_rx_pin: GPIO22
16 | # GPIO pins your JK-BMS UART-TTL is connected to the ESP32 TX->RX and RX->TX !
17 | tx_pin: GPIO17
18 | rx_pin: GPIO16
19 |
20 | # +--------------------------------------+
21 | # | ESP32 settings |
22 | # +--------------------------------------+
23 | # For a stable Bluetooth connection keep the "esp-idf" framework
24 | esp32:
25 | board: esp32doit-devkit-v1
26 | framework:
27 | type: esp-idf
28 |
--------------------------------------------------------------------------------
/config/config_atom-s3-lite-esp32-s3.yaml:
--------------------------------------------------------------------------------
1 |
2 | # *** These are the specific settings you need to change in the YAML if you use the Atom S3 Lite ESP32-S3 board ***
3 |
4 | # 1) Choose the configuration file you want to use:
5 | # - esp32_ble_jk-bms-can.yaml : If you don't want to connect a wire between the BMS and the ESP32 choose the Bluetooth version.
6 | # - esp32_wire_jk-bms-can.yaml : If you prefer to use a wired connection between the BMS and the ESP32 choose the Wire version.
7 |
8 | # 2) Edit the YAML file you have chosen with the information below :
9 |
10 | # +--------------------------------------+
11 | # | ESP32 CAN/serial port pins |
12 | # +--------------------------------------+
13 | # GPIO pins your CAN bus transceiver ATOMIC CANBus Base (CA-IS3050G)
14 | can_tx_pin: GPIO5
15 | can_rx_pin: GPIO6
16 | # GPIO pins your JK-BMS UART-TTL is connected to the grove port of Atom Lite
17 | tx_pin: GPIO1
18 | rx_pin: GPIO2
19 |
20 | # +--------------------------------------+
21 | # | ESP32 settings |
22 | # +--------------------------------------+
23 | esp32:
24 | board: esp32-s3-devkitc-1
25 | variant: esp32s3
26 | framework:
27 | type: esp-idf
28 |
29 | logger:
30 | hardware_uart: USB_SERIAL_JTAG
31 |
32 | # You must also change the GPIO of the LED, replace 2 with 35
33 | output:
34 | - platform: gpio
35 | pin: 35
36 | id: led
37 | inverted: true
38 |
--------------------------------------------------------------------------------
/documents/README/Cut-Off_Charging_Logic.md:
--------------------------------------------------------------------------------
1 | # JK-BMS-CAN
2 |
3 | [](https://www.gnu.org/licenses/gpl-3.0)
4 | [](https://github.com/Sleeper85/esphome-jk-bms-can/releases/latest)
5 | 
6 | 
7 | 
8 |
9 | ## Cut-Off Charging Logic Equation
10 |
11 | Source: [Charging Marine Lithium Battery Banks](https://nordkyndesign.com/charging-marine-lithium-battery-banks)
12 |
13 | Special thanks to [@shvmm](https://github.com/shvmm) for creating the equations.
14 |
15 | Note: The equations below are valid for other chemistries like Li-ion and LTO but with other CVmin and CVmax values.
16 |
17 | 
18 |
19 | ## Cut-Off Charging Logic Diagram
20 |
21 | Note: The diagram below is valid for other chemistries like Li-ion and LTO but with other CVmin and CVmax values.
22 |
23 | 
24 |
25 | ## LFP Cut-Off Values
26 |
27 | 
28 |
29 | ## Li-ion Cut-Off Values
30 |
31 | 
32 |
33 | ## LTO Cut-Off Values
34 |
35 | Will be added in the future.
36 |
--------------------------------------------------------------------------------
/documents/README/Auto_Charge_Voltage_Logic.md:
--------------------------------------------------------------------------------
1 | # JK-BMS-CAN
2 |
3 | [](https://www.gnu.org/licenses/gpl-3.0)
4 | [](https://github.com/Sleeper85/esphome-jk-bms-can/releases/latest)
5 | 
6 | 
7 | 
8 |
9 | ## Automatic Charge Voltage
10 |
11 | Special thanks to [@MrPabloUK](https://github.com/MrPabloUK) for creating the logic.
12 |
13 | When enabled, the Automatic Charge Voltage feature will dynamically adjust the request CVL (Charge Voltage Limit) as sent by the ESP32 device.
14 | This feature is designed to automatically reduce the liklihood of a cell reaching overvoltage thresholds, as well as reducing charge current enough for balancing to be more effective.
15 |
16 | There are several inputs used in this logic:
17 | - Cell bulk voltage - automatically calculated as bulk voltage / cell count.
18 | - Charge V factor - multiplier that alters the aggression of the logic. The higher the value, the more severe the cell penalty.
19 | - Cell voltage - as reported by the BMS
20 |
21 | This logic will first check if enabled, then if any cells are at or over cell bulk voltage.
22 | If either of these are not true, then standard full bulk voltage will be sent to the inverter as the requested CVL.
23 |
24 | If not, then each cell that is at or over cell bulk voltage will have an automatically calculated penalty voltage applied.
25 | This penalty will increase in severity as the cell voltage increases above bulk voltage.
26 | Penalties are then applied against the current battery voltage, resulting in a target voltage equal or lower than the current battery voltage.
27 | This target voltage is then sent to the inverter as the requested CVL.
28 |
29 | A reference spreadsheet has been created that shows how the CVL changes based on cell voltages:
30 | https://docs.google.com/spreadsheets/d/1UwZ94Qca-DBP5gppzKmAjbMJYZGjR4lMwZtwwQR9wWY/edit?usp=sharing
31 |
32 | 
33 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 | Preamble
9 |
10 | The GNU General Public License is a free, copyleft license for
11 | software and other kinds of works.
12 |
13 | The licenses for most software and other practical works are designed
14 | to take away your freedom to share and change the works. By contrast,
15 | the GNU General Public License is intended to guarantee your freedom to
16 | share and change all versions of a program--to make sure it remains free
17 | software for all its users. We, the Free Software Foundation, use the
18 | GNU General Public License for most of our software; it applies also to
19 | any other work released this way by its authors. You can apply it to
20 | your programs, too.
21 |
22 | When we speak of free software, we are referring to freedom, not
23 | price. Our General Public Licenses are designed to make sure that you
24 | have the freedom to distribute copies of free software (and charge for
25 | them if you wish), that you receive source code or can get it if you
26 | want it, that you can change the software or use pieces of it in new
27 | free programs, and that you know you can do these things.
28 |
29 | To protect your rights, we need to prevent others from denying you
30 | these rights or asking you to surrender the rights. Therefore, you have
31 | certain responsibilities if you distribute copies of the software, or if
32 | you modify it: responsibilities to respect the freedom of others.
33 |
34 | For example, if you distribute copies of such a program, whether
35 | gratis or for a fee, you must pass on to the recipients the same
36 | freedoms that you received. You must make sure that they, too, receive
37 | or can get the source code. And you must show them these terms so they
38 | know their rights.
39 |
40 | Developers that use the GNU GPL protect your rights with two steps:
41 | (1) assert copyright on the software, and (2) offer you this License
42 | giving you legal permission to copy, distribute and/or modify it.
43 |
44 | For the developers' and authors' protection, the GPL clearly explains
45 | that there is no warranty for this free software. For both users' and
46 | authors' sake, the GPL requires that modified versions be marked as
47 | changed, so that their problems will not be attributed erroneously to
48 | authors of previous versions.
49 |
50 | Some devices are designed to deny users access to install or run
51 | modified versions of the software inside them, although the manufacturer
52 | can do so. This is fundamentally incompatible with the aim of
53 | protecting users' freedom to change the software. The systematic
54 | pattern of such abuse occurs in the area of products for individuals to
55 | use, which is precisely where it is most unacceptable. Therefore, we
56 | have designed this version of the GPL to prohibit the practice for those
57 | products. If such problems arise substantially in other domains, we
58 | stand ready to extend this provision to those domains in future versions
59 | of the GPL, as needed to protect the freedom of users.
60 |
61 | Finally, every program is threatened constantly by software patents.
62 | States should not allow patents to restrict development and use of
63 | software on general-purpose computers, but in those that do, we wish to
64 | avoid the special danger that patents applied to a free program could
65 | make it effectively proprietary. To prevent this, the GPL assures that
66 | patents cannot be used to render the program non-free.
67 |
68 | The precise terms and conditions for copying, distribution and
69 | modification follow.
70 |
71 | TERMS AND CONDITIONS
72 |
73 | 0. Definitions.
74 |
75 | "This License" refers to version 3 of the GNU General Public License.
76 |
77 | "Copyright" also means copyright-like laws that apply to other kinds of
78 | works, such as semiconductor masks.
79 |
80 | "The Program" refers to any copyrightable work licensed under this
81 | License. Each licensee is addressed as "you". "Licensees" and
82 | "recipients" may be individuals or organizations.
83 |
84 | To "modify" a work means to copy from or adapt all or part of the work
85 | in a fashion requiring copyright permission, other than the making of an
86 | exact copy. The resulting work is called a "modified version" of the
87 | earlier work or a work "based on" the earlier work.
88 |
89 | A "covered work" means either the unmodified Program or a work based
90 | on the Program.
91 |
92 | To "propagate" a work means to do anything with it that, without
93 | permission, would make you directly or secondarily liable for
94 | infringement under applicable copyright law, except executing it on a
95 | computer or modifying a private copy. Propagation includes copying,
96 | distribution (with or without modification), making available to the
97 | public, and in some countries other activities as well.
98 |
99 | To "convey" a work means any kind of propagation that enables other
100 | parties to make or receive copies. Mere interaction with a user through
101 | a computer network, with no transfer of a copy, is not conveying.
102 |
103 | An interactive user interface displays "Appropriate Legal Notices"
104 | to the extent that it includes a convenient and prominently visible
105 | feature that (1) displays an appropriate copyright notice, and (2)
106 | tells the user that there is no warranty for the work (except to the
107 | extent that warranties are provided), that licensees may convey the
108 | work under this License, and how to view a copy of this License. If
109 | the interface presents a list of user commands or options, such as a
110 | menu, a prominent item in the list meets this criterion.
111 |
112 | 1. Source Code.
113 |
114 | The "source code" for a work means the preferred form of the work
115 | for making modifications to it. "Object code" means any non-source
116 | form of a work.
117 |
118 | A "Standard Interface" means an interface that either is an official
119 | standard defined by a recognized standards body, or, in the case of
120 | interfaces specified for a particular programming language, one that
121 | is widely used among developers working in that language.
122 |
123 | The "System Libraries" of an executable work include anything, other
124 | than the work as a whole, that (a) is included in the normal form of
125 | packaging a Major Component, but which is not part of that Major
126 | Component, and (b) serves only to enable use of the work with that
127 | Major Component, or to implement a Standard Interface for which an
128 | implementation is available to the public in source code form. A
129 | "Major Component", in this context, means a major essential component
130 | (kernel, window system, and so on) of the specific operating system
131 | (if any) on which the executable work runs, or a compiler used to
132 | produce the work, or an object code interpreter used to run it.
133 |
134 | The "Corresponding Source" for a work in object code form means all
135 | the source code needed to generate, install, and (for an executable
136 | work) run the object code and to modify the work, including scripts to
137 | control those activities. However, it does not include the work's
138 | System Libraries, or general-purpose tools or generally available free
139 | programs which are used unmodified in performing those activities but
140 | which are not part of the work. For example, Corresponding Source
141 | includes interface definition files associated with source files for
142 | the work, and the source code for shared libraries and dynamically
143 | linked subprograms that the work is specifically designed to require,
144 | such as by intimate data communication or control flow between those
145 | subprograms and other parts of the work.
146 |
147 | The Corresponding Source need not include anything that users
148 | can regenerate automatically from other parts of the Corresponding
149 | Source.
150 |
151 | The Corresponding Source for a work in source code form is that
152 | same work.
153 |
154 | 2. Basic Permissions.
155 |
156 | All rights granted under this License are granted for the term of
157 | copyright on the Program, and are irrevocable provided the stated
158 | conditions are met. This License explicitly affirms your unlimited
159 | permission to run the unmodified Program. The output from running a
160 | covered work is covered by this License only if the output, given its
161 | content, constitutes a covered work. This License acknowledges your
162 | rights of fair use or other equivalent, as provided by copyright law.
163 |
164 | You may make, run and propagate covered works that you do not
165 | convey, without conditions so long as your license otherwise remains
166 | in force. You may convey covered works to others for the sole purpose
167 | of having them make modifications exclusively for you, or provide you
168 | with facilities for running those works, provided that you comply with
169 | the terms of this License in conveying all material for which you do
170 | not control copyright. Those thus making or running the covered works
171 | for you must do so exclusively on your behalf, under your direction
172 | and control, on terms that prohibit them from making any copies of
173 | your copyrighted material outside their relationship with you.
174 |
175 | Conveying under any other circumstances is permitted solely under
176 | the conditions stated below. Sublicensing is not allowed; section 10
177 | makes it unnecessary.
178 |
179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
180 |
181 | No covered work shall be deemed part of an effective technological
182 | measure under any applicable law fulfilling obligations under article
183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or
184 | similar laws prohibiting or restricting circumvention of such
185 | measures.
186 |
187 | When you convey a covered work, you waive any legal power to forbid
188 | circumvention of technological measures to the extent such circumvention
189 | is effected by exercising rights under this License with respect to
190 | the covered work, and you disclaim any intention to limit operation or
191 | modification of the work as a means of enforcing, against the work's
192 | users, your or third parties' legal rights to forbid circumvention of
193 | technological measures.
194 |
195 | 4. Conveying Verbatim Copies.
196 |
197 | You may convey verbatim copies of the Program's source code as you
198 | receive it, in any medium, provided that you conspicuously and
199 | appropriately publish on each copy an appropriate copyright notice;
200 | keep intact all notices stating that this License and any
201 | non-permissive terms added in accord with section 7 apply to the code;
202 | keep intact all notices of the absence of any warranty; and give all
203 | recipients a copy of this License along with the Program.
204 |
205 | You may charge any price or no price for each copy that you convey,
206 | and you may offer support or warranty protection for a fee.
207 |
208 | 5. Conveying Modified Source Versions.
209 |
210 | You may convey a work based on the Program, or the modifications to
211 | produce it from the Program, in the form of source code under the
212 | terms of section 4, provided that you also meet all of these conditions:
213 |
214 | a) The work must carry prominent notices stating that you modified
215 | it, and giving a relevant date.
216 |
217 | b) The work must carry prominent notices stating that it is
218 | released under this License and any conditions added under section
219 | 7. This requirement modifies the requirement in section 4 to
220 | "keep intact all notices".
221 |
222 | c) You must license the entire work, as a whole, under this
223 | License to anyone who comes into possession of a copy. This
224 | License will therefore apply, along with any applicable section 7
225 | additional terms, to the whole of the work, and all its parts,
226 | regardless of how they are packaged. This License gives no
227 | permission to license the work in any other way, but it does not
228 | invalidate such permission if you have separately received it.
229 |
230 | d) If the work has interactive user interfaces, each must display
231 | Appropriate Legal Notices; however, if the Program has interactive
232 | interfaces that do not display Appropriate Legal Notices, your
233 | work need not make them do so.
234 |
235 | A compilation of a covered work with other separate and independent
236 | works, which are not by their nature extensions of the covered work,
237 | and which are not combined with it such as to form a larger program,
238 | in or on a volume of a storage or distribution medium, is called an
239 | "aggregate" if the compilation and its resulting copyright are not
240 | used to limit the access or legal rights of the compilation's users
241 | beyond what the individual works permit. Inclusion of a covered work
242 | in an aggregate does not cause this License to apply to the other
243 | parts of the aggregate.
244 |
245 | 6. Conveying Non-Source Forms.
246 |
247 | You may convey a covered work in object code form under the terms
248 | of sections 4 and 5, provided that you also convey the
249 | machine-readable Corresponding Source under the terms of this License,
250 | in one of these ways:
251 |
252 | a) Convey the object code in, or embodied in, a physical product
253 | (including a physical distribution medium), accompanied by the
254 | Corresponding Source fixed on a durable physical medium
255 | customarily used for software interchange.
256 |
257 | b) Convey the object code in, or embodied in, a physical product
258 | (including a physical distribution medium), accompanied by a
259 | written offer, valid for at least three years and valid for as
260 | long as you offer spare parts or customer support for that product
261 | model, to give anyone who possesses the object code either (1) a
262 | copy of the Corresponding Source for all the software in the
263 | product that is covered by this License, on a durable physical
264 | medium customarily used for software interchange, for a price no
265 | more than your reasonable cost of physically performing this
266 | conveying of source, or (2) access to copy the
267 | Corresponding Source from a network server at no charge.
268 |
269 | c) Convey individual copies of the object code with a copy of the
270 | written offer to provide the Corresponding Source. This
271 | alternative is allowed only occasionally and noncommercially, and
272 | only if you received the object code with such an offer, in accord
273 | with subsection 6b.
274 |
275 | d) Convey the object code by offering access from a designated
276 | place (gratis or for a charge), and offer equivalent access to the
277 | Corresponding Source in the same way through the same place at no
278 | further charge. You need not require recipients to copy the
279 | Corresponding Source along with the object code. If the place to
280 | copy the object code is a network server, the Corresponding Source
281 | may be on a different server (operated by you or a third party)
282 | that supports equivalent copying facilities, provided you maintain
283 | clear directions next to the object code saying where to find the
284 | Corresponding Source. Regardless of what server hosts the
285 | Corresponding Source, you remain obligated to ensure that it is
286 | available for as long as needed to satisfy these requirements.
287 |
288 | e) Convey the object code using peer-to-peer transmission, provided
289 | you inform other peers where the object code and Corresponding
290 | Source of the work are being offered to the general public at no
291 | charge under subsection 6d.
292 |
293 | A separable portion of the object code, whose source code is excluded
294 | from the Corresponding Source as a System Library, need not be
295 | included in conveying the object code work.
296 |
297 | A "User Product" is either (1) a "consumer product", which means any
298 | tangible personal property which is normally used for personal, family,
299 | or household purposes, or (2) anything designed or sold for incorporation
300 | into a dwelling. In determining whether a product is a consumer product,
301 | doubtful cases shall be resolved in favor of coverage. For a particular
302 | product received by a particular user, "normally used" refers to a
303 | typical or common use of that class of product, regardless of the status
304 | of the particular user or of the way in which the particular user
305 | actually uses, or expects or is expected to use, the product. A product
306 | is a consumer product regardless of whether the product has substantial
307 | commercial, industrial or non-consumer uses, unless such uses represent
308 | the only significant mode of use of the product.
309 |
310 | "Installation Information" for a User Product means any methods,
311 | procedures, authorization keys, or other information required to install
312 | and execute modified versions of a covered work in that User Product from
313 | a modified version of its Corresponding Source. The information must
314 | suffice to ensure that the continued functioning of the modified object
315 | code is in no case prevented or interfered with solely because
316 | modification has been made.
317 |
318 | If you convey an object code work under this section in, or with, or
319 | specifically for use in, a User Product, and the conveying occurs as
320 | part of a transaction in which the right of possession and use of the
321 | User Product is transferred to the recipient in perpetuity or for a
322 | fixed term (regardless of how the transaction is characterized), the
323 | Corresponding Source conveyed under this section must be accompanied
324 | by the Installation Information. But this requirement does not apply
325 | if neither you nor any third party retains the ability to install
326 | modified object code on the User Product (for example, the work has
327 | been installed in ROM).
328 |
329 | The requirement to provide Installation Information does not include a
330 | requirement to continue to provide support service, warranty, or updates
331 | for a work that has been modified or installed by the recipient, or for
332 | the User Product in which it has been modified or installed. Access to a
333 | network may be denied when the modification itself materially and
334 | adversely affects the operation of the network or violates the rules and
335 | protocols for communication across the network.
336 |
337 | Corresponding Source conveyed, and Installation Information provided,
338 | in accord with this section must be in a format that is publicly
339 | documented (and with an implementation available to the public in
340 | source code form), and must require no special password or key for
341 | unpacking, reading or copying.
342 |
343 | 7. Additional Terms.
344 |
345 | "Additional permissions" are terms that supplement the terms of this
346 | License by making exceptions from one or more of its conditions.
347 | Additional permissions that are applicable to the entire Program shall
348 | be treated as though they were included in this License, to the extent
349 | that they are valid under applicable law. If additional permissions
350 | apply only to part of the Program, that part may be used separately
351 | under those permissions, but the entire Program remains governed by
352 | this License without regard to the additional permissions.
353 |
354 | When you convey a copy of a covered work, you may at your option
355 | remove any additional permissions from that copy, or from any part of
356 | it. (Additional permissions may be written to require their own
357 | removal in certain cases when you modify the work.) You may place
358 | additional permissions on material, added by you to a covered work,
359 | for which you have or can give appropriate copyright permission.
360 |
361 | Notwithstanding any other provision of this License, for material you
362 | add to a covered work, you may (if authorized by the copyright holders of
363 | that material) supplement the terms of this License with terms:
364 |
365 | a) Disclaiming warranty or limiting liability differently from the
366 | terms of sections 15 and 16 of this License; or
367 |
368 | b) Requiring preservation of specified reasonable legal notices or
369 | author attributions in that material or in the Appropriate Legal
370 | Notices displayed by works containing it; or
371 |
372 | c) Prohibiting misrepresentation of the origin of that material, or
373 | requiring that modified versions of such material be marked in
374 | reasonable ways as different from the original version; or
375 |
376 | d) Limiting the use for publicity purposes of names of licensors or
377 | authors of the material; or
378 |
379 | e) Declining to grant rights under trademark law for use of some
380 | trade names, trademarks, or service marks; or
381 |
382 | f) Requiring indemnification of licensors and authors of that
383 | material by anyone who conveys the material (or modified versions of
384 | it) with contractual assumptions of liability to the recipient, for
385 | any liability that these contractual assumptions directly impose on
386 | those licensors and authors.
387 |
388 | All other non-permissive additional terms are considered "further
389 | restrictions" within the meaning of section 10. If the Program as you
390 | received it, or any part of it, contains a notice stating that it is
391 | governed by this License along with a term that is a further
392 | restriction, you may remove that term. If a license document contains
393 | a further restriction but permits relicensing or conveying under this
394 | License, you may add to a covered work material governed by the terms
395 | of that license document, provided that the further restriction does
396 | not survive such relicensing or conveying.
397 |
398 | If you add terms to a covered work in accord with this section, you
399 | must place, in the relevant source files, a statement of the
400 | additional terms that apply to those files, or a notice indicating
401 | where to find the applicable terms.
402 |
403 | Additional terms, permissive or non-permissive, may be stated in the
404 | form of a separately written license, or stated as exceptions;
405 | the above requirements apply either way.
406 |
407 | 8. Termination.
408 |
409 | You may not propagate or modify a covered work except as expressly
410 | provided under this License. Any attempt otherwise to propagate or
411 | modify it is void, and will automatically terminate your rights under
412 | this License (including any patent licenses granted under the third
413 | paragraph of section 11).
414 |
415 | However, if you cease all violation of this License, then your
416 | license from a particular copyright holder is reinstated (a)
417 | provisionally, unless and until the copyright holder explicitly and
418 | finally terminates your license, and (b) permanently, if the copyright
419 | holder fails to notify you of the violation by some reasonable means
420 | prior to 60 days after the cessation.
421 |
422 | Moreover, your license from a particular copyright holder is
423 | reinstated permanently if the copyright holder notifies you of the
424 | violation by some reasonable means, this is the first time you have
425 | received notice of violation of this License (for any work) from that
426 | copyright holder, and you cure the violation prior to 30 days after
427 | your receipt of the notice.
428 |
429 | Termination of your rights under this section does not terminate the
430 | licenses of parties who have received copies or rights from you under
431 | this License. If your rights have been terminated and not permanently
432 | reinstated, you do not qualify to receive new licenses for the same
433 | material under section 10.
434 |
435 | 9. Acceptance Not Required for Having Copies.
436 |
437 | You are not required to accept this License in order to receive or
438 | run a copy of the Program. Ancillary propagation of a covered work
439 | occurring solely as a consequence of using peer-to-peer transmission
440 | to receive a copy likewise does not require acceptance. However,
441 | nothing other than this License grants you permission to propagate or
442 | modify any covered work. These actions infringe copyright if you do
443 | not accept this License. Therefore, by modifying or propagating a
444 | covered work, you indicate your acceptance of this License to do so.
445 |
446 | 10. Automatic Licensing of Downstream Recipients.
447 |
448 | Each time you convey a covered work, the recipient automatically
449 | receives a license from the original licensors, to run, modify and
450 | propagate that work, subject to this License. You are not responsible
451 | for enforcing compliance by third parties with this License.
452 |
453 | An "entity transaction" is a transaction transferring control of an
454 | organization, or substantially all assets of one, or subdividing an
455 | organization, or merging organizations. If propagation of a covered
456 | work results from an entity transaction, each party to that
457 | transaction who receives a copy of the work also receives whatever
458 | licenses to the work the party's predecessor in interest had or could
459 | give under the previous paragraph, plus a right to possession of the
460 | Corresponding Source of the work from the predecessor in interest, if
461 | the predecessor has it or can get it with reasonable efforts.
462 |
463 | You may not impose any further restrictions on the exercise of the
464 | rights granted or affirmed under this License. For example, you may
465 | not impose a license fee, royalty, or other charge for exercise of
466 | rights granted under this License, and you may not initiate litigation
467 | (including a cross-claim or counterclaim in a lawsuit) alleging that
468 | any patent claim is infringed by making, using, selling, offering for
469 | sale, or importing the Program or any portion of it.
470 |
471 | 11. Patents.
472 |
473 | A "contributor" is a copyright holder who authorizes use under this
474 | License of the Program or a work on which the Program is based. The
475 | work thus licensed is called the contributor's "contributor version".
476 |
477 | A contributor's "essential patent claims" are all patent claims
478 | owned or controlled by the contributor, whether already acquired or
479 | hereafter acquired, that would be infringed by some manner, permitted
480 | by this License, of making, using, or selling its contributor version,
481 | but do not include claims that would be infringed only as a
482 | consequence of further modification of the contributor version. For
483 | purposes of this definition, "control" includes the right to grant
484 | patent sublicenses in a manner consistent with the requirements of
485 | this License.
486 |
487 | Each contributor grants you a non-exclusive, worldwide, royalty-free
488 | patent license under the contributor's essential patent claims, to
489 | make, use, sell, offer for sale, import and otherwise run, modify and
490 | propagate the contents of its contributor version.
491 |
492 | In the following three paragraphs, a "patent license" is any express
493 | agreement or commitment, however denominated, not to enforce a patent
494 | (such as an express permission to practice a patent or covenant not to
495 | sue for patent infringement). To "grant" such a patent license to a
496 | party means to make such an agreement or commitment not to enforce a
497 | patent against the party.
498 |
499 | If you convey a covered work, knowingly relying on a patent license,
500 | and the Corresponding Source of the work is not available for anyone
501 | to copy, free of charge and under the terms of this License, through a
502 | publicly available network server or other readily accessible means,
503 | then you must either (1) cause the Corresponding Source to be so
504 | available, or (2) arrange to deprive yourself of the benefit of the
505 | patent license for this particular work, or (3) arrange, in a manner
506 | consistent with the requirements of this License, to extend the patent
507 | license to downstream recipients. "Knowingly relying" means you have
508 | actual knowledge that, but for the patent license, your conveying the
509 | covered work in a country, or your recipient's use of the covered work
510 | in a country, would infringe one or more identifiable patents in that
511 | country that you have reason to believe are valid.
512 |
513 | If, pursuant to or in connection with a single transaction or
514 | arrangement, you convey, or propagate by procuring conveyance of, a
515 | covered work, and grant a patent license to some of the parties
516 | receiving the covered work authorizing them to use, propagate, modify
517 | or convey a specific copy of the covered work, then the patent license
518 | you grant is automatically extended to all recipients of the covered
519 | work and works based on it.
520 |
521 | A patent license is "discriminatory" if it does not include within
522 | the scope of its coverage, prohibits the exercise of, or is
523 | conditioned on the non-exercise of one or more of the rights that are
524 | specifically granted under this License. You may not convey a covered
525 | work if you are a party to an arrangement with a third party that is
526 | in the business of distributing software, under which you make payment
527 | to the third party based on the extent of your activity of conveying
528 | the work, and under which the third party grants, to any of the
529 | parties who would receive the covered work from you, a discriminatory
530 | patent license (a) in connection with copies of the covered work
531 | conveyed by you (or copies made from those copies), or (b) primarily
532 | for and in connection with specific products or compilations that
533 | contain the covered work, unless you entered into that arrangement,
534 | or that patent license was granted, prior to 28 March 2007.
535 |
536 | Nothing in this License shall be construed as excluding or limiting
537 | any implied license or other defenses to infringement that may
538 | otherwise be available to you under applicable patent law.
539 |
540 | 12. No Surrender of Others' Freedom.
541 |
542 | If conditions are imposed on you (whether by court order, agreement or
543 | otherwise) that contradict the conditions of this License, they do not
544 | excuse you from the conditions of this License. If you cannot convey a
545 | covered work so as to satisfy simultaneously your obligations under this
546 | License and any other pertinent obligations, then as a consequence you may
547 | not convey it at all. For example, if you agree to terms that obligate you
548 | to collect a royalty for further conveying from those to whom you convey
549 | the Program, the only way you could satisfy both those terms and this
550 | License would be to refrain entirely from conveying the Program.
551 |
552 | 13. Use with the GNU Affero General Public License.
553 |
554 | Notwithstanding any other provision of this License, you have
555 | permission to link or combine any covered work with a work licensed
556 | under version 3 of the GNU Affero General Public License into a single
557 | combined work, and to convey the resulting work. The terms of this
558 | License will continue to apply to the part which is the covered work,
559 | but the special requirements of the GNU Affero General Public License,
560 | section 13, concerning interaction through a network will apply to the
561 | combination as such.
562 |
563 | 14. Revised Versions of this License.
564 |
565 | The Free Software Foundation may publish revised and/or new versions of
566 | the GNU General Public License from time to time. Such new versions will
567 | be similar in spirit to the present version, but may differ in detail to
568 | address new problems or concerns.
569 |
570 | Each version is given a distinguishing version number. If the
571 | Program specifies that a certain numbered version of the GNU General
572 | Public License "or any later version" applies to it, you have the
573 | option of following the terms and conditions either of that numbered
574 | version or of any later version published by the Free Software
575 | Foundation. If the Program does not specify a version number of the
576 | GNU General Public License, you may choose any version ever published
577 | by the Free Software Foundation.
578 |
579 | If the Program specifies that a proxy can decide which future
580 | versions of the GNU General Public License can be used, that proxy's
581 | public statement of acceptance of a version permanently authorizes you
582 | to choose that version for the Program.
583 |
584 | Later license versions may give you additional or different
585 | permissions. However, no additional obligations are imposed on any
586 | author or copyright holder as a result of your choosing to follow a
587 | later version.
588 |
589 | 15. Disclaimer of Warranty.
590 |
591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
599 |
600 | 16. Limitation of Liability.
601 |
602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
610 | SUCH DAMAGES.
611 |
612 | 17. Interpretation of Sections 15 and 16.
613 |
614 | If the disclaimer of warranty and limitation of liability provided
615 | above cannot be given local legal effect according to their terms,
616 | reviewing courts shall apply local law that most closely approximates
617 | an absolute waiver of all civil liability in connection with the
618 | Program, unless a warranty or assumption of liability accompanies a
619 | copy of the Program in return for a fee.
620 |
621 | END OF TERMS AND CONDITIONS
622 |
623 | How to Apply These Terms to Your New Programs
624 |
625 | If you develop a new program, and you want it to be of the greatest
626 | possible use to the public, the best way to achieve this is to make it
627 | free software which everyone can redistribute and change under these terms.
628 |
629 | To do so, attach the following notices to the program. It is safest
630 | to attach them to the start of each source file to most effectively
631 | state the exclusion of warranty; and each file should have at least
632 | the "copyright" line and a pointer to where the full notice is found.
633 |
634 |
635 | Copyright (C)
636 |
637 | This program is free software: you can redistribute it and/or modify
638 | it under the terms of the GNU General Public License as published by
639 | the Free Software Foundation, either version 3 of the License, or
640 | (at your option) any later version.
641 |
642 | This program is distributed in the hope that it will be useful,
643 | but WITHOUT ANY WARRANTY; without even the implied warranty of
644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
645 | GNU General Public License for more details.
646 |
647 | You should have received a copy of the GNU General Public License
648 | along with this program. If not, see .
649 |
650 | Also add information on how to contact you by electronic and paper mail.
651 |
652 | If the program does terminal interaction, make it output a short
653 | notice like this when it starts in an interactive mode:
654 |
655 | Copyright (C)
656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
657 | This is free software, and you are welcome to redistribute it
658 | under certain conditions; type `show c' for details.
659 |
660 | The hypothetical commands `show w' and `show c' should show the appropriate
661 | parts of the General Public License. Of course, your program's commands
662 | might be different; for a GUI interface, you would use an "about box".
663 |
664 | You should also get your employer (if you work as a programmer) or school,
665 | if any, to sign a "copyright disclaimer" for the program, if necessary.
666 | For more information on this, and how to apply and follow the GNU GPL, see
667 | .
668 |
669 | The GNU General Public License does not permit incorporating your program
670 | into proprietary programs. If your program is a subroutine library, you
671 | may consider it more useful to permit linking proprietary applications with
672 | the library. If this is what you want to do, use the GNU Lesser General
673 | Public License instead of this License. But first, please read
674 | .
675 |
--------------------------------------------------------------------------------
/old_version/esp32_wire_jk-bms-can.yaml:
--------------------------------------------------------------------------------
1 | # JK-BMS-CAN ( PYLON, Seplos, GoodWe, SMA and Victron CAN bus protocol )
2 |
3 | # esp32_wire_jk-bms-can.yaml is free software: you can redistribute it
4 | # and/or modify it under the terms of the GNU General Public License
5 | # as published by the Free Software Foundation, either version 3
6 | # of the License, or (at your option) any later version.
7 | #
8 | # This program is distributed in the hope that it will be useful,
9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11 | # See the GNU General Public License for more details.
12 | #
13 | # You should have received a copy of the GNU General Public License
14 | # along with this program. If not, see .
15 |
16 | # V1.16.4 Sleeper85 : Improved Charging Logic for ESP32 startup/reboot and Float charge, Add CAN ID 0x356 bytes [06:07] cycles for Sofar, Change switch name
17 | # V1.16.3 Sleeper85 : ID 0x379 will be sent when choosing protocol 2 or 4 (Battery Capacity for Victron, Sol-Ark and Luxpower)
18 | # V1.16.2 Sleeper85 : Split the "Charge/Discharge values" section and added instructions for "Stop Discharging" + Set "esp-idf" framework by default
19 | # V1.16.1 Sleeper85 : Slider charging_current max value = ${charge_a}, Improved Alarm/Charging/Discharging Logic, Improved CAN protocol and Victron support
20 | # V1.15.5 Sleeper85 : Improved code and set api "reboot_timout" to "0s" by default (no reboot without HA)
21 | # V1.15.4 Sleeper85 : Improved documentation for API, Web Server and WiFi settings
22 | # V1.15.3 Sleeper85 : Add 'CAN Protocol Settings' and new CAN ID based on the SMA and Victron protocol (alpha)
23 | # V1.15.2 Sleeper85 : Improved Alarm handling, all alarms will set charge/discharge current to 0A and set 'Charging Status' to Alarm
24 | # V1.15.1 Sleeper85 : New CANBUS script with CANBUS Status in HA, stop sending CAN messages if the inverter is not responding (fix WDT reboot issues)
25 | # V1.14.3 Sleeper85 : Improved documentation + Charging Voltage tips for Deye
26 | # V1.14.2 Sleeper85 : Improve 'Charging Voltage' behavior
27 | # V1.14.1 Sleeper85 : Add 'Float charge function'
28 | # V1.13.6 Sleeper85 : Add 'Absorption time' and 'Absorption Offset V.' slider
29 | # V1.13.5 Sleeper85 : Set CAN manufacter to "PYLON" for improve compatibility with Deye and other inverters
30 | # V1.13.4 Sleeper85 : Improve 'Charge Status' behavior + add 'Rebulk Offset V.' slider
31 | # V1.13.3 uksa007 : Improve compatibility with Deye and other inverters
32 | # V1.13.2 uksa007 : Send Max Temperature of T1, T2 to inverter
33 | # V1.13.1 uksa007 : Fix compile issues with new version of ESPhome 2023.4.0, set rebulk offset to 2.5
34 |
35 | substitutions:
36 | # +--------------------------------------+
37 | # name that will appear in esphome and homeassistant.
38 | name: jk-bms-can
39 | # +--------------------------------------+
40 | # Number of Battery modules max 8. Each LX U5.4-L battery is 5.4kWh, select the number closest to your capactiy eg 3.2V * 280Ah * 16 = 14.3kWh
41 | batt_modules: "3"
42 | # +--------------------------------------+
43 | # | Battery Charge Settings |
44 | # +--------------------------------------+
45 | # Tips for Deye inverter : Add 0.1v to the settings below because the Deye charging voltage is always 0.1v lower than requested.
46 | # Float V. : 53.7v (3.35v/cell - Natural voltage of a fully charged cell at rest, I advise you not to go higher.)
47 | # Absorption V : 55.3v (3.45v/cell - It's not necessary to use a charging voltage higher than 55.2V for a full charge.)
48 | # Absorption Offset V. : 0.15v (The absorption phase will start at 55.15v (BMS voltage). Warning: the BMS voltage must be correctly calibrated.)
49 | # +--------------------------------------+
50 | # This is max charging amps eg 100A, for Bulk - Constant Current charging(CC), should be at least 10A less than BMS change current protection, 0.5C max
51 | # 100A * 50V = 5000W
52 | charge_a: "100"
53 | # Float Voltage : corresponds to the voltage at which the battery would be maintained at the end of the absorption phase. (53.6v eg 3.35v/cell for 16 cells 48V battery)
54 | float_v: "53.6"
55 | # Absorption Voltage : corresponds to the Bulk voltage that will be used to charge the battery. (55.2v eg 3.45v/cell for 16 cells 48V battery)
56 | absorption_v: "55.2"
57 | # Absorption time in minutes to hold charge voltage after charge voltage is reached eg 30
58 | absorption_time: "30"
59 | # Absorption offset, x Volts below absorption voltage battery will start the absorption timer, eg 55.2-0.05 = 52.15v
60 | absorption_offset_v: "0.05"
61 | # Rebulk offset, x Volts below absorption voltage battery will request rebulk, eg 55.2-2.5 = 52.7v
62 | rebulk_offset_v: "2.5"
63 | # +--------------------------------------+
64 | # | Battery Discharge Settings |
65 | # +--------------------------------------+
66 | # Max discharge amps eg 120, should be at least 10A less than BMS over discharge current protection, 0.5C max
67 | # 120A * 50V = 6000W
68 | discharge_a: "120"
69 | # Minimum discharge voltage eg 48v/16 = 3V per cell
70 | min_discharge_v: "48"
71 | # +--------------------------------------+
72 | # | Battery State of Health (SOH) |
73 | # +--------------------------------------+
74 | # Maximum charging cycles is used to calculate the battey SOH, LF280K v3 =8000.0, LF280K v2 =6000.0, LF280=3000.0 (decimal is required)
75 | max_cycles: "6000.0"
76 | # +--------------------------------------+
77 | # | CAN Protocol Settings |
78 | # +--------------------------------------+
79 | # CAN BMS Name (0x35E) : 0 NoSent / 1 PYLON / 2 GOODWE / 3 SEPLOS
80 | can_bms_name: "1"
81 | # CAN Protocol
82 | # 1 : PYLON 1.2 (Deye)
83 | # 2 : SEPLOS 1.0, PYLON 1.3, GOODWE 1.5 (GoodWe, Sol-Ark, Luxpower)
84 | # 3 : SMA (Sunny Island, Sofar)
85 | # 4 : VICTRON
86 | can_protocol: "1"
87 | # +--------------------------------------+
88 | # | ESP32 CAN/serial port pins |
89 | # +--------------------------------------+
90 | # GPIO pins your CAN bus transceiver (TJA1050, TJA1051T or SN65HVD230) is connected to the ESP32 TX->TX and RX->RX !
91 | can_tx_pin: GPIO23
92 | can_rx_pin: GPIO22
93 | # GPIO pins your JK-BMS UART-TTL is connected to the ESP32 TX->RX and RX->TX !
94 | tx_pin: GPIO17
95 | rx_pin: GPIO16
96 |
97 | # +------------------------------------------------------------------+
98 | # | ** The settings below can be modified according to your needs ** |
99 | # +------------------------------------------------------------------+
100 | external_components_source: github://syssi/esphome-jk-bms@main
101 | # components
102 | # github://syssi/esphome-jk-bms@main
103 |
104 | esphome:
105 | name: ${name}
106 | on_boot:
107 | then:
108 | - switch.turn_on: switch_charging
109 | - switch.turn_on: switch_discharging
110 | - switch.turn_on: switch_chg_float
111 |
112 | # +--------------------------------------+
113 | # | ESP32 settings |
114 | # +--------------------------------------+
115 | # For a stable Bluetooth connection keep the "esp-idf" framework
116 | esp32:
117 | board: esp32doit-devkit-v1
118 | framework:
119 | type: esp-idf
120 |
121 | external_components:
122 | - source: ${external_components_source}
123 | refresh: 0s
124 |
125 | logger:
126 | # level: DEBUG
127 |
128 | ota:
129 |
130 | # Please use the native `api` component instead of the `mqtt` section.
131 | # If you use Home Assistant, the native API is more lightweight.
132 | # If there is no HA server connected to this API, the ESP32 reboots every 15 minutes to try to resolve the problem.
133 | # If you don't use Home Assistant please uncomment the "reboot_timeout: 0s" option.
134 | api:
135 | reboot_timeout: 0s
136 |
137 | # If you don't want to use ESPHome's native API you can use MQQT instead.
138 | # In this case don't forget to remove the 'api:' section.
139 | # mqtt:
140 | # broker: !secret mqtt_host
141 | # username: !secret mqtt_username
142 | # password: !secret mqtt_password
143 | # id: mqtt_client
144 |
145 | # In the event of problems with the WiFi network, the ESP32 will reboot every 15 minutes to try to resolve the problem.
146 | # If we don't want to connect the ESP32 to the WiFi network please remove the 4 lines below.
147 | wifi:
148 | ssid: !secret wifi_ssid
149 | password: !secret wifi_password
150 | domain: !secret domain
151 |
152 | #web_server:
153 | # port: 80
154 | # log: false
155 | # ota: false
156 |
157 | # +--------------------------------------+
158 | # | ** Don't make changes below this ** |
159 | # +--------------------------------------+
160 |
161 | globals:
162 | - id: can_ack_counter
163 | type: int
164 | restore_value: no
165 | initial_value: '0'
166 | - id: charge_status
167 | type: std::string
168 | restore_value: no
169 | initial_value: '"Wait"'
170 | - id: can_status
171 | type: std::string
172 | restore_value: no
173 | initial_value: '"OFF"'
174 | - id: alarm_status
175 | type: std::string
176 | restore_value: no
177 | initial_value: '"NoAlarm"'
178 | - id: charging_v
179 | type: float
180 | restore_value: no
181 | initial_value: '0.0'
182 | - id: charging_a
183 | type: int
184 | restore_value: no
185 | initial_value: '0'
186 | - id: discharging_a
187 | type: int
188 | restore_value: no
189 | initial_value: '0'
190 | - id: can_msg_counter
191 | type: int
192 | restore_value: no
193 | initial_value: '0'
194 |
195 | output:
196 | - platform: gpio
197 | pin: 2
198 | id: led
199 | inverted: true
200 |
201 | light:
202 | - platform: binary
203 | output: led
204 | id: blue_led
205 | name: "Blue LED"
206 | internal: true
207 |
208 | # +--------------------------------------+
209 | # | JK-BMS UART connection |
210 | # +--------------------------------------+
211 |
212 | uart:
213 | id: uart_0
214 | baud_rate: 115200
215 | rx_buffer_size: 384
216 | tx_pin: ${tx_pin}
217 | rx_pin: ${rx_pin}
218 | # debug:
219 | # direction: BOTH
220 |
221 | jk_modbus:
222 | id: modbus0
223 | uart_id: uart_0
224 |
225 | jk_bms:
226 | id: bms0
227 | jk_modbus_id: modbus0
228 | # enable_fake_traffic: true
229 |
230 | # +--------------------------------------+
231 |
232 | binary_sensor:
233 | - platform: jk_bms
234 | balancing:
235 | name: "${name} balancing"
236 | balancing_switch:
237 | name: "${name} balancing switch"
238 | charging:
239 | name: "${name} charging"
240 | charging_switch:
241 | id: charging_switch
242 | name: "${name} charging switch"
243 | discharging:
244 | name: "${name} discharging"
245 | discharging_switch:
246 | id: discharging_switch
247 | name: "${name} discharging switch"
248 | dedicated_charger_switch:
249 | name: "${name} dedicated charger switch"
250 |
251 | sensor:
252 | - platform: jk_bms
253 | min_cell_voltage:
254 | id: min_cell_voltage
255 | name: "${name} min cell voltage"
256 | max_cell_voltage:
257 | id: max_cell_voltage
258 | name: "${name} max cell voltage"
259 | min_voltage_cell:
260 | id: min_voltage_cell
261 | name: "${name} min voltage cell"
262 | max_voltage_cell:
263 | id: max_voltage_cell
264 | name: "${name} max voltage cell"
265 | delta_cell_voltage:
266 | name: "${name} delta cell voltage"
267 | average_cell_voltage:
268 | name: "${name} average cell voltage"
269 | cell_voltage_1:
270 | name: "${name} cell voltage 1"
271 | cell_voltage_2:
272 | name: "${name} cell voltage 2"
273 | cell_voltage_3:
274 | name: "${name} cell voltage 3"
275 | cell_voltage_4:
276 | name: "${name} cell voltage 4"
277 | cell_voltage_5:
278 | name: "${name} cell voltage 5"
279 | cell_voltage_6:
280 | name: "${name} cell voltage 6"
281 | cell_voltage_7:
282 | name: "${name} cell voltage 7"
283 | cell_voltage_8:
284 | name: "${name} cell voltage 8"
285 | cell_voltage_9:
286 | name: "${name} cell voltage 9"
287 | cell_voltage_10:
288 | name: "${name} cell voltage 10"
289 | cell_voltage_11:
290 | name: "${name} cell voltage 11"
291 | cell_voltage_12:
292 | name: "${name} cell voltage 12"
293 | cell_voltage_13:
294 | name: "${name} cell voltage 13"
295 | cell_voltage_14:
296 | name: "${name} cell voltage 14"
297 | cell_voltage_15:
298 | name: "${name} cell voltage 15"
299 | cell_voltage_16:
300 | name: "${name} cell voltage 16"
301 | # cell_voltage_17:
302 | # name: "${name} cell voltage 17"
303 | # cell_voltage_18:
304 | # name: "${name} cell voltage 18"
305 | # cell_voltage_19:
306 | # name: "${name} cell voltage 19"
307 | # cell_voltage_20:
308 | # name: "${name} cell voltage 20"
309 | # cell_voltage_21:
310 | # name: "${name} cell voltage 21"
311 | # cell_voltage_22:
312 | # name: "${name} cell voltage 22"
313 | # cell_voltage_23:
314 | # name: "${name} cell voltage 23"
315 | # cell_voltage_24:
316 | # name: "${name} cell voltage 24"
317 | power_tube_temperature:
318 | id: power_tube_temperature
319 | name: "${name} power tube temperature"
320 | temperature_sensor_1:
321 | id: temperature_sensor_1
322 | name: "${name} temperature sensor 1"
323 | temperature_sensor_2:
324 | id: temperature_sensor_2
325 | name: "${name} temperature sensor 2"
326 | total_voltage:
327 | id: total_voltage
328 | name: "${name} total voltage"
329 | current:
330 | id: current
331 | name: "${name} current"
332 | power:
333 | name: "${name} power"
334 | charging_power:
335 | name: "${name} charging power"
336 | discharging_power:
337 | name: "${name} discharging power"
338 | capacity_remaining:
339 | id: capacity_remaining
340 | name: "${name} capacity remaining"
341 | capacity_remaining_derived:
342 | name: "${name} capacity remaining derived"
343 | temperature_sensors:
344 | name: "${name} temperature sensors"
345 | charging_cycles:
346 | id: charging_cycles
347 | name: "${name} charging cycles"
348 | total_charging_cycle_capacity:
349 | name: "${name} total charging cycle capacity"
350 | battery_strings:
351 | name: "${name} battery strings"
352 | errors_bitmask:
353 | id: errors_bitmask
354 | name: "${name} errors bitmask"
355 | operation_mode_bitmask:
356 | name: "${name} operation mode bitmask"
357 | total_voltage_overvoltage_protection:
358 | name: "${name} total voltage overvoltage protection"
359 | total_voltage_undervoltage_protection:
360 | name: "${name} total voltage undervoltage protection"
361 | cell_voltage_overvoltage_protection:
362 | name: "${name} cell voltage overvoltage protection"
363 | cell_voltage_overvoltage_recovery:
364 | name: "${name} cell voltage overvoltage recovery"
365 | cell_voltage_overvoltage_delay:
366 | name: "${name} cell voltage overvoltage delay"
367 | cell_voltage_undervoltage_protection:
368 | name: "${name} cell voltage undervoltage protection"
369 | cell_voltage_undervoltage_recovery:
370 | name: "${name} cell voltage undervoltage recovery"
371 | cell_voltage_undervoltage_delay:
372 | name: "${name} cell voltage undervoltage delay"
373 | cell_pressure_difference_protection:
374 | name: "${name} cell pressure difference protection"
375 | discharging_overcurrent_protection:
376 | name: "${name} discharging overcurrent protection"
377 | discharging_overcurrent_delay:
378 | name: "${name} discharging overcurrent delay"
379 | charging_overcurrent_protection:
380 | name: "${name} charging overcurrent protection"
381 | charging_overcurrent_delay:
382 | name: "${name} charging overcurrent delay"
383 | balance_starting_voltage:
384 | name: "${name} balance starting voltage"
385 | balance_opening_pressure_difference:
386 | name: "${name} balance opening pressure difference"
387 | power_tube_temperature_protection:
388 | name: "${name} power tube temperature protection"
389 | power_tube_temperature_recovery:
390 | name: "${name} power tube temperature recovery"
391 | temperature_sensor_temperature_protection:
392 | name: "${name} temperature sensor temperature protection"
393 | temperature_sensor_temperature_recovery:
394 | name: "${name} temperature sensor temperature recovery"
395 | temperature_sensor_temperature_difference_protection:
396 | name: "${name} temperature sensor temperature difference protection"
397 | charging_high_temperature_protection:
398 | name: "${name} charging high temperature protection"
399 | discharging_high_temperature_protection:
400 | name: "${name} discharging high temperature protection"
401 | charging_low_temperature_protection:
402 | name: "${name} charging low temperature protection"
403 | charging_low_temperature_recovery:
404 | name: "${name} charging low temperature recovery"
405 | discharging_low_temperature_protection:
406 | name: "${name} discharging low temperature protection"
407 | discharging_low_temperature_recovery:
408 | name: "${name} discharging low temperature recovery"
409 | total_battery_capacity_setting:
410 | id: total_battery_capacity_setting
411 | name: "${name} total battery capacity setting"
412 | current_calibration:
413 | name: "${name} current calibration"
414 | device_address:
415 | name: "${name} device address"
416 | sleep_wait_time:
417 | name: "${name} sleep wait time"
418 | alarm_low_volume:
419 | name: "${name} alarm low volume"
420 | manufacturing_date:
421 | name: "${name} manufacturing date"
422 | total_runtime:
423 | name: "${name} total runtime"
424 | # start_current_calibration:
425 | # name: "${name} start current calibration"
426 | actual_battery_capacity:
427 | name: "${name} actual battery capacity"
428 | # protocol_version:
429 | # name: "${name} protocol version"
430 | # +--------------------------------------+
431 | # | Uptime sensor |
432 | # +--------------------------------------+
433 | - platform: uptime
434 | name: ${name} Uptime Sensor
435 | id: uptime_sensor
436 | update_interval: 60s
437 | on_raw_value:
438 | then:
439 | - text_sensor.template.publish:
440 | id: uptime_human
441 | state: !lambda |-
442 | int seconds = round(id(uptime_sensor).raw_state);
443 | int days = seconds / (24 * 3600);
444 | seconds = seconds % (24 * 3600);
445 | int hours = seconds / 3600;
446 | seconds = seconds % 3600;
447 | int minutes = seconds / 60;
448 | seconds = seconds % 60;
449 | return (
450 | (days ? to_string(days) + "d " : "") +
451 | (hours ? to_string(hours) + "h " : "") +
452 | (minutes ? to_string(minutes) + "m " : "") +
453 | (to_string(seconds) + "s")
454 | ).c_str();
455 |
456 | text_sensor:
457 | - platform: jk_bms
458 | errors:
459 | name: "${name} errors"
460 | operation_mode:
461 | name: "${name} operation mode"
462 | battery_type:
463 | name: "${name} battery type"
464 | # password:
465 | # name: "${name} password"
466 | device_type:
467 | name: "${name} device type"
468 | software_version:
469 | name: "${name} software version"
470 | manufacturer:
471 | name: "${name} manufacturer"
472 | total_runtime_formatted:
473 | name: "${name} total runtime formatted"
474 | # +--------------------------------------+
475 | # | Template text sensors |
476 | # +--------------------------------------+
477 | - platform: template
478 | name: ${name} Uptime Human Readable
479 | id: uptime_human
480 | icon: mdi:clock-start
481 | - platform: template
482 | name: "${name} Charging Status"
483 | id: charging_status
484 | - platform: template
485 | name: "${name} CANBUS Status"
486 | id: canbus_status
487 |
488 | # +--------------------------------------+
489 | # | Slider |
490 | # +--------------------------------------+
491 | number:
492 | - platform: template
493 | name: "${name} Bulk voltage"
494 | id: "bulk_voltage"
495 | step: 0.1
496 | min_value: 52.8
497 | max_value: 57.6
498 | mode: slider
499 | initial_value: "${absorption_v}"
500 | unit_of_measurement: V
501 | icon: mdi:battery-charging
502 | optimistic: true
503 | - platform: template
504 | name: "${name} Float voltage"
505 | id: "float_voltage"
506 | step: 0.1
507 | min_value: 52.8
508 | max_value: 57.6
509 | mode: slider
510 | initial_value: "${float_v}"
511 | unit_of_measurement: V
512 | icon: mdi:battery-charging
513 | optimistic: true
514 | - platform: template
515 | name: "${name} Charging current max"
516 | id: "charging_current"
517 | step: 1
518 | min_value: 0
519 | max_value: "${charge_a}"
520 | mode: slider
521 | initial_value: "${charge_a}"
522 | unit_of_measurement: A
523 | icon: mdi:current-dc
524 | optimistic: true
525 | - platform: template
526 | name: "${name} Rebulk Offset V."
527 | id: "rebulk_offset"
528 | step: 0.1
529 | min_value: 0
530 | max_value: 5
531 | mode: slider
532 | initial_value: "${rebulk_offset_v}"
533 | unit_of_measurement: V
534 | icon: mdi:sine-wave
535 | optimistic: true
536 | - platform: template
537 | name: "${name} Absorption time"
538 | id: "absorption_time"
539 | step: 1
540 | min_value: 0
541 | max_value: 180
542 | mode: slider
543 | initial_value: "${absorption_time}"
544 | unit_of_measurement: min
545 | icon: mdi:clock-start
546 | optimistic: true
547 | - platform: template
548 | name: "${name} Absorption Offset V."
549 | id: "absorption_offset"
550 | step: 0.05
551 | min_value: 0
552 | max_value: 1
553 | mode: slider
554 | initial_value: "${absorption_offset_v}"
555 | unit_of_measurement: V
556 | icon: mdi:sine-wave
557 | optimistic: true
558 |
559 | script:
560 | - id: absorption_script
561 | then:
562 | - lambda: id(charge_status) = "Absorption";
563 | # delay value in ms
564 | - delay: !lambda "return id(absorption_time).state * 60 * 1000;"
565 | - lambda: id(charge_status) = "EOC";
566 |
567 | switch:
568 | - platform: template
569 | name: "${name} CAN Charge enabled"
570 | id: switch_charging
571 | optimistic: true
572 | - platform: template
573 | name: "${name} CAN Discharge enabled"
574 | id: switch_discharging
575 | optimistic: true
576 | - platform: template
577 | name: "${name} CAN Force bulk (top bal)"
578 | id: switch_chg_bulk
579 | optimistic: true
580 | - platform: template
581 | name: "${name} CAN Float charge enabled"
582 | id: switch_chg_float
583 | optimistic: true
584 |
585 |
586 | # +--------------------------------------+
587 | # | CAN bus script |
588 | # +--------------------------------------+
589 | canbus:
590 | - platform: esp32_can
591 | tx_pin: ${can_tx_pin}
592 | rx_pin: ${can_rx_pin}
593 | can_id: 4
594 | bit_rate: 500kbps
595 | on_frame:
596 | - can_id: 0x305 # Inverter ACK - SMA/LG/Pylon/Goodwe reply
597 | then:
598 | - light.toggle:
599 | id: blue_led
600 | - lambda: |-
601 | id(can_ack_counter) = 0; // Reset ACK counter
602 | id(can_status) = "ON"; // Set CANBUS Status to ON
603 | id(canbus_status).publish_state(id(can_status)); // Publish text sensor
604 | ESP_LOGI("main", "received can id: 0x305 ACK");
605 |
606 | interval:
607 | - interval: 120s
608 | then:
609 | - lambda: id(can_ack_counter) = 0; // Reset ACK counter for test inverter ACK
610 |
611 | - interval: 100ms
612 | then:
613 | # Start CAN Handling
614 | - if:
615 | condition:
616 | lambda: |-
617 |
618 | if (id(can_ack_counter) < 20) { // Inverter ACK ? => CANBUS ON
619 |
620 | id(can_ack_counter)++; // CANBUS ACK counter ++
621 | id(can_msg_counter)++; // CANBUS MSG counter ++
622 | return true; // Condition OK
623 |
624 | }
625 | else if (id(can_status) == "OFF") { // CANBUS already OFF ?
626 |
627 | return false; // Nothing to do
628 |
629 | }
630 | else {
631 |
632 | id(can_status) = "OFF"; // Set CANBUS Status to OFF
633 | id(canbus_status).publish_state(id(can_status)); // Publish text sensor
634 | ESP_LOGI("main", "No rx can 0x305 reply, Inverter not connected/responding...");
635 | return false; // Condition NOK
636 |
637 | }
638 |
639 | then:
640 | - if:
641 | condition:
642 | lambda: return ((id(can_msg_counter) == 1) & ((${can_protocol} == 1) | (${can_protocol} == 2)));
643 | then:
644 | canbus.send: # Protection Alarms, Warning and Flags ( Pylontech / Goodwe / Seplos )
645 | can_id: 0x359
646 | data: !lambda |-
647 |
648 | // +---------------------------+
649 | // | JK-BMS errors bitmask |
650 | // +---------------------------+
651 |
652 | // 0x8B 0x00 0x00: Battery warning message 0000 0000 0000 0000
653 | //
654 | // Bit 0 Low capacity 1 (alarm), 0 (normal) warning
655 | // Bit 1 Power tube overtemperature 1 (alarm), 0 (normal) alarm
656 | // Bit 2 Charging overvoltage 1 (alarm), 0 (normal) alarm
657 | // Bit 3 Discharging undervoltage 1 (alarm), 0 (normal) alarm
658 | // Bit 4 Battery over temperature 1 (alarm), 0 (normal) alarm
659 | // Bit 5 Charging overcurrent 1 (alarm), 0 (normal) alarm
660 | // Bit 6 Discharging overcurrent 1 (alarm), 0 (normal) alarm
661 | // Bit 7 Cell pressure difference 1 (alarm), 0 (normal) alarm
662 | // Bit 8 Overtemperature alarm in the battery box 1 (alarm), 0 (normal) alarm
663 | // Bit 9 Battery low temperature 1 (alarm), 0 (normal) alarm
664 | // Bit 10 Cell overvoltage 1 (alarm), 0 (normal) alarm
665 | // Bit 11 Cell undervoltage 1 (alarm), 0 (normal) alarm
666 | // Bit 12 309_A protection 1 (alarm), 0 (normal) alarm
667 | // Bit 13 309_A protection 1 (alarm), 0 (normal) alarm
668 | // Bit 14 Reserved
669 | // Bit 15 Reserved
670 | //
671 | // Examples:
672 | // 0x0001 = 00000000 00000001: Low capacity alarm
673 | // 0x0002 = 00000000 00000010: MOS tube over-temperature alarm
674 | // 0x0003 = 00000000 00000011: Low capacity alarm AND power tube over-temperature alarm
675 |
676 | // +---------------------------+
677 | // | Protection : byte 0 and 1 |
678 | // +---------------------------+
679 |
680 | uint8_t can_mesg[] = {0, 0, 0, 0, 0, 0, 0, 0};
681 |
682 | // JK-BMS alarm ?
683 | if (id(errors_bitmask).state > 1) {
684 | uint16_t jk_errormask = id(errors_bitmask).state;
685 |
686 | if ((jk_errormask & 0x04) | (jk_errormask & 0x80) | (jk_errormask & 0x400)) { // Hight.Voltage.Alarm JK bit 2,7,10
687 | can_mesg[0] = 0x02; // byte0_bit1 (0x02 = bin 10)
688 | id(alarm_status) = "OVP";
689 | ESP_LOGI("main", "Hight.Voltage.Alarm JK bit 2,7,10 - can_msg[0] : %x", can_mesg[0]);
690 | }
691 | if ((jk_errormask & 0x08) | (jk_errormask & 0x800)) { // Low.Voltage.Alarm JK bit 3,11
692 | can_mesg[0] = can_mesg[0] | 0x04; // byte0_bit2 (0x04 = bin 100)
693 | id(alarm_status) = "UVP";
694 | ESP_LOGI("main", "Low.Voltage.Alarm JK bit 3,11 - can_msg[0] : %x", can_mesg[0]);
695 | }
696 | if ((jk_errormask & 0x02) | (jk_errormask & 0x10) | (jk_errormask & 0x100)) { // Hight.Temp.Alarm JK bit 1,4,8
697 | can_mesg[0] = can_mesg[0] | 0x08; // byte0_bit3 (0x08 = bin 1000)
698 | id(alarm_status) = "OTP";
699 | ESP_LOGI("main", "Hight.Temp.Alarm JK bit 1,4,8 - can_msg[0] : %x", can_mesg[0]);
700 | }
701 | if ((jk_errormask & 0x200)) { // Low.Temp.Alarm JK bit 9
702 | can_mesg[0] = can_mesg[0] | 0x10; // byte0_bit4 (0x10 = bin 10000)
703 | id(alarm_status) = "UTP";
704 | ESP_LOGI("main", "Low.Temp.Alarm JK bit 9 - can_msg[0] : %x", can_mesg[0]);
705 | }
706 | if ((jk_errormask & 0x40)) { // Discharge.Over.Current JK bit 6
707 | can_mesg[0] = can_mesg[0] | 0x80; // byte0_bit7 (0x80 = bin 10000000)
708 | id(alarm_status) = "DOCP";
709 | ESP_LOGI("main", "Discharge.Over.Current JK bit 6 - can_msg[0] : %x", can_mesg[0]);
710 | }
711 | if ((jk_errormask & 0x20)) { // Charge.Over.Current JK bit 5
712 | can_mesg[1] = 0x01; // byte1_bit0 (0x01 = bin 1)
713 | id(alarm_status) = "COCP";
714 | ESP_LOGI("main", "Charge.Over.Current JK bit 5 - can_msg[1] : %x", can_mesg[1]);
715 | }
716 | if ((jk_errormask & 0x1000) | (jk_errormask & 0x2000)) { // BMS internal error JK bit 12,13
717 | can_mesg[1] = can_mesg[1] | 0x08; // byte1_bit3 (0x08 = bin 1000)
718 | id(alarm_status) = "BMS";
719 | ESP_LOGI("main", "BMS internal error JK bit 12,13 - can_msg[1] : %x", can_mesg[1]);
720 | }
721 | if ((jk_errormask & 0x80)) { // Cell Imbalance JK bit 7
722 | can_mesg[1] = can_mesg[1] | 0x10; // byte1_bit4 (0x10 = bin 10000)
723 | ESP_LOGI("main", "Cell Imbalance JK bit 7 - can_msg[1] : %x", can_mesg[1]);
724 | }
725 | }
726 | // No Alarm
727 | else id(alarm_status) = "NoAlarm";
728 |
729 | // +---------------------------+
730 | // | Warning : byte 2 and 3 |
731 | // +---------------------------+
732 |
733 | can_mesg[2] = 0x00; // byte2 (JK-BMS infos not available)
734 | can_mesg[3] = 0x00; // byte3 (JK-BMS infos not available)
735 |
736 | // +---------------------------+
737 | // | Flags : byte 4 to 7 |
738 | // +---------------------------+
739 |
740 | int batt_mods = ${batt_modules};
741 |
742 | can_mesg[4] = batt_mods; // byte4 - Module in parallel
743 | can_mesg[5] = 0x00; // byte5
744 | can_mesg[6] = 0x00; // byte6
745 | can_mesg[7] = 0x00; // byte7 - DIP switches 1,3 10000100 0x84
746 |
747 | ESP_LOGI("main", "send can id: 0x359 hex: %x %x %x %x %x %x %x %x", can_mesg[0], can_mesg[1], can_mesg[2], can_mesg[3], can_mesg[4], can_mesg[5], can_mesg[6], can_mesg[7]);
748 | return {can_mesg[0], can_mesg[1], can_mesg[2], can_mesg[3], can_mesg[4], can_mesg[5], can_mesg[6], can_mesg[7]};
749 |
750 | - if:
751 | condition:
752 | lambda: return ((id(can_msg_counter) == 1) & ((${can_protocol} == 3) | (${can_protocol} == 4)));
753 | then:
754 | canbus.send: # Protection Alarms and Warning ( SMA / Victron )
755 | can_id: 0x35A
756 | data: !lambda |-
757 |
758 | // +---------------------------+
759 | // | Protection : byte 0,1,2,3 |
760 | // +---------------------------+
761 |
762 | uint8_t can_mesg[] = {0, 0, 0, 0, 0, 0, 0, 0};
763 |
764 | // JK-BMS alarm ?
765 | if (id(errors_bitmask).state > 1) {
766 | uint16_t jk_errormask = id(errors_bitmask).state;
767 |
768 | if ((jk_errormask & 0x04) | (jk_errormask & 0x80) | (jk_errormask & 0x400)) { // Hight.Voltage.Alarm JK bit 2,7,10
769 | can_mesg[0] = 0x04; // byte0_bit2 (0x04 = bin 100)
770 | id(alarm_status) = "OVP";
771 | ESP_LOGI("main", "Hight.Voltage.Alarm JK bit 2,7,10 - can_msg[0] : %x", can_mesg[0]);
772 | }
773 | if ((jk_errormask & 0x08) | (jk_errormask & 0x800)) { // Low.Voltage.Alarm JK bit 3,11
774 | can_mesg[0] = can_mesg[0] | 0x10; // byte0_bit4 (0x10 = bin 10000)
775 | id(alarm_status) = "UVP";
776 | ESP_LOGI("main", "Low.Voltage.Alarm JK bit 3,11 - can_msg[0] : %x", can_mesg[0]);
777 | }
778 | if ((jk_errormask & 0x02) | (jk_errormask & 0x10) | (jk_errormask & 0x100)) { // Hight.Temp.Alarm JK bit 1,4,8
779 | can_mesg[0] = can_mesg[0] | 0x40; // byte0_bit6 (0x40 = bin 1000000)
780 | id(alarm_status) = "OTP";
781 | ESP_LOGI("main", "Hight.Temp.Alarm JK bit 1,4,8 - can_msg[0] : %x", can_mesg[0]);
782 | }
783 | if ((jk_errormask & 0x200)) { // Low.Temp.Alarm JK bit 9
784 | can_mesg[1] = 0x01; // byte1_bit0 (0x01 = bin 1)
785 | id(alarm_status) = "UTP";
786 | ESP_LOGI("main", "Low.Temp.Alarm JK bit 9 - can_msg[1] : %x", can_mesg[1]);
787 | }
788 | if ((jk_errormask & 0x40)) { // Discharge.Over.Current JK bit 6
789 | can_mesg[1] = can_mesg[1] | 0x40; // byte1_bit6 (0x40 = bin 1000000)
790 | id(alarm_status) = "DOCP";
791 | ESP_LOGI("main", "Discharge.Over.Current JK bit 6 - can_msg[1] : %x", can_mesg[1]);
792 | }
793 | if ((jk_errormask & 0x20)) { // Charge.Over.Current JK bit 5
794 | can_mesg[2] = 0x01; // byte2_bit0 (0x01 = bin 1)
795 | id(alarm_status) = "COCP";
796 | ESP_LOGI("main", "Charge.Over.Current JK bit 5 - can_msg[2] : %x", can_mesg[2]);
797 | }
798 | if ((jk_errormask & 0x1000) | (jk_errormask & 0x2000)) { // BMS.Internal.Error JK bit 12,13
799 | can_mesg[2] = can_mesg[2] | 0x40; // byte2_bit6 (0x40 = bin 1000000)
800 | id(alarm_status) = "BMS";
801 | ESP_LOGI("main", "BMS internal error JK bit 12,13 - can_msg[2] : %x", can_mesg[2]);
802 | }
803 | if ((jk_errormask & 0x80)) { // Cell.Imbalance JK bit 7
804 | can_mesg[3] = 0x01; // byte3_bit0 (0x01 = bin 1)
805 | ESP_LOGI("main", "Cell Imbalance JK bit 7 - can_msg[3] : %x", can_mesg[3]);
806 | }
807 | }
808 | // No Alarm
809 | else id(alarm_status) = "NoAlarm";
810 |
811 | ESP_LOGI("main", "send can id: 0x35A hex: %x %x %x %x %x %x %x %x", can_mesg[0], can_mesg[1], can_mesg[2], can_mesg[3], can_mesg[4], can_mesg[5], can_mesg[6], can_mesg[7]);
812 | return {can_mesg[0], can_mesg[1], can_mesg[2], can_mesg[3], can_mesg[4], can_mesg[5], can_mesg[6], can_mesg[7]};
813 |
814 | - if:
815 | condition:
816 | lambda: return id(can_msg_counter) == 2;
817 | then:
818 | canbus.send: # BMS instruction : Charge Volts, Charge Amps, Discharge Amps, Min voltage
819 | can_id: 0x351
820 | data: !lambda |-
821 |
822 | // +--------------------------------------+
823 | // | Charging Logic |
824 | // +--------------------------------------+
825 |
826 | // Warning : information from JK BMS is not available immediately after boot
827 |
828 | // Alarm : if JK-BMS alarm !
829 | if (id(errors_bitmask).state > 1) {
830 | id(charge_status) = "Alarm";
831 | }
832 | // No Alarm => Wait
833 | else if ((id(errors_bitmask).state < 2) & (id(charge_status) == "Alarm")) {
834 | id(charge_status) = "Wait";
835 | }
836 | // Charge ON : BMS and ESP32 charging switch is ON
837 | else if ((id(charging_switch).state) & (id(switch_charging).state)) {
838 |
839 | // Force Bulk : 'Charging manually (top bal)' switch is ON
840 | if (id(switch_chg_bulk).state) {
841 | id(charge_status) = "Force Bulk";
842 | }
843 | // No Force Bulk => Wait
844 | else if ((!id(switch_chg_bulk).state) & (id(charge_status) == "Force Bulk")) {
845 | id(charge_status) = "Wait";
846 | }
847 | // Bulk : Bat. V. <= Rebulk V. ( Absorption V. - Rebulk Offset V. ) ( Bulk : Rebulk V. = 55.2-2.5 = 52.7V by default )
848 | else if (id(total_voltage).state <= (id(bulk_voltage).state - id(rebulk_offset).state)) {
849 | id(charge_status) = "Bulk";
850 | if (id(absorption_script).is_running()) id(absorption_script).stop();
851 | }
852 | // Absorption : Bat. V >= ( Absorption V. - Absorption Offset V. ) ( Absorption V. = 55.2-0.05 = 55.15V by default )
853 | else if ((id(charge_status) == "Bulk") & (id(total_voltage).state >= (id(bulk_voltage).state - id(absorption_offset).state))) {
854 | id(charge_status) = "Absorption";
855 | if (!id(absorption_script).is_running()) id(absorption_script).execute(); // 10 % from top start absorption timer
856 | }
857 | // Bulk : (Bat. V. > Rebulk V.) + Wait / ESP32 startup ( Bulk : when starting the ESP32 )
858 | else if ((id(charge_status) == "Wait")) {
859 | id(charge_status) = "Bulk";
860 | if (id(absorption_script).is_running()) id(absorption_script).stop();
861 | }
862 | // Float : (Bat. V. > Rebulk V.) + Float switch ON and End Of Charge ( Float : after Absorption )
863 | else if ((id(switch_chg_float).state) & (id(charge_status) == "EOC")) {
864 | id(charge_status) = "Float";
865 | }
866 | // No Float => Wait
867 | else if ((!id(switch_chg_float).state) & (id(charge_status) == "Float")) {
868 | id(charge_status) = "Wait";
869 | }
870 | }
871 | // Charge OFF
872 | else id(charge_status) = "Wait";
873 |
874 | // +--------------------------------------+
875 | // | Charge values |
876 | // +--------------------------------------+
877 |
878 | // Bulk Charge
879 | if ((id(charge_status) == "Bulk") | (id(charge_status) == "Force Bulk") | (id(charge_status) == "Absorption")) {
880 | id(charging_v) = id(bulk_voltage).state;
881 | id(charging_a) = id(charging_current).state;
882 | }
883 | // Float Charge
884 | else if (id(charge_status) == "Float") {
885 | if (id(total_voltage).state > id(float_voltage).state){
886 | id(charging_v) = round(id(total_voltage).state * 10)/10; // Actual battery voltage
887 | id(charging_a) = 0;
888 | } else {
889 | id(charging_v) = id(float_voltage).state;
890 | id(charging_a) = id(charging_current).state;
891 | }
892 | }
893 | // End Of Charge (EOC) or Wait : Stop Charging
894 | else if ((id(charge_status) == "EOC") | (id(charge_status) == "Wait")) {
895 | id(charging_v) = round(id(total_voltage).state * 10)/10; // Actual battery voltage
896 | id(charging_a) = 0;
897 | }
898 |
899 | // +--------------------------------------+
900 | // | Discharge values |
901 | // +--------------------------------------+
902 |
903 | // Stop Discharging if BMS or ESP32 switch is OFF
904 | if ((!id(discharging_switch).state) | (!id(switch_discharging).state)) id(discharging_a) = 0;
905 | // Stop Discharging if battery voltage is low
906 | else if (id(total_voltage).state <= ${min_discharge_v}) id(discharging_a) = 0;
907 | // Discharging is OK
908 | else id(discharging_a) = ${discharge_a};
909 |
910 | // +--------------------------------------+
911 | // | Alarm overwrite values |
912 | // +--------------------------------------+
913 |
914 | ESP_LOGI("main", "Alarm Status : %s", id(alarm_status).c_str());
915 |
916 | // Alarm : Stop Charging and Discharging
917 | if ((id(alarm_status) == "OTP") | (id(alarm_status) == "BMS")){
918 | id(charging_v) = 51.2;
919 | id(charging_a) = 0;
920 | id(discharging_a) = 0;
921 | }
922 | // Alarm : Stop Charging
923 | else if ((id(alarm_status) == "OVP") | (id(alarm_status) == "UTP") | (id(alarm_status) == "COCP")){
924 | id(charging_v) = 51.2;
925 | id(charging_a) = 0;
926 | }
927 | // Alarm : Stop Discharging
928 | else if ((id(alarm_status) == "UVP") | (id(alarm_status) == "DOCP")){
929 | id(discharging_a) = 0;
930 | }
931 |
932 | // +--------------------------------------+
933 | // | CAN messages |
934 | // +--------------------------------------+
935 |
936 | // Byte [00:01] = CVL : Charge Limit Voltage
937 | // Byte [02:03] = CCL : Charge Limit Current
938 | // Byte [04:05] = DCL : Discharge Limit Current
939 | // Byte [06:07] = DVL : Discharge Limit Voltage
940 |
941 | uint8_t can_mesg[8];
942 |
943 | can_mesg[0] = uint16_t(id(charging_v) * 10) & 0xff;
944 | can_mesg[1] = uint16_t(id(charging_v) * 10) >> 8 & 0xff;
945 | can_mesg[2] = uint16_t(id(charging_a) * 10) & 0xff;
946 | can_mesg[3] = uint16_t(id(charging_a) * 10) >> 8 & 0xff;
947 | can_mesg[4] = uint16_t(id(discharging_a) * 10) & 0xff;
948 | can_mesg[5] = uint16_t(id(discharging_a) * 10) >> 8 & 0xff;
949 | can_mesg[6] = uint16_t(${min_discharge_v} * 10) & 0xff;
950 | can_mesg[7] = uint16_t(${min_discharge_v} * 10) >> 8 & 0xff;
951 |
952 | // Publish text sensor
953 | id(charging_status).publish_state(id(charge_status));
954 |
955 | // Logs
956 | ESP_LOGI("main", "send can id: 0x351 hex: %x %x %x %x %x %x %x %x", can_mesg[0], can_mesg[1], can_mesg[2], can_mesg[3], can_mesg[4], can_mesg[5], can_mesg[6], can_mesg[7]);
957 | ESP_LOGI("main", "Charge Status : %s", id(charge_status).c_str());
958 | return {can_mesg[0], can_mesg[1], can_mesg[2], can_mesg[3], can_mesg[4], can_mesg[5], can_mesg[6], can_mesg[7]};
959 |
960 | - if:
961 | condition:
962 | lambda: return id(can_msg_counter) == 3;
963 | then:
964 | canbus.send: # Actual State of Charge (SOC) / State of Health (SOH)
965 | can_id: 0x355
966 | data: !lambda |-
967 | int soh = round(((id(charging_cycles).state/${max_cycles})-1)*-100);
968 | uint8_t can_mesg[4];
969 | can_mesg[0] = uint16_t(id(capacity_remaining).state) & 0xff;
970 | can_mesg[1] = uint16_t(id(capacity_remaining).state) >> 8 & 0xff;
971 | can_mesg[2] = soh & 0xff;
972 | can_mesg[3] = soh >> 8 & 0xff;
973 | ESP_LOGI("main", "send can id: 0x355 hex: %x %x %x %x", can_mesg[0], can_mesg[1], can_mesg[2], can_mesg[3]);
974 | return {can_mesg[0], can_mesg[1], can_mesg[2], can_mesg[3]};
975 |
976 | - if:
977 | condition:
978 | lambda: return id(can_msg_counter) == 4;
979 | then:
980 | canbus.send: # Actual Voltage / Current / Temperature / Cycles (Deye 0x305 ACK)
981 | can_id: 0x356
982 | data: !lambda |-
983 |
984 | // Byte [00:01] : Actual Voltage
985 | // Byte [02:03] : Actual Current
986 | // Byte [04:05] : Actual Temperature
987 | // Byte [06:07] : Actual Cycles number (Sofar)
988 |
989 | uint8_t can_mesg[8];
990 | can_mesg[0] = uint16_t(id(total_voltage).state * 100) & 0xff;
991 | can_mesg[1] = uint16_t(id(total_voltage).state * 100) >> 8 & 0xff;
992 | can_mesg[2] = int16_t(id(current).state * 10) & 0xff;
993 | can_mesg[3] = int16_t(id(current).state * 10) >> 8 & 0xff;
994 | can_mesg[4] = int16_t(max(id(temperature_sensor_1).state, id(temperature_sensor_2).state)* 10) & 0xff;
995 | can_mesg[5] = int16_t(max(id(temperature_sensor_1).state, id(temperature_sensor_2).state)* 10) >> 8 & 0xff;
996 | can_mesg[6] = uint16_t(id(charging_cycles).state) & 0xff;
997 | can_mesg[7] = uint16_t(id(charging_cycles).state) >> 8 & 0xff;
998 | ESP_LOGI("main", "send can id: 0x356 hex: %x %x %x %x %x %x %x %x", can_mesg[0], can_mesg[1], can_mesg[2], can_mesg[3], can_mesg[4], can_mesg[5], can_mesg[6], can_mesg[7]);
999 | return {can_mesg[0], can_mesg[1], can_mesg[2], can_mesg[3], can_mesg[4], can_mesg[5], can_mesg[6], can_mesg[7]};
1000 |
1001 | - if:
1002 | condition:
1003 | lambda: return ((id(can_msg_counter) == 5) & ((${can_protocol} == 1) | (${can_protocol} == 2)));
1004 | then:
1005 | canbus.send: # Request flag to Enable/Disable: Charge, Discharge ( Pylontech / Goodwe / Seplos )
1006 | can_id: 0x35C
1007 | data: !lambda |-
1008 | uint8_t can_mesg[2];
1009 | can_mesg[0] = 0x00;
1010 | can_mesg[1] = 0x00;
1011 |
1012 | // Bit 7 : Charge enable
1013 | if ((id(charging_switch).state) & (id(switch_charging).state))
1014 | can_mesg[0] = 0x80;
1015 |
1016 | // Bit 6 : Discharge enable
1017 | if ((id(discharging_switch).state) & (id(switch_discharging).state))
1018 | can_mesg[0] = can_mesg[0] | 0x40;
1019 |
1020 | ESP_LOGI("main", "send can id: 0x35C hex: %x %x", can_mesg[0], can_mesg[1]);
1021 | return {can_mesg[0], can_mesg[1]};
1022 |
1023 | - if:
1024 | condition:
1025 | lambda: return ((id(can_msg_counter) == 6) & (${can_protocol} == 2));
1026 | then:
1027 | canbus.send: # Actual Max Cell Temp, Min Cell Temp, Max Cell V, Min Cell V ( Pylontech / Goodwe / Seplos )
1028 | can_id: 0x70
1029 | data: !lambda |-
1030 |
1031 | // Byte [00:01] : Max cell temperature
1032 | // Byte [02:03] : Min cell temperature
1033 | // Byte [04:05] : Max cell voltage
1034 | // Byte [06:07] : Min cell voltage
1035 |
1036 | int max_cell_voltage_i = id(max_cell_voltage).state * 100.0;
1037 | int min_cell_voltage_i = id(min_cell_voltage).state * 100.0;
1038 | uint8_t can_mesg[8];
1039 | can_mesg[0] = int16_t(max(id(temperature_sensor_1).state, id(temperature_sensor_2).state)* 10) & 0xff;
1040 | can_mesg[1] = int16_t(max(id(temperature_sensor_1).state, id(temperature_sensor_2).state)* 10) >> 8 & 0xff;
1041 | can_mesg[2] = int16_t(min(id(temperature_sensor_1).state, id(temperature_sensor_2).state)* 10) & 0xff;
1042 | can_mesg[3] = int16_t(min(id(temperature_sensor_1).state, id(temperature_sensor_2).state)* 10) >> 8 & 0xff;
1043 | can_mesg[4] = max_cell_voltage_i & 0xff;
1044 | can_mesg[5] = max_cell_voltage_i >> 8 & 0xff;
1045 | can_mesg[6] = min_cell_voltage_i & 0xff;
1046 | can_mesg[7] = min_cell_voltage_i >> 8 & 0xff;
1047 | ESP_LOGI("main", "send can id: 0x70 hex: %x %x %x %x %x %x %x %x", can_mesg[0], can_mesg[1], can_mesg[2], can_mesg[3], can_mesg[4], can_mesg[5], can_mesg[6], can_mesg[7]);
1048 | return {can_mesg[0], can_mesg[1], can_mesg[2], can_mesg[3], can_mesg[4], can_mesg[5], can_mesg[6], can_mesg[7]};
1049 |
1050 | - if:
1051 | condition:
1052 | lambda: return ((id(can_msg_counter) == 6) & (${can_protocol} == 2));
1053 | then:
1054 | - canbus.send: # Actual Max Cell Temp ID, Min Cell Temp ID, Max Cell V ID, Min Cell ID ( Pylontech / Goodwe / Seplos )
1055 | can_id: 0x371
1056 | data: !lambda |-
1057 |
1058 | // Byte [00:01] : Max cell temperature ID
1059 | // Byte [02:03] : Min cell temperature ID
1060 | // Byte [04:05] : Max cell voltage ID
1061 | // Byte [06:07] : Min cell voltage ID
1062 |
1063 | uint8_t can_mesg[8];
1064 |
1065 | // Min-Max Temp. Sensor ID ?
1066 | if (id(temperature_sensor_1).state >= id(temperature_sensor_2).state){
1067 | can_mesg[0] = 0x01;
1068 | can_mesg[2] = 0x02;
1069 | }
1070 | else {
1071 | can_mesg[0] = 0x02;
1072 | can_mesg[2] = 0x01;
1073 | }
1074 |
1075 | can_mesg[1] = 0x00;
1076 | can_mesg[3] = 0x00;
1077 | can_mesg[4] = uint16_t(id(max_voltage_cell).state) & 0xff;
1078 | can_mesg[5] = uint16_t(id(max_voltage_cell).state) >> 8 & 0xff;
1079 | can_mesg[6] = uint16_t(id(min_voltage_cell).state) & 0xff;
1080 | can_mesg[7] = uint16_t(id(min_voltage_cell).state) >> 8 & 0xff;
1081 | ESP_LOGI("main", "send can id: 0x371 hex: %x %x %x %x %x %x %x %x", can_mesg[0], can_mesg[1], can_mesg[2], can_mesg[3], can_mesg[4], can_mesg[5], can_mesg[6], can_mesg[7]);
1082 | return {can_mesg[0], can_mesg[1], can_mesg[2], can_mesg[3], can_mesg[4], can_mesg[5], can_mesg[6], can_mesg[7]};
1083 |
1084 | - if:
1085 | condition:
1086 | lambda: return ((id(can_msg_counter) == 7) & (${can_protocol} == 4));
1087 | then:
1088 | - canbus.send: # Battery modules information ( Victron )
1089 | can_id: 0x372
1090 | data: !lambda |-
1091 |
1092 | // Byte [00:01] : Nbr. of battery modules online
1093 | // Byte [02:03] : Nbr. of modules blocking charge
1094 | // Byte [04:05] : Nbr. of modules blocking discharge
1095 | // Byte [06:07] : Nbr. of battery modules offline
1096 |
1097 | uint8_t can_mesg[] = {0, 0, 0, 0, 0, 0, 0, 0};
1098 | can_mesg[0] = 0x01;
1099 |
1100 | ESP_LOGI("main", "send can id: 0x372 hex: %x %x %x %x %x %x %x %x", can_mesg[0], can_mesg[1], can_mesg[2], can_mesg[3], can_mesg[4], can_mesg[5], can_mesg[6], can_mesg[7]);
1101 | return {can_mesg[0], can_mesg[1], can_mesg[2], can_mesg[3], can_mesg[4], can_mesg[5], can_mesg[6], can_mesg[7]};
1102 |
1103 | - if:
1104 | condition:
1105 | lambda: return ((id(can_msg_counter) == 8) & (${can_protocol} == 4));
1106 | then:
1107 | canbus.send: # Actual Min Cell V, Max Cell V, Min Cell Temp (Kelvin), Max Cell Temp (Kelvin) ( Victron )
1108 | can_id: 0x373
1109 | data: !lambda |-
1110 |
1111 | // Byte [00:01] : Min cell voltage
1112 | // Byte [02:03] : Max cell voltage
1113 | // Byte [04:05] : Min cell temperature
1114 | // Byte [06:07] : Max cell temperature
1115 |
1116 | int min_cell_voltage_i = id(min_cell_voltage).state * 1000.0;
1117 | int max_cell_voltage_i = id(max_cell_voltage).state * 1000.0;
1118 | int min_temp_kelvin = min(id(temperature_sensor_1).state, id(temperature_sensor_2).state) + 273.15;
1119 | int max_temp_kelvin = max(id(temperature_sensor_1).state, id(temperature_sensor_2).state) + 273.15;
1120 |
1121 | uint8_t can_mesg[8];
1122 | can_mesg[0] = min_cell_voltage_i & 0xff;
1123 | can_mesg[1] = min_cell_voltage_i >> 8 & 0xff;
1124 | can_mesg[2] = max_cell_voltage_i & 0xff;
1125 | can_mesg[3] = max_cell_voltage_i >> 8 & 0xff;
1126 | can_mesg[4] = min_temp_kelvin & 0xff;
1127 | can_mesg[5] = min_temp_kelvin >> 8 & 0xff;
1128 | can_mesg[6] = max_temp_kelvin & 0xff;
1129 | can_mesg[7] = max_temp_kelvin >> 8 & 0xff;
1130 |
1131 | ESP_LOGI("main", "send can id: 0x373 hex: %x %x %x %x %x %x %x %x", can_mesg[0], can_mesg[1], can_mesg[2], can_mesg[3], can_mesg[4], can_mesg[5], can_mesg[6], can_mesg[7]);
1132 | return {can_mesg[0], can_mesg[1], can_mesg[2], can_mesg[3], can_mesg[4], can_mesg[5], can_mesg[6], can_mesg[7]};
1133 |
1134 | - if:
1135 | condition:
1136 | lambda: return ((id(can_msg_counter) == 8) & (${can_protocol} == 4));
1137 | then:
1138 | - canbus.send: # Min cell voltage ID [ASCII] ( Victron )
1139 | can_id: 0x374
1140 | data: !lambda |-
1141 |
1142 | int cell_id = id(min_voltage_cell).state;
1143 |
1144 | ESP_LOGI("main", "send can id: 0x374 [ASCII] Min cell voltage ID : %i", cell_id);
1145 |
1146 | if (cell_id == 1) return {0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1147 | else if (cell_id == 2) return {0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1148 | else if (cell_id == 3) return {0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1149 | else if (cell_id == 4) return {0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1150 | else if (cell_id == 5) return {0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1151 | else if (cell_id == 6) return {0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1152 | else if (cell_id == 7) return {0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1153 | else if (cell_id == 8) return {0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1154 | else if (cell_id == 9) return {0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1155 | else if (cell_id == 10) return {0x31, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1156 | else if (cell_id == 11) return {0x31, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1157 | else if (cell_id == 12) return {0x31, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1158 | else if (cell_id == 13) return {0x31, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1159 | else if (cell_id == 14) return {0x31, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1160 | else if (cell_id == 15) return {0x31, 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1161 | else if (cell_id == 16) return {0x31, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1162 | else return {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1163 |
1164 | - if:
1165 | condition:
1166 | lambda: return ((id(can_msg_counter) == 8) & (${can_protocol} == 4));
1167 | then:
1168 | - canbus.send: # Max cell voltage ID [ASCII] ( Victron )
1169 | can_id: 0x375
1170 | data: !lambda |-
1171 |
1172 | int cell_id = id(max_voltage_cell).state;
1173 |
1174 | ESP_LOGI("main", "send can id: 0x375 [ASCII] Max cell voltage ID : %i", cell_id);
1175 |
1176 | if (cell_id == 1) return {0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1177 | else if (cell_id == 2) return {0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1178 | else if (cell_id == 3) return {0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1179 | else if (cell_id == 4) return {0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1180 | else if (cell_id == 5) return {0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1181 | else if (cell_id == 6) return {0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1182 | else if (cell_id == 7) return {0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1183 | else if (cell_id == 8) return {0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1184 | else if (cell_id == 9) return {0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1185 | else if (cell_id == 10) return {0x31, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1186 | else if (cell_id == 11) return {0x31, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1187 | else if (cell_id == 12) return {0x31, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1188 | else if (cell_id == 13) return {0x31, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1189 | else if (cell_id == 14) return {0x31, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1190 | else if (cell_id == 15) return {0x31, 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1191 | else if (cell_id == 16) return {0x31, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1192 | else return {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1193 |
1194 | - if:
1195 | condition:
1196 | lambda: return ((id(can_msg_counter) == 8) & (${can_protocol} == 4));
1197 | then:
1198 | - canbus.send: # Min cell temperature ID [ASCII] ( Victron )
1199 | can_id: 0x376
1200 | data: !lambda |-
1201 |
1202 | // Min Temp. Sensor ID ?
1203 | if (id(temperature_sensor_1).state >= id(temperature_sensor_2).state){
1204 | ESP_LOGI("main", "send can id: 0x376 [ASCII] Min Temp. Sensor ID : 2");
1205 | return {0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1206 | }
1207 | else {
1208 | ESP_LOGI("main", "send can id: 0x376 [ASCII] Min Temp. Sensor ID : 1");
1209 | return {0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1210 | }
1211 |
1212 | - if:
1213 | condition:
1214 | lambda: return ((id(can_msg_counter) == 8) & (${can_protocol} == 4));
1215 | then:
1216 | - canbus.send: # Max cell temperature ID [ASCII] ( Victron )
1217 | can_id: 0x377
1218 | data: !lambda |-
1219 |
1220 | // Max Temp. Sensor ID ?
1221 | if (id(temperature_sensor_1).state >= id(temperature_sensor_2).state){
1222 | ESP_LOGI("main", "send can id: 0x377 [ASCII] Max Temp. Sensor ID : 1");
1223 | return {0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1224 | }
1225 | else {
1226 | ESP_LOGI("main", "send can id: 0x377 [ASCII] Max Temp. Sensor ID : 2");
1227 | return {0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1228 | }
1229 |
1230 | - if:
1231 | condition:
1232 | lambda: return ((id(can_msg_counter) == 9) & ((${can_protocol} == 2) | (${can_protocol} == 4)));
1233 | then:
1234 | - canbus.send: # Battery Installed Capacity Ah ( Victron, Sol-Ark, Luxpower )
1235 | can_id: 0x379
1236 | data: !lambda |-
1237 | uint8_t can_mesg[] = {0, 0, 0, 0, 0, 0, 0, 0};
1238 | can_mesg[0] = uint16_t(id(total_battery_capacity_setting).state) & 0xff;
1239 | can_mesg[1] = uint16_t(id(total_battery_capacity_setting).state) >> 8 & 0xff;
1240 |
1241 | ESP_LOGI("main", "send can id: 0x379 hex: %x %x %x %x %x %x %x %x", can_mesg[0], can_mesg[1], can_mesg[2], can_mesg[3], can_mesg[4], can_mesg[5], can_mesg[6], can_mesg[7]);
1242 | return {can_mesg[0], can_mesg[1], can_mesg[2], can_mesg[3], can_mesg[4], can_mesg[5], can_mesg[6], can_mesg[7]};
1243 |
1244 | - if:
1245 | condition:
1246 | lambda: return ((id(can_msg_counter) == 10) & (${can_protocol} == 4));
1247 | then:
1248 | - canbus.send: # Product identification [ASCII] ( Victron )
1249 | can_id: 0x382
1250 | data: !lambda |-
1251 | ESP_LOGI("main", "send can id: 0x382 [ASCII] Product : JK-BMS");
1252 | return {0x4A, 0x4B, 0x2D, 0x42, 0x4D, 0x53, 0x00, 0x00}; // JK-BMS
1253 |
1254 | - if:
1255 | condition:
1256 | lambda: return ((id(can_msg_counter) == 11) & ((${can_protocol} == 3) | (${can_protocol} == 4)));
1257 | then:
1258 | - canbus.send: # Battery information ( SMA, Victron )
1259 | can_id: 0x35F
1260 | data: !lambda |-
1261 |
1262 | // SMA Victron
1263 | // Byte [00:01] : Bat-Type Product ID
1264 | // Byte [02:03] : BMS Version Firmware version (1.16 => HEX [01:10])
1265 | // Byte [04:05] : Bat-Capacity Available Capacity Ah
1266 | // Byte [06:07] : Manufacturer ID Hardware version
1267 |
1268 | uint8_t can_mesg[] = {0, 0, 0, 0, 0, 0, 0, 0};
1269 | can_mesg[2] = 0x01;
1270 | can_mesg[3] = 0x10;
1271 | can_mesg[4] = uint16_t(id(total_battery_capacity_setting).state) & 0xff;
1272 | can_mesg[5] = uint16_t(id(total_battery_capacity_setting).state) >> 8 & 0xff;
1273 |
1274 | ESP_LOGI("main", "send can id: 0x35F hex: %x %x %x %x %x %x %x %x", can_mesg[0], can_mesg[1], can_mesg[2], can_mesg[3], can_mesg[4], can_mesg[5], can_mesg[6], can_mesg[7]);
1275 | return {can_mesg[0], can_mesg[1], can_mesg[2], can_mesg[3], can_mesg[4], can_mesg[5], can_mesg[6], can_mesg[7]};
1276 |
1277 | - if:
1278 | condition:
1279 | lambda: return id(can_msg_counter) == 12;
1280 | then:
1281 | - canbus.send:
1282 | can_id: 0x35E # Manufacturer name
1283 | data: !lambda |-
1284 | if (${can_bms_name} == 1){
1285 | ESP_LOGI("main", "send can id: 0x35E ASCII : PYLON");
1286 | return {0x50, 0x59, 0x4C, 0x4F, 0x4E, 0x20, 0x20, 0x20}; // PYLON ( recognized by Deye, display PYLON name and SOH )
1287 | }
1288 | else if (${can_bms_name} == 2){
1289 | ESP_LOGI("main", "send can id: 0x35E ASCII : GOODWE");
1290 | return {0x47, 0x4F, 0x4F, 0x44, 0x57, 0x45, 0x20, 0x20}; // GOODWE
1291 | }
1292 | else if (${can_bms_name} == 3){
1293 | ESP_LOGI("main", "send can id: 0x35E ASCII : SHEnergy");
1294 | return {0x53, 0x48, 0x45, 0x6E, 0x65, 0x72, 0x67, 0x79}; // SHEnergy (SEPLOS)
1295 | }
1296 | # Reset counter
1297 | - lambda: id(can_msg_counter) = 0;
1298 |
--------------------------------------------------------------------------------
/old_version/esp32_ble_jk-bms-can.yaml:
--------------------------------------------------------------------------------
1 | # JK-BMS-CAN ( PYLON, Seplos, GoodWe, SMA and Victron CAN bus protocol )
2 |
3 | # esp32_ble_jk-bms-can.yaml is free software: you can redistribute it
4 | # and/or modify it under the terms of the GNU General Public License
5 | # as published by the Free Software Foundation, either version 3
6 | # of the License, or (at your option) any later version.
7 | #
8 | # This program is distributed in the hope that it will be useful,
9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11 | # See the GNU General Public License for more details.
12 | #
13 | # You should have received a copy of the GNU General Public License
14 | # along with this program. If not, see .
15 |
16 | # V1.16.4 Sleeper85 : Improved Charging Logic for ESP32 startup/reboot and Float charge, Add CAN ID 0x356 bytes [06:07] cycles for Sofar, Change switch name
17 | # V1.16.3 Sleeper85 : ID 0x379 will be sent when choosing protocol 2 or 4 (Battery Capacity for Victron, Sol-Ark and Luxpower)
18 | # V1.16.2 Sleeper85 : Split the "Charge/Discharge values" section and added instructions for "Stop Discharging" + Set "esp-idf" framework by default
19 | # V1.16.1 Sleeper85 : Slider charging_current max value = ${charge_a}, Improved Alarm/Charging/Discharging Logic, Improved CAN protocol and Victron support
20 | # V1.15.5 Sleeper85 : Improved code and set api "reboot_timout" to "0s" by default (no reboot without HA)
21 | # V1.15.4 Sleeper85 : Improved documentation for API, Web Server and WiFi settings
22 | # V1.15.3 Sleeper85 : Add 'CAN Protocol Settings' and new CAN ID based on the SMA and Victron protocol (alpha)
23 | # V1.15.2 Sleeper85 : Improved Alarm handling, all alarms will set charge/discharge current to 0A and set 'Charging Status' to Alarm
24 | # V1.15.1 Sleeper85 : New CANBUS script with CANBUS Status in HA, stop sending CAN messages if the inverter is not responding (fix WDT reboot issues)
25 | # V1.14.3 Sleeper85 : Improved documentation + Charging Voltage tips for Deye
26 | # V1.14.2 Sleeper85 : Improve 'Charging Voltage' behavior
27 | # V1.14.1 Sleeper85 : Add 'Float charge function'
28 | # V1.13.6 Sleeper85 : Add 'Absorption time' and 'Absorption Offset V.' slider
29 | # V1.13.5 Sleeper85 : Set CAN manufacter to "PYLON" for improve compatibility with Deye and other inverters
30 | # V1.13.4 Sleeper85 : Improve 'Charge Status' behavior + add 'Rebulk Offset V.' slider
31 | # V1.13.3 uksa007 : Improve compatibility with Deye and other inverters
32 | # V1.13.2 uksa007 : Send Max Temperature of T1, T2 to inverter
33 | # V1.13.1 uksa007 : Fix compile issues with new version of ESPhome 2023.4.0, set rebulk offset to 2.5
34 |
35 | substitutions:
36 | # +--------------------------------------+
37 | # name that will appear in esphome and homeassistant.
38 | name: jk-bms-ble-can
39 | # +--------------------------------------+
40 | # | Bluetooth Settings |
41 | # +--------------------------------------+
42 | # Please use "JK02_24S" if you own a old JK-BMS < hardware version 11.0 (hardware version >= 6.0 and < 11.0)
43 | # Please use "JK02_32S" if you own a new JK-BMS >= hardware version 11.0 (f.e. JK-B2A8S20P hw 11.XW, sw 11.26)
44 | # Please use "JK04" if you have some old JK-BMS <= hardware version 3.0 (f.e. JK-B2A16S hw 3.0, sw. 3.3.0)
45 | protocol_version: JK02_32S
46 | mac_address: C8:47:8C:10:7E:AB
47 | # +--------------------------------------+
48 | # Number of Battery modules max 8. Each LX U5.4-L battery is 5.4kWh, select the number closest to your capactiy eg 3.2V * 280Ah * 16 = 14.3kWh
49 | batt_modules: "3"
50 | # +--------------------------------------+
51 | # | Battery Charge Settings |
52 | # +--------------------------------------+
53 | # Tips for Deye inverter : Add 0.1v to the settings below because the Deye charging voltage is always 0.1v lower than requested.
54 | # Float V. : 53.7v (3.35v/cell - Natural voltage of a fully charged cell at rest, I advise you not to go higher.)
55 | # Absorption V : 55.3v (3.45v/cell - It's not necessary to use a charging voltage higher than 55.2V for a full charge.)
56 | # Absorption Offset V. : 0.15v (The absorption phase will start at 55.15v (BMS voltage). Warning: the BMS voltage must be correctly calibrated.)
57 | # +--------------------------------------+
58 | # This is max charging amps eg 100A, for Bulk - Constant Current charging(CC), should be at least 10A less than BMS change current protection, 0.5C max
59 | # 100A * 50V = 5000W
60 | charge_a: "100"
61 | # Float Voltage : corresponds to the voltage at which the battery would be maintained at the end of the absorption phase. (53.6v eg 3.35v/cell for 16 cells 48V battery)
62 | float_v: "53.6"
63 | # Absorption Voltage : corresponds to the Bulk voltage that will be used to charge the battery. (55.2v eg 3.45v/cell for 16 cells 48V battery)
64 | absorption_v: "55.2"
65 | # Absorption time in minutes to hold charge voltage after charge voltage is reached eg 30
66 | absorption_time: "30"
67 | # Absorption offset, x Volts below absorption voltage battery will start the absorption timer, eg 55.2-0.05 = 52.15v
68 | absorption_offset_v: "0.05"
69 | # Rebulk offset, x Volts below absorption voltage battery will request rebulk, eg 55.2-2.5 = 52.7v
70 | rebulk_offset_v: "2.5"
71 | # +--------------------------------------+
72 | # | Battery Discharge Settings |
73 | # +--------------------------------------+
74 | # Max discharge amps eg 120, should be at least 10A less than BMS over discharge current protection, 0.5C max
75 | # 120A * 50V = 6000W
76 | discharge_a: "120"
77 | # Minimum discharge voltage eg 48v/16 = 3V per cell
78 | min_discharge_v: "48"
79 | # +--------------------------------------+
80 | # | Battery State of Health (SOH) |
81 | # +--------------------------------------+
82 | # Maximum charging cycles is used to calculate the battey SOH, LF280K v3 =8000.0, LF280K v2 =6000.0, LF280=3000.0 (decimal is required)
83 | max_cycles: "6000.0"
84 | # +--------------------------------------+
85 | # | CAN Protocol Settings |
86 | # +--------------------------------------+
87 | # CAN BMS Name (0x35E) : 0 NoSent / 1 PYLON / 2 GOODWE / 3 SEPLOS
88 | can_bms_name: "1"
89 | # CAN Protocol
90 | # 1 : PYLON 1.2 (Deye)
91 | # 2 : SEPLOS 1.0, PYLON 1.3, GOODWE 1.5 (GoodWe, Sol-Ark, Luxpower)
92 | # 3 : SMA (Sunny Island)
93 | # 4 : VICTRON
94 | can_protocol: "1"
95 | # +--------------------------------------+
96 | # | ESP32 CAN port pins |
97 | # +--------------------------------------+
98 | # GPIO pins your CAN bus transceiver (TJA1050, TJA1051T or SN65HVD230) is connected to the ESP, note! TX->TX and RX->RX.
99 | can_tx_pin: GPIO23
100 | can_rx_pin: GPIO22
101 |
102 | # +------------------------------------------------------------------+
103 | # | ** The settings below can be modified according to your needs ** |
104 | # +------------------------------------------------------------------+
105 | external_components_source: github://syssi/esphome-jk-bms@main
106 | # components
107 | # github://syssi/esphome-jk-bms@main
108 |
109 | esphome:
110 | name: ${name}
111 | on_boot:
112 | then:
113 | - switch.turn_on: switch_charging
114 | - switch.turn_on: switch_discharging
115 | - switch.turn_on: switch_chg_float
116 |
117 | # +--------------------------------------+
118 | # | ESP32 settings |
119 | # +--------------------------------------+
120 | # For a stable Bluetooth connection keep the "esp-idf" framework
121 | esp32:
122 | board: esp32doit-devkit-v1
123 | framework:
124 | type: esp-idf
125 |
126 | external_components:
127 | - source: ${external_components_source}
128 | refresh: 0s
129 |
130 | logger:
131 | # level: DEBUG
132 |
133 | ota:
134 | on_begin:
135 | then:
136 | - lambda: id(enable_bluetooth_connection).turn_off();
137 | - logger.log: "BLE shutdown for flashing"
138 |
139 | # Please use the native `api` component instead of the `mqtt` section.
140 | # If you use Home Assistant, the native API is more lightweight.
141 | # If there is no HA server connected to this API, the ESP32 reboots every 15 minutes to try to resolve the problem.
142 | # If you don't use Home Assistant please uncomment the "reboot_timeout: 0s" option.
143 | api:
144 | reboot_timeout: 0s
145 |
146 | # If you don't want to use ESPHome's native API you can use MQQT instead.
147 | # In this case don't forget to remove the 'api:' section.
148 | # mqtt:
149 | # broker: !secret mqtt_host
150 | # username: !secret mqtt_username
151 | # password: !secret mqtt_password
152 | # id: mqtt_client
153 |
154 | # In the event of problems with the WiFi network, the ESP32 will reboot every 15 minutes to try to resolve the problem.
155 | # If we don't want to connect the ESP32 to the WiFi network please remove the 4 lines below.
156 | wifi:
157 | ssid: !secret wifi_ssid
158 | password: !secret wifi_password
159 | domain: !secret domain
160 |
161 | #web_server:
162 | # port: 80
163 | # log: false
164 | # ota: false
165 |
166 | # +--------------------------------------+
167 | # | ** Don't make changes below this ** |
168 | # +--------------------------------------+
169 |
170 | globals:
171 | - id: can_ack_counter
172 | type: int
173 | restore_value: no
174 | initial_value: '0'
175 | - id: charge_status
176 | type: std::string
177 | restore_value: no
178 | initial_value: '"Wait"'
179 | - id: can_status
180 | type: std::string
181 | restore_value: no
182 | initial_value: '"OFF"'
183 | - id: alarm_status
184 | type: std::string
185 | restore_value: no
186 | initial_value: '"NoAlarm"'
187 | - id: charging_v
188 | type: float
189 | restore_value: no
190 | initial_value: '0.0'
191 | - id: charging_a
192 | type: int
193 | restore_value: no
194 | initial_value: '0'
195 | - id: discharging_a
196 | type: int
197 | restore_value: no
198 | initial_value: '0'
199 | - id: can_msg_counter
200 | type: int
201 | restore_value: no
202 | initial_value: '0'
203 |
204 | output:
205 | - platform: gpio
206 | pin: 2
207 | id: led
208 | inverted: true
209 |
210 | light:
211 | - platform: binary
212 | output: led
213 | id: blue_led
214 | name: "Blue LED"
215 | internal: true
216 |
217 | # +--------------------------------------+
218 | # | JK-BMS BLE connection |
219 | # +--------------------------------------+
220 |
221 | esp32_ble_tracker:
222 | on_ble_advertise:
223 | then:
224 | - lambda: |-
225 | if (x.get_name().rfind("JK-", 0) == 0) {
226 | ESP_LOGI("ble_adv", "New JK-BMS found");
227 | ESP_LOGI("ble_adv", " Name: %s", x.get_name().c_str());
228 | ESP_LOGI("ble_adv", " MAC address: %s", x.address_str().c_str());
229 | ESP_LOGD("ble_adv", " Advertised service UUIDs:");
230 | for (auto uuid : x.get_service_uuids()) {
231 | ESP_LOGD("ble_adv", " - %s", uuid.to_string().c_str());
232 | }
233 | }
234 |
235 | ble_client:
236 | - mac_address: ${mac_address}
237 | id: client0
238 |
239 | jk_bms_ble:
240 | - ble_client_id: client0
241 | protocol_version: ${protocol_version}
242 | throttle: 5s
243 | id: bms0
244 | # enable_fake_traffic: true
245 |
246 | # +--------------------------------------+
247 |
248 | binary_sensor:
249 | - platform: jk_bms_ble
250 | balancing:
251 | name: "${name} BMS Balancing"
252 | charging:
253 | name: "${name} BMS Charging"
254 | discharging:
255 | name: "${name} BMS Discharging"
256 | online_status:
257 | name: "${name} Online Status"
258 |
259 | button:
260 | - platform: jk_bms_ble
261 | retrieve_settings:
262 | name: "${name} retrieve settings"
263 | retrieve_device_info:
264 | name: "${name} retrieve device info"
265 |
266 | number:
267 | - platform: jk_bms_ble
268 | jk_bms_ble_id: bms0
269 | balance_trigger_voltage:
270 | name: "${name} balance trigger voltage"
271 | cell_count:
272 | name: "${name} cell count"
273 | total_battery_capacity:
274 | name: "${name} total battery capacity"
275 | cell_voltage_overvoltage_protection:
276 | name: "${name} cell voltage overvoltage protection"
277 | cell_voltage_overvoltage_recovery:
278 | name: "${name} cell voltage overvoltage recovery"
279 | cell_voltage_undervoltage_protection:
280 | name: "${name} cell voltage undervoltage protection"
281 | cell_voltage_undervoltage_recovery:
282 | name: "${name} cell voltage undervoltage recovery"
283 | balance_starting_voltage:
284 | name: "${name} balance starting voltage"
285 | voltage_calibration:
286 | name: "${name} voltage calibration"
287 | current_calibration:
288 | name: "${name} current calibration"
289 | power_off_voltage:
290 | name: "${name} power off voltage"
291 | max_balance_current:
292 | name: "${name} max balance current"
293 | max_charge_current:
294 | name: "${name} max charge current"
295 | max_discharge_current:
296 | name: "${name} max discharge current"
297 | # +--------------------------------------+
298 | # | Slider |
299 | # +--------------------------------------+
300 | - platform: template
301 | name: "${name} Bulk voltage"
302 | id: "bulk_voltage"
303 | step: 0.1
304 | min_value: 52.8
305 | max_value: 57.6
306 | mode: slider
307 | initial_value: "${absorption_v}"
308 | unit_of_measurement: V
309 | icon: mdi:battery-charging
310 | optimistic: true
311 | - platform: template
312 | name: "${name} Float voltage"
313 | id: "float_voltage"
314 | step: 0.1
315 | min_value: 52.8
316 | max_value: 57.6
317 | mode: slider
318 | initial_value: "${float_v}"
319 | unit_of_measurement: V
320 | icon: mdi:battery-charging
321 | optimistic: true
322 | - platform: template
323 | name: "${name} Charging current max"
324 | id: "charging_current"
325 | step: 1
326 | min_value: 0
327 | max_value: "${charge_a}"
328 | mode: slider
329 | initial_value: "${charge_a}"
330 | unit_of_measurement: A
331 | icon: mdi:current-dc
332 | optimistic: true
333 | - platform: template
334 | name: "${name} Rebulk Offset V."
335 | id: "rebulk_offset"
336 | step: 0.1
337 | min_value: 0
338 | max_value: 5
339 | mode: slider
340 | initial_value: "${rebulk_offset_v}"
341 | unit_of_measurement: V
342 | icon: mdi:sine-wave
343 | optimistic: true
344 | - platform: template
345 | name: "${name} Absorption time"
346 | id: "absorption_time"
347 | step: 1
348 | min_value: 0
349 | max_value: 180
350 | mode: slider
351 | initial_value: "${absorption_time}"
352 | unit_of_measurement: min
353 | icon: mdi:clock-start
354 | optimistic: true
355 | - platform: template
356 | name: "${name} Absorption Offset V."
357 | id: "absorption_offset"
358 | step: 0.05
359 | min_value: 0
360 | max_value: 1
361 | mode: slider
362 | initial_value: "${absorption_offset_v}"
363 | unit_of_measurement: V
364 | icon: mdi:sine-wave
365 | optimistic: true
366 |
367 | sensor:
368 | - platform: jk_bms_ble
369 | jk_bms_ble_id: bms0
370 | min_cell_voltage:
371 | id: min_cell_voltage
372 | name: "${name} min cell voltage"
373 | max_cell_voltage:
374 | id: max_cell_voltage
375 | name: "${name} max cell voltage"
376 | min_voltage_cell:
377 | id: min_voltage_cell
378 | name: "${name} min voltage cell"
379 | max_voltage_cell:
380 | id: max_voltage_cell
381 | name: "${name} max voltage cell"
382 | delta_cell_voltage:
383 | name: "${name} delta cell voltage"
384 | average_cell_voltage:
385 | name: "${name} average cell voltage"
386 | cell_voltage_1:
387 | name: "${name} cell voltage 1"
388 | cell_voltage_2:
389 | name: "${name} cell voltage 2"
390 | cell_voltage_3:
391 | name: "${name} cell voltage 3"
392 | cell_voltage_4:
393 | name: "${name} cell voltage 4"
394 | cell_voltage_5:
395 | name: "${name} cell voltage 5"
396 | cell_voltage_6:
397 | name: "${name} cell voltage 6"
398 | cell_voltage_7:
399 | name: "${name} cell voltage 7"
400 | cell_voltage_8:
401 | name: "${name} cell voltage 8"
402 | cell_voltage_9:
403 | name: "${name} cell voltage 9"
404 | cell_voltage_10:
405 | name: "${name} cell voltage 10"
406 | cell_voltage_11:
407 | name: "${name} cell voltage 11"
408 | cell_voltage_12:
409 | name: "${name} cell voltage 12"
410 | cell_voltage_13:
411 | name: "${name} cell voltage 13"
412 | cell_voltage_14:
413 | name: "${name} cell voltage 14"
414 | cell_voltage_15:
415 | name: "${name} cell voltage 15"
416 | cell_voltage_16:
417 | name: "${name} cell voltage 16"
418 | # cell_voltage_17:
419 | # name: "${name} cell voltage 17"
420 | # cell_voltage_18:
421 | # name: "${name} cell voltage 18"
422 | # cell_voltage_19:
423 | # name: "${name} cell voltage 19"
424 | # cell_voltage_20:
425 | # name: "${name} cell voltage 20"
426 | # cell_voltage_21:
427 | # name: "${name} cell voltage 21"
428 | # cell_voltage_22:
429 | # name: "${name} cell voltage 22"
430 | # cell_voltage_23:
431 | # name: "${name} cell voltage 23"
432 | # cell_voltage_24:
433 | # name: "${name} cell voltage 24"
434 | cell_resistance_1:
435 | name: "${name} cell resistance 1"
436 | cell_resistance_2:
437 | name: "${name} cell resistance 2"
438 | cell_resistance_3:
439 | name: "${name} cell resistance 3"
440 | cell_resistance_4:
441 | name: "${name} cell resistance 4"
442 | cell_resistance_5:
443 | name: "${name} cell resistance 5"
444 | cell_resistance_6:
445 | name: "${name} cell resistance 6"
446 | cell_resistance_7:
447 | name: "${name} cell resistance 7"
448 | cell_resistance_8:
449 | name: "${name} cell resistance 8"
450 | cell_resistance_9:
451 | name: "${name} cell resistance 9"
452 | cell_resistance_10:
453 | name: "${name} cell resistance 10"
454 | cell_resistance_11:
455 | name: "${name} cell resistance 11"
456 | cell_resistance_12:
457 | name: "${name} cell resistance 12"
458 | cell_resistance_13:
459 | name: "${name} cell resistance 13"
460 | cell_resistance_14:
461 | name: "${name} cell resistance 14"
462 | cell_resistance_15:
463 | name: "${name} cell resistance 15"
464 | cell_resistance_16:
465 | name: "${name} cell resistance 16"
466 | # cell_resistance_17:
467 | # name: "${name} cell resistance 17"
468 | # cell_resistance_18:
469 | # name: "${name} cell resistance 18"
470 | # cell_resistance_19:
471 | # name: "${name} cell resistance 19"
472 | # cell_resistance_20:
473 | # name: "${name} cell resistance 20"
474 | # cell_resistance_21:
475 | # name: "${name} cell resistance 21"
476 | # cell_resistance_22:
477 | # name: "${name} cell resistance 22"
478 | # cell_resistance_23:
479 | # name: "${name} cell resistance 23"
480 | # cell_resistance_24:
481 | # name: "${name} cell resistance 24"
482 | total_voltage:
483 | id: total_voltage
484 | name: "${name} total voltage"
485 | current:
486 | id: current
487 | name: "${name} current"
488 | power:
489 | name: "${name} power"
490 | charging_power:
491 | name: "${name} charging power"
492 | discharging_power:
493 | name: "${name} discharging power"
494 | temperature_sensor_1:
495 | id: temperature_sensor_1
496 | name: "${name} temperature sensor 1"
497 | temperature_sensor_2:
498 | id: temperature_sensor_2
499 | name: "${name} temperature sensor 2"
500 | power_tube_temperature:
501 | id: power_tube_temperature
502 | name: "${name} power tube temperature"
503 | state_of_charge:
504 | id: state_of_charge
505 | name: "${name} state of charge"
506 | capacity_remaining:
507 | name: "${name} capacity remaining"
508 | total_battery_capacity_setting:
509 | id: total_battery_capacity_setting
510 | name: "${name} total battery capacity setting"
511 | charging_cycles:
512 | id: charging_cycles
513 | name: "${name} charging cycles"
514 | total_charging_cycle_capacity:
515 | name: "${name} total charging cycle capacity"
516 | total_runtime:
517 | name: "${name} total runtime"
518 | balancing_current:
519 | name: "${name} balancing current"
520 | errors_bitmask:
521 | id: errors_bitmask
522 | name: "${name} errors bitmask"
523 | # +--------------------------------------+
524 | # | Uptime sensor |
525 | # +--------------------------------------+
526 | - platform: uptime
527 | name: ${name} Uptime Sensor
528 | id: uptime_sensor
529 | update_interval: 60s
530 | on_raw_value:
531 | then:
532 | - text_sensor.template.publish:
533 | id: uptime_human
534 | state: !lambda |-
535 | int seconds = round(id(uptime_sensor).raw_state);
536 | int days = seconds / (24 * 3600);
537 | seconds = seconds % (24 * 3600);
538 | int hours = seconds / 3600;
539 | seconds = seconds % 3600;
540 | int minutes = seconds / 60;
541 | seconds = seconds % 60;
542 | return (
543 | (days ? to_string(days) + "d " : "") +
544 | (hours ? to_string(hours) + "h " : "") +
545 | (minutes ? to_string(minutes) + "m " : "") +
546 | (to_string(seconds) + "s")
547 | ).c_str();
548 |
549 | switch:
550 | - platform: jk_bms_ble
551 | charging:
552 | id: charging_switch
553 | name: "${name} BMS Charge switch"
554 | discharging:
555 | id: discharging_switch
556 | name: "${name} BMS Discharge switch"
557 | balancer:
558 | name: "${name} BMS Balance switch"
559 | # +--------------------------------------+
560 | - platform: ble_client
561 | ble_client_id: client0
562 | id: enable_bluetooth_connection
563 | name: "${name} Enable Bluetooth connection"
564 | # +--------------------------------------+
565 | - platform: template
566 | name: "${name} CAN Charge enabled"
567 | id: switch_charging
568 | optimistic: true
569 | - platform: template
570 | name: "${name} CAN Discharge enabled"
571 | id: switch_discharging
572 | optimistic: true
573 | - platform: template
574 | name: "${name} CAN Force bulk (top bal)"
575 | id: switch_chg_bulk
576 | optimistic: true
577 | - platform: template
578 | name: "${name} CAN Float charge enabled"
579 | id: switch_chg_float
580 | optimistic: true
581 |
582 | text_sensor:
583 | - platform: jk_bms_ble
584 | errors:
585 | name: "${name} errors"
586 | total_runtime_formatted:
587 | name: "${name} total runtime formatted"
588 | # +--------------------------------------+
589 | # | Template text sensors |
590 | # +--------------------------------------+
591 | - platform: template
592 | name: ${name} Uptime Human Readable
593 | id: uptime_human
594 | icon: mdi:clock-start
595 | - platform: template
596 | name: "${name} Charging Status"
597 | id: charging_status
598 | - platform: template
599 | name: "${name} CANBUS Status"
600 | id: canbus_status
601 |
602 | script:
603 | - id: absorption_script
604 | then:
605 | - lambda: id(charge_status) = "Absorption";
606 | # delay value in ms
607 | - delay: !lambda "return id(absorption_time).state * 60 * 1000;"
608 | - lambda: id(charge_status) = "EOC";
609 |
610 | # +--------------------------------------+
611 | # | CAN bus script |
612 | # +--------------------------------------+
613 | canbus:
614 | - platform: esp32_can
615 | tx_pin: ${can_tx_pin}
616 | rx_pin: ${can_rx_pin}
617 | can_id: 4
618 | bit_rate: 500kbps
619 | on_frame:
620 | - can_id: 0x305 # Inverter ACK - SMA/LG/Pylon/Goodwe reply
621 | then:
622 | - light.toggle:
623 | id: blue_led
624 | - lambda: |-
625 | id(can_ack_counter) = 0; // Reset ACK counter
626 | id(can_status) = "ON"; // Set CANBUS Status to ON
627 | id(canbus_status).publish_state(id(can_status)); // Publish text sensor
628 | ESP_LOGI("main", "received can id: 0x305 ACK");
629 |
630 | interval:
631 | - interval: 120s
632 | then:
633 | - lambda: id(can_ack_counter) = 0; // Reset ACK counter for test inverter ACK
634 |
635 | - interval: 100ms
636 | then:
637 | # Start CAN Handling
638 | - if:
639 | condition:
640 | lambda: |-
641 |
642 | if (id(can_ack_counter) < 20) { // Inverter ACK ? => CANBUS ON
643 |
644 | id(can_ack_counter)++; // CANBUS ACK counter ++
645 | id(can_msg_counter)++; // CANBUS MSG counter ++
646 | return true; // Condition OK
647 |
648 | }
649 | else if (id(can_status) == "OFF") { // CANBUS already OFF ?
650 |
651 | return false; // Nothing to do
652 |
653 | }
654 | else {
655 |
656 | id(can_status) = "OFF"; // Set CANBUS Status to OFF
657 | id(canbus_status).publish_state(id(can_status)); // Publish text sensor
658 | ESP_LOGI("main", "No rx can 0x305 reply, Inverter not connected/responding...");
659 | return false; // Condition NOK
660 |
661 | }
662 |
663 | then:
664 | - if:
665 | condition:
666 | lambda: return ((id(can_msg_counter) == 1) & ((${can_protocol} == 1) | (${can_protocol} == 2)));
667 | then:
668 | canbus.send: # Protection Alarms, Warning and Flags ( Pylontech / Goodwe / Seplos )
669 | can_id: 0x359
670 | data: !lambda |-
671 |
672 | // +---------------------------+
673 | // | JK-BMS errors bitmask |
674 | // +---------------------------+
675 |
676 | // 0x8B 0x00 0x00: Battery warning message 0000 0000 0000 0000
677 | //
678 | // Bit 0 Low capacity 1 (alarm), 0 (normal) warning
679 | // Bit 1 Power tube overtemperature 1 (alarm), 0 (normal) alarm
680 | // Bit 2 Charging overvoltage 1 (alarm), 0 (normal) alarm
681 | // Bit 3 Discharging undervoltage 1 (alarm), 0 (normal) alarm
682 | // Bit 4 Battery over temperature 1 (alarm), 0 (normal) alarm
683 | // Bit 5 Charging overcurrent 1 (alarm), 0 (normal) alarm
684 | // Bit 6 Discharging overcurrent 1 (alarm), 0 (normal) alarm
685 | // Bit 7 Cell pressure difference 1 (alarm), 0 (normal) alarm
686 | // Bit 8 Overtemperature alarm in the battery box 1 (alarm), 0 (normal) alarm
687 | // Bit 9 Battery low temperature 1 (alarm), 0 (normal) alarm
688 | // Bit 10 Cell overvoltage 1 (alarm), 0 (normal) alarm
689 | // Bit 11 Cell undervoltage 1 (alarm), 0 (normal) alarm
690 | // Bit 12 309_A protection 1 (alarm), 0 (normal) alarm
691 | // Bit 13 309_A protection 1 (alarm), 0 (normal) alarm
692 | // Bit 14 Reserved
693 | // Bit 15 Reserved
694 | //
695 | // Examples:
696 | // 0x0001 = 00000000 00000001: Low capacity alarm
697 | // 0x0002 = 00000000 00000010: MOS tube over-temperature alarm
698 | // 0x0003 = 00000000 00000011: Low capacity alarm AND power tube over-temperature alarm
699 |
700 | // +---------------------------+
701 | // | Protection : byte 0 and 1 |
702 | // +---------------------------+
703 |
704 | uint8_t can_mesg[] = {0, 0, 0, 0, 0, 0, 0, 0};
705 |
706 | // JK-BMS alarm ?
707 | if (id(errors_bitmask).state > 1) {
708 | uint16_t jk_errormask = id(errors_bitmask).state;
709 |
710 | if ((jk_errormask & 0x04) | (jk_errormask & 0x80) | (jk_errormask & 0x400)) { // Hight.Voltage.Alarm JK bit 2,7,10
711 | can_mesg[0] = 0x02; // byte0_bit1 (0x02 = bin 10)
712 | id(alarm_status) = "OVP";
713 | ESP_LOGI("main", "Hight.Voltage.Alarm JK bit 2,7,10 - can_msg[0] : %x", can_mesg[0]);
714 | }
715 | if ((jk_errormask & 0x08) | (jk_errormask & 0x800)) { // Low.Voltage.Alarm JK bit 3,11
716 | can_mesg[0] = can_mesg[0] | 0x04; // byte0_bit2 (0x04 = bin 100)
717 | id(alarm_status) = "UVP";
718 | ESP_LOGI("main", "Low.Voltage.Alarm JK bit 3,11 - can_msg[0] : %x", can_mesg[0]);
719 | }
720 | if ((jk_errormask & 0x02) | (jk_errormask & 0x10) | (jk_errormask & 0x100)) { // Hight.Temp.Alarm JK bit 1,4,8
721 | can_mesg[0] = can_mesg[0] | 0x08; // byte0_bit3 (0x08 = bin 1000)
722 | id(alarm_status) = "OTP";
723 | ESP_LOGI("main", "Hight.Temp.Alarm JK bit 1,4,8 - can_msg[0] : %x", can_mesg[0]);
724 | }
725 | if ((jk_errormask & 0x200)) { // Low.Temp.Alarm JK bit 9
726 | can_mesg[0] = can_mesg[0] | 0x10; // byte0_bit4 (0x10 = bin 10000)
727 | id(alarm_status) = "UTP";
728 | ESP_LOGI("main", "Low.Temp.Alarm JK bit 9 - can_msg[0] : %x", can_mesg[0]);
729 | }
730 | if ((jk_errormask & 0x40)) { // Discharge.Over.Current JK bit 6
731 | can_mesg[0] = can_mesg[0] | 0x80; // byte0_bit7 (0x80 = bin 10000000)
732 | id(alarm_status) = "DOCP";
733 | ESP_LOGI("main", "Discharge.Over.Current JK bit 6 - can_msg[0] : %x", can_mesg[0]);
734 | }
735 | if ((jk_errormask & 0x20)) { // Charge.Over.Current JK bit 5
736 | can_mesg[1] = 0x01; // byte1_bit0 (0x01 = bin 1)
737 | id(alarm_status) = "COCP";
738 | ESP_LOGI("main", "Charge.Over.Current JK bit 5 - can_msg[1] : %x", can_mesg[1]);
739 | }
740 | if ((jk_errormask & 0x1000) | (jk_errormask & 0x2000)) { // BMS internal error JK bit 12,13
741 | can_mesg[1] = can_mesg[1] | 0x08; // byte1_bit3 (0x08 = bin 1000)
742 | id(alarm_status) = "BMS";
743 | ESP_LOGI("main", "BMS internal error JK bit 12,13 - can_msg[1] : %x", can_mesg[1]);
744 | }
745 | if ((jk_errormask & 0x80)) { // Cell Imbalance JK bit 7
746 | can_mesg[1] = can_mesg[1] | 0x10; // byte1_bit4 (0x10 = bin 10000)
747 | ESP_LOGI("main", "Cell Imbalance JK bit 7 - can_msg[1] : %x", can_mesg[1]);
748 | }
749 | }
750 | // No Alarm
751 | else id(alarm_status) = "NoAlarm";
752 |
753 | // +---------------------------+
754 | // | Warning : byte 2 and 3 |
755 | // +---------------------------+
756 |
757 | can_mesg[2] = 0x00; // byte2 (JK-BMS infos not available)
758 | can_mesg[3] = 0x00; // byte3 (JK-BMS infos not available)
759 |
760 | // +---------------------------+
761 | // | Flags : byte 4 to 7 |
762 | // +---------------------------+
763 |
764 | int batt_mods = ${batt_modules};
765 |
766 | can_mesg[4] = batt_mods; // byte4 - Module in parallel
767 | can_mesg[5] = 0x00; // byte5
768 | can_mesg[6] = 0x00; // byte6
769 | can_mesg[7] = 0x00; // byte7 - DIP switches 1,3 10000100 0x84
770 |
771 | ESP_LOGI("main", "send can id: 0x359 hex: %x %x %x %x %x %x %x %x", can_mesg[0], can_mesg[1], can_mesg[2], can_mesg[3], can_mesg[4], can_mesg[5], can_mesg[6], can_mesg[7]);
772 | return {can_mesg[0], can_mesg[1], can_mesg[2], can_mesg[3], can_mesg[4], can_mesg[5], can_mesg[6], can_mesg[7]};
773 |
774 | - if:
775 | condition:
776 | lambda: return ((id(can_msg_counter) == 1) & ((${can_protocol} == 3) | (${can_protocol} == 4)));
777 | then:
778 | canbus.send: # Protection Alarms and Warning ( SMA / Victron )
779 | can_id: 0x35A
780 | data: !lambda |-
781 |
782 | // +---------------------------+
783 | // | Protection : byte 0,1,2,3 |
784 | // +---------------------------+
785 |
786 | uint8_t can_mesg[] = {0, 0, 0, 0, 0, 0, 0, 0};
787 |
788 | // JK-BMS alarm ?
789 | if (id(errors_bitmask).state > 1) {
790 | uint16_t jk_errormask = id(errors_bitmask).state;
791 |
792 | if ((jk_errormask & 0x04) | (jk_errormask & 0x80) | (jk_errormask & 0x400)) { // Hight.Voltage.Alarm JK bit 2,7,10
793 | can_mesg[0] = 0x04; // byte0_bit2 (0x04 = bin 100)
794 | id(alarm_status) = "OVP";
795 | ESP_LOGI("main", "Hight.Voltage.Alarm JK bit 2,7,10 - can_msg[0] : %x", can_mesg[0]);
796 | }
797 | if ((jk_errormask & 0x08) | (jk_errormask & 0x800)) { // Low.Voltage.Alarm JK bit 3,11
798 | can_mesg[0] = can_mesg[0] | 0x10; // byte0_bit4 (0x10 = bin 10000)
799 | id(alarm_status) = "UVP";
800 | ESP_LOGI("main", "Low.Voltage.Alarm JK bit 3,11 - can_msg[0] : %x", can_mesg[0]);
801 | }
802 | if ((jk_errormask & 0x02) | (jk_errormask & 0x10) | (jk_errormask & 0x100)) { // Hight.Temp.Alarm JK bit 1,4,8
803 | can_mesg[0] = can_mesg[0] | 0x40; // byte0_bit6 (0x40 = bin 1000000)
804 | id(alarm_status) = "OTP";
805 | ESP_LOGI("main", "Hight.Temp.Alarm JK bit 1,4,8 - can_msg[0] : %x", can_mesg[0]);
806 | }
807 | if ((jk_errormask & 0x200)) { // Low.Temp.Alarm JK bit 9
808 | can_mesg[1] = 0x01; // byte1_bit0 (0x01 = bin 1)
809 | id(alarm_status) = "UTP";
810 | ESP_LOGI("main", "Low.Temp.Alarm JK bit 9 - can_msg[1] : %x", can_mesg[1]);
811 | }
812 | if ((jk_errormask & 0x40)) { // Discharge.Over.Current JK bit 6
813 | can_mesg[1] = can_mesg[1] | 0x40; // byte1_bit6 (0x40 = bin 1000000)
814 | id(alarm_status) = "DOCP";
815 | ESP_LOGI("main", "Discharge.Over.Current JK bit 6 - can_msg[1] : %x", can_mesg[1]);
816 | }
817 | if ((jk_errormask & 0x20)) { // Charge.Over.Current JK bit 5
818 | can_mesg[2] = 0x01; // byte2_bit0 (0x01 = bin 1)
819 | id(alarm_status) = "COCP";
820 | ESP_LOGI("main", "Charge.Over.Current JK bit 5 - can_msg[2] : %x", can_mesg[2]);
821 | }
822 | if ((jk_errormask & 0x1000) | (jk_errormask & 0x2000)) { // BMS.Internal.Error JK bit 12,13
823 | can_mesg[2] = can_mesg[2] | 0x40; // byte2_bit6 (0x40 = bin 1000000)
824 | id(alarm_status) = "BMS";
825 | ESP_LOGI("main", "BMS internal error JK bit 12,13 - can_msg[2] : %x", can_mesg[2]);
826 | }
827 | if ((jk_errormask & 0x80)) { // Cell.Imbalance JK bit 7
828 | can_mesg[3] = 0x01; // byte3_bit0 (0x01 = bin 1)
829 | ESP_LOGI("main", "Cell Imbalance JK bit 7 - can_msg[3] : %x", can_mesg[3]);
830 | }
831 | }
832 | // No Alarm
833 | else id(alarm_status) = "NoAlarm";
834 |
835 | ESP_LOGI("main", "send can id: 0x35A hex: %x %x %x %x %x %x %x %x", can_mesg[0], can_mesg[1], can_mesg[2], can_mesg[3], can_mesg[4], can_mesg[5], can_mesg[6], can_mesg[7]);
836 | return {can_mesg[0], can_mesg[1], can_mesg[2], can_mesg[3], can_mesg[4], can_mesg[5], can_mesg[6], can_mesg[7]};
837 |
838 | - if:
839 | condition:
840 | lambda: return id(can_msg_counter) == 2;
841 | then:
842 | canbus.send: # BMS instruction : Charge Volts, Charge Amps, Discharge Amps, Min voltage
843 | can_id: 0x351
844 | data: !lambda |-
845 |
846 | // +----------------+
847 | // | Charging Logic |
848 | // +----------------+
849 |
850 | // Warning : information from JK BMS is not available immediately after boot
851 |
852 | // Alarm : if JK-BMS alarm !
853 | if (id(errors_bitmask).state > 1) {
854 | id(charge_status) = "Alarm";
855 | }
856 | // No Alarm => Wait
857 | else if ((id(errors_bitmask).state < 2) & (id(charge_status) == "Alarm")) {
858 | id(charge_status) = "Wait";
859 | }
860 | // Charge ON : BMS and ESP32 charging switch is ON
861 | else if ((id(charging_switch).state) & (id(switch_charging).state)) {
862 |
863 | // Force Bulk : 'Charging manually (top bal)' switch is ON
864 | if (id(switch_chg_bulk).state) {
865 | id(charge_status) = "Force Bulk";
866 | }
867 | // No Force Bulk => Wait
868 | else if ((!id(switch_chg_bulk).state) & (id(charge_status) == "Force Bulk")) {
869 | id(charge_status) = "Wait";
870 | }
871 | // Bulk : Bat. V. <= Rebulk V. ( Absorption V. - Rebulk Offset V. ) ( Bulk : Rebulk V. = 55.2-2.5 = 52.7V by default )
872 | else if (id(total_voltage).state <= (id(bulk_voltage).state - id(rebulk_offset).state)) {
873 | id(charge_status) = "Bulk";
874 | if (id(absorption_script).is_running()) id(absorption_script).stop();
875 | }
876 | // Absorption : Bat. V >= ( Absorption V. - Absorption Offset V. ) ( Absorption V. = 55.2-0.05 = 55.15V by default )
877 | else if ((id(charge_status) == "Bulk") & (id(total_voltage).state >= (id(bulk_voltage).state - id(absorption_offset).state))) {
878 | id(charge_status) = "Absorption";
879 | if (!id(absorption_script).is_running()) id(absorption_script).execute(); // 10 % from top start absorption timer
880 | }
881 | // Bulk : (Bat. V. > Rebulk V.) + Wait / ESP32 startup ( Bulk : when starting the ESP32 )
882 | else if ((id(charge_status) == "Wait")) {
883 | id(charge_status) = "Bulk";
884 | if (id(absorption_script).is_running()) id(absorption_script).stop();
885 | }
886 | // Float : (Bat. V. > Rebulk V.) + Float switch ON and End Of Charge ( Float : after Absorption )
887 | else if ((id(switch_chg_float).state) & (id(charge_status) == "EOC")) {
888 | id(charge_status) = "Float";
889 | }
890 | // No Float => Wait
891 | else if ((!id(switch_chg_float).state) & (id(charge_status) == "Float")) {
892 | id(charge_status) = "Wait";
893 | }
894 | }
895 | // Charge OFF
896 | else id(charge_status) = "Wait";
897 |
898 | // +--------------------------------------+
899 | // | Charge values |
900 | // +--------------------------------------+
901 |
902 | // Bulk Charge
903 | if ((id(charge_status) == "Bulk") | (id(charge_status) == "Force Bulk") | (id(charge_status) == "Absorption")) {
904 | id(charging_v) = id(bulk_voltage).state;
905 | id(charging_a) = id(charging_current).state;
906 | }
907 | // Float Charge
908 | else if (id(charge_status) == "Float") {
909 | if (id(total_voltage).state > id(float_voltage).state){
910 | id(charging_v) = round(id(total_voltage).state * 10)/10; // Actual battery voltage
911 | id(charging_a) = 0;
912 | } else {
913 | id(charging_v) = id(float_voltage).state;
914 | id(charging_a) = id(charging_current).state;
915 | }
916 | }
917 | // End Of Charge (EOC) or Wait : Stop Charging
918 | else if ((id(charge_status) == "EOC") | (id(charge_status) == "Wait")) {
919 | id(charging_v) = round(id(total_voltage).state * 10)/10; // Actual battery voltage
920 | id(charging_a) = 0;
921 | }
922 |
923 | // +--------------------------------------+
924 | // | Discharge values |
925 | // +--------------------------------------+
926 |
927 | // Stop Discharging if BMS or ESP32 switch is OFF
928 | if ((!id(discharging_switch).state) | (!id(switch_discharging).state)) id(discharging_a) = 0;
929 | // Stop Discharging if battery voltage is low
930 | else if (id(total_voltage).state <= ${min_discharge_v}) id(discharging_a) = 0;
931 | // Discharging is OK
932 | else id(discharging_a) = ${discharge_a};
933 |
934 | // +--------------------------------------+
935 | // | Alarm overwrite values |
936 | // +--------------------------------------+
937 |
938 | ESP_LOGI("main", "Alarm Status : %s", id(alarm_status).c_str());
939 |
940 | // Alarm : Stop Charging and Discharging
941 | if ((id(alarm_status) == "OTP") | (id(alarm_status) == "BMS")){
942 | id(charging_v) = 51.2;
943 | id(charging_a) = 0;
944 | id(discharging_a) = 0;
945 | }
946 | // Alarm : Stop Charging
947 | else if ((id(alarm_status) == "OVP") | (id(alarm_status) == "UTP") | (id(alarm_status) == "COCP")){
948 | id(charging_v) = 51.2;
949 | id(charging_a) = 0;
950 | }
951 | // Alarm : Stop Discharging
952 | else if ((id(alarm_status) == "UVP") | (id(alarm_status) == "DOCP")){
953 | id(discharging_a) = 0;
954 | }
955 |
956 | // +--------------------------------------+
957 | // | CAN messages |
958 | // +--------------------------------------+
959 |
960 | // Byte [00:01] = CVL : Charge Limit Voltage
961 | // Byte [02:03] = CCL : Charge Limit Current
962 | // Byte [04:05] = DCL : Discharge Limit Current
963 | // Byte [06:07] = DVL : Discharge Limit Voltage
964 |
965 | uint8_t can_mesg[8];
966 |
967 | can_mesg[0] = uint16_t(id(charging_v) * 10) & 0xff;
968 | can_mesg[1] = uint16_t(id(charging_v) * 10) >> 8 & 0xff;
969 | can_mesg[2] = uint16_t(id(charging_a) * 10) & 0xff;
970 | can_mesg[3] = uint16_t(id(charging_a) * 10) >> 8 & 0xff;
971 | can_mesg[4] = uint16_t(id(discharging_a) * 10) & 0xff;
972 | can_mesg[5] = uint16_t(id(discharging_a) * 10) >> 8 & 0xff;
973 | can_mesg[6] = uint16_t(${min_discharge_v} * 10) & 0xff;
974 | can_mesg[7] = uint16_t(${min_discharge_v} * 10) >> 8 & 0xff;
975 |
976 | // Publish text sensor
977 | id(charging_status).publish_state(id(charge_status));
978 |
979 | // Logs
980 | ESP_LOGI("main", "send can id: 0x351 hex: %x %x %x %x %x %x %x %x", can_mesg[0], can_mesg[1], can_mesg[2], can_mesg[3], can_mesg[4], can_mesg[5], can_mesg[6], can_mesg[7]);
981 | ESP_LOGI("main", "Charge Status : %s", id(charge_status).c_str());
982 | return {can_mesg[0], can_mesg[1], can_mesg[2], can_mesg[3], can_mesg[4], can_mesg[5], can_mesg[6], can_mesg[7]};
983 |
984 | - if:
985 | condition:
986 | lambda: return id(can_msg_counter) == 3;
987 | then:
988 | canbus.send: # Actual State of Charge (SOC) / State of Health (SOH)
989 | can_id: 0x355
990 | data: !lambda |-
991 | int soh = round(((id(charging_cycles).state/${max_cycles})-1)*-100);
992 | uint8_t can_mesg[4];
993 | can_mesg[0] = uint16_t(id(state_of_charge).state) & 0xff;
994 | can_mesg[1] = uint16_t(id(state_of_charge).state) >> 8 & 0xff;
995 | can_mesg[2] = soh & 0xff;
996 | can_mesg[3] = soh >> 8 & 0xff;
997 | ESP_LOGI("main", "send can id: 0x355 hex: %x %x %x %x", can_mesg[0], can_mesg[1], can_mesg[2], can_mesg[3]);
998 | return {can_mesg[0], can_mesg[1], can_mesg[2], can_mesg[3]};
999 |
1000 | - if:
1001 | condition:
1002 | lambda: return id(can_msg_counter) == 4;
1003 | then:
1004 | canbus.send: # Actual Voltage / Current / Temperature / Cycles (Deye 0x305 ACK)
1005 | can_id: 0x356
1006 | data: !lambda |-
1007 |
1008 | // Byte [00:01] : Actual Voltage
1009 | // Byte [02:03] : Actual Current
1010 | // Byte [04:05] : Actual Temperature
1011 | // Byte [06:07] : Actual Cycles number (Sofar)
1012 |
1013 | uint8_t can_mesg[8];
1014 | can_mesg[0] = uint16_t(id(total_voltage).state * 100) & 0xff;
1015 | can_mesg[1] = uint16_t(id(total_voltage).state * 100) >> 8 & 0xff;
1016 | can_mesg[2] = int16_t(id(current).state * 10) & 0xff;
1017 | can_mesg[3] = int16_t(id(current).state * 10) >> 8 & 0xff;
1018 | can_mesg[4] = int16_t(max(id(temperature_sensor_1).state, id(temperature_sensor_2).state)* 10) & 0xff;
1019 | can_mesg[5] = int16_t(max(id(temperature_sensor_1).state, id(temperature_sensor_2).state)* 10) >> 8 & 0xff;
1020 | can_mesg[6] = uint16_t(id(charging_cycles).state) & 0xff;
1021 | can_mesg[7] = uint16_t(id(charging_cycles).state) >> 8 & 0xff;
1022 | ESP_LOGI("main", "send can id: 0x356 hex: %x %x %x %x %x %x %x %x", can_mesg[0], can_mesg[1], can_mesg[2], can_mesg[3], can_mesg[4], can_mesg[5], can_mesg[6], can_mesg[7]);
1023 | return {can_mesg[0], can_mesg[1], can_mesg[2], can_mesg[3], can_mesg[4], can_mesg[5], can_mesg[6], can_mesg[7]};
1024 |
1025 | - if:
1026 | condition:
1027 | lambda: return ((id(can_msg_counter) == 5) & ((${can_protocol} == 1) | (${can_protocol} == 2)));
1028 | then:
1029 | canbus.send: # Request flag to Enable/Disable: Charge, Discharge ( Pylontech / Goodwe / Seplos )
1030 | can_id: 0x35C
1031 | data: !lambda |-
1032 | uint8_t can_mesg[2];
1033 | can_mesg[0] = 0x00;
1034 | can_mesg[1] = 0x00;
1035 |
1036 | // Bit 7 : Charge enable
1037 | if ((id(charging_switch).state) & (id(switch_charging).state))
1038 | can_mesg[0] = 0x80;
1039 |
1040 | // Bit 6 : Discharge enable
1041 | if ((id(discharging_switch).state) & (id(switch_discharging).state))
1042 | can_mesg[0] = can_mesg[0] | 0x40;
1043 |
1044 | ESP_LOGI("main", "send can id: 0x35C hex: %x %x", can_mesg[0], can_mesg[1]);
1045 | return {can_mesg[0], can_mesg[1]};
1046 |
1047 | - if:
1048 | condition:
1049 | lambda: return ((id(can_msg_counter) == 6) & (${can_protocol} == 2));
1050 | then:
1051 | canbus.send: # Actual Max Cell Temp, Min Cell Temp, Max Cell V, Min Cell V ( Pylontech / Goodwe / Seplos )
1052 | can_id: 0x70
1053 | data: !lambda |-
1054 |
1055 | // Byte [00:01] : Max cell temperature
1056 | // Byte [02:03] : Min cell temperature
1057 | // Byte [04:05] : Max cell voltage
1058 | // Byte [06:07] : Min cell voltage
1059 |
1060 | int max_cell_voltage_i = id(max_cell_voltage).state * 100.0;
1061 | int min_cell_voltage_i = id(min_cell_voltage).state * 100.0;
1062 | uint8_t can_mesg[8];
1063 | can_mesg[0] = int16_t(max(id(temperature_sensor_1).state, id(temperature_sensor_2).state)* 10) & 0xff;
1064 | can_mesg[1] = int16_t(max(id(temperature_sensor_1).state, id(temperature_sensor_2).state)* 10) >> 8 & 0xff;
1065 | can_mesg[2] = int16_t(min(id(temperature_sensor_1).state, id(temperature_sensor_2).state)* 10) & 0xff;
1066 | can_mesg[3] = int16_t(min(id(temperature_sensor_1).state, id(temperature_sensor_2).state)* 10) >> 8 & 0xff;
1067 | can_mesg[4] = max_cell_voltage_i & 0xff;
1068 | can_mesg[5] = max_cell_voltage_i >> 8 & 0xff;
1069 | can_mesg[6] = min_cell_voltage_i & 0xff;
1070 | can_mesg[7] = min_cell_voltage_i >> 8 & 0xff;
1071 | ESP_LOGI("main", "send can id: 0x70 hex: %x %x %x %x %x %x %x %x", can_mesg[0], can_mesg[1], can_mesg[2], can_mesg[3], can_mesg[4], can_mesg[5], can_mesg[6], can_mesg[7]);
1072 | return {can_mesg[0], can_mesg[1], can_mesg[2], can_mesg[3], can_mesg[4], can_mesg[5], can_mesg[6], can_mesg[7]};
1073 |
1074 | - if:
1075 | condition:
1076 | lambda: return ((id(can_msg_counter) == 6) & (${can_protocol} == 2));
1077 | then:
1078 | - canbus.send: # Actual Max Cell Temp ID, Min Cell Temp ID, Max Cell V ID, Min Cell ID ( Pylontech / Goodwe / Seplos )
1079 | can_id: 0x371
1080 | data: !lambda |-
1081 |
1082 | // Byte [00:01] : Max cell temperature ID
1083 | // Byte [02:03] : Min cell temperature ID
1084 | // Byte [04:05] : Max cell voltage ID
1085 | // Byte [06:07] : Min cell voltage ID
1086 |
1087 | uint8_t can_mesg[8];
1088 |
1089 | // Min-Max Temp. Sensor ID ?
1090 | if (id(temperature_sensor_1).state >= id(temperature_sensor_2).state){
1091 | can_mesg[0] = 0x01;
1092 | can_mesg[2] = 0x02;
1093 | }
1094 | else {
1095 | can_mesg[0] = 0x02;
1096 | can_mesg[2] = 0x01;
1097 | }
1098 |
1099 | can_mesg[1] = 0x00;
1100 | can_mesg[3] = 0x00;
1101 | can_mesg[4] = uint16_t(id(max_voltage_cell).state) & 0xff;
1102 | can_mesg[5] = uint16_t(id(max_voltage_cell).state) >> 8 & 0xff;
1103 | can_mesg[6] = uint16_t(id(min_voltage_cell).state) & 0xff;
1104 | can_mesg[7] = uint16_t(id(min_voltage_cell).state) >> 8 & 0xff;
1105 | ESP_LOGI("main", "send can id: 0x371 hex: %x %x %x %x %x %x %x %x", can_mesg[0], can_mesg[1], can_mesg[2], can_mesg[3], can_mesg[4], can_mesg[5], can_mesg[6], can_mesg[7]);
1106 | return {can_mesg[0], can_mesg[1], can_mesg[2], can_mesg[3], can_mesg[4], can_mesg[5], can_mesg[6], can_mesg[7]};
1107 |
1108 | - if:
1109 | condition:
1110 | lambda: return ((id(can_msg_counter) == 7) & (${can_protocol} == 4));
1111 | then:
1112 | - canbus.send: # Battery modules information ( Victron )
1113 | can_id: 0x372
1114 | data: !lambda |-
1115 |
1116 | // Byte [00:01] : Nbr. of battery modules online
1117 | // Byte [02:03] : Nbr. of modules blocking charge
1118 | // Byte [04:05] : Nbr. of modules blocking discharge
1119 | // Byte [06:07] : Nbr. of battery modules offline
1120 |
1121 | uint8_t can_mesg[] = {0, 0, 0, 0, 0, 0, 0, 0};
1122 | can_mesg[0] = 0x01;
1123 |
1124 | ESP_LOGI("main", "send can id: 0x372 hex: %x %x %x %x %x %x %x %x", can_mesg[0], can_mesg[1], can_mesg[2], can_mesg[3], can_mesg[4], can_mesg[5], can_mesg[6], can_mesg[7]);
1125 | return {can_mesg[0], can_mesg[1], can_mesg[2], can_mesg[3], can_mesg[4], can_mesg[5], can_mesg[6], can_mesg[7]};
1126 |
1127 | - if:
1128 | condition:
1129 | lambda: return ((id(can_msg_counter) == 8) & (${can_protocol} == 4));
1130 | then:
1131 | canbus.send: # Actual Min Cell V, Max Cell V, Min Cell Temp (Kelvin), Max Cell Temp (Kelvin) ( Victron )
1132 | can_id: 0x373
1133 | data: !lambda |-
1134 |
1135 | // Byte [00:01] : Min cell voltage
1136 | // Byte [02:03] : Max cell voltage
1137 | // Byte [04:05] : Min cell temperature
1138 | // Byte [06:07] : Max cell temperature
1139 |
1140 | int min_cell_voltage_i = id(min_cell_voltage).state * 1000.0;
1141 | int max_cell_voltage_i = id(max_cell_voltage).state * 1000.0;
1142 | int min_temp_kelvin = min(id(temperature_sensor_1).state, id(temperature_sensor_2).state) + 273.15;
1143 | int max_temp_kelvin = max(id(temperature_sensor_1).state, id(temperature_sensor_2).state) + 273.15;
1144 |
1145 | uint8_t can_mesg[8];
1146 | can_mesg[0] = min_cell_voltage_i & 0xff;
1147 | can_mesg[1] = min_cell_voltage_i >> 8 & 0xff;
1148 | can_mesg[2] = max_cell_voltage_i & 0xff;
1149 | can_mesg[3] = max_cell_voltage_i >> 8 & 0xff;
1150 | can_mesg[4] = min_temp_kelvin & 0xff;
1151 | can_mesg[5] = min_temp_kelvin >> 8 & 0xff;
1152 | can_mesg[6] = max_temp_kelvin & 0xff;
1153 | can_mesg[7] = max_temp_kelvin >> 8 & 0xff;
1154 |
1155 | ESP_LOGI("main", "send can id: 0x373 hex: %x %x %x %x %x %x %x %x", can_mesg[0], can_mesg[1], can_mesg[2], can_mesg[3], can_mesg[4], can_mesg[5], can_mesg[6], can_mesg[7]);
1156 | return {can_mesg[0], can_mesg[1], can_mesg[2], can_mesg[3], can_mesg[4], can_mesg[5], can_mesg[6], can_mesg[7]};
1157 |
1158 | - if:
1159 | condition:
1160 | lambda: return ((id(can_msg_counter) == 8) & (${can_protocol} == 4));
1161 | then:
1162 | - canbus.send: # Min cell voltage ID [ASCII] ( Victron )
1163 | can_id: 0x374
1164 | data: !lambda |-
1165 |
1166 | int cell_id = id(min_voltage_cell).state;
1167 |
1168 | ESP_LOGI("main", "send can id: 0x374 [ASCII] Min cell voltage ID : %i", cell_id);
1169 |
1170 | if (cell_id == 1) return {0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1171 | else if (cell_id == 2) return {0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1172 | else if (cell_id == 3) return {0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1173 | else if (cell_id == 4) return {0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1174 | else if (cell_id == 5) return {0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1175 | else if (cell_id == 6) return {0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1176 | else if (cell_id == 7) return {0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1177 | else if (cell_id == 8) return {0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1178 | else if (cell_id == 9) return {0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1179 | else if (cell_id == 10) return {0x31, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1180 | else if (cell_id == 11) return {0x31, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1181 | else if (cell_id == 12) return {0x31, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1182 | else if (cell_id == 13) return {0x31, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1183 | else if (cell_id == 14) return {0x31, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1184 | else if (cell_id == 15) return {0x31, 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1185 | else if (cell_id == 16) return {0x31, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1186 | else return {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1187 |
1188 | - if:
1189 | condition:
1190 | lambda: return ((id(can_msg_counter) == 8) & (${can_protocol} == 4));
1191 | then:
1192 | - canbus.send: # Max cell voltage ID [ASCII] ( Victron )
1193 | can_id: 0x375
1194 | data: !lambda |-
1195 |
1196 | int cell_id = id(max_voltage_cell).state;
1197 |
1198 | ESP_LOGI("main", "send can id: 0x375 [ASCII] Max cell voltage ID : %i", cell_id);
1199 |
1200 | if (cell_id == 1) return {0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1201 | else if (cell_id == 2) return {0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1202 | else if (cell_id == 3) return {0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1203 | else if (cell_id == 4) return {0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1204 | else if (cell_id == 5) return {0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1205 | else if (cell_id == 6) return {0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1206 | else if (cell_id == 7) return {0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1207 | else if (cell_id == 8) return {0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1208 | else if (cell_id == 9) return {0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1209 | else if (cell_id == 10) return {0x31, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1210 | else if (cell_id == 11) return {0x31, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1211 | else if (cell_id == 12) return {0x31, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1212 | else if (cell_id == 13) return {0x31, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1213 | else if (cell_id == 14) return {0x31, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1214 | else if (cell_id == 15) return {0x31, 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1215 | else if (cell_id == 16) return {0x31, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1216 | else return {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1217 |
1218 | - if:
1219 | condition:
1220 | lambda: return ((id(can_msg_counter) == 8) & (${can_protocol} == 4));
1221 | then:
1222 | - canbus.send: # Min cell temperature ID [ASCII] ( Victron )
1223 | can_id: 0x376
1224 | data: !lambda |-
1225 |
1226 | // Min Temp. Sensor ID ?
1227 | if (id(temperature_sensor_1).state >= id(temperature_sensor_2).state){
1228 | ESP_LOGI("main", "send can id: 0x376 [ASCII] Min Temp. Sensor ID : 2");
1229 | return {0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1230 | }
1231 | else {
1232 | ESP_LOGI("main", "send can id: 0x376 [ASCII] Min Temp. Sensor ID : 1");
1233 | return {0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1234 | }
1235 |
1236 | - if:
1237 | condition:
1238 | lambda: return ((id(can_msg_counter) == 8) & (${can_protocol} == 4));
1239 | then:
1240 | - canbus.send: # Max cell temperature ID [ASCII] ( Victron )
1241 | can_id: 0x377
1242 | data: !lambda |-
1243 |
1244 | // Max Temp. Sensor ID ?
1245 | if (id(temperature_sensor_1).state >= id(temperature_sensor_2).state){
1246 | ESP_LOGI("main", "send can id: 0x377 [ASCII] Max Temp. Sensor ID : 1");
1247 | return {0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1248 | }
1249 | else {
1250 | ESP_LOGI("main", "send can id: 0x377 [ASCII] Max Temp. Sensor ID : 2");
1251 | return {0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1252 | }
1253 |
1254 | - if:
1255 | condition:
1256 | lambda: return ((id(can_msg_counter) == 9) & ((${can_protocol} == 2) | (${can_protocol} == 4)));
1257 | then:
1258 | - canbus.send: # Battery Installed Capacity Ah ( Victron, Sol-Ark, Luxpower )
1259 | can_id: 0x379
1260 | data: !lambda |-
1261 | uint8_t can_mesg[] = {0, 0, 0, 0, 0, 0, 0, 0};
1262 | can_mesg[0] = uint16_t(id(total_battery_capacity_setting).state) & 0xff;
1263 | can_mesg[1] = uint16_t(id(total_battery_capacity_setting).state) >> 8 & 0xff;
1264 |
1265 | ESP_LOGI("main", "send can id: 0x379 hex: %x %x %x %x %x %x %x %x", can_mesg[0], can_mesg[1], can_mesg[2], can_mesg[3], can_mesg[4], can_mesg[5], can_mesg[6], can_mesg[7]);
1266 | return {can_mesg[0], can_mesg[1], can_mesg[2], can_mesg[3], can_mesg[4], can_mesg[5], can_mesg[6], can_mesg[7]};
1267 |
1268 | - if:
1269 | condition:
1270 | lambda: return ((id(can_msg_counter) == 10) & (${can_protocol} == 4));
1271 | then:
1272 | - canbus.send: # Product identification [ASCII] ( Victron )
1273 | can_id: 0x382
1274 | data: !lambda |-
1275 | ESP_LOGI("main", "send can id: 0x382 [ASCII] Product : JK-BMS");
1276 | return {0x4A, 0x4B, 0x2D, 0x42, 0x4D, 0x53, 0x00, 0x00}; // JK-BMS
1277 |
1278 | - if:
1279 | condition:
1280 | lambda: return ((id(can_msg_counter) == 11) & ((${can_protocol} == 3) | (${can_protocol} == 4)));
1281 | then:
1282 | - canbus.send: # Battery information ( SMA, Victron )
1283 | can_id: 0x35F
1284 | data: !lambda |-
1285 |
1286 | // SMA Victron
1287 | // Byte [00:01] : Bat-Type Product ID
1288 | // Byte [02:03] : BMS Version Firmware version (1.16 => HEX [01:10])
1289 | // Byte [04:05] : Bat-Capacity Available Capacity Ah
1290 | // Byte [06:07] : Manufacturer ID Hardware version
1291 |
1292 | uint8_t can_mesg[] = {0, 0, 0, 0, 0, 0, 0, 0};
1293 | can_mesg[2] = 0x01;
1294 | can_mesg[3] = 0x10;
1295 | can_mesg[4] = uint16_t(id(total_battery_capacity_setting).state) & 0xff;
1296 | can_mesg[5] = uint16_t(id(total_battery_capacity_setting).state) >> 8 & 0xff;
1297 |
1298 | ESP_LOGI("main", "send can id: 0x35F hex: %x %x %x %x %x %x %x %x", can_mesg[0], can_mesg[1], can_mesg[2], can_mesg[3], can_mesg[4], can_mesg[5], can_mesg[6], can_mesg[7]);
1299 | return {can_mesg[0], can_mesg[1], can_mesg[2], can_mesg[3], can_mesg[4], can_mesg[5], can_mesg[6], can_mesg[7]};
1300 |
1301 | - if:
1302 | condition:
1303 | lambda: return id(can_msg_counter) == 12;
1304 | then:
1305 | - canbus.send:
1306 | can_id: 0x35E # Manufacturer name
1307 | data: !lambda |-
1308 | if (${can_bms_name} == 1){
1309 | ESP_LOGI("main", "send can id: 0x35E ASCII : PYLON");
1310 | return {0x50, 0x59, 0x4C, 0x4F, 0x4E, 0x20, 0x20, 0x20}; // PYLON ( recognized by Deye, display PYLON name and SOH )
1311 | }
1312 | else if (${can_bms_name} == 2){
1313 | ESP_LOGI("main", "send can id: 0x35E ASCII : GOODWE");
1314 | return {0x47, 0x4F, 0x4F, 0x44, 0x57, 0x45, 0x20, 0x20}; // GOODWE
1315 | }
1316 | else if (${can_bms_name} == 3){
1317 | ESP_LOGI("main", "send can id: 0x35E ASCII : SHEnergy");
1318 | return {0x53, 0x48, 0x45, 0x6E, 0x65, 0x72, 0x67, 0x79}; // SHEnergy (SEPLOS)
1319 | }
1320 | # Reset counter
1321 | - lambda: id(can_msg_counter) = 0;
1322 |
--------------------------------------------------------------------------------